4.2 LOGIC syntax
by Peter Kelly
Last updated: 27 January 1998
This is the "official" logic syntax that has been
decided on. It is not the same syntax as Sierra used. All logic
decoders and compilers should comply with this syntax, so that
programmers can be sure that code they produce can be used
properly with any program.
Action Commands
Normal action commands are specified by the command name
followed by brackets which contain the arguments, separated by
commas. A semicolon is placed after the brackets. The brackets
are required even if there are no arguments. The arguments given
must have the correct prefix for that type of argument as
explained later in this document (this is to make sure the
programmer does not use a var, for example, when they think they
are using a flag).
assign.v(v50,0);
program.control();
Multiple commands may be placed on the one line:
reset(f6); reset(f7);
Substitutions for the following action commands are available:
| increment(v30); |
v30++; |
| decrement(v30); |
v30--; |
| assignn(v30,4); |
v30 = 4; |
| assignv(v30,v32); |
v30 = v32; |
| addn(v30,4); |
v30 = v30 + 4; |
or |
v30 += 4; |
| addv(v30,v32); |
v30 = v30 + v32; |
or |
v30 += v32; |
| subn(v30,4); |
v30 = v30 - 4; |
or |
v30 -= 4; |
| subv(v30,v32); |
v30 = v30 - v32; |
or |
v30 -= v32; |
| mul.n(v30,4); |
v30 = v30 * 4; |
or |
v30 *= 4; |
| mul.v(v30,v32); |
v30 = v30 * v32; |
or |
v30 *= v32; |
| div.n(v30,4); |
v30 = v30 / 4; |
or |
v30 /= 4; |
| div.v(v30,v32); |
v30 = v30 / v32; |
or |
v30 /= v32; |
| lindirectn(v30,4); |
*v30 = 4; |
| lindirectv(v30,v32); |
*v30 = v32; |
| rindirect(v30,v32); |
v30 = *v32; |
IF structures and test commands
An if structure looks like this:
if (<test commands>) {
<action commands>
}
or like this :
if (<test commands>) {
<action commands>
}
else {
<more action commands>
}
Carriage returns are not necessary:
if (<test commands>) { <action Commands> } else { <more action commands> }
Test commands are coded like action commands except there is
no semicolon. They are separated by && or || for AND or
OR.
if (isset(f5) &&
greatern(v5,6)) { ......
Again, carriage returns are not necessary within the if
statement:
if (lessn(v5,6) && (greatern(v5,2)) { .......
if (isset(f90) && equalv(v32,v34)
&& greatern(v34,20)) { .......
A ! placed in front of a command signifies a NOT.
if (!isset(f7)) {
......
Boolean expressions are not necessarily simplified so they
must follow the rules set down by the file format. If test
commands are to be ORred together, they must be placed in
brackets.
if ((isset(f1) || isset(f2)) {
......
if (isset(f1) && (isset(f2) || isset(f3))) {
......
if (isset(1) || (isset(2) && isset(3))) { is NOT legal
Depending on the compiler, simplification of boolean
expressions may be supported, so the above may not apply in all
cases (although if these are rules are followed then the logic
will work with all compilers).
Substitutions for the following test commands are available:
| equaln(v30,4) |
v30 == 4 |
| equalv(v30,v32) |
v30 == v32 |
| greatern(v30,4) |
v30 > 4 |
| greaterv(v30,v32) |
v30 > v32 |
| lessn(v30,4) |
v30 < 4 |
| lessv(v30,v32) |
v30 < v32 |
| !equaln(v30,4) |
v30 != 4 |
| !equalv(v30,v32) |
v30 != v32 |
| !greatern(v30,4) |
v30 <= 4 |
| !greaterv(v30,v32) |
v30 <= v32 |
| !lessn(v30,4) |
v30 >= 4 |
| !lessv(v30,v32) |
v30 >= v32 |
Also, flags can be tested for by just using the name of the
flag:
if (f6) { .....
if (v7 > 0 && !f6) { .....
Argument types
There are 9 different types of arguments that commands use:
| Number |
(no prefix) |
| Var |
(prefix "v") |
| Flag |
(prefix "f") |
| Message |
(prefix "m") |
| Object |
(prefix "o") |
| Inventory Item |
(prefix "i") |
| String |
(prefix "s") |
| Word |
(prefix "w") |
| Controller |
(prefix "c") |
The said test command uses it's own special arguments which
will be described later.
Each of these types of arguments is given by the prefix and
then a number from 0-255, e.g. v5, f6, m27, o2.
The word type is words that the player has typed in, not words
that are stored in the WORDS.TOK file. Strings are the temporary
string variables stored in memory, not to be confused with
messages (that are stored in the logic resources). Controllers
are menu items and keys.
Compilers can enforce type checking, so that the programmer
must use the correct prefix for an argument so that they know
they are using the right type. Decoders should display arguments
with the right type.
move.obj(so4,80,120,2,f66);
if (obj.in.box(so2,30,60,120,40)) { .....
A complete list of the commands and their argument types is
available as part of AGI Specs.
Messages and inventory items may be given in either numerical
text format:
print("He's not here.");
print(m12);
if (has("Jetpack")) { .....
if (has(io9)) { .....
Messages can also be split over multiple lines:
print("This message is split "
"over multiple lines.");
Quote marks must be used around messages and object names.
This is important because some messages or object names may
contain brackets or commas, which could confuse the compiler.
This is also the case for the said command which will be
described shortly.
if (has("Buckazoid(s)")) { ..... // no ambiguity here about where
// the argument ends
If quote marks are part of the message or inventory object, a
\ should be placed in front of these. To use a \, \\ should be
used. \n can also be used for a new line.
The said test command uses different parameters to all the
other commands. Where as the others use 8 bit arguments (0-255),
said takes 16 bit arguments (0-65535). Also, the number of
arguments in a said command can vary. The numbers given in the
arguments are the word group numbers from the WORDS.TOK file.
if (said(4,80)) { .....
Words can also be given in place of the numbers:
if (said("look")) { .....
if (said("open","door")) { .....
Quote marks must also be used around the words.
Labels and the goto command
Labels are given like this:
Label1:
The label name can contain letters, numbers, and the
characters "_" and ".". No spaces are
allowed.
The goto command takes one parameter, the name of a label.
goto(Label1);
Comments
There are three ways that comments can be used.
// - rest of line is ignored
[ - rest of line is ignored
/* Text between these are ignored */
The /*...*/ can be nested:
/* comment start
print("Hello"); // won't be run
/* // a new comment start (will be ignored!)
v32 = 15; // won't be run
*/ // uncomments the most inner comment
print("Hey!"); // won't be run, still inside comments
*/ // uncomments
Defines
To give vars, flags etc. proper names the #define command is
used. The name of the define is given followed by the define
value:
#define ego o0
#define room_descr "This is a large hall with tall pillars down each side."
Then the define name can be used in place of the define value:
draw(ego);
print(room_descr);
Define names can only be used in arguments of commands
(including gotos and the v0 == 3 type syntax), although
some compilers may allow you to use them anywhere.
Defines must be defined in the file before they are used.
The define name can contain letters, numbers, and the
characters "_" and ".". No spaces are
allowed.
Including files
You can include another file in your logic source code by
using the #include command:
#include "file.txt"
When the compiler encounters the above line, it will replace
it with the contents of file.txt.
It is a good idea to have all the defines that you need for
multiple logics in an #include file, so if you need to change the
define value you only have to do it once (although you will need
to recompile all logics that use that define).
More on messages
In some cases you may want to assign a specific number to a
message so you can refer to it in other places. This is done by
using the #message command, followed by the number of the message
then the message itself:
#message 4 "You can't do that now."
Then you can give the message number as the parameter in
commands:
print(m4);
Or embed the message in commands as normal and the number you
assigned to it before will be used:
print("You can't do that now.");
#message can be used anywhere in the file, so you do not have
to set the message before you use it.
The return command
The return command is just a normal action command (number 0),
with no arguments. This must be the last command in every logic.