using Godot;
///
/// The height-redistribution curve, v4 — SPATIALLY MODULATED SHELVES (terrain-water
/// task 08). Pure, static, monotonic piecewise map; every per-column input is an
/// explicit PARAMETER (D-035): the seed spike max and the four modulated shelf
/// values. The generator samples the modulation fields; this class never touches
/// noise.
///
/// v4 (developer's task-07 gate finding: the terraces work, but uniform anchors put
/// a flat ring at exactly 100 m and exactly 220 m on every mountain — the bathtub
/// rings): the bench and plateau OUTPUT anchors become smooth spatial fields —
/// benchLo = 100 m ± 12 m and plateauLo = 220 m ± 20 m via two decorrelated
/// very-low-frequency noise fields — and a third field modulates SHELF STRENGTH,
/// blending each shelf between "pronounced flat" (output span ~2 m) and "barely a
/// hint" (~25 m of gentle slope), so not every flank at shelf height develops the
/// full terrace. Shelves stay locally flat; the two magic altitudes stop existing.
///
/// Structure otherwise v3's, unchanged: identity ≤ sea, frozen toe/rise (storm
/// ladder), foothill riser → bench → mid riser → plateau → per-seed-normalized
/// stiff spike (u⁴, 420 m cap) → tail. Input knots are v3's calibration.
///
/// ORDERING SAFETY BY CONSTRUCTION (asserted): with the amplitudes below, at every
/// column: red ceiling 0.27 < benchLo−… (bench min 0.5006), bench top max 0.6962 <
/// plateauLo min 0.9468, plateau top max 1.2062 < peak cap 1.8287. The numeric
/// assertion additionally sweeps all 8 modulation-extreme corners per generation.
///
public static class HeightCurve
{
public const ushort VERSION = 4;
// Input knots — v3 calibration (pooled batch-04 land CDF, P59/72/82/87/95/98).
public const float K1 = 0.509179f;
public const float K2 = 0.604081f;
public const float K3 = 0.698485f;
public const float K4 = 0.767213f;
public const float K5 = 0.930304f;
public const float K6 = 1.050720f;
// Fixed output anchors — storm ladder + ceiling (frozen).
public const float SEA = 0.15f;
public const float ORANGE_CEIL = 0.206f;
public const float RED_CEIL = 0.27f;
public const float PEAK_CAP = SEA + 420f / 251f; // ≈ 1.82869
public const float TAIL_SLOPE = 0.25f;
public const float SPIKE_MIN_SPAN = 0.01f;
// Modulated shelf anchors: base ± amplitude (raw units; 251 m per unit).
public const float BENCH_BASE = SEA + 100f / 251f; // ≈ 0.54841 (100 m)
public const float BENCH_AMP = 12f / 251f; // ± 12 m
public const float PLATEAU_BASE = SEA + 220f / 251f; // ≈ 1.02649 (220 m)
public const float PLATEAU_AMP = 20f / 251f; // ± 20 m
// Shelf strength: output span of each shelf segment, blended by the strength
// field. Strong (t=1) → SPAN_MIN (~2 m, pronounced flat). Weak (t=0) →
// SPAN_MAX (~25 m, barely a hint of a shelf).
public const float SHELF_SPAN_MIN = 0.008f;
public const float SHELF_SPAN_MAX = 0.10f;
// Modulation-field derivation (generator-side, recorded in TCRV): field seed =
// RESOLVED WorldSeed + offset; frequency = (periods per island width) / MapSize.
public const int BENCH_SEED_OFFSET = 7101;
public const int PLATEAU_SEED_OFFSET = 7207;
public const int STRENGTH_SEED_OFFSET = 7303;
public const float ELEV_FREQ_ISLANDS = 3.0f; // ~3 undulations across the island
public const float STRENGTH_FREQ_ISLANDS = 5.0f; // finer patchiness for shelf strength
public static float EffectiveSpikeMax(float hMaxSeed)
{
return Mathf.Max(hMaxSeed, K6 + SPIKE_MIN_SPAN);
}
/// Shelf output span for a strength sample t ∈ [0,1].
public static float ShelfSpan(float strength01)
{
return Mathf.Lerp(SHELF_SPAN_MAX, SHELF_SPAN_MIN, Mathf.Clamp(strength01, 0f, 1f));
}
/// Raw pre-curve height.
/// Seed's raw pre-curve maximum (per-seed spike normalizer).
/// This column's bench anchor (BENCH_BASE ± BENCH_AMP).
/// This column's bench output span (ShelfSpan of the strength field).
/// This column's plateau anchor (PLATEAU_BASE ± PLATEAU_AMP).
/// This column's plateau output span.
public static float Apply(float h, float hMaxSeed,
float benchLo, float benchSpan, float plateauLo, float plateauSpan)
{
if (h <= SEA) return h;
float u, s;
if (h < K1)
{
u = (h - SEA) / (K1 - SEA);
s = 0.3f * u + 0.7f * (u * (2f - u)); // frozen ease-out toe
return SEA + s * (ORANGE_CEIL - SEA);
}
if (h < K2)
{
u = (h - K1) / (K2 - K1);
return ORANGE_CEIL + u * (RED_CEIL - ORANGE_CEIL); // frozen linear rise
}
if (h < K3)
{
u = (h - K2) / (K3 - K2);
s = 0.2f * u + 0.8f * (u * u * (3f - 2f * u)); // foothill riser
return RED_CEIL + s * (benchLo - RED_CEIL);
}
if (h < K4)
{
u = (h - K3) / (K4 - K3);
return benchLo + u * benchSpan; // bench — modulated
}
float benchTop = benchLo + benchSpan;
if (h < K5)
{
u = (h - K4) / (K5 - K4);
s = 0.2f * u + 0.8f * (u * u * (3f - 2f * u)); // mid riser
return benchTop + s * (plateauLo - benchTop);
}
if (h < K6)
{
u = (h - K5) / (K6 - K5);
return plateauLo + u * plateauSpan; // plateau — modulated
}
float plateauTop = plateauLo + plateauSpan;
float spikeMax = EffectiveSpikeMax(hMaxSeed);
if (h < spikeMax)
{
u = (h - K6) / (spikeMax - K6);
s = 0.1f * u + 0.9f * (u * u * u * u); // summit spike (v2/v3 shape)
return plateauTop + s * (PEAK_CAP - plateauTop);
}
return PEAK_CAP + (h - spikeMax) * TAIL_SLOPE;
}
///
/// Per-generation numeric strict-monotonicity check of the EFFECTIVE curve:
/// sweeps the full domain at every one of the 8 modulation-extreme corners
/// (bench anchor ±, plateau anchor ±, strength min/max) with the per-seed
/// spikeMax — the adversarial corner set for the ordering constraints. Loud
/// throw, refuses to generate.
///
public static void AssertMonotonic(float hMaxSeed)
{
float[] benchLos = { BENCH_BASE - BENCH_AMP, BENCH_BASE + BENCH_AMP };
float[] plateauLos = { PLATEAU_BASE - PLATEAU_AMP, PLATEAU_BASE + PLATEAU_AMP };
float[] spans = { SHELF_SPAN_MIN, SHELF_SPAN_MAX };
foreach (float bl in benchLos)
{
foreach (float pl in plateauLos)
{
foreach (float sp in spans)
{
float prevH = -7f;
float prev = Apply(prevH, hMaxSeed, bl, sp, pl, sp);
void Check(double hd)
{
float h = (float)hd;
if (h <= prevH) return; // dedupe float32 samples (task-05 fix)
float v = Apply(h, hMaxSeed, bl, sp, pl, sp);
if (v <= prev)
throw new System.InvalidOperationException(
$"[HeightCurve] MONOTONICITY VIOLATION at h={h} (hMaxSeed={hMaxSeed}, benchLo={bl}, plateauLo={pl}, span={sp}): {v} <= {prev}. Refusing to generate.");
prev = v;
prevH = h;
}
double top = System.Math.Max(2.0, EffectiveSpikeMax(hMaxSeed) + 0.5);
for (double hh = -7.0 + 0.01; hh < 0.10; hh += 0.01) Check(hh);
for (double hh = 0.10; hh <= top; hh += 0.0001) Check(hh);
for (double hh = top + 0.05; hh <= top + 6.0; hh += 0.05) Check(hh);
}
}
}
GD.Print($"[HeightCurve] Monotonicity assertion passed (v{VERSION}, 8 modulation corners, effective spikeMax {EffectiveSpikeMax(hMaxSeed):F6}).");
}
}