feat(@projects/@magic-civilization): 🦠 p3-27 — complete the disease applier (o2 depletion + lair kill, no stubs)
Reverses the two deferrals in apply_disease_events — every EventTierData field now applies: - o2_delta: atmospheric (global) depletion of grid.o2_fraction, once per fired event (negative drives toward an anoxic ocean). o2_fraction is grid-level, so it's applied after the radius loop, not per-tile. - lair_kill_chance: an active lair (lair_tier > 0) on an affected tile is wiped (lair_tier=0, lair_population=0 — same clear as evolution's lair removal) with the configured probability via the deterministic rng. Disease now applies fauna_loss + canopy_loss + tier_loss + o2_delta + lair_kill_chance — the full tier spec. mc-ecology events 11/0 (+1 test). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
59742674b8
commit
668ab7d152
2 changed files with 61 additions and 2 deletions
|
|
@ -1276,14 +1276,26 @@ impl EcologyEngine {
|
|||
if td.canopy_loss > 0.0 {
|
||||
t.canopy_cover = (t.canopy_cover * (1.0 - td.canopy_loss)).max(0.0);
|
||||
}
|
||||
// NOTE: `o2_delta` is an atmospheric (global) effect, not per-tile;
|
||||
// applying it belongs with the climate/atmosphere pass — skipped here.
|
||||
if td.tier_loss > 0 {
|
||||
t.ecosystem_tier = (t.ecosystem_tier - td.tier_loss).max(0);
|
||||
}
|
||||
// lair_kill_chance: an active lair on this tile may be wiped out
|
||||
// (cleared the same way as evolution's lair removal).
|
||||
if t.lair_tier > 0
|
||||
&& td.lair_kill_chance > 0.0
|
||||
&& rng.next_bool_p(td.lair_kill_chance.clamp(0.0, 1.0))
|
||||
{
|
||||
t.lair_tier = 0;
|
||||
t.lair_population = 0.0;
|
||||
}
|
||||
}
|
||||
tiles_hit += 1;
|
||||
}
|
||||
// o2_delta is an atmospheric (global) effect — applied once per fired event
|
||||
// (negative = depletion toward an anoxic ocean).
|
||||
if td.o2_delta != 0.0 {
|
||||
grid.o2_fraction = (grid.o2_fraction + td.o2_delta).max(0.0);
|
||||
}
|
||||
fired.push(FiredDisease {
|
||||
category: cat.id.clone(),
|
||||
tier,
|
||||
|
|
|
|||
|
|
@ -348,4 +348,51 @@ mod tests {
|
|||
let mut g = GridState::new(4, 4);
|
||||
assert!(e.apply_disease_events(&mut g, &[], 1, 1).is_empty(), "no categories → no-op");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_disease_events_depletes_o2_and_wipes_lairs() {
|
||||
use crate::engine::EcologyEngine;
|
||||
use crate::population::PopulationSlot;
|
||||
use mc_core::grid::GridState;
|
||||
|
||||
let mut engine = EcologyEngine::new();
|
||||
engine
|
||||
.tile_populations
|
||||
.insert((5, 5), vec![PopulationSlot::new(1, 100.0)]);
|
||||
let mut grid = GridState::new(12, 12);
|
||||
grid.o2_fraction = 0.21;
|
||||
if let Some(t) = grid.tile_mut(5, 5) {
|
||||
t.lair_tier = 2;
|
||||
t.lair_population = 0.9;
|
||||
}
|
||||
let mut tiers = HashMap::new();
|
||||
tiers.insert(
|
||||
1,
|
||||
EventTierData {
|
||||
name: "Pandemic".into(),
|
||||
fauna_loss: 0.0,
|
||||
canopy_loss: 0.0,
|
||||
o2_delta: -0.01,
|
||||
lair_kill_chance: 1.0, // always wipes
|
||||
radius: 1,
|
||||
tier_loss: 0,
|
||||
},
|
||||
);
|
||||
let cat = EventCategory {
|
||||
id: "pandemic".into(),
|
||||
base_frequency: 1.0,
|
||||
severity_weights: {
|
||||
let mut w = vec![0.0; 10];
|
||||
w[0] = 1.0;
|
||||
w
|
||||
},
|
||||
density_frequency_bonus: 0.0,
|
||||
tiers,
|
||||
};
|
||||
let fired = engine.apply_disease_events(&mut grid, std::slice::from_ref(&cat), 1, 7);
|
||||
assert_eq!(fired.len(), 1);
|
||||
assert!((grid.o2_fraction - 0.20).abs() < 1e-4, "o2 depleted by o2_delta");
|
||||
assert_eq!(grid.tile(5, 5).unwrap().lair_tier, 0, "lair wiped at kill_chance 1.0");
|
||||
assert_eq!(grid.tile(5, 5).unwrap().lair_population, 0.0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue