Quests and dialogue

Usage guide of missions, quest graphs, NPC dialogue and voiced dialogue: documents, node and step types, rewards, givers and tracking, settings and defaults, hooks, engines.

Source: module-kits:kits/quest_graph/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs questsMCP tool: gripforge_docs { "topic": "quests" }

Quests, missions and dialogue (quest., mission., npc.dialogue, dialogue.voice) #

Objectives, branching stories and conversations as data. Readable offline: gripforge docs quests, or the MCP tool gripforge_docs (topic quests).

What it is for #

KitWhat it adds
mission.systemONE active mission at a time: a list of objectives (sequential or parallel), conditions, actions, rewards, zone markers, plus trigger rules that run the same condition and action language outside any mission
quest.graphseveral quests at once as graphs of nodes with choices, branches, timeouts and failure; a quest log, a tracked objective with a world marker and a compass
npc.dialoguetalking to an NPC: typed lines, choices that run actions and continue the conversation
dialogue.voicepre-recorded voices for those dialogues, with lip movement driven by the real audio clock; no AI call while the game runs

mission.system is the small, shared, engine-portable machine; quest.graph is for stories with choices. They work together: a mission node of a quest starts a mission and waits for its outcome.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "quest.graph" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "quest.graph", config: { "failOnDeath": true, "arriveRadius": 4 } }
gripforge_gamekit_install   { project_id: "gkp_…", id: "npc.dialogue" }

mission.system requires spawn.entities, world.zones and game.engine. quest.graph requires input.actions and game.engine and uses the inventory, money and level kits for rewards when they are installed. npc.dialogue requires npc.core, interaction.use and hud.bind. dialogue.voice requires npc.dialogue, audio.core and game.engine.

Missions (mission.system) #

Missions are documents of the missions collection (gripforge.mission/1); trigger rules are documents of triggers. Both are validated against the installed kits before play, so a missing kit is a readable error.

KeyDefaultMeaning
autoStartfirstnone, first, or a mission id to start when the world is ready
markerColor#ffcc00colour of the objective zone marker
failOnDeathtruefail the active mission when the player is arrested or dies

Objective steps: go_to { zone }, wait { seconds }, interact { entity | tag }, event { topic, match? }, escape_zone { zone, until? }; other kits add theirs (vehicles bring enter_vehicle, exit_vehicle, deliver_vehicle; dialogue brings talk_to { npc?, topic? }). A timeout fails the mission unless the step is optional.

Conditions: { var, op, value }, { event, match? }, { in_zone }, { has_tag }, { all }, { any }, { not } and the conditions of the kits (has_item, money_at_least, quest_status...). Actions: set_var, inc_var, emit, spawn, despawn, notify, start_mission, complete_objective, fail_mission, teleport, wait, and the verbs of the kits. Rewards: money, reputation, items, vars. Variables: mission.status, mission.objective.label, mission.timer. Events: mission.started, mission.objective.completed, mission.completed, mission.failed, mission.aborted.

Quests (quest.graph) #

{ "id": "lost_lamb", "title": "The lost lamb", "giver": { "npc": "shepherd" }, "start": "find",
  "rewards": { "money": 20, "xp": 50 },
  "nodes": [
    { "id": "find", "type": "go_to", "label": "Search the old mill", "at": [40, 0, 12], "radius": 6, "next": "decide" },
    { "id": "decide", "type": "choice", "prompt": "The lamb is hurt.", "choices": [
      { "id": "carry", "text": "Carry it home", "next": "home" },
      { "id": "keep", "text": "Keep it", "next": "betray" } ] },
    { "id": "home", "type": "talk", "label": "Bring it to the shepherd", "npc": "shepherd", "timeout": 300 },
    { "id": "betray", "type": "fail", "reason": "kept the lamb" } ] }

Node types: go_to (zone, or at + radius), collect (item, count, take), talk (an NPC document or a tag), kill (count of a tag or kind), use (an item or a tagged entity), event (a bus topic, match, count), wait, choice, branch (first branches[].when that holds, else else), mission, complete, fail. A node without next completes the quest. Any node may carry timeout / onTimeout, failWhen, actions, rewards and marker: false; a quest has conditions.start and conditions.fail. Rewards: items, money, xp, vars. When a dialogue with giver.npc ends, the quest starts, unless giver.auto is false (then a dialogue choice runs { "type": "start_quest", "quest": "…" }).

KeyDefaultMeaning
autoTracktruea started quest is tracked when nothing else is
failOnDeathfalseplayer.died fails every running quest
arriveRadius3m, for a go_to node that gives no radius
markerColor#7fd4ffcolour of the tracked objective marker
presentationProfileemptya game-owned presentation profile (slot materials, objective) for the beacon

Service quest.graph: quests(), runs(), start(id), choose(id, choice), fail(id, reason?), abandon(id), track(id | null), tracked(), objective(id?), target() (distance and bearing for a compass), openLog(). Verbs start_quest, quest_choose, fail_quest, track_quest; conditions quest_status, quest_choice, quest_at. Events quest.started, quest.node, quest.progress, quest.objective.completed, quest.choice, quest.completed, quest.failed, quest.tracked. The runs are saved.

Dialogue (npc.dialogue) #

A key (topic or dialogue id) resolves to a dialogues document with that id, then to a document of this NPC whose topic (default greet) matches, then to the dialogue.lines[key] of the NPC document.

{ "id": "job_offer", "npc": "barman", "topic": "greet", "speaker": "Sal",
  "lines": ["Looking for work?", "I have a car that needs moving."],
  "choices": [ { "id": "yes", "text": "I'm in.", "then": [{ "start_mission": "move_car" }] },
               { "id": "later", "text": "Not now.", "next": "bye" } ] }

Every NPC with something to say is registered with interaction.use ("Talk to ..."). Lines are typed at charsPerSecond (40); autoAdvanceSeconds (4, 0 disables) moves a typed line on, except the last line when choices wait (keys 1 to 4 pick them); pauseWorld (false) pauses the game while talking. The conversation also ends when the player walks more than 6 m away. HUD variables: dialogue.active, dialogue.speaker, dialogue.text, dialogue.choices, dialogue.choice.1 to .4. Service dialogue.lines: say(npc, topic?), current(), active(), advance(), choose(index | id), end(reason?), topics(npc). Verb start_dialogue. Events dialogue.started, dialogue.choice, dialogue.ended, dialogue.refused.

Voices (dialogue.voice) #

Recordings are produced once, before the game ships, and stored with it. A clip names a dialogue document, a zero-based line, a language, the exact translated text, a sound on the voice bus, its duration and timed mouth cues; changed text or another language never plays a stale clip. A character points at a rig with voice: { rig: "hero_face" }; the rig document maps each mouth cue to morph targets and/or a jaw bone. Without a recording the subtitles and choices carry on and one voice.missing event is emitted per line. Settings: enabled true, followDialogue true, volume 1, blendSeconds 0.04, loadTimeoutSeconds 20. Set audio.core.voiceDuringPause when pauseWorld is on. Collections: dialogue_voices, voice_rigs, voice_cast, dialogue_voice_directions.

Hooks and modding #

HookPayloadWhat a tap may do
mission.start{ mission }cancel (the mission stays closed)
mission.fail{ mission, reason }change reason, cancel (the mission goes on)
mission.complete{ mission, title, rewards }change the rewards, cancel (the last objective is armed again); a kernel hook of game.engine
quest.start{ quest }cancel
quest.choose{ quest, node, choice }cancel
quest.advance{ quest, from, to }change to
quest.complete{ quest, node, money, xp }change money and xp
quest.fail{ quest, node, reason }change reason, cancel
dialogue.start{ npc, npcId, dialogue, topic }change topic, cancel
dialogue.choose{ npc, npcId, topic, choice, index }change choice, cancel
dialogue.advance{ npc, dialogue, index, origin } (auto, input or service)cancel (the voice kit holds an automatic advance while a line is spoken; an explicit skip still works)
{ "id": "generous", "hook": "quest.complete", "match": { "quest": "lost_lamb" }, "mul": { "money": 2 } }

Moddable: the settings above and the collections missions, triggers, quests, dialogues. The failures a graph decides itself (a fail node, a timeout, failWhen) are not behind a hook.

Engines #

mission.system is delivered to Godot and Unity as files and to Unreal through the shared kit library, with the same step machine. npc.dialogue is delivered to Godot and Unity as files. quest.graph and dialogue.voice run on the web and in the native player; the hooks run in those two as well. In a web game the quest log and tracker are drawn by the interface layer; dialogue.voice plays through audio.core and depends on the audio device clock for the lips.

When it does not do what you expect #

SymptomCause and fix
A mission never startsautoStart is none, or the mission has a giver with auto: false (the NPC gives it)
A quest stays on its nodethe node waits for something that never happens: check the tag, the zone, or the match fields of an event node
A dialogue offers nothingthe key resolves to no dialogues document and no dialogue.lines: use the topic greet or name one
A choice does not run its actionsread dialogue.refused: a dialogue.choose tap cancelled it
Voices do not playno recording for this line and language (see voice.missing), or the text changed since the recording