001package net.minecraft.block; 002 003import cpw.mods.fml.relauncher.Side; 004import cpw.mods.fml.relauncher.SideOnly; 005import java.util.List; 006import net.minecraft.block.material.Material; 007import net.minecraft.client.renderer.texture.IconRegister; 008import net.minecraft.creativetab.CreativeTabs; 009import net.minecraft.entity.Entity; 010import net.minecraft.entity.EntityLiving; 011import net.minecraft.entity.player.EntityPlayer; 012import net.minecraft.item.ItemStack; 013import net.minecraft.tileentity.TileEntity; 014import net.minecraft.tileentity.TileEntityPiston; 015import net.minecraft.util.AxisAlignedBB; 016import net.minecraft.util.Facing; 017import net.minecraft.util.Icon; 018import net.minecraft.util.MathHelper; 019import net.minecraft.world.IBlockAccess; 020import net.minecraft.world.World; 021 022public class BlockPistonBase extends Block 023{ 024 /** This pistons is the sticky one? */ 025 private final boolean isSticky; 026 @SideOnly(Side.CLIENT) 027 private Icon field_94498_b; 028 @SideOnly(Side.CLIENT) 029 private Icon field_94499_c; 030 @SideOnly(Side.CLIENT) 031 private Icon field_94497_cO; 032 033 public BlockPistonBase(int par1, boolean par2) 034 { 035 super(par1, Material.piston); 036 this.isSticky = par2; 037 this.setStepSound(soundStoneFootstep); 038 this.setHardness(0.5F); 039 this.setCreativeTab(CreativeTabs.tabRedstone); 040 } 041 042 @SideOnly(Side.CLIENT) 043 044 /** 045 * Return the either 106 or 107 as the texture index depending on the isSticky flag. This will actually never get 046 * called by TileEntityRendererPiston.renderPiston() because TileEntityPiston.shouldRenderHead() will always return 047 * false. 048 */ 049 public Icon getPistonExtensionTexture() 050 { 051 return this.field_94497_cO; 052 } 053 054 @SideOnly(Side.CLIENT) 055 public void func_96479_b(float par1, float par2, float par3, float par4, float par5, float par6) 056 { 057 this.setBlockBounds(par1, par2, par3, par4, par5, par6); 058 } 059 060 @SideOnly(Side.CLIENT) 061 062 /** 063 * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata 064 */ 065 public Icon getBlockTextureFromSideAndMetadata(int par1, int par2) 066 { 067 int k = getOrientation(par2); 068 return k > 5 ? this.field_94497_cO : (par1 == k ? (!isExtended(par2) && this.minX <= 0.0D && this.minY <= 0.0D && this.minZ <= 0.0D && this.maxX >= 1.0D && this.maxY >= 1.0D && this.maxZ >= 1.0D ? this.field_94497_cO : this.field_94498_b) : (par1 == Facing.faceToSide[k] ? this.field_94499_c : this.field_94336_cN)); 069 } 070 071 @SideOnly(Side.CLIENT) 072 public static Icon func_94496_b(String par0Str) 073 { 074 return par0Str == "piston_side" ? Block.pistonBase.field_94336_cN : (par0Str == "piston_top" ? Block.pistonBase.field_94497_cO : (par0Str == "piston_top_sticky" ? Block.pistonStickyBase.field_94497_cO : (par0Str == "piston_inner_top" ? Block.pistonBase.field_94498_b : null))); 075 } 076 077 @SideOnly(Side.CLIENT) 078 public void func_94332_a(IconRegister par1IconRegister) 079 { 080 this.field_94336_cN = par1IconRegister.func_94245_a("piston_side"); 081 this.field_94497_cO = par1IconRegister.func_94245_a(this.isSticky ? "piston_top_sticky" : "piston_top"); 082 this.field_94498_b = par1IconRegister.func_94245_a("piston_inner_top"); 083 this.field_94499_c = par1IconRegister.func_94245_a("piston_bottom"); 084 } 085 086 /** 087 * The type of render function that is called for this block 088 */ 089 public int getRenderType() 090 { 091 return 16; 092 } 093 094 /** 095 * Is this block (a) opaque and (b) a full 1m cube? This determines whether or not to render the shared face of two 096 * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block. 097 */ 098 public boolean isOpaqueCube() 099 { 100 return false; 101 } 102 103 /** 104 * Called upon block activation (right click on the block.) 105 */ 106 public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9) 107 { 108 return false; 109 } 110 111 /** 112 * Called when the block is placed in the world. 113 */ 114 public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving, ItemStack par6ItemStack) 115 { 116 int l = determineOrientation(par1World, par2, par3, par4, par5EntityLiving); 117 par1World.setBlockMetadataWithNotify(par2, par3, par4, l, 2); 118 119 if (!par1World.isRemote) 120 { 121 this.updatePistonState(par1World, par2, par3, par4); 122 } 123 } 124 125 /** 126 * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are 127 * their own) Args: x, y, z, neighbor blockID 128 */ 129 public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5) 130 { 131 if (!par1World.isRemote) 132 { 133 this.updatePistonState(par1World, par2, par3, par4); 134 } 135 } 136 137 /** 138 * Called whenever the block is added into the world. Args: world, x, y, z 139 */ 140 public void onBlockAdded(World par1World, int par2, int par3, int par4) 141 { 142 if (!par1World.isRemote && par1World.getBlockTileEntity(par2, par3, par4) == null) 143 { 144 this.updatePistonState(par1World, par2, par3, par4); 145 } 146 } 147 148 /** 149 * handles attempts to extend or retract the piston. 150 */ 151 private void updatePistonState(World par1World, int par2, int par3, int par4) 152 { 153 int l = par1World.getBlockMetadata(par2, par3, par4); 154 int i1 = getOrientation(l); 155 156 if (i1 != 7) 157 { 158 boolean flag = this.isIndirectlyPowered(par1World, par2, par3, par4, i1); 159 160 if (flag && !isExtended(l)) 161 { 162 if (canExtend(par1World, par2, par3, par4, i1)) 163 { 164 par1World.addBlockEvent(par2, par3, par4, this.blockID, 0, i1); 165 } 166 } 167 else if (!flag && isExtended(l)) 168 { 169 par1World.setBlockMetadataWithNotify(par2, par3, par4, i1, 2); 170 par1World.addBlockEvent(par2, par3, par4, this.blockID, 1, i1); 171 } 172 } 173 } 174 175 /** 176 * checks the block to that side to see if it is indirectly powered. 177 */ 178 private boolean isIndirectlyPowered(World par1World, int par2, int par3, int par4, int par5) 179 { 180 return par5 != 0 && par1World.func_94574_k(par2, par3 - 1, par4, 0) ? true : (par5 != 1 && par1World.func_94574_k(par2, par3 + 1, par4, 1) ? true : (par5 != 2 && par1World.func_94574_k(par2, par3, par4 - 1, 2) ? true : (par5 != 3 && par1World.func_94574_k(par2, par3, par4 + 1, 3) ? true : (par5 != 5 && par1World.func_94574_k(par2 + 1, par3, par4, 5) ? true : (par5 != 4 && par1World.func_94574_k(par2 - 1, par3, par4, 4) ? true : (par1World.func_94574_k(par2, par3, par4, 0) ? true : (par1World.func_94574_k(par2, par3 + 2, par4, 1) ? true : (par1World.func_94574_k(par2, par3 + 1, par4 - 1, 2) ? true : (par1World.func_94574_k(par2, par3 + 1, par4 + 1, 3) ? true : (par1World.func_94574_k(par2 - 1, par3 + 1, par4, 4) ? true : par1World.func_94574_k(par2 + 1, par3 + 1, par4, 5))))))))))); 181 } 182 183 /** 184 * Called when the block receives a BlockEvent - see World.addBlockEvent. By default, passes it on to the tile 185 * entity at this location. Args: world, x, y, z, blockID, EventID, event parameter 186 */ 187 public boolean onBlockEventReceived(World par1World, int par2, int par3, int par4, int par5, int par6) 188 { 189 if (!par1World.isRemote) 190 { 191 boolean flag = this.isIndirectlyPowered(par1World, par2, par3, par4, par6); 192 193 if (flag && par5 == 1) 194 { 195 par1World.setBlockMetadataWithNotify(par2, par3, par4, par6 | 8, 2); 196 return false; 197 } 198 199 if (!flag && par5 == 0) 200 { 201 return false; 202 } 203 } 204 205 if (par5 == 0) 206 { 207 if (!this.tryExtend(par1World, par2, par3, par4, par6)) 208 { 209 return false; 210 } 211 212 par1World.setBlockMetadataWithNotify(par2, par3, par4, par6 | 8, 2); 213 par1World.playSoundEffect((double)par2 + 0.5D, (double)par3 + 0.5D, (double)par4 + 0.5D, "tile.piston.out", 0.5F, par1World.rand.nextFloat() * 0.25F + 0.6F); 214 } 215 else if (par5 == 1) 216 { 217 TileEntity tileentity = par1World.getBlockTileEntity(par2 + Facing.offsetsXForSide[par6], par3 + Facing.offsetsYForSide[par6], par4 + Facing.offsetsZForSide[par6]); 218 219 if (tileentity instanceof TileEntityPiston) 220 { 221 ((TileEntityPiston)tileentity).clearPistonTileEntity(); 222 } 223 224 par1World.setBlockAndMetadataWithNotify(par2, par3, par4, Block.pistonMoving.blockID, par6, 3); 225 par1World.setBlockTileEntity(par2, par3, par4, BlockPistonMoving.getTileEntity(this.blockID, par6, par6, false, true)); 226 227 if (this.isSticky) 228 { 229 int j1 = par2 + Facing.offsetsXForSide[par6] * 2; 230 int k1 = par3 + Facing.offsetsYForSide[par6] * 2; 231 int l1 = par4 + Facing.offsetsZForSide[par6] * 2; 232 int i2 = par1World.getBlockId(j1, k1, l1); 233 int j2 = par1World.getBlockMetadata(j1, k1, l1); 234 boolean flag1 = false; 235 236 if (i2 == Block.pistonMoving.blockID) 237 { 238 TileEntity tileentity1 = par1World.getBlockTileEntity(j1, k1, l1); 239 240 if (tileentity1 instanceof TileEntityPiston) 241 { 242 TileEntityPiston tileentitypiston = (TileEntityPiston)tileentity1; 243 244 if (tileentitypiston.getPistonOrientation() == par6 && tileentitypiston.isExtending()) 245 { 246 tileentitypiston.clearPistonTileEntity(); 247 i2 = tileentitypiston.getStoredBlockID(); 248 j2 = tileentitypiston.getBlockMetadata(); 249 flag1 = true; 250 } 251 } 252 } 253 254 if (!flag1 && i2 > 0 && canPushBlock(i2, par1World, j1, k1, l1, false) && (Block.blocksList[i2].getMobilityFlag() == 0 || i2 == Block.pistonBase.blockID || i2 == Block.pistonStickyBase.blockID)) 255 { 256 par2 += Facing.offsetsXForSide[par6]; 257 par3 += Facing.offsetsYForSide[par6]; 258 par4 += Facing.offsetsZForSide[par6]; 259 par1World.setBlockAndMetadataWithNotify(par2, par3, par4, Block.pistonMoving.blockID, j2, 3); 260 par1World.setBlockTileEntity(par2, par3, par4, BlockPistonMoving.getTileEntity(i2, j2, par6, false, false)); 261 par1World.func_94571_i(j1, k1, l1); 262 } 263 else if (!flag1) 264 { 265 par1World.func_94571_i(par2 + Facing.offsetsXForSide[par6], par3 + Facing.offsetsYForSide[par6], par4 + Facing.offsetsZForSide[par6]); 266 } 267 } 268 else 269 { 270 par1World.func_94571_i(par2 + Facing.offsetsXForSide[par6], par3 + Facing.offsetsYForSide[par6], par4 + Facing.offsetsZForSide[par6]); 271 } 272 273 par1World.playSoundEffect((double)par2 + 0.5D, (double)par3 + 0.5D, (double)par4 + 0.5D, "tile.piston.in", 0.5F, par1World.rand.nextFloat() * 0.15F + 0.6F); 274 } 275 276 return true; 277 } 278 279 /** 280 * Updates the blocks bounds based on its current state. Args: world, x, y, z 281 */ 282 public void setBlockBoundsBasedOnState(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) 283 { 284 int l = par1IBlockAccess.getBlockMetadata(par2, par3, par4); 285 286 if (isExtended(l)) 287 { 288 switch (getOrientation(l)) 289 { 290 case 0: 291 this.setBlockBounds(0.0F, 0.25F, 0.0F, 1.0F, 1.0F, 1.0F); 292 break; 293 case 1: 294 this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.75F, 1.0F); 295 break; 296 case 2: 297 this.setBlockBounds(0.0F, 0.0F, 0.25F, 1.0F, 1.0F, 1.0F); 298 break; 299 case 3: 300 this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.75F); 301 break; 302 case 4: 303 this.setBlockBounds(0.25F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); 304 break; 305 case 5: 306 this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.75F, 1.0F, 1.0F); 307 } 308 } 309 else 310 { 311 this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); 312 } 313 } 314 315 /** 316 * Sets the block's bounds for rendering it as an item 317 */ 318 public void setBlockBoundsForItemRender() 319 { 320 this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); 321 } 322 323 /** 324 * Adds all intersecting collision boxes to a list. (Be sure to only add boxes to the list if they intersect the 325 * mask.) Parameters: World, X, Y, Z, mask, list, colliding entity 326 */ 327 public void addCollisionBoxesToList(World par1World, int par2, int par3, int par4, AxisAlignedBB par5AxisAlignedBB, List par6List, Entity par7Entity) 328 { 329 this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); 330 super.addCollisionBoxesToList(par1World, par2, par3, par4, par5AxisAlignedBB, par6List, par7Entity); 331 } 332 333 /** 334 * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been 335 * cleared to be reused) 336 */ 337 public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4) 338 { 339 this.setBlockBoundsBasedOnState(par1World, par2, par3, par4); 340 return super.getCollisionBoundingBoxFromPool(par1World, par2, par3, par4); 341 } 342 343 /** 344 * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc) 345 */ 346 public boolean renderAsNormalBlock() 347 { 348 return false; 349 } 350 351 /** 352 * returns an int which describes the direction the piston faces 353 */ 354 public static int getOrientation(int par0) 355 { 356 return par0 & 7; 357 } 358 359 /** 360 * Determine if the metadata is related to something powered. 361 */ 362 public static boolean isExtended(int par0) 363 { 364 return (par0 & 8) != 0; 365 } 366 367 /** 368 * gets the way this piston should face for that entity that placed it. 369 */ 370 public static int determineOrientation(World par0World, int par1, int par2, int par3, EntityLiving par4EntityLiving) 371 { 372 if (MathHelper.abs((float)par4EntityLiving.posX - (float)par1) < 2.0F && MathHelper.abs((float)par4EntityLiving.posZ - (float)par3) < 2.0F) 373 { 374 double d0 = par4EntityLiving.posY + 1.82D - (double)par4EntityLiving.yOffset; 375 376 if (d0 - (double)par2 > 2.0D) 377 { 378 return 1; 379 } 380 381 if ((double)par2 - d0 > 0.0D) 382 { 383 return 0; 384 } 385 } 386 387 int l = MathHelper.floor_double((double)(par4EntityLiving.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3; 388 return l == 0 ? 2 : (l == 1 ? 5 : (l == 2 ? 3 : (l == 3 ? 4 : 0))); 389 } 390 391 /** 392 * returns true if the piston can push the specified block 393 */ 394 private static boolean canPushBlock(int par0, World par1World, int par2, int par3, int par4, boolean par5) 395 { 396 if (par0 == Block.obsidian.blockID) 397 { 398 return false; 399 } 400 else 401 { 402 if (par0 != Block.pistonBase.blockID && par0 != Block.pistonStickyBase.blockID) 403 { 404 if (Block.blocksList[par0].getBlockHardness(par1World, par2, par3, par4) == -1.0F) 405 { 406 return false; 407 } 408 409 if (Block.blocksList[par0].getMobilityFlag() == 2) 410 { 411 return false; 412 } 413 414 if (Block.blocksList[par0].getMobilityFlag() == 1) 415 { 416 if (!par5) 417 { 418 return false; 419 } 420 421 return true; 422 } 423 } 424 else if (isExtended(par1World.getBlockMetadata(par2, par3, par4))) 425 { 426 return false; 427 } 428 429 return !par1World.blockHasTileEntity(par2, par3, par4); 430 } 431 } 432 433 /** 434 * checks to see if this piston could push the blocks in front of it. 435 */ 436 private static boolean canExtend(World par0World, int par1, int par2, int par3, int par4) 437 { 438 int i1 = par1 + Facing.offsetsXForSide[par4]; 439 int j1 = par2 + Facing.offsetsYForSide[par4]; 440 int k1 = par3 + Facing.offsetsZForSide[par4]; 441 int l1 = 0; 442 443 while (true) 444 { 445 if (l1 < 13) 446 { 447 if (j1 <= 0 || j1 >= par0World.getHeight() - 1) 448 { 449 return false; 450 } 451 452 int i2 = par0World.getBlockId(i1, j1, k1); 453 454 if (i2 != 0) 455 { 456 if (!canPushBlock(i2, par0World, i1, j1, k1, true)) 457 { 458 return false; 459 } 460 461 if (Block.blocksList[i2].getMobilityFlag() != 1) 462 { 463 if (l1 == 12) 464 { 465 return false; 466 } 467 468 i1 += Facing.offsetsXForSide[par4]; 469 j1 += Facing.offsetsYForSide[par4]; 470 k1 += Facing.offsetsZForSide[par4]; 471 ++l1; 472 continue; 473 } 474 } 475 } 476 477 return true; 478 } 479 } 480 481 /** 482 * attempts to extend the piston. returns false if impossible. 483 */ 484 private boolean tryExtend(World par1World, int par2, int par3, int par4, int par5) 485 { 486 int i1 = par2 + Facing.offsetsXForSide[par5]; 487 int j1 = par3 + Facing.offsetsYForSide[par5]; 488 int k1 = par4 + Facing.offsetsZForSide[par5]; 489 int l1 = 0; 490 491 while (true) 492 { 493 int i2; 494 495 if (l1 < 13) 496 { 497 if (j1 <= 0 || j1 >= par1World.getHeight() - 1) 498 { 499 return false; 500 } 501 502 i2 = par1World.getBlockId(i1, j1, k1); 503 504 if (i2 != 0) 505 { 506 if (!canPushBlock(i2, par1World, i1, j1, k1, true)) 507 { 508 return false; 509 } 510 511 if (Block.blocksList[i2].getMobilityFlag() != 1) 512 { 513 if (l1 == 12) 514 { 515 return false; 516 } 517 518 i1 += Facing.offsetsXForSide[par5]; 519 j1 += Facing.offsetsYForSide[par5]; 520 k1 += Facing.offsetsZForSide[par5]; 521 ++l1; 522 continue; 523 } 524 525 Block.blocksList[i2].dropBlockAsItem(par1World, i1, j1, k1, par1World.getBlockMetadata(i1, j1, k1), 0); 526 par1World.func_94571_i(i1, j1, k1); 527 } 528 } 529 530 l1 = i1; 531 i2 = j1; 532 int j2 = k1; 533 int k2 = 0; 534 int[] aint; 535 int l2; 536 int i3; 537 int j3; 538 539 for (aint = new int[13]; i1 != par2 || j1 != par3 || k1 != par4; k1 = j3) 540 { 541 l2 = i1 - Facing.offsetsXForSide[par5]; 542 i3 = j1 - Facing.offsetsYForSide[par5]; 543 j3 = k1 - Facing.offsetsZForSide[par5]; 544 int k3 = par1World.getBlockId(l2, i3, j3); 545 int l3 = par1World.getBlockMetadata(l2, i3, j3); 546 547 if (k3 == this.blockID && l2 == par2 && i3 == par3 && j3 == par4) 548 { 549 par1World.setBlockAndMetadataWithNotify(i1, j1, k1, Block.pistonMoving.blockID, par5 | (this.isSticky ? 8 : 0), 4); 550 par1World.setBlockTileEntity(i1, j1, k1, BlockPistonMoving.getTileEntity(Block.pistonExtension.blockID, par5 | (this.isSticky ? 8 : 0), par5, true, false)); 551 } 552 else 553 { 554 par1World.setBlockAndMetadataWithNotify(i1, j1, k1, Block.pistonMoving.blockID, l3, 4); 555 par1World.setBlockTileEntity(i1, j1, k1, BlockPistonMoving.getTileEntity(k3, l3, par5, true, false)); 556 } 557 558 aint[k2++] = k3; 559 i1 = l2; 560 j1 = i3; 561 } 562 563 i1 = l1; 564 j1 = i2; 565 k1 = j2; 566 567 for (k2 = 0; i1 != par2 || j1 != par3 || k1 != par4; k1 = j3) 568 { 569 l2 = i1 - Facing.offsetsXForSide[par5]; 570 i3 = j1 - Facing.offsetsYForSide[par5]; 571 j3 = k1 - Facing.offsetsZForSide[par5]; 572 par1World.notifyBlocksOfNeighborChange(l2, i3, j3, aint[k2++]); 573 i1 = l2; 574 j1 = i3; 575 } 576 577 return true; 578 } 579 } 580}