New source found from dndbeyond.com
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
import { FC, HTMLAttributes, useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
ShortModelInfoContract,
|
||||
ApiRequests,
|
||||
serviceDataActions,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { Checkbox } from "~/components/Checkbox";
|
||||
import { RadioGroup } from "~/components/RadioGroup";
|
||||
import { PreferenceLongRestTypeEnum } from "~/constants";
|
||||
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
|
||||
import { useDispatch } from "~/hooks/useDispatch";
|
||||
import { Header } from "~/subApps/sheet/components/Sidebar/components/Header";
|
||||
import { toastMessageActions } from "~/tools/js/Shared/actions";
|
||||
import ThemeButton from "~/tools/js/Shared/components/common/Button/ThemeButton/ThemeButton";
|
||||
import DataLoadingStatusEnum from "~/tools/js/Shared/constants/DataLoadingStatusEnum";
|
||||
import { AppLoggerUtils } from "~/tools/js/Shared/utils";
|
||||
|
||||
import { RestoreLifeManager } from "../HitPointsManagePane/RestoreLifeManager";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
interface LongRestPaneProps extends HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const LongRestPane: FC<LongRestPaneProps> = ({ ...props }) => {
|
||||
const [resetMaxHpModifier, setResetMaxHpModifier] = useState(true);
|
||||
const [adjustConditionLevel, setAdjustConditionLevel] = useState(false);
|
||||
const [restMessageLoadingStatus, setRestMessageLoadingStatus] =
|
||||
useState<DataLoadingStatusEnum>(DataLoadingStatusEnum.NOT_INITIALIZED);
|
||||
|
||||
const {
|
||||
activeConditions,
|
||||
isDead,
|
||||
preferences,
|
||||
characterUtils,
|
||||
helperUtils,
|
||||
apiAdapterUtils,
|
||||
characterActions,
|
||||
longRestText,
|
||||
} = useCharacterEngine();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleSave = () => {
|
||||
dispatch(
|
||||
characterActions.longRest(resetMaxHpModifier, adjustConditionLevel)
|
||||
);
|
||||
dispatch(
|
||||
toastMessageActions.toastSuccess(
|
||||
"Long Rest Taken",
|
||||
"You have completed a long rest. Relevant abilities have been reset."
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleRestoreToLife = (restoreType: ShortModelInfoContract) => {
|
||||
const restoreChoice = restoreType.name === "Full" ? "full" : "1";
|
||||
|
||||
dispatch(characterActions.restoreLife(restoreType.id));
|
||||
dispatch(
|
||||
toastMessageActions.toastSuccess(
|
||||
"Character Restored to Life",
|
||||
`You have been restored to life with ${restoreChoice} HP.`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleChangeLongRestType = (value: number | null) => {
|
||||
const typedPrefKey = characterUtils.getPreferenceKey("longRestType");
|
||||
|
||||
if (typedPrefKey !== null) {
|
||||
dispatch(characterActions.preferenceChoose(typedPrefKey, value));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const abortController = new AbortController();
|
||||
|
||||
if (restMessageLoadingStatus !== DataLoadingStatusEnum.LOADED) {
|
||||
setRestMessageLoadingStatus(DataLoadingStatusEnum.LOADING);
|
||||
|
||||
ApiRequests.getCharacterRestLong({
|
||||
signal: abortController.signal,
|
||||
})
|
||||
.then((response) => {
|
||||
let message = apiAdapterUtils.getResponseData(response);
|
||||
if (message !== null) {
|
||||
dispatch(serviceDataActions.longRestTextSet(message));
|
||||
}
|
||||
setRestMessageLoadingStatus(DataLoadingStatusEnum.LOADED);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (abortController.signal.aborted) return;
|
||||
AppLoggerUtils.handleAdhocApiError(e);
|
||||
});
|
||||
}
|
||||
|
||||
// If the pane is quickly opened and closed multiple times, we cancel any in-flight requests.
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [restMessageLoadingStatus]);
|
||||
|
||||
const renderRecover = () => {
|
||||
if (isDead) {
|
||||
return <p>Your character is dead</p>;
|
||||
}
|
||||
const exhaustionIsActive = activeConditions.find(
|
||||
(condition) => condition.level !== null
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
{longRestText === null ||
|
||||
restMessageLoadingStatus === DataLoadingStatusEnum.LOADING
|
||||
? "Asking the server what will be reset..."
|
||||
: longRestText}
|
||||
</p>
|
||||
<div className={styles.recoverMaxHp}>
|
||||
<Checkbox
|
||||
className={styles.checkbox}
|
||||
id="reset-max-hp"
|
||||
label="Reset Maximum HP changes during this rest"
|
||||
checked={resetMaxHpModifier}
|
||||
onChange={() => setResetMaxHpModifier(!resetMaxHpModifier)}
|
||||
themed
|
||||
/>
|
||||
</div>
|
||||
{exhaustionIsActive && (
|
||||
<div className={styles.recoverExhaustion}>
|
||||
<Checkbox
|
||||
className={styles.checkbox}
|
||||
id="recover-exhaustion"
|
||||
label="Recover 1 Level of Exhaustion during this rest (requires food and drink)"
|
||||
checked={adjustConditionLevel}
|
||||
onChange={() => setAdjustConditionLevel(!adjustConditionLevel)}
|
||||
themed
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderActions = () => {
|
||||
if (isDead) {
|
||||
return (
|
||||
<>
|
||||
<RestoreLifeManager onSave={handleRestoreToLife} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.recoverActions}>
|
||||
<ThemeButton onClick={handleSave} enableConfirm={true}>
|
||||
Take Long Rest
|
||||
</ThemeButton>
|
||||
<ThemeButton
|
||||
onClick={() => {
|
||||
setResetMaxHpModifier(true);
|
||||
setAdjustConditionLevel(false);
|
||||
}}
|
||||
style="outline"
|
||||
>
|
||||
Reset
|
||||
</ThemeButton>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header>Long Rest</Header>
|
||||
<div>
|
||||
<p>
|
||||
A Long Rest is a period of extended downtime—at least 8
|
||||
hours—available to any creature. During a Long Rest, you sleep for at
|
||||
least 6 hours and perform no more than 2 hours of light activity, such
|
||||
as reading, talking, eating, or standing watch.
|
||||
</p>
|
||||
<p>
|
||||
While asleep, you have the Unconscious condition. After you finish a
|
||||
Long Rest, you must wait at least 16 hours before starting another
|
||||
one.
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.longRestType}>
|
||||
<RadioGroup
|
||||
className={styles.radioGroup}
|
||||
themed
|
||||
title="Long Rest Rules"
|
||||
name="long-rest-type"
|
||||
initialValue={preferences.longRestType}
|
||||
onChange={(e) =>
|
||||
handleChangeLongRestType(
|
||||
helperUtils.parseInputInt(e?.target?.value)
|
||||
)
|
||||
}
|
||||
options={[
|
||||
{
|
||||
label: "Recover 1/2 Hit Dice",
|
||||
description: "Use 5e Rules",
|
||||
value: PreferenceLongRestTypeEnum.HALF,
|
||||
},
|
||||
{
|
||||
label: "Recover all Hit Dice",
|
||||
description: "Use 5.5e Rules",
|
||||
value: PreferenceLongRestTypeEnum.FULL,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.recover}>
|
||||
<h2 className={styles.recoverHeading}>Recover</h2>
|
||||
{renderRecover()}
|
||||
</div>
|
||||
{renderActions()}
|
||||
<div className={styles.longRestInfo}>
|
||||
<p>
|
||||
<strong>
|
||||
<em>Benefits of the Rest.</em>
|
||||
</strong>{" "}
|
||||
To start a Long Rest, you must have at least 1 HP. When you finish the
|
||||
rest, you gain the following benefits:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
You regain all lost HP and all spent Hit Point Dice. If your HP
|
||||
maximum was reduced, it returns to normal.
|
||||
</li>
|
||||
<li>
|
||||
If any of your ability scores were reduced, they return to normal.
|
||||
</li>
|
||||
<li>
|
||||
If you have the Exhaustion condition, its level decreases by 1.
|
||||
</li>
|
||||
<li>
|
||||
If you have a feature that is recharged by a Long Rest, it recharges
|
||||
in the way specified in its description.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong>
|
||||
<em>Interrupting the Rest.</em>
|
||||
</strong>{" "}
|
||||
A Long Rest is stopped by the following interruptions:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Rolling Initiative</li>
|
||||
<li>Casting a spell other than a cantrip</li>
|
||||
<li>Taking any damage</li>
|
||||
<li>1 hour of walking or other physical exertion</li>
|
||||
</ul>
|
||||
<p>
|
||||
If you rested at least 1 hour before the interruption, you gain the
|
||||
benefits of a Short Rest.
|
||||
</p>
|
||||
<p>
|
||||
You can resume a Long Rest immediately after an interruption. If you
|
||||
do so, the rest requires 1 additional hour per interruption to finish.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user