Walls and doors fall to sustained assault

"It is possible, though time-consuming, to punch a wall down. A wall
takes 20 points of damage to destroy; a door takes 15. Any attack
against an inanimate object counts as your one attack for the turn."
Damage accumulates per edge in wallDamage (remapped through sector
rotations, public in the view), fed two ways: a punchWall command for
the bare-fisted (1 point, from a square touching the edge) and attack
spells cast at an edge target — LOS to the wall for L.O.S. cards,
touching it for same-square cards, amplify and power-attack honored,
wand charges spent, no counteractions since stonework plays none.
Thrown daggers and rocks clatter to the floor at the foot of the
wall. At the threshold the edge opens through the same override path
destroy-wall uses.

On the table: damaged walls wear spreading cracks, an attack card's
hint offers "or a wall line to batter it" with the edge layer live,
and a "Punch a wall…" stamp arms a click-the-wall mode. The chronicle
counts the blows: "alice batters the wall with bare fists — 3/20."

Not deployed — a live game is in progress.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 12:48:49 -04:00
co-authored by Claude Fable 5
parent 64454ffc3e
commit 41c384274d
6 changed files with 220 additions and 4 deletions
+20 -3
View File
@@ -24,6 +24,8 @@
let peekCard = $state<CardInstance | null>(null);
/** When the peeked card is a creature on the board, its live stats ride along. */
let peekCreatureId = $state<string | null>(null);
/** Bare-knuckle demolition: click a wall to punch it. */
let punchWallMode = $state(false);
let helpTab = $state<"play" | "rules" | "cards" | "about" | "tally">("play");
let hotseatCount = $state(2);
let setupName = $state("");
@@ -105,7 +107,12 @@
const MODIFIER_CARDS = new Set(["amplify", "add", "extend", "around-the-corner"]);
const NAMED_CARDS = new Set(["card-erasure", "drop-object", "deja-vu", "thief", "swap-meet", "remove-curse", "swarthmores-enchantment", "illusionary-attack"]);
const edgeSelectMode = $derived(selectedCard != null && EDGE_CARDS.has(selectedCard.cardId));
const attackVsWall = $derived(
selectedCard != null && cardDef(selectedCard.cardId).cardType === "attack" && !EDGE_CARDS.has(selectedCard.cardId),
);
const edgeSelectMode = $derived(
(selectedCard != null && EDGE_CARDS.has(selectedCard.cardId)) || attackVsWall || punchWallMode,
);
const cellSelectMode = $derived(
(selectedCard != null && CELL_CARDS.has(selectedCard.cardId)) || pendingCellFor !== null,
);
@@ -117,6 +124,7 @@
}
function clearSelection() {
punchWallMode = false;
ambushVia = null;
ambushTrigger = null;
ambushSpell = null;
@@ -461,11 +469,17 @@
}
function clickEdge(cell: { x: number; y: number }, side: Side) {
if (punchWallMode) {
dispatch({ type: "punchWall", cell, side });
punchWallMode = false;
return;
}
if (!selectedCard || !edgeSelectMode) return;
dispatch({
type: "cast",
instanceId: selectedCard.instanceId,
target: { kind: "edge", cell, side },
...(attackVsWall && attachedNumber ? { numberInstanceIds: [attachedNumber.instanceId] } : {}),
});
clearSelection();
}
@@ -1105,8 +1119,8 @@
{#if selectedDef}
<strong class="hint-name">{selectedDef.name}</strong>
{#if edgeSelectMode}<span>— click a wall line</span>{/if}
{#if selectedDef.cardType === "attack" && !edgeSelectMode && !cellSelectMode}
<span>— click a target{attachedNumber ? ` (powered by a ${numberTotal})` : " (tap a number card to power it)"}</span>
{#if selectedDef.cardType === "attack" && !cellSelectMode && !EDGE_CARDS.has(selectedCard?.cardId ?? "")}
<span>— click a target, or a wall line to batter it{attachedNumber ? ` (powered by a ${numberTotal})` : ""}</span>
{/if}
{#if attachedMods.length > 0}
<span class="hint-mods">[+ {attachedMods.map((m) => cardDef(m.cardId).name).join(", ")}]</span>
@@ -1183,6 +1197,9 @@
onclick={() => dispatch({ type: "pickUpObject", instanceId: obj.instanceId })}>
Pick up {cardDef(obj.cardId).name}</button>
{/each}
<button class="stamp" class:primary={punchWallMode} disabled={view.turn.attackUsed && !punchWallMode}
onclick={() => (punchWallMode = !punchWallMode)}>
{punchWallMode ? "Click the wall to punch — or cancel" : "Punch a wall…"}</button>
<button class="stamp" onclick={startDiscardMode}>Discard cards</button>
<label class="inline draw-pick">
draw