P13: dice — crit/fumble animation + roll visibility

- Crit/fumble emphasis: RollTray (and the Dice page result) highlight a nat-20 /
  critical-success as "✦ Critical ✦" (gold pop + glow) and a nat-1 / critical-
  failure as "✦ Fumble ✦" (red shake). naturalD20() exported from notation.
- DM sees players' rolls: RollFeed now also renders on the Combat page (players'
  rolls already broadcast to the GM).
- Players see the DM's public rolls: new gmRoll message — a hosting GM's rolls
  (Dice page + rollAndShow) broadcast to the table as "GM".
- DM secret-roll toggle (rollStore.secret) on the Dice page suppresses the broadcast.
- Realtime e2e: GM public roll appears in the player's table feed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 16:35:48 +02:00
parent d4ae60f199
commit d94df41a95
12 changed files with 129 additions and 25 deletions
+9
View File
@@ -184,6 +184,15 @@ export class RoomHub {
for (const p of room.players) if (p !== socket) p.send(msg);
}
/** The GM broadcasts one of their own (public) rolls to the players. */
gmRoll(socket: Sender, gmSecret: string, label: string, expression: string, total: number, breakdown: string): void {
const room = this.gmRoom(socket, gmSecret);
if (!room) return;
room.lastActivity = this.now();
const msg = { t: 'rollBroadcast', playerName: 'GM', label, expression, total, breakdown } as const;
for (const p of room.players) p.send(msg); // players only; the GM already sees it locally
}
disconnect(socket: Sender): void {
const conn = this.conns.get(socket);
if (!conn) return;