Raconteur, and why it exists

Taking a break for a moment from the tutorials to write about where Raconteur comes from, and where it’s going; starting with a survey of the landscape of choice-based game engines.

Mere anarchy (is loosed upon the world)

After releasing Terminator Chaser, I wanted to get something out the door in time for Spring Thing as well, because I have no sense of proportion. That project ended up being called Mere Anarchy, and it began with an honest scoping assessment, which was easy to do as the conclusion was pretty clear: There’s no way I’d be able to make a parser game, unless it was very small, because of the long testing period involved. While I can put up with my own horrid self-imposed deadlines, I can’t inflict them on other people so cavalierly. (I did have one heroic test reader for Mere Anarchy, though).

While I never considered anything other than Inform7 when planning Terminator Chaser, I went through various steps examining hypertext systems for Mere Anarchy, which at that point I had given the working title Craft.

A screenshot from Mere Anarchy: Looking around Pulpit's Shop

This scene — looking around at the oddities in Pulpit’s shop — was the first section of text written for Mere Anarchy. It acted as a sort of mission statement for the project. It was a concise taste of the tone, prose, and setting. And it also exemplified the type of hypertext interaction I wanted to play around with. Those kinds of hypertext effects were nothing new, but they were actually quite hard to do. I had a look through four engines for hypertext game development: Twine, ChoiceScript, Inkle Writer, and Undum.

ChoiceScript and Inkle Writer were right out from the start: They don’t support those kinds of interaction at all, and they are generally not very programmable. Since I knew my project wouldn’t fit into the model that those systems were pushing, I didn’t go with either of them.

This left me with Twine and Undum. Twine looked like the right choice initially, until I actually implemented Mr Pulpit’s shop scene, which is heavy on hypertext interaction, in Twine. There were two ways of doing this:

  • Using Twine 1.4, or Sugarcube in Twine 2, and custom macros
  • Using Twine 2 and Harlowe

Neither was really satisfactory. First of all: Writing custom Twine macros is at best underdocumented and at worst obscure, and those customs macros that are widely distributed are usually distributed in a minified form that makes them not so amenable to examination. The Twine community seems to rely on a sort of code bricolage process to make things happen, which I fear tends to make things a bit too arcane for me.

For a non-programmer, all programming is equally arcane. And for a master of the (alleged) craft, all programming is equally simple. I’m unfortunately neither of those things, so I figured I would rather use a system I could understand better than I understood Twine (Or had time to understand it).

And whether I was using someone else’s macros, or the base capabilities of the Harlowe story format, I couldn’t really get the effect I wanted, at least not without a lot of repetitive typing. The resulting passage source code was hard to read, and hard to modify, and I wasn’t too confident about writing a whole game like that. Plus, I don’t really care for the wiki markup that Twine used, especially its overzealous treatment of line breaks — Twine by default turns every line break into a <br> element unless the passage was tagged with nobr. This is further complicated by the fact that, for typographical reasons, I would much rather have text formatted with <p> elements. I’ve since used Twine for a different project (It Is Not So Much a Story), but felt that with what I wanted to do with Mere Anarchy, I would be cutting too much against the grain of the system.

This left me with Undum. And I didn’t much care for Undum either, initially. Though Undum stories are gorgeous-looking (Even without customising the stylesheet as I eventually did), and Undum is a pure JavaScript library that meant, unlike other systems, I’d be writing a game using a Real Programming Language (Well, as real as JavaScript anyway), Undum made so few assumptions about how an Undum game would be structured that it required a lot of customisation on the part of the author to work well.

Undum games are structured around situations, which are analogous to Twine’s passages. Each situation is just a JavaScript object that supplies a certain interface. This interface is very low-level. Base Situation objects are supposed to implement, by themselves, all of the logic of writing out their text, responding to actions (Hyperlinks that don’t lead to other situations), and updating the interface. As a result, the actual game code for an Undum game has a lot of boilerplate and repetition, unless the author essentially writes a helper library to hold all of their reusable code, starting with creating a new prototype for situations that is smarter.

In other words: Raconteur exists because writing it (or, rather, a precursor of it) was a pre-requisite for writing Mere Anarchy.

This is the source code for one of the shorter situations in Mere Anarchy:

situation 'waitforpulpit',
content: """
<p>
Taking the card from your hand, he disappears into the back of his shop
without a word, leaving you to wait #{replacera 'nervously'}nervously<a>.
</p>
"""
choices: ['fiddlewithphone', 'lookaroundshop']
replacers:
'nervously': '<span class="fade">quietly</span>'

This is what that situation would look like, written out as a default Undum situation:

undum.game.situations.waitforpulpit = new undum.Situation({
enter: function (_, system) {
system.write("<p>Taking the card from your hand, he disappears into the" +
" back of the shop without a word, leaving you to wait " +
" <a href=\"./_nervously_\" id=\"nervously\">nervously</a>.</p>");
system.writeChoices(
system.getSituationIdChoices(['fiddlewithphone', 'lookaroundshop']));
},
act: function (_, system) {
/* Since we only have one action in this situation, let's assume we got
that one. */
system.replaceWith('<span class="fade">quietly</span>', '#nervously');
}
});

This isn’t that much worse, but as situations get more complex, it becomes more and more repetitive. For example, every situation that supports more than one action has to have logic to figure out which action was called and respond accordingly. Overall, this isn’t really maintainable; Undum is not designed to be used this way — it’s designed to be used by first writing a few tools, and then using those tools to write your game. Raconteur is essentially a release of those tools in a pre-packaged form.

I care a lot about code readability, DRYness, and cognitive load, so the library of tools that I wrote was a fairly aggressive retooling of the Undum API. Using CoffeeScript is a fairly obvious move; CoffeeScript supports multi-line strings and interpolation. It also has a much more spare syntax that allows for the pseudo-DSL that Raconteur ended up being. This naturally leads to the other major bit of tooling: Using a build system rather than editing the game files directly, since the CoffeeScript files have to be compiled. This has some side benefits: When the game is packaged for distribution, for example, all files can be minified so it loads faster. Raconteur is, essentially, just a rewrite of the toolchain I built for Mere Anarchy in a form that should be more useful for other people.

The future of Raconteur (and Undum, maybe?)

Raconteur right now is still in an experimental phase, and a lot of architectural decisions have to be made regarding it. In particular, regarding the status of Undum: A lot of what I would like to do with it requires changes to the underlying Undum engine (The code inside Undum that handles scrolling text into view when it’s printed is a particular annoyance of mine) but that would involve either submitting some fairly brutal patches to Undum (and getting them accepted), or forking Undum; and forking a project isn’t something I want to do lightly. Right now, I am maintaining a slightly modified version of Undum to work with Raconteur’s Browserify setup, but that is likely to change over time and I get more information on what works better for users. Right now, the Raconteur scaffold is blissfully unconcerned with producing a very optimised end product; its nature as a game engine of sorts means that a reasonably long load time isn’t a huge problem.

Undum is another open question — I would be lying if I said I was totally happy with all aspects of the engine. In the process of writing Mere Anarchy, I actually submitted a couple of patches to Undum itself.

If you’re using Raconteur, or if you’ve looked at it and chosen not to use it, I’d love to hear about it — as always I can be reached on Twitter.