Nibbler has some very intricate programming and AI. For this reason, I have developed this language to help distribute the weight of the programming. The host program, NIB.EXE, interprets the opcodes from a compiled file, NIB.AI. The compiler is NCOMPILE.EXE. This program compiles a target file NIB.CON.
This language documentation is quite long--I have divided it into different sections to facilitate comprehension. For a complete list of commands and functions, click here.
NIB-OOL is object-oriented and relies on one or more
text files to direct how the game heuristics work out. There are five
major parts to the compilation sequence: keyword reference, macro
definitions, control structures, global variables, and object scripts.
Keyword reference: #kident "file" directs the compiler to use
the appropriate text file as a reference for all commands and
functions. The compiler is smart enough to know how many keywords
exist in each category from the line breaks. The #kident directive
must be the first line in the file, no exceptions.
Macro Definitions: All #define directives are synonymous with
those appearing in C/C++, with the exception that they may define
numbers only--no macro functions. Also, defines can only appear
between the beginning of the file and the start of the control
structures. A define anywhere else in the file will cause an error.
Control Structures: Special controls appear here. The number
and type of messages that can be sent or received by objects, the
number and type of general variables to expect when using generic
pointers, and the symbols used for addressing flags are all defined
here.
Global variables: Global variables are declared here with
virtually the same conventions as C/C++. All objects can access and
change global variables. Some global variables are jointly used by
NIB.EXE and the compiled script to control the flow of the game.
The order of the global variables must be: single values, arrays
with one subscript, and arrays with two subscripts.
Single variables may be given initial values, but all arrays are
initialized independently by NIB.EXE at the start of the sequence.
For simplicity, I have made NCOMPILE.EXE process all "grids," or
two-dimensional arrays, as having dimensions of [48][80].
Object Scripts: Object scripts are in the form of
object name
variable declarations;
{
statement1;
statement2;
statement3;
}
Objects have unique names and unique variable declarations. This is
where NIB-OOL is powerful: the designer can make each object type have
as many local variables to work with as desired. All objects are
autonomous with user-defined parameters, using only NIB.EXE to
interpret the command-based opcodes. Variable declarations are, once
again, exactly the same as local variable declarations in a C/C++
function. The only exception is that the objects cannot declare
personalized arrays--only singular values are permitted as local
variables. After all objects have been declared, the sequence is
followed by "//END".
The compiler is similar to C language, but scaled down
to provide more unit-based operations for objects to execute.
Constants: a constant can be a number of things. It can be
a number, positive or negative. It can be a defined macro that
represents a number. It can also be an object type (one of the
declared object names), which will evaluate as type char.
The size of the constant is defined as follows:
char: -128 to 127 (one byte)
int: -32768 to 32767 (two bytes)
long: -32768.99999 to 32767.99999 (four bytes)
Note that the long type is not exactly a long integer. It has the
size of a long integer, four bytes, but it is actually a
fixed-point decimal number. The low-order 16 bits are the
fractional part, and the high-order 16 bits are the whole part. In
assignments and conversions of type, integers assigned to long values
will throw away the fractional part, and long values assigned to
integers will assign the fractional part as zero.
All time-delay intervals are given in frames, or 1/60ths of
a second. A value of 20 for such a delay counts as 20 frames, or
20/60 = 1/3 of a second. To get a value such as 20 seconds, multiply
by 60: 60*20 = 1200 frames = 20 seconds.
For macro definitions that need to be incremented by one, a quick
suffix accomplishes this task. The most common case is in the turnru
and flipru functions. For example, flipru(WORM, WORM+1, dir); is
faster than assigning another value to WORM, increasing by one, and
then calling flipru(WORM, otherval, dir). This convention relies on
the assumption that sprites that operate on a turning basis are
stored in the left-then-up fashion. In most circumstances, this is
the case.
Other conventions: In all instances, scope is evaluated in this
order: constant, function, local, global. All identifiers are evaluated in left-to-right order.
Statements in this language are primarily a series of unit operations.
There is no implemented system of parenthesis and multiple arithmetic
operations on any one line. Functions and commands often make use of
parenthesis for multiple arguments, but the exact usage of parenthesis
depends on the command itself. See the command and function reference
for more information.
Remarks can be inserted into almost any part of the file. They should
be of the form /*Remark*/. The format //Remark is not
implemented.
When addressing a flags variable, the usage of +flag and -flag have
multiple meanings that depend on the context. When assigning flags,
the sequence +B-P+S will set the block flag, clear the palette flag,
and set the solid flag. Contrarily, when flags are used in evaluation
(such as conditional expressions), the sequence +B+P will act as an
"AND" mask that returns true for each flag if the corresponding flag
is set. The sequence -B-P in evaluation will apply "NOT" to the flags
variable, then take the AND mask of the corresponding flags.
The two "auto-constructor-initialized" local variables, type matrix
and type follows, are standard 2-byte integers and are the only local
variables that cannot be overriden by using specific initial values in
a place assignment.