Phase 4: interactive dice & sheet rolling

- Dice notation: exploding (Nd6!) and reroll-below (Nd6r2), capped + tested
- Degrees of success / roll-vs-DC (PF2e ±10 + nat 20/1 steps; 5e crit on nat 20/1)
- Global roll tray (shared store + RollTray in layout) with animated result + degree
- Roll from the character sheet: skills, saves, and attack to-hit/damage are
  clickable and post to the tray + history (rollAndShow/rollCheck helpers)
- Saved roll macros per campaign (persisted) on the Dice page

10 new unit tests (notation explode/reroll, degrees), interactive-dice e2e.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 01:32:15 +02:00
parent 3e5bdc06e2
commit 9647e6b3d6
14 changed files with 431 additions and 16 deletions
+25
View File
@@ -0,0 +1,25 @@
import { create } from 'zustand';
import type { RollResult } from '@/lib/dice/notation';
import type { Degree } from '@/lib/dice/check';
export interface TrayRoll {
/** increments each roll, used as an animation key */
seq: number;
label: string;
result: RollResult;
dc?: number;
degree?: Degree;
}
interface RollState {
last: TrayRoll | null;
push: (roll: Omit<TrayRoll, 'seq'>) => void;
dismiss: () => void;
}
let seq = 0;
export const useRollStore = create<RollState>((set) => ({
last: null,
push: (roll) => set({ last: { ...roll, seq: ++seq } }),
dismiss: () => set({ last: null }),
}));