64 lines
2.6 KiB
Markdown
64 lines
2.6 KiB
Markdown
# Data Model: Set Initiative
|
|
|
|
## Entity Changes
|
|
|
|
### Combatant (modified)
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| id | CombatantId (branded string) | Yes | Unique identifier |
|
|
| name | string | Yes | Display name (non-empty, trimmed) |
|
|
| initiative | integer | No | Initiative value for turn ordering. Unset means "not yet rolled." |
|
|
|
|
**Validation rules**:
|
|
- `initiative` must be an integer when set (no floats, NaN, or Infinity)
|
|
- Zero and negative integers are valid
|
|
- Unset (`undefined`) is valid — combatant has not rolled initiative yet
|
|
|
|
### Encounter (unchanged structure, new ordering behavior)
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| combatants | readonly Combatant[] | Yes | Ordered list. Now sorted by initiative descending (unset last, stable sort for ties). |
|
|
| activeIndex | number | Yes | Index of the active combatant. Adjusted to follow the active combatant's identity through reorders. |
|
|
| roundNumber | number | Yes | Current round (≥ 1). Unchanged by initiative operations. |
|
|
|
|
**Ordering invariant**: After any `setInitiative` call, `combatants` is sorted such that:
|
|
1. Combatants with initiative come first, ordered highest to lowest
|
|
2. Combatants without initiative come last
|
|
3. Ties within each group preserve relative insertion order (stable sort)
|
|
|
|
## New Domain Event
|
|
|
|
### InitiativeSet
|
|
|
|
Emitted when a combatant's initiative value is set, changed, or cleared.
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| type | "InitiativeSet" | Event discriminant |
|
|
| combatantId | CombatantId | The combatant whose initiative changed |
|
|
| previousValue | integer or undefined | The initiative value before the change |
|
|
| newValue | integer or undefined | The initiative value after the change |
|
|
|
|
## State Transitions
|
|
|
|
### setInitiative(encounter, combatantId, value)
|
|
|
|
**Input**: Current encounter, target combatant id, new initiative value (integer or undefined to clear)
|
|
|
|
**Output**: Updated encounter with reordered combatants and adjusted activeIndex, plus events
|
|
|
|
**Error conditions**:
|
|
- `combatant-not-found`: No combatant with the given id exists in the encounter
|
|
- `invalid-initiative`: Value is not an integer (when defined)
|
|
|
|
**Transition logic**:
|
|
1. Find target combatant by id → error if not found
|
|
2. Validate value is integer (when defined) → error if invalid
|
|
3. Record the active combatant's id (for preservation)
|
|
4. Update the target combatant's initiative value
|
|
5. Stable-sort combatants: initiative descending, unset last
|
|
6. Find the active combatant's new index in the sorted array
|
|
7. Return new encounter + `InitiativeSet` event
|