    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.entity.EntityLiving;
    008import net.minecraft.entity.player.EntityPlayer;
    009import net.minecraft.item.Item;
    010import net.minecraft.util.Direction;
    011import net.minecraft.util.MathHelper;
    012import net.minecraft.world.IBlockAccess;
    013import net.minecraft.world.World;
    014
    015public class BlockRedstoneRepeater extends BlockDirectional
    016{
    017    /** The offsets for the two torches in redstone repeater blocks. */
    018    public static final double[] repeaterTorchOffset = new double[] { -0.0625D, 0.0625D, 0.1875D, 0.3125D};
    019
    020    /** The states in which the redstone repeater blocks can be. */
    021    private static final int[] repeaterState = new int[] {1, 2, 3, 4};
    022
    023    /** Tells whether the repeater is powered or not */
    024    private final boolean isRepeaterPowered;
    025
    026    protected BlockRedstoneRepeater(int par1, boolean par2)
    027    {
    028        super(par1, 6, Material.circuits);
    029        this.isRepeaterPowered = par2;
    030        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
    031    }
    032
    033    /**
    034     * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
    035     */
    036    public boolean renderAsNormalBlock()
    037    {
    038        return false;
    039    }
    040
    041    /**
    042     * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z
    043     */
    044    public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
    045    {
    046        return !par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) ? false : super.canPlaceBlockAt(par1World, par2, par3, par4);
    047    }
    048
    049    /**
    050     * Can this block stay at this position.  Similar to canPlaceBlockAt except gets checked often with plants.
    051     */
    052    public boolean canBlockStay(World par1World, int par2, int par3, int par4)
    053    {
    054        return !par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) ? false : super.canBlockStay(par1World, par2, par3, par4);
    055    }
    056
    057    /**
    058     * Ticks the block if it's been scheduled
    059     */
    060    public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
    061    {
    062        int var6 = par1World.getBlockMetadata(par2, par3, par4);
    063        boolean var7 = this.func_82523_e(par1World, par2, par3, par4, var6);
    064
    065        if (!var7)
    066        {
    067            boolean var8 = this.ignoreTick(par1World, par2, par3, par4, var6);
    068
    069            if (this.isRepeaterPowered && !var8)
    070            {
    071                par1World.setBlockAndMetadataWithNotify(par2, par3, par4, Block.redstoneRepeaterIdle.blockID, var6);
    072            }
    073            else if (!this.isRepeaterPowered)
    074            {
    075                par1World.setBlockAndMetadataWithNotify(par2, par3, par4, Block.redstoneRepeaterActive.blockID, var6);
    076
    077                if (!var8)
    078                {
    079                    int var9 = (var6 & 12) >> 2;
    080                    par1World.scheduleBlockUpdate(par2, par3, par4, Block.redstoneRepeaterActive.blockID, repeaterState[var9] * 2);
    081                }
    082            }
    083        }
    084    }
    085
    086    /**
    087     * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
    088     */
    089    public int getBlockTextureFromSideAndMetadata(int par1, int par2)
    090    {
    091        return par1 == 0 ? (this.isRepeaterPowered ? 99 : 115) : (par1 == 1 ? (this.isRepeaterPowered ? 147 : 131) : 5);
    092    }
    093
    094    @SideOnly(Side.CLIENT)
    095
    096    /**
    097     * Returns true if the given side of this block type should be rendered, if the adjacent block is at the given
    098     * coordinates.  Args: blockAccess, x, y, z, side
    099     */
    100    public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    101    {
    102        return par5 != 0 && par5 != 1;
    103    }
    104
    105    /**
    106     * The type of render function that is called for this block
    107     */
    108    public int getRenderType()
    109    {
    110        return 15;
    111    }
    112
    113    /**
    114     * Returns the block texture based on the side being looked at.  Args: side
    115     */
    116    public int getBlockTextureFromSide(int par1)
    117    {
    118        return this.getBlockTextureFromSideAndMetadata(par1, 0);
    119    }
    120
    121    /**
    122     * Returns true if the block is emitting direct/strong redstone power on the specified side. Args: World, X, Y, Z,
    123     * side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
    124     */
    125    public boolean isProvidingStrongPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    126    {
    127        return this.isProvidingWeakPower(par1IBlockAccess, par2, par3, par4, par5);
    128    }
    129
    130    /**
    131     * Returns true if the block is emitting indirect/weak redstone power on the specified side. If isBlockNormalCube
    132     * returns true, standard redstone propagation rules will apply instead and this will not be called. Args: World, X,
    133     * Y, Z, side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
    134     */
    135    public boolean isProvidingWeakPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    136    {
    137        if (!this.isRepeaterPowered)
    138        {
    139            return false;
    140        }
    141        else
    142        {
    143            int var6 = getDirection(par1IBlockAccess.getBlockMetadata(par2, par3, par4));
    144            return var6 == 0 && par5 == 3 ? true : (var6 == 1 && par5 == 4 ? true : (var6 == 2 && par5 == 2 ? true : var6 == 3 && par5 == 5));
    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.setBlockWithNotify(par2, par3, par4, 0);
    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            int var6 = par1World.getBlockMetadata(par2, par3, par4);
    168            boolean var7 = this.func_82523_e(par1World, par2, par3, par4, var6);
    169
    170            if (!var7)
    171            {
    172                boolean var8 = this.ignoreTick(par1World, par2, par3, par4, var6);
    173                int var9 = (var6 & 12) >> 2;
    174
    175                if (this.isRepeaterPowered && !var8 || !this.isRepeaterPowered && var8)
    176                {
    177                    byte var10 = 0;
    178
    179                    if (this.func_83011_d(par1World, par2, par3, par4, var6))
    180                    {
    181                        var10 = -1;
    182                    }
    183
    184                    par1World.func_82740_a(par2, par3, par4, this.blockID, repeaterState[var9] * 2, var10);
    185                }
    186            }
    187        }
    188    }
    189
    190    private boolean ignoreTick(World par1World, int par2, int par3, int par4, int par5)
    191    {
    192        int var6 = getDirection(par5);
    193
    194        switch (var6)
    195        {
    196            case 0:
    197                return par1World.isBlockIndirectlyProvidingPowerTo(par2, par3, par4 + 1, 3) || par1World.getBlockId(par2, par3, par4 + 1) == Block.redstoneWire.blockID && par1World.getBlockMetadata(par2, par3, par4 + 1) > 0;
    198            case 1:
    199                return par1World.isBlockIndirectlyProvidingPowerTo(par2 - 1, par3, par4, 4) || par1World.getBlockId(par2 - 1, par3, par4) == Block.redstoneWire.blockID && par1World.getBlockMetadata(par2 - 1, par3, par4) > 0;
    200            case 2:
    201                return par1World.isBlockIndirectlyProvidingPowerTo(par2, par3, par4 - 1, 2) || par1World.getBlockId(par2, par3, par4 - 1) == Block.redstoneWire.blockID && par1World.getBlockMetadata(par2, par3, par4 - 1) > 0;
    202            case 3:
    203                return par1World.isBlockIndirectlyProvidingPowerTo(par2 + 1, par3, par4, 5) || par1World.getBlockId(par2 + 1, par3, par4) == Block.redstoneWire.blockID && par1World.getBlockMetadata(par2 + 1, par3, par4) > 0;
    204            default:
    205                return false;
    206        }
    207    }
    208
    209    public boolean func_82523_e(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    210    {
    211        int var6 = getDirection(par5);
    212
    213        switch (var6)
    214        {
    215            case 0:
    216            case 2:
    217                return par1IBlockAccess.isBlockProvidingPowerTo(par2 - 1, par3, par4, 4) && func_82524_c(par1IBlockAccess.getBlockId(par2 - 1, par3, par4)) || par1IBlockAccess.isBlockProvidingPowerTo(par2 + 1, par3, par4, 5) && func_82524_c(par1IBlockAccess.getBlockId(par2 + 1, par3, par4));
    218            case 1:
    219            case 3:
    220                return par1IBlockAccess.isBlockProvidingPowerTo(par2, par3, par4 + 1, 3) && func_82524_c(par1IBlockAccess.getBlockId(par2, par3, par4 + 1)) || par1IBlockAccess.isBlockProvidingPowerTo(par2, par3, par4 - 1, 2) && func_82524_c(par1IBlockAccess.getBlockId(par2, par3, par4 - 1));
    221            default:
    222                return false;
    223        }
    224    }
    225
    226    /**
    227     * Called upon block activation (right click on the block.)
    228     */
    229    public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9)
    230    {
    231        int var10 = par1World.getBlockMetadata(par2, par3, par4);
    232        int var11 = (var10 & 12) >> 2;
    233        var11 = var11 + 1 << 2 & 12;
    234        par1World.setBlockMetadataWithNotify(par2, par3, par4, var11 | var10 & 3);
    235        return true;
    236    }
    237
    238    /**
    239     * Can this block provide power. Only wire currently seems to have this change based on its state.
    240     */
    241    public boolean canProvidePower()
    242    {
    243        return true;
    244    }
    245
    246    /**
    247     * Called when the block is placed in the world.
    248     */
    249    public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving)
    250    {
    251        int var6 = ((MathHelper.floor_double((double)(par5EntityLiving.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3) + 2) % 4;
    252        par1World.setBlockMetadataWithNotify(par2, par3, par4, var6);
    253        boolean var7 = this.ignoreTick(par1World, par2, par3, par4, var6);
    254
    255        if (var7)
    256        {
    257            par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, 1);
    258        }
    259    }
    260
    261    /**
    262     * Called whenever the block is added into the world. Args: world, x, y, z
    263     */
    264    public void onBlockAdded(World par1World, int par2, int par3, int par4)
    265    {
    266        par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
    267        par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
    268        par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
    269        par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
    270        par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
    271        par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
    272    }
    273
    274    /**
    275     * Called right before the block is destroyed by a player.  Args: world, x, y, z, metaData
    276     */
    277    public void onBlockDestroyedByPlayer(World par1World, int par2, int par3, int par4, int par5)
    278    {
    279        if (this.isRepeaterPowered)
    280        {
    281            par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
    282            par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
    283            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
    284            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
    285            par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
    286            par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
    287        }
    288
    289        super.onBlockDestroyedByPlayer(par1World, par2, par3, par4, par5);
    290    }
    291
    292    /**
    293     * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
    294     * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
    295     */
    296    public boolean isOpaqueCube()
    297    {
    298        return false;
    299    }
    300
    301    /**
    302     * Returns the ID of the items to drop on destruction.
    303     */
    304    public int idDropped(int par1, Random par2Random, int par3)
    305    {
    306        return Item.redstoneRepeater.itemID;
    307    }
    308
    309    @SideOnly(Side.CLIENT)
    310
    311    /**
    312     * A randomly called display update to be able to add particles or other items for display
    313     */
    314    public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random)
    315    {
    316        if (this.isRepeaterPowered)
    317        {
    318            int var6 = par1World.getBlockMetadata(par2, par3, par4);
    319            int var7 = getDirection(var6);
    320            double var8 = (double)((float)par2 + 0.5F) + (double)(par5Random.nextFloat() - 0.5F) * 0.2D;
    321            double var10 = (double)((float)par3 + 0.4F) + (double)(par5Random.nextFloat() - 0.5F) * 0.2D;
    322            double var12 = (double)((float)par4 + 0.5F) + (double)(par5Random.nextFloat() - 0.5F) * 0.2D;
    323            double var14 = 0.0D;
    324            double var16 = 0.0D;
    325
    326            if (par5Random.nextInt(2) == 0)
    327            {
    328                switch (var7)
    329                {
    330                    case 0:
    331                        var16 = -0.3125D;
    332                        break;
    333                    case 1:
    334                        var14 = 0.3125D;
    335                        break;
    336                    case 2:
    337                        var16 = 0.3125D;
    338                        break;
    339                    case 3:
    340                        var14 = -0.3125D;
    341                }
    342            }
    343            else
    344            {
    345                int var18 = (var6 & 12) >> 2;
    346
    347                switch (var7)
    348                {
    349                    case 0:
    350                        var16 = repeaterTorchOffset[var18];
    351                        break;
    352                    case 1:
    353                        var14 = -repeaterTorchOffset[var18];
    354                        break;
    355                    case 2:
    356                        var16 = -repeaterTorchOffset[var18];
    357                        break;
    358                    case 3:
    359                        var14 = repeaterTorchOffset[var18];
    360                }
    361            }
    362
    363            par1World.spawnParticle("reddust", var8 + var14, var10, var12 + var16, 0.0D, 0.0D, 0.0D);
    364        }
    365    }
    366
    367    @SideOnly(Side.CLIENT)
    368
    369    /**
    370     * only called by clickMiddleMouseButton , and passed to inventory.setCurrentItem (along with isCreative)
    371     */
    372    public int idPicked(World par1World, int par2, int par3, int par4)
    373    {
    374        return Item.redstoneRepeater.itemID;
    375    }
    376
    377    public static boolean func_82524_c(int par0)
    378    {
    379        return par0 == Block.redstoneRepeaterActive.blockID || par0 == Block.redstoneRepeaterIdle.blockID;
    380    }
    381
    382    public boolean func_83011_d(World par1World, int par2, int par3, int par4, int par5)
    383    {
    384        int var6 = getDirection(par5);
    385
    386        if (func_82524_c(par1World.getBlockId(par2 - Direction.offsetX[var6], par3, par4 - Direction.offsetZ[var6])))
    387        {
    388            int var7 = par1World.getBlockMetadata(par2 - Direction.offsetX[var6], par3, par4 - Direction.offsetZ[var6]);
    389            int var8 = getDirection(var7);
    390            return var8 != var6;
    391        }
    392        else
    393        {
    394            return false;
    395        }
    396    }
    397}