Updated all feature based sapling Growing

This commit is contained in:
Frank 2022-06-07 21:23:02 +02:00
parent 7285ada6d6
commit 72a707f456
8 changed files with 119 additions and 90 deletions

View file

@ -17,6 +17,7 @@ import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.RandomFeatureConfiguration; import net.minecraft.world.level.levelgen.feature.configurations.RandomFeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.RandomPatchConfiguration;
import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.placement.PlacementModifier; import net.minecraft.world.level.levelgen.placement.PlacementModifier;
@ -62,6 +63,7 @@ public class BCLFeature<F extends Feature<FC>, FC extends FeatureConfiguration>
private final Decoration featureStep; private final Decoration featureStep;
private final F feature; private final F feature;
private final FC configuration; private final FC configuration;
public final ResourceLocation id;
public BCLFeature(ResourceLocation id, public BCLFeature(ResourceLocation id,
@ -81,6 +83,7 @@ public class BCLFeature<F extends Feature<FC>, FC extends FeatureConfiguration>
this.featureStep = featureStep; this.featureStep = featureStep;
this.feature = feature; this.feature = feature;
this.configuration = configuration; this.configuration = configuration;
this.id = id;
if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) { if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) {
Registry.register(BuiltinRegistries.PLACED_FEATURE, id, placedFeature.value()); Registry.register(BuiltinRegistries.PLACED_FEATURE, id, placedFeature.value());
@ -161,22 +164,47 @@ public class BCLFeature<F extends Feature<FC>, FC extends FeatureConfiguration>
} }
public boolean place(ServerLevel level, BlockPos pos, RandomSource random) { public boolean place(ServerLevel level, BlockPos pos, RandomSource random) {
return place(this.getFeature(), level, pos, random); return place(this.getFeature(), this.getConfiguration(), level, pos, random);
} }
public static boolean place(Feature<?> feature, ServerLevel level, BlockPos pos, RandomSource random) { private static boolean placeUnbound(Feature<?> feature,
if (feature instanceof UserGrowableFeature growable) { FeatureConfiguration config,
return growable.grow(level, pos, random); ServerLevel level,
BlockPos pos,
RandomSource random) {
if (config instanceof RandomPatchConfiguration rnd) {
var configured = rnd.feature().value().feature().value();
feature = configured.feature();
config = configured.config();
} }
if (feature instanceof UserGrowableFeature growable) {
return growable.grow(level, pos, random, config);
}
FeaturePlaceContext context = new FeaturePlaceContext( FeaturePlaceContext context = new FeaturePlaceContext(
Optional.empty(), Optional.empty(),
level, level,
level.getChunkSource().getGenerator(), level.getChunkSource().getGenerator(),
random, random,
pos, pos,
null config
); );
return feature.place(context); return feature.place(context);
} }
public static boolean place(Feature<NoneFeatureConfiguration> feature,
ServerLevel level,
BlockPos pos,
RandomSource random) {
return placeUnbound(feature, FeatureConfiguration.NONE, level, pos, random);
}
public static <FC extends FeatureConfiguration> boolean place(Feature<FC> feature,
FC config,
ServerLevel level,
BlockPos pos,
RandomSource random) {
return placeUnbound(feature, config, level, pos, random);
}
} }

View file

@ -20,10 +20,11 @@ import org.betterx.bclib.api.v2.levelgen.features.config.ScatterFeatureConfig;
public class FastFeatures { public class FastFeatures {
public static BCLFeature vine(ResourceLocation location, public static BCLFeature<ScatterFeature<ScatterFeatureConfig.OnSolid>, ScatterFeatureConfig.OnSolid> vine(
boolean onFloor, ResourceLocation location,
boolean sparse, boolean onFloor,
ScatterFeatureConfig.Builder builder) { boolean sparse,
ScatterFeatureConfig.Builder builder) {
return scatter(location, onFloor, sparse, builder, BCLFeature.SCATTER_ON_SOLID); return scatter(location, onFloor, sparse, builder, BCLFeature.SCATTER_ON_SOLID);
} }
@ -146,7 +147,7 @@ public class FastFeatures {
} }
public static <FC extends FeatureConfiguration> BCLFeature public static <FC extends FeatureConfiguration> BCLFeature<Feature<FC>, FC>
simple(ResourceLocation location, simple(ResourceLocation location,
int searchDist, int searchDist,
boolean rare, boolean rare,

View file

@ -2,73 +2,22 @@ package org.betterx.bclib.api.v2.levelgen.features;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.data.worldgen.placement.PlacementUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.util.valueproviders.ClampedNormalInt;
import net.minecraft.util.valueproviders.ConstantInt;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.feature.Feature; import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.SimpleRandomFeatureConfiguration;
import net.minecraft.world.level.levelgen.placement.*;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import org.betterx.bclib.api.v2.levelgen.features.config.ScatterFeatureConfig; import org.betterx.bclib.api.v2.levelgen.features.config.ScatterFeatureConfig;
import org.betterx.bclib.api.v2.tag.CommonBlockTags;
import org.betterx.bclib.util.BlocksHelper; import org.betterx.bclib.util.BlocksHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional; import java.util.Optional;
public class ScatterFeature<FC extends ScatterFeatureConfig> public class ScatterFeature<FC extends ScatterFeatureConfig>
extends Feature<FC> { extends Feature<FC> implements UserGrowableFeature<FC> {
public static <T extends ScatterFeatureConfig> BCLFeature createAndRegister(ResourceLocation location,
int minPerChunk,
int maxPerChunk,
T cfg,
Feature<T> inlineFeature) {
List<Holder<PlacedFeature>> set = new ArrayList<>(2);
if (cfg.floorChance > 0) set.add(PlacementUtils.inlinePlaced(inlineFeature,
cfg,
EnvironmentScanPlacement.scanningFor(Direction.DOWN,
BlockPredicate.matchesTag(CommonBlockTags.TERRAIN),
BlockPredicate.ONLY_IN_AIR_PREDICATE,
12),
RandomOffsetPlacement.vertical(ConstantInt.of(1))));
if (cfg.floorChance < 1) {
set.add(PlacementUtils.inlinePlaced(inlineFeature,
cfg,
EnvironmentScanPlacement.scanningFor(Direction.UP,
BlockPredicate.matchesTag(CommonBlockTags.TERRAIN),
BlockPredicate.ONLY_IN_AIR_PREDICATE,
12),
RandomOffsetPlacement.vertical(ConstantInt.of(-1))));
}
SimpleRandomFeatureConfiguration configuration = new SimpleRandomFeatureConfiguration(HolderSet.direct(set));
return BCLFeatureBuilder.start(location, SIMPLE_RANDOM_SELECTOR)
.decoration(GenerationStep.Decoration.VEGETAL_DECORATION)
.modifier(CountPlacement.of(UniformInt.of(minPerChunk, maxPerChunk)))
.modifier(InSquarePlacement.spread())
.randomHeight4FromFloorCeil()
.modifier(CountPlacement.of(UniformInt.of(2, 5)))
.modifier(RandomOffsetPlacement.of(
ClampedNormalInt.of(0.0f, 2.0f, -6, 6),
ClampedNormalInt.of(0.0f, 0.6f, -2, 2)))
.onlyInBiome()
.buildAndRegister(configuration);
}
public ScatterFeature(Codec<FC> configCodec) { public ScatterFeature(Codec<FC> configCodec) {
super(configCodec); super(configCodec);
@ -104,7 +53,8 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
if (config.isValidBase(level.getBlockState(basePos))) { if (config.isValidBase(level.getBlockState(basePos))) {
final Direction surfaceDirection = direction.getOpposite(); final Direction surfaceDirection = direction.getOpposite();
BlockPos.MutableBlockPos POS = new BlockPos.MutableBlockPos(); BlockPos.MutableBlockPos POS = new BlockPos.MutableBlockPos();
buildPillarWithBase(level, origin, basePos, direction, centerHeight, config, random); int adaptedHeight = freeHeight(level, direction, centerHeight, config, origin);
buildPillarWithBase(level, origin, basePos, direction, adaptedHeight, config, random, false);
final double distNormalizer = (config.maxSpread * Math.sqrt(2)); final double distNormalizer = (config.maxSpread * Math.sqrt(2));
final int tryCount = config.spreadCount.sample(random); final int tryCount = config.spreadCount.sample(random);
@ -114,7 +64,11 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
POS.set(x, basePos.getY(), z); POS.set(x, basePos.getY(), z);
if (BlocksHelper.findSurroundingSurface(level, POS, surfaceDirection, 4, config::isValidBase)) { if (BlocksHelper.findSurroundingSurface(level, POS, surfaceDirection, 4, config::isValidBase)) {
int myHeight = freeHeight(level, direction, centerHeight, config, POS); int myHeight = freeHeight(level,
direction,
centerHeight,
config,
POS);
int dx = x - POS.getX(); int dx = x - POS.getX();
int dz = z - POS.getZ(); int dz = z - POS.getZ();
@ -135,7 +89,7 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
direction, direction,
myHeight, myHeight,
config, config,
random); random, false);
} }
} }
} }
@ -143,9 +97,9 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
private int freeHeight(LevelAccessor level, private int freeHeight(LevelAccessor level,
Direction direction, Direction direction,
int centerHeight, int defaultHeight,
ScatterFeatureConfig config, ScatterFeatureConfig config,
BlockPos.MutableBlockPos POS) { BlockPos POS) {
int myHeight; int myHeight;
if (config.growWhileFree) { if (config.growWhileFree) {
myHeight = BlocksHelper.blockCount(level, myHeight = BlocksHelper.blockCount(level,
@ -155,9 +109,9 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
BlocksHelper::isFree BlocksHelper::isFree
); );
} else { } else {
myHeight = centerHeight; myHeight = defaultHeight;
} }
return myHeight; return Math.max(config.minHeight, myHeight);
} }
private void buildPillarWithBase(LevelAccessor level, private void buildPillarWithBase(LevelAccessor level,
@ -166,8 +120,9 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
Direction direction, Direction direction,
int height, int height,
ScatterFeatureConfig config, ScatterFeatureConfig config,
RandomSource random) { RandomSource random,
if (BlocksHelper.isFreeSpace(level, origin, direction, height, BlocksHelper::isFree)) { boolean force) {
if (force || BlocksHelper.isFreeSpace(level, origin, direction, height, BlocksHelper::isFree)) {
createPatchOfBaseBlocks(level, random, basePos, config); createPatchOfBaseBlocks(level, random, basePos, config);
BlockState bottom = config.bottomBlock.getState(random, origin); BlockState bottom = config.bottomBlock.getState(random, origin);
if (bottom.canSurvive(level, origin)) { if (bottom.canSurvive(level, origin)) {
@ -185,11 +140,7 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
final BlockPos.MutableBlockPos POS = origin.mutable(); final BlockPos.MutableBlockPos POS = origin.mutable();
for (int size = 0; size < height; size++) { for (int size = 0; size < height; size++) {
BlockState previous = level.getBlockState(POS);
BlockState state = config.createBlock(size, height - 1, random, POS); BlockState state = config.createBlock(size, height - 1, random, POS);
if (!BlocksHelper.isFree(previous)) {
System.out.println("Replaced " + previous + " with " + state + " at " + POS);
}
BlocksHelper.setWithoutUpdate(level, POS, state); BlocksHelper.setWithoutUpdate(level, POS, state);
POS.move(direction); POS.move(direction);
} }
@ -246,4 +197,29 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
levelAccessor.setBlock(blockPos, baseState, 2); levelAccessor.setBlock(blockPos, baseState, 2);
} }
} }
@Override
public boolean grow(ServerLevelAccessor level,
BlockPos origin,
RandomSource random,
FC config) {
Optional<Direction> oDirection = getTipDirection(level, origin, random, config);
if (oDirection.isEmpty()) {
return false;
}
Direction direction = oDirection.get();
BlockPos basePos = origin.relative(direction, -1);
if (config.isValidBase(level.getBlockState(basePos))) {
int centerHeight = (int) (random.nextFloat() * (1 + config.maxHeight - config.minHeight) + config.minHeight);
centerHeight = freeHeight(level,
direction,
centerHeight,
config,
origin.relative(direction, 1)) + 1;
buildPillarWithBase(level, origin, basePos, direction, centerHeight, config, random, true);
}
return false;
}
} }

View file

@ -3,9 +3,11 @@ package org.betterx.bclib.api.v2.levelgen.features;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
public interface UserGrowableFeature { public interface UserGrowableFeature<FC extends FeatureConfiguration> {
boolean grow(ServerLevelAccessor level, boolean grow(ServerLevelAccessor level,
BlockPos pos, BlockPos pos,
RandomSource random); RandomSource random,
FC configuration);
} }

View file

@ -223,6 +223,17 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
return this; return this;
} }
public Builder<T> tripleShapeCeil(Block s) {
return tripleShapeCeil(s.defaultBlockState());
}
public Builder<T> tripleShapeCeil(BlockState s) {
block(s.setValue(BlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.MIDDLE));
tipBlock(s.setValue(BlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.BOTTOM));
bottomBlock(s.setValue(BlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.TOP));
return this;
}
public Builder<T> block(BlockStateProvider s) { public Builder<T> block(BlockStateProvider s) {
this.clusterBlock = s; this.clusterBlock = s;
if (tipBlock == null) tipBlock = s; if (tipBlock == null) tipBlock = s;
@ -297,6 +308,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
return this; return this;
} }
public Builder<T> noSpread() {
return spread(0, 0, ConstantInt.of(0));
}
public Builder<T> spread(float maxSpread, float sizeVariation) { public Builder<T> spread(float maxSpread, float sizeVariation) {
return spread(maxSpread, sizeVariation, ConstantInt.of((int) Math.min(16, 4 * maxSpread * maxSpread))); return spread(maxSpread, sizeVariation, ConstantInt.of((int) Math.min(16, 4 * maxSpread * maxSpread)));
} }

View file

@ -6,25 +6,27 @@ import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.shapes.VoxelShape;
import org.betterx.bclib.api.v2.levelgen.features.BCLFeature;
import java.util.function.Function; import java.util.function.Function;
public abstract class FeatureHangingSaplingBlock extends FeatureSaplingBlock { public abstract class FeatureHangingSaplingBlock extends FeatureSaplingBlock {
private static final VoxelShape SHAPE = Block.box(4, 2, 4, 12, 16, 12); private static final VoxelShape SHAPE = Block.box(4, 2, 4, 12, 16, 12);
public FeatureHangingSaplingBlock(Function<BlockState, Feature<?>> featureSupplier) { public FeatureHangingSaplingBlock(Function<BlockState, BCLFeature> featureSupplier) {
super(featureSupplier); super(featureSupplier);
} }
public FeatureHangingSaplingBlock(Function<BlockState, Feature<?>> featureSupplier, int light) { public FeatureHangingSaplingBlock(Function<BlockState, BCLFeature> featureSupplier,
int light) {
super(light, featureSupplier); super(light, featureSupplier);
} }
public FeatureHangingSaplingBlock(BlockBehaviour.Properties properties, public FeatureHangingSaplingBlock(BlockBehaviour.Properties properties,
Function<BlockState, Feature<?>> featureSupplier) { Function<BlockState, BCLFeature> featureSupplier) {
super(properties, featureSupplier); super(properties, featureSupplier);
} }

View file

@ -16,7 +16,6 @@ import net.minecraft.world.level.block.SaplingBlock;
import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.material.Material; import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.CollisionContext;
@ -42,9 +41,9 @@ import org.jetbrains.annotations.Nullable;
public class FeatureSaplingBlock extends SaplingBlock implements RenderLayerProvider, BlockModelProvider { public class FeatureSaplingBlock extends SaplingBlock implements RenderLayerProvider, BlockModelProvider {
private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12); private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12);
private final Function<BlockState, Feature<?>> feature; private final Function<BlockState, BCLFeature> feature;
public FeatureSaplingBlock(Function<BlockState, Feature<?>> featureSupplier) { public FeatureSaplingBlock(Function<BlockState, BCLFeature> featureSupplier) {
this(FabricBlockSettings.of(Material.PLANT) this(FabricBlockSettings.of(Material.PLANT)
.collidable(false) .collidable(false)
.instabreak() .instabreak()
@ -54,7 +53,7 @@ public class FeatureSaplingBlock extends SaplingBlock implements RenderLayerProv
); );
} }
public FeatureSaplingBlock(int light, Function<BlockState, Feature<?>> featureSupplier) { public FeatureSaplingBlock(int light, Function<BlockState, BCLFeature> featureSupplier) {
this(FabricBlockSettings.of(Material.PLANT) this(FabricBlockSettings.of(Material.PLANT)
.collidable(false) .collidable(false)
.luminance(light) .luminance(light)
@ -65,12 +64,13 @@ public class FeatureSaplingBlock extends SaplingBlock implements RenderLayerProv
); );
} }
public FeatureSaplingBlock(BlockBehaviour.Properties properties, Function<BlockState, Feature<?>> featureSupplier) { public FeatureSaplingBlock(BlockBehaviour.Properties properties,
Function<BlockState, BCLFeature> featureSupplier) {
super(null, properties); super(null, properties);
this.feature = featureSupplier; this.feature = featureSupplier;
} }
protected Feature<?> getFeature(BlockState state) { protected BCLFeature getFeature(BlockState state) {
return feature.apply(state); return feature.apply(state);
} }
@ -97,7 +97,7 @@ public class FeatureSaplingBlock extends SaplingBlock implements RenderLayerProv
@Override @Override
public void advanceTree(ServerLevel world, BlockPos pos, BlockState blockState, RandomSource random) { public void advanceTree(ServerLevel world, BlockPos pos, BlockState blockState, RandomSource random) {
BCLFeature.place(getFeature(blockState), world, pos, random); getFeature(blockState).place(world, pos, random);
} }
@Override @Override

View file

@ -332,6 +332,10 @@ public class BlocksHelper {
return state.isAir(); return state.isAir();
} }
public static boolean isFreeOrReplaceable(BlockState state) {
return state.isAir() || state.getMaterial().isReplaceable();
}
public static boolean isFreeOrFluid(BlockState state) { public static boolean isFreeOrFluid(BlockState state) {
return state.isAir() || isFluid(state); return state.isAir() || isFluid(state);
} }