From 890f7308db0aacc63a201b9b5bb965b9702a9584 Mon Sep 17 00:00:00 2001 From: Adubbz Date: Sun, 24 Feb 2019 21:32:12 +1100 Subject: [PATCH] Re-added bayou trees --- .../world/gen/feature/BOPBiomeFeatures.java | 2 + .../gen/feature/tree/BasicTreeFeature.java | 5 +- .../gen/feature/tree/BayouTreeFeature.java | 352 ++++++++++++++++++ 3 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 src/main/java/biomesoplenty/common/world/gen/feature/tree/BayouTreeFeature.java diff --git a/src/main/java/biomesoplenty/common/world/gen/feature/BOPBiomeFeatures.java b/src/main/java/biomesoplenty/common/world/gen/feature/BOPBiomeFeatures.java index 777ac7207..c7c3cf04b 100644 --- a/src/main/java/biomesoplenty/common/world/gen/feature/BOPBiomeFeatures.java +++ b/src/main/java/biomesoplenty/common/world/gen/feature/BOPBiomeFeatures.java @@ -9,6 +9,7 @@ package biomesoplenty.common.world.gen.feature; import biomesoplenty.api.block.BOPBlocks; import biomesoplenty.common.world.gen.feature.tree.BasicTreeFeature; +import biomesoplenty.common.world.gen.feature.tree.BayouTreeFeature; import biomesoplenty.common.world.gen.feature.tree.TaigaTreeFeature; import net.minecraft.init.Blocks; import net.minecraft.world.gen.feature.AbstractTreeFeature; @@ -19,6 +20,7 @@ public class BOPBiomeFeatures { public static final AbstractTreeFeature CONIFEROUS_TREE = new TaigaTreeFeature.Builder().log(BOPBlocks.fir_log.getDefaultState()).leaves(BOPBlocks.fir_leaves.getDefaultState()).minHeight(5).maxHeight(28).create(); public static final AbstractTreeFeature OAK_TREE = new BasicTreeFeature.Builder().create(); + public static final AbstractTreeFeature BAYOU_TREE = new BayouTreeFeature.Builder().create(); public static final SurfaceBuilderConfig LOAMY_GRASS_DIRT_GRAVEL_SURFACE = new SurfaceBuilderConfig(BOPBlocks.loamy_grass_block.getDefaultState(), BOPBlocks.loamy_dirt.getDefaultState(), Blocks.GRAVEL.getDefaultState()); } diff --git a/src/main/java/biomesoplenty/common/world/gen/feature/tree/BasicTreeFeature.java b/src/main/java/biomesoplenty/common/world/gen/feature/tree/BasicTreeFeature.java index 3c36f26d1..c522759ae 100644 --- a/src/main/java/biomesoplenty/common/world/gen/feature/tree/BasicTreeFeature.java +++ b/src/main/java/biomesoplenty/common/world/gen/feature/tree/BasicTreeFeature.java @@ -45,7 +45,10 @@ public class BasicTreeFeature extends TreeFeatureBase public Builder() { - this.placeOn = (world, pos) -> true; + this.placeOn = (world, pos) -> + { + return world.getBlockState(pos).isSolid(); + }; this.replace = (world, pos) -> { Material mat = world.getBlockState(pos).getMaterial(); diff --git a/src/main/java/biomesoplenty/common/world/gen/feature/tree/BayouTreeFeature.java b/src/main/java/biomesoplenty/common/world/gen/feature/tree/BayouTreeFeature.java new file mode 100644 index 000000000..60e2bf6f5 --- /dev/null +++ b/src/main/java/biomesoplenty/common/world/gen/feature/tree/BayouTreeFeature.java @@ -0,0 +1,352 @@ +/******************************************************************************* + * Copyright 2014-2019, 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.gen.feature.tree; + +import biomesoplenty.api.block.BOPBlocks; +import biomesoplenty.common.util.block.IBlockPosQuery; +import net.minecraft.block.Block; +import net.minecraft.block.BlockCocoa; +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.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IWorld; + +import java.util.Random; +import java.util.Set; + +public class BayouTreeFeature extends TreeFeatureBase +{ + public static class Builder extends BuilderBase + { + protected int leafLayers; + protected int leavesOffset; + protected int maxLeavesRadius; + protected int leavesLayerHeight; + protected IBlockPosQuery placeVinesOn; + protected float hangingChance; + + public Builder leafLayers(int a) {this.leafLayers = a; return this;} + public Builder leavesOffset(int a) {this.leavesOffset = a; return this;} + public Builder leavesLayerHeight(int a) {this.leavesLayerHeight = a; return this;} + public Builder maxLeavesRadius(int a) {this.maxLeavesRadius = a; return this;} + + public Builder placeVinesOn(IBlockPosQuery a) {this.placeVinesOn = a; return this;} + + public Builder hangingChance(float a) {this.hangingChance = a; return this;} + + public Builder() + { + this.placeOn = (world, pos) -> + { + return world.getBlockState(pos).isSolid(); + }; + this.replace = (world, pos) -> + { + Material mat = world.getBlockState(pos).getMaterial(); + return mat == Material.AIR || mat == Material.LEAVES || mat == Material.VINE || mat == Material.WATER; + }; + this.log = BOPBlocks.willow_log.getDefaultState(); + this.leaves = BOPBlocks.willow_leaves.getDefaultState(); + this.vine = BOPBlocks.willow_vine.getDefaultState(); + this.minHeight = 12; + this.maxHeight = 20; + this.updateNeighbours = false; + this.leafLayers = 4; + this.leavesOffset = 0; + this.maxLeavesRadius = 1; + this.leavesLayerHeight = 2; + this.placeVinesOn = (world, pos) -> + { + Material mat = world.getBlockState(pos).getMaterial(); + return mat == Material.AIR; + }; + this.hangingChance = 0.0F; + } + + @Override + public BayouTreeFeature create() + { + return new BayouTreeFeature(this.updateNeighbours, this.placeOn, this.replace, this.log, this.leaves, this.altLeaves, this.vine, this.hanging, this.trunkFruit, this.minHeight, this.maxHeight, this.leafLayers, this.leavesOffset, this.maxLeavesRadius, this.leavesLayerHeight, this.placeVinesOn, this.hangingChance); + } + } + + private int leafLayers; + private int leavesOffset; + private int maxLeavesRadius; + private int leavesLayerHeight; + private IBlockPosQuery placeVinesOn; + private float hangingChance; + + protected BayouTreeFeature(boolean notify, IBlockPosQuery placeOn, IBlockPosQuery replace, IBlockState log, + IBlockState leaves, IBlockState altLeaves, IBlockState vine, IBlockState hanging, IBlockState trunkFruit, + int minHeight, int maxHeight, int leafLayers, int leavesOffset, int maxLeavesRadius, int leavesLayerHeight, + IBlockPosQuery placeVinesOn, float hangingChance) + { + super(notify, placeOn, replace, log, leaves, altLeaves, vine, hanging, trunkFruit, minHeight, maxHeight); + + this.leafLayers = leafLayers; + this.leavesOffset = leavesOffset; + this.maxLeavesRadius = maxLeavesRadius; + this.leavesLayerHeight = leavesLayerHeight; + this.placeVinesOn = placeVinesOn; + this.hangingChance = hangingChance; + } + + @Override + protected boolean place(Set changedBlocks, IWorld 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) + { + hasSpace = false; + } + } + } + } + + if (!hasSpace) + { + return false; + } + else + { + BlockPos soilPos = pos.down(); + Block soil = world.getBlockState(soilPos).getBlock(); + + if (this.placeOn.matches(world, soilPos) && pos.getY() < 256 - height - 1) + { + soil.onPlantGrow(world.getBlockState(soilPos), world, soilPos, pos); + int leavesLayers = (this.leafLayers - 1); + + //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 = this.maxLeavesRadius - currentLayer / this.leavesLayerHeight; + + 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); + if (this.replace.matches(world, leavesPos)) + { + if (this.altLeaves != Blocks.AIR.getDefaultState()) + { + if (random.nextInt(4) == 0) + { + this.setAltLeaves(world, leavesPos); + } + else + { + this.setLeaves(world, leavesPos); + } + } + else + { + this.setLeaves(world, leavesPos); + } + } + } + } + } + } + + this.generateTrunk(changedBlocks, world, pos, height); + + if (this.vine != Blocks.AIR.getDefaultState()) + { + 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 = (this.maxLeavesRadius + this.leavesOffset) - currentLayer / this.leavesLayerHeight; + + 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).getMaterial() == Material.LEAVES) + { + BlockPos westPos = blockpos3.west(); + BlockPos eastPos = blockpos3.east(); + BlockPos northPos = blockpos3.north(); + BlockPos southPos = blockpos3.south(); + + if (random.nextInt(4) == 0 && this.placeVinesOn.matches(world, westPos)) + { + this.extendVines(world, westPos, EnumFacing.EAST); + } + + if (random.nextInt(4) == 0 && this.placeVinesOn.matches(world, eastPos)) + { + this.extendVines(world, eastPos, EnumFacing.WEST); + } + + if (random.nextInt(4) == 0 && this.placeVinesOn.matches(world, northPos)) + { + this.extendVines(world, northPos, EnumFacing.SOUTH); + } + + if (random.nextInt(4) == 0 && this.placeVinesOn.matches(world, southPos)) + { + this.extendVines(world, southPos, EnumFacing.NORTH); + } + } + } + } + } + } + + //Generate fruit or any other blocks that may hang off of the tree + if (this.hanging != Blocks.AIR.getDefaultState()) this.generateHanging(world, pos, random, height); + + if (this.trunkFruit != Blocks.AIR.getDefaultState()) + { + if (random.nextInt(5) == 0 && height > 5) + { + for (int l3 = 0; l3 < 2; ++l3) + { + for (EnumFacing enumfacing : EnumFacing.Plane.HORIZONTAL) + { + if (random.nextInt(4 - l3) == 0) + { + EnumFacing enumfacing1 = enumfacing.getOpposite(); + this.generateTrunkFruit(world, random.nextInt(3), pos.add(enumfacing1.getXOffset(), height - 5 + l3, enumfacing1.getZOffset()), enumfacing); + } + } + } + } + } + + return true; + } + else + { + return false; + } + } + } + else + { + return false; + } + } + + protected void generateTrunk(Set changedBlocks, IWorld world, BlockPos start, int height) + { + //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 = start.up(layer); + if (this.replace.matches(world, blockpos2)) + { + this.setLog(changedBlocks, world, start.up(layer)); + } + } + } + + protected void generateHanging(IWorld world, BlockPos start, Random rand, int height) + { + //Generate below the bottom layer of leaves + int y = start.getY() + (height - this.leafLayers); + + for (int x = start.getX() - (maxLeavesRadius + 1); x <= start.getX() + (maxLeavesRadius + 1); x++) + { + for (int z = start.getZ() - (maxLeavesRadius + 1); z <= start.getZ() + (maxLeavesRadius + 1); z++) + { + BlockPos hangingPos = new BlockPos(x, y, z); + + if (!world.isAirBlock(hangingPos.up()) && (world.isAirBlock(hangingPos)) && rand.nextFloat() <= this.hangingChance) + { + this.setHanging(world, hangingPos); + } + } + } + } + + private void generateTrunkFruit(IWorld world, int age, BlockPos pos, EnumFacing direction) + { + if (this.trunkFruit == Blocks.COCOA.getDefaultState()) + { + this.setBlockState(world, pos, this.trunkFruit.with(BlockCocoa.AGE, Integer.valueOf(age)).with(BlockCocoa.HORIZONTAL_FACING, direction)); + } + else + { + this.setBlockState(world, pos, this.trunkFruit.with(BlockCocoa.HORIZONTAL_FACING, direction)); + } + } + + private IBlockState getVineStateForSide(EnumFacing side) + { + return this.vine.getBlock() instanceof BlockVine ? this.vine.with(BlockVine.getPropertyFor(side), Boolean.valueOf(true)) : this.vine; + } + + private void extendVines(IWorld world, BlockPos pos, EnumFacing side) + { + IBlockState vineState = this.getVineStateForSide(side); + this.setBlockState(world, pos, vineState); + + int length = 4; + + //Extend vine downwards for a maximum of 4 blocks + for (pos = pos.down(); this.placeVinesOn.matches(world, pos) && length > 0; length--) + { + this.setBlockState(world, pos, vineState); + pos = pos.down(); + } + } +}