magicciv/.project/objectives/p1-48-flora-species-renderer.md
Natalie 1e2f3ce082 feat(@projects): integrate wasm ecology module
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-01 02:29:55 -04:00

4.3 KiB
Raw Blame History

id title priority status scope owner updated_at wave canonical_doc coordinates_with evidence
p1-48 Flora species renderer — bind 149 species to world-map tile rendering (single source of truth) p1 done game1 terraformer 2026-05-01 C public/games/age-of-dwarves/docs/ECOLOGY_BINDING.md
p1-46
p1-47
p2-49
p2-50
src/simulator/crates/mc-ecology/src/flora_select.rs
src/simulator/crates/mc-ecology/tests/flora_selection.rs
src/game/engine/scenes/tests/fauna_render/fauna_render_proof.tscn

Summary

public/resources/ecology/flora/species/*.json defines 149 species with rich schema:

  • biomes[] — which terrain types support the species
  • tags[] including layer (layer_canopy / layer_understory / layer_ground / layer_fungal) and structure (structure_woody / conifer / broadleaf / evergreen)
  • lineage — taxonomic group (conifers, broadleaf_trees, tropical_broadleaf, cacti, palms, aquatic_plants, mosses_lichens, etc.)
  • quality_tier (010), canopy_contribution, undergrowth_contribution, fungi_contribution
  • drought_tolerance, fire_resistance, growth_rate

Wave C landed the Rust selector, WASM + GDExt bridges, and integration tests. The visual-proof bullets (tooltip, lab integration) are Wave-E work — relocated to p1-46. The TS twin (floraSpecies.ts) was deleted; Lab.tsx has TODO(p1-46) markers where the WASM calls will be wired in.

Single source of truth (Rail 1): the selector is implemented ONCE in mc-ecology (Rust), exposed to Godot via GDExtension and to the design-lab via WASM. NO TypeScript twin.

Acceptance

  • Species pool builder — module src/simulator/crates/mc-ecology/src/flora_select.rs builds TerrainFloraIndex keyed by (biome_id, T_band, P_band). Note: module path is flora_select.rs, not species.rs (which is reserved for the fauna Species struct). Objective path corrected per Wave-C doc-↔-code parity (Wave closing checklist §3).
  • Per-tile species selection — deterministic pick via mc_mapgen::seed::derive(map_seed, SeedDomain::FloraSelect) mixed with tile_rng(domain_seed, col, row). Same tile yields same species set across loads.
  • Contribution normalisation rule — weights rescaled by dividing by sum, never clipped. Implemented in pick_flora_for_tile.
  • Riparian feedback — tiles with riparian_distance <= 1 receive 1.4× weight boost for aquatic/riparian species. Verified by flora_selection::riparian_species_appear_near_water test.
  • Lineage → glyph mapping — documented in ECOLOGY_BINDING.md §5; FloraSpec::layer() returns FloraLayer enum carrying the layer tag. Glyph dispatch lives in the presentation shell as per spec.
  • Layer stratificationpick_flora_for_tile sorts output: canopy → understory → ground → fungal.
  • WASM ↔ GDExt parityWasmFloraIndex (api-wasm) and GdFloraSelector (api-gdext) both call pick_flora_for_tile with identical parameters. Both return {id, name, lineage, layer, quality_tier} per species. Parity is structural (same Rust function, same inputs → same outputs).
  • Determinism gatemc-ecology/tests/flora_selection.rs: 6 tests including determinism golden-vector (10 seeds × 5 biomes) pass.
  • Soil-layer acknowledgement — module rustdoc in flora_select.rs documents g2-06 retrofit point.
  • Tooltip integration — world-map tile tooltip shows top-3 flora species names per stratum (canopy / understory / ground), deduped and sorted by quality_tier desc. Delegated to p1-46 (Wave E) and now landed: WasmFloraIndex.tileFloraJson() wired into Lab.tsx info card. Evidence: .project/designs/app/src/pages/WorldGen/Lab.tsx (centreFlora memo + EcoSection render).

Non-goals

  • Per-species animation / sway — static icons only.
  • Successional drift over turns — one-shot pick at map gen, not dynamic.
  • Custom sprite assets per species — procedural canvas glyphs only.
  • Soil/lithology-driven species filtering — defer to g2-06.
  • TypeScript reimplementation of the selector — explicitly forbidden.

Dependencies

  • p2-49 (climate axes) — T_band, P_band keys depend on derived (T, P) being stable.
  • p1-47 (hydrology) — riparian_distance is consumed here.
  • p2-50 (RNG pin) — selection determinism.