[Feature] Ability to erode Template Structures

This commit is contained in:
Frank 2022-07-01 01:41:51 +02:00
parent 65e9140812
commit 0e0b7a5f51
3 changed files with 355 additions and 5 deletions

View file

@ -1,15 +1,23 @@
package org.betterx.bclib.api.v2.levelgen.structures; package org.betterx.bclib.api.v2.levelgen.structures;
import org.betterx.bclib.BCLib; import org.betterx.bclib.BCLib;
import org.betterx.bclib.util.MHelper;
import org.betterx.bclib.util.StructureErode;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.ServerLevelAccessor; 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.Mirror;
import net.minecraft.world.level.block.Rotation; 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.BoundingBox;
import net.minecraft.world.level.levelgen.structure.TemplateStructurePiece; 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.StructurePieceSerializationContext;
@ -20,6 +28,8 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
public class TemplatePiece extends TemplateStructurePiece { public class TemplatePiece extends TemplateStructurePiece {
private final int erosion;
private final boolean cover;
public static final StructurePieceType INSTANCE = setTemplatePieceId( public static final StructurePieceType INSTANCE = setTemplatePieceId(
TemplatePiece::new, TemplatePiece::new,
"template_piece" "template_piece"
@ -41,7 +51,6 @@ public class TemplatePiece extends TemplateStructurePiece {
public static void ensureStaticInitialization() { public static void ensureStaticInitialization() {
} }
public TemplatePiece( public TemplatePiece(
StructureTemplateManager structureTemplateManager, StructureTemplateManager structureTemplateManager,
ResourceLocation resourceLocation, ResourceLocation resourceLocation,
@ -49,6 +58,19 @@ public class TemplatePiece extends TemplateStructurePiece {
Rotation rotation, Rotation rotation,
Mirror mirror, Mirror mirror,
BlockPos halfSize BlockPos halfSize
) {
this(structureTemplateManager, resourceLocation, centerPos, rotation, mirror, halfSize, 0, false);
}
public TemplatePiece(
StructureTemplateManager structureTemplateManager,
ResourceLocation resourceLocation,
BlockPos centerPos,
Rotation rotation,
Mirror mirror,
BlockPos halfSize,
int erosion,
boolean cover
) { ) {
super( super(
INSTANCE, INSTANCE,
@ -59,6 +81,8 @@ public class TemplatePiece extends TemplateStructurePiece {
makeSettings(rotation, mirror, halfSize), makeSettings(rotation, mirror, halfSize),
shiftPos(rotation, mirror, halfSize, centerPos) shiftPos(rotation, mirror, halfSize, centerPos)
); );
this.erosion = erosion;
this.cover = cover;
} }
public TemplatePiece(StructureTemplateManager structureTemplateManager, CompoundTag compoundTag) { public TemplatePiece(StructureTemplateManager structureTemplateManager, CompoundTag compoundTag) {
@ -68,6 +92,15 @@ public class TemplatePiece extends TemplateStructurePiece {
structureTemplateManager, structureTemplateManager,
(ResourceLocation resourceLocation) -> makeSettings(compoundTag) (ResourceLocation resourceLocation) -> makeSettings(compoundTag)
); );
if (compoundTag.contains("E"))
this.erosion = compoundTag.getInt("E");
else
this.erosion = 0;
if (compoundTag.contains("C"))
this.cover = compoundTag.getBoolean("C");
else
this.cover = true;
} }
private static BlockPos shiftPos( private static BlockPos shiftPos(
@ -107,6 +140,8 @@ public class TemplatePiece extends TemplateStructurePiece {
tag.putInt("RX", this.placeSettings.getRotationPivot().getX()); tag.putInt("RX", this.placeSettings.getRotationPivot().getX());
tag.putInt("RY", this.placeSettings.getRotationPivot().getY()); tag.putInt("RY", this.placeSettings.getRotationPivot().getY());
tag.putInt("RZ", this.placeSettings.getRotationPivot().getZ()); tag.putInt("RZ", this.placeSettings.getRotationPivot().getZ());
tag.putInt("E", this.erosion);
tag.putBoolean("C", this.cover);
} }
@Override @Override
@ -119,4 +154,35 @@ public class TemplatePiece extends TemplateStructurePiece {
) { ) {
} }
@Override
public void postProcess(
WorldGenLevel world,
StructureManager structureManager,
ChunkGenerator chunkGenerator,
RandomSource random,
BoundingBox boundingBox,
ChunkPos chunkPos,
BlockPos blockPos
) {
super.postProcess(world, structureManager, chunkGenerator, random, boundingBox, chunkPos, blockPos);
BoundingBox bounds = BoundingBox.fromCorners(new Vec3i(
this.boundingBox.minX(),
this.boundingBox.minY(),
this.boundingBox.minZ()
), new Vec3i(this.boundingBox.maxX(), this.boundingBox.maxX(), this.boundingBox.maxZ()));
if (erosion > 0) {
int x1 = MHelper.min(bounds.maxX(), this.boundingBox.maxX());
int x0 = MHelper.max(bounds.minX(), this.boundingBox.minX());
int z1 = MHelper.min(bounds.maxZ(), this.boundingBox.maxZ());
int z0 = MHelper.max(bounds.minZ(), this.boundingBox.minZ());
bounds = BoundingBox.fromCorners(new Vec3i(x0, bounds.minY(), z0), new Vec3i(x1, bounds.maxY(), z1));
StructureErode.erode(world, bounds, erosion, random);
}
if (cover) {
StructureErode.cover(world, bounds, random, Blocks.END_STONE.defaultBlockState());
}
}
} }

View file

@ -23,7 +23,6 @@ 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;
@ -83,6 +82,14 @@ public abstract class TemplateStructure extends Structure {
return (state == null || state.is(Blocks.AIR)) && before.getMaterial().isSolid(); return (state == null || state.is(Blocks.AIR)) && before.getMaterial().isSolid();
} }
protected int erosion(RandomSource rnd) {
return 0;
}
protected boolean cover(RandomSource rnd) {
return false;
}
@Override @Override
public Optional<GenerationStub> findGenerationPoint(GenerationContext ctx) { public Optional<GenerationStub> findGenerationPoint(GenerationContext ctx) {
WorldGenerationContext worldGenerationContext = new WorldGenerationContext( WorldGenerationContext worldGenerationContext = new WorldGenerationContext(
@ -152,8 +159,7 @@ public abstract class TemplateStructure extends Structure {
blockPos.getZ(), blockPos.getZ(),
ctx.heightAccessor(), ctx.heightAccessor(),
ctx.randomState() ctx.randomState()
)) )).toList();
.collect(Collectors.toList());
int y = noiseColumns int y = noiseColumns
.stream() .stream()
@ -199,7 +205,9 @@ public abstract class TemplateStructure extends Structure {
), ),
rotation, rotation,
mirror, mirror,
halfSize halfSize,
erosion(ctx.random()),
cover(ctx.random())
)) ))
)); ));

View file

@ -0,0 +1,276 @@
package org.betterx.bclib.util;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI;
import org.betterx.worlds.together.tag.v3.CommonBlockTags;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
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.structure.BoundingBox;
import net.minecraft.world.level.material.Material;
import com.google.common.collect.Sets;
import java.util.Set;
public class StructureErode {
private static final Direction[] DIR = BlocksHelper.makeHorizontal();
public static void erode(WorldGenLevel world, BoundingBox bounds, int iterations, RandomSource random) {
MutableBlockPos mut = new MutableBlockPos();
boolean canDestruct = true;
for (int i = 0; i < iterations; i++) {
for (int x = bounds.minX(); x <= bounds.maxX(); x++) {
mut.setX(x);
for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) {
mut.setZ(z);
for (int y = bounds.maxY(); y >= bounds.minY(); y--) {
mut.setY(y);
BlockState state = world.getBlockState(mut);
boolean ignore = ignore(state, world, mut);
if (canDestruct && BlocksHelper.isInvulnerable(
state,
world,
mut
) && random.nextInt(8) == 0 && world.isEmptyBlock(
mut.below(2))) {
int r = MHelper.randRange(1, 4, random);
int cx = mut.getX();
int cy = mut.getY();
int cz = mut.getZ();
int x1 = cx - r;
int y1 = cy - r;
int z1 = cz - r;
int x2 = cx + r;
int y2 = cy + r;
int z2 = cz + r;
for (int px = x1; px <= x2; px++) {
int dx = px - cx;
dx *= dx;
mut.setX(px);
for (int py = y1; py <= y2; py++) {
int dy = py - cy;
dy *= dy;
mut.setY(py);
for (int pz = z1; pz <= z2; pz++) {
int dz = pz - cz;
dz *= dz;
mut.setZ(pz);
if (dx + dy + dz <= r && BlocksHelper.isInvulnerable(
world.getBlockState(mut),
world,
mut
)) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR);
}
}
}
}
mut.setX(cx);
mut.setY(cy);
mut.setZ(cz);
canDestruct = false;
continue;
} else if (ignore) {
continue;
}
if (!state.isAir() && random.nextBoolean()) {
MHelper.shuffle(DIR, random);
for (Direction dir : DIR) {
if (world.isEmptyBlock(mut.relative(dir)) && world.isEmptyBlock(mut.below()
.relative(dir))) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR);
mut.move(dir).move(Direction.DOWN);
for (int py = mut.getY(); y >= bounds.minY() - 10; y--) {
mut.setY(py - 1);
if (!world.isEmptyBlock(mut)) {
mut.setY(py);
BlocksHelper.setWithoutUpdate(world, mut, state);
break;
}
}
}
}
break;
} else if (random.nextInt(8) == 0 && !BlocksHelper.isInvulnerable(
world.getBlockState(mut.above()),
world,
mut
)) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR);
}
}
}
}
}
for (int x = bounds.minX(); x <= bounds.maxX(); x++) {
mut.setX(x);
for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) {
mut.setZ(z);
for (int y = bounds.maxY(); y >= bounds.minY(); y--) {
mut.setY(y);
BlockState state = world.getBlockState(mut);
if (!ignore(state, world, mut) && world.isEmptyBlock(mut.below())) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR);
for (int py = mut.getY(); py >= bounds.minY() - 10; py--) {
mut.setY(py - 1);
if (!world.isEmptyBlock(mut)) {
mut.setY(py);
BlocksHelper.setWithoutUpdate(world, mut, state);
break;
}
}
}
}
}
}
}
public static void erodeIntense(WorldGenLevel world, BoundingBox bounds, RandomSource random) {
MutableBlockPos mut = new MutableBlockPos();
MutableBlockPos mut2 = new MutableBlockPos();
int minY = bounds.minY() - 10;
for (int x = bounds.minX(); x <= bounds.maxX(); x++) {
mut.setX(x);
for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) {
mut.setZ(z);
for (int y = bounds.maxY(); y >= bounds.minY(); y--) {
mut.setY(y);
BlockState state = world.getBlockState(mut);
if (!ignore(state, world, mut)) {
if (random.nextInt(6) == 0) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR);
if (random.nextBoolean()) {
int px = MHelper.floor(random.nextGaussian() * 2 + x + 0.5);
int pz = MHelper.floor(random.nextGaussian() * 2 + z + 0.5);
mut2.set(px, y, pz);
while (world.getBlockState(mut2).getMaterial().isReplaceable() && mut2.getY() > minY) {
mut2.setY(mut2.getY() - 1);
}
if (!world.getBlockState(mut2).isAir() && state.canSurvive(world, mut2)) {
mut2.setY(mut2.getY() + 1);
BlocksHelper.setWithoutUpdate(world, mut2, state);
}
}
} else if (random.nextInt(8) == 0) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR);
}
}
}
}
}
drop(world, bounds);
}
private static void drop(WorldGenLevel world, BoundingBox bounds) {
MutableBlockPos mut = new MutableBlockPos();
Set<BlockPos> blocks = Sets.newHashSet();
Set<BlockPos> edge = Sets.newHashSet();
Set<BlockPos> add = Sets.newHashSet();
for (int x = bounds.minX(); x <= bounds.maxX(); x++) {
mut.setX(x);
for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) {
mut.setZ(z);
for (int y = bounds.minY(); y <= bounds.maxY(); y++) {
mut.setY(y);
BlockState state = world.getBlockState(mut);
if (!ignore(state, world, mut) && isTerrainNear(world, mut)) {
edge.add(mut.immutable());
}
}
}
}
if (edge.isEmpty()) {
return;
}
while (!edge.isEmpty()) {
for (BlockPos center : edge) {
for (Direction dir : BlocksHelper.DIRECTIONS) {
BlockState state = world.getBlockState(center);
if (state.isCollisionShapeFullBlock(world, center)) {
mut.set(center).move(dir);
if (bounds.isInside(mut)) {
state = world.getBlockState(mut);
if (!ignore(state, world, mut) && !blocks.contains(mut)) {
add.add(mut.immutable());
}
}
}
}
}
blocks.addAll(edge);
edge.clear();
edge.addAll(add);
add.clear();
}
int minY = bounds.minY() - 10;
for (int x = bounds.minX(); x <= bounds.maxX(); x++) {
mut.setX(x);
for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) {
mut.setZ(z);
for (int y = bounds.minY(); y <= bounds.maxY(); y++) {
mut.setY(y);
BlockState state = world.getBlockState(mut);
if (!ignore(state, world, mut) && !blocks.contains(mut)) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR);
while (world.getBlockState(mut).getMaterial().isReplaceable() && mut.getY() > minY) {
mut.setY(mut.getY() - 1);
}
if (mut.getY() > minY) {
mut.setY(mut.getY() + 1);
BlocksHelper.setWithoutUpdate(world, mut, state);
}
}
}
}
}
}
private static boolean ignore(BlockState state, WorldGenLevel world, BlockPos pos) {
if (state.is(CommonBlockTags.GEN_END_STONES) || state.is(BlockTags.NYLIUM)) {
return true;
}
return !state.getMaterial().equals(Material.STONE) || BlocksHelper.isInvulnerable(state, world, pos);
}
private static boolean isTerrainNear(WorldGenLevel world, BlockPos pos) {
for (Direction dir : BlocksHelper.DIRECTIONS) {
if (world.getBlockState(pos.relative(dir)).is(CommonBlockTags.GEN_END_STONES)) {
return true;
}
}
return false;
}
public static void cover(WorldGenLevel world, BoundingBox bounds, RandomSource random, BlockState defaultBlock) {
MutableBlockPos mut = new MutableBlockPos();
for (int x = bounds.minX(); x <= bounds.maxX(); x++) {
mut.setX(x);
for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) {
mut.setZ(z);
BlockState top = BiomeAPI.findTopMaterial(world.getBiome(mut)).orElse(defaultBlock);
for (int y = bounds.maxY(); y >= bounds.minY(); y--) {
mut.setY(y);
BlockState state = world.getBlockState(mut);
if (state.is(CommonBlockTags.TERRAIN) && !world.getBlockState(mut.above())
.getMaterial()
.isSolidBlocking()) {
BlocksHelper.setWithoutUpdate(world, mut, top);
}
}
}
}
}
}