No active sessions yet.

No games yet.

No conversations yet.

No characters yet.

Designing a plugin

How to make a system that's fun to play, not just one that saves without errors.

A plugin that validates isn't finished. Most broken systems here save cleanly and then do nothing, or do something nobody notices. This page is about getting past that.

Start with the decision

A system is only interesting if it makes the player choose something. Before you declare a field, finish this sentence:

Because of this system, the player has to decide whether to ___.

  • Hunger: spend a day foraging, or push on hungry.
  • Infection: treat the wound now with the only medicine, or save it.
  • Reputation: help the smugglers, or stay welcome in town.

If you can't fill the blank, you have a number, not a system.

Every meter needs a way in and a way out

For each field, write down:

  1. What raises it. Time, an action, a place, an enemy.
  2. What lowers it. An item, a rest, a person, a ritual.
  3. What the fix costs. A trip, money, a risk, a turn.

A meter nothing lowers is a countdown the player can't fight. A meter nothing raises never moves. A fix that costs nothing isn't a decision.

The fixes usually live in the world, not the plugin: the herbalist who sells the cure, the shrine that clears the curse. A plugin that ships without them is half a system. Say which items, NPCs or places your plugin expects in its description.

Make it visible

The player can't respond to what they can't see.

  • Tiers are how the narrator notices. Write tier prose that shows up in the scene: "his hands have started to shake" beats "infection: moderate". See Fields.
  • Leave the calm tier silent. If the lowest band says something, every turn carries a status report.
  • Put the important meter on the HUD. If losing to it ends the run, it belongs on screen.
  • Warn before you punish. Let a tier say "your stomach is hollow" before the damage starts. A penalty with no warning feels like a bug.

Tune with dials, not literals

Every number a designer might want to change should be a dial. Rates, thresholds, costs. Then an author can make your plague deadly or mild without opening code.

  • Pick defaults by playing. Play ten turns at the default. If the meter never reaches a tier, the rate is too low. If the player can't do anything else, it's too high.
  • Separate per-hour and per-turn rates if your system uses time. See Minutes are not time.
  • Let zero mean off. An author who sets damage to 0 wants no damage. Check the dial before calling $.damage, since damage floors at 1.

Play nicely with other plugins

  • Ask before you read. $.hasPlugin('hunger') && ..., never a bare read of someone else's field.
  • Offer events instead of reaching in. If other systems might want to stop your action, declare an event and let them veto it.
  • Don't take a seam unless you must. Only one plugin can own each seam. Taking one locks every other plugin out of it.

The test for all of it: delete either plugin in any pair and the other still makes sense. See Cooperation.

Keep it small

One plugin, one idea. "Survival" as a single plugin with hunger, thirst, cold and sleep can only be all on or all off. Four small plugins let an author take thirst and leave the cold.

Small plugins are also easier to debug. When a system does nothing, you want three hooks to read, not thirty.

When it does nothing

Most dead systems are one of these:

  • A field that's null. Nobody has it, so every comparison fails. See Missing values.
  • Reading a different place than you wrote. Values filled in a table, hook reads $.location.fields.
  • The wrong trigger. An onTimePasses rule in a game with no clock, or an onDamage rule that should have been onAttack.
  • A frozen number. A literal where a dial should be, now out of date.

The full list is on Common mistakes.

Checklist

  • I can say what decision this system forces.
  • Every field has something that raises it and something that lowers it.
  • The fix costs the player something.
  • Tier prose reads like narration, and the calm tier is silent.
  • Every tunable number is a dial.
  • Reads of other plugins' fields are guarded with $.hasPlugin.
  • Deleting the plugin leaves a working game.
  • I played ten turns with it on.