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 terminatenormally with an implicit break 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 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, an integer constant, a variable, 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 or if 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 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 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.
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. 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 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.
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, execution 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
The loop command is used to iterate through all the objects defined in the game. The loop command takes a single argument being the container 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 object pointer. When its matching endloop command is executed the pointer 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>"
}
This function will will output all the children of the object passed as an argument, using the variable POINTER to store each iteration:
variable POINTER
{+print_children
write "Children of " arg[0]{the} ":^"
loop POINTER
if POINTER(parent) = arg[0]
write " " POINTER{the} ^
endif
endloop
}
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 and object loop or vice versa. |
As a repeat loop will always happen at least once, if there is any chance that a loop should happen zero times, then a while loop is more appropriate. 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 "<p>Argument" INDEX " = " arg[INDEX] ".^"
set INDEX + 1
endwhile
}
break or break true
The command break 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 break command 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 just like a break 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 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 specified 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 arguments that are passed when the function is executed. For more information, see the chapter on Functions.
The following is an example of a conditional break that can be found at the top of many functions in the verbs.library file:
{+take
break +reach noun1
...
}
{+reach
if noun4 has OUT_OF_REACH
write <p> noun4{The} " " noun4{is} " out of reach."
set TIME = false
break true
endif
break false
}