How the Character Sheet Thinks
The character schema has one design rule: store decisions; derive everything else. A sheet stores the six base ability scores, level and experience, references to species, class, subclass, background, feats, and gear, current-state counters (hit points, slots used, conditions, exhaustionLevel) — and computes the other 114 fields.
The derivation chain
- Each final ability score is
min(20, base + feat increases + attuned-item overrides)— in this revision the background's increases arrive via its Origin-feat pipeline and the ASI feats, sobasemeans the raw rolled score. Modifiers, saves, and all 18 skills derive from there, readingskillProficienciesfor proficiency and Expertise. - armorClass reads the equipped armor file's
acBase,addDexterity, anddexterityCap, adds shield, magic bonuses, andmiscArmorBonus, and falls back to unarmored math — including class unarmored defenses. - Attack lines —
mainHandAttackBonus,mainHandDamageDice, and the off-hand set — key off the weapon file's finesse and ranged flags and magic bonus. The weapon'smasteryproperty rides along for the GM to apply. - Spell slots derive from
casterLevel—ceil(level × casterProgression − 0.01)so 2024 half-casters round up (a level 5 Paladin has 2nd-level slots) — with Warlock Pact Magic on its own computed track. - Level gating is a formula idiom:
classFeaturesLevel1–20and the subclass fields (levels 3–20 — nothing earlier exists in this revision) readfeaturesLevelNoff the linked files; the layout shows each block only whenlevelqualifies. A character levels up by changing one number.
Aggregation by shared field names
The sheet never knows which feat or relic you took — it sums every instance of shared field names across the feats array and three guarded attunedItem slots: strengthIncrease…charismaIncrease, acBonus, initiativeBonus, speedBonus, hpBonus, hpBonusPerLevel, saveBonusAll, plus score-override fields for Ogre-Power-style items. Give any feat or magic item those fields and it wires itself into every derived number. That is the whole integration contract — @Missing file and @Missing file are the worked examples.
The creation flow
The character type also carries the guided creation flow — 68 screens walking a player from biography through class, subclass, fighting styles, spell picks, background, and equipment to a finished level-5 hero. Its option screens hold references into the content folders, which is the sharpest gotcha in the project: if you copy this project's types into another project, every option's referenceId still points at this project's files and must be remapped. Edit the flow in the app or with care; it is data on the type, pushed with the type.
Platform limits that shaped this type
A type holds at most 200 top-level fields (this one sits at 195 — nest display-only fields into objects, as coins, deathSaves, personality, and skillProficiencies already are); formula nesting caps at depth 64 (split ternary tables into tier formulas); reference-picker filters only work on top-level fields — which is why preparedSpellsLevel1–9 live at the top, each filtered to spells of its level on the character's class. Schema edits push with craft push --include-types (unscoped) after craft check; the server validates against every existing file and names each record a change would break.