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 i = (int)(1.382D + Math.pow(this.leafDensity * (double)this.heightLimit / 13.0D, 2.0D)); 069 070 if (i < 1) 071 { 072 i = 1; 073 } 074 075 int[][] aint = new int[i * this.heightLimit][4]; 076 int j = this.basePos[1] + this.heightLimit - this.leafDistanceLimit; 077 int k = 1; 078 int l = this.basePos[1] + this.height; 079 int i1 = j - this.basePos[1]; 080 aint[0][0] = this.basePos[0]; 081 aint[0][1] = j; 082 aint[0][2] = this.basePos[2]; 083 aint[0][3] = l; 084 --j; 085 086 while (i1 >= 0) 087 { 088 int j1 = 0; 089 float f = this.layerSize(i1); 090 091 if (f < 0.0F) 092 { 093 --j; 094 --i1; 095 } 096 else 097 { 098 for (double d0 = 0.5D; j1 < i; ++j1) 099 { 100 double d1 = this.scaleWidth * (double)f * ((double)this.rand.nextFloat() + 0.328D); 101 double d2 = (double)this.rand.nextFloat() * 2.0D * Math.PI; 102 int k1 = MathHelper.floor_double(d1 * Math.sin(d2) + (double)this.basePos[0] + d0); 103 int l1 = MathHelper.floor_double(d1 * Math.cos(d2) + (double)this.basePos[2] + d0); 104 int[] aint1 = new int[] {k1, j, l1}; 105 int[] aint2 = new int[] {k1, j + this.leafDistanceLimit, l1}; 106 107 if (this.checkBlockLine(aint1, aint2) == -1) 108 { 109 int[] aint3 = new int[] {this.basePos[0], this.basePos[1], this.basePos[2]}; 110 double d3 = Math.sqrt(Math.pow((double)Math.abs(this.basePos[0] - aint1[0]), 2.0D) + Math.pow((double)Math.abs(this.basePos[2] - aint1[2]), 2.0D)); 111 double d4 = d3 * this.branchSlope; 112 113 if ((double)aint1[1] - d4 > (double)l) 114 { 115 aint3[1] = l; 116 } 117 else 118 { 119 aint3[1] = (int)((double)aint1[1] - d4); 120 } 121 122 if (this.checkBlockLine(aint3, aint1) == -1) 123 { 124 aint[k][0] = k1; 125 aint[k][1] = j; 126 aint[k][2] = l1; 127 aint[k][3] = aint3[1]; 128 ++k; 129 } 130 } 131 } 132 133 --j; 134 --i1; 135 } 136 } 137 138 this.leafNodes = new int[k][4]; 139 System.arraycopy(aint, 0, this.leafNodes, 0, k); 140 } 141 142 void genTreeLayer(int par1, int par2, int par3, float par4, byte par5, int par6) 143 { 144 int i1 = (int)((double)par4 + 0.618D); 145 byte b1 = otherCoordPairs[par5]; 146 byte b2 = otherCoordPairs[par5 + 3]; 147 int[] aint = new int[] {par1, par2, par3}; 148 int[] aint1 = new int[] {0, 0, 0}; 149 int j1 = -i1; 150 int k1 = -i1; 151 152 for (aint1[par5] = aint[par5]; j1 <= i1; ++j1) 153 { 154 aint1[b1] = aint[b1] + j1; 155 k1 = -i1; 156 157 while (k1 <= i1) 158 { 159 double d0 = Math.pow((double)Math.abs(j1) + 0.5D, 2.0D) + Math.pow((double)Math.abs(k1) + 0.5D, 2.0D); 160 161 if (d0 > (double)(par4 * par4)) 162 { 163 ++k1; 164 } 165 else 166 { 167 aint1[b2] = aint[b2] + k1; 168 int l1 = this.worldObj.getBlockId(aint1[0], aint1[1], aint1[2]); 169 170 if (l1 != 0 && l1 != Block.leaves.blockID) 171 { 172 ++k1; 173 } 174 else 175 { 176 this.setBlockAndMetadata(this.worldObj, aint1[0], aint1[1], aint1[2], par6, 0); 177 ++k1; 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 f = (float)this.heightLimit / 2.0F; 196 float f1 = (float)this.heightLimit / 2.0F - (float)par1; 197 float f2; 198 199 if (f1 == 0.0F) 200 { 201 f2 = f; 202 } 203 else if (Math.abs(f1) >= f) 204 { 205 f2 = 0.0F; 206 } 207 else 208 { 209 f2 = (float)Math.sqrt(Math.pow((double)Math.abs(f), 2.0D) - Math.pow((double)Math.abs(f1), 2.0D)); 210 } 211 212 f2 *= 0.5F; 213 return f2; 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 l = par2; 228 229 for (int i1 = par2 + this.leafDistanceLimit; l < i1; ++l) 230 { 231 float f = this.leafSize(l - par2); 232 this.genTreeLayer(par1, l, par3, f, (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[] aint2 = new int[] {0, 0, 0}; 242 byte b0 = 0; 243 byte b1; 244 245 for (b1 = 0; b0 < 3; ++b0) 246 { 247 aint2[b0] = par2ArrayOfInteger[b0] - par1ArrayOfInteger[b0]; 248 249 if (Math.abs(aint2[b0]) > Math.abs(aint2[b1])) 250 { 251 b1 = b0; 252 } 253 } 254 255 if (aint2[b1] != 0) 256 { 257 byte b2 = otherCoordPairs[b1]; 258 byte b3 = otherCoordPairs[b1 + 3]; 259 byte b4; 260 261 if (aint2[b1] > 0) 262 { 263 b4 = 1; 264 } 265 else 266 { 267 b4 = -1; 268 } 269 270 double d0 = (double)aint2[b2] / (double)aint2[b1]; 271 double d1 = (double)aint2[b3] / (double)aint2[b1]; 272 int[] aint3 = new int[] {0, 0, 0}; 273 int j = 0; 274 275 for (int k = aint2[b1] + b4; j != k; j += b4) 276 { 277 aint3[b1] = MathHelper.floor_double((double)(par1ArrayOfInteger[b1] + j) + 0.5D); 278 aint3[b2] = MathHelper.floor_double((double)par1ArrayOfInteger[b2] + (double)j * d0 + 0.5D); 279 aint3[b3] = MathHelper.floor_double((double)par1ArrayOfInteger[b3] + (double)j * d1 + 0.5D); 280 byte b5 = 0; 281 int l = Math.abs(aint3[0] - par1ArrayOfInteger[0]); 282 int i1 = Math.abs(aint3[2] - par1ArrayOfInteger[2]); 283 int j1 = Math.max(l, i1); 284 285 if (j1 > 0) 286 { 287 if (l == j1) 288 { 289 b5 = 4; 290 } 291 else if (i1 == j1) 292 { 293 b5 = 8; 294 } 295 } 296 297 this.setBlockAndMetadata(this.worldObj, aint3[0], aint3[1], aint3[2], par3, b5); 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 i = 0; 308 309 for (int j = this.leafNodes.length; i < j; ++i) 310 { 311 int k = this.leafNodes[i][0]; 312 int l = this.leafNodes[i][1]; 313 int i1 = this.leafNodes[i][2]; 314 this.generateLeafNode(k, l, i1); 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 i = this.basePos[0]; 333 int j = this.basePos[1]; 334 int k = this.basePos[1] + this.height; 335 int l = this.basePos[2]; 336 int[] aint = new int[] {i, j, l}; 337 int[] aint1 = new int[] {i, k, l}; 338 this.placeBlockLine(aint, aint1, Block.wood.blockID); 339 340 if (this.trunkSize == 2) 341 { 342 ++aint[0]; 343 ++aint1[0]; 344 this.placeBlockLine(aint, aint1, Block.wood.blockID); 345 ++aint[2]; 346 ++aint1[2]; 347 this.placeBlockLine(aint, aint1, Block.wood.blockID); 348 aint[0] += -1; 349 aint1[0] += -1; 350 this.placeBlockLine(aint, aint1, 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 i = 0; 360 int j = this.leafNodes.length; 361 362 for (int[] aint = new int[] {this.basePos[0], this.basePos[1], this.basePos[2]}; i < j; ++i) 363 { 364 int[] aint1 = this.leafNodes[i]; 365 int[] aint2 = new int[] {aint1[0], aint1[1], aint1[2]}; 366 aint[1] = aint1[3]; 367 int k = aint[1] - this.basePos[1]; 368 369 if (this.leafNodeNeedsBase(k)) 370 { 371 this.placeBlockLine(aint, aint2, (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[] aint2 = new int[] {0, 0, 0}; 383 byte b0 = 0; 384 byte b1; 385 386 for (b1 = 0; b0 < 3; ++b0) 387 { 388 aint2[b0] = par2ArrayOfInteger[b0] - par1ArrayOfInteger[b0]; 389 390 if (Math.abs(aint2[b0]) > Math.abs(aint2[b1])) 391 { 392 b1 = b0; 393 } 394 } 395 396 if (aint2[b1] == 0) 397 { 398 return -1; 399 } 400 else 401 { 402 byte b2 = otherCoordPairs[b1]; 403 byte b3 = otherCoordPairs[b1 + 3]; 404 byte b4; 405 406 if (aint2[b1] > 0) 407 { 408 b4 = 1; 409 } 410 else 411 { 412 b4 = -1; 413 } 414 415 double d0 = (double)aint2[b2] / (double)aint2[b1]; 416 double d1 = (double)aint2[b3] / (double)aint2[b1]; 417 int[] aint3 = new int[] {0, 0, 0}; 418 int i = 0; 419 int j; 420 421 for (j = aint2[b1] + b4; i != j; i += b4) 422 { 423 aint3[b1] = par1ArrayOfInteger[b1] + i; 424 aint3[b2] = MathHelper.floor_double((double)par1ArrayOfInteger[b2] + (double)i * d0); 425 aint3[b3] = MathHelper.floor_double((double)par1ArrayOfInteger[b3] + (double)i * d1); 426 int k = this.worldObj.getBlockId(aint3[0], aint3[1], aint3[2]); 427 428 if (k != 0 && k != Block.leaves.blockID) 429 { 430 break; 431 } 432 } 433 434 return i == j ? -1 : Math.abs(i); 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[] aint = new int[] {this.basePos[0], this.basePos[1], this.basePos[2]}; 445 int[] aint1 = new int[] {this.basePos[0], this.basePos[1] + this.heightLimit - 1, this.basePos[2]}; 446 int i = this.worldObj.getBlockId(this.basePos[0], this.basePos[1] - 1, this.basePos[2]); 447 448 Block soil = Block.blocksList[i]; 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 j = this.checkBlockLine(aint, aint1); 457 458 if (j == -1) 459 { 460 return true; 461 } 462 else if (j < 6) 463 { 464 return false; 465 } 466 else 467 { 468 this.heightLimit = j; 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 l = par2Random.nextLong(); 494 this.rand.setSeed(l); 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}