import { type CombatantId, type Creature, type CreatureId, calculateInitiative, type DomainError, type DomainEvent, isDomainError, type RollMode, rollInitiative, selectRoll, setInitiative, } from "@initiative/domain"; import type { EncounterStore } from "./ports.js"; export function rollInitiativeUseCase( store: EncounterStore, combatantId: CombatantId, diceRolls: readonly [number, ...number[]], getCreature: (id: CreatureId) => Creature | undefined, mode: RollMode = "normal", ): DomainEvent[] | DomainError { const encounter = store.get(); const combatant = encounter.combatants.find((c) => c.id === combatantId); if (!combatant) { return { kind: "domain-error", code: "combatant-not-found", message: `No combatant found with ID "${combatantId}"`, }; } if (!combatant.creatureId) { return { kind: "domain-error", code: "no-creature-link", message: `Combatant "${combatant.name}" has no linked creature`, }; } const creature = getCreature(combatant.creatureId); if (!creature) { return { kind: "domain-error", code: "creature-not-found", message: `Creature not found for ID "${combatant.creatureId}"`, }; } const { modifier } = calculateInitiative({ dexScore: creature.abilities.dex, cr: creature.cr, initiativeProficiency: creature.initiativeProficiency, }); const effectiveRoll = mode === "normal" ? diceRolls[0] : selectRoll(diceRolls[0], diceRolls[1] ?? diceRolls[0], mode); const value = rollInitiative(effectiveRoll, modifier); if (isDomainError(value)) { return value; } const result = setInitiative(encounter, combatantId, value); if (isDomainError(result)) { return result; } store.save(result.encounter); return result.events; }