diff --git a/src/main/java/ru/betterend/registry/EndFeatures.java b/src/main/java/ru/betterend/registry/EndFeatures.java index 0f80c135..fa96dd93 100644 --- a/src/main/java/ru/betterend/registry/EndFeatures.java +++ b/src/main/java/ru/betterend/registry/EndFeatures.java @@ -30,6 +30,7 @@ import ru.betterend.world.features.terrain.FloatingSpireFeature; import ru.betterend.world.features.terrain.GeyserFeature; import ru.betterend.world.features.terrain.RoundCaveFeature; import ru.betterend.world.features.terrain.SpireFeature; +import ru.betterend.world.features.terrain.SulphuricCaveFeature; import ru.betterend.world.features.terrain.SulphuricLakeFeature; import ru.betterend.world.features.trees.DragonTreeFeature; import ru.betterend.world.features.trees.LacugroveFeature; @@ -96,6 +97,7 @@ public class EndFeatures { public static final EndFeature FLOATING_SPIRE = EndFeature.makeRawGenFeature("floating_spire", new FloatingSpireFeature(), 8); public static final EndFeature GEYSER = EndFeature.makeRawGenFeature("geyser", new GeyserFeature(), 8); public static final EndFeature SULPHURIC_LAKE = EndFeature.makeLakeFeature("sulphuric_lake", new SulphuricLakeFeature(), 8); + public static final EndFeature SULPHURIC_CAVE = EndFeature.makeRawGenFeature("sulphuric_cave", new SulphuricCaveFeature(), 1); // Ores // public static final EndFeature ENDER_ORE = EndFeature.makeOreFeature("ender_ore", EndBlocks.ENDER_ORE, 6, 3, 0, 4, 96); diff --git a/src/main/java/ru/betterend/util/sdf/SDF.java b/src/main/java/ru/betterend/util/sdf/SDF.java index 1c7b3e98..fb881000 100644 --- a/src/main/java/ru/betterend/util/sdf/SDF.java +++ b/src/main/java/ru/betterend/util/sdf/SDF.java @@ -140,8 +140,10 @@ public abstract class SDF { if (infos.size() > 0) { Collections.sort(infos); infos.forEach((info) -> { - BlockState state = postProcess.apply(info); - BlocksHelper.setWithoutUpdate(world, info.getPos(), state); + info.setState(postProcess.apply(info)); + }); + infos.forEach((info) -> { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); }); infos.clear(); @@ -181,8 +183,10 @@ public abstract class SDF { if (infos.size() > 0) { Collections.sort(infos); infos.forEach((info) -> { - BlockState state = postProcess.apply(info); - BlocksHelper.setWithoutUpdate(world, info.getPos(), state); + info.setState(postProcess.apply(info)); + }); + infos.forEach((info) -> { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); }); infos.clear(); diff --git a/src/main/java/ru/betterend/world/biome/BiomeDefinition.java b/src/main/java/ru/betterend/world/biome/BiomeDefinition.java index 11880976..23bc08dc 100644 --- a/src/main/java/ru/betterend/world/biome/BiomeDefinition.java +++ b/src/main/java/ru/betterend/world/biome/BiomeDefinition.java @@ -23,7 +23,10 @@ import net.minecraft.world.biome.BiomeEffects.Builder; import net.minecraft.world.biome.BiomeParticleConfig; import net.minecraft.world.biome.GenerationSettings; import net.minecraft.world.biome.SpawnSettings; +import net.minecraft.world.gen.GenerationStep.Carver; import net.minecraft.world.gen.GenerationStep.Feature; +import net.minecraft.world.gen.ProbabilityConfig; +import net.minecraft.world.gen.carver.ConfiguredCarver; import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.ConfiguredStructureFeature; import net.minecraft.world.gen.surfacebuilder.ConfiguredSurfaceBuilder; @@ -42,6 +45,7 @@ public class BiomeDefinition { private final List> structures = Lists.newArrayList(); private final List features = Lists.newArrayList(); + private final List carvers = Lists.newArrayList(); private final List mobs = Lists.newArrayList(); private BiomeParticleConfig particleConfig; @@ -221,6 +225,7 @@ public class BiomeDefinition { generationSettings.surfaceBuilder(surface == null ? ConfiguredSurfaceBuilders.END : surface); structures.forEach((structure) -> generationSettings.structureFeature(structure)); features.forEach((info) -> generationSettings.feature(info.featureStep, info.feature)); + carvers.forEach((info) -> generationSettings.carver(info.carverStep, info.carver)); effects.skyColor(0).waterColor(waterColor).waterFogColor(waterFogColor).fogColor(fogColor).foliageColor(foliageColor).grassColor(grassColor); if (loop != null) effects.loopSound(loop); @@ -253,6 +258,11 @@ public class BiomeDefinition { Feature featureStep; ConfiguredFeature feature; } + + private static final class CarverInfo { + Carver carverStep; + ConfiguredCarver carver; + } public Identifier getID() { return id; @@ -269,4 +279,12 @@ public class BiomeDefinition { public boolean hasCaves() { return hasCaves; } + + public BiomeDefinition addCarver(Carver carverStep, ConfiguredCarver carver) { + CarverInfo info = new CarverInfo(); + info.carverStep = carverStep; + info.carver = carver; + carvers.add(info); + return this; + } } \ No newline at end of file diff --git a/src/main/java/ru/betterend/world/biome/BiomeSulfurSprings.java b/src/main/java/ru/betterend/world/biome/BiomeSulfurSprings.java index 38664808..934e8828 100644 --- a/src/main/java/ru/betterend/world/biome/BiomeSulfurSprings.java +++ b/src/main/java/ru/betterend/world/biome/BiomeSulfurSprings.java @@ -1,6 +1,8 @@ package ru.betterend.world.biome; import net.minecraft.entity.EntityType; +import net.minecraft.world.gen.GenerationStep; +import net.minecraft.world.gen.carver.ConfiguredCarvers; import ru.betterend.registry.EndFeatures; import ru.betterend.registry.EndParticles; import ru.betterend.world.surface.SurfaceBuilders; @@ -16,6 +18,8 @@ public class BiomeSulfurSprings extends EndBiome { .setParticles(EndParticles.SULPHUR_PARTICLE, 0.001F) .addFeature(EndFeatures.GEYSER) .addFeature(EndFeatures.SULPHURIC_LAKE) + .addFeature(EndFeatures.SULPHURIC_CAVE) + .addCarver(GenerationStep.Carver.AIR, ConfiguredCarvers.CAVE) .addMobSpawn(EntityType.ENDERMAN, 50, 1, 4)); } } diff --git a/src/main/java/ru/betterend/world/features/terrain/GeyserFeature.java b/src/main/java/ru/betterend/world/features/terrain/GeyserFeature.java index 5d55f9ff..7146f815 100644 --- a/src/main/java/ru/betterend/world/features/terrain/GeyserFeature.java +++ b/src/main/java/ru/betterend/world/features/terrain/GeyserFeature.java @@ -9,6 +9,7 @@ import net.minecraft.block.LeavesBlock; import net.minecraft.block.Material; import net.minecraft.client.util.math.Vector3f; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockPos.Mutable; import net.minecraft.util.math.MathHelper; import net.minecraft.world.StructureWorldAccess; import net.minecraft.world.gen.chunk.ChunkGenerator; @@ -16,20 +17,27 @@ import net.minecraft.world.gen.feature.DefaultFeatureConfig; import ru.betterend.noise.OpenSimplexNoise; import ru.betterend.registry.EndBlocks; import ru.betterend.registry.EndTags; +import ru.betterend.util.BlocksHelper; import ru.betterend.util.MHelper; import ru.betterend.util.sdf.SDF; import ru.betterend.util.sdf.operator.SDFCoordModify; +import ru.betterend.util.sdf.operator.SDFDisplacement; import ru.betterend.util.sdf.operator.SDFInvert; import ru.betterend.util.sdf.operator.SDFRotation; +import ru.betterend.util.sdf.operator.SDFScale3D; +import ru.betterend.util.sdf.operator.SDFSmoothUnion; import ru.betterend.util.sdf.operator.SDFSubtraction; import ru.betterend.util.sdf.operator.SDFTranslate; import ru.betterend.util.sdf.operator.SDFUnion; import ru.betterend.util.sdf.primitive.SDFCapedCone; import ru.betterend.util.sdf.primitive.SDFFlatland; +import ru.betterend.util.sdf.primitive.SDFPrimitive; +import ru.betterend.util.sdf.primitive.SDFSphere; import ru.betterend.world.features.DefaultFeature; public class GeyserFeature extends DefaultFeature { - protected static final Function REPLACE; + protected static final Function REPLACE1; + protected static final Function REPLACE2; @Override public boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos pos, DefaultFeatureConfig config) { @@ -93,7 +101,60 @@ public class GeyserFeature extends DefaultFeature { bowl = new SDFRotation().setRotation(Vector3f.POSITIVE_Y, i * 4F).setSource(bowl); sdf = /*new SDFSmoothUnion()*/new SDFUnion()/*.setRadius(3)*/.setSourceA(sdf).setSourceB(bowl); } - sdf.setReplaceFunction(REPLACE).fillRecursive(world, pos); + sdf.setReplaceFunction(REPLACE2).fillRecursive(world, pos); + + radius2 = radius2 * 0.5F; + if (radius2 < 0.7F) { + radius2 = 0.7F; + } + final OpenSimplexNoise noise = new OpenSimplexNoise(random.nextLong()); + + SDFPrimitive obj1; + SDFPrimitive obj2; + + obj1 = new SDFCapedCone().setHeight(halfHeight + 5).setRadius1(radius1 * 0.5F).setRadius2(radius2); + sdf = new SDFTranslate().setTranslate(0, halfHeight - 13, 0).setSource(obj1); + sdf = new SDFDisplacement().setFunction((vec) -> { + return (float) noise.eval(vec.getX() * 0.3F, vec.getY() * 0.3F, vec.getZ() * 0.3F) * 0.5F; + }).setSource(sdf); + + obj2 = new SDFSphere().setRadius(radius1); + SDF cave = new SDFScale3D().setScale(1.5F, 1, 1.5F).setSource(obj2); + cave = new SDFDisplacement().setFunction((vec) -> { + return (float) noise.eval(vec.getX() * 0.1F, vec.getY() * 0.1F, vec.getZ() * 0.1F) * 2F; + }).setSource(cave); + cave = new SDFTranslate().setTranslate(0, -halfHeight - 10, 0).setSource(cave); + + sdf = new SDFSmoothUnion().setRadius(5).setSourceA(cave).setSourceB(sdf); + + obj1.setBlock(EndBlocks.SULPHURIC_ROCK.stone); + obj2.setBlock(EndBlocks.SULPHURIC_ROCK.stone); + new SDFDisplacement().setFunction((vec) -> { + return -4F; + }).setSource(sdf).setReplaceFunction(REPLACE1).fillRecursive(world, pos); + + obj1.setBlock(EndBlocks.BRIMSTONE); + obj2.setBlock(EndBlocks.BRIMSTONE); + new SDFDisplacement().setFunction((vec) -> { + return -2F; + }).setSource(sdf).setReplaceFunction(REPLACE1).fillRecursive(world, pos); + + obj1.setBlock(WATER); + obj2.setBlock(WATER); + sdf.setReplaceFunction(REPLACE2); + sdf.fillRecursive(world, pos); + + Mutable mut = new Mutable().set(pos); + for (int i = 0; i < halfHeight + 5; i++) { + BlockState state = world.getBlockState(mut); + if (state.isIn(EndTags.GEN_TERRAIN) || state.isOf(Blocks.WATER)) { + BlocksHelper.setWithoutUpdate(world, mut, WATER); + mut.setY(mut.getY() + 1); + } + else { + break; + } + } return true; } @@ -102,8 +163,12 @@ public class GeyserFeature extends DefaultFeature { } static { - REPLACE = (state) -> { - if (state.isIn(EndTags.END_GROUND)) { + REPLACE1 = (state) -> { + return state.isAir() || (state.isIn(EndTags.GEN_TERRAIN) && !state.isOf(EndBlocks.SULPHURIC_ROCK.stone) && !state.isOf(EndBlocks.BRIMSTONE)); + }; + + REPLACE2 = (state) -> { + if (state.isIn(EndTags.GEN_TERRAIN)) { return true; } if (state.getBlock() instanceof LeavesBlock) { diff --git a/src/main/java/ru/betterend/world/features/terrain/SulphuricCaveFeature.java b/src/main/java/ru/betterend/world/features/terrain/SulphuricCaveFeature.java new file mode 100644 index 00000000..16e02c3e --- /dev/null +++ b/src/main/java/ru/betterend/world/features/terrain/SulphuricCaveFeature.java @@ -0,0 +1,102 @@ +package ru.betterend.world.features.terrain; + +import java.util.Random; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.Material; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockPos.Mutable; +import net.minecraft.world.Heightmap; +import net.minecraft.world.StructureWorldAccess; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.feature.DefaultFeatureConfig; +import ru.betterend.noise.OpenSimplexNoise; +import ru.betterend.registry.EndBlocks; +import ru.betterend.registry.EndTags; +import ru.betterend.util.BlocksHelper; +import ru.betterend.util.MHelper; +import ru.betterend.world.features.DefaultFeature; + +public class SulphuricCaveFeature extends DefaultFeature { + private static final BlockState CAVE_AIR = Blocks.CAVE_AIR.getDefaultState(); + private static final BlockState WATER = Blocks.WATER.getDefaultState(); + + @Override + public boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos pos, DefaultFeatureConfig config) { + int radius = MHelper.randRange(20, 40, random); + int bottom = BlocksHelper.upRay(world, new BlockPos(pos.getX(), 0, pos.getZ()), 32) + radius + 5; + int top = world.getTopY(Heightmap.Type.WORLD_SURFACE_WG, pos.getX(), pos.getZ()) - radius - 5; + + if (top <= bottom) { + return false; + } + + Mutable bpos = new Mutable(); + pos = new BlockPos(pos.getX(), MHelper.randRange(bottom, top, random), pos.getZ()); + + OpenSimplexNoise noise = new OpenSimplexNoise(MHelper.getSeed(534, pos.getX(), pos.getZ())); + + int x1 = pos.getX() - radius - 5; + int z1 = pos.getZ() - radius - 5; + int x2 = pos.getX() + radius + 5; + int z2 = pos.getZ() + radius + 5; + int y1 = MHelper.floor(pos.getY() - (radius + 5) / 1.6); + int y2 = MHelper.floor(pos.getY() + (radius + 5) / 1.6); + + double hr = radius * 0.75; + double nr = radius * 0.25; + + BlockState terrain = EndBlocks.BRIMSTONE.getDefaultState(); + BlockState rock = EndBlocks.SULPHURIC_ROCK.stone.getDefaultState(); + int waterLevel = random.nextBoolean() ? -200 : MHelper.floor(pos.getY() - radius * 0.3F); + + for (int x = x1; x <= x2; x++) { + int xsq = x - pos.getX(); + xsq *= xsq; + bpos.setX(x); + for (int z = z1; z <= z2; z++) { + int zsq = z - pos.getZ(); + zsq *= zsq; + bpos.setZ(z); + for (int y = y1; y <= y2; y++) { + int ysq = y - pos.getY(); + ysq *= 1.6; + ysq *= ysq; + bpos.setY(y); + double r = noise.eval(x * 0.1, y * 0.1, z * 0.1) * nr + hr; + double r2 = r + 5; + double dist = xsq + ysq + zsq; + if (dist < r * r) { + BlockState state = world.getBlockState(bpos); + if (isReplaceable(state)) { + BlocksHelper.setWithoutUpdate(world, bpos, y < waterLevel ? WATER : CAVE_AIR); + world.getFluidTickScheduler().schedule(bpos, Fluids.WATER, random.nextInt(16)); + } + int depth = MHelper.randRange(2, 4, random); + for (int i = 0; i < depth; i++) { + bpos.setY(y - 1); + if (world.getBlockState(bpos).isIn(EndTags.GEN_TERRAIN)) { + BlocksHelper.setWithoutUpdate(world, bpos, terrain); + } + } + } + else if (dist < r2 * r2) { + if (world.getBlockState(bpos).isIn(EndTags.GEN_TERRAIN)) { + BlocksHelper.setWithoutUpdate(world, bpos, rock); + } + } + } + } + } + + return true; + } + + private boolean isReplaceable(BlockState state) { + return state.isIn(EndTags.GEN_TERRAIN) + || state.getMaterial().isReplaceable() + || state.getMaterial().equals(Material.PLANT) + || state.getMaterial().equals(Material.LEAVES); + } +}