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.creativetab.CreativeTabs;
008import net.minecraft.entity.Entity;
009import net.minecraft.entity.EntityLiving;
010import net.minecraft.entity.player.EntityPlayer;
011import net.minecraft.tileentity.TileEntity;
012import net.minecraft.tileentity.TileEntityPiston;
013import net.minecraft.util.AxisAlignedBB;
014import net.minecraft.util.Facing;
015import net.minecraft.util.MathHelper;
016import net.minecraft.world.IBlockAccess;
017import net.minecraft.world.World;
018
019public 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}