|
|
| |
CUAL(6) |
FreeBSD Games Manual |
CUAL(6) |
Cual - Cuyo Animation Language
Cual is the main language used to describe the animations in cuyo.
Strictly speaking it's the stuff between the << >> brackets in
the level description files (xxx.ld).
On the other hand this man page aims at being a complete
description of how to write levels for cuyo. But 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. So if strange things
happen and you're sure it's not your fault, tell me (cuyo@karimmi.de).
The level description is organized in sections. There is a global section and
every level has its own section, which is a subsection of the global section.
It is common practice to place each level in a separate file, which then
basically starts by opening its section and ends by closing it.
A section is defined by name =
{contents}. name is the name of the new section and
contents contains the definitions that pertain to that section. This
is a sequence of definitions of the form name = stuff.
Here stuff can be {contents} as above, or it can
be a single datum, or it can be a comma-separated list of data. Inside such
a list, datum * number can be used as a shorthand for
datum, ..., datum, i.e. a number-fold
repetition of datum. A datum can be an identifier, a string (enclosed
by '"'), a word, or a number. In place of a number
<expression> can be used, where expression
is an arbitrary expression made up from literal numbers, previously defined
numeric data, and the operators +, -, *, / and
%.
Definitions can also depend on versions. See section
VERSIONING below.
Apart from definitions, a section can also contain cual
definitions (see below). These have to be enclosed in << and
>>.
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 12.5 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.]
- name
- The name of the level. This appears in the list of levels as well as in
the level intro.
- description
- This is an optional further description of the level in its intro.
- author
- The name of the level author(s) for credit at the beginning of a
level.
- bgpic
- Background picture (file name). If too small, placed at bottom. Defaults
to none.
- toppic
- Appearance of the top border coming down (file name). Defaults to
none.
- explosionpic
- Appearance of the explosions (file name). Has a default.
- numexplode
- The size that a group of blobs has to reach in order to explode. This is
only the level-wide default. Each kind can override this. Whether the
group does explode is also controlled by behaviour. See section
VARIABLES AND CONSTANTS for details.
- toptime
- Time the border takes to come down, in number of game steps. Each game
step lasts 80ms. The default value is 50 (i.e. one pixel every four
seconds).
- topoverlap
- Placement of toppic relative to the actual border. More precisely,
number of pixels the lower border of the picture is below the actual
border. Defaults to the height of the picture.
- topstop
- When the border comes down at the end of the level, number of pixels it
should stop before the bottom. Set this to the same value as topoverlap if
you want your toppic to be comletely visible at the end. Defaults to
0.
- chaingrass
- Must be 0 or 1. If set to 1, chain reactions are necessary to kill the
grass. Defaults to 0. More precisely, chaingrass only controls the
default for behaviour for grass blobs. See section VARIABLES AND
CONSTANTS for details.
- mirror
- Must be 0 or 1. If set to 1, the level appears upside-down. Defaults to
0.
- randomfallpos
- Must be 0 or 1. If set to 1, the initial fall position is randomized
horizontally. Defaults to 0.
- neighbours
- Determines in which directions the blobs can connect to each other in
order to form groups. This is only the level-wide default. Each kind can
override this. See section VARIABLES AND CONSTANTS for values.
Defaults to neighbours_rect.
- hexflip
- In hex mode, determines whether the even or the odd columns are shifted
upwards. By default (hexflip = 0), the odd columns are shifted. 1 means:
shift even columns of player 1; 2 means: shift even columns of player 2; 3
means: shift even columns of both players.
- randomgreys
- The expected time between two randomly appearing greys in game steps
(80ms). Use -1 for none at all, which is the default.
- nogreyprob
- The probability that a grey does not appear. See greyprob and
colourprob in section KIND DATA for details. The default is
0.
- aiu_color, aiu_grass, aiu_grey, aiu_two_above,
aiu_monochromic_vertical, aiu_height
- Parameters for the AI player's utility function. Default respectively to
<10*(number of kinds)>, 20, 10, <aiu_color/2>,
<aiu_color>, and 10. See section THE AI UTILITY FUNCTION for
details.
(A colour is an RGB triple of numbers between 0 and 255.)
- bgcolor
- The background colour. Defaults to white.
- textcolor
- Colour of any text. This includes the beginning-of-level information,
message()s, and score. Defaults to a certain shade of dark grey.
- topcolor
- The colour of the top border comming down (where not determined by
toppic). Defaults to a certain shade of light grey.
- startdist
- Distribution of blobs at the beginning of the level. It is a list of
strings, the format of which is described in the section
STARTDIST.
- pics, greypic, startpic, emptypic
- Lists of kinds. These can be either file names referring to the picture to
be used, or declarations of kinds that have to be defined later on. The
different keywords (e.g. pics, emptypic) define different defaults. In
fact, only the first three may be real lists, emptypic is limited
to exactly one entry. In these lists, it is advisable to use *
whenever possible. Besides being shorter to write, it also speeds up
loading of the level and cuts down memory usage. This is because cuyo does
some initializations only once for each entry with multiplier.
The intentions of these lists are normal blob kinds resp. grey
blob kind resp. grass blob kind resp. nothing-blob. However, the only
differences between pics, greypic and startpic are the
default values for behaviour, colourprob, goalprob,
greyprob, versions and distkey (see there). All of
these can also be overridden individually. Also, the default drawing code is
different. (The default drawing code for startpic does not draw
connections.)
- kind
- Each kind can have its own section. See KIND DATA below for the
entries of that section.
- numexplode, neighbours
- Defining these data in the section of a kind overrides the level-wide
value for the kind. See section LEVEL DATA for a description of
these data.
- pics
- A list of file names of pictures to be used for this kind. The nth
entry can later be accessed in cual with file=n-1.
- colourprob
- The probability that this kind appears as one of the two steered falling
blobs. More precisely, this is a nonnegative integer weight. For
determining the actual probability, the value is divided by the sum of the
colourprobs of all kinds. This sum must be positive. The default is
1 for kinds declared with pics= and 0 for all other kinds. The
probability is also used for + in startdist. For more
details see section STARTDIST.
- goalprob
- This affects the semantics of * in startdist in the same
way, as colourprob does for +. The default is 1 for kinds
declared with startpic= and 0 for all other kinds.
- greyprob
- The probability that this kind appears as a grey blob. This is similar to
colourprob, but there is a difference: For greyprob,
nogreyprob is included in the sum, so that it might happen that no
blob appears at all. There is a notable difference between a positive
nogreyprob and a positive greyprob in kind nothing,
when several lines of grey blobs appear: In the latter case, empty blobs
appear in the wall of greys, making holes. In the former case, the wall is
made less high. Usually this is preferable. The default is 1 for kinds
declared with greypic= and 0 for all other kinds. The value also
affects the semantics of - in startdist. In this case,
nogreyprob is not included in the sum.
- versions
- At the creation of a blob, its version variable is initialized.
Usually, it is chosen at random from 0 to versions-1, but
startdist provides the possibility to specify it exactly. See
section STARTDIST for details. The default is 1.
- distkey
- An alphanumerical key, which is used in startdist to identify this
kind of blob. The default is A for kinds declared with
startpic and undefined for all other kinds. See section
STARTDIST for details.
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 [: reapply]],
varname2 [= def2 [: reapply]], ...
;
- Defines variables with default values. If no default is specified, zero is
used. See section VARIABLES AND CONSTANTS about the meaning of the
default value and the optional suffix : reapply.
- default varname1 = def1 [: reapply],
varname2 = def2 [: reapply], ...
;
- Changes the default for already defined variables. Again, the suffix :
reapply is optional. This is useful to give to a single kind a
different default for a variable than to the other kinds. Also, the
default of a system variable can be changed this way.
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 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 VARIABLES AND CONSTANTS).
- *@(position)
- Like *, but draws the icon at some other position. This drawing is
performed after all drawing by *. If *@ is used from several
blobs, the further order of drawing is not specified. It is guaranteed,
however, that at any given time this order is the same for all positions.
(See section VARIABLES AND CONSTANTS for more details about
@.)
- @(position)*
- Like *, but draws the icon at some other position. This drawing is
performed before all drawing by *. If @* is used from
several blobs, the further order of drawing is not specified. It is
guaranteed, however, that at any given time this order is the same for all
positions. (See section 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". For 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. The else-arrow may only be omitted,
if the if-arrow is "->". Then the else-arrow also is
"->". (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. This is equivalent to setting
it to true.
- 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.
- lose
- The players immediately lose the level.
- sound(Filename)
- Plays the given sound file.
You can also omit the code completely. Then, of course, it does
not do anything. This can be useful as part of ,-sequences.
There's a shortcut for drawing: You may omit the ";"
between a number, a letter and the "*".
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 (listed here in
order of increasing precedence):
- ||
- Boolean or
- &&
- Boolean and
- ==, !=, <, >, <=,
>=
- Comparison
- ==..
- A special comparison
- !
- Boolean not
- +, -
- Add, substract
- :
- Special operator
- *, /, %
- Multiply, divide, modulo
- &, |, .+, .-
- Bitwise and, bitwise or, setting of bits (same as bitwise or), unsetting
of bits
- -
- Unary minus
- .
- Testing of bits (a.b is the same as
a& b != 0)
/ and % work mathematically correct and do not make
funny changes when the sign of the numerator changes. More specifically, if
b is positive, then a/b is the largest integer
n such that n*b<=a. If b
is negative, then a/b is the largest integer n
such that n*b>=a. In both cases
a %b is such that
(a/b)*
b+a%b = a. Examples:
13/5=2 |
13%5=3 |
-13/5=-3 |
-13%5=2 |
13/-5=-3 |
13%-5=-2 |
-13/-5=2 |
-13%-5=-3 |
The following are the special operators:
- expr1 == expr2 ..
expr3
- Is true, if expr1 lies between expr2 and expr3. You
may also omit one of expr2 and expr3. (Then, it does the
same as <= resp. >=.) The precedence implies that x==y==2..3
is the same as x==(y==2..3) and is neither (x==y)==2..3 nor
x==(y==2)..3. Note that this operator might change in the future.
(I plan to make something like "expr in set" in
Pascal.)
- expr1 : expr2
- Is true (that is, 1) with probability expr1/expr2
- 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 and "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.
For an empty blob the semantics is slightly different: If in some
direction there is no neighbour, because the field ends there, the entry in
the neighbour sequence is 1 nevertheless. So for an empty blob
1???0??? is true, iff the blob above this blob does not exist or is
empty as well, and the blob below this blob exists and is not empty.
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 VARIABLES AND CONSTANTS for details.)
The following functions exist:
- rnd(expr)
- Returns a random value between 0 and expr-1
- gcd(expr1, expr2)
- Returns the greatest common divisor of expr1 and expr2
The following kinds of variables and constants exist:
- —
- User defined variables (see section CUAL DEFINITIONS). At the start
of the level (or at the creation of the blob) the value is the default
value you provided. If you supplied the default with : reapply,
whenever a blob's kind changes, the value of the variable is also set to
the default of the new kind. There is a subtlety: This only happens if the
new value of kind is in fact different from the old one.
- —
- System variables. These variables are always defined and have special
meanings, e.g. file and pos. Some of them are
read-only.
- —
- User defined constants. These are defined in the main .ld part, not in
cual (not inside << >>).
- —
- System 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@@(x, y; side)
varname@@(x; side)
varname@@(; side)
varname@(dx, dy;
side)
varname@(dx; side)
varname@()
If x and y are given, these are absolute coordinates
in the grid of blops, that is the variable is taken from the blob with
loc_x=x and loc_y=y (see under The system variables). If only
x is given, it specifies one of the two blobs that are currently
falling. If there is only one such blob left, because the other one got
stuck on some tower, the remaining blob's coordinate is 0. Otherwise one of
the two has coordinate 0, the other 1.
In the @ variants, the coordinates are relative to the
current blob. The variant @@(; side) refers to the
semiglobal blob, the variant @() to the global blob (See section
THE GLOBAL BLOB). The extra part ; side is optional and
specifies the side of the game. This is only meaningful in two-player mode.
side = < specifies the left player, side =
> the right player, side = = the player to which the
current blob pertains, and side = ! the other player.
@() and @@() can also be given as @ respectively
@@.
This can be done for both, reading and writing variables. It also
works for system variables (but not for constants).
In hex mode levels, for odd dx, dy should be a
"half integer", that is a number ending in ".5". This is
the only place in Cual where non-integers appear. Especially, ".5"
is not allowed in composite expressions. Therefore, also integer dy
is always allowed. If a half-integer is expected and an integer is given, it
is assumed to be rounded to above, that is 5 then represents 4.5 and -5
represents -5.5.
Caution: With mirror=1 the absolute and the relative
coordinates use different coordinate systems. Handle with extreme care.
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 @ or @@ 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 @ or @@, 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 current 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 current value of X,
- 6)
- the value of X at the beginning of the step.
- —
- Whenever you try to access a variable at a location which doesn't exist,
you will get the default value. If default values depend on the kind, the
default pertaining to the blob executing the code is used. This may change
in the future.
- —
- Changing a variable which doesn't exist does nothing (and does not result
in an error).
- [Add explanation of time slices; roughly:
- @-access of variables in reality don't access the value at the
beginning/end of the game step, but of the time slice. The call of the
main procedure of all blobs happens in the same time slice, but each other
kind of event has its own time slice.]
- 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 three
things:
- —
- Expressions like "001???01" test the neighbour pattern at the
beginning of the current step. So the change of the variable
kind will not be reflected.
- —
- In the current step, the program to draw the blob has already been invoked
(it might even be 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.
- —
- Defaults of the new kind that are declared with : reapply are
applied. This happens at the same time that kind changed, but only if the
new kind is different from the old one.
- version
- Is assigned a hopefully distinctive value at the blob's creation. See
versions in section KIND DATA for details.
- 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. These variable are reset to "output nothing" before
the drawing procedure is executed. (In fact, "output nothing" is
one special big value.)
- weight
- When calculate_size is set in behaviour, size will be
regularly updated to the sum of weight in the connected component.
The default is 1.
- 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.
- behaviour
- This is a bit field. Refer to The Constants below for the
meaningful of its bits. The default is
calculate_size+explodes_on_size for normal blobs,
explodes_on_explosion+explodes_on_chainreaction for grey
blobs, floats for the empty blob and
goalblob+explodes_on_explosion+explodes_on_chainreaction
or goalblob+explodes_on_chainreaction (depending on whether
chaingrass is set) for grass blobs.
- falling_speed, falling_fast_speed
- These variables are only used in the semiglobal blobs. They define the
vertical speed of the steered falling blobs. The unit is pixels per game
step. The defaults are 6 and 32.
- time
- The number of time steps since the level was started.
- 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.)
- basekind
- The value of the constant generated for the name of the kind of the blop.
Example:
pics = orange, pear, apple * 3, banana, apple
Here, all four kinds apple have the same value for
basekind, and this value is apple.
- loc_x, loc_y
- The absolute coordinates of the blob. (0,0) = top left corner
- loc_xx, loc_yy
- The absolute coordinates of the blob in pixels. This is not always the
same as loc_x*32 and loc_y*32, particularly for the steered falling
blobs.
- loc_p
- The player of the blob (1 or 2)
- falling
- true, if the blob is falling or it is a preview of a falling blob.
(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.
- informational
- True, if the blob is one of the info-blobs at the side of the game area.
In this sense, the previews of the falling blobs also count as
info-blobs.
- players
- The number of players.
- exploding
- When the blob is exploding, the position in the explosion animation (1 to
8); 0 else.
Currently, there is one exception: if the explosion has been
triggered by the explode command, then exploding will have
value 1 only after the current game step [more precisely: time slice;
fix that]. Reason: when reading exploding@(x,y), we maybe don't know
yet that the other blob calls explode.
Constants for behaviour:
- goalblob
- Set goalblob 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.
- calculate_size
- When this bit is set, size will be regularly updated to the sum of
weight in the connected component.
- explodes_on_size
- When this bit is set, a connected component explodes, when it has
size>=numexplode.
- explodes_on_explosion, explodes_on_chainreaction
- When these bits are set, the blob explodes whenever an explosion that was
triggered by explodes_on_size happens in its neighbourhood.
explodes_on_chainreaction refers to those triggering explosions
that are the second or later part of a chain reaction.
explodes_on_explosion refers to the other ones.
- floats
- When this bit is set, the blob keeps its vertical position even if there
is an empty blob below. This bit has no effect on the steered falling
blobs.
Constants for kind:
- <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 kind = aKind. See kind
under The system variables for the side-effects of setting
kind.
Sometimes it is necessary to perform arithmetic on kinds, for
example when several have been declared using the * multiplier. The
values of the constants are successive in the order, in which the kinds have
been declared. When a name is used several times, the first use defines the
value. Example:
startpic = apple, orange
pics = orange, pear, apple * 3, banana
greypic = pineapple
This initializes 2 kinds with the defaults for startpic, 6 kinds
with the defaults for pics, and 1 kind with the defaults for greypic. The
value of the constant orange is 1 more than that of apple,
pear is 2 more than orange, banana is 4 more than
pear and pineapple is 1 more than banana. We do not
specify what these values actually are.
This constant also exists for the empty kind, if one has been
declared using emptypic. In this case the value's relation to the other
values is not specified at all.
- global, semiglobal
- Denote the kind of the global, respectively semiglobal, blob.
- 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.
Constants for neighbours:
- neighbours_rect
- A blob connects up, down, left, and right. This is the default.
- neighbours_horizontal
- A blob connects left and right.
- neighbours_vertical
- A blob connects up and down.
- neighbours_diagonal
- A blob connects diagonally.
- neighbours_hex6
- When used in the level-wide neighbours, this sets hex mode. A blob
connects up, down, left with a slight upwards shift, left with a slight
downwards shift, right with a slight upwards shift, and right with a
slight downwards shift.
- neighbours_hex4
- When used in the level-wide neighbours, this sets hex mode. A blob
connects left with a slight upwards shift, left with a slight downwards
shift, right with a slight upwards shift, and right with a slight
downwards shift.
- neighbours_knight
- A blob connects in knight moves (Two forward and then one sideways.
Forward is one of up, down, left or right. Sideways is perpendicular to
forward. This makes a total of eight directions.).
- neighbours_eight
- Combines neighbours_rect with neighbours_diagonal.
- neighbours_3D
- A more obscure mode created especially for 3d.ld. When used in the
level-wide neighbours, this sets hex mode. A blob connects up,
down, two (but not one) to the left, and two to the right. In even columns
it also connects right with a slight upwards shift. In odd columns it also
connects left with a slight downwards shift.
- neighbours_none
- A blob does not connect at all.
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.
Miscellanious constants:
- 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)
Sometimes it is necessary to define a level slightly differently for different
purposes. For example you might need to decrease numexplode for the
two-player version lest it becomes unplayable. The difficulty settings from
cuyo's main menu provide another example.
This is done by qualifiing a definition with the versions it
should apply to. It is best explained by an example:
numexplode = 8
numexplode[2] = 6
numexplode[1,hard] = 10
This specifies that normally numexplode should be 8. In two-player
mode it should be 6. In one-player hard mode it should be 10. Here the
specifiers 2 for two-player mode, 1 for one-player mode and
hard for hard mode are used. Along with easy for easy mode
these are all predefined specifiers intended for levels. Additionally (and
for internal purposes), there are specifiers for the level tracks:
main, all, game, extreme, nofx,
weird, and contrib.
Furthermore, you can make up and use your own specifiers. In order
for them to take effect, though, you have to give cuyo additional
information about the current version. This is done on the command line
using the --version option. For example
cuyo --version=hard,geek
Specifies both hard version (you can change that in the menu) and
version "geek", which is not predefined.
There are several constraints to be observed:
- —
- All versions of a definition must be made before the first use of the
thing defined. As sometimes it is not obvious where the defined thing is
used (for example startpic uses a previous greypic by assigning successive
numbers to kinds), it is best to group all these versions into one
block.
- —
- A given version also applies to every more specialized version, for which
no definition is given. In the above example, numexplode is set to
6 in two-player hard mode and in two-player easy mode as well.
All resulting conflicts must be resolved. For example, if you make
a definition for [2] and one for [hard], you must also make a
definition for [2,hard] (or, equivalently, for [hard,2]),
because otherwise it would be ambiguous which of the two former should apply
in two-player hard mode.
- —
- Cuyo knows that easy and hard exclude each other.
Consequently, it is unnecessary (and indeed prohibited) to give an
[easy,hard] definition, even if both [easy] and
[hard] are given. The same holds for 1 and 2, and for
level tracks.
- —
- Furthermore, cuyo knows that 1 and 2 are exhaustive: There
is no mode which is neither single-player nor two-player. (The
human-versus-AI mode counts as two-player as far as cual is concerned.)
Therefore, if there are definitions for both, it is unnecessary, (and
again illegal) also to define a version without any of both. For example,
if [1,de] and [2,de] are given, [de] must be omitted.
Alternatively, you could of course give [1,de] and [de] or
[de] and [de,2]. The level track specifiers are exhaustive
as well.
(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 consisting
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.)
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 AND CONSTANTS on
how to access them. Note that the global blob is always executed before any of
the normal blobs.
There are also semiglobal blobs. There is one for each player.
These are programmed with semiglobal=code.
The following events exist:
- init
- Is called only once, when the blob gets into life, just before the first
time its main drawing routine is called.
- turn
- Is called for falling blobs each time they are rotated.
- land
- Is called when the steered blob lands (just after it landed).
- changeside
- Is called when a blob moves from one player to the other, just after the
blob has arrived at the new player.
- connect
- Is called when the connection of blobs is recalculated.
- row_up
- Is called when a player got a row from the other side, after everything is
finished and just after the loc_y of all blobs has been decreased by 1. Is
only called for the semiglobal blob, though.
- row_down
- Is called when a player gives a row to the other side, before anything
visible happens, but just after the loc_y of all blobs has been increased
by 1. Is only called for the semiglobal blob, though.
- keyleft, keyright, keyturn, keyfall
- Are called when the player presses the left, right, turn or fall key. Are
only called for the steered falling blobs and the semiglobal blob, though.
(Note that, in contrast to turn, keyturn is called even if the steered
blob cannot be rotated due to some obstacles, and also if there is no
steered 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. More
precisely, the steered blobs already come into life when they appear as the
preview. 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 are 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.
The format of the startdist field is rather complicated. On the plus side, this
means that many things can be done with little effort. We first describe the
single-character format, which, at the time of this writing, has sufficed for
all needs. After that, we describe the general format as an extension.
Every line of the startdist describes one row of blobs in the
level's initial state. The lines are aligned to the bottom and the topmost
lines come first (normal reading order). Each line must contain exactly 10
or exactly 20 characters, except the last which is special. In a line of
length 20 the first 10 characters describe the left player, the second 10
characters describe the right player. A line of length 10 describes both
players. Hence, each character describes one blob. The semantics are:
- .
- An empty blop.
- +, -, *
- A blop chosen at random according to colourprob, respectively
greyprob, respectively goalprob. The value of
nogreyprob has no influence.
- 0..9, A..Z, a..z
- These characters denote a specific kind. If the character matches the
distkey of some kinds, the first of these is chosen. More
generally, these characters are ordered such that "9"
comes before "A" and "Z" comes before
"a". In this order, the maximal distkey, which
does not come after the character, specifies the blob's kind. The
difference between the character and the distkey then specifies the
blob's version.
Example 1: In the special case, where the character exactly
matches a distkey, version is set to 0.
Example 2: Suppose kind apple has distkey =
"A", kind orange has distkey = "O" and
no further distkeys exist. Then the character "C"
denotes an apple with version=2, the character "N"
denotes an apple with version=13, the character "O"
denotes an orange with version=0, the character "S"
denotes an orange with version=4, the character "a"
denotes an orange with version=12, and the character
"8" does not denote anything (and hence is illegal).
- %
- An info blop with the version set according to the level-wide
neighbours.
- &
- An info blop with the version set according to the level-wide
chaingrass.
The last line may have length 4, 8, 10, or 20. If it has length 10
or 20, it is just a normal line as above. Otherwise it describes the
informational blops next to the field. In case of length 4, the first entry
describes the blop which depicts the number of greys. The second entry
describes the blop which depicts the number of grass blops. The third entry
describes the blop which depicts connection information. The fourth entry
describes the blop whoch depicts chaingrass information. In case of length
8, the above holds for the left player. The remaining 4 entries then
describe the same for the right player, but in reversed order. The default
is "-*%&&%*-" (or equivalently "-*%&").
As seen above, startdist can reference 62 kind/version
combinations directly (and more at random). Because this might at some time
not be enough, the multichar extension has been introduced. In this case,
each blob is described by more than one character. However, the number of
characters per blob must be the same for all keys. Hence, the lengths of
startdist lines then must be this number multiplied by 10 or by 20.
Every multicharacter combination starting with ".",
"+", "-", or "*" is
treated as the corresponding character in single-character format. All other
character combinations are treated as numbers in base 62 representation.
Here, "A" to "Z" are digits with decimal
value 10 to 35, and "a" to "z" are digits
with decimal value 36 to 61. Leading spaces are allowed instead of zeroes
(however, the all-space string is forbidden). The maximal distkey
which (as a number) is not larger than the number given in startdist,
specifies the blob's kind. The difference between the
startdist number and the distkey then specifies the blob's
version. In the case of multichar distkeys, the default for
distkeys of kinds declared by startpic= is 10 in decimal.
For blops whose kinds are chosen at random (i.e. characters
"+", "-", "*" in
single-character startdists), cuyo tries to make these as different
as possible. That means, by a certain heuristic, cuyo minimizes the number
of neighbouring blobs of the same kind. "Neighbouring", of course,
refers to the neighbours entry. inhibit and the
calculate_size bit of behaviour have no effect (these are
mutable during the lifetime of blobs, while at the time of startdist
processing, no blob's lifetime has started yet). So the only way to
influence the unneighbouring (if you really wish to do so), is by setting
neighbours appropriately. (Of course, this possibility is even more
limited, when you intend to set the calculate_size bit during the
blob's lifetime.)
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.
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.)
When deciding how to place the steered falling blobs, the AI player tries to
maximize a certain utility function. Its value is the sum of the values for
both steered falling blobs plus aiu_monochromic_vertical in case both
blobs have the same color and they get placed vertically. The value for a
single blob is the sum of the following:
- —
- For each new neighbour of blob, the neighbour utility.
- —
- aiu_two_above, if the blob is two above a blob of same kind.
- —
- aiu_height*20/(the blob's y coordinate).
The neighbour utility for a single new blob and a single one of
its new neighbours is the sum of the following:
- —
- aiu_color, if the neighbour has the same kind as the blob.
- —
- aiu_grass, if the neighbour fulfills
behaviour.goalblob.
- —
- aiu_grey, if the neighbour fulfills
behaviour.explodes_on_explosion.
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.
Visit the GSP FreeBSD Man Page Interface. Output converted with ManDoc. |