    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.setBlock(par2, par3, par4, this.func_94484_i().blockID, l, 2);
    066            }
    067            else if (!this.isRepeaterPowered)
    068            {
    069                par1World.setBlock(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 getIcon(int par1, int par2)
    085    {
    086        return par1 == 0 ? (this.isRepeaterPowered ? Block.torchRedstoneActive.getBlockTextureFromSide(par1) : Block.torchRedstoneIdle.getBlockTextureFromSide(par1)) : (par1 == 1 ? this.blockIcon : Block.stoneDoubleSlab.getBlockTextureFromSide(1));
    087    }
    088
    089    @SideOnly(Side.CLIENT)
    090
    091    /**
    092     * When this method is called, your block should register all the icons it needs with the given IconRegister. This
    093     * is the only chance you get to register icons.
    094     */
    095    public void registerIcons(IconRegister par1IconRegister)
    096    {
    097        this.blockIcon = par1IconRegister.registerIcon(this.isRepeaterPowered ? "repeater_lit" : "repeater");
    098    }
    099
    100    @SideOnly(Side.CLIENT)
    101
    102    /**
    103     * Returns true if the given side of this block type should be rendered, if the adjacent block is at the given
    104     * coordinates.  Args: blockAccess, x, y, z, side
    105     */
    106    public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    107    {
    108        return par5 != 0 && par5 != 1;
    109    }
    110
    111    /**
    112     * The type of render function that is called for this block
    113     */
    114    public int getRenderType()
    115    {
    116        return 36;
    117    }
    118
    119    protected boolean func_96470_c(int par1)
    120    {
    121        return this.isRepeaterPowered;
    122    }
    123
    124    /**
    125     * Returns true if the block is emitting direct/strong redstone power on the specified side. Args: World, X, Y, Z,
    126     * side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
    127     */
    128    public int isProvidingStrongPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    129    {
    130        return this.isProvidingWeakPower(par1IBlockAccess, par2, par3, par4, par5);
    131    }
    132
    133    /**
    134     * Returns true if the block is emitting indirect/weak redstone power on the specified side. If isBlockNormalCube
    135     * returns true, standard redstone propagation rules will apply instead and this will not be called. Args: World, X,
    136     * Y, Z, side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
    137     */
    138    public int isProvidingWeakPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    139    {
    140        int i1 = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
    141
    142        if (!this.func_96470_c(i1))
    143        {
    144            return 0;
    145        }
    146        else
    147        {
    148            int j1 = getDirection(i1);
    149            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)));
    150        }
    151    }
    152
    153    /**
    154     * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
    155     * their own) Args: x, y, z, neighbor blockID
    156     */
    157    public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
    158    {
    159        if (!this.canBlockStay(par1World, par2, par3, par4))
    160        {
    161            this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0);
    162            par1World.setBlockToAir(par2, par3, par4);
    163            par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
    164            par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
    165            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
    166            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
    167            par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
    168            par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
    169        }
    170        else
    171        {
    172            this.func_94479_f(par1World, par2, par3, par4, par5);
    173        }
    174    }
    175
    176    protected void func_94479_f(World par1World, int par2, int par3, int par4, int par5)
    177    {
    178        int i1 = par1World.getBlockMetadata(par2, par3, par4);
    179
    180        if (!this.func_94476_e(par1World, par2, par3, par4, i1))
    181        {
    182            boolean flag = this.func_94478_d(par1World, par2, par3, par4, i1);
    183
    184            if ((this.isRepeaterPowered && !flag || !this.isRepeaterPowered && flag) && !par1World.isBlockTickScheduled(par2, par3, par4, this.blockID))
    185            {
    186                byte b0 = -1;
    187
    188                if (this.func_83011_d(par1World, par2, par3, par4, i1))
    189                {
    190                    b0 = -3;
    191                }
    192                else if (this.isRepeaterPowered)
    193                {
    194                    b0 = -2;
    195                }
    196
    197                par1World.func_82740_a(par2, par3, par4, this.blockID, this.func_94481_j_(i1), b0);
    198            }
    199        }
    200    }
    201
    202    public boolean func_94476_e(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    203    {
    204        return false;
    205    }
    206
    207    protected boolean func_94478_d(World par1World, int par2, int par3, int par4, int par5)
    208    {
    209        return this.getInputStrength(par1World, par2, par3, par4, par5) > 0;
    210    }
    211
    212    /**
    213     * Returns the signal strength at one input of the block. Args: world, X, Y, Z, side
    214     */
    215    protected int getInputStrength(World par1World, int par2, int par3, int par4, int par5)
    216    {
    217        int i1 = getDirection(par5);
    218        int j1 = par2 + Direction.offsetX[i1];
    219        int k1 = par4 + Direction.offsetZ[i1];
    220        int l1 = par1World.getIndirectPowerLevelTo(j1, par3, k1, Direction.directionToFacing[i1]);
    221        return l1 >= 15 ? l1 : Math.max(l1, par1World.getBlockId(j1, par3, k1) == Block.redstoneWire.blockID ? par1World.getBlockMetadata(j1, par3, k1) : 0);
    222    }
    223
    224    protected int func_94482_f(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    225    {
    226        int i1 = getDirection(par5);
    227
    228        switch (i1)
    229        {
    230            case 0:
    231            case 2:
    232                return Math.max(this.func_94488_g(par1IBlockAccess, par2 - 1, par3, par4, 4), this.func_94488_g(par1IBlockAccess, par2 + 1, par3, par4, 5));
    233            case 1:
    234            case 3:
    235                return Math.max(this.func_94488_g(par1IBlockAccess, par2, par3, par4 + 1, 3), this.func_94488_g(par1IBlockAccess, par2, par3, par4 - 1, 2));
    236            default:
    237                return 0;
    238        }
    239    }
    240
    241    protected int func_94488_g(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    242    {
    243        int i1 = par1IBlockAccess.getBlockId(par2, par3, par4);
    244        return this.func_94477_d(i1) ? (i1 == Block.redstoneWire.blockID ? par1IBlockAccess.getBlockMetadata(par2, par3, par4) : par1IBlockAccess.isBlockProvidingPowerTo(par2, par3, par4, par5)) : 0;
    245    }
    246
    247    /**
    248     * Can this block provide power. Only wire currently seems to have this change based on its state.
    249     */
    250    public boolean canProvidePower()
    251    {
    252        return true;
    253    }
    254
    255    /**
    256     * Called when the block is placed in the world.
    257     */
    258    public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving, ItemStack par6ItemStack)
    259    {
    260        int l = ((MathHelper.floor_double((double)(par5EntityLiving.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3) + 2) % 4;
    261        par1World.setBlockMetadataWithNotify(par2, par3, par4, l, 3);
    262        boolean flag = this.func_94478_d(par1World, par2, par3, par4, l);
    263
    264        if (flag)
    265        {
    266            par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, 1);
    267        }
    268    }
    269
    270    /**
    271     * Called whenever the block is added into the world. Args: world, x, y, z
    272     */
    273    public void onBlockAdded(World par1World, int par2, int par3, int par4)
    274    {
    275        this.func_94483_i_(par1World, par2, par3, par4);
    276    }
    277
    278    protected void func_94483_i_(World par1World, int par2, int par3, int par4)
    279    {
    280        int l = getDirection(par1World.getBlockMetadata(par2, par3, par4));
    281
    282        if (l == 1)
    283        {
    284            par1World.notifyBlockOfNeighborChange(par2 + 1, par3, par4, this.blockID);
    285            par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID, 4);
    286        }
    287
    288        if (l == 3)
    289        {
    290            par1World.notifyBlockOfNeighborChange(par2 - 1, par3, par4, this.blockID);
    291            par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID, 5);
    292        }
    293
    294        if (l == 2)
    295        {
    296            par1World.notifyBlockOfNeighborChange(par2, par3, par4 + 1, this.blockID);
    297            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID, 2);
    298        }
    299
    300        if (l == 0)
    301        {
    302            par1World.notifyBlockOfNeighborChange(par2, par3, par4 - 1, this.blockID);
    303            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID, 3);
    304        }
    305    }
    306
    307    /**
    308     * Called right before the block is destroyed by a player.  Args: world, x, y, z, metaData
    309     */
    310    public void onBlockDestroyedByPlayer(World par1World, int par2, int par3, int par4, int par5)
    311    {
    312        if (this.isRepeaterPowered)
    313        {
    314            par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
    315            par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
    316            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
    317            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
    318            par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
    319            par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
    320        }
    321
    322        super.onBlockDestroyedByPlayer(par1World, par2, par3, par4, par5);
    323    }
    324
    325    /**
    326     * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
    327     * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
    328     */
    329    public boolean isOpaqueCube()
    330    {
    331        return false;
    332    }
    333
    334    protected boolean func_94477_d(int par1)
    335    {
    336        Block block = Block.blocksList[par1];
    337        return block != null && block.canProvidePower();
    338    }
    339
    340    protected int func_94480_d(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    341    {
    342        return 15;
    343    }
    344
    345    public static boolean isRedstoneRepeaterBlockID(int par0)
    346    {
    347        return Block.redstoneRepeaterIdle.func_94487_f(par0) || Block.redstoneComparatorIdle.func_94487_f(par0);
    348    }
    349
    350    public boolean func_94487_f(int par1)
    351    {
    352        return par1 == this.func_94485_e().blockID || par1 == this.func_94484_i().blockID;
    353    }
    354
    355    public boolean func_83011_d(World par1World, int par2, int par3, int par4, int par5)
    356    {
    357        int i1 = getDirection(par5);
    358
    359        if (isRedstoneRepeaterBlockID(par1World.getBlockId(par2 - Direction.offsetX[i1], par3, par4 - Direction.offsetZ[i1])))
    360        {
    361            int j1 = par1World.getBlockMetadata(par2 - Direction.offsetX[i1], par3, par4 - Direction.offsetZ[i1]);
    362            int k1 = getDirection(j1);
    363            return k1 != i1;
    364        }
    365        else
    366        {
    367            return false;
    368        }
    369    }
    370
    371    protected int func_94486_g(int par1)
    372    {
    373        return this.func_94481_j_(par1);
    374    }
    375
    376    protected abstract int func_94481_j_(int i);
    377
    378    protected abstract BlockRedstoneLogic func_94485_e();
    379
    380    protected abstract BlockRedstoneLogic func_94484_i();
    381
    382    /**
    383     * Returns true if the given block ID is equivalent to this one. Example: redstoneTorchOn matches itself and
    384     * redstoneTorchOff, and vice versa. Most blocks only match themselves.
    385     */
    386    public boolean isAssociatedBlockID(int par1)
    387    {
    388        return this.func_94487_f(par1);
    389    }
    390}