1.8 KiB
1.8 KiB
Data Model: Combatant Concentration
Feature: 018-combatant-concentration | Date: 2026-03-06
Entity Changes
Combatant (modified)
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| id | CombatantId | yes | - | Existing, unchanged |
| name | string | yes | - | Existing, unchanged |
| initiative | number | no | undefined | Existing, unchanged |
| maxHp | number | no | undefined | Existing, unchanged |
| currentHp | number | no | undefined | Existing, unchanged |
| ac | number | no | undefined | Existing, unchanged |
| conditions | ConditionId[] | no | undefined | Existing, unchanged |
| isConcentrating | boolean | no | undefined (falsy) | New field. Independent of conditions. |
Domain Events (new)
| Event | Fields | Emitted When |
|---|---|---|
| ConcentrationStarted | type, combatantId |
Concentration toggled from off to on |
| ConcentrationEnded | type, combatantId |
Concentration toggled from on to off |
State Transitions
toggleConcentration(encounter, combatantId)
├── combatant not found → DomainError("combatant-not-found")
├── isConcentrating is falsy → set to true, emit ConcentrationStarted
└── isConcentrating is true → set to undefined, emit ConcentrationEnded
Validation Rules
combatantIdmust reference an existing combatant in the encounter.- No other validation needed (boolean toggle has no invalid input beyond missing combatant).
Storage Impact
- Format: JSON via localStorage (existing adapter).
- Migration: None. Field is optional; absent field is treated as
false. - Backward compatibility: Old data loads without
isConcentrating; new data with the field serializes/deserializes transparently.