001package net.minecraft.block; 002 003import cpw.mods.fml.relauncher.Side; 004import cpw.mods.fml.relauncher.SideOnly; 005import java.util.Random; 006import net.minecraft.block.material.Material; 007import net.minecraft.client.renderer.texture.IconRegister; 008import net.minecraft.entity.Entity; 009import net.minecraft.util.AxisAlignedBB; 010import net.minecraft.util.Icon; 011import net.minecraft.util.Vec3; 012import net.minecraft.world.IBlockAccess; 013import net.minecraft.world.World; 014 015public abstract class BlockFluid extends Block 016{ 017 @SideOnly(Side.CLIENT) 018 protected Icon[] theIcon; 019 020 protected BlockFluid(int par1, Material par2Material) 021 { 022 super(par1, par2Material); 023 float f = 0.0F; 024 float f1 = 0.0F; 025 this.setBlockBounds(0.0F + f1, 0.0F + f, 0.0F + f1, 1.0F + f1, 1.0F + f, 1.0F + f1); 026 this.setTickRandomly(true); 027 } 028 029 public boolean getBlocksMovement(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) 030 { 031 return this.blockMaterial != Material.lava; 032 } 033 034 @SideOnly(Side.CLIENT) 035 public int getBlockColor() 036 { 037 return 16777215; 038 } 039 040 @SideOnly(Side.CLIENT) 041 042 /** 043 * Returns a integer with hex for 0xrrggbb with this color multiplied against the blocks color. Note only called 044 * when first determining what to render. 045 */ 046 public int colorMultiplier(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) 047 { 048 if (this.blockMaterial != Material.water) 049 { 050 return 16777215; 051 } 052 else 053 { 054 int l = 0; 055 int i1 = 0; 056 int j1 = 0; 057 058 for (int k1 = -1; k1 <= 1; ++k1) 059 { 060 for (int l1 = -1; l1 <= 1; ++l1) 061 { 062 int i2 = par1IBlockAccess.getBiomeGenForCoords(par2 + l1, par4 + k1).getWaterColorMultiplier(); 063 l += (i2 & 16711680) >> 16; 064 i1 += (i2 & 65280) >> 8; 065 j1 += i2 & 255; 066 } 067 } 068 069 return (l / 9 & 255) << 16 | (i1 / 9 & 255) << 8 | j1 / 9 & 255; 070 } 071 } 072 073 /** 074 * Returns the percentage of the fluid block that is air, based on the given flow decay of the fluid. 075 */ 076 public static float getFluidHeightPercent(int par0) 077 { 078 if (par0 >= 8) 079 { 080 par0 = 0; 081 } 082 083 return (float)(par0 + 1) / 9.0F; 084 } 085 086 @SideOnly(Side.CLIENT) 087 088 /** 089 * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata 090 */ 091 public Icon getIcon(int par1, int par2) 092 { 093 return par1 != 0 && par1 != 1 ? this.theIcon[1] : this.theIcon[0]; 094 } 095 096 /** 097 * Returns the amount of fluid decay at the coordinates, or -1 if the block at the coordinates is not the same 098 * material as the fluid. 099 */ 100 protected int getFlowDecay(World par1World, int par2, int par3, int par4) 101 { 102 return par1World.getBlockMaterial(par2, par3, par4) == this.blockMaterial ? par1World.getBlockMetadata(par2, par3, par4) : -1; 103 } 104 105 /** 106 * Returns the flow decay but converts values indicating falling liquid (values >=8) to their effective source block 107 * value of zero. 108 */ 109 protected int getEffectiveFlowDecay(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) 110 { 111 if (par1IBlockAccess.getBlockMaterial(par2, par3, par4) != this.blockMaterial) 112 { 113 return -1; 114 } 115 else 116 { 117 int l = par1IBlockAccess.getBlockMetadata(par2, par3, par4); 118 119 if (l >= 8) 120 { 121 l = 0; 122 } 123 124 return l; 125 } 126 } 127 128 /** 129 * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc) 130 */ 131 public boolean renderAsNormalBlock() 132 { 133 return false; 134 } 135 136 /** 137 * Is this block (a) opaque and (b) a full 1m cube? This determines whether or not to render the shared face of two 138 * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block. 139 */ 140 public boolean isOpaqueCube() 141 { 142 return false; 143 } 144 145 /** 146 * Returns whether this block is collideable based on the arguments passed in Args: blockMetaData, unknownFlag 147 */ 148 public boolean canCollideCheck(int par1, boolean par2) 149 { 150 return par2 && par1 == 0; 151 } 152 153 /** 154 * Returns Returns true if the given side of this block type should be rendered (if it's solid or not), if the 155 * adjacent block is at the given coordinates. Args: blockAccess, x, y, z, side 156 */ 157 public boolean isBlockSolid(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 158 { 159 Material material = par1IBlockAccess.getBlockMaterial(par2, par3, par4); 160 return material == this.blockMaterial ? false : (par5 == 1 ? true : (material == Material.ice ? false : super.isBlockSolid(par1IBlockAccess, par2, par3, par4, par5))); 161 } 162 163 @SideOnly(Side.CLIENT) 164 165 /** 166 * Returns true if the given side of this block type should be rendered, if the adjacent block is at the given 167 * coordinates. Args: blockAccess, x, y, z, side 168 */ 169 public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 170 { 171 Material material = par1IBlockAccess.getBlockMaterial(par2, par3, par4); 172 return material == this.blockMaterial ? false : (par5 == 1 ? true : (material == Material.ice ? false : super.shouldSideBeRendered(par1IBlockAccess, par2, par3, par4, par5))); 173 } 174 175 /** 176 * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been 177 * cleared to be reused) 178 */ 179 public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4) 180 { 181 return null; 182 } 183 184 /** 185 * The type of render function that is called for this block 186 */ 187 public int getRenderType() 188 { 189 return 4; 190 } 191 192 /** 193 * Returns the ID of the items to drop on destruction. 194 */ 195 public int idDropped(int par1, Random par2Random, int par3) 196 { 197 return 0; 198 } 199 200 /** 201 * Returns the quantity of items to drop on block destruction. 202 */ 203 public int quantityDropped(Random par1Random) 204 { 205 return 0; 206 } 207 208 /** 209 * Returns a vector indicating the direction and intensity of fluid flow. 210 */ 211 private Vec3 getFlowVector(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) 212 { 213 Vec3 vec3 = par1IBlockAccess.getWorldVec3Pool().getVecFromPool(0.0D, 0.0D, 0.0D); 214 int l = this.getEffectiveFlowDecay(par1IBlockAccess, par2, par3, par4); 215 216 for (int i1 = 0; i1 < 4; ++i1) 217 { 218 int j1 = par2; 219 int k1 = par4; 220 221 if (i1 == 0) 222 { 223 j1 = par2 - 1; 224 } 225 226 if (i1 == 1) 227 { 228 k1 = par4 - 1; 229 } 230 231 if (i1 == 2) 232 { 233 ++j1; 234 } 235 236 if (i1 == 3) 237 { 238 ++k1; 239 } 240 241 int l1 = this.getEffectiveFlowDecay(par1IBlockAccess, j1, par3, k1); 242 int i2; 243 244 if (l1 < 0) 245 { 246 if (!par1IBlockAccess.getBlockMaterial(j1, par3, k1).blocksMovement()) 247 { 248 l1 = this.getEffectiveFlowDecay(par1IBlockAccess, j1, par3 - 1, k1); 249 250 if (l1 >= 0) 251 { 252 i2 = l1 - (l - 8); 253 vec3 = vec3.addVector((double)((j1 - par2) * i2), (double)((par3 - par3) * i2), (double)((k1 - par4) * i2)); 254 } 255 } 256 } 257 else if (l1 >= 0) 258 { 259 i2 = l1 - l; 260 vec3 = vec3.addVector((double)((j1 - par2) * i2), (double)((par3 - par3) * i2), (double)((k1 - par4) * i2)); 261 } 262 } 263 264 if (par1IBlockAccess.getBlockMetadata(par2, par3, par4) >= 8) 265 { 266 boolean flag = false; 267 268 if (flag || this.isBlockSolid(par1IBlockAccess, par2, par3, par4 - 1, 2)) 269 { 270 flag = true; 271 } 272 273 if (flag || this.isBlockSolid(par1IBlockAccess, par2, par3, par4 + 1, 3)) 274 { 275 flag = true; 276 } 277 278 if (flag || this.isBlockSolid(par1IBlockAccess, par2 - 1, par3, par4, 4)) 279 { 280 flag = true; 281 } 282 283 if (flag || this.isBlockSolid(par1IBlockAccess, par2 + 1, par3, par4, 5)) 284 { 285 flag = true; 286 } 287 288 if (flag || this.isBlockSolid(par1IBlockAccess, par2, par3 + 1, par4 - 1, 2)) 289 { 290 flag = true; 291 } 292 293 if (flag || this.isBlockSolid(par1IBlockAccess, par2, par3 + 1, par4 + 1, 3)) 294 { 295 flag = true; 296 } 297 298 if (flag || this.isBlockSolid(par1IBlockAccess, par2 - 1, par3 + 1, par4, 4)) 299 { 300 flag = true; 301 } 302 303 if (flag || this.isBlockSolid(par1IBlockAccess, par2 + 1, par3 + 1, par4, 5)) 304 { 305 flag = true; 306 } 307 308 if (flag) 309 { 310 vec3 = vec3.normalize().addVector(0.0D, -6.0D, 0.0D); 311 } 312 } 313 314 vec3 = vec3.normalize(); 315 return vec3; 316 } 317 318 /** 319 * Can add to the passed in vector for a movement vector to be applied to the entity. Args: x, y, z, entity, vec3d 320 */ 321 public void velocityToAddToEntity(World par1World, int par2, int par3, int par4, Entity par5Entity, Vec3 par6Vec3) 322 { 323 Vec3 vec31 = this.getFlowVector(par1World, par2, par3, par4); 324 par6Vec3.xCoord += vec31.xCoord; 325 par6Vec3.yCoord += vec31.yCoord; 326 par6Vec3.zCoord += vec31.zCoord; 327 } 328 329 /** 330 * How many world ticks before ticking 331 */ 332 public int tickRate(World par1World) 333 { 334 return this.blockMaterial == Material.water ? 5 : (this.blockMaterial == Material.lava ? (par1World.provider.hasNoSky ? 10 : 30) : 0); 335 } 336 337 /** 338 * Called whenever the block is added into the world. Args: world, x, y, z 339 */ 340 public void onBlockAdded(World par1World, int par2, int par3, int par4) 341 { 342 this.checkForHarden(par1World, par2, par3, par4); 343 } 344 345 /** 346 * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are 347 * their own) Args: x, y, z, neighbor blockID 348 */ 349 public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5) 350 { 351 this.checkForHarden(par1World, par2, par3, par4); 352 } 353 354 @SideOnly(Side.CLIENT) 355 356 /** 357 * Goes straight to getLightBrightnessForSkyBlocks for Blocks, does some fancy computing for Fluids 358 */ 359 public int getMixedBrightnessForBlock(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) 360 { 361 int l = par1IBlockAccess.getLightBrightnessForSkyBlocks(par2, par3, par4, 0); 362 int i1 = par1IBlockAccess.getLightBrightnessForSkyBlocks(par2, par3 + 1, par4, 0); 363 int j1 = l & 255; 364 int k1 = i1 & 255; 365 int l1 = l >> 16 & 255; 366 int i2 = i1 >> 16 & 255; 367 return (j1 > k1 ? j1 : k1) | (l1 > i2 ? l1 : i2) << 16; 368 } 369 370 @SideOnly(Side.CLIENT) 371 372 /** 373 * How bright to render this block based on the light its receiving. Args: iBlockAccess, x, y, z 374 */ 375 public float getBlockBrightness(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) 376 { 377 float f = par1IBlockAccess.getLightBrightness(par2, par3, par4); 378 float f1 = par1IBlockAccess.getLightBrightness(par2, par3 + 1, par4); 379 return f > f1 ? f : f1; 380 } 381 382 @SideOnly(Side.CLIENT) 383 384 /** 385 * Returns which pass should this block be rendered on. 0 for solids and 1 for alpha 386 */ 387 public int getRenderBlockPass() 388 { 389 return this.blockMaterial == Material.water ? 1 : 0; 390 } 391 392 @SideOnly(Side.CLIENT) 393 394 /** 395 * A randomly called display update to be able to add particles or other items for display 396 */ 397 public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random) 398 { 399 int l; 400 401 if (this.blockMaterial == Material.water) 402 { 403 if (par5Random.nextInt(10) == 0) 404 { 405 l = par1World.getBlockMetadata(par2, par3, par4); 406 407 if (l <= 0 || l >= 8) 408 { 409 par1World.spawnParticle("suspended", (double)((float)par2 + par5Random.nextFloat()), (double)((float)par3 + par5Random.nextFloat()), (double)((float)par4 + par5Random.nextFloat()), 0.0D, 0.0D, 0.0D); 410 } 411 } 412 413 for (l = 0; l < 0; ++l) 414 { 415 int i1 = par5Random.nextInt(4); 416 int j1 = par2; 417 int k1 = par4; 418 419 if (i1 == 0) 420 { 421 j1 = par2 - 1; 422 } 423 424 if (i1 == 1) 425 { 426 ++j1; 427 } 428 429 if (i1 == 2) 430 { 431 k1 = par4 - 1; 432 } 433 434 if (i1 == 3) 435 { 436 ++k1; 437 } 438 439 if (par1World.getBlockMaterial(j1, par3, k1) == Material.air && (par1World.getBlockMaterial(j1, par3 - 1, k1).blocksMovement() || par1World.getBlockMaterial(j1, par3 - 1, k1).isLiquid())) 440 { 441 float f = 0.0625F; 442 double d0 = (double)((float)par2 + par5Random.nextFloat()); 443 double d1 = (double)((float)par3 + par5Random.nextFloat()); 444 double d2 = (double)((float)par4 + par5Random.nextFloat()); 445 446 if (i1 == 0) 447 { 448 d0 = (double)((float)par2 - f); 449 } 450 451 if (i1 == 1) 452 { 453 d0 = (double)((float)(par2 + 1) + f); 454 } 455 456 if (i1 == 2) 457 { 458 d2 = (double)((float)par4 - f); 459 } 460 461 if (i1 == 3) 462 { 463 d2 = (double)((float)(par4 + 1) + f); 464 } 465 466 double d3 = 0.0D; 467 double d4 = 0.0D; 468 469 if (i1 == 0) 470 { 471 d3 = (double)(-f); 472 } 473 474 if (i1 == 1) 475 { 476 d3 = (double)f; 477 } 478 479 if (i1 == 2) 480 { 481 d4 = (double)(-f); 482 } 483 484 if (i1 == 3) 485 { 486 d4 = (double)f; 487 } 488 489 par1World.spawnParticle("splash", d0, d1, d2, d3, 0.0D, d4); 490 } 491 } 492 } 493 494 if (this.blockMaterial == Material.water && par5Random.nextInt(64) == 0) 495 { 496 l = par1World.getBlockMetadata(par2, par3, par4); 497 498 if (l > 0 && l < 8) 499 { 500 par1World.playSound((double)((float)par2 + 0.5F), (double)((float)par3 + 0.5F), (double)((float)par4 + 0.5F), "liquid.water", par5Random.nextFloat() * 0.25F + 0.75F, par5Random.nextFloat() * 1.0F + 0.5F, false); 501 } 502 } 503 504 double d5; 505 double d6; 506 double d7; 507 508 if (this.blockMaterial == Material.lava && par1World.getBlockMaterial(par2, par3 + 1, par4) == Material.air && !par1World.isBlockOpaqueCube(par2, par3 + 1, par4)) 509 { 510 if (par5Random.nextInt(100) == 0) 511 { 512 d5 = (double)((float)par2 + par5Random.nextFloat()); 513 d7 = (double)par3 + this.maxY; 514 d6 = (double)((float)par4 + par5Random.nextFloat()); 515 par1World.spawnParticle("lava", d5, d7, d6, 0.0D, 0.0D, 0.0D); 516 par1World.playSound(d5, d7, d6, "liquid.lavapop", 0.2F + par5Random.nextFloat() * 0.2F, 0.9F + par5Random.nextFloat() * 0.15F, false); 517 } 518 519 if (par5Random.nextInt(200) == 0) 520 { 521 par1World.playSound((double)par2, (double)par3, (double)par4, "liquid.lava", 0.2F + par5Random.nextFloat() * 0.2F, 0.9F + par5Random.nextFloat() * 0.15F, false); 522 } 523 } 524 525 if (par5Random.nextInt(10) == 0 && par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) && !par1World.getBlockMaterial(par2, par3 - 2, par4).blocksMovement()) 526 { 527 d5 = (double)((float)par2 + par5Random.nextFloat()); 528 d7 = (double)par3 - 1.05D; 529 d6 = (double)((float)par4 + par5Random.nextFloat()); 530 531 if (this.blockMaterial == Material.water) 532 { 533 par1World.spawnParticle("dripWater", d5, d7, d6, 0.0D, 0.0D, 0.0D); 534 } 535 else 536 { 537 par1World.spawnParticle("dripLava", d5, d7, d6, 0.0D, 0.0D, 0.0D); 538 } 539 } 540 } 541 542 @SideOnly(Side.CLIENT) 543 544 /** 545 * the sin and cos of this number determine the surface gradient of the flowing block. 546 */ 547 public static double getFlowDirection(IBlockAccess par0IBlockAccess, int par1, int par2, int par3, Material par4Material) 548 { 549 Vec3 vec3 = null; 550 551 if (par4Material == Material.water) 552 { 553 vec3 = Block.waterMoving.getFlowVector(par0IBlockAccess, par1, par2, par3); 554 } 555 556 if (par4Material == Material.lava) 557 { 558 vec3 = Block.lavaMoving.getFlowVector(par0IBlockAccess, par1, par2, par3); 559 } 560 561 return vec3.xCoord == 0.0D && vec3.zCoord == 0.0D ? -1000.0D : Math.atan2(vec3.zCoord, vec3.xCoord) - (Math.PI / 2D); 562 } 563 564 /** 565 * Forces lava to check to see if it is colliding with water, and then decide what it should harden to. 566 */ 567 private void checkForHarden(World par1World, int par2, int par3, int par4) 568 { 569 if (par1World.getBlockId(par2, par3, par4) == this.blockID) 570 { 571 if (this.blockMaterial == Material.lava) 572 { 573 boolean flag = false; 574 575 if (flag || par1World.getBlockMaterial(par2, par3, par4 - 1) == Material.water) 576 { 577 flag = true; 578 } 579 580 if (flag || par1World.getBlockMaterial(par2, par3, par4 + 1) == Material.water) 581 { 582 flag = true; 583 } 584 585 if (flag || par1World.getBlockMaterial(par2 - 1, par3, par4) == Material.water) 586 { 587 flag = true; 588 } 589 590 if (flag || par1World.getBlockMaterial(par2 + 1, par3, par4) == Material.water) 591 { 592 flag = true; 593 } 594 595 if (flag || par1World.getBlockMaterial(par2, par3 + 1, par4) == Material.water) 596 { 597 flag = true; 598 } 599 600 if (flag) 601 { 602 int l = par1World.getBlockMetadata(par2, par3, par4); 603 604 if (l == 0) 605 { 606 par1World.setBlock(par2, par3, par4, Block.obsidian.blockID); 607 } 608 else if (l <= 4) 609 { 610 par1World.setBlock(par2, par3, par4, Block.cobblestone.blockID); 611 } 612 613 this.triggerLavaMixEffects(par1World, par2, par3, par4); 614 } 615 } 616 } 617 } 618 619 /** 620 * Creates fizzing sound and smoke. Used when lava flows over block or mixes with water. 621 */ 622 protected void triggerLavaMixEffects(World par1World, int par2, int par3, int par4) 623 { 624 par1World.playSoundEffect((double)((float)par2 + 0.5F), (double)((float)par3 + 0.5F), (double)((float)par4 + 0.5F), "random.fizz", 0.5F, 2.6F + (par1World.rand.nextFloat() - par1World.rand.nextFloat()) * 0.8F); 625 626 for (int l = 0; l < 8; ++l) 627 { 628 par1World.spawnParticle("largesmoke", (double)par2 + Math.random(), (double)par3 + 1.2D, (double)par4 + Math.random(), 0.0D, 0.0D, 0.0D); 629 } 630 } 631 632 @SideOnly(Side.CLIENT) 633 634 /** 635 * When this method is called, your block should register all the icons it needs with the given IconRegister. This 636 * is the only chance you get to register icons. 637 */ 638 public void registerIcons(IconRegister par1IconRegister) 639 { 640 if (this.blockMaterial == Material.lava) 641 { 642 this.theIcon = new Icon[] {par1IconRegister.registerIcon("lava"), par1IconRegister.registerIcon("lava_flow")}; 643 } 644 else 645 { 646 this.theIcon = new Icon[] {par1IconRegister.registerIcon("water"), par1IconRegister.registerIcon("water_flow")}; 647 } 648 } 649 650 @SideOnly(Side.CLIENT) 651 public static Icon func_94424_b(String par0Str) 652 { 653 return par0Str == "water" ? Block.waterMoving.theIcon[0] : (par0Str == "water_flow" ? Block.waterMoving.theIcon[1] : (par0Str == "lava" ? Block.lavaMoving.theIcon[0] : (par0Str == "lava_flow" ? Block.lavaMoving.theIcon[1] : null))); 654 } 655}