Some Feature related fixes

This commit is contained in:
Frank 2022-06-03 00:08:26 +02:00
parent 6adf6486ac
commit 7b9936af05
16 changed files with 310 additions and 57 deletions

View file

@ -1,19 +1,20 @@
package org.betterx.bclib.api.features;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
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.levelgen.GenerationStep.Decoration;
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.placement.*;
import net.minecraft.world.level.material.Material;
import org.betterx.bclib.world.features.BCLFeature;
import org.betterx.bclib.world.features.placement.FindSolidInDirection;
import org.betterx.bclib.world.features.placement.IsEmptyAboveSampledFilter;
import org.betterx.bclib.world.features.placement.MinEmptyFilter;
import org.betterx.bclib.world.features.placement.*;
import java.util.ArrayList;
import java.util.List;
@ -137,6 +138,14 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
return modifier(InSquarePlacement.spread());
}
public BCLFeatureBuilder stencil() {
return modifier(Stencil.all());
}
public BCLFeatureBuilder stencilOneIn4() {
return modifier(Stencil.oneIn4());
}
/**
* Select random height that is 10 above min Build height and 10 below max generation height
*
@ -177,10 +186,38 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
return modifier(IsEmptyAboveSampledFilter.emptyAbove4());
}
public BCLFeatureBuilder isEmptyAbove() {
return modifier(IsEmptyAboveSampledFilter.emptyAbove());
}
public BCLFeatureBuilder isEmptyAbove(int d1, int d2) {
return modifier(new IsEmptyAboveSampledFilter(d1, d2));
}
public BCLFeatureBuilder onEveryLayer() {
return modifier(OnEveryLayer.simple());
}
public BCLFeatureBuilder spreadHorizontal(IntProvider p) {
return modifier(RandomOffsetPlacement.horizontal(p));
}
public BCLFeatureBuilder spreadVertical(IntProvider p) {
return modifier(RandomOffsetPlacement.horizontal(p));
}
public BCLFeatureBuilder spread(IntProvider horizontal, IntProvider vertical) {
return modifier(RandomOffsetPlacement.of(horizontal, vertical));
}
public BCLFeatureBuilder offset(Direction dir) {
return modifier(Offset.inDirection(dir));
}
public BCLFeatureBuilder offset(Vec3i dir) {
return modifier(new Offset(dir));
}
/**
* Cast a downward ray with max {@code distance} length to find the next solid Block.
*
@ -192,6 +229,22 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
return modifier(FindSolidInDirection.down(distance));
}
public BCLFeatureBuilder noiseBasedCount(float noiseLevel, int belowNoiseCount, int aboveNoiseCount) {
return modifier(NoiseThresholdCountPlacement.of(noiseLevel, belowNoiseCount, aboveNoiseCount));
}
public BCLFeatureBuilder extendDown(int min, int max) {
return modifier(new Extend(Direction.DOWN, UniformInt.of(min, max)));
}
public BCLFeatureBuilder inBasinOf(BlockPredicate... predicates) {
return modifier(new IsBasin(BlockPredicate.anyOf(predicates)));
}
public BCLFeatureBuilder is(BlockPredicate... predicates) {
return modifier(new Is(BlockPredicate.anyOf(predicates)));
}
public BCLFeatureBuilder findSolidCeil(int distance) {
return modifier(FindSolidInDirection.up(distance));
}

View file

@ -46,6 +46,7 @@ public class CommonBlockTags {
TagAPI.BLOCKS.add(SOUL_GROUND, Blocks.SOUL_SAND, Blocks.SOUL_SOIL);
TagAPI.BLOCKS.add(IS_OBSIDIAN, Blocks.OBSIDIAN, Blocks.CRYING_OBSIDIAN);
TagAPI.BLOCKS.add(TERRAIN, Blocks.MAGMA_BLOCK);
TagAPI.BLOCKS.addOtherTags(TERRAIN,
BlockTags.DRIPSTONE_REPLACEABLE,

View file

@ -28,11 +28,11 @@ public abstract class BiomeSourceMixin implements BiomeSourceAccessor {
@Inject(method = "<init>(Ljava/util/List;)V", at = @At("TAIL"))
public void bcl_init(List list, CallbackInfo ci) {
System.out.println("new BiomeSource (" + Integer.toHexString(hashCode()) + ", biomes=" + possibleBiomes().size() + ")");
if (possibleBiomes().size() == 27) {
System.out.println("Nether????");
} else if (possibleBiomes().size() == 2) {
System.out.println("Datapack Nether???");
}
// System.out.println("new BiomeSource (" + Integer.toHexString(hashCode()) + ", biomes=" + possibleBiomes().size() + ")");
// if (possibleBiomes().size() == 27) {
// System.out.println("Nether????");
// } else if (possibleBiomes().size() == 2) {
// System.out.println("Datapack Nether???");
// }
}
}

View file

@ -229,13 +229,39 @@ public class BlocksHelper {
return false;
}
public static boolean findSurroundingSurface(LevelAccessor level,
MutableBlockPos startPos,
Direction dir,
int length,
Predicate<BlockState> surface) {
for (int len = 0; len < length; len++) {
if (surface.test(level.getBlockState(startPos))) {
if (len == 0) { //we started inside of the surface
for (int lenUp = 0; lenUp < length; lenUp++) {
startPos.move(dir, -1);
if (!surface.test(level.getBlockState(startPos))) {
startPos.move(dir, 1);
return true;
}
}
return false;
}
return true;
}
startPos.move(dir, 1);
}
return false;
}
public static boolean isFreeSpace(LevelAccessor level,
BlockPos startPos,
Direction dir,
int length,
Predicate<BlockState> freeSurface) {
MutableBlockPos POS = startPos.mutable();
for (int len = 1; len < length; len++) {
for (int len = 0; len < length; len++) {
POS.move(dir, 1);
if (!freeSurface.test(level.getBlockState(POS))) {
return false;

View file

@ -110,13 +110,13 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
final double distNormalizer = (config.maxSpread * Math.sqrt(2));
final int tryCount = (int) Math.min(16, 4 * config.maxSpread * config.maxSpread);
final int tryCount = config.spreadCount.sample(random);
for (int i = 0; i < tryCount; i++) {
int x = origin.getX() + (int) (random.nextGaussian() * config.maxSpread);
int z = origin.getZ() + (int) (random.nextGaussian() * config.maxSpread);
POS.set(x, origin.getY(), z);
if (BlocksHelper.findSurface(level, POS, surfaceDirection, 4, config::isValidBase)) {
if (BlocksHelper.findSurroundingSurface(level, POS, surfaceDirection, 4, config::isValidBase)) {
int myHeight;
if (config.growWhileFree) {
myHeight = BlocksHelper.blockCount(level,

View file

@ -1,11 +1,14 @@
package org.betterx.bclib.world.features;
import net.minecraft.util.RandomSource;
import net.minecraft.util.valueproviders.ConstantInt;
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.feature.configurations.FeatureConfiguration;
import com.mojang.datafixers.util.Function14;
import com.mojang.datafixers.util.Function15;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import org.betterx.bclib.BCLib;
@ -14,7 +17,7 @@ import org.betterx.bclib.api.tag.CommonBlockTags;
import java.util.Optional;
public abstract class ScatterFeatureConfig implements FeatureConfiguration {
public interface Instancer<T extends ScatterFeatureConfig> extends Function14<BlockState, BlockState, BlockState, Optional<BlockState>, Float, Float, Float, Float, Integer, Integer, Float, Float, Float, Boolean, T> {
public interface Instancer<T extends ScatterFeatureConfig> extends Function15<BlockState, BlockState, BlockState, Optional<BlockState>, Float, Float, Float, Float, Integer, Integer, Float, Float, Float, Boolean, IntProvider, T> {
}
public final BlockState clusterBlock;
@ -31,6 +34,8 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
public final float sizeVariation;
public final float floorChance;
public final IntProvider spreadCount;
public final boolean growWhileFree;
public ScatterFeatureConfig(BlockState clusterBlock,
@ -46,7 +51,8 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
float maxSpread,
float sizeVariation,
float floorChance,
boolean growWhileFree) {
boolean growWhileFree,
IntProvider spreadCount) {
this.clusterBlock = clusterBlock;
this.tipBlock = tipBlock == null ? clusterBlock : tipBlock;
this.bottomBlock = bottomBlock == null ? clusterBlock : bottomBlock;
@ -61,6 +67,7 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
this.sizeVariation = sizeVariation;
this.floorChance = floorChance;
this.growWhileFree = growWhileFree;
this.spreadCount = spreadCount;
}
@ -137,7 +144,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
.BOOL
.fieldOf("grow_while_empty")
.orElse(false)
.forGetter((T cfg) -> cfg.growWhileFree)
.forGetter((T cfg) -> cfg.growWhileFree),
IntProvider.codec(0, 64)
.fieldOf("length")
.orElse(UniformInt.of(0, 3))
.forGetter(cfg -> cfg.spreadCount)
)
.apply(instance, instancer)
);
@ -158,6 +169,7 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
private float sizeVariation = 0;
private float floorChance = 0.5f;
private boolean growWhileFree = false;
public IntProvider spreadCount = ConstantInt.of(0);
private final Instancer<T> instancer;
public Builder(Instancer<T> instancer) {
@ -251,6 +263,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
}
public Builder<T> spread(float maxSpread, float sizeVariation) {
return spread(maxSpread, sizeVariation, ConstantInt.of((int) Math.min(16, 4 * maxSpread * maxSpread)));
}
public Builder<T> spread(float maxSpread, float sizeVariation, IntProvider spreadCount) {
this.spreadCount = spreadCount; //
this.maxSpread = maxSpread;
this.sizeVariation = sizeVariation;
return this;
@ -286,7 +303,8 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
this.maxSpread,
this.sizeVariation,
this.floorChance,
this.growWhileFree
this.growWhileFree,
this.spreadCount
);
}
}
@ -307,7 +325,8 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
float maxSpread,
float sizeVariation,
float floorChance,
boolean growWhileFree) {
boolean growWhileFree,
IntProvider spreadCount) {
super(clusterBlock,
tipBlock,
bottomBlock,
@ -321,9 +340,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
maxSpread,
sizeVariation,
floorChance,
growWhileFree);
growWhileFree,
spreadCount);
}
public static Builder<OnSolid> startOnSolid() {
return Builder.start(OnSolid::new);
}

View file

@ -2,7 +2,6 @@ package org.betterx.bclib.world.features;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
@ -37,14 +36,6 @@ public abstract class SurfaceFeature<T extends FeatureConfiguration> extends Fea
minHeight(ctx),
this::isValidSurface);
if (pos.isPresent()) {
int y2 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE_WG, ctx.origin().getX(), ctx.origin().getZ());
int y3 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE, ctx.origin().getX(), ctx.origin().getZ());
int y4 = ctx.level().getHeight(Heightmap.Types.OCEAN_FLOOR, ctx.origin().getX(), ctx.origin().getZ());
int y5 = ctx.level().getHeight(Heightmap.Types.OCEAN_FLOOR_WG, ctx.origin().getX(), ctx.origin().getZ());
System.out.println("Surfaces:" + pos
.get()
.getY() + ", " + y2 + ", " + y3 + ", " + y4 + ", " + y5 + ", " + ctx.origin().getY());
generate(pos.get(), ctx);
return true;
}

View file

@ -12,8 +12,6 @@ import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Extend extends PlacementModifier {
@ -42,12 +40,13 @@ public class Extend extends PlacementModifier {
public Stream<BlockPos> getPositions(PlacementContext placementContext,
RandomSource random,
BlockPos blockPos) {
var builder = Stream.<BlockPos>builder();
final int count = length.sample(random);
List<BlockPos> pos = new ArrayList<>(count);
for (int y = 0; y < count; y++) {
pos.add(blockPos.relative(direction, y + 1));
builder.add(blockPos);
for (int y = 1; y < count + 1; y++) {
builder.add(blockPos.relative(direction, y));
}
return pos.stream();
return builder.build();
}
@Override

View file

@ -16,8 +16,8 @@ import java.util.stream.Stream;
public class FindSolidInDirection extends PlacementModifier {
protected static final FindSolidInDirection DOWN = new FindSolidInDirection(Direction.DOWN, 12);
protected static final FindSolidInDirection UP = new FindSolidInDirection(Direction.UP, 12);
protected static final FindSolidInDirection DOWN = new FindSolidInDirection(Direction.DOWN, 6);
protected static final FindSolidInDirection UP = new FindSolidInDirection(Direction.UP, 6);
public static final Codec<FindSolidInDirection> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
Direction.CODEC.fieldOf("dir").orElse(Direction.DOWN).forGetter((p) -> p.direction),
@ -54,7 +54,7 @@ public class FindSolidInDirection extends PlacementModifier {
RandomSource randomSource,
BlockPos blockPos) {
BlockPos.MutableBlockPos POS = blockPos.mutable();
if (BlocksHelper.findSurface(placementContext.getLevel(),
if (BlocksHelper.findSurroundingSurface(placementContext.getLevel(),
POS,
direction,
maxSearchDistance,

View file

@ -0,0 +1,43 @@
package org.betterx.bclib.world.features.placement;
import net.minecraft.core.BlockPos;
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 com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
public class Is extends PlacementFilter {
public static final Codec<Is> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
BlockPredicate.CODEC
.fieldOf("predicate")
.forGetter(cfg -> cfg.predicate)
)
.apply(instance, Is::new));
private final BlockPredicate predicate;
public Is(BlockPredicate predicate) {
this.predicate = predicate;
}
public static Is simple(BlockPredicate predicate) {
return new Is(predicate);
}
@Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
WorldGenLevel level = ctx.getLevel();
return predicate.test(level, pos);
}
@Override
public PlacementModifierType<Is> type() {
return PlacementModifiers.IS;
}
}

View file

@ -1,9 +1,9 @@
package org.betterx.bclib.world.features.placement;
import net.minecraft.core.BlockPos;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
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;
@ -11,27 +11,34 @@ import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.List;
public class IsBasin extends PlacementFilter {
public static final Codec<IsBasin> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
ExtraCodecs.nonEmptyList(BlockState.CODEC.listOf())
.fieldOf("blocks")
.forGetter(cfg -> cfg.blocks)
BlockPredicate.CODEC
.fieldOf("predicate")
.forGetter(cfg -> cfg.predicate)
)
.apply(instance, IsBasin::new));
private final List<BlockState> blocks;
private final BlockPredicate predicate;
public IsBasin(List<BlockState> blocks) {
this.blocks = blocks;
public IsBasin(BlockPredicate predicate) {
this.predicate = predicate;
}
public static IsBasin simple(BlockPredicate predicate) {
return new IsBasin(predicate);
}
@Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
BlockState bs = ctx.getLevel().getBlockState(pos);
return blocks.stream().map(b -> b.getBlock()).anyMatch(b -> bs.is(b));
WorldGenLevel level = ctx.getLevel();
return predicate.test(level, pos.below())
&& predicate.test(level, pos.west())
&& predicate.test(level, pos.east())
&& predicate.test(level, pos.north())
&& predicate.test(level, pos.south());
}
@Override

View file

@ -15,6 +15,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
*/
public class IsEmptyAboveSampledFilter extends PlacementFilter {
private static final IsEmptyAboveSampledFilter DEFAULT = new IsEmptyAboveSampledFilter(4, 2);
private static final IsEmptyAboveSampledFilter DEFAULT1 = new IsEmptyAboveSampledFilter(1, 1);
public static final Codec<IsEmptyAboveSampledFilter> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
Codec.intRange(1, 32).fieldOf("d1").orElse(2).forGetter((p) -> p.distance1),
@ -26,6 +27,10 @@ public class IsEmptyAboveSampledFilter extends PlacementFilter {
return DEFAULT;
}
public static PlacementFilter emptyAbove() {
return DEFAULT1;
}
public IsEmptyAboveSampledFilter(int d1, int d2) {
this.distance1 = d1;
this.distance2 = d2;
@ -38,7 +43,8 @@ public class IsEmptyAboveSampledFilter extends PlacementFilter {
@Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
WorldGenLevel level = ctx.getLevel();
if (level.isEmptyBlock(pos.above(distance1)) && level.isEmptyBlock(pos.above(distance2))) {
if (level.isEmptyBlock(pos.above(distance1))
&& (distance1 == distance2 || level.isEmptyBlock(pos.above(distance2)))) {
return true;
}
return false;

View file

@ -1,18 +1,22 @@
package org.betterx.bclib.world.features.placement;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
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 com.google.common.collect.Maps;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.Map;
import java.util.stream.Stream;
public class Offset extends PlacementModifier {
private static Map<Direction, Offset> DIRECTIONS = Maps.newHashMap();
public static final Codec<Offset> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
Vec3i.CODEC
@ -27,6 +31,10 @@ public class Offset extends PlacementModifier {
this.offset = offset;
}
public static Offset inDirection(Direction dir) {
return DIRECTIONS.get(dir);
}
@Override
public Stream<BlockPos> getPositions(PlacementContext placementContext,
RandomSource randomSource,
@ -38,4 +46,9 @@ public class Offset extends PlacementModifier {
public PlacementModifierType<?> type() {
return PlacementModifiers.OFFSET;
}
static {
for (Direction d : Direction.values())
DIRECTIONS.put(d, new Offset(d.getNormal()));
}
}

View file

@ -0,0 +1,74 @@
package org.betterx.bclib.world.features.placement;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
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 com.mojang.serialization.Codec;
import java.util.stream.Stream;
public class OnEveryLayer
extends PlacementModifier {
private static OnEveryLayer INSTANCE = new OnEveryLayer();
public static final Codec<OnEveryLayer> CODEC = Codec.unit(() -> INSTANCE);
private OnEveryLayer() {
}
public static OnEveryLayer simple() {
return INSTANCE;
}
@Override
public Stream<BlockPos> getPositions(PlacementContext ctx,
RandomSource random,
BlockPos pos) {
Stream.Builder<BlockPos> builder = Stream.builder();
final int z = pos.getZ();
final int x = pos.getX();
int y = ctx.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z);
int layerY;
do {
layerY = OnEveryLayer.findOnGroundYPosition(ctx, x, y, z);
if (layerY != Integer.MAX_VALUE) {
builder.add(new BlockPos(x, layerY, z));
y = layerY - 1;
}
} while (layerY != Integer.MAX_VALUE);
return builder.build();
}
@Override
public PlacementModifierType<OnEveryLayer> type() {
return PlacementModifiers.ON_EVERY_LAYER;
}
private static int findOnGroundYPosition(PlacementContext ctx, int x, int startY, int z) {
BlockPos.MutableBlockPos mPos = new BlockPos.MutableBlockPos(x, startY, z);
BlockState nowState = ctx.getBlockState(mPos);
for (int y = startY; y >= ctx.getMinBuildHeight() + 1; --y) {
mPos.setY(y - 1);
BlockState belowState = ctx.getBlockState(mPos);
if (!OnEveryLayer.isEmpty(belowState) && OnEveryLayer.isEmpty(nowState) && !belowState.is(Blocks.BEDROCK)) {
return mPos.getY() + 1;
}
nowState = belowState;
}
return Integer.MAX_VALUE;
}
private static boolean isEmpty(BlockState blockState) {
return blockState.isAir() || blockState.is(Blocks.WATER) || blockState.is(Blocks.LAVA);
}
}

View file

@ -29,6 +29,10 @@ public class PlacementModifiers {
"is_basin",
IsBasin.CODEC);
public static final PlacementModifierType<Is> IS = register(
"is",
Is.CODEC);
public static final PlacementModifierType<Offset> OFFSET = register(
"offset",
Offset.CODEC);
@ -37,6 +41,10 @@ public class PlacementModifiers {
"extend",
Extend.CODEC);
public static final PlacementModifierType<OnEveryLayer> ON_EVERY_LAYER = register(
"on_every_layer",
OnEveryLayer.CODEC);
private static <P extends PlacementModifier> PlacementModifierType<P> register(String path, Codec<P> codec) {
return register(BCLib.makeID(path), codec);

View file

@ -20,23 +20,29 @@ public class Stencil extends PlacementModifier {
private static final Boolean[] BN_STENCIL;
private final List<Boolean> stencil;
private static final Stencil DEFAULT;
private static final Stencil DEFAULT4;
private final int selectOneIn;
private static List<Boolean> convert(Boolean[] s) {
return Arrays.stream(s).toList();
}
public Stencil(Boolean[] stencil) {
this(convert(stencil));
public Stencil(Boolean[] stencil, int selectOneIn) {
this(convert(stencil), selectOneIn);
}
public Stencil(List<Boolean> stencil) {
public Stencil(List<Boolean> stencil, int selectOneIn) {
this.stencil = stencil;
this.selectOneIn = selectOneIn;
}
public static Stencil basic() {
public static Stencil all() {
return DEFAULT;
}
public static Stencil oneIn4() {
return DEFAULT4;
}
@Override
public Stream<BlockPos> getPositions(PlacementContext placementContext,
@ -319,13 +325,18 @@ public class Stencil extends PlacementModifier {
false
};
DEFAULT = new Stencil(BN_STENCIL);
DEFAULT = new Stencil(BN_STENCIL, 1);
DEFAULT4 = new Stencil(BN_STENCIL, 4);
CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
ExtraCodecs.nonEmptyList(Codec.BOOL.listOf())
.fieldOf("structures")
.orElse(convert(BN_STENCIL))
.forGetter((Stencil a) -> a.stencil)
.forGetter((Stencil a) -> a.stencil),
Codec.INT
.fieldOf("one_in")
.orElse(1)
.forGetter((Stencil a) -> a.selectOneIn)
)
.apply(instance, Stencil::new)
);