Enhanced new Feature Builders.

This commit is contained in:
Frank 2022-06-18 17:42:31 +02:00
parent c4875b992a
commit a1d6de908c
17 changed files with 880 additions and 133 deletions

View file

@ -147,7 +147,8 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource {
private static boolean isValidNetherBiome(Holder<Biome> biome, ResourceLocation location) {
return biome.unwrapKey().get().location().toString().equals("betternether:bone_reef");
return biome.unwrapKey().get().location().toString().contains("bone_reef");
// return NetherBiomes.canGenerateInNether(biome.unwrapKey().get()) ||
// biome.is(BiomeTags.IS_NETHER) ||
// BiomeAPI.wasRegisteredAsNetherBiome(location);

View file

@ -636,6 +636,19 @@ public class BiomeAPI {
addBiomeFeature(biome, feature.getDecoration(), feature.getPlacedFeature());
}
/**
* Adds new features to existing biome.
*
* @param biome {@link Biome} to add features in.
* @param feature {@link ConfiguredFeature} to add.
*/
public static void addBiomeFeature(
Holder<Biome> biome,
org.betterx.bclib.api.v3.levelgen.features.BCLFeature feature
) {
addBiomeFeature(biome, feature.getDecoration(), feature.getPlacedFeature());
}
/**
* Adds new features to existing biome.
*

View file

@ -73,6 +73,11 @@ public class BCLFeature<F extends Feature<FC>, FC extends FeatureConfiguration>
BCLib.makeID("condition"),
new ConditionFeature()
);
public static final Feature<PillarFeatureConfig> PILLAR = register(
BCLib.makeID("pillar"),
new PillarFeature()
);
private final Holder<PlacedFeature> placedFeature;
private final Decoration featureStep;
private final F feature;

View file

@ -0,0 +1,101 @@
package org.betterx.bclib.api.v2.levelgen.features.config;
import org.betterx.bclib.blocks.BCLBlockProperties;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.util.StringRepresentable;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
public class PillarFeatureConfig implements FeatureConfiguration {
@FunctionalInterface
public interface StateTransform {
int apply(int height, int maxHeight, BlockState inputState, BlockPos pos, RandomSource rnd);
}
public enum KnownTransformers implements StringRepresentable {
SIZE_DECREASE(
"size_decrease",
BCLBlockProperties.SIZE,
(height, maxHeight, state, pos, rnd) -> Math.max(0, Math.min(7, maxHeight - height))
),
SIZE_INCREASE(
"size_increase",
BCLBlockProperties.SIZE,
(height, maxHeight, state, pos, rnd) -> Math.max(0, Math.min(7, height))
);
public static final StringRepresentable.EnumCodec<KnownTransformers> CODEC = StringRepresentable
.fromEnum(KnownTransformers::values);
public final String name;
public final IntegerProperty property;
public final StateTransform transform;
KnownTransformers(String name, IntegerProperty property, StateTransform transform) {
this.name = name;
this.property = property;
this.transform = transform;
}
@Override
public String toString() {
return this.name;
}
@Override
public String getSerializedName() {
return this.name;
}
}
public static final Codec<PillarFeatureConfig> CODEC = RecordCodecBuilder.create(instance -> instance
.group(
IntProvider.CODEC.fieldOf("height").forGetter(o -> o.height),
Direction.CODEC.fieldOf("direction").orElse(Direction.UP).forGetter(o -> o.direction),
BlockPredicate.CODEC.fieldOf("allowed_placement").forGetter(o -> o.allowedPlacement),
BlockStateProvider.CODEC.fieldOf("state").forGetter(o -> o.stateProvider),
KnownTransformers.CODEC.fieldOf("transform").forGetter(o -> o.transformer)
)
.apply(instance, PillarFeatureConfig::new));
public final IntProvider height;
public final BlockStateProvider stateProvider;
public final KnownTransformers transformer;
public final Direction direction;
public final BlockPredicate allowedPlacement;
public PillarFeatureConfig(
IntProvider height,
Direction direction,
BlockPredicate allowedPlacement,
BlockStateProvider stateProvider,
KnownTransformers transformer
) {
this.height = height;
this.stateProvider = stateProvider;
this.transformer = transformer;
this.direction = direction;
this.allowedPlacement = allowedPlacement;
}
public BlockState transform(int currentHeight, int maxHeight, BlockPos pos, RandomSource rnd) {
BlockState state = stateProvider.getState(rnd, pos);
return state.setValue(
transformer.property,
transformer.transform.apply(currentHeight, maxHeight, state, pos, rnd)
);
}
}

View file

@ -0,0 +1,47 @@
package org.betterx.bclib.api.v2.levelgen.features.features;
import org.betterx.bclib.api.v2.levelgen.features.config.PillarFeatureConfig;
import org.betterx.bclib.util.BlocksHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
public class PillarFeature extends Feature<PillarFeatureConfig> {
public PillarFeature() {
super(PillarFeatureConfig.CODEC);
}
@Override
public boolean place(FeaturePlaceContext<PillarFeatureConfig> featurePlaceContext) {
int height;
final WorldGenLevel level = featurePlaceContext.level();
final PillarFeatureConfig config = featurePlaceContext.config();
final RandomSource rnd = featurePlaceContext.random();
int maxHeight = config.height.sample(rnd);
BlockPos.MutableBlockPos posnow = featurePlaceContext.origin().mutable();
for (height = 0; height < maxHeight; ++height) {
if (!config.allowedPlacement.test(level, posnow)) {
maxHeight = height - 1;
break;
}
posnow.move(config.direction);
}
if (maxHeight < 0) return false;
posnow = featurePlaceContext.origin().mutable();
for (height = 0; height < maxHeight; ++height) {
BlockState state = config.transform(height, maxHeight, posnow, rnd);
BlocksHelper.setWithoutUpdate(level, posnow, state);
posnow.move(config.direction);
}
return true;
}
}

View file

@ -0,0 +1,47 @@
package org.betterx.bclib.api.v2.levelgen.features.placement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.levelgen.placement.PlacementContext;
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import java.util.List;
import java.util.stream.Stream;
public class ForAll extends PlacementModifier {
public static final Codec<ForAll> CODEC = RecordCodecBuilder.create(instance -> instance
.group(
ExtraCodecs.nonEmptyList(PlacementModifier.CODEC.listOf())
.fieldOf("modifiers")
.forGetter(a -> a.modifiers)
)
.apply(instance, ForAll::new));
private final List<PlacementModifier> modifiers;
public ForAll(List<PlacementModifier> modifiers) {
this.modifiers = modifiers;
}
@Override
public Stream<BlockPos> getPositions(
PlacementContext placementContext,
RandomSource randomSource,
BlockPos blockPos
) {
Stream.Builder<BlockPos> stream = Stream.builder();
for (PlacementModifier p : modifiers) {
p.getPositions(placementContext, randomSource, blockPos).forEach(pp -> stream.add(pp));
}
return stream.build();
}
@Override
public PlacementModifierType<?> type() {
return PlacementModifiers.FOR_ALL;
}
}

View file

@ -42,6 +42,10 @@ public class Is extends PlacementFilter {
return new Is(predicate, Optional.of(Direction.DOWN.getNormal()));
}
public static Is above(BlockPredicate predicate) {
return new Is(predicate, Optional.of(Direction.UP.getNormal()));
}
@Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
WorldGenLevel level = ctx.getLevel();

View file

@ -0,0 +1,64 @@
package org.betterx.bclib.api.v2.levelgen.features.placement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.placement.PlacementContext;
import net.minecraft.world.level.levelgen.placement.PlacementFilter;
import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import java.util.Optional;
import org.jetbrains.annotations.NotNull;
public class IsNextTo extends PlacementFilter {
public static final Codec<IsNextTo> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
BlockPredicate.CODEC
.fieldOf("predicate")
.forGetter(cfg -> cfg.predicate),
Vec3i.CODEC
.optionalFieldOf("offset")
.forGetter(cfg -> Optional.of(cfg.offset))
)
.apply(instance, IsNextTo::new));
private final BlockPredicate predicate;
private final Vec3i offset;
public IsNextTo(BlockPredicate predicate) {
this(predicate, Optional.of(Vec3i.ZERO));
}
public IsNextTo(BlockPredicate predicate, Optional<Vec3i> offset) {
this(predicate, offset.orElse(Vec3i.ZERO));
}
public IsNextTo(@NotNull BlockPredicate predicate, @NotNull Vec3i offset) {
this.predicate = predicate;
this.offset = offset;
}
public static PlacementFilter simple(BlockPredicate predicate) {
return new IsBasin(predicate);
}
@Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
WorldGenLevel level = ctx.getLevel();
pos = pos.offset(this.offset);
return predicate.test(level, pos.west())
|| predicate.test(level, pos.east())
|| predicate.test(level, pos.north())
|| predicate.test(level, pos.south());
}
@Override
public PlacementModifierType<?> type() {
return PlacementModifiers.IS_NEXT_TO;
}
}

View file

@ -19,6 +19,11 @@ public class PlacementModifiers {
MinEmptyFilter.CODEC
);
public static final PlacementModifierType<ForAll> FOR_ALL = register(
"for_all",
ForAll.CODEC
);
public static final PlacementModifierType<FindSolidInDirection> SOLID_IN_DIR = register(
"solid_in_dir",
FindSolidInDirection.CODEC
@ -39,6 +44,11 @@ public class PlacementModifiers {
IsBasin.CODEC
);
public static final PlacementModifierType<IsNextTo> IS_NEXT_TO = register(
"is_next_to",
IsNextTo.CODEC
);
public static final PlacementModifierType<Is> IS = register(
"is",
Is.CODEC

View file

@ -32,6 +32,7 @@ public class CommonBlockTags {
public static final TagKey<Block> IS_OBSIDIAN = TagAPI.makeCommonBlockTag("is_obsidian");
public static final TagKey<Block> TERRAIN = TagAPI.makeCommonBlockTag("terrain");
public static final TagKey<Block> NETHER_TERRAIN = TagAPI.makeCommonBlockTag("nether_terrain");
static {
TagAPI.BLOCKS.add(END_STONES, Blocks.END_STONE);
@ -67,15 +68,31 @@ public class CommonBlockTags {
);
TagAPI.BLOCKS.addOtherTags(
TERRAIN,
NETHER_TERRAIN,
BlockTags.DRIPSTONE_REPLACEABLE,
BlockTags.BASE_STONE_OVERWORLD,
BlockTags.NYLIUM,
MYCELIUM,
END_STONES
);
TagAPI.BLOCKS.add(
NETHER_TERRAIN,
Blocks.MAGMA_BLOCK,
Blocks.GRAVEL,
Blocks.RED_SAND,
Blocks.GLOWSTONE,
Blocks.BONE_BLOCK,
Blocks.SCULK
);
TagAPI.BLOCKS.addOtherTags(
NETHER_TERRAIN,
NETHERRACK,
BlockTags.NYLIUM,
NETHER_STONES,
NETHER_ORES,
SOUL_GROUND,
NETHER_MYCELIUM,
MYCELIUM,
END_STONES
NETHER_MYCELIUM
);
}
}

View file

@ -33,4 +33,12 @@ public class BCLFeature<F extends Feature<FC>, FC extends FeatureConfiguration>
this.placedFeature = placed;
this.decoration = decoration;
}
public Holder<PlacedFeature> getPlacedFeature() {
return placedFeature;
}
public GenerationStep.Decoration getDecoration() {
return decoration;
}
}

View file

@ -1,28 +1,447 @@
package org.betterx.bclib.api.v3.levelgen.features;
import org.betterx.bclib.api.v2.levelgen.features.BCLFeature;
import org.betterx.bclib.api.v2.levelgen.features.config.PillarFeatureConfig;
import org.betterx.bclib.api.v2.levelgen.features.config.SequenceFeatureConfig;
import org.betterx.bclib.api.v2.levelgen.features.config.TemplateFeatureConfig;
import org.betterx.bclib.api.v2.levelgen.features.features.PillarFeature;
import org.betterx.bclib.api.v2.levelgen.features.features.TemplateFeature;
import org.betterx.bclib.api.v2.levelgen.structures.StructurePlacementType;
import org.betterx.bclib.api.v2.levelgen.structures.StructureWorldNBT;
import org.betterx.bclib.api.v2.poi.BCLPoiType;
import org.betterx.bclib.blocks.BCLBlockProperties;
import org.betterx.bclib.blocks.BlockProperties;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.util.valueproviders.ConstantInt;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.feature.*;
import net.minecraft.world.level.levelgen.feature.configurations.*;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import net.minecraft.world.level.levelgen.feature.stateproviders.WeightedStateProvider;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockMatchTest;
import net.minecraft.world.level.levelgen.structure.templatesystem.RuleTest;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import org.jetbrains.annotations.NotNull;
public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends FeatureConfiguration> {
private final ResourceLocation featureID;
private final F feature;
private BCLFeatureBuilder(ResourceLocation featureID, F feature) {
this.featureID = featureID;
this.feature = feature;
}
/**
* Starts a new {@link BCLFeature} builder.
*
* @param featureID {@link ResourceLocation} feature identifier.
* @param feature {@link Feature} to construct.
* @return {@link org.betterx.bclib.api.v2.levelgen.features.BCLFeatureBuilder} instance.
*/
public static <F extends Feature<FC>, FC extends FeatureConfiguration> WithConfiguration<F, FC> start(
ResourceLocation featureID,
F feature
) {
return new WithConfiguration(featureID, feature);
}
public static ForSimpleBlock<SimpleBlockFeature> start(
ResourceLocation featureID,
Block block
) {
return start(featureID, BlockStateProvider.simple(block));
}
public static ForSimpleBlock<SimpleBlockFeature> start(
ResourceLocation featureID,
BlockState state
) {
return start(featureID, BlockStateProvider.simple(state));
}
public static ForSimpleBlock<SimpleBlockFeature> start(
ResourceLocation featureID,
BlockStateProvider provider
) {
ForSimpleBlock<SimpleBlockFeature> builder = new ForSimpleBlock(
featureID,
Feature.SIMPLE_BLOCK,
provider
);
return builder;
}
public static RandomPatch startRandomPatch(
ResourceLocation featureID,
Holder<PlacedFeature> featureToPlace
) {
RandomPatch builder = new RandomPatch(
featureID,
Feature.RANDOM_PATCH,
featureToPlace
);
return builder;
}
public static NetherForrestVegetation startNetherVegetation(
ResourceLocation featureID
) {
NetherForrestVegetation builder = new NetherForrestVegetation(
featureID,
Feature.NETHER_FOREST_VEGETATION
);
return builder;
}
public static WithTemplates<Feature<TemplateFeatureConfig>> startWithTemplates(
ResourceLocation featureID
) {
WithTemplates<Feature<TemplateFeatureConfig>> builder = new WithTemplates<>(
featureID,
(TemplateFeature) org.betterx.bclib.api.v3.levelgen.features.BCLFeature.TEMPLATE
);
return builder;
}
public static AsBlockColumn<BlockColumnFeature> startColumn(
ResourceLocation featureID
) {
AsBlockColumn<BlockColumnFeature> builder = new AsBlockColumn<>(
featureID,
(BlockColumnFeature) Feature.BLOCK_COLUMN
);
return builder;
}
public static AsPillar startPillar(
ResourceLocation featureID,
PillarFeatureConfig.KnownTransformers transformer
) {
AsPillar builder = new AsPillar(
featureID,
(PillarFeature) BCLFeature.PILLAR,
transformer
);
return builder;
}
public static AsSequence<Feature<SequenceFeatureConfig>> startSequence(
ResourceLocation featureID
) {
AsSequence<Feature<SequenceFeatureConfig>> builder = new AsSequence<>(
featureID,
org.betterx.bclib.api.v3.levelgen.features.BCLFeature.SEQUENCE
);
return builder;
}
public static AsOre startOre(
ResourceLocation featureID
) {
AsOre builder = new AsOre(
featureID,
(OreFeature) Feature.ORE
);
return builder;
}
/**
* Internally used by the builder. Normally you should not have to call this method directly as it is
* handled by {@link #buildAndRegister()}
*
* @param id The ID to register this feature with
* @param cFeature The configured Feature
* @param <F> The Feature Class
* @param <FC> The FeatureConfiguration Class
* @return The Holder for the new Feature
*/
public static <F extends Feature<FC>, FC extends FeatureConfiguration> Holder<ConfiguredFeature<FC, F>> register(
ResourceLocation id,
ConfiguredFeature<FC, F> cFeature
) {
return (Holder<ConfiguredFeature<FC, F>>) (Object) BuiltinRegistries.register(
BuiltinRegistries.CONFIGURED_FEATURE,
id,
cFeature
);
}
public abstract FC createConfiguration();
protected BCLConfigureFeature<F, FC> buildAndRegister(BiFunction<ResourceLocation, ConfiguredFeature<FC, F>, Holder<ConfiguredFeature<FC, F>>> holderBuilder) {
FC config = createConfiguration();
if (config == null) {
throw new IllegalStateException("Feature configuration for " + featureID + " can not be null!");
}
ConfiguredFeature<FC, F> cFeature = new ConfiguredFeature<FC, F>(feature, config);
Holder<ConfiguredFeature<FC, F>> holder = holderBuilder.apply(featureID, cFeature);
return new BCLConfigureFeature<>(featureID, holder, true);
}
public BCLConfigureFeature<F, FC> buildAndRegister() {
return buildAndRegister(BCLFeatureBuilder::register);
}
public BCLConfigureFeature<F, FC> build() {
return buildAndRegister((id, cFeature) -> Holder.direct(cFeature));
}
public BCLInlinePlacedBuilder<F, FC> inlinePlace() {
BCLConfigureFeature<F, FC> f = build();
return BCLInlinePlacedBuilder.place(f);
}
public Holder<PlacedFeature> inlinePlace(BCLInlinePlacedBuilder<F, FC> placer) {
BCLConfigureFeature<F, FC> f = build();
return placer.build(f);
}
public static class AsOre extends BCLFeatureBuilder<OreFeature, OreConfiguration> {
private final List<OreConfiguration.TargetBlockState> targetStates = new LinkedList<>();
private int size = 6;
private float discardChanceOnAirExposure = 0;
private AsOre(ResourceLocation featureID, OreFeature feature) {
super(featureID, feature);
}
public AsOre add(Block containedIn, Block ore) {
return this.add(containedIn, ore.defaultBlockState());
}
public AsOre add(Block containedIn, BlockState ore) {
return this.add(new BlockMatchTest(containedIn), ore);
}
public AsOre add(RuleTest containedIn, Block ore) {
return this.add(containedIn, ore.defaultBlockState());
}
public AsOre add(RuleTest containedIn, BlockState ore) {
targetStates.add(OreConfiguration.target(
containedIn,
ore
));
return this;
}
public AsOre veinSize(int size) {
this.size = size;
return this;
}
public AsOre discardChanceOnAirExposure(float chance) {
this.discardChanceOnAirExposure = chance;
return this;
}
@Override
public OreConfiguration createConfiguration() {
return new OreConfiguration(targetStates, size, discardChanceOnAirExposure);
}
}
public static class AsPillar extends BCLFeatureBuilder<PillarFeature, PillarFeatureConfig> {
private final List<Holder<PlacedFeature>> features = new LinkedList<>();
private IntProvider height;
private BlockStateProvider stateProvider;
private final PillarFeatureConfig.KnownTransformers transformer;
private Direction direction = Direction.UP;
private BlockPredicate allowedPlacement = BlockPredicate.ONLY_IN_AIR_PREDICATE;
private AsPillar(
ResourceLocation featureID,
PillarFeature feature,
PillarFeatureConfig.KnownTransformers transformer
) {
super(featureID, feature);
this.transformer = transformer;
}
public AsPillar allowedPlacement(BlockPredicate predicate) {
this.allowedPlacement = predicate;
return this;
}
public AsPillar direction(Direction v) {
this.direction = v;
return this;
}
public AsPillar blockState(Block v) {
return blockState(BlockStateProvider.simple(v.defaultBlockState()));
}
public AsPillar blockState(BlockState v) {
return blockState(BlockStateProvider.simple(v));
}
public AsPillar blockState(BlockStateProvider v) {
this.stateProvider = v;
return this;
}
public AsPillar height(int v) {
this.height = ConstantInt.of(v);
return this;
}
public AsPillar height(IntProvider v) {
this.height = v;
return this;
}
@Override
public PillarFeatureConfig createConfiguration() {
if (stateProvider == null) {
throw new IllegalStateException("A Pillar Features need a stateProvider");
}
if (height == null) {
throw new IllegalStateException("A Pillar Features need a height");
}
return new PillarFeatureConfig(height, direction, allowedPlacement, stateProvider, transformer);
}
}
public static class AsSequence<FF extends Feature<SequenceFeatureConfig>> extends BCLFeatureBuilder<FF, SequenceFeatureConfig> {
private final List<Holder<PlacedFeature>> features = new LinkedList<>();
private AsSequence(ResourceLocation featureID, FF feature) {
super(featureID, feature);
}
public AsSequence add(org.betterx.bclib.api.v3.levelgen.features.BCLFeature p) {
return add(p.placedFeature);
}
public AsSequence add(Holder<PlacedFeature> p) {
features.add(p);
return this;
}
@Override
public SequenceFeatureConfig createConfiguration() {
return new SequenceFeatureConfig(features);
}
}
public static class AsBlockColumn<FF extends Feature<BlockColumnConfiguration>> extends BCLFeatureBuilder<FF, BlockColumnConfiguration> {
private final List<BlockColumnConfiguration.Layer> layers = new LinkedList<>();
private Direction direction = Direction.UP;
private BlockPredicate allowedPlacement = BlockPredicate.ONLY_IN_AIR_PREDICATE;
private boolean prioritizeTip = false;
private AsBlockColumn(ResourceLocation featureID, FF feature) {
super(featureID, feature);
}
public AsBlockColumn<FF> add(int height, Block block) {
return add(ConstantInt.of(height), BlockStateProvider.simple(block));
}
public AsBlockColumn<FF> add(int height, BlockState state) {
return add(ConstantInt.of(height), BlockStateProvider.simple(state));
}
public AsBlockColumn<FF> add(int height, BlockStateProvider state) {
return add(ConstantInt.of(height), state);
}
public AsBlockColumn<FF> add(IntProvider height, Block block) {
return add(height, BlockStateProvider.simple(block));
}
public AsBlockColumn<FF> add(IntProvider height, BlockState state) {
return add(height, BlockStateProvider.simple(state));
}
public AsBlockColumn<FF> add(IntProvider height, BlockStateProvider state) {
layers.add(new BlockColumnConfiguration.Layer(height, state));
return this;
}
public AsBlockColumn<FF> addTripleShape(BlockState state, IntProvider midHeight) {
return this
.add(1, state.setValue(BCLBlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.BOTTOM))
.add(midHeight, state.setValue(BCLBlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.MIDDLE))
.add(1, state.setValue(BCLBlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.TOP));
}
public AsBlockColumn<FF> addTripleShapeUpsideDown(BlockState state, IntProvider midHeight) {
return this
.add(1, state.setValue(BCLBlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.TOP))
.add(midHeight, state.setValue(BCLBlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.MIDDLE))
.add(1, state.setValue(BCLBlockProperties.TRIPLE_SHAPE, BlockProperties.TripleShape.BOTTOM));
}
public AsBlockColumn<FF> direction(Direction dir) {
direction = dir;
return this;
}
public AsBlockColumn<FF> prioritizeTip() {
return this.prioritizeTip(true);
}
public AsBlockColumn<FF> prioritizeTip(boolean v) {
prioritizeTip = v;
return this;
}
public AsBlockColumn<FF> allowedPlacement(BlockPredicate v) {
allowedPlacement = v;
return this;
}
@Override
public BlockColumnConfiguration createConfiguration() {
return new BlockColumnConfiguration(layers, direction, allowedPlacement, prioritizeTip);
}
}
public static class WithTemplates<FF extends Feature<TemplateFeatureConfig>> extends BCLFeatureBuilder<FF, TemplateFeatureConfig> {
private final List<StructureWorldNBT> templates = new LinkedList<>();
private WithTemplates(ResourceLocation featureID, FF feature) {
super(featureID, feature);
}
public WithTemplates<FF> add(
ResourceLocation location,
int offsetY,
StructurePlacementType type,
float chance
) {
templates.add(TemplateFeatureConfig.cfg(location, offsetY, type, chance));
return this;
}
@Override
public TemplateFeatureConfig createConfiguration() {
return new TemplateFeatureConfig(templates);
}
}
public static class NetherForrestVegetation<FF extends Feature<NetherForestVegetationConfig>> extends BCLFeatureBuilder<FF, NetherForestVegetationConfig> {
private SimpleWeightedRandomList.Builder<BlockState> blocks;
private WeightedStateProvider stateProvider;
@ -64,7 +483,7 @@ public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends Featur
throw new IllegalStateException("You can not add new state once a WeightedStateProvider was built. (" + state + ", " + weight + ")");
}
if (blocks == null) {
blocks = SimpleWeightedRandomList.<BlockState>builder();
blocks = SimpleWeightedRandomList.builder();
}
blocks.add(state, weight);
return this;
@ -90,10 +509,10 @@ public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends Featur
}
public static class RandomPatch<FF extends Feature<RandomPatchConfiguration>> extends BCLFeatureBuilder<FF, RandomPatchConfiguration> {
private final Holder<PlacedFeature> featureToPlace;
private int tries = 96;
private int xzSpread = 7;
private int ySpread = 3;
private final Holder<PlacedFeature> featureToPlace;
private RandomPatch(
@NotNull ResourceLocation featureID,
@ -104,6 +523,17 @@ public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends Featur
this.featureToPlace = featureToPlace;
}
public RandomPatch likeDefaultNetherVegetation() {
return likeDefaultNetherVegetation(8, 4);
}
public RandomPatch likeDefaultNetherVegetation(int xzSpread, int ySpread) {
this.xzSpread = xzSpread;
this.ySpread = ySpread;
tries = xzSpread * xzSpread;
return this;
}
public RandomPatch tries(int v) {
tries = v;
return this;
@ -163,127 +593,6 @@ public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends Featur
return new SimpleBlockConfiguration(provider);
}
}
private final ResourceLocation featureID;
private final F feature;
private BCLFeatureBuilder(ResourceLocation featureID, F feature) {
this.featureID = featureID;
this.feature = feature;
}
/**
* Starts a new {@link BCLFeature} builder.
*
* @param featureID {@link ResourceLocation} feature identifier.
* @param feature {@link Feature} to construct.
* @return {@link org.betterx.bclib.api.v2.levelgen.features.BCLFeatureBuilder} instance.
*/
public static <F extends Feature<FC>, FC extends FeatureConfiguration> WithConfiguration<F, FC> start(
ResourceLocation featureID,
F feature
) {
return new WithConfiguration(featureID, feature);
}
public static ForSimpleBlock start(
ResourceLocation featureID,
Block block
) {
return start(featureID, BlockStateProvider.simple(block));
}
public static ForSimpleBlock start(
ResourceLocation featureID,
BlockState state
) {
return start(featureID, BlockStateProvider.simple(state));
}
public static ForSimpleBlock start(
ResourceLocation featureID,
BlockStateProvider provider
) {
ForSimpleBlock builder = new ForSimpleBlock(
featureID,
Feature.SIMPLE_BLOCK,
provider
);
return builder;
}
public static RandomPatch startRandomPatch(
ResourceLocation featureID,
Holder<PlacedFeature> featureToPlace
) {
RandomPatch builder = new RandomPatch(
featureID,
Feature.RANDOM_PATCH,
featureToPlace
);
return builder;
}
public static NetherForrestVegetation startNetherVegetation(
ResourceLocation featureID
) {
NetherForrestVegetation builder = new NetherForrestVegetation(
featureID,
Feature.NETHER_FOREST_VEGETATION
);
return builder;
}
public abstract FC createConfiguration();
protected BCLConfigureFeature<F, FC> buildAndRegister(BiFunction<ResourceLocation, ConfiguredFeature<FC, F>, Holder<ConfiguredFeature<FC, F>>> holderBuilder) {
FC config = createConfiguration();
if (config == null) {
throw new IllegalStateException("Feature configuration for " + featureID + " can not be null!");
}
ConfiguredFeature<FC, F> cFeature = new ConfiguredFeature<FC, F>(feature, config);
Holder<ConfiguredFeature<FC, F>> holder = holderBuilder.apply(featureID, cFeature);
return new BCLConfigureFeature<>(featureID, holder, true);
}
public BCLConfigureFeature<F, FC> buildAndRegister() {
return buildAndRegister(BCLFeatureBuilder::register);
}
public BCLConfigureFeature<F, FC> build() {
return buildAndRegister((id, cFeature) -> Holder.direct(cFeature));
}
public BCLInlinePlacedBuilder<F, FC> inlinePlace() {
BCLConfigureFeature<F, FC> f = build();
return BCLInlinePlacedBuilder.place(f);
}
public Holder<PlacedFeature> inlinePlace(BCLInlinePlacedBuilder<F, FC> placer) {
BCLConfigureFeature<F, FC> f = build();
return placer.build(f);
}
/**
* Internally used by the builder. Normally you should not have to call this method directly as it is
* handled by {@link #buildAndRegister()}
*
* @param id The ID to register this feature with
* @param cFeature The configured Feature
* @param <F> The Feature Class
* @param <FC> The FeatureConfiguration Class
* @return The Holder for the new Feature
*/
public static <F extends Feature<FC>, FC extends FeatureConfiguration> Holder<ConfiguredFeature<FC, F>> register(
ResourceLocation id,
ConfiguredFeature<FC, F> cFeature
) {
return (Holder<ConfiguredFeature<FC, F>>) (Object) BuiltinRegistries.register(
BuiltinRegistries.CONFIGURED_FEATURE,
id,
cFeature
);
}
}

View file

@ -80,4 +80,5 @@ public class BCLInlinePlacedBuilder<F extends Feature<FC>, FC extends FeatureCon
return PlacementUtils.inlinePlaced(feature, configuration, modifiers);
}
}

View file

@ -65,7 +65,7 @@ public class BCLPlacedFeatureBuilder<F extends Feature<FC>, FC extends FeatureCo
*
* @return created {@link BCLFeature} instance.
*/
protected Holder<PlacedFeature> build() {
public Holder<PlacedFeature> build() {
Holder<PlacedFeature> p = PlacementUtils.register(
featureID.toString(),
cFeature.configuredFeature,

View file

@ -0,0 +1,15 @@
package org.betterx.bclib.api.v3.levelgen.features;
import org.betterx.bclib.api.v2.tag.CommonBlockTags;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.material.Fluids;
public class BlockPredicates {
public static final BlockPredicate ONLY_NYLIUM = BlockPredicate.matchesTag(BlockTags.NYLIUM);
public static final BlockPredicate ONLY_NETHER_GROUND = BlockPredicate.matchesTag(CommonBlockTags.NETHER_TERRAIN);
public static final BlockPredicate ONLY_GROUND = BlockPredicate.matchesTag(CommonBlockTags.TERRAIN);
public static final BlockPredicate ONLY_LAVA = BlockPredicate.matchesFluids(Fluids.LAVA);
}

View file

@ -1,6 +1,7 @@
package org.betterx.bclib.api.v3.levelgen.features;
import org.betterx.bclib.api.v2.levelgen.features.config.PlaceFacingBlockConfig;
import org.betterx.bclib.api.v2.levelgen.features.features.SequenceFeature;
import org.betterx.bclib.api.v2.levelgen.features.placement.*;
import org.betterx.bclib.api.v2.tag.CommonBlockTags;
@ -11,9 +12,14 @@ import net.minecraft.data.worldgen.placement.PlacementUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.RandomPatchFeature;
import net.minecraft.world.level.levelgen.feature.SimpleBlockFeature;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import net.minecraft.world.level.levelgen.placement.*;
import net.minecraft.world.level.material.Material;
@ -334,14 +340,82 @@ abstract class CommonPlacedFeatureBuilder<F extends Feature<FC>, FC extends Feat
return modifier(PlacementUtils.HEIGHTMAP_WORLD_SURFACE);
}
public T onlyWhenEmpty() {
public T extendXZ(int xzSpread) {
IntProvider xz = UniformInt.of(0, xzSpread);
return (T) modifier(
new ForAll(List.of(
new Extend(Direction.NORTH, xz),
new Extend(Direction.SOUTH, xz),
new Extend(Direction.EAST, xz),
new Extend(Direction.WEST, xz)
)),
new ForAll(List.of(
new Extend(Direction.EAST, xz),
new Extend(Direction.WEST, xz),
new Extend(Direction.NORTH, xz),
new Extend(Direction.SOUTH, xz)
))
);
}
public T extendXYZ(int xzSpread, int ySpread) {
IntProvider xz = UniformInt.of(0, xzSpread);
return (T) extendXZ(xzSpread).extendDown(1, ySpread);
}
public T isEmpty() {
return modifier(BlockPredicateFilter.forPredicate(BlockPredicate.ONLY_IN_AIR_PREDICATE));
}
public T filtered(BlockPredicate predicate) {
public T is(BlockPredicate predicate) {
return modifier(BlockPredicateFilter.forPredicate(predicate));
}
public T isNextTo(BlockPredicate predicate) {
return modifier(new IsNextTo(predicate));
}
public T belowIsNextTo(BlockPredicate predicate) {
return modifier(new IsNextTo(predicate, Direction.DOWN.getNormal()));
}
public T isNextTo(BlockPredicate predicate, Vec3i offset) {
return modifier(new IsNextTo(predicate, offset));
}
public T isOn(BlockPredicate predicate) {
return modifier(Is.below(predicate));
}
public T isEmptyAndOn(BlockPredicate predicate) {
return (T) this.isEmpty().isOn(predicate);
}
public T isEmptyAndOnNylium() {
return isEmptyAndOn(BlockPredicates.ONLY_NYLIUM);
}
public T isEmptyAndOnNetherGround() {
return isEmptyAndOn(BlockPredicates.ONLY_NETHER_GROUND);
}
public T isUnder(BlockPredicate predicate) {
return modifier(Is.above(predicate));
}
public T isEmptyAndUnder(BlockPredicate predicate) {
return (T) this.isEmpty().isUnder(predicate);
}
public T isEmptyAndUnderNylium() {
return isEmptyAndUnder(BlockPredicates.ONLY_NYLIUM);
}
public T isEmptyAndUnderNetherGround() {
return isEmptyAndUnder(BlockPredicates.ONLY_NETHER_GROUND);
}
public T vanillaNetherGround(int countPerLayer) {
return (T) this.onEveryLayer(countPerLayer).onlyInBiome();
}
@ -369,7 +443,26 @@ abstract class CommonPlacedFeatureBuilder<F extends Feature<FC>, FC extends Feat
*/
abstract Holder<PlacedFeature> build();
public BCLFeatureBuilder.RandomPatch inRandomPatch(ResourceLocation id) {
public BCLFeatureBuilder.RandomPatch<RandomPatchFeature> inRandomPatch(ResourceLocation id) {
return BCLFeatureBuilder.startRandomPatch(id, build());
}
public BCLFeatureBuilder.AsSequence<SequenceFeature> then(ResourceLocation id) {
return BCLFeatureBuilder.startSequence(id).add(build());
}
public BCLFeatureBuilder.ForSimpleBlock<SimpleBlockFeature> putBlock(ResourceLocation id, Block block) {
return BCLFeatureBuilder.start(id, block);
}
public BCLFeatureBuilder.ForSimpleBlock<SimpleBlockFeature> putBlock(ResourceLocation id, BlockState state) {
return BCLFeatureBuilder.start(id, state);
}
public BCLFeatureBuilder.ForSimpleBlock<SimpleBlockFeature> putBlock(
ResourceLocation id,
BlockStateProvider provider
) {
return BCLFeatureBuilder.start(id, provider);
}
}

View file

@ -0,0 +1,12 @@
package org.betterx.bclib.blocks;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
public class BCLBlockProperties {
public static final EnumProperty<BlockProperties.TripleShape> TRIPLE_SHAPE = BlockProperties.TRIPLE_SHAPE;
public static final IntegerProperty SIZE = IntegerProperty.create("size", 0, 7);
public static final IntegerProperty AGE_THREE = BlockStateProperties.AGE_2;
public static final IntegerProperty AGE_FOUR = BlockStateProperties.AGE_3;
}