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