Writing IF with Raconteur, part 1: a difficult situation
Raconteur is “Undum with batteries included,” a set of tools and libraries that speed up Undum development and give it a gentle learning curve. Raconteur, like Undum, has an API documentation out; but API documentation is great as a reference, not so much for learning something new. And Raconteur, while (I hope) still substantially easier to work with than Undum, still has a learning curve.
This is the first in a series of posts walking through the authoring of an IF game with Raconteur/Undum. This one goes from setting up a development environment to writing down your first situation.
Before we begin
Raconteur’s “toolchain” is built on a number of command line tools. OS X and Linux users are probably more comfortable using those; if you’re on Windows, I recommend installing PowerShell or cygwin to get a better command-line interface that’s more like the Unix interface I’m using and will be referencing in this tutorial. Lines beginning with a
$ symbol are command lines, as is tradition.
Setting up Raconteur
Raconteur relies on several tools to work. Most of them will be installed automatically by npm, but first we have to install Node.js. You can download a Node installer directly and install it (Recommended for Windows and OS X users). On Linux, your distribution probably packages Node, and you can install it with apt, yum, or pacman (or one of the GUI frontends for those commands, like Ubuntu’s software center). OS X users can also install Node through Homebrew.
The node package manager, or npm, is bundled with Node itself, though if you installed Node through a Linux package manager, for example on Ubuntu, you may also have to install npm as a separate package. Once you have npm working on your system, you’ll want to install Gulp.
Gulp is Raconteur’s build system. You’ll want Gulp installed “globally” (Really, locally to your user account) for ease of use, since we’ll be using Gulp as a command-line tool:
$ npm install -g gulp
Finally, we can set up Raconteur itself. Download the scaffold zip file and unpack it; you can move or rename the
raconteur-scaffold-master directory anywhere you like. From inside that directory, do:
$ npm install
This will install a local copy of Raconteur for your game, along with all of the tools and dependencies it uses. It’ll take a while to install everything. Once it’s done, however, you can start up the development server:
$ gulp serve
If everything is working correctly, you should see something that looks like this on your terminal:
If you open your browser and point it at
localhost:3000 (Which should happen automatically), you will see a “Raconteur Scaffold” title card, and you’ll be able to click it and see the first situation of the scaffolded game. You’re good to go!
The scaffold has the following file structure, ignoring folders like the
node_modules directory where npm installed all of the dependencies:
. |-- game | `-- main.coffee |-- Gulpfile.js |-- html | `-- index.html |-- img | `-- storyteller.jpg `-- less |-- main.less `-- mobile.less
- main.coffee is the entry point for your game. You can add other files (And
requirethem), but the assumption is that your game will mostly live on this one file, which will define your game’s story and mechanics. Mostly we’re going to be dealing with this file.
- Gulpfile.js is the build system configuration file. Feel free to peek inside if you’re curious, but you don’t need to edit this.
- index.html is the actual html page for your game. You’ll want to make a few straightforward edits to this to add your game’s name, legal information, and so on. It’s commented with
EDITin caps just before everything that needs to be changed. If you want you can leave this alone for now, too.
- storyteller.jpg is an example image included with the scaffold. Any files with the extensions .png, .jpg, and .jpeg that you put in the
img/folder will be copied directly to your game distribution; obviously this is where you can put any images you want to include in your story.
- main.less and mobile.less are Less files. Less is a language that gets compiled down to CSS. It has things like variables, mixins, and functions; you can think of it as a more convenient version of CSS, though all valid CSS is also valid Less. main.less is the file that Gulp is set up to compile; it includes mobile.less, which contains mobile-specific CSS definitions. For now, don’t worry about them.
# Require the libraries we rely on
This is basically boilerplate.
require() is a function (in this case defined by Browserify) that essentially does the equivalent of an “import” or “include” statement in another language. All of this is stuff you don’t really need to touch for now, but it’s helpful to look at because you can see the names of everything we’ve imported:
img. Deliberately, this is all of Raconteur; if at the end of your project you find you didn’t use some part of it, you can take out the
require() statement to make the bundle smaller.
The line saying
situation.exportUndum() is a call to a function in Raconteur that makes sure there is a global Undum object which is the same global Undum object everything is using. How this works might change in the future, but for now, Undum relies on that global object existing.
Every Undum game should have an unique id and version. Those are used for save games; if your game has the same ID as another game, then your saves will collide. Very bad. I strongly recommend using an [UUID], which are pretty much guaranteed to be unique and are also valid IFIDs.
This is where it actually begins: The first situation. I’ll explain what all of this means in a moment, but first let’s finish the tour of the game file.
Qualities are one of Undum’s most unique features, an explicit list of items that represent your character, which is very malleable. Here I have an example cribbed from fantasy gaming, but really qualities can represent anything: The Play used them to represent the moods of the various performers; Living Will, the various legal fees and bequeaths; Almost Goodbye used it as a combination checklist (of people to talk to) and indicator of the player character’s mood.
I will talk more about qualities in a future tutorial.
You should set
undum.game.init once and only once. It gets passed
system, Undum’s two state objects (which are not globals but rather are passed as arguments into your code). This function gets called once by Undum, right when the game begins, Here as an example I am initialising all of the game’s qualities by setting them initial values.
The last line is a little mysterious, probably because it’s a bit of a hack.
undum.begin() is a nonstandard addition of the modified version of Undum used by Raconteur; it tells Undum to start running. Passing a function as an argument to JQuery binds that function to run when the DOM is ready, ie when everything has been loaded by the browser and is ready to go.
situation() is the heart of Raconteur, a new way of defining Undum situations. It has a lot of complexities, but it’s designed so that you don’t have to know about all of them until you need them.
As you can see, CoffeeScript lets us eschew parenthesis and curly braces. Instead, it relies on indentation to tell where things are supposed to go. Let’s go back and look at the example situation the scaffold starts out with:
situation() two arguments: “start” and an object literal with one property,
content. From that, the function will make an actual Situation object and pass that on to Undum, adding it to the list of game situations.
A situation, in Undum, is a basic story building block. They’re equivalent to Twine’s passages or ChoiceScript’s scenes. While all of the text from previous situations remains on the page they are functionally “dead”; only the “current” situation is considered by Undum. A situation isn’t just a description of some content to print; it also holds game logic. For example, whenever a special link (an “action link”) is clicked, Undum asks the current Situation object how to proceed.
When a situation becomes the active situation (is “entered”), Undum calls its
enter() method. Raconteur supplies (a very advanced version of) that method for you, so you don’t have to define it by yourself. That method gets passed three arguments:
system (who are objects holding Undum’s state and properties, and we will get to know them better later) and the name of the previous situation, which for the first situation entered is just going to be
This situation, called “start” has special status: Undum looks for such a situation to begin the game. It is the only situation that has that kind of special status. An “ending” is really just a situation that goes nowhere, for example, and there is no demand that the start situation, or any situation for that matter, be visited only once.
The only property of our situation is
content, as the name implies, is the main content of the situation: What is printed when the situation is entered. We can change the content and watch as the game itself changes:
If you have
gulp serve running, saving the file should cause it to rebuild and reload your browser. Start your game again and note what happens.
content, like most (but not all) strings in Raconteur, is formatted using [Markdown]. So the line starting with a
# becomes a header, a
<h1> element; and the other two lines, separated by a blank line, become a paragraph.
We can add more situations:
A situation name should be a valid identifier - that is, it should consist only of the characters
a-zA-Z0-9_. One nice thing about Undum is that if you use the name of a situation as the target of a link, it will become a situation link, connecting to the next situation.
[example](http://example.com) is Markdown syntax for a hyperlink; it translates to
<a href="http://example.com">example</a> in html. So you can save your main.coffee file with that set of situations, and it should produce a rather short story.
Undum automatically disables all links when a situation is exited, so you don’t have to worry about players being able to click old links and backtrack. This means that the second situation, “enter_the_cave”, is a branching point; the player has to choose to go left or right, and they will only be able to pick one, since the other link will be disabled as soon as they click on one.
If you’ve gotten this far: Congratulations, you now know just enough to write a Raconteur story. With just simple situations and basic links, you can write a Choose Your Own Adventure-style branching story. In the next part of this tutorial, I’ll talk about more complex choice and adding logic to a Raconteur story, so that you can have more complex mechanics than a pure CYOA-style game.