Rev 25: a bush or the ooze is attacked like a wall — POWER ATTACK counts, thrown weapons land at its foot

Older games took neither; both sides pinned.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
Eric Wagoner
2026-09-22 12:19:15 -04:00
co-authored by Claude Fable 5.1
parent 66b5c06f17
commit 5193b12b8e
2 changed files with 52 additions and 3 deletions
+20 -2
View File
@@ -247,7 +247,7 @@ export interface CastParams {
}
/** The revision new games are dealt under; GameConfig.deckRev pins it per game. */
export const CURRENT_RULES_REV = 24;
export const CURRENT_RULES_REV = 25;
/** Every rulings revision since the baseline, newest last — the entries a
* game's deckRev freezes it before or after. Shown to players as the house
@@ -276,6 +276,7 @@ export const RULES_REVISIONS: { rev: number; note: string }[] = [
{ rev: 22, note: "TELEPORT ignores the maze's outer edge as it ignores any wall: a teleporter leaving the maze at any square's edge re-enters at the opposite edge on the same line, one space on. Older games crossed the edge only at the lettered openings." },
{ rev: 23, note: "POWER DRAIN drains the number played: the caster gains it whether the blow is BLUNTed or ABSORBed (FAQ: the counter blunts the damage done, not the drain), and a wall drained for its points gives them up too. Older games gave the caster only what the opponent lost, and nothing from a wall." },
{ rev: 24, note: "A REFLECTION against a permanent curse afflicts both wizards: WALKING DEAD and IDIOT say so on the card, and SLOW DEATH follows REFLECTION's own rule that a spell works for both parties. Older games left the caster untouched." },
{ rev: 25, note: "An attack on a bush or the ooze is an attack on an inanimate object like any wall: POWER ATTACK's bought points count, and a thrown DAGGER or LARGE ROCK lands at the foot of the bush instead of vanishing into the discard. Older games took neither." },
];
export interface GameConfig {
@@ -5551,13 +5552,22 @@ function doCast(prev: GameState, cmd: Extract<Command, { type: "cast" }>): Comma
if (content.kind === "ooze" && inHand.cardId !== "fireball") {
return err("only fire hurts the ooze");
}
const dmg = effect.baseDamage(mods.magnitude.numberValue, cmd.params ?? null) * (2 ** mods.amplifies.length);
// Rev 25: an inanimate object like any wall — POWER ATTACK's bought
// points count, and thrown weapons land at its foot.
const likeAWall = (state.config.deckRev ?? 1) >= 25;
const dmg =
effect.baseDamage(mods.magnitude.numberValue, cmd.params ?? null) * (2 ** mods.amplifies.length) +
(likeAWall ? mods.powerAttackPoints : 0);
if (dmg <= 0) return err(`that spell would not singe the ${what}`);
const wandEvents: GameEvent[] = [];
{
const werr = spendWandCharge(state, caster, wandEvents);
if (werr) return err(werr);
}
if (likeAWall && mods.powerAttackPoints > 0) {
caster.life -= mods.powerAttackPoints;
wandEvents.push({ type: "lifeTraded", player: caster.id, points: mods.powerAttackPoints, newAllowance: state.turn.movementAllowance });
}
consumeCast(state, caster, inHand, mods, effect.keepInHand ?? false);
if (state.turn.attackUsed) state.turn.secondAttackUsed = true;
state.turn.attackUsed = true;
@@ -5573,6 +5583,14 @@ function doCast(prev: GameState, cmd: Extract<Command, { type: "cast" }>): Comma
delete state.squareContents[cellKey(cell)];
events.push({ type: "squareContentDestroyed", attacker: caster.id, cell: { ...cell }, kind: content.kind });
}
if (likeAWall && (inHand.cardId === "dagger" || inHand.cardId === "large-rock")) {
const di = state.discard.findIndex((c) => c.instanceId === inHand.instanceId);
if (di !== -1) {
const [card] = state.discard.splice(di, 1);
state.groundObjects[cellKey(cell)] = [...(state.groundObjects[cellKey(cell)] ?? []), card!];
events.push({ type: "objectThrown", attacker: caster.id, cardId: inHand.cardId, landedAt: cell });
}
}
return { ok: true, state, events };
}
}