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 normal 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 a 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 counter on that function is incremented by one. The counter is set to zero when the game starts and is preserved by save / restore, so an arbitrarily old save will reload with the exact counts it had when it was written.

To read a counter, use the at symbol (@):
@The counter for the currently executing function. The first call into a function returns 1 here, because the interpreter increments the counter before running the function body.
@funcnameThe counter for any function in the game, addressed by its full internal name. Use the auto-suffixed name for object-scoped functions (see below).

The most common use is gating first-time behaviour. The example below is the same one used in the section User Attributes, but rewritten without the user attribute — the call counter does the same job for free:

{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.^"
}

Reading another function's counter

You can ask any function how many times it has run. This is useful when one function wants to react to whether another has executed at all yet, without you having to maintain a separate integer flag for it.

{+eachturn
if @+intro = 0
   # Intro hasn't fired yet -- skip the per-turn weather report.
   return
endif
execute "+weather"
}

For functions defined inside an object or location, JACL auto-suffixes the function name with the object's label when it stores the function (see Function Names earlier in this chapter). So a block like:

object fido : fido
 ...

{examine
   ...
}

is stored internally as examine_fido. To check its counter, use the full name:

if @examine_fido + @examine_eecom + @examine_guidance + @examine_retro = 0
   write "(Tip: try ~examine fido~ to hear what FIDO is reading.)^"
endif

Idiomatic patterns

if @ = 1Run this branch only on the first call.
if @ < 4Show a tutorial nudge for the first few calls, then go quiet.
if @+intro = 0True only before +intro has ever fired.
if @ % 5 = 0Trigger something every fifth time this function runs.

Prefer these patterns over a hand-rolled integer hint_X_shown flag whenever the trigger is "has this function fired N times" rather than "has the world reached state X" — one fewer variable to declare, save and clear on restart.
Information

The counter increments on every entry into the function, regardless of which branch the body takes. If your function toggles state (e.g. open/close on each press), @ advances on both branches; gate against the specific state you care about if the distinction matters.

Fallback: array length

If @name does not match any function, the interpreter falls back to interpreting name as an array and returns the array's length. This is why the +adder example later in this chapter uses while INDEX != @argarg is the integer array of arguments, and @arg resolves via the fallback to "how many arguments were passed". The same form works on any integer or string array you have declared.

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. If this function exists, and does not return false, no further processing is performed with regard to this move.

The next 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 +possessed<noun1 = true
   return
endif
if noun1 is *held
   write "You are already holding " noun1{the} ".^"
   set time = false
   return
endif
if player has SITTING
   write "You will have to stand up first.^"
   set time = false
   return true
endif
if noun1 has ANIMATE
   write "I don't think " noun1{sub} " would appreciate that.^"
   set time = false
   return true
endif
if +move_scenery<noun1 = true
   return true
endif
if noun1(mass) > player(quantity)
   write "You are carrying too much to take " noun1{the} .^
   set time = false
   return true
endif
if noun1 has LIQUID
   write  noun1{The} " run" noun1{s} " through your fingers.^"
   return true
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 stage in processing the player's move calls a series of after functions, regardless of the outcome of any preceding it. It is not important whether any after function executes a return or return false command, as all three after functions will execute in order regardless of outcome of the one before. The first function to be called in this final stage is the local function after_take that is associated with the object pole. This function provides the opportunity to perform any processing required after the move take pole is issued by the player, regardless of any previous outcome.

The next function called is +after_take. This function provides the opportunity to display any additional text relevant only to the take verb, but independent of any objects referred to. 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
}

Finally the global function +after is called. This function provides the opportunity to perform any additional processing before the player's move is complete, regardless of the verb used or any objects referred to.

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
+bootstrap This function is only executed once when a game first loads. Any initialisation code that must be run before the +header function is executed must go here.
+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 when playing with a CGI interpreter.
+footer This function is the very last to be executed after the player's command has been processed when playing with a CGI interpreter.
+top This function is the very first to be executed before the player's command is processed when playing with a GLK interpreter.
+bottom This function is the very last to be executed after the player's command has been processed when playing with a GLK interpreter.
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 verbs.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