Programming Trouble: FAQ

In any programming language, you can code everything in at least three different ways: the right way, the wrong way, and the ugly way. In the following we've collected some common problems happening mostly to beginners, but sometimes also to experts!

Setting events

Q. I've written a very simple event function, like this:

function my_collision_event() { beep(); }
and assigned it to the my entity through
my.emask |= ENABLE_IMPACT;
my.event = my_collision_event();
But when my entity is hit, it does not beep - instead an error message pops up! What's happening?

A. Functions are called by writing them into the code and adding parentheses . So what you did was calling the function my_collision_event and assign the returned value - which is undefined - to the event pointer. This sets the event to zero, or even worse, to a random value which is called when the event is triggered, and causes a crash. This is certainly not what you intended. What you wanted instead was assigning the function itself:

my.event = my_collision_event;

Comparing variables and strings

Q. I want to check whether my variable has the value 0 or not, so

if (my_var = 0) { do something; } 
Hey, my_var turns out to be always 0! What's wrong?

A. You confused '=' and '=='. Instead of comparing, you set your variable at 0. This is valid C syntax because you can set variables in a comparison. This happens even to experienced programmers occasionally. The programmer of this engine has therefore adopted the habit to place the constant first in a comparison. When now the second '=' is omitted, the compiler will complain:

if (0 == my_var) { do something; } 

Q. Ok, now I have a different problem, and I'm sure I've used '==' this time! Why is the following comparison always wrong?

var x = 0.6;
x += 0.1;
if (x == 0.7) { do something; } 

A. On a computer, all variables are stored with a limited precision. For this reason the real value of float, double, or non-integer var variables slightly differs from 0.6, 0.1 or whatever you want to set them to. Thus you can normally not compare them with '==' to some number. Only greater or less comparisons (<, <=, >, >=) work for floating point variables or for non-integer var variables.

Q. Why does my string comparison fail?

STRING* string1 = "The same";
STRING* string2 = "The same";

...
if (string1 == string2) { do something; } 

A. You are not comparing strings, but string pointers. string1 and string2 are different pointers and thus the comparison always fails, no matter if they contain the same text. For comparing string contents, use str_cmp:

if (str_cmp(string1,string2)) { do something; }   

Confusion with global and local variables

Q. The following code that should produce seven sounds doesn't work. However it does work if I delete the wait(1) line! Why?

var i;
...

function seven_chimes()
{
  i = 0;
  while (i < 7) // repeat seven times
  {
     beep(); // play a chime
     i += 1; // increase the counter variable 
     wait(-0.5); // wait half a second
  }
}

A. The global i variable is also used by other functions in your code for counting. All those other functions run during the wait time. At first encountering the wait(-0.5), i has certainly a value of 1 - but after that, it can have any value, depending on what the other actions did with it. This won't happen with a local variable:

function seven_chimes()
{
  var i = 0;
  while (i < 7) // repeat seven times
  {
     beep(); // play a chime
     i += 1; // increase the counter variable 
     wait(-0.5); // wait half a second
  }
}

Treat wait() as a function that definitely can change all global variables and pointers. Only the my and you entity pointers, and local variables are guaranteed to keep their values during the wait time.

Confusion with pointers

Q. My function below is supposed to multiply two vectors and return the result, however all it returns is a crash error message! How come?

function vec_multiply(VECTOR* a, VECTOR* b)
{
  VECTOR* c;
  c.x = a.x * b.x;
  c.y = a.y * b.y;
  c.z = a.z * b.z;
  return c;
}

A. You've defined a local VECTOR pointer, but forgot to assign something to that pointer - so it just points to nothing. Any attempt to access the non-existing components of that VECTOR results in a crash.

Q. Ok, here's my next try. I now made sure to define a real VECTOR and not a mere pointer to it. But this time it doesn't even compile!

function vec_multiply(VECTOR* a, VECTOR* b)
{
  VECTOR vc;
  c.x = a.x * b.x;
  c.y = a.y * b.y;
  c.z = a.z * b.z;
  return c;
}

A. Only a single value can be returned by a function. Thus, functions return variables, or pointers to structs, but they can not return whole structs.

Q. Ok, next attempt. I know that I can get the pointer of something by preceding an '&'. Now finally it compiles and I even don't get a crash... but I don't get a valid result either. When I check the returned VECTOR, it seems to contain not the multiplication results but just garbage!

function vec_multiply(VECTOR* a, VECTOR* b)
{
  VECTOR c;
  c.x = a.x * b.x;
  c.y = a.y * b.y;
  c.z = a.z * b.z;
  return &c;
}

A. You have defined a local VECTOR, meaning that it's only valid inside a function. After the function returns, the VECTOR content becomes invalid and thus the pointer points to garbage. This happens even when the function continues with a wait() loop: wait() preserves the content of local variables or vectors, but not their memory locations, so pointers to them become wrong in the next frame. For fixing this I could now define a global VECTOR c outside my function, but what if I want to keep everything inside? I could just pass the result vector already in the function argument list:

function vec_multiply(VECTOR* a, VECTOR* b, VECTOR* c)
{
  c.x = a.x * b.x;
  c.y = a.y * b.y;
  c.z = a.z * b.z;
  return c;
}

Alternatively I can use a static vector. Static variables are defined in a function, but behave like global variables:

function vec_multiply(VECTOR* a, VECTOR* b)
{
  static VECTOR c;
  c.x = a.x * b.x;
  c.y = a.y * b.y;
  c.z = a.z * b.z;
  return &c;
}
But be aware in that case that the static vector c will change when the function is called the next time!

Bad timing

Q. The following two actions are assigned to two entities in the level. Yesterday they worked perfectly. But today, after changing something in the level at a totally different place, suddenly the second action spits out a crash error message!

ENTITY* monster;

action become_monster() { monster = my; // set a global pointer, that can be used by other functions }

action get_monster_height() { my.z = monster.z; // use the pointer to set my vertical position to the monster's }
A. The second action requires the monster pointer being already set. However this depends on whether the first action was started before the second - and there's only a 50% chance. It is not defined in which order the actions assigned to entities are started at level loading. Replace your second action by:
action set_monster_height()
{
  while (!monster) { wait(1); } // wait until there is a monster
  my.z = monster.z;
}

When using a pointer - even a simple one like my or you - always think about the possibility that it could be undefined at that time!

Q. I want to emit a blue and a green particle, but I always get two green particles! What is wrong with the following code?
var particle_color = 0;
... 
particle_color = 2; // this should be a blue particle
effect(particle_start,1,my.x,nullvector);
particle_color = 1; // this should be a green particle
effect(particle_start,1,my.x,nullvector);
...
function particle_start(PARTICLE* p)
{
  if (particle_color = 0) { p.red = 255; p.green = 0; p.blue = 0; }
  else if (particle_color = 1) { p.red = 0; p.green = 255; p.blue = 0; }
  else if (particle_color = 2) { p.red = 0; p.green = 0; p.blue = 255; }
  ...
}

A. Unlike calling a function directly, functions indirectly started by instructions like effect, ent_create, game_load etc. are not guaranteed to start immediately after that instruction. They can start up to two frame cycles later, and sometimes not even on the same PC in multiplayer games! Particle functions, for example, run on the clients when effect() is executed on the server. In the above example, both particle actions start with particle_color set to 1 (or to any other value particle_color may get afterwards). The solution would be to either use different particle functions, or passing the particle color in the 4th parameter of the effect function.

Bad mathematics

Q. I found the following formula for calculating a two dimensional distance to the origin in my mathematics book - however in my script it sometimes delivers a wrong result!
dist2d = sqrt(my.x*my.x + my.y*my.y);

A. The var variable with all its intermediate results has a range between -9999999.999 and +999999.999. So the above formula becomes invalid if my.x or my.y exceeds 1000 (because then the intermediate result my.x*my.x exceeds 1000000). A solution is converting your vars intermediately to float or double for such calculations, and convert the result back to var:

double fx = my.x;
double fy = my.y;
dist2d = sqrt(fx*fx + fy*fy);

Alternatively, if you expect the intermediate results of formulas to exceed the range, limit them by multiplication by a carefully chosen factor, and then multiply the result with a correction factor:

dist2d = 10000 * sqrt((0.01*my.x)*(0.01*my.x) + (0.01*my.y)*(0.01*my.y)); // 10000 = 1/(0.01*0.01)

This mathematically identical formula gives my.x and my.y a valid range between 10 and 100000, and delivers zero if they are below 10. In normal cases the game variables won't exceed the critical boundaries, the only exception can happen when multiplying two distances.

Bad timing, again

Q. We have a model entity with a bright red light at it's very centre. For improving the light effect, we've attached a TGA sprite to that model:

action attach_sprite()
{
  set(my,BRIGHT); // make light more brilliant
  my.roll = 1;    // always face the camera
  while (1)
  {
    vec_set(my.x,you.x); // place the sprite at the xyz position of the entity that created it
    wait(1);
  }
}

action red_model()	
{
  ent_create("light.tga", my.x, attach_sprite); // attach light sprite
  ...
  patrol() // some movement action
}
We expected that the light sprite is fixed to the center of the model, and moves together with it. However it always seems to lag behind by one frame. And even worse, the model now does not move anymore! What's happening?

A. The second problem is easy to fix: We've forgotten to make the light passable. So the model permanently collides with it's own light. Entities attached to other ones should be PASSABLE:

action attach_sprite()
{
  set(my,BRIGHT|PASSABLE); // make light more brilliant and passable
	... // etc.
But what produces the strange time lag? If two entities influence each other, it's important to keep in mind the order of their two simultaneously running actions. They won't run really at the same time. In the example, during one frame the function scheduler runs first the attach_sprite action, and then the red_model's movement action. Functions that are started first will run first. So the sprite is always placed to the position the red_model had in the frame before. The solution is simple: By changing the order of the actions ...
action red_model()
{
  patrol(); // perform move behavior first
  ent_create ("light.pcx", my.x, flare_light); // attach light sprite and start it's action after that
}
...the light stays perfectly at the entities' center. We could alternatively use PROC_LATE for changing the order of actions:
action attach_sprite()
{
  set(my,BRIGHT|PASSABLE); // make light more brilliant and passable
  my.roll = 1;    // always face the camera
  while (1)
  {
    proc_mode = PROC_LATE;
    vec_set(my.x,you.x); // place the sprite at the xyz position of the entity that created it
 	  wait(1);
  }
}

 

 

► latest version online