package rtg.world.biome.deco;
import java.util.Random;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.gen.feature.WorldGenerator;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.Event;
import static net.minecraftforge.event.terraingen.DecorateBiomeEvent.Decorate.EventType.TREE;
import rtg.api.util.RandomUtil;
import rtg.api.util.WorldUtil;
import rtg.api.world.RTGWorld;
import rtg.event.terraingen.DecorateBiomeEventRTG;
import rtg.util.DecoUtil;
import rtg.world.biome.realistic.RealisticBiomeBase;
import rtg.world.gen.feature.tree.rtg.TreeRTG;
/**
* @author WhichOnesPink
*/
public class DecoTree extends DecoBase {
protected int loops;
protected float strengthFactorForLoops; // If set, this overrides and dynamically calculates 'loops' based on the strength parameter.
protected boolean strengthNoiseFactorForLoops; // If true, this overrides and dynamically calculates 'loops' based on (noise * strength)
protected boolean strengthNoiseFactorXForLoops; // If true, this overrides and dynamically calculates 'loops' based on (noise * X * strength)
protected TreeType treeType; // Enum for the various tree presets.
protected TreeRTG tree;
protected WorldGenerator worldGen;
protected DecoTree.Distribution distribution; // Parameter object for noise calculations.
protected TreeCondition treeCondition; // Enum for the various conditions/chances for tree gen.
protected float treeConditionNoise; // Only applies to a noise-related TreeCondition.
protected float treeConditionNoise2; // Only applies to a noise-related TreeCondition.
protected int treeConditionChance; // Only applies to a chance-related TreeCondition.
protected float treeConditionFloat; // Multi-purpose float.
protected int minY; // Lower height restriction.
protected int maxY; // Upper height restriction.
protected IBlockState logBlock;
protected IBlockState leavesBlock;
protected int minSize; // Min tree height (only used with certain tree presets)
protected int maxSize; // Max tree height (only used with certain tree presets)
protected int minTrunkSize; // Min tree height (only used with certain tree presets)
protected int maxTrunkSize; // Max tree height (only used with certain tree presets)
protected int minCrownSize; // Min tree height (only used with certain tree presets)
protected int maxCrownSize; // Max tree height (only used with certain tree presets)
protected boolean noLeaves;
protected Scatter scatter;
public DecoTree() {
super();
/**
* Default values.
* These can be overridden when configuring the Deco object in the realistic biome.
*/
this.setLoops(1);
this.setStrengthFactorForLoops(0f);
this.setStrengthNoiseFactorForLoops(false);
this.setStrengthNoiseFactorXForLoops(false);
this.setTreeType(TreeType.RTG_TREE);
this.tree = null;
this.worldGen = null;
this.setDistribution(new DecoTree.Distribution(100f, 5f, 0.8f));
this.setTreeCondition(TreeCondition.NOISE_GREATER_AND_RANDOM_CHANCE);
this.setTreeConditionNoise(0f);
this.setTreeConditionNoise2(0f);
this.setTreeConditionFloat(0f);
this.setTreeConditionChance(1);
this.setMinY(63); // No underwater trees by default.
this.setMaxY(230); // Sensible upper height limit by default.
this.setLogBlock(Blocks.LOG.getDefaultState());
this.setLeavesBlock(Blocks.LEAVES.getDefaultState());
this.setMinSize(2);
this.setMaxSize(4);
this.setMinTrunkSize(2);
this.setMaxTrunkSize(4);
this.setMinCrownSize(2);
this.setMaxCrownSize(4);
this.setNoLeaves(false);
this.setScatter(new Scatter(16, 0));
this.addDecoTypes(DecoType.TREE);
}
public DecoTree(DecoTree source) {
this();
this.setLoops(source.loops);
this.setStrengthFactorForLoops(source.strengthFactorForLoops);
this.setStrengthNoiseFactorForLoops(source.strengthNoiseFactorForLoops);
this.setStrengthNoiseFactorXForLoops(source.strengthNoiseFactorXForLoops);
this.setTreeType(source.treeType);
this.tree = source.tree;
this.worldGen = source.worldGen;
this.setDistribution(source.distribution);
this.setTreeCondition(source.treeCondition);
this.setTreeConditionNoise(source.treeConditionNoise);
this.setTreeConditionNoise2(source.treeConditionNoise2);
this.setTreeConditionFloat(source.treeConditionFloat);
this.setTreeConditionChance(source.treeConditionChance);
this.setMinY(source.minY);
this.setMaxY(source.maxY);
this.setLogBlock(source.logBlock);
this.setLeavesBlock(source.leavesBlock);
this.setMinSize(source.minSize);
this.setMaxSize(source.maxSize);
this.setMinTrunkSize(source.minTrunkSize);
this.setMaxTrunkSize(source.maxTrunkSize);
this.setMinCrownSize(source.minCrownSize);
this.setMaxCrownSize(source.maxCrownSize);
this.setNoLeaves(source.noLeaves);
this.setScatter(source.scatter);
}
public DecoTree(TreeRTG tree) {
this();
this.tree = tree;
this.setLogBlock(tree.getLogBlock());
this.setLeavesBlock(tree.getLeavesBlock());
this.setMinTrunkSize(tree.getMinTrunkSize());
this.setMaxTrunkSize(tree.getMaxTrunkSize());
this.setMinCrownSize(tree.getMinCrownSize());
this.setMaxCrownSize(tree.getMaxCrownSize());
this.setNoLeaves(tree.getNoLeaves());
}
public DecoTree(WorldGenerator worldGen) {
this();
this.worldGen = worldGen;
}
public boolean properlyDefined() {
if (this.treeType == TreeType.RTG_TREE) {
if (this.tree == null) {
return false;
}
}
return super.properlyDefined();
}
@Override
public void generate(RealisticBiomeBase biome, RTGWorld rtgWorld, Random rand, int worldX, int worldZ, float strength, float river, boolean hasPlacedVillageBlocks) {
if (this.allowed) {
/*
* Determine how many trees we're going to try to generate (loopCount).
* The actual number of trees that end up being generated could be *less* than this value,
* depending on environmental conditions.
*/
float noise = rtgWorld.simplex.noise2(worldX / this.distribution.noiseDivisor, worldZ / this.distribution.noiseDivisor) * this.distribution.noiseFactor + this.distribution.noiseAddend;
int loopCount = this.loops;
loopCount = (this.strengthFactorForLoops > 0f) ? (int) (this.strengthFactorForLoops * strength) : loopCount;
loopCount = (this.strengthNoiseFactorForLoops) ? (int) (noise * strength) : loopCount;
loopCount = (this.strengthNoiseFactorXForLoops) ? (int) (noise * this.strengthFactorForLoops * strength) : loopCount;
if (loopCount < 1) {
return;
}
// Now let's check the configs to see if we should increase/decrease this value.
DecoUtil decoUtil = new DecoUtil(this);
loopCount = decoUtil.calculateLoopCountFromTreeDensity(loopCount, biome);
if (loopCount < 1) {
return;
}
/*
* Since RTG posts a TREE event for each batch of trees it tries to generate (instead of one event per chunk),
* we post this custom event so that we can pass the number of trees RTG expects to generate in each batch.
*
* This provides more contextual information to mods like Recurrent Complex, which can use the info to better
* determine how to handle each batch of trees.
*
* Because the custom event extends DecorateBiomeEvent.Decorate, it still works with mods that don't need
* the additional context.
*/
DecorateBiomeEventRTG.DecorateRTG event = new DecorateBiomeEventRTG.DecorateRTG(
rtgWorld.world, rand, new BlockPos(worldX, 0, worldZ), TREE, loopCount
);
MinecraftForge.TERRAIN_GEN_BUS.post(event);
if (event.getResult() != Event.Result.DENY) {
loopCount = event.getModifiedAmount();
if (loopCount < 1) {
return;
}
WorldUtil worldUtil = new WorldUtil(rtgWorld.world);
DecoBase.tweakTreeLeaves(this, false, true);
for (int i = 0; i < loopCount; i++) {
int intX = scatter.get(rand, worldX); // + 8;
int intZ = scatter.get(rand, worldZ); // + 8;
int intY = rtgWorld.world.getHeight(new BlockPos(intX, 0, intZ)).getY();
//Logger.info("noise = %f", noise);
if (intY <= this.maxY && intY >= this.minY && isValidTreeCondition(noise, rand, strength)) {
// If we're in a village, check to make sure the tree has extra room to grow to avoid corrupting the village.
if (hasPlacedVillageBlocks) {
if (!worldUtil.isSurroundedByBlock(Blocks.AIR.getDefaultState(), 2, WorldUtil.SurroundCheckType.CARDINAL, rand, intX, intY, intZ)) {
return;
}
}
switch (this.treeType) {
case RTG_TREE:
//this.setLogBlock(strength < 0.2f ? BlockUtil.getStateLog(2) : this.logBlock);
this.tree.setLogBlock(this.logBlock);
this.tree.setLeavesBlock(this.leavesBlock);
this.tree.setTrunkSize(RandomUtil.getRandomInt(rand, this.minTrunkSize, this.maxTrunkSize));
this.tree.setCrownSize(RandomUtil.getRandomInt(rand, this.minCrownSize, this.maxCrownSize));
this.tree.setNoLeaves(this.noLeaves);
this.tree.generate(rtgWorld.world, rand, new BlockPos(intX, intY, intZ));
break;
case WORLDGEN:
WorldGenerator worldgenerator = this.worldGen;
worldgenerator.generate(rtgWorld.world, rand, new BlockPos(intX, intY, intZ));
break;
default:
break;
}
}
}
}
else {
//Logger.info("Tree generation was cancelled.");
}
}
}
public boolean isValidTreeCondition(float noise, Random rand, float strength) {
switch (this.treeCondition) {
case ALWAYS_GENERATE:
return true;
case NOISE_GREATER_AND_RANDOM_CHANCE:
return (noise > this.treeConditionNoise && rand.nextInt(this.treeConditionChance) == 0);
case NOISE_LESSER_AND_RANDOM_CHANCE:
return (noise < this.treeConditionNoise && rand.nextInt(this.treeConditionChance) == 0);
case NOISE_BETWEEN_AND_RANDOM_CHANCE:
return (noise > this.treeConditionNoise && noise < this.treeConditionNoise2 && rand.nextInt(this.treeConditionChance) == 0);
case RANDOM_CHANCE:
return rand.nextInt(this.treeConditionChance) == 0;
case X_DIVIDED_BY_STRENGTH:
return rand.nextInt((int) (this.treeConditionFloat / strength)) == 0;
default:
return false;
}
}
public enum TreeType {
RTG_TREE,
WORLDGEN;
}
public enum TreeCondition {
ALWAYS_GENERATE,
NOISE_GREATER_AND_RANDOM_CHANCE,
NOISE_LESSER_AND_RANDOM_CHANCE,
NOISE_BETWEEN_AND_RANDOM_CHANCE,
RANDOM_CHANCE,
X_DIVIDED_BY_STRENGTH;
}
public static class Scatter {
int bound;
int reach;
public Scatter(int bound, int reach) {
if (bound < 1) {
throw new RuntimeException("Scatter bound must be greater than 0.");
};
this.bound = bound;
this.reach = reach;
}
public int get(Random rand, int coord) {
return coord + rand.nextInt(bound) + reach;
}
}
/**
* Parameter object for noise calculations.
* <p>
* simplex.noise2(chunkX / noiseDivisor, chunkZ / noiseDivisor) * noiseFactor + noiseAddend;
*
* @author WhichOnesPink
* @author Zeno410
*/
public static class Distribution {
protected float noiseDivisor;
protected float noiseFactor;
protected float noiseAddend;
public Distribution(float noiseDivisor, float noiseFactor, float noiseAddend) {
this.noiseDivisor = noiseDivisor;
this.noiseFactor = noiseFactor;
this.noiseAddend = noiseAddend;
}
public float getNoiseDivisor() {
return noiseDivisor;
}
public Distribution setNoiseDivisor(float noiseDivisor) {
this.noiseDivisor = noiseDivisor;
return this;
}
public float getNoiseFactor() {
return noiseFactor;
}
public Distribution setNoiseFactor(float noiseFactor) {
this.noiseFactor = noiseFactor;
return this;
}
public float getNoiseAddend() {
return noiseAddend;
}
public Distribution setNoiseAddend(float noiseAddend) {
this.noiseAddend = noiseAddend;
return this;
}
}
public int getLoops() {
return loops;
}
public DecoTree setLoops(int loops) {
this.loops = loops;
return this;
}
public float getStrengthFactorForLoops() {
return strengthFactorForLoops;
}
public DecoTree setStrengthFactorForLoops(float strengthFactorForLoops) {
this.strengthFactorForLoops = strengthFactorForLoops;
return this;
}
public boolean isStrengthNoiseFactorForLoops() {
return strengthNoiseFactorForLoops;
}
public DecoTree setStrengthNoiseFactorForLoops(boolean strengthNoiseFactorForLoops) {
this.strengthNoiseFactorForLoops = strengthNoiseFactorForLoops;
return this;
}
public boolean isStrengthNoiseFactorXForLoops() {
return strengthNoiseFactorXForLoops;
}
public DecoTree setStrengthNoiseFactorXForLoops(boolean strengthNoiseFactorXForLoops) {
this.strengthNoiseFactorXForLoops = strengthNoiseFactorXForLoops;
return this;
}
public TreeType getTreeType() {
return treeType;
}
public DecoTree setTreeType(TreeType treeType) {
this.treeType = treeType;
return this;
}
public TreeRTG getTree() {
return tree;
}
public DecoTree setTree(TreeRTG tree) {
this.tree = tree;
return this;
}
public WorldGenerator getWorldGen() {
return worldGen;
}
public DecoTree setWorldGen(WorldGenerator worldGen) {
this.worldGen = worldGen;
return this;
}
public Distribution getDistribution() {
return distribution;
}
public DecoTree setDistribution(Distribution distribution) {
this.distribution = distribution;
return this;
}
public TreeCondition getTreeCondition() {
return treeCondition;
}
public DecoTree setTreeCondition(TreeCondition treeCondition) {
this.treeCondition = treeCondition;
return this;
}
public float getTreeConditionNoise() {
return treeConditionNoise;
}
public DecoTree setTreeConditionNoise(float treeConditionNoise) {
this.treeConditionNoise = treeConditionNoise;
return this;
}
public float getTreeConditionNoise2() {
return treeConditionNoise2;
}
public DecoTree setTreeConditionNoise2(float treeConditionNoise2) {
this.treeConditionNoise2 = treeConditionNoise2;
return this;
}
public int getTreeConditionChance() {
return treeConditionChance;
}
public DecoTree setTreeConditionChance(int treeConditionChance) {
this.treeConditionChance = treeConditionChance;
return this;
}
public float getTreeConditionFloat() {
return treeConditionFloat;
}
public DecoTree setTreeConditionFloat(float treeConditionFloat) {
this.treeConditionFloat = treeConditionFloat;
return this;
}
public int getMinY() {
return minY;
}
public DecoTree setMinY(int minY) {
this.minY = minY;
return this;
}
public int getMaxY() {
return maxY;
}
public DecoTree setMaxY(int maxY) {
this.maxY = maxY;
return this;
}
public IBlockState getLogBlock() {
return logBlock;
}
public DecoTree setLogBlock(IBlockState logBlock) {
this.logBlock = logBlock;
return this;
}
public IBlockState getLeavesBlock() {
return leavesBlock;
}
public DecoTree setLeavesBlock(IBlockState leavesBlock) {
this.leavesBlock = leavesBlock;
return this;
}
public int getMinSize() {
return minSize;
}
public DecoTree setMinSize(int minSize) {
this.minSize = minSize;
return this;
}
public int getMaxSize() {
return maxSize;
}
public DecoTree setMaxSize(int maxSize) {
this.maxSize = maxSize;
return this;
}
public int getMinTrunkSize() {
return minTrunkSize;
}
public DecoTree setMinTrunkSize(int minTrunkSize) {
this.minTrunkSize = minTrunkSize;
return this;
}
public int getMaxTrunkSize() {
return maxTrunkSize;
}
public DecoTree setMaxTrunkSize(int maxTrunkSize) {
this.maxTrunkSize = maxTrunkSize;
return this;
}
public int getMinCrownSize() {
return minCrownSize;
}
public DecoTree setMinCrownSize(int minCrownSize) {
this.minCrownSize = minCrownSize;
return this;
}
public int getMaxCrownSize() {
return maxCrownSize;
}
public DecoTree setMaxCrownSize(int maxCrownSize) {
this.maxCrownSize = maxCrownSize;
return this;
}
public boolean isNoLeaves() {
return noLeaves;
}
public DecoTree setNoLeaves(boolean noLeaves) {
this.noLeaves = noLeaves;
return this;
}
public Scatter getScatter() {
return scatter;
}
public DecoTree setScatter(Scatter scatter) {
this.scatter = scatter;
return this;
}
}