46 lines
1.8 KiB
Markdown
46 lines
1.8 KiB
Markdown
# 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
|
|
|
|
- `combatantId` must 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.
|