1414 lines
36 KiB
C
1414 lines
36 KiB
C
|
|
#include "g_local.h"
|
|
#include "ai_thug.h"
|
|
|
|
#include "voice_punk.h"
|
|
|
|
void thug_end_kneel_attack( edict_t *self );
|
|
void thug_talk( edict_t *self );
|
|
void thug_end_stand( edict_t *self );
|
|
void thug_kneel_shoot( edict_t *self );
|
|
void thug_talk_think( edict_t *self );
|
|
|
|
void thug_firegun( edict_t *self, vec3_t ofs );
|
|
void thug_right_fire( edict_t *self );
|
|
void thug_left_fire( edict_t *self );
|
|
void thug_firegun_right( edict_t *self );
|
|
void thug_firegun_left( edict_t *self );
|
|
|
|
void thug_show_guns( edict_t *self );
|
|
void thug_climb_loop( edict_t *self );
|
|
void thug_melee( edict_t *self );
|
|
|
|
void thug_evade_amb( edict_t *self );
|
|
void thug_evade_adjust( edict_t *self );
|
|
void thug_evade_checkadjust( edict_t *self );
|
|
|
|
void thug_catch_fire( edict_t *self, edict_t *other );
|
|
|
|
// function that will bail out of the melee attack
|
|
// if thug doesnt stand a chance of hitting his enemy
|
|
void thug_melee_bail (edict_t *self);
|
|
|
|
#define THUG_MELEE 64 // spawnflag
|
|
|
|
// =========================================================================
|
|
|
|
#include "ai_thug_tables.h"
|
|
|
|
// =========================================================================
|
|
|
|
void thug_backoff( edict_t *self, edict_t *other )
|
|
{
|
|
Voice_Random( self, other, m_backoff, NUM_BACKOFF );
|
|
}
|
|
|
|
void thug_catch_fire( edict_t *self, edict_t *other )
|
|
{
|
|
self->enemy = NULL; // stop attacking
|
|
self->cast_info.currentmove = &thug_move_run_on_fire;
|
|
}
|
|
|
|
void thug_evade_amb( edict_t *self )
|
|
{
|
|
self->cast_info.currentmove = &thug_move_evade_amb;
|
|
}
|
|
|
|
void thug_evade_checkadjust( edict_t *self )
|
|
{
|
|
if (self->enemy && !directly_infront(self, self->enemy ))
|
|
self->cast_info.currentmove = &thug_move_evade_adjust;
|
|
else if (self->cast_info.avoid_ent && !directly_infront(self, self->cast_info.avoid_ent ))
|
|
self->cast_info.currentmove = &thug_move_evade_adjust;
|
|
}
|
|
|
|
void thug_evade_adjust (edict_t *self )
|
|
{
|
|
vec3_t vec;
|
|
edict_t *other;
|
|
|
|
if (self->enemy)
|
|
other = self->enemy;
|
|
else if (self->cast_info.avoid_ent)
|
|
other = self->cast_info.avoid_ent;
|
|
else
|
|
return;
|
|
|
|
VectorSubtract( other->s.origin, self->s.origin, vec );
|
|
VectorNormalize( vec );
|
|
self->ideal_yaw = vectoyaw( vec );
|
|
}
|
|
|
|
void thug_end_kneel_attack( edict_t *self )
|
|
{
|
|
AI_EndAttack(self);
|
|
|
|
if (self->cast_info.currentmove == self->cast_info.move_stand)
|
|
self->cast_info.currentmove = &thug_move_kneel_up;
|
|
}
|
|
|
|
void thug_talk_think( edict_t *self )
|
|
{
|
|
AI_TalkThink( self, true );
|
|
}
|
|
|
|
void thug_talk( edict_t *self )
|
|
{
|
|
int rnd;
|
|
|
|
// only make talking gesture 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;
|
|
|
|
rnd = rand() % 5;
|
|
|
|
if (rnd < 1)
|
|
self->cast_info.currentmove = &thug_move_talk1;
|
|
else if (rnd < 2)
|
|
self->cast_info.currentmove = &thug_move_handwave;
|
|
else if (rnd < 3)
|
|
self->cast_info.currentmove = &thug_move_whoa;
|
|
else //if (rnd < 4)
|
|
self->cast_info.currentmove = &thug_move_crch_grab;
|
|
// else if (rnd < 5)
|
|
// self->cast_info.currentmove = &thug_move_nod_yes;
|
|
// else if (rnd < 6)
|
|
// self->cast_info.currentmove = &thug_move_up_yours;
|
|
// else //if (rnd < 6)
|
|
// self->cast_info.currentmove = &thug_move_nod_no;
|
|
// else
|
|
// self->cast_info.currentmove = &thug_move_cigar;
|
|
}
|
|
|
|
void thug_avoid ( edict_t *self, edict_t *other, qboolean face )
|
|
{
|
|
vec3_t vec;
|
|
|
|
if (self->health <= 0)
|
|
return;
|
|
|
|
if (!self->groundentity)
|
|
return;
|
|
|
|
self->cast_info.last_avoid = level.time;
|
|
|
|
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 );
|
|
self->cast_info.avoid_ent = NULL;
|
|
}
|
|
VectorNormalize( vec );
|
|
|
|
self->ideal_yaw = vectoyaw( vec );
|
|
|
|
if (self->maxs[2] > DUCKING_MAX_Z)
|
|
{
|
|
|
|
//#if 0 // Ridah, these don't look right, and translations are incorrect
|
|
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 = &thug_move_lside_step;
|
|
else
|
|
self->cast_info.currentmove = &thug_move_rside_step;
|
|
|
|
return;
|
|
}
|
|
|
|
// if no move set, just turn to face
|
|
M_ChangeYaw( self );
|
|
|
|
return;
|
|
}
|
|
}
|
|
//#endif
|
|
|
|
if ((VectorDistance( self->s.origin, other->s.origin ) > 72) || !face)
|
|
self->cast_info.currentmove = &thug_move_avoid_walk;
|
|
else
|
|
self->cast_info.currentmove = &thug_move_avoid_reverse_walk;
|
|
|
|
}
|
|
else
|
|
{
|
|
self->cast_info.currentmove = &thug_move_crouch_avoid_walk;
|
|
}
|
|
|
|
}
|
|
|
|
void thug_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
|
|
|
|
self->cast_info.currentmove = self->cast_info.move_stand;
|
|
|
|
// return;
|
|
}
|
|
|
|
AI_CheckTalk(self);
|
|
}
|
|
|
|
void thug_show_guns( edict_t *self )
|
|
{
|
|
self->s.model_parts[PART_GUN].invisible_objects = 0;
|
|
self->s.model_parts[PART_GUN2].invisible_objects = 0;
|
|
}
|
|
|
|
void thug_kneel_shoot( edict_t *self )
|
|
{
|
|
self->cast_info.currentmove = &thug_move_knl_shoot;
|
|
}
|
|
|
|
void thug_long_attack( edict_t *self )
|
|
{
|
|
// keep running, and try to fire if possible
|
|
|
|
if (self->maxs[2] < self->cast_info.standing_max_z)
|
|
return;
|
|
|
|
if (self->s.model_parts[PART_GUN].invisible_objects)
|
|
{ // pull guns out
|
|
self->cast_info.currentmove = &thug_move_pull_guns;
|
|
return;
|
|
}
|
|
|
|
self->cast_info.currentmove = &thug_move_run_shoot;
|
|
}
|
|
|
|
// thug_attack:
|
|
// returns TRUE if starting an attack
|
|
qboolean thug_attack( edict_t *self )
|
|
{
|
|
vec3_t vec;
|
|
float dist;
|
|
|
|
// this is where we would start some of the fancy side-ways or rolling attacks
|
|
|
|
if (self->maxs[2] < self->cast_info.standing_max_z)
|
|
{
|
|
if ((self->cast_info.aiflags & AI_DUCKATTACK) && (rand()%20 < 1))
|
|
{ // stop duck attacking
|
|
self->cast_info.aiflags &= ~AI_DUCKATTACK;
|
|
self->maxs[2] = self->cast_info.standing_max_z;
|
|
if (ValidBoxAtLoc( self->s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID ))
|
|
{
|
|
self->cast_info.currentmove = self->cast_info.move_stand_up;
|
|
self->s.frame = self->cast_info.move_stand_up->firstframe;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
self->maxs[2] = DUCKING_MAX_Z;
|
|
}
|
|
}
|
|
|
|
if (self->cast_info.aiflags & AI_MELEE)
|
|
{ // try to stand at all costs
|
|
self->maxs[2] = self->cast_info.standing_max_z;
|
|
|
|
if (ValidBoxAtLoc( self->s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID ))
|
|
goto standing;
|
|
|
|
self->maxs[2] = DUCKING_MAX_Z;
|
|
// gi.dprintf( "ANIM TODO: crouch melee attack\n" );
|
|
self->cast_info.currentmove = &thug_move_low_melee1;
|
|
return true;
|
|
}
|
|
|
|
// HACK TO SHOW THE GUNS
|
|
self->s.model_parts[PART_GUN].invisible_objects = 0;
|
|
self->s.model_parts[PART_GUN2].invisible_objects = 0;
|
|
|
|
self->cast_info.currentmove = &thug_move_crch_shoot;
|
|
return true;
|
|
}
|
|
else if (!(self->cast_info.aiflags & AI_MELEE) && (rand()%20 < 1)) // duck attack
|
|
{
|
|
|
|
self->viewheight = -4;
|
|
|
|
if (AI_ClearSight( self, self->enemy, false ))
|
|
{
|
|
//gi.dprintf("5\n");
|
|
self->cast_info.aiflags |= AI_DUCKATTACK;
|
|
self->maxs[2] = DUCKING_MAX_Z;
|
|
self->cast_info.currentmove = self->cast_info.move_crouch_down;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
standing:
|
|
|
|
VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
|
|
dist = VectorNormalize( vec );
|
|
|
|
if (self->cast_info.aiflags & AI_MELEE)
|
|
{
|
|
qboolean attack=false;
|
|
|
|
if (dist < 64)
|
|
{
|
|
attack = true;
|
|
}
|
|
else // are they running towards us?
|
|
{
|
|
VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
|
|
|
|
if ((VectorLength(vec) < 200) && (DotProduct(vec, self->enemy->velocity) < 0))
|
|
{
|
|
attack = true;
|
|
}
|
|
}
|
|
|
|
if (attack)
|
|
{
|
|
int rnd;
|
|
|
|
self->ideal_yaw = vectoyaw(vec);
|
|
|
|
M_ChangeYaw( self );
|
|
|
|
// JOSEPH 12-MAR-99
|
|
Voice_Random( self, self->enemy, grunting, 5);
|
|
// END JOSEPH
|
|
|
|
if (self->maxs[2] > self->enemy->maxs[2])
|
|
{
|
|
self->cast_info.currentmove = &thug_move_low_melee1;
|
|
}
|
|
else
|
|
{
|
|
rnd = rand()%7;
|
|
|
|
switch (rnd)
|
|
{
|
|
case 0 :
|
|
self->cast_info.currentmove = &thug_move_melee1;
|
|
break;
|
|
case 1 :
|
|
self->cast_info.currentmove = &thug_move_melee2;
|
|
break;
|
|
case 2 :
|
|
self->cast_info.currentmove = &thug_move_melee3;
|
|
break;
|
|
case 3 :
|
|
self->cast_info.currentmove = &thug_move_melee4;
|
|
break;
|
|
default :
|
|
self->cast_info.currentmove = &thug_move_melee5;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (self->maxs[2] > self->enemy->maxs[2] || self->s.origin[2] > self->enemy->s.origin[2])
|
|
self->cast_info.currentmove = &thug_move_low_melee1;
|
|
|
|
self->cast_info.aiflags &=~ AI_RUSH_THE_PLAYER;
|
|
return true;
|
|
}
|
|
else if (dist < 128)
|
|
{
|
|
self->ideal_yaw = vectoyaw(vec);
|
|
M_ChangeYaw( self );
|
|
|
|
self->cast_info.currentmove = &thug_move_run_melee;
|
|
return true;
|
|
}
|
|
else if (dist < 400)
|
|
self->cast_info.aiflags |= AI_RUSH_THE_PLAYER;
|
|
|
|
return false;
|
|
}
|
|
|
|
if (self->s.model_parts[PART_GUN].invisible_objects)
|
|
{ // pull guns out
|
|
self->cast_info.currentmove = &thug_move_pull_guns;
|
|
return true;
|
|
}
|
|
|
|
if (self->cast_info.currentmove == &thug_move_knl_shoot)
|
|
{
|
|
// HACK TO SHOW GUN
|
|
self->s.model_parts[PART_GUN].invisible_objects = 0;
|
|
self->s.model_parts[PART_GUN2].invisible_objects = 0;
|
|
self->cast_info.currentmove = &thug_move_knl_shoot;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Special case: enemy has flamethrower, run backwards, keep firing
|
|
if ( (dist < 384) // they're close
|
|
&& (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 = &thug_move_reverse_run_shoot;
|
|
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))
|
|
{
|
|
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, 96, 90, side_result );
|
|
|
|
if (side_result2 == side_result)
|
|
{
|
|
if (side_result < 0)
|
|
self->cast_info.currentmove = &thug_move_lside_run;
|
|
else
|
|
self->cast_info.currentmove = &thug_move_rside_run;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ( (self->cast_info.currentmove != &thug_move_shoot)
|
|
&& (dist > 512)
|
|
&& (random() < 0.5)
|
|
&& (directly_infront(self, self->enemy))
|
|
&& (self->s.model_parts[PART_GUN].invisible_objects)) // only kneel if we haven't drawn our guns yet
|
|
{
|
|
self->cast_info.currentmove = &thug_move_kneel;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// walk shooting?
|
|
// Ridah, I don't like this animation
|
|
// if ((dist > 512) && directly_infront( self, self->enemy ) && AI_SideTrace(self, 32, 0, 1 ) )
|
|
// {
|
|
// self->cast_info.currentmove = &thug_move_walk_shoot;
|
|
// return true;
|
|
// }
|
|
// else
|
|
if ((dist < 128) && AI_SideTrace(self, -32, 0, 1 ) )
|
|
{
|
|
self->cast_info.currentmove = &thug_move_reverse_walk_shoot;
|
|
return true;
|
|
}
|
|
|
|
stand_shoot:
|
|
|
|
// normal attack
|
|
|
|
self->ideal_yaw = vectoyaw(vec);
|
|
|
|
M_ChangeYaw( self );
|
|
|
|
self->cast_info.currentmove = &thug_move_shoot;
|
|
|
|
return true;
|
|
}
|
|
|
|
void thug_climb_loop(edict_t *self)
|
|
{
|
|
self->cast_info.currentmove = &thug_move_clmb_loop;
|
|
}
|
|
|
|
|
|
void thug_melee_bail (edict_t *self)
|
|
{
|
|
vec3_t vec;
|
|
float dist;
|
|
|
|
if (self->enemy)
|
|
{
|
|
VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
|
|
dist = VectorNormalize( vec );
|
|
|
|
if (dist > 48)
|
|
{
|
|
// AI_EndAttack(self);
|
|
self->cast_info.currentmove = &thug_move_run_melee;
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void thug_melee( edict_t *self )
|
|
{
|
|
vec3_t start, offset;
|
|
vec3_t forward, right;
|
|
|
|
float damage = 8;
|
|
|
|
if (self->cast_info.currentmove == &thug_move_melee5)
|
|
damage *= 2; // double handed attack
|
|
|
|
if (self->cast_info.currentmove == &thug_move_low_melee1)
|
|
damage *= 3;
|
|
|
|
// yell at them?
|
|
if (self->last_talk_time < (level.time - TALK_FIGHTING_DELAY))
|
|
{
|
|
// we can't play any "eat lead" type sounds, because we're not firing lead..
|
|
|
|
if (self->cast_group != 1)
|
|
Voice_Random( self, self->enemy, grunting, 5);
|
|
else
|
|
Voice_Random(self, self->enemy, friendlycombat, NUM_FRIENDLYCOMBAT );
|
|
}
|
|
|
|
// VectorSet(offset, 0, 8, self->viewheight - 4);
|
|
VectorSet (offset, 0, 8, 16);
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
if (self->cast_info.currentmove == &thug_move_low_melee1)
|
|
forward[2] -= 0.5;
|
|
G_ProjectSource (self->s.origin, offset, forward, right, start);
|
|
if (self->cast_info.currentmove == &thug_move_low_melee1)
|
|
start[2] -= 8;
|
|
|
|
// HACK, higher Moral does more damage
|
|
if (self->moral > 4)
|
|
damage *= 1.0 + (self->moral - 4)*0.25;
|
|
|
|
// if (skill->value > 1.0)
|
|
// damage *= 1.0 + 0.5*(float)(skill->value - 1.0);
|
|
|
|
fire_blackjack( self, start, forward, damage, 10, MOD_BLACKJACK );
|
|
|
|
}
|
|
|
|
void thug_firegun( edict_t *self, vec3_t ofs )
|
|
{
|
|
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.currentmove == &thug_move_knl_shoot)
|
|
{
|
|
if (!directly_infront( self, self->enemy ))
|
|
{
|
|
self->cast_info.currentmove = &thug_move_kneel_up;
|
|
self->s.frame++; // skip the firing frame since it might have a muzzle flash
|
|
return;
|
|
}
|
|
|
|
self->cast_info.aiflags |= AI_FACE_ATTACK;
|
|
}
|
|
else
|
|
{
|
|
if (self->noise_time < (level.time - 1))
|
|
{ // haven't fired for a while, prevent we reloaded
|
|
self->duration = 0;
|
|
}
|
|
|
|
// check for reload
|
|
if ( (self->maxs[2] == self->cast_info.standing_max_z)
|
|
&& !(self->cast_info.aiflags & AI_SIDE_ATTACK)
|
|
&& (self->duration++ > 12))
|
|
{
|
|
self->duration = 0;
|
|
self->cast_info.currentmove = &thug_move_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;
|
|
}
|
|
|
|
self->cast_info.aiflags &= ~AI_FACE_ATTACK;
|
|
|
|
// yell at them?
|
|
if (self->last_talk_time < (level.time - TALK_FIGHTING_DELAY))
|
|
{
|
|
if (self->cast_group != 1)
|
|
Voice_Random(self, self->enemy, fightsounds, NUM_FIGHTING);
|
|
else
|
|
Voice_Random(self, self->enemy, friendlycombat, NUM_FRIENDLYCOMBAT );
|
|
}
|
|
|
|
// fire the gun
|
|
|
|
VectorSet(offset, 0 + ofs[0], 8 + ofs[1], self->viewheight-8 + ofs[2]);
|
|
|
|
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 * (random()*0.8 + 0.2)) * (1.0 - (skill->value/4.0)), self->enemy->velocity, target);
|
|
target[2] += self->enemy->viewheight;
|
|
|
|
flash_number = MZ2_GUNNER_MACHINEGUN_1;
|
|
|
|
VectorSubtract (target, start, aim);
|
|
dist = VectorNormalize (aim);
|
|
|
|
self->ideal_yaw = vectoyaw( aim );
|
|
|
|
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 = &thug_move_shoot;
|
|
else
|
|
self->cast_info.attack( self );
|
|
}
|
|
|
|
cast_fire_bullet (self, start, aim, 3, 0, DEFAULT_BULLET_HSPREAD>>self->acc, DEFAULT_BULLET_VSPREAD>>self->acc, flash_number);
|
|
|
|
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/machinegun/machgf1b.wav"), 1, ATTN_NORM, 0);
|
|
|
|
}
|
|
|
|
void thug_right_fire( edict_t *self )
|
|
{
|
|
static vec3_t ofs = {4, 0, 0};
|
|
thug_firegun( self, ofs );
|
|
}
|
|
|
|
void thug_left_fire( edict_t *self )
|
|
{
|
|
static vec3_t ofs = {-4, 0, 0};
|
|
thug_firegun( self, ofs );
|
|
}
|
|
|
|
void thug_firegun_right( edict_t *self )
|
|
{
|
|
// vec3_t vec;
|
|
// float oldyaw;
|
|
static vec3_t ofs = {0,0,0};
|
|
|
|
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, 128, 90, 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;
|
|
thug_firegun(self, ofs);
|
|
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 thug_firegun_left( edict_t *self )
|
|
{
|
|
// vec3_t vec;
|
|
// float oldyaw;
|
|
static vec3_t ofs = {0,0,0};
|
|
|
|
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, 128, -90, 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;
|
|
thug_firegun(self, ofs);
|
|
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;
|
|
*/
|
|
}
|
|
|
|
// JOSEPH 17-NOV-98
|
|
|
|
void think_playthud( edict_t *self )
|
|
{
|
|
char Temp[128];
|
|
|
|
strcpy ((char*)Temp, "actors/player/bodyfalls/");
|
|
|
|
if (self->thudsurf & SURF_FABRIC)
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"rug");
|
|
}
|
|
else if (self->thudsurf & SURF_GRAVEL)
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"gravel");
|
|
}
|
|
else if (self->thudsurf & SURF_METAL)
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"metalh");
|
|
}
|
|
else if (self->thudsurf & SURF_METAL_L)
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"metall");
|
|
}
|
|
else if (self->thudsurf & SURF_SNOW)
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"tin");
|
|
}
|
|
else if (self->thudsurf & SURF_TILE)
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"marble");
|
|
}
|
|
else if (self->thudsurf & SURF_WOOD)
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"wood");
|
|
}
|
|
else
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"pavement");
|
|
}
|
|
|
|
if (self->thudsnd == 1)
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"d1.wav");
|
|
gi.positioned_sound (self->s.origin, self, CHAN_VOICE|CHAN_RELIABLE, gi.soundindex((char*)&Temp[0]),
|
|
1.0, ATTN_NORM, 0);
|
|
}
|
|
else
|
|
{
|
|
strcat((char*)&Temp[0], (char*)"d2.wav");
|
|
gi.positioned_sound (self->s.origin, self, CHAN_VOICE|CHAN_RELIABLE, gi.soundindex((char*)&Temp[0]),
|
|
1.0, ATTN_NORM, 0);
|
|
}
|
|
|
|
self->nextthink = level.time + (0.1*20.0);
|
|
self->think = G_FreeEdict;
|
|
}
|
|
// END JOSEPH
|
|
|
|
// JOSEPH 26-FEB-99
|
|
void thug_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;
|
|
|
|
// regular death
|
|
self->takedamage = DAMAGE_YES;
|
|
|
|
if (DeathByGib(self, inflictor, attacker, damage))
|
|
{ // gib
|
|
GibEntity( self, inflictor, damage );
|
|
return;
|
|
}
|
|
|
|
if (self->deadflag == DEAD_DEAD)
|
|
return;
|
|
|
|
self->deadflag = DEAD_DEAD;
|
|
|
|
if (!(self->cast_info.aiflags & AI_MELEE))
|
|
SpawnTheWeapon (self, "weapon_pistol_e");
|
|
|
|
// EP_SpecialEventDeath (self);
|
|
|
|
if (!self->onfiretime)
|
|
gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
|
|
|
|
VectorCopy (self->s.origin, end);
|
|
end[2] -= 64;
|
|
tr = gi.trace (self->s.origin, self->mins, self->maxs, end, self, MASK_SHOT);
|
|
|
|
// RAFAEL 01-25-99
|
|
self->s.model_parts[PART_GUN].invisible_objects = (1<<0 | 1<<1);
|
|
self->s.model_parts[PART_GUN2].invisible_objects = (1<<0 | 1<<1);
|
|
|
|
|
|
if (!strcmp(inflictor->classname, "rocket"))
|
|
{
|
|
self->cast_info.currentmove = &thug_move_crch_dth;
|
|
|
|
// throw backwards
|
|
VectorNormalize2(inflictor->velocity, self->velocity);
|
|
|
|
VectorNegate( self->velocity, self->velocity );
|
|
self->ideal_yaw = vectoyaw( self->velocity );
|
|
VectorNegate( self->velocity, self->velocity );
|
|
|
|
VectorMA( self->velocity, 500, self->velocity, self->velocity );
|
|
self->gravity = 0.7;
|
|
self->velocity[2] = 220;
|
|
|
|
self->groundentity = NULL;
|
|
}
|
|
else if (mdx_part == PART_HEAD)
|
|
{
|
|
self->cast_info.currentmove = &thug_move_death5;
|
|
|
|
playthud1 = G_Spawn();
|
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
|
playthud1->thudsurf = tr.surface->flags;
|
|
playthud1->thudsnd = 1;
|
|
playthud1->nextthink = level.time + (0.1*6.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 (self->maxs[2] < self->cast_info.standing_max_z)
|
|
{
|
|
self->cast_info.currentmove = &thug_move_crch_dth;
|
|
|
|
playthud1 = G_Spawn();
|
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
|
playthud1->thudsurf = tr.surface->flags;
|
|
playthud1->thudsnd = 1;
|
|
playthud1->nextthink = level.time + (0.1*6.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
|
|
{
|
|
int n;
|
|
|
|
n = rand() % 5;
|
|
if (n == 0)
|
|
{
|
|
self->cast_info.currentmove = &thug_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*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*8.0);
|
|
playthud2->think = think_playthud;
|
|
gi.linkentity (playthud2);
|
|
}
|
|
else if (n == 1)
|
|
{
|
|
self->cast_info.currentmove = &thug_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*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*10.0);
|
|
playthud2->think = think_playthud;
|
|
gi.linkentity (playthud2);
|
|
}
|
|
else if (n == 2)
|
|
{
|
|
self->cast_info.currentmove = &thug_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*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 == 3)
|
|
{
|
|
self->cast_info.currentmove = &thug_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*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
|
|
{
|
|
self->cast_info.currentmove = &thug_move_death5;
|
|
|
|
playthud1 = G_Spawn();
|
|
VectorCopy (self->s.origin, playthud1->s.origin);
|
|
playthud1->thudsurf = tr.surface->flags;
|
|
playthud1->thudsnd = 1;
|
|
playthud1->nextthink = level.time + (0.1*6.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);
|
|
}
|
|
}
|
|
|
|
}
|
|
// END JOSEPH
|
|
|
|
void thug_pain (edict_t *self, edict_t *other, float kick, int damage, int mdx_part, int mdx_subobject)
|
|
{
|
|
int orientation;
|
|
|
|
AI_CheckMakeEnemy( self, other );
|
|
|
|
if (level.time < self->pain_debounce_time)
|
|
return;
|
|
|
|
self->pain_debounce_time = level.time + 3 + random();
|
|
|
|
if (skill->value >= 3)
|
|
return; // no pain anims in nightmare
|
|
|
|
// faked client pain sound
|
|
{
|
|
int r,l;
|
|
|
|
r = 1 + (rand()&1);
|
|
if (self->health < 25)
|
|
l = 25;
|
|
else if (self->health < 50)
|
|
l = 50;
|
|
else if (self->health < 75)
|
|
l = 75;
|
|
else
|
|
l = 100;
|
|
|
|
gi.sound (self, CHAN_BODY, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0);
|
|
}
|
|
|
|
if (self->cast_info.currentmove == &thug_move_knl_shoot)
|
|
return; // no pain anim for crouch shoot
|
|
|
|
|
|
// 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 = &thug_move_crouch_pain1;
|
|
break;
|
|
}
|
|
case ORIENTATION_LEFT :
|
|
{
|
|
self->cast_info.currentmove = &thug_move_crouch_pain2;
|
|
break;
|
|
}
|
|
case ORIENTATION_RIGHT :
|
|
{
|
|
self->cast_info.currentmove = &thug_move_crouch_pain3;
|
|
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 = &thug_move_pain_chest;
|
|
break;
|
|
}
|
|
case ORIENTATION_LEFT :
|
|
{
|
|
self->cast_info.currentmove = &thug_move_pain_Larm;
|
|
break;
|
|
}
|
|
case ORIENTATION_RIGHT :
|
|
{
|
|
self->cast_info.currentmove = &thug_move_pain_Rarm;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (mdx_part == PART_LEGS)
|
|
{
|
|
switch (orientation)
|
|
{
|
|
case ORIENTATION_CENTER :
|
|
{
|
|
if (infront(self, other))
|
|
{
|
|
self->cast_info.currentmove = &thug_move_pain_crch;
|
|
}
|
|
else
|
|
{
|
|
self->cast_info.currentmove = &thug_move_pain_butt;
|
|
}
|
|
break;
|
|
}
|
|
case ORIENTATION_LEFT :
|
|
{
|
|
self->cast_info.currentmove = &thug_move_pain_Lleg;
|
|
break;
|
|
}
|
|
case ORIENTATION_RIGHT :
|
|
{
|
|
self->cast_info.currentmove = &thug_move_pain_Rleg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (mdx_part == PART_HEAD)
|
|
{
|
|
self->cast_info.currentmove = &thug_move_pain_head;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// =========================================================================
|
|
|
|
/*QUAKED cast_thug (1 .5 0) (-16 -16 -24) (16 16 48) x TRIGGERED_START x x x IMMEDIATE_FOLLOW_PATH MELEE NOSHADOW
|
|
cast_group 0 neutral
|
|
cast_group 1 friendly
|
|
cast_group 2 or greater enemy
|
|
guy with the pistol
|
|
|
|
model="models\actors\thug\"
|
|
*/
|
|
void SP_cast_thug(edict_t *self)
|
|
{
|
|
int i;
|
|
char *head_skin, *body_skin, *legs_skin;
|
|
int skin;
|
|
|
|
if (deathmatch->value)
|
|
{
|
|
G_FreeEdict (self);
|
|
return;
|
|
}
|
|
|
|
self->movetype = MOVETYPE_STEP;
|
|
self->solid = SOLID_BBOX;
|
|
VectorSet (self->mins, -16, -16, -24);
|
|
VectorSet (self->maxs, 16, 16, 48);
|
|
|
|
self->s.skinnum = (self->skin-1) * 3;
|
|
|
|
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;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// 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/thug/bald_head.mdx");
|
|
break;
|
|
case 2:
|
|
self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/thug/ski_head.mdx");
|
|
break;
|
|
case 3:
|
|
self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/thug/weld_head.mdx");
|
|
break;
|
|
default:
|
|
self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/thug/head.mdx");
|
|
break;
|
|
}
|
|
if (head_skin)
|
|
{
|
|
// Com_sprintf(skinname, sizeof(skinname), "models/actors/thug/head_%s.tga", head_skin);
|
|
skin = gi.skinindex( self->s.model_parts[PART_HEAD].modelindex, head_skin );
|
|
// skin = gi.skinindex( self->s.model_parts[PART_HEAD].modelindex, skinname );
|
|
}
|
|
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/thug/head.mdx", &self->s.model_parts[PART_HEAD] );
|
|
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/thug/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/thug/legs.mdx", &self->s.model_parts[PART_LEGS] );
|
|
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/thug/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/thug/body.mdx", &self->s.model_parts[PART_BODY] );
|
|
|
|
if (self->spawnflags & THUG_MELEE)
|
|
{
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/thug/pipe.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;
|
|
gi.GetObjectBounds( "models/actors/thug/pipe.mdx", &self->s.model_parts[PART_GUN] );
|
|
|
|
self->cast_info.aiflags |= AI_MELEE;
|
|
}
|
|
else
|
|
{
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/thug/R_pstl.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;
|
|
gi.GetObjectBounds( "models/actors/thug/R_pstl.mdx", &self->s.model_parts[PART_GUN] );
|
|
|
|
// the guns should not be visible until we go to use them
|
|
self->s.model_parts[PART_GUN].invisible_objects = (1<<0 | 1<<1);
|
|
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_GUN2].modelindex = gi.modelindex("models/actors/thug/L_pstl.mdx");
|
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
|
self->s.model_parts[PART_GUN2].baseskin = self->s.model_parts[PART_GUN2].skinnum[i] = 0;
|
|
gi.GetObjectBounds( "models/actors/thug/L_pstl.mdx", &self->s.model_parts[PART_GUN2] );
|
|
|
|
// the guns should not be visible until we go to use them
|
|
self->s.model_parts[PART_GUN].invisible_objects = (1<<0 | 1<<1);
|
|
self->s.model_parts[PART_GUN2].invisible_objects = (1<<0 | 1<<1);
|
|
}
|
|
|
|
if (self->count & 1) // cigar
|
|
{
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_CIGAR].modelindex = gi.modelindex("models/actors/thug/cigar.mdx");
|
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
|
self->s.model_parts[PART_CIGAR].baseskin = self->s.model_parts[PART_CIGAR].skinnum[i] = 0;
|
|
gi.GetObjectBounds( "models/actors/thug/cigar.mdx", &self->s.model_parts[PART_CIGAR] );
|
|
}
|
|
|
|
if (self->count & 2) // fedora hat
|
|
{
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_HAT].modelindex = gi.modelindex("models/actors/thug/fedora.mdx");
|
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
|
self->s.model_parts[PART_HAT].baseskin = self->s.model_parts[PART_HAT].skinnum[i] = 0;
|
|
gi.GetObjectBounds( "models/actors/thug/fedora.mdx", &self->s.model_parts[PART_HAT] );
|
|
}
|
|
else if (self->count & 4) // stetson hat
|
|
{
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_HAT].modelindex = gi.modelindex("models/actors/thug/stetson.mdx");
|
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
|
self->s.model_parts[PART_HAT].baseskin = self->s.model_parts[PART_HAT].skinnum[i] = 0;
|
|
gi.GetObjectBounds( "models/actors/thug/stetson.mdx", &self->s.model_parts[PART_HAT] );
|
|
}
|
|
else if (self->count & 8) // cap (hat)
|
|
{
|
|
self->s.num_parts++;
|
|
self->s.model_parts[PART_HAT].modelindex = gi.modelindex("models/actors/thug/cap.mdx");
|
|
for (i=0; i<MAX_MODELPART_OBJECTS; i++)
|
|
self->s.model_parts[PART_HAT].baseskin = self->s.model_parts[PART_HAT].skinnum[i] = 0;
|
|
gi.GetObjectBounds( "models/actors/thug/cap.mdx", &self->s.model_parts[PART_HAT] );
|
|
}
|
|
|
|
// 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_MALE;
|
|
|
|
self->pain = thug_pain;
|
|
self->die = thug_die;
|
|
|
|
self->cast_info.checkattack = AI_CheckAttack;
|
|
|
|
self->cast_info.attack = thug_attack;
|
|
self->cast_info.long_attack = thug_long_attack;
|
|
self->cast_info.talk = thug_talk;
|
|
self->cast_info.avoid = thug_avoid;
|
|
self->cast_info.backoff = thug_backoff;
|
|
|
|
self->cast_info.catch_fire = thug_catch_fire;
|
|
|
|
self->cast_info.max_attack_distance = 2000;
|
|
|
|
self->cast_info.move_stand = &thug_move_amb_stand;
|
|
self->cast_info.move_crstand = &thug_move_crch_amb_sdt;
|
|
|
|
self->cast_info.move_run = &thug_move_run_guns_dn;
|
|
self->cast_info.move_runwalk = &thug_move_walk_guns_dn;
|
|
self->cast_info.move_crwalk = &thug_move_crouch_walk;
|
|
|
|
self->cast_info.move_jump = &thug_move_jump;
|
|
|
|
self->cast_info.move_avoid_walk = &thug_move_avoid_walk;
|
|
self->cast_info.move_avoid_run = &thug_move_avoid_run;
|
|
self->cast_info.move_avoid_reverse_walk = &thug_move_avoid_reverse_walk;
|
|
self->cast_info.move_avoid_reverse_run = &thug_move_avoid_reverse_run;
|
|
self->cast_info.move_avoid_crwalk = &thug_move_crouch_avoid_walk;
|
|
|
|
self->cast_info.move_crouch_down = &thug_move_crch_knl_dn;
|
|
self->cast_info.move_stand_up = &thug_move_stand_up;
|
|
|
|
self->cast_info.move_lside_step = &thug_move_lside_step;
|
|
self->cast_info.move_rside_step = &thug_move_rside_step;
|
|
|
|
// self->cast_info.move_start_climb = &thug_move_st_clmb;
|
|
self->cast_info.move_start_climb = &thug_move_clmb_loop;
|
|
self->cast_info.move_end_climb = &thug_move_clmb_jmp;
|
|
|
|
self->cast_info.move_evade = &thug_move_evade;
|
|
self->cast_info.move_stand_evade = &thug_move_evade_stand;
|
|
|
|
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;
|
|
|
|
walking_cast_start (self);
|
|
|
|
if (!(self->acc))
|
|
self->acc = 2;
|
|
|
|
if (self->spawnflags & 128)
|
|
self->s.renderfx2 |= RF2_NOSHADOW;
|
|
|
|
}
|