[Change] Improved Placement of Template Structure

This commit is contained in:
Frank 2022-06-30 23:30:55 +02:00
parent 37a26f25bb
commit ae5b3e1417

View file

@ -17,10 +17,13 @@ import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import java.util.stream.Collectors;
public abstract class TemplateStructure extends Structure { public abstract class TemplateStructure extends Structure {
protected final List<Config> configs; protected final List<Config> configs;
@ -73,16 +76,15 @@ public abstract class TemplateStructure extends Structure {
} }
protected boolean isLavaPlaceable(BlockState state, BlockState before) { protected boolean isLavaPlaceable(BlockState state, BlockState before) {
return state.is(Blocks.AIR) && before.is(Blocks.LAVA); return (state == null || state.is(Blocks.AIR)) && before.is(Blocks.LAVA);
} }
protected boolean isFloorPlaceable(BlockState state, BlockState before) { protected boolean isFloorPlaceable(BlockState state, BlockState before) {
return state.is(Blocks.AIR) && before.getMaterial().isSolid(); return (state == null || state.is(Blocks.AIR)) && before.getMaterial().isSolid();
} }
@Override @Override
public Optional<GenerationStub> findGenerationPoint(GenerationContext ctx) { public Optional<GenerationStub> findGenerationPoint(GenerationContext ctx) {
WorldGenerationContext worldGenerationContext = new WorldGenerationContext( WorldGenerationContext worldGenerationContext = new WorldGenerationContext(
ctx.chunkGenerator(), ctx.chunkGenerator(),
ctx.heightAccessor() ctx.heightAccessor()
@ -90,22 +92,26 @@ public abstract class TemplateStructure extends Structure {
final Config config = randomConfig(ctx.random()); final Config config = randomConfig(ctx.random());
if (config == null) return Optional.empty(); if (config == null) return Optional.empty();
ChunkPos chunkPos = ctx.chunkPos(); ChunkPos chunkPos = ctx.chunkPos();
final int x = chunkPos.getMiddleBlockX(); final int x = chunkPos.getMinBlockX();
final int z = chunkPos.getMiddleBlockZ(); final int z = chunkPos.getMinBlockZ();
NoiseColumn column = ctx.chunkGenerator().getBaseColumn(x, z, ctx.heightAccessor(), ctx.randomState());
StructureTemplate structureTemplate = ctx.structureTemplateManager().getOrCreate(config.location); StructureTemplate structureTemplate = ctx.structureTemplateManager().getOrCreate(config.location);
BiPredicate<BlockState, BlockState> isCorrectBase; final BiPredicate<BlockState, BlockState> isCorrectBase;
int searchStep; final int searchStep;
final int minBaseCount;
final float minAirRatio = 0.6f;
if (config.type == StructurePlacementType.LAVA) { if (config.type == StructurePlacementType.LAVA) {
isCorrectBase = this::isLavaPlaceable; isCorrectBase = this::isLavaPlaceable;
minBaseCount = 5;
searchStep = 1; searchStep = 1;
} else if (config.type == StructurePlacementType.CEIL) { } else if (config.type == StructurePlacementType.CEIL) {
isCorrectBase = this::isFloorPlaceable; isCorrectBase = this::isFloorPlaceable;
minBaseCount = 3;
searchStep = -1; searchStep = -1;
} else { } else {
isCorrectBase = this::isFloorPlaceable; isCorrectBase = this::isFloorPlaceable;
minBaseCount = 3;
searchStep = 1; searchStep = 1;
} }
@ -117,16 +123,6 @@ public abstract class TemplateStructure extends Structure {
worldGenerationContext.getGenDepth() worldGenerationContext.getGenDepth()
- 4 - 4
- (searchStep > 0 ? (structureTemplate.getSize(Rotation.NONE).getY() + config.offsetY) : 0); - (searchStep > 0 ? (structureTemplate.getSize(Rotation.NONE).getY() + config.offsetY) : 0);
int y = searchStep > 0 ? seaLevel : maxHeight - 1;
BlockState state = column.getBlock(y - searchStep);
for (; y < maxHeight && y >= seaLevel; y += searchStep) {
BlockState before = state;
state = column.getBlock(y);
if (isCorrectBase.test(state, before)) break;
}
if (y >= maxHeight || y < seaLevel) return Optional.empty();
if (!BCLStructure.isValidBiome(ctx, y)) return Optional.empty();
BlockPos halfSize = new BlockPos( BlockPos halfSize = new BlockPos(
structureTemplate.getSize().getX() / 2, structureTemplate.getSize().getX() / 2,
@ -135,13 +131,58 @@ public abstract class TemplateStructure extends Structure {
); );
Rotation rotation = StructureNBT.getRandomRotation(ctx.random()); Rotation rotation = StructureNBT.getRandomRotation(ctx.random());
Mirror mirror = StructureNBT.getRandomMirror(ctx.random()); Mirror mirror = StructureNBT.getRandomMirror(ctx.random());
BlockPos centerPos = new BlockPos( BlockPos.MutableBlockPos centerPos = new BlockPos.MutableBlockPos(
x, x,
y - (searchStep == 1 ? 0 : (structureTemplate.getSize(Rotation.NONE).getY())), 0,
z z
); );
BoundingBox boundingBox = structureTemplate.getBoundingBox(centerPos, rotation, halfSize, mirror); BoundingBox boundingBox = structureTemplate.getBoundingBox(centerPos, rotation, halfSize, mirror);
var noiseColumns = ImmutableList
.of(
new BlockPos(boundingBox.getCenter().getX(), 0, boundingBox.getCenter().getZ()),
new BlockPos(boundingBox.minX(), 0, boundingBox.minZ()),
new BlockPos(boundingBox.maxX(), 0, boundingBox.minZ()),
new BlockPos(boundingBox.minX(), 0, boundingBox.maxZ()),
new BlockPos(boundingBox.maxX(), 0, boundingBox.maxZ())
)
.stream()
.map(blockPos -> ctx.chunkGenerator().getBaseColumn(
blockPos.getX(),
blockPos.getZ(),
ctx.heightAccessor(),
ctx.randomState()
))
.collect(Collectors.toList());
int y = noiseColumns
.stream()
.map(column -> findY(column, isCorrectBase, searchStep, seaLevel, maxHeight))
.reduce(
searchStep > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE,
(p, c) -> searchStep > 0 ? Math.min(p, c) : Math.max(p, c)
);
if (y >= maxHeight || y < seaLevel) return Optional.empty();
if (!BCLStructure.isValidBiome(ctx, y)) return Optional.empty();
int baseCount = noiseColumns
.stream()
.map(column -> isCorrectBase.test(null, column.getBlock(y - searchStep)))
.filter(b -> b)
.map(b -> 1)
.reduce(0, (p, c) -> p + c);
if (baseCount < minBaseCount) return Optional.empty();
float airRatio = noiseColumns
.stream()
.map(column -> airRatio(column, y, boundingBox.getYSpan(), searchStep))
.reduce(0.0f, (p, c) -> p + c) / noiseColumns.size();
if (airRatio < minAirRatio) return Optional.empty();
centerPos.setY(y - (searchStep == 1 ? 0 : (structureTemplate.getSize(Rotation.NONE).getY())));
// if (!structure.canGenerate(ctx.chunkGenerator()., centerPos)) // if (!structure.canGenerate(ctx.chunkGenerator()., centerPos))
return Optional.of(new GenerationStub( return Optional.of(new GenerationStub(
@ -164,6 +205,37 @@ public abstract class TemplateStructure extends Structure {
} }
private float airRatio(NoiseColumn column, int y, int height, int searchStep) {
int airCount = 0;
for (int i = y; i < y + height && i > y - height; i += searchStep) {
BlockState state = column.getBlock(i);
if (state.isAir() || state.getMaterial().isReplaceable()) {
airCount++;
}
}
return airCount / (float) height;
}
private int findY(
NoiseColumn column,
BiPredicate<BlockState, BlockState> isCorrectBase,
int searchStep,
int seaLevel,
int maxHeight
) {
int y = searchStep > 0 ? seaLevel : maxHeight - 1;
BlockState state = column.getBlock(y - searchStep);
for (; y < maxHeight && y >= seaLevel; y += searchStep) {
BlockState before = state;
state = column.getBlock(y);
if (isCorrectBase.test(state, before)) break;
}
return y;
}
public record Config(ResourceLocation location, int offsetY, StructurePlacementType type, float chance) { public record Config(ResourceLocation location, int offsetY, StructurePlacementType type, float chance) {
public static final Codec<Config> CODEC = public static final Codec<Config> CODEC =
RecordCodecBuilder.create((instance) -> RecordCodecBuilder.create((instance) ->