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