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
013public abstract class BlockRailBase extends Block
014{
015    /** Power related rails have this field at true. */
016    protected final boolean isPowered;
017
018    /**
019     * Returns true if the block at the coordinates of world passed is a valid rail block (current is rail, powered or
020     * detector).
021     */
022    public static final boolean isRailBlockAt(World par0World, int par1, int par2, int par3)
023    {
024        return isRailBlock(par0World.getBlockId(par1, par2, par3));
025    }
026
027    /**
028     * Return true if the parameter is a blockID for a valid rail block (current is rail, powered or detector).
029     */
030    public static final boolean isRailBlock(int par0)
031    {
032        return Block.blocksList[par0] instanceof BlockRailBase;
033    }
034
035    protected BlockRailBase(int par1, boolean par2)
036    {
037        super(par1, Material.circuits);
038        this.isPowered = par2;
039        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
040        this.setCreativeTab(CreativeTabs.tabTransport);
041    }
042
043    /**
044     * Returns true if the block is power related rail.
045     */
046    public boolean isPowered()
047    {
048        return this.isPowered;
049    }
050
051    /**
052     * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been
053     * cleared to be reused)
054     */
055    public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
056    {
057        return null;
058    }
059
060    /**
061     * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
062     * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
063     */
064    public boolean isOpaqueCube()
065    {
066        return false;
067    }
068
069    /**
070     * Ray traces through the blocks collision from start vector to end vector returning a ray trace hit. Args: world,
071     * x, y, z, startVec, endVec
072     */
073    public MovingObjectPosition collisionRayTrace(World par1World, int par2, int par3, int par4, Vec3 par5Vec3, Vec3 par6Vec3)
074    {
075        this.setBlockBoundsBasedOnState(par1World, par2, par3, par4);
076        return super.collisionRayTrace(par1World, par2, par3, par4, par5Vec3, par6Vec3);
077    }
078
079    /**
080     * Updates the blocks bounds based on its current state. Args: world, x, y, z
081     */
082    public void setBlockBoundsBasedOnState(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
083    {
084        int l = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
085
086        if (l >= 2 && l <= 5)
087        {
088            this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F);
089        }
090        else
091        {
092            this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
093        }
094    }
095
096    /**
097     * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
098     */
099    public boolean renderAsNormalBlock()
100    {
101        return false;
102    }
103
104    /**
105     * The type of render function that is called for this block
106     */
107    public int getRenderType()
108    {
109        return renderType;
110    }
111
112    /**
113     * Returns the quantity of items to drop on block destruction.
114     */
115    public int quantityDropped(Random par1Random)
116    {
117        return 1;
118    }
119
120    /**
121     * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z
122     */
123    public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
124    {
125        return par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4);
126    }
127
128    /**
129     * Called whenever the block is added into the world. Args: world, x, y, z
130     */
131    public void onBlockAdded(World par1World, int par2, int par3, int par4)
132    {
133        if (!par1World.isRemote)
134        {
135            this.refreshTrackShape(par1World, par2, par3, par4, true);
136
137            if (this.isPowered)
138            {
139                this.onNeighborBlockChange(par1World, par2, par3, par4, this.blockID);
140            }
141        }
142    }
143
144    /**
145     * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
146     * their own) Args: x, y, z, neighbor blockID
147     */
148    public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
149    {
150        if (!par1World.isRemote)
151        {
152            int i1 = par1World.getBlockMetadata(par2, par3, par4);
153            int j1 = i1;
154
155            if (this.isPowered)
156            {
157                j1 = i1 & 7;
158            }
159
160            boolean flag = false;
161
162            if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4))
163            {
164                flag = true;
165            }
166
167            if (j1 == 2 && !par1World.doesBlockHaveSolidTopSurface(par2 + 1, par3, par4))
168            {
169                flag = true;
170            }
171
172            if (j1 == 3 && !par1World.doesBlockHaveSolidTopSurface(par2 - 1, par3, par4))
173            {
174                flag = true;
175            }
176
177            if (j1 == 4 && !par1World.doesBlockHaveSolidTopSurface(par2, par3, par4 - 1))
178            {
179                flag = true;
180            }
181
182            if (j1 == 5 && !par1World.doesBlockHaveSolidTopSurface(par2, par3, par4 + 1))
183            {
184                flag = true;
185            }
186
187            if (flag)
188            {
189                this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0);
190                par1World.setBlockToAir(par2, par3, par4);
191            }
192            else
193            {
194                this.func_94358_a(par1World, par2, par3, par4, i1, j1, par5);
195            }
196        }
197    }
198
199    protected void func_94358_a(World par1World, int par2, int par3, int par4, int par5, int par6, int par7) {}
200
201    /**
202     * Completely recalculates the track shape based on neighboring tracks
203     */
204    protected void refreshTrackShape(World par1World, int par2, int par3, int par4, boolean par5)
205    {
206        if (!par1World.isRemote)
207        {
208            (new BlockBaseRailLogic(this, par1World, par2, par3, par4)).func_94511_a(par1World.isBlockIndirectlyGettingPowered(par2, par3, par4), par5);
209        }
210    }
211
212    /**
213     * Returns the mobility information of the block, 0 = free, 1 = can't push but can move over, 2 = total immobility
214     * and stop pistons
215     */
216    public int getMobilityFlag()
217    {
218        return 0;
219    }
220
221    /**
222     * ejects contained items into the world, and notifies neighbours of an update, as appropriate
223     */
224    public void breakBlock(World par1World, int par2, int par3, int par4, int par5, int par6)
225    {
226        int j1 = par6;
227
228        if (this.isPowered)
229        {
230            j1 = par6 & 7;
231        }
232
233        super.breakBlock(par1World, par2, par3, par4, par5, par6);
234
235        if (j1 == 2 || j1 == 3 || j1 == 4 || j1 == 5)
236        {
237            par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, par5);
238        }
239
240        if (this.isPowered)
241        {
242            par1World.notifyBlocksOfNeighborChange(par2, par3, par4, par5);
243            par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, par5);
244        }
245    }
246    
247        /**
248     * Return true if the rail can make corners.
249     * Used by placement logic.
250     * @param world The world.
251     * @param x The rail X coordinate.
252     * @param y The rail Y coordinate.
253     * @param z The rail Z coordinate.
254     * @return True if the rail can make corners.
255     */
256    public boolean isFlexibleRail(World world, int y, int x, int z)
257    {
258        return !isPowered;
259    }
260
261    /**
262     * Returns true if the rail can make up and down slopes.
263     * Used by placement logic.
264     * @param world The world.
265     * @param x The rail X coordinate.
266     * @param y The rail Y coordinate.
267     * @param z The rail Z coordinate.
268     * @return True if the rail can make slopes.
269     */
270    public boolean canMakeSlopes(World world, int x, int y, int z)
271    {
272        return true;
273    }
274
275    /**
276     * Return the rail's metadata (without the power bit if the rail uses one).
277     * Can be used to make the cart think the rail something other than it is,
278     * for example when making diamond junctions or switches.
279     * The cart parameter will often be null unless it it called from EntityMinecart.
280     * 
281     * Valid rail metadata is defined as follows:
282     * 0x0: flat track going North-South
283     * 0x1: flat track going West-East
284     * 0x2: track ascending to the East
285     * 0x3: track ascending to the West
286     * 0x4: track ascending to the North
287     * 0x5: track ascending to the South
288     * 0x6: WestNorth corner (connecting East and South)
289     * 0x7: EastNorth corner (connecting West and South)
290     * 0x8: EastSouth corner (connecting West and North)
291     * 0x9: WestSouth corner (connecting East and North)
292     * 
293     * @param world The world.
294     * @param cart The cart asking for the metadata, null if it is not called by EntityMinecart.
295     * @param y The rail X coordinate.
296     * @param x The rail Y coordinate.
297     * @param z The rail Z coordinate.
298     * @return The metadata.
299     */
300    public int getBasicRailMetadata(IBlockAccess world, EntityMinecart cart, int x, int y, int z)
301    {
302        int meta = world.getBlockMetadata(x, y, z);
303        if(isPowered)
304        {
305            meta = meta & 7;
306        }
307        return meta;
308    }
309
310    /**
311     * Returns the max speed of the rail at the specified position.
312     * @param world The world.
313     * @param cart The cart on the rail, may be null.
314     * @param x The rail X coordinate.
315     * @param y The rail Y coordinate.
316     * @param z The rail Z coordinate.
317     * @return The max speed of the current rail.
318     */
319    public float getRailMaxSpeed(World world, EntityMinecart cart, int y, int x, int z)
320    {
321        return 0.4f;
322    }
323
324    /**
325     * This function is called by any minecart that passes over this rail.
326     * It is called once per update tick that the minecart is on the rail.
327     * @param world The world.
328     * @param cart The cart on the rail.
329     * @param y The rail X coordinate.
330     * @param x The rail Y coordinate.
331     * @param z The rail Z coordinate.
332     */
333    public void onMinecartPass(World world, EntityMinecart cart, int y, int x, int z)
334    {
335    }    
336    
337    /**
338     * Forge: Moved render type to a field and a setter.
339     * This allows for a mod to change the render type
340     * for vanilla rails, and any mod rails that extend
341     * this class.
342     */
343    private int renderType = 9;
344    
345    public void setRenderType(int value)
346    {
347        renderType = value;
348    }
349}