// enemy.c
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Enemy movement code
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var speed_down = 2; // downward speed by gravity
var stand_percentage, run_percentage, attack_percentage;
var frames_blocked = 0;
////////////////////////////////////////////////////////////////////////////
FONT* arial_font = "Arial#20";
PANEL* framesblocked_pan =
{
layer = 16;
digits(600, 4, "Frames Blocked: %.f", arial_font, 1, frames_blocked);
digits(600, 20, "Zufall: %.f", arial_font, 1, zufall);
flags = SHOW;
}
////////////////////////////////////////////////////////////////////////////
ENTITY* enemy_weapon;
VECTOR weapon_tip;
VECTOR weapon_base;
////////////////////////////////////////////////////////////////////////////
function enemy_stands()
{
ent_animate(my, "stand", stand_percentage, ANM_CYCLE); // then play the "stand" animation
stand_percentage += 2 * time_step; // 2 sets the "stand" animation speed
}
function enemy_attacks()
{
ent_animate(my, "attack", attack_percentage, ANM_CYCLE); // then play the "attack" animation
attack_percentage += 6 * time_step; // 6 sets the "attack" animation speed
vec_for_vertex(weapon_tip, me, 41); // sword tip vertex coords - get the value in Med!!
vec_for_vertex(weapon_base, me, 11); // sword base vertex coords - get the value in Med!!
result=c_trace(weapon_tip.x, weapon_base.x, IGNORE_ME|IGNORE_PASSABLE );
if (result > 0) // detected the player? Then hurt it!
{
players_health -= 5 * time_step; // 5 gives the damage factor
players_health = maxv(players_health, 0); // don't allow the health to go below zero
}
}
function attach_enemyweapon() // keep in sync the enemy and its weapon
{
enemy_weapon = my; // this is the weapon that's attached to the enemy
proc_mode = PROC_LATE;
set (my, PASSABLE);
while (you)
{
vec_set(my.x, you.x);
vec_set(my.pan, you.pan);
my.frame = you.frame;
my.next_frame = you.next_frame;
wait(1);
}
}
function enemy_was_hit()
{
if (you.skill88 == 1357) // the enemy was hit by one of player's bullets?
my.skill99 = 0; // then the enemy must die!
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
action enemy_code()
{
var dist_down;
var chasing_player = 0;
VECTOR temp1, enemy_target;
VECTOR pos1, pos2;
ANGLE temp_angle;
VECTOR vFeet;
vec_for_min(vFeet,me); // vFeet.z = distance from player origin to lowest vertex
// wait until the player model is loaded
while (!player) {wait (1);}
my.emask |= (ENABLE_IMPACT | ENABLE_ENTITY);
my.event = enemy_was_hit; // run the enemy event function when it collides with an entity
my.skill99 = 100; // stores the enemy's health
my.skill98 = frames_blocked; // frames_blocked
ent_create("mace.mdl", my.x, attach_enemyweapon);
while (my.skill99 > 0) // run this loop for as long as the enemy is alive
{
// determine the ground distance by a downwards trace
if (c_trace(my.x,vector(my.x,my.y,my.z-1000),IGNORE_ME | IGNORE_PASSABLE | USE_BOX) > 0)
dist_down = my.z + vFeet.z - target.z; // get distance between player's feet and the ground
else
dist_down = 0;
// apply gravity when the player is in the air
if (dist_down > 0) // above floor, fall down with increasing speed
dist_down = clamp(dist_down,0,accelerate(speed_down,5,0.1));
else // on or below floor, set downward speed to zero
speed_down = 0;
vec_set(pos2.x, my.x);
c_scan(my.x, my.pan, vector(120, 60, 1000), IGNORE_ME | SCAN_ENTS | SCAN_LIMIT);
if (you == player)
{
if (vec_dist (player.x, my.x) > 80) // the enemy didn't come close enough to the player yet?
{
vec_set(enemy_target, player.x);
vec_sub(enemy_target, my.x);
vec_to_angle(temp_angle, enemy_target);
my.pan += ang(temp_angle.pan - my.pan) * 0.25 * time_step;
ent_animate(my, "run", run_percentage, ANM_CYCLE); // then play the "run" animation
c_move (my, vector(10 * time_step, 0, -dist_down), nullvector, IGNORE_PASSABLE | GLIDE); //
run_percentage += 6 * time_step; // 6 = "run" animation speed
chasing_player = 1;
}
else // the enemy is close enough to the player here
{
vec_set(temp1, player.x);
vec_sub(temp1, my.x);
vec_to_angle(my.pan, temp1);
my.tilt = 0;
enemy_attacks(); // so let's play the enemy's "attack" animation
chasing_player = 2;
}
}
else // the player wasn't detected
{
chasing_player = 0;
enemy_stands(); // so let's play the enemy's "stand" animation
}
vec_set(pos1.x, my.x);
wait (1);
// the enemy is supposed to chase the player, but it can't move?
if ((abs(vec_dist(pos1.x, pos2.x)) < 0.1) && (chasing_player == 1))
{
my.skill98 += 1; // frames_blocked will store the number of consecutive frames when the enemy is unable to move
frames_blocked = my.skill98;
}
else // the enemy managed to move?
my.skill98 = 0; // then let's reset frames_blocked
if (my.skill98 > 50) // the enemy was unable to move for 50 consecutive frames?
{
my.skill55 = my.pan;
while (my.pan < (my.skill55 + 90))
{
my.pan += 50 * time_step;
wait (1);
}
my.skill44 = 0;
while (my.skill44 < 1.5) // move sideways for 1.5 seconds
{
my.skill44 += time_step / 16;
ent_animate(my, "run", run_percentage, ANM_CYCLE); // play the "run" animation
run_percentage += 6 * time_step; // 6 = "run" animation speed
c_move (my, vector(10 * time_step, 0, 0), nullvector, IGNORE_PASSABLE | GLIDE);
wait (1);
}
my.skill55 = my.pan;
while (my.pan > (my.skill55 - 90))
{
my.pan -= 50 * time_step;
wait (1);
}
}
}
// the enemy is dead here
my.skill40 = 0; // skill40 controls the "death" animation
while (my.skill40 < 95) // don't play all the animation frames because the result doesn't always look good
{
ent_animate(my, "death", my.skill40, NULL); // play the "death" animation
my.skill40 += 2 * time_step; // "death" animation speed
wait (1);
}
set (my, PASSABLE); // the corpse will be passable from now on
}