2013-04-19 02:52:48 +00:00
//NPC_utils.cpp
# include "b_local.h"
# include "../icarus/Q3_Interface.h"
# include "../ghoul2/G2.h"
int teamNumbers [ TEAM_NUM_TEAMS ] ;
int teamStrength [ TEAM_NUM_TEAMS ] ;
int teamCounter [ TEAM_NUM_TEAMS ] ;
# define VALID_ATTACK_CONE 2.0f //Degrees
extern void G_DebugPrint ( int level , const char * format , . . . ) ;
/*
void CalcEntitySpot ( gentity_t * ent , spot_t spot , vec3_t point )
Added : Uses shootAngles if a NPC has them
*/
void CalcEntitySpot ( const gentity_t * ent , const spot_t spot , vec3_t point )
{
vec3_t forward , up , right ;
vec3_t start , end ;
trace_t tr ;
if ( ! ent )
{
return ;
}
switch ( spot )
{
case SPOT_ORIGIN :
if ( VectorCompare ( ent - > r . currentOrigin , vec3_origin ) )
{ //brush
VectorSubtract ( ent - > r . absmax , ent - > r . absmin , point ) ; //size
VectorMA ( ent - > r . absmin , 0.5 , point , point ) ;
}
else
{
VectorCopy ( ent - > r . currentOrigin , point ) ;
}
break ;
case SPOT_CHEST :
case SPOT_HEAD :
if ( ent - > client & & VectorLengthSquared ( ent - > client - > renderInfo . eyePoint ) /*&& (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD)*/ )
{ //Actual tag_head eyespot!
//FIXME: Stasis aliens may have a problem here...
VectorCopy ( ent - > client - > renderInfo . eyePoint , point ) ;
if ( ent - > client - > NPC_class = = CLASS_ATST )
{ //adjust up some
point [ 2 ] + = 28 ; //magic number :)
}
if ( ent - > NPC )
{ //always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
point [ 0 ] = ent - > r . currentOrigin [ 0 ] ;
point [ 1 ] = ent - > r . currentOrigin [ 1 ] ;
}
/*
else if ( ent - > s . eType = = ET_PLAYER )
{
SubtractLeanOfs ( ent , point ) ;
}
*/
}
else
{
VectorCopy ( ent - > r . currentOrigin , point ) ;
if ( ent - > client )
{
point [ 2 ] + = ent - > client - > ps . viewheight ;
}
}
if ( spot = = SPOT_CHEST & & ent - > client )
{
if ( ent - > client - > NPC_class ! = CLASS_ATST )
{ //adjust up some
point [ 2 ] - = ent - > r . maxs [ 2 ] * 0.2f ;
}
}
break ;
case SPOT_HEAD_LEAN :
if ( ent - > client & & VectorLengthSquared ( ent - > client - > renderInfo . eyePoint ) /*&& (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD*/ )
{ //Actual tag_head eyespot!
//FIXME: Stasis aliens may have a problem here...
VectorCopy ( ent - > client - > renderInfo . eyePoint , point ) ;
if ( ent - > client - > NPC_class = = CLASS_ATST )
{ //adjust up some
point [ 2 ] + = 28 ; //magic number :)
}
if ( ent - > NPC )
{ //always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
point [ 0 ] = ent - > r . currentOrigin [ 0 ] ;
point [ 1 ] = ent - > r . currentOrigin [ 1 ] ;
}
/*
else if ( ent - > s . eType = = ET_PLAYER )
{
SubtractLeanOfs ( ent , point ) ;
}
*/
//NOTE: automatically takes leaning into account!
}
else
{
VectorCopy ( ent - > r . currentOrigin , point ) ;
if ( ent - > client )
{
point [ 2 ] + = ent - > client - > ps . viewheight ;
}
//AddLeanOfs ( ent, point );
}
break ;
//FIXME: implement...
//case SPOT_CHEST:
//Returns point 3/4 from tag_torso to tag_head?
//break;
case SPOT_LEGS :
VectorCopy ( ent - > r . currentOrigin , point ) ;
point [ 2 ] + = ( ent - > r . mins [ 2 ] * 0.5 ) ;
break ;
case SPOT_WEAPON :
if ( ent - > NPC & & ! VectorCompare ( ent - > NPC - > shootAngles , vec3_origin ) & & ! VectorCompare ( ent - > NPC - > shootAngles , ent - > client - > ps . viewangles ) )
{
AngleVectors ( ent - > NPC - > shootAngles , forward , right , up ) ;
}
else
{
AngleVectors ( ent - > client - > ps . viewangles , forward , right , up ) ;
}
CalcMuzzlePoint ( ( gentity_t * ) ent , forward , right , up , point ) ;
//NOTE: automatically takes leaning into account!
break ;
case SPOT_GROUND :
// if entity is on the ground, just use it's absmin
if ( ent - > s . groundEntityNum ! = - 1 )
{
VectorCopy ( ent - > r . currentOrigin , point ) ;
point [ 2 ] = ent - > r . absmin [ 2 ] ;
break ;
}
// if it is reasonably close to the ground, give the point underneath of it
VectorCopy ( ent - > r . currentOrigin , start ) ;
start [ 2 ] = ent - > r . absmin [ 2 ] ;
VectorCopy ( start , end ) ;
end [ 2 ] - = 64 ;
trap_Trace ( & tr , start , ent - > r . mins , ent - > r . maxs , end , ent - > s . number , MASK_PLAYERSOLID ) ;
if ( tr . fraction < 1.0 )
{
VectorCopy ( tr . endpos , point ) ;
break ;
}
// otherwise just use the origin
VectorCopy ( ent - > r . currentOrigin , point ) ;
break ;
default :
VectorCopy ( ent - > r . currentOrigin , point ) ;
break ;
}
}
//===================================================================================
/*
qboolean NPC_UpdateAngles ( qboolean doPitch , qboolean doYaw )
Added : option to do just pitch or just yaw
Does not include " aim " in it ' s calculations
FIXME : stop compressing angles into shorts ! ! ! !
*/
qboolean NPC_UpdateAngles ( qboolean doPitch , qboolean doYaw )
{
# if 1
float error ;
float decay ;
float targetPitch = 0 ;
float targetYaw = 0 ;
float yawSpeed ;
qboolean exact = qtrue ;
// if angle changes are locked; just keep the current angles
// aimTime isn't even set anymore... so this code was never reached, but I need a way to lock NPC's yaw, so instead of making a new SCF_ flag, just use the existing render flag... - dmv
if ( ! NPC - > enemy & & ( ( level . time < NPCInfo - > aimTime ) /*|| NPC->client->renderInfo.renderFlags & RF_LOCKEDANGLE*/ ) )
{
if ( doPitch )
targetPitch = NPCInfo - > lockedDesiredPitch ;
if ( doYaw )
targetYaw = NPCInfo - > lockedDesiredYaw ;
}
else
{
// we're changing the lockedDesired Pitch/Yaw below so it's lost it's original meaning, get rid of the lock flag
// NPC->client->renderInfo.renderFlags &= ~RF_LOCKEDANGLE;
if ( doPitch )
{
targetPitch = NPCInfo - > desiredPitch ;
NPCInfo - > lockedDesiredPitch = NPCInfo - > desiredPitch ;
}
if ( doYaw )
{
targetYaw = NPCInfo - > desiredYaw ;
NPCInfo - > lockedDesiredYaw = NPCInfo - > desiredYaw ;
}
}
if ( NPC - > s . weapon = = WP_EMPLACED_GUN )
{
// FIXME: this seems to do nothing, actually...
yawSpeed = 20 ;
}
else
{
yawSpeed = NPCInfo - > stats . yawSpeed ;
}
if ( NPC - > s . weapon = = WP_SABER & & NPC - > client - > ps . fd . forcePowersActive & ( 1 < < FP_SPEED ) )
{
char buf [ 128 ] ;
float tFVal = 0 ;
trap_Cvar_VariableStringBuffer ( " timescale " , buf , sizeof ( buf ) ) ;
tFVal = atof ( buf ) ;
yawSpeed * = 1.0f / tFVal ;
}
if ( doYaw )
{
// decay yaw error
error = AngleDelta ( NPC - > client - > ps . viewangles [ YAW ] , targetYaw ) ;
if ( fabs ( error ) > MIN_ANGLE_ERROR )
{
if ( error )
{
exact = qfalse ;
decay = 60.0 + yawSpeed * 3 ;
decay * = 50.0f / 1000.0f ; //msec
if ( error < 0.0 )
{
error + = decay ;
if ( error > 0.0 )
{
error = 0.0 ;
}
}
else
{
error - = decay ;
if ( error < 0.0 )
{
error = 0.0 ;
}
}
}
}
ucmd . angles [ YAW ] = ANGLE2SHORT ( targetYaw + error ) - client - > ps . delta_angles [ YAW ] ;
}
//FIXME: have a pitchSpeed?
if ( doPitch )
{
// decay pitch error
error = AngleDelta ( NPC - > client - > ps . viewangles [ PITCH ] , targetPitch ) ;
if ( fabs ( error ) > MIN_ANGLE_ERROR )
{
if ( error )
{
exact = qfalse ;
decay = 60.0 + yawSpeed * 3 ;
decay * = 50.0f / 1000.0f ; //msec
if ( error < 0.0 )
{
error + = decay ;
if ( error > 0.0 )
{
error = 0.0 ;
}
}
else
{
error - = decay ;
if ( error < 0.0 )
{
error = 0.0 ;
}
}
}
}
ucmd . angles [ PITCH ] = ANGLE2SHORT ( targetPitch + error ) - client - > ps . delta_angles [ PITCH ] ;
}
ucmd . angles [ ROLL ] = ANGLE2SHORT ( NPC - > client - > ps . viewangles [ ROLL ] ) - client - > ps . delta_angles [ ROLL ] ;
if ( exact & & trap_ICARUS_TaskIDPending ( NPC , TID_ANGLE_FACE ) )
{
trap_ICARUS_TaskIDComplete ( NPC , TID_ANGLE_FACE ) ;
}
return exact ;
# else
float error ;
float decay ;
float targetPitch = 0 ;
float targetYaw = 0 ;
float yawSpeed ;
//float runningMod = NPCInfo->currentSpeed/100.0f;
qboolean exact = qtrue ;
qboolean doSound = qfalse ;
// if angle changes are locked; just keep the current angles
if ( level . time < NPCInfo - > aimTime )
{
if ( doPitch )
targetPitch = NPCInfo - > lockedDesiredPitch ;
if ( doYaw )
targetYaw = NPCInfo - > lockedDesiredYaw ;
}
else
{
if ( doPitch )
targetPitch = NPCInfo - > desiredPitch ;
if ( doYaw )
targetYaw = NPCInfo - > desiredYaw ;
// NPCInfo->aimTime = level.time + 250;
if ( doPitch )
NPCInfo - > lockedDesiredPitch = NPCInfo - > desiredPitch ;
if ( doYaw )
NPCInfo - > lockedDesiredYaw = NPCInfo - > desiredYaw ;
}
yawSpeed = NPCInfo - > stats . yawSpeed ;
if ( doYaw )
{
// decay yaw error
error = AngleDelta ( NPC - > client - > ps . viewangles [ YAW ] , targetYaw ) ;
if ( fabs ( error ) > MIN_ANGLE_ERROR )
{
/*
if ( NPC - > client - > playerTeam = = TEAM_BORG & &
NPCInfo - > behaviorState ! = BS_FACE & & NPCInfo - > tempBehavior ! = BS_FACE )
{ //HACK - borg turn more jittery
if ( error )
{
exact = qfalse ;
decay = 60.0 + yawSpeed * 3 ;
decay * = 50.0 / 1000.0 ; //msec
//Snap to
if ( fabs ( error ) > 10 )
{
if ( random ( ) > 0.6 )
{
doSound = qtrue ;
}
}
if ( error < 0.0 ) //-10.0 )
{
error + = decay ;
if ( error > 0.0 )
{
error = 0.0 ;
}
}
else if ( error > 0.0 ) //10.0 )
{
error - = decay ;
if ( error < 0.0 )
{
error = 0.0 ;
}
}
}
}
else */
if ( error )
{
exact = qfalse ;
decay = 60.0 + yawSpeed * 3 ;
decay * = 50.0 / 1000.0 ; //msec
if ( error < 0.0 )
{
error + = decay ;
if ( error > 0.0 )
{
error = 0.0 ;
}
}
else
{
error - = decay ;
if ( error < 0.0 )
{
error = 0.0 ;
}
}
}
}
ucmd . angles [ YAW ] = ANGLE2SHORT ( targetYaw + error ) - client - > ps . delta_angles [ YAW ] ;
}
//FIXME: have a pitchSpeed?
if ( doPitch )
{
// decay pitch error
error = AngleDelta ( NPC - > client - > ps . viewangles [ PITCH ] , targetPitch ) ;
if ( fabs ( error ) > MIN_ANGLE_ERROR )
{
/*
if ( NPC - > client - > playerTeam = = TEAM_BORG & &
NPCInfo - > behaviorState ! = BS_FACE & & NPCInfo - > tempBehavior ! = BS_FACE )
{ //HACK - borg turn more jittery
if ( error )
{
exact = qfalse ;
decay = 60.0 + yawSpeed * 3 ;
decay * = 50.0 / 1000.0 ; //msec
//Snap to
if ( fabs ( error ) > 10 )
{
if ( random ( ) > 0.6 )
{
doSound = qtrue ;
}
}
if ( error < 0.0 ) //-10.0 )
{
error + = decay ;
if ( error > 0.0 )
{
error = 0.0 ;
}
}
else if ( error > 0.0 ) //10.0 )
{
error - = decay ;
if ( error < 0.0 )
{
error = 0.0 ;
}
}
}
}
else */
if ( error )
{
exact = qfalse ;
decay = 60.0 + yawSpeed * 3 ;
decay * = 50.0 / 1000.0 ; //msec
if ( error < 0.0 )
{
error + = decay ;
if ( error > 0.0 )
{
error = 0.0 ;
}
}
else
{
error - = decay ;
if ( error < 0.0 )
{
error = 0.0 ;
}
}
}
}
ucmd . angles [ PITCH ] = ANGLE2SHORT ( targetPitch + error ) - client - > ps . delta_angles [ PITCH ] ;
}
ucmd . angles [ ROLL ] = ANGLE2SHORT ( NPC - > client - > ps . viewangles [ ROLL ] ) - client - > ps . delta_angles [ ROLL ] ;
/*
if ( doSound )
{
G_Sound ( NPC , G_SoundIndex ( va ( " sound/enemies/borg/borgservo%d.wav " , Q_irand ( 1 , 8 ) ) ) ) ;
}
*/
return exact ;
# endif
}
void NPC_AimWiggle ( vec3_t enemy_org )
{
//shoot for somewhere between the head and torso
//NOTE: yes, I know this looks weird, but it works
if ( NPCInfo - > aimErrorDebounceTime < level . time )
{
NPCInfo - > aimOfs [ 0 ] = 0.3 * flrand ( NPC - > enemy - > r . mins [ 0 ] , NPC - > enemy - > r . maxs [ 0 ] ) ;
NPCInfo - > aimOfs [ 1 ] = 0.3 * flrand ( NPC - > enemy - > r . mins [ 1 ] , NPC - > enemy - > r . maxs [ 1 ] ) ;
if ( NPC - > enemy - > r . maxs [ 2 ] > 0 )
{
NPCInfo - > aimOfs [ 2 ] = NPC - > enemy - > r . maxs [ 2 ] * flrand ( 0.0f , - 1.0f ) ;
}
}
VectorAdd ( enemy_org , NPCInfo - > aimOfs , enemy_org ) ;
}
/*
qboolean NPC_UpdateFiringAngles ( qboolean doPitch , qboolean doYaw )
Includes aim when determining angles - so they don ' t always hit . . .
*/
qboolean NPC_UpdateFiringAngles ( qboolean doPitch , qboolean doYaw )
{
#if 0
float diff ;
float error ;
float targetPitch = 0 ;
float targetYaw = 0 ;
qboolean exact = qtrue ;
if ( level . time < NPCInfo - > aimTime )
{
if ( doPitch )
targetPitch = NPCInfo - > lockedDesiredPitch ;
if ( doYaw )
targetYaw = NPCInfo - > lockedDesiredYaw ;
}
else
{
if ( doPitch )
{
targetPitch = NPCInfo - > desiredPitch ;
NPCInfo - > lockedDesiredPitch = NPCInfo - > desiredPitch ;
}
if ( doYaw )
{
targetYaw = NPCInfo - > desiredYaw ;
NPCInfo - > lockedDesiredYaw = NPCInfo - > desiredYaw ;
}
}
if ( doYaw )
{
// add yaw error based on NPCInfo->aim value
error = ( ( float ) ( 6 - NPCInfo - > stats . aim ) ) * flrand ( - 1 , 1 ) ;
if ( Q_irand ( 0 , 1 ) )
error * = - 1 ;
diff = AngleDelta ( NPC - > client - > ps . viewangles [ YAW ] , targetYaw ) ;
if ( diff )
exact = qfalse ;
ucmd . angles [ YAW ] = ANGLE2SHORT ( targetYaw + diff + error ) - client - > ps . delta_angles [ YAW ] ;
}
if ( doPitch )
{
// add pitch error based on NPCInfo->aim value
error = ( ( float ) ( 6 - NPCInfo - > stats . aim ) ) * flrand ( - 1 , 1 ) ;
diff = AngleDelta ( NPC - > client - > ps . viewangles [ PITCH ] , targetPitch ) ;
if ( diff )
exact = qfalse ;
ucmd . angles [ PITCH ] = ANGLE2SHORT ( targetPitch + diff + error ) - client - > ps . delta_angles [ PITCH ] ;
}
ucmd . angles [ ROLL ] = ANGLE2SHORT ( NPC - > client - > ps . viewangles [ ROLL ] ) - client - > ps . delta_angles [ ROLL ] ;
return exact ;
# else
float error , diff ;
float decay ;
float targetPitch = 0 ;
float targetYaw = 0 ;
qboolean exact = qtrue ;
// if angle changes are locked; just keep the current angles
if ( level . time < NPCInfo - > aimTime )
{
if ( doPitch )
targetPitch = NPCInfo - > lockedDesiredPitch ;
if ( doYaw )
targetYaw = NPCInfo - > lockedDesiredYaw ;
}
else
{
if ( doPitch )
targetPitch = NPCInfo - > desiredPitch ;
if ( doYaw )
targetYaw = NPCInfo - > desiredYaw ;
// NPCInfo->aimTime = level.time + 250;
if ( doPitch )
NPCInfo - > lockedDesiredPitch = NPCInfo - > desiredPitch ;
if ( doYaw )
NPCInfo - > lockedDesiredYaw = NPCInfo - > desiredYaw ;
}
if ( NPCInfo - > aimErrorDebounceTime < level . time )
{
if ( Q_irand ( 0 , 1 ) )
{
NPCInfo - > lastAimErrorYaw = ( ( float ) ( 6 - NPCInfo - > stats . aim ) ) * flrand ( - 1 , 1 ) ;
}
if ( Q_irand ( 0 , 1 ) )
{
NPCInfo - > lastAimErrorPitch = ( ( float ) ( 6 - NPCInfo - > stats . aim ) ) * flrand ( - 1 , 1 ) ;
}
NPCInfo - > aimErrorDebounceTime = level . time + Q_irand ( 250 , 2000 ) ;
}
if ( doYaw )
{
// decay yaw diff
diff = AngleDelta ( NPC - > client - > ps . viewangles [ YAW ] , targetYaw ) ;
if ( diff )
{
exact = qfalse ;
decay = 60.0 + 80.0 ;
decay * = 50.0f / 1000.0f ; //msec
if ( diff < 0.0 )
{
diff + = decay ;
if ( diff > 0.0 )
{
diff = 0.0 ;
}
}
else
{
diff - = decay ;
if ( diff < 0.0 )
{
diff = 0.0 ;
}
}
}
// add yaw error based on NPCInfo->aim value
error = NPCInfo - > lastAimErrorYaw ;
/*
if ( Q_irand ( 0 , 1 ) )
{
error * = - 1 ;
}
*/
ucmd . angles [ YAW ] = ANGLE2SHORT ( targetYaw + diff + error ) - client - > ps . delta_angles [ YAW ] ;
}
if ( doPitch )
{
// decay pitch diff
diff = AngleDelta ( NPC - > client - > ps . viewangles [ PITCH ] , targetPitch ) ;
if ( diff )
{
exact = qfalse ;
decay = 60.0 + 80.0 ;
decay * = 50.0f / 1000.0f ; //msec
if ( diff < 0.0 )
{
diff + = decay ;
if ( diff > 0.0 )
{
diff = 0.0 ;
}
}
else
{
diff - = decay ;
if ( diff < 0.0 )
{
diff = 0.0 ;
}
}
}
error = NPCInfo - > lastAimErrorPitch ;
ucmd . angles [ PITCH ] = ANGLE2SHORT ( targetPitch + diff + error ) - client - > ps . delta_angles [ PITCH ] ;
}
ucmd . angles [ ROLL ] = ANGLE2SHORT ( NPC - > client - > ps . viewangles [ ROLL ] ) - client - > ps . delta_angles [ ROLL ] ;
return exact ;
# endif
}
//===================================================================================
/*
static void NPC_UpdateShootAngles ( vec3_t angles , qboolean doPitch , qboolean doYaw )
Does update angles on shootAngles
*/
void NPC_UpdateShootAngles ( vec3_t angles , qboolean doPitch , qboolean doYaw )
{ //FIXME: shoot angles either not set right or not used!
float error ;
float decay ;
float targetPitch = 0 ;
float targetYaw = 0 ;
if ( doPitch )
targetPitch = angles [ PITCH ] ;
if ( doYaw )
targetYaw = angles [ YAW ] ;
if ( doYaw )
{
// decay yaw error
error = AngleDelta ( NPCInfo - > shootAngles [ YAW ] , targetYaw ) ;
if ( error )
{
decay = 60.0 + 80.0 * NPCInfo - > stats . aim ;
decay * = 100.0f / 1000.0f ; //msec
if ( error < 0.0 )
{
error + = decay ;
if ( error > 0.0 )
{
error = 0.0 ;
}
}
else
{
error - = decay ;
if ( error < 0.0 )
{
error = 0.0 ;
}
}
}
NPCInfo - > shootAngles [ YAW ] = targetYaw + error ;
}
if ( doPitch )
{
// decay pitch error
error = AngleDelta ( NPCInfo - > shootAngles [ PITCH ] , targetPitch ) ;
if ( error )
{
decay = 60.0 + 80.0 * NPCInfo - > stats . aim ;
decay * = 100.0f / 1000.0f ; //msec
if ( error < 0.0 )
{
error + = decay ;
if ( error > 0.0 )
{
error = 0.0 ;
}
}
else
{
error - = decay ;
if ( error < 0.0 )
{
error = 0.0 ;
}
}
}
NPCInfo - > shootAngles [ PITCH ] = targetPitch + error ;
}
}
/*
void SetTeamNumbers ( void )
Sets the number of living clients on each team
FIXME : Does not account for non - respawned players !
FIXME : Don ' t include medics ?
*/
void SetTeamNumbers ( void )
{
gentity_t * found ;
int i ;
for ( i = 0 ; i < TEAM_NUM_TEAMS ; i + + )
{
teamNumbers [ i ] = 0 ;
teamStrength [ i ] = 0 ;
}
for ( i = 0 ; i < 1 ; i + + )
{
found = & g_entities [ i ] ;
if ( found - > client )
{
if ( found - > health > 0 ) //FIXME: or if a player!
{
teamNumbers [ found - > client - > playerTeam ] + + ;
teamStrength [ found - > client - > playerTeam ] + = found - > health ;
}
}
}
for ( i = 0 ; i < TEAM_NUM_TEAMS ; i + + )
{ //Get the average health
teamStrength [ i ] = floor ( ( ( float ) ( teamStrength [ i ] ) ) / ( ( float ) ( teamNumbers [ i ] ) ) ) ;
}
}
extern stringID_table_t BSTable [ ] ;
extern stringID_table_t BSETTable [ ] ;
qboolean G_ActivateBehavior ( gentity_t * self , int bset )
{
bState_t bSID = ( bState_t ) - 1 ;
char * bs_name = NULL ;
if ( ! self )
{
return qfalse ;
}
bs_name = self - > behaviorSet [ bset ] ;
if ( ! ( VALIDSTRING ( bs_name ) ) )
{
return qfalse ;
}
if ( self - > NPC )
{
bSID = ( bState_t ) ( GetIDForString ( BSTable , bs_name ) ) ;
}
if ( bSID > - 1 )
{
self - > NPC - > tempBehavior = BS_DEFAULT ;
self - > NPC - > behaviorState = bSID ;
}
else
{
/*
char newname [ MAX_FILENAME_LENGTH ] ;
sprintf ( ( char * ) & newname , " %s/%s " , Q3_SCRIPT_DIR , bs_name ) ;
*/
//FIXME: between here and actually getting into the ICARUS_RunScript function, the stack gets blown!
//if ( ( ICARUS_entFilter == -1 ) || ( ICARUS_entFilter == self->s.number ) )
if ( 0 )
{
G_DebugPrint ( WL_VERBOSE , " %s attempting to run bSet %s (%s) \n " , self - > targetname , GetStringForID ( BSETTable , bset ) , bs_name ) ;
}
trap_ICARUS_RunScript ( self , va ( " %s/%s " , Q3_SCRIPT_DIR , bs_name ) ) ;
}
return qtrue ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Extended Functions
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
//rww - special system for sync'ing bone angles between client and server.
void NPC_SetBoneAngles ( gentity_t * ent , char * bone , vec3_t angles )
{
# ifdef _XBOX
byte * thebone = & ent - > s . boneIndex1 ;
byte * firstFree = NULL ;
# else
int * thebone = & ent - > s . boneIndex1 ;
int * firstFree = NULL ;
# endif
int i = 0 ;
int boneIndex = G_BoneIndex ( bone ) ;
int flags , up , right , forward ;
vec3_t * boneVector = & ent - > s . boneAngles1 ;
vec3_t * freeBoneVec = NULL ;
while ( thebone )
{
if ( ! * thebone & & ! firstFree )
{ //if the value is 0 then this index is clear, we can use it if we don't find the bone we want already existing.
firstFree = thebone ;
freeBoneVec = boneVector ;
}
else if ( * thebone )
{
if ( * thebone = = boneIndex )
{ //this is it
break ;
}
}
switch ( i )
{
case 0 :
thebone = & ent - > s . boneIndex2 ;
boneVector = & ent - > s . boneAngles2 ;
break ;
case 1 :
thebone = & ent - > s . boneIndex3 ;
boneVector = & ent - > s . boneAngles3 ;
break ;
case 2 :
thebone = & ent - > s . boneIndex4 ;
boneVector = & ent - > s . boneAngles4 ;
break ;
default :
thebone = NULL ;
boneVector = NULL ;
break ;
}
i + + ;
}
if ( ! thebone )
{ //didn't find it, create it
if ( ! firstFree )
{ //no free bones.. can't do a thing then.
Com_Printf ( " WARNING: NPC has no free bone indexes \n " ) ;
return ;
}
thebone = firstFree ;
* thebone = boneIndex ;
boneVector = freeBoneVec ;
}
//If we got here then we have a vector and an index.
//Copy the angles over the vector in the entitystate, so we can use the corresponding index
//to set the bone angles on the client.
VectorCopy ( angles , * boneVector ) ;
//Now set the angles on our server instance if we have one.
if ( ! ent - > ghoul2 )
{
return ;
}
flags = BONE_ANGLES_POSTMULT ;
up = POSITIVE_X ;
right = NEGATIVE_Y ;
forward = NEGATIVE_Z ;
//first 3 bits is forward, second 3 bits is right, third 3 bits is up
ent - > s . boneOrient = ( ( forward ) | ( right < < 3 ) | ( up < < 6 ) ) ;
trap_G2API_SetBoneAngles ( ent - > ghoul2 , 0 , bone , angles , flags , up , right , forward , NULL , 100 , level . time ) ;
}
//rww - and another method of automatically managing surface status for the client and server at once
# define TURN_ON 0x00000000
# define TURN_OFF 0x00000100
void NPC_SetSurfaceOnOff ( gentity_t * ent , const char * surfaceName , int surfaceFlags )
{
int i = 0 ;
qboolean foundIt = qfalse ;
while ( i < BG_NUM_TOGGLEABLE_SURFACES & & bgToggleableSurfaces [ i ] )
{
if ( ! Q_stricmp ( surfaceName , bgToggleableSurfaces [ i ] ) )
{ //got it
foundIt = qtrue ;
break ;
}
i + + ;
}
if ( ! foundIt )
{
Com_Printf ( " WARNING: Tried to toggle NPC surface that isn't in toggleable surface list (%s) \n " , surfaceName ) ;
return ;
}
if ( surfaceFlags = = TURN_ON )
{ //Make sure the entitystate values reflect this surface as on now.
ent - > s . surfacesOn | = ( 1 < < i ) ;
ent - > s . surfacesOff & = ~ ( 1 < < i ) ;
}
else
{ //Otherwise make sure they're off.
ent - > s . surfacesOn & = ~ ( 1 < < i ) ;
ent - > s . surfacesOff | = ( 1 < < i ) ;
}
if ( ! ent - > ghoul2 )
{
return ;
}
trap_G2API_SetSurfaceOnOff ( ent - > ghoul2 , surfaceName , surfaceFlags ) ;
}
//rww - cheap check to see if an armed client is looking in our general direction
qboolean NPC_SomeoneLookingAtMe ( gentity_t * ent )
{
int i = 0 ;
gentity_t * pEnt ;
while ( i < MAX_CLIENTS )
{
pEnt = & g_entities [ i ] ;
if ( pEnt & & pEnt - > inuse & & pEnt - > client & & pEnt - > client - > sess . sessionTeam ! = TEAM_SPECTATOR & &
! ( pEnt - > client - > ps . pm_flags & PMF_FOLLOW ) & & pEnt - > s . weapon ! = WP_NONE )
{
if ( trap_InPVS ( ent - > r . currentOrigin , pEnt - > r . currentOrigin ) )
{
if ( InFOV ( ent , pEnt , 30 , 30 ) )
{ //I'm in a 30 fov or so cone from this player.. that's enough I guess.
return qtrue ;
}
}
}
i + + ;
}
return qfalse ;
}
qboolean NPC_ClearLOS ( const vec3_t start , const vec3_t end )
{
return G_ClearLOS ( NPC , start , end ) ;
}
qboolean NPC_ClearLOS5 ( const vec3_t end )
{
return G_ClearLOS5 ( NPC , end ) ;
}
qboolean NPC_ClearLOS4 ( gentity_t * ent )
{
return G_ClearLOS4 ( NPC , ent ) ;
}
qboolean NPC_ClearLOS3 ( const vec3_t start , gentity_t * ent )
{
return G_ClearLOS3 ( NPC , start , ent ) ;
}
qboolean NPC_ClearLOS2 ( gentity_t * ent , const vec3_t end )
{
return G_ClearLOS2 ( NPC , ent , end ) ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_ValidEnemy
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_ValidEnemy ( gentity_t * ent )
{
int entTeam = TEAM_FREE ;
//Must be a valid pointer
if ( ent = = NULL )
return qfalse ;
//Must not be me
if ( ent = = NPC )
return qfalse ;
//Must not be deleted
if ( ent - > inuse = = qfalse )
return qfalse ;
//Must be alive
if ( ent - > health < = 0 )
return qfalse ;
//In case they're in notarget mode
if ( ent - > flags & FL_NOTARGET )
return qfalse ;
//Must be an NPC
if ( ent - > client = = NULL )
{
// if ( ent->svFlags&SVF_NONNPC_ENEMY )
if ( ent - > s . eType ! = ET_NPC )
{ //still potentially valid
if ( ent - > alliedTeam = = NPC - > client - > playerTeam )
{
return qfalse ;
}
else
{
return qtrue ;
}
}
else
{
return qfalse ;
}
}
else if ( ent - > client & & ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR )
{ //don't go after spectators
return qfalse ;
}
if ( ent - > NPC & & ent - > client )
{
entTeam = ent - > client - > playerTeam ;
}
else if ( ent - > client )
{
if ( g_gametype . integer < GT_TEAM )
{
entTeam = NPCTEAM_PLAYER ;
}
else
{
if ( ent - > client - > sess . sessionTeam = = TEAM_BLUE )
{
entTeam = NPCTEAM_PLAYER ;
}
else if ( ent - > client - > sess . sessionTeam = = TEAM_RED )
{
entTeam = NPCTEAM_ENEMY ;
}
else
{
entTeam = NPCTEAM_NEUTRAL ;
}
}
}
//Can't be on the same team
if ( ent - > client - > playerTeam = = NPC - > client - > playerTeam )
return qfalse ;
//if haven't seen him in a while, give up
//if ( NPCInfo->enemyLastSeenTime != 0 && level.time - NPCInfo->enemyLastSeenTime > 7000 )//FIXME: make a stat?
// return qfalse;
if ( entTeam = = NPC - > client - > enemyTeam //simplest case: they're on my enemy team
| | ( NPC - > client - > enemyTeam = = NPCTEAM_FREE & & ent - > client - > NPC_class ! = NPC - > client - > NPC_class ) //I get mad at anyone and this guy isn't the same class as me
| | ( ent - > client - > NPC_class = = CLASS_WAMPA & & ent - > enemy ) //a rampaging wampa
| | ( ent - > client - > NPC_class = = CLASS_RANCOR & & ent - > enemy ) //a rampaging rancor
| | ( entTeam = = NPCTEAM_FREE & & ent - > client - > enemyTeam = = NPCTEAM_FREE & & ent - > enemy & & ent - > enemy - > client & & ( ent - > enemy - > client - > playerTeam = = NPC - > client - > playerTeam | | ( ent - > enemy - > client - > playerTeam ! = NPCTEAM_ENEMY & & NPC - > client - > playerTeam = = NPCTEAM_PLAYER ) ) ) //enemy is a rampaging non-aligned creature who is attacking someone on our team or a non-enemy (this last condition is used only if we're a good guy - in effect, we protect the innocent)
)
{
return qtrue ;
}
return qfalse ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_TargetVisible
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_TargetVisible ( gentity_t * ent )
{
//Make sure we're in a valid range
if ( DistanceSquared ( ent - > r . currentOrigin , NPC - > r . currentOrigin ) > ( NPCInfo - > stats . visrange * NPCInfo - > stats . visrange ) )
return qfalse ;
//Check our FOV
if ( InFOV ( ent , NPC , NPCInfo - > stats . hfov , NPCInfo - > stats . vfov ) = = qfalse )
return qfalse ;
//Check for sight
if ( NPC_ClearLOS4 ( ent ) = = qfalse )
return qfalse ;
return qtrue ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_GetCheckDelta
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
# define CHECK_TIME_BASE 250
# define CHECK_TIME_BASE_SQUARED ( CHECK_TIME_BASE * CHECK_TIME_BASE )
static int NPC_GetCheckDelta ( void )
{
if ( NPC_ValidEnemy ( NPC - > enemy ) = = qfalse )
{
int distance = DistanceSquared ( NPC - > r . currentOrigin , g_entities [ 0 ] . currentOrigin ) ;
distance / = CHECK_TIME_BASE_SQUARED ;
return ( CHECK_TIME_BASE * distance ) ;
}
return 0 ;
}
*/
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_FindNearestEnemy
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
# define MAX_RADIUS_ENTS 256 //NOTE: This can cause entities to be lost
# define NEAR_DEFAULT_RADIUS 256
int NPC_FindNearestEnemy ( gentity_t * ent )
{
int iradiusEnts [ MAX_RADIUS_ENTS ] ;
gentity_t * radEnt ;
vec3_t mins , maxs ;
int nearestEntID = - 1 ;
float nearestDist = ( float ) WORLD_SIZE * ( float ) WORLD_SIZE ;
float distance ;
int numEnts , numChecks = 0 ;
int i ;
//Setup the bbox to search in
for ( i = 0 ; i < 3 ; i + + )
{
mins [ i ] = ent - > r . currentOrigin [ i ] - NPCInfo - > stats . visrange ;
maxs [ i ] = ent - > r . currentOrigin [ i ] + NPCInfo - > stats . visrange ;
}
//Get a number of entities in a given space
numEnts = trap_EntitiesInBox ( mins , maxs , iradiusEnts , MAX_RADIUS_ENTS ) ;
for ( i = 0 ; i < numEnts ; i + + )
{
radEnt = & g_entities [ iradiusEnts [ i ] ] ;
//Don't consider self
if ( radEnt = = ent )
continue ;
//Must be valid
if ( NPC_ValidEnemy ( radEnt ) = = qfalse )
continue ;
numChecks + + ;
//Must be visible
if ( NPC_TargetVisible ( radEnt ) = = qfalse )
continue ;
distance = DistanceSquared ( ent - > r . currentOrigin , radEnt - > r . currentOrigin ) ;
//Found one closer to us
if ( distance < nearestDist )
{
nearestEntID = radEnt - > s . number ;
nearestDist = distance ;
}
}
return nearestEntID ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_PickEnemyExt
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
gentity_t * NPC_PickEnemyExt ( qboolean checkAlerts )
{
//Check for Hazard Team status and remove this check
/*
if ( NPC - > client - > playerTeam ! = TEAM_STARFLEET )
{
//If we've found the player, return it
if ( NPC_FindPlayer ( ) )
return & g_entities [ 0 ] ;
}
*/
//If we've asked for the closest enemy
int entID = NPC_FindNearestEnemy ( NPC ) ;
//If we have a valid enemy, use it
if ( entID > = 0 )
return & g_entities [ entID ] ;
if ( checkAlerts )
{
int alertEvent = NPC_CheckAlertEvents ( qtrue , qtrue , - 1 , qtrue , AEL_DISCOVERED ) ;
//There is an event to look at
if ( alertEvent > = 0 )
{
alertEvent_t * event = & level . alertEvents [ alertEvent ] ;
//Don't pay attention to our own alerts
if ( event - > owner = = NPC )
return NULL ;
if ( event - > level > = AEL_DISCOVERED )
{
//If it's the player, attack him
if ( event - > owner = = & g_entities [ 0 ] )
return event - > owner ;
//If it's on our team, then take its enemy as well
if ( ( event - > owner - > client ) & & ( event - > owner - > client - > playerTeam = = NPC - > client - > playerTeam ) )
return event - > owner - > enemy ;
}
}
}
return NULL ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_FindPlayer
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_FindPlayer ( void )
{
return NPC_TargetVisible ( & g_entities [ 0 ] ) ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_CheckPlayerDistance
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
static qboolean NPC_CheckPlayerDistance ( void )
{
return qfalse ; //MOOT in MP
/*
float distance ;
//Make sure we have an enemy
if ( NPC - > enemy = = NULL )
return qfalse ;
//Only do this for non-players
if ( NPC - > enemy - > s . number = = 0 )
return qfalse ;
//must be set up to get mad at player
if ( ! NPC - > client | | NPC - > client - > enemyTeam ! = NPCTEAM_PLAYER )
return qfalse ;
//Must be within our FOV
if ( InFOV ( & g_entities [ 0 ] , NPC , NPCInfo - > stats . hfov , NPCInfo - > stats . vfov ) = = qfalse )
return qfalse ;
distance = DistanceSquared ( NPC - > r . currentOrigin , NPC - > enemy - > r . currentOrigin ) ;
if ( distance > DistanceSquared ( NPC - > r . currentOrigin , g_entities [ 0 ] . r . currentOrigin ) )
{ //rwwFIXMEFIXME: care about all clients not just client 0
G_SetEnemy ( NPC , & g_entities [ 0 ] ) ;
return qtrue ;
}
return qfalse ;
*/
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_FindEnemy
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_FindEnemy ( qboolean checkAlerts )
{
gentity_t * newenemy ;
//We're ignoring all enemies for now
//if( NPC->svFlags & SVF_IGNORE_ENEMIES )
if ( 0 ) //rwwFIXMEFIXME: support for flag
{
G_ClearEnemy ( NPC ) ;
return qfalse ;
}
//we can't pick up any enemies for now
if ( NPCInfo - > confusionTime > level . time )
{
return qfalse ;
}
//Don't want a new enemy
//rwwFIXMEFIXME: support for locked enemy
//if ( ( ValidEnemy( NPC->enemy ) ) && ( NPC->svFlags & SVF_LOCKEDENEMY ) )
// return qtrue;
//See if the player is closer than our current enemy
if ( NPC_CheckPlayerDistance ( ) )
{
return qtrue ;
}
//Otherwise, turn off the flag
// NPC->svFlags &= ~SVF_LOCKEDENEMY;
//See if the player is closer than our current enemy
if ( NPC - > client - > NPC_class ! = CLASS_RANCOR
& & NPC - > client - > NPC_class ! = CLASS_WAMPA
//&& NPC->client->NPC_class != CLASS_SAND_CREATURE
& & NPC_CheckPlayerDistance ( ) )
{ //rancors, wampas & sand creatures don't care if player is closer, they always go with closest
return qtrue ;
}
//If we've gotten here alright, then our target it still valid
if ( NPC_ValidEnemy ( NPC - > enemy ) )
return qtrue ;
newenemy = NPC_PickEnemyExt ( checkAlerts ) ;
//if we found one, take it as the enemy
if ( NPC_ValidEnemy ( newenemy ) )
{
G_SetEnemy ( NPC , newenemy ) ;
return qtrue ;
}
return qfalse ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_CheckEnemyExt
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_CheckEnemyExt ( qboolean checkAlerts )
{
//Make sure we're ready to think again
/*
if ( NPCInfo - > enemyCheckDebounceTime > level . time )
return qfalse ;
//Get our next think time
NPCInfo - > enemyCheckDebounceTime = level . time + NPC_GetCheckDelta ( ) ;
//Attempt to find an enemy
return NPC_FindEnemy ( ) ;
*/
return NPC_FindEnemy ( checkAlerts ) ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_FacePosition
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_FacePosition ( vec3_t position , qboolean doPitch )
{
vec3_t muzzle ;
vec3_t angles ;
float yawDelta ;
qboolean facing = qtrue ;
//Get the positions
if ( NPC - > client & & ( NPC - > client - > NPC_class = = CLASS_RANCOR | | NPC - > client - > NPC_class = = CLASS_WAMPA ) ) // || NPC->client->NPC_class == CLASS_SAND_CREATURE) )
{
CalcEntitySpot ( NPC , SPOT_ORIGIN , muzzle ) ;
muzzle [ 2 ] + = NPC - > r . maxs [ 2 ] * 0.75f ;
}
else if ( NPC - > client & & NPC - > client - > NPC_class = = CLASS_GALAKMECH )
{
CalcEntitySpot ( NPC , SPOT_WEAPON , muzzle ) ;
}
else
{
CalcEntitySpot ( NPC , SPOT_HEAD_LEAN , muzzle ) ; //SPOT_HEAD
}
//Find the desired angles
GetAnglesForDirection ( muzzle , position , angles ) ;
NPCInfo - > desiredYaw = AngleNormalize360 ( angles [ YAW ] ) ;
NPCInfo - > desiredPitch = AngleNormalize360 ( angles [ PITCH ] ) ;
if ( NPC - > enemy & & NPC - > enemy - > client & & NPC - > enemy - > client - > NPC_class = = CLASS_ATST )
{
// FIXME: this is kind of dumb, but it was the easiest way to get it to look sort of ok
NPCInfo - > desiredYaw + = flrand ( - 5 , 5 ) + sin ( level . time * 0.004f ) * 7 ;
NPCInfo - > desiredPitch + = flrand ( - 2 , 2 ) ;
}
//Face that yaw
NPC_UpdateAngles ( qtrue , qtrue ) ;
//Find the delta between our goal and our current facing
yawDelta = AngleNormalize360 ( NPCInfo - > desiredYaw - ( SHORT2ANGLE ( ucmd . angles [ YAW ] + client - > ps . delta_angles [ YAW ] ) ) ) ;
//See if we are facing properly
if ( fabs ( yawDelta ) > VALID_ATTACK_CONE )
facing = qfalse ;
if ( doPitch )
{
//Find the delta between our goal and our current facing
float currentAngles = ( SHORT2ANGLE ( ucmd . angles [ PITCH ] + client - > ps . delta_angles [ PITCH ] ) ) ;
float pitchDelta = NPCInfo - > desiredPitch - currentAngles ;
//See if we are facing properly
if ( fabs ( pitchDelta ) > VALID_ATTACK_CONE )
facing = qfalse ;
}
return facing ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_FaceEntity
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_FaceEntity ( gentity_t * ent , qboolean doPitch )
{
vec3_t entPos ;
//Get the positions
CalcEntitySpot ( ent , SPOT_HEAD_LEAN , entPos ) ;
return NPC_FacePosition ( entPos , doPitch ) ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_FaceEnemy
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_FaceEnemy ( qboolean doPitch )
{
if ( NPC = = NULL )
return qfalse ;
if ( NPC - > enemy = = NULL )
return qfalse ;
return NPC_FaceEntity ( NPC - > enemy , doPitch ) ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_CheckCanAttackExt
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_CheckCanAttackExt ( void )
{
//We don't want them to shoot
if ( NPCInfo - > scriptFlags & SCF_DONT_FIRE )
return qfalse ;
//Turn to face
if ( NPC_FaceEnemy ( qtrue ) = = qfalse )
return qfalse ;
//Must have a clear line of sight to the target
if ( NPC_ClearShot ( NPC - > enemy ) = = qfalse )
return qfalse ;
return qtrue ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_ClearLookTarget
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
void NPC_ClearLookTarget ( gentity_t * self )
{
if ( ! self - > client )
{
return ;
}
if ( ( self - > client - > ps . eFlags2 & EF2_HELD_BY_MONSTER ) )
{ //lookTarget is set by and to the monster that's holding you, no other operations can change that
return ;
}
self - > client - > renderInfo . lookTarget = ENTITYNUM_NONE ; //ENTITYNUM_WORLD;
self - > client - > renderInfo . lookTargetClearTime = 0 ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_SetLookTarget
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
void NPC_SetLookTarget ( gentity_t * self , int entNum , int clearTime )
{
if ( ! self - > client )
{
return ;
}
if ( ( self - > client - > ps . eFlags2 & EF2_HELD_BY_MONSTER ) )
{ //lookTarget is set by and to the monster that's holding you, no other operations can change that
return ;
}
self - > client - > renderInfo . lookTarget = entNum ;
self - > client - > renderInfo . lookTargetClearTime = clearTime ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_CheckLookTarget
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
qboolean NPC_CheckLookTarget ( gentity_t * self )
{
if ( self - > client )
{
if ( self - > client - > renderInfo . lookTarget > = 0 & & self - > client - > renderInfo . lookTarget < ENTITYNUM_WORLD )
{ //within valid range
if ( ( & g_entities [ self - > client - > renderInfo . lookTarget ] = = NULL ) | | ! g_entities [ self - > client - > renderInfo . lookTarget ] . inuse )
{ //lookTarget not inuse or not valid anymore
NPC_ClearLookTarget ( self ) ;
}
else if ( self - > client - > renderInfo . lookTargetClearTime & & self - > client - > renderInfo . lookTargetClearTime < level . time )
{ //Time to clear lookTarget
NPC_ClearLookTarget ( self ) ;
}
else if ( g_entities [ self - > client - > renderInfo . lookTarget ] . client & & self - > enemy & & ( & g_entities [ self - > client - > renderInfo . lookTarget ] ! = self - > enemy ) )
{ //should always look at current enemy if engaged in battle... FIXME: this could override certain scripted lookTargets...???
NPC_ClearLookTarget ( self ) ;
}
else
{
return qtrue ;
}
}
}
return qfalse ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - -
NPC_CheckCharmed
- - - - - - - - - - - - - - - - - - - - - - - - -
*/
extern void G_AddVoiceEvent ( gentity_t * self , int event , int speakDebounceTime ) ;
void NPC_CheckCharmed ( void )
{
if ( NPCInfo - > charmedTime & & NPCInfo - > charmedTime < level . time & & NPC - > client )
{ //we were charmed, set us back!
NPC - > client - > playerTeam = NPC - > genericValue1 ;
NPC - > client - > enemyTeam = NPC - > genericValue2 ;
NPC - > s . teamowner = NPC - > genericValue3 ;
NPC - > client - > leader = NULL ;
if ( NPCInfo - > tempBehavior = = BS_FOLLOW_LEADER )
{
NPCInfo - > tempBehavior = BS_DEFAULT ;
}
G_ClearEnemy ( NPC ) ;
NPCInfo - > charmedTime = 0 ;
//say something to let player know you've snapped out of it
G_AddVoiceEvent ( NPC , Q_irand ( EV_CONFUSE1 , EV_CONFUSE3 ) , 2000 ) ;
}
}
void G_GetBoltPosition ( gentity_t * self , int boltIndex , vec3_t pos , int modelIndex )
{
mdxaBone_t boltMatrix ;
vec3_t result , angles ;
if ( ! self | | ! self - > inuse )
{
return ;
}
if ( self - > client )
{ //clients don't actually even keep r.currentAngles maintained
VectorSet ( angles , 0 , self - > client - > ps . viewangles [ YAW ] , 0 ) ;
}
else
{
VectorSet ( angles , 0 , self - > r . currentAngles [ YAW ] , 0 ) ;
}
if ( /*!self || ...haha (sorry, i'm tired)*/ ! self - > ghoul2 )
{
return ;
}
trap_G2API_GetBoltMatrix ( self - > ghoul2 , modelIndex ,
boltIndex ,
& boltMatrix , angles , self - > r . currentOrigin , level . time ,
NULL , self - > modelScale ) ;
if ( pos )
{
BG_GiveMeVectorFromMatrix ( & boltMatrix , ORIGIN , result ) ;
VectorCopy ( result , pos ) ;
}
}
float NPC_EntRangeFromBolt ( gentity_t * targEnt , int boltIndex )
{
vec3_t org ;
if ( ! targEnt )
{
return Q3_INFINITE ;
}
G_GetBoltPosition ( NPC , boltIndex , org , 0 ) ;
return ( Distance ( targEnt - > r . currentOrigin , org ) ) ;
}
float NPC_EnemyRangeFromBolt ( int boltIndex )
{
return ( NPC_EntRangeFromBolt ( NPC - > enemy , boltIndex ) ) ;
}
int NPC_GetEntsNearBolt ( int * radiusEnts , float radius , int boltIndex , vec3_t boltOrg )
{
vec3_t mins , maxs ;
int i ;
//get my handRBolt's position
vec3_t org ;
G_GetBoltPosition ( NPC , boltIndex , org , 0 ) ;
VectorCopy ( org , boltOrg ) ;
//Setup the bbox to search in
for ( i = 0 ; i < 3 ; i + + )
{
mins [ i ] = boltOrg [ i ] - radius ;
maxs [ i ] = boltOrg [ i ] + radius ;
}
//Get the number of entities in a given space
return ( trap_EntitiesInBox ( mins , maxs , radiusEnts , 128 ) ) ;
}