1559 lines
43 KiB
C
1559 lines
43 KiB
C
|
// ai_whore.c
|
||
|
#include "g_local.h"
|
||
|
#include "ai_whore.h"
|
||
|
|
||
|
#include "voice_bitch.h"
|
||
|
|
||
|
void whore_end_stand( edict_t *self );
|
||
|
void whore_firegun( edict_t *self );
|
||
|
void whore_firegun_left( edict_t *self );
|
||
|
void whore_firegun_right( edict_t *self );
|
||
|
void whore_talk_think( edict_t *self );
|
||
|
// Joseph 20-NOV-98
|
||
|
void think_killedict( edict_t *self );
|
||
|
void think_playthud( edict_t *self );
|
||
|
// END JOSEPH
|
||
|
|
||
|
void whore_firegun_cr (edict_t *self);
|
||
|
void whore_shotgun_reload( edict_t *self );
|
||
|
|
||
|
void whore_reload_snd (edict_t *self);
|
||
|
|
||
|
void whore_firehmg( edict_t *self );
|
||
|
void whore_firehmg_delay (edict_t *self);
|
||
|
void whore_hmg_avoid (edict_t *self);
|
||
|
|
||
|
void whore_bazooka_delay (edict_t *self);
|
||
|
|
||
|
// =========================================================================
|
||
|
|
||
|
#include "ai_whore_tables.h"
|
||
|
|
||
|
// =========================================================================
|
||
|
|
||
|
#define WHORE_FLAMEGUN 4
|
||
|
#define WHORE_BAZOOKA 8
|
||
|
#define WHORE_HMG 16
|
||
|
|
||
|
#define WHORE_TOMMYGUN 64 // spawnflag
|
||
|
|
||
|
#define WHORE_GRENADE 128
|
||
|
#define WHORE_SHOTGUN 8192
|
||
|
|
||
|
|
||
|
int tommy_soundindex;
|
||
|
int shotgun_soundindex;
|
||
|
int shotgun_reload_soundindex;
|
||
|
int heavymachinegun_soundindex;
|
||
|
int rocketgun_soundindex;
|
||
|
|
||
|
void whore_reload_snd (edict_t *self)
|
||
|
{
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/machinegun/machgcock.wav"), 1, ATTN_NORM, 0);
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/machinegun/machgcock.wav"), 1, ATTN_NORM, 0);
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/rocket_launcher/reload.wav"), 1, ATTN_NORM, 0);
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/grenade_launcher/reload.wav"), 1, ATTN_NORM, 0);
|
||
|
}
|
||
|
|
||
|
void whore_catch_fire( edict_t *self, edict_t *other )
|
||
|
{
|
||
|
self->enemy = NULL; // stop attacking
|
||
|
self->cast_info.currentmove = &whore_move_run_on_fire;
|
||
|
}
|
||
|
|
||
|
void whore_talk_think( edict_t *self )
|
||
|
{
|
||
|
AI_TalkThink( self, false );
|
||
|
}
|
||
|
|
||
|
void whore_talk( edict_t *self )
|
||
|
{
|
||
|
float rnd;
|
||
|
|
||
|
// only make talking jester if we've recently said something
|
||
|
if (!(self->cast_info.aiflags & AI_REPEAT_TALK_JESTURE) && self->last_talk_time < (level.time - 1.0))
|
||
|
{
|
||
|
self->cast_info.currentmove = self->cast_info.move_stand;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self->cast_info.currentmove != self->cast_info.move_stand)
|
||
|
return;
|
||
|
|
||
|
if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
{
|
||
|
rnd = random() * 10;
|
||
|
|
||
|
if (rnd < 1)
|
||
|
self->cast_info.currentmove = &whore_move_sg_stand;
|
||
|
else if (rnd < 2)
|
||
|
self->cast_info.currentmove = &whore_move_sg_talk1;
|
||
|
else if (rnd < 3)
|
||
|
self->cast_info.currentmove = &whore_move_sg_talk2;
|
||
|
else if (rnd < 4)
|
||
|
self->cast_info.currentmove = &whore_move_sg_talk3;
|
||
|
//else if (rnd < 5)
|
||
|
// self->cast_info.currentmove = &whore_move_sg_talk4;
|
||
|
else if (rnd < 6)
|
||
|
self->cast_info.currentmove = &whore_move_sg_talk5;
|
||
|
//else if (rnd < 7)
|
||
|
// self->cast_info.currentmove = &whore_move_sg_talk6;
|
||
|
else if (rnd < 8)
|
||
|
self->cast_info.currentmove = &whore_move_sg_talk7;
|
||
|
else
|
||
|
self->cast_info.currentmove = &whore_move_sg_talk8;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rnd = random() * 6;
|
||
|
|
||
|
if (rnd < 1)
|
||
|
self->cast_info.currentmove = &whore_move_talk1;
|
||
|
else if (rnd < 2)
|
||
|
self->cast_info.currentmove = &whore_move_talk2;
|
||
|
else if (rnd < 3)
|
||
|
self->cast_info.currentmove = &whore_move_talk3;
|
||
|
else if (rnd < 4)
|
||
|
self->cast_info.currentmove = &whore_move_talk4;
|
||
|
else/* if (rnd < 5)*/
|
||
|
self->cast_info.currentmove = &whore_move_talk5;
|
||
|
//else
|
||
|
// self->cast_info.currentmove = &whore_move_talk6;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void whore_avoid ( edict_t *self, edict_t *other, qboolean face )
|
||
|
{
|
||
|
vec3_t vec;
|
||
|
|
||
|
if (self->health <= 0)
|
||
|
return;
|
||
|
|
||
|
if (!self->groundentity)
|
||
|
return;
|
||
|
|
||
|
if (!other)
|
||
|
{
|
||
|
self->cast_info.currentmove = self->cast_info.move_stand;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
self->cast_info.last_avoid = level.time;
|
||
|
self->cast_info.avoid_ent = NULL;
|
||
|
|
||
|
if (face)
|
||
|
{ // turn to face them
|
||
|
VectorSubtract( other->s.origin, self->s.origin, vec );
|
||
|
self->cast_info.avoid_ent = other;
|
||
|
}
|
||
|
else
|
||
|
{ // turn to face away from them
|
||
|
VectorSubtract( self->s.origin, other->s.origin, vec );
|
||
|
}
|
||
|
VectorNormalize( vec );
|
||
|
|
||
|
self->ideal_yaw = vectoyaw( vec );
|
||
|
|
||
|
if (self->maxs[2] > DUCKING_MAX_Z)
|
||
|
{
|
||
|
if (self->cast_info.aiflags & AI_NOWALK_FACE)
|
||
|
{
|
||
|
// if (face)
|
||
|
{
|
||
|
int side_result;
|
||
|
|
||
|
side_result = AI_SideTrace( self, 48, 90, SIDE_RANDOM );
|
||
|
|
||
|
if (side_result == AI_SideTrace( self, 48, 90 + (self->ideal_yaw - self->s.angles[YAW]), side_result ))
|
||
|
{
|
||
|
if (side_result < 0)
|
||
|
self->cast_info.currentmove = &whore_move_lside_step;
|
||
|
//else
|
||
|
// self->cast_info.currentmove = &whore_move_rside_step;
|
||
|
// hack
|
||
|
// note to self: they forgot to make an rside anim
|
||
|
else
|
||
|
self->cast_info.currentmove = &whore_move_avoid_walk;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// if no move set, just turn to face
|
||
|
M_ChangeYaw( self );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((VectorDistance( self->s.origin, other->s.origin ) > 72) || !face)
|
||
|
self->cast_info.currentmove = &whore_move_avoid_walk;
|
||
|
else
|
||
|
self->cast_info.currentmove = &whore_move_avoid_reverse_walk;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_avoid_crouch_walk;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void whore_end_stand( edict_t *self )
|
||
|
{
|
||
|
if (self->cast_info.move_stand_evade && (self->last_stand_evade > (level.time - 3)))
|
||
|
return;
|
||
|
|
||
|
if (self->cast_info.currentmove == self->cast_info.move_crstand)
|
||
|
return;
|
||
|
|
||
|
// if ( ((!self->cast_group) && (random() < 0.8))
|
||
|
// || ((self->cast_group) && (random() < 0.3)))
|
||
|
{ // stand normally
|
||
|
|
||
|
if ( (self->cast_info.currentmove != &whore_move_talk5)
|
||
|
&& (random() < 0.3) && self->spawnflags & WHORE_TOMMYGUN)
|
||
|
{ // randomly wipe our face
|
||
|
self->cast_info.currentmove = &whore_move_talk5;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self->cast_info.currentmove = self->cast_info.move_stand;
|
||
|
}
|
||
|
|
||
|
// return;
|
||
|
}
|
||
|
|
||
|
AI_CheckTalk(self);
|
||
|
}
|
||
|
|
||
|
void whore_long_attack( edict_t *self )
|
||
|
{
|
||
|
// keep running, and try to fire if possible
|
||
|
|
||
|
if (self->maxs[2] < self->cast_info.standing_max_z)
|
||
|
return;
|
||
|
|
||
|
// self->cast_info.currentmove = &whore_move_run_shoot;
|
||
|
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
self->cast_info.currentmove = &whore_move_run_tg_sht;
|
||
|
else if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
self->cast_info.currentmove = &whore_move_run_shg_sht;
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
self->cast_info.currentmove = &whore_move_run_hmg_sht;
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
self->cast_info.currentmove = &whore_move_run_bazooka_sht;
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
self->cast_info.currentmove = &whore_move_run_flamegun_sht;
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
self->cast_info.currentmove = &whore_move_run_grenade_sht;
|
||
|
else
|
||
|
{
|
||
|
gi.dprintf ("actor is missing weapon spawnflag\n");
|
||
|
self->cast_info.currentmove = &whore_move_run_shg_sht;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qboolean whore_attack( edict_t *self )
|
||
|
{
|
||
|
|
||
|
if (self->maxs[2] < self->cast_info.standing_max_z)
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_crouch_shoot;
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vec3_t vec;
|
||
|
float dist;
|
||
|
|
||
|
|
||
|
VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
|
||
|
dist = VectorNormalize( vec );
|
||
|
|
||
|
// Special case: enemy has flamethrower, run backwards, keep firing
|
||
|
if ( (dist < 384) // they're close
|
||
|
&& (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
&& (self->enemy->client)
|
||
|
&& (self->enemy->client->pers.weapon)
|
||
|
&& (stricmp(self->enemy->client->pers.weapon->classname, "weapon_flamethrower") == 0))
|
||
|
{
|
||
|
int side_result;
|
||
|
|
||
|
// see if we can go backwards
|
||
|
if (side_result = AI_SideTrace( self, -64, 0, 1 ))
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_run_reverse_tg_sht;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// if just popped out from a corner, just stand here
|
||
|
if (self->dont_takecover_time > (level.time - 2))
|
||
|
{
|
||
|
goto stand_shoot;
|
||
|
}
|
||
|
|
||
|
|
||
|
if ((skill->value > random()*2) && (self->cast_info.last_side_attack_time < (level.time - 1)) && (dist > 128))
|
||
|
{
|
||
|
// attempt a side-ways attack
|
||
|
|
||
|
int side_result, side_result2;
|
||
|
|
||
|
side_result = AI_SideTrace( self, 64, 90, -self->cast_info.last_side_attack );
|
||
|
|
||
|
if (side_result)
|
||
|
{
|
||
|
side_result2 = AI_SideTrace( self, 128, 90, side_result );
|
||
|
|
||
|
if (side_result2 == side_result)
|
||
|
{
|
||
|
/*
|
||
|
if (side_result < 0)
|
||
|
self->cast_info.currentmove = &whore_move_lside_run;
|
||
|
else
|
||
|
self->cast_info.currentmove = &whore_move_rside_run;
|
||
|
*/
|
||
|
if (side_result < 0)
|
||
|
{
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
self->cast_info.currentmove = &whore_move_lsd_tg_run;
|
||
|
else if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
self->cast_info.currentmove = &whore_move_lsd_shg_run;
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
// self->cast_info.currentmove = &whore_move_lsd_hmg_run;
|
||
|
self->cast_info.currentmove = &whore_move_walk_hmg_sht;
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
self->cast_info.currentmove = &whore_move_lsd_bazooka_run;
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
self->cast_info.currentmove = &whore_move_lsd_flamegun_run;
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
self->cast_info.currentmove = &whore_move_lsd_grenade_run;
|
||
|
else
|
||
|
{
|
||
|
gi.dprintf ("missing weapon spawnflag\n");
|
||
|
self->cast_info.currentmove = &whore_move_lsd_shg_run;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
self->cast_info.currentmove = &whore_move_rsd_tg_run;
|
||
|
else if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
self->cast_info.currentmove = &whore_move_rsd_shg_run;
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
// self->cast_info.currentmove = &whore_move_rsd_hmg_run;
|
||
|
self->cast_info.currentmove = &whore_move_walk_hmg_sht;
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
self->cast_info.currentmove = &whore_move_rsd_bazooka_run;
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
self->cast_info.currentmove = &whore_move_rsd_flamegun_run;
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
self->cast_info.currentmove = &whore_move_rsd_grenade_run;
|
||
|
else
|
||
|
{
|
||
|
gi.dprintf ("missing weapon spawnflag\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
self->ideal_yaw = vectoyaw(vec) + side_result * 90;
|
||
|
|
||
|
M_ChangeYaw( self );
|
||
|
|
||
|
self->cast_info.last_side_attack = side_result;
|
||
|
self->cast_info.last_side_attack_time = level.time;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
self->ideal_yaw = vectoyaw(vec);
|
||
|
|
||
|
M_ChangeYaw( self );
|
||
|
|
||
|
// just shoot like normal
|
||
|
|
||
|
// walk shooting?
|
||
|
if ((dist > 256) && directly_infront( self, self->enemy ) && AI_SideTrace(self, 32, 0, 1 ) )
|
||
|
{
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
self->cast_info.currentmove = &whore_move_walk_tg_sht;
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
self->cast_info.currentmove = &whore_move_walk_hmg_sht;
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
self->cast_info.currentmove = &whore_move_walk_bazooka_sht;
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
self->cast_info.currentmove = &whore_move_walk_flamegun_sht;
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
self->cast_info.currentmove = &whore_move_walk_grenade_sht;
|
||
|
else if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
{
|
||
|
if (dist > 800) // shotgun dude should get closer
|
||
|
self->cast_info.currentmove = &whore_move_walk_shg_sht;
|
||
|
else
|
||
|
self->cast_info.currentmove = &whore_move_run_shg_sht;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
/* TODO: Reverse walking anim table
|
||
|
else if ((dist < 128) && AI_SideTrace(self, -32, 0, 1 ) )
|
||
|
{
|
||
|
self->cast_info.currentmove = &thug_move_reverse_walk_shoot;
|
||
|
return true;
|
||
|
}
|
||
|
*/
|
||
|
// normal attack
|
||
|
|
||
|
stand_shoot:
|
||
|
|
||
|
self->ideal_yaw = vectoyaw(vec);
|
||
|
|
||
|
M_ChangeYaw( self );
|
||
|
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
self->cast_info.currentmove = &whore_move_tg_shoot;
|
||
|
else if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
self->cast_info.currentmove = &whore_move_shg_shoot;
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
self->cast_info.currentmove = &whore_move_hmg_shoot;
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
self->cast_info.currentmove = &whore_move_bazooka_shoot;
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
self->cast_info.currentmove = &whore_move_flamegun_shoot;
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
self->cast_info.currentmove = &whore_move_grenade_shoot;
|
||
|
else
|
||
|
{
|
||
|
gi.dprintf ("missing weapon spawnflag\n");
|
||
|
self->cast_info.currentmove = &whore_move_shg_shoot;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void whore_firegun_cr ( edict_t *self )
|
||
|
{
|
||
|
if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
return;
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
return;
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
return;
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
return;
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
return;
|
||
|
else if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
whore_firegun (self);
|
||
|
else
|
||
|
gi.dprintf ("error : not tagged right!\n");
|
||
|
}
|
||
|
|
||
|
void whore_hmg_avoid (edict_t *self)
|
||
|
{
|
||
|
if (self->s.frame == FRAME_tg_shoot_14)
|
||
|
self->cast_info.avoid(self, self->enemy, true);
|
||
|
else
|
||
|
self->cast_info.avoid(self, self->enemy, false);
|
||
|
}
|
||
|
|
||
|
void whore_firehmg_delay (edict_t *self)
|
||
|
{
|
||
|
|
||
|
if (self->s.frame == FRAME_tg_shoot_14)
|
||
|
self->cast_info.currentmove = &whore_move_hmg_shoot2;
|
||
|
else if (self->s.frame == FRAME_walk_tg_sht_10)
|
||
|
self->cast_info.currentmove = &whore_move_walk_hmg_sht2;
|
||
|
else
|
||
|
self->cast_info.currentmove = &whore_move_run_hmg_sht2;
|
||
|
}
|
||
|
|
||
|
void whore_bazooka_delay (edict_t *self)
|
||
|
{
|
||
|
if (self->s.frame == FRAME_tg_shoot_14)
|
||
|
self->cast_info.currentmove = &whore_move_bazooka_shoot2;
|
||
|
else if (self->s.frame == FRAME_walk_tg_sht_10)
|
||
|
self->cast_info.currentmove = &whore_move_walk_bazooka_shoot2;
|
||
|
else
|
||
|
self->cast_info.currentmove = &whore_move_run_bazooka_shoot2;
|
||
|
}
|
||
|
|
||
|
void whore_firehmg( edict_t *self )
|
||
|
{
|
||
|
|
||
|
vec3_t start;
|
||
|
vec3_t forward, right;
|
||
|
vec3_t target;
|
||
|
vec3_t aim;
|
||
|
vec3_t offset;
|
||
|
int flash_number;
|
||
|
float dist;
|
||
|
|
||
|
if (!AI_BeginAttack( self ))
|
||
|
{
|
||
|
// self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
||
|
self->cast_info.currentmove = self->cast_info.move_stand;
|
||
|
|
||
|
// may need to hack this
|
||
|
// self->s.frame == standframe;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self->duration++ > 30)
|
||
|
{
|
||
|
self->duration = 0;
|
||
|
self->cast_info.currentmove = &whore_move_tg_reload;
|
||
|
self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// yell at them?
|
||
|
if (self->last_talk_time < (level.time - TALK_FIGHTING_DELAY))
|
||
|
{
|
||
|
|
||
|
if (self->name_index == NAME_BLUNT)
|
||
|
{
|
||
|
Voice_Random (self, self->enemy, &blunt[6], 10);
|
||
|
}
|
||
|
else
|
||
|
Voice_Random(self, self->enemy, f_fightsounds, F_NUM_FIGHTING);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
VectorSet(offset, 0, 8, self->viewheight-8);
|
||
|
|
||
|
AngleVectors (self->s.angles, forward, right, NULL);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, start);
|
||
|
|
||
|
// project enemy back a bit and target there
|
||
|
VectorCopy (self->enemy->s.origin, target);
|
||
|
VectorMA (target, (-0.5 * (crandom())) * (1.0 - (skill->value/4.0)), self->enemy->velocity, target);
|
||
|
|
||
|
if (self->enemy->maxs[2] < self->cast_info.standing_max_z)
|
||
|
target[2] += - ( 8 * random());
|
||
|
else
|
||
|
target[2] += self->enemy->viewheight - 4 - (16 * random());
|
||
|
|
||
|
flash_number = MZ2_GUNNER_MACHINEGUN_1;
|
||
|
|
||
|
VectorSubtract (target, start, aim);
|
||
|
dist = VectorNormalize (aim);
|
||
|
}
|
||
|
|
||
|
self->ideal_yaw = vectoyaw( aim );
|
||
|
|
||
|
{
|
||
|
fire_bullet (self, start, aim, 15, 50, DEFAULT_BULLET_HSPREAD>>self->acc, DEFAULT_BULLET_VSPREAD>>self->acc, MOD_BARMACHINEGUN);
|
||
|
{
|
||
|
if (self->s.frame == FRAME_tg_shoot_01
|
||
|
|| self->s.frame == FRAME_walk_tg_sht_01
|
||
|
|| self->s.frame == FRAME_run_tg_sht_01)
|
||
|
{
|
||
|
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/hmg/hmg.wav"), 1, ATTN_NORM, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
void whore_firegun( edict_t *self )
|
||
|
{
|
||
|
vec3_t start;
|
||
|
vec3_t forward, right;
|
||
|
vec3_t target;
|
||
|
vec3_t aim;
|
||
|
vec3_t offset;
|
||
|
int flash_number;
|
||
|
float dist;
|
||
|
|
||
|
if (self->cast_info.aiflags & AI_RELOAD)
|
||
|
{ // we need to reload first, just do it now (since it's a similar motion)
|
||
|
whore_shotgun_reload( self );
|
||
|
self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
{
|
||
|
if ( (self->maxs[2] == self->cast_info.standing_max_z)
|
||
|
&& !(self->cast_info.aiflags & AI_SIDE_ATTACK)
|
||
|
&& (self->duration++ > 40))
|
||
|
{
|
||
|
|
||
|
self->duration = 0;
|
||
|
self->cast_info.currentmove = &whore_move_tg_reload;
|
||
|
self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
{
|
||
|
|
||
|
if (self->durationflag)
|
||
|
{
|
||
|
self->durationflag++;
|
||
|
if (self->durationflag > 6)
|
||
|
self->durationflag = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self->lastduration++ > 2)
|
||
|
self->lastduration = 0;
|
||
|
|
||
|
if ( (self->maxs[2] == self->cast_info.standing_max_z)
|
||
|
/*&& !(self->cast_info.aiflags & AI_SIDE_ATTACK)*/
|
||
|
&& (self->duration++ > 30))
|
||
|
{
|
||
|
self->duration = 0;
|
||
|
self->cast_info.currentmove = &whore_move_tg_reload;
|
||
|
self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
{
|
||
|
if ( (self->maxs[2] == self->cast_info.standing_max_z)
|
||
|
&& !(self->cast_info.aiflags & AI_SIDE_ATTACK)
|
||
|
&& (self->duration++ > 3))
|
||
|
{
|
||
|
|
||
|
self->duration = 0;
|
||
|
self->cast_info.currentmove = &whore_move_tg_reload;
|
||
|
self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
{
|
||
|
if ( (self->maxs[2] == self->cast_info.standing_max_z)
|
||
|
&& !(self->cast_info.aiflags & AI_SIDE_ATTACK)
|
||
|
&& (self->duration++ > 2))
|
||
|
{
|
||
|
|
||
|
self->duration = 0;
|
||
|
self->cast_info.currentmove = &whore_move_tg_reload;
|
||
|
self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!AI_BeginAttack( self ))
|
||
|
{
|
||
|
self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// yell at them?
|
||
|
if (self->last_talk_time < (level.time - TALK_FIGHTING_DELAY))
|
||
|
{
|
||
|
//if (self->cast_group != 1)
|
||
|
Voice_Random(self, self->enemy, f_fightsounds, F_NUM_FIGHTING);
|
||
|
//else
|
||
|
// Voice_Random(self, self->enemy, f_friendlycombat, F_NUM_FRIENDLYCOMBAT );
|
||
|
}
|
||
|
|
||
|
// fire the gun
|
||
|
if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
{
|
||
|
VectorSet(offset, 0, 8, self->viewheight-8);
|
||
|
|
||
|
AngleVectors (self->s.angles, forward, right, NULL);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, start);
|
||
|
|
||
|
// project enemy back a bit and target there
|
||
|
VectorCopy (self->enemy->s.origin, target);
|
||
|
|
||
|
target[2] -= 24; // this will help create more splash damage
|
||
|
|
||
|
VectorMA (target, (-0.5 * (crandom())) * (1.0 - (skill->value/4.0)), self->enemy->velocity, target);
|
||
|
|
||
|
if (self->enemy->maxs[2] < self->cast_info.standing_max_z)
|
||
|
target[2] += - ( 8 * random());
|
||
|
else
|
||
|
target[2] += self->enemy->viewheight - 4 - (16 * random());
|
||
|
|
||
|
flash_number = MZ2_GUNNER_MACHINEGUN_1;
|
||
|
|
||
|
VectorSubtract (target, start, aim);
|
||
|
dist = VectorNormalize (aim);
|
||
|
|
||
|
// idiot check
|
||
|
{
|
||
|
// clean shot???
|
||
|
trace_t tr;
|
||
|
vec3_t mins;
|
||
|
vec3_t maxs;
|
||
|
vec3_t origin;
|
||
|
vec3_t destination;
|
||
|
|
||
|
VectorCopy (self->s.origin, origin);
|
||
|
origin[2] += self->viewheight;
|
||
|
|
||
|
VectorCopy (self->enemy->s.origin, destination);
|
||
|
destination[2] += self->enemy->viewheight;
|
||
|
|
||
|
VectorSet (mins, -8, -8, -8);
|
||
|
VectorSet (maxs, 8, 8, 8);
|
||
|
|
||
|
tr = gi.trace (origin, mins, maxs, destination, self, MASK_SHOT);
|
||
|
|
||
|
if (tr.ent != self->enemy)
|
||
|
{
|
||
|
if (!(AI_ForceTakeCover( self, self->enemy, false )))
|
||
|
{
|
||
|
self->cast_info.avoid(self, self->enemy, false);
|
||
|
}
|
||
|
|
||
|
if (self->duration)
|
||
|
self->duration--;
|
||
|
return;
|
||
|
}
|
||
|
goto skipbail; // causes bazooka dude to shoot twice
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VectorSet(offset, 0, 8, self->viewheight-8);
|
||
|
|
||
|
AngleVectors (self->s.angles, forward, right, NULL);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, start);
|
||
|
|
||
|
// project enemy back a bit and target there
|
||
|
VectorCopy (self->enemy->s.origin, target);
|
||
|
VectorMA (target, (-0.5 * (crandom())) * (1.0 - (skill->value/4.0)), self->enemy->velocity, target);
|
||
|
|
||
|
if (self->enemy->maxs[2] < self->cast_info.standing_max_z)
|
||
|
target[2] += - ( 8 * random());
|
||
|
else
|
||
|
target[2] += self->enemy->viewheight - 4 - (16 * random());
|
||
|
|
||
|
flash_number = MZ2_GUNNER_MACHINEGUN_1;
|
||
|
|
||
|
VectorSubtract (target, start, aim);
|
||
|
dist = VectorNormalize (aim);
|
||
|
}
|
||
|
|
||
|
// look for a new attack?
|
||
|
if ( (dist < self->cast_info.max_attack_distance)
|
||
|
&& ( ((random() < 0.1) && (self->cast_info.last_side_attack_time < (level.time - 2)))
|
||
|
|| ( (!(self->cast_info.aiflags & AI_SIDE_ATTACK) || (self->cast_info.last_side_attack_time < (level.time - 2)))
|
||
|
&& (directly_infront( self->enemy, self))
|
||
|
&& (self->enemy->client)
|
||
|
&& (self->enemy->client->pers.weapon)
|
||
|
&& (self->enemy->client->pers.weapon->ammo)))) // if we are directly infront of them, try to strafe
|
||
|
{
|
||
|
if (self->cast_info.aiflags & AI_SIDE_ATTACK)
|
||
|
{
|
||
|
// self->cast_info.currentmove = &whore_move_shoot;
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
self->cast_info.currentmove = &whore_move_run_tg_sht;
|
||
|
else if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
self->cast_info.currentmove = &whore_move_run_shg_sht;
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
self->cast_info.currentmove = &whore_move_run_hmg_sht;
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
self->cast_info.currentmove = &whore_move_run_bazooka_sht;
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
self->cast_info.currentmove = &whore_move_run_flamegun_sht;
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
self->cast_info.currentmove = &whore_move_run_grenade_sht;
|
||
|
else
|
||
|
{
|
||
|
gi.dprintf ("missing weapon spawnflag\n");
|
||
|
self->cast_info.currentmove = &whore_move_run_shg_sht;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
self->cast_info.attack( self );
|
||
|
}
|
||
|
|
||
|
skipbail:
|
||
|
|
||
|
self->ideal_yaw = vectoyaw( aim );
|
||
|
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
{
|
||
|
if (self->acc)
|
||
|
cast_fire_bullet (self, start, aim, 4, 0, DEFAULT_BULLET_HSPREAD>>self->acc, DEFAULT_BULLET_VSPREAD>>self->acc, flash_number);
|
||
|
else
|
||
|
cast_fire_bullet (self, start, aim, 4, 0, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
|
||
|
gi.sound(self, CHAN_WEAPON, tommy_soundindex, 1, ATTN_NORM, 0);
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
{
|
||
|
// tweeking the damage
|
||
|
cast_fire_shotgun (self, start, aim, 6, 0, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, flash_number);
|
||
|
gi.sound(self, CHAN_WEAPON, shotgun_soundindex, 1, ATTN_NORM, 0);
|
||
|
self->cast_info.aiflags |= AI_RELOAD;
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
{
|
||
|
fire_bullet (self, start, aim, 15, 50, DEFAULT_BULLET_HSPREAD>>self->acc, DEFAULT_BULLET_VSPREAD>>self->acc, MOD_BARMACHINEGUN);
|
||
|
{
|
||
|
if (self->lastduration == 1)
|
||
|
{
|
||
|
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/hmg/hmg.wav"), 1, ATTN_NORM, 0);
|
||
|
}
|
||
|
|
||
|
if (self->lastduration == 0)
|
||
|
self->durationflag = 1;
|
||
|
}
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
{
|
||
|
{
|
||
|
int damage = 100;
|
||
|
int speed = 900;
|
||
|
|
||
|
cast_fire_rocket (self, start, aim, damage, speed, MOD_ROCKET);
|
||
|
gi.sound (self, CHAN_WEAPON, rocketgun_soundindex, 1, ATTN_NORM, 0);
|
||
|
}
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
{
|
||
|
int damage = 1;
|
||
|
int kick = 4;
|
||
|
static int flamesnd = 0;
|
||
|
|
||
|
extern void fire_target_flamethrower (edict_t *self, vec3_t start, vec3_t forward, int damage, int kick, int mod);
|
||
|
|
||
|
fire_flamethrower(self, start, aim, damage, 0, MOD_FLAMETHROWER);
|
||
|
self->s.renderfx2 |= RF2_FLAMETHROWER;
|
||
|
// fire_target_flamethrower(self, start, aim, damage, 0, MOD_FLAMETHROWER);
|
||
|
// self->s.renderfx2 = RF2_FLAMESHOOTER;
|
||
|
|
||
|
flamesnd++;
|
||
|
|
||
|
if (flamesnd > 2)
|
||
|
flamesnd = 0;
|
||
|
|
||
|
if (flamesnd == 1)
|
||
|
gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/flame_thrower/flame2.wav"), 1, ATTN_NORM, 0);
|
||
|
else if (flamesnd == 2)
|
||
|
gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/flame_thrower/flame3.wav"), 1, ATTN_NORM, 0);
|
||
|
else
|
||
|
gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/flame_thrower/flame1.wav"), 1, ATTN_NORM, 0);
|
||
|
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
{
|
||
|
fire_grenade (self, start, aim, 150, 450, 2.0, 256);
|
||
|
gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/grenade_launcher/gl_fire.wav"), 1, ATTN_NORM, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gi.dprintf ("Error: Bang Bang not tagged right\n");
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void whore_shotgun_reload( edict_t *self )
|
||
|
{
|
||
|
if (self->cast_info.aiflags & AI_TURN_BLOCKED)
|
||
|
{ // abort the side run
|
||
|
self->cast_info.aiflags &= ~AI_TURN_BLOCKED;
|
||
|
AI_EndAttack(self);
|
||
|
}
|
||
|
|
||
|
if (!(self->spawnflags & WHORE_SHOTGUN))
|
||
|
{ // no need to reload
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gi.sound(self, CHAN_AUTO, shotgun_reload_soundindex, 1, ATTN_NORM, 0);
|
||
|
self->cast_info.aiflags &= ~AI_RELOAD;
|
||
|
}
|
||
|
|
||
|
void whore_firegun_right( edict_t *self )
|
||
|
{
|
||
|
// vec3_t vec;
|
||
|
// float oldyaw;
|
||
|
|
||
|
if (!self->enemy || (self->enemy->health <= 0))
|
||
|
{
|
||
|
self->cast_info.currentmove = self->cast_info.move_stand;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self->cast_info.aiflags & AI_TURN_BLOCKED)
|
||
|
{ // abort the side run
|
||
|
self->cast_info.aiflags &= ~AI_TURN_BLOCKED;
|
||
|
AI_EndAttack(self);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// if the path ahead is not clear, abort
|
||
|
if (!AI_SideTrace( self, 96, 0, 1 ))
|
||
|
{
|
||
|
AI_EndAttack(self);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
oldyaw = self->s.angles[YAW];
|
||
|
|
||
|
VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
|
||
|
VectorNormalize( vec );
|
||
|
|
||
|
self->s.angles[YAW] = vectoyaw( vec );
|
||
|
*/
|
||
|
self->cast_info.aiflags |= AI_SIDE_ATTACK;
|
||
|
whore_firegun(self);
|
||
|
self->cast_info.aiflags &= ~AI_SIDE_ATTACK;
|
||
|
/*
|
||
|
// keep running to the side
|
||
|
self->ideal_yaw = self->s.angles[YAW] + 90;
|
||
|
|
||
|
self->s.angles[YAW] = oldyaw;
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
void whore_firegun_left( edict_t *self )
|
||
|
{
|
||
|
// vec3_t vec;
|
||
|
// float oldyaw;
|
||
|
|
||
|
if (!self->enemy)
|
||
|
{
|
||
|
self->cast_info.currentmove = self->cast_info.move_stand;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self->cast_info.aiflags & AI_TURN_BLOCKED)
|
||
|
{ // abort the side run
|
||
|
self->cast_info.aiflags &= ~AI_TURN_BLOCKED;
|
||
|
AI_EndAttack(self);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// if the path ahead is not clear, abort
|
||
|
if (!AI_SideTrace( self, 96, 0, 1 ))
|
||
|
{
|
||
|
AI_EndAttack(self);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
oldyaw = self->s.angles[YAW];
|
||
|
|
||
|
VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
|
||
|
VectorNormalize( vec );
|
||
|
|
||
|
self->s.angles[YAW] = vectoyaw( vec );
|
||
|
*/
|
||
|
self->cast_info.aiflags |= AI_SIDE_ATTACK;
|
||
|
whore_firegun(self);
|
||
|
self->cast_info.aiflags &= ~AI_SIDE_ATTACK;
|
||
|
/*
|
||
|
// keep running to the side
|
||
|
self->ideal_yaw = self->s.angles[YAW] - 90;
|
||
|
|
||
|
self->s.angles[YAW] = oldyaw;
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
void whore_pain (edict_t *self, edict_t *other, float kick, int damage, int mdx_part, int mdx_subobject)
|
||
|
{
|
||
|
int orientation;
|
||
|
|
||
|
if (self->s.renderfx2 & RF2_FLAMETHROWER)
|
||
|
self->s.renderfx2 &= ~RF2_FLAMETHROWER;
|
||
|
|
||
|
AI_CheckMakeEnemy( self, other );
|
||
|
|
||
|
if (level.time < self->pain_debounce_time)
|
||
|
return;
|
||
|
|
||
|
self->pain_debounce_time = level.time + 3 + random();
|
||
|
|
||
|
|
||
|
if (self->name_index == NAME_BLUNT)
|
||
|
Voice_Random (self, other, &blunt[2], 4);
|
||
|
|
||
|
|
||
|
if (skill->value >= 3)
|
||
|
return; // no pain anims in nightmare
|
||
|
|
||
|
// faked client pain sound
|
||
|
|
||
|
|
||
|
// gi.sound (self, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0);
|
||
|
|
||
|
|
||
|
// Ridah, randomly don't play an animation, since it' leaves them WAY open to be killed
|
||
|
if (skill->value > 0 && rand()%2)
|
||
|
return;
|
||
|
|
||
|
|
||
|
if (other->client || (other->svflags & SVF_MONSTER))
|
||
|
{
|
||
|
orientation = AI_GetOrientation( self, other );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
orientation = ORIENTATION_CENTER;
|
||
|
}
|
||
|
|
||
|
if (self->maxs[2] < self->cast_info.standing_max_z)
|
||
|
{ // crouching
|
||
|
|
||
|
switch (orientation)
|
||
|
{
|
||
|
case ORIENTATION_CENTER :
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_crouch_painC;
|
||
|
break;
|
||
|
}
|
||
|
case ORIENTATION_LEFT :
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_crouch_painL;
|
||
|
break;
|
||
|
}
|
||
|
case ORIENTATION_RIGHT :
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_crouch_painR;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else // standing
|
||
|
{
|
||
|
if ( (mdx_part == PART_BODY)
|
||
|
|| (other->client && other->client->pers.weapon && !(other->client->pers.weapon->ammo) && (orientation = rand()%2+1)))
|
||
|
{
|
||
|
switch (orientation)
|
||
|
{
|
||
|
case ORIENTATION_CENTER :
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_pain_chest;
|
||
|
break;
|
||
|
}
|
||
|
case ORIENTATION_LEFT :
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_pain_Larm;
|
||
|
break;
|
||
|
}
|
||
|
case ORIENTATION_RIGHT :
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_pain_Rarm;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (mdx_part == PART_LEGS)
|
||
|
{
|
||
|
switch (orientation)
|
||
|
{
|
||
|
case ORIENTATION_CENTER :
|
||
|
{
|
||
|
if (infront(self, other))
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_pain_crch;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_pain_butt;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case ORIENTATION_LEFT :
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_pain_Lleg;
|
||
|
break;
|
||
|
}
|
||
|
case ORIENTATION_RIGHT :
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_pain_Rleg;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (mdx_part == PART_HEAD)
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_pain_head;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// JOSEPH 26-FEB-99
|
||
|
void whore_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject)
|
||
|
{
|
||
|
trace_t tr;
|
||
|
vec3_t end;
|
||
|
edict_t *playthud1, *playthud2;
|
||
|
|
||
|
if (self->s.renderfx2 & RF2_FLAMETHROWER)
|
||
|
self->s.renderfx2 &= ~RF2_FLAMETHROWER;
|
||
|
|
||
|
self->s.model_parts[PART_GUN].invisible_objects = (1<<0 | 1<<1);
|
||
|
self->s.model_parts[PART_GUN2].invisible_objects = (1<<0 | 1<<1);
|
||
|
|
||
|
// regular death
|
||
|
self->takedamage = DAMAGE_YES;
|
||
|
|
||
|
if (DeathByGib(self, inflictor, attacker, damage))
|
||
|
{ // gib
|
||
|
self->deadflag = DEAD_DEAD;
|
||
|
GibEntity( self, inflictor, damage );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self->deadflag == DEAD_DEAD)
|
||
|
return;
|
||
|
|
||
|
self->deadflag = DEAD_DEAD;
|
||
|
|
||
|
// JOSEPH 7-MAY-99
|
||
|
if (!(self->cast_info.aiflags & AI_MELEE))
|
||
|
{
|
||
|
if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
SpawnTheWeapon (self, "weapon_shotgun_e");
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
SpawnTheWeapon (self, "weapon_heavymachinegun_e");
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
SpawnTheWeapon (self, "weapon_bazooka_e");
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
SpawnTheWeapon (self, "weapon_flamethrower_e");
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
SpawnTheWeapon (self, "weapon_grenadelauncher_e");
|
||
|
else
|
||
|
SpawnTheWeapon (self, "weapon_tommygun_e");
|
||
|
}
|
||
|
// END JOSEPH
|
||
|
|
||
|
// EP_SpecialEventDeath (self);
|
||
|
|
||
|
VectorCopy (self->s.origin, end);
|
||
|
end[2] -= 64;
|
||
|
tr = gi.trace (self->s.origin, self->mins, self->maxs, end, self, MASK_SHOT);
|
||
|
|
||
|
// JOSEPH 5-JUN-99
|
||
|
if (mdx_part == PART_HEAD)
|
||
|
{ // probably head impact, we should really be passing in the model_part here
|
||
|
self->cast_info.currentmove = &whore_move_death3;
|
||
|
|
||
|
playthud1 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
||
|
playthud1->thudsurf = tr.surface->flags;
|
||
|
playthud1->thudsnd = 1;
|
||
|
playthud1->nextthink = level.time + (0.1*4.0);
|
||
|
playthud1->think = think_playthud;
|
||
|
gi.linkentity (playthud1);
|
||
|
|
||
|
playthud2 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud2->s.origin);
|
||
|
playthud2->thudsurf = tr.surface->flags;
|
||
|
playthud2->thudsnd = 2;
|
||
|
playthud2->nextthink = level.time + (0.1*9.0);
|
||
|
playthud2->think = think_playthud;
|
||
|
gi.linkentity (playthud2);
|
||
|
}
|
||
|
else if (self->maxs[2] < self->cast_info.standing_max_z)
|
||
|
{
|
||
|
if (rand()%100 > 50)
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_crouch_death1;
|
||
|
|
||
|
playthud1 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
||
|
playthud1->thudsurf = tr.surface->flags;
|
||
|
playthud1->thudsnd = 1;
|
||
|
playthud1->nextthink = level.time + (0.1*7.0);
|
||
|
playthud1->think = think_playthud;
|
||
|
gi.linkentity (playthud1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_crouch_death2;
|
||
|
|
||
|
playthud1 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
||
|
playthud1->thudsurf = tr.surface->flags;
|
||
|
playthud1->thudsnd = 1;
|
||
|
playthud1->nextthink = level.time + (0.1*14.0);
|
||
|
playthud1->think = think_playthud;
|
||
|
gi.linkentity (playthud1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int n;
|
||
|
|
||
|
n = rand() % 4;
|
||
|
if (n == 0)
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_death1;
|
||
|
|
||
|
playthud1 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
||
|
playthud1->thudsurf = tr.surface->flags;
|
||
|
playthud1->thudsnd = 1;
|
||
|
playthud1->nextthink = level.time + (0.1*7.0);
|
||
|
playthud1->think = think_playthud;
|
||
|
gi.linkentity (playthud1);
|
||
|
|
||
|
playthud2 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud2->s.origin);
|
||
|
playthud2->thudsurf = tr.surface->flags;
|
||
|
playthud2->thudsnd = 2;
|
||
|
playthud2->nextthink = level.time + (0.1*11.0);
|
||
|
playthud2->think = think_playthud;
|
||
|
gi.linkentity (playthud2);
|
||
|
}
|
||
|
else if (n == 1)
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_death2;
|
||
|
|
||
|
playthud1 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
||
|
playthud1->thudsurf = tr.surface->flags;
|
||
|
playthud1->thudsnd = 1;
|
||
|
playthud1->nextthink = level.time + (0.1*5.0);
|
||
|
playthud1->think = think_playthud;
|
||
|
gi.linkentity (playthud1);
|
||
|
|
||
|
playthud2 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud2->s.origin);
|
||
|
playthud2->thudsurf = tr.surface->flags;
|
||
|
playthud2->thudsnd = 2;
|
||
|
playthud2->nextthink = level.time + (0.1*7.0);
|
||
|
playthud2->think = think_playthud;
|
||
|
gi.linkentity (playthud2);
|
||
|
}
|
||
|
else if (n == 2)
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_death3;
|
||
|
|
||
|
playthud1 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
||
|
playthud1->thudsurf = tr.surface->flags;
|
||
|
playthud1->thudsnd = 1;
|
||
|
playthud1->nextthink = level.time + (0.1*4.0);
|
||
|
playthud1->think = think_playthud;
|
||
|
gi.linkentity (playthud1);
|
||
|
|
||
|
playthud2 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud2->s.origin);
|
||
|
playthud2->thudsurf = tr.surface->flags;
|
||
|
playthud2->thudsnd = 2;
|
||
|
playthud2->nextthink = level.time + (0.1*9.0);
|
||
|
playthud2->think = think_playthud;
|
||
|
gi.linkentity (playthud2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self->cast_info.currentmove = &whore_move_death4;
|
||
|
|
||
|
playthud1 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
||
|
playthud1->thudsurf = tr.surface->flags;
|
||
|
playthud1->thudsnd = 1;
|
||
|
playthud1->nextthink = level.time + (0.1*7.0);
|
||
|
playthud1->think = think_playthud;
|
||
|
gi.linkentity (playthud1);
|
||
|
|
||
|
playthud2 = G_Spawn();
|
||
|
VectorCopy (self->s.origin, playthud2->s.origin);
|
||
|
playthud2->thudsurf = tr.surface->flags;
|
||
|
playthud2->thudsnd = 2;
|
||
|
playthud2->nextthink = level.time + (0.1*15.0);
|
||
|
playthud2->think = think_playthud;
|
||
|
gi.linkentity (playthud2);
|
||
|
}
|
||
|
}
|
||
|
// END JOSEPH
|
||
|
|
||
|
if (!self->onfiretime)
|
||
|
Voice_Random (self, attacker, &female_specific[4], 2);
|
||
|
|
||
|
}
|
||
|
// END JOSEPH
|
||
|
|
||
|
|
||
|
// =========================================================================
|
||
|
|
||
|
/*QUAKED cast_whore (1 .5 0) (-16 -16 -24) (16 16 48) FLASH_LIGHT TRIGGERED_START FLAM_BE BAZOOKA HMG IMMEDIATE_FOLLOW_PATH TOMMYGUN GRENADE
|
||
|
Fidel Character
|
||
|
cast_group defines which group the character is a member of
|
||
|
default cast_group is 0, which is neutral (won't help others out)
|
||
|
player's cast_group is 1 (friendly characters)
|
||
|
|
||
|
The default weapon for a whore is the shotgun
|
||
|
|
||
|
model="models\actors\whore\"
|
||
|
|
||
|
|
||
|
*/
|
||
|
void SP_cast_whore(edict_t *self)
|
||
|
{
|
||
|
int i;
|
||
|
char *head_skin, *body_skin, *legs_skin;
|
||
|
int skin;
|
||
|
|
||
|
if (deathmatch->value)
|
||
|
{
|
||
|
G_FreeEdict (self);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!(self->spawnflags & (WHORE_FLAMEGUN|WHORE_BAZOOKA|WHORE_HMG|WHORE_TOMMYGUN|WHORE_GRENADE)))
|
||
|
{
|
||
|
self->spawnflags |= WHORE_SHOTGUN;
|
||
|
}
|
||
|
|
||
|
self->movetype = MOVETYPE_STEP;
|
||
|
self->solid = SOLID_BBOX;
|
||
|
self->s.skinnum = (self->skin-1) * 3;
|
||
|
|
||
|
VectorSet (self->mins, -16, -16, -24);
|
||
|
VectorSet (self->maxs, 16, 16, 48);
|
||
|
|
||
|
if (!self->art_skins)
|
||
|
{ // use default skins
|
||
|
self->art_skins = "001 001 001";
|
||
|
}
|
||
|
|
||
|
if (self->art_skins)
|
||
|
{
|
||
|
// convert spaces to NULL's
|
||
|
for (i=0; i<11; i++)
|
||
|
if (self->art_skins[i] == ' ')
|
||
|
self->art_skins[i] = '\0';
|
||
|
|
||
|
head_skin = &self->art_skins[0];
|
||
|
body_skin = &self->art_skins[4];
|
||
|
legs_skin = &self->art_skins[8];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
head_skin = body_skin = legs_skin = NULL;
|
||
|
}
|
||
|
|
||
|
tommy_soundindex = gi.soundindex("weapons/machinegun/machgf1b.wav");
|
||
|
shotgun_soundindex = gi.soundindex("weapons/shotgun/shotgf1b.wav");
|
||
|
shotgun_reload_soundindex = gi.soundindex("weapons/shotgun/shotgr1b.wav");
|
||
|
heavymachinegun_soundindex = gi.soundindex ("weapons/hmg/single.wav");
|
||
|
rocketgun_soundindex = gi.soundindex ("weapons/rocket_launcher/rl_fire.wav");
|
||
|
|
||
|
// ------------------------------------------------------------------------
|
||
|
// initialize all model_part data
|
||
|
memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);
|
||
|
|
||
|
self->s.num_parts++;
|
||
|
switch (self->head)
|
||
|
{
|
||
|
case 1:
|
||
|
self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/whore/bald_head.mdx");
|
||
|
break;
|
||
|
case 2:
|
||
|
self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/whore/pony_head.mdx");
|
||
|
break;
|
||
|
default:
|
||
|
self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/whore/head.mdx");
|
||
|
break;
|
||
|
}
|
||
|
if (head_skin)
|
||
|
{
|
||
|
skin = gi.skinindex( self->s.model_parts[PART_HEAD].modelindex, head_skin );
|
||
|
}
|
||
|
else
|
||
|
skin = self->s.skinnum;
|
||
|
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_HEAD].baseskin = self->s.model_parts[PART_HEAD].skinnum[i] = skin;
|
||
|
gi.GetObjectBounds( "models/actors/whore/head.mdx", &self->s.model_parts[PART_HEAD] );
|
||
|
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/whore/legs.mdx");
|
||
|
if (head_skin)
|
||
|
skin = gi.skinindex( self->s.model_parts[PART_LEGS].modelindex, legs_skin );
|
||
|
else
|
||
|
skin = self->s.skinnum;
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_LEGS].baseskin = self->s.model_parts[PART_LEGS].skinnum[i] = skin;
|
||
|
gi.GetObjectBounds( "models/actors/whore/legs.mdx", &self->s.model_parts[PART_LEGS] );
|
||
|
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/whore/body.mdx");
|
||
|
if (head_skin)
|
||
|
skin = gi.skinindex( self->s.model_parts[PART_BODY].modelindex, body_skin );
|
||
|
else
|
||
|
skin = self->s.skinnum;
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_BODY].baseskin = self->s.model_parts[PART_BODY].skinnum[i] = skin;
|
||
|
gi.GetObjectBounds( "models/actors/whore/body.mdx", &self->s.model_parts[PART_BODY] );
|
||
|
|
||
|
// Ridah, temp only for testing, hard to test with all grenade guys
|
||
|
//if (self->spawnflags & WHORE_GRENADE)
|
||
|
//self->spawnflags |= WHORE_SHOTGUN;
|
||
|
|
||
|
if (self->spawnflags & WHORE_TOMMYGUN)
|
||
|
{
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/whore/tommygun.mdx");
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = 0;// self->s.skinnum;
|
||
|
gi.GetObjectBounds( "models/actors/whore/tommygun.mdx", &self->s.model_parts[PART_GUN] );
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
{
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/whore/shotgun.mdx");
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = 0;// self->s.skinnum;
|
||
|
gi.GetObjectBounds( "models/actors/whore/shotgun.mdx", &self->s.model_parts[PART_GUN] );
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_HMG)
|
||
|
{
|
||
|
// NOTE TO SELF: tell Jason to make a gun.mdx for this weapon
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/whore/hmg.mdx");
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = 0;// self->s.skinnum;
|
||
|
gi.GetObjectBounds( "models/actors/whore/hmg.mdx", &self->s.model_parts[PART_GUN] );
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_BAZOOKA)
|
||
|
{
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/whore/rocket_lnch.mdx");
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = 0;// self->s.skinnum;
|
||
|
gi.GetObjectBounds( "models/actors/whore/rocket_lnch.mdx", &self->s.model_parts[PART_GUN] );
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
{
|
||
|
// NOTE TO SELF: tell Jason to make a gun.mdx for this weapon
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/whore/rocket_lnch.mdx");
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = 0;// self->s.skinnum;
|
||
|
gi.GetObjectBounds( "models/actors/whore/rocket_lnch.mdx", &self->s.model_parts[PART_GUN] );
|
||
|
}
|
||
|
else if (self->spawnflags & WHORE_GRENADE)
|
||
|
{
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/whore/grenade_lnch.mdx");
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = 0;// self->s.skinnum;
|
||
|
gi.GetObjectBounds( "models/actors/whore/grenade_lnch.mdx", &self->s.model_parts[PART_GUN] );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gi.dprintf ("error no weapon spawnflag\n");
|
||
|
|
||
|
self->s.num_parts++;
|
||
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/whore/shotgun.mdx");
|
||
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
||
|
self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = 0;// self->s.skinnum;
|
||
|
gi.GetObjectBounds( "models/actors/whore/shotgun.mdx", &self->s.model_parts[PART_GUN] );
|
||
|
}
|
||
|
|
||
|
// remove NULL's
|
||
|
if (self->art_skins)
|
||
|
self->art_skins[3] = self->art_skins[7] = ' ';
|
||
|
|
||
|
// ------------------------------------------------------------------------
|
||
|
|
||
|
if (!self->health)
|
||
|
self->health = 100;
|
||
|
|
||
|
self->gib_health = -200;
|
||
|
self->mass = 200;
|
||
|
|
||
|
self->gender = GENDER_FEMALE;
|
||
|
|
||
|
self->pain = whore_pain;
|
||
|
self->die = whore_die;
|
||
|
|
||
|
self->cast_info.checkattack = AI_CheckAttack;
|
||
|
|
||
|
self->cast_info.attack = whore_attack;
|
||
|
self->cast_info.long_attack = whore_long_attack;
|
||
|
self->cast_info.talk = whore_talk;
|
||
|
self->cast_info.avoid = whore_avoid;
|
||
|
|
||
|
self->cast_info.catch_fire = whore_catch_fire;
|
||
|
|
||
|
// Ridah, new AI system stuff!
|
||
|
self->cast_info.max_attack_distance = 2000;
|
||
|
|
||
|
// Ridah, so they don't shoot the flamegun from too far away
|
||
|
if (self->spawnflags & WHORE_FLAMEGUN)
|
||
|
self->cast_info.max_attack_distance = 384;
|
||
|
|
||
|
if (self->spawnflags & WHORE_SHOTGUN)
|
||
|
self->cast_info.move_stand = &whore_move_sg_stand;
|
||
|
else
|
||
|
self->cast_info.move_stand = &whore_move_stand;
|
||
|
|
||
|
// self->cast_info.move_crstand = &whore_move_crouch_stand;
|
||
|
self->cast_info.move_crstand = &whore_move_crch_astand;
|
||
|
|
||
|
self->cast_info.move_run = &whore_move_run_gun_down;
|
||
|
|
||
|
// RAFAEL
|
||
|
if (self->spawnflags & 1)
|
||
|
self->cast_info.move_runwalk = &whore_move_walk_gdownFLASHLIGHT;
|
||
|
else
|
||
|
self->cast_info.move_runwalk = &whore_move_walk_gdown;
|
||
|
|
||
|
self->cast_info.move_crwalk = &whore_move_crh_shuf;
|
||
|
|
||
|
self->cast_info.move_jump = &whore_move_jump;
|
||
|
|
||
|
self->cast_info.move_avoid_walk = &whore_move_avoid_walk;
|
||
|
self->cast_info.move_avoid_run = &whore_move_avoid_run;
|
||
|
self->cast_info.move_avoid_reverse_walk = &whore_move_avoid_reverse_walk;
|
||
|
self->cast_info.move_avoid_reverse_run = &whore_move_avoid_reverse_run;
|
||
|
self->cast_info.move_avoid_crwalk = &whore_move_avoid_crouch_walk;
|
||
|
|
||
|
self->cast_info.move_crouch_down = &whore_move_crouch_stand_down;
|
||
|
self->cast_info.move_stand_up = &whore_move_crouch_stand_up;
|
||
|
|
||
|
self->cast_info.move_lside_step = &whore_move_lside_step;
|
||
|
|
||
|
// self->cast_info.move_rside_step = &whore_move_rside_step;
|
||
|
// hack rside step is missing
|
||
|
self->cast_info.move_rside_step = &whore_move_avoid_walk;
|
||
|
|
||
|
self->cast_info.move_start_climb = &whore_move_clmb_loop;
|
||
|
self->cast_info.move_end_climb = &whore_move_clmb_over;
|
||
|
|
||
|
// Ridah, done.
|
||
|
|
||
|
gi.linkentity (self);
|
||
|
|
||
|
self->cast_info.currentmove = self->cast_info.move_stand;
|
||
|
|
||
|
if (!self->cast_info.scale)
|
||
|
self->cast_info.scale = MODEL_SCALE;
|
||
|
|
||
|
self->s.scale = self->cast_info.scale - 1.0;
|
||
|
|
||
|
// talk by default
|
||
|
self->cast_info.aiflags |= AI_TALK;
|
||
|
|
||
|
if (!self->acc)
|
||
|
self->acc = 2;
|
||
|
|
||
|
walking_cast_start (self);
|
||
|
}
|