diff --git a/src/main/java/ru/betterend/registry/EndFeatures.java b/src/main/java/ru/betterend/registry/EndFeatures.java index facd90cc..036d141b 100644 --- a/src/main/java/ru/betterend/registry/EndFeatures.java +++ b/src/main/java/ru/betterend/registry/EndFeatures.java @@ -36,6 +36,7 @@ import ru.betterend.world.features.terrain.GeyserFeature; import ru.betterend.world.features.terrain.IceStarFeature; import ru.betterend.world.features.terrain.RoundCaveFeature; import ru.betterend.world.features.terrain.SpireFeature; +import ru.betterend.world.features.terrain.SulphurHillFeature; import ru.betterend.world.features.terrain.SulphuricCaveFeature; import ru.betterend.world.features.terrain.SulphuricLakeFeature; import ru.betterend.world.features.terrain.SurfaceVentFeature; @@ -124,8 +125,8 @@ public class EndFeatures { public static final EndFeature SULPHURIC_CAVE = EndFeature.makeCountRawFeature("sulphuric_cave", new SulphuricCaveFeature(), 2); public static final EndFeature ICE_STAR = EndFeature.makeRawGenFeature("ice_star", new IceStarFeature(5, 15, 10, 25), 15); public static final EndFeature ICE_STAR_SMALL = EndFeature.makeRawGenFeature("ice_star_small", new IceStarFeature(3, 5, 7, 12), 8); - public static final EndFeature ICE_STAR_GIANT = EndFeature.makeRawGenFeature("ice_star_giant", new IceStarFeature(15, 25, 20, 40), 512); public static final EndFeature SURFACE_VENT = EndFeature.makeChansedFeature("surface_vent", new SurfaceVentFeature(), 4); + public static final EndFeature SULPHUR_HILL = EndFeature.makeChansedFeature("sulphur_hill", new SulphurHillFeature(), 8); // 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/registry/EndStructures.java b/src/main/java/ru/betterend/registry/EndStructures.java index c96039e0..67756626 100644 --- a/src/main/java/ru/betterend/registry/EndStructures.java +++ b/src/main/java/ru/betterend/registry/EndStructures.java @@ -12,6 +12,7 @@ import net.minecraft.world.gen.feature.ConfiguredStructureFeature; import ru.betterend.BetterEnd; import ru.betterend.world.structures.EndStructureFeature; import ru.betterend.world.structures.features.StructureEternalPortal; +import ru.betterend.world.structures.features.StructureGiantIceStar; import ru.betterend.world.structures.features.StructureGiantMossyGlowshroom; import ru.betterend.world.structures.features.StructureMegaLake; import ru.betterend.world.structures.features.StructureMountain; @@ -36,6 +37,7 @@ public class EndStructures { public static final EndStructureFeature MOUNTAIN = new EndStructureFeature("mountain", new StructureMountain(), Feature.RAW_GENERATION, 3, 2); public static final EndStructureFeature PAINTED_MOUNTAIN = new EndStructureFeature("painted_mountain", new StructurePaintedMountain(), Feature.RAW_GENERATION, 3, 2); public static final EndStructureFeature ETERNAL_PORTAL = new EndStructureFeature("eternal_portal", new StructureEternalPortal(), Feature.SURFACE_STRUCTURES, 16, 6); + public static final EndStructureFeature GIANT_ICE_STAR = new EndStructureFeature("giant_ice_star", new StructureGiantIceStar(), Feature.SURFACE_STRUCTURES, 16, 8); public static void register() {} diff --git a/src/main/java/ru/betterend/world/biome/BiomeIceStarfield.java b/src/main/java/ru/betterend/world/biome/BiomeIceStarfield.java index c71420e9..36df8654 100644 --- a/src/main/java/ru/betterend/world/biome/BiomeIceStarfield.java +++ b/src/main/java/ru/betterend/world/biome/BiomeIceStarfield.java @@ -3,6 +3,7 @@ package ru.betterend.world.biome; import net.minecraft.entity.EntityType; import ru.betterend.registry.EndFeatures; import ru.betterend.registry.EndParticles; +import ru.betterend.registry.EndStructures; public class BiomeIceStarfield extends EndBiome { public BiomeIceStarfield() { @@ -12,7 +13,7 @@ public class BiomeIceStarfield extends EndBiome { .setFoliageColor(193, 244, 244) .setGenChance(0.25F) .setParticles(EndParticles.SNOWFLAKE, 0.002F) - .addFeature(EndFeatures.ICE_STAR_GIANT) + .addStructureFeature(EndStructures.GIANT_ICE_STAR) .addFeature(EndFeatures.ICE_STAR) .addFeature(EndFeatures.ICE_STAR_SMALL) .addMobSpawn(EntityType.ENDERMAN, 20, 1, 4)); diff --git a/src/main/java/ru/betterend/world/biome/BiomeSulphurSprings.java b/src/main/java/ru/betterend/world/biome/BiomeSulphurSprings.java index c09f5871..7e9cfcea 100644 --- a/src/main/java/ru/betterend/world/biome/BiomeSulphurSprings.java +++ b/src/main/java/ru/betterend/world/biome/BiomeSulphurSprings.java @@ -18,6 +18,7 @@ public class BiomeSulphurSprings extends EndBiome { .setParticles(EndParticles.SULPHUR_PARTICLE, 0.001F) .addFeature(EndFeatures.GEYSER) .addFeature(EndFeatures.SURFACE_VENT) + //.addFeature(EndFeatures.SULPHUR_HILL) .addFeature(EndFeatures.SULPHURIC_LAKE) .addFeature(EndFeatures.SULPHURIC_CAVE) .addFeature(EndFeatures.HYDRALUX) diff --git a/src/main/java/ru/betterend/world/features/terrain/SulphurHillFeature.java b/src/main/java/ru/betterend/world/features/terrain/SulphurHillFeature.java new file mode 100644 index 00000000..532568a4 --- /dev/null +++ b/src/main/java/ru/betterend/world/features/terrain/SulphurHillFeature.java @@ -0,0 +1,94 @@ +package ru.betterend.world.features.terrain; + +import java.util.Random; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.BlockPos.Mutable; +import net.minecraft.world.StructureWorldAccess; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.feature.DefaultFeatureConfig; +import ru.betterend.blocks.BlockProperties; +import ru.betterend.noise.OpenSimplexNoise; +import ru.betterend.registry.EndBlocks; +import ru.betterend.util.BlocksHelper; +import ru.betterend.util.MHelper; +import ru.betterend.world.features.DefaultFeature; + +public class SulphurHillFeature extends DefaultFeature { + @Override + public boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos pos, DefaultFeatureConfig config) { + pos = getPosOnSurfaceWG(world, pos); + if (pos.getY() < 57 || pos.getY() > 70) { + return false; + } + + int count = MHelper.randRange(5, 13, random); + OpenSimplexNoise noise = new OpenSimplexNoise(random.nextLong()); + for (int i = 0; i < count; i++) { + int dist = count - i; + int px = pos.getX() + MHelper.floor(random.nextGaussian() * dist * 0.6 + 0.5); + int pz = pos.getZ() + MHelper.floor(random.nextGaussian() * dist * 0.6 + 0.5); + int py = getYOnSurface(world, px, pz); + if (py > 56 && py - pos.getY() <= count) { + makeCircle(world, new BlockPos(px, py, pz), noise, random); + } + } + return true; + } + + private void makeCircle(StructureWorldAccess world, BlockPos pos, OpenSimplexNoise noise, Random random) { + int radius = MHelper.randRange(5, 9, random); + int min = -radius - 3; + int max = radius + 4; + Mutable mut = new Mutable(); + BlockState rock = EndBlocks.SULPHURIC_ROCK.stone.getDefaultState(); + BlockState brimstone = EndBlocks.BRIMSTONE.getDefaultState().with(BlockProperties.ACTIVATED, true); + for (int x = min; x < max; x++) { + int x2 = x * x; + int px = pos.getX() + x; + mut.setX(px); + for (int z = min; z < max; z++) { + int z2 = z * z; + int pz = pos.getZ() + z; + mut.setZ(pz); + double r1 = radius * (noise.eval(px * 0.1, pz * 0.1) * 0.2 + 0.8); + double r2 = r1 - 1.5; + double r3 = r1 - 3; + int d = x2 + z2; + mut.setY(pos.getY()); + BlockState state = world.getBlockState(mut); + if (state.getMaterial().isReplaceable() || state.isOf(EndBlocks.HYDROTHERMAL_VENT)) { + if (d < r2 * r2) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.WATER); + mut.move(Direction.DOWN); + if (d < r3 * r3) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.WATER); + mut.move(Direction.DOWN); + } + BlocksHelper.setWithoutUpdate(world, mut, brimstone); + mut.move(Direction.DOWN); + state = world.getBlockState(mut); + int maxIt = MHelper.floor(10 - Math.sqrt(d)) + random.nextInt(1); + for (int i = 0; i < maxIt && state.getMaterial().isReplaceable(); i++) { + BlocksHelper.setWithoutUpdate(world, mut, rock); + mut.move(Direction.DOWN); + } + } + else if (d < r1 * r1) { + BlocksHelper.setWithoutUpdate(world, mut, brimstone); + mut.move(Direction.DOWN); + state = world.getBlockState(mut); + int maxIt = MHelper.floor(10 - Math.sqrt(d)) + random.nextInt(1); + for (int i = 0; i < maxIt && state.getMaterial().isReplaceable(); i++) { + BlocksHelper.setWithoutUpdate(world, mut, rock); + mut.move(Direction.DOWN); + } + } + } + } + } + } +} diff --git a/src/main/java/ru/betterend/world/features/terrain/SurfaceVentFeature.java b/src/main/java/ru/betterend/world/features/terrain/SurfaceVentFeature.java index 4dfaf1e5..413d0b6c 100644 --- a/src/main/java/ru/betterend/world/features/terrain/SurfaceVentFeature.java +++ b/src/main/java/ru/betterend/world/features/terrain/SurfaceVentFeature.java @@ -18,11 +18,14 @@ import ru.betterend.world.features.DefaultFeature; public class SurfaceVentFeature extends DefaultFeature { @Override public boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos pos, DefaultFeatureConfig config) { + pos = getPosOnSurface(world, new BlockPos(pos.getX() + random.nextInt(16), pos.getY(), pos.getZ() + random.nextInt(16))); + if (pos.getY() < 57) { + return false; + } + Mutable mut = new Mutable(); int count = MHelper.randRange(15, 30, random); BlockState vent = EndBlocks.HYDROTHERMAL_VENT.getDefaultState().with(BlockHydrothermalVent.WATERLOGGED, false); - - pos = getPosOnSurface(world, new BlockPos(pos.getX() + random.nextInt(16), pos.getY(), pos.getZ() + random.nextInt(16))); for (int i = 0; i < count; i++) { mut.set(pos).move(MHelper.floor(random.nextGaussian() * 2 + 0.5), 5, MHelper.floor(random.nextGaussian() * 2 + 0.5)); int dist = MHelper.floor(2 - MHelper.length(mut.getX() - pos.getX(), mut.getZ() - pos.getZ())) + random.nextInt(2); diff --git a/src/main/java/ru/betterend/world/structures/features/StructureGiantIceStar.java b/src/main/java/ru/betterend/world/structures/features/StructureGiantIceStar.java new file mode 100644 index 00000000..89e29468 --- /dev/null +++ b/src/main/java/ru/betterend/world/structures/features/StructureGiantIceStar.java @@ -0,0 +1,119 @@ +package ru.betterend.world.structures.features; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import net.minecraft.block.BlockState; +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.structure.StructureManager; +import net.minecraft.structure.StructureStart; +import net.minecraft.util.math.BlockBox; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.feature.DefaultFeatureConfig; +import net.minecraft.world.gen.feature.StructureFeature; +import ru.betterend.registry.EndBlocks; +import ru.betterend.util.MHelper; +import ru.betterend.util.sdf.SDF; +import ru.betterend.util.sdf.operator.SDFRotation; +import ru.betterend.util.sdf.operator.SDFTranslate; +import ru.betterend.util.sdf.operator.SDFUnion; +import ru.betterend.util.sdf.primitive.SDFCapedCone; +import ru.betterend.world.structures.piece.VoxelPiece; + +public class StructureGiantIceStar extends SDFStructureFeature { + private final float minSize = 20; + private final float maxSize = 35; + private final int minCount = 25; + private final int maxCount = 40; + + @Override + protected SDF getSDF(BlockPos pos, Random random) { + float size = MHelper.randRange(minSize, maxSize, random); + int count = MHelper.randRange(minCount, maxCount, random); + List points = getFibonacciPoints(count); + SDF sdf = null; + SDF spike = new SDFCapedCone().setRadius1(3 + (size - 5) * 0.2F).setRadius2(0).setHeight(size).setBlock(EndBlocks.DENSE_SNOW); + spike = new SDFTranslate().setTranslate(0, size - 0.5F, 0).setSource(spike); + for (Vector3f point: points) { + SDF rotated = spike; + point = MHelper.normalize(point); + float angle = MHelper.angle(Vector3f.POSITIVE_Y, point); + if (angle > 0.01F && angle < 3.14F) { + Vector3f axis = MHelper.normalize(MHelper.cross(Vector3f.POSITIVE_Y, point)); + rotated = new SDFRotation().setRotation(axis, angle).setSource(spike); + } + else if (angle > 1) { + rotated = new SDFRotation().setRotation(Vector3f.POSITIVE_Y, (float) Math.PI).setSource(spike); + } + sdf = (sdf == null) ? rotated : new SDFUnion().setSourceA(sdf).setSourceB(rotated); + } + + final float ancientRadius = size * 0.7F; + final float denseRadius = size * 0.9F; + final float iceRadius = size < 7 ? size * 5 : size * 1.3F; + final float randScale = size * 0.3F; + + final BlockPos center = pos; + final BlockState ice = EndBlocks.EMERALD_ICE.getDefaultState(); + final BlockState dense = EndBlocks.DENSE_EMERALD_ICE.getDefaultState(); + final BlockState ancient = EndBlocks.ANCIENT_EMERALD_ICE.getDefaultState(); + final SDF sdfCopy = sdf; + + return sdf.setPostProcess((info) -> { + BlockPos bpos = info.getPos(); + float px = bpos.getX() - center.getX(); + float py = bpos.getY() - center.getY(); + float pz = bpos.getZ() - center.getZ(); + float distance = MHelper.length(px, py, pz) + sdfCopy.getDistance(px, py, pz) * 0.4F + random.nextFloat() * randScale; + if (distance < ancientRadius) { + return ancient; + } + else if (distance < denseRadius) { + return dense; + } + else if (distance < iceRadius) { + return ice; + } + return info.getState(); + }); + } + + private List getFibonacciPoints(int count) { + float max = count - 1; + List result = new ArrayList(count); + for (int i = 0; i < count; i++) { + float y = 1F - (i / max) * 2F; + float radius = (float) Math.sqrt(1F - y * y); + float theta = MHelper.PHI * i; + float x = (float) Math.cos(theta) * radius; + float z = (float) Math.sin(theta) * radius; + result.add(new Vector3f(x, y, z)); + } + return result; + } + + @Override + public StructureFeature.StructureStartFactory getStructureStartFactory() { + return StarStructureStart::new; + } + + public static class StarStructureStart extends StructureStart { + public StarStructureStart(StructureFeature feature, int chunkX, int chunkZ, BlockBox box, int references, long seed) { + super(feature, chunkX, chunkZ, box, references, seed); + } + + @Override + public void init(DynamicRegistryManager registryManager, ChunkGenerator chunkGenerator, StructureManager manager, int chunkX, int chunkZ, Biome biome, DefaultFeatureConfig config) { + int x = (chunkX << 4) | MHelper.randRange(4, 12, random); + int z = (chunkZ << 4) | MHelper.randRange(4, 12, random); + BlockPos start = new BlockPos(x, MHelper.randRange(32, 128, random), z); + VoxelPiece piece = new VoxelPiece((world) -> { ((SDFStructureFeature) this.getFeature()).getSDF(start, this.random).fillRecursive(world, start); }, random.nextInt()); + this.children.add(piece); + this.setBoundingBoxFromChildren(); + } + } +}