    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.setBlockToAir(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.setBlockToAir(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
    429    /**
    430     * When this method is called, your block should register all the icons it needs with the given IconRegister. This
    431     * is the only chance you get to register icons.
    432     */
    433    public void registerIcons(IconRegister par1IconRegister) {}
    434}