More Features and Placement Modifiers
This commit is contained in:
parent
aa4133fad2
commit
6adf6486ac
15 changed files with 962 additions and 41 deletions
|
@ -5,14 +5,15 @@ import net.minecraft.data.worldgen.placement.PlacementUtils;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -188,14 +189,26 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
|
|||
* @see #findSolidSurface(Direction, int) for Details
|
||||
*/
|
||||
public BCLFeatureBuilder findSolidFloor(int distance) {
|
||||
return findSolidSurface(Direction.DOWN, distance);
|
||||
return modifier(FindSolidInDirection.down(distance));
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder findSolidCeil(int distance) {
|
||||
return modifier(FindSolidInDirection.up(distance));
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder hasMinimumDownwardSpace() {
|
||||
return modifier(MinEmptyFilter.down());
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder hasMinimumUpwardSpace() {
|
||||
return modifier(MinEmptyFilter.up());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cast a ray with max {@code distance} length to find the next solid Block. The ray will travel through replaceable
|
||||
* Blocks (see {@link Material#isReplaceable()}) and will be accepted if it hits a solid one
|
||||
* (see {@link Material#isSolid()} ()})
|
||||
* Blocks (see {@link Material#isReplaceable()}) and will be accepted if it hits a block with the
|
||||
* {@link org.betterx.bclib.api.tag.CommonBlockTags#TERRAIN}-tag
|
||||
*
|
||||
* @param dir The direction the ray is cast
|
||||
* @param distance The maximum search Distance
|
||||
|
@ -203,10 +216,7 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
|
|||
* @see #findSolidSurface(Direction, int) for Details
|
||||
*/
|
||||
public BCLFeatureBuilder findSolidSurface(Direction dir, int distance) {
|
||||
return modifier(EnvironmentScanPlacement.scanningFor(dir,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.replaceable(),
|
||||
distance));
|
||||
return modifier(new FindSolidInDirection(dir, distance));
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder heightmap() {
|
||||
|
|
|
@ -29,7 +29,7 @@ public class CommonBlockTags {
|
|||
public static final TagKey<Block> MINABLE_WITH_HAMMER = TagAPI.makeCommonBlockTag("mineable/hammer");
|
||||
|
||||
public static final TagKey<Block> IS_OBSIDIAN = TagAPI.makeCommonBlockTag("is_obsidian");
|
||||
public static final TagKey<Block> STALAGMITE_BASE = TagAPI.makeCommonBlockTag("stalagmite_base_blocks");
|
||||
public static final TagKey<Block> TERRAIN = TagAPI.makeCommonBlockTag("terrain");
|
||||
|
||||
static {
|
||||
TagAPI.BLOCKS.add(END_STONES, Blocks.END_STONE);
|
||||
|
@ -47,8 +47,7 @@ public class CommonBlockTags {
|
|||
|
||||
TagAPI.BLOCKS.add(IS_OBSIDIAN, Blocks.OBSIDIAN, Blocks.CRYING_OBSIDIAN);
|
||||
|
||||
TagAPI.BLOCKS.add(STALAGMITE_BASE, Blocks.DIAMOND_BLOCK);
|
||||
TagAPI.BLOCKS.addOtherTags(STALAGMITE_BASE,
|
||||
TagAPI.BLOCKS.addOtherTags(TERRAIN,
|
||||
BlockTags.DRIPSTONE_REPLACEABLE,
|
||||
BlockTags.BASE_STONE_OVERWORLD,
|
||||
NETHER_STONES,
|
||||
|
|
|
@ -31,7 +31,7 @@ public class BlocksHelper {
|
|||
public static final int FORSE_RERENDER = 8;
|
||||
public static final int FLAG_IGNORE_OBSERVERS = 16;
|
||||
|
||||
public static final int SET_SILENT = FLAG_UPDATE_BLOCK | FLAG_IGNORE_OBSERVERS | FLAG_SEND_CLIENT_CHANGES;
|
||||
public static final int SET_SILENT = FLAG_IGNORE_OBSERVERS | FLAG_SEND_CLIENT_CHANGES;
|
||||
public static final int SET_OBSERV = FLAG_UPDATE_BLOCK | FLAG_SEND_CLIENT_CHANGES;
|
||||
public static final Direction[] HORIZONTAL = makeHorizontal();
|
||||
public static final Direction[] DIRECTIONS = Direction.values();
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package org.betterx.bclib.world.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.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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package org.betterx.bclib.world.features;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -105,32 +105,44 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
|
|||
if (config.isValidBase(level.getBlockState(basePos))) {
|
||||
final Direction surfaceDirection = direction.getOpposite();
|
||||
BlockPos.MutableBlockPos POS = new BlockPos.MutableBlockPos();
|
||||
basePos = basePos.relative(direction, 1);
|
||||
buildPillarWithBase(level, origin, basePos, direction, centerHeight, config, random);
|
||||
|
||||
|
||||
final double distNormalizer = (config.maxSpread * Math.sqrt(2));
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
final int tryCount = (int) Math.min(16, 4 * config.maxSpread * config.maxSpread);
|
||||
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)) {
|
||||
int myHeight;
|
||||
if (config.growWhileFree) {
|
||||
myHeight = BlocksHelper.blockCount(level,
|
||||
POS,
|
||||
direction,
|
||||
config.maxHeight,
|
||||
state -> state.getMaterial().isReplaceable());
|
||||
} else {
|
||||
myHeight = centerHeight;
|
||||
}
|
||||
|
||||
POS.move(direction, 1);
|
||||
int dx = x - POS.getX();
|
||||
int dz = z - POS.getZ();
|
||||
float sizeFactor = (1 - (float) (Math.sqrt(dx * dx + dz * dz) / distNormalizer));
|
||||
sizeFactor = (1 - (random.nextFloat() * config.sizeVariation)) * sizeFactor;
|
||||
final int height = (int) Math.max(
|
||||
myHeight = (int) Math.min(Math.max(
|
||||
config.minHeight,
|
||||
config.minHeight + sizeFactor * (centerHeight - config.minHeight)
|
||||
);
|
||||
config.minHeight + sizeFactor * (myHeight - config.minHeight)
|
||||
), config.maxHeight);
|
||||
|
||||
buildPillarWithBase(level,
|
||||
POS,
|
||||
POS.relative(direction.getOpposite()),
|
||||
direction,
|
||||
height,
|
||||
myHeight,
|
||||
config,
|
||||
random);
|
||||
}
|
||||
|
@ -147,7 +159,7 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
|
|||
RandomSource random) {
|
||||
if (BlocksHelper.isFreeSpace(level, origin, direction, height, (state) -> state.is(Blocks.AIR))) {
|
||||
createPatchOfBaseBlocks(level, random, basePos, config);
|
||||
buildPillar(level, origin, direction, height, config);
|
||||
buildPillar(level, origin, direction, height, config, random);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,18 +167,32 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
|
|||
BlockPos origin,
|
||||
Direction direction,
|
||||
int height,
|
||||
ScatterFeatureConfig config) {
|
||||
ScatterFeatureConfig config,
|
||||
RandomSource random) {
|
||||
|
||||
final BlockPos.MutableBlockPos POS = origin.mutable();
|
||||
buildBaseToTipColumn(height, (blockState) -> {
|
||||
BlocksHelper.setWithoutUpdate(level, POS, blockState);
|
||||
POS.move(direction);
|
||||
}, config);
|
||||
}, config, random);
|
||||
}
|
||||
|
||||
protected void buildBaseToTipColumn(int totalHeight, Consumer<BlockState> consumer, ScatterFeatureConfig config) {
|
||||
for (int size = totalHeight; size >= 0; size--) {
|
||||
consumer.accept(config.createBlock(size));
|
||||
protected void buildBaseToTipColumn(int totalHeight,
|
||||
Consumer<BlockState> consumer,
|
||||
ScatterFeatureConfig config,
|
||||
RandomSource random) {
|
||||
for (int size = 0; size < totalHeight; size++) {
|
||||
consumer.accept(config.createBlock(size, totalHeight - 1, random));
|
||||
// Block s = config.createBlock(size, totalHeight - 1, random).getBlock();
|
||||
// if (size == 0) s = Blocks.YELLOW_CONCRETE;
|
||||
// else if (size == 1) s = Blocks.LIME_CONCRETE;
|
||||
// else if (size == 2) s = Blocks.CYAN_CONCRETE;
|
||||
// else if (size == 3) s = Blocks.LIGHT_BLUE_CONCRETE;
|
||||
// else if (size == 4) s = Blocks.BLUE_CONCRETE;
|
||||
// else if (size == 5) s = Blocks.PURPLE_CONCRETE;
|
||||
// else if (size == 6) s = Blocks.MAGENTA_CONCRETE;
|
||||
// else s = Blocks.GRAY_CONCRETE;
|
||||
// consumer.accept(s.defaultBlockState());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,7 +243,7 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
|
|||
BlockPos blockPos,
|
||||
BlockState baseState) {
|
||||
BlockState blockState = levelAccessor.getBlockState(blockPos);
|
||||
if (blockState.is(CommonBlockTags.STALAGMITE_BASE)) {
|
||||
if (blockState.is(CommonBlockTags.TERRAIN)) {
|
||||
levelAccessor.setBlock(blockPos, baseState, 2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
package org.betterx.bclib.world.features;
|
||||
|
||||
import net.minecraft.util.RandomSource;
|
||||
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.Function11;
|
||||
import com.mojang.datafixers.util.Function14;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import org.betterx.bclib.BCLib;
|
||||
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 final BlockState clusterBlock;
|
||||
public final BlockState tipBlock;
|
||||
public final BlockState bottomBlock;
|
||||
public final Optional<BlockState> baseState;
|
||||
public final float baseReplaceChance;
|
||||
public final float chanceOfDirectionalSpread;
|
||||
|
@ -23,7 +31,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
|
|||
public final float sizeVariation;
|
||||
public final float floorChance;
|
||||
|
||||
public final boolean growWhileFree;
|
||||
|
||||
public ScatterFeatureConfig(BlockState clusterBlock,
|
||||
BlockState tipBlock,
|
||||
BlockState bottomBlock,
|
||||
Optional<BlockState> baseState,
|
||||
float baseReplaceChance,
|
||||
float chanceOfDirectionalSpread,
|
||||
|
@ -33,8 +45,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
|
|||
int maxHeight,
|
||||
float maxSpread,
|
||||
float sizeVariation,
|
||||
float floorChance) {
|
||||
float floorChance,
|
||||
boolean growWhileFree) {
|
||||
this.clusterBlock = clusterBlock;
|
||||
this.tipBlock = tipBlock == null ? clusterBlock : tipBlock;
|
||||
this.bottomBlock = bottomBlock == null ? clusterBlock : bottomBlock;
|
||||
this.baseState = baseState;
|
||||
this.baseReplaceChance = baseReplaceChance;
|
||||
this.chanceOfDirectionalSpread = chanceOfDirectionalSpread;
|
||||
|
@ -45,6 +60,7 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
|
|||
this.maxSpread = maxSpread;
|
||||
this.sizeVariation = sizeVariation;
|
||||
this.floorChance = floorChance;
|
||||
this.growWhileFree = growWhileFree;
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,13 +70,21 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
|
|||
|
||||
public abstract boolean isValidBase(BlockState state);
|
||||
|
||||
public abstract BlockState createBlock(int height);
|
||||
public abstract BlockState createBlock(int height, int maxHeight, RandomSource random);
|
||||
|
||||
public static <T extends ScatterFeatureConfig> Codec<T> buildCodec(Function11<BlockState, Optional<BlockState>, Float, Float, Float, Float, Integer, Integer, Float, Float, Float, T> instancer) {
|
||||
public static <T extends ScatterFeatureConfig> Codec<T> buildCodec(Instancer<T> instancer) {
|
||||
return RecordCodecBuilder.create((instance) -> instance
|
||||
.group(BlockState.CODEC
|
||||
.fieldOf("cluster_block")
|
||||
.forGetter((T cfg) -> cfg.clusterBlock),
|
||||
BlockState.CODEC
|
||||
.fieldOf("tip_block")
|
||||
.orElse(null)
|
||||
.forGetter((T cfg) -> cfg.tipBlock),
|
||||
BlockState.CODEC
|
||||
.fieldOf("bottom_block")
|
||||
.orElse(null)
|
||||
.forGetter((T cfg) -> cfg.bottomBlock),
|
||||
BlockState.CODEC
|
||||
.optionalFieldOf("base_state")
|
||||
.forGetter((T cfg) -> cfg.baseState),
|
||||
|
@ -108,10 +132,215 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
|
|||
.floatRange(0, 1)
|
||||
.fieldOf("floor_chance")
|
||||
.orElse(0.5f)
|
||||
.forGetter((T cfg) -> cfg.floorChance)
|
||||
.forGetter((T cfg) -> cfg.floorChance),
|
||||
Codec
|
||||
.BOOL
|
||||
.fieldOf("grow_while_empty")
|
||||
.orElse(false)
|
||||
.forGetter((T cfg) -> cfg.growWhileFree)
|
||||
)
|
||||
.apply(instance, instancer)
|
||||
);
|
||||
}
|
||||
|
||||
public static class Builder<T extends ScatterFeatureConfig> {
|
||||
private BlockState clusterBlock;
|
||||
private BlockState tipBlock;
|
||||
private BlockState bottomBlock;
|
||||
private Optional<BlockState> baseState = Optional.empty();
|
||||
private float baseReplaceChance = 0;
|
||||
private float chanceOfDirectionalSpread = 0;
|
||||
private float chanceOfSpreadRadius2 = 0;
|
||||
private float chanceOfSpreadRadius3 = 0;
|
||||
private int minHeight = 2;
|
||||
private int maxHeight = 12;
|
||||
private float maxSpread = 0;
|
||||
private float sizeVariation = 0;
|
||||
private float floorChance = 0.5f;
|
||||
private boolean growWhileFree = false;
|
||||
private final Instancer<T> instancer;
|
||||
|
||||
public Builder(Instancer<T> instancer) {
|
||||
this.instancer = instancer;
|
||||
}
|
||||
|
||||
public static <T extends ScatterFeatureConfig> Builder<T> start(Instancer<T> instancer) {
|
||||
return new Builder<>(instancer);
|
||||
}
|
||||
|
||||
public Builder<T> block(Block b) {
|
||||
return block(b.defaultBlockState());
|
||||
}
|
||||
|
||||
public Builder<T> singleBlock(Block b) {
|
||||
return block(b.defaultBlockState()).heightRange(1, 1).spread(0, 0);
|
||||
}
|
||||
|
||||
public Builder<T> block(BlockState s) {
|
||||
this.clusterBlock = s;
|
||||
if (tipBlock == null) tipBlock = s;
|
||||
if (bottomBlock == null) bottomBlock = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> tipBlock(BlockState s) {
|
||||
tipBlock = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> bottomBlock(BlockState s) {
|
||||
bottomBlock = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> heightRange(int min, int max) {
|
||||
minHeight = min;
|
||||
maxHeight = max;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> growWhileFree() {
|
||||
growWhileFree = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> minHeight(int h) {
|
||||
minHeight = h;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> maxHeight(int h) {
|
||||
maxHeight = h;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> generateBaseBlock(BlockState baseState) {
|
||||
return generateBaseBlock(baseState, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
public Builder<T> generateBaseBlock(BlockState baseState, float baseReplaceChance) {
|
||||
return generateBaseBlock(baseState, baseReplaceChance, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
public Builder<T> generateBaseBlock(BlockState baseState,
|
||||
float chanceOfDirectionalSpread,
|
||||
float chanceOfSpreadRadius2,
|
||||
float chanceOfSpreadRadius3) {
|
||||
return generateBaseBlock(baseState,
|
||||
1,
|
||||
chanceOfDirectionalSpread,
|
||||
chanceOfSpreadRadius2,
|
||||
chanceOfSpreadRadius3);
|
||||
}
|
||||
|
||||
public Builder<T> generateBaseBlock(BlockState baseState,
|
||||
float baseReplaceChance,
|
||||
float chanceOfDirectionalSpread,
|
||||
float chanceOfSpreadRadius2,
|
||||
float chanceOfSpreadRadius3) {
|
||||
if (this.baseState.isPresent() && this.baseReplaceChance == 0) {
|
||||
BCLib.LOGGER.error("Base generation was already selected.");
|
||||
}
|
||||
this.baseState = Optional.of(baseState);
|
||||
this.baseReplaceChance = baseReplaceChance;
|
||||
this.chanceOfDirectionalSpread = chanceOfDirectionalSpread;
|
||||
this.chanceOfSpreadRadius2 = chanceOfSpreadRadius2;
|
||||
this.chanceOfSpreadRadius3 = chanceOfSpreadRadius3;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> spread(float maxSpread, float sizeVariation) {
|
||||
this.maxSpread = maxSpread;
|
||||
this.sizeVariation = sizeVariation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> floorChance(float chance) {
|
||||
this.floorChance = chance;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> onFloor() {
|
||||
this.floorChance = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> onCeil() {
|
||||
this.floorChance = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public T build() {
|
||||
return instancer.apply(
|
||||
this.clusterBlock,
|
||||
this.tipBlock,
|
||||
this.bottomBlock,
|
||||
this.baseState,
|
||||
this.baseReplaceChance,
|
||||
this.chanceOfDirectionalSpread,
|
||||
this.chanceOfSpreadRadius2,
|
||||
this.chanceOfSpreadRadius3,
|
||||
this.minHeight,
|
||||
this.maxHeight,
|
||||
this.maxSpread,
|
||||
this.sizeVariation,
|
||||
this.floorChance,
|
||||
this.growWhileFree
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnSolid extends ScatterFeatureConfig {
|
||||
public static final Codec<OnSolid> CODEC = buildCodec(OnSolid::new);
|
||||
|
||||
public OnSolid(BlockState clusterBlock,
|
||||
BlockState tipBlock,
|
||||
BlockState bottomBlock,
|
||||
Optional<BlockState> baseState,
|
||||
float baseReplaceChance,
|
||||
float chanceOfDirectionalSpread,
|
||||
float chanceOfSpreadRadius2,
|
||||
float chanceOfSpreadRadius3,
|
||||
int minHeight,
|
||||
int maxHeight,
|
||||
float maxSpread,
|
||||
float sizeVariation,
|
||||
float floorChance,
|
||||
boolean growWhileFree) {
|
||||
super(clusterBlock,
|
||||
tipBlock,
|
||||
bottomBlock,
|
||||
baseState,
|
||||
baseReplaceChance,
|
||||
chanceOfDirectionalSpread,
|
||||
chanceOfSpreadRadius2,
|
||||
chanceOfSpreadRadius3,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
maxSpread,
|
||||
sizeVariation,
|
||||
floorChance,
|
||||
growWhileFree);
|
||||
}
|
||||
|
||||
public static Builder<OnSolid> startOnSolid() {
|
||||
return Builder.start(OnSolid::new);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValidBase(BlockState state) {
|
||||
return state.is(CommonBlockTags.TERRAIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState createBlock(int height, int maxHeight, RandomSource random) {
|
||||
if (height == 0) return this.bottomBlock;
|
||||
return height == maxHeight
|
||||
? this.tipBlock
|
||||
: this.clusterBlock;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ public class TemplateFeatureConfig implements FeatureConfiguration {
|
|||
.group(
|
||||
ExtraCodecs.nonEmptyList(StructureWorldNBT.CODEC.listOf())
|
||||
.fieldOf("structures")
|
||||
.forGetter((TemplateFeatureConfig ruinedPortalStructure) -> ruinedPortalStructure.structures)
|
||||
.forGetter((TemplateFeatureConfig cfg) -> cfg.structures)
|
||||
)
|
||||
.apply(instance, TemplateFeatureConfig::new)
|
||||
);
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package org.betterx.bclib.world.features.placement;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.util.valueproviders.IntProvider;
|
||||
import net.minecraft.util.valueproviders.UniformInt;
|
||||
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 com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Extend extends PlacementModifier {
|
||||
public static final Codec<Extend> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
Direction.CODEC
|
||||
.fieldOf("direction")
|
||||
.orElse(Direction.DOWN)
|
||||
.forGetter(cfg -> cfg.direction),
|
||||
IntProvider.codec(0, 16)
|
||||
.fieldOf("length")
|
||||
.orElse(UniformInt.of(0, 3))
|
||||
.forGetter(cfg -> cfg.length)
|
||||
)
|
||||
.apply(instance, Extend::new));
|
||||
|
||||
private final Direction direction;
|
||||
private final IntProvider length;
|
||||
|
||||
public Extend(Direction direction, IntProvider length) {
|
||||
this.direction = direction;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<BlockPos> getPositions(PlacementContext placementContext,
|
||||
RandomSource random,
|
||||
BlockPos blockPos) {
|
||||
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));
|
||||
}
|
||||
return pos.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.EXTEND;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package org.betterx.bclib.world.features.placement;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
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.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.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);
|
||||
public static final Codec<FindSolidInDirection> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
Direction.CODEC.fieldOf("dir").orElse(Direction.DOWN).forGetter((p) -> p.direction),
|
||||
Codec.intRange(1, 32).fieldOf("dist").orElse(12).forGetter((p) -> p.maxSearchDistance)
|
||||
)
|
||||
.apply(instance, FindSolidInDirection::new));
|
||||
|
||||
public FindSolidInDirection(Direction direction, int maxSearchDistance) {
|
||||
this.direction = direction;
|
||||
this.maxSearchDistance = maxSearchDistance;
|
||||
}
|
||||
|
||||
public static PlacementModifier down() {
|
||||
return DOWN;
|
||||
}
|
||||
|
||||
public static PlacementModifier up() {
|
||||
return UP;
|
||||
}
|
||||
|
||||
public static PlacementModifier down(int dist) {
|
||||
if (dist == DOWN.maxSearchDistance) return DOWN;
|
||||
return new FindSolidInDirection(Direction.DOWN, dist);
|
||||
}
|
||||
|
||||
public static PlacementModifier up(int dist) {
|
||||
if (dist == UP.maxSearchDistance) return UP;
|
||||
return new FindSolidInDirection(Direction.UP, dist);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Stream<BlockPos> getPositions(PlacementContext placementContext,
|
||||
RandomSource randomSource,
|
||||
BlockPos blockPos) {
|
||||
BlockPos.MutableBlockPos POS = blockPos.mutable();
|
||||
if (BlocksHelper.findSurface(placementContext.getLevel(),
|
||||
POS,
|
||||
direction,
|
||||
maxSearchDistance,
|
||||
state -> state.is(CommonBlockTags.TERRAIN))
|
||||
) return Stream.of(POS);
|
||||
|
||||
return Stream.of();
|
||||
}
|
||||
|
||||
private final Direction direction;
|
||||
private final int maxSearchDistance;
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.SOLID_IN_DIR;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
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.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;
|
||||
|
||||
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)
|
||||
)
|
||||
.apply(instance, IsBasin::new));
|
||||
|
||||
private final List<BlockState> blocks;
|
||||
|
||||
public IsBasin(List<BlockState> blocks) {
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.IS_BASIN;
|
||||
}
|
||||
}
|
|
@ -13,40 +13,48 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|||
import org.betterx.bclib.util.BlocksHelper;
|
||||
|
||||
public class MinEmptyFilter extends PlacementFilter {
|
||||
private static MinEmptyFilter DOWN = new MinEmptyFilter(Direction.DOWN, 12);
|
||||
private static MinEmptyFilter DOWN = new MinEmptyFilter(Direction.DOWN, 2);
|
||||
private static MinEmptyFilter UP = new MinEmptyFilter(Direction.UP, 2);
|
||||
public static final Codec<MinEmptyFilter> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
Direction.CODEC.fieldOf("dir").orElse(Direction.DOWN).forGetter((p) -> p.direction),
|
||||
Codec.intRange(1, 32).fieldOf("dist").orElse(12).forGetter((p) -> p.maxSearchDistance)
|
||||
Codec.intRange(1, 32).fieldOf("dist").orElse(12).forGetter((p) -> p.distance)
|
||||
)
|
||||
.apply(instance, MinEmptyFilter::new));
|
||||
|
||||
private final Direction direction;
|
||||
private final int maxSearchDistance;
|
||||
private final int distance;
|
||||
|
||||
protected MinEmptyFilter(Direction direction, int maxSearchDistance) {
|
||||
protected MinEmptyFilter(Direction direction, int distance) {
|
||||
this.direction = direction;
|
||||
this.maxSearchDistance = maxSearchDistance;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
public PlacementModifier down() {
|
||||
public static PlacementModifier down() {
|
||||
return DOWN;
|
||||
}
|
||||
|
||||
public PlacementModifier down(int dist) {
|
||||
public static PlacementModifier down(int dist) {
|
||||
return new MinEmptyFilter(Direction.DOWN, dist);
|
||||
}
|
||||
|
||||
public static PlacementModifier up() {
|
||||
return UP;
|
||||
}
|
||||
|
||||
public static PlacementModifier up(int dist) {
|
||||
return new MinEmptyFilter(Direction.UP, dist);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldPlace(PlacementContext ctx, RandomSource randomSource, BlockPos pos) {
|
||||
int h = BlocksHelper.blockCount(
|
||||
return BlocksHelper.isFreeSpace(
|
||||
ctx.getLevel(),
|
||||
pos.relative(direction),
|
||||
direction,
|
||||
maxSearchDistance,
|
||||
distance - 1,
|
||||
state -> state.getMaterial().isReplaceable()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package org.betterx.bclib.world.features.placement;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
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.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Offset extends PlacementModifier {
|
||||
public static final Codec<Offset> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
Vec3i.CODEC
|
||||
.fieldOf("blocks")
|
||||
.forGetter(cfg -> cfg.offset)
|
||||
)
|
||||
.apply(instance, Offset::new));
|
||||
|
||||
private final Vec3i offset;
|
||||
|
||||
public Offset(Vec3i offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<BlockPos> getPositions(PlacementContext placementContext,
|
||||
RandomSource randomSource,
|
||||
BlockPos blockPos) {
|
||||
return Stream.of(blockPos.offset(offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.OFFSET;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,26 @@ public class PlacementModifiers {
|
|||
"min_empty_filter",
|
||||
MinEmptyFilter.CODEC);
|
||||
|
||||
public static final PlacementModifierType<FindSolidInDirection> SOLID_IN_DIR = register(
|
||||
"solid_in_dir",
|
||||
FindSolidInDirection.CODEC);
|
||||
|
||||
public static final PlacementModifierType<Stencil> STENCIL = register(
|
||||
"stencil",
|
||||
Stencil.CODEC);
|
||||
|
||||
public static final PlacementModifierType<IsBasin> IS_BASIN = register(
|
||||
"is_basin",
|
||||
IsBasin.CODEC);
|
||||
|
||||
public static final PlacementModifierType<Offset> OFFSET = register(
|
||||
"offset",
|
||||
Offset.CODEC);
|
||||
|
||||
public static final PlacementModifierType<Extend> EXTEND = register(
|
||||
"extend",
|
||||
Extend.CODEC);
|
||||
|
||||
|
||||
private static <P extends PlacementModifier> PlacementModifierType<P> register(String path, Codec<P> codec) {
|
||||
return register(BCLib.makeID(path), codec);
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
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.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 com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Stencil extends PlacementModifier {
|
||||
public static final Codec<Stencil> CODEC;
|
||||
private static final Boolean[] BN_STENCIL;
|
||||
private final List<Boolean> stencil;
|
||||
private static final Stencil DEFAULT;
|
||||
|
||||
private static List<Boolean> convert(Boolean[] s) {
|
||||
return Arrays.stream(s).toList();
|
||||
}
|
||||
|
||||
public Stencil(Boolean[] stencil) {
|
||||
this(convert(stencil));
|
||||
}
|
||||
|
||||
public Stencil(List<Boolean> stencil) {
|
||||
this.stencil = stencil;
|
||||
}
|
||||
|
||||
public static Stencil basic() {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Stream<BlockPos> getPositions(PlacementContext placementContext,
|
||||
RandomSource randomSource,
|
||||
BlockPos blockPos) {
|
||||
List<BlockPos> pos = new ArrayList<>(16 * 16);
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
if (stencil.get(x << 4 | y)) {
|
||||
pos.add(blockPos.offset(x, 0, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pos.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.STENCIL;
|
||||
}
|
||||
|
||||
static {
|
||||
BN_STENCIL = new Boolean[]{
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
};
|
||||
|
||||
DEFAULT = new Stencil(BN_STENCIL);
|
||||
CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
ExtraCodecs.nonEmptyList(Codec.BOOL.listOf())
|
||||
.fieldOf("structures")
|
||||
.orElse(convert(BN_STENCIL))
|
||||
.forGetter((Stencil a) -> a.stencil)
|
||||
)
|
||||
.apply(instance, Stencil::new)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue