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