From f552a475c17b68e22e94e0c8b96c4f0899c3c9e6 Mon Sep 17 00:00:00 2001 From: Aaron Wood Date: Thu, 9 Apr 2026 14:19:18 -0400 Subject: [PATCH] Wire up talent HP bonus with per-level scaling (e.g. Grit +2 HP and +1/level) Co-Authored-By: Claude Opus 4.6 (1M context) --- client/src/components/CharacterCard.tsx | 3 ++- .../src/components/CharacterSheet.module.css | 6 +++++ client/src/components/CharacterSheet.tsx | 27 ++++++++++++++----- client/src/utils/talent-effects.ts | 5 +++- server/src/db.ts | 8 ++++++ server/src/seed-dev-data.ts | 2 +- server/src/seed-talents.ts | 2 +- 7 files changed, 42 insertions(+), 11 deletions(-) diff --git a/client/src/components/CharacterCard.tsx b/client/src/components/CharacterCard.tsx index 8cba526..c42438d 100644 --- a/client/src/components/CharacterCard.tsx +++ b/client/src/components/CharacterCard.tsx @@ -3,6 +3,7 @@ import HpBar from "./HpBar"; import StatBlock from "./StatBlock"; import styles from "./CharacterCard.module.css"; import { calculateAC } from "../utils/derived-ac"; +import { getTalentHpBonus } from "../utils/talent-effects"; interface CharacterCardProps { character: Character; @@ -38,7 +39,7 @@ export default function CharacterCard({
e.stopPropagation()}> onHpChange(character.id, hp)} />
diff --git a/client/src/components/CharacterSheet.module.css b/client/src/components/CharacterSheet.module.css index d3bb2ad..8086390 100644 --- a/client/src/components/CharacterSheet.module.css +++ b/client/src/components/CharacterSheet.module.css @@ -76,6 +76,12 @@ font-weight: 600; } +.hpBonus { + color: #4caf50; + font-size: 0.65rem; + margin-left: 0.15rem; +} + .xpThreshold { font-size: 0.75rem; color: #666; diff --git a/client/src/components/CharacterSheet.tsx b/client/src/components/CharacterSheet.tsx index 56754c9..b1192c5 100644 --- a/client/src/components/CharacterSheet.tsx +++ b/client/src/components/CharacterSheet.tsx @@ -1,6 +1,7 @@ import { useState, useRef, useEffect } from "react"; import type { Character, GameItem } from "../types"; import { calculateAC } from "../utils/derived-ac"; +import { getTalentHpBonus } from "../utils/talent-effects"; import AcDisplay from "./AcDisplay"; import InlineNumber from "./InlineNumber"; import StatsPanel from "./StatsPanel"; @@ -51,6 +52,8 @@ export default function CharacterSheet({ const [confirmDelete, setConfirmDelete] = useState(false); const debounceRef = useRef>(); const acBreakdown = calculateAC(character); + const hpBonus = getTalentHpBonus(character); + const effectiveHpMax = character.hp_max + hpBonus; const xpThreshold = character.level * 10; useEffect(() => { @@ -122,14 +125,24 @@ export default function CharacterSheet({ /> / {mode === "edit" ? ( - onUpdate(character.id, { hp_max: hp })} - className={styles.hpMax} - min={0} - /> + <> + onUpdate(character.id, { hp_max: hp })} + className={styles.hpMax} + min={0} + /> + {hpBonus > 0 && ( + (+{hpBonus}) + )} + ) : ( - {character.hp_max} + + {effectiveHpMax} + {hpBonus > 0 && ( + (+{hpBonus}) + )} + )}
diff --git a/client/src/utils/talent-effects.ts b/client/src/utils/talent-effects.ts index 9d7453c..2fe9d30 100644 --- a/client/src/utils/talent-effects.ts +++ b/client/src/utils/talent-effects.ts @@ -73,7 +73,7 @@ export function getTalentGearSlotsBonus(character: Character): number { } /** - * Get HP bonus from talents (e.g. Grit +2). + * Get HP bonus from talents (e.g. Grit +2 and +1 per level). */ export function getTalentHpBonus(character: Character): number { let bonus = 0; @@ -81,6 +81,9 @@ export function getTalentHpBonus(character: Character): number { if (typeof talent.effect.hp_bonus === "number") { bonus += talent.effect.hp_bonus; } + if (typeof talent.effect.hp_per_level === "number") { + bonus += talent.effect.hp_per_level * character.level; + } } return bonus; } diff --git a/server/src/db.ts b/server/src/db.ts index 7a03994..5c265bf 100644 --- a/server/src/db.ts +++ b/server/src/db.ts @@ -155,4 +155,12 @@ if (talentCount === 0) { } } +// --- Migration: update Grit talent to include hp_per_level --- +db.prepare( + `UPDATE game_talents SET effect = '{"hp_bonus":2,"hp_per_level":1}' WHERE name = 'Grit' AND effect = '{"hp_bonus":2}'`, +).run(); +db.prepare( + `UPDATE character_talents SET effect = '{"hp_bonus":2,"hp_per_level":1}' WHERE name = 'Grit' AND effect = '{"hp_bonus":2}'`, +).run(); + export default db; diff --git a/server/src/seed-dev-data.ts b/server/src/seed-dev-data.ts index 50d2429..e5b2208 100644 --- a/server/src/seed-dev-data.ts +++ b/server/src/seed-dev-data.ts @@ -202,7 +202,7 @@ export function seedDevData() { brynnId, "Grit", "+2 HP and +1 HP each level", - '{"hp_bonus":2}', + '{"hp_bonus":2,"hp_per_level":1}', null, ); diff --git a/server/src/seed-talents.ts b/server/src/seed-talents.ts index b012889..9b9edfd 100644 --- a/server/src/seed-talents.ts +++ b/server/src/seed-talents.ts @@ -23,7 +23,7 @@ export const SEED_TALENTS: SeedTalent[] = [ name: "Grit", source: "Fighter", description: "Gain +2 HP and +1 HP each level", - effect: { hp_bonus: 2 }, + effect: { hp_bonus: 2, hp_per_level: 1 }, }, // --- Priest ---