magicciv/.project/objectives/p3-10c-lair-raid-mode.md
Natalie 1e97b908e5 feat(@projects/@magic-civilization): add tier gap ai quality objective
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-07 02:36:25 -07:00

3.3 KiB

id title priority status scope owner updated_at evidence blocked_by
p3-10c Lair raid mode — grab-and-exit p3 done game1 combat-dev 2026-05-07
src/simulator/crates/mc-combat/src/lair.rs::resolve_raid
src/simulator/crates/mc-combat/tests/lair_raid.rs (4 tests passed)
src/simulator/crates/mc-core/src/lair.rs:223 (RaidOutcome enum)
public/games/age-of-dwarves/data/balance/lair_combat.json
public/resources/ecology/fauna/lair_combat_modes.json (raid_loot_fraction added)
src/simulator/api-gdext/src/lib.rs::GdTurnProcessor::resolve_raid_on_lair (cycle 47)
src/simulator/api-gdext/src/lib.rs::GdTurnProcessor::tick_raid_aftermath (cycle 47)
src/simulator/crates/mc-turn/src/game_state.rs::GameState.raid_aftermath field (cycle 47)
cargo check --workspace: Finished dev profile, 0 errors (cycle 47)
p3-10a

Context

Per public/games/age-of-dwarves/docs/combat/LAIRS.md, Raid mode is a single-turn opportunistic strike: the attacker enters, takes a small loot fraction, and attempts to disengage on the same turn. Higher chance of partial success but lower total reward; lair remains active and may pursue.

Acceptance

  • mc-combat::lair::resolve_raid(...) -> RaidResult returns LootGrabbed { amount, escaped } / Caught { losses } / Aborted via the RaidOutcome enum at src/simulator/crates/mc-core/src/lair.rs:223-239. Implementation: src/simulator/crates/mc-combat/src/lair.rs::resolve_raid.
  • ✓ Loot fraction per lair from RaidTuning::raid_loot_fraction (default 0.30), authored in public/games/age-of-dwarves/data/balance/lair_combat.json::raid.raid_loot_fraction and public/resources/ecology/fauna/lair_combat_modes.json::combat_modes[raid].raid_loot_fraction.
  • ✓ Lair retaliation: on Caught, RaidResult.pursuit returns a RaidAftermath with pursuing_until_turn = current_turn + tuning.pursuit_turns. Verified by test_raid_repulsed_when_attacker_loses in src/simulator/crates/mc-combat/tests/lair_raid.rs.
  • cargo test -p mc-combat --test lair_raid green: 4 passed (test_raid_partial_loot_on_success, test_raid_lair_persists, test_raid_attacker_keeps_movement, test_raid_repulsed_when_attacker_loses).
  • ✓ GDExt bridge: GdTurnProcessor::resolve_raid_on_lair(state, lair_col, lair_row, attacker_hp, attacker_attack, lair_tier, lair_counter_attack, attacker_id, turn_seed) -> Dictionary at src/simulator/api-gdext/src/lib.rs. Returns {outcome, loot_amount, escaped, raider_damage, pursuing_until_turn}. On Caught, writes RaidAftermath into state.raid_aftermath[(col,row)]. tick_raid_aftermath(state) expiry-prunes the map each turn. mc-turn::GameState.raid_aftermath: BTreeMap<(u16,u16), RaidAftermath> added with raid_aftermath_as_pairs serde adapter (mirrors siege_pressure pattern). cargo check --workspace clean (cycle 47).

Source-of-truth rails

  • Rust crate: mc-combat::lair owns raid math + retaliation flag.
  • JSON path: public/resources/lairs/*.json + public/resources/lairs/_config.json.
  • mc-core wrapper: RaidOutcome typed sum.

Out of scope

  • Assault mode — p3-10a.
  • Siege mode — p3-10b.
  • AI strategic decision of which mode to pick — separate AI ticket.

References

  • public/games/age-of-dwarves/docs/combat/LAIRS.md
  • Parent: p3-10a
  • Sibling: p3-10b