Tutorial Game

JACL is a language for writing interactive fiction. By providing short definitions for objects and locations you will rapidly build your virtual world. By associating small amounts of simple code with these objects and locations you will bring your world to life. During the course of this tutorial you will see the construction of a small text-only game from beginning to end. The full source code for this tutorial game is included in Appendix D and in the games subdirectory of your JACL distribution.

Language Syntax

JACL is an interpreted language. This means that the interpreter directly reads your program source code and executes it without the need for it to be compiled first. Each JACL command or definition must be on a line of its own and no single line may not exceed 1024 characters in length. Any single parameter that includes spaces, commas, colons or tabs must be contained within double quotes, as these are all considered to be white space. If a command accepts parameters, each parameter must be separated by white space.

Any text that appears between a hash (#) or a semicolon (;) and the end of the line is considered a comment and has no affect on the program's execution. Comments are useful to explain the purpose of your code to other authors or to refresh your own memory when returning to old code.

One special use of comments is to tell the command shell the location of the interpreter that can execute your program. The first line of most JACL programs will be something like:

#!../bin/jacl

If a JACL program has a first line similar to this, when it is executed directly the shell will pass the name of the program as an argument to the interpreter specified after the #!. This means that the following shell command:

./grail.jacl

becomes the equivalent to the following command:

../bin/jacl grail.jacl

This line presumes that all games are run from the games directory of your JACL distribution and that you are currently in that directory. To remove this restriction change the first line of your game to indicate the full absolute path to your JACL interpreter of choice. For example:

#!/usr/local/bin/garjacl
Warning In order for this to work on Microsoft Windows, you must be running a Unix shell under Cygwin. To work under Unix, the JACL program must be set to executable using a command such as:
chmod 755 grail.jacl

Program Structure

A JACL program consists of two fundamental components: data and code. Data is provided in the form of definitions, code in the form of functions. There are nine types of definition, their keywords being location, object, synonym, filter, integer, constant, string, attribute and grammar. A function begins with a left curly brace ({) followed directly by its name, and ends with a right curly brace (}) on a line on its own. Any text that appears on the lines between the two curly braces is considered to be the code body of that function. Other than comments, the only text that can appear outside a function is one of the nine definitions. While definitions are read once at the start of the game then stored in memory, functions are executed from the game file as required during play.

Getting Started

To begin writing your first adventure game using JACL you will need to copy the file frame.jacl from the directory jacl-2.x/games/include to a file called game.jacl in the directory jacl-2.x/games. This new file is the one you will edit to create your game.

The file frame.jacl is a skeleton program that provides a good starting point when writing a piece of interactive fiction from scratch. The file verbs.library, is also included from the last line of frame.jacl. While the code in frame.jacl should be modified to suit your game, the code in verbs.library should never be modified. This allows new versions of verbs.library to be installed without adversely affecting your game. Verbs.library contains a extensive set of verbs for use by the player during the game.

Now open game.jacl, the beginnings of our tutorial game, with your favourite text editor. The name game.jacl has been chosen for the purpose of this tutorial, your game file can be given any name you like.

At the top of this file you will find four constants. These constants are used to store bibliographical information about your game that can be directly read by cataloguing tools. For this reason the names of these constants, including the case, must be left unchanged which their values changed as appropriate. For information visit The Treaty of Babel.

Locations

In text adventure games, a location represents a discrete physical space that the player and other objects can be in, such as a room in a house. Although objects can also be placed inside other objects, the player can only be in a location. We will now begin to define the first location of this tutorial game. To do this, insert the following line of code below the bibliographical constants:

location bedroom: master bedroom

This line states that we wish to define a location with the label bedroom. This label is the name by which we will refer to this location within our code. For those of you with previous programming experience, you can think of this label much in the same way as a variable name. Following the location's label is a space-delimited list of names by which the player can refer to this location during game play. All locations must have at least one name, and may have as many as can fit into a single line of code. In this example, the location has two names: master and bedroom. If you do not supply any names, the location's label will become its one and only name.

All the locations and objects you define have associated properties. When you define an object or location it is possible to set the initial value of these properties. For the new location bedroom we are going to set the initial value of two properties: short and west. This is done by adding two more lines to the location definition so it looks like this:

location bedroom: master bedroom
  short       a "master bedroom"
  west        bathroom

short is a two-value property used to supply a short description of the location. This description is used by the library code when referring to the location in a sentence. The first parameter after the keyword short is its indefinite article, the second is its description. The supplied indefinite article is used to prefix the description when the {list} macro is used. This would normally be a or an.

The {list} macro is used as a parameter of a write command as a way of outputing text that refers to an object. It is normally used when referring to an object or location in a list of several objects or locations. The most common example of this is a list of objects that are inside another object or held by another object, such as a desk drawer or the player's inventory. Below is an example of the {list} macro:

write "The house you are in has " bedroom{list} ".^"

When executed, this line of code will display:

The house you are in has a master bedroom.

The write command above uses this macro by specifying the label of the location bedroom followed by {list}, the name of a macro. The other point of note with the above write command is the use of the caret character (^). The caret character is used to indicate where a new line on the screen should be started. The end of a write command does not automatically mean the end of a line of text.

The {list} macro is more commonly used with objects than locations, as only objects can be taken or placed inside other objects. It is more common for locations to be referred to using the {the} macro. This macro displays the object's definite article (the word the by default) followed by the object or locations short description text. For example:

write "You are in " bedroom{the} ".^"

will display:

You are in the master bedroom.

"Why not just write You are in the master bedroom directly?" I hear you ask. Given the above two lines of code, you could. The {list} and {the} macros are normally used, however, with an integer variable, not an object label. All objects and locations are assigned a unique number starting at one and numbered sequentially as they appear in your program. Using this numbering system, an integer variable can be used to refer to an object or location. The most common integer variable used as an object pointer is noun1. This variable represents the first object referred to in any given command typed by the player. For example:

write "There is nothing special about " noun1{the} ".^"

If the player was to type the command examine bedroom, the above code would produce the output:

There is nothing special about the master bedroom.

There is also one special purpose indefinite article: name. If name is specified as an object's article, the short description text will not be prefixed with anything whether displayed using noun1{the} or noun1{list}. This would normally be used when the description text is a proper noun. For more information on macros, see the section on Printing the Names and Descriptions of Objects.

The third line of our location definition states that if the player travels west from this location, then they should be moved to a yet-to-be-coded location with the label bathroom. You will learn more about this soon.

The next thing we will do is give the location its long description. This is the text that the player will see when they are in this location. We do this by associating a function called look with the location definition.

A function can be associated with an object, a location or be global. Functions are defined by starting a line with an opening curly brace ({) followed directly by the function's name. Any function whose name does not begin with a plus sign is automatically associated with the last object or location defined above it in the game file. Any function whose name does begin with a plus sign is said to be global. A global function is in no way associated with a particular object or location. A function definition is finished by placing a closing curly brace (}) on a line of its own. More information about functions and how they are associated with items is provided in the chapter on Functions.

Below is the code for the look function to be added directly beneath the definition for the location bedroom. This function simply outputs plain text and therefore makes use of the print command instead of the write command we used earlier. This command takes no parameters and starts outputing all text from the following line until it reaches a line that has a period (.) as its first non-whitespace character.

{look
print:
   You are in your bedroom. There is a
   large, soft bed in the centre of the room
   while a doorway to the west leads into the
   bathroom.^
.
}

The colon after the print command is only an optional whitespace character used for presentation purposes. When using the print command, any following line of text that does not end with a caret (^), will have an implicit space added to the end before printing the following line. To prevent this space from being added, finish the line with a backslash (\) character. For more information see the chapter on Screen Display.

As discussed earlier, the definition of the location bedroom refers to a second location called bathroom, located to its west. We will now add this second location to the game. The code for this new location should be placed after the look function that is associated with the bedroom:

location bathroom: bathroom
  short        the "bathroom"
  east         bedroom
  out          bedroom

{look
print:
   You are in the bathroom. The only
   exit from here is back east to the bedroom.
.
}

{movement
if compass = east : compass = out
   write "You bang your head as you walk "
   write "through the doorway.^"
   return false
endif
write "The only exit from here is to the "
write "east.^"
}

There are two points of note with this new location definition. Firstly, it demonstrates that it is quite valid (and in some cases highly desirable), to have more than one direction lead to the same location. In this case, if the player travels east or out from this location, they will be moved to the location with the label of bedroom. This completes the logical two-way connection of the bedroom to the bathroom, and the bathroom to the bedroom.

Secondly, it has a movement function associated with it. This function is called automatically by the interpreter whenever the player moves, or attempts to move, out of this location. If this function does not exist or returns false, the move will continue as normal. If the function returns true, the move will be prevented from taking place. Any function that reaches its closing brace will terminate as though it had issued a return true command. For move information on movement functions, see the section on Movement. The if statement in the movement function says that if variable compass equals east or out, then the code up until the matching endif command should be executed. The variable compass is set to the direction the player is attempting to move in by the interpreter before the movement function is called. For more information on the if command, see the section on Flow Control.
Information east and out are constants predefined in the interpreter to represent static numerical values. All the directions that can be travelled in from a location are stored in a 12 element array. These numerical values correspond to the index of the array element that stores the destination for that direction.

The Player

Now that we have our small, two-location world, we need a player to explore it. There is a definition for an object with the label kryten in frame.jacl. This object has been made with the specific purpose of representing the player and looks as follows:

object kryten: myself self me
 short      name "yourself"
 has        ANIMATE; FEMALE
 capacity   42
 parent     ;SPECIFY STARTING LOCATION HERE
 player
Information The label kryten was chosen for the object representing the player after playing Infocom's Zork I with Frotz's -o option. This shows the object representing the player as having the name cretin. Being both an Infocom fan and a Red Dwarf fan, the choice was obvious.

The first line of this definition starts with the keyword object. This tells the interpreter that we want to define a new object with the label kryten. The words following this label are a list of names for the object. This, as you will have noticed, is the same format as defining a new location.
Information In JACL, a colon is treated as white space (as is a comma and tab). I simply use a colon in preference to a space in certain places to make the code more readable.

The second line contains a short keyword. This has the same purpose as the short keyword associated with bedroom above. In the case of our object kryten, however, the word name appears as the indefinite article. When using a {list} or {the} macro, anything other than the word name is printed verbatim. When name is specified, the JACL interpreter will not prefix the short description text with anything. For example, when noun1 points to the object kryten, the following code:

write "You can't take " noun1{the} .^
write "You can't take " noun1{list} .^ 

will produce the output:

You can't take yourself.
You can't take yourself.

The third line of the object definition starts with the keyword has followed by the attribute ANIMATE. Both objects and locations may have as many or as few of the available attributes as required. Attributes are simply boolean flags that can be given to and taken from objects and locations at any stage during the game. They are frequently tested for by code in verbs.library. For example, the standard function to open an object will first check that it doesn't have the attribute LOCKED before taking away the attribute CLOSED, thus resulting in it becoming open. The attribute ANIMATE is tested for by functions associated with actions such as talking. If the player attempts to talk to an object that does not have the attribute ANIMATE, they are informed that the action is not logically possible. For a more information, see the chapter on Attributes.

Following the attribute ANIMATE, you will see a semicolon then the attribute FEMALE. A semicolon (or hash symbol) signifies that all text following it, until the end of the line, is a comment. Comments have no effect on a game, they are only there to serve as notes for the author's benefit. In this case, deleting the semicolon will mean that the extra attribute will no longer be ignored. This will cause the player to be referred to in the feminine by all code in verbs.library.

The fourth line, beginning with the keyword capacity, indicates how many mass units the object can hold. In the case of an object with the attribute ANIMATE (such as this object representing the player), it indicates how much they can carry. In the case of an object with the attribute CONTAINER, it indicates how much can be placed inside it, while in the case of an object with the attribute SURFACE, it indicates how much can be placed on top of it.

The fifth line, beginning with the keyword parent, is followed by the label of another object or location. This indicates where the object is to be when the game starts. In this game, the player is to start in the location bedroom, so we must modify this line to read:

parent    bedroom

If an object doesn't have a parent property explicitly defined it will start the game with its parent property set to the nearest location defined above it in the game file.

The final line has the keyword player. This keyword tells the interpreter to set the interger variable player to point to the object kryten before the game begins. Only one object should have the keyword player defined, although the object pointer player can be changed to point to another object at any stage during the game if desired.

The properties that are associated with an object definition may be placed in any order. There are other properties that can be associated with an object that we have not set here, as the defaults used when they are absent are valid for our purposes. All of the possible properties are discussed in the chapter Definitions in Detail.

Some Introductory Text

When a game is started or restarted, the function +intro is executed. The main purpose of this function is to display the game's title, the author's name and some text introducing the game. Any other commands required to set the initial state of the game-world can also be placed in +intro. Below is the +intro function for the tutorial game. This is simply the +intro function already present in your skeleton game file with an extra print block added after the title and the author of the game are written to the screen:

{+intro
style bold
write "^^" GAME_TITLE
style normal
write " by " GAME_AUTHOR "^^^"

print:
   Your alarm rings and you climb out
   of bed. Monday morning again so soon. Oh well,
   at least your house doesn't have a front door
   so you have a good excuse for not going to
   work.^^
.

if here hasnt OUTDOORS
   move north_wall to here
   move south_wall to here
   move east_wall to here
   move west_wall to here
endall

move ground to here

look
}

This function begins by using the style command to set the font to bold and then printing the value of the string constant GAME_TITLE. This will be whatever string you set it to at the top of your game file. Next the font style is set back to normal and the value of the string constant GAME_AUTHOR is output. The author's name is followed by three newline character to insert two blank lines. For more information on these commands see the chapter on Screen Display.

After the title header, a game-specific print block outputs the introductory text for this game. This is the text that should set the scene for the player and introduce them to the game character they are playing.

The next block of code moves all the wall objects to the current location if the current location doesn't have the attribute OUTDOORS. These objects are defined in the file frame.jacl and should be moved to the current location each time the player moves to a location that doesn't have the attribute OUTDOORS. This is done using a similar block of code placed in the global function +movement. When the function +intro is executed, the current location will always be the starting location. For this reason, the if statement is not strictly necessary, but it is a good safeguard none the less.

The final line of the function contains a look command. This command prints the description of the current location and objects that are in this location.

Below is the default content of the function +movement that is executed each time the player attempts to move out of the current location:

{+movement
ifall destination != nowhere : destination hasnt OUTDOORS
   move north_wall to destination
   move south_wall to destination
   move east_wall to destination
   move west_wall to destination
endif
return false
}

Objects

You can now try playing the beginnings of this game by first typing the command from within the games subdirectory:

../bin/garjacl game.jacl

When you do so, you should see something like the screen below:

 
The tutorial game running in the JACL interpreter
 

Once playing, the following commands should give you the responses shown:

>w
You are in the bathroom. The only exit from 
here is back east into the bedroom.

>i
You are empty-handed.

>smell
Nothing strikes you as out of the ordinary.

>listen
You don't hear anything out of the ordinary.

>sit
You plonk yourself down for a moments rest.

>examine north wall
There is nothing special about the north wall.

As you can see, at this stage there is very little for the player to interact with in this game. Therefore, the next thing we will do is add another object: a small wooden box. We add the small wooden box by inserting the following object definition after the bathroom's look function:

object box: small wooden box
 has        CLOSABLE CONTAINER CLOSED
 short      a "small wooden box"
 long       "There is a small wooden box here."
 mass       25
 capacity   20

The first line says that we are defining an object and that it should have the label box. It then goes on to say that it can be referred to by the player with any combination of the names small, wooden and box.

The second line states that it should have the attributes CLOSABLE, CONTAINER and CLOSED. These attributes tell the appropriate verbs in the library that this object may be opened and closed, have things placed inside it and that it should be closed when the game begins.

The third line, as with the short statement for the object kryten, indicates the text to appear when either the object's {list} or {the} macro is used. In this case, however, rather than name, the article is set to a. Therefore, when noun1 is set to box:

write noun1{list}

will display "a small wooden box", while

write noun1{the}

will display "the small wooden box".

The fourth line, beginning with the keyword long, details the text to be displayed when this object is in the current location. If an object has a mass of scenery, the long text will not be displayed. This is because a mass of scenery indicates that the object cannot be taken and should therefore be described in its parent location's look function instead, if at all.
Information Internally, scenery is a constant with a value of 100 while heavy has a value of 99. An object that has its mass set to heavy will have its long text displayed when the object is in the current location, but can't be taken by the player. It is good to keep this in mind when choosing a mass value for each object.

The fifth line sets this object to have a mass of 25. When the player takes an object, the value of the object's mass property is subtracted from the player's capacity property. This indicates that the player's capacity property must currently be 25 or greater for him or her to be able to take the box. Any other container object the player attempts to put the box in or on must also have a capacity property that is currently 25 or greater.

The final line, a capacity property, indicates how many mass units the box can hold. In this case it is set to 20. This means that other objects may be placed inside this object (due to it having the CONTAINER attribute) until the total of their mass properties equals 20.

As this new object has no parent property, it will start the game in the bathroom. This is because the bathroom is the nearest location defined above it in the game file. Any objects with no parent property will begin in the nearest location above it regardless of the number of object or function definitions inbetween.

Before we play the game again, we will add a second object by putting the following code beneath the definition for the box.

object note: orange note
 short    an "orange note"
 long     "An orange note rests on the ground."
 parent   box
 mass     5

As you can see, the note's parent property is followed by the label of the box object. This indicates that the note is to start the game inside the box. If this parent property where to be omitted, the note would begin in the bathroom along with the box, as this is the last location defined above it in the game file.

If the box object had the attribute SURFACE rather than CONTAINER, the note would start the game on top of it. If the box object had the attribute ANIMATE, the note would start the game being carried by it.

Verbs and Functions

Now if you restart the game, you can walk west into the bathroom and find the small wooden box. You can take it, open it, look in it, close it, and apply a whole range of other standard verbs present in verbs.library to it. When it is open, you will also be able to take the orange note out of it.

To explain a little bit about how verbs work, lets take a closer look at the verb read. The grammar definition and the global function for the verb read can be found in verbs.library and are reproduced here:

grammar read *present >read

{+read
if +important<noun1 = true
   return true
endif
if +darkness = true
   return true
endif
write "There is nothing on " noun1{the} " to read.^"
}

This grammar definition states that if the player types a command of the format "read ObjectThatIsPresent" during the game, then certain functions with the base name read should be executed.

When the player makes a move, the JACL interpreter will set the integer variables noun1 and noun2 to point to the objects referred to in the command. They will be set based on the order the objects appear in the move. For example, if the player types the command give sword to troll the +give_to function would be called with noun1 being set to the sword and noun2 being set to the troll.

Returning to our read example, upon identifying the player's command as matching this grammar definition, the JACL interpreter will first attempt to execute a function called read that is associated with the object that the player is attempting to read. If this does not exist, it will try to execute the same function name only prefixed with a plus sign. This is a global function and can be thought of as the default action that occurs for that verb if no object-specific one is provided.

Since we have not associated a read function with our note object, when the player attempts to read it the global function +read will be executed. If you try this you will see that the default action for the read verb is very simple, but appropriate for most objects. The default function +read is not an appropriate, however, for our note object. We will therefore replace it by adding a function called read that is associated with the object note. This function is even simpler:

{read
write "Welcome to Jamaica and have a nice day.^"
}

We associate this function with the note object by typing it directly after the note object's definition. For clarity, you can of course leave a blank line or two in between the note definition and read function. This local read function will now be executed in place of +read whenever the player types the command read note, provided the note is visible to the player.
Information The read function above should really be given two names. This is done by placing the second name after the first, such as {read : examine. This means that both reading and examining the note will lead to the same code being executed. Any function may have as many names as can fit in a single line of JACL code.

Overriding Functions

Compared to +read, the +close function is quite long. Although the default +close function is perfectly suitable for our needs, we will use it to demonstrate overriding the default outcome of a function. The +close function is reproduced here:

{+close
if +important<noun1 = true
   return true
endif
if +darkness = true
   return true
endif
if +reach<noun1 = true
   return true
endif
if noun1 hasnt CLOSABLE
  write "You can't close " noun1{the} .^
  set time = false
  return
endif
if noun1 has CLOSED
  write  noun1{The} noun1{is} " already closed.^"
  set time = false
  return
endif
override
write "You close " noun1{the} .^
ensure noun1 has CLOSED
}

During the course of the +close function, several tests are performed to determine the appropriate outcome. At the point in the function where it is decided that the command should be successful there is an override command. This command tells the JACL interpreter to look for a function called close_override that is associated with the object that the player is attempting to close. If this exists it will be executed in place of anything beyond the override command. If this does not exist, then execution of the +close function continues as normal from the line following the override command.

The reason for the override command is that a close function that is associated with any object will get called straight away, completely replacing all the code in the default function +close. This means that any test, such as whether the object is already closed, will have to be repeated manually in the new, local function. This is not so much of a problem with a simple verb like read, as no tests are performed, but with some other verbs this can be a considerable amount of code. The override command therefore provides an opportunity to override only the outcome, not the entire function.

To demonstrate, we will override the normal outcome of closing the box. This is done by associating the following function with the object box:

{close_override
write "The lid creaks as you push it closed.^"
ensure box has CLOSED
}

When the box is closed by the player, all the tests before the override command in the +close function will be executed. If all the tests pass, the code in close_override will be executed in place of any code after the override command.
Warning It is important to be careful that you use an override function (or perform the tests manually) whenever a verb has several possible outcomes. If the above function was to be called close, as opposed to close_override, then the box could be closed over and over again - clearly a bug.

Doors

We will now add a third and final location. This location will be a living room and will be placed south of the bedroom. We are also going to place a door between the bedroom and living room. Doors are common in interactive fiction, implemented using a regular object, and yet in some ways odd and out of the ordinary. The thing that makes a doors different to most of the other objects in your game is that they live between two locations, rather than inside one, and need to be accessible from both locations.

Before we move onto the door itself, add the following location definition beneath the close_override function that is associated with the box:

location living_room: living room
  short        the "living room"
  north        nowhere

{look
if here has VISITED
   print:
      You have returned to the living
      room.^
   .
else
   print:
      You are in the living room. There
      is a small television perched on a low-lying
      table in front of a sofa.^
   .
endif
}

The look function associated with the living room is slightly more complex than those associated with the other locations. After the player enters a location for the first time, it is automatically given the attribute VISITED by the JACL interpreter. This latest look function tests the current location for this attribute and displays a shorter, more appropriate description if it has it. Feel free at this stage to go back and add this test to the look functions for the bedroom and bathroom. The north direction is specified as leading to nowhere as the game will start with the door closed. As nowhere is the default for an unspecified direction, we could have simply omitted it. I prefer to specify nowhere for directions that only currently lead nowhere, but will change during the course of the game.

As you may have guessed, I would also add this line to the bedroom giving the complete definition of:

location bedroom: master bedroom
  west        bathroom
  south       nowhere

As you will see later, we will make the door accessible from two locations by moving it around, so it does not really matter when you define its object. As the player begins the game in the bedroom, and this is where they will first encounter the door, beneath the bedroom definition is as good a place as any for the door's definition:

object door : bedroom door
 short      the "bedroom door"
 has        CLOSABLE CLOSED

{open_override
set bedroom(south) = living_room
set living_room(north) = bedroom
return false
}

{close_override
set bedroom(south) = nowhere
set living_room(north) = nowhere
return false
}

The above code defines an object for the door that has the attributes CLOSABLE and CLOSED. These attributes allow the verbs in the library to manipulate the door appropriately. Like the box, the outcome of the close verb is overriden, as is the outcome of the open verb. Unlike the box, the override functions for the door both end with a return false command.

As discussed above, when the interpreter encounters the override command in the global function +close, it will look for the function close_override that is associated with the object being closed. If this function does not exist, or returns false, execution will continue from the first line after the override command. As we have defined an associated close_override function, it will be executed at this point. It is possible, however, for the override function to issue a return false after a test has determined that the default outcome is sufficient in a particular instance. If this happens, the close_override function simply provides some code to execute in addition to the default outcome. In other words, the default outcome will occur as if the override function did not exist at all if the override function returns false.

Now it is time to move the door between the bedroom and living room so that it is accessible from both locations. As with most programming challenges, there are many ways the desired effect could be achieved. One option is to create two doors, one in the bedroom and one in the living room, then keeping these two doors synchronised. In this case we are going to add two eachturn functions: one associated with the bedroom and one with the living room. These two functions move the door to the current location and each have a single line of identical code:

{eachturn
move door to here
}

It is possible to give a function more than one name by supplying a list of names separated by white space. This is a very common technique for making several possible moves by the player cause the same outcome. For example, in The Unholy Grail, the message for examining the rod for opening and closing the blinds is exactly the same as the one for attemting to take it:

{examine : take
print:
   The plastic rod is attached to the blinds and
   can be turned in order to open and close them.^
.
}

Internally, when a function is associated with an object, the function's name is stored as the supplied name followed by an underscore, then the label of the object it is associated with. This means that the above function would have two full internal names being examine_rod and take_rod. As the two eachturn functions in this tutorial game have identical content it would be good to use a similar technique to avoid the duplication. The difference between the eachturn functions and the examine and take functions is that with the eachturn functions, it is the same action for two different objects (in this case, locations) rather than two different actions for the same object. It is possible to overcome this problem by prefixing one of the function's names with an asterisk (*), and then supplying a full internal name manually. The asterisk will not become part of the full internal name (unlike the plus sign at the start of a global function), it simply tells the interpreter that you are going to handle the association manually and that it should not automatically add the label of the object above it in the source file as a suffix.

Using this technique we are able to write a single eachturn function and associate it with both the bedroom and the living room by adding the following code somewhere below the definition for the living room:

{eachturn : *eachturn_bedroom
move door to here
}

This will create a function that has two names. The full internal version of these two names will be eachturn_living_room and eachturn_bedroom. The first association is done automatically while the second is done manually by using the asterisk prefix. Don't worry if this doesn't make complete sense to you right now, it is not an essential technique for writing JACL games, particularly with functions as short as this eachturn function. More information on creating a function name using the asterisk prefix can be found in the chapter on Functions.

Regardless of whether you use this technique, or associate an identical eachturn function with the bedroom and the living room in the usual manner, you will now have a functioning door. The door is handled correctly by the library verbs open and close due to having the CLOSABLE attribute and it is always accessible from the bedroom and living room thanks to the eachturn functions. Finally, the extra code supplied in the open_override and close_override functions create and destroy the extra links between the bedroom and living room as appropriate.
Information It is also possible to have moved the door from location to location using movement functions that are associated with bedroom and living room. These functions would need to test which way the player is travelling, and move the door to that location if it is into the bedroom or living room. Although this requires slightly more complicated code, it is more efficient as the move is only done once when required, not every turn that the player then spends in that location. With a small game like this, however, simplicity is of greater concern than performance.

Non-player Characters

Most games will include at least one character other than the player, and this small tutorial game is no exception. The character we will add is the player's son who will be sitting in the living room watching television. Fortunately for us, simulating the responsiveness of a teenager watching television is not hard.

We will begin by adding the following two object definitions and their associated examine functions beneath the definition for the living room:

attribute EXAMINED

object television: television tv tele
 short    a "television"
 mass     scenery

{examine
if self has EXAMINED
   write "It's Rick who is the TV addict, not you.^"
   return
endif
write "There is currently a cartoon showing on the "
write "television.^"
ensure self has EXAMINED
}

object rick: son boy teenager rick
 has     ANIMATE
 short   name "Rick"
 long    "Rick is here, watching television."
 mass    heavy

{examine
if @ = 1
   print:
      Rick is staring blankly at the television screen.^
   .
else
   print:
      Rick is still gazing into the television's screen.^
   .
endif
}

Before the object definitions is the definition of a user attribute called EXAMINED. Up to 32 user attributes can be defined, and like the system attributes CLOSABLE and CLOSED, they don't have a value, they are simply flags that an object either has or hasn't. The examine function for the television makes use of this attribute to test if the television has already been examined before. Unlike defining a constant as a synonym for one of an object's integer properties (see the section on The Passing of Time below), user attributes are not synonyms for system attributes, they are an entirely separate set of 32 attributes.

The examine function for Rick achieves a similar result using an entirely different mechanism. When the JACL interpreter executes a function it automatically creates and increments a counter that records how many times that function has been called. This counter is referred to in code as an at sign (@) followed by the name of the function. For example, the number of times the function called when examining Rick has been called can be checked from any other code by examining the contents of the container @examine_rick. When an at sign is referred to on its own, such as in the example above, it is taken to refer to the call count of function that is currently running.

To make both Rick and the television respond to the player's commands, we will be required to associate more functions with each of them. This process of creating objects and associating functions with them is the essence of writing text adventure games using JACL.

The first action we will cater for is talking to Rick. The grammar statement that matches the command talk to rick calls the function talk_to. Therefore, in order to give a custom response to this command, we must associate a talk_to function with the object rick. This function should look as follows:

{talk_to
print:
   ~Uh, yeah, I'll do it in a minute,~
   Rick mumbles with out looking up. You have
   quite a strong suspicion that he didn't
   really hear a word you said.^
.
}

The above talk_to function is associated with Rick in the same way that the read function was associated with the note earlier. For a complete list of all the grammar statements defined in the library and the names of the functions they call, see APPENDIX B: Library Verb Functions.
Warning As double quotes are used to enclose any command parameter that contains spaces, attempting to print a double quote directly to the screen will not give the desired results. Wherever you want to print a double quote put a tilde character (~) and the interpreter will print a double quote in its place.

To code a response to the player asking Rick about something in particular, you need to associate a function that has a compound name with the object rick. The grammar definition for the command ask noun1 about noun2 calls the function ask_about, so this is the beginning of the function name. You specify which object is being asked about by appending an underscore and then the label of the object to the function name. For example, the following function (when associated with the object rick) will be called if the player types the command ask rick about note:

object rick: son boy teenager rick
 ...

{ask_about_note
print:
   ~I don't know nothing about no note,~ Rick says 
   looking at your blankly.^
.
}

This gives a custom response when the player asks Rick about the note, but what if we want to give the same custom response no matter what object Rick is asked about? The hard way would be to associate a function for each and every object that prints your required response. The easy way is to write a +default_ask_about function and test whether the person being asked is Rick. Any +default function can be thought of as a replacement for the code that comes after the override command. If you search through the verbs.library for override commands you will see that they occur at the point in a verb's global function where it has been decided that the verb should be successful. This allows a +default function to override the default outcome of a verb without needing to repeat all the preliminary checks. The global default function for any given action is called when an override command is reached and a specific override function for the object or objects in question does not exist. The order that functions are called in and the precedence they have over each other is fully detailed in the chapter on Functions.

This is +default function is a global function (as indicated by the leading plus sign), so it doesn't matter where you put it in the code. Here is the code to create a default response for whenever Rick is asked about an object:

{+default_ask_about
if noun1 = rick
   print:
      Rick blinks several times then
      pokes out his bottom lip. This, you have
      figured out over the years, translates to,
      ~Not a clue.~^
   .
   return
endif
return false
}
Information If both this function and the specific ask_about_note function exist, the ask_about_note function will be executed if player types ask rick about note in preference over this function.

The first thing this function does is test the current value of noun1. As disscussed above, noun1 and noun2 are object pointers that point to the first and second objects referred to in the player's move. If this is currently set to rick, our new default message will be displayed. If noun1 is not set to rick, a return false command is executed. This command causes the interpreter to do whatever it would have normally done had this function not existed at all. In this case, the result would be to perform the original default action specified in the library.

The Passing of Time

In many games actions will occur based on the passing of time rather than the direct actions of the player. If the player was to do nothing other than type wait, people would still come and go, the song playing on a nearby radio would change and the sun would set. All these events would be coded for within an eachturn function. There can be an eachturn function associated with each location and a single global one.

The variable time is automatically set to true before the player types each of their moves. If the player's move is not possible, this variable should be set to false. If, when the command has been fully processed, time is still set to true, the appropriate eachturn functions are executed. The first function executed, if it exists, is the eachturn function that is associated with the location the player is currently in. When this has finished, the global function +eachturn is executed. Once +eachturn has finished executing, the processing of the player's command is complete.

To demonstrate, we will make Rick take a sip from his drink every five turns, regardless of what the player does. As this would happen regardless of whether the player is in the room or not, we will put the code to handle this in the global eachturn function:

constant turns_since_last_sip      5

{+eachturn
set rick(turns_since_last_sip) + 1
if rick(turns_since_last_sip) = 5
   if here = living_room
      write "Rick takes a sip from his drink.^"
   endif
   set rick(turns_since_last_sip) = 0
endif
}  

Each object has sixteen integer properties that can be set and tested. The properties parent, capacity and mass are three of these integer properties that you have already seen. For a complete listing, see the chapter Definitions in Detail. These sixteen properties are stored in an array, with parent, capacity and mass being the first three integers, stored in the order starting at index 0. This means that the following code will print the number 25 twice:

object cube : timber cube
 short	a "timber cube"
 mass		25

{+some_function
...
write cube(mass) ^
write cube(2) ^
...
}

As the parent property is an integer property, only the index of the parent object is stored. When objects are defined in your game, there are given a sequential integer index starting with 1. As objects and location are both stored internally as objects, this numbering system makes no distinction between the two. This means that if a location representing a forest is the first object or location defined in the game, setting any other objects parent property to 1 will place it in the forest. Although potentially dangerous if you rearrange your game code, this also allows you to test if the player is in a certain section of your game world by testing if the value of here (an internally defined synonym for player(parent)) is within a certain range of values.

The status property of an object is set and tested in the same way as the parent property. The status property, however, has no pre-determined use so we are free to use it as our counter. In order to make our code more readable and self-commenting, before the +eachturn definition, is a constant definition with the name turns_since_last_sip with a value of 5. The status property is the sixth integer property and therefore has an index of 5. By creating this constant we are able to use an object's status property through a more appropriate synonym. The code in the +eachturn function increments this property by one after each successful move made. When it equals 5 a message is displayed (if the player is in the living room) and it is set back to zero. The process will then loop over and over again.

Winning and Losing the Game

Our mini game is not much good unless it can be won. For this to happen, the player must have a goal and be awarded points for each obstacle they overcome along the way. The goal for this game is going to be to find the television guide and give it to Rick. To make this game a fiendishly-clever all-time classic we are going to hide the guide under the bed.

Before we do this, however, we are going to introduce an element of danger by adding some code to cater for switching the television off. We already have an object for the television, so all we need to do now is associate a turn_off function with it. This should look like the following:

{turn_off
print:
   As you reach over and switch off the
   television, you get quite a shock to see Rick
   rapidly growing a coat of hair and foaming at
   the mouth. The shock of this is only surpassed 
   by that of him sinking his newly
   acquired fangs into your throat.^^
.
execute "+game_over"
}

Okay, so killing the player without any real warning is grossly unfair, but is serves as a demonstration of how to handle the player dying. The last line of the above function is an execute command that calls another function with the name +game_over. This function is included in the file frame.jacl and looks like this:

{+game_over
write "^"
execute "+score"
endgame
}

This function uses another execute command to call a function that displays the player's score. It then uses the endgame command to signify that the game has ended and that the player should be presented with the option to restore a previously saved game, undo the last move, restart or quit.

Before we move on to winning the game, it is worth mentioning that the default response for turning an object on is to say that this cannot be done. In the case of the television, it would be important to add a turn_on function stating that the television is already on.

Now, on to the television guide. To implement this puzzle we are going to need two more objects: the guide itself and the bed to hide it under. Begin by defining an object for the bed somewhere beneath the definition for the bedroom (but before that for the bathroom). Beneath that, add a definition for the television guide. The television guide must have its parent property set to limbo until the player has discovered it. The location limbo is defined in the library and is used exclusively for situations such as this where we need somewhere to temporarily store objects that the player should not have access to. Finally, we must associate a look_under function with the bed that moves the guide to the bedroom and awards the appropriate points. Here is the complete code for all of this:

object bed: bed
 short       a "bed"
 mass        scenery

{look_under
if guide(parent) = limbo
   print:
      Hidden under the bed you
      find this week's television guide.^
   .
   set guide(parent) = here
   points 50
   return
endif
write "You don't find anything else.^"
}

object guide: television tv tele guide
 short     a "television guide"
 long      "The television guide is here."
 parent    limbo
 mass      5

{examine : read : look_in
write "It contains a listing of this "
write "week's programmes.^"
}
Warning It is important with objects that share names, such as the television and the television guide to be aware of which object has the most names. If only shared names are used, the object with the lowest number of names will be selected. For more information, see the chapter on Object Resolution.

Now, when the player looks under the bed, the guide will be moved from its initial location, limbo, to the bedroom. Once this has been done, the player will be able to take it. The points command will increase the player's score by 50%. The if statement in this function ensures that this can only be done once.

We will now associate a give_to_rick function with the guide. This will be the winning move and should look like this:

{give_to_rick
print:
   ~Cool!~ Rick exclaims as he
   snatches the guide from your hands.^^
   Satisfied that you have achieved
   at least one thing today, you decide to
   go back to bed.^
.
points 50
execute +game_over
}

And so the game is won. The extra 50 points give the player a total of 100, and the function +game_over is executed.

In order to make this tutorial game more complete, other moves the player is likely to try would need to have custom responses added by associating the appropriate functions with the appropriate objects. The more obvious of these include: showing the guide to Rick, telling Rick about the guide and sleeping on the bed. In fact, the more moves you can give custom responses to, the more depth and character the game will have. Also, anything prominent that is mentioned in a location's description, such as the table and sofa in the living room, should also be defined as objects. This enables the player to refer to them, even if they aren't important to solving the game.

If you have an object that logically needs to be in your game as a part of the scenery, but don't want players to waste their time on it as it has no real purpose, you can give it the attribute NOT_IMPORTANT. This attribute causes all the verbs in the library to give a special response that tells the player there is no need to worry about interacting with this object. By giving a minimal implementation of objects this way, you avoid the player receiving a "You can't see any such thing." message, without the object being perceived as a red herring. For example, this could be used with the sofa in living room using the following minimal code:

object sofa : sofa
   short    a "sofa"
   has      NOT_IMPORTANT		

No anytime the player attempts to refer to the sofa they will receive the message:

The sofa is not important, you don't need to worry about that.

Although not a large or complex game, this tutorial game does demonstrate most of the elements of JACL that you will need to create a more complete piece of interactive fiction. The rest of this guide contains a complete reference description of every feature of the JACL language and interpreters. It is recommended that you at least skim through these chapters in order to gain an awareness of the features at your disposal.

Back to Contents