How to Create an Interactive Fiction Game Like "Zork"

by Yoruk in Circuits > Computers

43406 Views, 43 Favorites, 0 Comments

How to Create an Interactive Fiction Game Like "Zork"

Zork_I_box_art.jpg
zork1debut.PNG

Hi all !

Remember the good old times, when it was impossible to have more than one colour on a screen, and no graphics at all ? In the early 80's, a series of text-based adventure games, the Zork serie where created by a company called Infocom. To play, you just have to tell orders to the game, with simple sentences like : GO NORTH, TAKE SWORD, TALK TO TROLL, CLIMB TREE... The game engine can understand a lot of verbs and actions, and with theses commands you where able to explore a place called the Great Underground Empire, a place full of mystical creatures, treasures and magic.

I can understand that now, these games seem outdated. Maybe or may be not. If you like to write stories, if you like to create incredible universes, adventures and characters (and if you have poor drawing knowledge like me), it can be a good idea to write an interactive fiction game. It's not very hard, only a few knowledges in programming. Different platforms can produce such games. Today, I chose to talk about the language Inform 6, by creating a small game from scratch. In the last part of my (first !) Instructable, I'll talk about other systems.

First, we'll create the game. A simple one, only a few rooms, some objects, and a character. The goal of the game will be to find a key to unlock a door. The action will take place in a strange castle.

This Instructable is divided into six steps :

Design the game : we will create the map of the game, the objects list, and the global story.

Setting up the development environment : we will download the compiler, the ready-made English libraries, and a program to "play" our story (everything for free).

Create the rooms : we will start programming ! First, the rooms, and the first tests.

Adding objects : empty rooms are sad. Let's add some stuff !

Adding a character : for not being alone

Finish and play it ! : add a victory end to the game.

Enjoy this Instructable ! Its my first one. Please comment ! And please forgive me if you find grammar mistakes, I'm not English mother-tongue.

Game Design

map.png

Okay, so let's start the game design. In this example, we will have three rooms, some objects, and a dragon. The global story of the game is :

  • explore the rooms, understanding that you are locked. There is a door, but she is locked.
  • in a room, you'll find two treasure chests. A sign says that you will find the door key in one, and death in the second (if you don't want to kill the player, of course you can change the story !)
  • as the chests are similar, you'll need to ask someone. The dragon can helps you, you just have to solve an enigma.
  • talk to the dragon, answer the enigma, open the chest, get the key, unlock the door.
  • that's all !


Its a quite small game, but we will see all the main aspects of game design. Let's start by drawing the global map. In interactive fictions games, rooms directions are the main cardinal points (north, south, east, west). We will use these directions.

Rooms:

  • Initial room, with door
  • Treasure room
  • Dragon room


Objects :

  • two treasure chests
  • one key hidden in a chest
  • a door


Characters:

  • a dragon


It can be a good idea to add some stuff to make the game more realistic. We will not do it in this tutorial, but you will be able to do it by yourself.

The Development Environment

folder1.png
folder2.png
compiler source.png
first compilation result.png
new zcode file.PNG
select gargoyle.PNG
gargoyle first version.png

Ok, the game is created (only on paper or in your mind). Its now time to turn on the computer.

I choose to talk about the Inform 6 language. Its not the only language to create this kind of games. Of course, you can create everything from scratch with your favorite language (C, VB.net, Java, Basic...).

The Inform 6 language is based on a C-based syntax. The great advantage of this language is we will focus the coding only on the game. We will use ready-made "libraries" containing all the boring stuff : the syntax analyzer (the code that "read" what the player wants to do, and extract the actions.) Also, we will use an English library contains a big amount of classical verbs, actions, and default messages, like "You can't go this way". Thanks to these libraries, it will be possible to create only the game.

The main website for the Inform 6 language is here. You can also get a lot of information here.

Before downloading anything, let's explain in details how we will work.

1-We will write the Inform 6 source into a plain text file. We will use the windows notepad, but of course, you can use something else. Notepad ++ is also a good program. Link here.
2-We will turn our source into a compiled source called "z code" file (extension .z5). This compiled file is the full game. It's like a PDF file. You can't launch the game by double-clicking on the file (its not an executable). You need a "player" to run the game, as you need a software to open a pdf file. The great advantage of this is that the ".z5" file can be shared for every kind of final users : PC, Mac, Linux, mobile phones... The z5 file will stay the same, only the interpreter will be platform-dependent, like a PDF file reader.
3-So, to launch our game, we will download an "interpreter". I really like Gargoyle, as it can play a wide range of "interactive fiction" files (from different systems), and it is easy to use.

Let's start !

The compiler and the libraries are here.
The windows compiler is here, last link of the page.
The Inform 6 resource and help file : The Designer manual 4
The Gargoyle interpreter (download links on the left)

Lets unzip everything and create the folder structure showed in the screenshots.

We will also need to create a small BATCH file to run the compiler with the following options. Open the notepad, enter the following code and save it as COMPILER.BAT in the first folder, near the "inform-632.exe" file (watch out for the extension !)


@inform-632 MyGame.inf +inform_library611\
@pause


Now, open a new notepad session. It will be the main source file of our game. Save the file as "mygame.inf", put it in the same folder.

Here is the main header file for our game. Copy it into the notepad. The init section contains, now, only the initial text introduction of our game.



Constant Story "My first Inform 6 game";
Constant Headline "^A demo by Yoruk.^";

Constant No_score;

#Include "Parser";
#Include "VerbLib";

[Initialise;
print "^^This is a demo game written in Inform 6. Can you solve it ?^^";
];

Include "Grammar";



Save the file, and run the COMPILER batch file. You should get the result shown in the screenshot.

No errors, and a new file should be created in the game folder. Open Gargoyle, and select the file "MyGame.z5".

Yepee, the core is ready ! Let's add some rooms.

Creating the Rooms

gargoyle first map.png

Ok, the first room code will take place just under the init section, after the grammar include. Here is the code for the first room (the "Initial Room"):

object InitialRoom "Initial Room"
with
description "You are alone in a strange room.",
has light ;


The room description is the text who will appear when the player will move in, or when he will ask for the room description, like "look" or "examine".

Everything in the game universe will be declared as inform "objects", even people or rooms. The final has statement gave some main informations about the object. Here, the keyword light means that the object is a room, full of light (you can see in it). All the attributes are listed here.

Before compiling, we must add a line in the init block :

location = InitialRoom;

Just after the print statement. The variable location contains the name of the initial room, where the action starts.

At this time, we can compile the game and to run it. Its simple : just launch our "compile.bat" script file. You should not have errors.

Interesting, but still flat. Let's add the two other rooms (put this code after the first room) :


object TreasureRoom "Treasure Room"
with
description "This room is quite the same as the previous one. You can go to the initial room by walking in the south. There is an other room on the east.",
has light ;



object DragonRoom "Dragon Room"
with
description "You can reach the Treasure room by walking on the west.",
has light ;


It will work, but we need to create the links between the rooms (following our map). Let's add some code in the room blocks:


object InitialRoom "Initial Room"
with
description "You are alone in a strange room.",
n_to TreasureRoom,
has light ;

object TreasureRoom "Treasure Room"
with
description "This room is quite the same as the previous one. You can go to the initial room by walking in the south. There is an other room on the east.",
s_to InitialRoom,
e_to DragonRoom,

has light ;

object DragonRoom "Dragon Room"
with
description "You can reach the Treasure room by walking on the west.",
w_to TreasureRoom,
has light ;


The n_to, s_to, w_to, e_to keywords are giving the global directions. In the InitialRoom block, the n_to keyword mean that we can reach the TreasureRoom by going in the north. In the other room, the keyword s_to allow the player to come back in the first room.

It's always useful to tell the player what are the directions. It's sometimes boring to try every directions in a new room. Our game have small descriptions, but it can help the player.

Save the source, compile and run the new z5 file with gargoyle. You can now explore the map.

You can try some basic commands:

go north
open door
smell
listen...

Adding Objects

gargoyle objects.png

Ok, the room design is now done, its time to add stuff into these rooms. It's always good to add a lot of things that the player can see/touch/smell/listen, it makes the game more pleasant. Here, we will add the objects of our adventure : the door, the chests, the key.

The door:

A door in the Inform 6 language can be hard to implement. Here is the code. Put it just after the "Initial Room" block.


Object -> WoodenDoor "wooden door"
with description "A big wooden door. It have a lock.",
name 'big' 'wooden' 'door' ,
when_closed "You can see a big wooden door, on the south. The door is closed.",
when_open "The door is now open.",
door_to ExitRoom,
door_dir s_to,
with_key goldkey,
has static door openable lockable locked ;


the "->" arrow means that the door object is located in the previous room, here, the Initial Room
description : the text who appears when asking commands like "look at the door"
name : you can put here some synonyms of the objet. Thanks to this, the game can understand sentences like "examine the big door"
when_closed , when_open : additional descriptions
door_to : the name of the room behind the room. We will create a "dummy" room for this right now.
door_dir : here, s_to. To cross the door, we will have to "go south"
with_key : hehey, our door will be locked ! The key object will be a gold key. We will create the object later.
the attributes : a lot of them for a simple door..
static : you can't take the door with you
door : yes, its a door
openable : you can open and close it (yes !)
lockable : you can lock it with a key
locked : and it is already locked !

We now need to add an other line of code in the InitialRoom block.

object InitialRoom "Initial Room"
with
description "You are alone in a strange room.",
n_to TreasureRoom,
s_to WoodenDoor,
has light ;


The new s_to line means that the player will cross the door by moving in the south. If the door is closed, he will not be able to reach the other room.

Now, we will create two treasure chests. One made of silver, one made of gold. Of course, we will hide the golden key inside the second one. Here is the code for these three objects. Put this code at the end of the file, after the last room:


object SilverChest "Silver Chest"
with description "A beautiful silver chest.",
name 'silver' 'chest',
found_in TreasureRoom,
has static openable container;

object GoldenChest "Golden Chest"
with description "A beautiful Chest, made of Gold.",
name 'gold' 'golden' 'chest',
found_in TreasureRoom,
has static openable container;

object Goldkey "golden key"
with description "A small golden key.",
has female;


The two chest are similar. the found_in statement means that both are in the Treasure Room. The attribute static means again that you can't pick up them with you, openable means that you can open and close then, and finally the container attribute means that you can hide things in it.

The golden key object is simple. Only a short description. The only attribute is female, with this the game will apply specific words when describing the object.

We need to do two last things. First, create a last dummy room, it will be the other side of the door. The player will win the game by entering in it. Here is the code:


object ExitRoom "Exit Room"
with
description "You are free !",
has light ;


We don't have to add any directions. Put this code after the Goldkey object.

The last thing is... hide the key ! Actually, she is not really in the game, as we don't put a location for this object. Instead of using the found_in keyword, we will use a line of code in the Init block. Just add:


move GoldKey to GoldenChest;


After the location line. Save and compile the file. You should not get any errors. Open the game in Gargoyle and enter the following commands :


go north
look at golden chest
get the key
open the silver chest
open the golden chest
get the key

It should work as shown in the screenshot. The defaults messages "You can't see any such thing" are stored into the English library. Of course, you can change them, but for now we will use the default messages.

Actually, you can open both chests without any problems. We final goal of the game will be to open only one chest, as the second one will be trapped. The real chest will be the golden one. A dragon will help the player to find the good chest. A dragon ? Yes, in the next step !

Adding a Character

gargoyle dragon.png

Until this step, you where alone in the game. Characters are useful to help the player or, to kill it. Don't worry, we will just add a nice dragon in this game. He will be able to help you to get the key, and you will be able to ask him about a lot of subjects.

As always, a character is defined by the "object" statement. But as it have the animate attribute, the compiler knows that it is a living character.

The specific actions are given in the Life block. the "Ask" statement contains a switch on the second variable. the second variable contains the subject of what you are talking about.


Object dragon "dragon"
with name 'red' 'dragon' 'animal',
description "A red dragon is standing here. He doesn't stop looking at you.",
initial "A red dragon is here.",
found_in DragonRoom,
life [ word1 word2;

Tell : print "The dragon doesn't listen you.^"; rtrue;
Attack : print "You should not. He can split fire.^"; rtrue ;
Kiss : print "Hmm..no.^";rtrue;

Answer , Ask :
word1 = noun;
word2 = second;
if (word1==33) {word1=word2;}

switch (word1) {
'hi', 'hello' : "~Hello, stranger.~";
'key', 'help' : "~The exit door is locked. To find the key, you must answer an enigma. If you are ready, I can sing it.~";
'hi', 'hello' : "~Hello, stranger.~";
'door', 'exit', 'lock': "~Hmmm. I can help you to find the key.~";
'enigma', 'game' , 'song' : "~Here is the enigma : On the morning, it have four legs. At noon, it have two legs; on the night ; it have three. What is it ?~";
'man', 'boy', 'girl', 'human', 'men', 'lady' : "~You found the solution of the enigma. Congratulations ! The key is inside the golden Chest. Watch out, don't try to open the other one !~^";
'solution' : "You must find the answer by yourself.";
default : print "No answer.^"; rtrue;
}

],
has animate static ;


the name gives again the dragon's name synonyms
description : the... description
initial : the text showed when the player enters the room
found_in : the location of the dragon
the Life block contains all the "living" code :
tell : text printed when the player will use "tell dragon about something"
Attack, Kiss : You should try. the Rtrue keyword means that the game will print the text, and cancel the default messages. Without it, you will have two sentences : yours and the default one. They are listed here.

Answer, Ask : Here, I use a special code to get the player's actions.
the noun keyword is the subject when the player say something to the character
second is the subject when the player asks about something

The idea here is to mix the two actions Answer and Ask in one block of code.
if the noun word is empty (=33 in Inform) it means that the subject of the question is given by the word second.

To finish, I use a switch statement to give the answers.

'hi', 'hello' : "~Hello, stranger.~";

Means that when the player will say hi or hello to the dragon, he will answer 'Hello stranger'. The ~ means that a double-quote (") will be printed.

The idea for the player is to ask the dragon for help, or for the keys. The dragon will answer with an enigma. The enigma clue is stored in the line :


man', 'boy', 'girl', 'human', 'men', 'lady' : "~You found the solution of the enigma. Congratulations ! The key is inside the golden Chest. Watch out, don't try to open the other one !~^";


Save the code, compile and run it. Move to the dragon room. It should work as presented in the screenshot :

Play It !

final game.png

Ok, the source is now almost complete. Call the "compile.bat" file to update mygame.z5. Open it with Gargoyle. try the following commands :

go north
take all
go east
examine dragon
go west
south
open door
south

To finish the game, we just have to:
-kill the player when he tries to open the chests before asking the dragon for the answer,
-end the game in victory when the player open the door.


For the first point, we will add a global variable in the game. It will tell the game if the dragon enigma was solved or not. if not, both chest will be trapped.

A global variable is declared in the first part of the source, just after the three Constant lines. We will add :


Global EnigmaSolved = false;


Now, we will change the code of our two chests :


object SilverChest "Silver Chest"
with description "A beautiful silver chest.",
name 'silver' 'chest',
found_in TreasureRoom,

before [;
open : print "You open the Silver Chest. A bright light came from the inside. You immediately feel bad." ;
deadflag = 1;
rtrue ;
],


has static openable container;

object GoldenChest "Golden Chest"
with description "A beautiful Chest, made of Gold.",
name 'gold' 'golden' 'chest',
found_in TreasureRoom,

before [;
open : if (EnigmaSolved == false){
print "You open the Golden Chest. A bright light came from the inside. You immedialty feel bad." ;
deadflag = 1;
rtrue ;
}
],


has static openable container ;


The new Before block means that we will catch the actions asked by the player, here the "open" action. We will test the EnigmaSolved variable, if false, we will display a message, and we will change the value of a system variable, deadflag. The value 1 means that the player dies. The Rtrue keyword means that we don't want to have the default message. For the silver chest, we don't have to test the variable, as this chest is always dangerous...

The last modification is to change the EnigmaSolved variable when... the player answer the enigma to the dragon. Its easy, just add this to the dragon code :


Object dragon "dragon"
with name 'red' 'dragon' 'animal',
description "A red dragon is standing here. He doesn't stop looking at you.",
initial "A red dragon is here.",
found_in DragonRoom,
life [ word1 word2;

Tell : print "The dragon doesn't listen you.^"; rtrue;
Attack : print "You should not. He can split fire.^"; rtrue ;
Kiss : print "Hmm..no.^";rtrue;

Answer , Ask :
word1 = noun;
word2 = second;
if (word1==33) {word1=word2;}

switch (word1) {
'hi', 'hello' : "~Hello, stranger.~";
'key', 'help' : "~The exit door is locked. To find the key, you must answer an enigma. If you are ready, I can sing it.~";
'hi', 'hello' : "~Hello, stranger.~";
'door', 'exit', 'lock': "~Hmmm. I can help you to find the key.~";
'enigma', 'game' , 'song' : "~Here is the enigma : On the morning, it have four legs. At noon, it have two legs; on the night ; it have three. What is it ?~";
'man', 'boy', 'girl', 'human', 'men', 'lady' : print "~You found the solution of the enigma. Congratulations ! The key is inside the golden Chest. Watch out, don't try to open the other one !~^"; EnigmaSolved = true; rtrue ;
'solution' : "You must find the answer by yourself.";
default : print "No answer.^"; rtrue;
}

],
has animate static ;


So now, when the player gives the answer, the variable EnigmaSolved will be set at true. So, its now possible to open the chest, and get the key.

Now, to finish our game, we want to end the game in victory when the player open and reach the door. Its easy, we just have to modify the dummy room code :


object ExitRoom "Exit Room"
with
description [;
print "You are free ! Congratulation, you solved this game !^^"; deadflag = 2; rtrue ;
],

has light ;


So now, when the player will reach the room, a finish message will be printed, and the system variable is set at 2, its means that you won the adventure.

Save the code, and compile it. You should not get errors. If any, please check your code. The full code is given in the last step.

Here are the commands to solve the game. Of course, it's not the only way to solve it. But as you know the walk-trough, it's easy !!


EXAMINE DOOR
OPEN DOOR
GO NORTH
GO EAST
LOOK AT DRAGON
SAY HI TO DRAGON
ASK DRAGON ABOUT HELP
ASK DRAGON ABOUT KEY
ASK DRAGON ABOUT ENIGMA
SAY MAN TO DRAGON
GO WEST
OPEN GOLD CHEST
EXAMINE KEY
TAKE IT
SOUTH
UNLOCK DOOR
OPEN DOOR
GO SOUTH


That's all, I hope that you understand the global concept and the possibilities of this language. The The Inform Designer's Manual is really a good source of informations as well as the Inform Beginner's Guide.


Please let me know if something doesn't work.

Full Code

Here is the final Inform 6 code for this game :


Constant Story "My first Inform 6 game";
Constant Headline "^A demo by Yoruk.^";

Constant No_score;

Global EnigmaSolved = false;

#Include "Parser";
#Include "VerbLib";

[Initialise;
print "^^This is a demo game written in Inform 6. Can you solve it ?^^";
location = InitialRoom;
move GoldKey to GoldenChest;
];


Include "Grammar";


object InitialRoom "Initial Room"
with
description "You are alone in a strange room.",
n_to TreasureRoom,
s_to WoodenDoor,
has light ;




Object -> WoodenDoor "wooden door"
with description "A big wooden door. It have a lock.",
name 'big' 'wooden' 'door' ,
when_closed "You can see a big wooden door, on the south. The door is closed.",
when_open "The door is now open.",
door_to ExitRoom,
door_dir s_to,
with_key goldkey,
has static door openable lockable locked ;



object TreasureRoom "Treasure Room"
with
description "This room is quite the same as the previous one. You can go to the initial room by walking in the south. There is an other room on the east.",
s_to InitialRoom,
e_to DragonRoom,
has light ;


object DragonRoom "Dragon Room"
with
description "You can reach the Treasure room by walking on the west.",
w_to TreasureRoom,
has light ;






object SilverChest "Silver Chest"
with description "A beautiful silver chest.",
name 'silver' 'chest',
found_in TreasureRoom,

before [;
open : print "You open the Silver Chest. A bright light came from the inside. You immedialty feel bad." ;
deadflag = 1;
rtrue ;
],

has static openable container;



object GoldenChest "Golden Chest"
with description "A beautiful Chest, made of Gold.",
name 'gold' 'golden' 'chest',
found_in TreasureRoom,

before [;
open : if (EnigmaSolved == false){
print "You open the Golden Chest. A bright light came from the inside. You immedialty feel bad." ;
deadflag = 1;
rtrue ;
}
],

has static openable container ;







object Goldkey "golden key"
with description "A small golden key.",
name 'small' 'key' 'golden',
has female;




object ExitRoom "Exit Room"
with
description [;
print "You are free ! Congratulation, you solved this game !^^"; deadflag = 2; rtrue ;
],
has light ;





Object dragon "dragon"
with name 'red' 'dragon' 'animal',
description "A red dragon is standing here. He doesn't stop looking at you.",
initial "A red dragon is here.",
found_in DragonRoom,
life [ word1 word2;

Tell : print "The dragon doesn't listen you.^"; rtrue;
Attack : print "You should not. He can split fire.^"; rtrue ;
Kiss : print "Hmm..no.^";rtrue;

Answer , Ask :
word1 = noun;
word2 = second;
if (word1==33) {word1=word2;}

switch (word1) {
'hi', 'hello' : "~Hello, stranger.~";
'key', 'help' : "~The exit door is locked. To find the key, you must answer an enigma. If you are ready, I can sing it.~";
'hi', 'hello' : "~Hello, stranger.~";
'door', 'exit', 'lock': "~Hmmm. I can help you to find the key.~";
'enigma', 'game' , 'song' : "~Here is the enigma : On the morning, it have four legs. At noon, it have two legs; on the night ; it have three. What is it ?~";
'man', 'boy', 'girl', 'human', 'men', 'lady' : print "~You found the solution of the enigma. Congratulations ! The key is inside the golden Chest. Watch out, don't try to open the other one !~^"; EnigmaSolved = true; rtrue ;
'solution' : "You must find the answer by yourself.";
default : print "No answer.^"; rtrue;
}

],
has animate static ;