The Scripting Language
In the
.scm files of a level the designer can use a powerful
scripting language to decide which objects should exist in the game
and, optionally, even customize how they should behave. Using these
features it is possible to create very different kind of games.
The scripting language chosen for this is called
Guile and is a
language developed by
GNU to be
used for customizing applications. The language is a version of
Scheme and on a first appearance seems to constructed by lots of
incredibly silly parenthesis (LISP).
The easiest way to get started with the language is by copying an
existing .scm file and customize it for your new map. We start by
taking a look at, for instance,
con1.scm.
;;; steep - by Russel Valentine
(set-track-name "Steep")
(set-author "Russell Valentine")
(start-time 120)
(set-start-position 253.5 253.5)
(add-goal 230 228 #f "con2")
(add-flag 248 220 100 1 0.1)
(add-flag 247 220 100 1 0.1)
(add-flag 246 220 150 1 0.1)
(add-modpill 254.5 217.5 *mod-jump* 20 0)
In this script file we see first a comment, this is any line which
starts with a ';' and is ignored by the program.
Following this is a number of function calls which use some of the
built-in functions in trackballs to setup the settings for this level
and to create objects. The first of these function calls are the
"set-track-name" function. As you can see all function calls must be
encapsulated by parenthesis (that's how scheme knows they are
functions and not variables or values) and this first function expects
one argument. In this example it is given the argument "Steep" which
is the name of the level. Similarly the function "set-author" is used
to tell trackballs whom has created this level. These two pieces of
information is used in the beginning of the level to give proper credit
to you, the author!
Some other inter sting functions you see here is the one to decide how
much time the player has to complete the level, start-time, expressed
as seconds, and where he starts. The later function,
set-start-position, expects the x and y position for where the player
starts and the easiest way to decide which coordinate to use is to
look at the coordinates of a cell in the map editor. Note that this
function expects a floating point number, so it is possible to very
precisely decide exactly where in the cell to start.
The next function, add-goal, expresses where the goal for this level
is placed and expects x/y coordinates again and a true or false value
if the goal should be horizontal or vertical (use #f or #t) and finally
the file name of the next level to go to.
The remaining functions, add-flag and add-modpill, are used to create
some objects in the game. In this case flags which gives points and a
modpill which makes the player capable of jumping further. See the
API Reference for more information about
these and other functions.
Apart from a number of built-in function trackballs also includes a
large number of built-in variables. These variables are typically used
as constant values to be passed to functions. One example was the
variable
*mod-jump* which declared which kind of bonus pill
was created in the example above.
Programming in Scheme
If you want to do more fancy setup in you levels, perhaps selecting
starting time and position depending on the difficulty settings or
other customizations, you need to know a little bit of scheme
programming. If you want to learn how to use scheme properly i would
recommend reading a
tutorial
on using scheme. Otherwise, you can probably just learn as you go
by examining examples.
Creating your own variables and functions
Apart from using the predefined functions and variables in trackballs
you can create your own. This is good since it allows you to simplify
the code for doing complex repetitive things. For instance, if you
want to create an opponent ball with a customized colour you can do so
by doing:
(define erik (new-mr-black 241.5 245.5))
(set-primary-color erik 1.0 1.0 0.0)
This would create an opponent "erik" which is yellow. If you want to
create lots of eriks, you can create a function which does this:
(define new-erik (x y)
(set-primary-color (new-mr-black x y)
1.0 1.0 0.0))
and use this function repeatedly.
(new-erik 100.0 100.0)
(new-erik 200.0 100.0)
(new-erik 200.0 200.0)
Another more useful way of using functions is to have them as "hooks"
which get called by the game when different events occur. There exists
a number of different functions with which you can create a hook which
calls one of your functions when something happens in the
game. Depending on on which event you have your function called your
function may need different number of arguments. For instance, if we
want to create a modpill when an opponent "mr-black" gets killed
we can do this by first creating a function to be called:
(define my-modpill-function (subject object)
(add-modpill (get-position-x subject)
(get-position-y subject)
*mod-extra-life* 30 -1))
This function accepts one argument, subject, which is the mr-black
that got killed and another argument which is ignored. It then gets
the position where he got killed and creates a new "extra life"
modpill at that position. In order to use this function when different
opponents gets killed we need to tell Trackballs to use it:
(define erik (new-mr-black 241.5 245.5))
(set-primary-color erik 1.0 1.0 0.0)
(on-event *death* erik my-modpill-function)
Congratulations, you now know the basic steps on how to completely
customize the game. By using your own functions and hooks which plug
in to different parts of the game (like when objects get killed, or
the player is close to a switch, or whatever) you can accomplish
almost anything. Well... at least that's the general idea. Go ahead now
and read the
API Reference to see which
functions, variables and hooks are available to you and take a look at
the
Examples to get you started. If you
have any great ideas for extra hooks or functions that are needed in
the game please post a request at the
issue tracker
and we'll see what we can do.