From e7b17f764334c93057a98f216171b8c7426549bc Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Sun, 16 Aug 2026 11:17:07 -0400 Subject: [PATCH] The finished table lays every hand face-up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once the game is won, the view stops keeping secrets: revealedHands carries every player's cards, and the table replaces your hand row with a face-up gallery — each wizard's name (trophy on the winner) beside their final hand, tap any card to enlarge, "empty-handed" written in script for those who died with nothing. In hotseat this is the thing the winner passes around after dismissing the fanfare; in async games it greets each player when they open the finished game. Redaction still guards every phase before "finished." Co-Authored-By: Claude Fable 5 --- packages/engine/src/view.ts | 5 +++ packages/web/src/App.svelte | 83 ++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/packages/engine/src/view.ts b/packages/engine/src/view.ts index 2dbe8fd..597210f 100644 --- a/packages/engine/src/view.ts +++ b/packages/engine/src/view.ts @@ -67,6 +67,8 @@ export interface GameView { outOfTurnWindow: { playerId: PlayerId; kind: "interrupt" | "opportunity-fire" } | null; /** YOUR armed ambushes. Other players' ambushes are invisible. */ yourAmbushes: AmbushState[]; + /** Once the game is finished, every hand goes face-up on the table. */ + revealedHands: Record | null; } export function viewFor(state: GameState, playerId: PlayerId): GameView { @@ -107,6 +109,9 @@ export function viewFor(state: GameState, playerId: PlayerId): GameView { displayed: p.hand.filter((c) => p.displayed.includes(c.instanceId)), })), yourHand: you ? [...you.hand] : [], + revealedHands: state.phase === "finished" + ? Object.fromEntries(state.players.map((p) => [p.id, [...p.hand]])) + : null, treasures: state.treasures.map((t) => ({ ...t })), deckCount: state.deck.length, discardCount: state.discard.length, diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 4d2abd3..ca84da6 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -1212,8 +1212,29 @@ {/if} + {#if view.phase === "finished" && view.revealedHands} +
+
The hands, face-up
+ {#each view.players as p (p.id)} +
+ + {p.id === view.winner ? "🏆 " : ""}{p.id} + +
+ {#each view.revealedHands[p.id] ?? [] as card (card.instanceId)} + (peekCard = card)} /> + {/each} + {#if (view.revealedHands[p.id] ?? []).length === 0} + empty-handed + {/if} +
+
+ {/each} +
+ {/if} +
- {#each view.yourHand as card (card.instanceId)} + {#each view.phase === "finished" ? [] : view.yourHand as card (card.instanceId)}