using System; using IslaApocalypse.Core; namespace IslaApocalypse.Tools { /// /// ⭐⭐ THE LOCKED SHAPE — `terrain-shape-v1` (commit a59e52f), AS AN ASSERTION. /// /// ═══ ⚠⚠ THIS TYPE INVERTED AT rivers/01. READ THIS BEFORE USING IT. ═══ /// /// It used to be a PRESET: Apply(cfg) stamped the locked shape onto a bare config, because /// the bare defaults did not reproduce the terrain the developer had judged. That was the single /// most expensive fact in the codebase — *"run the default generator" ≠ "the terrain the developer /// locked"* — and a fresh chat comparing bare-default output against the locked renders would see /// differences that were CONFIGURATION, not regression. /// /// **rivers/01 re-baselined the defaults so that `new TerrainGenConfig()` IS the locked shape.** /// So Apply is GONE — there is nothing left to apply, and re-stamping the values on top of /// the defaults would mask a default drift instead of catching it. /// /// > ### What survives is the OPPOSITE job: these constants are now the ASSERTION TARGET. /// > They are the values the developer judged, written down once, and holds /// > the live defaults against them. If a default is ever edited, the batch tools that claim to /// > render the locked shape REFUSE TO RUN rather than quietly rendering something else. /// /// ⚠ DO NOT "fix" a drift by editing these constants — they are the record of what was approved, /// and `10_frag4_seed_gallery` / `11_erosion` are its pixels. A deliberate shape change moves the /// defaults AND these constants AND re-runs the acceptance, in one task, as rivers/01 did. /// /// ⚠ THE SHELF IS OFF, DELIBERATELY. The locked shape has no coast shelf; evaluating it (→ D-041) /// is its own later task, once water renders. ⚠ THE ISLETS ARE OFF, PERMANENTLY (→ D-063): islands /// are organic-only, made by the stretch + fragmentation and identified by the region layer. /// /// ⚠ EROSION IS NOT PART OF THIS SHAPE. `terrain-shape-v1` is the pass-1/1c/2a field; erosion is /// pass 2b, on top, render-only (`11_erosion` = `ea291ea`). It is asserted separately. /// public static class TerrainShapeV1 { /// The tagged commit this shape is defined by. ⚠ Reference the COMMIT — the tag is annotated and local-only until the developer pushes it. public const string Commit = "a59e52f"; public const float FragmentAmp = 0.5f, FragmentFreq = 12f, BandCentre = 0.66f, BandHalfWidth = 0.18f; public const bool BitesOnly = false; public const float Stretch = 2f, BandStart = 0.70f, BandFeather = 0.05f; public const bool StretchSinker = true; public const float SpeckFrac = 2.5e-7f; /// /// ⭐ THE DEFAULT-DRIFT GUARD. Throws unless a bare carries the /// locked shape exactly. Every batch tool that renders or asserts `terrain-shape-v1` calls this /// before it generates anything. /// /// ⚠ It is a THROW, not a warning, for the same reason is: a batch /// that renders the wrong terrain still produces beautiful, browsable, wrong PNGs, and a human /// gate cannot see a default from a picture. /// /// The calling tool, for the message. public static void Assert(string who) { var d = new TerrainGenConfig(); string bad = null; void Want(string name, object got, object want) { if (!Equals(got, want)) bad = (bad == null ? "" : bad + "; ") + $"{name} = {got}, expected {want}"; } Want(nameof(d.SouthStretch), d.SouthStretch, Stretch); Want(nameof(d.SouthBandStartFrac), d.SouthBandStartFrac, BandStart); Want(nameof(d.SouthBandFeatherFrac), d.SouthBandFeatherFrac, BandFeather); Want(nameof(d.StretchSinker), d.StretchSinker, StretchSinker); Want(nameof(d.FragmentAmp), d.FragmentAmp, FragmentAmp); Want(nameof(d.FragmentFreqPerMapWidth), d.FragmentFreqPerMapWidth, FragmentFreq); Want(nameof(d.FragmentBandCentre), d.FragmentBandCentre, BandCentre); Want(nameof(d.FragmentBandHalfWidth), d.FragmentBandHalfWidth, BandHalfWidth); Want(nameof(d.FragmentBitesOnly), d.FragmentBitesOnly, BitesOnly); Want(nameof(d.SpeckRevert), d.SpeckRevert, true); Want(nameof(d.MinLandComponentFrac), d.MinLandComponentFrac, SpeckFrac); Want(nameof(d.CoastShelf), d.CoastShelf, false); Want(nameof(d.RegionLabeling), d.RegionLabeling, true); Want("Offshore.Mode", d.Offshore.Mode, OffshoreMode.Off); if (bad != null) throw new InvalidOperationException( $"[{who}] LOCKED-SHAPE DRIFT: the bare TerrainGenConfig no longer reproduces " + $"terrain-shape-v1 ({Commit}) — {bad}. Refusing to run: this tool's output is only " + "meaningful if the defaults ARE the locked shape. → Tools/Scripts/TerrainShapeV1.cs (rivers/01)."); } /// /// The same guard for the EROSION default, kept separate because erosion is pass 2b and is not /// part of the shape. Tools that render the erosion A/B set the flag per variant and call this /// only if they rely on the default. /// public static void AssertErosionDefaultOn(string who) { if (!new TerrainGenConfig().Erosion) throw new InvalidOperationException( $"[{who}] EROSION DEFAULT DRIFT: bare TerrainGenConfig.Erosion is false; rivers/01 " + "made it true (the rivers baseline routes on the eroded render field). Refusing to run."); } /// /// One line for a run header, READ FROM THE LIVE DEFAULTS rather than from the constants — /// so the header states what actually ran, and states whether that is /// still the locked shape. /// public static string Describe() { var d = new TerrainGenConfig(); return $"terrain-shape-v1 ({Commit}) from the BARE DEFAULTS: frag amp {d.FragmentAmp} freq {d.FragmentFreqPerMapWidth} " + $"window {d.FragmentBandCentre}±{d.FragmentBandHalfWidth} · stretch {d.SouthStretch} " + $"(band {d.SouthBandStartFrac}/{d.SouthBandFeatherFrac}, sinker {(d.StretchSinker ? "stretched" : "real-y")}) · " + $"speck revert {d.MinLandComponentFrac:G2} · offshore {d.Offshore.Mode} · shelf {(d.CoastShelf ? "ON" : "OFF")} · labeling {(d.RegionLabeling ? "ON" : "OFF")}"; } } }