Skip to content

Building an app with OCF

These guides are for people building apps that use OCF characters: a VTT, a character builder, a campaign manager, a Discord bot, a game server. They explain how to load packs, store a character, show a sheet, change it, roll for it and upgrade it, using the reference engine.

The specification defines the formats and the engine calls for people implementing them. These guides cover using them. When a guide and the spec disagree, the spec wins.

Parts of the engine interface marked PROVISIONAL in the spec may still change.

The most important idea in OCF is the split between the engine and your app:

The engine knows the rules. The app owns the experience.

You never write a game’s math. Your app doesn’t know that a 5E Strength modifier is floor((score - 10) / 2), that Mage Armor competes with Unarmored Defense, or that a FitD roll of 6 is a full success. The packs say that, and the engine works it out. Your app asks the engine questions and shows the answers.

The engine does Your app does
Computes every value, with a breakdown of where it came from Stores characters, however it likes
Says which choices are pending and which options are eligible Decides whether to ask the player or pick the recommended one
Applies changes, checking them against the rules Decides who may change what, and when
Builds rolls, does the dice math, proposes the resulting changes Decides who rolls the dice and how they look, and applies the changes
Upgrades saved characters to new pack versions Decides when to upgrade, and shows the player what changed
Returns keys, codes and data Turns them into text in the player’s language
Never tracks time Turns temporary effects (Bless, Poisoned) on and off

So “implementing characters” in your app means four things: storing a file, calling the engine, rendering what it returns, and routing changes back through it.

Almost every OCF app runs the same loop:

The OCF loop: packs load into a ruleset once; computeSheet turns the character file into a sheet; your UI renders it; the player's edits, picks and rolls become changes; applyChanges writes them into a new character file. Packs loadRuleset Ruleset loaded once, cached every call uses it Character file inputs, items, picks computeSheet Sheet values, actions, choices render Your UI the player acts edits, picks and rolls startAction · continueAction Changes checked against the rules applyChanges then save
  1. Load the packs a character uses into a ruleset, once.
  2. Call computeSheet and render the result.
  3. When the player does something, turn it into changes (or run an action, which proposes changes) and call applyChanges.
  4. Save the new character file and go back to step 2.

The character file only ever holds base state: the inputs, owned items and picks. Everything your UI shows comes from computeSheet, so the sheet can never disagree with the rules.

Read them in order the first time.

  • Quick start: load a pack, compute a sheet, apply a change, in about twenty lines.
  • Implementing OCF: the view from above: how much of OCF your app needs, level by level, and where the engine runs, with Realms of Shod as a worked example.
  • Apps not written in JavaScript: ports, the test suite, and running the reference engine from another language.
  • Packs and rulesets: where packs come from, load order, homebrew, pinned versions and caching.
  • Storing characters: the character file, IDs, your own data in extensions, change logs, undo and sync.
  • Showing a sheet: reading computeSheet, breakdowns, display hints, competing options and rules issues.
  • Text and translation: turning keys and codes into text in the player’s language.
  • Showing text: a helper that turns engine output into finished text: prompts, options, issues, rolls and dice.
  • Rolling dice: actions, checkpoint policies, your own dice, paused actions and logs.
  • Other characters: attacks on a target, monsters and NPCs, effects cast by one character on another, linked sheets such as a FitD crew.
  • A game-specific app (a 5E builder) can refer to fields and content by ID: 5e-srd-5.1:combat.ac, 5e-srd-5.1:classes.fighter. IDs are permanent and never reused, so this is safe. Hand-design the sheet and let the engine fill it in.
  • An any-game app (a VTT) can’t know any IDs ahead of time. It builds its sheet from the pack’s display hints and its actions and choices from what computeSheet lists. This is more work up front, and any pack works afterwards, homebrew included.

Most apps end up in between: a hand-designed sheet for the games they care about most, and a generic one for the rest.