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 }