001    package net.minecraft.block;
002    
003    import cpw.mods.fml.relauncher.Side;
004    import cpw.mods.fml.relauncher.SideOnly;
005    import java.util.Random;
006    import net.minecraft.block.material.Material;
007    import net.minecraft.entity.EntityLiving;
008    import net.minecraft.entity.player.EntityPlayer;
009    import net.minecraft.item.Item;
010    import net.minecraft.util.Direction;
011    import net.minecraft.util.MathHelper;
012    import net.minecraft.world.IBlockAccess;
013    import net.minecraft.world.World;
014    
015    public 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    }