Files
initiative/specs/026-roll-initiative/quickstart.md

55 lines
2.8 KiB
Markdown

# 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/`)
1. **`roll-initiative.ts`** (NEW) — Pure function: `rollInitiative(diceRoll: number, modifier: number) → number`. Validates dice roll range [1,20], returns `diceRoll + modifier`.
2. **`__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/`)
3. **`roll-initiative-use-case.ts`** (NEW) — Single combatant roll. Receives `(store, combatantId, diceRoll, getCreature)`. Looks up creature via `creatureId`, computes modifier via `calculateInitiative`, computes final value via `rollInitiative`, delegates to `setInitiative` domain function.
4. **`roll-all-initiative-use-case.ts`** (NEW) — Batch roll. Receives `(store, diceRolls: Map<CombatantId, number>, getCreature)`. Iterates eligible combatants, applies `setInitiative` in sequence on evolving encounter state, saves once.
### Web Adapter (`apps/web/src/`)
5. **`components/d20-icon.tsx`** (NEW) — React component rendering the d20.svg inline. Accepts className for sizing.
6. **`components/combatant-row.tsx`** (MODIFY) — Add d20 button next to initiative input. Only shown when `combatant.creatureId` is defined. New prop: `onRollInitiative: (id: CombatantId) => void`.
7. **`components/turn-navigation.tsx`** (MODIFY) — Add "Roll All Initiative" d20 button in right section. New prop: `onRollAllInitiative: () => void`.
8. **`hooks/use-encounter.ts`** (MODIFY) — Add `rollInitiative(id)` and `rollAllInitiative()` callbacks that generate dice rolls via `Math.floor(Math.random() * 20) + 1` and call the respective use cases.
9. **`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
```bash
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
```