Skip to content

The quest generation system

Procedural quests are usually filler. Go somewhere, fetch a thing, come back. Players read the pattern within an hour, and the quests never touch the world around them.

@kiberon-labs/quests takes the other route. It builds quests as graph grammars (rewrite rules over a graph) and grounds every one in your live world state. The result reads authored, plays with real choice, and costs a fraction of hand-writing the same volume.

It does not replace handmade quests. A designer with a specific idea still wins on the set piece a whole game bends around. This raises the floor instead: the “always something to do” layer, kept alive and interactive rather than mechanical.

Decide first how the generator relates to the world.

  • Authorial: it invents facts to fit the quest. Need a warlord menacing a village? It creates one. Fast to build, but the quest floats free of your world, because it does.
  • Simulationist: it invents nothing. Every quest grounds in facts already true: real entities, real relationships, real needs. If a giver wants a foe dead, that foe exists and already threatens them.

This library is simulationist. It is the harder problem and the more interesting one. You get quests that read like they belong, because they do.

Every quest shares one spine: exposition sets the stakes, a body does the work, a reward closes it.

Generation starts by picking an archetype (slay, escort, defend, and 11 more). An archetype is an abstract layout, nodes and relationships attached to nothing real yet. A slay quest is just “a foe exists, the foe must die, killing it resolves a threat.”

flowchart LR
    A[Select archetype<br/>e.g. SLAY] --> B[Lay out abstract<br/>nodes & relationships]
    B --> C[Reify against<br/>world state]
    C --> D[Expand slots:<br/>complications,<br/>nuance]
    D --> E[Walk the graph:<br/>side effects,<br/>exposition, presentation]
    C -->|no valid match| X[Discard / try<br/>another archetype]

Reification binds the abstract template to your world, one pass at a time. Each pass matches what is true right now.

Take slay. Abstractly, something must die. To reify, find a foe with a menaces edge to the giver, or to something the giver cares about. No such foe, no quest here, and the generator moves on. That discipline is what keeps every quest grounded.

Then it opens slots: “what stands in the way?”, “why has the giver not handled this already?”, “what does the kill cost?”. Each slot fills by reading nearby relations in the graph, and filling one can open more. The quest grows outward from the seed and stays anchored to real relationships.

The graph holds a village elder who fears a frost troll, and the troll raids the village granary. Those facts already exist. Elder becomes giver, troll becomes foe, granary becomes stake.

A slot reads the troll’s edges: it dens in a cave held by a poacher gang. A real obstacle, so it becomes a complication. Another slot asks how you get past the gang, and it offers three routes: fight through, bribe, or argue the troll is bad for their business too.

The reward comes from what the elder can actually offer: stored grain and a small standing boost. The quest reads authored, and every piece of it was already true.

A story is one chain of events. A quest gives choice. That difference is where the design effort goes, and where the replay value comes from.

  • Player agency. Every obstacle offers multiple routes: fight, talk, or pass a skill check. The obstacle is fixed; the way past it is not.
  • Optional branches. A player who wants more can divert into a harder side thread instead of being locked to one path.
  • Multiple endings. Place several exit points, and gate the short ones behind harder play so the quick road still costs something. The choice stays real.
  • Cheap branches buy breadth. When generation is cheap, spend branches freely. You route players into genuinely different content that would be expensive to hand-write.
  • Dynamic gates. Check world or character state at the moment the player arrives. Pass it and a new path opens; fail it and the mainline holds. Nothing breaks.
  • Partial generation. Lay out the spine now, build each complication when the player reaches it, using the world state at arrival.

Budget for rare, high-interest moments. Hold some archetypes, complications, and rewards behind low trigger rates so they surface now and then and land hard.

This matters most for long-term players. After enough quests they start to feel the machinery underneath. Rare events break that read and keep the world surprising, well past the point where common content runs dry.

The hard parts, and where they actually sit

Section titled “The hard parts, and where they actually sit”

The difficulty is not where most people expect.

  • The graph grammar is solved. Rewriting abstract nodes into concrete subgraphs works.
  • The corpus is the work. The system is only as good as the library of interactions it can fire, and that library is specific to your game. Writing generic, parameterized modules that survive across contexts is the goal of this package.
  • Reifying into play is its own job. A graph is not a playable quest. You need a walk that turns nodes into side effects: spawn entities, set flags, present exposition. Most of the quality lives there.
  • Execution decides quality. “Slay a beast” reads dull until the fight is hard and demands planning. Structure sets the stage; moment-to-moment mechanics decide whether it is worth doing.

Expository dialogue between beats is one place patterns show. That is a fair use for an LLM, either live at high temperature or ahead of time to build a large dialogue corpus.

A simulationist generator can hit a wall: sometimes no fact matches a slot, or an archetype will not reify at all. Three answers, and they combine.

  • Tune the triggers so the failure rate approaches zero. Most demanding, but a tuned system rarely dead-ends.
  • Generate in batches and rank. Keep the valid candidates, then ship the most exciting one. Robustness and quality from the same step.
  • Collapse the slot. Drop the piece that will not fill and proceed. The quest is simpler, still valid, still grounded.
  • Nodes vs. properties. Use nodes for discrete things (a person, a place, an object) and properties for scalars (an NPC’s strength). Node count drives matching cost, so keep the graph lean.
  • Do not match the whole world. Scope the matchable graph to what a giver would plausibly know, or to a local area.
  • Localize and cull. Run matching in phases and drop nodes and rules once later passes no longer need them. One giant global match becomes a series of cheap local ones.

Content here is nodes, relationships, and rules, not hand-wired quest scripts. A mod does not author bespoke quests. It exposes a few rules, and the generator folds its content into the world: the new NPC becomes a giver, the new location a foe’s den, the new interaction a complication. The base game needs to know nothing about the mod in advance, and an opt-out flag keeps a mod’s hand-written NPC out of generated quests.

Built by Kiberon Labs