Grendel is a Python interactive fiction engine I created around seven years ago. How the engine came about is a bit of an odd story.

I was working at a web development company at the time, but we were itching to branch out. A really great opportunity presented itself, and we pitched to make an educational adventure game based on Canadian First Nations folklore (which would be paired with teaching materials for grade school classes). The client was pretty pumped, and we had a handful of great meetings with them.

I burnt a few weeks looking into a handful of engine options. Popular high-level engines like Unity were still years off, and working with pretty much any game engine was a steep learning curve. A month into the project, though, we hit a small bump: since the game was being funded in part by the Canadian government, it had to adhere to their media accessibility guidelines. Among those guidelines? The requirement that our game was playable by the blind.

The project could have died then and there, but the very next meeting I pitched an idea that would fulfil all their guidelines, and then some: we’d do the game as interactive fiction. I’d wanted to do an IF game for years, and this was the perfect opportunity. I had already made two If engines in the past, one in PHP and one in Python. The second engine–which I had named Grendel–wasn’t quite done, but this was the perfect excuse to finish development.

The client gave the project the green light, and I started working in earnest on finishing Grendel, while at the same time designing and writing story that would run on it. Meanwhile, the studio would work on a client for Grendel (since, until then, the engine only had a command line interface).

Grendel was unique. It was important to me that the game was thick with atmosphere, and I felt like the IF engines at the time lacked the tools to accomplish that. Paired with the client we made for it, Grendel could cue up sound and music, supported dynamic backgrounds (that even had its own particle system for effects like snow and falling leaves), and was totally accessible, supporting both text-to-speech and speech-to-text software. It also had just about the simplest story authoring system, allowing you to write absolutely everything (including branching dialogue and puzzles) in plain XML (as opposed to systems like Inform and their odd scripting languages, which I always found needlessly cumbersome).

With the engine and game nearly complete, I found myself relocating to Vancouver and left the project behind for others to finish (mostly, just dropping in some educational copy that we were still waiting on). But sadly, this never happened; the owner of the studio I was working for disappeared, never replying to a single one of my emails, and the game (as far as I know) was never finished or released. A short while later, the studio closed completely. I never found out why.

The engine was a labour of love. Even though it’s almost seven years old, it’s still a project I’m extremely proud of. Several people interested in Python-based IF have asked me about the source, so I’m posting it here. This release lacks the main client (which was developed at the studio), so the fancy audio and graphical features are moot, but it should still be completely capable of playing stories through its original command-line interface.

Story examples

Grendel has too many features to go over quickly, and any documentation has been lost, but I can show what a rough approximation of the first room of Zork would look like in Grendel’s story format:

<story>

	<game>
		<intro>
			This is displayed when the game first starts.
		</intro>
		<name>Zork I: The Great Underground Empire</name>
		<startroom>west_of_house</startroom>
	</game>
	
	<location name="west_of_house">
		<desc>
			<short>Forest path</short>
			<long>You are standing in an open field west of a white house, with a boarded front door.</long>
		</desc>
		<decoration name="mailbox" alt="box">
			<long>There is a small mailbox here.</long>
		</decoration>
		<block>
			<east>The door is locked, and there is evidently no key.</east>
		</block>
		<connections>
			<north>north_of_house</north>
			<south>south_of_house</south>
			<west>forest</west>
		</connections>
	</location>
	
	<item name="mailbox" alt="box" adj="small" location="west_of_house" isContainer="true" isOpen="false" canOpen="true" canTake="false">
		<desc>
			<long>I see nothing special about the mailbox.</long>
			<initial>There is a small mailbox here.</initial>
		</desc>
		<actions>
			<open>
				{items.Get("cache").isOpen = True}
				{"That's already open." if items.Get("mailbox").isOpen else "You open the mailbox, revealing a small leaflet."}
			</open>
		</actions>
	</item>

	<item name="leaflet" adj="small" location="mailbox" canTake="true">
		<desc>
			<long>ZORK is a game of adventure, danger, and low cunning. In it you will explore some of the most amazing territory ever seen by mortal man. Hardened adventurers have run screaming from the terrors contained within!</long>
			<initial>The mailbox contains a small leaflet.</initial>
		</desc>
		<actions>
			<read>
				ZORK is a game of adventure, danger, and low cunning. In it you will explore some of the most amazing territory ever seen by mortal man. Hardened adventurers have run screaming from the terrors contained within!
			</read>
		</actions>
	</item>

</story>

Unfortunately, this is missing a lot of neat stuff, like the ability to set up basic puzzles, have actions change based on indirect objects, or a situation where Grendel pulls off some cool stunts to determine object salience (essentially, allowing you to have multiple similar objects in a location (or in the player’s inventory) without confusing them, and keeping track of the player’s interest so ambiguity problems are rarely an issue).

You’ll notice the curly braces–this lets you trigger Python commands at any point in the game (or dialogue). By default, whatever the command returns is printed as part of the paragraph, though commands can be run silently by adding a tilde–as in, {~command}.

Grendel also supports branching conversations, which are also done in XML. That looks like this:

<dialogue>
	<conversation name="example">
		<text>
			This is the start of the conversation.
		</text>
		<option text="This is a possible option that returns to the main branch upon completion.">
			<text>"This is a response."</text>
			<return />
		</option>
		<option text="This is an option that leads to its own dialogue branch.">
			<option text="This is an option that only shows up after selecting its parent.">
				<text>"This is another response."</text>
				<return />
			</option>
		</option>
		<option text="This is another option, that ends the conversation.">
			<text>"Take care."</text>
		</option>
	</conversation>
</dialogue>

As I said, this only scratches the surface of what can be accomplished with Grendel’s XML story structure. Sadly, the only game ever produced for Grendel isn’t freely available, so you’d have to take a look at the engine source to see what all can be done and the tags and attributes at your disposal. You could write an entire game without a single line of Python, puzzles and all, or use Python exclusively and never touch the XML. Obviously the best route is usually a mix of the two, since the XML allows super rapid prototyping and Python allows complex interaction with minimal fuss.

Source

I wouldn’t recommend actually attempting to use Grendel for any real IF work–the lack of documentation would make it a nightmare. But you can absolutely dig into the source and pull out whatever treasures you might find. Just make sure you credit me if you use it, and get in contact with me if you want to use anything from Grendel in anything commercial. The source to Grendel (including the engine, story compilers, and tools) is provided as-is.

Grendel on GitHub