using System.Collections.Generic; namespace IslaApocalypse.Tools { /// /// Everything pass 1 produces. → . /// /// ⭐ THREE OF THESE FIELDS ARE THE PHASE-2 SEAM. The reference computed them in pass 1 and /// consumed them in pass 2; exposing them now is what keeps the deferred ports clean, and it /// costs nothing to carry. /// public sealed class Pass1Result { /// Map side in columns. public readonly int MapSize; /// The resolved seed this was generated with. Always positive, always recorded. public readonly int Seed; /// /// The raw pass-1 height field, [x, y]. Pre-curve, pre-erosion, pre-crater. /// The reference's finalH (~:619) written to _heightMap (~:666). /// public readonly float[,] Height; /// /// ⭐ PHASE-2 SEAM — the falloff value captured BEFORE the Pow(·, 2.5f) and BEFORE the /// Trench wall (reference ~:591, preTrenchFalloff). /// /// It is the "how far past the island body are we" number, uncontaminated by the power's /// curvature and by the Trench's additive ramp. The DEFERRED offshore-islet layer reads it /// as its distance mask (OffshoreZoneWeight), which is the only reason it is captured /// mid-computation rather than recomputed. /// /// ⚠ A port that "tidies" the falloff chain into one expression destroys this capture point, /// and the loss would not show up until Phase 2's offshore mask moved. It is exposed here so /// that cannot happen quietly. /// public readonly float[,] PreTrenchFalloff; /// /// ⭐ PHASE-2 SEAM — the wobbled latitude scalar, [x, y]. /// /// ⚠ THIS IS NOT A CLIMATE TEMPERATURE MAP. See for the full note. /// It is a stage-1-local latitude field that terrain GEOMETRY reads. Phase 2 needs it if the /// latitude sea-level model is ever switched on (SeaLevelModel = "field"); under the /// shipped flat model it has no consumer beyond the spine. /// public readonly float[,] LatitudeField; /// /// ⭐ PHASE-2 SEAM — the maximum height over the whole map (reference _hMaxSeed, ~:665). /// /// The reference's v2+ redistribution curve is SEED-DEPENDENT: its spike maps /// [t4, hMaxSeed] onto the peak band, so pass 2 cannot start until pass 1 has scanned /// every pixel. That is precisely why the pass-1/pass-2 boundary is a hard one and not an /// interleave. Carried here so Phase 2 inherits it rather than rediscovering the dependency. /// public readonly float HMaxSeed; /// The minimum height. Not a reference field — carried for the renderer's ramp and the report. public readonly float HMinSeed; /// /// ⚠ The map-wide max BEFORE the coast shelf and offshore islets ran (chat2/05). The /// reference takes _hMaxSeed AFTER both, inside the same loop; v2 now does too — /// is the post-shelf/offshore value the curve normalizes against. /// This one is carried so the report can state whether the two differed (expected: no, an /// islet crest of ~34 m is far below any peak — but "expected" is measured, not assumed). /// public readonly float HMaxSeedBeforeOffshore; // ═══ ⭐ THE OFFSHORE TAG — chat2/05's one forward-looking piece ═══ // // DATA, set here, carried downstream, READ BY NOTHING IN THIS PHASE. It exists so a later // pass (biome, fertility, placement) can find offshore-island land without re-deriving it // from geometry. Downstream that ignores it is unaffected; downstream that reads it gets a // clean flag. Both arrays are NULL when the offshore layer is off — a consumer checks for // null, not for all-false. /// /// Per column: is this land an offshore island (as opposed to mainland)? Set for every cell /// the islet layer lifted from below sea to at-or-above sea. Null when offshore is off. /// public readonly bool[,] IsOffshoreIsland; /// /// Per column: / /// for tagged cells, otherwise. The convention is the /// row midline — see . Null when offshore is off. /// public readonly byte[,] IslandHemisphere; /// Cells the islet layer lifted above sea. The reference printed this too. public readonly long OffshoreLiftedCells; /// The islet layer's numbers — thresholds, pre-guard count, guard ledger, final count. Null when offshore is off. public readonly OffshoreLedger OffshoreLedger; /// Lines worth printing from the shelf/offshore pass: thresholds, guard ledger, lifted counts. public readonly IReadOnlyList Notes; /// Wall-clock milliseconds the pass took. public readonly ulong ElapsedMs; public Pass1Result(int mapSize, int seed, float[,] height, float[,] preTrenchFalloff, float[,] latitudeField, float hMaxSeed, float hMinSeed, ulong elapsedMs, float hMaxSeedBeforeOffshore = float.NaN, bool[,] isOffshoreIsland = null, byte[,] islandHemisphere = null, long offshoreLiftedCells = 0, IReadOnlyList notes = null, OffshoreLedger offshoreLedger = null) { MapSize = mapSize; Seed = seed; Height = height; PreTrenchFalloff = preTrenchFalloff; LatitudeField = latitudeField; HMaxSeed = hMaxSeed; HMinSeed = hMinSeed; ElapsedMs = elapsedMs; HMaxSeedBeforeOffshore = float.IsNaN(hMaxSeedBeforeOffshore) ? hMaxSeed : hMaxSeedBeforeOffshore; IsOffshoreIsland = isOffshoreIsland; IslandHemisphere = islandHemisphere; OffshoreLiftedCells = offshoreLiftedCells; OffshoreLedger = offshoreLedger; Notes = notes ?? System.Array.Empty(); } /// Whether the offshore layer ran on this field (the tag arrays are present). public bool HasOffshoreTag => IsOffshoreIsland != null; /// Fraction of the map at or above the sea threshold. A cheap shape sanity number. public float LandFraction(float seaLevel) { long land = 0; for (int x = 0; x < MapSize; x++) for (int y = 0; y < MapSize; y++) if (Height[x, y] >= seaLevel) land++; return land / (float)((long)MapSize * MapSize); } } }