import {
type Creature,
calculateInitiative,
formatInitiativeModifier,
} from "@initiative/domain";
interface StatBlockProps {
creature: Creature;
}
function abilityMod(score: number): string {
const mod = Math.floor((score - 10) / 2);
return mod >= 0 ? `+${mod}` : `${mod}`;
}
function PropertyLine({
label,
value,
}: {
label: string;
value: string | undefined;
}) {
if (!value) return null;
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})
{/* Traits */}
{creature.traits && creature.traits.length > 0 && (
<>
{creature.traits.map((t) => (
{t.name}. {t.text}
))}
>
)}
{/* 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(", ")}
))}
))}
>
)}
{/* Actions */}
{creature.actions && creature.actions.length > 0 && (
<>
Actions
{creature.actions.map((a) => (
{a.name}. {a.text}
))}
>
)}
{/* Bonus Actions */}
{creature.bonusActions && creature.bonusActions.length > 0 && (
<>
Bonus Actions
{creature.bonusActions.map((a) => (
{a.name}. {a.text}
))}
>
)}
{/* Reactions */}
{creature.reactions && creature.reactions.length > 0 && (
<>
Reactions
{creature.reactions.map((a) => (
{a.name}. {a.text}
))}
>
)}
{/* Legendary Actions */}
{creature.legendaryActions && (
<>
Legendary Actions
{creature.legendaryActions.preamble}
{creature.legendaryActions.entries.map((a) => (
{a.name}. {a.text}
))}
>
)}
);
}