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    }