Add min/max hit point variants for D&D creatures

A D&D stat block opened for a combatant now offers a Min/Avg/Max toggle
below the Hit Points line, deriving both ends from the Hit Dice formula
(3d6 + 6 -> 9 / 16 / 24). The switch is applied as a delta, so manual HP
edits and damage already taken survive it. Unlike the PF2e weak/elite
adjustment this is a convenience tool, not a rules mechanic: nothing but
HP changes and the combatant is not renamed. The toggle is hidden when
the HP field carries prose instead of a dice pool.

Also extracts a SegmentedControl primitive. Game system, theme and the
PF2e weak/elite toggle were three hand-rolled copies of the same markup,
which is why the D&D toggle first came out chunkier and in the wrong
accent color. All four now share one definition, and segments expose
aria-pressed instead of signalling the active state by color alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-08-05 17:06:01 +02:00
co-authored by Claude Opus 5
parent 7b599e9ad9
commit be3778a0c9
22 changed files with 942 additions and 88 deletions
+1
View File
@@ -27,6 +27,7 @@ export { setAcUseCase } from "./set-ac-use-case.js";
export { setConditionValueUseCase } from "./set-condition-value-use-case.js";
export { setCrUseCase } from "./set-cr-use-case.js";
export { setHpUseCase } from "./set-hp-use-case.js";
export { setHpVariantUseCase } from "./set-hp-variant-use-case.js";
export { setInitiativeUseCase } from "./set-initiative-use-case.js";
export { setSideUseCase } from "./set-side-use-case.js";
export { setTempHpUseCase } from "./set-temp-hp-use-case.js";
@@ -0,0 +1,21 @@
import {
type CombatantId,
type DomainError,
type DomainEvent,
type HpRange,
type HpVariant,
setHpVariant,
} from "@initiative/domain";
import type { EncounterStore } from "./ports.js";
import { runEncounterAction } from "./run-encounter-action.js";
export function setHpVariantUseCase(
store: EncounterStore,
combatantId: CombatantId,
variant: HpVariant | undefined,
range: HpRange,
): DomainEvent[] | DomainError {
return runEncounterAction(store, (encounter) =>
setHpVariant(encounter, combatantId, variant, range),
);
}