Functions

Functions in JACL are similar in principle to functions and procedures in many other programming languages. They act as sub-routines, discrete units of code that can be executed manually from other functions or internally by the interpreter. There are two fundamental types of functions: global and associated. A global function is independent of any object and is designated as such by being given a name beginning with a plus sign. Any function whose name does not begin with a plus sign is automatically associated with the nearest object or location above it in the game file.
Warning It is illegal to define a non-global function before the first object or location as it will not have an object or location to be associated with.

A function begins with a opening curly brace ({) that is followed directly by the name of the function. It is possible for a function to have multiple names by providing additional names on the same line as the opening curly brace, each separated by whitespace. Below is an example of a function associated with an object:

object boulder

{take : push : turn
write "The boulder is way too heavy to move.^"
set time = false
}

This function has three names. As none of the names begin with a plus sign, they are all names associated with the object boulder.

The full internal name of an associated function is constructed by taking the name as it appears in the program, then appending an underscore and the label of the object or location that it is associated with. For example, the above function has the full internal names of: take_boulder, push_boulder and turn_boulder.

If the name of a function begins with a plus sign, it is considered to be a global function and the label of the nearest object is not appended to the supplied name. For example, the function +eachturn has the full internal name of +eachturn.

The EXECUTE and CALL Commands

All execute and call commands are of the following format:

execute/call [object.]FunctionName[<arg1<arg2...]

The execute command allows the manual execution of any function by specifying its full internal name. The call command is almost identical except it will not display an error if the function does not exist. This behaviour is required when calling a function that contains some optional, extra code that may or may not exist. When executing functions manually using the execute or call commands, the full internal name must be specified. With global functions, this is simply the name of the function as it appears in the program. For example, the following function:

{+hello
write "Hello world!"
}

would be called with the command:

execute +hello

The full internal name of an associated function is constructed by taking the name as it appears in the program, then appending an underscore and the label of the object or location that it is associated with. For example, the following function:

object wheel : steering wheel

{examine
write "The steering wheel is covered in black leather.^"
}

would be called with the command:

execute examine_wheel

Once a function is executing, the string constant function_name is set to contain the full internal name that was used to call the function. This can be useful if the function has multiple names and you need to modify its behaviour based on which function was used to call it. For example:

{+intro
execute "+example"
}

{+test : +example : +multi
write function_name ^
}

This code will simply output the string +example

An execute command also allows the name of a function be to prefixed with an item label or pointer, followed by a period. This tells the interpreter to execute the specified function that is associated with the specified item. For example, the above command could also be expressed as:

execute wheel.examine

The advantage of this syntax is that an item pointer or variable can be used in place of the object label wheel. The following code snippet is equivalent to the command above:

set noun4 = wheel
execute noun4.examine

When using the syntax of an object pointer or label followed by a period, it is legal to supply a variable, integer constant or object element as the function name. This is useful when you wish to either iterate through a series of functions associated with an object or dynamically map an action to a function at run-time. The name of the function called will be the current integer value of the supplied variable. For example, the code below demonstrates two ways to call the function 1 that is associated with the dial:

constant	setting 2

object dial : dial
 short	   a "dial"
 setting   5

{1
write "You set the dial to one^"
}

set dial(setting) = 1

execute dial.dial(setting)

# OR, MORE DIRECTLY...
execute 1_dial

It is possible to associate a function with more than one object by prefixing the function name with an asterisk (*). When you prefix a function with an asterisk the full internal name of the function will be stored exactly as the name supplied. This allows you to construct a name that mirrors the name that would be created for a normal associated function. For example:

object redball : red ball

object blueball : blue ball

object yellowball : yellow ball

{kick : *kick_redball : *kick_blueball
write noun1{The} " sails high in the air.^"
}

The above function has three names, the first automatically associating it with the yellow ball in the normally fashion with the second two manually associating it with the objects redball and blueball. The order the names are defined in is not important.
Warning When using the above technique to manually associate a function with an object, the label of the object must not contain an underscore. This is because the supplied function name is parsed from the right, with everything after the first underscore encountered being considered the object label. If a function was to be given the name *kick_blue_ball, the interpreter would attempt to associate the function kick_blue with the object ball.

It is also possible to supply the name of the function to be executed in a string variable or constant. This technique allows strings to be used as function pointers and is used by the menu.library as a way of passing a call-back function to function in the library. See the chapter on the menu.library for an example.

A function will stop executing and return to the function that it was called from when it encounters a return command or arrives at the closing brace. If a function reaches its closing brace, an implicit return true is executed.

Passing Arguments to a Function

It is possible to pass string and integer arguments to a function when executing it. This is done by following the function name by a less-than symbol (<) followed by the value to pass. Each additional argument is separated by another less-than symbol. When the specified function is executed, the arrays arg and string_arg are populated with the values passed. The array string_arg stores a copy of the string value of every argument passed. If a string variable or constant is supplied as an argument, the value of the string constant is stored, not the name of the constant itself. The array arg stores integer value for every argument that can be resolved to an integer. If an argument can't be resolved to an integer, -1 is stored at that point in the array. The arrays arg and string_arg are always of equal length, being the total number of arguments supplied. Below is an example of a function call that passes seven arguments and a function that displays them:

string   test   "This is a string constant."

variable DEPTH  0
variable INDEX  0

{+some_function 
...
set DEPTH = 99
execute "+subfunction<This is a literal string.<42<Fred<test<12<DEPTH<13"
...
}

{+subfunction
set INDEX = 0
while INDEX != @arg
  write "arg[" INDEX "]: " arg[INDEX] "  string_arg[" INDEX "]: " string_arg[INDEX] ^
  set INDEX + 1
endwhile
}

The above code produces the following output:

arg[0]: -1   string_arg[0]: This is a literal string.
arg[1]: 42   string_arg[1]: 42
arg[2]: -1   string_arg[2]: Fred
arg[3]: -1   string_arg[3]: This is a string constant.
arg[4]: 12   string_arg[4]: 12
arg[5]: 99   string_arg[5]: fuel_left
arg[6]: 13   string_arg[6]: 13
Information

The first argument passed to a function is also stored in the object pointer noun3 for historical reasons.

The function-call count

Every time a function is executed, an internal count of the number of times it has been called is increased by one. The value of this count is obtained by prefixing the full internal name of the function with an at symbol (@). If an at symbol is used on its own, the number of times the current function has executed is returned.

For example, below is the same example used in the section User Attributes, modified to use the function-call count:

{open_override
ensure door hasnt CLOSED
if @ = 1
   write "You hold your breath as the door slowly "
   write "creaks open.^"
   return
endif
write "You open the door again.^"
}

The RETURN Command

The return command is used to pass a value back to the function that called it, or the interpreter if called internally. A return command with no parameters will return the value 1, which is the same as a function simply reaching its closing bracket. If a value is specified as a parameter to a return command, that value will be returned instead. For example:

{+some_function
set RESULT = +adder<16<21<42<75
write "The result is: " RESULT
}

{+adder
set INDEX = 0
set COUNTER = 0
while INDEX != @arg
  set COUNTER + arg[INDEX]
  set INDEX + 1
endwhile
return COUNTER
}

The above function +adder will sum all the values passed as arguments and then return the result to the calling function.

Responding to the Player's Moves

In this section you will learn more about the function calls made by the interpreter when the player types a move while playing a game. As the first step in processing the player's move, the interpreter attempts to find a grammar statement that matches the command typed. For more information see the section on Grammar Statements. If a match is found, the function after the greater-than symbol at the end of the grammar statement is used as the core name for a series of function calls. This mapping of the player's moves to functions through the use of grammar statements is one of the fundamental principles of writing a JACL game.

Before calling any functions, the interpreter will set two object pointers, noun1 and noun2. These are set to the objects referred to in the move typed by the player in the order they occur. For example, for a move like "insert card in slot", noun1 would be set to the card, and noun2 would be set to the slot. We will start, however, by examining a move that refers to a single object, such as "take wooden pole".

The grammar statement that matches the move "take wooden pole" looks like this:

grammar take *here >take

This grammar statement says that if a move consisting of the word take followed by an object that is in the current location is typed, execute the function take. In reality there are a number of possible functions that can be called, each having take as a part of their name. For the purpose of the following examples, we will assume that the object "wooden pole" has the label pole.

After the player types the move "take wooden pole", the first function the interpreter will attempt to execute is the global function +before_take. If this function exists, and does not return false, no further processing is performed with regard to this move. Below is an example of what this function might look like:

{+before_take
if guard is *here
   write "You decided to leave " noun1{the} " alone "
   write "while the guard is around."
   return 
endif
return false ;continue as normal
}

As you can see, the +before_take function is independent of the object being taken. This makes it ideal for situations that affect all objects. If this function returns false, or does not exist at all, the interpreter will next attempt to execute the function take_pole. This function will appear in the game file as a function called take that is associated with the object pole.

If this function exists, it will be executed in place of the default global action for the take verb. If this function does not exist, or returns false, the global function +take will be executed. This function contains the default outcome for the take verb. Below is the +take function from the library:

{+take         
if +important<noun1 = true
   return true
endif
if +darkness = true
   return true
endif
if +reach<noun1 = true
   return true
endif
if player has SITTING
  write "You will have to stand up first.^"
  set TIME = false
  return
endif
if noun1(mass) >= heavy : noun1 has LOCATION
  execute +move_scenery
  return
endif  
if noun1(mass) > player(capacity)
  write "You are carrying too much to take " noun1{the} .^
  set TIME = false
  return
endif
if noun1 has LIQUID
  write noun1{The} " run" noun1{s} " through your fingers.^"
  return
endif
override
write "You take " noun1{the} .^
move noun1 to player
ensure noun1 has TOUCHED
}

This function performs a few simple tests to confirm the move is possible then moves the object being taken to the player. When this function reaches the override command (the fourth line from the end), the interpreter will attempt to execute the function take_override_pole. This will appear in the game file as a function called take_override that is associated with the object pole. If this function exists, it will replace all the code that comes after the override command in the function +take. This allows an object-specific outcome to be coded for, while still taking advantage of all the tests that precede the override command performed. For this reason, an override function is only of use when there is a chance that the player's move may not be possible. This is the case with the take command in situations such as when the player is already carrying too much, the object they are attempting to take is out of reach, or it is a liquid.

If the function take_override_pole does not exist, the interpreter will attempt to execute the function +default_take. This function allows the author to code a default override function that applies to all objects.
Information The same effect can be achieved by modifying the code after the override command of the +take function in the library. Putting this code in +default_take, however, allows the library to be upgraded to a newer version at any time without losing your game-specific modifications. This is obviously the preferred method.

If the override command in the function +take is reached, and neither a take_override_pole or +default_take function exists, execution will continue from the line after the override command.

The final function to be called in the processing of the player's move, regardless of the outcome of any preceding it, is +after_take. This function provides the opportunity to display any additional text before the player's move is complete. Below is an example of this:

{+after_take
if noun1 = cookie
   if cookie(parent) = player
      if fred is *here
          write "Fred looks a bit upset that you have"
          write "taken the last cookie.^"
          return
      endif
   endif
endif
}

It is not important whether an +after function executes a return or return false command, as no further processing is performed.

The above example details the function calls made for a command referring to a single object. The following three lists detail all the functions called for an in-game command containing no objects, one object and two objects respectively.

grammar verb >CoreFunction

  1. The interpreter attempts to execute the function +before_CoreFunction. If this function exists and does not return false, execution will skip directly to +after_CoreFunction.
  2. If it does not exist, or returns false, an attempt will be made to execute CoreFunction_CurrentLocation. This is a function called CoreFunction that is associated with the current location.
  3. If this does not exist, an attempt will be made to execute the global function +CoreFunction.
  4. If this function contains an override command, an attempt will be made to execute CoreFunction_override_CurrentLocation. This is a function called CoreFunction_override that is associated with the current location.
  5. If it does not exist, or returns false, an attempt will be made to execute the function +default_CoreFunction.
  6. If this does not exist, or returns false, execution will continue from the line after the override command.
  7. The interpreter attempts to execute the function +after_CoreFunction.

grammar verb *Object1 >CoreFunction

  1. The interpreter attempts to execute the function +before_CoreFunction. If this function exists and does not return false, execution will skip directly to +after_CoreFunction.
  2. If it does not exist, or returns false, an attempt will be made to execute CoreFunction_Object1. This is a function called CoreFunction that is associated with Object1.
  3. If this does not exist, an attempt will be made to execute the global function +CoreFunction.
  4. If this function contains an override command, an attempt will be made to execute CoreFunction_override_Object1. This is a function called CoreFunction_override that is associated with the specified object.
  5. If it does not exist, or returns false, an attempt will be made to execute the function +default_CoreFunction.
  6. If this does not exist, or returns false, execution will continue from the line after the override command.
  7. The interpreter attempts to execute the function +after_CoreFunction.

grammar verb *Object1 preposition *Object2 >CoreFunction

  1. The interpreter attempts to execute the function +before_CoreFunction. If this function exists and does not return false, execution will skip directly to +after_CoreFunction.
  2. If it does not exist, or returns false, an attempt will be made to execute CoreFunction_Object2_Object1. This is a function called CoreFunction_Object2 that is associated with Object1.
  3. If this does not exist, or returns false, an attempt will be made to execute the global function +CoreFunction.
  4. If this function contains an override command, an attempt will be made to execute CoreFunction_ Object2_override_Object1. This is a function called CoreFunction_Object2_override that is associated with Object1.
  5. If it does not exist, an attempt will be made to execute the function +default_CoreFunction.
  6. If this does not exist, or returns false, execution will continue from the line after the override command.
  7. The interpreter attempts to execute the function +after_CoreFunction.

Special Functions

The following are some special purpose functions that are called internally by the JACL interpreter:

Function Description
+intro This function is executed when a game is first run or restarted. It is used to display introductory text and set the starting values for any variables required.
+header This function is the very first to be executed before the player's command is processed.
+footer This function is the very last to be executed after the player's command has been processed.
eachturn_here If the current location has an eachturn function associated with it, it will be executed directly before, and under the same conditions as, +eachturn.
+eachturn This function is executed each time a successful command is entered by the player. The interpreter decides on whether or not a command was successful by examining the state of the variable TIME. If it is set to true, the +eachturn function will be executed (and the TOTAL_MOVES variable will be incremented by one), just before +footer is executed.
+system_eachturn This is the final function to be executed after each successful command is entered by the player. This function is used to execute library code that is not game-specific and must be run after each of the player's commands.
+dark_description This function is called by the interpreter if a look command is executed in a location that has the attribute DARKNESS.
+object_descriptions This function is called by the interpreter as the last step in processing a look command. It must display text that indicates the presence of all objects in the current location that don't have a mass of scenery.
+no_light This function is called by verbs in the library if they are attempted by the player in a location that has the attribute DARKNESS.
+movement This function is executed each time the player attempts to move to another location. If this function returns false (it does not exist or exited with the command return false), then the player's attempted movement is successful. If it does exist and does not exit with the command return false (reaches the end of the function or executes a return (return true) command), then the player is not moved. In this case, some text explaining why the movement did not occur should be displayed.
movement_here If the current location has a local movement function associated with it, it will be executed directly before, and under the same conditions as, +movement.
+before_look This is the first function executed whenever a look command is executed. If it returns true no further processing of the look command occurs.
+title This function is executed after +before_look, but before the locations associated look function. This function is the place to put any generic code that prints the title of each location, or extra meta information such as whether the player is currently sitting down.
look_here This function is executed whenever the player types a look command, moves into a new location or restores as saved game.
+object_descriptions This function is executed after the above look function to output the descriptions of all the objects present in the current location.
+after_look This is the last function executed whenever a look command is executed.
constructor_item This function is executed for each item defined straight after the game file is loaded and before +intro is executed.
+save_game
+restore_game
+restart_game
+undo_move
+quit_game
These functions may be defined to override the internal implementation of the respective system-level commands. When the player attempts to use one of these commands, the interpreter will first look for the presence of the corresponding global function. If this function exists it will be executed. If it does not exist, the default implementation inside the interpreter will be used.

Utility Functions

The following are utility functions that are provided by utils.library:
Function Description
+no_light This function is called by verbs in the verbs.library if the player attempts to use them in a location that has the attribute DARKNESS.
+details Object This function displays information about the object that is passed to it as a parameter. This information includes whether the object is open or closed and any other objects that are contained within or being carried by this object.
+contents Object This function displays a list of any other objects that are contained within or being carried by the object passed to it as a parameter. This function is called by +details.
+spaced_contents Object This function is similar to +contents except that it starts a new paragraph if there are any objects to list. It is more suited for use after location descriptions.

Back to Contents