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