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