Implement the 009-combatant-hp feature that adds optional max HP and current HP tracking per combatant with +/- controls, direct entry, and persistence
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
77
packages/domain/src/adjust-hp.ts
Normal file
77
packages/domain/src/adjust-hp.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import type { DomainEvent } from "./events.js";
|
||||
import type { CombatantId, DomainError, Encounter } from "./types.js";
|
||||
|
||||
export interface AdjustHpSuccess {
|
||||
readonly encounter: Encounter;
|
||||
readonly events: DomainEvent[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure function that adjusts a combatant's current HP by a delta.
|
||||
*
|
||||
* The result is clamped to [0, maxHp]. Requires the combatant to have
|
||||
* HP tracking enabled (maxHp must be set).
|
||||
*/
|
||||
export function adjustHp(
|
||||
encounter: Encounter,
|
||||
combatantId: CombatantId,
|
||||
delta: number,
|
||||
): AdjustHpSuccess | DomainError {
|
||||
const targetIdx = encounter.combatants.findIndex((c) => c.id === combatantId);
|
||||
|
||||
if (targetIdx === -1) {
|
||||
return {
|
||||
kind: "domain-error",
|
||||
code: "combatant-not-found",
|
||||
message: `No combatant found with ID "${combatantId}"`,
|
||||
};
|
||||
}
|
||||
|
||||
const target = encounter.combatants[targetIdx];
|
||||
|
||||
if (target.maxHp === undefined || target.currentHp === undefined) {
|
||||
return {
|
||||
kind: "domain-error",
|
||||
code: "no-hp-tracking",
|
||||
message: `Combatant "${combatantId}" does not have HP tracking enabled`,
|
||||
};
|
||||
}
|
||||
|
||||
if (delta === 0) {
|
||||
return {
|
||||
kind: "domain-error",
|
||||
code: "zero-delta",
|
||||
message: "Delta must not be zero",
|
||||
};
|
||||
}
|
||||
|
||||
if (!Number.isInteger(delta)) {
|
||||
return {
|
||||
kind: "domain-error",
|
||||
code: "invalid-delta",
|
||||
message: `Delta must be an integer, got ${delta}`,
|
||||
};
|
||||
}
|
||||
|
||||
const previousHp = target.currentHp;
|
||||
const newHp = Math.max(0, Math.min(target.maxHp, previousHp + delta));
|
||||
|
||||
return {
|
||||
encounter: {
|
||||
combatants: encounter.combatants.map((c) =>
|
||||
c.id === combatantId ? { ...c, currentHp: newHp } : c,
|
||||
),
|
||||
activeIndex: encounter.activeIndex,
|
||||
roundNumber: encounter.roundNumber,
|
||||
},
|
||||
events: [
|
||||
{
|
||||
type: "CurrentHpAdjusted",
|
||||
combatantId,
|
||||
previousHp,
|
||||
newHp,
|
||||
delta,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user