56 lines
1.6 KiB
Markdown
56 lines
1.6 KiB
Markdown
# Domain API Contract: Turn Navigation
|
|
|
|
## retreatTurn(encounter: Encounter): RetreatTurnSuccess | DomainError
|
|
|
|
### Input
|
|
- `encounter`: Encounter (combatants, activeIndex, roundNumber)
|
|
|
|
### Success Output
|
|
```
|
|
{
|
|
encounter: Encounter // Updated state
|
|
events: DomainEvent[] // TurnRetreated, optionally RoundRetreated
|
|
}
|
|
```
|
|
|
|
### Error Cases
|
|
|
|
| Condition | Error Code | Message |
|
|
|-----------|------------|---------|
|
|
| combatants.length === 0 | `invalid-encounter` | Cannot retreat turn on an encounter with no combatants |
|
|
| roundNumber === 1 && activeIndex === 0 | `no-previous-turn` | Cannot retreat before the start of the encounter |
|
|
|
|
### Event Contracts
|
|
|
|
**TurnRetreated** (always emitted on success):
|
|
```
|
|
{
|
|
type: "TurnRetreated"
|
|
previousCombatantId: CombatantId // Was active before retreat
|
|
newCombatantId: CombatantId // Now active after retreat
|
|
roundNumber: number // Round number after retreat
|
|
}
|
|
```
|
|
|
|
**RoundRetreated** (emitted when crossing round boundary):
|
|
```
|
|
{
|
|
type: "RoundRetreated"
|
|
newRoundNumber: number // Decremented round number
|
|
}
|
|
```
|
|
|
|
**Emission order**: TurnRetreated first, then RoundRetreated (when applicable).
|
|
|
|
## UI Contract: TurnNavigation Component
|
|
|
|
### Props
|
|
- `encounter`: Encounter (current state)
|
|
- `onAdvanceTurn`: () => void
|
|
- `onRetreatTurn`: () => void
|
|
|
|
### Behavior
|
|
- Previous Turn button: calls onRetreatTurn; disabled when roundNumber === 1 && activeIndex === 0, or combatants.length === 0
|
|
- Next Turn button: calls onAdvanceTurn; disabled when combatants.length === 0
|
|
- Displays: round number and active combatant name
|