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 c7c3cf04b..421606b27 100644 --- a/src/main/java/biomesoplenty/common/world/gen/feature/BOPBiomeFeatures.java +++ b/src/main/java/biomesoplenty/common/world/gen/feature/BOPBiomeFeatures.java @@ -10,6 +10,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.BulbTreeFeature; import biomesoplenty.common.world.gen.feature.tree.TaigaTreeFeature; import net.minecraft.init.Blocks; import net.minecraft.world.gen.feature.AbstractTreeFeature; @@ -21,6 +22,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 AbstractTreeFeature BULB_TREE = new BulbTreeFeature.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/BulbTreeFeature.java b/src/main/java/biomesoplenty/common/world/gen/feature/tree/BulbTreeFeature.java new file mode 100644 index 000000000..bdc79cbcb --- /dev/null +++ b/src/main/java/biomesoplenty/common/world/gen/feature/tree/BulbTreeFeature.java @@ -0,0 +1,233 @@ +/******************************************************************************* + * 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.common.util.biome.GeneratorUtil; +import biomesoplenty.common.util.block.IBlockPosQuery; +import net.minecraft.block.BlockDirectional; +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 BulbTreeFeature extends TreeFeatureBase +{ + public static class Builder extends BuilderBase + { + public Builder() + { + this.minHeight = 6; + this.maxHeight = 12; + + this.replace = (world, pos) -> + { + Material mat = world.getBlockState(pos).getMaterial(); + return mat == Material.AIR || mat == Material.LEAVES; + }; + } + + @Override + public BulbTreeFeature create() + { + return new BulbTreeFeature(this.updateNeighbours, this.placeOn, this.replace, this.log, this.leaves, this.altLeaves, this.vine, this.hanging, this.trunkFruit, this.minHeight, this.maxHeight); + } + + } + + protected BulbTreeFeature(boolean notify, IBlockPosQuery placeOn, IBlockPosQuery replace, IBlockState log, IBlockState leaves, IBlockState altLeaves, IBlockState vine, IBlockState hanging, IBlockState trunkFruit, int minHeight, int maxHeight) + { + super(notify, placeOn, replace, log, leaves, altLeaves, vine, hanging, trunkFruit, minHeight, maxHeight); + } + + public boolean setCocoa(IWorld world, BlockPos pos, EnumFacing side) + { + IBlockState cocoaState = Blocks.COCOA.getDefaultState().with(BlockDirectional.FACING, side); + if (this.replace.matches(world, pos)) + { + this.setBlockState(world, pos, cocoaState); + return true; + } + return false; + } + + public boolean checkSpace(IWorld world, BlockPos pos, int baseHeight, int height) + { + for (int y = 0; y <= height; y++) + { + // require 3x3 for the leaves, 1x1 for the trunk + int radius = (y <= baseHeight ? 0 : 1); + + for (int x = -radius; x <= radius; x++) + { + for (int z = -radius; z <= radius; z++) + { + BlockPos pos1 = pos.add(x, y, z); + // note, there may be a sapling on the first layer - make sure this.replace matches it! + if (pos1.getY() >= 255 || !this.replace.matches(world, pos1)) + { + return false; + } + } + } + } + return true; + } + + // generates a 'branch' of a leaf layer + public void generateBranch(IWorld world, Random random, BlockPos pos, EnumFacing direction) + { + EnumFacing sideways = direction.rotateY(); + this.setLeaves(world, pos.offset(direction, 1)); + this.setLeaves(world, pos.up().offset(direction, 1)); + if (random.nextInt(3) > 0) + { + this.setLeaves(world, pos.up().offset(direction, 1).offset(sideways, 1)); + } + } + + // generates a layer of leafs (2 blocks high) + public void generateLeafLayer(Set changedBlocks, IWorld world, Random random, BlockPos pos) + { + for (EnumFacing direction : EnumFacing.Plane.HORIZONTAL) + { + this.generateBranch(world, random, pos, direction); + } + + // add the trunk in the middle + this.setLog(changedBlocks, world, pos); + this.setLog(changedBlocks, world, pos.up()); + } + + public void generateTop(Set changedBlocks, IWorld world, Random random, BlockPos pos, int topHeight) + { + for (int y = 0; y < topHeight; y++) + { + int radius = topHeight - 1 - y; + + for (int x = -radius; x <= radius; ++x) + { + for (int z = -radius; z <= radius; ++z) + { + if (Math.abs(x) < radius || Math.abs(z) < radius || random.nextInt(2) == 0) + { + this.setLeaves(world, pos.add(x, y, z)); + } + } + } + if (y < topHeight - 1) + { + // add the trunk in the middle + this.setLog(changedBlocks, world, pos.add(0, y, 0)); + } else { + // add leaves on top for certain + this.setLeaves(world, pos.add(0, y, 0)); + } + } + } + + @Override + protected boolean place(Set changedBlocks, IWorld world, Random random, BlockPos startPos) + { + + // Move down until we reach the ground + while (startPos.getY() > 1 && world.isAirBlock(startPos) || world.getBlockState(startPos).getMaterial() == Material.LEAVES) {startPos = startPos.down();} + + if (!this.placeOn.matches(world, startPos)) + { + // Abandon if we can't place the tree on this block + return false; + } + + // Choose heights + int height = GeneratorUtil.nextIntBetween(random, this.minHeight, this.maxHeight); + if (height < 6) {return false;} + int topHeight = 3; + int heightMinusTop = height - topHeight; + int numBranches = heightMinusTop / 5; + int baseHeight = heightMinusTop - (numBranches * 2); + + // Start on the space above ground + BlockPos pos = startPos.up(); + + if (!this.checkSpace(world, pos, baseHeight, height)) + { + // Abandon if there isn't enough room + return false; + } + + // Generate bottom of tree (trunk only) + for(int i = 0; i < baseHeight; i++) + { + this.setLog(changedBlocks, world, pos); + pos = pos.up(); + } + + // Generate middle of the tree - 2 steps at a time (trunk and leaves) + for (int i = 0; i < numBranches; i++) + { + this.generateLeafLayer(changedBlocks, world, random, pos); + pos = pos.up(2); + } + + // Generate the top of the tree + this.generateTop(changedBlocks, world, random, pos, topHeight); + + // Add vines + this.addVines(world, random, startPos, baseHeight, height, 3, 10); + + // Add cocoa + // this.addCocoa(world, random, startPos, baseHeight, 3); + + return true; + } + + protected void addVines(IWorld world, Random rand, BlockPos startPos, int baseHeight, int height, int leavesRadius, int generationAttempts) + { + if (this.vine == null) {return;} + for (int i = 0; i < generationAttempts; i++) + { + // choose a random direction + EnumFacing direction = EnumFacing.Plane.HORIZONTAL.random(rand); + EnumFacing back = direction.getOpposite(); + EnumFacing sideways = direction.rotateY(); + + // choose a random starting point somewhere just outside the boundary of the tree leaves + BlockPos pos = startPos.up(GeneratorUtil.nextIntBetween(rand, baseHeight + 1, height)).offset(direction, leavesRadius + 1).offset(sideways, GeneratorUtil.nextIntBetween(rand, -leavesRadius, leavesRadius)); + + // move back towards the center until we meet a leaf, then stick a vine on it + for (int l = 0; l < leavesRadius; l++) + { + if (world.getBlockState(pos.offset(back, 1 + l)) == this.leaves) { + this.setVine(world, rand, pos.offset(back, l), back, 4); + break; + } + } + } + } + + protected void addCocoa(IWorld world, Random rand, BlockPos startPos, int baseHeight, int generationAttempts) + { + for (int i = 0; i < generationAttempts; i++) + { + // choose a random direction + EnumFacing direction = EnumFacing.Plane.HORIZONTAL.random(rand); + EnumFacing back = direction.getOpposite(); + + // choose a random point next to the trunk + BlockPos pos = startPos.up(GeneratorUtil.nextIntBetween(rand, 1, baseHeight)).offset(direction, 1); + + // stick a cocoa pod on it + this.setCocoa(world, pos, back); + } + } +}