    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}