---
title: "NPC dialog conversations and press-to-talk prompts"
description: "The player walks up to a villager and a speech box appears, or presses a key to open a shopkeeper's menu, or picks a reply that branches the conversation. There is no `InteractableComponent` or dialog component (stripped); dialog is a trigger, a UI panel, and branching state."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/knowledge/playbooks/npc-dialog-and-interaction-prompts/
---

# NPC dialog conversations and press-to-talk prompts

## When to use

The player walks up to a villager and a speech box appears, or presses a key to
open a shopkeeper's menu, or picks a reply that branches the conversation. There
is no `InteractableComponent` or dialog component (stripped); dialog is a
trigger, a UI panel, and branching state.

## The recipe

Author the panel with `project_create_ui_panel` and the logic with
`project_apply_script_semantic_patch` using the `ai_safe` op pack.

1. Show it is talkable: a floating `TextRenderableComponent` prompt over the NPC
   (`textSource:"static"`, `text:"Talk [E]"`), toggled on while the player is in
   range.
2. Detect intent: give the NPC a trigger `ColliderComponent`
   (`collision:"trigger"`) and `addTriggerHandler` for proximity, or declare an
   `interact` action (see doors-buttons-and-interactions) and `addActionHandler`
   `{ "actionKey":"interact" }` for a press-to-talk.
3. Open the dialog: in a GessaScript body (`replaceScriptFromGessaScript`) call
   `ctx.ui.openPanel` on a dialog panel and `ctx.ui.setWidgetValue` to fill the
   speaker name and the current line.
4. Branch on choices: store the conversation node in durable state with
   `writeTypedStateValue` (a `dialogNode` field); when the player picks a reply,
   advance the node and `setBooleanState` any flags (met, quest accepted). Read
   back with `readTypedStateValue` to gate later branches.
5. Close and react: `ctx.ui.closePanel` when the talk ends, and `emitCustomEvent`
   a `quest.accepted` or `shop.open` event other systems (inventory, match) act
   on.

## Pitfalls

- Dialogue TEXT is authored content you write into the panel; there is no LLM or
  text-generation host op to write lines at runtime (see capability-ceiling).
- Keep the conversation node in durable state, not a local variable, so a reload
  or a second interaction resumes the right branch.
- Gate the open on proximity; an `interact` action fires everywhere, so confirm
  the player is near this NPC before opening its panel (the door-handler lesson).
- The prompt label is presentation only; the actual talk is the panel plus the
  handler, not the `TextRenderableComponent`.

## Verify

- `simulation_run` (`qa.run.start`) to prove approaching or pressing interact
  opens the dialog and a choice advances the branch.
- `project_get_graph_snapshot` to confirm the trigger or interact handler and the
  dialog panel exist.
