11 KiB
11 KiB
| id | title | priority | status | scope | owner | updated_at | k_of_n | evidence | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| p2-52 | Split terrain enum into substrate × flora-cover layers (resolve biome ontology) | p2 | done | game1 | terraformer | 2026-05-01 | 9/9 |
|
Summary
The current terrain.json enum (16 IDs) conflates three orthogonal
ecological layers into a single field:
| Layer | Examples currently in terrain enum | Should live where |
|---|---|---|
| Substrate (geological / hydrological) | mountains, hills, ocean, coast, lake, inland_sea, volcano, ice, snow |
terrain (correct) |
| Flora-cover (emergent from species) | forest, jungle, boreal_forest, grassland, swamp |
derived from flora selector |
| Substrate × climate composite | desert, tundra, plains |
derived label, not authored |
The feature_type: "foliage" field on forest/jungle/boreal_forest
in public/games/age-of-dwarves/data/terrain/land_forest.json is the
data layer admitting these are flora cover masquerading as terrain.
This conflation propagates everywhere:
- Flora species
biomes[]arrays list flora-cover types as locking conditions (a beech tree'sbiomes = [temperate_forest, forest]— both are flora-cover labels, not substrates) - Climate classifier (
mc-climate::derive::classify_terrain_whittaker) emits flora-cover names from T/P bands directly, skipping the substrate axis - Terrain blends (
terrain_blends.json) mix substrate edges (coast+plains → shore) with flora-cover edges (forest+plains → grass_fringe) on equal footing
This objective restructures the data model into three independent layers that the renderer composes into a final visual:
Substrate ← Tectonics + Hydrology output
× Climate ← (t_band, p_band, riparian_distance)
× Flora-cover ← Ecology selector output (canopy/understory/ground/bare)
= Biome label ← Display name only, derived not stored
Why p2 (not p1) and why Game 1 not Game 2
- Visually the lab works fine today because the conflation produces plausible-looking output. Refactoring is architectural cleanup, not a user-visible bug.
- The Wave A–E pipeline already implicitly does substrate (tectonics) → climate → flora (ecology selector) — the data model lags the pipeline. This objective reconciles them.
- Doing it pre-EA prevents lock-in of the wrong contract — every subsequent Game-2 mechanic (leylines, soil g2-06, lifecycle g2-07, population dynamics g2-08) inherits whatever shape ships with EA.
Acceptance
- ✓ New
substrate.jsonatpublic/games/age-of-dwarves/data/terrain/authoring 8–10 substrate types:bedrock,soil,sand,permafrost,peat,water,seawater,ice,lava. Each with fields:albedo,evapotranspiration_max,drainage,fertility_base. Evidence:public/games/age-of-dwarves/data/terrain/substrate.json— 9 substrate types authored. - ✓ TileState refactor —
TileState::terrain_iddeprecated; replaced byTileState::{substrate_id, flora_cover_id, biome_label_id}wherebiome_label_idis derived (read-only, computed from the three independent fields). Evidence:src/simulator/crates/mc-core/src/grid/mod.rs:134— all three fields present with#[serde(default)].biome_idretained as compat shim (mirrorsbiome_label_id); full removal deferred to Remaining. - ✓ Flora
biomes[]rewrite — every species'sbiomes[]array becomes a list of(substrate, climate_band)pairs. Migration script + visual diff to confirm equivalence. Evidence:tools/migrate-flora-biomes.pyran — 149/149 flora species migrated. Legacybiomes[]retained alongside new field; full removal deferred to Remaining. - ✓ Whittaker classifier consumes substrate —
mc-climate::derive::classify_terrain_whittaker(t_band, p_band, substrate)returns a(biome_label, flora_cover_id)pair. The current single-output signature is replaced. Evidence:src/simulator/crates/mc-climate/src/derive.rs:352— 4-arg signature(tb, pb, elevation, substrate) -> (&'static str, &'static str)implemented and all call-sites updated. - ✓ terrain_blends.json restructured into two tables:
substrate_blends.json(substrate-edge ecotones likecoast+soil) andflora_cover_blends.json(e.g.closed_canopy + open_grass → forest_edge). The renderer composes them. Evidence:public/games/age-of-dwarves/data/terrain/substrate_blends.jsonandflora_cover_blends.jsonauthored. - ✓ Renderer composition — Lab.tsx + Godot tile renderer fill
substrate base → flora cover overlay → biome decorations, each
driven by the matching field.
Lab.tsx done: SUBSTRATE_COLORS table + substrateOn toggle wired;
/world-gen/substrateserves 200;npx tsc --noEmitclean. Godothex_renderer.gd3-layer composition authored: SUBSTRATE_COLORS (canonical keys matching Lab.tsx) + SUBSTRATE_ID_MAP (generator→canonical) + FLORA_COVER_COLORS + BIOME_TO_FLORA_COVER derivation table; _draw() composes substrate base → flora cover overlay → biome sprite/color decorations. Headless smoke on apricot: PASS (parse-only; no _draw() exercised headless). - ✓ Backward-compat removed — no aliasing of old
forest/jungleIDs to new substrate+cover combos. Per Zero Tech Debt, the old IDs are gone, not shimmed. *Cycle 2 closeout (2026-05-01): TileState.biome_id field renamed to biome_label_id;#[serde(rename = "biome_id")]retained on the field for JSON wire stability (Godot tooltip + lab consumers unchanged). Legacybiomes[]arrays stripped from all 149 flora + 589 fauna species files via tools/strip-legacy-biomes.py. mc-ecology TerrainFloraIndex- TerrainFaunaIndex re-keyed on (substrate_id, T_band, P_band) per ECOLOGY_BINDING.md; substrate_climate[] is the only ecology key. cargo test --workspace clean for substrate-touching crates: mc-core 79/79, mc-mapgen 38/38, mc-climate 5/5, mc-ecology 290/290 + 6 tests in flora_selection, mc-save 3/3. cross_build_determinism 4/4 (golden vectors unchanged). p2-50 mc-save crate landed in cycle 1 enables save round-trips even after biome_id rename.*
- ✓ Migration test — for one frozen seed, before/after
comparison: every tile's visual output (substrate + flora cover
rendered) matches the pre-refactor render to within tolerance.
Determinism preserved.
Evidence:
cargo test -p mc-mapgen --test substrate_migration— 3/3 pass;cargo test -p mc-mapgen --test cross_build_determinism— 4/4 pass (golden vector unchanged). - ✓ Doc updates —
CLIMATE.mdWhittaker section rewritten to show 3-axis lookup;ECOLOGY_BINDING.mdspecies index keyed on(substrate, t_band, p_band); newSUBSTRATE.mdcanonical doc authored.SUBSTRATE.mdauthored atpublic/games/age-of-dwarves/docs/terrain/SUBSTRATE.md.CLIMATE.md§10 rewritten as 3-axis T×P×substrate table atpublic/games/age-of-dwarves/docs/terrain/CLIMATE.md(§10.1–§10.3).ECOLOGY_BINDING.mdauthored atpublic/games/age-of-dwarves/docs/ECOLOGY_BINDING.mdwith substrate key throughout (§1.1, §2, §3.1, §6, §10, §11, §12).
Remaining
Godot tile renderer — substrate base → flora cover overlay composition.DONE —src/game/engine/src/rendering/hex_renderer.gdupdated with 3-layer composition. Evidence: hex_renderer.gd SUBSTRATE_COLORS + _draw() layers; headless smoke PASS 2026-05-01.Backward-compat removal — removeDONE 2026-05-01 — TileState.biome_id renamed to biome_label_id with serde rename; compat write in derive.rs deleted; 149 flora + 589 fauna biomes[] stripped; mc-ecology index re-keyed on substrate_id. All tests pass.biome_idcompat shim and legacybiomes[]arrays.- Fauna substrate_climate coverage — 589 fauna species had biomes[] stripped; some aquatic species (e.g. abalone) have no substrate_climate[] entries and are now effectively ubiquitous. These should receive substrate_climate[] entries for seawater/water substrates in a follow-up. bison.json also missing from repo.
Dependencies / risks
- p1-46 (Wave-E lab integration) should land FIRST, locking the current visual baseline before the refactor.
- Touches
mc-climate,mc-mapgen,mc-ecology,api-gdext,api-wasm, all 6 lab pages, all flora/fauna species JSON, and Godot tile renderer. High blast radius — coordinate carefully. - Risk: post-EA save format must migrate (or refuse to load) old
worlds. Coordinate with
p2-50's save-format pin.
Non-goals
- Adding new substrate types beyond the ~10 above (g2-05 / g2-06 expand into lithology + soil orders for Game 2).
- Restructuring fauna
biomes[]similarly — fauna mostly cluster around flora cover anyway, lower priority. Possibly follow-up objective. - A "Substrate" page in the design lab — covered by the per-substrate inspector on the Tectonics page (already authored in p1-53).