Your own JSON
Six ways to put data and behaviour the format never anticipated into your world.
Every object in the format rejects a property it does not recognise. Adding your own goes through one of these.
1. The extra bag
Most entities carry an extra object. The engine passes it through untouched.
{ "id": "bog_hound",
"extra": { "morale": 6, "packLeader": true, "houseRule": "hates iron" } }ref path walks, so a formula cannot read it. Use a flag for data a formula needs.2. A custom predicate
Every requirement carries custom, which takes a raw predicate. It runs alongside the other clauses. All must hold.
"requires": {
"minLevel": 3,
"custom": { "any": [ { "gte": [ { "ref": "reputation.wardens" }, 20 ] },
{ "test": { "ref": "flags.spoke_for_us" } } ] } }3. Your own events
The emit effect plus on: "custom" is an event bus. Quests, triggers, reactions, and opportunities all watch it.
// an effect emits
{ "emit": { "event": "bell_rung", "data": { "tower": "west" } } }
// a trigger listens
{ "id": "the_answer", "on": "custom", "event": "bell_rung",
"mode": "once", "effects": [ ... ] }
// an objective completes on it
{ "id": "ring_it", "kind": "reach", "target": "bell_rung" }4. Flags
An arbitrary key holding a string, a number, or a boolean. start.initialFlags seeds them, setFlag writes them, flags.<name> reads them. Nothing checks the spelling. Keep a prefix convention. See linking for the ones the engine writes.
5. Tags
Most entities carry a free tags list. Two places read them:
npc.shop.buysTags | What a merchant will buy. |
damageInteraction.unless | Damage tags that cancel an immunity. Immune to slashing except silvered. |
Everywhere else they are yours, for filtering and for your own predicates.
6. Layering on another world
{ "id": "more_husks", "extends": "aurendel@1.0.0",
"content": { "monsters": [ { "id": "husk", "xp": 25 },
{ "id": "old_husk", "$delete": true } ] } }| Collections | Merge by id. An existing id is overridden field by field. A new one is added. |
| Other objects | Merge key by key. |
| Other arrays | Replaced whole. |
{ "$delete": true } | Removes an inherited entry. |
| Cycles | Rejected. |
extends overrides whole entries; it does not supply half of one.Beyond JSON: mods
A mod is code in a sandbox. Your world lists the ones it expects under mods, each pinned by id and by a fingerprint of its contents. A changed mod is reported, not run.
Ten attachment points:
before an action · after an action · an unrecognised operation · an occasion · after the world settles · after time passes · whether a trigger fires · passives · reactions · events being emitted
- No clock, no ambient randomness. A modded run still replays exactly.
- A mod cannot make a valid world invalid, so it cannot block an export.
While building: prefabs
- A template for an entry, with named parameters and lookups against your own tables.
- Four strings become an eleven key entry.
- Instances stay linked. Editing a prefab shows the fan out first, and which fields you overrode by hand.
- Prefabs stay in the studio and never appear in the exported JSON.