Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/gamedev-skills-dialogue-systems/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 33% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 48% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 38% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 99% | 0% |
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.
(flags, relationship values) that affect later dialogue.
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.
dialogue-heavy or CYOA narrative. Integrate via ink runtime / inkle plugins.
<<commands>>, strong forgame-driven dialogue with lots of engine hooks.
full control or minimal dependencies. Don't build a language; build a graph.
a set of choices, a command/side-effect, or an end/jump. The runner advances through nodes and hands lines/choices to the UI.
strings) the dialogue reads/writes; gate choices with conditions over it.
displayed text comes from a string table keyed by locale.
current node→ emit content → wait for input (continue or choice) → advance.
variable writes, and that every branch reaches an end or a valid jump.
json{ "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.
gdscript# 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
ink// 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).
yarntitle: GuardIntro --- <<declare $gold = 60>> 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}.
rewrite. Author against a string table from day one.
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.
dialogue works in cutscenes, menus, and tests. Persist it via save-systems.
next, choices, or endsilently stalls. Validate that every node terminates or branches.
golddrained twice). Apply set on the transition, or guard with a seen-flag.
* (once-only) and + (sticky) by accident: looped menusneed sticky + choices or the options vanish after one use.
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.
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.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | pass→pass | 12,866 | 9,122 | -29% | 1 | 1 | 0% | 2,406 | 3,205 | +33% | 0 | 0 | — |
case-01 | pass→pass | 14,836 | 8,370 | -44% | 1 | 1 | 0% | 2,188 | 3,236 | +48% | 0 | 0 | — |
case-02 | pass→pass | 15,570 | 9,820 | -37% | 1 | 1 | 0% | 2,442 | 3,370 | +38% | 0 | 0 | — |
case-03 | fail→pass | 20,752 | 19,513 | -6% | 1 | 1 | 0% | 3,942 | 5,042 | +28% | 0 | 0 | — |
case-04 | pass→pass | 8,014 | 5,406 | -33% | 1 | 1 | 0% | 1,431 | 2,848 | +99% | 0 | 0 | — |
case-05 | pass→pass | 12,229 | 7,508 | -39% | 1 | 1 | 0% | 2,011 | 3,188 | +59% | 0 | 0 | — |
case-07 | pass→pass | 16,388 | 10,137 | -38% | 1 | 1 | 0% | 2,793 | 3,626 | +30% | 0 | 0 | — |
case-08 | pass→pass | 6,209 | 3,591 | -42% | 1 | 1 | 0% | 1,133 | 2,487 | +120% | 0 | 0 | — |
case-09 | pass→pass | 8,113 | 5,781 | -29% | 1 | 1 | 0% | 1,428 | 2,820 | +97% | 0 | 0 | — |
case-10 | pass→pass | 3,817 | 1,945 | -49% | 1 | 1 | 0% | 650 | 2,191 | +237% | 0 | 0 | — |
case-11 | pass→pass | 5,236 | 3,738 | -29% | 1 | 1 | 0% | 901 | 2,498 | +177% | 0 | 0 | — |
case-12 | pass→pass | 3,760 | 2,298 | -39% | 1 | 1 | 0% | 595 | 2,205 | +271% | 0 | 0 | — |
case-13 | pass→pass | 13,980 | 7,737 | -45% | 1 | 1 | 0% | 2,296 | 3,181 | +39% | 0 | 0 | — |
case-14 | pass→pass | 10,279 | 5,368 | -48% | 1 | 1 | 0% | 1,739 | 2,809 | +62% | 0 | 0 | — |
case-15 | pass→pass | 3,015 | 1,418 | -53% | 1 | 1 | 0% | 481 | 2,085 | +333% | 0 | 0 | — |
case-16 | pass→pass | 7,764 | 5,848 | -25% | 1 | 1 | 0% | 1,711 | 3,182 | +86% | 0 | 0 | — |
case-17 | pass→pass | 15,081 | 8,915 | -41% | 1 | 1 | 0% | 2,846 | 3,584 | +26% | 0 | 0 | — |
case-18 | pass→pass | 10,754 | 6,891 | -36% | 1 | 1 | 0% | 1,806 | 3,111 | +72% | 0 | 0 | — |
case-19 | pass→pass | 11,645 | 8,375 | -28% | 1 | 1 | 0% | 2,000 | 3,208 | +60% | 0 | 0 | — |
case-20 | pass→pass | 22,648 | 15,890 | -30% | 1 | 1 | 0% | 3,828 | 4,994 | +30% | 0 | 0 | — |
case-21 | pass→pass | 18,286 | 13,869 | -24% | 1 | 1 | 0% | 3,555 | 4,613 | +30% | 0 | 0 | — |
case-22 | pass→pass | 13,697 | 11,444 | -16% | 1 | 1 | 0% | 2,830 | 4,207 | +49% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.