Proper big trees

This commit is contained in:
Matthias Schiffer 2014-05-28 02:36:56 +02:00
parent a5803b1320
commit 0b3e9e6ffe
4 changed files with 454 additions and 24 deletions

View file

@ -13,11 +13,13 @@ import net.minecraft.item.ItemStack;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.gen.feature.WorldGenAbstractTree; import net.minecraft.world.gen.feature.WorldGenAbstractTree;
import net.minecraft.world.gen.feature.WorldGenBigTree;
import net.minecraft.world.gen.feature.WorldGenTrees; import net.minecraft.world.gen.feature.WorldGenTrees;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.terraingen.BiomeEvent.CreateDecorator; import net.minecraftforge.event.terraingen.BiomeEvent.CreateDecorator;
import net.universe_factory.minecraft.test.generic.GenericCrops; import net.universe_factory.minecraft.test.generic.GenericCrops;
import net.universe_factory.minecraft.test.generic.GenericWood; import net.universe_factory.minecraft.test.generic.GenericWood;
import net.universe_factory.minecraft.test.generic.WorldGenGenericBigTree;
import net.universe_factory.minecraft.test.generic.WorldGenGenericTrees; import net.universe_factory.minecraft.test.generic.WorldGenGenericTrees;
import net.universe_factory.minecraft.test.world.ExtensibleBiomeDecorator; import net.universe_factory.minecraft.test.world.ExtensibleBiomeDecorator;
import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod;
@ -67,8 +69,8 @@ public class Test {
} }
}; };
WorldGenGenericTrees cherryTreeGenerator = WorldGenGenericTrees.newInstance(false, false, cherryTree); WorldGenGenericTrees cherryTreeGenerator = new WorldGenGenericTrees(false, cherryTree);
WorldGenGenericTrees cherryTreeGeneratorBig = WorldGenGenericTrees.newInstance(false, true, cherryTree); WorldGenGenericBigTree cherryTreeGeneratorBig = new WorldGenGenericBigTree(false, cherryTree);
public static final GenericCrops saladField = new GenericCrops() { public static final GenericCrops saladField = new GenericCrops() {
@Override @Override
@ -141,12 +143,13 @@ public class Test {
@Override @Override
public Entry<WorldGenAbstractTree, Float> replaceTree(World world, BiomeGenBase biome, Random random, int x, int y, int z, public Entry<WorldGenAbstractTree, Float> replaceTree(World world, BiomeGenBase biome, Random random, int x, int y, int z,
WorldGenAbstractTree orig) { WorldGenAbstractTree orig) {
if (!(orig instanceof WorldGenTrees) || biome.getTempCategory() != BiomeGenBase.TempCategory.MEDIUM) if (!(orig instanceof WorldGenTrees || orig instanceof WorldGenBigTree)
|| biome.getTempCategory() != BiomeGenBase.TempCategory.MEDIUM)
return null; return null;
WorldGenAbstractTree generator = (random.nextInt(3) == 0) ? cherryTreeGenerator : cherryTreeGeneratorBig; WorldGenAbstractTree generator = (random.nextInt(3) == 0) ? cherryTreeGeneratorBig : cherryTreeGenerator;
return new AbstractMap.SimpleEntry(generator, 0.5f); return new AbstractMap.SimpleEntry(generator, 0.3f);
} }
}); });
} }

View file

@ -229,8 +229,8 @@ public abstract class GenericWood {
} }
private class Sapling extends BlockSapling { private class Sapling extends BlockSapling {
WorldGenerator generator = WorldGenGenericTrees.newInstance(true, false, GenericWood.this); WorldGenerator generator = new WorldGenGenericTrees(true, GenericWood.this);
WorldGenerator generatorBig = WorldGenGenericTrees.newInstance(true, true, GenericWood.this); WorldGenerator generatorBig = new WorldGenGenericBigTree(true, GenericWood.this);
public Sapling() { public Sapling() {
setBlockName("sapling" + getName()); setBlockName("sapling" + getName());

View file

@ -0,0 +1,442 @@
package net.universe_factory.minecraft.test.generic;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockSapling;
import net.minecraft.init.Blocks;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.WorldGenAbstractTree;
import net.minecraftforge.common.util.ForgeDirection;
public class WorldGenGenericBigTree extends WorldGenAbstractTree {
private final GenericWood genericWood;
public WorldGenGenericBigTree(boolean notify, GenericWood genericWood) {
super(notify);
this.genericWood = genericWood;
}
/**
* Contains three sets of two values that provide complimentary indices
* for a given 'major' index - 1 and 2 for 0, 0 and 2 for 1, and 0 and 1
* for 2.
*/
static final byte[] otherCoordPairs = new byte[] { (byte) 2, (byte) 0, (byte) 0, (byte) 1, (byte) 2, (byte) 1 };
/** random seed for GenBigTree */
Random rand = new Random();
/** Reference to the World object. */
World worldObj;
int[] basePos = new int[] { 0, 0, 0 };
int heightLimit;
int height;
double heightAttenuation = 0.618D;
double branchDensity = 1.0D;
double branchSlope = 0.381D;
double scaleWidth = 1.0D;
double leafDensity = 1.0D;
/**
* Currently always 1, can be set to 2 in the class constructor to
* generate a double-sized tree trunk for big trees.
*/
int trunkSize = 1;
/**
* Sets the limit of the random value used to initialize the height
* limit.
*/
int heightLimitLimit = 12;
/**
* Sets the distance limit for how far away the generator will populate
* leaves from the base leaf node.
*/
int leafDistanceLimit = 4;
/** Contains a list of a points at which to generate groups of leaves. */
int[][] leafNodes;
/**
* Generates a list of leaf nodes for the tree, to be populated by
* generateLeaves.
*/
void generateLeafNodeList() {
this.height = (int) ((double) this.heightLimit * this.heightAttenuation);
if (this.height >= this.heightLimit) {
this.height = this.heightLimit - 1;
}
int i = (int) (1.382D + Math.pow(this.leafDensity * (double) this.heightLimit / 13.0D, 2.0D));
if (i < 1) {
i = 1;
}
int[][] aint = new int[i * this.heightLimit][4];
int j = this.basePos[1] + this.heightLimit - this.leafDistanceLimit;
int k = 1;
int l = this.basePos[1] + this.height;
int i1 = j - this.basePos[1];
aint[0][0] = this.basePos[0];
aint[0][1] = j;
aint[0][2] = this.basePos[2];
aint[0][3] = l;
--j;
while (i1 >= 0) {
int j1 = 0;
float f = this.layerSize(i1);
if (f < 0.0F) {
--j;
--i1;
} else {
for (double d0 = 0.5D; j1 < i; ++j1) {
double d1 = this.scaleWidth * (double) f * ((double) this.rand.nextFloat() + 0.328D);
double d2 = (double) this.rand.nextFloat() * 2.0D * Math.PI;
int k1 = MathHelper.floor_double(d1 * Math.sin(d2) + (double) this.basePos[0] + d0);
int l1 = MathHelper.floor_double(d1 * Math.cos(d2) + (double) this.basePos[2] + d0);
int[] aint1 = new int[] { k1, j, l1 };
int[] aint2 = new int[] { k1, j + this.leafDistanceLimit, l1 };
if (this.checkBlockLine(aint1, aint2) == -1) {
int[] aint3 = new int[] { this.basePos[0], this.basePos[1], this.basePos[2] };
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));
double d4 = d3 * this.branchSlope;
if ((double) aint1[1] - d4 > (double) l) {
aint3[1] = l;
} else {
aint3[1] = (int) ((double) aint1[1] - d4);
}
if (this.checkBlockLine(aint3, aint1) == -1) {
aint[k][0] = k1;
aint[k][1] = j;
aint[k][2] = l1;
aint[k][3] = aint3[1];
++k;
}
}
}
--j;
--i1;
}
}
this.leafNodes = new int[k][4];
System.arraycopy(aint, 0, this.leafNodes, 0, k);
}
void func_150529_a(int p_150529_1_, int p_150529_2_, int p_150529_3_, float p_150529_4_, byte p_150529_5_, Block p_150529_6_) {
int l = (int) ((double) p_150529_4_ + 0.618D);
byte b1 = otherCoordPairs[p_150529_5_];
byte b2 = otherCoordPairs[p_150529_5_ + 3];
int[] aint = new int[] { p_150529_1_, p_150529_2_, p_150529_3_ };
int[] aint1 = new int[] { 0, 0, 0 };
int i1 = -l;
int j1 = -l;
for (aint1[p_150529_5_] = aint[p_150529_5_]; i1 <= l; ++i1) {
aint1[b1] = aint[b1] + i1;
j1 = -l;
while (j1 <= l) {
double d0 = Math.pow((double) Math.abs(i1) + 0.5D, 2.0D) + Math.pow((double) Math.abs(j1) + 0.5D, 2.0D);
if (d0 > (double) (p_150529_4_ * p_150529_4_)) {
++j1;
} else {
aint1[b2] = aint[b2] + j1;
Block block1 = this.worldObj.getBlock(aint1[0], aint1[1], aint1[2]);
if (!block1.isAir(worldObj, aint1[0], aint1[1], aint1[2]) && !block1.isLeaves(worldObj, aint1[0], aint1[1], aint1[2])) {
++j1;
} else {
this.setBlockAndNotifyAdequately(this.worldObj, aint1[0], aint1[1], aint1[2], p_150529_6_, 0);
++j1;
}
}
}
}
}
/**
* Gets the rough size of a layer of the tree.
*/
float layerSize(int par1) {
if ((double) par1 < (double) ((float) this.heightLimit) * 0.3D) {
return -1.618F;
} else {
float f = (float) this.heightLimit / 2.0F;
float f1 = (float) this.heightLimit / 2.0F - (float) par1;
float f2;
if (f1 == 0.0F) {
f2 = f;
} else if (Math.abs(f1) >= f) {
f2 = 0.0F;
} else {
f2 = (float) Math.sqrt(Math.pow((double) Math.abs(f), 2.0D) - Math.pow((double) Math.abs(f1), 2.0D));
}
f2 *= 0.5F;
return f2;
}
}
float leafSize(int par1) {
return par1 >= 0 && par1 < this.leafDistanceLimit ? (par1 != 0 && par1 != this.leafDistanceLimit - 1 ? 3.0F : 2.0F) : -1.0F;
}
/**
* Generates the leaves surrounding an individual entry in the leafNodes
* list.
*/
void generateLeafNode(int par1, int par2, int par3) {
int l = par2;
for (int i1 = par2 + this.leafDistanceLimit; l < i1; ++l) {
float f = this.leafSize(l - par2);
this.func_150529_a(par1, l, par3, f, (byte) 1, genericWood.leaves);
}
}
void func_150530_a(int[] p_150530_1_, int[] p_150530_2_, Block p_150530_3_) {
int[] aint2 = new int[] { 0, 0, 0 };
byte b0 = 0;
byte b1;
for (b1 = 0; b0 < 3; ++b0) {
aint2[b0] = p_150530_2_[b0] - p_150530_1_[b0];
if (Math.abs(aint2[b0]) > Math.abs(aint2[b1])) {
b1 = b0;
}
}
if (aint2[b1] != 0) {
byte b2 = otherCoordPairs[b1];
byte b3 = otherCoordPairs[b1 + 3];
byte b4;
if (aint2[b1] > 0) {
b4 = 1;
} else {
b4 = -1;
}
double d0 = (double) aint2[b2] / (double) aint2[b1];
double d1 = (double) aint2[b3] / (double) aint2[b1];
int[] aint3 = new int[] { 0, 0, 0 };
int i = 0;
for (int j = aint2[b1] + b4; i != j; i += b4) {
aint3[b1] = MathHelper.floor_double((double) (p_150530_1_[b1] + i) + 0.5D);
aint3[b2] = MathHelper.floor_double((double) p_150530_1_[b2] + (double) i * d0 + 0.5D);
aint3[b3] = MathHelper.floor_double((double) p_150530_1_[b3] + (double) i * d1 + 0.5D);
byte b5 = 0;
int k = Math.abs(aint3[0] - p_150530_1_[0]);
int l = Math.abs(aint3[2] - p_150530_1_[2]);
int i1 = Math.max(k, l);
if (i1 > 0) {
if (k == i1) {
b5 = 4;
} else if (l == i1) {
b5 = 8;
}
}
this.setBlockAndNotifyAdequately(this.worldObj, aint3[0], aint3[1], aint3[2], p_150530_3_, b5);
}
}
}
/**
* Generates the leaf portion of the tree as specified by the leafNodes
* list.
*/
void generateLeaves() {
int i = 0;
for (int j = this.leafNodes.length; i < j; ++i) {
int k = this.leafNodes[i][0];
int l = this.leafNodes[i][1];
int i1 = this.leafNodes[i][2];
this.generateLeafNode(k, l, i1);
}
}
/**
* Indicates whether or not a leaf node requires additional wood to be
* added to preserve integrity.
*/
boolean leafNodeNeedsBase(int par1) {
return (double) par1 >= (double) this.heightLimit * 0.2D;
}
/**
* Places the trunk for the big tree that is being generated. Able to
* generate double-sized trunks by changing a field that is always 1 to
* 2.
*/
void generateTrunk() {
int i = this.basePos[0];
int j = this.basePos[1];
int k = this.basePos[1] + this.height;
int l = this.basePos[2];
int[] aint = new int[] { i, j, l };
int[] aint1 = new int[] { i, k, l };
this.func_150530_a(aint, aint1, genericWood.log);
if (this.trunkSize == 2) {
++aint[0];
++aint1[0];
this.func_150530_a(aint, aint1, genericWood.log);
++aint[2];
++aint1[2];
this.func_150530_a(aint, aint1, genericWood.log);
aint[0] += -1;
aint1[0] += -1;
this.func_150530_a(aint, aint1, genericWood.log);
}
}
/**
* Generates additional wood blocks to fill out the bases of different
* leaf nodes that would otherwise degrade.
*/
void generateLeafNodeBases() {
int i = 0;
int j = this.leafNodes.length;
for (int[] aint = new int[] { this.basePos[0], this.basePos[1], this.basePos[2] }; i < j; ++i) {
int[] aint1 = this.leafNodes[i];
int[] aint2 = new int[] { aint1[0], aint1[1], aint1[2] };
aint[1] = aint1[3];
int k = aint[1] - this.basePos[1];
if (this.leafNodeNeedsBase(k)) {
this.func_150530_a(aint, aint2, genericWood.log);
}
}
}
/**
* Checks a line of blocks in the world from the first coordinate to
* triplet to the second, returning the distance (in blocks) before a
* non-air, non-leaf block is encountered and/or the end is encountered.
*/
int checkBlockLine(int[] par1ArrayOfInteger, int[] par2ArrayOfInteger) {
int[] aint2 = new int[] { 0, 0, 0 };
byte b0 = 0;
byte b1;
for (b1 = 0; b0 < 3; ++b0) {
aint2[b0] = par2ArrayOfInteger[b0] - par1ArrayOfInteger[b0];
if (Math.abs(aint2[b0]) > Math.abs(aint2[b1])) {
b1 = b0;
}
}
if (aint2[b1] == 0) {
return -1;
} else {
byte b2 = otherCoordPairs[b1];
byte b3 = otherCoordPairs[b1 + 3];
byte b4;
if (aint2[b1] > 0) {
b4 = 1;
} else {
b4 = -1;
}
double d0 = (double) aint2[b2] / (double) aint2[b1];
double d1 = (double) aint2[b3] / (double) aint2[b1];
int[] aint3 = new int[] { 0, 0, 0 };
int i = 0;
int j;
for (j = aint2[b1] + b4; i != j; i += b4) {
aint3[b1] = par1ArrayOfInteger[b1] + i;
aint3[b2] = MathHelper.floor_double((double) par1ArrayOfInteger[b2] + (double) i * d0);
aint3[b3] = MathHelper.floor_double((double) par1ArrayOfInteger[b3] + (double) i * d1);
Block block = this.worldObj.getBlock(aint3[0], aint3[1], aint3[2]);
if (!this.isReplaceable(worldObj, aint3[0], aint3[1], aint3[2])) {
break;
}
}
return i == j ? -1 : Math.abs(i);
}
}
/**
* Returns a boolean indicating whether or not the current location for
* the tree, spanning basePos to to the height limit, is valid.
*/
boolean validTreeLocation() {
int[] aint = new int[] { this.basePos[0], this.basePos[1], this.basePos[2] };
int[] aint1 = new int[] { this.basePos[0], this.basePos[1] + this.heightLimit - 1, this.basePos[2] };
Block block = this.worldObj.getBlock(this.basePos[0], this.basePos[1] - 1, this.basePos[2]);
boolean isSoil = block.canSustainPlant(worldObj, basePos[0], basePos[1] - 1, basePos[2], ForgeDirection.UP, (BlockSapling) Blocks.sapling);
if (!isSoil) {
return false;
} else {
int i = this.checkBlockLine(aint, aint1);
if (i == -1) {
return true;
} else if (i < 6) {
return false;
} else {
this.heightLimit = i;
return true;
}
}
}
/**
* Rescales the generator settings, only used in WorldGenBigTree
*/
public void setScale(double par1, double par3, double par5) {
this.heightLimitLimit = (int) (par1 * 12.0D);
if (par1 > 0.5D) {
this.leafDistanceLimit = 5;
}
this.scaleWidth = par3;
this.leafDensity = par5;
}
public boolean generate(World par1World, Random par2Random, int par3, int par4, int par5) {
this.worldObj = par1World;
long l = par2Random.nextLong();
this.rand.setSeed(l);
this.basePos[0] = par3;
this.basePos[1] = par4;
this.basePos[2] = par5;
if (this.heightLimit == 0) {
this.heightLimit = 5 + this.rand.nextInt(this.heightLimitLimit);
}
if (!this.validTreeLocation()) {
return false;
} else {
this.generateLeafNodeList();
this.generateLeaves();
this.generateTrunk();
this.generateLeafNodeBases();
return true;
}
}
}

View file

@ -9,33 +9,18 @@ import net.minecraftforge.common.util.ForgeDirection;
public class WorldGenGenericTrees extends WorldGenAbstractTree { public class WorldGenGenericTrees extends WorldGenAbstractTree {
private final GenericWood genericWood; private final GenericWood genericWood;
private final int minHeight;
private final boolean big;
public static WorldGenGenericTrees newInstance(boolean notify, boolean big, GenericWood genericWood) { public WorldGenGenericTrees(boolean notify, GenericWood genericWood) {
return new WorldGenGenericTrees(notify, big, genericWood, genericWood.getMinTreeHeight());
}
public static WorldGenGenericTrees newForestInstance(boolean notify, boolean big, GenericWood genericWood) {
return new WorldGenGenericTrees(notify, big, genericWood, 5);
}
protected WorldGenGenericTrees(boolean notify, boolean big, GenericWood genericWood, int minHeight) {
super(notify); super(notify);
this.genericWood = genericWood; this.genericWood = genericWood;
this.minHeight = minHeight;
this.big = big;
} }
@Override @Override
public boolean generate(World world, Random random, int x, int y, int z) { public boolean generate(World world, Random random, int x, int y, int z) {
int height = random.nextInt(3) + minHeight; int height = random.nextInt(3) + genericWood.getMinTreeHeight();
boolean flag = true; boolean flag = true;
if (big)
height += random.nextInt(7);
if (y >= 1 && y + height + 1 <= 256) { if (y >= 1 && y + height + 1 <= 256) {
byte b0; byte b0;
int k1; int k1;