Implement the 012-turn-navigation feature that adds a RetreatTurn domain operation and relocates turn controls to a navigation bar at the top of the encounter tracker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-05 23:11:11 +01:00
parent a0d85a07e3
commit 7d440677be
19 changed files with 946 additions and 13 deletions

View File

@@ -0,0 +1,59 @@
import type { DomainEvent } from "./events.js";
import type { DomainError, Encounter } from "./types.js";
interface RetreatTurnSuccess {
readonly encounter: Encounter;
readonly events: DomainEvent[];
}
export function retreatTurn(
encounter: Encounter,
): RetreatTurnSuccess | DomainError {
if (encounter.combatants.length === 0) {
return {
kind: "domain-error",
code: "invalid-encounter",
message: "Cannot retreat turn on an encounter with no combatants",
};
}
if (encounter.roundNumber === 1 && encounter.activeIndex === 0) {
return {
kind: "domain-error",
code: "no-previous-turn",
message: "Cannot retreat before the start of the encounter",
};
}
const previousIndex = encounter.activeIndex;
const wraps = previousIndex === 0;
const newIndex = wraps ? encounter.combatants.length - 1 : previousIndex - 1;
const newRoundNumber = wraps
? encounter.roundNumber - 1
: encounter.roundNumber;
const events: DomainEvent[] = [
{
type: "TurnRetreated",
previousCombatantId: encounter.combatants[previousIndex].id,
newCombatantId: encounter.combatants[newIndex].id,
roundNumber: newRoundNumber,
},
];
if (wraps) {
events.push({
type: "RoundRetreated",
newRoundNumber,
});
}
return {
encounter: {
combatants: encounter.combatants,
activeIndex: newIndex,
roundNumber: newRoundNumber,
},
events,
};
}