# 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) - `XP_BUDGET_PER_CHARACTER` lookup (Record) - `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).