Files
the-sunken-reach/docs/SEEDS.md
T
joshuaandClaude Opus 5.5 e7b6e0065a V1.22.0: campaign settings for dice, scores, multiclassing, auto-rolls, XP and crits
- Settings grouped into Characters, Dice & rolls, Combat and Progression
- Dice: digital, physical or both; a campaign's own choice applies to every character
- Ability scores by 4d6, the standard array or point buy (27 points, 8 to 15)
- Track XP can be turned off for milestone levelling: XP is hidden and not awarded
- New settings saved ahead of their features: multiclassing, auto-rolled monster
  and NPC attacks, roll visibility, hidden monster rolls, HP on level up, crit rule
- docs/CAMPAIGN_SETTINGS.md lists every setting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:12:05 +02:00

10 KiB
Raw Blame History

World Seeds

A world seed is a ready-made campaign: locations, weapons, items, spells, monsters, NPCs with their wares, encounters with their rewards, and notes. A DM starts a campaign from one by typing its code.

This describes the code as it stands. Related: ITEMS.md, WEAPONS.md, SPELLS.md, MONSTERS.md, NPCS.md, ENCOUNTERS.md.

Contents

  1. The basics
  2. Starting a campaign from a code
  3. Adding a world
  4. What a seed file holds
  5. Checking a seed
  6. The database seed
  7. The worlds that come with the app

1. The basics

  • Every .js file in /backend/seeds is one world. The file says what its code is: six capital letters or digits.
  • Anyone who is logged in and knows a code can create that world as a new campaign. They become its DM and owner, exactly as with a blank campaign.
  • The same code can be used any number of times. Each use makes a separate campaign with its own invite code; nothing is shared between copies.
  • A seed only says what a campaign starts as. Once created, the campaign is the DM's to change, and changing the seed file later does not touch campaigns already made from it.
  • The folder is read each time a code is entered, so a new file is live as soon as it is on the server. No restart, no migration.

2. Starting a campaign from a code

On the Campaigns page, click + New Campaign. Under the form for a blank campaign is Or start from a ready-made world: type the code and click Create From Code. Capitals and spaces do not matter (aldenf, ALDENF and ald-enf are the same code).

The whole world is created in one go, and you land on its campaign page. If anything in it fails, nothing is created.

Reply Meaning
"A world code is 6 letters or numbers" What was typed is not six characters long
"There is no world with that code" No valid seed file has that code

The API is POST /campaigns/from-seed with { "code": "ALDENF" }. It answers with the new campaign, as POST /campaigns does.

3. Adding a world

  1. Write the world as a file in /backend/seeds, for example /backend/seeds/my-world.js (section 4).
  2. Run npm run seeds:check (section 5) and fix whatever it reports.
  3. Put the file on the server. The code works straight away.

A file with a mistake in it is never offered: its code simply answers "There is no world with that code" until the file is fixed. Two files cannot share a code; the one whose file name comes first keeps it.

To retire a world, delete its file. Campaigns already made from it stay.

4. What a seed file holds

A seed file is a JavaScript module that exports one object. Only code and campaign.name are required; every list can be left out.

module.exports = {
  code: 'MYWRLD',
  campaign: { name: 'My World', description: '...', startingGold: 20, startingItemCount: 2, startingSpellCount: 2 },
  locations: [{ name: 'The Old Mill', description: '...' }],
  weapons: [
    'dagger',                                      // a copy from the site library
    { library: 'longsword', isStartingOption: true }, // a copy, with changes
    { name: 'Millstone Hammer', damageDice: '2d6', damageType: 'bludgeoning', attackAbility: 'STR', minLevel: 3 },
  ],
  items: [
    { library: 'shield', isStartingOption: true },
    { name: 'Miller’s Charm', description: '...', effects: [{ stat: 'SKILL_PERCEPTION', amount: 1 }] },
  ],
  spells: [{ library: 'fire-bolt', isStartingOption: true }, 'fireball'],
  monsters: ['goblin', { name: 'Mill Wraith', ac: 13, maxHp: 30, attackBonus: 4, damageDice: '1d8+2 necrotic' }],
  npcs: [
    {
      name: 'Old Hob', role: 'Miller', disposition: 'FRIENDLY', description: '...', dmNotes: '...',
      wares: [{ item: 'Shield', price: 10 }, { weapon: 'Dagger', price: 2, stock: 3 }],
    },
  ],
  encounters: [
    {
      name: 'The Mill at Night', mode: 'BATTLE', location: 'The Old Mill', description: '...', dmNotes: '...',
      monsters: [['Goblin', 3], ['Mill Wraith', 1, 'The Miller’s Wife', 40]],
      npcs: ['Old Hob'],
      rewards: [{ gold: 25 }, { silver: 40 }, { weapon: 'Millstone Hammer', note: 'Behind the wheel' }],
    },
  ],
  sharedNotes: ['Welcome to My World...'],
  dmNotes: ['The secret of the mill...'],
};

The file is ordinary JavaScript, so it can build its lists however it likes (the Hollowcrown seed adds the whole spell library with a map).

Top level

Field What it is
code Six capital letters or digits. What a DM types
campaign The campaign's settings: name, and optionally description, startingGold, startingItemCount, startingSpellCount, skipMotivationRoll, showMonsterHp, areaAttackAcPenalty, maxEquipped, maxEquippedWeapons, subclassesEnabled, multiclassEnabled, diceMode, autoRollMonsters, autoRollNpcs, trackXp, hpOnLevelUp, abilityScoreMethod, rollVisibility, hideMonsterRolls, critRule. Anything left out takes the app's default
locations { name, description }
weapons, items, spells, monsters The pools. Each entry is a row or a library copy (below)
npcs NPC rows, each optionally with wares
encounters In the order they should list
sharedNotes, dmNotes Note bodies, in reading order: the first is shown at the top. Shared notes are for everyone, DM notes for the DM

Pool entries

An entry in weapons, items, spells or monsters is one of:

  • A library key such as 'longsword': a copy of that entry from the site's built-in library (src/game/data/*Library.js).
  • A library copy with changes: { library: 'longsword', isStartingOption: true, minLevel: 2 }. Any field of the row can be changed, including its name.
  • A full row, with the same fields the app's own forms have:
Pool Fields (required in bold)
Weapon name, damageDice, damageType, attackAbility (STR, DEX or FINESSE), properties, rangeNormal, rangeLong, damageScaling (1 to 3), minLevel (1 to 9), isStartingOption, effects, effectNote
Item name, description, damageDice, damageScaling, minLevel, isStartingOption, effects, effectNote
Spell name, level (0 to 9), damageDice, school, castingTime, range, duration, description, isStartingOption, revives
Monster name, ac, maxHp, speed, strScore ... chaScore, attackBonus, damageDice (may name its type: "1d6+2 necrotic"), challengeRating, specialTrait, notes

effects is a list of { stat, amount }, as described in ITEMS.md: armor is an item with an AC effect.

Within a pool a name can only be used once, because everything else in the file points at pool entries by name.

NPCs

name, role, disposition (FRIENDLY, NEUTRAL or HOSTILE), description (what players see), dmNotes, and for those who might fight ac, maxHp, attackBonus, damageDice.

  • A money changer has exchangeGold and exchangeCash together: exchangeGold: 1, exchangeCash: 50 is 1 gold for $50, either way.
  • A shopkeeper has wares, a list of things for sale (below).

Encounters

Field What it is
name
mode BATTLE (the default) or QUEST. Buying and money changing only happen in a quest
description The scene, shown to the players in it
dmNotes DM only
location A location's name
monsters [monster name, how many, display name, max HP]. The last two are optional; more than one gets "#1", "#2"...
npcs NPC names, or { name, hp } to set the hit points for this scene
rewards Lined up unawarded (below)
wares For sale at the scene itself, with nobody behind it: a market, a vending machine

A reward is exactly one of gold, silver, copper, cash (an amount), weapon or item (a name), with an optional note. A weapon or item can also have quantity and level.

A ware is { weapon | item, price } with optional currency (GOLD, the default, SILVER, COPPER or CASH), level and stock (left out: no limit).

A reward's or ware's level defaults to the entry's own starting level, and cannot be below it.

Limits

Text fields have the same length limits as the app's forms (for example 4,000 characters for an encounter's DM notes and 10,000 for a note), so that everything a seed creates can still be edited on screen. The checker says which field is over.

5. Checking a seed

npm run seeds:check

lists every file in /backend/seeds with its code and what it holds, or everything wrong with it:

✓ aldenfell.js — code ALDENF — Aldenfell
    24 weapons (16 starting), 31 items (10 starting), 45 spells (30 starting), 21 locations
    15 monsters, 13 NPCs (8 wares), 38 encounters (40 rewards, 9 wares), 23 notes
✗ my-world.js
    npcs.0.desciption: Unrecognized key: "desciption"
    Encounter "The Mill at Night": there is no monster called "Gobblin"

It checks the things that would otherwise only show up as a half-working campaign:

  • A field name that is not one of the fields above (a typo).
  • A library key that is not in the library.
  • The same name twice in one pool.
  • An encounter, reward or ware that names a location, monster, NPC, weapon or item the file does not have.
  • A reward that is not exactly one thing, or gear handed out below its starting level.
  • An exchange rate with only one half.

npm test runs the same check on every seed in the folder.

6. The database seed

npx prisma db seed creates the admin account if it is missing, and nothing else. It never adds a campaign: worlds come from codes, in the app. See the README for the account it creates and --reset.

7. The worlds that come with the app

Code World From
ALDENF Aldenfell The Aldenfell Campaign Book
HOLLOW Hollowcrown: The Rift Between Worlds The Hollowcrown Dungeon Master's Book (V3)

Change a world's code by editing code in its file.