The SKALD Markup Language

The Basics

SKALD is a game engine specifically written for digital gamebooks and interactive novels. The design vision for SKALD has been to create an engine that allows for a strong narrative to be built upon a foundation of classic RPG features (character development, tactical combat, loot etc.) as well as certain rouge-like features (procedural generation). The game engine is designed with a strong separation between data and logic in mind. The reasoning being that it must be possible for a designer with minimal technical expertise to add content. Also keeping all data separate from the logic will allow multiple gamebooks to be published using SKALD without making changes to the engine itself.

A mage
The mark-up language is where the magic happens

The SKALD markup language has three main components:

  • The XML that encases all the data
  • A simple string markup used to create procedural content and to set the order of resolution for any embedded dynamic code.
  • The formatting for embedding dynamic code.

The XML

SKALD uses ordinary XML to store data about all its game objects (scenes, items, NPCs). I briefly considered using JSON. However, after doing some research I arrived at the conclusion that the advantages of using JSON over XML were nil for this application. JSON would perhaps have been more relevant if I had a stronger emphasis on developing for a web-based service (as JavaScript loves JSON).

XML

 

The String Markup

All the data in the XML files are parsed to strings. Those strings are then run through a processString() function that performs any action prescribed by the string markup, resolves any embedded code and returns a copy of the processed string.

One of the first things I found that I wanted was a short-hand for returning random strings from the XML. My solution was simply to use the ‘/’ character. Adding ‘/’ to any string will cause the processString() function to return the result on either the left or right side of the ‘/’. For example:

processString(“I like hamburgers/I like sushi/I like ramen”)

returns either “I like hamburgers”, “I like ramen” or “I like sushi”.

Right of the bat you can see that this can get verbose. The solution was to add parenthesis. Now the previous string can be shortened like so:

processString(“I like (hamburgers/sushi/ramen)”).

By nesting parenthesis (resolves from inner, to outer) you can make complex string such as:

processString(“I like ((cheese-burgers/bacon-burgers/BBQburgers)/sushi/ramen)”)

The applications for this is to create variety in the text (especially for scenes that the player keeps returning to) or to provide randomized output from certain XML tags.

For instance, each scene has one or more exits that lead to the next scene. That exit contains a tag called <tar> (target) which is basically the id of the next scene to load. Since the value of <tar> is stored as a string and ran through the processString() function, you can write an exit with a target like this:

<tar>2/3/4</tar>

The result is an exit that randomly sends you to scene 2, 3 or 4. Pretty neat for making things like random encounters.

Dynamic Code

So far, the system is completely stateless. Processing the string

processString(“I like (hamburgers/sushi/ramen)”)

will return a random output every time. The next logical step was to add a system for storing and modifying variables at run-time. The solution is for implemented by using “Reflection” in C# to access functions in the code via strings passed from the XML. For instance I have a function addVariable(key, value) that creates a variable (named key) and sets it to value. So

addVariable(“food”,”hamburgers”)

will set the variable “food” to “hamburgers” and

getVariable(“food”)

now returns “hamburgers”. Using my own notation to embed functions in the xml datafiles the functions must be wrapped in curly braces like this:

{addVariable|food;hamburgers}

It is now possible to write:

processString(“I like {addVariable|food;(hamburgers/sushi/ramen)}”)

This line of code will return for instance, “I like sushi”. And then

processString(“My favorite food is still {getVariable|food}”)

Will utilize the value stored above and return “My favorite food is still sushi”. Note that the “processString” function will substitute everything wrapped in curly brackets for the return value of the function wrapped in the curly brackets. 

The SKALD system contains about a dozen functions that can be called dynamically. In large part these are used to manipulate and perform logical operations on variables (allowing for conditional branching etc.). The result is that the number of scenes that need to be written can be reduced drastically. It also allows procedural content to be added by the designer, working only through the XML file.

Overall, I’m pleased with the functionality of the SKALD markup language, as it exists today. It’s expressive enough tell any kind of story and to give a direct binding between the story elements and the underlying RPG mechanics. It also supports the concept of separating data and logic and it greatly reduces duplication of data by cutting down on the number of scenes that need to be written. At the same time, it’s still possible to write an entire module by using only XML and entirely forgoing the use of the string markup and dynamic.

Now, off to write some adventures!