Flow Control

If, Endif and Endall

An if command tests if an expression is true or false for the purpose of selectively executing code. If the expression is true, execution will continue from the next line. If the expression is false, execution will continue from the line after the matching endif or else command. If no endif command is found by the end of the function, the function will terminate normally. An if command must be of the format:

   if LeftValue test RightValue [LeftValue Test RightValue]...

In an if command, each set of three parameters (two values and a test), is called an expression. Each expression is evaluated to equal either true or false. If more than one expression is supplied, the entire statement is considered be true if any one of the expressions is true — a logical or.

The following table lists the possible tests that can be used with an if command when the left and right values are both either an integer, a variable, an item, the word random or a constant. For more information on constants and random see Internals.
Each item defined is assigned a unique number when the game is loaded. In a set or if command, an item's label is substituted for this number. Items are numbered in the order they appear in the game file starting at one.
Test Description
= or == This tests if the left value is equal to the right value.
> This tests if the left value is greater than the right value.
< This tests if the left value is less than the right value.
>= or =>

This tests if the left value is equal to or greater than the right value.

<= or =< This tests if the left value is equal to or less than the right value.
!= or <> This tests if the left value is not equal to the right value.

The following are the possible tests that can be used with an if command when the left and right values are both set to an item:
Test Description
grandof This tests if the left item is the grand parent of the right item.
!grandof This tests if the left item is not the grand parent of the right item.

When an object is placed inside another object, which is then placed inside yet another object, the last object is said to be the grand parent of the first. This is the case no matter how many intermediate objects there are.

The following are the possible tests that can be used with an if command when the left value is an object or a location and the right value is an attribute. For more information see the section on Attributes.

Test Description
has This tests if the item has the specified attribute set
hasnt This tests if the item hasn't the specified attribute set

The following are the possible tests that can be used with an if command when the left value is an object and the right value is one of the words *here, *held, *present or *anywhere. These words have the same meanings when used in an if statement as in a grammar statement. For more information see the section on Grammar Statements.

Test Description
is This tests if the object is in the specified scope.
isnt This tests if the object isnt in the specified scope.

To help clarify, here are some examples of the various types of if commands:

   if sand grandof bucket
   if TOTAL_MOVES >= 42
   if glove has WORN
   if guard isnt *present : id_card has WORN
   if noun1 = sword : noun1 = knife
   if sword(parent) = field

It is possible to nest if statements. Nesting involves placing a second if command before the matching endif command of a first. The end result of this is that the code between the second if command and its matching endif command will only be executed if both statements are true — a logical and.

When multiple if statements are nested, it is possible to avoid the use of multiple endif statements by using the command endall. To demonstrate, the following two functions are equivalent:

   {wave_to
   if noun1 has ANIMATE
      if noun1 hasnt DEAD
         write "<p>" noun1{The} " waves back.^"
         break
      endif
   endif
   write "<p>You get no response.^"
   }

and

   {wave_to
   if noun1 has ANIMATE
      if noun1 hasnt DEAD
         write "<p>" noun1{The} " waves back.^"
         break
   endall
   write "<p>You get no response.^"
   }

A simple way of thinking of the endall command is that the following line will be executed no matter what the result of any if commands that came before it.

Ifstring

An ifstring command is currently only of use if the player's command matches a grammar statement containing a $text token. A $text token in a grammar statement says that the player can type an atribitrary string of text at that point in the command. If the string of text is to contain any spaces, it must be enclosed in double quotes. For more information see the section on Grammar Statements. ifstring commands are of the following format:

   ifstring $text test String [$text Test String]...

Below is a list of the possible tests:

Test Description
== or = This tests if the part of the player's command indicated by $text equals the specified string
!= or <> This tests if the part of the player's command indicated by $text doesn't equal the specified string
contains This tests if the part of the player's command indicated by $text contains the specified string
!contains This tests if the part of the player's command indicated by $text doesn't contain the specified string

For an example of these tests, see the section on Special Tokens.

Else

The fifth command in the if-ifstring-endif-endall partnership is the indispensable else. The code following an else command is executed only if the previous if command was false. If the previous if command was true, executing will continue from the line after the matching endif command. For example:

   if mulder has DEAD
      write "<p>Clamminess on your lips.^"
   else
      write "<p>He looks quite surprised to say the least.^"
   endif

Nested if commands may also make use of the else command. This is demonstrated in the following section of code taken from the +take_all function:

  loop
     if noun3 childof noun1 
        if noun3(mass) < heavy
           if noun3 hasnt LIQUID
              if noun3(mass) <= player(info)
                 if TURN_WORKED = true
                    set TOTAL_MOVES + 1
                    execute "+eachturn"
                 endif
                 execute "+take_routine"
                 set INDEX + 1
              else
                 write "<p>You are carrying too much to take "
                 write noun3{the} .^
                 set INDEX + 1
              endif
           else
              write "<p>" Noun3{the} " run" noun3{s} " through "
              write "your fingers.^"
              set INDEX + 1
     endall
  endloop

Loop and Endloop

The loop command is used to iterate through all the objects defined in the game. When a loop command is executed, the object pointer noun3 is set to point to the first object defined in the game file. When its matching endloop command is executed, noun3 is incremented to point to the next object and the function continues executing from the first line after the original loop command. For example, the following code will output the short description of each object as a list:

   {+print_objects
   write "<UL>"
   loop
      write "<LI>" noun3{List} 
   endloop
   write "</UL>"
   }

Loop commands cannot be nested even if the second loop is contained within another function.

Repeat and Until

A repeat... until loop allows you to repeat a section of code until a specified condition is true. The expression or expressions to be tested should follow the until statement, using the same syntax as an if statement.

The following is an example of a repeat... until loop:

   set INDEX = 10
   repeat
     write "DON'T PANIC! "
     set INDEX - 1
   until INDEX = 0

Like loop... next loops, repeat... until loops cannot be nested within a single function. Unlike a loop command, however, a second repeat loop may be placed within a function that is called from within the first. For example:

   variable INDEX
   variable COUNTER

   {+first_loop
   set INDEX = 10
   repeat
      write "<p>Hello "
      execute "+second_loop"
      set INDEX - 1
   until INDEX = 0
   }

   {+second_loop
   set COUNTER = 5
   repeat
     write " world"
     set COUNTER - 1
   until COUNTER = 0
   write ".</p>^"
   }

Break

break or break true
The command break will stop execution of the current function. What happens next depends of how the function was called. If it was called by another function, execution will continue from the next line after the corresponding execute command in the calling function. If it was called internally by the JACL interpreter, processing will continue, eventually returning to the command prompt for the player's next move.

break false
The command break false will stop execution of the current function. When it returns, the JACL interpreter will behave as though the function it returned from did not exist, and was therefore not executed at all. This will, in the case of calls originating from grammar statements, cause the default action to occur.

The following is a demonstration of the use of break false:

   object bond: james bond

   {ask_about_bomb
   if bomb(parent) = limbo
      break false ;The function +ask_about will now be
                  ; executed as though this function did
                  ; not exist.
   endif
   write "<p>~I'm glad you asked...~</p>"
   }

This is a common technique in JACL games. Once the player has typed a command that refers to an object, the interpreter will attempt to execute the appropriate function that is associated with that object. For example, If the player was to type ask bond about the bomb, the interpreter would attempt to execute the function ask_about_bomb that is associated with the object bond (the function above). If this function does not exist, the function +ask_about will be called instead. When a break false command executed, the interpreter will behave as though the local function containing the break false does not exist. In essence, this allows a function to override the default result of a verb under some conditions and accept it under others.

break function_name
A break command followed by a function name is a conditional break. When it is encountered, the supplied function is executed. If this function executes a break or break true command, or terminates normally, the original conditional break statement will behave like a break true statement. If the executed function executes a break false command, execution will continue from the line after the original conditional break statement.

Like an execute command, a break command that calls a function may also specify an initial value for the object pointer noun4 when the function is executed. For more information, see the section on Functions.

The following is an example of a condition break that can be found at the top of many functions in the library file:

   {+take
   break +reach noun1
   ...
   }

   {+reach
   if noun4 has OUT_OF_REACH
      write <p> noun4{The} " " noun4{is} " out of reach.</p>"
      set TIME = false
      break false
   }