Encounters and Combat
Encounters are campaign-level prep objects. Combat is the live tracker state for running an encounter or ad-hoc fight.
Backend
Primary files:
server/app/routers/encounters.pyserver/app/routers/combat.pyserver/app/lib/combat.pyserver/app/lib/encounter_xp.pyserver/app/lib/encounter_suggest.pyserver/app/lib/statblock.pyserver/app/lib/statblock_ref.pyserver/app/lib/tools/get_stat_block.py
Encounters contain monster rows and optional session links. Running an encounter expands monster counts into combatants, seeds party rows, and resets combat round/turn state. Combat routes mutate current round, active turn, combatants, ordering, and generated loot.
Frontend
Primary files:
web/src/components/SessionEncounters.tsxweb/src/components/CombatTracker.tsxweb/src/components/MonsterAutocomplete.tsxweb/src/components/StatBlock.tsxweb/src/components/LootModal.tsxweb/src/lib/encounterDifficulty.ts
The frontend mirrors difficulty math for live editing, but the server remains the source of truth for saved encounter responses.
Difficulty Math
Encounter difficulty uses deterministic 2024-style XP budgets from party member levels. The AI can suggest monster choices and flavor, but tier calculation, XP totals, CR-to-XP mapping, and final labels come from code.
Monster Sources
Monster lookup can draw from:
- Campaign-extracted monsters.
- Core-rule extracted monsters.
- SRD proxy data.
Stat-block references use stable locators such as campaign/core/SRD sources so cards can be rehydrated in chat and combat.
AI Suggestion Boundary
AI encounter suggestion is DM-only and AI-gated. The model picks a coherent set
from eligible candidates; code validates party levels, scopes visible monsters,
calculates difficulty, and serializes the result. Each suggested monster row
carries its statBlockRef locator (campaign:<id> / core:<id>), so
encounters saved from a suggestion open stat blocks in the combat tracker just
like autocomplete-picked monsters — numbered combatant names ("Goblin 2")
can't be resolved by name alone.
Change Checklist
- Keep server and frontend difficulty math aligned.
- Enforce DM-only access for encounter/combat mutations.
- Preserve run semantics: templates do not mutate when run.
- Check stat-block reference behavior when monster DTOs change.
- Add tests for edge cases around party levels, CR, and active combat conflicts.