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                        int var11 = getDirection(var6);
168    
169                        if (func_82524_c(par1World.getBlockId(par2 - Direction.offsetX[var11], par3, par4 - Direction.offsetZ[var11])))
170                        {
171                            var10 = -1;
172                        }
173    
174                        par1World.func_82740_a(par2, par3, par4, this.blockID, repeaterState[var9] * 2, var10);
175                    }
176                }
177            }
178        }
179    
180        private boolean ignoreTick(World par1World, int par2, int par3, int par4, int par5)
181        {
182            int var6 = getDirection(par5);
183    
184            switch (var6)
185            {
186                case 0:
187                    return par1World.isBlockIndirectlyProvidingPowerTo(par2, par3, par4 + 1, 3) || par1World.getBlockId(par2, par3, par4 + 1) == Block.redstoneWire.blockID && par1World.getBlockMetadata(par2, par3, par4 + 1) > 0;
188                case 1:
189                    return par1World.isBlockIndirectlyProvidingPowerTo(par2 - 1, par3, par4, 4) || par1World.getBlockId(par2 - 1, par3, par4) == Block.redstoneWire.blockID && par1World.getBlockMetadata(par2 - 1, par3, par4) > 0;
190                case 2:
191                    return par1World.isBlockIndirectlyProvidingPowerTo(par2, par3, par4 - 1, 2) || par1World.getBlockId(par2, par3, par4 - 1) == Block.redstoneWire.blockID && par1World.getBlockMetadata(par2, par3, par4 - 1) > 0;
192                case 3:
193                    return par1World.isBlockIndirectlyProvidingPowerTo(par2 + 1, par3, par4, 5) || par1World.getBlockId(par2 + 1, par3, par4) == Block.redstoneWire.blockID && par1World.getBlockMetadata(par2 + 1, par3, par4) > 0;
194                default:
195                    return false;
196            }
197        }
198    
199        public boolean func_82523_e(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
200        {
201            int var6 = getDirection(par5);
202    
203            switch (var6)
204            {
205                case 0:
206                case 2:
207                    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));
208                case 1:
209                case 3:
210                    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));
211                default:
212                    return false;
213            }
214        }
215    
216        /**
217         * Called upon block activation (right click on the block.)
218         */
219        public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9)
220        {
221            int var10 = par1World.getBlockMetadata(par2, par3, par4);
222            int var11 = (var10 & 12) >> 2;
223            var11 = var11 + 1 << 2 & 12;
224            par1World.setBlockMetadataWithNotify(par2, par3, par4, var11 | var10 & 3);
225            return true;
226        }
227    
228        /**
229         * Can this block provide power. Only wire currently seems to have this change based on its state.
230         */
231        public boolean canProvidePower()
232        {
233            return true;
234        }
235    
236        /**
237         * Called when the block is placed in the world.
238         */
239        public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving)
240        {
241            int var6 = ((MathHelper.floor_double((double)(par5EntityLiving.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3) + 2) % 4;
242            par1World.setBlockMetadataWithNotify(par2, par3, par4, var6);
243            boolean var7 = this.ignoreTick(par1World, par2, par3, par4, var6);
244    
245            if (var7)
246            {
247                par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, 1);
248            }
249        }
250    
251        /**
252         * Called whenever the block is added into the world. Args: world, x, y, z
253         */
254        public void onBlockAdded(World par1World, int par2, int par3, int par4)
255        {
256            par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
257            par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
258            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
259            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
260            par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
261            par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
262        }
263    
264        /**
265         * Called right before the block is destroyed by a player.  Args: world, x, y, z, metaData
266         */
267        public void onBlockDestroyedByPlayer(World par1World, int par2, int par3, int par4, int par5)
268        {
269            if (this.isRepeaterPowered)
270            {
271                par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
272                par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
273                par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
274                par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
275                par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
276                par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
277            }
278    
279            super.onBlockDestroyedByPlayer(par1World, par2, par3, par4, par5);
280        }
281    
282        /**
283         * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
284         * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
285         */
286        public boolean isOpaqueCube()
287        {
288            return false;
289        }
290    
291        /**
292         * Returns the ID of the items to drop on destruction.
293         */
294        public int idDropped(int par1, Random par2Random, int par3)
295        {
296            return Item.redstoneRepeater.shiftedIndex;
297        }
298    
299        @SideOnly(Side.CLIENT)
300    
301        /**
302         * A randomly called display update to be able to add particles or other items for display
303         */
304        public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random)
305        {
306            if (this.isRepeaterPowered)
307            {
308                int var6 = par1World.getBlockMetadata(par2, par3, par4);
309                int var7 = getDirection(var6);
310                double var8 = (double)((float)par2 + 0.5F) + (double)(par5Random.nextFloat() - 0.5F) * 0.2D;
311                double var10 = (double)((float)par3 + 0.4F) + (double)(par5Random.nextFloat() - 0.5F) * 0.2D;
312                double var12 = (double)((float)par4 + 0.5F) + (double)(par5Random.nextFloat() - 0.5F) * 0.2D;
313                double var14 = 0.0D;
314                double var16 = 0.0D;
315    
316                if (par5Random.nextInt(2) == 0)
317                {
318                    switch (var7)
319                    {
320                        case 0:
321                            var16 = -0.3125D;
322                            break;
323                        case 1:
324                            var14 = 0.3125D;
325                            break;
326                        case 2:
327                            var16 = 0.3125D;
328                            break;
329                        case 3:
330                            var14 = -0.3125D;
331                    }
332                }
333                else
334                {
335                    int var18 = (var6 & 12) >> 2;
336    
337                    switch (var7)
338                    {
339                        case 0:
340                            var16 = repeaterTorchOffset[var18];
341                            break;
342                        case 1:
343                            var14 = -repeaterTorchOffset[var18];
344                            break;
345                        case 2:
346                            var16 = -repeaterTorchOffset[var18];
347                            break;
348                        case 3:
349                            var14 = repeaterTorchOffset[var18];
350                    }
351                }
352    
353                par1World.spawnParticle("reddust", var8 + var14, var10, var12 + var16, 0.0D, 0.0D, 0.0D);
354            }
355        }
356    
357        @SideOnly(Side.CLIENT)
358    
359        /**
360         * only called by clickMiddleMouseButton , and passed to inventory.setCurrentItem (along with isCreative)
361         */
362        public int idPicked(World par1World, int par2, int par3, int par4)
363        {
364            return Item.redstoneRepeater.shiftedIndex;
365        }
366    
367        public static boolean func_82524_c(int par0)
368        {
369            return par0 == Block.redstoneRepeaterActive.blockID || par0 == Block.redstoneRepeaterIdle.blockID;
370        }
371    }