001package net.minecraft.block; 002 003import cpw.mods.fml.relauncher.Side; 004import cpw.mods.fml.relauncher.SideOnly; 005import java.util.List; 006import java.util.Random; 007import net.minecraft.block.material.Material; 008import net.minecraft.client.renderer.texture.IconRegister; 009import net.minecraft.creativetab.CreativeTabs; 010import net.minecraft.entity.Entity; 011import net.minecraft.entity.player.EntityPlayer; 012import net.minecraft.entity.projectile.EntityArrow; 013import net.minecraft.util.AxisAlignedBB; 014import net.minecraft.world.IBlockAccess; 015import net.minecraft.world.World; 016 017import net.minecraftforge.common.ForgeDirection; 018import static net.minecraftforge.common.ForgeDirection.*; 019 020public abstract class BlockButton extends Block 021{ 022 /** Whether this button is sensible to arrows, used by wooden buttons. */ 023 protected boolean sensible; 024 025 protected BlockButton(int par1, boolean par2) 026 { 027 super(par1, Material.circuits); 028 this.setTickRandomly(true); 029 this.setCreativeTab(CreativeTabs.tabRedstone); 030 this.sensible = par2; 031 } 032 033 /** 034 * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been 035 * cleared to be reused) 036 */ 037 public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4) 038 { 039 return null; 040 } 041 042 /** 043 * How many world ticks before ticking 044 */ 045 public int tickRate(World par1World) 046 { 047 return this.sensible ? 30 : 20; 048 } 049 050 /** 051 * Is this block (a) opaque and (b) a full 1m cube? This determines whether or not to render the shared face of two 052 * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block. 053 */ 054 public boolean isOpaqueCube() 055 { 056 return false; 057 } 058 059 /** 060 * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc) 061 */ 062 public boolean renderAsNormalBlock() 063 { 064 return false; 065 } 066 067 /** 068 * checks to see if you can place this block can be placed on that side of a block: BlockLever overrides 069 */ 070 public boolean canPlaceBlockOnSide(World par1World, int par2, int par3, int par4, int par5) 071 { 072 ForgeDirection dir = ForgeDirection.getOrientation(par5); 073 return (dir == NORTH && par1World.isBlockSolidOnSide(par2, par3, par4 + 1, NORTH)) || 074 (dir == SOUTH && par1World.isBlockSolidOnSide(par2, par3, par4 - 1, SOUTH)) || 075 (dir == WEST && par1World.isBlockSolidOnSide(par2 + 1, par3, par4, WEST)) || 076 (dir == EAST && par1World.isBlockSolidOnSide(par2 - 1, par3, par4, EAST)); 077 } 078 079 /** 080 * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z 081 */ 082 public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4) 083 { 084 return (par1World.isBlockSolidOnSide(par2 - 1, par3, par4, EAST)) || 085 (par1World.isBlockSolidOnSide(par2 + 1, par3, par4, WEST)) || 086 (par1World.isBlockSolidOnSide(par2, par3, par4 - 1, SOUTH)) || 087 (par1World.isBlockSolidOnSide(par2, par3, par4 + 1, NORTH)); 088 } 089 090 /** 091 * Called when a block is placed using its ItemBlock. Args: World, X, Y, Z, side, hitX, hitY, hitZ, block metadata 092 */ 093 public int onBlockPlaced(World par1World, int par2, int par3, int par4, int par5, float par6, float par7, float par8, int par9) 094 { 095 int j1 = par1World.getBlockMetadata(par2, par3, par4); 096 int k1 = j1 & 8; 097 j1 &= 7; 098 099 100 ForgeDirection dir = ForgeDirection.getOrientation(par5); 101 102 if (dir == NORTH && par1World.isBlockSolidOnSide(par2, par3, par4 + 1, NORTH)) 103 { 104 j1 = 4; 105 } 106 else if (dir == SOUTH && par1World.isBlockSolidOnSide(par2, par3, par4 - 1, SOUTH)) 107 { 108 j1 = 3; 109 } 110 else if (dir == WEST && par1World.isBlockSolidOnSide(par2 + 1, par3, par4, WEST)) 111 { 112 j1 = 2; 113 } 114 else if (dir == EAST && par1World.isBlockSolidOnSide(par2 - 1, par3, par4, EAST)) 115 { 116 j1 = 1; 117 } 118 else 119 { 120 j1 = this.getOrientation(par1World, par2, par3, par4); 121 } 122 123 return j1 + k1; 124 } 125 126 /** 127 * Get side which this button is facing. 128 */ 129 private int getOrientation(World par1World, int par2, int par3, int par4) 130 { 131 if (par1World.isBlockSolidOnSide(par2 - 1, par3, par4, EAST)) return 1; 132 if (par1World.isBlockSolidOnSide(par2 + 1, par3, par4, WEST)) return 2; 133 if (par1World.isBlockSolidOnSide(par2, par3, par4 - 1, SOUTH)) return 3; 134 if (par1World.isBlockSolidOnSide(par2, par3, par4 + 1, NORTH)) return 4; 135 return 1; 136 } 137 138 /** 139 * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are 140 * their own) Args: x, y, z, neighbor blockID 141 */ 142 public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5) 143 { 144 if (this.redundantCanPlaceBlockAt(par1World, par2, par3, par4)) 145 { 146 int i1 = par1World.getBlockMetadata(par2, par3, par4) & 7; 147 boolean flag = false; 148 149 if (!par1World.isBlockSolidOnSide(par2 - 1, par3, par4, EAST) && i1 == 1) 150 { 151 flag = true; 152 } 153 154 if (!par1World.isBlockSolidOnSide(par2 + 1, par3, par4, WEST) && i1 == 2) 155 { 156 flag = true; 157 } 158 159 if (!par1World.isBlockSolidOnSide(par2, par3, par4 - 1, SOUTH) && i1 == 3) 160 { 161 flag = true; 162 } 163 164 if (!par1World.isBlockSolidOnSide(par2, par3, par4 + 1, NORTH) && i1 == 4) 165 { 166 flag = true; 167 } 168 169 if (flag) 170 { 171 this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0); 172 par1World.setBlockToAir(par2, par3, par4); 173 } 174 } 175 } 176 177 /** 178 * This method is redundant, check it out... 179 */ 180 private boolean redundantCanPlaceBlockAt(World par1World, int par2, int par3, int par4) 181 { 182 if (!this.canPlaceBlockAt(par1World, par2, par3, par4)) 183 { 184 this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0); 185 par1World.setBlockToAir(par2, par3, par4); 186 return false; 187 } 188 else 189 { 190 return true; 191 } 192 } 193 194 /** 195 * Updates the blocks bounds based on its current state. Args: world, x, y, z 196 */ 197 public void setBlockBoundsBasedOnState(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) 198 { 199 int l = par1IBlockAccess.getBlockMetadata(par2, par3, par4); 200 this.func_82534_e(l); 201 } 202 203 private void func_82534_e(int par1) 204 { 205 int j = par1 & 7; 206 boolean flag = (par1 & 8) > 0; 207 float f = 0.375F; 208 float f1 = 0.625F; 209 float f2 = 0.1875F; 210 float f3 = 0.125F; 211 212 if (flag) 213 { 214 f3 = 0.0625F; 215 } 216 217 if (j == 1) 218 { 219 this.setBlockBounds(0.0F, f, 0.5F - f2, f3, f1, 0.5F + f2); 220 } 221 else if (j == 2) 222 { 223 this.setBlockBounds(1.0F - f3, f, 0.5F - f2, 1.0F, f1, 0.5F + f2); 224 } 225 else if (j == 3) 226 { 227 this.setBlockBounds(0.5F - f2, f, 0.0F, 0.5F + f2, f1, f3); 228 } 229 else if (j == 4) 230 { 231 this.setBlockBounds(0.5F - f2, f, 1.0F - f3, 0.5F + f2, f1, 1.0F); 232 } 233 } 234 235 /** 236 * Called when the block is clicked by a player. Args: x, y, z, entityPlayer 237 */ 238 public void onBlockClicked(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer) {} 239 240 /** 241 * Called upon block activation (right click on the block.) 242 */ 243 public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9) 244 { 245 int i1 = par1World.getBlockMetadata(par2, par3, par4); 246 int j1 = i1 & 7; 247 int k1 = 8 - (i1 & 8); 248 249 if (k1 == 0) 250 { 251 return true; 252 } 253 else 254 { 255 par1World.setBlockMetadataWithNotify(par2, par3, par4, j1 + k1, 3); 256 par1World.markBlockRangeForRenderUpdate(par2, par3, par4, par2, par3, par4); 257 par1World.playSoundEffect((double)par2 + 0.5D, (double)par3 + 0.5D, (double)par4 + 0.5D, "random.click", 0.3F, 0.6F); 258 this.func_82536_d(par1World, par2, par3, par4, j1); 259 par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate(par1World)); 260 return true; 261 } 262 } 263 264 /** 265 * ejects contained items into the world, and notifies neighbours of an update, as appropriate 266 */ 267 public void breakBlock(World par1World, int par2, int par3, int par4, int par5, int par6) 268 { 269 if ((par6 & 8) > 0) 270 { 271 int j1 = par6 & 7; 272 this.func_82536_d(par1World, par2, par3, par4, j1); 273 } 274 275 super.breakBlock(par1World, par2, par3, par4, par5, par6); 276 } 277 278 /** 279 * Returns true if the block is emitting indirect/weak redstone power on the specified side. If isBlockNormalCube 280 * returns true, standard redstone propagation rules will apply instead and this will not be called. Args: World, X, 281 * Y, Z, side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block. 282 */ 283 public int isProvidingWeakPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 284 { 285 return (par1IBlockAccess.getBlockMetadata(par2, par3, par4) & 8) > 0 ? 15 : 0; 286 } 287 288 /** 289 * Returns true if the block is emitting direct/strong redstone power on the specified side. Args: World, X, Y, Z, 290 * side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block. 291 */ 292 public int isProvidingStrongPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 293 { 294 int i1 = par1IBlockAccess.getBlockMetadata(par2, par3, par4); 295 296 if ((i1 & 8) == 0) 297 { 298 return 0; 299 } 300 else 301 { 302 int j1 = i1 & 7; 303 return j1 == 5 && par5 == 1 ? 15 : (j1 == 4 && par5 == 2 ? 15 : (j1 == 3 && par5 == 3 ? 15 : (j1 == 2 && par5 == 4 ? 15 : (j1 == 1 && par5 == 5 ? 15 : 0)))); 304 } 305 } 306 307 /** 308 * Can this block provide power. Only wire currently seems to have this change based on its state. 309 */ 310 public boolean canProvidePower() 311 { 312 return true; 313 } 314 315 /** 316 * Ticks the block if it's been scheduled 317 */ 318 public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random) 319 { 320 if (!par1World.isRemote) 321 { 322 int l = par1World.getBlockMetadata(par2, par3, par4); 323 324 if ((l & 8) != 0) 325 { 326 if (this.sensible) 327 { 328 this.func_82535_o(par1World, par2, par3, par4); 329 } 330 else 331 { 332 par1World.setBlockMetadataWithNotify(par2, par3, par4, l & 7, 3); 333 int i1 = l & 7; 334 this.func_82536_d(par1World, par2, par3, par4, i1); 335 par1World.playSoundEffect((double)par2 + 0.5D, (double)par3 + 0.5D, (double)par4 + 0.5D, "random.click", 0.3F, 0.5F); 336 par1World.markBlockRangeForRenderUpdate(par2, par3, par4, par2, par3, par4); 337 } 338 } 339 } 340 } 341 342 /** 343 * Sets the block's bounds for rendering it as an item 344 */ 345 public void setBlockBoundsForItemRender() 346 { 347 float f = 0.1875F; 348 float f1 = 0.125F; 349 float f2 = 0.125F; 350 this.setBlockBounds(0.5F - f, 0.5F - f1, 0.5F - f2, 0.5F + f, 0.5F + f1, 0.5F + f2); 351 } 352 353 /** 354 * Triggered whenever an entity collides with this block (enters into the block). Args: world, x, y, z, entity 355 */ 356 public void onEntityCollidedWithBlock(World par1World, int par2, int par3, int par4, Entity par5Entity) 357 { 358 if (!par1World.isRemote) 359 { 360 if (this.sensible) 361 { 362 if ((par1World.getBlockMetadata(par2, par3, par4) & 8) == 0) 363 { 364 this.func_82535_o(par1World, par2, par3, par4); 365 } 366 } 367 } 368 } 369 370 protected void func_82535_o(World par1World, int par2, int par3, int par4) 371 { 372 int l = par1World.getBlockMetadata(par2, par3, par4); 373 int i1 = l & 7; 374 boolean flag = (l & 8) != 0; 375 this.func_82534_e(l); 376 List list = par1World.getEntitiesWithinAABB(EntityArrow.class, AxisAlignedBB.getAABBPool().getAABB((double)par2 + this.minX, (double)par3 + this.minY, (double)par4 + this.minZ, (double)par2 + this.maxX, (double)par3 + this.maxY, (double)par4 + this.maxZ)); 377 boolean flag1 = !list.isEmpty(); 378 379 if (flag1 && !flag) 380 { 381 par1World.setBlockMetadataWithNotify(par2, par3, par4, i1 | 8, 3); 382 this.func_82536_d(par1World, par2, par3, par4, i1); 383 par1World.markBlockRangeForRenderUpdate(par2, par3, par4, par2, par3, par4); 384 par1World.playSoundEffect((double)par2 + 0.5D, (double)par3 + 0.5D, (double)par4 + 0.5D, "random.click", 0.3F, 0.6F); 385 } 386 387 if (!flag1 && flag) 388 { 389 par1World.setBlockMetadataWithNotify(par2, par3, par4, i1, 3); 390 this.func_82536_d(par1World, par2, par3, par4, i1); 391 par1World.markBlockRangeForRenderUpdate(par2, par3, par4, par2, par3, par4); 392 par1World.playSoundEffect((double)par2 + 0.5D, (double)par3 + 0.5D, (double)par4 + 0.5D, "random.click", 0.3F, 0.5F); 393 } 394 395 if (flag1) 396 { 397 par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate(par1World)); 398 } 399 } 400 401 private void func_82536_d(World par1World, int par2, int par3, int par4, int par5) 402 { 403 par1World.notifyBlocksOfNeighborChange(par2, par3, par4, this.blockID); 404 405 if (par5 == 1) 406 { 407 par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID); 408 } 409 else if (par5 == 2) 410 { 411 par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID); 412 } 413 else if (par5 == 3) 414 { 415 par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID); 416 } 417 else if (par5 == 4) 418 { 419 par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID); 420 } 421 else 422 { 423 par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID); 424 } 425 } 426 427 @SideOnly(Side.CLIENT) 428 429 /** 430 * When this method is called, your block should register all the icons it needs with the given IconRegister. This 431 * is the only chance you get to register icons. 432 */ 433 public void registerIcons(IconRegister par1IconRegister) {} 434}