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}