Waves spend their force as they travel (rev 24); bots wield Destroy Wall

Rules rev 24: a waterwall wave that finds a victim dist cells from its
source has only range-dist spaces of push left — a range-2 wave throws its
adjacent victim two spaces but a victim at its far edge only one, and
spent force never converts into crush damage. Applies to WATERWALL and
both STONE TO WATER waves; older revisions keep the flat full-range wash
so stored games replay unchanged.

Automatons now use DESTROY WALL on the march: two BFS distance maps (from
the bot, from its objectives) price every visible wall by the shortcut its
removal opens; the bot blasts when it saves 4+ steps of walking — or when
no road exists at all — never standing beside the blast unless trapped
and healthy. The card also leaves the shed pile: discardValue 2 -> 6.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
Eric Wagoner
2026-08-17 19:40:41 -04:00
co-authored by Claude Fable 5
parent dfd7a2ff56
commit 2d53ba4bc0
6 changed files with 215 additions and 27 deletions
+16 -4
View File
@@ -2476,6 +2476,16 @@ function terrainEffect(kind: SquareContent["kind"]): NeutralEffect {
};
}
/**
* How hard a wave still pushes someone it finds `dist` cells from its
* source: the force spent reaching them is gone (rules rev 24), so a
* range-2 wave throws its adjacent victim 2 spaces but a victim at its far
* edge only 1 — and never converts spent force into crush damage.
*/
function waveForce(state: GameState, range: number, dist: number): number {
return (state.config.deckRev ?? 1) >= 24 ? range - dist : range;
}
/** A collapsing waterwall wave from an edge: wash players back `range`. */
function waveFromEdge(state: GameState, events: GameEvent[], cell: Cell, side: Side, range: number, reason = "rushing water"): void {
const away = (s2: Side): Side => (s2 === "N" ? "S" : s2 === "S" ? "N" : s2 === "E" ? "W" : "E");
@@ -2486,15 +2496,16 @@ function waveFromEdge(state: GameState, events: GameEvent[], cell: Cell, side: S
for (const { start, dir } of pushes) {
let probe = start;
for (let dist = 0; dist < range; dist++) {
const force = waveForce(state, range, dist);
for (const p of state.players) {
if (p.alive && cellKey(p.position) === cellKey(probe)) washBackN(state, events, p, dir, range);
if (p.alive && cellKey(p.position) === cellKey(probe)) washBackN(state, events, p, dir, force);
}
for (const c of [...state.creatures]) {
if (cellKey(c.position) !== cellKey(probe)) continue;
if (c.kind === "fire-imp") {
destroyCreature(state, events, c, reason);
} else if ((state.config.deckRev ?? 1) >= 17) {
washBackCreature(state, events, c, dir, range);
washBackCreature(state, events, c, dir, force);
}
}
if (state.squareContents[cellKey(probe)]?.kind === "slime") {
@@ -2513,15 +2524,16 @@ function waveFromCell(state: GameState, events: GameEvent[], center: Cell, range
let probe = center;
for (let dist = 0; dist < range; dist++) {
probe = neighbor(probe, dir);
const force = waveForce(state, range, dist);
for (const p of state.players) {
if (p.alive && cellKey(p.position) === cellKey(probe)) washBackN(state, events, p, dir, range);
if (p.alive && cellKey(p.position) === cellKey(probe)) washBackN(state, events, p, dir, force);
}
for (const c of [...state.creatures]) {
if (cellKey(c.position) !== cellKey(probe)) continue;
if (c.kind === "fire-imp") {
destroyCreature(state, events, c, "rushing water");
} else if ((state.config.deckRev ?? 1) >= 17) {
washBackCreature(state, events, c, dir, range);
washBackCreature(state, events, c, dir, force);
}
}
}