Files
systemic-game-design-course/course/04-interceptors/scenarios.md
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 <noreply@anthropic.com>
2026-10-29 02:54:37 +01:00

5.1 KiB

Module 4 scenarios: Interceptors and "trigger twice"

Each heading is a test name. The Rust test lives in tests/rust/test_04.rs and the TypeScript test in tests/ts/test_04.test.ts; both state exactly what is written here. Player 0 is the first player, player 1 the second. Every minion in these tests belongs to player 0 and is summoned in the order listed, so the first one listed has the lowest order of play and stands furthest left. Each test calls clearLog() after its setup, so log() holds only the events of the action under test. Every test starts with newGame and a deck of 5 Test Filler for player 0.

Cards used (text from course/catalog.json):

  • Baron Rivendare, 1/7: "Your minions trigger their Deathrattles twice."
  • Loot Hoarder, 2/1: "Deathrattle: Draw a card."
  • Harvest Golem, 2/3: "Deathrattle: Summon a 2/1 Damaged Golem."
  • Damaged Golem, 2/1, no text. Only Harvest Golem creates it.
  • Novice Engineer, 1/1: "Battlecry: Draw a card."
  • Whirlwind, spell: "Deal 1 damage to ALL minions."
  • Twisting Nether, spell: "Destroy all minions and locations." The course has no locations, so it destroys all minions (the Module 3 Expansion Drop card).
  • Test Filler (fictional), 1/1, no text. Used to fill decks.

A trigger multiplier is an aura: data on a card that says "triggers of this kind, belonging to my owner's minions, activate N times". The set of active multipliers is read at two points only: when a minion is summoned, and in the Aura Update (Other) that follows each Death Creation Step. A queued trigger reads that set when it starts to resolve and then activates that many times in a row, recording Triggered{source, trigger} for each activation, before the next queued trigger starts. Baron Rivendare's multiplier does not stack: two Barons double, they do not quadruple.

test_04_baron_doubles

  • Setup: Summon Baron Rivendare (id B), then Loot Hoarder (id H), then Harvest Golem (id G). dealDamage(G, 2), which leaves G at 1 Health.
  • Action: player 0 casts Whirlwind.
  • Assertions:
    • hand(0) has 2 cards; call them C1 and C2, in hand order. board(0) has 3 minions: B, then D1, then D2.
    • The log is exactly, in this order: SpellCast{card: "Whirlwind"}, Damaged{target: B, amount: 1}, Damaged{target: H, amount: 1}, Damaged{target: G, amount: 1}, Destroyed{id: H}, Destroyed{id: G}, DeathPhase{round: 1}, Triggered{source: H, trigger: "deathrattle"}, Drew{player: 0, id: C1}, Triggered{source: H, trigger: "deathrattle"}, Drew{player: 0, id: C2}, Triggered{source: G, trigger: "deathrattle"}, Summoned{id: D1}, Triggered{source: G, trigger: "deathrattle"}, Summoned{id: D2}. Each Deathrattle is queued once and activates twice back to back: the Hoarder's two activations both finish before the Golem's first one starts.
    • nameOf(D1) and nameOf(D2) are "Damaged Golem".
    • health(B) is 6: Baron survives the Whirlwind, so his aura is still active when the Death Phase starts.

test_04_simultaneous_death_not_doubled

  • Setup: Summon Baron Rivendare (id B), then Loot Hoarder (id H).
  • Action: player 0 casts Twisting Nether.
  • Assertions:
    • hand(0) has exactly 1 card.
    • The log contains exactly one Triggered{source: H, trigger: "deathrattle"}.
    • zoneOf(B) and zoneOf(H) are graveyard.
    • Baron and the Hoarder leave play in the same Death Creation Step. The Aura Update (Other) after that step finds no Baron in play, so the Hoarder's Deathrattle activates once.

test_04_two_barons_do_not_stack

  • Setup: Summon Baron Rivendare (id B1), then another Baron Rivendare (id B2), then Loot Hoarder (id H).
  • Action: dealDamage(H, 1).
  • Assertions:
    • hand(0) has 2 cards; call them C1 and C2, in hand order.
    • The log is exactly, in this order: Damaged{target: H, amount: 1}, Destroyed{id: H}, DeathPhase{round: 1}, Triggered{source: H, trigger: "deathrattle"}, Drew{player: 0, id: C1}, Triggered{source: H, trigger: "deathrattle"}, Drew{player: 0, id: C2}. Two Barons give two activations, not three or four.

test_04_aura_timing_order

  • Setup: Summon Loot Hoarder (id H), then Baron Rivendare (id B). This time the Hoarder is older than Baron.
  • Action: player 0 casts Twisting Nether.
  • Assertions:
    • hand(0) has exactly 1 card; call it C.
    • The log is exactly, in this order: SpellCast{card: "Twisting Nether"}, Destroyed{id: H}, Destroyed{id: B}, DeathPhase{round: 1}, Triggered{source: H, trigger: "deathrattle"}, Drew{player: 0, id: C}. The Hoarder's Death Event is created before Baron's, while Baron is still on the board, but Destroyed{id: B} comes before the Hoarder's only Triggered: the multiplier is read after the whole Death Creation Step, in the Aura Update (Other), not while each Death Event is created.

test_04_battlecry_on_play

  • Setup: addToHand("Novice Engineer", 0) (id E).
  • Action: play(E).
  • Assertions:
    • hand(0) has exactly 1 card; call it C.
    • The log is exactly, in this order: Summoned{id: E}, Triggered{source: E, trigger: "battlecry"}, Drew{player: 0, id: C}. Playing a minion from hand puts it on the board, then its Battlecry resolves.
    • zoneOf(E) is board and board(0) is [E].