kingpin-sdk/gamesrc/AI_DOG.C
2000-03-27 00:00:00 +00:00

589 lines
14 KiB
C

// dog
#include "g_local.h"
#include "ai_dog.h"
void dog_sound (edict_t *self);
void dog_end_stand (edict_t *self);
void dog_sound_think (edict_t *self);
void dog_melee (edict_t *self);
void dog_evade_amb (edict_t *self);
void dog_melee_bail (edict_t *self);
void dog_evade_checkadjust (edict_t *self);
void dog_evade_adjust (edict_t *self);
void dog_lick2( edict_t *self );
void dog_pounce (edict_t *self);
void dog_talk_think ( edict_t *self );
void dog_bite (edict_t *self);
void dog_bark (edict_t *self);
void dog_growl (edict_t *self);
#include "ai_dog_tables.h"
char *wuf[7] =
{
"actors/dog/dg_bark1.wav",
"actors/dog/dg_bark2.wav",
"actors/dog/dg_die.wav",
"actors/dog/dg_grwl1.wav",
"actors/dog/dg_grwl2.wav",
"actors/dog/dg_lunge.wav",
"actors/dog/dg_yelp.wav"
};
void dog_yelp (edict_t *self)
{
self->last_talk_time = level.time;
gi.sound( self, CHAN_VOICE, gi.soundindex( wuf[6] ), 1, ATTN_NORM, 0 );
}
void dog_bark (edict_t *self)
{
self->last_talk_time = level.time;
gi.sound( self, CHAN_VOICE, gi.soundindex( wuf[rand()%2] ), 1, ATTN_NORM, 0 );
}
void dog_growl (edict_t *self)
{
self->last_talk_time = level.time + 0.5; // growl lasts a bit longer
gi.sound( self, CHAN_BODY, gi.soundindex( wuf[3+rand()%3] ), 1, ATTN_NORM, 0 );
}
void dog_talk_think ( edict_t *self )
{
edict_t *talk_ent;
cast_memory_t *mem;
if (!(talk_ent = self->cast_info.talk_ent))
return;
if (VectorDistance( talk_ent->s.origin, self->s.origin ) > 600)
{
self->cast_info.talk_ent = NULL;
return;
}
if ( (self->cast_info.talk_ent == &g_edicts[1])
&& (self->cast_info.talk_ent->last_talk_time < (level.time - TALK_OTHER_DELAY*2))
&& (self->last_talk_time > (level.time - TALK_SELF_DELAY)))
{
return;
}
// if (last_client_talk && last_client_talk > (level.time - TALK_OTHER_DELAY))
// return; // don't talk too much around the client
if ((talk_ent->health <= 0) || !visible(self, talk_ent) || !infront(talk_ent, self))
{
self->cast_info.talk_ent = NULL;
return;
}
mem = level.global_cast_memory[self->character_index][talk_ent->character_index];
if (!mem || (mem->flags & MEMORY_NO_TALK))
return;
// say something!
if ( ( (self->last_talk_time < (level.time - TALK_SELF_DELAY)) // we haven't spoken for a while
|| ( (talk_ent->client || talk_ent->last_talk_time) // if they haven't spoken yet, don't bother
&& (talk_ent->last_talk_time > (level.time - TALK_OTHER_DELAY*1.5)) // or they've just said something, and we've allowed some time for them to finish saying it
&& (talk_ent->cast_info.talk_ent == self)
&& (self->last_talk_time < talk_ent->last_talk_time)
&& (self->last_talk_time < (level.time - TALK_OTHER_DELAY))))
&& (talk_ent->last_talk_time < (level.time - TALK_OTHER_DELAY)))
{
if (talk_ent->client)
{
// should we be aggressive? or friendly?
if ((self->moral < MORAL_AGGRESSIVE) || (!self->profanity_level && talk_ent->profanity_level && (self->moral < rand()%MORAL_MAX)))
{ // friendly
self->cast_info.currentmove = &dog_move_sniff;
dog_bark( self );
}
else if (self->profanity_level)
{ // attack!
AI_MakeEnemy( self, talk_ent, 0 );
dog_bark( self );
}
else // ready to attack at any second
{
self->cast_info.currentmove = &dog_move_sniff;
dog_growl (self);
self->profanity_level = 3;
}
self->last_talk_time = level.time;
if (!infront( self, talk_ent ))
{
self->cast_info.avoid( self, talk_ent, true );
}
}
}
}
void dog_end_stand( edict_t *self )
{
if (self->cast_info.move_stand_evade && (self->last_stand_evade > (level.time - 3)))
return;
if (random() <= 0.2)
{
self->cast_info.currentmove = &dog_move_sniff;
}
else if (random() <= 0.1)
{
self->cast_info.currentmove = &dog_move_bark;
dog_bark (self);
}
/*
else if ( (random() <= 0.1)
&& (AI_SideTrace( self, 32, 90, -1 )))
{
self->cast_info.currentmove = &dog_move_pee;
}
*/
else
{
self->cast_info.currentmove = self->cast_info.move_stand;
}
}
void dog_backoff( edict_t *self, edict_t *other )
{
}
void dog_pain (edict_t *self, edict_t *other, float kick, int damage, int mdx_part, int mdx_subobject)
{
int rnd;
int baseskin;//, currentskin;
baseskin = self->s.model_parts[mdx_part].baseskin;
if (self->health < (self->max_health * 0.5))
{
self->s.model_parts[PART_HEAD].skinnum[0] = baseskin + 2;
}
else if (self->health < (self->max_health * 0.75))
{
self->s.model_parts[PART_HEAD].skinnum[0] = baseskin + 1;
}
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
if (rand()%2)
dog_yelp (self);
// Ridah, randomly don't play an animation, since it' leaves them WAY open to be killed
if (skill->value > 0 && rand()%2)
return;
rnd = rand()%3;
switch (rnd)
{
case 0:
self->cast_info.currentmove = &dog_move_pain1;
break;
case 1:
self->cast_info.currentmove = &dog_move_pain2;
break;
case 2:
self->cast_info.currentmove = &dog_move_pain3;
break;
}
}
void dog_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject)
{
int rnd;
// self->s.modelindex2 = 0;
// regular death
self->takedamage = DAMAGE_YES;
if (DeathByGib(self, inflictor, attacker, damage))
{ // gib
self->deadflag = DEAD_DEAD;
GibEntity( self, inflictor, damage );
self->s.model_parts[PART_GUN].invisible_objects = (1<<0 | 1<<1);
return;
}
if (self->deadflag == DEAD_DEAD)
return;
self->deadflag = DEAD_DEAD;
// if (!self->onfiretime)
dog_yelp (self);
rnd = rand()%1;
switch (rnd)
{
case 0:
self->cast_info.currentmove = &dog_move_death1;
break;
case 1:
self->cast_info.currentmove = &dog_move_death2;
break;
}
}
qboolean dog_attack (edict_t *self)
{
vec3_t vec;
float dist;
self->cast_info.aiflags |= AI_RUN_LIKE_HELL; // dog runs really fast
VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
dist = VectorNormalize( vec );
// start panting now when we stop
self->cast_info.move_stand = &dog_move_pant;
// yell at them?
if (self->last_talk_time < (level.time - 0.5*TALK_FIGHTING_DELAY))
{
if (rand()%2)
dog_bark(self);
else
dog_growl(self);
}
{
qboolean attack=false;
if (dist < 48 && VectorLength( self->enemy->velocity ) < 250)
{
attack = true;
}
else if (dist < 64)
{
attack = false; // so we do a high attack
}
else // are they running towards us?
{
VectorSubtract( self->enemy->s.origin, self->s.origin, vec );
VectorMA( vec, 0.5, self->enemy->velocity, vec );
dist = VectorNormalize( vec );
if (dist < 96)
{
attack = true;
}
}
if (attack)
{
int rnd;
self->ideal_yaw = vectoyaw(vec);
M_ChangeYaw( self );
if (vec[2] < -0.5)
{
self->cast_info.currentmove = &dog_move_low_atk;
}
else if (vec[2] < -0.1)
{
self->cast_info.currentmove = &dog_move_med_atk;
}
else if (vec[2] > 0.5)
{
self->cast_info.currentmove = &dog_move_upr_atk;
}
else if (self->enemy->maxs[2] < self->enemy->cast_info.standing_max_z)
{
self->cast_info.currentmove = &dog_move_med_atk;
}
/*
if (self->enemy->maxs[2] < 30)
{
if (rand()%10 < 8)
self->cast_info.currentmove = &dog_move_med_atk;
else
self->cast_info.currentmove = &dog_move_low_atk;
}
else if (self->maxs[2] > self->enemy->maxs[2] || self->s.origin[2] > self->enemy->s.origin[2])
{
self->cast_info.currentmove = &dog_move_low_atk;
}
else if (self->s.origin[2] < self->enemy->s.origin[2])
{
self->cast_info.currentmove = &dog_move_upr_atk;
}
*/
else
{
rnd = rand()%10;
if (dist < 48 && rnd < 4)
self->cast_info.currentmove = &dog_move_low_atk;
else if (rnd < 6)
self->cast_info.currentmove = &dog_move_med_atk;
else
self->cast_info.currentmove = &dog_move_upr_atk;
}
self->cast_info.aiflags &=~ AI_RUSH_THE_PLAYER;
return true;
}
else if (dist < 180) // Ridah, increased this to help SR4 dogs jump onto cars
{
self->ideal_yaw = vectoyaw(vec);
M_ChangeYaw( self );
self->cast_info.currentmove = &dog_move_upr_atk;
return true;
}
else if (dist < 400)
self->cast_info.aiflags |= AI_RUSH_THE_PLAYER;
}
return false;
}
void dog_bite (edict_t *self)
{
vec3_t start;//, offset;
vec3_t forward, right;
float damage = 8;
if (self->cast_info.currentmove == &dog_move_upr_atk)
damage *= 2; // double handed attack
// yell at them?
if (self->last_talk_time < (level.time - 1.0))
{
if (rand()%2)
dog_bark(self);
else
dog_growl(self);
}
// VectorSet(offset, 0, 8, self->viewheight - 4);
//VectorSet (offset, 0, 8, 16);
AngleVectors (self->s.angles, forward, right, NULL);
//G_ProjectSource (self->s.origin, offset, forward, right, start);
/*
if (self->cast_info.currentmove == &dog_move_low_atk)
start[2] -= 20;
else if (self->cast_info.currentmove == &dog_move_med_atk)
start[2] -= 8;
else if (self->cast_info.currentmove == &dog_move_upr_atk)
start[2] += 8;
*/
damage *= 0.5;
// fire_dogbite (self, start, forward, damage, 10, MOD_DOGBITE);
if (self->enemy)
{
// trace_t tr;
vec3_t aimdir, dang, end;
VectorSubtract (self->enemy->s.origin, self->s.origin, aimdir);
vectoangles (aimdir, dang);
AngleVectors (dang, forward, NULL, NULL);
VectorMA (self->s.origin, 16, forward, start);
VectorMA (start, 32, forward, end);
// ok it seems to line up with the head ok now
// NAV_DrawLine (start, end);
fire_blackjack( self, start, forward, damage, 0, MOD_DOGBITE );
/*
if (VectorLength (aimdir) < 96)
{
tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT );
if (tr.ent->takedamage)
T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, 0, DAMAGE_BULLET, MOD_DOGBITE);
}
// ok lets see why this isnt working
else if (self->enemy->groundentity == self)
T_Damage (self->enemy, self, self, aimdir, self->enemy->s.origin, vec3_origin, damage, 0, DAMAGE_BULLET, MOD_DOGBITE);
*/
}
}
void dog_long_attack (edict_t *self)
{
}
void dog_pounce (edict_t *self)
{
if (!self->enemy)
return;
self->ideal_yaw = entyaw( self, self->enemy );
M_ChangeYaw( self );
// leap if on ground
if (self->groundentity)
{
AngleVectors( self->s.angles, self->velocity, NULL, NULL );
VectorScale( self->velocity, 500, self->velocity );
self->velocity[2] = 200;
self->groundentity = NULL;
}
}
void dog_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 (!AI_SideTrace( self, -64, 0, 1))
self->cast_info.currentmove = &dog_move_avoid_reverse_walk;
else
self->cast_info.currentmove = &dog_move_avoid_walk;
}
void dog_catch_fire( edict_t *self, edict_t *other )
{
self->enemy = NULL; // stop attacking
self->cast_info.currentmove = &dog_move_run_on_fire;
}
/*QUAKED cast_dog (1 .5 0) (-16 -16 -16) (16 16 22) x TRIGGERED_START x x x
Dog
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)
skin = 1 or 2
model="models\actors\dog\"
*/
void SP_cast_dog (edict_t *self)
{
int i;
int skin;
// return;
if (deathmatch->value)
{
G_FreeEdict (self);
return;
}
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
skin = self->s.skinnum = (self->skin-1) * 3;
VectorSet (self->mins, -16, -16, -24);
VectorSet (self->maxs, 16, 16, DUCKING_MAX_Z+2);
memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);
self->art_skins = NULL;
self->s.num_parts++;
self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/enemy_dog/enemy_dog.mdx");
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/enemy_dog/enemy_dog.mdx", &self->s.model_parts[PART_HEAD] );
if (!self->health)
self->health = 200;
self->gib_health = -200;
self->mass = 100;
self->gender = GENDER_NONE;
self->yaw_speed = 20;
self->pain = dog_pain;
self->die = dog_die;
self->cast_info.checkattack = AI_CheckAttack;
self->cast_info.attack = dog_attack;
self->cast_info.long_attack = dog_long_attack;
self->cast_info.talk = dog_end_stand;
self->cast_info.avoid = dog_avoid;
self->cast_info.backoff = dog_backoff;
self->cast_info.catch_fire = dog_catch_fire;
self->cast_info.max_attack_distance = 128;
self->cast_info.move_stand = &dog_move_amb;
self->cast_info.move_run = &dog_move_run;
self->cast_info.move_runwalk = &dog_move_trot;
// self->cast_info.move_jump = &dog_move_jump; // Jumping animation screws up jump attacking, so don't do one
self->cast_info.move_avoid_walk = &dog_move_avoid_walk;
self->cast_info.move_avoid_run = &dog_move_avoid_run;
self->cast_info.move_avoid_reverse_walk = &dog_move_avoid_reverse_walk;
self->cast_info.move_avoid_reverse_run = &dog_move_avoid_reverse_run;
self->cast_info.move_evade = &dog_move_growl;
self->cast_info.currentmove = self->cast_info.move_stand;
self->cast_info.aiflags |= AI_MELEE;
self->cast_info.aiflags |= AI_NO_TALK;
self->cast_info.aiflags |= AI_ADJUSTPITCH;
// self->gravity = 0.7;
// Temp fix for Dog in SR2 that follows lamont
//if (self->leader_target)
// self->target = NULL;
// we're a dog, therefore we don't hide
self->cast_info.aiflags |= AI_NO_TAKE_COVER;
gi.linkentity (self);
walking_cast_start (self);
}