import type { Creature } from "@initiative/domain"; import { calculateInitiative, formatInitiativeModifier, } from "@initiative/domain"; import { PropertyLine, SectionDivider, TraitEntry, TraitSection, } from "./stat-block-parts.js"; interface DndStatBlockProps { creature: Creature; } function abilityMod(score: number): string { const mod = Math.floor((score - 10) / 2); return mod >= 0 ? `+${mod}` : `${mod}`; } export function DndStatBlock({ creature }: Readonly) { const abilities = [ { label: "STR", score: creature.abilities.str }, { label: "DEX", score: creature.abilities.dex }, { label: "CON", score: creature.abilities.con }, { label: "INT", score: creature.abilities.int }, { label: "WIS", score: creature.abilities.wis }, { label: "CHA", score: creature.abilities.cha }, ]; const initiative = calculateInitiative({ dexScore: creature.abilities.dex, cr: creature.cr, initiativeProficiency: creature.initiativeProficiency, }); return (
{/* Header */}

{creature.name}

{creature.size} {creature.type}, {creature.alignment}

{creature.sourceDisplayName}

{/* Stats bar */}
Armor Class {creature.ac} {!!creature.acSource && ( {" "} ({creature.acSource}) )} Initiative{" "} {formatInitiativeModifier(initiative.modifier)} ( {initiative.passive})
Hit Points{" "} {creature.hp.average}{" "} ({creature.hp.formula})
Speed {creature.speed}
{/* Ability scores */}
{abilities.map(({ label, score }) => (
{label}
{score}{" "} ({abilityMod(score)})
))}
{/* Properties */}
Challenge {creature.cr}{" "} (Proficiency Bonus +{creature.proficiencyBonus})
{/* Spellcasting */} {creature.spellcasting && creature.spellcasting.length > 0 && ( <> {creature.spellcasting.map((sc) => (
{sc.name}.{" "} {sc.headerText}
{sc.atWill && sc.atWill.length > 0 && (
At Will:{" "} {sc.atWill.join(", ")}
)} {sc.daily?.map((d) => (
{d.uses}/day {d.each ? " each" : ""}: {" "} {d.spells.join(", ")}
))} {sc.restLong?.map((d) => (
{d.uses}/long rest {d.each ? " each" : ""}: {" "} {d.spells.join(", ")}
))}
))} )} {/* Legendary Actions */} {!!creature.legendaryActions && ( <>

Legendary Actions

{creature.legendaryActions.preamble}

{creature.legendaryActions.entries.map((a) => ( ))}
)}
); }