Provided by:
cuyo_1.8.6-1_i386 
NAME
Cual - Cuyo Animation Language
Cual is the main language used to describe the animations in cuyo. It’s
the stuff between the << >> brackets in the level description files
(xxx.ld).
This man page is not a complete description of how to write levels for
cuyo; it only describes Cual. And it’s still under construction. See
the file "example.ld" to get an idea of how the rest of the level
description works. There’s also a bit of example Cual code in
"example.ld". And of course, all the existing levels are examples.
Note that Cual is probably still very buggy. It has almost not been
tested. (The existing levels work, but that’s all.) So if strange
things happen and you’re sure it’s not your fault, tell me
(cuyo@karimmi.de).
HOW IT WORKS
Each blob has its own (main) Cual procedure which does the drawing and
the animation stuff. The procedure only depends on the kind of the
blob, that is, it is the same for blobs of the same kind. However each
blob has its own instance of the variables.
In every game step, the procedure of each of the blobs is called once.
(There are 10 game steps per second.) It has to draw the blob each
time, even if nothing has changed. (However, there’s an internal
routine in cuyo which checks if the same is drawn as in the last step
and which then supresses the drawing.)
There may be other procedures associated to a kind of blob, which are
executed at special events, for example when a falling blob lands. In
contrast to the main procedure, these event handlers are not allowed to
draw anything. See section "event handlers" for a list of the existing
events.
The name of the main procedure of a blob (the one which draws the blob)
is the name of the kind of the blob. Normally, that name is the word
listed after pics= entry in the .ld file; but if that "word" contains a
dot, only the part before the dot makes up the name. (E. g. with
pics=redblob.xpm,greenblob.xpm, the names are "redblob" and
"greenblob".)
The name of an event handler procedure is the name of the kind,
followed by a dot, followed by the event name. (E.g. "redblob.land" for
the landing event of the redblob from above.)
[Explain the default procedures.]
DEFINITIONS
Inside << >>, variable and procedure definitions are expected.
procname = code ;
Defines a "procedure". The next section describes how code looks
like.
Example:
redblob = {
schema16; 0*;
1; A,B,C; *;
};
var varname1 = def1, varname2 = def2, ... ;
Defines variables with default values. The default value can be
omitted (together with the "=" before it). Then, the default
value is zero. See section about variables and constants about
the meaning of the default value.
CODE
A code fragment can be one of the following:
{ code; code; ...}
Executes one command after the other.
code, code, ...
This is useful for simple animations. Executes exactly one of
the commands: In the n-th call, the n-th command is executed.
After the last command, the first one is executed again.
However, if one of the commands is "busy" (see section about
busieness), this one will be executed until it stops being busy,
and only after that, the next command will be executed.
procname
Executes the procedure procname, which has to be already
defined. The result is the same as if the code from procname
would have been inserted in that place.
&procname
Executes the procedure procname; however, every instance of such
a procname is the same. This concerns busieness and the state of
an animation sequence. (See sections BUSIENESS and AMPERSAND-
CALL.)
busy Does nothing except being busy. (See section BUSIENESS.)
varname = expr
Sets the variable. See section "VARIABLES AND CONSTANTS" for
details.
The same with +=, -=, *=, /=, %=
Does what you would expect.
[ varname = expr ] code
Sets the variable varname to expr, executes code and then resets
the variable to the old value.
number A shortcut for "file = number".
letter A shortcut for "pos = number", where different letters mean
different numbers: A: 0, B: 1, ..., Z: 25, a: 26, ..., z: 51
* Draw the icon specified by the variables kind, file and pos.
May also draw only a part of the icon, if specified by the
variable qu (see section about variables and constants).
*@(x, y)
Like *, but draws the icon at relative coordinates x, y.
Relative drawing is always performed after all other drawing.
(See section about variables and constants for more details
about @.)
if expr if-arrow if-code ;
if expr if-arrow if-code else [else-arrow] else-code ;
The arrows can be either "->" or "=>". If you use "->"
arrows, it does exactly what you would expect.
If the if-arrow is "=>", then once the expression gets
true, the if-code will be executed every subsequent time
(without testing the condition), as long as it is "busy".
More details see section BUSIENESS.
If the else-arrow is "=>", then once the expression gets
false, the else-code will be executed every subsequent
time as long as it is busy.
If the else-arrow is omitted, "=>" is used. (But this
might change in the future.)
switch {
expr1 arrow1 code1 ;
expr2 arrow2 code2 ;
...
} The arrows can be either "->" or "=>". Does the same as:
if expr1 arrow1 code1
else if expr2 arrow2 code2
...
The last expr may be omitted to get an else part without
further condition.
bonus(expr)
The player gets expr bonus points.
message(String)
The string is displayed (blinking) on the screen. To be
used together with bonus(...).
Example:
bonus(50);
message("You get 50 bonus points");
explode
Makes the blob explode. For the next 8 steps or so, the
blob is still what it was before, but the explosion is
drawn over the graphics. After that, it’s changed to a
nothing-blob.
There’s a shortcut for drawing: You may omit the ";" between a
number, a letter and the "*".
EXPRESSIONS
The only data type in cual is int. Bools are represented by 0
and 1, like in C. (And any number other than 0 is interpreted as
true, if a boolean is expected.)
Of course, variables, constants and numbers are expressions, and
you can use parentheses. There are the following operators:
The following operators do exacly the same as in C (listed here
by precedence):
|| Boolean or
&& Boolean and
==, !=, <, >, <=, >= Comparision
! Boolean not
+, - Add, substract
*, /, % Multiply, divide, modulo
[Insert precedence of special operators.]
The following special operators exist:
expr == number1 .. number2
Is true, if expr lies between the two numbers. You may
also omit one of the numbers. (Then, it does the same as
<= resp. >=.) Note that this operator might change in the
future. (I plan to make something like "expr in set" in
pascal.)
number1 : number2
Is true (that is, 1) with probability number1/number2
neighbour_pattern
neighbour_pattern is a sequence of six or eight
characters 0, 1 and ?. It is true if the sequence fits
to the neighbour sequence of the blob. The neighbour
sequence is a string of "0"s and "1"s with a "1" for each
neighbour of the same kind, starting above and going
clockwise. This way, you get a string of "0"s an "1"s
(six or eight, depending on wether this level is in hex
mode).
Example: 1???0??? is true iff the blob above this blob is
of the same kind and the blob below it is of different
kind.
If some blob changes its kind during a step, the
expression will still test the neighbours as they were at
the beginning of the step. (See the section about
variables and constants for details.)
The following functions exist:
rnd(expr)
Returns a random value between 0 and expr-1
VARIABLES AND CONSTANTS
The following kinds of variables and constants exist:
- User defined variables (see section "Definitions"). At the
start of the level (or at the creation of the blob) the value
is the default value you provided.
- System variables. These variables are always defined and have
special meanings, e.g. file and pos. Some of them are read-
only.
- Constants. Some of them depend on properties of the level,
some are really constant.
Of each variable, there’s one instance in each blob. Normally,
you access the instance in your own blob, but with the following
syntax, you can access variables of other blops:
varname@(dx, dy)
dx and dy are relative coordinates. This can be done for both,
reading and writing variables. It also works for system
variables (but not for constants). Be aware that dx and dy
can’t be expressions; they have to be fixed numbers.
In hex mode levels, for odd dx, dy has to be a "half integer",
that is a number ending in ".5". This is the only place in Cual
where non-integers appear.
There’s also one global instance of each variable. That one can
be acessed by
variable@
(without coordinates). See section "The Global Blob" for
details.
Accessing foreign variables is not as easy as it might look at
first glance; it might easily introduce a dependence of the
internal order of execution of the blob codes. For this reason,
- reading variables with @ always returns the value of the
variable it had at the beginning of the current step, that is,
before any of the blob codes has been executed.
- when writing variables with @, the write operation will only
be executed at the end of the current step. (The write
operations are stored in a kind of queue.)
This is also true if a blob accesses its own variables with
@(0,0).
The operators +=, -=, etc. are also performed in the future if
the left hand side is an @-variable. (To be more precice, the
right hand side is calculated instantanousely.)
For illustration, look at the following six statements:
1) X += 1
2) X@(0, 0) += 1
3) X = X + 1
4) X = X@(0, 0) + 1
5) X@(0, 0) = X + 1
6) X@(0, 0) = X@(0, 0) + 1
Only 1) and 3) do the same; they simply increment X by 1.
Statement 4) sets X to one more than it was at the beginning of
the step.
Statements 2), 5) and 6) cause the value of X to be changed in
the future (after the actual step): X is set to one more than:
2) the value of X just before the change (that is, X is
incremented in the future),
5) the actual value of X,
6) the value of X at the beginning of the step.
Some more details
- Whenever you try to access a variable at a location which
doesn’t exist, you will get the default value.
- Changing a variable which doesn’t exist does nothing (and does
not result in an error).
The system variables
file Specifies the file number from which to take the icon
that is drawn by "*". This variable is reset to 0 before
the drawing procedure is executed.
pos Specifies the position in the file of the icon that is
drawn by "*". This variable is reset to 0 before the
drawing procedure is executed.
kind The kind of the blob. There are constants for the
possible values of this variable. If you change the
kind, you should be aware of two things:
Expressions like "001???01" test the neighbour pattern at
the beginning of the actual step. So the change of the
variable kind will not be reflected.
In the actual step, the program to draw the blob has
already been invoked (in fact, the program which changed
this variable); so in this step, the blob will still look
like one of the old kind. However, if things are drawn
after the kind has been changed, icons from the new kind
are taken.
version
Only important for the "grass": Is set to the the number
specified by the letter in the startdist. (A = 0, ..., z
= 51).
qu Tells "*" which part of the icon to draw. It’s possible
to draw the whole icon, or only one of its quarters. If a
quarter is drawn, you may specify independently which of
the quarters to take and at which position to draw it.
Use the constants (see below). This variable is reset to
"draw all" before the drawing procedure is executed.
out1, out2
Set these Variables for debug output. The values will be
printed on top of the blob. There variable are reset to
"output nothing" before the drawing procedure is
executed. (In fact, "output nothing" is one special big
value.)
inhibit
Set this variable to a sum of the constants DIR_...; this
will inhibit that this blob connects into the given
directions. This is not for the graphics but for the
calculation of the connected components and the
explosions.
goalblob
Set goalblob to 1 if this blob should act like grass: You
will have to get rid of it to win the level and making
this blob explode will give more points. Note that grass
does not need goalblob = 1 to act like grass.
The system read-only variables
turn Is 1 resp. 2 if the blob is falling and just being turned
by the user and 0 otherwise. (1 in the first turning
step, 2 in the second one.) Be aware that if the user
presses the turn key fast several times, some of these
steps may be omitted. (Use the turn event if you want to
be sure that a program block is executed once for every
turn.)
connect
Contains internal data. Will be removed. Probably.
size The size of the component of the blob. (That is, how many
blobs are connected.)
loc_x, loc_y
The absolute coordinates of the blob. (0,0) = top left
corner
loc_p The player of the blob (1 or 2)
falling
true, if the blob is falling. (Falling in the sense of
steered by the player. Grey blobs are not falling in that
sense.)
falling_fast
true, if the blob is falling fast, that is, the user
pressed the down key.
players
The number of players.
exploding
When the blob is exploding, the position in the explosion
animation (1 - 8); 0 else.
The Constants:
Constants for qu:
Q_ALL Value for qu, which means "draw the complete picture".
Q_TL, Q_TR, Q_BL, Q_BR
Values for qu. "TL" means draw top-left quarter, etc.
(See the "*" command in the Code section.)
Q_SRC_DST
SRC and DST may be TL, TR, BL, BR. Take quarter SRC and
draw it at position DST
DIR_XX To be used with the variable inhibit to prevent the blob
connecting in the given directions. XX can be U, D, L, R
(horizontal and vertical); UL, UR, DL, DR (diagonal);
UUL, UUR, DDL, DDR, LLU, LLD, RRU, RRD (knight); F, B
(3d)
Constants for kind and for change:
<name of kind of blob>
For each kind of blob, there’s one constant with the name
of that kind. Use it to check if a blob is of that kind
using "kind@(x,y) == aKind" or to change to that kind
using "change = aKind" These constants do also exist for
the grass kind, the grey kind and, if existent, the empty
kind.
nothing
Is the same as the constant for the empty kind. Is
provided, because sometimes, you don’t have an empty
kind, but you still need to test if a blob is empty.
outside
The value of kind if the coordinates are outside of the
game board.
BUSIENESS
(No, not Business ;-)
Busieness is a concept to make it easier to implement simple
animated sequences which are triggered by certain events. Each
code fragment has an internal state which tells if it is busy.
- Normal statements like assignments are never busy.
- A chain of commands separated by "," is busy as long as not
all of the commands have been executed.
- code1 ; code2 is busy as long as at least one of code1 and
code2 are busy.
Here’s an example of how to use busieness for an animation which
appears at random intervals:
switch {
1/100 => {B*, C*, D*, E*};
-> A*;
};
This code fragment normally draws the icon at position A (0).
But in each step, with a probability of 1/100, an animation
sequence consiting of icons B, C, D and E is started. With a
normal arrow ("->") after the "1/100", after the step in which B
has been drawn, the probability would be 99/100 that A is drawn
again. But with the double arrow, the switch statement won’t
switch back to A until the animation has terminated.
(Btw: It doesn’t matter if there’s a "->" or a "=>" before the
"A*"; A* isn’t busy anyway.)
THE GLOBAL BLOB
Apart from the normal blobs which you can see on screen, there’s
one global blob (for the whole game, not one for each player),
which, well, isn’t really a blob, but behaves a bit like it. It
has its own set of variables, and it can have a program which is
run once every step. To define such a global program, use
"global=code". However, the global variables do exist even if
you don’t define global code. See section "Variables" on how to
access them. Note that the global blob is always executed
before any of the normal blobs.
EVENT HANDLERS
The following events exist:
init Is called only once, when the blob gets into live, just
before the first time its main drawing routine is called.
turn Is called for falling blobs each time the user presses
the turn key.
land Is called when the steered blob lands.
changeside
Is called when a blob moves from one player to the other,
just after the blob has arrived at the new player.
THE LIFE OF A BLOB
Normal blobs come into life at the beginning of the game, or
they fall into life: either as colored blobs, steered by the
user, or as grey blobs. When a blob moves (by gravitiy or when
rows go from one player to another), it takes its variables with
it. When a blob explodes, it does not stop existing. Rather, it
transforms into an empty blob. That’s important for the
variables: The empty blob still has all the variables set to the
values they had before; only its kind is different. Empty blobs
are everywhere where there’s no other blob. (However, the
falling blobs steered by the user is in some sense "above"
everything else; there are empty blobs beneath them.)
The life of empty blobs is different from the one of normal
blobs. Empty blobs are not affected by gravity, and they often
start or stop existing. For example, when a single grey blob is
falling down, the empty blob below it stops existing when the
grey blob arrives and a new empty blob starts existing when the
grey blob moves on. There is only one situation in which empty
blobs move: When a row moves from one player to the other, and
everything moves up resp. down, the empty blobs move, too.
WHERE DO I PUT THE CUAL CODE?
Cual procedures and variables can be defined in different
sections of the .ld files:
- Outside of everything; that code is accessible from every
level coming after that definition.
- In the section of a level.
- In the section of a kind.
This basically does what you expect. However, there’s one thing
you might want to know: Even if you define a variable inside a
kind, every blob in that level will have that variable. The only
effect of defining the variable in the section of a kind is that
this kind is the only one which can access it.
AMPERSAND-CALL
To explain a bit what calling a procecure with an & means, here
two examples:
Example 1:
<<
myblob = {
...
switch {
myvar -> { 0A*; 1; A,B,C,D; *; 2A*};
-> { 0B*; 1; A,B,C,D; *; 2B*};
};
};
>>
Example 2:
<<
anim = {1; A,B,C,D; *};
myblob = {
...
switch {
myvar -> { 0A*; &anim; 2A*};
-> { 0B*; &anim; 2B*};
};
};
>>
The difference between these examples is what happens when myvar
changes. In example 1, the animation "A, B, C, D" will restart
at the beginning (because the two animations are different
ones); in example 2, the "same" animation is used in both cases,
so the animation will simply continue. (Removing the ampersands
from example 2 will turn the behaviour to the one of example 1.)
SEE ALSO
cuyo(6)
BUGS
Probably a lot. The following are just a few known ones:
There are several problems with busieness and that stuff. There
are several situations in which Cual doesn’t behave in the way I
would like, and in other situations I don’t know how Cual should
behave.
This man-page is still incomplete. And there’s no man-page for
the part of the ld-files which is not Cual.
Sep 10, 2002 CUAL(6)