+
RECOVERY
+
+
+ setRestHours(e.currentTarget.value)}
+ onKeyDown={(e) => {
+ if (e.key === "Enter") void rest("rest");
+ }}
+ />
+
+
+
+
+
+ setAtNexus((v) => !v)}
+ tone="ley"
+ >
+ Nexus
+
+
+
+
+ setDayInput(e.currentTarget.value)}
+ onKeyDown={(e) => {
+ if (e.key === "Enter") void treatDay();
+ }}
+ />
+
+ setProfessional((v) => !v)}
+ >
+ Pro
+
+
+
}
/>
diff --git a/packages/backend/convex/characters.ts b/packages/backend/convex/characters.ts
index 202090d..f1bf32d 100644
--- a/packages/backend/convex/characters.ts
+++ b/packages/backend/convex/characters.ts
@@ -5,9 +5,13 @@ import {
deriveSheet,
getOcc,
getSpell,
+ leyLineDraw as leyLineDrawAmount,
+ restRecovery,
rollHitPoints,
rollPhysicalSdc,
rollPpe,
+ rollSpellHealing,
+ treatmentRecovery,
type Character,
} from "@riftforge/rules";
import { v } from "convex/values";
@@ -141,26 +145,99 @@ export const rollVitals = mutation({
},
});
+/**
+ * Add server-derived amounts into the live pools, clamped at the rolled
+ * maximums — the ONE heal path every recovery lands through (`heal`, `rest`,
+ * `treat`, `leyLineDraw`, healing casts). Returns the next `current` plus
+ * what each pool actually gained after clamping (what telemetry reports).
+ */
+function healPools(
+ character: Character,
+ amounts: { hitPoints?: number; sdc?: number; ppe?: number },
+): {
+ current: NonNullable