2.8 KiB
Quickstart: Roll Initiative
Overview
This feature adds dice-rolling to the encounter tracker. The initiative column uses a click-to-edit pattern: bestiary combatants without initiative show a d20 icon that rolls 1d20 + modifier on click; once set, the value displays as plain text (click to edit/clear). Manual combatants show "--" (click to type). A "Roll All" button in the top bar batch-rolls for all bestiary combatants that don't yet have initiative.
Key Files to Touch
Domain Layer (packages/domain/src/)
-
roll-initiative.ts(NEW) — Pure function:rollInitiative(diceRoll: number, modifier: number) → number. Validates dice roll range [1,20], returnsdiceRoll + modifier. -
__tests__/roll-initiative.test.ts(NEW) — Tests for the pure function covering normal, boundary (1, 20), and negative modifier cases.
Application Layer (packages/application/src/)
-
roll-initiative-use-case.ts(NEW) — Single combatant roll. Receives(store, combatantId, diceRoll, getCreature). Looks up creature viacreatureId, computes modifier viacalculateInitiative, computes final value viarollInitiative, delegates tosetInitiativedomain function. -
roll-all-initiative-use-case.ts(NEW) — Batch roll. Receives(store, diceRolls: Map<CombatantId, number>, getCreature). Iterates eligible combatants, appliessetInitiativein sequence on evolving encounter state, saves once.
Web Adapter (apps/web/src/)
-
components/d20-icon.tsx(NEW) — React component rendering the d20.svg inline. Accepts className for sizing. -
components/combatant-row.tsx(MODIFY) — Add d20 button next to initiative input. Only shown whencombatant.creatureIdis defined. New prop:onRollInitiative: (id: CombatantId) => void. -
components/turn-navigation.tsx(MODIFY) — Add "Roll All Initiative" d20 button in right section. New prop:onRollAllInitiative: () => void. -
hooks/use-encounter.ts(MODIFY) — AddrollInitiative(id)androllAllInitiative()callbacks that generate dice rolls viaMath.floor(Math.random() * 20) + 1and call the respective use cases. -
App.tsx(MODIFY) — Wire new callbacks to components.
Architecture Pattern
[Browser Math.random()] → diceRoll (number 1-20)
↓
[Application Use Case] → looks up creature, computes modifier, calls domain
↓
[Domain rollInitiative()] → diceRoll + modifier = initiative value
↓
[Domain setInitiative()] → updates combatant, re-sorts encounter
↓
[Store.save()] → persists to localStorage
Running
pnpm --filter web dev # Dev server at localhost:5173
pnpm test # Run all tests
pnpm vitest run packages/domain/src/__tests__/roll-initiative.test.ts # Single test
pnpm check # Full merge gate