import clsx from "clsx"; import { FC, HTMLAttributes, useEffect, useMemo, useState } from "react"; import { CharacterUtils, FormatUtils, HelperUtils, HtmlSelectOption, RuleDataUtils, } from "@dndbeyond/character-rules-engine"; import { useCharacterEngine } from "~/hooks/useCharacterEngine"; import XpBar from "~/tools/js/smartComponents/XpBar"; import { Select } from "~/tools/js/smartComponents/legacy"; import { Button } from "../Button"; import styles from "./styles.module.css"; const XP_CHANGE_TYPE = { ADD: "ADD", REMOVE: "REMOVE", }; interface XpManagerProps extends HTMLAttributes { handleXpUpdates: (newXpTotal: number) => void; shouldReset: boolean; } export const XpManager: FC = ({ handleXpUpdates, shouldReset, ...props }) => { const { experienceInfo: xpInfo, ruleData } = useCharacterEngine(); const [xpNew, setXpNew] = useState(null); const [xpChange, setXpChange] = useState(null); const [levelChosen, setLevelChosen] = useState(null); const [changeType, setChangeType] = useState(XP_CHANGE_TYPE.ADD); const [newXpTotal, setNewXpTotal] = useState(xpInfo.currentLevelXp); useEffect(() => { let xpDiff: number = xpChange ?? 0; if (changeType === XP_CHANGE_TYPE.REMOVE) { xpDiff *= -1; } let newXpTotal: number = xpInfo.currentLevelXp; if (xpDiff) { newXpTotal += xpDiff; } else if (levelChosen !== null) { newXpTotal = CharacterUtils.deriveCurrentLevelXp(levelChosen, ruleData); } else if (xpNew !== null) { newXpTotal = xpNew; } newXpTotal = Math.min( Math.max(0, newXpTotal), CharacterUtils.deriveMaxXp(ruleData) ); setNewXpTotal((prev) => { if (newXpTotal !== prev) { handleXpUpdates(newXpTotal); } return newXpTotal; }); }, [ xpChange, changeType, levelChosen, xpNew, xpInfo, ruleData, handleXpUpdates, ]); useEffect(() => { if (shouldReset) { setXpNew(null); setXpChange(null); setLevelChosen(null); setChangeType(XP_CHANGE_TYPE.ADD); setNewXpTotal(xpInfo.currentLevelXp); } }, [shouldReset, xpInfo]); const levelOptions = useMemo(() => { let levelOptions: Array = []; for (let i = 1; i <= RuleDataUtils.getMaxCharacterLevel(ruleData); i++) { levelOptions.push({ label: `${i}`, value: i, }); } return levelOptions; }, [ruleData]); const displayCurrentLevelXp = useMemo(() => { return xpInfo.currentLevel === ruleData.maxCharacterLevel ? CharacterUtils.deriveCurrentLevelXp( ruleData.maxCharacterLevel - 1, ruleData ) : xpInfo.currentLevelXp; }, [xpInfo, ruleData]); const handleChooseLevel = (value: string): void => { setXpNew(null); setXpChange(null); setLevelChosen(HelperUtils.parseInputInt(value)); }; const handleXpChange = (evt: React.ChangeEvent): void => { setXpNew(null); setXpChange(HelperUtils.parseInputInt(evt.target.value)); setLevelChosen(null); }; const handleXpSet = (evt: React.ChangeEvent): void => { setXpNew(HelperUtils.parseInputInt(evt.target.value)); setXpChange(null); setLevelChosen(null); }; return (

Current XP Total:{" "} {FormatUtils.renderLocaleNumber(xpInfo.currentLevelXp)} (Level{" "} {xpInfo.currentLevel})

{FormatUtils.renderLocaleNumber(displayCurrentLevelXp)}
{FormatUtils.renderLocaleNumber(xpInfo.nextLevelXp)}

New XP Total: {FormatUtils.renderLocaleNumber(newXpTotal)} (Level{" "} {CharacterUtils.deriveXpLevel(newXpTotal, ruleData)})

); };