From 676372efab9f33381ef6ddc6429fe8adaa67e31a Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Tue, 1 Sep 2026 19:08:20 -0400 Subject: [PATCH] River world hooks - Skip nib, pothole, wall and slab post passes on river footprints and their collar - Keep automatic object scatter off the channel and shore band - Expose deeper biome layers on eroded banks and use the bank biome on the blend band - Give dry cave headroom the flooded cave biome --- .../art/arcane/iris/engine/IrisComplex.java | 9 +++-- .../actuator/IrisTerrainNormalActuator.java | 17 ++++++++-- .../hydrology/HydrologyColumnLayer.java | 2 +- .../components/MantleHydrologyComponent.java | 2 +- .../engine/modifier/IrisPostModifier.java | 34 ++++++++++++++++--- .../engine/object/IrisSurfaceSupport.java | 2 +- .../engine/IrisComplexSurfaceBiomeTest.java | 4 +-- .../IrisPostModifierHydrologyTest.java | 20 +++++++++++ 8 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 core/src/test/java/art/arcane/iris/engine/modifier/IrisPostModifierHydrologyTest.java diff --git a/core/src/main/java/art/arcane/iris/engine/IrisComplex.java b/core/src/main/java/art/arcane/iris/engine/IrisComplex.java index 883121adc..aa82cb086 100644 --- a/core/src/main/java/art/arcane/iris/engine/IrisComplex.java +++ b/core/src/main/java/art/arcane/iris/engine/IrisComplex.java @@ -632,7 +632,7 @@ public class IrisComplex implements DataProvider { if (layer.channel()) { return 1D; } - return layer.shore() ? 0.5D : 0.25D; + return layer.shore() ? 0.75D : 0.5D; } private double resolveHydrologyFluidSurface(double x, double z) { @@ -658,6 +658,11 @@ public class IrisComplex implements DataProvider { return surfaceFluidLayer(x, z) != null; } + public boolean hasHydrologyChannelOrShore(int x, int z) { + HydrologyColumnLayer layer = surfaceLayer(x, z); + return layer != null && (layer.channel() || layer.shore()); + } + public ProceduralStream getBiomeStream(InferredType type) { switch (type) { case CAVE: @@ -732,7 +737,7 @@ public class IrisComplex implements DataProvider { throw new IllegalStateException("Hydrology biome does not exist: " + biomeKey); } if (layer.biomeKey() == null) { - return biome; + return implode(biome.withInferredType(InferredType.LAND), x, z); } InferredType inferredType = layer.shore() ? InferredType.SHORE diff --git a/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java b/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java index 556812fc8..eb249afe3 100644 --- a/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java +++ b/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java @@ -103,6 +103,8 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator dimensionUndergroundOres = hideOres ? null : dimension.getUndergroundOreGenerators(); IrisOreGeneratorBounds dimensionSurfaceOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : dimension.getSurfaceOreGeneratorBounds(); IrisOreGeneratorBounds dimensionUndergroundOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : dimension.getUndergroundOreGeneratorBounds(); + boolean exposeCutStrata = dimension.getHydrology() != null + && dimension.getHydrology().getRivers().getSurface().getBanks().isExposeCutStrata(); for (int zf = 0; zf < chunkDepth; zf++) { int realZ = zf + z; @@ -125,6 +127,15 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator * resolve the same height stream entry up to five times. Resolve the padded plane once instead. The * four diagonal corners are never read, so they are left unresolved. */ + private boolean riverColumn(int x, int z) { + ProceduralStream carveWeights = getComplex().getRiverCarveWeightStream(); + return skipsHydrologyColumn(new double[] { + carveWeights.get(x, z), + carveWeights.get(x + 1, z), + carveWeights.get(x - 1, z), + carveWeights.get(x, z + 1), + carveWeights.get(x, z - 1) + }); + } + + static boolean skipsHydrologyColumn(double[] carveWeights) { + for (double weight : carveWeights) { + if (weight > 0D) { + return true; + } + } + return false; + } + private int[] heightPlane(int x, int z, int width, int depth, int planeWidth) { int[] heights = new int[planeWidth * (depth + 2)]; EngineMantle mantle = getEngine().getMantle(); @@ -100,6 +121,7 @@ public class IrisPostModifier extends EngineAssignedModifier int hc = heights[center - 1]; int hd = heights[center - planeWidth]; int fluidHeight = (int) Math.round(getComplex().getRiverWaterSurfaceStream().get(x, z)); + boolean river = riverColumn(x, z); // Floating Nibs int g = 0; @@ -113,7 +135,7 @@ public class IrisPostModifier extends EngineAssignedModifier g += hc < h - 1 ? 1 : 0; g += hd < h - 1 ? 1 : 0; - if (g == 4 && isAir(x, h - 1, z, originX, originZ, currentData)) { + if (!river && g == 4 && isAir(x, h - 1, z, originX, originZ, currentData)) { setPostBlock(x, h, z, States.AIR, originX, originZ, currentData); for (int i = h - 1; i > 0; i--) { @@ -131,6 +153,10 @@ public class IrisPostModifier extends EngineAssignedModifier g += hc == h - 1 ? 1 : 0; g += hd == h - 1 ? 1 : 0; + if (river) { + g = -1; + } + if (g >= 4) { PlatformBlockState bcState = getPostBlock(x, h, z, originX, originZ, currentData); PlatformBlockState bState = getPostBlock(x, h + 1, z, originX, originZ, currentData); @@ -141,7 +167,7 @@ public class IrisPostModifier extends EngineAssignedModifier h--; } } - } else { + } else if (!river) { // Potholes g = 0; g += ha == h + 1 ? 1 : 0; @@ -170,7 +196,7 @@ public class IrisPostModifier extends EngineAssignedModifier // Wall Patcher IrisBiome biome = context.getBiome().get(currentPostX, currentPostZ); - if (walls) { + if (walls && !river) { if (!biome.getWall().getPalette().isEmpty()) { if (ha < h - 2 || hb < h - 2 || hc < h - 2 || hd < h - 2) { boolean brokeGround = false; @@ -197,7 +223,7 @@ public class IrisPostModifier extends EngineAssignedModifier } // Slab - if (slabs) { + if (slabs && !river) { //@builder if ((ha == h + 1 && isSolidNonSlab(x + 1, ha, z, originX, originZ, currentData)) || (hb == h + 1 && isSolidNonSlab(x, hb, z + 1, originX, originZ, currentData)) diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisSurfaceSupport.java b/core/src/main/java/art/arcane/iris/engine/object/IrisSurfaceSupport.java index 9ad400911..2364505a9 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisSurfaceSupport.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisSurfaceSupport.java @@ -205,7 +205,7 @@ public final class IrisSurfaceSupport { } int columnX = x + minX + stencilX; int columnZ = z + minZ + stencilZ; - if (complex.hasHydrologySurfaceFluid(columnX, columnZ)) { + if (complex.hasHydrologyChannelOrShore(columnX, columnZ)) { return true; } } diff --git a/core/src/test/java/art/arcane/iris/engine/IrisComplexSurfaceBiomeTest.java b/core/src/test/java/art/arcane/iris/engine/IrisComplexSurfaceBiomeTest.java index f2d6bc775..ac3a99db5 100644 --- a/core/src/test/java/art/arcane/iris/engine/IrisComplexSurfaceBiomeTest.java +++ b/core/src/test/java/art/arcane/iris/engine/IrisComplexSurfaceBiomeTest.java @@ -21,7 +21,7 @@ import static org.mockito.Mockito.mock; public class IrisComplexSurfaceBiomeTest { @Test - public void gradingFootprintKeepsItsExactNaturalParentBiome() { + public void gradingFootprintUsesTheBankBiome() { HydrologyFeatureRef feature = new HydrologyFeatureRef( 1L, HydrologyFeatureType.SURFACE_POOL, @@ -65,7 +65,7 @@ public class IrisComplexSurfaceBiomeTest { List.of(grading) ); - assertEquals("exact_parent", IrisComplex.hydrologySurfaceBiomeKey(sample)); + assertEquals("dry", IrisComplex.hydrologySurfaceBiomeKey(sample)); } @Test diff --git a/core/src/test/java/art/arcane/iris/engine/modifier/IrisPostModifierHydrologyTest.java b/core/src/test/java/art/arcane/iris/engine/modifier/IrisPostModifierHydrologyTest.java new file mode 100644 index 000000000..e34a36df9 --- /dev/null +++ b/core/src/test/java/art/arcane/iris/engine/modifier/IrisPostModifierHydrologyTest.java @@ -0,0 +1,20 @@ +package art.arcane.iris.engine.modifier; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class IrisPostModifierHydrologyTest { + @Test + public void columnsTouchingARiverFootprintSkipThePostPasses() { + assertTrue(IrisPostModifier.skipsHydrologyColumn(new double[] {1D, 0D, 0D, 0D, 0D})); + assertTrue(IrisPostModifier.skipsHydrologyColumn(new double[] {0D, 0D, 0.5D, 0D, 0D})); + assertTrue(IrisPostModifier.skipsHydrologyColumn(new double[] {0D, 0D, 0D, 0D, 0.75D})); + } + + @Test + public void columnsAwayFromRiversKeepThePostPasses() { + assertFalse(IrisPostModifier.skipsHydrologyColumn(new double[] {0D, 0D, 0D, 0D, 0D})); + } +}