Gameplay — Dialogue
DialogueBehavior renders a branching conversation with an NPC. Dialogue is defined as a tree of nodes — each node has text, and optionally a list of player choices. Choices can be conditionally hidden and can trigger side effects (start quest, give item, set flag, etc.).
Dialogue Tree Structure
dialogue: {
npcName: 'Elder Bjorn',
tree: [
// node 0 — entry point (always first)
{
id: 'root',
text: 'Welcome back, traveller.',
choices: [
{
label: 'Tell me about the goblins.',
next: 'goblin_info',
},
{
label: 'I have dealt with the goblins.',
condition: { type: 'questStatus', questId: 'goblin_bounty', status: 'complete' },
effect: { type: 'turnInQuest', questId: 'goblin_bounty' },
next: 'quest_complete',
},
{
label: 'Nothing, goodbye.',
next: null, // null closes the dialogue
},
],
},
{
id: 'goblin_info',
text: 'They come from the eastern caves. Be careful.',
choices: [], // empty choices = "OK" button, then close
},
{
id: 'quest_complete',
text: 'Excellent work! Here is your reward.',
choices: [],
},
]
}
The conversation always starts at the first node (tree[0]). The NPC’s text is displayed; the player’s choices appear below. Clicking a choice runs its effect, then navigates to its next node. next: null closes the popup.
Conditions
Conditions hide a choice unless their expression is true. If a condition evaluates to false, the choice is invisible (not grayed out).
type | Required fields | Description |
|---|---|---|
hasFlag | flag, value | gameMgr.flags.get(flag) === value |
questStatus | questId, status | Quest has this status |
hasItem | itemId, count | Player inventory has ≥ count |
hasGold | amount | Player gold ≥ amount |
playerLevel | minLevel | Player level ≥ minLevel |
noCondition | — | Always shown (default when condition omitted) |
// Show "I'm ready to fight" only after the player reaches level 3
{
label: "I'm ready to fight.",
condition: { type: 'playerLevel', minLevel: 3 },
next: 'fight_accepted',
}
Side Effects
Effects fire immediately when the player selects a choice (before navigating to next).
Single Effects
// Set a world flag
effect: { type: 'setFlag', flag: 'bridge_rebuilt', value: true }
// Start a quest
effect: { type: 'startQuest', questId: 'goblin_bounty' }
// Turn in a completed quest (grants rewards)
effect: { type: 'turnInQuest', questId: 'goblin_bounty' }
// Give and equip an item (Armorer pattern — no loot window)
effect: { type: 'equipItem', itemId: 'iron_sword', slot: 'mainhand' }
// Deduct gold (pair with equipItem to charge for direct equips)
effect: { type: 'spendCurrency', currency: 'gold', amount: 50 }
Multiple Effects
// Apply several effects at once
effect: {
type: 'multi',
effects: [
{ type: 'spendCurrency', currency: 'gold', amount: 50 },
{ type: 'equipItem', itemId: 'iron_shield', slot: 'offhand' },
{ type: 'setFlag', flag: 'armorer_visited', value: true },
]
}
Authoring Tips
Keep entry node neutral. The first node is shown every time the player talks to the NPC. Use it as a hub with conditionally filtered choices, not a one-time intro.
// Good — entry is always valid, choices filter by quest state
{
id: 'root',
text: 'How can I help you?',
choices: [
{ label: 'Tell me about your quests.', next: 'quest_list' },
{ label: '[Quest turn-in]', condition: { type: 'questStatus', ... }, effect: { ... }, next: 'reward' },
{ label: 'Nothing.', next: null },
],
}
One phase = one interaction. Because advancePhase() only completes a quest on the last phase, put the quest’s completion trigger (kill count or NPC interaction) on the last and only phase. See Gameplay — Quests for the full explanation.
Use null for next to close. Any choice with next: null closes the dialogue immediately after applying its effect.
Test condition visibility carefully. Conditions hide choices but don’t block interaction. If you need to prevent interaction entirely (e.g., a gate that requires a key), use a TriggerZoneBehavior with a flag condition, not dialogue.
Popup Rendering
The dialogue popup renders in the interactive popup type with a Talk tab. Other tabs (Missions, Craft, etc.) appear alongside it if the same entity also has MissionGiverBehavior, CrafterBehavior, etc.
The dialogue window shows:
- NPC name (top)
- Current node text (middle)
- Choice buttons (bottom) — one button per visible, condition-passing choice
A choice with no text just shows an “OK” button.