Samstag, 1. August 2015

Creating powerful languages with Xtext

[This is a reprint of an article I wrote in 2013 for AltDevBlogADay, which ceased to exist, which is why I felt like putting it on my blog. No doubt, I would write this article completely differently today, but I think it is actually kind of nice to preserve my perspective I had back then by not altering the article.]

Today, I would like to introduce the Xtext framework to you. Xtext is an open source framework which allows you to define your own textual programming languages before you know it. Xtext was not developed specifically with game developers as a target group in mind. It is rather independent in terms of its application context, and I’m convinced that every tool department that ever needed to define their own programming language can immensely benefit from Xtext. So, although there already exists convenient documentation, examples, forums, and so forth, I would like to provide an article that uses a small, game-related example to introduce the technology.

Xtext ships as a plugin for the eclipse IDE. Hence, we will need to cover some basics of eclipse, too. I tried to do this in a pragmatic way and within a reasonable scope. 

So, what exactly is Xtext?

Xtext is a free to use, open source language development framework, available under the Eclipse Public License. It comes with some powerful language design tools, which provide you with
  • an easy way to design new programming languages, be it large general purpose or little domain-specific languages,
  • powerful default configuration to automatically build rich tooling for your new language
  • a highly modular architecture and API which enable you to customize and enhance almost every aspect of your language and its tools
  • an open and active community.

Our goal

The hardest part of writing such an article is to construct a decent example. So I didn’t. Instead, I use a script format example suggested by Steve Ince in his book Writing for Video Games (thanks Steve!). We iterated a bit over the language design to come up with a more concise version, but anyway, the screencast shows the language and the editor we want to create throughout this article.



Let’s not put the specific language design to discussion here. Instead, we will learn how to build such a language + rich editor using Xtext, such that you are able to create your own language as you desire.

The cool thing is: In order to get this language plus the tooling I show in the video, all we have to do are these steps:
  1.  Create a new Xtext project
  2.  Define the grammar of our language in the grammar definition file
  3.  Trigger Xtext to generate everything for us

Getting started

Xtext ships as an Eclipse plugin. Hence, you can
  • either add it to your existing Eclipse application using the update mechanism of Eclipse,
  • or, if you don’t already use Eclipse, download a complete distribution of it, containing Xtext and all the necessary dependencies, at once
You can find the download as well as a short installation guide here. Note that you need to choose an Eclipse version that matches your installed Java Runtime Environment. That means that you can only use 64 bit version of Eclipse if you are running a 64 bit JRE on your system. Let me know in the comments section if you encounter problems running Xtext.

In this tutorial, I work with the Eclipse Kepler release and Xtext version 2.4.2. The file I downloaded from the linked site is “eclipse-dsl-kepler-R-win32.zip”. 

Download the example

Refer to my blog to download the language as a standalone product with some enhancements . You can also download the Xtext projects (sources+runtime project) that are shown in this article. 

Intended benefits

What are the benefits creating such a modeling language in the first place? First, the language features domain abstractions that allow users to express information in a natural way. In combination with the rich tooling, users are strongly supported in creating syntactically and semantically correct contents. In our case, it supports game writers to stick with the dialog format and create dialog scripts using a simple language.


Since the language conforms to an underlying model, every dialog script automatically has an object graph we can access programmatically. This means that we can create generators that translate dialog scripts into other formats, like
  • XML, C++ or C#, to feed the scripts into a dialog engine,
  • Excel and screenplay formats for localization purposes,
  • or statistical reports, e.g. how many lines each character has.
Alternatively, we could create an interpreter, a program that is able to execute a dialog script directly, by working on the object graph of a script. A use case for this would be to allow writers to play through their dialogs already during creation.

Moreover, the language comes with syntactic validation, so that writers know if there dialogs are syntactically correct. But since we also have the underlying data model, we can additionally provide semantic validation. This turns the language into a powerful tool, as we will cover in an upcoming article.

Put another way, it makes dialog scripts in their meaning equal to source code, since they provide detailed and concise information, while they still can be written by non-programmers, thanks to the used abstractions and limited scope of a DSL. In combination with the tooling that comes with an Xtext language, the users—in our case game writers—are supported in many ways to efficiently create their content.

But before I start to talk about shortened turnaround times, let’s move on with the article and discover the benefits step by step.

Setting up an Xtext project

Note that I work on a PC, but if you’re on a Unix-based system, you should be able to follow the described steps accordingly.

Start your Eclipse application by launching the eclipse.exe/*.app. By default, Eclipse asks you for a workspace location. The workspace is the root directory where this instance of Eclipse will store your projects, as well as the related metadata Eclipse requires. Check the box at the bottom if you want to set this workspace as your default.



Eclipse starts with showing us a “Welcome page”, which we can just ignore. Instead, select File --> New --> Project from the main menu. This leads us to a wizard selection dialog. Type Xtext in the filter field at the top. As you will see in the filtered list of available project types, Xtext already ships with many example projects, which you might want to explore later for yourself. For now, select Xtext Project and press Next >.



We are directed to the New Xtext Project wizard which already features some defaults. Let’s go through the fields step-by-step. 
  • Project name: Xtext projects need to feature a project name that starts with a lower case letter since it will derive Java packages from that name. Let’s call our project adbad.dialogScript.sample. 
  • Use default location: Let it checked so that the project will be stored in our workspace 
  • Language --> Name: reuse the project name and append a valid Java identifier as your language name, e.g. adbad.dialogScript.sample.DialogScriptDSL 
  • Language --> Extensions: Here we can define several file extensions. For now, one is sufficient. Let’s use dialog.
  • Layout --> Create SDK Feature Project: Uncheck that. It wouldn’t do any harm to have it created, but we don’t need it for this article
  • Working sets --> Add project to working sets: We can group projects in Eclipse into so called working sets. We don’t need to do that.
This is how it should look like if you want to follow the suggestions:



Hit Finish and Xtext starts working. We will be redirected to Eclipse. If you haven’t closed the welcome page yet, do it now. Xtext has created three projects for us:
  • adbad.dialogScript.sample: The actual language project. All sources regarding the language itself belong here. Note that this runtime project is independent from the user interface and Eclipse. That means we can use our language definition outside of Eclipse. 
  • adbad.dialogScript.sample.tests: A convenient project for testing our language (we won’t cover testing in this article, hence we won’t have to look at this project any further)
  • adbad.dialogScript.sample.ui: All sources related to the user interface of our language, like the rich editor, its features like content assist, highlighting, etc., and the outline view, will be stored here. The ui project is a so called Eclipse plugin, meaning that it depends on Eclipse as a platform.



All the code we are going to create should be stored within the src folders, which are available in every project. The folders src-gen resp. xtend-gen are to separate all generated code from the manually written one. Xtext takes care of that, so it’s just important to not place any manually written code in one of these folders.

Exploring the project in the package explorer shows us that there already exist some files in our language project:

  • DialogScriptDSL.xtext: This file is already opened in the editor area. It’s our central resource to define our language’s grammar. It features a grammar definition language, which has been created—you guessed it—with Xtext itself. The language resembles EBNF and it allows us to define context free grammars.
  • GenerateDialogScriptDSL.mwe2: This file is to configure Xtext, i.e. to tell Xtext what features we want to use in our language and what the framework should generate for our language. We can stick with the default settings for now. 

The Grammar Definition File (DialogScriptDSL.xtext)

Let’s see what we have so far in our grammar file to get a first idea of how to define grammars in Xtext.

HINT – Showing line numbers in Eclipse editors: In order to see line numbers in your editors

  • Select Window --> Preferences from the main menu
  • Type text editors in the filter field at the top left corner of the new window
  • Select the item Text Editors (highlighted) from below the filter field to open its configuration page
  • Check the box show line numbers (fourth from above) there and press OK

The grammar definition file starts with the “grammar” keyword, followed by the language name we defined earlier in the New Xtext Project wizard. Grammars can make use of other grammars. With the statement in line 2, we tell Xtext to use the org.eclipse.xtext.common.Terminals grammar, which comes with Xtext. It provides us with some handy language features, like:
  • Single- and multi-line comments you might know from programming languages like Java or C#. They allow users to annotate programs with additional, free text information that is ignored by the parser.
  • An ID rule that allows us to define identifiers in our language. Identifiers have a specific semantic in programming languages, and so they have in Xtext. Things like classes, variables, and methods, or in our case characters and conditions can be named using identifiers. This makes them identifiable and thus referable from other locations.
  • INT and STRING rules to use integers and strings in our language. Both rules also feature some specific semantics, as they are mapped to specific data types in our underlying meta model… wait, meta-what?
Okay, I mentioned earlier that our language needs to comply with an underlying model. In my experience, the whole model / meta-model / meta-meta-model terminology tends to be more confusing than helpful. Still, I want you to understand what’s going on in the grammar file, and I’m referring to line 4 now. So let’s try this.

Whenever we define a textual modeling language with Xtext, we define its so called concrete syntax, i.e. how the editor displays programs written in our language to us, the users. In order to work with our language programmatically, we also need an object graph that represents our language (an abstract syntax tree, AST). The parser creates this representation for us when it parses a dialog script file. The abstract syntax of our language determines how such trees can look like. But where does the abstract syntax, the structure of our language, come from? Xtext offers us two possibilities:

  • Either, we can define a concrete syntax for an existing abstract syntax by importing an abstract syntax model
  • or, we let Xtext derive the abstract syntax from our grammar definition file automatically.

The latter is done by the statement in line 4. We need to provide a name (dialogScriptDSL) for the abstract syntax model as well as a namespace URI ("http://www.dialogScript.adbad/sample/DialogScriptDSL") that makes it referable.

The remaining part of the file is to define the actual grammar of our language. Lines 6-10 show two grammar rules that already define a simple language. Now, instead of diving into the details of defining context-free grammars with Xtext, let’s approach it pragmatically:

Defining a language can be seen as a top-down process, where you divide your language step-by-step into its components until you have defined all its tokens. The rules in the grammar language allow us to do exactly that.

Again, we are not able to discuss the grammar file for the used example in detail in this article (instead, I made a screencast), but the important part is to understand that this file is the central resource where we define our language’s concrete syntax.



You can copy the rules from the provided grammar file to your own grammar file. I inserted some comments that help you understand the grammar.

Creating the language infrastructure and editor

In order to use our language, we need to make Xtext generate the infrastructure for it. We can do this by opening the context menu somewhere in the Xtext grammar editor and selecting Run As --> Generate Xtext Artifacts. This invokes the GenerateDialogScriptDSL.mwe2 workflow and generates the infrastructure for our language according to the information provided in the workflow file. This might take some seconds, and you might be asked in the Console View at the bottom to download the ANTRL parser generator, which is necessary, so enter ‘y’ in order to proceed.
 

Running the editor

After that, we can launch a new Eclipse instance from within our current environment. That instance will contain our language, plus the editor as an Eclipse plugin, so we can try it out immediately. We need to select the run configuration first (we need to do this only once). To do so, select Run Configuration … from the Run toolbar menu (click the small black arrow pointing down):

 
The Run Configurations dialog appears. There, you can see an Eclipse Application entry on the top left side to create a new configuration. Grouped below, we find the configuration we want to start. It is called Launch Eclipse Runtime. Select it and hit the Run button at the bottom. (Note that I renamed it to DialogScriptRunner):


This starts a new Eclipse instance just from within our development environment. Note that, from now on, you can always directly select the DialogScriptRunner configuration to directly start Eclipse!



In order to test the language and the editor, create a new project, e.g. by pressing CTRL+N and then selecting the wizard of your choice (I often use the General Project Wizard without any bells and whistles).



Give the project a name, like sample, and feel free to add sub-folders using the project’s context menu in the Package Explorer. You can now add a new dialog script, again using the project’s context menu:


It is important to explicitly state the file extension of our language when naming the dialog script file. Since we defined dialog as our file extension back when we have created the Xtext project, we might just name our first file sample.dialog. Now, you should be asked whether or not we want to add the Xtext nature to your project, and since we want to have the full Xtext support in our sample project, we sure do.

The editor that Xtext has created for us is used by default now whenever we open *.dialog files, and we can already test our language and editor. Actually, the language already features everything we saw in the first screen cast. It is always great to see how many tooling features are provided by default. You can try out the features that I show in the first screencast now for yourself.

Note that we just edited the grammar file so far, and we receive a fully-fledged editor in combination with our language.

Summing up

Game developers use a multitude of development tools for all kinds of purposes. Due to the individual requirements, it is rare that the same ecosystem of tools is used twice. Instead, developers often are in need of introduce new tools to address the specialties a game project and in most cases there is little to none budget for that. Especially when it comes to non-technical domains, like game design or writing, makeshift solutions like screenwriting or office software are often the status quo to describe how a game should ‘work’.

Technologies like Xtext support tool smiths in creating their own programming languages with a corresponding development environment. We explored a very basic example that shows—just by defining a single language grammar file—how Xtext provides us with a complete language runtime as well as a rich editor. The additionally provided application gives you an impression of how the development environment can be enhanced, and I’m looking forward to provide you with some subsequent articles on how to do that.

With great powers ..

[This is a reprint of an article I wrote in 2011 for AltDevBlogADay, which ceased to exist, which is why I felt like putting it on my blog. No doubt, I would write this article completely differently today, but I think it is actually kind of nice to preserve my perspective I had back then by not altering the article.]

Once, James Gosling (inventor of Java) was asked what he'd change if he could do Java over again. He replied: "I'd leave out classes". I've read about this in this —kind of controversial— article by Allen Holub: Why extends is evil.

But to set things clear: I don't want to start the same discussion here as the article got ("he's so wrong, 'extends' rulez!" vs. "he's absolutely right, worship 'implements'!"), and I'm pretty sure it wasn't Holub's intension either. Anyway, Gosling explained right after his statement that he actually addresses implementation inheritance to be the problem, not classes in general.

Now, when I recap my own programming education, I remember that object-orientation was always taught as something that is strongly connected to the mechanism of inheritance (which is not necessarily wrong, but only part of the truth).

And, talking to my students nowadays highlights the same issues I had back then: It is hard for novices to differentiate between implementation inheritance (as a reuse mechanism) and interface inheritance (as a software design mechanism), especially when you learn OO with Java or C++, where implementation inheritance always comes with interface inheritance automatically (reusing a class' implementation by extending it implicitly means that you inherit its interface).

So, soon you got statements like: "Why should I use explicit interfaces anyway?"… or, "I don't get the idea of interfaces, I use inheritance instead". What's more, other important aspects of object-orientation, like polymorphism, are also intertwined with inheritance in statically-typed languages (nothing to blame them for, it's just how it works).

My point here is, that this — in the minds of programming novices often, and in the minds of veterans often enough — leads to a simplified relationship: "inheritance is object-orientation"… which we could display in UML like this:

Beware! Not true!

In this post, I would like to introduce and discuss the fragile base class problem (FBCP). I think, it is a very good showcase why the introduction of an explicit interface concept in Java or C# has its reasons, but, first and foremost, I hope that it will illustrate how tricky your code can get when you use implementation inheritance (strong coupling). I also hope that this is not only interesting for the novices among us ;).

Note that the examples are dead simple and not good quality code, but intended to highlight the basics of the FBCP. If you are interested in getting a deeper insight, I recommend the paper A Study of The Fragile Base Class Problem.

Let us imagine the following classes, where the Collection class is part of a framework (base system) and the CountingCollection class is part of an extension somewhere else (sub system):

// Version 1
import java.util.ArrayList;


public class Collection {

 ArrayList data = new ArrayList();

 public void addItem (Object item) {
  data.add(item);
 }

 public void addRange (ArrayList items) {
  for(Object o : items) {
   this.addItem(o);
  }
 }
}
 
public class CountingCollection extends Collection {
 int n = 0;

 public void addItem (Object item) {
  n++;
  super.add(item);
 }

 public int getSize() {
  return n;
 }
}

The Collection class represents a collection of items and you can add either a single item or a range of them. The extension, CountingCollection, adds a counter variable to be aware the number of added items. Everything works as intended.

Now, after a revision of the base system, the base class got changed.


// Version 2
import java.util.ArrayList;


public class Collection {
 ArrayList data = new ArrayList();

 public void addItem (Object item) {
  data.add(item);
 }

 public void addRange (ArrayList items) {
  // revised
  data.addRange(items)
 }
}

This change is, considering the base system, valid, since it does not change the externally observable behavior of objects of type Collection. However, it breaks the sub system. This is because the subclass relies on the self-call in the first version of the base system in line 13, which means that it relies on the internal behavior (the implementation) of Collection. Here we face the FBCP.

Having a more general look at this circumstance, it means that "any open system applying code inheritance and self-recursion in an ad-hoc manner is vulnerable to this problem."

The fact that the immediate cause and the observable effect of the FBCP can spread between different systems makes it hard to track down, though the goal should be to avoid its occurrence in the first place. But how?

Well, in their above mentioned study, the authors introduce a flexibility property that must not be harmed by the programmers in order to avoid the FBCP. In short, the property describes that a modification M to a class C (the actual extension) must remain a valid refinement of C when applied to a refined version of C (C' in the figure below; mod reads "modifies").


Flexibility Property to avoid the FBCP

This is a bit theoretical, but in the essence it means that it is the duty of the programmer to ensure that everything's coded fine; in the end, everyone can easily google for things like the Open-Closed Principle, can't we?

Let's take a more cynical or maybe naive perspective while looking at the upcoming example. It is also borrowed from the mentioned study, and it is only one of five examples that show orthogonal aspects of the FBCP, making it more than a trivial thing.


public class BaseClass {

 int x = 0;

 public void aMethod() {
  x = x+1;
 }

 public void anotherMethod() {
  x = x+1;
 }
}
 
public class SubClass {

 public void anotherMethod() {
  this.aMethod();
 }
}
 
//New base class
public class BaseClass {
 
 int x = 0;

 public void aMethod() {
  this.anotherMethod();
 }

 public void anotherMethod() {
  x = x+1;
 }
}

This example highlights the aspect of "Unanticipated Mutual Recursion", and it could make us ask "Why do modern languages even allow that these problems can arise?", or in other words "Why don't we have languages that eliminate such issues by definition?"

Well, on the one hand, there are code validation and checking tools that already support us programmers in writing good quality code. But I don't think that, especially considering the last example, tools are able to detect fragile base classes automatically.

On the other hand, the questions address something that accompanies the history of programming from the very beginning. Take pointers, for example. In the hands of an expert powerful weapon, but amateurs can do horrible things (while having good intensions!). And every one of us knows a guy who still swears that Algol 60 is the best language ever.

So, maybe there will be a new language in the near future that explicitly separates implementation inheritance and interface inheritance (and maybe no one will consider it useful), but until then, we, as lecturers and senior programmers, need to make sure that the upcoming generation of programmers is aware of the dangers in implementation inheritance and that they understand object-orientation more like this:


Object-Orientation how it should be considered
In the end, it is just like Stan Lee once said: "With great power there must also come — great responsibility!"

Freitag, 28. März 2014

DialogScript 0.3.0 and an exciting upcoming feature!

Long time no see (again!).

But I'm happy to tell you that you can download a new version of DialogScriptDSL (0.3.0).
The source code for that version is available on GitHub.

I'm not quite sure what I already had back in version 0.1.0, but the current version features a lot of new cool features and a ton of detailed improvements. Let me start with the character scripting language which allows you to define characters and dynamic character templates, which means that they are made available to the content assist feature dynamically.
I definitely need to do some docu on that, but since I decided to freeze this feature for now to work on something even more exciting (!), it doesn't have the highest priority. In fact, the character definition language is not hooked up with the actual dialog scripting language, just because I haven't thought it through enough. I still included it in version 0.3.0 so you can fool around with the language. I'm especially happy with and interested in your thoughts on the dynamic character templates, so let me elaborate a little bit on that.

Character definition with dynamic templates


 
The character definition language is very declaratively. Use the ".chara" extension to create a character definition file.
In the example above, we see two characters defined using the available default attributes, which are full name, description, age, and type. Type can be PC (playable character) or NPC (non-playable character).

 

You can define templates for characters, like the StarWars template above, defining your own attributes. An attribute needs a type. Right now, NUMBER, TEXT, and ENUMERATION are available. NUMBER and TEXT are pretty straight forward, I think. An enumeration is just a predefined list of constant values an attribute of this type can have. In the example, the jediLevel of a character using the template StarWars is either, None, Padawan, Knight, or Master.

This template is instantly available in the content assist (Hit CTRL+SPACE when the cursor is on the root level of the document, i.e. not inside a character definition).


Here you can see the dynamically added template to the content assist as well as a preview of what that template pastes into your file if selected. Let's add Luke to the party:

 
The dynamic template features template expressions just as we know from any static template, so we can use the TAB key to navigate through the different default values (the framed text) and change them by typing. In the screenshot above, I already filled out the template expressions with values for Luke. Note that "Knight", or in general any value in an ENUMERATION is a real cross reference in our script, so we get an error message when we try to use a value different from the ones defined in the template attribute!
We can even mark custom attributes as being mandatory (I did this for the "hands" attribute as can be seen in the screenshot below).

 
Consequently, when we make use of that template we have an integrated validation that makes sure that we actually use all mandatory attributes. We even get a quickfix that allows us to add missing fields.

I know that there have been even more features, like an import mechanism to reuse character template throughout different files as well as global attributes which are available to any character no matter which template he or she relies on, but I'm not sure if these will make the cut, so I won't describe them any further.

A Live Graph View!?

I don't know if I'm the only one excited about that feature, but boy, I AM excited. What I'm talking about: How would you like to have a graphical representation of your dialog structure for free, right next to your dialog script. Live. Did I mention it's just there, like in 'free'?

Take a look:

This graph is just generated while you type your script and only updated when it detects meaningful changes to the dialog content. As you can see, the graph only supports a subset of the language features so far, which are scenes, dialog lines, and arbitrarily nested conditions. This means no hubs, choices, jumps, and special conditions like the "first time" and "parting" concepts.
It might be only a view, but you have several options as a user to manipulate its representation. I descibe the smal blue and whitish buttons in the view toolbar from left to right:
  • You can toggle the orientation between "top down" (default) and "left to right" and rearrange the views, so that you can work with the tool like this, for example

  • You can toggle if the graph should be kept in synch automatically (default) or pause that behavior
  • You can crop the dialog lines if they extend a certain amount of letters (8 by default, just randomly chosen) to keep the nodes handy, or you can display the complete text per line.

  • You can increase and reduce the horizontal and vertical distance between nodes to avoid node overlaps within reasonable (read: randomly selected) limits
  • Last but not least the view is zoomable. This means you can select a zoom percentage using the small arrow button pointing down at the toolbar

  • I just selected 50%, which might be reasonable especially when the graph gets bigger. In this mode, the nodes act in a fisheye-like manner. This means that, whenever you hover over a node, it is "brought to front" like in its 100% representation so you can easily inspect the nodes by hovering with the mouse above the graph, as shown below for the "Ah, you again..." dialog node.

Of course, this is just the beginning of this exciting new feature and I'm looking forward to map all the language constucts to the graph view in a reasonable way!
But anyway, I think it is pretty cool and I should definitely make a video to show you how it works in motion. Speaking of videos, I also tried to record some "getting started" sessions. But man, that is time consuming and odd... anyway, check out the videos if the written documentation you can find in the downloadable archive is not sufficiant: https://www.youtube.com/user/MrRobWalter/videos

As always, feedback is very welcome!

As a side note: I write all my code for this project with the superior Xtend language. This language is so convenient and pure awesome, that I always fell like being punished whenever I have to read or write Java code, which luckily doesn't happen all to often anymore.

Donnerstag, 8. August 2013

DialogScript: New Version 0.1.0

I just uploaded a new version of the DialogScript tool: https://dl.dropboxusercontent.com/u/10307034/bobscodingcorner/DialogScript_v0.1.0.zip

The language now features player choices and some smaller features like smart modifiers and wildcards. Everything is documented in the PDF file available in the archive.

I also implemented some examples which are available in the content assist feature, but I also provide them here, to give you an idea of how the new language design works:

#############################################################################

Example 1: Scout and Skull
#############################################################################
 
characters: Scout, Skull
 
// switches replace the former conditions and are either on or off (off by default!)
// I wanted to find a simple metaphor for boolean variables everyone can relate to
switches: talked_Skull is off, name, blinky_Nigel, skull_Nigel, pole,
skull_Rock, skull_Moved
 
// conditions allow to concatenate switches with 'and' operator
// this might seem like an unnecessary intermediate step compared to boolean logic,
// but I want to see if it proofs useful in practice to separate boolean logic that way
conditions
  HasTalkedSkull when talked_Skull is off
  HasTalkedPole when pole is off
  HasTalkedSkullRock when skull_Rock is off
  HasNotTalkedJake when name is off
  KnowsNameIsNigel when blinky_Nigel is on and skull_Nigel is off
  IsSkullMoved when skull_Moved is on
end
 
// scene names are now also in quotation marks, they are referable now
scene "Larger Scene Sample"
  // default lines now feature a 'defaults'...'end' structure
  // inside, you can also use conditional dialog, 
  // just as in 'first time' or 'parting' cases
  defaults
    Skull: "Hey!"
  end
 
  // the 'first time' and 'other times' conditionals are now merged together...
  // seems to me to be more homogenous 
  first time
    Skull: "What are you doing here?"
    Scout: "Huh!?"
  else
    Skull: "You again."
    // as mentioned, you can also nest conditionals here
    // they feature the more common and shorter 'if'-'else if'-'else' keywords now
    if (HasNotTalkedJake)
      Scout: "Hi Jake!"
    else if (KnowsNameIsNigel)
      Scout: "Nigel, how's it hangin'?"
    end 
  end
 
  // hubs are meant to be loops which contain player choices
  // hence, hub's need to be explicitly left using an 'exit'-statement or
  // by invoking another scene or hub (yes, hubs can be called :))
  hub "Main Hub"
    
    // by default, choices are offered to the player every time a hub (loop)
    // is executed. The modifier 'single' allows to mark a choice as 'only available once'.
    // Thought of it as nice abbreviation instead of creating a switch/condition pair for every 'single choice'
    // The name of the choice (e.g. "Talking Skull") can be used as a preview text for the player on screen.
    single choice "Talking Skull"
      Scout: "I never expected you to talk."
      Skull: "Why not? You seem to be doing remarkably well for a pussy-cat."
      Scout: "But you're just a skull."
      Skull: "So, you've got something against skulls have you?"
      Scout: "No, I..."
      Skull: "It's always the same - prejudice just follows me around."
      Scout: "Really?"
      Skull: "Well, not that I actually go anywhere..."
      Skull: "I blame Ray Harryhausen for stereotyping the whole subject of skeletons."
      switch on talked_Skull // at the end of a choice or block, you can change the value of switches and/or leave the hub
    end
 
    // Writing the examples proofed to me that it is useful to make choices optional
    // hence, inside hubs, you can use the special conditional statement 'choices if'
    // Note that it is not possible to nest these kind of choices to avoid too complex structures
    // but I'm sure that this isn't necessary
    // Conditions can be chained with the 'or' operator.
    // That way, you can create 'chunks' of booleans that need to apply together at the
    // 'conditions'-statement above, and chain them here as alternatives
    choices if (HasNotTalkedJake or KnowsNameIsNigel)
 
      choice "Name"
      // inside choices, you can use "standard" conditions (can also be nested just as outside of hubs)
        if (HasNotTalkedJake)
          Scout: "What's your name?"
          Skull: "I'm not in the habit of giving my name out to just anyone, you know."
          Scout: "It's something embarrassing, isn't it?"
          Skull: "Not at all! It's, er... Cutthroat Jake! Yes."
          Scout: "You just made that up."
          Skull: "No I didn’t!"
          Skull: "You can call me Jake if you like."
          switch on name
        else if (KnowsNameIsNigel)
          Scout: "That mouse over there..."
          Skull: "I hate that mouse.  Do you have any idea what he did to me?"
          Scout: "I..."
          Skull: "He had the audacity to build a nest in my eye socket."
          Skull: "I was only able to drive him out by whistling for six hours straight."
          Skull: "Do you realise how difficult that is when you don’t have any lips...?"
          Scout: "The mouse told me your real name is Nigel."
          Skull: "Well..."
          Scout: "What sort of a name is Nigel for a skull?"
          Skull: "I know, I know..."
          switch on skull_Nigel
        end
      end
    end
 
    single choice "Pole"
      if (IsSkullMoved)
        Scout: "Why were you stuck on that pole?"
        Skull: "I was placed there as a warning to intruders."
      else
        Scout: "Why are you stuck on that pole?"
        Skull: "I've been placed here as a warning to intruders."
      end
      Scout: "Intruders?"
      Skull: "People like yourself, who come here looking for the treasure hidden in Skull 
              Rock."
      Skull: "Oops!  Forget I ever said that."
      Scout: "Not very good at your job are you?"
      Skull: "You call this a job? It’s certainly not one I would have chosen myself, if I’d 
              had any say in the matter."
      Skull: "It's not as if I can just walk away and get another job."
      Skull: "When a skull doesn't even have the rest of his skeleton with him, career 
              options are severely limited."
      Scout: "Talk a lot, don't you?"
      Skull: "It's the sheer and utter boredom – I haven't had a decent conversation in 
              forty three years."
      Skull: "Listen, you won't tell anyone that I mentioned the treasure, will you?"
      switch on pole
       end
 
    single choice "Skull Rock"
      Scout: "What can you tell me about Skull Rock?"
      Skull: "Not very much, I'm afraid.  I’ve never even seen it."
      Scout: "Why, because you don’t have any eyes?"
      Skull: "No, I can see you just fine."
      Scout: "Weird, that..."
      Skull: "Ever since I was placed here I’ve been facing in the same direction."
      Skull: "And the local monkeys are really awful – telling me how beautiful the view is 
              and how wonderful the sun looks as it sets over the sea."
      Scout: "Now, don’t get upset."
      Skull: "I’d weep if I had any tear ducts..."
      switch on skull_Rock
    end
 
    choice "Exit"
      Scout: "I’ll be seeing you."
      Skull: "Okay, bye.  Don't forget to come back soon."
      exit scene // at least one of these 'jumps' is mandatory, since the hub could never be left otherwise
    end
  end
end scene
      
#############################################################################
Example 2: Risen
#############################################################################
 
characters: PROTAGONIST, JAN
switches: place, foodOrGold, safe, offer, harbourTownQuest, followJanToTheWest
 
conditions
  AskedAboutPlace when place is on
  AskedAboutFoodOrGold when foodOrGold is on
  AskedAboutSafety when safe is on
  TalkedOffer when offer is on 
end
 
scene "BANDIT OUTPOST"
  
  first time
    PROTAGONIST: "I found this sword."
    JAN: "Good! I hope you know how to use it."
  end
   
  hub "Main"
    
    single choice "place"
      PROTAGONIST: "What can you tell me about this place?"
      JAN: "What do want to know?"
      switch on place
    end
 
    single choice "girl"
      PROTAGONIST: "There’s a girl came ashore with me, she’s in an abandoned house to the 
                    South."
      JAN: "South... Must be the old ship wrecker's house."
      JAN: "I’ll head that way later."
      JAN: "If she’s there."
      JAN: "I’ll take her somewhere safe."
    end
 
    single choice "help"
      PROTAGONIST: "I could do with some help before I go. I’m injured."
      JAN: "Hmm, so you are."
      JAN: "There’s a water barrel in the corner. Take a good drink and freshen up. 
            You stink of sea water."
      JAN: "If you’re planning on getting hurt again, you should get your hands on a few  
            healing potions."
      PROTAGONIST: "Where can I find some?"
      JAN: "Well, they don’t grow on trees. You’ll have to find a trader for those. 
            There are a few traders still around."
    end
 
    choices if (AskedAboutPlace)
 
      single choice "FoodOrGold"
        PROTAGONIST: "Looks like I’m going to be stuck here for a while. Anywhere I can get 
                      some food or pick up some gold?"
        JAN: "Ha! Not round here at any rate!"
        JAN: "I just asked for this post so I can watch the storms. Amazing things when they 
              blow past."
        JAN: "Strange how they never come inland."
        JAN: "Two weeks ago I would have sent you straight to Harbour Town. But not anymore."
        PROTAGONIST: "Why not?"
        JAN: "It’s full of inquisition. Protecting those ruins and recruiting for the damn 
              white robed cult at the Monestary."
        JAN: "I’m with the Don myself. We’re the ones who’re still free. Living in a camp in 
              the middle of a swamp. But freedom’s freedom."
        JAN: "I’d head to the swamp, see if the Don’ll take you in, or try your luck in 
              Harbour Town. Just stay away from the Monestary."
        switch on foodOrGold
      end
 
      single choice "monestary"
        PROTAGONIST: "You mentioned a Monestary. What happens there?"
        JAN: "The last place you wanna be!"
        JAN: "It’s where the Inquisition train their recruits. Brain wash’em!"
        JAN: "Stay well away from there."
      end
    end
 
    choices if (AskedAboutFoodOrGold)
 
      single choice "Safe"
        PROTAGONIST: "So, is Harbour Town safe? What are the Inquisition doing there?"
        JAN: "Not much at the moment. There are still some of our boys in there. They might 
              help you out."
        JAN: "And that white robed lot probably won’t bother you too much."
        JAN: "I can show you the way, but believe me, you’d be better off at our camp."
        switch on safe
      end
 
      single choice "Offer"
        PROTAGONIST: "What’s the Don got to offer me?"
        JAN: "Meat, bear, a job? They might even train you as a fighter. You got a sword, 
              after all. Most they’ll let you have up at the Monestary is a staff! Just a 
              big stick! That’s not a real weapon!"
        switch on offer
      end
 
    end
 
    choices if (AskedAboutSafety)
      single choice "Harbour Town"
        PROTAGONIST: "Can you show me the way to Harbour Town?"
        JAN: "Sure. Follow me."
        switch on harbourTownQuest
        exit scene
      end
    end
 
    choices if (TalkedOffer)
      single choice "Camp"
        PROTAGONIST: "All right, show me the way to your camp."
        JAN: "Good man! Follow me."
        switch on followJanToTheWest
        exit scene
      end
    end
 
    choice "End"
       exit scene
    end
 end
end scene
      

#############################################################################
Example 3: Heavy Rain
#############################################################################
 
characters: LAUREN, SCOTT
switches: convince, compassionate, persist, trick, buy, disappearance, johnny, johnnysHome, suspect, inRoomCenter is on, offerCigarette, police, johnnysFather, leads
 
conditions
       UnlockPhase2A          when convince      is on and compassionate  is on
       UnlockPhase2B          when convince      is on and persist        is on
       UnlockPhase2C          when compassionate is on and persist        is on
       TriedToBuy             when buy           is on
       HasTalkedDisappearance when disappearance is on
       HasTalkedJohnny        when johnny        is on
       HasTalkedJohnnysHome   when johnnysHome   is on
       HasTalkedSuspect       when suspect       is on
       HasntOfferedCigarette  when inRoomCenter  is on and offerCigarette is off
       TimesUpA               when police        is on and johnnysFather  is on
       TimesUpB               when police        is on and leads          is on
       TimesUpC               when leads         is on and johnnysFather  is on
       TimesUpD               when leads         is on and johnnysFather  is on and police is on
end   
 
scene "LAUREN’S MOTEL ROOM. EVENING"
 
  first time
    SCOTT: "My name is Scott Shelby. I’m a private detective.
            The families of the victims of the Origami Killer
            asked me to investigate the murders. I came here
            just to ask you some questions about Johnny."
    LAUREN: "I already told the police all I know and have nothing
            to add. Leave me alone."
  end
             
  if (TriedToBuy)
    LAUREN: "Leave me alone."
    exit scene // exiting a scene is now possible
  end
 
  hub "Phase 1"
      
    single choice "Convince"
      SCOTT: "The killer is walking around free as we speak.
              He’ll kill again if he’s not arrested."
      LAUREN: "My Johnny’s dead, so what difference does it make?"
      switch on convince
    end
            
    single choice "Compassionate"
      SCOTT: "I understand, Lauren. I know what you’re going through."
      LAUREN: "Oh, yeah? You know what it feels like to find your own
              son’s body on a wasteland? I’m sorry, I don’t believe you
              have the slightest idea what I’m going through, Mr. Shelby."
      switch on compassionate
    end
            
    single choice "Persist"
      SCOTT: "There’ll be other victims if we don’t stop the killer.
              You have got to help me, Lauren. You may know something
              that can aid the investigation."
      LAUREN: "Help you? There’s nothing you can do! My son is dead!
              Do you hear me? He’s dead!"
      switch on persist
    end         
      
    choices if (UnlockPhase2A or UnlockPhase2B or UnlockPhase2C)
 
      choice "Trick"
        SCOTT: "If we don’t find the killer, there’ll be other mothers
                who find their son’s body on a deserted wasteland.
                But you’re right! Why should you care? It’s not your problem
                anymore, right?"
        [Long pause, Lauren will sit down on the bed before she continues]
        LAUREN: "What do you want to know?"
        switch on trick
        enter hub "Phase 2" // this instantly switches to hub "Phase 2"
      end
                   
      choice "Buy"
        SCOTT: "I bought ten minutes of your time, didn’t I? All I ask is
                that you use that time to answer some questions"
        LAUREN: "You want to pay me to tell you about my son, is that it?
                You can buy my body, Mr. Shelby, but my son is not for sale!
                Get out of here! GET THE FUCK OUT!"
        switch on buy
        exit scene // player made the wrong choice    
      end
    end
  end
               
  hub "Phase 2" 
 
    // random is a modifier for conditionals and choices and allows the author to mark them
    // as random. The exact implementation needs to be done by the dialog engine
    random if (TimesUpA or TimesUpB or TimesUpC) 
      LAUREN: "Time's up Mr. Shelby. I hope you got what you wanted. Now get out of here."
      exit scene
    end
            
    if (TimesUpD) 
      LAUREN: "Time's up Mr. Shelby. I hope you got what you wanted. Now get out of here."
      exit scene
    end
            
    single random if (HasntOfferedCigarette) [Random, when the player does not move towards the bed]
      LAUREN: "Do you want a cigarette?"
      SCOTT: "No thanks, I quit."
      LAUREN: "That's brave."
      switch on offerCigarette
    end
                   
    single choice "Disappearance"
      SCOTT: "How did your son disappear?"
      LAUREN: "He used to go play with the neighborhood kids after school.
               It was pouring down something awful that day. I’ll never forget it.
               All his friends came home 'round 3, all except him."
      switch on disappearance                
    end
            
    single choice "Johnny?"
      SCOTT: "Tell me about Johnny. What kind of kid was he?"
      LAUREN: "Johnny was really a good boy. Sometimes, he fought with other kids who called 
               me a… you know. In his own way, I think he understood what was going on."
      switch on johnny
    end
            
    single choice "Suspect"
      SCOTT: "Did you suspect anyone after he disappeared?"
      LAUREN: "I meet a lot of pretty shady characters in my line of work. Sure, I thought 
               of it at first. But it didn’t seem to make any sense. I don’t believe any of  
               my clients could have done that to my Johnny... and all those other kids?"
      switch on suspect
    end
            
    single choice "Johnny's home"
      SCOTT: "Did Johnny live with you?"
      LAUREN: "Yes. Of course I made sure he never met any of my clients. I wanted to stop, 
               you know... but we needed the money. I was trying to earn enough to get us 
               out of here."
      switch on johnnysHome                         
    end
                                       
    random choices if (HasTalkedDisappearance or HasTalkedJohnny or HasTalkedJohnnysHome or HasTalkedSuspect)
            
      single choice "Police"
        SCOTT: "When did you sound the alarm?"
        LAUREN: "About eight o’clock. I began to get worried, I went all around the  
                 neighborhood, I went to the wasteland where they liked to play, I went to 
                 see his friends. I called the cops about ten o’clock."
        switch on police
      end
                   
      single choice "Johnny's father"
        SCOTT: "Tell me about Johnny’s father."
        LAUREN: "A loser without a job who liked to beat me after a few drinks...
                 He left the day Johnny disappeared. I ain’t seen him since. Coward!
                 Good thing he left."
        switch on johnnysFather
      end
 
      single choice "Leads"
        SCOTT: "Do you know if they found anything on the wasteland? Any leads or witnesses?"
        LAUREN: "No. They said he must’ve run away and would probably end up coming back.  
                 His body was found five days later with an origami figure in his hand and 
                 an orchid on his chest."
        switch on leads
      end
    end
  end
end scene
       
#############################################################################
Example 4: Mass Effect 2 (a bit more tricky, but it still works ;))
#############################################################################
 
characters: EDI, SHEPARD, GRUNT
switches: openTank, gaveName, phase5, convinced, playerShoots, shooting, hasShot
 
conditions
  OpenTank when openTank is on
  KnowsName when gaveName is on
  Phase5 when phase5 is on
  GruntIsConvinced when convinced is on
  PlayerShoots when playerShoots is on
  EndsWithShooting when shooting is on
  DoneShooting when hasShot is on
end
 
scene "NORMANDY SR-2. PORT CARGO. OPENING THE TANK."
 
  defaults
    EDI: "The subject is stable, Shepard. Integration with onboard systems was seamless."
  end
 
  hub "Phase 1"
 
    choice "Keep it sealed."
      exit hub // this directly leads to the 'parting' section at the end of the script
    end
 
    choice "Open the tank"
      SHEPARD: "Stand by. I'm going to open the tank and let him out."
      EDI: "Cerberus protocol is very clear regarding untested alien technology."
      enter hub "Phase 2"
    end
 
    choice "Is he aware?"
      SHEPARD: "Can he see anything in there? Does he know where he is?"
      EDI: "Unlikely. Current neural patterns indicate minimum cognition. Barring ship-wide 
            power loss, the nutrients in the tank could sustain him for over a year."
    end
 
    choice "Is he dangerous?"
      SHEPARD: "Any idea how dangerous this guy is?"
      EDI: "He is a krogan, Shepard. If you are asking whether he is actively hostile,
            don't have the necessary data to answer. Okeer's technology could impart 
            data, not methods of thinking. The subject may know of his views, but would not 
            necessarily share them."
    end
 
    choice "Detect anything odd?"
      SHEPARD: "What can you tell me about this guy? Anything unusual?"
      EDI: "The subject is an exceptional example of the krogan species, with fully formed 
            primary, secondary, and tertiary organs, where applicable. No defects of any 
            kind, aside from the genetic markers of the genophage present in all krogan. I 
            cannot judge mental functioning."
    end
 
  end
 
  // hidden hubs won’t be accessed unless they are called explicitly
  hidden hub "Phase 2"
 
    single choice "Leave him where he is then"
      exit hub
    end
 
    single choice "He's to valuable to leave"
      SHEPARD: "He's either a powerful addition to the crew or a time bomb. I'd rather deal 
                with it now."
      switch on openTank
      exit hub
    end
 
    single choice "Do as I say, EDI"
      SHEPARD: "I won't be second guessed on my own ship, by my own ship. Do it."
      switch on openTank
      exit hub
    end
 
  end
 
  if (OpenTank)
    EDI: "Very well, Shepard. The controls are online. The switch - and consequences - are 
          yours."
    [Cutscene where the tank is opened, forwarding to next scene]
    enter scene "NORMANDY SR-2. PORT CARGO. RECRUITING GRUNT." // here, we invoke another scene
  end
 
  parting
    SHEPARD: "I'm leaving it the way it is. Make sure nobody tempers with it."
    EDI: "Of course, Shepard."
  end
 
end scene
 
 
scene "NORMANDY SR-2. PORT CARGO. RECRUITING GRUNT."
 
  first time [Grunt smashes Shepard against the wall and holds him there]
    // wildcards (in braces) can be used in dialog lines to set placeholders
    // which can be replaced by an appropriate dialog engine during runtime
    GRUNT: "Human. {Gender}. Before you die, I need a name."
  end
 
  // this hub and the subsequent dialog line show how you can model choices which are handled completely
  // identical in the game, without the need to write redundant dialog lines
  // (GRUNT will respond identically, no matter what the player chooses)
  hub "Phase 3"
 
    choice "You'll get more than that"
      SHEPARD: "I'm Commander Shepard, and I don't take threats lightly. I suggest you 
                relax."
      exit hub
    end
 
    choice "Of course"
      SHEPARD: "I'm Commander Shepard of the Normandy."
      exit hub
    end
  end
      
  GRUNT: "Not your name. Mine. I am trained. I know things, but the tank... Okeer couldn't 
          implant connection. His words are hollow. Warlord, legacy, grunt... grunt. 
          \"Grunt\" was among the last. It has no meaning. It'll do. I am Grunt."
 
  hub "Phase 4"
 
    // Quotation marks need to be escaped with a backslash
    choice "Why \"Grunt\"?"
      SHEPARD: "You wouldn't prefer \"Okeer\"? Or \"legacy\"?"
      GRUNT: "It's short. Matches the training in my blood. The other words are big things I 
              don't feel. Maybe they fit your mouth better. I feel nothing for Okeer's clan 
              or his enemies. I will do what I am bred to do... fight and determine the 
              strongest -- but his imprint has failed." 
      exit hub
    end
 
    choice "You want to die?"
      SHEPARD: "Why do you want me to try to kill you?"
      GRUNT: "Want? I do what I am meant to-- fight and reveal the strongest. Nothing in the 
              tank ever asked what I want. I feel nothing for Okeer's clan or his enemies. 
              That imprint failed. He has failed."
      exit hub
    end
            
  end
      
  GRUNT: "Without a reason that's mine, one fight is as good as any other. Might as well 
          start with you."
 
  hub "Phase 5"
 
    choice "Join my crew. We’ll find it" [Paragon choice]
      SHEPARD: "I have a strong ship and a strong crew, a strong clan. You'd make it 
                stronger."
      GRUNT: "If you're weak and choose weak enemies, I'll have to kill you."
      SHEPARD: "Our enemies are worthy. No doubt about that."
      switch on convinced
      exit hub
    end
 
    choice "You feel nothing for Okeer?"
      SHEPARD: "Is it that easy for Okeer's perfect krogan to abandon his mission?"
      GRUNT: "Okeer is just a voice in the tank. If his imprints are true, then he created 
              something stronger than him. So he's not worthy of me. And if his hatreds 
              aren't strong enough to compel me, they've failed, too. I feel nothing. I have 
              no connection."
    end
 
    choice "My command is your reason" [Renegade choice]
      SHEPARD: "I took you and I released you. Follow my command, and you'll have purpose."
      GRUNT: "Nothing in the tank imprints indicated humans could be so forceful. 
              You command as though you've earned it."
      SHEPARD: "My enemies threaten galaxies. Everyone on my ship has earned their place."
      switch on convinced
      exit hub
    end
 
    choice "Stand down or else"
      SHEPARD: "I'm offering you the chance to join my crew. Stand down. I won't ask again."
      GRUNT: "Asking marks you as unworthy. That's why you'll die first."
      if (PlayerShoots) [Player chooses to shoot Grunt in a QTE]
        SHEPARD: "You will stand down."
        switch on hasShot
        exit hub
      else
        enter hub "Phase 6-1"
      end
    end
 
    choice "I released you. You owe me"
      SHEPARD: "You should show more gratitude. Refusing an ally can bite you in the ass."
      GRUNT: "If I can brush an ally aside, what use are they? You're only worthy of dying 
              first."
      enter hub "Phase 6-1"
    end
 
  end
 
  hidden hub "Phase 6-1"
 
    choice "You leave me no choice"
      SHEPARD: "I gave you a chance. This is on you."
      switch on shooting
      exit hub
    end
 
    choice "I was being generous"
      SHEPARD: "Talking was the generous Plan A. Here's Plan B."
      switch on shooting
      exit hub
    end
  end
 
  if (GruntIsConvinced)
    GRUNT: "Hmm. Hmph! That's... acceptable. I'll fight for you."
    [camera reveals that Shepard has been pointing his gun at Grunt the whole time]
    SHEPARD: "I'm glad you saw reason."
    GRUNT: "Hmm? Ha! Offer one hand, but arm the other."
  else if (EndsWithShooting)
    [gets shot by Shepard several times, but shows no signs of weakness]
    GRUNT: "Huh?"
    switch on hasShot
  end
 
  if (DoneShooting)
    GRUNT: "You offer one hand but arm the other -- and don't hesitate. Maybe you are 
            worthy. You will give me strong enemies? A chance to find my own reason for the 
            skills in my blood? Very well. I will fight for you."
    SHEPARD: "Try that again and I won't be so patient."
  end
 
  parting
    GRUNT: "Wise, Shepard. If I find a clan, if I find what I... I want, I will be honored 
            to eventually pit them against you."
  end
end scene