Deferred death: damage and destroy only mark minions; the Death Creation Step removes them when the outermost Phase ends, and Death Phases resolve Deathrattles in order of play until no new deaths occur. Deathrattle summons use the board position the Death Event remembered. Harness adds destroy(target). Expansion Drop: Twisting Nether as content only. test_02_mortally_wounded_still_triggers no longer asserts the Acolyte is still on the board: from Module 3 on it leaves at the end of the Sequence, and verify-references runs Module 2's tests against the Module 3 reference. Co-Authored-By: Claude Opus 5.5 <[email protected]>
158 lines
4.2 KiB
Rust
158 lines
4.2 KiB
Rust
// Module 3: Deferred death. Each test matches a heading in scenarios.md.
|
|
use engine::harness::*;
|
|
|
|
fn fillers(n: usize) -> Vec<String> {
|
|
vec!["Test Filler".to_string(); n]
|
|
}
|
|
|
|
fn damaged(target: Id, amount: i32) -> GameEvent {
|
|
GameEvent::Damaged { target, amount }
|
|
}
|
|
|
|
fn triggered(source: Id, trigger: &str) -> GameEvent {
|
|
GameEvent::Triggered {
|
|
source,
|
|
trigger: trigger.to_string(),
|
|
}
|
|
}
|
|
|
|
fn whirlwind() -> GameEvent {
|
|
GameEvent::SpellCast {
|
|
card: "Whirlwind".to_string(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_03_removed_only_after_sequence() {
|
|
let mut g = new_game(GameOpts {
|
|
decks: Some([fillers(5), vec![]]),
|
|
});
|
|
let h = g.summon("Loot Hoarder", 0);
|
|
let s = g.summon("Armorsmith", 0);
|
|
g.clear_log();
|
|
|
|
g.cast("Whirlwind", 0, None);
|
|
|
|
let hand = g.hand(0);
|
|
assert_eq!(hand.len(), 1);
|
|
let c = hand[0];
|
|
assert_eq!(
|
|
g.log(),
|
|
vec![
|
|
whirlwind(),
|
|
damaged(h, 1),
|
|
damaged(s, 1),
|
|
triggered(s, "damaged"),
|
|
GameEvent::ArmorGained {
|
|
player: 0,
|
|
amount: 1
|
|
},
|
|
triggered(s, "damaged"),
|
|
GameEvent::ArmorGained {
|
|
player: 0,
|
|
amount: 1
|
|
},
|
|
GameEvent::Destroyed { id: h },
|
|
GameEvent::DeathPhase { round: 1 },
|
|
triggered(h, "deathrattle"),
|
|
GameEvent::Drew { player: 0, id: c },
|
|
]
|
|
);
|
|
assert_eq!(g.zone_of(h), Zone::Graveyard);
|
|
assert_eq!(g.board(0), vec![s]);
|
|
assert_eq!(g.armor(0), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_03_chained_death_phases() {
|
|
let mut g = new_game(GameOpts::default());
|
|
let golem = g.summon("Harvest Golem", 0);
|
|
let ghoul = g.summon("Unstable Ghoul", 0);
|
|
let r = g.summon("Test Reactor", 0);
|
|
g.deal_damage(golem, 2);
|
|
g.clear_log();
|
|
|
|
g.deal_damage(ghoul, 3);
|
|
|
|
let d = g.board(0)[0];
|
|
assert_eq!(
|
|
g.log(),
|
|
vec![
|
|
damaged(ghoul, 3),
|
|
GameEvent::Destroyed { id: ghoul },
|
|
GameEvent::DeathPhase { round: 1 },
|
|
triggered(ghoul, "deathrattle"),
|
|
damaged(golem, 1),
|
|
damaged(r, 1),
|
|
GameEvent::Destroyed { id: golem },
|
|
GameEvent::DeathPhase { round: 2 },
|
|
triggered(golem, "deathrattle"),
|
|
GameEvent::Summoned { id: d },
|
|
]
|
|
);
|
|
assert_eq!(g.name_of(d), "Damaged Golem");
|
|
assert_eq!(g.board(0), vec![d, r]);
|
|
assert_eq!(g.zone_of(ghoul), Zone::Graveyard);
|
|
assert_eq!(g.zone_of(golem), Zone::Graveyard);
|
|
assert_eq!(g.health(r), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_03_simultaneous_removal() {
|
|
let mut g = new_game(GameOpts {
|
|
decks: Some([fillers(5), vec![]]),
|
|
});
|
|
let h1 = g.summon("Loot Hoarder", 0);
|
|
let h2 = g.summon("Loot Hoarder", 0);
|
|
g.clear_log();
|
|
|
|
g.cast("Whirlwind", 0, None);
|
|
|
|
let hand = g.hand(0);
|
|
assert_eq!(hand.len(), 2);
|
|
let (c1, c2) = (hand[0], hand[1]);
|
|
assert_eq!(
|
|
g.log(),
|
|
vec![
|
|
whirlwind(),
|
|
damaged(h1, 1),
|
|
damaged(h2, 1),
|
|
GameEvent::Destroyed { id: h1 },
|
|
GameEvent::Destroyed { id: h2 },
|
|
GameEvent::DeathPhase { round: 1 },
|
|
triggered(h1, "deathrattle"),
|
|
GameEvent::Drew { player: 0, id: c1 },
|
|
triggered(h2, "deathrattle"),
|
|
GameEvent::Drew { player: 0, id: c2 },
|
|
]
|
|
);
|
|
assert_eq!(g.zone_of(h1), Zone::Graveyard);
|
|
assert_eq!(g.zone_of(h2), Zone::Graveyard);
|
|
assert_eq!(g.board(0), Vec::<Id>::new());
|
|
}
|
|
|
|
#[test]
|
|
fn test_03_destroy_marks_pending() {
|
|
let mut g = new_game(GameOpts::default());
|
|
let golem = g.summon("Harvest Golem", 0);
|
|
g.clear_log();
|
|
|
|
g.destroy(golem);
|
|
|
|
let board = g.board(0);
|
|
assert_eq!(board.len(), 1);
|
|
let d = board[0];
|
|
assert_eq!(
|
|
g.log(),
|
|
vec![
|
|
GameEvent::Destroyed { id: golem },
|
|
GameEvent::DeathPhase { round: 1 },
|
|
triggered(golem, "deathrattle"),
|
|
GameEvent::Summoned { id: d },
|
|
]
|
|
);
|
|
assert_eq!(g.health(golem), 3);
|
|
assert_eq!(g.zone_of(golem), Zone::Graveyard);
|
|
assert_eq!(g.name_of(d), "Damaged Golem");
|
|
}
|