Moved Classes
This commit is contained in:
parent
35ce65674b
commit
49edee32a8
28 changed files with 31 additions and 52 deletions
|
@ -29,6 +29,7 @@ import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
|
|||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.betterx.bclib.api.features.BCLFeature;
|
||||
import org.betterx.bclib.api.structures.BCLStructure;
|
||||
import org.betterx.bclib.api.surface.SurfaceRuleBuilder;
|
||||
import org.betterx.bclib.entity.BCLEntityWrapper;
|
||||
|
@ -37,7 +38,6 @@ import org.betterx.bclib.util.CollectionsUtil;
|
|||
import org.betterx.bclib.util.ColorUtil;
|
||||
import org.betterx.bclib.util.Pair;
|
||||
import org.betterx.bclib.util.TriFunction;
|
||||
import org.betterx.bclib.world.features.BCLFeature;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
|
@ -42,6 +42,7 @@ import com.google.common.collect.Maps;
|
|||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.bclib.api.features.BCLFeature;
|
||||
import org.betterx.bclib.api.tag.CommonBiomeTags;
|
||||
import org.betterx.bclib.api.tag.TagAPI;
|
||||
import org.betterx.bclib.interfaces.BiomeSourceAccessor;
|
||||
|
@ -51,7 +52,6 @@ import org.betterx.bclib.interfaces.SurfaceRuleProvider;
|
|||
import org.betterx.bclib.mixin.common.BiomeGenerationSettingsAccessor;
|
||||
import org.betterx.bclib.mixin.common.MobSpawnSettingsAccessor;
|
||||
import org.betterx.bclib.util.CollectionsUtil;
|
||||
import org.betterx.bclib.world.features.BCLFeature;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
|
@ -9,8 +9,6 @@ import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguratio
|
|||
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockMatchTest;
|
||||
|
||||
import org.betterx.bclib.world.features.BCLFeature;
|
||||
|
||||
public class BCLCommonFeatures {
|
||||
/**
|
||||
* Will create a basic plant feature.
|
||||
|
|
127
src/main/java/org/betterx/bclib/api/features/BCLFeature.java
Normal file
127
src/main/java/org/betterx/bclib/api/features/BCLFeature.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
package org.betterx.bclib.api.features;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.data.worldgen.features.FeatureUtils;
|
||||
import net.minecraft.data.worldgen.placement.PlacementUtils;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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.placement.PlacedFeature;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
|
||||
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.bclib.api.features.config.BlockPlaceFeatureConfig;
|
||||
import org.betterx.bclib.api.features.config.ScatterFeatureConfig;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
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)
|
||||
);
|
||||
public static final Feature<BlockPlaceFeatureConfig> PLACE_BLOCK = register(
|
||||
BCLib.makeID("place_block"),
|
||||
new BlockPlaceFeature<>(BlockPlaceFeatureConfig.CODEC)
|
||||
);
|
||||
private final Holder<PlacedFeature> placedFeature;
|
||||
private final Decoration featureStep;
|
||||
private final Feature<?> feature;
|
||||
|
||||
|
||||
public <FC extends FeatureConfiguration, F extends Feature<FC>> BCLFeature(ResourceLocation id,
|
||||
F feature,
|
||||
Decoration featureStep,
|
||||
FC configuration,
|
||||
PlacementModifier[] modifiers) {
|
||||
this(id, feature, featureStep, buildPlacedFeature(id, feature, configuration, modifiers));
|
||||
}
|
||||
|
||||
public BCLFeature(ResourceLocation id,
|
||||
Feature<?> feature,
|
||||
Decoration featureStep,
|
||||
Holder<PlacedFeature> placedFeature) {
|
||||
this.placedFeature = placedFeature;
|
||||
this.featureStep = featureStep;
|
||||
this.feature = feature;
|
||||
|
||||
if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) {
|
||||
Registry.register(BuiltinRegistries.PLACED_FEATURE, id, placedFeature.value());
|
||||
}
|
||||
if (!Registry.FEATURE.containsKey(id) && !containsObj(Registry.FEATURE, feature)) {
|
||||
Registry.register(Registry.FEATURE, id, feature);
|
||||
}
|
||||
}
|
||||
|
||||
private static <FC extends FeatureConfiguration, F extends Feature<FC>> Holder<PlacedFeature> buildPlacedFeature(
|
||||
ResourceLocation id,
|
||||
F feature,
|
||||
FC configuration,
|
||||
PlacementModifier[] modifiers) {
|
||||
Holder<ConfiguredFeature<?, ?>> configuredFeature;
|
||||
if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) {
|
||||
configuredFeature = (Holder<ConfiguredFeature<?, ?>>) (Object) FeatureUtils.register(id.toString(),
|
||||
feature,
|
||||
configuration);
|
||||
} else {
|
||||
configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE
|
||||
.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(),
|
||||
id))
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) {
|
||||
return PlacementUtils.register(id.toString(), configuredFeature, modifiers);
|
||||
} else {
|
||||
return BuiltinRegistries.PLACED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.PLACED_FEATURE.key(),
|
||||
id)).orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
private static <E> boolean containsObj(Registry<E> registry, E obj) {
|
||||
Optional<Entry<ResourceKey<E>, E>> optional = registry
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue() == obj)
|
||||
.findAny();
|
||||
return optional.isPresent();
|
||||
}
|
||||
|
||||
public static <C extends FeatureConfiguration, F extends Feature<C>> F register(ResourceLocation string,
|
||||
F feature) {
|
||||
return Registry.register(Registry.FEATURE, string, feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw feature.
|
||||
*
|
||||
* @return {@link Feature}.
|
||||
*/
|
||||
public Feature<?> getFeature() {
|
||||
return feature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured feature.
|
||||
*
|
||||
* @return {@link PlacedFeature}.
|
||||
*/
|
||||
public Holder<PlacedFeature> getPlacedFeature() {
|
||||
return placedFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get feature decoration step.
|
||||
*
|
||||
* @return {@link Decoration}.
|
||||
*/
|
||||
public Decoration getDecoration() {
|
||||
return featureStep;
|
||||
}
|
||||
}
|
|
@ -13,8 +13,7 @@ import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfigur
|
|||
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.*;
|
||||
import org.betterx.bclib.api.features.placement.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.betterx.bclib.api.features;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap.Types;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
|
||||
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
|
||||
public abstract class DefaultFeature extends Feature<NoneFeatureConfiguration> {
|
||||
protected static final BlockState AIR = Blocks.AIR.defaultBlockState();
|
||||
protected static final BlockState WATER = Blocks.WATER.defaultBlockState();
|
||||
|
||||
public DefaultFeature() {
|
||||
super(NoneFeatureConfiguration.CODEC);
|
||||
}
|
||||
|
||||
public static int getYOnSurface(WorldGenLevel world, int x, int z) {
|
||||
return world.getHeight(Types.WORLD_SURFACE, x, z);
|
||||
}
|
||||
|
||||
public static int getYOnSurfaceWG(WorldGenLevel world, int x, int z) {
|
||||
return world.getHeight(Types.WORLD_SURFACE_WG, x, z);
|
||||
}
|
||||
|
||||
public static BlockPos getPosOnSurface(WorldGenLevel world, BlockPos pos) {
|
||||
return world.getHeightmapPos(Types.WORLD_SURFACE, pos);
|
||||
}
|
||||
|
||||
public static BlockPos getPosOnSurfaceWG(WorldGenLevel world, BlockPos pos) {
|
||||
return world.getHeightmapPos(Types.WORLD_SURFACE_WG, pos);
|
||||
}
|
||||
|
||||
public static BlockPos getPosOnSurfaceRaycast(WorldGenLevel world, BlockPos pos) {
|
||||
return getPosOnSurfaceRaycast(world, pos, 256);
|
||||
}
|
||||
|
||||
public static BlockPos getPosOnSurfaceRaycast(WorldGenLevel world, BlockPos pos, int dist) {
|
||||
int h = BlocksHelper.downRay(world, pos, dist);
|
||||
return pos.below(h);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
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.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
|
||||
import org.betterx.bclib.util.StructureHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ListFeature extends NBTFeature {
|
||||
private final List<StructureInfo> list;
|
||||
private StructureInfo selected;
|
||||
|
||||
public ListFeature(List<StructureInfo> list, BlockState defaultBlock) {
|
||||
super(defaultBlock);
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StructureTemplate getStructure(WorldGenLevel world, BlockPos pos, RandomSource random) {
|
||||
selected = list.get(random.nextInt(list.size()));
|
||||
return selected.getStructure();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSpawn(WorldGenLevel world, BlockPos pos, RandomSource random) {
|
||||
int cx = pos.getX() >> 4;
|
||||
int cz = pos.getZ() >> 4;
|
||||
return ((cx + cz) & 1) == 0 && pos.getY() > 58;// && world.getBlockState(pos.below()).is(EndTags.GEN_TERRAIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Rotation getRotation(WorldGenLevel world, BlockPos pos, RandomSource random) {
|
||||
return Rotation.getRandom(random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mirror getMirror(WorldGenLevel world, BlockPos pos, RandomSource random) {
|
||||
return Mirror.values()[random.nextInt(3)];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getYOffset(StructureTemplate structure, WorldGenLevel world, BlockPos pos, RandomSource random) {
|
||||
return selected.offsetY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TerrainMerge getTerrainMerge(WorldGenLevel world, BlockPos pos, RandomSource random) {
|
||||
return selected.terrainMerge;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addStructureData(StructurePlaceSettings data) {
|
||||
}
|
||||
|
||||
public static final class StructureInfo {
|
||||
public final TerrainMerge terrainMerge;
|
||||
public final String structurePath;
|
||||
public final int offsetY;
|
||||
|
||||
private StructureTemplate structure;
|
||||
|
||||
public StructureInfo(String structurePath, int offsetY, TerrainMerge terrainMerge) {
|
||||
this.terrainMerge = terrainMerge;
|
||||
this.structurePath = structurePath;
|
||||
this.offsetY = offsetY;
|
||||
}
|
||||
|
||||
public StructureTemplate getStructure() {
|
||||
if (structure == null) {
|
||||
structure = StructureHelper.readStructure(structurePath);
|
||||
}
|
||||
return structure;
|
||||
}
|
||||
}
|
||||
}
|
235
src/main/java/org/betterx/bclib/api/features/NBTFeature.java
Normal file
235
src/main/java/org/betterx/bclib/api/features/NBTFeature.java
Normal file
|
@ -0,0 +1,235 @@
|
|||
package org.betterx.bclib.api.features;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.BlockPos.MutableBlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
|
||||
import org.betterx.bclib.api.biomes.BiomeAPI;
|
||||
import org.betterx.bclib.api.tag.CommonBlockTags;
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
import org.betterx.bclib.world.processors.DestructionStructureProcessor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
//TODO: 1.19 Check if we can merge this with the new TemplateFeature!
|
||||
public abstract class NBTFeature extends DefaultFeature {
|
||||
private final BlockState defaultBlock;
|
||||
|
||||
public NBTFeature(BlockState defaultBlock) {
|
||||
this.defaultBlock = defaultBlock;
|
||||
}
|
||||
|
||||
protected static final DestructionStructureProcessor DESTRUCTION = new DestructionStructureProcessor();
|
||||
|
||||
protected abstract StructureTemplate getStructure(WorldGenLevel world, BlockPos pos, RandomSource random);
|
||||
|
||||
protected abstract boolean canSpawn(WorldGenLevel world, BlockPos pos, RandomSource random);
|
||||
|
||||
protected abstract Rotation getRotation(WorldGenLevel world, BlockPos pos, RandomSource random);
|
||||
|
||||
protected abstract Mirror getMirror(WorldGenLevel world, BlockPos pos, RandomSource random);
|
||||
|
||||
protected abstract int getYOffset(StructureTemplate structure,
|
||||
WorldGenLevel world,
|
||||
BlockPos pos,
|
||||
RandomSource random);
|
||||
|
||||
protected abstract TerrainMerge getTerrainMerge(WorldGenLevel world, BlockPos pos, RandomSource random);
|
||||
|
||||
protected abstract void addStructureData(StructurePlaceSettings data);
|
||||
|
||||
protected BlockPos getGround(WorldGenLevel world, BlockPos center) {
|
||||
Holder<Biome> biome = world.getBiome(center);
|
||||
ResourceLocation id = BiomeAPI.getBiomeID(biome);
|
||||
if (id.getNamespace().contains("moutain") || id.getNamespace().contains("lake")) {
|
||||
int y = getAverageY(world, center);
|
||||
return new BlockPos(center.getX(), y, center.getZ());
|
||||
} else {
|
||||
int y = getAverageYWG(world, center);
|
||||
return new BlockPos(center.getX(), y, center.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
protected int getAverageY(WorldGenLevel world, BlockPos center) {
|
||||
int y = getYOnSurface(world, center.getX(), center.getZ());
|
||||
y += getYOnSurface(world, center.getX() - 2, center.getZ() - 2);
|
||||
y += getYOnSurface(world, center.getX() + 2, center.getZ() - 2);
|
||||
y += getYOnSurface(world, center.getX() - 2, center.getZ() + 2);
|
||||
y += getYOnSurface(world, center.getX() + 2, center.getZ() + 2);
|
||||
return y / 5;
|
||||
}
|
||||
|
||||
protected int getAverageYWG(WorldGenLevel world, BlockPos center) {
|
||||
int y = getYOnSurfaceWG(world, center.getX(), center.getZ());
|
||||
y += getYOnSurfaceWG(world, center.getX() - 2, center.getZ() - 2);
|
||||
y += getYOnSurfaceWG(world, center.getX() + 2, center.getZ() - 2);
|
||||
y += getYOnSurfaceWG(world, center.getX() - 2, center.getZ() + 2);
|
||||
y += getYOnSurfaceWG(world, center.getX() + 2, center.getZ() + 2);
|
||||
return y / 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<NoneFeatureConfiguration> context) {
|
||||
WorldGenLevel world = context.level();
|
||||
RandomSource random = context.random();
|
||||
BlockPos center = context.origin();
|
||||
|
||||
center = new BlockPos(((center.getX() >> 4) << 4) | 8, 128, ((center.getZ() >> 4) << 4) | 8);
|
||||
center = getGround(world, center);
|
||||
|
||||
if (!canSpawn(world, center, random)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int posY = center.getY() + 1;
|
||||
StructureTemplate structure = getStructure(world, center, random);
|
||||
Rotation rotation = getRotation(world, center, random);
|
||||
Mirror mirror = getMirror(world, center, random);
|
||||
BlockPos offset = StructureTemplate.transform(
|
||||
new BlockPos(structure.getSize()),
|
||||
mirror,
|
||||
rotation,
|
||||
BlockPos.ZERO
|
||||
);
|
||||
center = center.offset(0, getYOffset(structure, world, center, random) + 0.5, 0);
|
||||
|
||||
BoundingBox bounds = makeBox(center);
|
||||
StructurePlaceSettings placementData = new StructurePlaceSettings()
|
||||
.setRotation(rotation)
|
||||
.setMirror(mirror)
|
||||
.setBoundingBox(bounds);
|
||||
addStructureData(placementData);
|
||||
center = center.offset(-offset.getX() * 0.5, 0, -offset.getZ() * 0.5);
|
||||
structure.placeInWorld(world, center, center, placementData, random, 4);
|
||||
|
||||
TerrainMerge merge = getTerrainMerge(world, center, random);
|
||||
int x1 = center.getX();
|
||||
int z1 = center.getZ();
|
||||
int x2 = x1 + offset.getX();
|
||||
int z2 = z1 + offset.getZ();
|
||||
if (merge != TerrainMerge.NONE) {
|
||||
MutableBlockPos mut = new MutableBlockPos();
|
||||
|
||||
if (x2 < x1) {
|
||||
int a = x1;
|
||||
x1 = x2;
|
||||
x2 = a;
|
||||
}
|
||||
|
||||
if (z2 < z1) {
|
||||
int a = z1;
|
||||
z1 = z2;
|
||||
z2 = a;
|
||||
}
|
||||
|
||||
int surfMax = posY - 1;
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
mut.setX(x);
|
||||
for (int z = z1; z <= z2; z++) {
|
||||
mut.setZ(z);
|
||||
mut.setY(surfMax);
|
||||
BlockState state = world.getBlockState(mut);
|
||||
if (!isTerrain(state) && state.isFaceSturdy(world, mut, Direction.DOWN)) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mut.setY(mut.getY() - 1);
|
||||
BlockState stateSt = world.getBlockState(mut);
|
||||
if (!isTerrain(stateSt)) {
|
||||
if (merge == TerrainMerge.SURFACE) {
|
||||
boolean isTop = mut.getY() == surfMax && state.getMaterial().isSolidBlocking();
|
||||
Holder<Biome> b = world.getBiome(mut);
|
||||
BlockState top = (isTop
|
||||
? BiomeAPI.findTopMaterial(b)
|
||||
: BiomeAPI.findUnderMaterial(b)).orElse(defaultBlock);
|
||||
BlocksHelper.setWithoutUpdate(world, mut, top);
|
||||
} else {
|
||||
BlocksHelper.setWithoutUpdate(world, mut, state);
|
||||
}
|
||||
} else {
|
||||
if (isTerrain(state) && state.getMaterial().isSolidBlocking()) {
|
||||
if (merge == TerrainMerge.SURFACE) {
|
||||
Holder<Biome> b = world.getBiome(mut);
|
||||
BlockState bottom = BiomeAPI.findUnderMaterial(b).orElse(defaultBlock);
|
||||
BlocksHelper.setWithoutUpdate(world, mut, bottom);
|
||||
} else {
|
||||
BlocksHelper.setWithoutUpdate(world, mut, state);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//BlocksHelper.fixBlocks(world, new BlockPos(x1, center.getY(), z1), new BlockPos(x2, center.getY() + offset.getY(), z2));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isTerrain(BlockState state) {
|
||||
return state.is(CommonBlockTags.END_STONES) || state.is(CommonBlockTags.NETHER_STONES);
|
||||
}
|
||||
|
||||
protected BoundingBox makeBox(BlockPos pos) {
|
||||
int sx = ((pos.getX() >> 4) << 4) - 16;
|
||||
int sz = ((pos.getZ() >> 4) << 4) - 16;
|
||||
int ex = sx + 47;
|
||||
int ez = sz + 47;
|
||||
return BoundingBox.fromCorners(new Vec3i(sx, 0, sz), new Vec3i(ex, 255, ez));
|
||||
}
|
||||
|
||||
protected static StructureTemplate readStructure(ResourceLocation resource) {
|
||||
String ns = resource.getNamespace();
|
||||
String nm = resource.getPath();
|
||||
|
||||
try {
|
||||
InputStream inputstream = MinecraftServer.class.getResourceAsStream("/data/" + ns + "/structures/" + nm + ".nbt");
|
||||
return readStructureFromStream(inputstream);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static StructureTemplate readStructureFromStream(InputStream stream) throws IOException {
|
||||
CompoundTag nbttagcompound = NbtIo.readCompressed(stream);
|
||||
|
||||
StructureTemplate template = new StructureTemplate();
|
||||
template.load(nbttagcompound);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
public enum TerrainMerge {
|
||||
NONE, SURFACE, OBJECT;
|
||||
|
||||
public static TerrainMerge getFromString(String type) {
|
||||
if (type.equals("surface")) {
|
||||
return SURFACE;
|
||||
} else if (type.equals("object")) {
|
||||
return OBJECT;
|
||||
} else {
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
250
src/main/java/org/betterx/bclib/api/features/ScatterFeature.java
Normal file
250
src/main/java/org/betterx/bclib/api/features/ScatterFeature.java
Normal file
|
@ -0,0 +1,250 @@
|
|||
package org.betterx.bclib.api.features;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.data.worldgen.placement.PlacementUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.util.valueproviders.ClampedNormalInt;
|
||||
import net.minecraft.util.valueproviders.ConstantInt;
|
||||
import net.minecraft.util.valueproviders.UniformInt;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.SimpleRandomFeatureConfiguration;
|
||||
import net.minecraft.world.level.levelgen.placement.*;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import org.betterx.bclib.api.features.config.ScatterFeatureConfig;
|
||||
import org.betterx.bclib.api.tag.CommonBlockTags;
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ScatterFeature<FC extends ScatterFeatureConfig>
|
||||
extends Feature<FC> {
|
||||
|
||||
public static <T extends ScatterFeatureConfig> BCLFeature createAndRegister(ResourceLocation location,
|
||||
int minPerChunk,
|
||||
int maxPerChunk,
|
||||
T cfg,
|
||||
Feature<T> inlineFeature) {
|
||||
List<Holder<PlacedFeature>> set = new ArrayList<>(2);
|
||||
if (cfg.floorChance > 0) set.add(PlacementUtils.inlinePlaced(inlineFeature,
|
||||
cfg,
|
||||
EnvironmentScanPlacement.scanningFor(Direction.DOWN,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.ONLY_IN_AIR_PREDICATE,
|
||||
12),
|
||||
RandomOffsetPlacement.vertical(ConstantInt.of(1))));
|
||||
|
||||
if (cfg.floorChance < 1) {
|
||||
set.add(PlacementUtils.inlinePlaced(inlineFeature,
|
||||
cfg,
|
||||
EnvironmentScanPlacement.scanningFor(Direction.UP,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.ONLY_IN_AIR_PREDICATE,
|
||||
12),
|
||||
RandomOffsetPlacement.vertical(ConstantInt.of(-1))));
|
||||
}
|
||||
SimpleRandomFeatureConfiguration configuration = new SimpleRandomFeatureConfiguration(HolderSet.direct(set));
|
||||
|
||||
return BCLFeatureBuilder.start(location, SIMPLE_RANDOM_SELECTOR)
|
||||
.decoration(GenerationStep.Decoration.VEGETAL_DECORATION)
|
||||
.modifier(CountPlacement.of(UniformInt.of(minPerChunk, maxPerChunk)))
|
||||
.modifier(InSquarePlacement.spread())
|
||||
.randomHeight4FromFloorCeil()
|
||||
.modifier(CountPlacement.of(UniformInt.of(2, 5)))
|
||||
.modifier(RandomOffsetPlacement.of(
|
||||
ClampedNormalInt.of(0.0f, 2.0f, -6, 6),
|
||||
ClampedNormalInt.of(0.0f, 0.6f, -2, 2)))
|
||||
.modifier(BiomeFilter.biome())
|
||||
.buildAndRegister(configuration);
|
||||
}
|
||||
|
||||
public ScatterFeature(Codec<FC> configCodec) {
|
||||
super(configCodec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<FC> featurePlaceContext) {
|
||||
final WorldGenLevel level = featurePlaceContext.level();
|
||||
final BlockPos origin = featurePlaceContext.origin();
|
||||
final RandomSource random = featurePlaceContext.random();
|
||||
|
||||
ScatterFeatureConfig config = featurePlaceContext.config();
|
||||
Optional<Direction> direction = getTipDirection(level, origin, random, config);
|
||||
if (direction.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
BlockPos basePos = origin.relative(direction.get().getOpposite());
|
||||
|
||||
int i = (int) (random.nextFloat() * (1 + config.maxHeight - config.minHeight) + config.minHeight);
|
||||
growCenterPillar(level, origin, basePos, direction.get(), i, config, random);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected void growCenterPillar(LevelAccessor level,
|
||||
BlockPos origin,
|
||||
BlockPos basePos,
|
||||
Direction direction,
|
||||
int centerHeight,
|
||||
ScatterFeatureConfig config,
|
||||
RandomSource random) {
|
||||
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));
|
||||
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.findSurroundingSurface(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;
|
||||
myHeight = (int) Math.min(Math.max(
|
||||
config.minHeight,
|
||||
config.minHeight + sizeFactor * (myHeight - config.minHeight)
|
||||
), config.maxHeight);
|
||||
|
||||
buildPillarWithBase(level,
|
||||
POS,
|
||||
POS.relative(direction.getOpposite()),
|
||||
direction,
|
||||
myHeight,
|
||||
config,
|
||||
random);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buildPillarWithBase(LevelAccessor level,
|
||||
BlockPos origin,
|
||||
BlockPos basePos,
|
||||
Direction direction,
|
||||
int height,
|
||||
ScatterFeatureConfig config,
|
||||
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, random);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildPillar(LevelAccessor level,
|
||||
BlockPos origin,
|
||||
Direction direction,
|
||||
int height,
|
||||
ScatterFeatureConfig config,
|
||||
RandomSource random) {
|
||||
|
||||
final BlockPos.MutableBlockPos POS = origin.mutable();
|
||||
buildBaseToTipColumn(height, (blockState) -> {
|
||||
BlocksHelper.setWithoutUpdate(level, POS, blockState);
|
||||
POS.move(direction);
|
||||
}, config, random);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Direction> getTipDirection(LevelAccessor levelAccessor,
|
||||
BlockPos blockPos,
|
||||
RandomSource randomSource,
|
||||
ScatterFeatureConfig config) {
|
||||
boolean onCeil = config.floorChance < 1 && config.isValidBase(levelAccessor.getBlockState(blockPos.above()));
|
||||
boolean onFloor = config.floorChance > 0 && config.isValidBase(levelAccessor.getBlockState(blockPos.below()));
|
||||
|
||||
if (onCeil && onFloor) {
|
||||
return Optional.of(config.isFloor(randomSource) ? Direction.DOWN : Direction.UP);
|
||||
}
|
||||
if (onCeil) {
|
||||
return Optional.of(Direction.DOWN);
|
||||
}
|
||||
if (onFloor) {
|
||||
return Optional.of(Direction.UP);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void createPatchOfBaseBlocks(LevelAccessor levelAccessor,
|
||||
RandomSource randomSource,
|
||||
BlockPos blockPos,
|
||||
ScatterFeatureConfig config) {
|
||||
if (config.baseState.isPresent() && config.baseReplaceChance > 0 && randomSource.nextFloat() < config.baseReplaceChance) {
|
||||
final BlockState baseState = config.baseState.get();
|
||||
BlockPos pos;
|
||||
for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
if (randomSource.nextFloat() > config.chanceOfDirectionalSpread) continue;
|
||||
pos = blockPos.relative(direction);
|
||||
placeBaseBlockIfPossible(levelAccessor, pos, baseState);
|
||||
|
||||
if (randomSource.nextFloat() > config.chanceOfSpreadRadius2) continue;
|
||||
pos = pos.relative(Direction.getRandom(randomSource));
|
||||
placeBaseBlockIfPossible(levelAccessor, pos, baseState);
|
||||
|
||||
if (randomSource.nextFloat() > config.chanceOfSpreadRadius3) continue;
|
||||
pos = pos.relative(Direction.getRandom(randomSource));
|
||||
placeBaseBlockIfPossible(levelAccessor, pos, baseState);
|
||||
}
|
||||
placeBaseBlockIfPossible(levelAccessor, blockPos, baseState);
|
||||
}
|
||||
}
|
||||
|
||||
protected void placeBaseBlockIfPossible(LevelAccessor levelAccessor,
|
||||
BlockPos blockPos,
|
||||
BlockState baseState) {
|
||||
BlockState blockState = levelAccessor.getBlockState(blockPos);
|
||||
if (blockState.is(CommonBlockTags.TERRAIN)) {
|
||||
levelAccessor.setBlock(blockPos, baseState, 2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.betterx.bclib.api.features;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
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 net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class SurfaceFeature<T extends FeatureConfiguration> extends Feature<T> {
|
||||
public static abstract class DefaultConfiguration extends SurfaceFeature<NoneFeatureConfiguration> {
|
||||
protected DefaultConfiguration() {
|
||||
super(NoneFeatureConfiguration.CODEC);
|
||||
}
|
||||
}
|
||||
|
||||
protected SurfaceFeature(Codec<T> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
protected abstract boolean isValidSurface(BlockState state);
|
||||
|
||||
protected int minHeight(FeaturePlaceContext<T> ctx) {
|
||||
return ctx.chunkGenerator().getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<T> ctx) {
|
||||
Optional<BlockPos> pos = BlocksHelper.findSurfaceBelow(ctx.level(),
|
||||
ctx.origin(),
|
||||
minHeight(ctx),
|
||||
this::isValidSurface);
|
||||
if (pos.isPresent()) {
|
||||
generate(pos.get(), ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract void generate(BlockPos centerPos, FeaturePlaceContext<T> ctx);
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package org.betterx.bclib.api.features;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
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,
|
||||
int onceEveryChunk) {
|
||||
|
||||
|
||||
return BCLFeatureBuilder
|
||||
.start(location, INSTANCE)
|
||||
.decoration(GenerationStep.Decoration.SURFACE_STRUCTURES)
|
||||
.oncePerChunks(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
|
||||
.findSolidFloor(12) //cast downward ray to find solid surface
|
||||
.isEmptyAbove4() //make sure we have 4 free blocks above
|
||||
.onlyInBiome() //ensure that we still are in the correct biome
|
||||
|
||||
.buildAndRegister(configuration);
|
||||
}
|
||||
|
||||
public static <T extends TemplateFeatureConfig> BCLFeature createAndRegister(ResourceLocation location,
|
||||
TemplateFeatureConfig configuration,
|
||||
int count) {
|
||||
return BCLFeatureBuilder
|
||||
.start(location, INSTANCE)
|
||||
.decoration(GenerationStep.Decoration.SURFACE_STRUCTURES)
|
||||
.count(count)
|
||||
.squarePlacement()
|
||||
.randomHeight10FromFloorCeil()
|
||||
.findSolidFloor(12) //cast downward ray to find solid surface
|
||||
.isEmptyAbove4()
|
||||
.onlyInBiome()
|
||||
.buildAndRegister(configuration);
|
||||
}
|
||||
|
||||
public TemplateFeature(Codec<FC> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
protected StructureWorldNBT randomStructure(TemplateFeatureConfig cfg, RandomSource random) {
|
||||
|
||||
if (cfg.structures.size() > 1) {
|
||||
final float chanceSum = cfg.structures.parallelStream().map(c -> c.chance).reduce(0.0f, (p, c) -> p + c);
|
||||
float rnd = random.nextFloat() * chanceSum;
|
||||
|
||||
for (StructureWorldNBT c : cfg.structures) {
|
||||
rnd -= c.chance;
|
||||
if (rnd <= 0) return c;
|
||||
}
|
||||
} else {
|
||||
return cfg.structures.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<FC> ctx) {
|
||||
StructureWorldNBT structure = randomStructure(ctx.config(), ctx.random());
|
||||
return structure.generateIfPlaceable(ctx.level(),
|
||||
ctx.origin(),
|
||||
StructureNBT.getRandomRotation(ctx.random()),
|
||||
StructureNBT.getRandomMirror(ctx.random())
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
package org.betterx.bclib.api.features.config;
|
||||
|
||||
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.Function15;
|
||||
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 Function15<BlockState, BlockState, BlockState, Optional<BlockState>, Float, Float, Float, Float, Integer, Integer, Float, Float, Float, Boolean, IntProvider, 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;
|
||||
public final float chanceOfSpreadRadius2;
|
||||
public final float chanceOfSpreadRadius3;
|
||||
public final int minHeight;
|
||||
public final int maxHeight;
|
||||
public final float maxSpread;
|
||||
public final float sizeVariation;
|
||||
public final float floorChance;
|
||||
|
||||
public final IntProvider spreadCount;
|
||||
|
||||
public final boolean growWhileFree;
|
||||
|
||||
public ScatterFeatureConfig(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,
|
||||
IntProvider spreadCount) {
|
||||
this.clusterBlock = clusterBlock;
|
||||
this.tipBlock = tipBlock == null ? clusterBlock : tipBlock;
|
||||
this.bottomBlock = bottomBlock == null ? clusterBlock : bottomBlock;
|
||||
this.baseState = baseState;
|
||||
this.baseReplaceChance = baseReplaceChance;
|
||||
this.chanceOfDirectionalSpread = chanceOfDirectionalSpread;
|
||||
this.chanceOfSpreadRadius2 = chanceOfSpreadRadius2;
|
||||
this.chanceOfSpreadRadius3 = chanceOfSpreadRadius3;
|
||||
this.minHeight = minHeight;
|
||||
this.maxHeight = maxHeight;
|
||||
this.maxSpread = maxSpread;
|
||||
this.sizeVariation = sizeVariation;
|
||||
this.floorChance = floorChance;
|
||||
this.growWhileFree = growWhileFree;
|
||||
this.spreadCount = spreadCount;
|
||||
}
|
||||
|
||||
|
||||
public boolean isFloor(RandomSource random) {
|
||||
return random.nextFloat() < floorChance;
|
||||
}
|
||||
|
||||
public abstract boolean isValidBase(BlockState state);
|
||||
|
||||
public abstract BlockState createBlock(int height, int maxHeight, RandomSource random);
|
||||
|
||||
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),
|
||||
Codec
|
||||
.floatRange(0.0F, 1.0F)
|
||||
.fieldOf("baseReplaceChance")
|
||||
.orElse(1.0F)
|
||||
.forGetter((T cfg) -> cfg.baseReplaceChance),
|
||||
Codec
|
||||
.floatRange(0.0F, 1.0F)
|
||||
.fieldOf("chance_of_directional_spread")
|
||||
.orElse(0.7F)
|
||||
.forGetter((T cfg) -> cfg.chanceOfDirectionalSpread),
|
||||
Codec
|
||||
.floatRange(0.0F, 1.0F)
|
||||
.fieldOf("chance_of_spread_radius2")
|
||||
.orElse(0.5F)
|
||||
.forGetter((T cfg) -> cfg.chanceOfSpreadRadius2),
|
||||
Codec
|
||||
.floatRange(0.0F, 1.0F)
|
||||
.fieldOf("chance_of_spread_radius3")
|
||||
.orElse(0.5F)
|
||||
.forGetter((T cfg) -> cfg.chanceOfSpreadRadius3),
|
||||
Codec
|
||||
.intRange(1, 20)
|
||||
.fieldOf("min_height")
|
||||
.orElse(2)
|
||||
.forGetter((T cfg) -> cfg.minHeight),
|
||||
Codec
|
||||
.intRange(1, 20)
|
||||
.fieldOf("max_height")
|
||||
.orElse(7)
|
||||
.forGetter((T cfg) -> cfg.maxHeight),
|
||||
Codec
|
||||
.floatRange(0, 10)
|
||||
.fieldOf("max_spread")
|
||||
.orElse(2f)
|
||||
.forGetter((T cfg) -> cfg.maxSpread),
|
||||
Codec
|
||||
.floatRange(0, 1)
|
||||
.fieldOf("size_variation")
|
||||
.orElse(0.7f)
|
||||
.forGetter((T cfg) -> cfg.sizeVariation),
|
||||
Codec
|
||||
.floatRange(0, 1)
|
||||
.fieldOf("floor_chance")
|
||||
.orElse(0.5f)
|
||||
.forGetter((T cfg) -> cfg.floorChance),
|
||||
Codec
|
||||
.BOOL
|
||||
.fieldOf("grow_while_empty")
|
||||
.orElse(false)
|
||||
.forGetter((T cfg) -> cfg.growWhileFree),
|
||||
IntProvider.codec(0, 64)
|
||||
.fieldOf("length")
|
||||
.orElse(UniformInt.of(0, 3))
|
||||
.forGetter(cfg -> cfg.spreadCount)
|
||||
)
|
||||
.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;
|
||||
public IntProvider spreadCount = ConstantInt.of(0);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
this.spreadCount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
IntProvider spreadCount) {
|
||||
super(clusterBlock,
|
||||
tipBlock,
|
||||
bottomBlock,
|
||||
baseState,
|
||||
baseReplaceChance,
|
||||
chanceOfDirectionalSpread,
|
||||
chanceOfSpreadRadius2,
|
||||
chanceOfSpreadRadius3,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
maxSpread,
|
||||
sizeVariation,
|
||||
floorChance,
|
||||
growWhileFree,
|
||||
spreadCount);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.betterx.bclib.api.features.config;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import org.betterx.bclib.world.structures.StructurePlacementType;
|
||||
import org.betterx.bclib.world.structures.StructureWorldNBT;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TemplateFeatureConfig implements FeatureConfiguration {
|
||||
public static final Codec<TemplateFeatureConfig> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
ExtraCodecs.nonEmptyList(StructureWorldNBT.CODEC.listOf())
|
||||
.fieldOf("structures")
|
||||
.forGetter((TemplateFeatureConfig cfg) -> cfg.structures)
|
||||
)
|
||||
.apply(instance, TemplateFeatureConfig::new)
|
||||
);
|
||||
|
||||
public final List<StructureWorldNBT> structures;
|
||||
|
||||
public static StructureWorldNBT cfg(ResourceLocation location,
|
||||
int offsetY,
|
||||
StructurePlacementType type,
|
||||
float chance) {
|
||||
return StructureWorldNBT.create(location, offsetY, type, chance);
|
||||
}
|
||||
|
||||
public TemplateFeatureConfig(ResourceLocation location, int offsetY, StructurePlacementType type) {
|
||||
this(List.of(cfg(location, offsetY, type, 1.0f)));
|
||||
}
|
||||
|
||||
public TemplateFeatureConfig(List<StructureWorldNBT> structures) {
|
||||
this.structures = structures;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.betterx.bclib.api.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.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) {
|
||||
var builder = Stream.<BlockPos>builder();
|
||||
final int count = length.sample(random);
|
||||
builder.add(blockPos);
|
||||
for (int y = 1; y < count + 1; y++) {
|
||||
builder.add(blockPos.relative(direction, y));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.EXTEND;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package org.betterx.bclib.api.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, 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),
|
||||
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.findSurroundingSurface(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,43 @@
|
|||
package org.betterx.bclib.api.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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.betterx.bclib.api.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 IsBasin extends PlacementFilter {
|
||||
public static final Codec<IsBasin> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
BlockPredicate.CODEC
|
||||
.fieldOf("predicate")
|
||||
.forGetter(cfg -> cfg.predicate)
|
||||
)
|
||||
.apply(instance, IsBasin::new));
|
||||
|
||||
private final BlockPredicate predicate;
|
||||
|
||||
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) {
|
||||
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
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.IS_BASIN;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package org.betterx.bclib.api.features.placement;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests if there is air at two locations above the tested block position
|
||||
*/
|
||||
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),
|
||||
Codec.intRange(1, 32).fieldOf("d2").orElse(4).forGetter((p) -> p.distance1)
|
||||
)
|
||||
.apply(instance, IsEmptyAboveSampledFilter::new));
|
||||
|
||||
public static PlacementFilter emptyAbove4() {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
public static PlacementFilter emptyAbove() {
|
||||
return DEFAULT1;
|
||||
}
|
||||
|
||||
public IsEmptyAboveSampledFilter(int d1, int d2) {
|
||||
this.distance1 = d1;
|
||||
this.distance2 = d2;
|
||||
}
|
||||
|
||||
private final int distance1;
|
||||
private final int distance2;
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
|
||||
WorldGenLevel level = ctx.getLevel();
|
||||
if (level.isEmptyBlock(pos.above(distance1))
|
||||
&& (distance1 == distance2 || level.isEmptyBlock(pos.above(distance2)))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.IS_EMPTY_ABOVE_SAMPLED_FILTER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package org.betterx.bclib.api.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.PlacementFilter;
|
||||
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.util.BlocksHelper;
|
||||
|
||||
public class MinEmptyFilter extends PlacementFilter {
|
||||
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.distance)
|
||||
)
|
||||
.apply(instance, MinEmptyFilter::new));
|
||||
|
||||
private final Direction direction;
|
||||
private final int distance;
|
||||
|
||||
protected MinEmptyFilter(Direction direction, int distance) {
|
||||
this.direction = direction;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
public static PlacementModifier down() {
|
||||
return DOWN;
|
||||
}
|
||||
|
||||
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) {
|
||||
return BlocksHelper.isFreeSpace(
|
||||
ctx.getLevel(),
|
||||
pos.relative(direction),
|
||||
direction,
|
||||
distance - 1,
|
||||
state -> state.getMaterial().isReplaceable()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.MIN_EMPTY_FILTER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.betterx.bclib.api.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
|
||||
.fieldOf("blocks")
|
||||
.forGetter(cfg -> cfg.offset)
|
||||
)
|
||||
.apply(instance, Offset::new));
|
||||
|
||||
private final Vec3i offset;
|
||||
|
||||
public Offset(Vec3i offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public static Offset inDirection(Direction dir) {
|
||||
return DIRECTIONS.get(dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<BlockPos> getPositions(PlacementContext placementContext,
|
||||
RandomSource randomSource,
|
||||
BlockPos blockPos) {
|
||||
return Stream.of(blockPos.offset(offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.OFFSET;
|
||||
}
|
||||
|
||||
static {
|
||||
for (Direction d : Direction.values())
|
||||
DIRECTIONS.put(d, new Offset(d.getNormal()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package org.betterx.bclib.api.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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package org.betterx.bclib.api.features.placement;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import org.betterx.bclib.BCLib;
|
||||
|
||||
public class PlacementModifiers {
|
||||
public static final PlacementModifierType<IsEmptyAboveSampledFilter> IS_EMPTY_ABOVE_SAMPLED_FILTER = register(
|
||||
"is_empty_above_sampled_filter",
|
||||
IsEmptyAboveSampledFilter.CODEC);
|
||||
|
||||
public static final PlacementModifierType<MinEmptyFilter> MIN_EMPTY_FILTER = register(
|
||||
"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<Is> IS = register(
|
||||
"is",
|
||||
Is.CODEC);
|
||||
|
||||
public static final PlacementModifierType<Offset> OFFSET = register(
|
||||
"offset",
|
||||
Offset.CODEC);
|
||||
|
||||
public static final PlacementModifierType<Extend> EXTEND = register(
|
||||
"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);
|
||||
}
|
||||
|
||||
public static <P extends PlacementModifier> PlacementModifierType<P> register(ResourceLocation location,
|
||||
Codec<P> codec) {
|
||||
return Registry.register(Registry.PLACEMENT_MODIFIERS, location, () -> codec);
|
||||
}
|
||||
|
||||
public static void ensureStaticInitialization() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,344 @@
|
|||
package org.betterx.bclib.api.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 final Stencil DEFAULT4;
|
||||
private final int selectOneIn;
|
||||
|
||||
private static List<Boolean> convert(Boolean[] s) {
|
||||
return Arrays.stream(s).toList();
|
||||
}
|
||||
|
||||
public Stencil(Boolean[] stencil, int selectOneIn) {
|
||||
this(convert(stencil), selectOneIn);
|
||||
}
|
||||
|
||||
public Stencil(List<Boolean> stencil, int selectOneIn) {
|
||||
this.stencil = stencil;
|
||||
this.selectOneIn = selectOneIn;
|
||||
}
|
||||
|
||||
public static Stencil all() {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
public static Stencil oneIn4() {
|
||||
return DEFAULT4;
|
||||
}
|
||||
|
||||
@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, 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),
|
||||
Codec.INT
|
||||
.fieldOf("one_in")
|
||||
.orElse(1)
|
||||
.forGetter((Stencil a) -> a.selectOneIn)
|
||||
)
|
||||
.apply(instance, Stencil::new)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue