diff --git a/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/TemplatePiece.java b/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/TemplatePiece.java index a8245d00..e7204457 100644 --- a/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/TemplatePiece.java +++ b/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/TemplatePiece.java @@ -1,15 +1,23 @@ package org.betterx.bclib.api.v2.levelgen.structures; 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.Registry; +import net.minecraft.core.Vec3i; 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.TemplateStructurePiece; 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; public class TemplatePiece extends TemplateStructurePiece { + private final int erosion; + private final boolean cover; public static final StructurePieceType INSTANCE = setTemplatePieceId( TemplatePiece::new, "template_piece" @@ -41,7 +51,6 @@ public class TemplatePiece extends TemplateStructurePiece { public static void ensureStaticInitialization() { } - public TemplatePiece( StructureTemplateManager structureTemplateManager, ResourceLocation resourceLocation, @@ -49,6 +58,19 @@ public class TemplatePiece extends TemplateStructurePiece { Rotation rotation, Mirror mirror, 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( INSTANCE, @@ -59,6 +81,8 @@ public class TemplatePiece extends TemplateStructurePiece { makeSettings(rotation, mirror, halfSize), shiftPos(rotation, mirror, halfSize, centerPos) ); + this.erosion = erosion; + this.cover = cover; } public TemplatePiece(StructureTemplateManager structureTemplateManager, CompoundTag compoundTag) { @@ -68,6 +92,15 @@ public class TemplatePiece extends TemplateStructurePiece { structureTemplateManager, (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( @@ -107,6 +140,8 @@ public class TemplatePiece extends TemplateStructurePiece { tag.putInt("RX", this.placeSettings.getRotationPivot().getX()); tag.putInt("RY", this.placeSettings.getRotationPivot().getY()); tag.putInt("RZ", this.placeSettings.getRotationPivot().getZ()); + tag.putInt("E", this.erosion); + tag.putBoolean("C", this.cover); } @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()); + } + } } diff --git a/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/TemplateStructure.java b/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/TemplateStructure.java index 5cf3a4d4..cfac7fe6 100644 --- a/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/TemplateStructure.java +++ b/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/TemplateStructure.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.BiPredicate; -import java.util.stream.Collectors; public abstract class TemplateStructure extends Structure { protected final List configs; @@ -83,6 +82,14 @@ public abstract class TemplateStructure extends Structure { 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 public Optional findGenerationPoint(GenerationContext ctx) { WorldGenerationContext worldGenerationContext = new WorldGenerationContext( @@ -152,8 +159,7 @@ public abstract class TemplateStructure extends Structure { blockPos.getZ(), ctx.heightAccessor(), ctx.randomState() - )) - .collect(Collectors.toList()); + )).toList(); int y = noiseColumns .stream() @@ -199,7 +205,9 @@ public abstract class TemplateStructure extends Structure { ), rotation, mirror, - halfSize + halfSize, + erosion(ctx.random()), + cover(ctx.random()) )) )); diff --git a/src/main/java/org/betterx/bclib/util/StructureErode.java b/src/main/java/org/betterx/bclib/util/StructureErode.java new file mode 100644 index 00000000..adbb5a01 --- /dev/null +++ b/src/main/java/org/betterx/bclib/util/StructureErode.java @@ -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 blocks = Sets.newHashSet(); + Set edge = Sets.newHashSet(); + Set 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); + } + } + } + } + } +}