PluginBench
Skill
Pass
Audit score 90

dialogue-systems

gamedev-skills/awesome-gamedev-agent-skills

Build branching dialogue trees with Ink, Yarn Spinner, or a custom JSON runner.

What is dialogue-systems?

Design conversations as node/choice graphs with conditions, variables, and localization. Choose between proven authoring tools (Ink or Yarn Spinner) or write a minimal data-driven runner. Use when designing branching dialogue, choice menus, or narrative state that affects later dialogue.

  • Model conversations as a graph with nodes, choices, conditions, and variables
  • Choose between Ink (prose-first), Yarn Spinner (node-based), or custom JSON/resource runners
  • Separate dialogue logic from UI—wire scripts into your game loop to advance lines and present choices
  • Gate dialogue options with conditions and mutate narrative state via variable writes
  • Localize dialogue by keying lines to string-table IDs instead of hardcoded text
  • Validate dialogue branches to ensure every node terminates, branches, or jumps correctly

How to install dialogue-systems

npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill dialogue-systems
Claude Code
Cursor
Windsurf
Cline

How to use dialogue-systems

  1. 1.Choose your authoring approach: Ink (prose-first, writer-friendly), Yarn Spinner (node-based, explicit commands), or a custom JSON graph runner
  2. 2.Define your node contract—each node yields a line, choices, a command, or an end/jump
  3. 3.Separate variables from flow: maintain a variable store (booleans, numbers, strings) that dialogue reads and writes
  4. 4.Author with line IDs, not raw strings, so displayed text comes from a localization string table
  5. 5.Implement a runner as a state machine: current node → emit content → wait for input → advance
  6. 6.Walk each dialogue branch to verify conditions, variable writes, and that every path reaches an end or valid jump

Use cases

Good for
  • Design a branching NPC conversation where player choices affect available options and narrative flags
  • Build a choice-driven narrative game or visual novel with persistent state across scenes
  • Create a dialogue system that works engine-neutrally across Godot, Unity, or custom engines
  • Implement a bribery or persuasion mechanic where dialogue choices depend on player resources or prior decisions
  • Author dialogue-heavy CYOA (Choose Your Own Adventure) content with a writer-friendly tool like Ink
Who it's for
  • Game designers building narrative-driven or dialogue-heavy games
  • Writers authoring branching dialogue and interactive fiction
  • Developers integrating dialogue systems into Godot, Unity, or custom engines
  • Teams needing engine-neutral dialogue that persists state across sessions

dialogue-systems FAQ

Should I use Ink, Yarn Spinner, or a custom runner?

Use Ink for prose-first, writer-friendly dialogue with complex flow control; Yarn Spinner for node-based dialogue with explicit engine hooks; custom JSON runner for minimal dependencies and full control over a simple branching tree. Don't build a language—build a graph.

How do I avoid hardcoding dialogue strings?

Author against line IDs (e.g., 'DLG_GUARD_001') from the start, then populate a string table keyed by locale and line ID. The runner looks up the ID at display time, making localization a data change, not a rewrite.

How do I prevent variables from being mutated twice when a player revisits a node?

Apply variable mutations (`set`) on the transition (choice or next), not on the line node itself. Alternatively, guard mutations with a seen-flag so they only fire once.

What's the difference between Ink's '*' and '+' choices?

'*' is once-only: the choice disappears after selection. '+' is sticky: the choice remains available on revisits. Use '+' for looped menus; use '*' for one-time branching.

How do I persist dialogue state across game sessions?

Store narrative variables and seen-flags separately from the dialogue runner, then use the `save-systems` skill to persist them to disk. Load the state when the game starts.

Full instructions (SKILL.md)

Source of truth, from gamedev-skills/awesome-gamedev-agent-skills.


name: dialogue-systems description: > Build branching dialogue and narrative — a node/choice graph with conditions, variables, and localization hooks — and choose between authoring tools Ink and Yarn Spinner or a custom data-driven runner. Engine-neutral. Use when the user mentions dialogue system, branching dialogue, conversation tree, choices, Ink (.ink), Yarn Spinner (.yarn), or NPC dialogue.

Dialogue systems

Model conversations as a graph: nodes hold lines, choices branch the flow, conditions gate options, and variables remember what the player did. The first real decision is build vs. buy — adopt a proven authoring tool (Ink or Yarn Spinner) or write a small data-driven runner. This skill owns both Ink and Yarn; the visual-novel and rpg genres consume it.

When to use

  • Use to design branching conversations, choice menus, or narrative state (flags, relationship values) that affect later dialogue.
  • Use to decide between Ink, Yarn Spinner, and a custom JSON/resource format.
  • Use to wire a dialogue script into your game loop (advance line, present choices, run commands, resolve variables).

When not to use: for engine UI (text boxes, portraits, choice buttons), use godot-ui-control or the engine's UI skill. For persisting narrative variables across sessions, use save-systems. For data-as-resources in Godot/Unity, see godot-resources / unity-scriptableobjects.

Core workflow

  1. Choose the authoring approach.
    • Ink — prose-first, writer-friendly, weave/gather flow; great for dialogue-heavy or CYOA narrative. Integrate via ink runtime / inkle plugins.
    • Yarn Spinner — node-based, explicit <<commands>>, strong for game-driven dialogue with lots of engine hooks.
    • Custom runner — a JSON/resource graph + a small interpreter when you need full control or minimal dependencies. Don't build a language; build a graph.
  2. Define the node contract. A node yields one of: a line (speaker + text), a set of choices, a command/side-effect, or an end/jump. The runner advances through nodes and hands lines/choices to the UI.
  3. Separate variables from flow. Keep a variable store (booleans, numbers, strings) the dialogue reads/writes; gate choices with conditions over it.
  4. Localize from the start. Author with line IDs, not raw strings, so the displayed text comes from a string table keyed by locale.
  5. Drive it from the game loop. The runner is a state machine: current node → emit content → wait for input (continue or choice) → advance.
  6. Verify by walking branches. Exercise each choice path; confirm conditions, variable writes, and that every branch reaches an end or a valid jump.

Patterns

1. Engine-neutral dialogue graph (data, not code)

{
  "start": "guard_intro",
  "nodes": {
    "guard_intro": {
      "speaker": "Guard", "line": "DLG_GUARD_001",
      "choices": [
        { "text": "DLG_OPT_BRIBE", "to": "bribe", "if": "gold >= 50" },
        { "text": "DLG_OPT_LEAVE", "to": "end" }
      ]
    },
    "bribe": {
      "speaker": "Guard", "line": "DLG_GUARD_BRIBED",
      "set": { "gate_open": true, "gold": "gold - 50" },
      "next": "end"
    },
    "end": { "end": true }
  }
}

line/text are string-table IDs (localization), not literal text. if gates a choice; set mutates the variable store. The full interpreter that walks this graph is in references/runner.md.

2. Runner step (a state machine over the graph)

# The runner holds the current node and a variable store; the UI calls advance().
func present(node):
    if node.has("line"):
        ui.show_line(node.speaker, localize(node.line))
    if node.has("choices"):
        var shown = node.choices.filter(func(c): return eval_cond(c.get("if", "")))
        ui.show_choices(shown)            # only choices whose condition passes

func choose(choice):                       # called when the player clicks a choice
    apply_set(choice.get("set", {}))       # write variables
    goto(choice.to)

func goto(id):
    current = graph.nodes[id]
    apply_set(current.get("set", {}))
    if current.get("end", false): ui.close(); return
    present(current)
    if current.has("next") and not current.has("choices"):
        goto(current.next)                 # auto-advance linear nodes

3. Ink — branching with knots, choices, and variables (inkle)

// Ink: '*' = once-only choice, '+' = sticky. [bracketed] text shows only in the
// choice, not the printed result. '->' diverts; '-> END' stops the flow.
VAR gold = 60

=== guard_intro ===
The guard blocks the gate.
* {gold >= 50} [Offer 50 gold]   "Here, take it."
    ~ gold = gold - 50
    The guard pockets it and steps aside. -> END
* [Leave]   You turn back. -> END

Ink tracks how often each knot was seen, so {visited_knot} is a built-in condition. Variables are global (VAR) or temporary (~ temp).

4. Yarn Spinner — nodes, options, and commands (Yarn Spinner 3.x)

title: GuardIntro
---
<<declare $gold = 60>>
<<declare $gate_open = false>>
Guard: You can't pass.
-> Offer 50 gold <<if $gold >= 50>>
    <<set $gold = $gold - 50>>
    Guard: ...fine. Go on through.
    <<set $gate_open to true>>
-> Leave
    Guard: Good choice.
===

Yarn lines may start with Speaker:; options use ->; <<set>>/<<declare>> manage $variables; <<if>> gates an option; <<jump NodeName>> moves between nodes. Interpolate values in text with {$gold}. Declare every variable before first use: an undeclared one still compiles (Yarn infers its type and a zero/false/empty start value), but that hides typos. These constructs are unchanged from Yarn Spinner 2.x.

Pitfalls

  • Hardcoding display strings instead of line IDs makes localization a rewrite. Author against a string table from day one.
  • Inventing a scripting language for a simple branching tree. If you only need lines + choices + flags, a JSON/resource graph plus a 50-line runner beats a parser you must maintain. Use Ink/Yarn when writers need real flow control.
  • Variables coupled to the UI: store narrative state separately so the same dialogue works in cutscenes, menus, and tests. Persist it via save-systems.
  • Unreachable or dead-end nodes: a node with no next, choices, or end silently stalls. Validate that every node terminates or branches.
  • Mutating state in a line node the player can revisit double-applies (gold drained twice). Apply set on the transition, or guard with a seen-flag.
  • Mixing Ink's * (once-only) and + (sticky) by accident: looped menus need sticky + choices or the options vanish after one use.

References

  • references/ink-and-yarn.md — side-by-side syntax cheat sheet (choices, diverts/jumps, variables, conditions, includes) and integration notes.
  • references/runner.md — a complete custom dialogue runner: graph schema, condition/expression evaluation, variable store, and localization lookup.

Related skills

  • save-systems — persist narrative variables and seen-flags.
  • godot-resources, unity-scriptableobjects — store dialogue as engine data.
  • godot-ui-control — render text boxes, portraits, and choice buttons.
  • visual-novel, rpg — genres that compose this skill.