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