magicciv/.project/objectives/p3-13c-biological-events.md
Natalie 35181826c4 feat(@projects/@magic-civilization): update pioneer engineer action-point pool status
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-13 12:17:05 -07:00

7 KiB
Raw Blame History

id title priority status scope owner updated_at evidence blocked_by
p3-13c Biological events — plague, bloom, migration_pulse p3 done game1 unassigned 2026-05-13
src/simulator/crates/mc-ecology/src/biological.rs:42-65 (typed BiologicalEvent enum: Plague | Bloom | MigrationPulse)
src/simulator/crates/mc-ecology/src/biological.rs:218-410 (derive_biological_events pure derivation, AXIAL_DIRECTIONS scan, det_roll splitmix)
src/simulator/crates/mc-ecology/src/biological.rs:454-560 (4 bloom/plague/migration acceptance tests including test_bloom_requires_consecutive_turns streak gate)
src/simulator/crates/mc-ecology/src/lib.rs:45 (pub use advance_bloom_streak, derive_biological_events, BiologicalEvent, BiologicalThresholds)
public/games/age-of-dwarves/data/balance/biological_events.json (tunable thresholds, EVENT_FREQUENCY_SPEC plague target 0.0025/turn, bloom.streak_min=3)
cargo test -p mc-ecology --lib: 324 passed (313 pre-existing + 7 new biological); cargo test -p mc-sim --lib: 8 passed
src/simulator/crates/mc-ecology/src/biological.rs:340-370 (plague adjacency spread second pass, HashSet dedup, channel 14)
src/simulator/crates/mc-ecology/src/biological.rs:283-337 (migration chain walk, source_pop held constant, max_hops cap)
public/games/age-of-dwarves/data/balance/biological_events.json (plague.spread_factor, plague.spread_severity_scale, migration.max_hops, bloom.streak_min added)
src/simulator/crates/mc-sim/src/event_dispatch.rs:73-95 — dispatch_world_events calls advance_bloom_streak (mutable) before derive_biological_events each turn; Plague events routed through apply_damage (Water channel) into eco_map; Bloom/MigrationPulse chronicle-only.
cargo test -p mc-sim p3_13_event_dispatch_biological_plague_applies_water_damage: PASS
src/simulator/crates/mc-core/src/grid/mod.rs:284-291 (TileState::bloom_streak: u8 with #[serde(default)] for save backward-compat)
src/simulator/crates/mc-ecology/src/biological.rs:205-232 (advance_bloom_streak — pure per-turn increment/reset of TileState::bloom_streak; called by mc-sim::dispatch_world_events before derive)
src/simulator/crates/mc-ecology/src/biological.rs:504-561 (test_bloom_requires_consecutive_turns — streak<min suppresses bloom, streak>=min fires, window break resets streak to 0)

Context

public/games/age-of-dwarves/docs/ECOLOGY_BINDING.md describes biological dynamics riding on the flora/fauna indexes. This objective adds three turn-derived biological events: plague (pop-density × low-medical), bloom (favourable climate window → flora yield spike), migration_pulse (fauna density wave through corridors).

Acceptance

  • mc-ecology::derive_biological_events(grid, thresholds, turn, seed) -> Vec<BiologicalEvent> returns Plague { col,row,severity }, Bloom { col,row,intensity }, MigrationPulse { from_col,from_row,to_col,to_row,magnitude }. Signature differs from the spec (turn,world,players): tile-only signals per Out-of-scope §; players deferred. src/simulator/crates/mc-ecology/src/biological.rs:175-260.
  • ✓ Typed BiologicalEvent enum lives in mc-ecology::biological (re-exported from crate root) rather than mc-core::events — sibling crates mc-climate::weather::WeatherEvent follow the same in-domain pattern, no mc-core::events module exists. src/simulator/crates/mc-ecology/src/biological.rs:42-65 + src/simulator/crates/mc-ecology/src/lib.rs:36-43.
  • ✓ Plague per-city pop-density × inverse-medical-buildings + adjacent-city spread. Tile proxy: civilization_presence × low quality. Adjacency spread second pass (lines 327-357): for each primary plague source, each of 6 axial neighbours receives a spread Plague event if civilization_presence ≥ plague_civ_min * plague_spread_factor and quality ≤ plague_quality_max; spread severity = source × plague_spread_severity_scale; deduped via HashSet so primary-infected tiles are never double-counted. test_plague_spreads_to_adjacent_cities covers the cluster and severity-attenuation assertions.
  • ✓ Bloom "N consecutive turns" optimal window. Streak implemented (p3-13c closure, 2026-05-13): TileState::bloom_streak: u8 added to mc-core::grid::TileState with #[serde(default)] for save backward-compat (src/simulator/crates/mc-core/src/grid/mod.rs:284-291). mc_ecology::biological::advance_bloom_streak(grid, thresholds) (biological.rs:205-232) increments per-tile when the climate window holds (mean_temp ∈ [temp_min,temp_max], mean_precip ≥ precip_min, canopy_cover ≥ canopy_min, undergrowth ≥ undergrowth_min) and resets to 0 otherwise; saturates at u8::MAX. Bloom emission gates on tile.bloom_streak >= thresholds.bloom_streak_min (default 3, configurable via JSON bloom.streak_min). Wired into mc-sim::event_dispatch::dispatch_world_events immediately before derive_biological_events each turn (event_dispatch.rs:73-95). test_bloom_requires_consecutive_turns verifies streak<min suppresses bloom across turns 1+2, streak≥min fires on turn 3, and window-break resets streak to 0.
  • ✓ Migration pulse along corridor with per-tile fauna-density check. Multi-hop chain walk (lines 270-324): from each qualifying source (≥ migration_source_min), attempts up to migration_max_hops single-turn hops; each hop picks the first axial neighbour with lair_population ≤ migration_neighbour_max and (source_pop - neighbour_pop) ≥ migration_differential_min; source_pop held constant across the chain so the wave propagates through depleted corridor tiles without diminishing; emits one MigrationPulse per hop. test_migration_multi_hop_corridor covers 2-hop corridor and max_hops cap.
  • cargo test -p mc-ecology green: test_plague_spreads_via_density, test_bloom_requires_optimal_window, test_migration_pulse_traverses_corridor all pass; suite total 317 (313 pre-existing + 4 new incl. thresholds_load_from_spec_json). Run from src/simulator/: cargo test -p mc-ecology --lib. src/simulator/crates/mc-ecology/src/biological.rs:280-410.

Source-of-truth rails

  • Rust crate: mc-ecology::biological owns event derivation. ✓
  • JSON path: spec said public/resources/ecology/biological/_config.json; landed at public/games/age-of-dwarves/data/balance/biological_events.json per closing-task directive (the data/balance/ dir is the established home for tunable thresholds — EVENT_FREQUENCY_SPEC.md cross-refs balance-style files; public/resources/ecology/{flora,fauna}/ remains species data).
  • Enum location: mc-ecology::biological::BiologicalEvent (typed; not in mc-core — sibling mc-climate::weather::WeatherEvent precedent). No EventSeverity reuse: p3-13a's WeatherEvent encodes severity inline as f32, so we follow that convention.

Out of scope

  • Cross-realm plague diplomacy effects — separate ticket.
  • New species authoring.
  • Player remediation (medical buildings effect) tuning.

References

  • public/games/age-of-dwarves/docs/ECOLOGY_BINDING.md
  • Sibling: p3-13a, p3-13b, p3-13d