Files
systemic-game-design-course/course/03-death-step/scenarios.md
T
Lucas WintherandClaude Opus 5.5 8a4eba1948 feat: module 03 death step
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 <noreply@anthropic.com>
2026-10-29 02:42:12 +01:00

5.0 KiB

Module 3 scenarios: Deferred death

Each heading is a test name. The Rust test lives in tests/rust/test_03.rs and the TypeScript test in tests/ts/test_03.test.ts; both state exactly what is written here. Player 0 is the first player, player 1 the second. Every minion in these tests is summoned for player 0, 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.

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

  • 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.
  • Unstable Ghoul, 1/3: "Taunt. Deathrattle: Deal 1 damage to all minions." Taunt has no effect in these tests.
  • Armorsmith, 1/4: "Whenever a friendly minion takes damage, gain 1 Armor."
  • Test Reactor (fictional), 1/5: "Whenever a hero takes damage, gain +1 Attack." No hero takes damage in these tests, so it never triggers; it is here to stand on the board.
  • Whirlwind, spell: "Deal 1 damage to ALL minions."
  • Test Filler (fictional), 1/1, no text. Used to fill decks.

A minion is removed from play only in the Death Creation Step, which runs when the Sequence's outermost Phase ends. The step removes every minion at 0 or less Health and every minion marked by a destroy effect, all at once, and records Destroyed{id} for each in order of play. If it removed anything, a Death Phase follows: it records DeathPhase{round} (1 for the first Death Phase of the Sequence, 2 for the next, and so on) and resolves the Deathrattles of the removed minions in order of play. When the Death Phase ends, the step runs again, until it removes nothing.

test_03_removed_only_after_sequence

  • Setup: newGame with a deck of 5 Test Filler for player 0. Summon Loot Hoarder (id H), then Armorsmith (id S).
  • Action: player 0 casts Whirlwind.
  • Assertions:
    • The log is exactly, in this order: SpellCast{card: "Whirlwind"}, Damaged{target: H, amount: 1}, Damaged{target: S, amount: 1}, Triggered{source: S, trigger: "damaged"}, ArmorGained{player: 0, amount: 1}, Triggered{source: S, trigger: "damaged"}, ArmorGained{player: 0, amount: 1}, Destroyed{id: H}, DeathPhase{round: 1}, Triggered{source: H, trigger: "deathrattle"}, Drew{player: 0, id: C}, where C is the only card in hand(0). H is at 0 Health from the first Damaged event, but it is removed only after every reaction to the Whirlwind has resolved.
    • zoneOf(H) is graveyard.
    • board(0) is [S].
    • armor(0) is 2.

test_03_chained_death_phases

  • Setup: an empty game. Summon Harvest Golem (id G), then Unstable Ghoul (id U), then Test Reactor (id R). dealDamage(G, 2), which leaves G at 1 Health.
  • Action: dealDamage(U, 3).
  • Assertions:
    • The log is exactly, in this order: Damaged{target: U, amount: 3}, Destroyed{id: U}, DeathPhase{round: 1}, Triggered{source: U, trigger: "deathrattle"}, Damaged{target: G, amount: 1}, Damaged{target: R, amount: 1}, Destroyed{id: G}, DeathPhase{round: 2}, Triggered{source: G, trigger: "deathrattle"}, Summoned{id: D}, where D is the first id in board(0). The Ghoul's Deathrattle does not hit the Ghoul: it has already left play. Its damage leaves G at 0 Health, and G is removed by the second Death Creation Step, after the first Death Phase has ended.
    • nameOf(D) is "Damaged Golem".
    • board(0) is [D, R]: the Damaged Golem takes the board position G had when it died, left of R, not the right end.
    • zoneOf(U) and zoneOf(G) are graveyard.
    • health(R) is 4.

test_03_simultaneous_removal

  • Setup: newGame with a deck of 5 Test Filler for player 0. Summon Loot Hoarder (id H1), then another Loot Hoarder (id H2).
  • Action: player 0 casts Whirlwind.
  • Assertions:
    • hand(0) has 2 cards; call them C1 and C2, in hand order.
    • The log is exactly, in this order: SpellCast{card: "Whirlwind"}, Damaged{target: H1, amount: 1}, Damaged{target: H2, amount: 1}, Destroyed{id: H1}, Destroyed{id: H2}, DeathPhase{round: 1}, Triggered{source: H1, trigger: "deathrattle"}, Drew{player: 0, id: C1}, Triggered{source: H2, trigger: "deathrattle"}, Drew{player: 0, id: C2}. Both minions leave play before either Deathrattle resolves, and the Deathrattles resolve in order of play, in one Death Phase.
    • zoneOf(H1) and zoneOf(H2) are graveyard, and board(0) is empty.

test_03_destroy_marks_pending

  • Setup: an empty game. Summon Harvest Golem (id G).
  • Action: destroy(G).
  • Assertions:
    • The log is exactly, in this order: Destroyed{id: G}, DeathPhase{round: 1}, Triggered{source: G, trigger: "deathrattle"}, Summoned{id: D}, where D is the only id in board(0). A destroy effect deals no damage and does not remove the minion itself: it marks G, and the Death Creation Step removes it like any other death.
    • health(G) is 3: G was at full Health when it was removed.
    • zoneOf(G) is graveyard.
    • nameOf(D) is "Damaged Golem".