🗒️ Resource: Dialogue DSL specifications
1. Context
1.1. Dialogues in video games
In many sorts of video games, and in particular in adventure games or role-playing games, the player will be able to start a dialogue by speaking with one the characters of the game − also called the non-player characters of the game, or NPCs.
-
A dialogue begins with the NPC telling something ("Good morning, can I help you?"), then a list of possible pre-written answers are presented to the player ("I’m looking for help" or "No thanks, good bye")
-
The player can choose one of the possible answers, which will make the NPC tell something else. An important point is that what the NPC says depends on the choice just made by the player.
In most video games, the possible dialogue choices offered to the player depend on the context, such as items possessed by the player, or the characteristics (charisma, intelligence) of the player character, or the past actions of the player, or the past choices made in the dialogue (for example one dialogue choice can make the NPC angry, and refuse to answer more questions).
1.2. Modeling dialogues as dialogue trees
In the video game industry, a typical way to encode a dialogue in a video game is in the form of a dialogue tree. The nodes of a dialogue tree represent all possible ways to unfold a dialogue, such as lines that a given NPC can tell the player in the dialogue, or the choices that the player can make.
A possible graphical syntax, is to represent each NPC line as a node, and each possible player choice by an arrow going to another node of the tree.
| This simplistic graphical syntax will not be sufficient to represent more complex dialogue trees in the later versions of the DSL you will create. |
| Despite the term, a "dialogue tree" is in fact not a tree data structure, because a player choice can point to any existing node of the tree. Therefore, technically a dialogue tree is more a kind of directed graph. This means it is OK for a dialogue tree to contain a loop, making the dialogue possibility infinite. |
2. Objective: designing and implementing a Dialogue DSL
In the following series of practice works, you will have to design and implement a DSL called Dialogue DSL to represent dialogue trees. The goal is to allow this DSL is to have a textual concrete syntax, and to be usable by videogame experts that are not software engineers, despite having a basic understanding of what is coding.
We imagine this DSL to be part of a larger engineering context where the different core parts of a video game are made using DSLs that are directly put in the videogame experts hands:
| We imagine that our Dialogue DSL will be mostly used by writers. Hence, in terms of concrete syntax, we would like the Dialogue DSL to not look like a programming language, and instead to look similar to natural language, or movie scripts, or dialogues in a novel. |
3. Milestones
For this practice work, we will distinguish four different milestones you can reach with your DSL:
-
Version 1: simple dialogue trees similar to the example shown previously.
-
Version 2: we add to the DSL flags and conditions, allowing a dialogue to have a "memory" of what has been said, and to change the NPC lines and the available choices in consequence.
-
Version 3: we add to the DSL branch nodes, allowing different directions to be taken in a dialogue based on the activated flags.
-
Version 4: we allow the definition of multiple dialogues in the same model, and the flags are shared between dialogues (for example: if a first NPC gives an information, it can make visible a player choice when discussion with a second NPC).
Based on your motivation and your progress, you can iteratively first work on V1, then expand it into V2, then into V3, and so on.
3.1. Version 1: simple stateless dialogues
3.1.1. Syntax
The V1 of the Dialogue DSL should be able to represent dialogue trees similar to the Figure 2 shown above.
-
A dialogue tree has a name, the name of the speaking NPC, and a starting line pronounced by the NPC. It is composed of a set of nodes, each node with one or zero NPC line , and many or zero player choices.
-
If a node has no player choices, then it directly has a pointer to the next NPC line.
-
If a node has no NPC line, then it directly allows the player to make a choice.
-
-
An NPC line contains the text pronounced by the NPC.
-
A player choice contains the text pronounced by the player, and a pointer to an existing node of the tree.
Our running example will be the very first dialogue a player can encounter in the notorious video game The Secret of Monkey Island (1990). This dialog happens when discussing with an NPC named Mancomb in the Scumm bar near the start of the game.
With the V1 version of the Dialogue DSL that we want, we would like to be able to represent a simplified version of this dialog which can be schematized like this:
| You can right-click the model and select "Open image in new tab" to display a version where the text can be selected and copy/paster. |
We use here the following graphical concrete syntax:
-
Regular rectangles are NPC lines.
-
Rectangles in bold show the NPC lines with no outgoing pointers (which end the dialogue).
-
Rectangles with a hatch fill are nodes without NPC lines
-
-
Arrows depict either player choices (if there is a label), or the pointer after a NPC line without player choices.
-
The thick arrow at the top shows the starting NPC line.
| In this simplified version of the dialogue, it is possible to the same question twice, which was not possible in the original video game. With the V2 of the Dialogue DSL, we will design how to remember that a question was asked, to hide the question once it was asked. |
3.1.2. Semantics
We would like to implement the semantics of the Dialogue DSL as a code generator that produces Java code.
This Java code should contain a Java class with one Java method per NPC line, and a start method that calls the first NPC line.
An NPC line method should print the NPC line and print all possible choices, then should expect a keyboard input to allow the player to choose.
For example, with the V1 of the DSL, the example dialogue tree showed in Figure 2 should lead to code similar to this:
import java.util.ArrayList;
import java.util.List;
public class MyDialogue {
public void start() {
line1();
}
public void line1() {
// NPC line
Util.display("You don't look like you're from around here");
// Preparing list of all choices
List<String> choices = new ArrayList<>();
final String c1 = "I've lived here all my life!";
choices.add(c1);
final String c2 = "I came here from Newton.";
choices.add(c2);
// Print choices and ask answer
Util.printChoices(choices);
String choice = Util.askUserChoice(choices);
// Move to next NPC line
switch (choice) {
case c1:
line2();
break;
case c2:
line3();
break;
}
}
public void line2() {
// NPC line
Util.display("Oh really? Then you must know Mr. Bowler.");
// Preparing list of all choices
List<String> choices = new ArrayList<>();
final String c1 = "Mr. Bowler is a good friend of mine!";
choices.add(c1);
final String c2 = "Who?";
choices.add(c2);
// Print choices and ask answer
Util.printChoices(choices);
String choice = Util.askUserChoice(choices);
// Move to next NPC line
switch (choice) {
case c1:
line4();
break;
case c2:
line5();
break;
}
}
public void line3() {
// NPC line
Util.display("Newton, eh? I heard there's trouble brewing down there.");
// Preparing list of all choices
List<String> choices = new ArrayList<>();
final String c1 = "I haven't heard about any trouble.";
choices.add(c1);
final String c2 = "Did I say Newton? I'm actually from Springville.";
choices.add(c2);
// Print choices and ask answer
Util.printChoices(choices);
String choice = Util.askUserChoice(choices);
// Move to next NPC line
switch (choice) {
case c1:
line5();
break;
case c2:
line2();
break;
}
}
public void line4() {
// NPC line
Util.display("You liar! There ain't no Mr. Bowler, I made him up!");
}
public void line5() {
// NPC line
Util.display("Don't you worry about it. Say, do you have something to eat? I'm starving.");
}
}
This assumes that an Util class is already available with the following methods:
import java.util.List;
import java.util.Scanner;
public class Util {
static Scanner scanner = new Scanner(System.in);
public static void printChoices(List<String> choices) {
for (String choice : choices) {
System.out.println("(" + (choices.indexOf(choice)+1) +") " + choice);
}
}
public static String askUserChoice(List<String> choices) {
int choice = -1;
while (choice < 1 || choice > choices.size()) {
System.out.println("\nEnter choice (1-" + choices.size() + "): ");
String choiceString = scanner.nextLine();
try {
choice = Integer.parseInt(choiceString);
} catch (NumberFormatException e) {
System.out.println("Input must be a number.");
}
}
return choices.get(choice-1);
}
public static void display(String s) {
System.out.println(s);
}
}
3.2. Version 2: state, flags and conditions
In version 2 (v2), we would like the Dialogue DSL to store a simple internal state allowing to remember lines heard by the player, and choices made by the player earlier in the dialogue. Using this state, the dialogue can be more dynamic, and "unlock" player options based on what was said earlier in the dialogue.
3.2.1. Syntax
We would like this state to be represented by flags, which are variables similar to booleans that can be either set or unset.
-
We should be able to declare the list of all flags at the beginning of a program (by default all flags are unset).
-
We should be able to set (or unset) one or multiple flags when an NPC line is reached.
-
We should be able to hide (or show) certain choices based on whether one or multiple flags are set or unset.
Let’s imagine a very simple dialogue in a murder mystery video game, where we must ask questions to a witness:
We introduce the following new notation:
-
A grey parallelogram with the ✅ icon is placed where we want to set a flag, for example to remember that we asked a question.
-
A black long hexagon represent a condition required to show a given player choice:
-
✅ means the condition requires a flag to be set,
-
❌ means the condition requires a flag to be unset.
-
Such conditions are used to only show a question if it was not asked already, or to make a new question available due to some acquired information earlier in the dialogue.
We continue with the V1 dialogue taken from the video game The Secret of Monkey Island (1990). This time we would like to represent that we cannot ask the same question twice, and that some questions are only available under some conditions.
To be closer to the dialogue in the original video game, we can model the following more complete version (where we can ask about the Governor):
| We are still not able to represent the full dialogue of the original game! We miss a concept allowing us to change the direction of the dialogue at any possible time, which will be added in the V3. |
3.2.2. Semantics
You must improve the code generator of the V1 in order to generate appropriate code for the newly introduced concepts:
-
For each possible flag, a corresponding boolean variable can be generated in the enclosing Java class:
boolean my_flag = false; -
When a flag must be set, the corresponding boolean variable can be assigned the value true:
my_flag = true; -
When a choice must be hidden because of flags, a Java conditional (
if) can be generated to only consider the choice when the corresponding boolean variables have the right values:if (my_flag) { choices.add(some_choice); }