# Talking to NPCs (npc.dialogue) Make an NPC talk: the player interacts with it, its lines are typed on screen, and the player may pick between up to four choices that run actions or lead to another dialogue. The quest side (givers, `talk_to` steps) is described in the [quests guide](guide:quests); the NPCs themselves in the [NPC guide](guide:npcs). Readable offline: `gripforge docs npc-dialogue`, or the MCP tool `gripforge_docs` (topic `npc-dialogue`). ## What it is for `npc.dialogue` owns the conversation: which text to show for an NPC and a topic, how it is typed, when it moves on, what a choice does. It registers every NPC that has something to say with `interaction.use` ("Talk to "), so the player starts a conversation with the interaction key. It shows nothing by itself beyond HUD variables and the presentation module (the speaker, the line typed so far and up to four choices); the look is the HUD's. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "npc.dialogue" } gripforge_gamekit_configure { project_id: "gkp_…", id: "npc.dialogue", config: { "charsPerSecond": 50, "pauseWorld": true } } ``` It requires `npc.behavior`, `interaction.use`, `hud.bind` and `game.engine`. An NPC is known by the id of its document in `npcs` (the `npc.brain.npcId` of `npc.core`, or a component `dialogue.npc` set to that id on a placed instance). The simplest content is the `dialogue.lines` of the NPC document: ```json { "id": "elder", "name": "Elder", "dialogue": { "lines": { "greet": ["Hi.", "Bye."] } } } ``` ## First example A `dialogues` document with a choice that runs an action and continues with another dialogue: ```json { "id": "d_offer", "npc": "elder", "topic": "offer", "speaker": "Old Elder", "lines": ["Help?"], "choices": [ { "id": "yes", "text": "Yes", "next": "d_thanks", "then": [ { "set_var": { "path": "helped", "value": true } } ] } ] } { "id": "d_thanks", "topic": "thanks", "lines": ["Thanks."] } ``` Calling `dialogue.lines.say(npc, "offer")` with the entity id of the NPC (or interacting) opens the conversation; the line is typed, the variable `dialogue.choice.1` reads "Yes", key `1` picks it, the action runs, and `d_thanks` follows. A `talk_to` mission step on topic `thanks` completes when that dialogue ends. ## Settings and defaults | Setting | Type | Default | Meaning | |---|---|---|---| | `charsPerSecond` | number, 5 to 200 | `40` | typing speed of a line | | `autoAdvanceSeconds` | number, 0 to 30 | `4` | a fully typed line moves on by itself after this delay; `0` turns it off. The last line of a dialogue with choices always waits | | `pauseWorld` | boolean | `false` | pause the game during the conversation (the dialogue clock keeps running) | All three are open to mods. ## Content: the dialogues collection | Field | Meaning | |---|---| | `id` | required, unique; a choice `next` or `say` can name it | | `npc` | npcs document that offers it as a topic; without it the dialogue is reached by id only | | `topic` | default `greet` | | `speaker` | default: the name of the NPC document | | `lines` | required, at least one string | | `choices` | up to 4 of `{ id, text, next?, then? }`; `next` is a dialogue id or a topic of the same NPC (absent: the dialogue ends); `then` is a list of trigger actions run when picked | A key (topic or dialogue id) resolves to the `dialogues` document with that id, then to a document of this NPC with that topic, then to `dialogue.lines[key]` of the NPC document. Texts go through `i18n.text` when that kit is installed, so a line can be a translation key. ## Services, events, variables, hooks - `dialogue.lines` (service): `say(npc, topic?)` opens a conversation, `current()` describes the running one, `active()`, `advance()`, `choose(index | id)`, `end(reason?)`, `topics(npc)`. - `start_dialogue` (action): open a dialogue without an interaction (a cut-in), `{ npc?, dialogue? }`. Ignored while another one runs. Mission step `talk_to { npc?, topic? }` completes when a dialogue of that NPC (and topic) ends by `finished` or `choice`. - `dialogue.started`, `dialogue.choice`, `dialogue.ended` (events): `{ npc, npcId, topic }`, plus `{ choice, index }` and `{ reason }`. The reasons are `finished`, `choice`, `cancelled`, `walked_away`, `despawned`, or the one given to `end()`. - `dialogue.refused` (event): a hook cancelled a start or a choice. - `dialogue.active`, `dialogue.speaker`, `dialogue.text`, `dialogue.choices`, `dialogue.choice.1` to `dialogue.choice.4` (variables): for the HUD; `dialogue.text` is the line typed so far. - `dialogue.next`, `dialogue.choice_1` to `dialogue.choice_4` (actions of the input map): next line (`E`, `Enter`, mouse button 2 by default) and choices (keys `1` to `4`). `dialogue.next` finishes the typing of the line, then goes to the next one. - `dialogue.start` (hook): before a dialogue opens; `topic` is mutable; cancellable. - `dialogue.choose` (hook): before a choice is taken; `choice` is mutable; cancellable (the choices keep waiting). - `dialogue.advance` (hook): before a line is revealed or advanced, with `origin` `auto`, `input` or `service`; cancellable. A voice kit uses it to hold the automatic advance while a clip plays. ## Hooks and modding A mod can change the three settings and replace or add `dialogues` documents; hooks let a game or a mod rewrite the topic, refuse a start, or swap a choice. The conversation is saved with the game and resumes on the same line. ## Engines The conversation logic is the same Rust code on the web, natively and in the engine wrappers. The line, the speaker and the choices are HUD variables, drawn by the HUD of each engine. ## When it does not do what you expect - The NPC cannot be talked to: it has no document in `npcs` and no `dialogue.npc` component, or nothing to say (no dialogue and no `dialogue.lines`). - Nothing opens and `dialogue.refused` fires: a `dialogue.start` tap cancelled it. - A second dialogue does not start: one conversation runs at a time. - The conversation closes by itself: the player walked more than 6 metres away (`walked_away`), or the NPC was removed (`despawned`). - A choice list is cut: only the first four choices exist. - A line does not advance by itself: `autoAdvanceSeconds` is 0, or it is the last line and choices wait. ## Limits No branching beyond four choices per dialogue, no conditions on choices (put them in the `then` actions or in separate dialogues), no voice (see the voice kit of the [quests guide](guide:quests)), no camera work. ## Versions Kit 1.4.0; service `dialogue.lines` 1.1.0. 1.4.0 added the `dialogue.advance` hook.