Files
systemic-game-design-course/course/03-death-step/tests/ts/test_03.test.ts
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 <[email protected]>
2026-10-29 02:42:12 +01:00

114 lines
3.6 KiB
TypeScript

// Module 3: Deferred death. Each test matches a heading in scenarios.md.
import { expect, test } from "vitest";
import { newGame } from "../src/harness";
const fillers = (n: number): string[] => Array(n).fill("Test Filler");
test("test_03_removed_only_after_sequence", () => {
const g = newGame({ decks: [fillers(5), []] });
const h = g.summon("Loot Hoarder", 0);
const s = g.summon("Armorsmith", 0);
g.clearLog();
g.cast("Whirlwind", 0);
const hand = g.hand(0);
expect(hand).toHaveLength(1);
const c = hand[0];
expect(g.log()).toEqual([
{ type: "SpellCast", card: "Whirlwind" },
{ type: "Damaged", target: h, amount: 1 },
{ type: "Damaged", target: s, amount: 1 },
{ type: "Triggered", source: s, trigger: "damaged" },
{ type: "ArmorGained", player: 0, amount: 1 },
{ type: "Triggered", source: s, trigger: "damaged" },
{ type: "ArmorGained", player: 0, amount: 1 },
{ type: "Destroyed", id: h },
{ type: "DeathPhase", round: 1 },
{ type: "Triggered", source: h, trigger: "deathrattle" },
{ type: "Drew", player: 0, id: c },
]);
expect(g.zoneOf(h)).toBe("graveyard");
expect(g.board(0)).toEqual([s]);
expect(g.armor(0)).toBe(2);
});
test("test_03_chained_death_phases", () => {
const g = newGame();
const golem = g.summon("Harvest Golem", 0);
const ghoul = g.summon("Unstable Ghoul", 0);
const r = g.summon("Test Reactor", 0);
g.dealDamage(golem, 2);
g.clearLog();
g.dealDamage(ghoul, 3);
const d = g.board(0)[0];
expect(g.log()).toEqual([
{ type: "Damaged", target: ghoul, amount: 3 },
{ type: "Destroyed", id: ghoul },
{ type: "DeathPhase", round: 1 },
{ type: "Triggered", source: ghoul, trigger: "deathrattle" },
{ type: "Damaged", target: golem, amount: 1 },
{ type: "Damaged", target: r, amount: 1 },
{ type: "Destroyed", id: golem },
{ type: "DeathPhase", round: 2 },
{ type: "Triggered", source: golem, trigger: "deathrattle" },
{ type: "Summoned", id: d },
]);
expect(g.nameOf(d)).toBe("Damaged Golem");
expect(g.board(0)).toEqual([d, r]);
expect(g.zoneOf(ghoul)).toBe("graveyard");
expect(g.zoneOf(golem)).toBe("graveyard");
expect(g.health(r)).toBe(4);
});
test("test_03_simultaneous_removal", () => {
const g = newGame({ decks: [fillers(5), []] });
const h1 = g.summon("Loot Hoarder", 0);
const h2 = g.summon("Loot Hoarder", 0);
g.clearLog();
g.cast("Whirlwind", 0);
const hand = g.hand(0);
expect(hand).toHaveLength(2);
const [c1, c2] = hand;
expect(g.log()).toEqual([
{ type: "SpellCast", card: "Whirlwind" },
{ type: "Damaged", target: h1, amount: 1 },
{ type: "Damaged", target: h2, amount: 1 },
{ type: "Destroyed", id: h1 },
{ type: "Destroyed", id: h2 },
{ type: "DeathPhase", round: 1 },
{ type: "Triggered", source: h1, trigger: "deathrattle" },
{ type: "Drew", player: 0, id: c1 },
{ type: "Triggered", source: h2, trigger: "deathrattle" },
{ type: "Drew", player: 0, id: c2 },
]);
expect(g.zoneOf(h1)).toBe("graveyard");
expect(g.zoneOf(h2)).toBe("graveyard");
expect(g.board(0)).toEqual([]);
});
test("test_03_destroy_marks_pending", () => {
const g = newGame();
const golem = g.summon("Harvest Golem", 0);
g.clearLog();
g.destroy(golem);
const board = g.board(0);
expect(board).toHaveLength(1);
const d = board[0];
expect(g.log()).toEqual([
{ type: "Destroyed", id: golem },
{ type: "DeathPhase", round: 1 },
{ type: "Triggered", source: golem, trigger: "deathrattle" },
{ type: "Summoned", id: d },
]);
expect(g.health(golem)).toBe(3);
expect(g.zoneOf(golem)).toBe("graveyard");
expect(g.nameOf(d)).toBe("Damaged Golem");
});