    001package net.minecraft.block;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.Random;
    006import net.minecraft.block.material.Material;
    007import net.minecraft.client.renderer.texture.IconRegister;
    008import net.minecraft.entity.EntityLiving;
    009import net.minecraft.item.ItemStack;
    010import net.minecraft.util.Direction;
    011import net.minecraft.util.Icon;
    012import net.minecraft.util.MathHelper;
    013import net.minecraft.world.IBlockAccess;
    014import net.minecraft.world.World;
    015
    016public abstract class BlockRedstoneLogic extends BlockDirectional
    017{
    018    /** Tells whether the repeater is powered or not */
    019    protected final boolean isRepeaterPowered;
    020
    021    protected BlockRedstoneLogic(int par1, boolean par2)
    022    {
    023        super(par1, Material.circuits);
    024        this.isRepeaterPowered = par2;
    025        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
    026    }
    027
    028    /**
    029     * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
    030     */
    031    public boolean renderAsNormalBlock()
    032    {
    033        return false;
    034    }
    035
    036    /**
    037     * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z
    038     */
    039    public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
    040    {
    041        return !par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) ? false : super.canPlaceBlockAt(par1World, par2, par3, par4);
    042    }
    043
    044    /**
    045     * Can this block stay at this position.  Similar to canPlaceBlockAt except gets checked often with plants.
    046     */
    047    public boolean canBlockStay(World par1World, int par2, int par3, int par4)
    048    {
    049        return !par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) ? false : super.canBlockStay(par1World, par2, par3, par4);
    050    }
    051
    052    /**
    053     * Ticks the block if it's been scheduled
    054     */
    055    public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
    056    {
    057        int l = par1World.getBlockMetadata(par2, par3, par4);
    058
    059        if (!this.func_94476_e(par1World, par2, par3, par4, l))
    060        {
    061            boolean flag = this.func_94478_d(par1World, par2, par3, par4, l);
    062
    063            if (this.isRepeaterPowered && !flag)
    064            {
    065                par1World.setBlockAndMetadataWithNotify(par2, par3, par4, this.func_94484_i().blockID, l, 2);
    066            }
    067            else if (!this.isRepeaterPowered)
    068            {
    069                par1World.setBlockAndMetadataWithNotify(par2, par3, par4, this.func_94485_e().blockID, l, 2);
    070
    071                if (!flag)
    072                {
    073                    par1World.func_82740_a(par2, par3, par4, this.func_94485_e().blockID, this.func_94486_g(l), -1);
    074                }
    075            }
    076        }
    077    }
    078
    079    @SideOnly(Side.CLIENT)
    080
    081    /**
    082     * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
    083     */
    084    public Icon getBlockTextureFromSideAndMetadata(int par1, int par2)
    085    {
    086        return par1 == 0 ? (this.isRepeaterPowered ? Block.torchRedstoneActive.getBlockTextureFromSide(par1) : Block.torchRedstoneIdle.getBlockTextureFromSide(par1)) : (par1 == 1 ? this.field_94336_cN : Block.stoneDoubleSlab.getBlockTextureFromSide(1));
    087    }
    088
    089    @SideOnly(Side.CLIENT)
    090    public void func_94332_a(IconRegister par1IconRegister)
    091    {
    092        this.field_94336_cN = par1IconRegister.func_94245_a(this.isRepeaterPowered ? "repeater_lit" : "repeater");
    093    }
    094
    095    @SideOnly(Side.CLIENT)
    096
    097    /**
    098     * Returns true if the given side of this block type should be rendered, if the adjacent block is at the given
    099     * coordinates.  Args: blockAccess, x, y, z, side
    100     */
    101    public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    102    {
    103        return par5 != 0 && par5 != 1;
    104    }
    105
    106    /**
    107     * The type of render function that is called for this block
    108     */
    109    public int getRenderType()
    110    {
    111        return 36;
    112    }
    113
    114    protected boolean func_96470_c(int par1)
    115    {
    116        return this.isRepeaterPowered;
    117    }
    118
    119    /**
    120     * Returns true if the block is emitting direct/strong redstone power on the specified side. Args: World, X, Y, Z,
    121     * side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
    122     */
    123    public int isProvidingStrongPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    124    {
    125        return this.isProvidingWeakPower(par1IBlockAccess, par2, par3, par4, par5);
    126    }
    127
    128    /**
    129     * Returns true if the block is emitting indirect/weak redstone power on the specified side. If isBlockNormalCube
    130     * returns true, standard redstone propagation rules will apply instead and this will not be called. Args: World, X,
    131     * Y, Z, side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
    132     */
    133    public int isProvidingWeakPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    134    {
    135        int i1 = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
    136
    137        if (!this.func_96470_c(i1))
    138        {
    139            return 0;
    140        }
    141        else
    142        {
    143            int j1 = getDirection(i1);
    144            return j1 == 0 && par5 == 3 ? this.func_94480_d(par1IBlockAccess, par2, par3, par4, i1) : (j1 == 1 && par5 == 4 ? this.func_94480_d(par1IBlockAccess, par2, par3, par4, i1) : (j1 == 2 && par5 == 2 ? this.func_94480_d(par1IBlockAccess, par2, par3, par4, i1) : (j1 == 3 && par5 == 5 ? this.func_94480_d(par1IBlockAccess, par2, par3, par4, i1) : 0)));
    145        }
    146    }
    147
    148    /**
    149     * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
    150     * their own) Args: x, y, z, neighbor blockID
    151     */
    152    public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
    153    {
    154        if (!this.canBlockStay(par1World, par2, par3, par4))
    155        {
    156            this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0);
    157            par1World.func_94571_i(par2, par3, par4);
    158            par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
    159            par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
    160            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
    161            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
    162            par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
    163            par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
    164        }
    165        else
    166        {
    167            this.func_94479_f(par1World, par2, par3, par4, par5);
    168        }
    169    }
    170
    171    protected void func_94479_f(World par1World, int par2, int par3, int par4, int par5)
    172    {
    173        int i1 = par1World.getBlockMetadata(par2, par3, par4);
    174
    175        if (!this.func_94476_e(par1World, par2, par3, par4, i1))
    176        {
    177            boolean flag = this.func_94478_d(par1World, par2, par3, par4, i1);
    178
    179            if ((this.isRepeaterPowered && !flag || !this.isRepeaterPowered && flag) && !par1World.func_94573_a(par2, par3, par4, this.blockID))
    180            {
    181                byte b0 = -1;
    182
    183                if (this.func_83011_d(par1World, par2, par3, par4, i1))
    184                {
    185                    b0 = -3;
    186                }
    187                else if (this.isRepeaterPowered)
    188                {
    189                    b0 = -2;
    190                }
    191
    192                par1World.func_82740_a(par2, par3, par4, this.blockID, this.func_94481_j_(i1), b0);
    193            }
    194        }
    195    }
    196
    197    public boolean func_94476_e(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    198    {
    199        return false;
    200    }
    201
    202    protected boolean func_94478_d(World par1World, int par2, int par3, int par4, int par5)
    203    {
    204        return this.ignoreTick(par1World, par2, par3, par4, par5) > 0;
    205    }
    206
    207    protected int ignoreTick(World par1World, int par2, int par3, int par4, int par5)
    208    {
    209        int i1 = getDirection(par5);
    210        int j1 = par2 + Direction.offsetX[i1];
    211        int k1 = par4 + Direction.offsetZ[i1];
    212        int l1 = par1World.isBlockIndirectlyProvidingPowerTo(j1, par3, k1, Direction.headInvisibleFace[i1]);
    213        return l1 >= 15 ? l1 : Math.max(l1, par1World.getBlockId(j1, par3, k1) == Block.redstoneWire.blockID ? par1World.getBlockMetadata(j1, par3, k1) : 0);
    214    }
    215
    216    protected int func_94482_f(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    217    {
    218        int i1 = getDirection(par5);
    219
    220        switch (i1)
    221        {
    222            case 0:
    223            case 2:
    224                return Math.max(this.func_94488_g(par1IBlockAccess, par2 - 1, par3, par4, 4), this.func_94488_g(par1IBlockAccess, par2 + 1, par3, par4, 5));
    225            case 1:
    226            case 3:
    227                return Math.max(this.func_94488_g(par1IBlockAccess, par2, par3, par4 + 1, 3), this.func_94488_g(par1IBlockAccess, par2, par3, par4 - 1, 2));
    228            default:
    229                return 0;
    230        }
    231    }
    232
    233    protected int func_94488_g(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    234    {
    235        int i1 = par1IBlockAccess.getBlockId(par2, par3, par4);
    236        return this.func_94477_d(i1) ? (i1 == Block.redstoneWire.blockID ? par1IBlockAccess.getBlockMetadata(par2, par3, par4) : par1IBlockAccess.isBlockProvidingPowerTo(par2, par3, par4, par5)) : 0;
    237    }
    238
    239    /**
    240     * Can this block provide power. Only wire currently seems to have this change based on its state.
    241     */
    242    public boolean canProvidePower()
    243    {
    244        return true;
    245    }
    246
    247    /**
    248     * Called when the block is placed in the world.
    249     */
    250    public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving, ItemStack par6ItemStack)
    251    {
    252        int l = ((MathHelper.floor_double((double)(par5EntityLiving.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3) + 2) % 4;
    253        par1World.setBlockMetadataWithNotify(par2, par3, par4, l, 3);
    254        boolean flag = this.func_94478_d(par1World, par2, par3, par4, l);
    255
    256        if (flag)
    257        {
    258            par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, 1);
    259        }
    260    }
    261
    262    /**
    263     * Called whenever the block is added into the world. Args: world, x, y, z
    264     */
    265    public void onBlockAdded(World par1World, int par2, int par3, int par4)
    266    {
    267        this.func_94483_i_(par1World, par2, par3, par4);
    268    }
    269
    270    protected void func_94483_i_(World par1World, int par2, int par3, int par4)
    271    {
    272        int l = getDirection(par1World.getBlockMetadata(par2, par3, par4));
    273
    274        if (l == 1)
    275        {
    276            par1World.notifyBlockOfNeighborChange(par2 + 1, par3, par4, this.blockID);
    277            par1World.func_96439_d(par2 + 1, par3, par4, this.blockID, 4);
    278        }
    279
    280        if (l == 3)
    281        {
    282            par1World.notifyBlockOfNeighborChange(par2 - 1, par3, par4, this.blockID);
    283            par1World.func_96439_d(par2 - 1, par3, par4, this.blockID, 5);
    284        }
    285
    286        if (l == 2)
    287        {
    288            par1World.notifyBlockOfNeighborChange(par2, par3, par4 + 1, this.blockID);
    289            par1World.func_96439_d(par2, par3, par4 + 1, this.blockID, 2);
    290        }
    291
    292        if (l == 0)
    293        {
    294            par1World.notifyBlockOfNeighborChange(par2, par3, par4 - 1, this.blockID);
    295            par1World.func_96439_d(par2, par3, par4 - 1, this.blockID, 3);
    296        }
    297    }
    298
    299    /**
    300     * Called right before the block is destroyed by a player.  Args: world, x, y, z, metaData
    301     */
    302    public void onBlockDestroyedByPlayer(World par1World, int par2, int par3, int par4, int par5)
    303    {
    304        if (this.isRepeaterPowered)
    305        {
    306            par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
    307            par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
    308            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
    309            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
    310            par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
    311            par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
    312        }
    313
    314        super.onBlockDestroyedByPlayer(par1World, par2, par3, par4, par5);
    315    }
    316
    317    /**
    318     * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
    319     * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
    320     */
    321    public boolean isOpaqueCube()
    322    {
    323        return false;
    324    }
    325
    326    protected boolean func_94477_d(int par1)
    327    {
    328        Block block = Block.blocksList[par1];
    329        return block != null && block.canProvidePower();
    330    }
    331
    332    protected int func_94480_d(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    333    {
    334        return 15;
    335    }
    336
    337    public static boolean isRedstoneRepeaterBlockID(int par0)
    338    {
    339        return Block.redstoneRepeaterIdle.func_94487_f(par0) || Block.field_94346_cn.func_94487_f(par0);
    340    }
    341
    342    public boolean func_94487_f(int par1)
    343    {
    344        return par1 == this.func_94485_e().blockID || par1 == this.func_94484_i().blockID;
    345    }
    346
    347    public boolean func_83011_d(World par1World, int par2, int par3, int par4, int par5)
    348    {
    349        int i1 = getDirection(par5);
    350
    351        if (isRedstoneRepeaterBlockID(par1World.getBlockId(par2 - Direction.offsetX[i1], par3, par4 - Direction.offsetZ[i1])))
    352        {
    353            int j1 = par1World.getBlockMetadata(par2 - Direction.offsetX[i1], par3, par4 - Direction.offsetZ[i1]);
    354            int k1 = getDirection(j1);
    355            return k1 != i1;
    356        }
    357        else
    358        {
    359            return false;
    360        }
    361    }
    362
    363    protected int func_94486_g(int par1)
    364    {
    365        return this.func_94481_j_(par1);
    366    }
    367
    368    protected abstract int func_94481_j_(int i);
    369
    370    protected abstract BlockRedstoneLogic func_94485_e();
    371
    372    protected abstract BlockRedstoneLogic func_94484_i();
    373
    374    public boolean func_94334_h(int par1)
    375    {
    376        return this.func_94487_f(par1);
    377    }
    378}