Files
systemic-game-design-course/course/04-interceptors/tests/rust/test_04.rs
T
Lucas WintherandClaude Opus 5.5 bcdaca0cd2 feat: module 04 interceptors
Trigger multipliers as aura data on cards, snapshotted by an Aura Update
(Other) after each Death Creation Step and on summon, and applied by one
generic routine (activations) when each queued trigger starts. Baron
Rivendare is content only (stacking "max"); Brann Bronzebeard is the
content-only Expansion Drop. Adds play (now required) and Battlecry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 02:54:37 +01:00

164 lines
4.2 KiB
Rust

// Module 4: Interceptors and "trigger twice". Each test matches a heading in scenarios.md.
use engine::harness::*;
fn game_with_deck() -> Game {
new_game(GameOpts {
decks: Some([vec!["Test Filler".to_string(); 5], vec![]]),
})
}
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 spell_cast(card: &str) -> GameEvent {
GameEvent::SpellCast {
card: card.to_string(),
}
}
#[test]
fn test_04_baron_doubles() {
let mut g = game_with_deck();
let b = g.summon("Baron Rivendare", 0);
let h = g.summon("Loot Hoarder", 0);
let golem = g.summon("Harvest Golem", 0);
g.deal_damage(golem, 2);
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]);
let board = g.board(0);
assert_eq!(board.len(), 3);
assert_eq!(board[0], b);
let (d1, d2) = (board[1], board[2]);
assert_eq!(
g.log(),
vec![
spell_cast("Whirlwind"),
damaged(b, 1),
damaged(h, 1),
damaged(golem, 1),
GameEvent::Destroyed { id: h },
GameEvent::Destroyed { id: golem },
GameEvent::DeathPhase { round: 1 },
triggered(h, "deathrattle"),
GameEvent::Drew { player: 0, id: c1 },
triggered(h, "deathrattle"),
GameEvent::Drew { player: 0, id: c2 },
triggered(golem, "deathrattle"),
GameEvent::Summoned { id: d1 },
triggered(golem, "deathrattle"),
GameEvent::Summoned { id: d2 },
]
);
assert_eq!(g.name_of(d1), "Damaged Golem");
assert_eq!(g.name_of(d2), "Damaged Golem");
assert_eq!(g.health(b), 6);
}
#[test]
fn test_04_simultaneous_death_not_doubled() {
let mut g = game_with_deck();
let b = g.summon("Baron Rivendare", 0);
let h = g.summon("Loot Hoarder", 0);
g.clear_log();
g.cast("Twisting Nether", 0, None);
assert_eq!(g.hand(0).len(), 1);
let hoarder_triggers = g
.log()
.into_iter()
.filter(|e| *e == triggered(h, "deathrattle"))
.count();
assert_eq!(hoarder_triggers, 1);
assert_eq!(g.zone_of(b), Zone::Graveyard);
assert_eq!(g.zone_of(h), Zone::Graveyard);
}
#[test]
fn test_04_two_barons_do_not_stack() {
let mut g = game_with_deck();
g.summon("Baron Rivendare", 0);
g.summon("Baron Rivendare", 0);
let h = g.summon("Loot Hoarder", 0);
g.clear_log();
g.deal_damage(h, 1);
let hand = g.hand(0);
assert_eq!(hand.len(), 2);
let (c1, c2) = (hand[0], hand[1]);
assert_eq!(
g.log(),
vec![
damaged(h, 1),
GameEvent::Destroyed { id: h },
GameEvent::DeathPhase { round: 1 },
triggered(h, "deathrattle"),
GameEvent::Drew { player: 0, id: c1 },
triggered(h, "deathrattle"),
GameEvent::Drew { player: 0, id: c2 },
]
);
}
#[test]
fn test_04_aura_timing_order() {
let mut g = game_with_deck();
let h = g.summon("Loot Hoarder", 0);
let b = g.summon("Baron Rivendare", 0);
g.clear_log();
g.cast("Twisting Nether", 0, None);
let hand = g.hand(0);
assert_eq!(hand.len(), 1);
let c = hand[0];
assert_eq!(
g.log(),
vec![
spell_cast("Twisting Nether"),
GameEvent::Destroyed { id: h },
GameEvent::Destroyed { id: b },
GameEvent::DeathPhase { round: 1 },
triggered(h, "deathrattle"),
GameEvent::Drew { player: 0, id: c },
]
);
}
#[test]
fn test_04_battlecry_on_play() {
let mut g = game_with_deck();
let e = g.add_to_hand("Novice Engineer", 0);
g.clear_log();
g.play(e);
let hand = g.hand(0);
assert_eq!(hand.len(), 1);
let c = hand[0];
assert_eq!(
g.log(),
vec![
GameEvent::Summoned { id: e },
triggered(e, "battlecry"),
GameEvent::Drew { player: 0, id: c },
]
);
assert_eq!(g.zone_of(e), Zone::Board);
assert_eq!(g.board(0), vec![e]);
}