xatrix/src/monster/gunner/gunner.c
Yamagi Burmeister 00f0bb8df1 Remove superfluous whitespaces and end of lines
With this commit 'xatrix' is cleaned up. Every single line was audited,
many bugs removed and hundered of sanity checks added. Additionally the
code was reformated. All in all 'Xatrix' should now be much more
reliable and for the first time in ~15 years completeable without any
crashes, logic bugs or the like. Until now about 130 hours spread over
more than a year were spend for this project.

Of course this code is totaly untested. The next step is testing,
testing and testing. After that the spawn points needs to be fixed
so that all maps can pe started by "map $mapname".
2013-02-12 18:49:17 +01:00

860 lines
15 KiB
C

/* =======================================================================
*
* Gunner.
*
* =======================================================================
*/
#include "../../header/local.h"
#include "gunner.h"
static int sound_pain;
static int sound_pain2;
static int sound_death;
static int sound_idle;
static int sound_open;
static int sound_search;
static int sound_sight;
qboolean visible(edict_t *self, edict_t *other);
void GunnerGrenade(edict_t *self);
void GunnerFire(edict_t *self);
void gunner_fire_chain(edict_t *self);
void gunner_refire_chain(edict_t *self);
void gunner_stand(edict_t *self);
void
gunner_idlesound(edict_t *self)
{
if (!self)
{
return;
}
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
}
void
gunner_sight(edict_t *self, edict_t *other)
{
if (!self)
{
return;
}
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
}
void
gunner_search(edict_t *self)
{
if (!self)
{
return;
}
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
}
mframe_t gunner_frames_fidget[] = {
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, gunner_idlesound},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL}
};
mmove_t gunner_move_fidget = {
FRAME_stand31,
FRAME_stand70,
gunner_frames_fidget,
gunner_stand
};
void
gunner_fidget(edict_t *self)
{
if (!self)
{
return;
}
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
{
return;
}
if (random() <= 0.05)
{
self->monsterinfo.currentmove = &gunner_move_fidget;
}
}
mframe_t gunner_frames_stand[] = {
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, gunner_fidget},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, gunner_fidget},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, NULL},
{ai_stand, 0, gunner_fidget}
};
mmove_t gunner_move_stand = {
FRAME_stand01,
FRAME_stand30,
gunner_frames_stand,
NULL
};
void
gunner_stand(edict_t *self)
{
if (!self)
{
return;
}
self->monsterinfo.currentmove = &gunner_move_stand;
}
mframe_t gunner_frames_walk[] = {
{ai_walk, 0, NULL},
{ai_walk, 3, NULL},
{ai_walk, 4, NULL},
{ai_walk, 5, NULL},
{ai_walk, 7, NULL},
{ai_walk, 2, NULL},
{ai_walk, 6, NULL},
{ai_walk, 4, NULL},
{ai_walk, 2, NULL},
{ai_walk, 7, NULL},
{ai_walk, 5, NULL},
{ai_walk, 7, NULL},
{ai_walk, 4, NULL}
};
mmove_t gunner_move_walk = {
FRAME_walk07,
FRAME_walk19,
gunner_frames_walk,
NULL
};
void
gunner_walk(edict_t *self)
{
if (!self)
{
return;
}
self->monsterinfo.currentmove = &gunner_move_walk;
}
mframe_t gunner_frames_run[] = {
{ai_run, 26, NULL},
{ai_run, 9, NULL},
{ai_run, 9, NULL},
{ai_run, 9, NULL},
{ai_run, 15, NULL},
{ai_run, 10, NULL},
{ai_run, 13, NULL},
{ai_run, 6, NULL}
};
mmove_t gunner_move_run = {
FRAME_run01,
FRAME_run08,
gunner_frames_run,
NULL
};
void
gunner_run(edict_t *self)
{
if (!self)
{
return;
}
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
{
self->monsterinfo.currentmove = &gunner_move_stand;
}
else
{
self->monsterinfo.currentmove = &gunner_move_run;
}
}
mframe_t gunner_frames_runandshoot[] = {
{ai_run, 32, NULL},
{ai_run, 15, NULL},
{ai_run, 10, NULL},
{ai_run, 18, NULL},
{ai_run, 8, NULL},
{ai_run, 20, NULL}
};
mmove_t gunner_move_runandshoot = {
FRAME_runs01,
FRAME_runs06,
gunner_frames_runandshoot,
NULL
};
void
gunner_runandshoot(edict_t *self)
{
if (!self)
{
return;
}
self->monsterinfo.currentmove = &gunner_move_runandshoot;
}
mframe_t gunner_frames_pain3[] = {
{ai_move, -3, NULL},
{ai_move, 1, NULL},
{ai_move, 1, NULL},
{ai_move, 0, NULL},
{ai_move, 1, NULL}
};
mmove_t gunner_move_pain3 = {
FRAME_pain301,
FRAME_pain305,
gunner_frames_pain3,
gunner_run
};
mframe_t gunner_frames_pain2[] = {
{ai_move, -2, NULL},
{ai_move, 11, NULL},
{ai_move, 6, NULL},
{ai_move, 2, NULL},
{ai_move, -1, NULL},
{ai_move, -7, NULL},
{ai_move, -2, NULL},
{ai_move, -7, NULL}
};
mmove_t gunner_move_pain2 = {
FRAME_pain201,
FRAME_pain208,
gunner_frames_pain2,
gunner_run
};
mframe_t gunner_frames_pain1[] = {
{ai_move, 2, NULL},
{ai_move, 0, NULL},
{ai_move, -5, NULL},
{ai_move, 3, NULL},
{ai_move, -1, NULL},
{ai_move, 0, NULL},
{ai_move, 0, NULL},
{ai_move, 0, NULL},
{ai_move, 0, NULL},
{ai_move, 1, NULL},
{ai_move, 1, NULL},
{ai_move, 2, NULL},
{ai_move, 1, NULL},
{ai_move, 0, NULL},
{ai_move, -2, NULL},
{ai_move, -2, NULL},
{ai_move, 0, NULL},
{ai_move, 0, NULL}
};
mmove_t gunner_move_pain1 = {
FRAME_pain101,
FRAME_pain118,
gunner_frames_pain1,
gunner_run
};
void
gunner_pain(edict_t *self, edict_t *other /* unused */,
float kick /* unused */, int damage)
{
if (!self)
{
return;
}
if (self->health < (self->max_health / 2))
{
self->s.skinnum = 1;
}
if (level.time < self->pain_debounce_time)
{
return;
}
self->pain_debounce_time = level.time + 3;
if (rand() & 1)
{
gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
}
else
{
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
}
if (skill->value == 3)
{
return; /* no pain anims in nightmare */
}
if (damage <= 10)
{
self->monsterinfo.currentmove = &gunner_move_pain3;
}
else if (damage <= 25)
{
self->monsterinfo.currentmove = &gunner_move_pain2;
}
else
{
self->monsterinfo.currentmove = &gunner_move_pain1;
}
}
void
gunner_dead(edict_t *self)
{
if (!self)
{
return;
}
VectorSet(self->mins, -16, -16, -24);
VectorSet(self->maxs, 16, 16, -8);
self->movetype = MOVETYPE_TOSS;
self->svflags |= SVF_DEADMONSTER;
self->nextthink = 0;
gi.linkentity(self);
}
mframe_t gunner_frames_death[] = {
{ai_move, 0, NULL},
{ai_move, 0, NULL},
{ai_move, 0, NULL},
{ai_move, -7, NULL},
{ai_move, -3, NULL},
{ai_move, -5, NULL},
{ai_move, 8, NULL},
{ai_move, 6, NULL},
{ai_move, 0, NULL},
{ai_move, 0, NULL},
{ai_move, 0, NULL}
};
mmove_t gunner_move_death = {
FRAME_death01,
FRAME_death11,
gunner_frames_death,
gunner_dead
};
void
gunner_die(edict_t *self, edict_t *inflictor /* unused */,
edict_t *attacker /* unused */, int damage /* unused */,
vec3_t point)
{
int n;
if (!self)
{
return;
}
/* check for gib */
if (self->health <= self->gib_health)
{
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
for (n = 0; n < 2; n++)
{
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
}
for (n = 0; n < 4; n++)
{
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
}
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
self->deadflag = DEAD_DEAD;
return;
}
if (self->deadflag == DEAD_DEAD)
{
return;
}
/* regular death */
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
self->deadflag = DEAD_DEAD;
self->takedamage = DAMAGE_YES;
self->monsterinfo.currentmove = &gunner_move_death;
}
void
gunner_duck_down(edict_t *self)
{
if (!self)
{
return;
}
if (self->monsterinfo.aiflags & AI_DUCKED)
{
return;
}
self->monsterinfo.aiflags |= AI_DUCKED;
if (skill->value >= 2)
{
if (random() > 0.5)
{
GunnerGrenade(self);
}
}
self->maxs[2] -= 32;
self->takedamage = DAMAGE_YES;
self->monsterinfo.pausetime = level.time + 1;
gi.linkentity(self);
}
void
gunner_duck_hold(edict_t *self)
{
if (!self)
{
return;
}
if (level.time >= self->monsterinfo.pausetime)
{
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
}
else
{
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
}
}
void
gunner_duck_up(edict_t *self)
{
if (!self)
{
return;
}
self->monsterinfo.aiflags &= ~AI_DUCKED;
self->maxs[2] += 32;
self->takedamage = DAMAGE_AIM;
gi.linkentity(self);
}
mframe_t gunner_frames_duck[] = {
{ai_move, 1, gunner_duck_down},
{ai_move, 1, NULL},
{ai_move, 1, gunner_duck_hold},
{ai_move, 0, NULL},
{ai_move, -1, NULL},
{ai_move, -1, NULL},
{ai_move, 0, gunner_duck_up},
{ai_move, -1, NULL}
};
mmove_t gunner_move_duck = {
FRAME_duck01,
FRAME_duck08,
gunner_frames_duck,
gunner_run
};
void
gunner_dodge(edict_t *self, edict_t *attacker, float eta /* unsued */)
{
if (!self || !attacker)
{
return;
}
if (random() > 0.25)
{
return;
}
if (!self->enemy)
{
self->enemy = attacker;
}
self->monsterinfo.currentmove = &gunner_move_duck;
}
void
gunner_opengun(edict_t *self)
{
if (!self)
{
return;
}
gi.sound(self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
}
void
GunnerFire(edict_t *self)
{
vec3_t start;
vec3_t forward, right;
vec3_t target;
vec3_t aim;
int flash_number;
if (!self)
{
return;
}
flash_number = MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216);
AngleVectors(self->s.angles, forward, right, NULL);
G_ProjectSource(self->s.origin, monster_flash_offset[flash_number],
forward, right, start);
/* project enemy back a bit and target there */
VectorCopy(self->enemy->s.origin, target);
VectorMA(target, -0.2, self->enemy->velocity, target);
target[2] += self->enemy->viewheight;
VectorSubtract(target, start, aim);
VectorNormalize(aim);
monster_fire_bullet(self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD,
DEFAULT_BULLET_VSPREAD, flash_number);
}
void
GunnerGrenade(edict_t *self)
{
vec3_t start;
vec3_t forward, right;
vec3_t aim;
int flash_number;
if (!self)
{
return;
}
if (self->s.frame == FRAME_attak105)
{
flash_number = MZ2_GUNNER_GRENADE_1;
}
else if (self->s.frame == FRAME_attak108)
{
flash_number = MZ2_GUNNER_GRENADE_2;
}
else if (self->s.frame == FRAME_attak111)
{
flash_number = MZ2_GUNNER_GRENADE_3;
}
else
{
flash_number = MZ2_GUNNER_GRENADE_4;
}
AngleVectors(self->s.angles, forward, right, NULL);
G_ProjectSource(self->s.origin, monster_flash_offset[flash_number],
forward, right, start);
VectorCopy(forward, aim);
monster_fire_grenade(self, start, aim, 50, 600, flash_number);
}
mframe_t gunner_frames_attack_chain[] = {
{ai_charge, 0, gunner_opengun},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL}
};
mmove_t gunner_move_attack_chain = {
FRAME_attak209,
FRAME_attak215,
gunner_frames_attack_chain,
gunner_fire_chain
};
mframe_t gunner_frames_fire_chain[] = {
{ai_charge, 0, GunnerFire},
{ai_charge, 0, GunnerFire},
{ai_charge, 0, GunnerFire},
{ai_charge, 0, GunnerFire},
{ai_charge, 0, GunnerFire},
{ai_charge, 0, GunnerFire},
{ai_charge, 0, GunnerFire},
{ai_charge, 0, GunnerFire}
};
mmove_t gunner_move_fire_chain = {
FRAME_attak216,
FRAME_attak223,
gunner_frames_fire_chain,
gunner_refire_chain
};
mframe_t gunner_frames_endfire_chain[] = {
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL}
};
mmove_t gunner_move_endfire_chain = {
FRAME_attak224,
FRAME_attak230,
gunner_frames_endfire_chain,
gunner_run
};
mframe_t gunner_frames_attack_grenade[] = {
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, GunnerGrenade},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, GunnerGrenade},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, GunnerGrenade},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, GunnerGrenade},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL},
{ai_charge, 0, NULL}
};
mmove_t gunner_move_attack_grenade = {
FRAME_attak101,
FRAME_attak121,
gunner_frames_attack_grenade,
gunner_run
};
void
gunner_attack(edict_t *self)
{
if (!self)
{
return;
}
if (range(self, self->enemy) == RANGE_MELEE)
{
self->monsterinfo.currentmove = &gunner_move_attack_chain;
}
else
{
if (random() <= 0.5)
{
self->monsterinfo.currentmove = &gunner_move_attack_grenade;
}
else
{
self->monsterinfo.currentmove = &gunner_move_attack_chain;
}
}
}
void
gunner_fire_chain(edict_t *self)
{
if (!self)
{
return;
}
self->monsterinfo.currentmove = &gunner_move_fire_chain;
}
void
gunner_refire_chain(edict_t *self)
{
if (!self)
{
return;
}
if (self->enemy->health > 0)
{
if (visible(self, self->enemy))
{
if (random() <= 0.5)
{
self->monsterinfo.currentmove = &gunner_move_fire_chain;
return;
}
}
}
self->monsterinfo.currentmove = &gunner_move_endfire_chain;
}
/*
* QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
*/
void
SP_monster_gunner(edict_t *self)
{
if (!self)
{
return;
}
if (deathmatch->value)
{
G_FreeEdict(self);
return;
}
sound_death = gi.soundindex("gunner/death1.wav");
sound_pain = gi.soundindex("gunner/gunpain2.wav");
sound_pain2 = gi.soundindex("gunner/gunpain1.wav");
sound_idle = gi.soundindex("gunner/gunidle1.wav");
sound_open = gi.soundindex("gunner/gunatck1.wav");
sound_search = gi.soundindex("gunner/gunsrch1.wav");
sound_sight = gi.soundindex("gunner/sight1.wav");
gi.soundindex("gunner/gunatck2.wav");
gi.soundindex("gunner/gunatck3.wav");
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
self->s.modelindex = gi.modelindex("models/monsters/gunner/tris.md2");
VectorSet(self->mins, -16, -16, -24);
VectorSet(self->maxs, 16, 16, 32);
self->health = 175;
self->gib_health = -70;
self->mass = 200;
self->pain = gunner_pain;
self->die = gunner_die;
self->monsterinfo.stand = gunner_stand;
self->monsterinfo.walk = gunner_walk;
self->monsterinfo.run = gunner_run;
self->monsterinfo.dodge = gunner_dodge;
self->monsterinfo.attack = gunner_attack;
self->monsterinfo.melee = NULL;
self->monsterinfo.sight = gunner_sight;
self->monsterinfo.search = gunner_search;
gi.linkentity(self);
self->monsterinfo.currentmove = &gunner_move_stand;
self->monsterinfo.scale = MODEL_SCALE;
walkmonster_start(self);
}