51 lines
1.8 KiB
Markdown
51 lines
1.8 KiB
Markdown
# Data Model: Persist Encounter
|
|
|
|
## Existing Entities (unchanged)
|
|
|
|
### Combatant
|
|
| Field | Type | Notes |
|
|
|-------|------|-------|
|
|
| id | CombatantId (branded string) | Unique identifier |
|
|
| name | string | Display name |
|
|
| initiative | number or undefined | Initiative value, optional |
|
|
|
|
#### ID Format Convention (existing code)
|
|
- Demo combatants use plain numeric IDs: `"1"`, `"2"`, `"3"`
|
|
- User-added combatants use the pattern `c-{N}` (e.g., `"c-1"`, `"c-2"`)
|
|
- On reload, `nextId` counter is derived by scanning existing IDs matching `c-{N}` and starting from `max(N) + 1`
|
|
- IDs not matching `c-{N}` (e.g., demo IDs) are ignored during counter derivation
|
|
|
|
### Encounter
|
|
| Field | Type | Notes |
|
|
|-------|------|-------|
|
|
| combatants | readonly Combatant[] | Ordered list of combatants |
|
|
| activeIndex | number | Index of the combatant whose turn it is |
|
|
| roundNumber | number | Current round (positive integer) |
|
|
|
|
## Persisted State
|
|
|
|
### Storage Key
|
|
`"initiative:encounter"`
|
|
|
|
### Serialized Format
|
|
The `Encounter` object is serialized as-is via `JSON.stringify`. The branded `CombatantId` serializes as a plain string. On deserialization, IDs are rehydrated with `combatantId()`.
|
|
|
|
### Validation on Load
|
|
1. Parse JSON string into unknown value
|
|
2. Structural check: verify it is an object with `combatants` (array), `activeIndex` (number), `roundNumber` (number)
|
|
3. Verify each combatant has `id` (string) and `name` (string)
|
|
4. Pass through `createEncounter` to enforce domain invariants
|
|
5. On any failure: discard and return `null` (caller falls back to demo encounter)
|
|
|
|
### State Transitions
|
|
|
|
```
|
|
App Load
|
|
├─ localStorage has valid data → restore Encounter
|
|
└─ localStorage empty/invalid → create demo Encounter
|
|
|
|
State Change (any use case)
|
|
└─ new Encounter saved to React state
|
|
└─ useEffect triggers → serialize to localStorage
|
|
```
|