import clsx from "clsx"; import { FC, FocusEvent, useMemo, useState } from "react"; import { useDispatch } from "react-redux"; import { characterActions, ClassUtils, Constants, DiceUtils, FormatUtils, HelperUtils, RuleDataUtils, } from "@dndbeyond/character-rules-engine"; import { ConfirmModal, ConfirmModalProps } from "~/components/ConfirmModal"; import { useCharacterEngine } from "~/hooks/useCharacterEngine"; import { HP_BASE_MAX_VALUE, HP_BONUS_VALUE, HP_OVERRIDE_MAX_VALUE, } from "~/subApps/sheet/constants"; import { InputField } from "../InputField"; import styles from "./styles.module.css"; export interface HpManageModalProps extends Omit {} export const HpManageModal: FC = ({ onClose, ...props }) => { const dispatch = useDispatch(); const { hpInfo, preferences, ruleData } = useCharacterEngine(); const [baseHp, setBaseHp] = useState(hpInfo.baseHp); const [bonusHp, setBonusHp] = useState(hpInfo.bonusHp); const [overrideHp, setOverrideHp] = useState(hpInfo.overrideHp); const reset = () => { setBaseHp(hpInfo.baseHp); setBonusHp(hpInfo.bonusHp); setOverrideHp(hpInfo.overrideHp); }; const handleClose = () => { onClose(); reset(); }; const onConfirm = () => { if (baseHp !== hpInfo.baseHp) { dispatch(characterActions.baseHitPointsSet(baseHp)); } if (bonusHp !== hpInfo.bonusHp) { dispatch(characterActions.bonusHitPointsSet(bonusHp)); } if (overrideHp !== hpInfo.overrideHp) { dispatch(characterActions.overrideHitPointsSet(overrideHp)); } onClose(); reset(); }; const handleBaseHp = (e: FocusEvent) => { let parsedNumber = HelperUtils.parseInputInt( e.target.value, RuleDataUtils.getMinimumHpTotal(ruleData) ); const clampedValue = HelperUtils.clampInt( parsedNumber, RuleDataUtils.getMinimumHpTotal(ruleData), HP_BASE_MAX_VALUE ); setBaseHp(clampedValue); }; const handleBonusHp = (e: FocusEvent) => { let parsedNumber = HelperUtils.parseInputInt(e.target.value); if (parsedNumber === null) return parsedNumber; const clampedValue = HelperUtils.clampInt( parsedNumber, HP_BONUS_VALUE.MIN, HP_BONUS_VALUE.MAX ); setBonusHp(clampedValue); }; const handleOverrideHp = (e: FocusEvent) => { let parsedNumber = HelperUtils.parseInputInt(e.target.value, null); if (parsedNumber === null) { setOverrideHp(null); return parsedNumber; } const clampedValue = HelperUtils.clampInt( parsedNumber, RuleDataUtils.getMinimumHpTotal(ruleData) ); const clampedOverride = HelperUtils.clampInt( clampedValue, RuleDataUtils.getMinimumHpTotal(ruleData), HP_OVERRIDE_MAX_VALUE ); setOverrideHp(clampedOverride); }; const totalHp = useMemo(() => { let totalHp: number = baseHp + hpInfo.totalHitPointSources + (bonusHp === null ? 0 : bonusHp); if (overrideHp !== null) { totalHp = overrideHp; } return Math.max(RuleDataUtils.getMinimumHpTotal(ruleData), totalHp); }, [baseHp, bonusHp, overrideHp, hpInfo, ruleData]); return ( {/* Total Max Hit Points */}

Maximum Hit Points {totalHp}

{/* Hit Point Controls */}
{preferences.hitPointType === Constants.PreferenceHitPointTypeEnum.FIXED ? (

Fixed HP {baseHp}

) : ( )}
{/* Hit Point Bonus Sources */} {hpInfo.hitPointSources.length > 0 && (

Hit Point Bonuses

{hpInfo.hitPointSources.map((hitPointSource, idx) => (

{FormatUtils.renderSignedNumber(hitPointSource.amount)} from{" "} {hitPointSource.source}

))}
)} {/* Hit Dice & Potential Values */}

Hit Dice

{hpInfo.classesHitDice.map((classHitDice) => (
{ClassUtils.getName(classHitDice.charClass)}: {DiceUtils.renderDie(classHitDice.dice)}
))}

Potential Values

Total Fixed Value HP: {hpInfo.totalFixedValueHp}
Total Average HP: {hpInfo.totalAverageHp}
Total Possible HP: {hpInfo.possibleMaxHitPoints}
{/* HP Help */}

Max Hit Points

Your hit point maximum is determined by the number you roll for your hit dice each level or a fixed value determined by your hit dice and your Constitution modifier

Bonus Hit Points

Use this field to record any miscellaneous bonus hit points you want to add to your normal hit point maximum. These hit points are different from temporary hit points, which you can add on your character sheet during play.

Override Hit Points

Use this field to override your typical hit point maximum. The number you enter here will display as your hit point maximum on your character sheet.

); };