KINBARROW modding reference
v0.2.0+main.e011511f4 dataset 4a103dcdb658a75a

Campaigns as data

The content tree — the contract for the data/ tree.

The authored SOUL of a world — the cast, venues, factions, and laws of a test town — lives under campaigns/, the way the economy already lives in goods/, buildings/, and professions/. A campaign is INTERPRETED into a live world by crates/engine/src/campaign/ through the same constructors the fixture (crates/engine/src/seed.rs) uses; the equivalence lock proves the two build the SAME world, node-for-node and tick-for-tick.

data/campaigns/
└── <id>/                      one folder per campaign, id == folder name
    ├── campaign.ron           the manifest (Campaign)
    └── towns/
        └── <id>.ron           one authored settlement each (Town), id == stem
  • The manifest (campaign.ron, Campaign): name (the realm-scale region), default_seed, the death_day and start_day/start_hour, the towns list, the roads between named nodes (route + travel days, ), the procedural slots (a Worldgen slot is what makes the realm the realm — an authored coast plus a generated vale), and the opening chronicle.
  • A slot’s road_from names the settlement at the road’s other end, and has two forms because a realm’s towns are not all named at once: Town("Greyharbor") for an authored town, or Slot(0) for an EARLIER slot’s town — whose name is drawn from the realm seed at compose time and does not exist while the manifest is read. The index form is what lets a wide realm CHAIN its towns off each other instead of starring every road off the authored coast. It must point strictly backwards, and the validator refuses both a name no town bears and a forward reference.
  • A town (towns/<id>.ron, Town): its terrain (the ground its venues’ building types are permitted on), knowledge keys, wards, civic_venues and waters, power (the polity, its regent, the dead predecessor, the laws), factions (charter goal, confidence keys, hostilities, enforced laws, the seat-holder), folk, knowledge (gated events, spreading rumors, false beliefs), goods (coin, the data staples, the engine-seeded kits, the lock ladder), production venues with their owner-operators, free items, residences, and seeded rosters. Each trade’s artisan kit is bound from its data/professions/*.ron tool field, not restated per campaign.

Handles vs ids. Within a town, goods and venues are named by short handle strings ("fish", "forge") that the interpreter resolves to graph nodes as it mints them; characters, wards, factions, and laws are referenced by name/id. Ids that reach OUTSIDE the campaign — staple ids, building type ids, terrain tags, profession handles — resolve against the loaded Dataset.

The typing rule is the tree’s: enums for the semantic sets the engine branches on (Source, Ability, NeedRef, Demand, GoalKind, Placement, SlotKind, FactionKind), open handles for content. The one deviation from “every field explicit”: a campaign record is large (a Character carries ~25 fields), so absent optionals default — the record shows what is SET. Named struct forms (Character(...), Venue(...)) are used throughout, matching the goods/buildings style.

Validation is the cross-reference pass extended to campaign files (crates/engine/src/campaign/validate.rs — the one validator, ): every town id names a file, every venue’s building type is real and PERMITTED by the town’s ground (requires ⊆ terrain), terrain tags/staples/professions resolve, every good handle a produce/require/stock/carry names is minted by the town. Every OTHER cross-reference resolves too — a road’s endpoints, a ward’s adjacency, a law, a knowledge key, an org, a soul, a deity, an event and the truth edge a belief denies, an item, a place — against what the campaign itself mints, of the kind that field names (the interpreter resolves these by name, and a miss used to panic it). Note the rule: a campaign may reference only what a campaign MINTS, even though the interpreter would also find engine-minted nodes (a good’s display name, a profession, a furnishing). A broken campaign fails at BOOT loudly with file and reason.

Selecting a campaign. RPGPT_CAMPAIGN=<id> interprets that campaign (default: the fixture-equivalent thornwood). A modder ships a campaigns/<id>/ folder — a manifest plus town files — and the engine interprets it; the overlay/ledger mod model applies unchanged.