I decided to use the interactive fiction system Inform (2010) to implement the story world. At the time, this seemed like a good decision for a number of reasons.
It is very easy to define virtual world objects in Inform using its boiler-plate world model. Its libraries already include a number of default verb definitions to interact with those world objects. Inform also includes an input parser to handle text input from the user. This meant I would not have to deal with any natural language processing myself. All of these Inform features can easily be customized or extended to support game-specific requirements.
Created in 1993, Inform has already been used by other authors to create hundreds of games. It is debugged, stable, and still has an active community. Inform games run using a virtual Z-Machine architecture that has been ported to a impressively-wide array of hardware platforms, including some hand-held devices. More generally, interactive fiction games have a thirty-year history, so it is a well-known, established game genre.
I also had some prior experience using Inform, but I have never worked with a graphics engine. So I felt that a text-based approach would be much faster and easier to develop than a graphical one, especially for a simple prototype game. In short, interactive fiction seemed like a good place to start for interactive narrative.
This decision to Inform as a game engine then had a number of consequences.
Since this implementation was a prototype, I wasn't certain yet to what extent I could separate the implementation of the story level--which includes the drama manager, scenes, and internal character states--from the implementation of the world level. If I separated them, they would effectively become two distinct programs. Then the drama manager would not have direct access to the state of world objects. Instead, the drama manager would have to send queries across some sort of bridge between the two programs. But it would not be technically easy to connect such a bridge to the Z-Machine containing the world implementation. This may have been workable in a controlled environment, but I also wanted users to be able to play the game online from their own computers. So, instead of using some sort of communication bridge between two separate programs, I decided it would be safer to implement the story level using Inform as well.
Inform is a very simple, low-level, C-like programming language. It has no dynamic memory allocation, so an author must manually specify the maximum number of each type of object he might use during the course of the game. This restriction forced minor changes in the design of the event and action data structures. Also, because Marlinspike may potentially require a high number of event objects, the Demeter game exceeded the memory limitations of the Z-Machine architecture. Thankfully, it is possible to compile Inform games to an alternate virtual machine architecture named Glulx (Plotkin 2011). However, this switch to Glulx limited the choice of possible interpreters I could use to deliver the game online.
There is no garbage collection in Inform, so object use must be very carefully managed to prevent memory leaks or bugs resulting from accidentally reusing the same object in memory from two different code locations.
Inform has no advanced or dynamic data structures, such as hashes or lists. So, before even starting on Marlinspike, the first task I undertook was writing an array-based linked list implementation. Inform does not have floating point numbers either, which is why character affinities were written to vary from -100 to 100 rather than from -1.0 to 1.0.
It is also not possible to easily build or change strings of characters in memory in Inform. This means any output cannot be assembled and then manipulated before being printed. Instead, all output must be printed directly in linear order. Because of this, a fair amount of work went into handling simple things like printing "I" or "We" when a speaker is part of a group or adding an "s" to verbs depending on the number of character involved.
DST_GoParty.detail.prettyPrint(); print " enter"; if (DST_GoParty.detail.size() == 1) { print "s"; } print " from "; ...An example of Inform code used to report that the members of the current GoParty enter the room.
This makes narration very tedious to produce. It is not uncommon for a sentence of scene narration to take 5 to 10 lines to produce, and, occasionally, even more than that.
Another string-related headache was controlling the number of blank lines between paragraphs. Because the output of a scene can vary so much, certain paragraphs of a scene's text may or not print based on the current world and story contexts. Accurately printing one and only one line-break between each of a series of optional paragraphs is trickier than it first sounds, especially when any duplicate blank lines cannot be removed after assembling the output but before printing it.
Related to output, Inform will not display any of the output printed during a turn until the end of that turn. This means that if a bug causes an infinite loop at any point during the turn, there is no output displayed at all. Instead, the game simply hangs. The harried author then knows that an infinite loop was just activated somewhere within 23,000 lines of code but has no output to indicate where. And so the author must iteratively repeat the situation that caused the lockup while logically paring down the code that executed that turn until the bug is eventually located.
There exist a number of third-party extensions that do overcome some of these Inform shortcomings. However, as an author, I was often hesitant to use them. There is always some question as to how tested and supported these extra libraries are. If an extension fails, then I would be forced to puzzle through its code to debug it. Frequently, rather than risking that headache, I chose to do without the extension or write a quick work-around myself.
This worry is not unfounded. I used parts of the ORLibrary set of extensions (Fisher 2011), most notably to produce the menu of conversation options displayed when the PC talks to another character. The ORLibrary's talk menu code needed to be tweaked a couple times to run properly in different interpreters, such as Quixe (Plotkin 2011). The Zag interpreter (Zeppieri 2005) also had a very small bug that would cause lockups when the talk menu was used.
I do not mean to disparage the work of these fine programmers! Every piece of software has bugs that must be worked out. I am grateful for their work and the fact that their code was freely available so I could tweak it to meet my needs. My point is simply that the existence of extensions is not an automatic time-saver. Also, when libraries are part of the core language, they are generally more likely to be debugged and maintained than those maintained by third parties.
Yet admittedly, even if Inform did have an extensive core library of general data structures--like variable-length lists--Marlinspike would still need certain custom variations of these. For example, Marlinspike would greatly benefit from a robust searching mechanism to find matching objects in a list. Scenes need this functionality to easily find events that match their preconditions and hooks within the event history list. It is also frequently necessary to grab all NPCs that meet a certain criteria, such as those NPCs that are currently conscious, in a certain location, armed, and/or having a certain affinity or plan preference.
In writing Marlinspike and Demeter, this was another constant tension: whether to spend time implementing better custom tools and libraries or to just to press on without them. Given that this was meant to be a quick prototype and that later implementations would not be written in Inform, I generally tended towards the latter option. That said, I did write a number of useful helper methods for common tasks, though these tended to be rather ad hoc.
In short, using Inform meant starting with a simple language that required a lot of attention be diverted to nitty-gritty technical details. Then any advanced programming features I wanted had to be built from the ground up.
Aside from impacting the implementation of Marlinspike, choosing Inform as a game engine also determined the game's interface. Since the user perceives the Inform-defined objects through a textual description of the world and then enters commands handled by Inform's input parser, Inform determined the medium and constrained the manner of the finished game. Obviously, using Inform meant that Demeter would be an interactive fiction (IF) game.
Although interactive fiction has a 30+ year history, it is no longer widely popular today. Even the general command line mode of interacting with a computer through entering typed input and reading text responses is becoming increasingly rare. This means interactive fiction may be completely foreign to modern players.
Yet this shouldn't be a problem if the interface is still fairly easy to use. After all, every computer user usually knows how to read and type. However, in my own experience and based on watching a few people new to IF struggle through alpha versions of Demeter, the affordances of the interactive fiction medium are far from ideal.
The first step for the player is making sense of the world. Although each room is described in text, it is frequently hard to know which objects described there actually support interactions. Even when the exits of each room are explicitly listed in the room's description, it still requires a fair degree of visualization and memory to build a cognitive map of the virtual space. Simply examining an object for more information about it requires an explicit command on the part of the user, such as look at chair
.
Once the player determines what is present in the world, then the next step is interacting with it. The player must know what verbs are possible in general. Traditional interactive fiction games generally contained a lot of puzzles for the player to solve. In this context, part of the challenge of the game is determining what is even possible. Because each verb can have multiple synonyms, it is usually possible to produce a valid command after a few naive attempts. However, in the context of an interactive drama, where I want world-level interactions to be quick and simple so that players can focus on the story-level effects, this trial-and-error approach to learning verbs becomes a distracting and frustrating hurdle.
Some players also assume that, if they see a word in the game's output, then that word should be valid input. For example, if they read that an NPC just smiled at them, they assume that they can smile in return. However, in Inform, the output displayed is separate from the input accepted by the parser. It is up to the game author to make sure that every noun and adjective used to describe a world object in all of its possible states can also be used by the user to refer to that object in input. Adding support for new verbs is no small task either, since their grammar and their effects on various objects must then be defined. Frequently, it is easier as an author to not support a verb than to go through this work for the limited return of supporting a verb few players are likely to use anyway.
To compound this problem of forming valid input, not all verb-object combinations are valid. That is, even if the user has found a valid verb and is correctly referring to a world object, the resulting deed may still not be valid. For example, if an author added a Smile
verb, it would still probably not be useful to smile at a door or a table. But refusal text must still authored to handle these situations.
So there are many obstacles to producing valid input in IF: recognizing which objects can be interacted with, determining how to refer to those objects in the way the author intended, knowing which verbs those objects support, and then combining object and verb into a syntactically valid command. In Demeter, there is a further disconnect here since even a valid deed in the world may not be reported to the Marlinspike drama manager. As discussed previously, this is because the Inform library provides bland results for a number of verbs--such as Jump
and Sleep
--that the Demeter game does not in fact use. Thus, the user may even produce valid deeds that still do not affect the world or the story.
Most of these input affordances could be overcome by doing away with the command line and using a menu-based approach instead. For example, the names of world objects that support interactions could be highlighted in the description of a room. Clicking on these objects would then bring up a menu of verbs that that object supports. Using this approach, it would be obvious exactly which objects can be interacted with and what actions are possible with each. It would also be impossible for the user to enter an unparsable command or invalid deed.
This menu-based approach is not revolutionary. Chris Crawford (2004) has recommended the use of a graphical inverse parser that uses contextual menus to help users build valid commands. Emily Short, an active interactive fiction author, has lamented the problems arising from command line interface and suggested essentially the same possible solution (2010). Graphical games, such as Neverwinter Nights and the The Sims, have long used radial menus to display what actions are possible to perform on an object.
However, such a major overhauling of Inform's input system was beyond the scope of this implementation. I did at least use menus for conversations in order to make it clear what conversation topics were supported by the game.
Another issue stemming from the use of Inform is its turn-based structure. With its proactive NPCs, an interactive drama can support the sensation of being caught up and carried along in a story, being able to influence it while not completely controlling it. I had exactly this experience in playing Mateas and Stern's Facade; I had not experienced this feeling in a game before. Yet, at the end of each turn in Demeter, the empty waiting prompt implicitly says to the player: "Do something!" This makes it difficult to support passivity as a valid option. I did try to make waiting a simple thing to do: just hitting Enter at the prompt will result in a wait
command in Demeter. It also would have been possible to use a time limit after which the game would advance automatically. However, I did not feel this was a valid option in a text-based game given people's varying reading and typing speeds.
Overall, using Inform was a fair choice, especially for a prototype system. Inform did a fine job at what it was designed to do. Defining world objects and verbs proved simple (though that was the simplest part of the project to begin with). Affordances of the interface aside, Inform's parser did a good job handling input. Producing reliable output was still troublesome, though, and some of Inform's other quirks made debugging a headache. Because Inform is such a simple low-level language, it was a hindrance in implementing Marlinspike and all the scenes.
I have since learned that some of these issues could have been overcome using TADS3 (2009). However, at the time I started implementing, TADS3 was still fairly new. Also, TADS still does not have a web-based interpreter option, which was necessary to run an online evaluation study.
A text-based approach to interactive drama still seems worthwhile. This is especially true given the proliferation of hand-held mobile devices these days; text-based games could work well in this environment. Many of the IF affordance issues could be overcome with a object-based menu input system. This would even be an aid in a mobile environment where typing is more cumbersome.
Argax Project : Dissertation :
A Rough Draft Node http://www2.hawaii.edu/~ztomasze/argax |
Last Edited: 11 Apr 2011 ©2010 by Z. Tomaszewski. |