Monster Script

What is Monster?

Monster Script (or just Monster) is an advanced scripting language made for game programming.

(Or it will be, when it is finished.)

Monster is an object oriented programming language with syntax similar to D, C++ and Java. It is a byte-code compiled language (like Java) and scripts must be run through a virtual machine (VM). Rather than being a stand-alone program, however, the Monster VM is a library. It is meant to be integrated into an existing client program (written in D or C++, for example), where the VM runs side-by-side with "native" code.

Monster is meant to be a control language - it should give you a simple but flexible language to express high-level behavior, and let you put the low-level "back end" code in native code. Exactly where you draw the line between scripted code and native code is up to you.

The Monster compiler and VM are written entirely in the D programming language, but bindings to C++ are included. You do not need a D compiler og any knowledge of D to use Monster in C++. The language is still in the early stages of development, and many key features are not implemented yet. However, I have clear goals for what Monster should become:

Examples

Here are some basic samples of Monster syntax:

/* Monster has one class per file. Every file must begin
   with a class declaration. */
class Test;

// Functions look and work like in C and D.
float power(float base, int exp)
{
    float res = base;
    for( ; exp>1 ; exp--)
        res *= base;
    return res;
}

// Member variables. Uninitialized variables are always
// set to a default value, eg. zero for ints.
int i, j=2;

// This function does not return a value.
test()
{
    if( power(2.0, i+j) > 10.0 )
        doSomething(i,j);
}

/* A native function is a function whose body is declared
   in native code (eg. in C++ or D.) Native functions are
   one of the main ways you communicate with the "outside
   world", along with idle functions (see below.) They are
   called like normal functions and can also return values.
 */
native doSomething(int i, int j);

// Classes work like other types. Objects are always
// passed by reference (like in Java and D), not by value
// (as in C++.)
Test makeNew(int i, int j)
{
    Test t = new Test;
    t.i = i; t.j = j;
    return t;
}

Idle functions and states

Idle functions and states are essential concepts in the Monster language. In Monster, each object is always in one of several states. All states are user defined, except the zero state (also called the empty state) which is the default state that every object starts in. Every state has its own code, which is run continuously as long as the object remains in that state. This allows objects to act on their own, without using a centralized event loop.

As an example, consider a ball that bounces around on the screen. We assume that all movement and visualization is handled in native code, and that Monster only controls what happens when the ball hits a wall.

class Ball;

// Native functions used to manipulate the ball's velocity
native flipVelX();
native flipVelY();

// This function is called from native code when we hit a
// wall.
hitWall(int wall)
{
    // We define 0 = top wall, 1 = left wall, 2 = bottom,
    // 3 = right.
    if(wall == 0 || wall == 2) flipVelY();
    else flipVelX();
}

So far, this example is pretty event driven (an external function call causes all the action), and not terribly interesting. But say that we also want the ball to flip one of its velocities every five seconds, without depending on outside events. This can be done by adding the following code to the class:

// Call this to start flipping every 5 seconds
startFlip() { state = flipFlop; }

// Call this to stop flipping (enter the empty state)
stopFlip() { state = null; }

// Define the state flipFlop
state flipFlop;
{
    begin:

    // Sleep five seconds and then flip X
    sleep(5.0);
    flipVelX();

    // Ditto for Y
    sleep(5.0);
    flipVelY();

    // Loop to the top
    goto begin;
}

idle sleep(float secs);

When the function startFlip() is called, it puts the object into the state called flipFlop. When in this state, the ball will continuously run the state code associated with the state. The code runs in its own virtual thread, so the ball continues to move while the state code is running. In this example the ball will wait for five seconds (continuing to move normally), then flip the x velocity, then wait five seconds more before flipping the y velocity, and finally loop around to repeat the process.

State code always begin execution at the label called begin, and the code continues to run until it either reaches the end of the code block, a halt statement is executed, or the state is changed again. If there is no begin label in the state, no code will be executed when the state is entered.

Notice that sleep() is an idle function. There is an important difference between idle functions and native functions. If sleep() was a native function (that called the system sleep), then the entire program would freeze for five seconds until sleep returned.

Idle functions on the other hand actually give the control back to the VM, allowing it to run other code. The idle version of sleep() tells the VM that it should continue to run the flipFlop state in five seconds from now, and in the meanwhile it should let other parts of your program (including other Monster objects) run normally. Idle functions are therefore an essential part of multitasking in Monster. They can only be run from state code, and for the multitasking scheme to work optimally, a state should spend most of its time inside idle functions.