Functions

A function is a list of instructions to the engine. When the function is executed, the instructions will be executed one after another. Such an instruction can assign a value to a variable, or call another user-defined function or an internal engine function. A function is defined this way:

function name (Parameter1, Parameter2...) { instruction1; instruction2; ... }

This creates a function with the given name. Optional parameters - variables, expressions or pointers - can be handed over on calling the function, in order to pass information to it. The function accesses the parameters under the name given between the parentheses, like a normal variable. As with all variables, care must be taken not to use reserved names for the parameters, like x, y, z, my, you, etc. The instructions are written between the winged brackets. A simple function definition without parameters looks like this:

function beep3() 
{ 
  beep();
  beep();
  beep();
} 
...
beep3(); // beeps 3 times

This function is executed whenever the engine encounters a line containing beep3() . The next example is a function that takes a var as parameter:

function beeps(times)
{
  while (times > 0) { 
    beep(); 
    times -= 1; 
    wait(1); 
  }
}
...
beeps(7); // beeps seven times

If a global variable or another object of the same name as the parameter exists, all references within the function refer to the 'local' parameter and not to the 'global' variable. The function may change any parameter, as 'times' in the example, but the original parameter in the calling function remains unchanged. The parameters can be omitted, in that case the space between the parentheses remains empty.

 LC  In lite-C, a function can have any number of parameters of any type. Structs can not be used for parameters, but struct pointers can. If the type is not a var, it must be given before the parameter name, as in "function beeps (int times,float loudness)". In C-Script however a function can only have up to 4 parameters, and they must be either a var or a var pointer . Because a var can also store handles or object pointers, you can also pass a a handle or a pointer to any object to a C-Script function. You just must then assign it to a 'real' pointer before you can access its parameters directly.

Example (C-Script):

// this function takes a var set to an entity pointer as argument.
function ent_init(ent)
{
   my = ent; // necessary, because my.bright = on works, but ent.bright = on won't work!
   my.bright = on;
   my.abient = 100;
   ent_animate(ent,"walk",50,0); // it does not matter whether you pass my or ent here
}
...
ent_init(you);
...

Example (lite-C):

// this function takes an entity pointer as argument.
function ent_init(ENTITY* ent)
{
   set(ent,BRIGHT);
   ent.ambient(100);
   ent_animate(ent,"walk",50,0);
}
...
ent_init(you);
...

Functions also accept pointers to arrays or to structs as parameters. In C-Script we prefix the parameter name by a "&" in order to indicate that a parameter is a var pointer instead of a single number. In lite-C however we indicate this like any other non-var parameter by preceding the parameter name by its var* type. If an array pointer is passed to a function, its elements can be accessed as usual through [..] indices:

Example (C-Script):

function vector_add2(&sum,&v1,&v2)
{ // calculate the sum of two vectors
   sum[0] = v1[0] + v2[0];
   sum[1] = v1[1] + v2[1];
   sum[2] = v1[2] + v2[2];
} 	

var vector1[3] = 1,2,3;
var vector2[3] = 4,5,6;
var vector3[3];
...
vector_add2(vector3,vector1,vector2); // vector3 now contains 5,7,9

Example (lite-C):

function vector_add2(var* sum,var* v1,var* v2)
{ // calculate the sum of two vectors
   sum[0] = v1[0] + v2[0];
   sum[1] = v1[1] + v2[1];
   sum[2] = v1[2] + v2[2];
} 	

var vector1[3] = { 1,2,3 };
var vector2[3] = { 4,5,6 };
var vector3[3];
...
vector_add2(vector3,vector1,vector2); // vector3 now contains 5,7,9
You can also pass struct parameters to functions that expect var pointers. This is often useful to pass entity or view positions, angles or skills to a function:

Example (C-Script):

function vector_direction(&result,&from,&to)
{ // calculate the direction angles between two positions
    result[0] = to[0] - from[0];
    result[1] = to[1] - from[1];
    result[2] = to[2] - from[2];
    vec_to_angle(result,result);
} 		

var direction[3];
...
vector_direction(direction,my.x,your.x);
// calculates the angle pointing from my to you
vector_direction(direction,camera.x,nullvector);
// calculates the angle from the camera to the level origin

Example (lite-C):

function vector_direction(var* result, var* from, var* to)
{ // calculate the direction angles between two positions
    result[0] = to[0] - from[0];
    result[1] = to[1] - from[1];
    result[2] = to[2] - from[2];
    vec_to_angle(result,result);
} 		

var direction[3];
...
vector_direction(direction,my.x,your.x);
// calculates the angle pointing from my to you
vector_direction(direction,camera.x,nullvector);
// calculates the angle from the camera to the level origin

You can pass the same variable to a function as a variable parameter, or as a variable pointer. However there is a subtle difference. If the function modifies the variable, in the first case it only operates on a copy of the variable content, i.e. the modification happens only internally within the scope of the function. The passed variable itself is not changed. But when a variable pointer is passed, the function directly operates on the content and the variable is thus modified 'globally'.

Saving, loading, multitasking

A game engine is normally a multitasking engine. This means that many functions can run simultaneously, or several instances of the same function can run at the same time. This requires special precautions in some cases, like saving or loading a game, or idling during a wait cycle.

The current state of a function, its local variables and the predefined my and you pointers are preserved during a wait() call, and are saved with the game_save function. This ensures that when you load a previously saved game, all functions continue to run at the point where they were saved. Well, almost. Pointers other than my or you however are not saved, and thus will become invalid within functions after loading a game. This can only happen during wait() time, so you should be aware that your pointers could become invalid after wait() when you load a game. If you need pointers to remain valid in your function during wait() time, convert them to a handle before wait() and back to a pointer afterwards.

Waiting functions are stored on a Scheduler List and called in the order of their last wait() execution. This order can be modified by the proc_mode variable (lite-C) resp. the proc_late function (C-Script).

Function prototypes and pointers

Like all other objects, functions must be defined before they are assigned to another object or called by another function. Sometimes this is inconvenient, for instance if two functions call each other. In that case, a function can be predefined through a prototype (known to C++ programmers). The prototype consists of the function title with the parameters, followed by a semicolon, but without the instruction list in {...}. Example:
function beep_sum(beep1,beep2); // prototype of function beep_sum        

function use_beep_sum()
{
   beep_sum(3,4); // beep_sum is used here, and needs the prototype defined before
}

function beep_sum(beep1,beep2) // this is the real function
{
   beep1 += beep2;
   while (beep1 > 0)
   {
      beep();
      beep1 -= 1;
      wait(1);
   }
}

 LC  In lite-C a function prototype can also be used as a function pointer. Example:

long MessageBox(HWND,char *,char *,long); // prototype of function MessageBox
...
MessageBox = DefineApi("user32!MessageBoxA"); // set the MessageBox pointer to a function in the user32.dll

Return values

A function can return a value to its caller, through the return instruction. Example:
function average(a,b) 
{ 
  return((a+b)/2); 
} 


// this is called this way:
x = average(123,456); // assign the average of 123 and 456 to x
 LC  While C-Script can only return a var, lite-C, C, or C++ can return any variable or pointer type. Instead of "function", the function is then defined with the returned type, like float, ENTITY*, or whatever. Examples (lite-C):
PANEL* emptypanel(layer) 
{ 
  return(pan_create("",layer)); 
}

float fAverage(float a, float b) 
{ 
  return((a+b)/2.0); 
} 
...
float x = fAverage(123.0,456.0); // assign the average of 123 and 456 to x

Recursive functions

A function can call itself - this is named a recursive function. Recursive functions are useful for solving certain mathematical or AI problems. This is an Example:
function factorial(x)
{
  if (x <= 1) { return(1); }
  if (x >= 10) { return(0); } // number becomes too big...
  return (x*factorial(x-1));
}

Recursive functions must be used with care, and are for advanced users only. If you make a mistake, like a function that infinitely calls itself, you can easily produce a 'stack overflow' which crashes the computer. The stack size allows a recursion depth of around 10,000 nested function calls.

Overloaded functions

 LC  In lite-C or C++, functions can be overloaded, which means you can define several functions with the same name, but different arguments. The compiler knows which function to use depending on the types and numbers of arguments passed. Example:
double square(double x) 
{ 
  return(x*x); 
}
int square(int x) 
{ 
  return(x*x); 
}
...
double x = square(3.0); // calls the first square function
int i = square(3);      // calls the second square function 
 !!  Take care however: when the argument types don't match any of the overloaded functions, the first function is automatically used! This can happen, for instance, when the function expects a var argument and an int is passed, or when it expects a STRING* and a char* is passed. This is important when passing constants to overloaded functions: Text constants like "this is a text" are of type char* (not STRING*), integer numeric like 12345 are of type int (not var), and single letter constants such as 'A' or 'B' are also of type int (not char). Make sure that overloaded functions exist for all types of arguments that can occur - or use typecasting to convert the arguments to the right type. Example:
int square(int x) 
{ 
  return(x*x); 
}
var square(var x) 
{ 
  return(x*x); 
}
...
int i = square(3); 		// calls the first square function
var a = square((var)3);   // calls the second square function 

Calling modifiers

 LC  In lite-C, C,or C++ the __stdcall calling modifier is used to call Win32 API functions. The called function cleans the stack. The __cdecl calling modifier is the default calling convention in C-Script, lite-C, C and C++ programs. Because the stack is cleaned up by the caller, it can do variable argument (vararg) functions, like printf(...).

Special functions

A function named main() is automatically executed at the start of the program.  LC  In lite-C, main() is executed before the engine and video device is initialized. This allows to set redefinable variables, like video_mode or dplay_maxclients, in the main function before the first wait() call.

Functions ending with ..._startup are executed right after the main() function, and  LC  before the engine and video device is initialized. This way, every script can have its own startup function which initializes its variables or objects. Example:

function debug_startup() // automatically started if this script is included
{
  set(debug_panel,VISIBLE);
  while (1) 
  { // run forever
    debug_panel.pos_y = screen_size.y - 15;
    fps = 0.9*fps + 0.1/time_step;
    wait(1);
  }
}

 LC  Functions ending with ..._event are automatically assigned to the corresponding event. An event function is started by a certain trigger - like an entity collision, a button or a mouse click, or the game start or level loading. A list of events can be found in the Predefined Variables chapter. Example:

// the following function is automatically assigned to the on_x event
function on_x_event()
{
   printf("[X] key pressed!");
}

 !!  An event function can start during execution of any engine function that allows events, like a collision instruction or a wait() instruction. It can start when you don't expect it. Therefore care must be taken that event functions don't alter global variables that other functions rely upon.

Actions are another special kind of functions. They don't have parameters and don't return a value, but will appear in the action pop-up list in WED's entity property panel. So actions can be assigned to entities through WED, and are automatically executed after level loading. Action names are limited to 20 characters. Besides that, there is no difference between functions and actions. This is our usual example for an entity action, which consists of just three instructions, and lets the entity rotate. Example:

action ent_rotate()
{
   while (1) 
   {
     my.pan += 3*time_step;
     wait(1);
   }
}

In a multiplayer environment, actions normally run only on the server; this way they influence the whole game world. All other functions, normally assigned to the user interface - that means functions attached to keystrokes or panel buttons - are run there where they were triggered, normally on a client. In a single-player environment the server and the client are the same PC, so you don't have to bother about where your functions run.

See also:

Variables, Pointers, Structs ► latest version online