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.setBlockAndMetadataWithNotify(par2, par3, par4, this.func_94484_i().blockID, l, 2);
066            }
067            else if (!this.isRepeaterPowered)
068            {
069                par1World.setBlockAndMetadataWithNotify(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.field_94336_cN : Block.stoneDoubleSlab.getBlockTextureFromSide(1));
087    }
088
089    @SideOnly(Side.CLIENT)
090    public void func_94332_a(IconRegister par1IconRegister)
091    {
092        this.field_94336_cN = par1IconRegister.func_94245_a(this.isRepeaterPowered ? "repeater_lit" : "repeater");
093    }
094
095    @SideOnly(Side.CLIENT)
096
097    /**
098     * Returns true if the given side of this block type should be rendered, if the adjacent block is at the given
099     * coordinates.  Args: blockAccess, x, y, z, side
100     */
101    public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
102    {
103        return par5 != 0 && par5 != 1;
104    }
105
106    /**
107     * The type of render function that is called for this block
108     */
109    public int getRenderType()
110    {
111        return 36;
112    }
113
114    protected boolean func_96470_c(int par1)
115    {
116        return this.isRepeaterPowered;
117    }
118
119    /**
120     * Returns true if the block is emitting direct/strong redstone power on the specified side. Args: World, X, Y, Z,
121     * side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
122     */
123    public int isProvidingStrongPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
124    {
125        return this.isProvidingWeakPower(par1IBlockAccess, par2, par3, par4, par5);
126    }
127
128    /**
129     * Returns true if the block is emitting indirect/weak redstone power on the specified side. If isBlockNormalCube
130     * returns true, standard redstone propagation rules will apply instead and this will not be called. Args: World, X,
131     * Y, Z, side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block.
132     */
133    public int isProvidingWeakPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
134    {
135        int i1 = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
136
137        if (!this.func_96470_c(i1))
138        {
139            return 0;
140        }
141        else
142        {
143            int j1 = getDirection(i1);
144            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)));
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.func_94571_i(par2, par3, par4);
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            this.func_94479_f(par1World, par2, par3, par4, par5);
168        }
169    }
170
171    protected void func_94479_f(World par1World, int par2, int par3, int par4, int par5)
172    {
173        int i1 = par1World.getBlockMetadata(par2, par3, par4);
174
175        if (!this.func_94476_e(par1World, par2, par3, par4, i1))
176        {
177            boolean flag = this.func_94478_d(par1World, par2, par3, par4, i1);
178
179            if ((this.isRepeaterPowered && !flag || !this.isRepeaterPowered && flag) && !par1World.func_94573_a(par2, par3, par4, this.blockID))
180            {
181                byte b0 = -1;
182
183                if (this.func_83011_d(par1World, par2, par3, par4, i1))
184                {
185                    b0 = -3;
186                }
187                else if (this.isRepeaterPowered)
188                {
189                    b0 = -2;
190                }
191
192                par1World.func_82740_a(par2, par3, par4, this.blockID, this.func_94481_j_(i1), b0);
193            }
194        }
195    }
196
197    public boolean func_94476_e(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
198    {
199        return false;
200    }
201
202    protected boolean func_94478_d(World par1World, int par2, int par3, int par4, int par5)
203    {
204        return this.ignoreTick(par1World, par2, par3, par4, par5) > 0;
205    }
206
207    protected int ignoreTick(World par1World, int par2, int par3, int par4, int par5)
208    {
209        int i1 = getDirection(par5);
210        int j1 = par2 + Direction.offsetX[i1];
211        int k1 = par4 + Direction.offsetZ[i1];
212        int l1 = par1World.isBlockIndirectlyProvidingPowerTo(j1, par3, k1, Direction.headInvisibleFace[i1]);
213        return l1 >= 15 ? l1 : Math.max(l1, par1World.getBlockId(j1, par3, k1) == Block.redstoneWire.blockID ? par1World.getBlockMetadata(j1, par3, k1) : 0);
214    }
215
216    protected int func_94482_f(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
217    {
218        int i1 = getDirection(par5);
219
220        switch (i1)
221        {
222            case 0:
223            case 2:
224                return Math.max(this.func_94488_g(par1IBlockAccess, par2 - 1, par3, par4, 4), this.func_94488_g(par1IBlockAccess, par2 + 1, par3, par4, 5));
225            case 1:
226            case 3:
227                return Math.max(this.func_94488_g(par1IBlockAccess, par2, par3, par4 + 1, 3), this.func_94488_g(par1IBlockAccess, par2, par3, par4 - 1, 2));
228            default:
229                return 0;
230        }
231    }
232
233    protected int func_94488_g(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
234    {
235        int i1 = par1IBlockAccess.getBlockId(par2, par3, par4);
236        return this.func_94477_d(i1) ? (i1 == Block.redstoneWire.blockID ? par1IBlockAccess.getBlockMetadata(par2, par3, par4) : par1IBlockAccess.isBlockProvidingPowerTo(par2, par3, par4, par5)) : 0;
237    }
238
239    /**
240     * Can this block provide power. Only wire currently seems to have this change based on its state.
241     */
242    public boolean canProvidePower()
243    {
244        return true;
245    }
246
247    /**
248     * Called when the block is placed in the world.
249     */
250    public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving, ItemStack par6ItemStack)
251    {
252        int l = ((MathHelper.floor_double((double)(par5EntityLiving.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3) + 2) % 4;
253        par1World.setBlockMetadataWithNotify(par2, par3, par4, l, 3);
254        boolean flag = this.func_94478_d(par1World, par2, par3, par4, l);
255
256        if (flag)
257        {
258            par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, 1);
259        }
260    }
261
262    /**
263     * Called whenever the block is added into the world. Args: world, x, y, z
264     */
265    public void onBlockAdded(World par1World, int par2, int par3, int par4)
266    {
267        this.func_94483_i_(par1World, par2, par3, par4);
268    }
269
270    protected void func_94483_i_(World par1World, int par2, int par3, int par4)
271    {
272        int l = getDirection(par1World.getBlockMetadata(par2, par3, par4));
273
274        if (l == 1)
275        {
276            par1World.notifyBlockOfNeighborChange(par2 + 1, par3, par4, this.blockID);
277            par1World.func_96439_d(par2 + 1, par3, par4, this.blockID, 4);
278        }
279
280        if (l == 3)
281        {
282            par1World.notifyBlockOfNeighborChange(par2 - 1, par3, par4, this.blockID);
283            par1World.func_96439_d(par2 - 1, par3, par4, this.blockID, 5);
284        }
285
286        if (l == 2)
287        {
288            par1World.notifyBlockOfNeighborChange(par2, par3, par4 + 1, this.blockID);
289            par1World.func_96439_d(par2, par3, par4 + 1, this.blockID, 2);
290        }
291
292        if (l == 0)
293        {
294            par1World.notifyBlockOfNeighborChange(par2, par3, par4 - 1, this.blockID);
295            par1World.func_96439_d(par2, par3, par4 - 1, this.blockID, 3);
296        }
297    }
298
299    /**
300     * Called right before the block is destroyed by a player.  Args: world, x, y, z, metaData
301     */
302    public void onBlockDestroyedByPlayer(World par1World, int par2, int par3, int par4, int par5)
303    {
304        if (this.isRepeaterPowered)
305        {
306            par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID);
307            par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID);
308            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID);
309            par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID);
310            par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
311            par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
312        }
313
314        super.onBlockDestroyedByPlayer(par1World, par2, par3, par4, par5);
315    }
316
317    /**
318     * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
319     * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
320     */
321    public boolean isOpaqueCube()
322    {
323        return false;
324    }
325
326    protected boolean func_94477_d(int par1)
327    {
328        Block block = Block.blocksList[par1];
329        return block != null && block.canProvidePower();
330    }
331
332    protected int func_94480_d(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
333    {
334        return 15;
335    }
336
337    public static boolean isRedstoneRepeaterBlockID(int par0)
338    {
339        return Block.redstoneRepeaterIdle.func_94487_f(par0) || Block.field_94346_cn.func_94487_f(par0);
340    }
341
342    public boolean func_94487_f(int par1)
343    {
344        return par1 == this.func_94485_e().blockID || par1 == this.func_94484_i().blockID;
345    }
346
347    public boolean func_83011_d(World par1World, int par2, int par3, int par4, int par5)
348    {
349        int i1 = getDirection(par5);
350
351        if (isRedstoneRepeaterBlockID(par1World.getBlockId(par2 - Direction.offsetX[i1], par3, par4 - Direction.offsetZ[i1])))
352        {
353            int j1 = par1World.getBlockMetadata(par2 - Direction.offsetX[i1], par3, par4 - Direction.offsetZ[i1]);
354            int k1 = getDirection(j1);
355            return k1 != i1;
356        }
357        else
358        {
359            return false;
360        }
361    }
362
363    protected int func_94486_g(int par1)
364    {
365        return this.func_94481_j_(par1);
366    }
367
368    protected abstract int func_94481_j_(int i);
369
370    protected abstract BlockRedstoneLogic func_94485_e();
371
372    protected abstract BlockRedstoneLogic func_94484_i();
373
374    public boolean func_94334_h(int par1)
375    {
376        return this.func_94487_f(par1);
377    }
378}