| 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) |
|
|
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