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 getBlockTextureFromSideAndMetadata(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.headInvisibleFace[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}