Dialogue Nodes

How to create and structure dialogue nodes in Dialogue Forge

Dialogue Nodes

Every piece of dialogue in Dialogue Forge is a node. Nodes connect to form branching conversation trees.

Node Types

AI Message (type: 'ai')

What the character says. These display as left-aligned chat bubbles.

Typescript
{
  id: 'greeting',
  type: 'ai',
  speaker: 'Stranger',
  content: "Hello, traveler. What brings you here?",
  nextNodeId: 'first_choice'  // Auto-advances to this node
}

Properties:

  • id — Unique identifier (use snake_case)
  • speaker — Character name shown above the message
  • content — The dialogue text (supports \n for line breaks)
  • nextNodeId — Which node to go to next (optional—omit for endings)
  • setFlags — Array of memory flags to set when this node plays

Choice Point (type: 'choice_point')

Where the player picks a response. Displays as buttons at the bottom.

Typescript
{
  id: 'first_choice',
  type: 'choice_point',
  content: '',  // Usually empty
  choices: [
    {
      id: 'choice_friendly',
      text: "I come in peace.",
      nextNodeId: 'friendly_response',
      setFlags: ['was_friendly']
    },
    {
      id: 'choice_hostile',
      text: "None of your business.",
      nextNodeId: 'hostile_response',
      setFlags: ['was_hostile']
    }
  ]
}

Choices

Each choice in a choice_point has:

PropertyTypeDescription
idstringUnique identifier
textstringWhat the player sees on the button
nextNodeIdstringWhere this choice leads
setFlagsstring[]Flags to set when chosen
conditionsCondition[]When to show this choice

Memory Flags

Flags are strings that persist throughout the conversation. Use them to:

  1. Track choicessetFlags: ['chose_treasure']
  2. Unlock options — Show choices only if a flag is set
  3. Lock options — Hide choices if a flag is set
  4. Change dialogue — Different AI responses based on flags

Setting Flags

On AI nodes:

Typescript
{
  id: 'give_map',
  type: 'ai',
  speaker: 'Stranger',
  content: "Take this map.",
  setFlags: ['has_map'],  // Player now has the map
  nextNodeId: 'next_node'
}

On choices:

Typescript
{
  id: 'accept_quest',
  text: "I'll help you.",
  nextNodeId: 'quest_accepted',
  setFlags: ['accepted_quest', 'is_ally']  // Multiple flags
}

Conditional Choices

Show/hide choices based on flags:

Typescript
choices: [
  {
    id: 'use_map',
    text: "I have a map that shows the way.",
    nextNodeId: 'map_path',
    conditions: [
      { flag: 'has_map', operator: 'is_set' }
    ]
  },
  {
    id: 'bribe',
    text: "Perhaps some gold will change your mind?",
    nextNodeId: 'bribe_attempt',
    conditions: [
      { flag: 'already_bribed', operator: 'is_not_set' }
    ]
  }
]

Operators:

  • is_set — Choice appears only if the flag exists
  • is_not_set — Choice appears only if the flag does NOT exist

Multiple Conditions

All conditions must be true (AND logic):

Typescript
conditions: [
  { flag: 'has_key', operator: 'is_set' },
  { flag: 'guard_asleep', operator: 'is_set' },
  { flag: 'already_entered', operator: 'is_not_set' }
]
// Shows only if: has key AND guard is asleep AND haven't entered yet

Creating Dialogue Trees

Basic Structure

Typescript
const myDialogue: DialogueTree = {
  id: 'my-dialogue',
  title: 'My Story',
  startNodeId: 'start',
  nodes: {
    'start': { /* first node */ },
    'node_2': { /* ... */ },
    // ... more nodes
  }
}

Flow Patterns

Linear: A → B → C

Typescript
'a': { type: 'ai', content: '...', nextNodeId: 'b' },
'b': { type: 'ai', content: '...', nextNodeId: 'c' },
'c': { type: 'ai', content: '...' }  // No nextNodeId = ending

Branch: A → [choice] → B or C

Typescript
'a': { type: 'ai', content: '...', nextNodeId: 'choice_1' },
'choice_1': { 
  type: 'choice_point',
  choices: [
    { text: 'Option 1', nextNodeId: 'b' },
    { text: 'Option 2', nextNodeId: 'c' }
  ]
}

Merge: B and C → D

Typescript
'b': { type: 'ai', content: '...', nextNodeId: 'd' },
'c': { type: 'ai', content: '...', nextNodeId: 'd' },
'd': { type: 'ai', content: '...' }

Loop: D → [choice] → back to A

Typescript
'choice_at_d': {
  type: 'choice_point',
  choices: [
    { text: 'Ask another question', nextNodeId: 'a' },
    { text: 'Leave', nextNodeId: 'ending' }
  ]
}

Endings

A node without nextNodeId is an ending. The UI shows "End of dialogue" with a restart button.

Typescript
'bad_ending': {
  id: 'bad_ending',
  type: 'ai',
  speaker: 'Narrator',
  content: "You walk away into the mist, alone.\n\n— THE END —"
  // No nextNodeId = this is an ending
}

Tips

  1. Use descriptive IDstreasure_path_choice not choice_3
  2. Group related flagsquest_dragon_accepted, quest_dragon_complete
  3. Test branches — Use the Editor panel to see all nodes and flags
  4. Create loops carefully — Ensure there's always a way out
  5. Write endings — Every branch should eventually reach an ending node