Code snippets

Top  Previous  Next

Virtua Cup

If you have ever played Virtua Cop you know that we are talking about a fast, furious action game. If you haven't played it, here's how it works: you are a cop and you try to shoot as many criminals as possible. You stand still and the bad guys appear from time to time at random positions. You shoot at them, they shoot at you... pretty simple, huh? Virtua Cup is a Virtua Cop clone.

This game is another standalone example so it has its own main function:

function main()
{
  level_load (virtuacup_wmb);
  wait (2);
  clip_size = 0;
  fps_max = 50;
  generate_enemies();
}

After we load the level, we lock the frame rate at 50 and we start generating the enemies:

function generate_enemies()
{
  while (1)
  {
     waitt (48 + random (48));
     index = 3 * int(random(10));
     temp.x = enemy_pos[index + 0];
     temp.y = enemy_pos[index + 1];
     temp.z = enemy_pos[index + 2];
     ent_create(enemy_mdl, temp, enemy_shoots);
     if (index > 9) {index = 0;}
  }
}

The bad guys appear every 3...6 seconds at random positions. I have defined an array that holds the xyz values for every position:

var enemy_pos[30] = -450,315,0, 60,315,0, 575,315,0, -325,540,255, -65,540,255, 195,540,255, 450,540,255, -190,850,515, 65,850,515, 320,850,515;

Nobody wanted to write those values for me so I had to get them in Wed. The first 3 numbers are the xyz coords for the 1st position, the following numbers are the xyz coords for the 2nd position and so on. My array has 30 elements so it stores 10 different positions. Let's get back to function generate_enemies:

index = 3 * int(random(10));

will generate a random number between 0 and 27 step 3 (0, 3, 6, 9, 12, ..., 27). We are copying the random coordinates to temp and then we create an enemy that will use its enemy_shoots function:

function enemy_shoots()
{
  wait (1);
  my.pan += 270;
  my.enable_impact = on;
  my.event = kill_enemy;
  ent_create(enemygun_mdl, nullvector, attach_enemygun);
  my.skill10 = 0;
  while (my != null && my.skill10 < 100)
  {
     ent_cycle("attack", my.skill10); // play "attack" frames animation
     my.skill10 += 3 * time;
     wait (1);
  }
  snd_play (enemyshot_snd, 70, 0);
  vec_set (temp.x, player.x); // turn towards the player
  vec_sub (temp.x, enemy_gun.x);
  vec_to_angle (my.pan, temp); 
  ent_create (bullet_mdl, enemy_gun, move_bullet);
  waitt (32);
  ent_remove (me);
}

This enemy will be rotated to face the player. If the enemy is hit by a bullet, its kill_enemy action will be executed - we'll talk about it a little later. The line below and the function that follows it:

ent_create(enemygun_mdl, nullvector, attach_enemygun);

function attach_enemygun()
{
  proc_late();
  my.passable = on;
  my.metal = on;
  while(you != null)
  {
     vec_set(my.x,you.x);
     vec_set(my.pan,you.pan);
     my.frame = you.frame;
     my.next_frame = you.next_frame;
     vec_for_vertex(enemy_gun, my, 148); 
     wait(1);
  }
  ent_remove(my); 
}

will give the enemy a gun. The enemy bullet will be created at the position given by vertex 148 on the gun model.

The enemy will play its attack animation frames if it hasn't been killed by the player:

  while (my != null && my.skill10 < 100)
  {
     ent_cycle("attack", my.skill10); 
     my.skill10 += 3 * time;
     wait (1);
  }

As soon as the enemy ends its "attack" animation frames, it plays a sound, rotates towards the player and shoots a bullet. The enemy will remain visible for two more seconds, giving the player the chance to shoot it.

  snd_play (enemyshot_snd, 70, 0);
  vec_set (temp.x, player.x); 
  vec_sub (temp.x, enemy_gun.x);
  vec_to_angle (my.pan, temp); 
  ent_create (bullet_mdl, enemy_gun, move_bullet);
  waitt (32);
  ent_remove (me);

Here's the function that moves the bullet (same function for the player and for the enemy):

function move_bullet()
{
  wait (1);
  if (my == null) {return;}
  my.enable_entity = on;
  my.enable_block = on;
  my.event = remove_bullets;
  my.passable = on;
  my.pan = you.pan;
  my.tilt = you.tilt;

  my.skill1 = 0;
  bullet_speed.x = 500;
  bullet_speed.y = 0;
  bullet_speed.z = 0;
  bullet_speed *= time;
  while (my.skill1 < 50)
  {
     if (my == null) {return;}
     if (my.skill1 < 0.1)  // don't collide with the creator
     {
        my.passable = on;
     }
     else
    {
        my.passable = off;
     }
     my.skill1 += 0.1 * time;
     ent_move (bullet_speed, nullvector);
     wait (1);
  }
  remove me;
}

function remove_bullets()
{
  wait (1);
  ent_remove (me);
}

The bullet is sensitive to impact with entities or level blocks; if it hits one of these it will be removed using the simple function remove_bullets. The bullet will get its pan and tilt from its creators (the enemies or the player) so if these guys are pointing in the right direction, so is the bullet - got it? You have seen slightly modified versions of function move_bullet in other Aum editions.

Time to take a look at function kill_enemy:

function kill_enemy()
{
  wait (1);
  exclusive_entity; // stop all the other functions for this enemy
  my.enable_impact = off; // can't be shot from now on
  my.event = null;
  dead_enemies += 1;
  my.skill10 = 0;
  snd_play (enemydead_snd, 70, 0);
  while (my.skill10 < 90)
  {
     ent_cycle("die", my.skill10); // play "die" frames animation
     my.skill10 += 5 * time;
     wait (1);
  }
  waitt (16);
  ent_remove (me);
}

If the enemy is hit by a player bullet, we stop all the other functions associated to this enemy that might run at the same time. The enemy will be insensitive to other bullets from now on and its event is set to null. We are adding one more body to the dead_enemies var, we play a death sound and the "die" animation frames. The corpse will remove visible for one more second and then it will disappear.

This code associated to the enemies has been discussed - let's move on to the code that is associated to the player:

action virtua_player // attached to the player
{
  player = me;
  player.invisible = on;
  player.skill1 = 100;
  my.enable_impact = on;
  my.event = decrease_players_health;
  camera.x = player.x;
  camera.y = player.y;
  camera.z = player.z;
  camera.pan = 90;
  while (player.skill1 > 0) // as long as the player is alive
  {
     camera.tilt += 10 * mouse_force.y * time;
     if (camera.tilt < -25) {camera.tilt += 1;}
     if (camera.tilt > 25) {camera.tilt -= 1;}
     camera.pan -= 10 * mouse_force.x * time;
     if (camera.pan < 60) {camera.pan += 1;} // 90 - 30
     if (camera.pan > 120) {camera.pan -= 1;} // 90 + 30
     if (mouse_left == 1 && bullets > 0)
     {
        player_shoots();
     }
     player.pan = camera.pan; // rotate the player too
     player.tilt = camera.tilt; // not just the camera
     if (mouse_right == 1 && bullets == 0) // right click to reload the gun
     {
        bullets = 10;
        while (mouse_right == 1) {wait (1);}
     }
     wait (1);
  }
}

The player is invisible because we don't want it to stand in our way but don't worry - the enemies know its position very well! The player has 100 health points (stored in skill1). If the player is hit by the enemy bullets, his event (decrease_players_health) will run.

The camera is placed in the origin of the player so make sure that you choose a good player position. As long as skill1 > 0 (the player is alive) we can move the camera around and shoot bullets. I have limited the camera movement to 60...120 degrees for pan and -25...25 degrees for tilt. Why would I need to look behind my shoulder when all the enemies are in front? If we run out of bullets we have to press the right mouse button to reload the weapon. If we press the left mouse button (LMB) and bullets > 0 we run the function player_shoots:

function player_shoots()
{
  exclusive_global;
  while (mouse_left == 1) {wait (1);}
  snd_play (playershot_snd, 70, 0);
  ent_create (bullet_mdl, player.pos, move_bullet);
  bullets -= 1; // you have used a bullet
}

When you press the LMB function player_shoots would run several times if it wouldn't be stopped by exclusive_global. We wait until the player releases the LMB, we play a shot sound and then we create a bullet ans we move it using the same move_bullet function that was discussed above. Finally, we decrease the number of bullets.

The last function in this game runs when the player is hit by an enemy bullet:

function decrease_players_health()
{
  wait (1);
  if (player.skill1 > 0)
  {
     player.skill1 -= 10; // decrease player's health with 10 points every shot
  }
  else
  {
     player.skill1 = 0;
  }
}

If the player is alive, we decrease 10 point from his health; if the player is already dead we set his health to 0 (it isn't professional to display health values that go below zero).

Power - ups

Sometimes you need to give your player a speed boost. Maybe you want to give him more health. What about instant ammo packs for all the weapons?

action power_health
{
  show_panels(); // display the panels
  my.metal = on;
  my.passable = on;
  while (vec_dist(my.x, player.x) > 50)
  {
     my.pan += 3 * time;
     wait (1);
  }
  my.invisible = on;
  player.light = on;
  player.lightrange = 300; // the player will glow red
  player.lightred = 100;
  snd_play (gotitem_snd, 70, 0);
  while (my.skill1 < 500) // ~ 30 seconds
  {
     if (player._armor < 100)
     {
        player._armor += 0.8 * time;
     }
     else
     {
        if (player._health < 200)
        {
           player._health += 0.5 * time;
        }
     }
     my.skill1 += 1 * time;
     wait (1);
   }
  ent_remove (me);
  player.light = off; // clean up the mess
  player.lightrange = 0;
  player.lightred = 0;
}

The first line will display the ammo / health / armor panels that come with the templates. The power_health object will rotates until the player comes close to it. If this happens, the object will become invisible and the player will generate a red light around him. As long as skill1 < 500 (for 30 seconds or so) the armor will go up to 100 points and the health will be increased until it reaches 200 points. When the time has expires (skill1 is bigger than 500) the invisible power-up will disappear; we have kept it "alive" so far because we have used its skill1. The player stops emitting light red and the things go back to normal.

Let's take a look at action power_ammo:

action power_ammo
{
  my.metal = on;
  my.light = on;
  my.lightrange = 0;
  my.lightgreen = 200;
  my.transparent = on;
  my.passable = on;
  while (vec_dist(my.x, player.x) > 50)
  {
     my.pan += 1 * time;
     my.tilt += 1 * time;
     my.roll += 1 * time;
     wait (1);
  }
  ent_remove (me);
  snd_play (gotitem_snd, 70, 0);

  // 50 bullets for every weapon - use different values here
  ammo1 = 50;
  ammo2 = 50;
  ammo3 = 50;
  ammo4 = 50;
  ammo5 = 50;
  ammo6 = 50;
  ammo7 = 50;
}

This power - up rotates on all the axis (pan, tilt, roll). If the player comes close to this item, it will disappear and ammo1...ammo7 (the vars that hold the number of bullets in the templates) will be increased. You can give a different number of bullets to every gun or you can give ammo only to certain guns.

The last power - up:

action power_speed
{
  my.metal = on;
  my.light = on;
  my.lightrange = 0;
  my.lightblue = 200;
  my.transparent = on;
  my.passable = on;
  while (vec_dist(my.x, player.x) > 50)
  {
     my.pan += 3 * time;
     my.tilt += 2 * time;
     my.roll += 1 * time;
     wait (1);
  }
  my.invisible = on;
  player.light = on;
  player.lightrange = 300;
  player.lightblue = 250;
  while (my.skill1 < 300) // ~ 20 seconds
  {
     strength.x = 10; // strength.x = 5 in templates
     strength.y = 8; // strength.y = 4 in templates
     my.skill1 += 1 * time;
     wait (1);
  }
  ent_remove (me);
  snd_play (gotitem_snd, 70, 0);

  // restore defaults
  strength.x = 5;
  strength.y = 4;

  player.light = off;
  player.lightrange = 0;
  player.lightblue = 0;
}

This power - up doubles the speed. The template player moves with the speed given by "strength" in movement.wdl. When the player picks up this power - up he will create a blue light around him and he will move with a huge speed for about 20 seconds. When the time has expired, the "normal" speed is restored and the light around the player will disappear.

I wanted to give you the chance to work with all these power - ups in my included test level, so I have added a gun and our special guest star:

action thebutcher
{
  my.light = on;
  my.lightred = 100;
  my.lightgreen = 100;
  my.lightblue = 100;
  my.frame = 31;
  while (player == null) {wait (1);}
  while (1)
  {
     if (vec_dist(my.x, player.x) < 60 && player._health > 0)
     {
        if (player._armor > 0)
        {
           player._armor -= 3 * time;
        }
        else
        {
           player._armor = 0;
           player._health -= 2 * time;
        }
     }
     wait (1);
  }
}

This stiff guy will hurt the player if he (or she) comes too close. Now you can test the power_health and power_ammo (fire all the bullets and then get the power_ammo). Get the power_speed power - up and try to run as far as possible... I'm chasing you with the power_power_gun :)