The die rolls in the open

Every player-facing D4 roll now lands in the chronicle as pips with
its purpose: "🎲 alice rolls a 3 — aiming at the unseen — only a 1
finds them." A shared rollD4 helper threads the generic dieRolled
event through blind staggers and swings, pit leaps, ooze and pit
struggles, and the invisible/shrink miss rolls — the table sees the
die land, exactly as it would in person. Rolls whose numbers already
showed (the troll's swing, the misdirection direction, the opening
roll-off) keep their lines.

And the physical die's other job — "any 50-50 call" — gets its
button: 🎲 beside the say-box in online games (server-rolled,
published through the chat ledger so it persists, replays, and counts
toward unread badges) and under the chronicle in hotseat, rolled on
the device. Chronicle only; no game state touched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 15:57:28 -04:00
co-authored by Claude Fable 5
parent d6d34887e3
commit ef661c79ad
6 changed files with 221 additions and 17 deletions
+5
View File
@@ -145,6 +145,7 @@ export function humanize(e: GameEvent): string | null {
case "objectPickedUp": return `${e.player} picks up the ${cardDef(e.card.cardId).name} — actions over.`;
case "wardSet": return e.armed ? "Your ward is set — the next thief bleeds." : "Your ward stands down.";
case "chaosShielded": return `${e.player} raises a FULL SHIELD and sits out the chaos.`;
case "dieRolled": return `\u{1F3B2} ${e.player ?? "The maze"} rolls a ${e.roll}${e.purpose}.`;
case "pushed": return e.player
? `${e.by} shoves ${e.player} down the corridor!`
: `${e.by} shoves the beast down the corridor!`;
@@ -428,6 +429,10 @@ class Net {
}
/** Ask the server how all our games are doing. */
rollTableDie(): void {
this.send({ type: "rollDie" });
}
sendChat(text: string): void {
this.send({ type: "chat", text });
}