Difference between revisions of "AGI Logic Language Reference"

From AGI Wiki
Jump to navigationJump to search
(Much of the information on the commands is either incomplete or incorrect here; up to date and detailed infomration on commands are available as separate wiki pages.)
 
Line 299: Line 299:
  
 
The return command is just a normal action command (number 0), with no arguments. This must be the last command in every script.
 
The return command is just a normal action command (number 0), with no arguments. This must be the last command in every script.
 
<<span id="Sample"></span>
 
  
 
==Discussion of sample LOGIC code from KQ4==
 
==Discussion of sample LOGIC code from KQ4==

Latest revision as of 16:12, 27 March 2019

The script language

Source code conventions

This section lists conventions that will be adopted in the AGI script language description in this document. They may differ from Sierra's original syntax, but the resulting bytecode is strictly the same.

Commands and operators

Normal 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).

<syntaxhighlight lang="cpp">

   assign.v(v50,0);
   program.control();

</syntaxhighlight>

Multiple commands may be placed on the one line:

<syntaxhighlight lang="cpp">

  reset(f6); reset(f7);

</syntaxhighlight>

Substitutions for the following action commands are available:

<syntaxhighlight lang="cpp">

   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;

</syntaxhighlight>

Conditionals and tests

An if structure looks like this:

<syntaxhighlight lang="cpp">

   if (<test commands>) {
       <action commands>
   }

</syntaxhighlight>

or like this :

<syntaxhighlight lang="cpp">

   if (<test commands>) {
       <action commands>
   }
   else {
     <more action commands>
   }

</syntaxhighlight>

Multiple commands can be placed in a single line:

<syntaxhighlight lang="cpp">

   if (<test commands>) { <action Commands> } else { <more action commands> }

</syntaxhighlight>

Test commands are coded like action commands except there is no semicolon. They are separated by && or || for AND or OR.

<syntaxhighlight lang="cpp">

   if (isset(f5) &&
       greatern(v5,6)) { ......

</syntaxhighlight>

Multiple tests can be placed in a single line within the if statement:

<syntaxhighlight lang="cpp">

   if (lessn(v5,6) && (greatern(v5,2)) { .......
   if (isset(f90) && equalv(v32,v34)
       && greatern(v34,20)) { .......

</syntaxhighlight>

A ! placed in front of a command negates its result:

<syntaxhighlight lang="cpp">

   if (!isset(f7)) {
     ......

</syntaxhighlight>

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.

<syntaxhighlight lang="cpp">

   if ((isset(f1) || isset(f2)) {
     ......
   if (isset(f1) && (isset(f2) || isset(f3))) {
     ......
   if (isset(1) || (isset(2) && isset(3))) {    // is NOT legal

</syntaxhighlight>

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).

The following test commands and operations are equivalent:

<syntaxhighlight lang="cpp">

   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

</syntaxhighlight>

Also, flags can be tested for by just using the name of the flag:

<syntaxhighlight lang="cpp">

   if (f6) { .....
   if (v7 > 0 && !f6) { .....

</syntaxhighlight>

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 its 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.

<syntaxhighlight lang="cpp">

   move.obj(so4,80,120,2,f66);
   if (obj.in.box(so2,30,60,120,40)) { .....

</syntaxhighlight>

A complete list of the commands and their argument types is available in Command list and argument types table.

Messages and inventory items may be given in either numerical text format:

<syntaxhighlight lang="cpp">

   print("He's not here.");
   print(m12);
   if (has("Jetpack")) { .....
   if (has(io9)) { .....

</syntaxhighlight>

Messages can also be split over multiple lines:

<syntaxhighlight lang="cpp">

   print("This message is split "
         "over multiple lines.");

</syntaxhighlight>

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.

<syntaxhighlight lang="cpp">

   if (has("Buckazoid(s)")) { .....        // no ambiguity here about where
                                           // the argument ends

</syntaxhighlight>

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.

<syntaxhighlight lang="cpp">

   if (said(4,80)) { .....

</syntaxhighlight>

Words can also be given in place of the numbers:

<syntaxhighlight lang="cpp">

   if (said("look")) { .....
   if (said("open","door")) { .....

</syntaxhighlight>

Quote marks must also be used around the words.

Labels and the goto command

Labels are given like this:

<syntaxhighlight lang="cpp">

   Label1:

</syntaxhighlight>

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.

<syntaxhighlight lang="cpp">

   goto(Label1);

</syntaxhighlight>

Comments

There are three ways that comments can be used.

<syntaxhighlight lang="cpp">

   // Rest of line is ignored
   [  Rest of line is ignored
   /* Text between these are ignored */

</syntaxhighlight>

The /*...*/ can be nested:

<syntaxhighlight lang="cpp">

   /* 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

</syntaxhighlight>

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:

<syntaxhighlight lang="cpp">

   #define ego o0
   #define room_descr "This is a large hall with tall pillars down each side."

</syntaxhighlight>

Then the define name can be used in place of the define value:

<syntaxhighlight lang="cpp">

   draw(ego);
   print(room_descr);

</syntaxhighlight>

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:

<syntaxhighlight lang="cpp">

   #include "file.txt"

</syntaxhighlight>

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:

<syntaxhighlight lang="cpp">

   #message 4 "You can't do that now."

</syntaxhighlight>

Then you can give the message number as the parameter in commands:

<syntaxhighlight lang="cpp">

   print(m4);

</syntaxhighlight>

Or embed the message in commands as normal and the number you assigned to it before will be used:

<syntaxhighlight lang="cpp">

   print("You can't do that now.");

</syntaxhighlight>

  1. 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 script.

Discussion of sample LOGIC code from KQ4

(Last updated: 31 August 1997).

Some of you may know that "The Official Book of King's Quest" included three small fragments of AGI code for room 7 in the AGI version of KQ4. These fragments are given below along with the same fragments taken from the game itself. There are a few differences which is to be expected but generally the code is very similar. These examples show how the coder wrote the code and what it now looks like in the final product. I've included a few comments where some interesting observations can be seen. Animating the smoke

From the book:

<syntaxhighlight lang="cpp"> animate.obj(smoke); ignore.horizon(smoke); set.view(smoke, v.fish.cabin); set.loop(smoke, 1); ignore.blocks(smoke); position(smoke, 95, 16); work = 3; step.time(smoke, work); cycle.time(smoke, work); draw(smoke); </syntaxhighlight>

From the game:

<syntaxhighlight lang="cpp"> animate.obj(7); ignore.horizon(7); set.view(7, 114); set.loop(7, 1); ignore.objs(7); // These two lines have been added. set.priority(7, 5); // ignore.blocks(7); position(7, 95, 16); assignn(152, 3); // Equivalent to 'work = 3;' step.time(7, 152); cycle.time(7, 152); draw(7); </syntaxhighlight>

Opening the door

From the book:

<syntaxhighlight lang="cpp"> if (said( open, door)) { // must be close enough

 if (posn( ego, 86, 120, 106, 133)) {
   if (!night) {
     if ( door.open) {
       print("The door is already open. . .");
     }
     else {
       set( game.control);
       set.priority( ego, 11);
       start.update( door);
       end.of.loop( door, door.done);
     }
   }
   else {
     print("You can't -- it's locked...");
   }
 }
 else {
   set( notCloseEnough);
 }

} </syntaxhighlight>

From the game:

<syntaxhighlight lang="cpp"> if (said(OPEN, DOOR||DOORS||DOORWAY||DOORWAYS)) {

 if (posn(0, 86, 120, 106, 133)) {
   if (!isset(38)) {
     if (isset(231)) {
       print("The door is already open.");
     }
     else {
       set(36);
       prevent.input();
       start.update(5);
       assignn(152, 3);
       cycle.time(5, 152);
       end.of.loop(5, 232);
       sound(70, 154);
     }
   }
   else {
     print("You can't.  It's locked and you
            don't have the key.");
   }
 }
 else {
   set(113);
 }

} </syntaxhighlight>

Unlocking the door

From the book:

<syntaxhighlight lang="cpp"> if (said( unlock, door)) { // must be close enough

 if (posn( ego, 86, 120, 106, 133)) {
   if (!night) {
     print("The door is already unlocked. . .");
   }
   else {
     printf("You can't, it's locked. . .");
   }
 }
 else {
   set( notCloseEnough);
 }

} </syntaxhighlight>

From the game:

<syntaxhighlight lang="cpp"> if (said(UNLATCH||UNLOCK, DOOR||DOORS||DOORWAY||DOORWAYS)) {

 if (posn(0, 86, 120, 106, 133)) {
   if (!isset(38)) {
     print("The door is already unlocked.");
   }
   else {
     print("You can't.  It's locked and you
            don't have the key.");
   }
 }
 else {
   set(113);
 }

} </syntaxhighlight>

Knocking on the door

From the book:

<syntaxhighlight lang="cpp"> if ((said( knock, at, door) || said( knock) ||

    said( knock, on, door) || said( knock, door)) {
 if (posn( ego, 86, 120, 106, 133)) {
   if (!night) {
     print("You knock on the door. . .
            a woman says. . .");
   }
   else {
     printf("You knock on the. . .
             a man calls out. . .");
   }
 }
 else {
   set( notCloseEnough);
 }

} </syntaxhighlight>

From the game:

<syntaxhighlight lang="cpp"> if (said(BANG||KNOCK||RAP||TAP) ||

   said(BANG||KNOCK||RAP||TAP, DOOR||DOORS||DOORWAY||DOORWAYS)) {
 if (posn(0, 86, 120, 106, 133)) {
   if (!isset(38)) {
     print("You assertively knock on the shanty
            door.  A woman's voice answers, "Jest come on in!"");
   }
   else {
     print("You assertively knock on the shanty
            door.  From inside, a man's voice calls out, "D'ya know
            what TIME it is?!  GO AWAY!!"");
   }
 }
 else {
   set(113);
 }

} </syntaxhighlight>

Fall rocks

File:Agispec-kq4 fall rocks.png
King's Quest IV fall rocks

From the book:

<syntaxhighlight lang="cpp"> if (hit.special) {

 if ((rf2 || rf3 || rf4)) {
   reset(hit.special);
   get.posn(ego, priorx, priory);
   position.f(dude, priorx, priory);
   ignore.blocks(dude);
   set( game.control);
   set.view( dude, v.ego.land);
   if ((ego.dir == 3 || ego.dir == 4)) {
     set.loop( dude, 2);
   }
   else {
     set.loop( dude, 3);
   }
   fix.loop(dude);
   work = 3;
   step.size( dude, work);
   work = 3;
   cycle.time( dude, work);
   start.cycling(dude);
   erase(ego);
   draw(dude);
   if (rf3) {
     work6 = 0;
     move.obj.f( dude, tempx, tempy, work6, fall.done);
   }
   if (rf4) {
     work6 = 0;
     move.obj.f( dude, tempx, tempy, work6, fall.done);
   }
   if (rf2) {
     if (priory < 125) {
       set.priority( dude, 9);
       tempy=132;
       work6=0;
       move.obj.f( dude, ego,x, tempy, work6, fall.done);
     }
     else {
       set.priority(dude, 15);
       tempy = 156;
       work6 = 0;
       move.obj.f( dude, ego.x, tempy, work6, fall.done);
     }
   }
 }

} </syntaxhighlight>

From the game:

<syntaxhighlight lang="cpp"> if (isset(3)) { [ hit.special

 if (isset(222) || isset(223) || isset(224)) {     // rf2, rf3, rf4
   reset(3);
   sound(51, 154);
   get.posn(0, 134, 135);
   position.v(12, 134, 135);
   ignore.blocks(12);
   set(36);
   prevent.input();
   set.view(12, 11);
   if (equaln(6, 3) || equaln(6, 4)) {
     set.loop(12, 2);
   }
   else {
     set.loop(12, 3);
   }
   fix.loop(12);
   assignn(152, 3);
   step.size(12, 152);
   assignn(152, 3);
   cycle.time(12, 152);
   start.cycling(12);
   erase(0);
   draw(12);
   if (isset(223)) {
     assignn(158, 0);
     move.obj.v(12, 107, 108, 158, 226);
   }
   if (isset(224)) {
     assignn(158, 0);
     move.obj.v(12, 107, 108, 158, 226);
     set.priority(12, 14);
   }
   if (isset(222)) {
     if (lessn(135, 125)) {
       set.priority(12, 9);
       assignn(108, 132);
       assignn(158, 0);
       move.obj.v(12, 33, 108, 158, 226);
     }
     else {
       set.priority(12, 14);
       assignn(108, 158);
       assignn(158, 0);
       move.obj.v(12, 33, 108, 158, 226);
     }
   }
 }

} </syntaxhighlight>