Add encounter difficulty indicator (5.5e XP budget)
Live 3-bar difficulty indicator in the top bar showing encounter difficulty (Trivial/Low/Moderate/High) based on the 2024 5.5e XP budget system. Automatically derived from PC levels and bestiary creature CRs. - Add optional level field (1-20) to PlayerCharacter - Add CR-to-XP and XP Budget per Character lookup tables in domain - Add calculateEncounterDifficulty pure function - Add DifficultyIndicator component with color-coded bars and tooltip - Add useDifficulty hook composing encounter, PC, and bestiary contexts - Indicator hidden when no PCs with levels or no bestiary-linked monsters - Level field in PC create/edit forms, persisted in storage Closes #18 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
specs/008-encounter-difficulty/quickstart.md
Normal file
67
specs/008-encounter-difficulty/quickstart.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Quickstart: Encounter Difficulty Indicator
|
||||
|
||||
**Date**: 2026-03-27 | **Feature**: 008-encounter-difficulty
|
||||
|
||||
## Implementation Order
|
||||
|
||||
### Phase 1: Domain — Level field + validation
|
||||
|
||||
1. Add `level?: number` to `PlayerCharacter` in `player-character-types.ts`
|
||||
2. Add level validation to `createPlayerCharacter()` — validate if provided: integer, 1-20
|
||||
3. Add level validation to `editPlayerCharacter()` — same rules in `validateFields()`, apply in `applyFields()`
|
||||
4. Add tests for level validation in existing test files
|
||||
5. Export updated types from `index.ts`
|
||||
|
||||
### Phase 2: Domain — Difficulty calculation
|
||||
|
||||
1. Create `encounter-difficulty.ts` with:
|
||||
- `CR_TO_XP` lookup (Record<string, number>)
|
||||
- `XP_BUDGET_PER_CHARACTER` lookup (Record<number, { low, moderate, high }>)
|
||||
- `crToXp(cr: string): number` — returns 0 for unknown CRs
|
||||
- `calculateEncounterDifficulty(partyLevels: number[], monsterCrs: string[]): DifficultyResult`
|
||||
- `DifficultyTier` type and `DifficultyResult` type
|
||||
2. Add comprehensive unit tests covering:
|
||||
- All CR string formats (0, 1/8, 1/4, 1/2, integers)
|
||||
- All difficulty tiers including trivial
|
||||
- DMG example encounters (from issue comments)
|
||||
- Edge cases: empty arrays, unknown CRs, mixed levels
|
||||
3. Export from `index.ts`
|
||||
|
||||
### Phase 3: Application — Pass level through use cases
|
||||
|
||||
1. Update `CreatePlayerCharacterUseCase` to accept and pass `level`
|
||||
2. Update `EditPlayerCharacterUseCase` to accept and pass `level`
|
||||
|
||||
### Phase 4: Web — Level field in PC forms
|
||||
|
||||
1. Update player characters context to pass `level` in create/edit calls
|
||||
2. Add level input field to create player modal (optional number, 1-20)
|
||||
3. Add level display + edit in player character manager
|
||||
4. Test: create PC with level, edit level, verify persistence
|
||||
|
||||
### Phase 5: Web — Difficulty indicator
|
||||
|
||||
1. Create `useDifficulty()` hook:
|
||||
- Consume encounter context, player characters context, bestiary hook
|
||||
- Map combatants → party levels + monster CRs
|
||||
- Call domain `calculateEncounterDifficulty()`
|
||||
- Return `DifficultyResult | null` (null when insufficient data)
|
||||
2. Create `DifficultyIndicator` component:
|
||||
- Render 3 bars with conditional fill colors
|
||||
- Add `title` attribute for tooltip
|
||||
- Hidden when hook returns null
|
||||
3. Add indicator to `TurnNavigation` component, right of active combatant name
|
||||
4. Test: manual verification with various encounter compositions
|
||||
|
||||
## Key Patterns to Follow
|
||||
|
||||
- **Domain purity**: `calculateEncounterDifficulty` takes `number[]` and `string[]`, not domain types
|
||||
- **Validation pattern**: Follow `color`/`icon` optional field pattern in create/edit
|
||||
- **Hook composition**: `useDifficulty` composes multiple contexts like `useInitiativeRolls`
|
||||
- **Component size**: DifficultyIndicator should be <8 props (likely 0-1, just the result)
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- **Domain tests** (unit): Exhaustive coverage of `calculateEncounterDifficulty` and `crToXp` with table-driven tests. Cover all 34 CR values, all 20 levels, and the DMG example encounters.
|
||||
- **Domain tests** (level validation): Test create/edit with valid levels, invalid levels, and undefined level.
|
||||
- **Integration**: Verify indicator appears/hides correctly through component rendering (if existing test patterns support this).
|
||||
Reference in New Issue
Block a user