import type { HpRange, HpVariant } from "@initiative/domain"; import { combatantId, creatureId, EMPTY_UNDO_REDO_STATE, } from "@initiative/domain"; import { describe, expect, it } from "vitest"; import { type EncounterState, encounterReducer } from "../use-encounter.js"; // An ogre with 3d6 + 6 hit points. const RANGE: HpRange = { min: 9, average: 16, max: 24 }; function stateWithCreature( maxHp: number, currentHp: number, hpVariant?: HpVariant, ): EncounterState { return { encounter: { combatants: [ { id: combatantId("c-1"), name: "Ogre", maxHp, currentHp, creatureId: creatureId("mm:ogre"), ...(hpVariant !== undefined && { hpVariant }), }, ], activeIndex: 0, roundNumber: 1, }, undoRedoState: EMPTY_UNDO_REDO_STATE, events: [], nextId: 1, lastCreatureId: null, }; } function setVariant(state: EncounterState, variant: HpVariant | undefined) { return encounterReducer(state, { type: "set-hp-variant", id: combatantId("c-1"), variant, range: RANGE, }); } describe("set-hp-variant", () => { it("raises max HP to the maximum roll and stores the variant", () => { const next = setVariant(stateWithCreature(16, 16), "max"); const c = next.encounter.combatants[0]; expect(c.maxHp).toBe(24); expect(c.currentHp).toBe(24); expect(c.hpVariant).toBe("max"); }); it("lowers max HP to the minimum roll", () => { const next = setVariant(stateWithCreature(16, 16), "min"); const c = next.encounter.combatants[0]; expect(c.maxHp).toBe(9); expect(c.hpVariant).toBe("min"); }); it("returns to the average and clears the variant", () => { const next = setVariant(stateWithCreature(24, 24, "max"), undefined); const c = next.encounter.combatants[0]; expect(c.maxHp).toBe(16); expect(c.hpVariant).toBeUndefined(); }); it("does not rename the combatant", () => { const next = setVariant(stateWithCreature(16, 16), "max"); expect(next.encounter.combatants[0].name).toBe("Ogre"); }); it("is undoable", () => { const start = stateWithCreature(16, 16); const next = setVariant(start, "max"); const undone = encounterReducer(next, { type: "undo" }); expect(undone.encounter.combatants[0].maxHp).toBe(16); expect(undone.encounter.combatants[0].hpVariant).toBeUndefined(); }); it("leaves state untouched for an unknown combatant", () => { const start = stateWithCreature(16, 16); const next = encounterReducer(start, { type: "set-hp-variant", id: combatantId("c-99"), variant: "max", range: RANGE, }); expect(next).toBe(start); }); });