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