Rev 21: collapsing walls crush anyone — monsters included

DESTROY WALL's "anyone in either square next to the wall takes 4
points" swept only wizards; a troll leaned on the falling masonry
untouched. The expansion sheet is explicit — "if a card specifies
'Opponent' or 'Anyone' as a target, this includes Monsters" — so the
collapse now crushes creatures in both squares, at the rim breach's
far side too. Earlier ledgers keep their charmed monsters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-17 17:47:14 -04:00
co-authored by Claude Fable 5
parent 6380fae134
commit 7df58d29fa
4 changed files with 61 additions and 2 deletions
+16
View File
@@ -1108,6 +1108,13 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
applyDamage(state, events, p, 4, "collapsing wall", caster.id, "physical");
}
}
if ((state.config.deckRev ?? 1) >= 21) {
for (const cr of [...state.creatures]) {
if (cellKey(cr.position) === cellKey(far.cell)) {
damageCreature(state, events, cr, 4, "collapsing wall");
}
}
}
state.board.warps.push(
{ from: { cell, side }, to: { cell: far.cell, side: far.side } },
{ from: { cell: far.cell, side: far.side }, to: { cell, side } },
@@ -1121,6 +1128,15 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
applyDamage(state, events, p, 4, "collapsing wall", caster.id, "physical");
}
}
// "Anyone in either square" includes monsters — the expansion sheet
// says so in as many words (rules rev 21).
if ((state.config.deckRev ?? 1) >= 21) {
for (const cr of [...state.creatures]) {
if (cellKey(cr.position) === cellKey(c)) {
damageCreature(state, events, cr, 4, "collapsing wall");
}
}
}
}
checkVictory(state, events);
return null;
+43
View File
@@ -640,3 +640,46 @@ describe("a mid-round democratic monster claws on the next turn (rules rev 18)",
expect(r.state.stack?.creatureId ?? null).toBe(fresh.id);
});
});
describe("collapsing walls crush monsters too (rules rev 21)", () => {
function wallRig(deckRev: number) {
let { state } = createGame({ playerIds: ["a", "b"], seed: 42, sets: ["basic", "expansion1"], deckRev });
state = toRound2(state);
const view = boardView(state);
for (const [key, edge] of Object.entries(view.edges)) {
if (edge !== "wall") continue;
const [kind, coords] = key.split(":") as [string, string];
const [x, y] = coords.split(",").map(Number) as [number, number];
const side = kind === "V" ? ("E" as Side) : ("S" as Side);
const other = { x: x + (side === "E" ? 1 : 0), y: y + (side === "S" ? 1 : 0) };
if (!view.cells[cellKey(other)]) continue;
const caster = activePlayer(state);
caster.position = { x, y };
state.creatures.push({
id: "tr1", kind: "troll", controllerId: caster.id,
position: { ...other }, damage: 0, maxDamage: 6, movesPerTurn: 3,
movementUsed: 0, attackUsed: false, justCreated: false,
wallPassesPerTurn: 0, wallPassUsed: 0, scorchedThisTurn: [],
});
const dw = giveCard(state, caster.id, "destroy-wall");
const r = applyCommand(state, caster.id, {
type: "cast", instanceId: dw.instanceId, target: { kind: "edge", cell: { x, y }, side },
});
if (!r.ok) throw new Error(r.error);
return r.state;
}
throw new Error("setup: no interior wall found");
}
it("rev 21: the adjacent troll takes the four points", () => {
const state = wallRig(21);
const troll = state.creatures.find((c) => c.id === "tr1");
// Four points on a six-point troll: hurt but standing (or regenerating).
expect(troll?.damage).toBe(4);
});
it("earlier revisions leave the troll dusty but unharmed", () => {
const state = wallRig(20);
expect(state.creatures.find((c) => c.id === "tr1")!.damage).toBe(0);
});
});
+1 -1
View File
@@ -54,7 +54,7 @@ export interface Room {
const rooms = new Map<string, Room>();
/** Rules revision new games are dealt under (stored games keep their own). */
const RULES_REV = 20;
const RULES_REV = 21;
const ROOM_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
+1 -1
View File
@@ -136,7 +136,7 @@ class LocalGame {
seed,
sets: expansion ? ["basic", "expansion1"] : ["basic"],
...(colors ? { colors } : {}),
deckRev: 20,
deckRev: 21,
};
const { state, events } = createGame(config);
for (const e of events) {