stvoy-sp-sdk/game/AI_Reaver.cpp
2002-11-22 00:00:00 +00:00

363 lines
8.1 KiB
C++

//Reaver
#include "b_local.h"
#include "anims.h"
#include "g_nav.h"
void Reaver_Hunt( void );
void Reaver_Strike( gentity_t *target );
void Reaver_Attack( void );
qboolean Reaver_CheckAttack( attack_e type );
void Reaver_Melee( void );
void Reaver_Ranged( float dist );
/*
-------------------------
NPC_BSReaver_Idle
-------------------------
*/
void NPC_BSReaver_Idle( void )
{
//See if we have a goal to run to
if ( UpdateGoal() )
{
NPC_MoveToGoal();
}
NPC_UpdateAngles( qfalse, qtrue );
}
/*
-------------------------
Reaver_Strike
-------------------------
*/
#define STRIKE_DIST 80
#define STRIKE_DAMAGE 10
void Reaver_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 )
{
//FIXME: Pass in proper weapon information
G_Damage( traceEnt, NPC, NPC, forward, trace.endpos, STRIKE_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PHASER );
}
}
/*
-------------------------
Reaver_Attack
-------------------------
*/
void Reaver_Attack( void )
{
// The slash trail "formation" hasn't been started yet, so don't try and use the old points.
NPC->trigger_formation = qfalse;
if ( rand() & 1 )
{
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_MELEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
NPCInfo->weaponTime = level.time + 1000;
NPCInfo->standTime = level.time + 2000;
}
else
{
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_MELEE2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
NPCInfo->weaponTime = level.time + 1000;
NPCInfo->standTime = level.time + 2000;
}
//Inflict damage
Reaver_Strike( NPC->enemy );
}
/*
-------------------------
Reaver_CheckAttack
-------------------------
*/
qboolean Reaver_CheckAttack( attack_e type )
{
switch( type )
{
case ATTACK_MELEE:
break;
case ATTACK_RANGE:
break;
}
return ( NPCInfo->weaponTime < level.time );
}
/*
-------------------------
Reaver_Melee
-------------------------
*/
void Reaver_Melee( void )
{
//Check to hit
if ( Reaver_CheckAttack( ATTACK_MELEE ) == qtrue )
{
//Get um!
Reaver_Attack();
}
NPC_FaceEnemy();
NPC_UpdateAngles( qtrue, qtrue );
}
/*
-------------------------
Reaver_Pounce
-------------------------
*/
#define REAVER_JUMP_VELOCITY 400
#define REAVER_JUMP_HEIGHT 325
void Reaver_Start_Pounce( void )
{
// Set up the jump animations, but don't jump just yet
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_JUMP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
client->ps.pm_flags |= PMF_JUMP_HELD;
client->ps.groundEntityNum = ENTITYNUM_NONE;
NPCInfo->pauseTime = level.time + 2000; //Time to fire a ranged shot again
NPCInfo->standTime = level.time + 1500; //Time to stand still
NPCInfo->weaponTime = level.time + 2000; //Time to do any attack again
NPCInfo->jumpTime = level.time + 3000; //Time to jump again
}
/*
-------------------------
Reaver_Jump
-------------------------
*/
void Reaver_Jump( void )
{
if ( NPC->enemy )
NPC_FaceEnemy();
// This seems a bit risky, but here I'm trying to actually delay the jump process so it's timed up better with its anims
if ( NPCInfo->jumpTime - 2500 > level.time && level.time + 2700 > NPCInfo->jumpTime )
{
vec3_t dir;
AngleVectors( NPC->currentAngles, dir, NULL, NULL );
VectorScale( dir, REAVER_JUMP_VELOCITY, dir );
dir[2] += REAVER_JUMP_HEIGHT;
VectorCopy( dir, NPC->client->ps.velocity );
}
}
/*
-------------------------
Reaver_JumpOK
-------------------------
*/
// This could be a bit expensive, but fortunately, it doesn't get called very often...
// ...and it helps to keep them from doing jumps that would make them look stupid
qboolean Reaver_JumpOK( void )
{
vec3_t dir, org, end, mins, maxs;
trace_t tr;
// build a trace box
VectorSet( maxs, 8, 8, 8 );
VectorScale( maxs, -1, mins );
AngleVectors( NPC->currentAngles, dir, NULL, NULL );
// trace diagonally up
VectorMA( NPC->currentOrigin, 160, dir, org );
org[2] += 160;
gi.trace( &tr, NPC->currentOrigin, mins, maxs, org, NPC->s.number, MASK_NPCSOLID ); // NOTE: box trace
if ( tr.fraction != 1.0f )
return qfalse; // hit something
// trace forward
VectorMA( NPC->currentOrigin, 256, dir, org );
gi.trace( &tr, NPC->currentOrigin, NULL, NULL, org, NPC->s.number, MASK_NPCSOLID );
if ( tr.fraction != 1.0f )
return qfalse; // hit something
// trace down from the forward point to see if we might fall to our deaths from this jump
VectorSet( dir, 0, 0, -1 );
VectorMA( org, 200, dir, end );
gi.trace( &tr, org, NULL, NULL, end, NPC->s.number, MASK_NPCSOLID );
if ( tr.fraction == 1.0f )
return qfalse; // would fall too far
return qtrue; // jump, baby, jump!!
}
/*
-------------------------
Reaver_Ranged
-------------------------
*/
#define MIN_JUMP_DIST 380
#define MIN_JUMP_DIST_SQR ( MIN_JUMP_DIST * MIN_JUMP_DIST )
void Reaver_Ranged( float dist )
{
if ( NPCInfo->weaponTime < level.time && NPC_CheckCanAttackExt() )
{
//Attempt to fire off a shot at the player
if ( NPCInfo->pauseTime < level.time )
{
//Fire
NPC->client->ps.weapon = WP_FORGE_PROJ;
ucmd.buttons |= BUTTON_ATTACK;
NPC_ApplyWeaponFireDelay();
//Setup delays
NPCInfo->pauseTime = level.time + 2000; //Time to fire a ranged shot again
NPCInfo->standTime = level.time + 1500; //Time to stand still
NPCInfo->weaponTime = level.time + 2500; //Time to do any attack again
return;
}
}
//See if we should leap at the player
if ( dist > MIN_JUMP_DIST_SQR )
{
if ( ( Q_irand( 0, 50 ) > 48 ) && ( NPCInfo->jumpTime < level.time ) )
{
if ( Reaver_JumpOK() )
{
Reaver_Start_Pounce();
return;
}
}
}
Reaver_Hunt();
}
/*
-------------------------
Reaver_Hunt
-------------------------
*/
void Reaver_Hunt( void )
{
NPCInfo->combatMove = qtrue;
//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();
}
//Update our angles regardless
NPC_UpdateAngles( qtrue, qtrue );
}
/*
-------------------------
Reaver_Backup
-------------------------
*/
void Reaver_Backup()
{
vec3_t dir;
// Cheesy push
AngleVectors( NPC->currentAngles, dir, NULL, NULL );
VectorMA( NPC->client->ps.velocity, -50, dir, NPC->client->ps.velocity );
}
/*
-------------------------
NPC_BSReaver_Attack
-------------------------
*/
#define MIN_CRITICAL_DIST 64
#define MIN_CRITICAL_DIST_SQR ( MIN_CRITICAL_DIST * MIN_CRITICAL_DIST )
#define MIN_MELEE_RANGE 80
#define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
void NPC_BSReaver_Attack( void )
{
// We may have a pounce animation started and are waiting to actually start the jump movement...
Reaver_Jump();
//If we don't have an enemy, just idle
if ( NPC_CheckEnemyExt() == qfalse )
{
NPC_BSReaver_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 )
{
Reaver_Hunt();
return;
}
if ( distance < MIN_CRITICAL_DIST_SQR )
{
// We're not happy when the player gets too close
Reaver_Backup();
}
//Decide what to do next
switch ( distRate )
{
case DIST_MELEE:
Reaver_Melee();
break;
case DIST_LONG:
Reaver_Ranged( distance );
break;
}
}