A JACL game file consists of two fundamental components: code and data. This chapter focuses on the data and provides a detailed break down of all the possible elements that may be defined. In the case of objects and locations, all their associated properties are also listed.
object Label : [Name1 Name2 Name3...] plural Name1 [Name2 Name3 Name4...] has Attribute1 [Attribute2 Attribute3...] short IndefiniteArticle ShortDescription definite DefiniteArticle long LongDescription / function parent ItemLabel mass Integer / heavy / scenery capacity Integer player static
Each object definition begins with the keyword object followed by the object's label. This label is a unique name by which the object will be referred to by any code within the program. The object's label is then followed by a space-delimited list of names. You may specify as many names as you can fit into a single line of JACL code, and if you do not specify any names, the object's label will be set as its one and only name. These are the names the player will use to refer to this object during the course of the game. For more information on object names, see the chapter on Object Resolution.
Following this header are any properties you wish to specify that pertain to the object that you are currently defining. Below is a description of all the possible object properties:
A plural keyword must be followed by a space-delimited list of one or more names that the player can use to refer to this object and others like it as a group.
Be sure not to confuse this keyword with the attribute PLURAL. The attribute PLURAL is given to any object that, by itself, is considered to be plural. An example of this would be an object that represents a bunch of flowers. Objects that have plural names, on the other hand, may be singular objects, like a coin. If a coin is also given the name coins using the plural keyword, it can then be referred to as a group along with other objects that also have the plural name coins. |
If, for example, a game has two coins defined, a silver coin and a gold coin, and the player types take coin, the parser will ask the player which coin they are referring to. If each of the coins was given the plural name coins, and the player typed take coins, the parser would issue the take command for both coins. In a situation where there is more than one gold coin and more than one silver coin, this plural name can also be qualified by one of the object's other names. This is done with a command like take gold coins. This will cause the parser to issue the take command for all the objects present that have the plural name coins and also have the regular name gold. If the player had typed take gold, the parser would have asked the player which gold coin they were referring to, as without a plural name being used it assumes the intent was to refer to a single object.
A has keyword must be followed by a space-delimited list of attributes that the object is to have when the game begins. For more information, see the chapter on Attributes. If this property is omitted the object will start with no attributes.
A short property must be followed by two parameters: the object's indefinite article and short description. The ShortDescription text, prefixed by the specified IndefiniteArticle, is displayed using the object's {list} macro in conjunction with a write command. When using the object's {the} macro, the ShortDescription text will normally be displayed prefixed with the word the or the specified definite article. If the word name is specified as the IndefiniteArticle, the ShortDescription text is not prefixed with anything, such as in the case of a proper noun. This applies to both the {list} and {the} macros. If the ShortDescription text is plural, the object should be given the attribute PLURAL to ensure that it is referred to appropriately by code in the library. If the short property is omitted, the IndefiniteArticle will be set to the and the ShortDescription will be set to the object's label.
A definite property is used to override the default of the that is output when using the object's {the} macro. This is only of use when writing games in languages other than English that have gender for inanimate objects.
A long property is followed by the text to be displayed when the object is in the current location and the player types a look command. If this property is omitted, the object's label is used.
If the text following a long property is the word function, the function long that is associated with this object will be executed whenever the long description text should be displayed. This provides the ability to have lengthy or dynamic descriptions. |
A parent property must be followed by either a location label (indicating that the object begins the game in that location), or an object label (indicating that the object begins the game within, on top of or being carried by that object). If the object's parent is set to here, or the parent keyword is omitted altogether, then the object will start in the nearest location defined above it in the game file.
A mass property indicates the physical bulk of the object. It must be followed by an integer, the word heavy or the word scenery. An integer indicates exactly how much the object encumbers the player or fills a container (see the capacity property below for further information). The word scenery indicates that the object is immovable and that the interpreter should not display its long description after the location description. For this reason, no long property is required for object with a mass of scenery. The word heavy indicates that the object is immovable, but should have the text following its long description printed after the look function that is associated with its parent location is executed. If this property is omitted, the mass for the object is set to scenery.
Behind the scenes, a mass of heavy translates to 99 and a mass of scenery translates to 100. It is important to keep this in mind if you change the capacity property for the player or create any containers. |
A capacity keyword must be followed by an integer, indicating the number of mass units an object with the attribute CONTAINER, SURFACE or ANIMATE can hold. If this property is omitted, the object will have a capacity of zero.
Due to this property's default, any object that has the attribute CONTAINER, SURFACE or ANIMATE must also have a capacity property that is set to a suitably large value in order to allow an object to accept other objects. The exception to this rule is if the object being given or inserted has a mass of 0, but this value should only be used for insignificantly small objects. |
In the file frame.jacl there is an object with the label kryten that is set up to represent the player. This item has a capacity of 42. If left unchanged, the player can not simultaneously carry objects whose mass properties total more than 42. This figure of 42 should be used as a guide when setting the capacity property of other characters, containers or surfaces and the mass of takeable objects. |
A player property has no parameters and indicates that this object is to represent the player in the game. Behind the scenes this sets the object pointer player to point to this object. The value of this pointer can be changed during the course of the game if required.
The properties bearing, velocity, x and y are used by the special-purpose commands position, bearing and distance. See the chapter on Special-Purpose Commands for more information.
The properties next, previous, child, index, status, state, counter, points and class have no pre-determined meaning for objects. You are free to set and test these values as required.
When a command requires a numerical value as a parameter, the following object elements can be referred to:
object_label(parent) | 0 | object_label(index) | 8 |
object_label(capacity) | 1 | object_label(status) | 9 |
object_label(mass) | 2 | object_label(state) | 10 |
object_label(bearing) | 3 | object_label(counter) | 11 |
object_label(velocity) | 4 | object_label(points) | 12 |
object_label(next) | 5 | object_label(class) | 13 |
object_label(previous) | 6 | object_label(x) | 14 |
object_label(child) | 7 | object_label(y) | 15 |
These elements can be referred to by name or index number. For example, the following two commands are equivalent:
set noun4(parent) = chest set noun4(10) = chest # ...and to iterate through all properties set INDEX = 0 repeat write "PROPERTY " INDEX ": " noun4(INDEX) set INDEX + 1 until INDEX = 16
If an object element is to be given a game-specific use, it is often wise to define a constant that describes its use. For example:
constant fuel_left 11 # 11 is the index of 'counter' {+accelerate set space_ship(fuel_left) - 1 ... }
These constants can also be used to set the initial state of an object's properties. For example:
constant fuel_left 11 # 11 is the index of 'counter' object space_ship : space ship fuel_left 20
This code will set the element space_ship(11) to equal 20.
location Label : [Name1 Name2 Name3...] has Attribute1 [Attribute2 Attribute3...] short IndefiniteArticle ShortDescription definite DefiniteArticle north LocationLabel / nowhere northeast LocationLabel / nowhere east LocationLabel / nowhere southeast LocationLabel / nowhere south LocationLabel / nowhere southwest LocationLabel / nowhere west LocationLabel / nowhere northwest LocationLabel / nowhere up LocationLabel / nowhere down LocationLabel / nowhere in LocationLabel / nowhere out LocationLabel / nowhere static
Each location definition begins with the keyword location followed by the location's label. This label is a unique name by which the location will be referred to by any code within the game file. The location's label is then followed by the location's space-delimited list of names. You may specify as many names as you can fit into a single line of JACL code, and if you do not specify any, the location's label will be set as its one and only name. These are the names the player will use to refer to this location during the course of the game. For more information, see the chapter on Object Resolution.
Following this header are any properties you wish to specify that pertain to the location you are currently defining. Below is a description of all the possible location properties:
A has keyword must be followed by a space-delimited list of attributes that the location is to have when the game begins. For more information, see the chapter on Attributes. If this property is omitted, the only attribute the location will have when the game is started is LOCATION.
Internally, objects and locations are both stored using the same data-structure. In fact, once the game is running, the only difference between the two is that a location has the attribute LOCATION. |
A short property must be followed by two parameters: the location's indefinite article and short description. The ShortDescription text, prefixed by the specified IndefiniteArticle, is displayed using the location's {list} macro in conjunction with a write command. When using the location's {the} macro, the ShortDescription text will normally be displayed prefixed with the word the or the specified definite article. If the word name is specified as the IndefiniteArticle, the ShortDescription text is not prefixed with anything, such as in the case of a proper noun. This applies to both the {list} and {the} macros. If the ShortDescription text is plural, the location should be given the attribute PLURAL to ensure the it is referred to appropriately by code in the library. If the short property is omitted, the IndefiniteArticle will be set to the and the ShortDescription will be set to the location's label.
The directions the player can travel in from this location are defined by the properties north, northeast, northwest, south, southeast, southwest, east, west, up, down, in and out. A direction property must be followed by the label of the location that the direction leads to when the game is started. The links between locations may be modified during the course of the game to reflect doors opening etc. The constant nowhere (0), may be used in place of a location label to indicate that the player may not move in that direction. If a direction is not listed, nowhere is the default.
Following each completed location definition should be an associated function called look. This function will be executed every time the description for the location is due to be displayed.
The properties points and class have no pre-determined meaning for locations. You are free to set and test these values as required.
When a command requires a numerical value as a parameter, the following location elements can be referred to:
location_label(north) | 0 | location_label(up) | 8 |
location_label(south) | 1 | location_label(down) | 9 |
location_label(east) | 2 | location_label(in) | 10 |
location_label(west) | 3 | location_label(out) | 11 |
location_label(northeast) | 4 | location_label(points) | 12 |
location_label(northwest) | 5 | location_label(class) | 13 |
location_label(southeast) | 6 | location_label(x) | 14 |
location_label(southwest) | 7 | location_label(y) | 15 |
These elements can be referred to by name or number. For example, the following two commands are equivalent:
set noun4(west) = beach set noun4(3) = beach # ...or iterating across all directions to trap # the player in the current location set INDEX = 0 repeat set here(INDEX) = nowhere set INDEX + 1 until INDEX = 12
Integer variables are defined using the keyword integer followed by the name of the variable. The starting value for the variable can be set by following the name of the variable with an integer or a previously defined constant. If no value is specified on definition, the variable is initialised with a value of zero.
The following are some examples of variable definitions:
constant DEFAULT_POWER 42 integer AIR_LEFT 100 integer LAGERS_DRUNK ; Set to zero by default integer AIRLOCK_SEALED true integer MOTOR_POWER DEFAULT_POWER
Like all other data definitions, variables can not be defined within the body of a function. |
The following is a list of integer variables defined internally by the JACL interpreter.
Variable | Description |
---|---|
compass | This variable is used to store the direction the player moved in when they travel between locations. This variable may be tested in either of the movement functions allowing you to prevent the move from occurring or displaying some special text as required. The direction travelled is encoded as an integer that can be compared to a set of constants. See the section on Moving Non-player Characters for details. |
total_moves | This variable records the number of successful moves entered by the player so far. This variable starts at 0 and is incremented each time a valid command is entered by the player. This is indicated by the value of the variable TIME (see below). |
score | This variable indicates how many points the player has scored during the course of the game. |
display_mode |
This variable indicates whether the interpreter is in verbose or brief mode, with a value of 1 being verbose and 0 being brief. The starting value is 0. In brief mode, each location is given the attribute VISITED when the player enters it, this is not the case when in verbose mode. |
internal_version | This variable is set to the major version number of the JACL interpreter that you are using. This can be tested for in the +intro function to ensure compatibility between your game and the version of the interpreter being used to play it. |
time | This variable is set to true when the player makes a move. If at no time during the processing of that move it is set to false, two things will happen. Firstly, the variable TOTAL_MOVES will be incremented by one. Secondly, the eachturn functions will be executed if they exist. |
max_rand | When the word random is supplied as a parameter to a command expecting an integer value, a random number between one and the current value of max_rand will be generated. The default value is 100. |
notify | When set to true the player will be notified of any increase in their score when the points command is used. |
String variables are defined using the keyword string in the same format as an integer. For example:
string menu_title "Options:"
Once defined, the value of a string can be output by passing the name of the variable as a parameter to a write statement. Strings have a maximum length of 256 bytes.
It is possible to define more than one integer or string with the same name, thereby creating an array of values. Arrays can also be created by supplying more than one value during a single declaration. For example:
integer FIBONACCI 0 1 1 2 3 5 8 13 21 34 55 89 144 integer FIBONACCI 233 377 610 987 1597 2584 4181
The above code will create a single array of variables called FIBONACCI that contains 20 values. The number of values held by an array is fixed when the game is first executed and is referenced by using an at symbol (@) followed by the name of the array. Individual elements of an array are accessed by directly following the name of the array with a set of square brackets ([ ]) containing the index of the element. The first element of an array is at index 0. The following code displays the contents of the FIBONACCI array:
integer INDEX {+display_fibonacci set INDEX = 0 repeat write FIBONACCI[INDEX] ^ set INDEX + 1 until INDEX = @FIBONACCI }
In the above code, @FIBONACCI returns the number of elements in the array, which is one greater than the index of the last element. Take care when using this value to iterate over an array not to access the element ARRAY[@ARRAY]. |
This same technique can be applied to other data types such as strings. For example, consider the following code from verbs.library:
string LCNumber zero one two three four five six seven eight nine ten string UCNumber Zero One Two Three Four Five Six Seven Eight Nine Ten {+number_upper if arg[0] < 0 : arg[0] > 10 write arg[0] else write UCNumber[arg[0]] endif } {+number_lower if arg[0] < 0 : arg[0] > 10 write arg[0] else write LCNumber[arg[0]] endif }
As it is possible to change the value of an integer and a string after they have been created, there is also a shortcut for creating arrays of variables with all elements being loaded with a default value. This is done by using the keywords integer_array or string_array followed by the name of the variable and the number of elements to create. For example, the following line of code will create an array of 10 integer variables called OPTIONS, all with the value 0:
integer_array OPTIONS 10
It is also possible to specify a custom default value after the size of the array like this:
integer_array OPTIONS 10 42
This will create ten variable with the name OPTIONS and the value of 42.
The basic syntax for a constant statement is:
constant ConstantName Value
It is possible to define integer and string constants, the type being inferred from the value. If you would like to create a string constant that contains only a number, enclose the number in double quotes: Below is an example of creating constants. This example is taken from the beginning of The Unholy Grail and defines one integer and three string constants that contain the bibliographical information required by the Treaty of Babel:
constant game_title "The Unholy Grail" constant game_author "Stuart Allen" constant game_version 2 constant ifid "JACL-002"
Unlike a variable, the initial value of a constant is not an optional parameter. A value must be specified and this value will remain unchanged for the duration of the game.
Although a variable can be used wherever a constant can be used, if a value is not to change during the game, there are two advantages to using a constant. The first is that the value cannot be changed by accident using a set command. The second is that constants are not saved each time the player makes a move so for this reason they also provide a performance increase over the use of variables. |
Synonyms are a way of substituting a word in the player's move for another word. They are defined using the keyword synonym followed by the word to be substituted and then the word to be put in its place. Care should be taken when defining synonyms, as duplicate grammar statements are often the better approach. Consider the following examples:
synonym get take synonym grab take
With the above synonyms defined, the command 'get note' would be translated to 'take note' before being parsed. With the below grammar statements defined, but no synonyms in place, the command "get note" would be parsed as is, but will still be mapped to the take function.
grammar take **here >take grammar get **here >take grammar grab **here >take
The problem with the synonym approach is that if we were then to define the following grammar statement, we would run into trouble:
grammar get out >exit
With the above synonym defined, the command 'get out' would be translated to 'take out' before being parsed, a sentence that the game would not understand.
There are right and wrong times to use both approaches to broadening your game's vocabulary. Just be sure to take care and consider the potential effects of any synonyms you define.
There are a few filters defined in verbs.library, and chances are you will never need to define any of your own. They are defined using the keyword filter followed by the word to be filtered from the player's input before it is parsed.
The following are some examples of filter definitions (taken from verbs.library):
filter the filter quickly
With the above filters defined, if the player typed the command:
quickly take the coin from the bag
the parser would process:
take coin from bag
Filters should be defined very sparingly. They are a designed to give the illusion of the parser understanding more than it really does. Although at times this can be good, at other times it can be very, very bad. |
The basic syntax for a grammar statement is:
grammar MoveSyntax >FunctionName
The keyword grammar defines a move that may be typed by the player and the function that will be executed when this move is made. The MoveSyntax section defines the syntax of the move and consists of one or more parameters that may either be a word to be typed verbatim or one of several special tokens. The last parameter of a grammar statement is a greater-than symbol directly followed by the core name of the function to be executed if a move of this format is typed by the player.
The table below details the tokens that can be used as part of the MoveSyntax:
Token | Description |
---|---|
*here | Indicates that an object in the current location must be supplied at this point in the move. |
**here | Indicates that one or more objects in the current location may be supplied at this point in the move. |
*held | Indicates that an object held by the player must be supplied at this point in the move. |
**held | Indicates that one or more objects held by the player must be supplied at this point in the move. |
*present | Indicates that an object either in the current location or held by the player must be specified at this point in the move. |
**present | Indicates that one or more objects either in the current location or held by the player must be specified at this point in the move. |
*anywhere | Indicates that an object anywhere in the game world must be specified at this point in the move. |
**anywhere | Indicates that one or more objects anywhere in the game world must be specified at this point in the move. |
*inside | This scope indicator can only be used as the second noun of verbs that have two nouns. It indicates that an object that is a child of the first noun must be specified at this point in the move. |
**inside | This scope indicator can only be used as the second noun of verbs that have two nouns. It indicates that multiple objects that are children of the first noun must be specified at this point in the move. |
*location | Indicates that a location in the game world must be specified at this point in the move. |
$string | Indicates that an arbitrary text string must occur at this point in the move. If this text string is to contain spaces, it must be enclosed in double quotes. |
$integer | Indicates that an integer must be supplied at this point in the move. |
For example, the following are some valid grammar statements:
grammar take **here >take grammar insert **held in *present >insert_in grammar set *held to $integer >set_to
The first statement says that if the player types the word take, followed by one or more objects that are in the current location, then the function take should be executed. The second states that if the player types the word insert, followed by one or more objects that are being held, followed by the word in, followed by an object that is either being held or is in the current location, then the function insert_in should be executed. The third states that if the word set followed by a single object that is being held, followed by the word to, followed by an integer then execute the function set_to. Feel free to add extra grammar statements that map to library functions into your program. Keeping these extra grammar statements within the game-specific part of your code means that you can upgrade the library at a later date without needing to re-enter your additions. The exact way in which these functions are executed is detailed in the section on Responding to Player's Moves.
When entering a new grammar definition, be sure not to leave a space between the greater-than symbol (>) and the name of the function to be executed. |
The basic syntax for an attribute statement is:
attribute AttributeName [AttributeName AttributeName...]
The keyword attribute defines a user attribute that can be used throughout your game for any custom purpose you require. Once a user attribute is defined, it is used in the exact same manner as a system attribute. You can define up to 32 user attributes.