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 race, class, subclass, background, feats, and gear, current-state counters (hit points, slots used, conditions, exhaustionLevel) — and computes the other 115 fields.
The derivation chain
- Each final ability score is
min(20, base + racial increase + feat increases + attuned-item overrides)— the race file carriesstrengthIncrease…charismaIncrease, so choosing a race applies its scores with nothing to remember. Modifiers, saves, and all 18 skills derive from there, reading proficiency choices and Expertise fromskillProficiencies. - armorClass reads the equipped armor file's
acBase,addDexterity, anddexterityCap, adds shield and 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, magic bonus, and your modifiers. - Spell slots derive from
casterLevel—ceil(level × casterProgression − 0.01)so half-casters round correctly — with Warlock pact magic computed on its own track. - Level gating is a formula idiom:
classFeaturesLevel1–20and the subclass equivalents readfeaturesLevelNoff the class and subclass files, and 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 the three guarded attunedItem slots: strengthIncrease…charismaIncrease, acBonus, initiativeBonus, speedBonus, hpBonus, hpBonusPerLevel, saveBonusAll. Give any feat or magic item those fields and it wires itself into every derived number, attributed and stackable. That is the whole integration contract — @Missing file and @Missing file are the two worked examples.
Platform limits that shaped this type
Learned the hard way, worth knowing before you edit: a type holds at most 200 top-level fields (this one sits at 196 — nest display-only fields into objects, as coins and personality already are); formula nesting caps at depth 64 (split long ternary tables into tier formulas); reference-picker filters only work on top-level fields, which is why the nine preparedSpellsLevelN arrays live at the top. Schema edits push with craft push --include-types (unscoped — pathspec pushes refuse types) after craft check; the server validates the new schema against every existing file and names each record a change would break.