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