diff --git a/src/main/java/biomesoplenty/api/biome/BOPBiomes.java b/src/main/java/biomesoplenty/api/biome/BOPBiomes.java index 31bf1affb..564cfc46d 100644 --- a/src/main/java/biomesoplenty/api/biome/BOPBiomes.java +++ b/src/main/java/biomesoplenty/api/biome/BOPBiomes.java @@ -17,4 +17,5 @@ public class BOPBiomes public static Optional alps = Optional.absent(); public static Optional arctic = Optional.absent(); public static Optional crag = Optional.absent(); + public static Optional originValley = Optional.absent(); } diff --git a/src/main/java/biomesoplenty/api/biome/generation/CustomizableWeightedGenerator.java b/src/main/java/biomesoplenty/api/biome/generation/CustomizableWeightedGenerator.java new file mode 100644 index 000000000..d8f2bed3b --- /dev/null +++ b/src/main/java/biomesoplenty/api/biome/generation/CustomizableWeightedGenerator.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright 2015, the Biomes O' Plenty Team + * + * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. + * + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + ******************************************************************************/ + +package biomesoplenty.api.biome.generation; + +import java.util.Random; + +import net.minecraft.util.BlockPos; +import net.minecraft.util.WeightedRandom; +import net.minecraft.world.World; + +public abstract class CustomizableWeightedGenerator extends WeightedRandom.Item implements IGenerator +{ + private final String identifier; + private String name; + private GeneratorStage stage; + + protected CustomizableWeightedGenerator() + { + this(-1); + } + + protected CustomizableWeightedGenerator(int weight) + { + super(weight); + + this.identifier = GeneratorRegistry.getIdentifier((Class)this.getClass()); + this.stage = GeneratorStage.PARENT; + + if (this.identifier == null) + { + throw new RuntimeException("The identifier for " + this.getClass().getCanonicalName() + " cannot be null!"); + } + } + + //Scattering should be handled in GeneratorWeighted + @Override + public final void scatter(World world, Random random, BlockPos pos) {} + + @Override + public void setName(String name) + { + this.name = name; + } + + @Override + public void setStage(GeneratorStage stage) + { + this.stage = stage; + } + + @Override + public String getName() + { + return this.name; + } + + @Override + public GeneratorStage getStage() + { + return this.stage; + } + + @Override + public final String getIdentifier() + { + return this.identifier; + } +} diff --git a/src/main/java/biomesoplenty/api/biome/generation/GeneratorStage.java b/src/main/java/biomesoplenty/api/biome/generation/GeneratorStage.java index c71fb697c..49c1acf2a 100644 --- a/src/main/java/biomesoplenty/api/biome/generation/GeneratorStage.java +++ b/src/main/java/biomesoplenty/api/biome/generation/GeneratorStage.java @@ -47,7 +47,9 @@ public enum GeneratorStage @SerializedName("tree") TREE(Decorate.EventType.TREE), @SerializedName("post") - POST(null); + POST(null), + @SerializedName("parent") + PARENT(null); private Decorate.EventType decorateType; diff --git a/src/main/java/biomesoplenty/api/biome/generation/IGenerator.java b/src/main/java/biomesoplenty/api/biome/generation/IGenerator.java index b9746d5df..358a5dfd4 100644 --- a/src/main/java/biomesoplenty/api/biome/generation/IGenerator.java +++ b/src/main/java/biomesoplenty/api/biome/generation/IGenerator.java @@ -20,7 +20,7 @@ import com.google.gson.JsonSerializationContext; public interface IGenerator { public void scatter(World world, Random random, BlockPos pos); - public void generate(World world, Random random, BlockPos pos); + public boolean generate(World world, Random random, BlockPos pos); public void setName(String name); public void setStage(GeneratorStage stage); diff --git a/src/main/java/biomesoplenty/common/biome/overworld/BiomeGenCrag.java b/src/main/java/biomesoplenty/common/biome/overworld/BiomeGenCrag.java index 41a851556..b68e16f6a 100644 --- a/src/main/java/biomesoplenty/common/biome/overworld/BiomeGenCrag.java +++ b/src/main/java/biomesoplenty/common/biome/overworld/BiomeGenCrag.java @@ -24,7 +24,7 @@ public class BiomeGenCrag extends BOPBiome { this.setHeight(biomeHeight); this.setColor(5209457); - this.setTemperatureRainfall(2.0F, 0.0F); + this.setTemperatureRainfall(1.0F, 0.0F); this.addWeight(BiomeType.COOL, 3); diff --git a/src/main/java/biomesoplenty/common/biome/overworld/BiomeGenOriginValley.java b/src/main/java/biomesoplenty/common/biome/overworld/BiomeGenOriginValley.java new file mode 100644 index 000000000..fd145750d --- /dev/null +++ b/src/main/java/biomesoplenty/common/biome/overworld/BiomeGenOriginValley.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright 2015, the Biomes O' Plenty Team + * + * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. + * + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + ******************************************************************************/ + +package biomesoplenty.common.biome.overworld; + +import net.minecraft.init.Blocks; +import net.minecraft.util.BlockPos; +import net.minecraftforge.common.BiomeManager.BiomeType; +import biomesoplenty.api.biome.BOPBiome; +import biomesoplenty.api.biome.generation.GeneratorStage; +import biomesoplenty.api.block.BOPBlocks; +import biomesoplenty.api.block.BOPTreeEnums.AllTrees; +import biomesoplenty.common.block.BlockBOPGrass; +import biomesoplenty.common.block.BlockBOPLeaves; +import biomesoplenty.common.block.BlockBOPLeaves2; +import biomesoplenty.common.block.BlockBOPGrass.BOPGrassType; +import biomesoplenty.common.config.MiscConfigurationHandler; +import biomesoplenty.common.world.feature.tree.GeneratorBasicTree; +import biomesoplenty.common.world.feature.tree.GeneratorWeighted; + +public class BiomeGenOriginValley extends BOPBiome +{ + private static final Height biomeHeight = new Height(0.1F, 0.3F); + + public BiomeGenOriginValley() + { + this.setHeight(biomeHeight); + this.setColor(10341485); + this.setTemperatureRainfall(0.7F, 0.8F); + + this.addWeight(BiomeType.WARM, 1); + + this.topBlock = BOPBlocks.grass.getDefaultState().withProperty(BlockBOPGrass.VARIANT, BOPGrassType.ORIGIN); + + GeneratorWeighted treeGenerator = new GeneratorWeighted(4); + treeGenerator.add(new GeneratorBasicTree(1, false, 5, 8, Blocks.log.getDefaultState(), + BOPBlocks.leaves_2.getDefaultState().withProperty(BlockBOPLeaves.getVariantProperty(BlockBOPLeaves2.PAGENUM), AllTrees.ORIGIN))); + + this.addGenerator("trees", GeneratorStage.TREE, treeGenerator); + } + + @Override + public int getGrassColorAtPos(BlockPos pos) + { + return 10682207; + } + + @Override + public int getFoliageColorAtPos(BlockPos pos) + { + return 3866368; + } + + @Override + public int getSkyColorByTemp(float temperature) + { + if (MiscConfigurationHandler.skyColors) + return 8441086; + + return super.getSkyColorByTemp(temperature); + } +} diff --git a/src/main/java/biomesoplenty/common/block/BlockBOPGrass.java b/src/main/java/biomesoplenty/common/block/BlockBOPGrass.java index 1f72c7311..b56712e35 100644 --- a/src/main/java/biomesoplenty/common/block/BlockBOPGrass.java +++ b/src/main/java/biomesoplenty/common/block/BlockBOPGrass.java @@ -112,30 +112,34 @@ public class BlockBOPGrass extends BlockGrass implements IBOPBlock @Override public boolean canSustainPlant(IBlockAccess world, BlockPos pos, EnumFacing direction, net.minecraftforge.common.IPlantable plantable) { - + IBlockState state = world.getBlockState(pos); net.minecraftforge.common.EnumPlantType plantType = plantable.getPlantType(world, pos.offset(direction)); - switch ((BOPGrassType) state.getValue(VARIANT)) + //Forge calls this method regardless of whether the block is infact ours, so we have to check this + //(Somewhat illogical, I know) + if (state.getBlock() == this) { - // smoldering grass supports no plants + switch ((BOPGrassType) state.getValue(VARIANT)) + { + // smoldering grass supports no plants case SMOLDERING: return false; - - // origin grass supports all plants (including crop type - no need for hoe) + + // origin grass supports all plants (including crop type - no need for hoe) case ORIGIN: return true; - - // overgrown_netherrack supports Nether plants in addition to the defaults + + // overgrown_netherrack supports Nether plants in addition to the defaults case OVERGROWN_NETHERRACK: if (plantType == net.minecraftforge.common.EnumPlantType.Nether) {return true;} break; - + default: break; - } - - switch (plantType) - { + } + + switch (plantType) + { // support desert and plains plants case Desert: case Plains: return true; // support cave plants @@ -143,15 +147,18 @@ public class BlockBOPGrass extends BlockGrass implements IBOPBlock // support beach plants if there's water alongside case Beach: return ( - world.getBlockState(pos.east()).getBlock().getMaterial() == Material.water || - world.getBlockState(pos.west()).getBlock().getMaterial() == Material.water || - world.getBlockState(pos.north()).getBlock().getMaterial() == Material.water || - world.getBlockState(pos.south()).getBlock().getMaterial() == Material.water - ); - // don't support nether plants, water plants, or crops (require farmland), or anything else by default + world.getBlockState(pos.east()).getBlock().getMaterial() == Material.water || + world.getBlockState(pos.west()).getBlock().getMaterial() == Material.water || + world.getBlockState(pos.north()).getBlock().getMaterial() == Material.water || + world.getBlockState(pos.south()).getBlock().getMaterial() == Material.water + ); + // don't support nether plants, water plants, or crops (require farmland), or anything else by default default: return false; + } } + + return super.canSustainPlant(world, pos, direction, plantable); } diff --git a/src/main/java/biomesoplenty/common/handler/decoration/DecorateBiomeEventHandler.java b/src/main/java/biomesoplenty/common/handler/decoration/DecorateBiomeEventHandler.java index 0b2517742..78b28f0df 100644 --- a/src/main/java/biomesoplenty/common/handler/decoration/DecorateBiomeEventHandler.java +++ b/src/main/java/biomesoplenty/common/handler/decoration/DecorateBiomeEventHandler.java @@ -15,7 +15,9 @@ import net.minecraft.world.World; import net.minecraft.world.biome.BiomeGenBase; import net.minecraftforge.event.terraingen.DecorateBiomeEvent; import net.minecraftforge.event.terraingen.DecorateBiomeEvent.Decorate; +import net.minecraftforge.fml.common.eventhandler.Event.Result; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import biomesoplenty.api.biome.BiomeOwner; import biomesoplenty.api.biome.IExtendedBiome; import biomesoplenty.api.biome.generation.GenerationManager; import biomesoplenty.api.biome.generation.GeneratorStage; @@ -35,7 +37,9 @@ public class DecorateBiomeEventHandler { if (event.type != Decorate.EventType.CUSTOM) { - runGeneratorStage(event.world, event.rand, event.pos, GeneratorStage.mapDecorateType(event.type)); + boolean allow = runGeneratorStage(event.world, event.rand, event.pos, GeneratorStage.mapDecorateType(event.type)); + + event.setResult(allow ? Result.ALLOW : Result.DENY); } } @@ -45,7 +49,7 @@ public class DecorateBiomeEventHandler runGeneratorStage(event.world, event.rand, event.pos, GeneratorStage.POST); } - private static void runGeneratorStage(World world, Random random, BlockPos pos, GeneratorStage stage) + private static boolean runGeneratorStage(World world, Random random, BlockPos pos, GeneratorStage stage) { BiomeGenBase biome = world.getBiomeGenForCoords(pos.add(16, 0, 16)); IExtendedBiome extendedBiome = ExtendedBiomeRegistry.getExtension(biome); @@ -58,6 +62,17 @@ public class DecorateBiomeEventHandler { generator.scatter(world, random, pos); } + + //Biomes should explicitly allow for the following by defining their own generators + if (extendedBiome.getBiomeOwner() == BiomeOwner.BIOMESOPLENTY) + { + if (stage == GeneratorStage.PUMPKIN) + { + return false; + } + } } + + return true; } } diff --git a/src/main/java/biomesoplenty/common/init/ModBiomes.java b/src/main/java/biomesoplenty/common/init/ModBiomes.java index dba641ede..f18ea2d44 100644 --- a/src/main/java/biomesoplenty/common/init/ModBiomes.java +++ b/src/main/java/biomesoplenty/common/init/ModBiomes.java @@ -20,6 +20,7 @@ import org.apache.commons.io.FileUtils; import biomesoplenty.common.biome.overworld.BiomeGenAlps; import biomesoplenty.common.biome.overworld.BiomeGenArctic; import biomesoplenty.common.biome.overworld.BiomeGenCrag; +import biomesoplenty.common.biome.overworld.BiomeGenOriginValley; import biomesoplenty.common.command.BOPCommand; import biomesoplenty.common.util.config.JsonBiome; import biomesoplenty.common.world.WorldTypeBOP; @@ -47,6 +48,7 @@ public class ModBiomes alps = registerBiome(new BiomeGenAlps().setBiomeName("Alps"), "alps"); arctic = registerBiome(new BiomeGenArctic().setBiomeName("Arctic"), "arctic"); crag = registerBiome(new BiomeGenCrag().setBiomeName("Crag"), "crag"); + originValley = registerBiome(new BiomeGenOriginValley().setBiomeName("Origin Valley"), "origin_valley"); } private static void registerExternalBiomes() diff --git a/src/main/java/biomesoplenty/common/init/ModGenerators.java b/src/main/java/biomesoplenty/common/init/ModGenerators.java index fbac9dd5a..87416d8ef 100644 --- a/src/main/java/biomesoplenty/common/init/ModGenerators.java +++ b/src/main/java/biomesoplenty/common/init/ModGenerators.java @@ -11,6 +11,8 @@ package biomesoplenty.common.init; import static biomesoplenty.api.biome.generation.GeneratorRegistry.registerGenerator; import biomesoplenty.common.world.feature.GeneratorOreCluster; import biomesoplenty.common.world.feature.GeneratorOreSingle; +import biomesoplenty.common.world.feature.tree.GeneratorBasicTree; +import biomesoplenty.common.world.feature.tree.GeneratorWeighted; public class ModGenerators { @@ -18,5 +20,7 @@ public class ModGenerators { registerGenerator("ore_single", GeneratorOreSingle.class); registerGenerator("ore_cluster", GeneratorOreCluster.class); + registerGenerator("weighted", GeneratorWeighted.class); + registerGenerator("basic_tree", GeneratorBasicTree.class); } } diff --git a/src/main/java/biomesoplenty/common/util/biome/GeneratorUtils.java b/src/main/java/biomesoplenty/common/util/biome/GeneratorUtils.java new file mode 100644 index 000000000..c7295a811 --- /dev/null +++ b/src/main/java/biomesoplenty/common/util/biome/GeneratorUtils.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright 2015, the Biomes O' Plenty Team + * + * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. + * + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + ******************************************************************************/ + +package biomesoplenty.common.util.biome; + +import org.apache.commons.lang3.tuple.Pair; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.util.BlockPos; +import net.minecraft.world.World; + +public class GeneratorUtils +{ + public static Pair validateMinMaxHeight(int minHeight, int maxHeight) + { + if (maxHeight < minHeight) + { + //Swap min and max height so that max is higher than min + int prevMinHeight = minHeight; + minHeight = maxHeight; + maxHeight = prevMinHeight; + } + else if (maxHeight == minHeight) + { + if (minHeight < 255) + { + //Increase max height to be higher than min height + ++maxHeight; + } + else + { + //Decrease min height so that max is higher + --minHeight; + } + } + + return Pair.of(minHeight, maxHeight); + } + + public static boolean isBlockTreeReplacable(Block block) + { + return block.getMaterial() == Material.air || block.getMaterial() == Material.leaves || block == Blocks.grass || block == Blocks.dirt || block == Blocks.log || block == Blocks.log2 || block == Blocks.sapling || block == Blocks.vine; + } + + public static boolean canTreeReplace(World world, BlockPos pos) + { + IBlockState state = world.getBlockState(pos); + return state.getBlock().isAir(world, pos) || state.getBlock().isLeaves(world, pos) || state.getBlock().isWood(world, pos) || isBlockTreeReplacable(state.getBlock()); + } +} diff --git a/src/main/java/biomesoplenty/common/util/config/GeneratorTypeAdaptor.java b/src/main/java/biomesoplenty/common/util/config/GeneratorTypeAdaptor.java index 1ff9b125c..4bed47de7 100644 --- a/src/main/java/biomesoplenty/common/util/config/GeneratorTypeAdaptor.java +++ b/src/main/java/biomesoplenty/common/util/config/GeneratorTypeAdaptor.java @@ -60,8 +60,8 @@ public class GeneratorTypeAdaptor implements JsonSerializer, JsonDes generator = (IGenerator)generatorClass.newInstance(); Type generatorStageType = new TypeToken() {}.getType(); - String generatorStageName = jsonObject.get("stage").getAsString(); GeneratorStage generatorStage = (GeneratorStage)context.deserialize(jsonObject.get("stage"), generatorStageType); + String generatorStageName = jsonObject.get("stage") != null ? jsonObject.get("stage").getAsString() : null; if (generatorStage == null) { diff --git a/src/main/java/biomesoplenty/common/world/feature/GeneratorOreBase.java b/src/main/java/biomesoplenty/common/world/feature/GeneratorOreBase.java index 783cfd0b4..1606d92f9 100644 --- a/src/main/java/biomesoplenty/common/world/feature/GeneratorOreBase.java +++ b/src/main/java/biomesoplenty/common/world/feature/GeneratorOreBase.java @@ -12,7 +12,11 @@ import java.util.Random; import net.minecraft.util.BlockPos; import net.minecraft.world.World; + +import org.apache.commons.lang3.tuple.Pair; + import biomesoplenty.api.biome.generation.CustomizableGenerator; +import biomesoplenty.common.util.biome.GeneratorUtils; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; @@ -29,8 +33,10 @@ public abstract class GeneratorOreBase extends CustomizableGenerator protected GeneratorOreBase(int amountPerChunk, int minHeight, int maxHeight) { this.amountPerChunk = amountPerChunk; - this.minHeight = minHeight; - this.maxHeight = maxHeight; + + Pair heights = GeneratorUtils.validateMinMaxHeight(minHeight, maxHeight); + this.minHeight = heights.getLeft(); + this.maxHeight = heights.getRight(); } @Override @@ -59,33 +65,8 @@ public abstract class GeneratorOreBase extends CustomizableGenerator int minHeight = json.get("min_height").getAsInt(); int maxHeight = json.get("max_height").getAsInt(); - validateMinMaxHeight(minHeight, maxHeight); - } - - protected void validateMinMaxHeight(int minHeight, int maxHeight) - { - if (maxHeight < minHeight) - { - //Swap min and max height so that max is higher than min - int prevMinHeight = minHeight; - minHeight = maxHeight; - maxHeight = prevMinHeight; - } - else if (maxHeight == minHeight) - { - if (minHeight < 255) - { - //Increase max height to be higher than min height - ++maxHeight; - } - else - { - //Decrease min height so that max is higher - --minHeight; - } - } - - this.minHeight = minHeight; - this.maxHeight = maxHeight; + Pair heights = GeneratorUtils.validateMinMaxHeight(minHeight, maxHeight); + this.minHeight = heights.getLeft(); + this.maxHeight = heights.getRight(); } } diff --git a/src/main/java/biomesoplenty/common/world/feature/GeneratorOreCluster.java b/src/main/java/biomesoplenty/common/world/feature/GeneratorOreCluster.java index 65122ff6f..0adbb2640 100644 --- a/src/main/java/biomesoplenty/common/world/feature/GeneratorOreCluster.java +++ b/src/main/java/biomesoplenty/common/world/feature/GeneratorOreCluster.java @@ -33,9 +33,9 @@ public class GeneratorOreCluster extends GeneratorOreBase } @Override - public void generate(World world, Random random, BlockPos pos) + public boolean generate(World world, Random random, BlockPos pos) { - this.generator.generate(world, random, pos); + return this.generator.generate(world, random, pos); } @Override diff --git a/src/main/java/biomesoplenty/common/world/feature/GeneratorOreSingle.java b/src/main/java/biomesoplenty/common/world/feature/GeneratorOreSingle.java index 4147aa11d..8f90bdff3 100644 --- a/src/main/java/biomesoplenty/common/world/feature/GeneratorOreSingle.java +++ b/src/main/java/biomesoplenty/common/world/feature/GeneratorOreSingle.java @@ -39,12 +39,14 @@ public class GeneratorOreSingle extends GeneratorOreBase } @Override - public void generate(World world, Random random, BlockPos pos) + public boolean generate(World world, Random random, BlockPos pos) { if (world.getBlockState(pos).getBlock().isReplaceableOreGen(world, pos, this.replace)) { - world.setBlockState(pos, this.state, 2); + return world.setBlockState(pos, this.state, 2); } + + return false; } @Override diff --git a/src/main/java/biomesoplenty/common/world/feature/tree/GeneratorBasicTree.java b/src/main/java/biomesoplenty/common/world/feature/tree/GeneratorBasicTree.java new file mode 100644 index 000000000..ad05133c2 --- /dev/null +++ b/src/main/java/biomesoplenty/common/world/feature/tree/GeneratorBasicTree.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright 2015, the Biomes O' Plenty Team + * + * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. + * + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + ******************************************************************************/ + +package biomesoplenty.common.world.feature.tree; + +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockSapling; +import net.minecraft.block.BlockVine; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +import org.apache.commons.lang3.tuple.Pair; + +import biomesoplenty.api.biome.generation.CustomizableWeightedGenerator; +import biomesoplenty.common.util.biome.GeneratorUtils; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; + +public class GeneratorBasicTree extends CustomizableWeightedGenerator +{ + private boolean updateNeighbours; + private int minHeight; + private int maxHeight; + private IBlockState log; + private IBlockState leaves; + private IBlockState vine; + + public GeneratorBasicTree() {} + + public GeneratorBasicTree(int weight, boolean updateNeighbours, int minHeight, int maxHeight, IBlockState log, IBlockState leaves, IBlockState vine) + { + super(weight); + + this.updateNeighbours = updateNeighbours; + + Pair heights = GeneratorUtils.validateMinMaxHeight(minHeight, maxHeight); + this.minHeight = heights.getLeft(); + this.maxHeight = heights.getRight(); + + this.log = log; + this.leaves = leaves; + this.vine = vine; + } + + public GeneratorBasicTree(int weight, boolean updateNeighbours, int minHeight, int maxHeight, IBlockState log, IBlockState leaves) + { + this(weight, updateNeighbours, minHeight, maxHeight, log, leaves, null); + } + + @Override + public boolean generate(World world, Random random, BlockPos pos) + { + int height = random.nextInt(this.maxHeight - this.minHeight) + this.minHeight; + boolean hasSpace = true; + + //Generate only if we are above the lowest bedrock level (1) and reach less than the world height + //There must be a gap of 1 between the top leaf block and the world height + if (pos.getY() >= 1 && pos.getY() + height + 1 <= 256) + { + int radius; + + for (int y = pos.getY(); y <= pos.getY() + 1 + height; y++) + { + radius = 1; + + //Don't check for space on the first level, if we are a sapling then there will + //already be a block here (the sapling itself) + if (y == pos.getY()) + { + radius = 0; + } + + //At and above the top log block, require a radius of 2 to be empty + if (y >= pos.getY() + 1 + height - 2) + { + radius = 2; + } + + for (int x = pos.getX() - radius; x <= pos.getX() + radius && hasSpace; ++x) + { + for (int z = pos.getZ() - radius; z <= pos.getZ() + radius && hasSpace; ++z) + { + if (y >= 0 && y < 256) + { + if (!GeneratorUtils.canTreeReplace(world, new BlockPos(x, y, z))) + { + hasSpace = false; + } + } + else + { + hasSpace = false; + } + } + } + } + + if (!hasSpace) + { + return false; + } + else + { + BlockPos soilPos = pos.down(); + Block soil = world.getBlockState(soilPos).getBlock(); + boolean isSoil = soil.canSustainPlant(world, soilPos, EnumFacing.UP, (BlockSapling)Blocks.sapling); + + if (isSoil && pos.getY() < 256 - height - 1) + { + soil.onPlantGrow(world, soilPos, pos); + int leavesLayers = 3; + + //Generates leaves at the top of the tree, going one block above the top log (<= rather than <) + for (int y = pos.getY() + height - leavesLayers; y <= pos.getY() + height; y++) + { + //Determines the distance from the top of the tree as a negative number + int currentLayer = y - (pos.getY() + height); + //Uses integer division truncation (-3 / 2 = -1, -2 / 2 = -1) to reduce + //the radius closer to the top of the tree. (2, 2, 1, 1) + int leavesRadius = 1 - currentLayer / 2; + + for (int x = pos.getX() - leavesRadius; x <= pos.getX() + leavesRadius; x++) + { + int xDiff = x - pos.getX(); + + for (int z = pos.getZ() - leavesRadius; z <= pos.getZ() + leavesRadius; ++z) + { + int zDiff = z - pos.getZ(); + + //Randomly prevent the generation of leaves on the corners of each layer + //If the layer is the top layer, never generate the corners + if (Math.abs(xDiff) != leavesRadius || Math.abs(zDiff) != leavesRadius || random.nextInt(2) != 0 && currentLayer != 0) + { + BlockPos leavesPos = new BlockPos(x, y, z); + Block block = world.getBlockState(leavesPos).getBlock(); + + if (block.isAir(world, leavesPos) || block.isLeaves(world, leavesPos) || block.getMaterial() == Material.vine) + { + this.setBlockAndNotifyAdequately(world, leavesPos, this.leaves); + } + } + } + } + } + + //Create the trunk from the bottom up, using < to ensure it is covered with one layer of leaves + for (int layer = 0; layer < height; ++layer) + { + BlockPos blockpos2 = pos.up(layer); + Block block2 = world.getBlockState(blockpos2).getBlock(); + + if (block2.isAir(world, blockpos2) || block2.isLeaves(world, blockpos2) || block2.getMaterial() == Material.vine) + { + this.setBlockAndNotifyAdequately(world, pos.up(layer), this.log); + + //If vines are enabled, randomly cover the sides of the trunk with vines from the bottom up + if (this.vine != null && layer > 0) + { + if (random.nextInt(3) > 0 && world.isAirBlock(pos.add(-1, layer, 0))) + { + this.setBlockAndNotifyAdequately(world, pos.add(-1, layer, 0), this.getVineStateForSide(EnumFacing.EAST)); + } + + if (random.nextInt(3) > 0 && world.isAirBlock(pos.add(1, layer, 0))) + { + this.setBlockAndNotifyAdequately(world, pos.add(1, layer, 0), this.getVineStateForSide(EnumFacing.WEST)); + } + + if (random.nextInt(3) > 0 && world.isAirBlock(pos.add(0, layer, -1))) + { + this.setBlockAndNotifyAdequately(world, pos.add(0, layer, -1), this.getVineStateForSide(EnumFacing.SOUTH)); + } + + if (random.nextInt(3) > 0 && world.isAirBlock(pos.add(0, layer, 1))) + { + this.setBlockAndNotifyAdequately(world, pos.add(0, layer, 1), this.getVineStateForSide(EnumFacing.NORTH)); + } + } + } + } + + if (this.vine != null) + { + for (int y = pos.getY() - leavesLayers + height; y <= pos.getY() + height; y++) + { + //Determines the distance from the top of the tree as a negative number + int currentLayer = y - (pos.getY() + height); + //Uses integer division truncation (-3 / 2 = -1, -2 / 2 = -1) to reduce + //the radius closer to the top of the tree. (3, 3, 2, 2) + int leavesRadius = 2 - currentLayer / 2; + + for (int x = pos.getX() - leavesRadius; x <= pos.getX() + leavesRadius; x++) + { + for (int z = pos.getZ() - leavesRadius; z <= pos.getZ() + leavesRadius; z++) + { + BlockPos blockpos3 = new BlockPos(x, y, z); + + //Surround leaves on the edge of the tree with vines and extend them downwards + if (world.getBlockState(blockpos3).getBlock().isLeaves(world, blockpos3)) + { + BlockPos westPos = blockpos3.west(); + BlockPos eastPos = blockpos3.east(); + BlockPos northPos = blockpos3.north(); + BlockPos southPos = blockpos3.south(); + + if (random.nextInt(4) == 0 && world.getBlockState(westPos).getBlock().isAir(world, westPos)) + { + this.extendVines(world, westPos, EnumFacing.EAST); + } + + if (random.nextInt(4) == 0 && world.getBlockState(eastPos).getBlock().isAir(world, eastPos)) + { + this.extendVines(world, eastPos, EnumFacing.WEST); + } + + if (random.nextInt(4) == 0 && world.getBlockState(northPos).getBlock().isAir(world, northPos)) + { + this.extendVines(world, northPos, EnumFacing.SOUTH); + } + + if (random.nextInt(4) == 0 && world.getBlockState(southPos).getBlock().isAir(world, southPos)) + { + this.extendVines(world, southPos, EnumFacing.NORTH); + } + } + } + } + } + } + + return true; + } + else + { + return false; + } + } + } + else + { + return false; + } + } + + private IBlockState getVineStateForSide(EnumFacing side) + { + return this.vine.withProperty(BlockVine.getPropertyFor(side), Boolean.valueOf(true)); + } + + private void extendVines(World world, BlockPos pos, EnumFacing side) + { + IBlockState vineState = this.getVineStateForSide(side); + this.setBlockAndNotifyAdequately(world, pos, vineState); + + int length = 4; + + //Extend vine downwards for a maximum of 4 blocks + for (pos = pos.down(); world.getBlockState(pos).getBlock().isAir(world, pos) && length > 0; length--) + { + this.setBlockAndNotifyAdequately(world, pos, vineState); + pos = pos.down(); + } + } + + private void setBlockAndNotifyAdequately(World world, BlockPos pos, IBlockState state) + { + if (this.updateNeighbours) + { + world.setBlockState(pos, state, 3); + } + else + { + world.setBlockState(pos, state, 2); + } + } + + @Override + public void writeToJson(JsonObject json, JsonSerializationContext context) + { + json.addProperty("weight", this.itemWeight); + json.addProperty("update_neighbours", this.updateNeighbours); + json.addProperty("min_height", this.minHeight); + json.addProperty("max_height", this.maxHeight); + json.add("log_state", context.serialize(this.log)); + json.add("leaves_state", context.serialize(this.leaves)); + json.add("vine_state", context.serialize(this.vine)); + } + + @Override + public void readFromJson(JsonObject json, JsonDeserializationContext context) + { + this.itemWeight = json.get("weight").getAsInt(); + this.updateNeighbours = json.get("update_neighbours").getAsBoolean(); + + Pair heights = GeneratorUtils.validateMinMaxHeight(minHeight, maxHeight); + this.minHeight = heights.getLeft(); + this.maxHeight = heights.getRight(); + + this.log = context.deserialize(json.get("log_state"), IBlockState.class); + this.leaves = context.deserialize(json.get("leaves_state"), IBlockState.class); + this.vine = context.deserialize(json.get("vine_state"), IBlockState.class); + } +} diff --git a/src/main/java/biomesoplenty/common/world/feature/tree/GeneratorWeighted.java b/src/main/java/biomesoplenty/common/world/feature/tree/GeneratorWeighted.java new file mode 100644 index 000000000..e50b8f5da --- /dev/null +++ b/src/main/java/biomesoplenty/common/world/feature/tree/GeneratorWeighted.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright 2015, the Biomes O' Plenty Team + * + * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. + * + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + ******************************************************************************/ + +package biomesoplenty.common.world.feature.tree; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import net.minecraft.util.BlockPos; +import net.minecraft.util.WeightedRandom; +import net.minecraft.world.World; +import biomesoplenty.api.biome.generation.CustomizableGenerator; +import biomesoplenty.api.biome.generation.CustomizableWeightedGenerator; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.reflect.TypeToken; + +public class GeneratorWeighted extends CustomizableGenerator +{ + private int amountPerChunk; + private List weightedEntries = new ArrayList(); + + public GeneratorWeighted() {} + + public GeneratorWeighted(int amountPerChunk) + { + this.amountPerChunk = amountPerChunk; + } + + public void add(CustomizableWeightedGenerator entry) + { + this.weightedEntries.add(entry); + } + + @Override + public void scatter(World world, Random random, BlockPos pos) + { + for (int i = 0; i < this.amountPerChunk; i++) + { + int x = random.nextInt(16) + 8; + int z = random.nextInt(16) + 8; + BlockPos genPos = world.getHeight(pos.add(x, 0, z)); + + generate(world, random, genPos); + } + } + + @Override + public boolean generate(World world, Random random, BlockPos pos) + { + CustomizableWeightedGenerator generator = (CustomizableWeightedGenerator)WeightedRandom.getRandomItem(random, this.weightedEntries); + + return generator.generate(world, random, pos); + } + + @Override + public void writeToJson(JsonObject json, JsonSerializationContext context) + { + json.addProperty("amount_per_chunk", this.amountPerChunk); + json.add("entries", context.serialize(this.weightedEntries)); + } + + @Override + public void readFromJson(JsonObject json, JsonDeserializationContext context) + { + this.amountPerChunk = json.get("amount_per_chunk").getAsInt(); + this.weightedEntries = context.deserialize(json.get("entries"), new TypeToken>() {}.getType()); + } +}