feat: curve v5 — corner fixes + COMPACT/BALANCED taste presets (terrain-water task 09)

The three corner fixes (both presets): riser endpoint slope floors
0.2 -> 0.1 (0.1u + 0.9*smoothstep — climbs decelerate into shelves and
accelerate out); summit-spike base floor 0.1 -> 0.05
(0.05u + 0.95*u^4 — no hard skirt under the peaks); SHELF_SPAN_MIN
2 m -> 6 m (pronounced shelves keep a gentle tilt).

Knots are preset-parameterized (CurveKnots, explicit parameter — pure,
D-035), calibrated from the same pooled batch-04 CDF:
COMPACT  P65/77/84/88/94/97 -> 65/12/7/4/6/3/3 land split
BALANCED P60/73/83/88/96/99 -> 60/13/10/5/8/3/1
Gate: "off"|"v5-compact"|"v5-balanced" (batch-only tri-state;
interim default v5-balanced pending the developer's verdict — the
winner becomes plain "v5" in a follow-up commit); v1-v4 retired
loudly. Assertion runs per preset (8 corners x per-seed spikeMax; the
lowered floors stay strictly positive). TCRV v5 extension (+9 B):
preset id + K5/K6 — the effective curve is unambiguous from the
record; old records parse (version-dispatched); harness compares.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Stewart Howe 2026-08-08 04:11:53 -04:00
parent 68f7bca8e0
commit 02577872f2
6 changed files with 137 additions and 101 deletions

View file

@ -142,6 +142,13 @@ namespace IslaApocalypse.Core
writer.Write(c.BenchSeedOffset); writer.Write(c.PlateauSeedOffset);
writer.Write(c.StrengthSeedOffset);
}
// v5+ extension: knot preset id + K5/K6.
if (c.Version >= 5)
{
writer.Write(c.PresetId);
writer.Write(c.K5); writer.Write(c.K6);
}
}
private static void WriteWaterBodyIds(BinaryWriter writer, WorldBlueprint bp)

View file

@ -26,12 +26,13 @@ namespace IslaApocalypse.Core // Change this if your namespace is different
public static string SeaLevelModel = "flat";
public static float SeaLevelValue = 0.15f;
// Height-redistribution curve (tasks 05–08, graduation M-7): "v4" applies the
// terraced-ascent curve with spatially modulated shelves and the per-seed peak
// spike (HeightCurve.cs); "off" is the raw legacy profile. "v1"–"v3" were
// retired by successor recalibrations (old blueprints are regenerable). Biome
// classification is curve-invariant by construction either way. Default: v4.
public static string TerrainCurve = "v4";
// Height-redistribution curve (tasks 05–09, graduation M-7): the task-09 taste
// batch runs a tri-state — "v5-compact" (maximum lowland) vs "v5-balanced"
// (task-07/08 lineage), both with the corner fixes; "off" is the raw legacy
// profile. The loser retires after the developer's gate and the winner becomes
// plain "v5". "v1"–"v4" retired. INTERIM default pending the verdict:
// v5-balanced.
public static string TerrainCurve = "v5-balanced";
public static void LoadConfig()
{
@ -106,10 +107,10 @@ namespace IslaApocalypse.Core // Change this if your namespace is different
if (data.ContainsKey("TerrainCurve"))
{
string curve = (string)data["TerrainCurve"];
if (curve == "off" || curve == "v4")
if (curve == "off" || curve == "v5-compact" || curve == "v5-balanced")
TerrainCurve = curve;
else if (curve == "v1" || curve == "v2" || curve == "v3")
GD.PrintErr($"[ConfigManager] TerrainCurve '{curve}' was retired by a later recalibration (v4, task 08). Keeping '{TerrainCurve}' — use \"v4\" or \"off\".");
else if (curve == "v1" || curve == "v2" || curve == "v3" || curve == "v4")
GD.PrintErr($"[ConfigManager] TerrainCurve '{curve}' was retired by a later recalibration (v5, task 09). Keeping '{TerrainCurve}' — use \"v5-compact\", \"v5-balanced\" or \"off\".");
else
GD.PrintErr($"[ConfigManager] Unknown TerrainCurve '{curve}'. Keeping '{TerrainCurve}'.");
}

View file

@ -80,6 +80,11 @@ namespace IslaApocalypse.Core
public float BenchAmp, PlateauAmp, ShelfSpanMin, ShelfSpanMax;
public float ElevFreqIslands, StrengthFreqIslands;
public int BenchSeedOffset, PlateauSeedOffset, StrengthSeedOffset;
// v5 extension — knot preset (1 = compact, 2 = balanced) and the two knots the
// four base slots cannot carry; with these the effective curve is unambiguous.
public byte PresetId;
public float K5, K6;
}
public class WorldBlueprint
@ -416,6 +421,11 @@ namespace IslaApocalypse.Core
c.BenchSeedOffset = reader.ReadInt32(); c.PlateauSeedOffset = reader.ReadInt32();
c.StrengthSeedOffset = reader.ReadInt32();
}
if (c.Version >= 5)
{
c.PresetId = reader.ReadByte();
c.K5 = reader.ReadSingle(); c.K6 = reader.ReadSingle();
}
blueprint.TerrainCurve = c;
return true;
}