Functions

Functions in JACL are similar in principle to functions and procedures in many other languages. They act as sub-routines, discreet 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 indicated by 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.

It will cause an error if a non-global function is defined before the first object or location in the game file. This is because it must be associated with the first item above it, and in this case there will not be one.

Execute

All execute commands are of the following format:
   execute [object.]FunctionName

The execute command allows the manual execution of any function by specifying its full internal name. When executing global functions, the full internal name is the same as the name that 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 look function that is associated with the location bedroom in the tutorial would be called with the command:

   execute "look_bedroom"

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 above command could also be expressed as:

   execute "bedroom.look"

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

   set noun4 = bedroom
   execute "noun4.bedroom"

When the player types a move that contains two objects, the name of the function must have an underscore appended to it followed by the label of the second object they have referred to. This new compound name is then associated with the first object.

For example, if the player attempts to unlock the object door with the object key, then the function unlock_with_key that is associated with the object door is executed. This would look like the following:


     grammar unlock *present with *held >unlock_with

     object door: front door
       inventory     a "front door"
       mass          scenery

     {unlock_with_key
     ...
     }

If you were to call this function manually using the execute command, you would need to use one of the following:

   execute "unlock_with_key_door"

or

   exucute "door.unlock_with_key"

In the latter syntax, only the object lebel door can be replaced with an item pointer or variable. For this reason it is not possible to call a multi-object function without knowing the label of the second object. As you will see with experience, however, the need to call a function where neither object is known does not arise.

Passing a Value to a Function

It is possible to pass an integer value to a function when executing it. This is done by following the function name by a space and then a value. When the specified function is executed, the object pointer noun4 and the variable ARGUMENT are both set to be equal to value. As stated in the section Type Casting, any variable, item pointer or item element can then be set to have noun4's value, or noun4 can be used directly. For example, consider the following two functions and function calls:

     {+some_function 
     ...
     execute +is_liquid? ball
     execute +multi_hello 100
     ...
     }

     {+is_liquid
     write <p>noun4{The} " is "
     if noun4 has LIQUID
        write "a liquid.^"
     else
        write "not a liquid.^"
     }

     variable INDEX

     {+multi_hello
     repeat
        write "Hello World!^"
        set ARGUMENT - 1
     until ARGUMENT = 0
     }

If there is a chance that the value passed will be zero, always use the variable ARGUMENT in preference to noun4. Referring to any object pointer that is currently set to zero will result in an error being displayed.

When using a break command to call a function, an initial value for noun4 can be specified in the same manner.

Functions Called by the Parser

In this section you will learn more about the function calls made by the interpreter when the player types a move. As the first step in processing the player's move, the interpreter attempts to find a matching grammar statement (for more information see the section on grammar). 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.

Responding to the Player's Commands

Before calling any functions, the interpreter will set two object pointers, noun1 and noun2. These are set to represent 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 would match the move "take wooden pole" would look like the following:


   grammar take *here >take

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

The first function the interpreter will attempt to execute is +before_take. If this function exists, and doesn't issue a break false command, no further processing is performed with regard to the current 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."
        break 
     endif
     break 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 does execute a break false command, or does not exist at all, the interpreter will attempt to execute the function take_pole. This 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 action for the take verb. Once this function has finished executing, the interpreter will attempt to execute the function +after_take. See below for more information on this.

If this function does not exist, or executes a break false command, 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         
   break +darkness 
   break +reach noun1
   if player has SITTING
     write "<p>You will have to stand up first.^"
     set TIME = false
     break
   endif
   if noun1(mass) >= heavy : noun1 has LOCATION
     execute +move_scenery
     break
   endif  
   if noun1(mass) > player(capacity)
     write "<p>You are carrying too much to take " noun1{the} .^
     set TIME = false
     break
   endif
   if noun1 has LIQUID
     write <p> noun1{The} " run" noun1{s} " through your fingers.^"
     break
   endif
   override
   write "<p>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 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 having 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 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 an alternate default outcome that applies to all objects, while still preserving all the tests that precede the override command.

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 stripped off and a later version concatenated back onto your game at any time without losing your game-specific modifications. This is obviously the preferred method.

If the override command +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, regardless 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 fred is *here
         write "<p>Fred looks a bit upset that you have"
         write "taken the last cookie.</p>"
         break
      endif
   endif
   }

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

The above example details the function calls make 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 exist issue a break false command, execution will skip directly to +after_CoreFunction.
  2. If if does not exist, or does issue a break false command, an attempt will be made to execute CoreFunction_CurrentLocation, being the function 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. If this function exists, it will be executed.
  5. If it does not exist, an attempt will be mad to execute the function +default_CoreFunction.
  6. If this does not exist, or issues a break false command, execution will continue from the line after the override command.
  7. The interpreter attempts to execute the function +afterverb.

grammar verb *object1 >CoreFunction

  1. The interpreter attempts to execute the function +before_CoreFunction. If this function exists and does not exist issue a break false command, execution will skip directly to +after_CoreFunction.
  2. An attempt will be made to execute CoreFunction_object1, being the function 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. If this function exists, it will be executed.
  5. If it does not exist, an attempt will be mad to execute the function +default_CoreFunction.
  6. If this does not exist, or issues a break false command, execution will continue from the line after the override command.
  7. The interpreter attempts to execute the function +afterCoreFunction.

grammar verb *object1 preposition *object2 >CoreFunction

  1. The interpreter attempts to execute the function +before_CoreFunction. If this function exists and does not exist issue a break false command, execution will skip directly to +after_CoreFunction.
  2. An attempt will be made to execute CoreFunction_object2_object1, being the function CoreFunction_object2 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_ object2_override_object1. This is a function called CoreFunction_object2_override that is associated with object1. If this function exists, it will be executed.
  5. If it does not exist, an attempt will be made to execute the function +default_CoreFunction.
  6. If this does not exist, or issues a break false command, execution will continue from the line after the override command.
  7. The interpreter attempts to execute the function +afterCoreFunction.

Special Functions

The following are some special purpose functions that are called internally by the JACL interpreter at the appropriate moment.

Function Description
+intro This function is executed when game is first run or restarted. It is usually used to display introduction text and set the starting values for any variables required.
+header This function is executed before the player's command is processed. The function must output the HTTP header as well as the HTML header and opening <BODY> tag. This function is only called by the JACL interpreter.
+before This function is called by the TACL interpreter in place of the +header function.
+footer This function is the very last to be executed after the player's command has been processed. This function must contain the HTML tags </BODY> and </HTML>. It would normally contain the player's command prompt or command buttons. This function is only called by the JACL interpreter.
+after This function is called by the TACL interpreter in place of the +footer function.
+eachturn This function is executed each time a successful command is entered by the player. 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.
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.
+dark_description This function is called by the interpreter if a look command is executed in a location that has the attribute DARKNESS.
+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 break false), then the player's attempted movement is successful. If it does exist and does not exit with the command break false (reaches the end of the function or executes a break (break 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.
look_here This function is executed whenever the player types a look command, moves into a new location or restores as saved game.