# Flow Control

## The IF, IFALL and ENDIF Commands

An if command tests whether 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 with an implicit return true. 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 ifall command has the same format as an if command except the entire statement is only considered to be true if all of the expressions are true — a logical AND.

The following table lists the possible tests that can be used with an if or ifall command when the left and right values are both either an integer, an integer constant, an object or the word random. For more information on constants and random see the chapter on Internals.

| Each object defined is assigned a unique number when the game is loaded. In a set, if or ifall command, an object's label is substituted for this number. Objects 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 object:

Test | Description
--- | ---
grandof | This tests if the left object is the grand parent of the right object.
!grandof | This tests if the left object is not the grand parent of the right object.

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 a location and the right value is an object:

Test | Description
--- | ---
locationof | This tests if the left value is the location of the right object.
!locationof | This tests if the left value is not the location of the right object.

The difference between grandof and locationof is that, for example, if a key is put on a keyring, which is put in a box, which is put into a bag, and the bag is in a room called "beach", 'locationof key' would refer to the beach, but 'grandof key' would refer to the bag.

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 an attribute. For more information see the chapter on Attributes.

Test | Description
--- | ---
has | This tests if the object has the specified attribute set
hasnt | This tests if the object 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 beach locationof bucket
    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.

## The IFSTRING Command

An ifstring command is used to compare two strings of text. If the string of text is to contain any spaces, it must be enclosed in double quotes. An ifstring command must use 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 first string equals the second string
!= or <> | This tests if the first string doesn't equal the second string
contains | This tests if the first string contains the second string
!contains | This tests if the first string doesn't contain the second string

Below are some examples of the ifstring command being used.

    ifstring string_arg[0] contains "help"
    ifstring command[0] == "take"

## The IFEXECUTE Command

The ifexecute command works in a similar manner to the call command (see the chapter on Functions of more information.) If the function specified after the ifexecute command exists and does not return false, executing will continue from the line after the ifexecute command. If the function being called does not exist or returns false executing will continue from after the matching endif or else command.

This command is normally only used by library code to test if the game author has provided some specific associated function and to perform some sort of default action if not. For example:

    ifexecute "here.look"
       # ASSOCIATED FUNCTION PROVIDED, DO NOTHING
    else
       # NO ASSOCIATED FUNCTION, PERFORM DEFAULT
       execute "+look"
    endif

If the ifexecute command is true, in other words, a look function associated with the player's current location exists and does not return false, the block of code below it will be executed. As the associated look function has already performed any required action, this block contains only a comment. If this function did not exist or returned false the function +look would be executed.

## The ELSE Command

The code following an else command is executed only if the matching if command was false. If the matching if command was true, execution will continue from the line after the matching endif command. For example:

    if mulder has DEAD
       write "Clamminess on your lips.^"
    else
       write "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 "You are carrying too much to take "
                   write noun3{the} .^
                   set INDEX + 1
                endif
             else
                write noun3{The} " run" noun3{s} " through "
                write "your fingers.^"
                set INDEX + 1
       endall
    endloop

In the above code, the block of code after the first else command is executed if the matching if command above it:

    if noun3(mass) <= player(info)

is false. This line of code will not get executed, however, if the line:

    if noun3 hasnt LIQUID

is false as execution will have already jumped to after the second else command.

## The LOOP and ENDLOOP Commands

The loop command is used to iterate through all the objects (and locations) defined in the game. The loop command takes a single argument being the integer variable to use as a pointer to the current object during iteration. If a loop command is executed with no parameters, noun3 is used as the default variable. When the loop command is executed, iteration starts with the variable being set to 1 (the first object or location in the game file). When the matching endloop command is executed the variable is incremented to point to the next object or location and execution continues from the first line after the original loop command. The loop will end when the endloop command is executed with the iteration variable already pointing to the last object or location. When the loop ends, execution continues from the first line after the endloop command. For example, the following code will output the short description of each object and location as a list:

    {+print_objects
    loop
       write noun3{List} ^
    endloop
    }

As another example, this function will output all the children of the object passed as an argument to it, using the variable POINTER as the iteration variable:

    integer POINTER

    {+print_children
    write "Children of " arg[0]{the} ":^"
    loop POINTER
      if POINTER(parent) = arg[0]
         write "  " POINTER{the} ^
      endif
    endloop
    }

It is not legal to nest loops unless the inner loop is within a function that is called from within the outer loop. To return out of a loop early you can either return from the currently executing function or set the iteration variable to point to the last object or location manually. For example, to return out of a loop after the fifth object, use the constant objects like this:

    loop
     write noun3{The} ^
     if noun3 = 5
        # SET THE CONTAINER noun3 TO POINT TO THE
        # LAST OBJECT OR LOCATION SO THAT THE
        # LOOP WILL STOP ITERATING
        set noun3 = objects
     endif
    endloop

The index of the last object or location (which is also the number of object and locations in the game) is stored in the constant objects.

## The REPEAT and UNTIL Commands

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

| loop...endloop loops and repeat...until loops cannot be nested within a single function. It is legal, however, to place a second loop within a function that is called from within the first loop. For example:
    {+print_objects
    set INDEX = 10
    loop noun3
      write noun3{the} ^
      execute "+print_children"
    endloop
    }

    {+print_children
    loop noun4
      if noun3 grandof noun4
         write "   " noun4{the} ^
      endif
    endloop
    }
Note that it is legal, however, to nest a repeat loop inside an object loop or vice versa.

## The WHILE and ENDWHILE Commands

As a repeat loop will always happen at least once, if there is any chance that a loop should happen zero times, a while loop must be used. A while loop performs its test first, then executes the code following the expression if it evaluates to true. On reaching an endwhile command it will return to the matching while command and re-evaluate the expression. When the expression eventually evaluates to false, execution will continue from the line after the endwhile command. For example, the following function will output the value of the passed arguments:

    {+arg_values
    set INDEX = 0
    while INDEX != @arg
      write "Argument" INDEX " = " arg[INDEX] ".^"
      set INDEX + 1
    endwhile
    }

## The RETURN Command

return or return true
The command return will stop execution of the current function and return to the next line after the corresponding execute command in the calling function. If the function containing the return command was called internally by the JACL interpreter, processing will continue, eventually returning to the command prompt for the player's next move.

return false
The command return false will stop execution of the current function just like a return command. 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 return false:

    object bond: james bond

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

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 return false command executed, the interpreter will behave as though the local function containing the return 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.

Back to Contents