299 lines
6.1 KiB
C++
299 lines
6.1 KiB
C++
|
//Avatar
|
||
|
|
||
|
#include "b_local.h"
|
||
|
#include "anims.h"
|
||
|
#include "g_nav.h"
|
||
|
|
||
|
void Avatar_Hunt( void );
|
||
|
qboolean Avatar_Backup( void );
|
||
|
void Avatar_Strike( gentity_t *target );
|
||
|
void Avatar_Attack( void );
|
||
|
qboolean Avatar_CheckAttack( attack_e type );
|
||
|
void Avatar_Melee( void );
|
||
|
void Avatar_Ranged( float dist );
|
||
|
extern void NPC_ApplyWeaponFireDelay( void );
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
NPC_BSAvatar_Idle
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
void NPC_BSAvatar_Idle( void )
|
||
|
{
|
||
|
//See if we have a goal to run to
|
||
|
if ( UpdateGoal() )
|
||
|
{
|
||
|
NPC_MoveToGoal();
|
||
|
}
|
||
|
|
||
|
//Update our angles regardless
|
||
|
NPC_UpdateAngles( qtrue, qtrue );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
Avatar_Strike
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
#define STRIKE_DIST 64
|
||
|
#define STRIKE_DAMAGE 10
|
||
|
|
||
|
void Avatar_Strike( gentity_t *target )
|
||
|
{
|
||
|
trace_t trace;
|
||
|
vec3_t endpos, forward;
|
||
|
|
||
|
//Get our forward direction
|
||
|
AngleVectors( NPC->currentAngles, forward, NULL, NULL );
|
||
|
|
||
|
//Get the extent of our reach
|
||
|
VectorMA( NPC->currentOrigin, STRIKE_DIST, forward, endpos );
|
||
|
|
||
|
vec3_t mins = { -8, -8, -8 }, maxs = { 8, 8, 8 };
|
||
|
|
||
|
//Trace to hit
|
||
|
gi.trace( &trace, NPC->currentOrigin, mins, maxs, endpos, NPC->s.number, MASK_SHOT );
|
||
|
|
||
|
gentity_t *traceEnt = &g_entities[ trace.entityNum ];
|
||
|
|
||
|
//If we hit, damage the victim
|
||
|
if ( traceEnt->takedamage )
|
||
|
{
|
||
|
G_Damage( traceEnt, NPC, NPC, forward, trace.endpos, STRIKE_DAMAGE, 0, MOD_PHASER );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
Avatar_Attack
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
void Avatar_Attack( void )
|
||
|
{
|
||
|
NPC->client->ps.weapon = WP_MELEE;
|
||
|
ucmd.buttons |= BUTTON_ALT_ATTACK;
|
||
|
|
||
|
NPC->attackDebounceTime = level.time + 1100;
|
||
|
|
||
|
//Inflict damage
|
||
|
Avatar_Strike( NPC->enemy );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
Avatar_CheckAttack
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
qboolean Avatar_CheckAttack( attack_e type )
|
||
|
{
|
||
|
//TODO: Implement or remove
|
||
|
switch( type )
|
||
|
{
|
||
|
case ATTACK_MELEE:
|
||
|
break;
|
||
|
|
||
|
case ATTACK_RANGE:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ( NPC->attackDebounceTime < level.time );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
Avatar_Melee
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
void Avatar_Melee( void )
|
||
|
{
|
||
|
//Check to hit
|
||
|
if ( Avatar_CheckAttack( ATTACK_MELEE ) == qtrue )
|
||
|
{
|
||
|
//Get um!
|
||
|
Avatar_Attack();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
Avatar_Ranged
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
#define MIN_CRITICAL_DIST 256
|
||
|
#define MIN_CRITICAL_DIST_SQR ( MIN_CRITICAL_DIST * MIN_CRITICAL_DIST )
|
||
|
|
||
|
#define MIN_DESIRED_DIST 512
|
||
|
#define MIN_DESIRED_DIST_SQR ( MIN_DESIRED_DIST * MIN_DESIRED_DIST )
|
||
|
|
||
|
void Avatar_Ranged( float dist )
|
||
|
{
|
||
|
//Attempt to fire off a shot at the player
|
||
|
if ( Avatar_CheckAttack( ATTACK_RANGE ) == qtrue )
|
||
|
{
|
||
|
if ( ( Q_irand( 0, 10 ) > 8 ) && ( NPCInfo->pauseTime < level.time ) )
|
||
|
{
|
||
|
//Fire
|
||
|
NPC->client->ps.weapon = WP_FORGE_PSYCH;
|
||
|
ucmd.buttons |= BUTTON_ATTACK;
|
||
|
|
||
|
NPC_ApplyWeaponFireDelay();
|
||
|
|
||
|
//Setup delays
|
||
|
NPCInfo->pauseTime = level.time + 2000; //Time to fire a ranged shot again
|
||
|
NPCInfo->standTime = level.time + 1400; //Time to stand still
|
||
|
NPCInfo->weaponTime = level.time + 2000; //Time to do any attack again
|
||
|
NPC->attackDebounceTime = level.time + 1100;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( dist > MIN_DESIRED_DIST_SQR )
|
||
|
{
|
||
|
// We want to get closer
|
||
|
Avatar_Hunt();
|
||
|
}
|
||
|
else if ( dist < MIN_CRITICAL_DIST_SQR )
|
||
|
{
|
||
|
// We're not happy when the player gets too close
|
||
|
Avatar_Backup();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
Avatar_Hunt
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
void Avatar_Hunt( void )
|
||
|
{
|
||
|
//If we're not supposed to stand still, pursue the player
|
||
|
if ( NPCInfo->standTime < level.time )
|
||
|
{
|
||
|
//Move towards our goal
|
||
|
NPCInfo->goalEntity = NPC->enemy;
|
||
|
NPCInfo->goalRadius = 12;
|
||
|
NPC_MoveToGoal();
|
||
|
|
||
|
//Add a delay time so they don't attack immediately out of a run
|
||
|
NPCInfo->weaponTime = level.time + 1000;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
Avatar_Backup
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
#define BACKUP_CHECK_DIST -64
|
||
|
#define DROP_DIST 72
|
||
|
|
||
|
qboolean Avatar_Backup( void )
|
||
|
{
|
||
|
trace_t trace;
|
||
|
vec3_t endpos, forward;
|
||
|
|
||
|
// Get our forward direction
|
||
|
AngleVectors( NPC->currentAngles, forward, NULL, NULL );
|
||
|
|
||
|
// Check behind us a bit
|
||
|
VectorMA( NPC->currentOrigin, BACKUP_CHECK_DIST, forward, endpos );
|
||
|
gi.trace( &trace, NPC->currentOrigin, NULL, NULL, endpos, NPC->s.number, MASK_SHOT );
|
||
|
|
||
|
if ( trace.fraction < 0.8f )
|
||
|
{
|
||
|
// There is a wall behind us
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Check down so we don't run off a cliff
|
||
|
VectorSet( forward, 0, 0, -1 );
|
||
|
|
||
|
VectorMA( endpos, DROP_DIST, forward, forward );
|
||
|
gi.trace( &trace, endpos, NULL, NULL, forward, NPC->s.number, MASK_SHOT );
|
||
|
|
||
|
if ( trace.fraction == 1.0f )
|
||
|
{
|
||
|
// there is a drop so don't backup
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( NPCInfo->standTime < level.time )
|
||
|
{
|
||
|
NPCInfo->goalEntity = NPC->enemy;
|
||
|
NPCInfo->goalRadius = 12;
|
||
|
NPC_MoveToGoal();
|
||
|
|
||
|
ucmd.forwardmove *= -1;
|
||
|
ucmd.rightmove *= -1;
|
||
|
}
|
||
|
|
||
|
return qtrue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
-------------------------
|
||
|
NPC_BSAvatar_Attack
|
||
|
-------------------------
|
||
|
*/
|
||
|
|
||
|
#define MIN_MELEE_RANGE 82
|
||
|
#define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
|
||
|
|
||
|
void NPC_BSAvatar_Attack( void )
|
||
|
{
|
||
|
//Update our angles regardless
|
||
|
NPC_UpdateAngles( qtrue, qtrue );
|
||
|
|
||
|
//If we don't have an enemy, just idle
|
||
|
if ( NPC_CheckEnemyExt() == qfalse )
|
||
|
{
|
||
|
NPC_BSAvatar_Idle();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Rate our distance to the target, and our visibilty
|
||
|
float distance = (int) DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
|
||
|
distance_e distRate = ( distance > MIN_MELEE_RANGE_SQR ) ? DIST_LONG : DIST_MELEE;
|
||
|
int visRate = NPC_ClearLOS( NPC->enemy );
|
||
|
|
||
|
//If we cannot see our target, move to see it
|
||
|
if ( visRate == qfalse )
|
||
|
{
|
||
|
Avatar_Hunt();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( distance < MIN_CRITICAL_DIST )
|
||
|
{
|
||
|
// We're not happy when the player gets too close
|
||
|
if ( Avatar_Backup() )
|
||
|
{
|
||
|
// we could backup so don't do anything else for now
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( NPC->enemy )
|
||
|
NPC_FaceEnemy();
|
||
|
|
||
|
//Decide what to do next
|
||
|
switch ( distRate )
|
||
|
{
|
||
|
case DIST_MELEE:
|
||
|
Avatar_Melee();
|
||
|
break;
|
||
|
|
||
|
case DIST_LONG:
|
||
|
Avatar_Ranged( distance );
|
||
|
break;
|
||
|
}
|
||
|
}
|