From 028ff813f35bb4cc89260f9abe33d688ad26f949 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Mon, 17 Aug 2026 17:10:06 -0400 Subject: [PATCH] Amplify announces itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Room 9BBV's "impossible" 14-point powerthrust was an archmage playing correctly: 2 + 5, AMPLIFIED. The engine's math was honest and its narration was not — neither the log line nor the attack modal mentioned the amplify, so a legal kill read as a bug. The spellCast event now carries its amplify count, the chronicle says "casts Powerthrust with a 5, AMPLIFIED, at Kestrel", and the modal's power line shows "powered by a 5 — AMPLIFIED ×2" while there is still time to respect it. Co-Authored-By: Claude Fable 5 --- packages/engine/src/game.ts | 4 +++- packages/web/src/App.svelte | 7 +++++-- packages/web/src/net.svelte.ts | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/engine/src/game.ts b/packages/engine/src/game.ts index c4670fa..1c33f5f 100644 --- a/packages/engine/src/game.ts +++ b/packages/engine/src/game.ts @@ -483,7 +483,8 @@ export type GameEvent = | { type: "moved"; player: PlayerId; from: Cell; to: Cell; direction: Side; via: "step" | "warp" | "passWall" } | { type: "numberPlayedForMovement"; player: PlayerId; card: CardInstance; value: number; newAllowance: number } | { type: "punched"; attacker: PlayerId; target: PlayerId; at: Cell } - | { type: "spellCast"; caster: PlayerId; card: CardInstance; cardId: string; numberCards: CardInstance[]; numberValue: number | null; from: Cell; target: PlayerId | null; targetCell: Cell | null } + | { type: "spellCast"; caster: PlayerId; card: CardInstance; cardId: string; numberCards: CardInstance[]; numberValue: number | null; from: Cell; target: PlayerId | null; targetCell: Cell | null; + /** AMPLIFY cards attached (each doubles). */ amplifies?: number } | { type: "counteractionPlayed"; player: PlayerId; card: CardInstance; cardId: string; against: string } | { type: "counterNullified"; player: PlayerId; card: CardInstance; by: CardInstance } | { type: "attackAbsorbedIntoHand"; player: PlayerId; attackCard: CardInstance } @@ -4496,6 +4497,7 @@ function doCast(prev: GameState, cmd: Extract): Comma cardId: inHand.cardId, numberCards: mods.numbers, numberValue: mods.magnitude.numberValue, + amplifies: mods.amplifies.length, from: caster.position, target: target.id, targetCell: target.position, diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 97a21d1..cbe1c65 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -1091,8 +1091,11 @@ {:else}
{/if} - {#if view.stack.numberValue != null} -
powered by a {view.stack.numberValue}
+ {#if view.stack.numberValue != null || view.stack.amplifyFactor > 1} +
+ {view.stack.numberValue != null ? `powered by a ${view.stack.numberValue}` : ""} + {view.stack.amplifyFactor > 1 ? ` — AMPLIFIED ×${view.stack.amplifyFactor}` : ""} +
{/if} {:else if view.stack.defenderId === view.you}
👊 a bare-knuckled punch
diff --git a/packages/web/src/net.svelte.ts b/packages/web/src/net.svelte.ts index 7c185c3..d50430b 100644 --- a/packages/web/src/net.svelte.ts +++ b/packages/web/src/net.svelte.ts @@ -39,8 +39,9 @@ export function humanize(e: GameEvent): string | null { case "punched": return `${e.attacker} punches ${e.target}!`; case "spellCast": { const num = e.numberValue ? ` with a ${e.numberValue}` : ""; + const amp = e.amplifies ? `, AMPLIFIED${e.amplifies > 1 ? ` ×${2 ** e.amplifies}` : ""},` : ""; const at = e.target ? ` at ${e.target}` : ""; - return `${e.caster} casts ${cardDef(e.cardId).name}${num}${at}.`; + return `${e.caster} casts ${cardDef(e.cardId).name}${num}${amp}${at}.`; } case "counteractionPlayed": return `${e.player} counters with ${cardDef(e.cardId).name}!`; case "counterNullified": return `${cardDef(e.card.cardId).name} is nullified by Anti-Anti!`;