Here we are some months later, with that project “Finished”. As a quick reminder, the idea was to create a scripting language close to theater blocking, to program dialogues in games such as VN. You can read my thoughts and me laying down the basis of the project here.

I implemented this in Godot, for several reasons, the first being that it’s a game engine, and I want to make games and profit from everything the engine has to offer. Having VN sections followed by gameplay will be good. The second reason is I like how Godot is centered around the concept of emitting and receiving signals, allowing us to completely decouple our scripts from actual dirty code.

The first step necessary to make this scripting language is to parse the text. And the first step to parse this text is to create tokens. The computer doesn’t know when a word ends or finishes. It doesn’t know what to do when there is a new line or a comma. So you need to tell him. The sentence Alice is waking up, can become a list of tokens Alice, is, waking, up, and probably an end of a statement or a newline.

Now you have a bunch of tokens but none of it have much sense. It sounds reasonable to everyone that programmed that, in a statement like cat_number = 2, not all these words are the same. cat_number is the variable name, = is an operator and 2 is an int. However, for now, it’s still a bunch of characters.

On the other hand, in most languages, if I write "cat_number = 2", I tell the computer to just have a sentence cat_number = 2.

We cut the sentence in pieces and we try to see which pieces have meaning together

To give meaning to all those tokens and put them into statements, we have to write a parser. This will take a bunch of tokens, and give them meaning, according to a bunch of rules you would have written (your grammar).

Now, I never wrote this kind of code before, nor I have seen it during my studies (we don’t study compilers in Biology) but I had a very good idea of what I wanted to achieve with this script which limited the problem space by a lot. I decided that statement would be either on a new line or between {}. At first, everything is a sentence. My first goal, after all, is display sentences on the screen.. Sentences that would start with the name of one of the “Actors”, would get special treatment. They would be either something that the actor is saying to the stage or that they do. As such:

The boat was rocking gently on the lake surface. is a sentence

Alice appears left is an action composed of Alice (actor) appears (action) and left (argument).

Alice - Nothing like a day of fishing! Nothing like a day of fishing is a sentence pronounced by Alice (actor) separated by - (separator).

We get then a simple algorithm.

Simple Algorithm helping me parsing the statements

Then if you are astute, you are already asking “how does it know what’s an actor?”. I chose to declare this at the top of my file. This way my script knows that if a sentence starts with a word that is at the top of the file, it will be an actor.

---
Alice
---
The boat was rocking gently on the lake surface.
Alice appears left.
Alice - Nothing like a day of fishing!

Now, most of the work is done. I need to put this rule into objects Godot understands. I create different nodes from the statements. For example a statement with an action will be transformed into a Godot node with the actor, the action being performed, supplementaty arguments, and a bunch of methods telling what to do with it.

I won’t describe them all because there is little interest to do so but you get the gist, my tokens are now a collection of objects with a meaning (statements), such as an Action or a Dialogue.

Our snippet of script in action

Finally, the last step is to assemble all these nodes into a tree and interpret them. At first, I put the nodes into a linked list structure. My Godot program will go on each Node sequentially, and react accordingly. As such, a Dialogue Node will be recognized as a Dialogue and will be fed to the Textbox. An action node will call a Godot event and the program will do whatever that event triggers. It allows for huge freedom.

At this point, I already met my objective. This allows me to type the text that reads naturally and will be understood by my engine. However, I pushed myself a bit more to introduce choices in the scripting language. A choice needs to be parsed as such and then, for each choice, create a new branch. Once you select a choice, you should be able to display stuff specific to that choice, until you go back to the main branch (We do not have a linked list anymore for our collections of Nodes, but a real tree).

Here’s how it looks with a new character called Bob.

---
Alice
Bob
Beach
---
The boat was rocking gently on the lake surface.

Alice enters left

Alice - Nothing like a day of fishing!

Bob enters right

Bob - Hey it's me, Bob!

Choice [
Wanna get back on land?
{For sure! ; Alice - Let's Go! }
{No, I am fine here! ; Bob - Alright.
Bob - Let's compete to catch some big fish!
}
]

Alice - I really love fishing!!!

As you can see, it’s already much less readable and I don’t intend to use it extensively. Just when it makes sense in terms of time spent on coding versus readability.

Adding a second character, a background and choice is easy.

However, I can only have once choice at a given time. This limitation results from how I designed my parsing. If I wanted to handle it, I would probably have to write my parsing to handle recursion and then, build proper AST.

But my objectives were complete already, I made a domain-specific language to use for speeding up my productivity, I didn’t want to design a real computer language.

The engine is far from complete but it offers already quite a lot of features. One that I find important is the ability to load Resources and assets dynamically. Second is that I can parse and load the tree dynamically as well which allows you to not have to load your whole script at once, but you can load several files, just when you need it.

So far I have:

  • Text can be displayed in the Textbox
  • Text can have an author to tell who is speaking
  • Can load background, sprites, and music/sounds dynamically
  • Can play sounds
  • Can play animations
  • Can load other text files or even Godot Scenes from the script

I still need to implement save and a function to read the backlog, but this is already fairly complete to work on some games! I’m having a lot of fun with it and work on it on my spare time. Building that in Godot allows me to play with a lot of animation/effects I couldn’t do with something like Ren’Py or could not do with a lot of pain. Check below the video of my current project!