    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}