265 lines
5.7 KiB
C++
265 lines
5.7 KiB
C++
//Head Bot
|
|
|
|
#include "b_local.h"
|
|
#include "g_nav.h"
|
|
#include "g_functions.h"
|
|
#include "Q3_Interface.h"
|
|
|
|
extern void Q3_RemoveEnt( gentity_t *victim );
|
|
extern void CG_SurfaceExplosion( vec3_t origin, vec3_t normal, float radius, float shake_speed, qboolean smoke );
|
|
|
|
/*
|
|
-------------------------
|
|
HeadBot_ApplyFriction
|
|
-------------------------
|
|
*/
|
|
|
|
#define VELOCITY_DECAY 0.25
|
|
|
|
static void HeadBot_ApplyFriction( void )
|
|
{
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
if ( NPC->client->ps.velocity[i] )
|
|
{
|
|
NPC->client->ps.velocity[i] *= VELOCITY_DECAY;
|
|
|
|
if ( fabs( NPC->client->ps.velocity[i] ) < 1 )
|
|
{
|
|
NPC->client->ps.velocity[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
HeadBot_MaintainHeight
|
|
-------------------------
|
|
*/
|
|
|
|
static void HeadBot_MaintainHeight( void )
|
|
{
|
|
trace_t trace;
|
|
|
|
vec3_t endPos;
|
|
|
|
VectorSet( endPos, NPC->currentOrigin[0], NPC->currentOrigin[1], NPC->currentOrigin[2] - 1024 );
|
|
gi.trace( &trace, NPC->currentOrigin, NULL, NULL, endPos, NPC->s.number, MASK_SOLID );
|
|
|
|
if ( trace.fraction != 1.0f )
|
|
{
|
|
float length = ( trace.fraction * 1024 );
|
|
|
|
if ( length < 64 )
|
|
{
|
|
ucmd.upmove = 32;
|
|
}
|
|
else if ( length > 120 )
|
|
{
|
|
ucmd.upmove = -32;
|
|
}
|
|
}
|
|
|
|
HeadBot_ApplyFriction();
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
Headbot_MoveToGoal
|
|
-------------------------
|
|
*/
|
|
|
|
#define MIN_NAVGOAL_DIST 32
|
|
#define MAX_ZMOVE 128
|
|
#define MIN_ZMOVE -MAX_ZMOVE
|
|
#define MOVE_INCR 25
|
|
|
|
static void Headbot_MoveToGoal( void )
|
|
{
|
|
//Must have a destination
|
|
if ( NPCInfo->goalEntity == NULL )
|
|
return;
|
|
|
|
vec3_t moveDir;
|
|
|
|
//Get our move info
|
|
VectorSubtract( NPCInfo->goalEntity->currentOrigin, NPC->currentOrigin, moveDir );
|
|
|
|
float len = VectorNormalize( moveDir );
|
|
|
|
//See if we're in range of the goal
|
|
if ( len < MIN_NAVGOAL_DIST )
|
|
{
|
|
//FIXME: We want to increment our next goal if there is one
|
|
NPCInfo->goalEntity = UpdateGoal();
|
|
}
|
|
|
|
if ( ( NPC->speed + MOVE_INCR ) < NPCInfo->stats.runSpeed )
|
|
NPC->speed += MOVE_INCR;
|
|
|
|
//Move
|
|
VectorScale( moveDir, NPC->speed, NPC->client->ps.velocity );
|
|
|
|
vec3_t moveAngles;
|
|
|
|
vectoangles( moveDir, moveAngles );
|
|
|
|
NPCInfo->desiredYaw = AngleNormalize360( moveAngles[YAW] );
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
Headbot_CalculatePlugSpot
|
|
-------------------------
|
|
*/
|
|
|
|
#define SCOUTBOT_PLUG_X -2
|
|
#define SCOUTBOT_PLUG_Y -2
|
|
#define SCOUTBOT_PLUG_Z -14
|
|
|
|
#define WARBOT_PLUG_X 0
|
|
#define WARBOT_PLUG_Y 12
|
|
#define WARBOT_PLUG_Z 32
|
|
|
|
static void Headbot_CalculatePlugSpot( gentity_t *target, vec3_t out )
|
|
{
|
|
if ( stricmp( target->NPC_type, "scoutbot" ) == 0 )
|
|
{
|
|
vec3_t forward, right;
|
|
|
|
AngleVectors( target->currentAngles, forward, right, NULL );
|
|
|
|
VectorMA( target->currentOrigin, SCOUTBOT_PLUG_Y, forward, out );
|
|
VectorMA( out, SCOUTBOT_PLUG_X, right, out );
|
|
out[2] += SCOUTBOT_PLUG_Z;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( ( stricmp( target->NPC_type, "warriorbot" ) == 0 ) || ( stricmp( target->NPC_type, "warriorbot_boss" ) == 0 ) )
|
|
{
|
|
vec3_t forward, right;
|
|
|
|
AngleVectors( target->currentAngles, forward, right, NULL );
|
|
|
|
VectorMA( target->currentOrigin, WARBOT_PLUG_Y, forward, out );
|
|
VectorMA( out, WARBOT_PLUG_X, right, out );
|
|
out[2] += WARBOT_PLUG_Z;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
Headbot_Remove
|
|
-------------------------
|
|
*/
|
|
|
|
static void Headbot_Remove( void )
|
|
{
|
|
//Remove ourself
|
|
NPC->s.eFlags |= EF_NODRAW;
|
|
NPC->s.eFlags &= ~EF_NPC;
|
|
NPC->svFlags &= ~SVF_NPC;
|
|
NPC->s.eType = ET_INVISIBLE;
|
|
NPC->contents = 0;
|
|
NPC->health = 0;
|
|
NPC->targetname = NULL;
|
|
|
|
//Disappear in half a second
|
|
NPC->e_ThinkFunc = thinkF_G_FreeEntity;
|
|
NPC->nextthink = level.time + 50;
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
Headbot_Plugin
|
|
-------------------------
|
|
*/
|
|
|
|
#define MIN_PLUG_DIST 16
|
|
#define MIN_PLUG_DIST_SQR ( MIN_PLUG_DIST * MIN_PLUG_DIST )
|
|
|
|
static void Headbot_Plugin( const char *name )
|
|
{
|
|
gentity_t *target = G_Find( NULL, FOFS(targetname), (char*) name );
|
|
|
|
//If we can't find a target, then just go away
|
|
if ( ( target == NULL ) || ( target->health < 0 ) || ( Q_stricmp( name, "!INVALID!" ) == 0 ) )
|
|
{
|
|
Headbot_Remove();
|
|
|
|
vec3_t normal = { 0, 0, 1 };
|
|
CG_SurfaceExplosion( NPC->currentOrigin, normal, 16, 0.2f, qfalse );
|
|
|
|
return;
|
|
}
|
|
|
|
//Must be of a valid type
|
|
if ( ( stricmp( target->NPC_type, "scoutbot" ) ) && ( stricmp( target->NPC_type, "warriorbot" ) ) && ( stricmp( target->NPC_type, "warriorbot_boss" ) ))
|
|
return;
|
|
|
|
vec3_t targetPos;
|
|
|
|
//Get our destination point
|
|
Headbot_CalculatePlugSpot( target, targetPos );
|
|
|
|
if ( DistanceSquared( targetPos, NPC->currentOrigin ) < MIN_PLUG_DIST_SQR )
|
|
{
|
|
//TODO: Probably going to have to play a special effect here to cover the transition
|
|
//Turn on the target's bolt on
|
|
G_AddBoltOn( target, va("headbot_%s", target->NPC_type) );
|
|
|
|
//Make it a target to enemies
|
|
target->flags &= ~FL_NOTARGET;
|
|
|
|
Headbot_Remove();
|
|
|
|
//Callback a completion
|
|
Q3_TaskIDComplete( NPC, TID_MOVE_NAV );
|
|
|
|
return;
|
|
}
|
|
|
|
//Move there
|
|
NPC_SetMoveGoal( NPC, targetPos, 1, qtrue );
|
|
Headbot_MoveToGoal();
|
|
|
|
VectorScale( NPC->client->ps.velocity, 2, NPC->client->ps.velocity );
|
|
//HeadBot_ApplyFriction();
|
|
|
|
//Turn to match
|
|
NPCInfo->desiredYaw = target->currentAngles[YAW];
|
|
|
|
NPC_UpdateAngles( qtrue, qtrue );
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
NPC_BSHeadBot_Run
|
|
-------------------------
|
|
*/
|
|
|
|
void NPC_BSHeadBot_Run( void )
|
|
{
|
|
//Plug into our target, if we have one
|
|
if VALIDSTRING( NPC->target )
|
|
{
|
|
Headbot_Plugin( NPC->target );
|
|
return;
|
|
}
|
|
|
|
HeadBot_MaintainHeight();
|
|
|
|
NPCInfo->goalEntity = UpdateGoal();
|
|
|
|
if ( NPCInfo->goalEntity )
|
|
{
|
|
Headbot_MoveToGoal();
|
|
}
|
|
|
|
NPC_UpdateAngles( qtrue, qtrue );
|
|
}
|