Extending the System
Everything above describes the file types this SRD already has. This chapter is for the two things you'll eventually want to do that aren't covered by "add another file of an existing type": adding a genuinely new file type, and changing an existing one without breaking what's already here.
Adding a new file type
Reach for a new type when content has a shape none of the existing ten fit — a Faction, a Deity, a structured Bestiary entry with real numeric fields instead of statBlock prose. Before creating one, check whether an existing type actually covers it with different field values (a new equipment category, a new rule file) — that's cheaper and keeps character.inventory and similar references working across everything.
If you do need a new type:
- Decide
content_type—jsonwith a schema for anything looked up as structured data;markdownfor prose read start to finish. Every type in this project is one or the other; there's no in-between. - Give it a
designationonly if it needs special platform behavior —character,location,game_start, andgm_instructionsall carry one here because the platform treats those kinds of file specially (character sheets, maps, session starts, GM behavior). A new Faction or Deity type almost certainly wantsdesignation: null, likeskill,spell,power,equipment,creature, andvehicleall are. - Match
context_visibilityto the existing pattern — every reference type here is"searchable", meaning it surfaces in context without being pinned. Don't default to"hidden"for compendium content; that's for things like raw config, not lore a Games Master might need mid-session. - Build the schema by copying the closest existing type's shape (
equipmentfor a new item-like type,skill/spell/powerfor a new simple glossary-like type) rather than starting from a blank object — consistency with what's already here matters more than a theoretically ideal field list. - Let the layout auto-generate unless you have a specific reason to hand-design one. Every JSON type in this project either uses a generated layout or a hand-built one that mirrors the same card/section pattern (hero image, then cards grouping related fields, then optional sections that hide when empty) — match that pattern if you write your own.
- Create the matching folder (or let it auto-create) and file new entries there, following the conventions of whichever existing chapter above is closest in spirit.
Changing an existing type
The types in this project are load-bearing — character.skills resolves against every file in /Skills/, character.powers against every spell and power, character.inventory against every equipment file. Before changing a schema:
- Adding an optional field is safe — every existing file just won't have it set, and the layout can choose to hide the field when empty (see how
notes,background,description, andpersonalitysections all vanish oncharacterwhen blank). - Renaming or removing a field breaks every file that used it and any expression or reference that pointed at it. Check for the field name across the project with a broad search before you rename one, and update every file that had it set.
- Changing a reference field's target type (say, letting
character.inventoryalso referencecreaturefiles, for a mount) needs the target's filter and slug qualification rules worked out deliberately — re-read the reference-field platform instructions before doing this, and qualify content slugs as"<type>/<slug>"wherever the field now spans more than one type, same ascharacter.powersalready does forspell/power.
Adding a computed field
Every derived-looking value in this project today (character.attributes.damageModifier, skill.baseValue, a creature's Damage Modifier inside its statBlock) is hand-entered text or a number, not a computed expression — see Characters and Skills for why that's the deliberate baseline here. If you want to promote one of these to an actual computed field (so damageModifier recalculates live from characteristics.strength and characteristics.size instead of being typed in at character creation):
- Read the reference-and-computed-fields platform instructions first — the expression language,
$self, andload()all have specific rules, especially once a reference field is involved. - Write the expression on the schema property in
/file-types/character.json, not in any individual character file — computed fields are defined once, at the type level. - Remember computed values are read-only on data files: once
attributes.damageModifieris computed, no character file writes that field directly anymore, and any character that had a hand-entered value there will show the computed one instead. Decide if that is actually the outcome you want for every existing character before you flip the switch — it will change values on rosters built under the old, hand-entered rule.
When in doubt
Every chapter before this one exists because a builder six months from now will look for the pattern the existing files already follow. If you're adding something and can't find a precedent, that's a signal to either follow the closest existing shape deliberately, or to write a short addition to this manual explaining the new pattern you chose and why — so the next builder doesn't have to reverse-engineer it from the files alone.