The Character Sheet
The character type is the most opinionated thing in this project, and the piece most likely to
need extending. Here is why it is shaped the way it is.
Abilities are stored flat
strength, agility, presence, toughness, spirit are five top-level numbers bounded to
-3..+6 — not a nested abilities object.
In most d20 systems you store a score and compute a modifier. In Pirate Borg the modifier is the only thing that exists — the 3d6 sum is discarded at creation and never referenced again. So there is nothing to derive, and a nested object would only add a path segment to every binding and expression. They render as steppers with min -3 and max 6, which is also the entire legal range, so the sheet cannot express an illegal character.
What is computed, and why
Nothing derived is stored. The computed fields are:
carryingCapacity—8 + strength.breathMinutes—max(1, 1 + toughness).armorTier,armorDie,arcanaBlocked— read throughload($self.armor).defenseDr—12 + (load($self.armor)?.defenseDrPenalty ?? 0).luckDie— read off the referenced class.className— the class's name, or "Landlubber".
If you add a derived value, add it as an expression field. Do not add a number the player has to remember to update.
The armor penalty trap
armor carries two penalty fields and they do not stack.
agilityDrPenalty is the penalty on general Agility tests. defenseDrPenalty is the total
penalty on defense specifically — it replaces the Agility penalty rather than adding to it.
This exists because conquistador plate is +4 DR on Agility tests but only +2 on defense. If you add new armor, set both fields explicitly. Setting only one will silently give you the wrong defense DR.
Daily resources are stored counts
ritualUses, prayerUses, and spellUses are plain numbers rendered as tappable dots, not a
computed resource array zipped against a class definition.
That is a deliberate exception to the usual Craft idiom, because these maximums are rolled, not derived: d4 + Spirit each dawn for rituals, d2 + Spirit for prayers and spells. There is no fixed maximum to compute against. The player rolls, sets the dots, and spends them down.
If you add a resource with a fixed maximum, do it the normal way instead: define the rows on the class file and zip them with a stored state map.
Conditions
conditions is a string array driven by ConditionToggles with a fixed option list. Add to the
options in the layout when your setting introduces a new status; the schema needs no change.