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 }