001package net.minecraft.block;
002
003import java.util.Random;
004import net.minecraft.block.material.Material;
005import net.minecraft.creativetab.CreativeTabs;
006import net.minecraft.entity.item.EntityMinecart;
007import net.minecraft.util.AxisAlignedBB;
008import net.minecraft.util.MovingObjectPosition;
009import net.minecraft.util.Vec3;
010import net.minecraft.world.IBlockAccess;
011import net.minecraft.world.World;
012
013import net.minecraftforge.common.ForgeDirection;
014import static net.minecraftforge.common.ForgeDirection.*;
015
016public class BlockRail extends Block
017{
018    /** Power related rails have this field at true. */
019    private final boolean isPowered;
020
021    /**
022     * Returns true if the block at the coordinates of world passed is a valid rail block (current is rail, powered or
023     * detector).
024     */
025    public static final boolean isRailBlockAt(World par0World, int par1, int par2, int par3)
026    {
027        int var4 = par0World.getBlockId(par1, par2, par3);
028        return isRailBlock(var4);
029    }
030
031    /**
032     * Return true if the parameter is a blockID for a valid rail block (current is rail, powered or detector).
033     */
034    public static final boolean isRailBlock(int par0)
035    {
036        return Block.blocksList[par0] instanceof BlockRail;
037    }
038
039    protected BlockRail(int par1, int par2, boolean par3)
040    {
041        super(par1, par2, Material.circuits);
042        this.isPowered = par3;
043        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
044        this.setCreativeTab(CreativeTabs.tabTransport);
045    }
046
047    /**
048     * Returns true if the block is power related rail.
049     */
050    public boolean isPowered()
051    {
052        return this.isPowered;
053    }
054
055    /**
056     * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been
057     * cleared to be reused)
058     */
059    public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
060    {
061        return null;
062    }
063
064    /**
065     * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
066     * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
067     */
068    public boolean isOpaqueCube()
069    {
070        return false;
071    }
072
073    /**
074     * Ray traces through the blocks collision from start vector to end vector returning a ray trace hit. Args: world,
075     * x, y, z, startVec, endVec
076     */
077    public MovingObjectPosition collisionRayTrace(World par1World, int par2, int par3, int par4, Vec3 par5Vec3, Vec3 par6Vec3)
078    {
079        this.setBlockBoundsBasedOnState(par1World, par2, par3, par4);
080        return super.collisionRayTrace(par1World, par2, par3, par4, par5Vec3, par6Vec3);
081    }
082
083    /**
084     * Updates the blocks bounds based on its current state. Args: world, x, y, z
085     */
086    public void setBlockBoundsBasedOnState(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
087    {
088        int var5 = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
089
090        if (var5 >= 2 && var5 <= 5)
091        {
092            this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F);
093        }
094        else
095        {
096            this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
097        }
098    }
099
100    /**
101     * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
102     */
103    public int getBlockTextureFromSideAndMetadata(int par1, int par2)
104    {
105        if (this.isPowered)
106        {
107            if (this.blockID == Block.railPowered.blockID && (par2 & 8) == 0)
108            {
109                return this.blockIndexInTexture - 16;
110            }
111        }
112        else if (par2 >= 6)
113        {
114            return this.blockIndexInTexture - 16;
115        }
116
117        return this.blockIndexInTexture;
118    }
119
120    /**
121     * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
122     */
123    public boolean renderAsNormalBlock()
124    {
125        return false;
126    }
127
128    /**
129     * The type of render function that is called for this block
130     */
131    public int getRenderType()
132    {
133        return renderType;
134    }
135
136    /**
137     * Returns the quantity of items to drop on block destruction.
138     */
139    public int quantityDropped(Random par1Random)
140    {
141        return 1;
142    }
143
144    /**
145     * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z
146     */
147    public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
148    {
149        return par1World.isBlockSolidOnSide(par2, par3 - 1, par4, UP);
150    }
151
152    /**
153     * Called whenever the block is added into the world. Args: world, x, y, z
154     */
155    public void onBlockAdded(World par1World, int par2, int par3, int par4)
156    {
157        if (!par1World.isRemote)
158        {
159            this.refreshTrackShape(par1World, par2, par3, par4, true);
160
161            if (this.blockID == Block.railPowered.blockID)
162            {
163                this.onNeighborBlockChange(par1World, par2, par3, par4, this.blockID);
164            }
165        }
166    }
167
168    /**
169     * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
170     * their own) Args: x, y, z, neighbor blockID
171     */
172    public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
173    {
174        if (!par1World.isRemote)
175        {
176            int var6 = par1World.getBlockMetadata(par2, par3, par4);
177            int var7 = var6;
178
179            if (this.isPowered)
180            {
181                var7 = var6 & 7;
182            }
183
184            boolean var8 = false;
185
186            if (!par1World.isBlockSolidOnSide(par2, par3 - 1, par4, UP))
187            {
188                var8 = true;
189            }
190
191            if (var7 == 2 && !par1World.isBlockSolidOnSide(par2 + 1, par3, par4, UP))
192            {
193                var8 = true;
194            }
195
196            if (var7 == 3 && !par1World.isBlockSolidOnSide(par2 - 1, par3, par4, UP))
197            {
198                var8 = true;
199            }
200
201            if (var7 == 4 && !par1World.isBlockSolidOnSide(par2, par3, par4 - 1, UP))
202            {
203                var8 = true;
204            }
205
206            if (var7 == 5 && !par1World.isBlockSolidOnSide(par2, par3, par4 + 1, UP))
207            {
208                var8 = true;
209            }
210
211            if (var8)
212            {
213                this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0);
214                par1World.setBlockWithNotify(par2, par3, par4, 0);
215            }
216            else if (this.blockID == Block.railPowered.blockID)
217            {
218                boolean var9 = par1World.isBlockIndirectlyGettingPowered(par2, par3, par4);
219                var9 = var9 || this.isNeighborRailPowered(par1World, par2, par3, par4, var6, true, 0) || this.isNeighborRailPowered(par1World, par2, par3, par4, var6, false, 0);
220                boolean var10 = false;
221
222                if (var9 && (var6 & 8) == 0)
223                {
224                    par1World.setBlockMetadataWithNotify(par2, par3, par4, var7 | 8);
225                    var10 = true;
226                }
227                else if (!var9 && (var6 & 8) != 0)
228                {
229                    par1World.setBlockMetadataWithNotify(par2, par3, par4, var7);
230                    var10 = true;
231                }
232
233                if (var10)
234                {
235                    par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID);
236
237                    if (var7 == 2 || var7 == 3 || var7 == 4 || var7 == 5)
238                    {
239                        par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID);
240                    }
241                }
242            }
243            else if (par5 > 0 && Block.blocksList[par5].canProvidePower() && !this.isPowered && RailLogic.getAdjacentTrackCount(new RailLogic(this, par1World, par2, par3, par4)) == 3)
244            {
245                this.refreshTrackShape(par1World, par2, par3, par4, false);
246            }
247        }
248    }
249
250    /**
251     * Completely recalculates the track shape based on neighboring tracks
252     */
253    private void refreshTrackShape(World par1World, int par2, int par3, int par4, boolean par5)
254    {
255        if (!par1World.isRemote)
256        {
257            (new RailLogic(this, par1World, par2, par3, par4)).refreshTrackShape(par1World.isBlockIndirectlyGettingPowered(par2, par3, par4), par5);
258        }
259    }
260
261    /**
262     * Powered minecart rail is conductive like wire, so check for powered neighbors
263     */
264    private boolean isNeighborRailPowered(World par1World, int par2, int par3, int par4, int par5, boolean par6, int par7)
265    {
266        if (par7 >= 8)
267        {
268            return false;
269        }
270        else
271        {
272            int var8 = par5 & 7;
273            boolean var9 = true;
274
275            switch (var8)
276            {
277                case 0:
278                    if (par6)
279                    {
280                        ++par4;
281                    }
282                    else
283                    {
284                        --par4;
285                    }
286
287                    break;
288                case 1:
289                    if (par6)
290                    {
291                        --par2;
292                    }
293                    else
294                    {
295                        ++par2;
296                    }
297
298                    break;
299                case 2:
300                    if (par6)
301                    {
302                        --par2;
303                    }
304                    else
305                    {
306                        ++par2;
307                        ++par3;
308                        var9 = false;
309                    }
310
311                    var8 = 1;
312                    break;
313                case 3:
314                    if (par6)
315                    {
316                        --par2;
317                        ++par3;
318                        var9 = false;
319                    }
320                    else
321                    {
322                        ++par2;
323                    }
324
325                    var8 = 1;
326                    break;
327                case 4:
328                    if (par6)
329                    {
330                        ++par4;
331                    }
332                    else
333                    {
334                        --par4;
335                        ++par3;
336                        var9 = false;
337                    }
338
339                    var8 = 0;
340                    break;
341                case 5:
342                    if (par6)
343                    {
344                        ++par4;
345                        ++par3;
346                        var9 = false;
347                    }
348                    else
349                    {
350                        --par4;
351                    }
352
353                    var8 = 0;
354            }
355
356            return this.isRailPassingPower(par1World, par2, par3, par4, par6, par7, var8) ? true : var9 && this.isRailPassingPower(par1World, par2, par3 - 1, par4, par6, par7, var8);
357        }
358    }
359
360    /**
361     * Returns true if the specified rail is passing power to its neighbor
362     */
363    private boolean isRailPassingPower(World par1World, int par2, int par3, int par4, boolean par5, int par6, int par7)
364    {
365        int var8 = par1World.getBlockId(par2, par3, par4);
366
367        if (var8 == Block.railPowered.blockID)
368        {
369            int var9 = par1World.getBlockMetadata(par2, par3, par4);
370            int var10 = var9 & 7;
371
372            if (par7 == 1 && (var10 == 0 || var10 == 4 || var10 == 5))
373            {
374                return false;
375            }
376
377            if (par7 == 0 && (var10 == 1 || var10 == 2 || var10 == 3))
378            {
379                return false;
380            }
381
382            if ((var9 & 8) != 0)
383            {
384                if (par1World.isBlockIndirectlyGettingPowered(par2, par3, par4))
385                {
386                    return true;
387                }
388
389                return this.isNeighborRailPowered(par1World, par2, par3, par4, var9, par5, par6 + 1);
390            }
391        }
392
393        return false;
394    }
395
396    /**
397     * Returns the mobility information of the block, 0 = free, 1 = can't push but can move over, 2 = total immobility
398     * and stop pistons
399     */
400    public int getMobilityFlag()
401    {
402        return 0;
403    }
404
405    /**
406     * Return true if the blocks passed is a power related rail.
407     * @deprecated
408     * This function is no longer called by Minecraft
409     */
410    @Deprecated
411    static boolean isPoweredBlockRail(BlockRail par0BlockRail)
412    {
413        return par0BlockRail.isPowered;
414    }
415
416    /**
417     * Return true if the rail can make corners.
418     * Used by placement logic.
419     * @param world The world.
420     * @param x The rail X coordinate.
421     * @param y The rail Y coordinate.
422     * @param z The rail Z coordinate.
423     * @return True if the rail can make corners.
424     */
425    public boolean isFlexibleRail(World world, int y, int x, int z)
426    {
427        return !isPowered;
428    }
429
430    /**
431     * Returns true if the rail can make up and down slopes.
432     * Used by placement logic.
433     * @param world The world.
434     * @param x The rail X coordinate.
435     * @param y The rail Y coordinate.
436     * @param z The rail Z coordinate.
437     * @return True if the rail can make slopes.
438     */
439    public boolean canMakeSlopes(World world, int x, int y, int z)
440    {
441        return true;
442    }
443
444    /**
445     * Return the rails metadata (without the power bit if the rail uses one).
446     * Can be used to make the cart think the rail something other than it is,
447     * for example when making diamond junctions or switches.
448     * The cart parameter will often be null unless it it called from EntityMinecart.
449     * 
450     * Valid rail metadata is defined as follows:
451     * 0x0: flat track going North-South
452     * 0x1: flat track going West-East
453     * 0x2: track ascending to the East
454     * 0x3: track ascending to the West
455     * 0x4: track ascending to the North
456     * 0x5: track ascending to the South
457     * 0x6: WestNorth corner (connecting East and South)
458     * 0x7: EastNorth corner (connecting West and South)
459     * 0x8: EastSouth corner (connecting West and North)
460     * 0x9: WestSouth corner (connecting East and North)
461     * 
462     * All directions are Notch defined.
463     * In MC Beta 1.8.3 the Sun rises in the North.
464     * In MC 1.0.0 the Sun rises in the East.
465     * 
466     * @param world The world.
467     * @param cart The cart asking for the metadata, null if it is not called by EntityMinecart.
468     * @param y The rail X coordinate.
469     * @param x The rail Y coordinate.
470     * @param z The rail Z coordinate.
471     * @return The metadata.
472     */
473    public int getBasicRailMetadata(IBlockAccess world, EntityMinecart cart, int x, int y, int z)
474    {
475        int meta = world.getBlockMetadata(x, y, z);
476        if(isPowered)
477        {
478            meta = meta & 7;
479        }
480        return meta;
481    }
482
483    /**
484     * Returns the max speed of the rail at the specified position.
485     * @param world The world.
486     * @param cart The cart on the rail, may be null.
487     * @param x The rail X coordinate.
488     * @param y The rail Y coordinate.
489     * @param z The rail Z coordinate.
490     * @return The max speed of the current rail.
491     */
492    public float getRailMaxSpeed(World world, EntityMinecart cart, int y, int x, int z)
493    {
494        return 0.4f;
495    }
496
497    /**
498     * This function is called by any minecart that passes over this rail.
499     * It is called once per update tick that the minecart is on the rail.
500     * @param world The world.
501     * @param cart The cart on the rail.
502     * @param y The rail X coordinate.
503     * @param x The rail Y coordinate.
504     * @param z The rail Z coordinate.
505     */
506    public void onMinecartPass(World world, EntityMinecart cart, int y, int x, int z)
507    {
508    }
509
510    /**
511     * Return true if this rail uses the 4th bit as a power bit.
512     * Avoid using this function when getBasicRailMetadata() can be used instead.
513     * The only reason to use this function is if you wish to change the rails metadata.
514     * @param world The world.
515     * @param x The rail X coordinate.
516     * @param y The rail Y coordinate.
517     * @param z The rail Z coordinate.
518     * @return True if the 4th bit is a power bit.
519     */
520    public boolean hasPowerBit(World world, int x, int y, int z)
521    {
522        return isPowered;
523    }
524    
525    
526    /**
527     * Forge: Moved render type to a field and a setter.
528     * This allows for a mod to change the render type
529     * for vanilla rails, and any mod rails that extend
530     * this class.
531     */
532    private int renderType = 9;
533    
534    public void setRenderType(int value)
535    {
536        renderType = value;
537    }
538}