Dialog engine

Analysis of Current DialogueEngine Implementation
I've completed an initial analysis of the current (Techdemo1) DialogueEngine implementation. The following sections describe in detail how the DialogueEngine works in its current implementation and discusses some of the implications.

General Workflow


As can be seen from the flowchart, the current implementation goes through the following major stages once a dialogue has been initiated:

Initialization In the initialization stage the DialogueEngine loads the dialogue data for a specific NPC and extracts the start dialog node from the dialogue data, which is used to start the dialogue flow. The programmer also has to define some dialogue command callbacks (see ) and a dictionary of game state data used to check response conditions (see ).

Process Dialogue Commands Once a start dialogue node is selected the DialogueEngine begins processing any dialogue commands it finds in the dialogue node (see ). Depending upon the commands it encounters the DialogueEngine may end the dialog, change the dialogue flow by returning to a previous dialog node, or present the player with a list of valid responses which ultimately causes the DialogueEngine to start processing the commands from a different dialog node.

Process Responses and User Input Once a 'responses' command is encountered within a dialogue node, the DialogueEngine processes any conditional statements for defined responses (see ) and presents the user with a list of valid responses contingent upon the result of the conditional tests. The user can then select a repy and feed it back into the DialogueEngine, which as noted above begins processing the corresponding dialogue node's commands. This cycle continues until an 'end' command is reached, at which point the DialogEngine exits the dialogue.

Dialogue Data Structures
TODO

YAML Dialogue Files
Dialogue nodes are defined by entries under the "SECTIONS" YAML tag. For example, consider the following dialogue definition file:

NPC: Old Man AVATAR: gui/icons/crazy_swede.png START: main_dialogue

SECTIONS: main_dialogue: - say: "[The old man looks up at you from his chair with a dazed look upon his face] ...Lucy?" - meet: old_man - responses: -            - "Who's Lucy?" - help_find_lucy -            - "I don't have time for your dulusions old man!" - parting_shot

help_find_lucy: - say: "[The old man looks at you expectedly] Can you help me find Lucy?" - responses: -            - "Where is Lucy?" - rusted_lockbox -            - "I have better things to do." - parting_shot rusted_lockbox: - say: "[The old man gazes over his shoulder to the far side of the shanty where a metal lockbox rests on the floor] Can't open it any more... rusted..." - responses: -            - "I'll see what I can do." - parting_shot -            - "Fix your own problems, old man!" - parting_shot

parting_shot: - say: "[The old man sighs and begins to stare remorsefully at the smouldering embers in the fireplace.]" - responses: -            - end

This dialogue definition file defines the following dialogue nodes:
 * main_dialogue
 * help_find_lucy
 * rusted_lockbox
 * parting_shot

Dialogue Commands
Each dialogue node contains one or more dialogue commands which tell the DialogueEngine how to interpret the dialogue node. There are essentially two kinds of dialogue commands:
 * flow commands, or commands that manipulate the flow of the dialogue
 * user-defined commands supplied by the programmer

Flow Commands
Flow commands are those dialogue commands that change the dialogue flow, usually by modifying the. There are four such commands:
 * 'end', which tells the DialogueEngine to end the dialogue
 * 'back', which tells the DialogueEngine to process the previous dialogue node in the stack
 * 'responses', which defines a set of responses the player can choose from and tells the DialogueEngine to put a new dialogue node on the stack and begin processing it once the player has made a choice (more on this later).
 * 'dialogue', which is a special command that allows use of the 'end' and 'back' commands outside of a 'responses' command (see below).

In many ways the 'responses' command is both the most complex flow command and the most important. The 'responses' command takes one or more 'response' subcommand as positional arguments. TODO

Note that the flow commands 'end' and 'back' must either be declared as the last argument to a 'response' subcommand or as the sole argument to the special 'dialogue' command.

User-defined Commands
These commands are defined at runtime when the programmer supplies a dictionary of callback functions to the DialogueEngine. When the DialogueEngine encounters a command it first looks up the command in the callback dictionary and, if it finds a match, it calls the callback function with two arguments: the game state dictionary used to initialize the DialogueEngine, and the content of the command (note: some callbacks are called with additional arguments, and these are noted below). This is also true for flow commands, so in addition to controlling dialogue flow these commands can also be customized by supplying the appropriate callback functions.

Currently, the following user-defined command callbacks are defined:
 * say(state, content)
 * start_quest(state, content)
 * restart_quest(state, content)
 * complete_quest(state, content)
 * fail_quest(state, content)
 * increase_value(state, quest, variable, value)
 * decrease_value(state, quest, variable, value)
 * set_value(state, quest, variable, value)
 * meet(state, content)
 * get_stuff(state, content)
 * give_stuff(state, content)
 * replace_stuff(state, who, old_items, new_items)
 * replace_thing(state, who, old_item, new_item)

Response Conditionals
TODO

Dialogue Stack
The Dialogue Engine stores a particular dialogue's state as a stack of dialogue node identifiers. For instance, if a player responded "Who's Lucy?" and then "I have better things to do.", the dialogue stack would look like this just before the player ended the dialogue:

Note that the dialogue stack is implemented as a simple Python list. Currently the stack is cleared at the end of each dialogue, meaning that dialogues with the same NPC follow exactly the same flow each time assuming no response conditions change. In theory, the stack could be used to persist the state of the dialogue instead of relying upon game state checks to determine dialogue flow, though this might not be the best method of dialogue persistence since the memory needed would scale proportionally to the number of NPC's with persistable dialogues.

Discussion and Proposed Changes
Here's a section for discussing the current DialogueEngine implementation and proposing changes that should be implemented in the Techdemo2 release. Add subsections as necessary. Please do sign your comments to facilitate communication.