Files
Lukas ef76b9c90b
All checks were successful
CI / check (push) Successful in 1m13s
CI / build-image (push) Successful in 16s
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>
2026-03-27 22:55:48 +01:00

3.9 KiB

Data Model: Encounter Difficulty Indicator

Date: 2026-03-27 | Feature: 008-encounter-difficulty

Entities

PlayerCharacter (modified)

Existing entity from spec 005. Adding one optional field.

Field Type Required Notes
id PlayerCharacterId yes Existing — branded string
name string yes Existing
ac number yes Existing
maxHp number yes Existing
color PlayerColor no Existing
icon PlayerIcon no Existing
level number no NEW — integer 1-20. Used for XP budget calculation. PCs without level are excluded from difficulty calc.

Validation rules for level:

  • If provided, must be an integer
  • If provided, must be >= 1 and <= 20
  • If omitted/undefined, PC is excluded from difficulty budget

DifficultyTier (new)

Enumeration of encounter difficulty categories.

Value Display Label Visual
"trivial" Trivial 3 empty bars
"low" Low 1 green bar
"moderate" Moderate 2 yellow bars
"high" High 3 red bars

DifficultyResult (new)

Output of the difficulty calculation. Pure data object.

Field Type Notes
tier DifficultyTier The determined difficulty category
totalMonsterXp number Sum of XP for all bestiary-linked combatants
partyBudget { low: number; moderate: number; high: number } XP thresholds for the party

XP Budget per Character (static lookup)

Maps character level to XP thresholds. Data from 2024 5.5e DMG.

Level Low Moderate High
1 50 75 100
2 100 150 200
3 150 225 400
4 250 375 500
5 500 750 1,100
6 600 1,000 1,400
7 750 1,300 1,700
8 1,000 1,700 2,100
9 1,300 2,000 2,600
10 1,600 2,300 3,100
11 1,900 2,900 4,100
12 2,200 3,700 4,700
13 2,600 4,200 5,400
14 2,900 4,900 6,200
15 3,300 5,400 7,800
16 3,800 6,100 9,800
17 4,500 7,200 11,700
18 5,000 8,700 14,200
19 5,500 10,700 17,200
20 6,400 13,200 22,000

CR-to-XP (static lookup)

Maps challenge rating strings to XP values. Standard 5e values.

CR XP
0 0
1/8 25
1/4 50
1/2 100
1 200
2 450
3 700
4 1,100
5 1,800
6 2,300
7 2,900
8 3,900
9 5,000
10 5,900
11 7,200
12 8,400
13 10,000
14 11,500
15 13,000
16 15,000
17 18,000
18 20,000
19 22,000
20 25,000
21 33,000
22 41,000
23 50,000
24 62,000
25 75,000
26 90,000
27 105,000
28 120,000
29 135,000
30 155,000

Relationships

PlayerCharacter (has optional level)
    │
    ▼ linked via playerCharacterId
Combatant (in Encounter)
    │
    ▼ linked via creatureId
Creature (has cr string)
    │
    ▼ lookup via CR_TO_XP table
XP value (number)

Party levels ──► XP_BUDGET_TABLE ──► { low, moderate, high } thresholds
Monster XP total ──► compare against thresholds ──► DifficultyTier

State Transitions

The difficulty calculation is stateless — it's a pure derivation from current encounter state. No state machine or transitions to model.

Input derivation (at adapter layer):

  1. For each combatant with playerCharacterId → look up PlayerCharacter.level → collect non-undefined levels
  2. For each combatant with creatureId → look up Creature.cr → collect CR strings
  3. Pass (levels[], crs[]) to domain function

Pure calculation (domain layer):

  1. Sum XP budget per level → partyBudget.{low, moderate, high}
  2. Convert each CR to XP → sum → totalMonsterXp
  3. Compare totalMonsterXp against thresholds → DifficultyTier