001    package net.minecraft.block;
002    
003    import java.util.Random;
004    import net.minecraft.block.material.Material;
005    import net.minecraft.creativetab.CreativeTabs;
006    import net.minecraft.entity.item.EntityMinecart;
007    import net.minecraft.util.AxisAlignedBB;
008    import net.minecraft.util.MovingObjectPosition;
009    import net.minecraft.util.Vec3;
010    import net.minecraft.world.IBlockAccess;
011    import net.minecraft.world.World;
012    
013    import net.minecraftforge.common.ForgeDirection;
014    import static net.minecraftforge.common.ForgeDirection.*;
015    
016    public 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    }