001package net.minecraft.world.gen.feature; 002 003import java.util.Random; 004 005import net.minecraft.block.Block; 006import net.minecraft.block.BlockSapling; 007import net.minecraft.util.MathHelper; 008import net.minecraft.world.World; 009import net.minecraftforge.common.ForgeDirection; 010 011public class WorldGenBigTree extends WorldGenerator 012{ 013 /** 014 * Contains three sets of two values that provide complimentary indices for a given 'major' index - 1 and 2 for 0, 0 015 * and 2 for 1, and 0 and 1 for 2. 016 */ 017 static final byte[] otherCoordPairs = new byte[] {(byte)2, (byte)0, (byte)0, (byte)1, (byte)2, (byte)1}; 018 019 /** random seed for GenBigTree */ 020 Random rand = new Random(); 021 022 /** Reference to the World object. */ 023 World worldObj; 024 int[] basePos = new int[] {0, 0, 0}; 025 int heightLimit = 0; 026 int height; 027 double heightAttenuation = 0.618D; 028 double branchDensity = 1.0D; 029 double branchSlope = 0.381D; 030 double scaleWidth = 1.0D; 031 double leafDensity = 1.0D; 032 033 /** 034 * Currently always 1, can be set to 2 in the class constructor to generate a double-sized tree trunk for big trees. 035 */ 036 int trunkSize = 1; 037 038 /** 039 * Sets the limit of the random value used to initialize the height limit. 040 */ 041 int heightLimitLimit = 12; 042 043 /** 044 * Sets the distance limit for how far away the generator will populate leaves from the base leaf node. 045 */ 046 int leafDistanceLimit = 4; 047 048 /** Contains a list of a points at which to generate groups of leaves. */ 049 int[][] leafNodes; 050 051 public WorldGenBigTree(boolean par1) 052 { 053 super(par1); 054 } 055 056 /** 057 * Generates a list of leaf nodes for the tree, to be populated by generateLeaves. 058 */ 059 void generateLeafNodeList() 060 { 061 this.height = (int)((double)this.heightLimit * this.heightAttenuation); 062 063 if (this.height >= this.heightLimit) 064 { 065 this.height = this.heightLimit - 1; 066 } 067 068 int var1 = (int)(1.382D + Math.pow(this.leafDensity * (double)this.heightLimit / 13.0D, 2.0D)); 069 070 if (var1 < 1) 071 { 072 var1 = 1; 073 } 074 075 int[][] var2 = new int[var1 * this.heightLimit][4]; 076 int var3 = this.basePos[1] + this.heightLimit - this.leafDistanceLimit; 077 int var4 = 1; 078 int var5 = this.basePos[1] + this.height; 079 int var6 = var3 - this.basePos[1]; 080 var2[0][0] = this.basePos[0]; 081 var2[0][1] = var3; 082 var2[0][2] = this.basePos[2]; 083 var2[0][3] = var5; 084 --var3; 085 086 while (var6 >= 0) 087 { 088 int var7 = 0; 089 float var8 = this.layerSize(var6); 090 091 if (var8 < 0.0F) 092 { 093 --var3; 094 --var6; 095 } 096 else 097 { 098 for (double var9 = 0.5D; var7 < var1; ++var7) 099 { 100 double var11 = this.scaleWidth * (double)var8 * ((double)this.rand.nextFloat() + 0.328D); 101 double var13 = (double)this.rand.nextFloat() * 2.0D * Math.PI; 102 int var15 = MathHelper.floor_double(var11 * Math.sin(var13) + (double)this.basePos[0] + var9); 103 int var16 = MathHelper.floor_double(var11 * Math.cos(var13) + (double)this.basePos[2] + var9); 104 int[] var17 = new int[] {var15, var3, var16}; 105 int[] var18 = new int[] {var15, var3 + this.leafDistanceLimit, var16}; 106 107 if (this.checkBlockLine(var17, var18) == -1) 108 { 109 int[] var19 = new int[] {this.basePos[0], this.basePos[1], this.basePos[2]}; 110 double var20 = Math.sqrt(Math.pow((double)Math.abs(this.basePos[0] - var17[0]), 2.0D) + Math.pow((double)Math.abs(this.basePos[2] - var17[2]), 2.0D)); 111 double var22 = var20 * this.branchSlope; 112 113 if ((double)var17[1] - var22 > (double)var5) 114 { 115 var19[1] = var5; 116 } 117 else 118 { 119 var19[1] = (int)((double)var17[1] - var22); 120 } 121 122 if (this.checkBlockLine(var19, var17) == -1) 123 { 124 var2[var4][0] = var15; 125 var2[var4][1] = var3; 126 var2[var4][2] = var16; 127 var2[var4][3] = var19[1]; 128 ++var4; 129 } 130 } 131 } 132 133 --var3; 134 --var6; 135 } 136 } 137 138 this.leafNodes = new int[var4][4]; 139 System.arraycopy(var2, 0, this.leafNodes, 0, var4); 140 } 141 142 void genTreeLayer(int par1, int par2, int par3, float par4, byte par5, int par6) 143 { 144 int var7 = (int)((double)par4 + 0.618D); 145 byte var8 = otherCoordPairs[par5]; 146 byte var9 = otherCoordPairs[par5 + 3]; 147 int[] var10 = new int[] {par1, par2, par3}; 148 int[] var11 = new int[] {0, 0, 0}; 149 int var12 = -var7; 150 int var13 = -var7; 151 152 for (var11[par5] = var10[par5]; var12 <= var7; ++var12) 153 { 154 var11[var8] = var10[var8] + var12; 155 var13 = -var7; 156 157 while (var13 <= var7) 158 { 159 double var15 = Math.pow((double)Math.abs(var12) + 0.5D, 2.0D) + Math.pow((double)Math.abs(var13) + 0.5D, 2.0D); 160 161 if (var15 > (double)(par4 * par4)) 162 { 163 ++var13; 164 } 165 else 166 { 167 var11[var9] = var10[var9] + var13; 168 int var14 = this.worldObj.getBlockId(var11[0], var11[1], var11[2]); 169 170 if (var14 != 0 && var14 != Block.leaves.blockID) 171 { 172 ++var13; 173 } 174 else 175 { 176 this.setBlockAndMetadata(this.worldObj, var11[0], var11[1], var11[2], par6, 0); 177 ++var13; 178 } 179 } 180 } 181 } 182 } 183 184 /** 185 * Gets the rough size of a layer of the tree. 186 */ 187 float layerSize(int par1) 188 { 189 if ((double)par1 < (double)((float)this.heightLimit) * 0.3D) 190 { 191 return -1.618F; 192 } 193 else 194 { 195 float var2 = (float)this.heightLimit / 2.0F; 196 float var3 = (float)this.heightLimit / 2.0F - (float)par1; 197 float var4; 198 199 if (var3 == 0.0F) 200 { 201 var4 = var2; 202 } 203 else if (Math.abs(var3) >= var2) 204 { 205 var4 = 0.0F; 206 } 207 else 208 { 209 var4 = (float)Math.sqrt(Math.pow((double)Math.abs(var2), 2.0D) - Math.pow((double)Math.abs(var3), 2.0D)); 210 } 211 212 var4 *= 0.5F; 213 return var4; 214 } 215 } 216 217 float leafSize(int par1) 218 { 219 return par1 >= 0 && par1 < this.leafDistanceLimit ? (par1 != 0 && par1 != this.leafDistanceLimit - 1 ? 3.0F : 2.0F) : -1.0F; 220 } 221 222 /** 223 * Generates the leaves surrounding an individual entry in the leafNodes list. 224 */ 225 void generateLeafNode(int par1, int par2, int par3) 226 { 227 int var4 = par2; 228 229 for (int var5 = par2 + this.leafDistanceLimit; var4 < var5; ++var4) 230 { 231 float var6 = this.leafSize(var4 - par2); 232 this.genTreeLayer(par1, var4, par3, var6, (byte)1, Block.leaves.blockID); 233 } 234 } 235 236 /** 237 * Places a line of the specified block ID into the world from the first coordinate triplet to the second. 238 */ 239 void placeBlockLine(int[] par1ArrayOfInteger, int[] par2ArrayOfInteger, int par3) 240 { 241 int[] var4 = new int[] {0, 0, 0}; 242 byte var5 = 0; 243 byte var6; 244 245 for (var6 = 0; var5 < 3; ++var5) 246 { 247 var4[var5] = par2ArrayOfInteger[var5] - par1ArrayOfInteger[var5]; 248 249 if (Math.abs(var4[var5]) > Math.abs(var4[var6])) 250 { 251 var6 = var5; 252 } 253 } 254 255 if (var4[var6] != 0) 256 { 257 byte var7 = otherCoordPairs[var6]; 258 byte var8 = otherCoordPairs[var6 + 3]; 259 byte var9; 260 261 if (var4[var6] > 0) 262 { 263 var9 = 1; 264 } 265 else 266 { 267 var9 = -1; 268 } 269 270 double var10 = (double)var4[var7] / (double)var4[var6]; 271 double var12 = (double)var4[var8] / (double)var4[var6]; 272 int[] var14 = new int[] {0, 0, 0}; 273 int var15 = 0; 274 275 for (int var16 = var4[var6] + var9; var15 != var16; var15 += var9) 276 { 277 var14[var6] = MathHelper.floor_double((double)(par1ArrayOfInteger[var6] + var15) + 0.5D); 278 var14[var7] = MathHelper.floor_double((double)par1ArrayOfInteger[var7] + (double)var15 * var10 + 0.5D); 279 var14[var8] = MathHelper.floor_double((double)par1ArrayOfInteger[var8] + (double)var15 * var12 + 0.5D); 280 byte var17 = 0; 281 int var18 = Math.abs(var14[0] - par1ArrayOfInteger[0]); 282 int var19 = Math.abs(var14[2] - par1ArrayOfInteger[2]); 283 int var20 = Math.max(var18, var19); 284 285 if (var20 > 0) 286 { 287 if (var18 == var20) 288 { 289 var17 = 4; 290 } 291 else if (var19 == var20) 292 { 293 var17 = 8; 294 } 295 } 296 297 this.setBlockAndMetadata(this.worldObj, var14[0], var14[1], var14[2], par3, var17); 298 } 299 } 300 } 301 302 /** 303 * Generates the leaf portion of the tree as specified by the leafNodes list. 304 */ 305 void generateLeaves() 306 { 307 int var1 = 0; 308 309 for (int var2 = this.leafNodes.length; var1 < var2; ++var1) 310 { 311 int var3 = this.leafNodes[var1][0]; 312 int var4 = this.leafNodes[var1][1]; 313 int var5 = this.leafNodes[var1][2]; 314 this.generateLeafNode(var3, var4, var5); 315 } 316 } 317 318 /** 319 * Indicates whether or not a leaf node requires additional wood to be added to preserve integrity. 320 */ 321 boolean leafNodeNeedsBase(int par1) 322 { 323 return (double)par1 >= (double)this.heightLimit * 0.2D; 324 } 325 326 /** 327 * Places the trunk for the big tree that is being generated. Able to generate double-sized trunks by changing a 328 * field that is always 1 to 2. 329 */ 330 void generateTrunk() 331 { 332 int var1 = this.basePos[0]; 333 int var2 = this.basePos[1]; 334 int var3 = this.basePos[1] + this.height; 335 int var4 = this.basePos[2]; 336 int[] var5 = new int[] {var1, var2, var4}; 337 int[] var6 = new int[] {var1, var3, var4}; 338 this.placeBlockLine(var5, var6, Block.wood.blockID); 339 340 if (this.trunkSize == 2) 341 { 342 ++var5[0]; 343 ++var6[0]; 344 this.placeBlockLine(var5, var6, Block.wood.blockID); 345 ++var5[2]; 346 ++var6[2]; 347 this.placeBlockLine(var5, var6, Block.wood.blockID); 348 var5[0] += -1; 349 var6[0] += -1; 350 this.placeBlockLine(var5, var6, Block.wood.blockID); 351 } 352 } 353 354 /** 355 * Generates additional wood blocks to fill out the bases of different leaf nodes that would otherwise degrade. 356 */ 357 void generateLeafNodeBases() 358 { 359 int var1 = 0; 360 int var2 = this.leafNodes.length; 361 362 for (int[] var3 = new int[] {this.basePos[0], this.basePos[1], this.basePos[2]}; var1 < var2; ++var1) 363 { 364 int[] var4 = this.leafNodes[var1]; 365 int[] var5 = new int[] {var4[0], var4[1], var4[2]}; 366 var3[1] = var4[3]; 367 int var6 = var3[1] - this.basePos[1]; 368 369 if (this.leafNodeNeedsBase(var6)) 370 { 371 this.placeBlockLine(var3, var5, (byte)Block.wood.blockID); 372 } 373 } 374 } 375 376 /** 377 * Checks a line of blocks in the world from the first coordinate to triplet to the second, returning the distance 378 * (in blocks) before a non-air, non-leaf block is encountered and/or the end is encountered. 379 */ 380 int checkBlockLine(int[] par1ArrayOfInteger, int[] par2ArrayOfInteger) 381 { 382 int[] var3 = new int[] {0, 0, 0}; 383 byte var4 = 0; 384 byte var5; 385 386 for (var5 = 0; var4 < 3; ++var4) 387 { 388 var3[var4] = par2ArrayOfInteger[var4] - par1ArrayOfInteger[var4]; 389 390 if (Math.abs(var3[var4]) > Math.abs(var3[var5])) 391 { 392 var5 = var4; 393 } 394 } 395 396 if (var3[var5] == 0) 397 { 398 return -1; 399 } 400 else 401 { 402 byte var6 = otherCoordPairs[var5]; 403 byte var7 = otherCoordPairs[var5 + 3]; 404 byte var8; 405 406 if (var3[var5] > 0) 407 { 408 var8 = 1; 409 } 410 else 411 { 412 var8 = -1; 413 } 414 415 double var9 = (double)var3[var6] / (double)var3[var5]; 416 double var11 = (double)var3[var7] / (double)var3[var5]; 417 int[] var13 = new int[] {0, 0, 0}; 418 int var14 = 0; 419 int var15; 420 421 for (var15 = var3[var5] + var8; var14 != var15; var14 += var8) 422 { 423 var13[var5] = par1ArrayOfInteger[var5] + var14; 424 var13[var6] = MathHelper.floor_double((double)par1ArrayOfInteger[var6] + (double)var14 * var9); 425 var13[var7] = MathHelper.floor_double((double)par1ArrayOfInteger[var7] + (double)var14 * var11); 426 int var16 = this.worldObj.getBlockId(var13[0], var13[1], var13[2]); 427 428 if (var16 != 0 && var16 != Block.leaves.blockID) 429 { 430 break; 431 } 432 } 433 434 return var14 == var15 ? -1 : Math.abs(var14); 435 } 436 } 437 438 /** 439 * Returns a boolean indicating whether or not the current location for the tree, spanning basePos to to the height 440 * limit, is valid. 441 */ 442 boolean validTreeLocation() 443 { 444 int[] var1 = new int[] {this.basePos[0], this.basePos[1], this.basePos[2]}; 445 int[] var2 = new int[] {this.basePos[0], this.basePos[1] + this.heightLimit - 1, this.basePos[2]}; 446 int var3 = this.worldObj.getBlockId(this.basePos[0], this.basePos[1] - 1, this.basePos[2]); 447 448 Block soil = Block.blocksList[var3]; 449 boolean isValidSoil = (soil != null && soil.canSustainPlant(worldObj, basePos[0], basePos[1] - 1, basePos[2], ForgeDirection.UP, (BlockSapling)Block.sapling)); 450 if (!isValidSoil) 451 { 452 return false; 453 } 454 else 455 { 456 int var4 = this.checkBlockLine(var1, var2); 457 458 if (var4 == -1) 459 { 460 return true; 461 } 462 else if (var4 < 6) 463 { 464 return false; 465 } 466 else 467 { 468 this.heightLimit = var4; 469 return true; 470 } 471 } 472 } 473 474 /** 475 * Rescales the generator settings, only used in WorldGenBigTree 476 */ 477 public void setScale(double par1, double par3, double par5) 478 { 479 this.heightLimitLimit = (int)(par1 * 12.0D); 480 481 if (par1 > 0.5D) 482 { 483 this.leafDistanceLimit = 5; 484 } 485 486 this.scaleWidth = par3; 487 this.leafDensity = par5; 488 } 489 490 public boolean generate(World par1World, Random par2Random, int par3, int par4, int par5) 491 { 492 this.worldObj = par1World; 493 long var6 = par2Random.nextLong(); 494 this.rand.setSeed(var6); 495 this.basePos[0] = par3; 496 this.basePos[1] = par4; 497 this.basePos[2] = par5; 498 499 if (this.heightLimit == 0) 500 { 501 this.heightLimit = 5 + this.rand.nextInt(this.heightLimitLimit); 502 } 503 504 if (!this.validTreeLocation()) 505 { 506 return false; 507 } 508 else 509 { 510 this.generateLeafNodeList(); 511 this.generateLeaves(); 512 this.generateTrunk(); 513 this.generateLeafNodeBases(); 514 return true; 515 } 516 } 517}