islaApocalypse-v2/Core
beezm 8e55326a84 Phase 2a: continuous grade — smooth the upper staircase, preserve the lowlands
rev 3 of the curve redesign. The developer's verdict on the 01 baseline was that the
LOWLANDS ARE GOOD; the fault is the terracing above them. So this adds a second curve
mode that preserves the low plain bit-for-bit and replaces everything above the flood
line with one smooth monotone climb.

Core/ContinuousCurve — piecewise, and the pieces have different loyalties:
- at/below sea: identity, as ever.
- above sea to K2: DELEGATES to HeightCurve's own toe+red branches. Not "equivalent" —
  the same code path, so the same floats. Oracle (d) holds it to that.
- above the ceiling: a Fritsch-Carlson (PCHIP) monotone spline to the 420 m cap,
  C1-joined to the red band's exit slope. Monotone by construction for any ordered
  control points, which retires the 24-corner sweep; a 10k strict-increase sample runs
  per seed anyway, because "cannot fail" is worth a millisecond.
- Build() REFUSES rather than degrades: a ceiling near the old bench, a drama that folds
  the summit under its own onset, control-point secants that are not strictly increasing
  (the no-magnet rule, enforced rather than hoped for).

Only BENCH_*/PLATEAU_* are dropped. SEA/ORANGE_CEIL/RED_CEIL survive because they are
the storm-ladder FLOOD TIERS and they live inside the preserved lowland; PEAK_CAP and
the per-seed spikeMax normalization survive as the summit.

Shelf detail is forced off in continuous mode: the flat benches it de-slabbed no longer
exist, and painting noise on the climb now would pre-judge what erosion should carve.

Oracle, all hard checks passing:
- (a1) curve off is bit-identical to Phase 1's dump.
- (a2) staircase mode is bit-identical to TASK 01's dump — the control is provably the
  control, not a re-derivation. (CurveBaselineTool is pinned to Staircase so the config
  default moving to Continuous cannot drift it.)
- (d) lowlands bit-identical to the staircase over 3.6M cells, every continuous variant,
  both seeds. The lifted_WRONG bookend fails it on 1.6M cells, as intended.
- (f) sea identity per CELL, not per count, including 67M cells at 8192.

The finding, measured and recorded in the batch scratch: the massif SHRANK. Land above
100 m goes 14.9% -> 4.8%, above 220 m 4.5% -> 0.6%. A feather sweep to the practical
floor recovers ~1.3 points, so this is structural, not a tuning miss: the staircase's
highland area was an artifact of the bench and plateau acting as magnets, and a curve
with no magnets preserves the raw distribution's bottom-heavy shape. "No terraces" and
"the same land up high" are not both available from curve work alone.

Exploration batch, not convergence. A tuning pass follows once a direction is picked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCWNaDZPfTiAy3meGNGgqt
2026-08-20 03:41:27 -04:00
..
Scripts Phase 2a: continuous grade — smooth the upper staircase, preserve the lowlands 2026-08-20 03:41:27 -04:00
README.md Phase 2a: the faithful redistribution curve, re-measured against v2's own output 2026-08-20 01:38:10 -04:00

Core — math and data only

Holds: the column model, the material schemas, the scaling discipline, tooling path resolution and the file-safety rails. Constants and contracts.

Boundary: Core depends on nothing above it. Not on Server/, not on Client/, not on Tools/. The dependency arrow points one way into this folder and never out of it.

⚠ Additional boundary, tighter than the layer rule: Core is engine-free.

No using Godot; anywhere in this folder. Core is plain C# — no Node, no GD.Print, no ProjectSettings, no engine types in any signature.

Two reasons, both load-bearing. (1) D-049 commits to "C# for fast iteration, with C++-ready seams… port path-by-path when settled and hot" — the hot paths that get ported first are exactly the ones in here, and a type that names Godot.Vector2 cannot cross that seam. (2) It keeps the column model testable and reviewable without standing up an engine.

Where Core genuinely needs something the engine knows — the resolved user:// directory — the engine layer hands it in (ToolingPaths.Configure), rather than Core reaching for it. If you find yourself wanting an engine type here, the type belongs one layer up.

What is in here

File What it is
Scripts/Column.cs ★★ The load-bearing contract (D-053). A column as run-length bands; MaterialRun; RunOrigin. Read its header before touching anything that stores world data.
Scripts/ColumnGrid.cs The 2D grid of columns. Shape only — chunking and streaming are later phases.
Scripts/MaterialKey.cs Material identity — a registry key, never an ordinal.
Scripts/TerrainMaterial.cs A row in the terrain schema (D-054).
Scripts/BuildingMaterial.cs A row in the building schema (D-054).
Scripts/MaterialRegistry.cs Both registries, append-only, seed rows only.
Scripts/RecipeRegistry.cs The transformation seam — deliberately empty.
Scripts/GenerationScale.cs The scaling discipline. MapSize, ScaleFactor, normalized offsets, the two frequency conventions.
Scripts/WorldScale.cs The vertical yardstick. The ONE metres↔raw conversion (251 m/unit). No literal 251f anywhere else.
Scripts/HeightCurve.cs The height-redistribution curve (v5), 7 bands. Identity at and below sea.
Scripts/CurveKnots.cs The six INPUT knots — percentiles of the measured land CDF, plus the reference's for comparison.
Scripts/CurveAnchors.cs The OUTPUT anchors — the storm-ladder elevations each band lands at.
Scripts/TerrainDetailPass.cs Shelf micro-relief + the shelf-edge knot warp. Output-height only.
Scripts/ToolingPaths.cs Every tooling path, env-overridable, resolved in one place.
Scripts/FileSafety.cs The permanent file-safety rules, as throws rather than sentences.

The three rules this layer exists to make unbreakable

  1. Air is the top run. There is no "air vs solid" height branch, anywhere, ever. → Scripts/Column.cs.
  2. Properties come from the registry, never from storage. A run stores an identity. → Scripts/MaterialRegistry.cs.
  3. Nothing uses a raw pixel number. Every distance is a fraction of MapSize. → Scripts/GenerationScale.cs.
  4. There is one metres-per-raw-unit number. The prototype scattered 251 across three duplicate constants and ~20 literals, and the generator never used the derived one at all. → Scripts/WorldScale.cs.

Not here, and not by accident

  • No water. Water is an overlay over the columns (levels-not-cells), never a band.
  • No biomes. Biomes are a later classification of finished shape, not an input to it (D-049).
  • No algorithms beyond the height curve. Phase 2 added HeightCurve and TerrainDetailPass here because they are pure, engine-free, C++-candidate math that defines the world's elevation profile — a contract, not a tool's dial. Stratigraphy, feature passes, meshing and run-splitting on dig are still later phases.
  • No erosion, rivers, water bodies, crater carve, coast shelf or offshore islets. Later chat2 tasks; the curve is deliberately the only pass-2 element present.

Design - Data - Column Model.md, Design - Data - Material Schema.md, Design - Tooling - Scaling Discipline.md