New combined Feature Strategy and Features for BoneReef

This commit is contained in:
Frank 2022-06-04 01:43:25 +02:00
parent 4a95927e1c
commit c14928ea80
11 changed files with 260 additions and 31 deletions

View file

@ -84,7 +84,7 @@ public class BCLCommonFeatures {
int chance) {
return BCLFeatureBuilder.start(id, feature)
.decoration(decoration)
.oncePerChunks(chance)
.onceEvery(chance)
.squarePlacement()
.onlyInBiome()
.buildAndRegister();
@ -136,7 +136,7 @@ public class BCLCommonFeatures {
BCLFeatureBuilder builder = BCLFeatureBuilder.start(id, Feature.ORE).decoration(Decoration.UNDERGROUND_ORES);
if (rare) {
builder.oncePerChunks(veins);
builder.onceEvery(veins);
} else {
builder.count(veins);
}

View file

@ -11,11 +11,13 @@ import net.minecraft.world.level.levelgen.GenerationStep.Decoration;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.RandomFeatureConfiguration;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.features.config.ScatterFeatureConfig;
import org.betterx.bclib.api.features.config.TemplateFeatureConfig;
import java.util.Map.Entry;
import java.util.Optional;
@ -23,8 +25,14 @@ import java.util.Optional;
public class BCLFeature {
public static final Feature<ScatterFeatureConfig.OnSolid> SCATTER_ON_SOLID = register(
BCLib.makeID("scatter_on_solid"),
new ScatterFeature<>(ScatterFeatureConfig.OnSolid.CODEC)
);
new ScatterFeature<>(ScatterFeatureConfig.OnSolid.CODEC));
public static final Feature<RandomFeatureConfiguration> RANDOM_SELECT = register(
BCLib.makeID("random_select"),
new WeightedRandomSelectorFeature());
public static final Feature<TemplateFeatureConfig> TEMPLATE = register(BCLib.makeID("template"),
new TemplateFeature(
TemplateFeatureConfig.CODEC));
private final Holder<PlacedFeature> placedFeature;
private final Decoration featureStep;
private final Feature<?> feature;
@ -62,12 +70,12 @@ public class BCLFeature {
Holder<ConfiguredFeature<?, ?>> configuredFeature;
if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) {
configuredFeature = (Holder<ConfiguredFeature<?, ?>>) (Object) FeatureUtils.register(id.toString(),
feature,
configuration);
feature,
configuration);
} else {
configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE
.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(),
id))
id))
.orElseThrow();
}
@ -75,7 +83,7 @@ public class BCLFeature {
return PlacementUtils.register(id.toString(), configuredFeature, modifiers);
} else {
return BuiltinRegistries.PLACED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.PLACED_FEATURE.key(),
id)).orElseThrow();
id)).orElseThrow();
}
}

View file

@ -119,13 +119,13 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
}
/**
* Will place feature once in certain amount of chunks (in average).
* Will place feature once every n-th attempts (in average).
*
* @param chunks amount of chunks.
* @param n amount of attempts.
* @return same {@link BCLFeatureBuilder} instance.
*/
public BCLFeatureBuilder oncePerChunks(int chunks) {
return modifier(RarityFilter.onAverageOnceEvery(chunks));
public BCLFeatureBuilder onceEvery(int n) {
return modifier(RarityFilter.onAverageOnceEvery(n));
}
/**

View file

@ -0,0 +1,25 @@
package org.betterx.bclib.api.features;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import com.mojang.serialization.Codec;
import org.betterx.bclib.api.features.config.BlockPlaceFeatureConfig;
import org.betterx.bclib.util.BlocksHelper;
import java.util.Optional;
public class BlockPlaceFeature<FC extends BlockPlaceFeatureConfig> extends Feature<FC> {
public BlockPlaceFeature(Codec<FC> codec) {
super(codec);
}
@Override
public boolean place(FeaturePlaceContext<FC> ctx) {
Optional<BlockState> state = ctx.config().getRandomBlock(ctx.random());
if (state.isPresent())
BlocksHelper.setWithoutUpdate(ctx.level(), ctx.origin(), state.get());
return true;
}
}

View file

@ -0,0 +1,93 @@
package org.betterx.bclib.api.features;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.feature.Feature;
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.RandomPatchConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.SimpleBlockConfiguration;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import org.betterx.bclib.api.features.config.ScatterFeatureConfig;
public class FastFeatures {
public static BCLFeature vine(ResourceLocation location,
boolean onFloor,
boolean sparse,
ScatterFeatureConfig.Builder builder) {
return scatter(location, onFloor, sparse, builder, BCLFeature.SCATTER_ON_SOLID);
}
public static BCLFeature scatter(ResourceLocation location,
boolean onFloor,
boolean sparse,
ScatterFeatureConfig.Builder builder,
Feature scatterFeature) {
BCLFeatureBuilder fBuilder = BCLFeatureBuilder.start(location, scatterFeature);
if (onFloor) {
fBuilder.findSolidFloor(3).isEmptyAbove2();
builder.onFloor();
} else {
fBuilder.findSolidCeil(3).isEmptyBelow2();
builder.onCeil();
}
if (sparse) {
fBuilder.onceEvery(3);
}
return fBuilder
.is(BlockPredicate.ONLY_IN_AIR_PREDICATE)
.buildAndRegister(builder.build());
}
public static BCLFeature patch(ResourceLocation location, Block block) {
return patch(location, block, 96, 7, 3);
}
public static BCLFeature
patch(ResourceLocation location, Block block, int attempts, int xzSpread, int ySpread) {
return patch(location,
attempts,
xzSpread,
ySpread,
Feature.SIMPLE_BLOCK,
new SimpleBlockConfiguration(BlockStateProvider.simple(block)));
}
public static BCLFeature
patch(ResourceLocation location, Feature<NoneFeatureConfiguration> feature) {
return patch(location, 96, 7, 3, feature, FeatureConfiguration.NONE);
}
public static BCLFeature
patch(ResourceLocation location,
int attempts,
int xzSpread,
int ySpread,
Feature<NoneFeatureConfiguration> feature) {
return patch(location, attempts, xzSpread, ySpread, feature, FeatureConfiguration.NONE);
}
public static <FC extends FeatureConfiguration> BCLFeature
patch(ResourceLocation location,
int attempts,
int xzSpread,
int ySpread,
Feature<FC> feature,
FC config) {
ResourceLocation patchLocation = new ResourceLocation(location.getNamespace(), location.getPath() + "_patch");
final BCLFeature SINGLE = BCLFeatureBuilder
.start(location, feature)
.findSolidFloor(Math.min(12, ySpread))
.is(BlockPredicate.ONLY_IN_AIR_PREDICATE)
.buildAndRegister(config);
return BCLFeatureBuilder
.start(patchLocation, Feature.RANDOM_PATCH)
.buildAndRegister(new RandomPatchConfiguration(attempts, xzSpread, ySpread, SINGLE.getPlacedFeature()));
}
}

View file

@ -161,7 +161,9 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
RandomSource random) {
if (BlocksHelper.isFreeSpace(level, origin, direction, height, BlocksHelper::isFree)) {
createPatchOfBaseBlocks(level, random, basePos, config);
buildPillar(level, origin, direction, height, config, random);
if (config.bottomBlock.canSurvive(level, origin)) {
buildPillar(level, origin, direction, height, config, random);
}
}
}

View file

@ -7,15 +7,11 @@ import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import com.mojang.serialization.Codec;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.features.config.TemplateFeatureConfig;
import org.betterx.bclib.world.structures.StructureNBT;
import org.betterx.bclib.world.structures.StructureWorldNBT;
public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<FC> {
public static final Feature<TemplateFeatureConfig> INSTANCE = BCLFeature.register(BCLib.makeID("template"),
new TemplateFeature(
TemplateFeatureConfig.CODEC));
public static <T extends TemplateFeatureConfig> BCLFeature createAndRegisterRare(ResourceLocation location,
TemplateFeatureConfig configuration,
@ -23,9 +19,9 @@ public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<F
return BCLFeatureBuilder
.start(location, INSTANCE)
.start(location, BCLFeature.TEMPLATE)
.decoration(GenerationStep.Decoration.SURFACE_STRUCTURES)
.oncePerChunks(onceEveryChunk) //discard neighboring chunks
.onceEvery(onceEveryChunk) //discard neighboring chunks
.count(16) //try 16 placements in chunk
.squarePlacement() //randomize x/z in chunk
.randomHeight10FromFloorCeil() //randomize height 10 above and 10 below max vertical
@ -40,7 +36,7 @@ public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<F
TemplateFeatureConfig configuration,
int count) {
return BCLFeatureBuilder
.start(location, INSTANCE)
.start(location, BCLFeature.TEMPLATE)
.decoration(GenerationStep.Decoration.SURFACE_STRUCTURES)
.count(count)
.squarePlacement()

View file

@ -0,0 +1,39 @@
package org.betterx.bclib.api.features;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.WeightedPlacedFeature;
import net.minecraft.world.level.levelgen.feature.configurations.RandomFeatureConfiguration;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
public class WeightedRandomSelectorFeature extends Feature<RandomFeatureConfiguration> {
public WeightedRandomSelectorFeature() {
super(RandomFeatureConfiguration.CODEC);
}
public boolean place(FeaturePlaceContext<RandomFeatureConfiguration> ctx) {
final WorldGenLevel level = ctx.level();
final ChunkGenerator generator = ctx.chunkGenerator();
final RandomFeatureConfiguration cfg = ctx.config();
final RandomSource random = ctx.random();
final BlockPos pos = ctx.origin();
PlacedFeature selected = cfg.defaultFeature.value();
if (!cfg.features.isEmpty()) {
final float totalWeight = cfg.features.stream().map(w -> w.chance).reduce(0.0f, (p, c) -> p + c);
float bar = random.nextFloat() * totalWeight;
for (WeightedPlacedFeature f : cfg.features) {
selected = f.feature.value();
bar -= f.chance;
if (bar < 0) break;
}
}
return selected.place(level, generator, random, pos);
}
}

View file

@ -0,0 +1,59 @@
package org.betterx.bclib.api.features.config;
import net.minecraft.util.RandomSource;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import java.util.List;
import java.util.Optional;
public class BlockPlaceFeatureConfig implements FeatureConfiguration {
public static final Codec<BlockPlaceFeatureConfig> CODEC = SimpleWeightedRandomList
.wrappedCodec(BlockState.CODEC)
.comapFlatMap(BlockPlaceFeatureConfig::create, cfg -> cfg.weightedList)
.fieldOf("entries").codec();
private final SimpleWeightedRandomList<BlockState> weightedList;
private static DataResult<BlockPlaceFeatureConfig> create(SimpleWeightedRandomList<BlockState> simpleWeightedRandomList) {
if (simpleWeightedRandomList.isEmpty()) {
return DataResult.error("BlockPlaceFeatureConfig with no states");
}
return DataResult.success(new BlockPlaceFeatureConfig(simpleWeightedRandomList));
}
private static SimpleWeightedRandomList<BlockState> convert(List<BlockState> states) {
var builder = SimpleWeightedRandomList.<BlockState>builder();
for (BlockState s : states) builder.add(s, 1);
return builder.build();
}
public BlockPlaceFeatureConfig(Block block) {
this(block.defaultBlockState());
}
public BlockPlaceFeatureConfig(BlockState state) {
this(SimpleWeightedRandomList
.<BlockState>builder()
.add(state, 1)
.build());
}
public BlockPlaceFeatureConfig(List<BlockState> states) {
this(convert(states));
}
public BlockPlaceFeatureConfig(SimpleWeightedRandomList<BlockState> blocks) {
this.weightedList = blocks;
}
public Optional<BlockState> getRandomBlock(RandomSource random) {
return this.weightedList.getRandomValue(random);
}
}

View file

@ -10,7 +10,6 @@ import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import org.betterx.bclib.api.tag.CommonBlockTags;
import org.betterx.bclib.util.BlocksHelper;
import java.util.List;
@ -26,7 +25,7 @@ public class FindSolidInDirection extends PlacementModifier {
.forGetter(a -> a.direction),
Codec.intRange(1, 32).fieldOf("dist").orElse(12).forGetter((p) -> p.maxSearchDistance))
.apply(instance,
FindSolidInDirection::new));
FindSolidInDirection::new));
protected static final FindSolidInDirection DOWN = new FindSolidInDirection(Direction.DOWN, 6);
protected static final FindSolidInDirection UP = new FindSolidInDirection(Direction.UP, 6);
private final List<Direction> direction;
@ -70,11 +69,12 @@ public class FindSolidInDirection extends PlacementModifier {
BlockPos.MutableBlockPos POS = blockPos.mutable();
Direction d = randomDirection(randomSource);
if (BlocksHelper.findOnSurroundingSurface(placementContext.getLevel(),
POS,
d,
maxSearchDistance,
state -> state.is(CommonBlockTags.TERRAIN)))
POS,
d,
maxSearchDistance,
BlocksHelper::isTerrain)) {
return Stream.of(POS);
}
return Stream.of();
}

View file

@ -5,6 +5,7 @@ import net.minecraft.util.KeyDispatchDataCodec;
import net.minecraft.world.level.levelgen.SurfaceRules;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.surface.rules.SurfaceNoiseCondition;
import org.betterx.bclib.mixin.common.SurfaceRulesContextAccessor;
@ -14,8 +15,14 @@ import org.betterx.bclib.util.MHelper;
public class DoubleBlockSurfaceNoiseCondition extends SurfaceNoiseCondition {
public static final DoubleBlockSurfaceNoiseCondition CONDITION = new DoubleBlockSurfaceNoiseCondition(0);
private static final OpenSimplexNoise NOISE = new OpenSimplexNoise(4141);
public static final KeyDispatchDataCodec<DoubleBlockSurfaceNoiseCondition> CODEC = KeyDispatchDataCodec.of(Codec.DOUBLE.fieldOf(
"threshold").xmap(DoubleBlockSurfaceNoiseCondition::new, obj -> obj.threshold).codec());
public static final Codec<DoubleBlockSurfaceNoiseCondition> CODEC = RecordCodecBuilder.create(instance -> instance
.group(
Codec.DOUBLE.fieldOf("threshold").orElse(0.0).forGetter(o -> o.threshold)
)
.apply(instance, DoubleBlockSurfaceNoiseCondition::new));
public static final KeyDispatchDataCodec<DoubleBlockSurfaceNoiseCondition> KEY_CODEC = KeyDispatchDataCodec.of(
CODEC);
private final double threshold;
public DoubleBlockSurfaceNoiseCondition(double threshold) {
@ -24,7 +31,7 @@ public class DoubleBlockSurfaceNoiseCondition extends SurfaceNoiseCondition {
@Override
public KeyDispatchDataCodec<? extends SurfaceRules.ConditionSource> codec() {
return CODEC;
return KEY_CODEC;
}
private static int lastX = Integer.MIN_VALUE;
@ -47,7 +54,7 @@ public class DoubleBlockSurfaceNoiseCondition extends SurfaceNoiseCondition {
static {
Registry.register(Registry.CONDITION,
BCLib.makeID("doubleblock_surface"),
DoubleBlockSurfaceNoiseCondition.CODEC.codec());
BCLib.makeID("doubleblock_surface"),
DoubleBlockSurfaceNoiseCondition.CODEC);
}
}