Template Structures (but missaligned)

This commit is contained in:
Frank 2022-05-31 13:52:08 +02:00
parent 86a92c560b
commit 38f337cd15
6 changed files with 307 additions and 137 deletions

View file

@ -25,6 +25,7 @@ import org.betterx.bclib.util.Logger;
import org.betterx.bclib.world.generator.BCLibEndBiomeSource;
import org.betterx.bclib.world.generator.BCLibNetherBiomeSource;
import org.betterx.bclib.world.generator.GeneratorOptions;
import org.betterx.bclib.world.structures.TemplatePiece;
import java.util.List;
@ -58,6 +59,7 @@ public class BCLib implements ModInitializer {
);
BCLibPatch.register();
TemplatePiece.ensureStaticInitialization();
Configs.save();
if (isDevEnvironment()) {
Biome.BiomeBuilder builder = new Biome.BiomeBuilder()

View file

@ -12,10 +12,12 @@ import net.minecraft.world.level.levelgen.placement.EnvironmentScanPlacement;
import com.mojang.serialization.Codec;
import org.betterx.bclib.api.features.BCLFeatureBuilder;
import org.betterx.bclib.world.structures.StructureNBT;
public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<FC> {
public static final Feature<TemplateFeatureConfig> INSTANCE = BCLFeature.register("template",
new TemplateFeature(TemplateFeatureConfig.CODEC));
new TemplateFeature(
TemplateFeatureConfig.CODEC));
public static <T extends TemplateFeatureConfig> BCLFeature createAndRegister(TemplateFeatureConfig configuration,
int onveEveryChunk) {
@ -28,7 +30,9 @@ public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<F
.distanceToTopAndBottom10()
.modifier(EnvironmentScanPlacement.scanningFor(Direction.DOWN,
BlockPredicate.solid(),
BlockPredicate.matchesBlocks(Blocks.AIR, Blocks.WATER, Blocks.LAVA),
BlockPredicate.matchesBlocks(Blocks.AIR,
Blocks.WATER,
Blocks.LAVA),
12))
.modifier(BiomeFilter.biome())
.buildAndRegister(configuration);
@ -40,6 +44,10 @@ public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<F
@Override
public boolean place(FeaturePlaceContext<FC> ctx) {
return ctx.config().structure.generateInRandomOrientation(ctx.level(), ctx.origin(), ctx.random());
return ctx.config().structure.generateIfPlaceable(ctx.level(),
ctx.origin(),
StructureNBT.getRandomRotation(ctx.random()),
StructureNBT.getRandomMirror(ctx.random())
);
}
}

View file

@ -26,8 +26,7 @@ import java.util.Map;
public class StructureNBT {
public final ResourceLocation location;
protected StructureTemplate structure;
protected Mirror mirror = Mirror.NONE;
protected Rotation rotation = Rotation.NONE;
protected StructureNBT(ResourceLocation location) {
this.location = location;
@ -39,44 +38,33 @@ public class StructureNBT {
this.structure = structure;
}
public static Rotation getRandomRotation(RandomSource random) {
return Rotation.getRandom(random) == Rotation.NONE ? Rotation.CLOCKWISE_90 : Rotation.COUNTERCLOCKWISE_90;
}
public static Mirror getRandomMirror(RandomSource random) {
return Mirror.values()[random.nextInt(3)];
}
private static final Map<ResourceLocation, StructureNBT> STRUCTURE_CACHE = Maps.newHashMap();
public static StructureNBT create(ResourceLocation location) {
return STRUCTURE_CACHE.computeIfAbsent(location, r -> new StructureNBT(r));
}
public StructureNBT setRotation(Rotation rotation) {
this.rotation = rotation;
return this;
}
public Mirror getMirror() {
return mirror;
}
public StructureNBT setMirror(Mirror mirror) {
this.mirror = mirror;
return this;
}
public void randomRM(RandomSource random) {
rotation = Rotation.values()[random.nextInt(4)];
mirror = Mirror.values()[random.nextInt(3)];
}
public boolean generateCentered(ServerLevelAccessor world, BlockPos pos) {
public boolean generateCentered(ServerLevelAccessor world, BlockPos pos, Rotation rotation, Mirror mirror) {
if (structure == null) {
BCLib.LOGGER.error("No structure: " + location.toString());
return false;
}
MutableBlockPos blockpos2 = new MutableBlockPos().set(structure.getSize());
if (this.mirror == Mirror.FRONT_BACK)
if (mirror == Mirror.FRONT_BACK)
blockpos2.setX(-blockpos2.getX());
if (this.mirror == Mirror.LEFT_RIGHT)
if (mirror == Mirror.LEFT_RIGHT)
blockpos2.setZ(-blockpos2.getZ());
blockpos2.set(blockpos2.rotate(rotation));
StructurePlaceSettings data = new StructurePlaceSettings().setRotation(this.rotation).setMirror(this.mirror);
StructurePlaceSettings data = new StructurePlaceSettings().setRotation(rotation).setMirror(mirror);
BlockPos newPos = pos.offset(-blockpos2.getX() >> 1, 0, -blockpos2.getZ() >> 1);
structure.placeInWorld(
world,
@ -118,7 +106,7 @@ public class StructureNBT {
return template;
}
public BlockPos getSize() {
public BlockPos getSize(Rotation rotation) {
if (rotation == Rotation.NONE || rotation == Rotation.CLOCKWISE_180)
return new BlockPos(structure.getSize());
else {
@ -133,7 +121,7 @@ public class StructureNBT {
return location.getPath();
}
public BoundingBox getBoundingBox(BlockPos pos) {
return structure.getBoundingBox(new StructurePlaceSettings().setRotation(this.rotation).setMirror(mirror), pos);
public BoundingBox getBoundingBox(BlockPos pos, Rotation rotation, Mirror mirror) {
return structure.getBoundingBox(new StructurePlaceSettings().setRotation(rotation).setMirror(mirror), pos);
}
}

View file

@ -3,10 +3,10 @@ package org.betterx.bclib.world.structures;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
@ -33,27 +33,29 @@ public class StructureWorldNBT extends StructureNBT {
}
public boolean generateInRandomOrientation(ServerLevelAccessor level, BlockPos pos, RandomSource random) {
randomRM(random);
return generate(level, pos);
}
public boolean generate(ServerLevelAccessor level, BlockPos pos) {
if (canGenerate(level, pos)) {
return generateCentered(level, pos.above(offsetY));
public boolean generateIfPlaceable(ServerLevelAccessor level,
BlockPos pos,
Rotation r,
Mirror m) {
if (canGenerate(level, pos, r)) {
return generate(level, pos, r, m);
}
return false;
}
protected boolean canGenerate(LevelAccessor level, BlockPos pos) {
public boolean generate(ServerLevelAccessor level, BlockPos pos, Rotation r, Mirror m) {
return generateCentered(level, pos.above(offsetY), r, m);
}
protected boolean canGenerate(LevelAccessor level, BlockPos pos, Rotation rotation) {
if (type == StructurePlacementType.FLOOR)
return canGenerateFloor(level, pos);
return canGenerateFloor(level, pos, rotation);
else if (type == StructurePlacementType.LAVA)
return canGenerateLava(level, pos);
return canGenerateLava(level, pos, rotation);
else if (type == StructurePlacementType.UNDER)
return canGenerateUnder(level, pos);
return canGenerateUnder(level, pos, rotation);
else if (type == StructurePlacementType.CEIL)
return canGenerateCeil(level, pos);
return canGenerateCeil(level, pos, rotation);
else
return false;
}
@ -67,46 +69,35 @@ public class StructureWorldNBT extends StructureNBT {
return false;
}
protected boolean canGenerateFloor(LevelAccessor world, BlockPos pos) {
protected boolean canGenerateFloor(LevelAccessor world, BlockPos pos, Rotation rotation) {
if (containsBedrock(world, pos)) return false;
return getAirFraction(world, pos) > 0.6 && getAirFractionFoundation(world, pos) < 0.5;
return getAirFraction(world, pos, rotation) > 0.6 && getAirFractionFoundation(world, pos, rotation) < 0.5;
}
protected boolean canGenerateLava(LevelAccessor world, BlockPos pos) {
protected boolean canGenerateLava(LevelAccessor world, BlockPos pos, Rotation rotation) {
if (containsBedrock(world, pos)) return false;
return getLavaFractionFoundation(world, pos) > 0.9 && getAirFraction(world, pos) > 0.9;
return getLavaFractionFoundation(world, pos, rotation) > 0.9 && getAirFraction(world, pos, rotation) > 0.9;
}
protected boolean canGenerateUnder(LevelAccessor world, BlockPos pos) {
protected boolean canGenerateUnder(LevelAccessor world, BlockPos pos, Rotation rotation) {
if (containsBedrock(world, pos)) return false;
return getAirFraction(world, pos) < 0.2;
return getAirFraction(world, pos, rotation) < 0.2;
}
protected boolean canGenerateCeil(LevelAccessor world, BlockPos pos) {
protected boolean canGenerateCeil(LevelAccessor world, BlockPos pos, Rotation rotation) {
if (containsBedrock(world, pos)) return false;
return getAirFractionBottom(world, pos) > 0.8 && getAirFraction(world, pos) < 0.6;
return getAirFractionBottom(world, pos, rotation) > 0.8 && getAirFraction(world, pos, rotation) < 0.6;
}
public BoundingBox boundingBox(Rotation r, BlockPos p) {
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
size.setX(Math.abs(size.getX()) >> 1);
size.setY(Math.abs(size.getY()) >> 1);
size.setZ(Math.abs(size.getZ()) >> 1);
return new BoundingBox(
p.getX() - size.getX(),
p.getY() - size.getY(),
p.getZ() - size.getZ(),
p.getX() + size.getX(),
p.getY() + size.getY(),
p.getZ() + size.getZ()
);
return getBoundingBox(p, r, Mirror.NONE);
}
protected float getAirFraction(LevelAccessor world, BlockPos pos) {
protected float getAirFraction(LevelAccessor world, BlockPos pos, Rotation rotation) {
final MutableBlockPos POS = new MutableBlockPos();
int airCount = 0;
@ -134,7 +125,7 @@ public class StructureWorldNBT extends StructureNBT {
return (float) airCount / count;
}
private float getLavaFractionFoundation(LevelAccessor world, BlockPos pos) {
private float getLavaFractionFoundation(LevelAccessor world, BlockPos pos, Rotation rotation) {
final MutableBlockPos POS = new MutableBlockPos();
int lavaCount = 0;
@ -161,7 +152,7 @@ public class StructureWorldNBT extends StructureNBT {
return (float) lavaCount / count;
}
private float getAirFractionFoundation(LevelAccessor world, BlockPos pos) {
private float getAirFractionFoundation(LevelAccessor world, BlockPos pos, Rotation rotation) {
final MutableBlockPos POS = new MutableBlockPos();
int airCount = 0;
@ -189,7 +180,7 @@ public class StructureWorldNBT extends StructureNBT {
return (float) airCount / count;
}
private float getAirFractionBottom(LevelAccessor world, BlockPos pos) {
private float getAirFractionBottom(LevelAccessor world, BlockPos pos, Rotation rotation) {
final MutableBlockPos POS = new MutableBlockPos();
int airCount = 0;

View file

@ -6,31 +6,151 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import net.minecraft.world.level.levelgen.structure.TemplateStructurePiece;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceType;
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockIgnoreProcessor;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.util.BlocksHelper;
public class TemplatePiece extends StructurePiece {
public static final StructurePieceType INSTANCE = register("template_piece", TemplatePiece::new);
public class TemplatePiece extends TemplateStructurePiece {
public static final StructurePieceType INSTANCE = setTemplatePieceId(TemplatePiece::new,
"template_piece");
private static StructurePieceType setFullContextPieceId(StructurePieceType structurePieceType, String id) {
return Registry.register(Registry.STRUCTURE_PIECE, BCLib.makeID(id), structurePieceType);
}
private static StructurePieceType setTemplatePieceId(StructurePieceType.StructureTemplateType structureTemplateType,
String string) {
return setFullContextPieceId(structureTemplateType, string);
}
public static void ensureStaticInitialization() {
}
public TemplatePiece(StructureTemplateManager structureTemplateManager,
ResourceLocation resourceLocation,
BlockPos centerPos,
Rotation rotation,
Mirror mirror,
BlockPos halfSize) {
super(INSTANCE,
0,
structureTemplateManager,
resourceLocation,
resourceLocation.toString(),
makeSettings(rotation, mirror, halfSize),
shiftPos(halfSize, centerPos));
}
public TemplatePiece(StructureTemplateManager structureTemplateManager, CompoundTag compoundTag) {
super(INSTANCE,
compoundTag,
structureTemplateManager,
(ResourceLocation resourceLocation) -> makeSettings(compoundTag));
}
@Override
public void postProcess(WorldGenLevel level,
StructureManager structureManager,
ChunkGenerator chunkGenerator,
RandomSource randomSource,
BoundingBox boundingBox,
ChunkPos chunkPos,
BlockPos blockPos) {
super.postProcess(level,
structureManager,
chunkGenerator,
randomSource,
boundingBox,
chunkPos,
blockPos);
BlocksHelper.setWithoutUpdate(level, new BlockPos(boundingBox.minX(), boundingBox.minY(), boundingBox.minZ()),
Blocks.YELLOW_CONCRETE);
BlocksHelper.setWithoutUpdate(level, new BlockPos(boundingBox.maxX(), boundingBox.maxY(), boundingBox.maxZ()),
Blocks.LIGHT_BLUE_CONCRETE);
BlocksHelper.setWithoutUpdate(level, boundingBox.getCenter(),
Blocks.LIME_CONCRETE);
BlocksHelper.setWithoutUpdate(level, blockPos,
Blocks.ORANGE_CONCRETE);
}
private static BlockPos shiftPos(BlockPos halfSize,
BlockPos pos) {
return pos.offset(-(2 * halfSize.getX()), 0, -(2 * halfSize.getZ()));
//return pos;
}
private static StructurePlaceSettings makeSettings(CompoundTag compoundTag) {
return makeSettings(
Rotation.valueOf(compoundTag.getString("R")),
Mirror.valueOf(compoundTag.getString("M")),
new BlockPos(compoundTag.getInt("RX"), compoundTag.getInt("RY"), compoundTag.getInt("RZ")));
}
private static StructurePlaceSettings makeSettings(Rotation rotation, Mirror mirror, BlockPos halfSize) {
return new StructurePlaceSettings().setRotation(rotation)
.setMirror(mirror)
.setRotationPivot(halfSize)
.addProcessor(BlockIgnoreProcessor.STRUCTURE_BLOCK);
}
@Override
protected void addAdditionalSaveData(StructurePieceSerializationContext structurePieceSerializationContext,
CompoundTag tag) {
super.addAdditionalSaveData(structurePieceSerializationContext, tag);
tag.putString("R", this.placeSettings.getRotation().name());
tag.putString("M", this.placeSettings.getMirror().name());
tag.putInt("RX", this.placeSettings.getRotationPivot().getX());
tag.putInt("RY", this.placeSettings.getRotationPivot().getY());
tag.putInt("RZ", this.placeSettings.getRotationPivot().getZ());
}
@Override
protected void handleDataMarker(String string,
BlockPos blockPos,
ServerLevelAccessor serverLevelAccessor,
RandomSource randomSource,
BoundingBox boundingBox) {
}
}
class TemplatePiece2 extends StructurePiece {
public static final StructurePieceType INSTANCE = register("template_piece_bcl", TemplatePiece2::new);
private static StructurePieceType register(String id, StructurePieceType pieceType) {
return Registry.register(Registry.STRUCTURE_PIECE, BCLib.makeID(id), pieceType);
}
public static void ensureStaticInitialization() {
}
public final StructureWorldNBT structure;
public final BlockPos pos;
public final Rotation rot;
public final Mirror mir;
protected TemplatePiece(StructureWorldNBT structure,
protected TemplatePiece2(StructureWorldNBT structure,
BlockPos pos,
Rotation rot,
Mirror mir) {
@ -41,7 +161,7 @@ public class TemplatePiece extends StructurePiece {
this.pos = pos;
}
public TemplatePiece(StructurePieceSerializationContext ctx, CompoundTag compoundTag) {
public TemplatePiece2(StructurePieceSerializationContext ctx, CompoundTag compoundTag) {
super(INSTANCE, compoundTag);
ResourceLocation location = new ResourceLocation(compoundTag.getString("L"));
@ -64,7 +184,7 @@ public class TemplatePiece extends StructurePiece {
tag.putString("L", structure.location.toString());
tag.putInt("OY", structure.offsetY);
tag.putString("T", structure.type.getSerializedName());
tag.putString("T", structure.type.name());
tag.putString("Rot", this.rot.name());
tag.putString("Mir", this.mir.name());
tag.putInt("PX", this.pos.getX());
@ -80,6 +200,6 @@ public class TemplatePiece extends StructurePiece {
BoundingBox boundingBox,
ChunkPos chunkPos,
BlockPos blockPos) {
structure.generateInRandomOrientation(worldGenLevel, blockPos, randomSource);
structure.generate(worldGenLevel, this.pos, this.rot, this.mir);
}
}

View file

@ -2,88 +2,149 @@ package org.betterx.bclib.world.structures;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.NoiseColumn;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.WorldGenerationContext;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePiecesBuilder;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import com.mojang.datafixers.util.Function4;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
public abstract class TemplateStructure extends Structure {
public static <T extends TemplateStructure> Codec<T> simpleCodec(Function4 instancer) {
protected final List<Config> configs;
public static <T extends TemplateStructure> Codec<T> simpleCodec(BiFunction instancer) {
return RecordCodecBuilder.create((instance) -> instance
.group(
Structure.settingsCodec(instance),
ResourceLocation.CODEC
.fieldOf("location")
.forGetter((T cfg) -> cfg.structure.location),
Codec
.INT
.fieldOf("offset_y")
.orElse(0)
.forGetter((T cfg) -> cfg.structure.offsetY),
StructurePlacementType.CODEC
.fieldOf("placement")
.orElse(StructurePlacementType.FLOOR)
.forGetter((T cfg) -> cfg.structure.type)
ExtraCodecs.nonEmptyList(Config.CODEC.listOf())
.fieldOf("configs")
.forGetter((T ruinedPortalStructure) -> ruinedPortalStructure.configs)
)
.apply(instance, instancer)
);
}
public final StructureWorldNBT structure;
protected TemplateStructure(StructureSettings structureSettings,
ResourceLocation location,
int offsetY,
StructurePlacementType type) {
super(structureSettings);
structure = StructureWorldNBT.create(location, offsetY, type);
StructurePlacementType type,
float chance) {
this(structureSettings, List.of(new Config(location, offsetY, type, chance)));
}
protected TemplateStructure(StructureSettings structureSettings,
List<Config> configs) {
super(structureSettings);
this.configs = configs;
}
protected Config randomConfig(RandomSource random) {
Config config = null;
if (this.configs.size() > 1) {
final float chanceSum = configs.parallelStream().map(c -> c.chance()).reduce(0.0f, (p, c) -> p + c);
float rnd = random.nextFloat() * chanceSum;
for (Config c : configs) {
rnd -= c.chance();
if (rnd <= 0) return c;
}
} else {
return this.configs.get(0);
}
return null;
}
@Override
public Optional<GenerationStub> findGenerationPoint(GenerationContext ctx) {
WorldGenerationContext worldGenerationContext = new WorldGenerationContext(ctx.chunkGenerator(),
ctx.heightAccessor());
final Config config = randomConfig(ctx.random());
if (config == null) return Optional.empty();
ChunkPos chunkPos = ctx.chunkPos();
final int x = chunkPos.getMiddleBlockX();
final int z = chunkPos.getMiddleBlockZ();
int y = ctx
.chunkGenerator()
.getFirstOccupiedHeight(x,
z,
Heightmap.Types.WORLD_SURFACE_WG,
ctx.heightAccessor(),
ctx.randomState());
NoiseColumn column = ctx.chunkGenerator().getBaseColumn(x, z, ctx.heightAccessor(), ctx.randomState());
final int seaLevel = ctx.chunkGenerator().getSeaLevel();
StructureTemplate structureTemplate = ctx.structureTemplateManager().getOrCreate(config.location);
final int maxHeight = worldGenerationContext.getGenDepth() - 4 - (structureTemplate.getSize(Rotation.NONE)
.getY() + config.offsetY);
int y = seaLevel;
BlockState state = column.getBlock(y - 1);
for (; y < maxHeight; y++) {
BlockState below = state;
state = column.getBlock(y);
if (state.is(Blocks.AIR) && below.is(Blocks.LAVA)) break;
}
if (y >= maxHeight) return Optional.empty();
BlockPos halfSize = new BlockPos(structureTemplate.getSize().getX() / 2,
0,
structureTemplate.getSize().getZ() / 2);
Rotation rotation = StructureNBT.getRandomRotation(ctx.random());
Mirror mirror = StructureNBT.getRandomMirror(ctx.random());
BlockPos centerPos = new BlockPos(x, y, z);
Rotation rotation = Rotation.getRandom(ctx.random());
BoundingBox bb = structure.boundingBox(rotation, centerPos);
BoundingBox boundingBox = structureTemplate.getBoundingBox(centerPos, rotation, halfSize, mirror);
// if (!structure.canGenerate(ctx.chunkGenerator()., centerPos))
return Optional.of(new GenerationStub(centerPos,
structurePiecesBuilder -> this.generatePieces(structurePiecesBuilder, centerPos, rotation, ctx)));
structurePiecesBuilder ->
structurePiecesBuilder.addPiece(
new TemplatePiece(ctx.structureTemplateManager(),
config.location,
centerPos.offset(
0,
config.offsetY,
0),
rotation,
mirror,
halfSize))
));
}
private void generatePieces(StructurePiecesBuilder structurePiecesBuilder,
BlockPos centerPos,
Rotation rotation,
Structure.GenerationContext generationContext) {
WorldgenRandom worldgenRandom = generationContext.random();
public record Config(ResourceLocation location, int offsetY, StructurePlacementType type, float chance) {
public static final Codec<Config> CODEC =
RecordCodecBuilder.create((instance) ->
instance.group(
ResourceLocation.CODEC
.fieldOf("location")
.forGetter((cfg) -> cfg.location),
Mirror mirror = Mirror.values()[worldgenRandom.nextInt(3)];
Codec
.INT
.fieldOf("offset_y")
.orElse(0)
.forGetter((cfg) -> cfg.offsetY),
structurePiecesBuilder.addPiece(new TemplatePiece(structure, centerPos, rotation, mirror));
StructurePlacementType.CODEC
.fieldOf("placement")
.orElse(StructurePlacementType.FLOOR)
.forGetter((cfg) -> cfg.type),
Codec
.FLOAT
.fieldOf("chance")
.orElse(1.0f)
.forGetter((cfg) -> cfg.chance)
)
.apply(instance, Config::new)
);
}
}