import type { Character } from "../types"; import HpBar from "./HpBar"; import TorchTimer from "./TorchTimer"; import styles from "./CharacterCard.module.css"; import { calculateAC } from "../utils/derived-ac"; import { getTalentHpBonus } from "../utils/talent-effects"; import { getEffectiveStat } from "../utils/talent-effects"; import { getModifier, formatModifier } from "../utils/modifiers"; function getAvatarUrl(character: Character): string { const style = (character.overrides?.avatar_style as string) || "micah"; const seed = encodeURIComponent(character.name || "hero"); return `https://api.dicebear.com/9.x/${style}/svg?seed=${seed}`; } const STATS = ["STR", "DEX", "CON", "INT", "WIS", "CHA"]; interface CharacterCardProps { character: Character; onHpChange: (characterId: number, hp: number) => void; onUpdate: (characterId: number, data: Partial) => void; onClick: (characterId: number) => void; canEdit?: boolean; focusSpell?: string; } export default function CharacterCard({ character, onHpChange, onUpdate, onClick, canEdit = true, focusSpell, }: CharacterCardProps) { return (
onClick(character.id)} style={{ borderLeftColor: character.color, borderLeftWidth: "3px" }} >
{character.name} {character.title ? ` ${character.title}` : ""} Lvl {character.level}
{character.ancestry} {character.class}
{focusSpell && (
● Focusing: {focusSpell}
)}
e.stopPropagation()}> onHpChange(character.id, hp)} />
AC {calculateAC(character).effective}
{character.luck_token ? "\u2605" : "\u2606"} { const isLit = character.torch_lit_at !== null; onUpdate(character.id, { torch_lit_at: isLit ? null : new Date().toISOString(), } as Partial); }} />
{STATS.map((stat) => { const value = getEffectiveStat(character, stat); const mod = getModifier(value); return ( {stat} {formatModifier(mod)} ); })}
XP {character.xp} / {character.level * 10}
); }