001package net.minecraft.world.gen.feature;
002
003import java.util.Random;
004import net.minecraft.block.Block;
005import net.minecraft.util.MathHelper;
006import net.minecraft.world.World;
007
008public 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}