codemp/game/b_local.h File Reference

#include "g_local.h"
#include "b_public.h"
#include "say.h"
#include "ai.h"

Go to the source code of this file.

Data Structures

struct  navInfo_s

Defines

#define AI_TIMERS   0
#define NAVF_DUCK   0x00000001
#define NAVF_JUMP   0x00000002
#define NAVF_HOLD   0x00000004
#define NAVF_SLOW   0x00000008
#define DEBUG_LEVEL_DETAIL   4
#define DEBUG_LEVEL_INFO   3
#define DEBUG_LEVEL_WARNING   2
#define DEBUG_LEVEL_ERROR   1
#define DEBUG_LEVEL_NONE   0
#define MAX_GOAL_REACHED_DIST_SQUARED   256
#define MIN_ANGLE_ERROR   0.01f
#define MIN_ROCKET_DIST_SQUARED   16384
#define COLLISION_RADIUS   32
#define NUM_POSITIONS   30
#define SFB_SMALLHULL   1
#define SFB_RIFLEMAN   2
#define SFB_OLDBORG   2
#define SFB_PHASER   4
#define SFB_GUN   4
#define SFB_TRICORDER   8
#define SFB_TASER   8
#define SFB_DRILL   16
#define SFB_CINEMATIC   32
#define SFB_NOTSOLID   64
#define SFB_STARTINSOLID   128
#define ALERT_CLEAR_TIME   200
#define CHECK_PVS   1
#define CHECK_360   2
#define CHECK_FOV   4
#define CHECK_SHOOT   8
#define CHECK_VISRANGE   16
#define CP_ANY   0
#define CP_COVER   0x00000001
#define CP_CLEAR   0x00000002
#define CP_FLEE   0x00000004
#define CP_DUCK   0x00000008
#define CP_NEAREST   0x00000010
#define CP_AVOID_ENEMY   0x00000020
#define CP_INVESTIGATE   0x00000040
#define CP_SQUAD   0x00000080
#define CP_AVOID   0x00000100
#define CP_APPROACH_ENEMY   0x00000200
#define CP_CLOSEST   0x00000400
#define CP_FLANK   0x00000800
#define CP_HAS_ROUTE   0x00001000
#define CP_SNIPE   0x00002000
#define CP_SAFE   0x00004000
#define CP_HORZ_DIST_COLL   0x00008000
#define CP_NO_PVS   0x00010000
#define CP_RETREAT   0x00020000
#define CPF_NONE   0
#define CPF_DUCK   0x00000001
#define CPF_FLEE   0x00000002
#define CPF_INVESTIGATE   0x00000004
#define CPF_SQUAD   0x00000008
#define CPF_LEAN   0x00000010
#define CPF_SNIPE   0x00000020
#define MAX_COMBAT_POINT_CHECK   32
#define NIF_NONE   0x00000000
#define NIF_FAILED   0x00000001
#define NIF_MACRO_NAV   0x00000002
#define NIF_COLLISION   0x00000004
#define NIF_BLOCKED   0x00000008

Typedefs

typedef navInfo_s navInfo_t

Functions

void SetNPCGlobals (gentity_t *ent)
void SaveNPCGlobals (void)
void RestoreNPCGlobals (void)
void NPC_Think (gentity_t *self)
void NPC_Pain (gentity_t *self, gentity_t *attacker, int damage)
void NPC_Touch (gentity_t *self, gentity_t *other, trace_t *trace)
void NPC_Use (gentity_t *self, gentity_t *other, gentity_t *activator)
float NPC_GetPainChance (gentity_t *self, int damage)
void Debug_Printf (vmCvar_t *cv, int level, char *fmt,...)
void Debug_NPCPrintf (gentity_t *printNPC, vmCvar_t *cv, int debugLevel, char *fmt,...)
qboolean NPC_CheckInvestigate (int alertEventNum)
qboolean NPC_StandTrackAndShoot (gentity_t *NPC, qboolean canDuck)
void NPC_BSIdle (void)
void NPC_BSPointShoot (qboolean shoot)
void NPC_BSStandGuard (void)
void NPC_BSPatrol (void)
void NPC_BSHuntAndKill (void)
void NPC_BSStandAndShoot (void)
void NPC_BSRunAndShoot (void)
void NPC_BSWait (void)
void NPC_BSDefault (void)
void NPC_BSAdvanceFight (void)
void NPC_BSInvestigate (void)
void NPC_BSSleep (void)
void NPC_BSFlee (void)
void NPC_BSFollowLeader (void)
void NPC_BSJump (void)
void NPC_BSRemove (void)
void NPC_BSSearch (void)
void NPC_BSSearchStart (int homeWp, bState_t bState)
void NPC_BSWander (void)
void NPC_StartFlee (gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int fleeTimeMin, int fleeTimeMax)
void G_StartFlee (gentity_t *self, gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int fleeTimeMin, int fleeTimeMax)
int ChooseBestWeapon (void)
void NPC_ChangeWeapon (int newWeapon)
void ShootThink (void)
void WeaponThink (qboolean inCombat)
qboolean HaveWeapon (int weapon)
qboolean CanShoot (gentity_t *ent, gentity_t *shooter)
void NPC_CheckPossibleEnemy (gentity_t *other, visibility_t vis)
gentity_tNPC_PickEnemy (gentity_t *closestTo, int enemyTeam, qboolean checkVis, qboolean findPlayersFirst, qboolean findClosest)
gentity_tNPC_CheckEnemy (qboolean findNew, qboolean tooFarOk, qboolean setEnemy)
qboolean NPC_CheckAttack (float scale)
qboolean NPC_CheckDefend (float scale)
qboolean NPC_CheckCanAttack (float attack_scale, qboolean stationary)
int NPC_AttackDebounceForWeapon (void)
qboolean EntIsGlass (gentity_t *check)
qboolean ShotThroughGlass (trace_t *tr, gentity_t *target, vec3_t spot, int mask)
qboolean ValidEnemy (gentity_t *ent)
void G_ClearEnemy (gentity_t *self)
gentity_tNPC_PickAlly (qboolean facingEachOther, float range, qboolean ignoreGroup, qboolean movingOnly)
void NPC_LostEnemyDecideChase (void)
float NPC_MaxDistSquaredForWeapon (void)
qboolean NPC_EvaluateShot (int hit, qboolean glassOK)
int NPC_ShotEntity (gentity_t *ent, vec3_t impactPos)
qboolean NPC_SlideMoveToGoal (void)
float NPC_FindClosestTeammate (gentity_t *self)
void NPC_CalcClosestFormationSpot (gentity_t *self)
void G_MaintainFormations (gentity_t *self)
void NPC_BSFormation (void)
void NPC_CreateFormation (gentity_t *self)
void NPC_DropFormation (gentity_t *self)
void NPC_ReorderFormation (gentity_t *self)
void NPC_InsertIntoFormation (gentity_t *self)
void NPC_DeleteFromFormation (gentity_t *self)
void SetGoal (gentity_t *goal, float rating)
void NPC_SetGoal (gentity_t *goal, float rating)
void NPC_ClearGoal (void)
void NPC_ReachedGoal (void)
qboolean ReachedGoal (gentity_t *goal)
gentity_tUpdateGoal (void)
qboolean NPC_ClearPathToGoal (vec3_t dir, gentity_t *goal)
qboolean NPC_MoveToGoal (qboolean tryStraight)
qboolean CanSee (gentity_t *ent)
qboolean InFOV (gentity_t *ent, gentity_t *from, int hFOV, int vFOV)
qboolean InFOV2 (vec3_t origin, gentity_t *from, int hFOV, int vFOV)
qboolean InFOV3 (vec3_t spot, vec3_t from, vec3_t fromAngles, int hFOV, int vFOV)
visibility_t NPC_CheckVisibility (gentity_t *ent, int flags)
qboolean InVisrange (gentity_t *ent)
void NPC_Spawn (gentity_t *ent, gentity_t *other, gentity_t *activator)
int NPC_ReactionTime (void)
qboolean NPC_ParseParms (const char *NPCName, gentity_t *NPC)
void NPC_LoadParms (void)
void CalcEntitySpot (const gentity_t *ent, const spot_t spot, vec3_t point)
qboolean NPC_UpdateAngles (qboolean doPitch, qboolean doYaw)
void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw)
qboolean NPC_UpdateFiringAngles (qboolean doPitch, qboolean doYaw)
void SetTeamNumbers (void)
qboolean G_ActivateBehavior (gentity_t *self, int bset)
void NPC_AimWiggle (vec3_t enemy_org)
void NPC_SetLookTarget (gentity_t *self, int entNum, int clearTime)
int NAV_FindClosestWaypointForEnt (gentity_t *ent, int targWp)
qboolean NAV_CheckAhead (gentity_t *self, vec3_t end, trace_t *trace, int clipmask)
float IdealDistance (gentity_t *self)
void NPC_SetSayState (gentity_t *self, gentity_t *to, int saying)
qboolean G_CheckInSolid (gentity_t *self, qboolean fix)
void NPC_SetAnim (gentity_t *ent, int type, int anim, int priority)
qboolean NPC_EnemyTooFar (gentity_t *enemy, float dist, qboolean toShoot)
void NPC_SetBoneAngles (gentity_t *ent, char *bone, vec3_t angles)
void NPC_SetSurfaceOnOff (gentity_t *ent, const char *surfaceName, int surfaceFlags)
qboolean NPC_ClearLOS (const vec3_t start, const vec3_t end)
qboolean NPC_ClearLOS5 (const vec3_t end)
qboolean NPC_ClearLOS4 (gentity_t *ent)
qboolean NPC_ClearLOS3 (const vec3_t start, gentity_t *ent)
qboolean NPC_ClearLOS2 (gentity_t *ent, const vec3_t end)
qboolean NPC_ClearShot (gentity_t *ent)
int NPC_FindCombatPoint (const vec3_t position, const vec3_t avoidPosition, vec3_t enemyPosition, const int flags, const float avoidDist, const int ignorePoint)
qboolean NPC_ReserveCombatPoint (int combatPointID)
qboolean NPC_FreeCombatPoint (int combatPointID, qboolean failed)
qboolean NPC_SetCombatPoint (int combatPointID)
qboolean NPC_ValidEnemy (gentity_t *ent)
qboolean NPC_CheckEnemyExt (qboolean checkAlerts)
qboolean NPC_FindPlayer (void)
qboolean NPC_CheckCanAttackExt (void)
int NPC_CheckAlertEvents (qboolean checkSight, qboolean checkSound, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel)
qboolean NPC_CheckForDanger (int alertEvent)
void G_AlertTeam (gentity_t *victim, gentity_t *attacker, float radius, float soundDist)
int NPC_FindSquadPoint (vec3_t position)
void ClearPlayerAlertEvents (void)
qboolean G_BoundsOverlap (const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2)
qboolean NAV_HitNavGoal (vec3_t point, vec3_t mins, vec3_t maxs, vec3_t dest, int radius, qboolean flying)
void NPC_SetMoveGoal (gentity_t *ent, vec3_t point, int radius, qboolean isNavGoal, int combatPoint, gentity_t *targetEnt)
qboolean NAV_ClearPathToPoint (gentity_t *self, vec3_t pmins, vec3_t pmaxs, vec3_t point, int clipmask, int okToHitEnt)
void NPC_ApplyWeaponFireDelay (void)
qboolean NPC_FacePosition (vec3_t position, qboolean doPitch)
qboolean NPC_FaceEntity (gentity_t *ent, qboolean doPitch)
qboolean NPC_FaceEnemy (qboolean doPitch)
int NAV_MoveToGoal (gentity_t *self, navInfo_t *info)
void NAV_GetLastMove (navInfo_t *info)
qboolean NAV_AvoidCollision (gentity_t *self, gentity_t *goal, navInfo_t *info)

Variables

vmCvar_t debugNPCAI
vmCvar_t debugNPCFreeze
vmCvar_t debugNPCAimingBeam
vmCvar_t debugBreak
vmCvar_t debugNoRoam
vmCvar_t d_JediAI
vmCvar_t d_saberCombat
gentity_tNPC
gNPC_tNPCInfo
gclient_tclient
usercmd_t ucmd
visibility_t enemyVisibility
int teamNumbers [TEAM_NUM_TEAMS]
int teamStrength [TEAM_NUM_TEAMS]
int teamCounter [TEAM_NUM_TEAMS]
vmCvar_t g_spskill


Define Documentation

#define AI_TIMERS   0
 

Definition at line 12 of file b_local.h.

#define ALERT_CLEAR_TIME   200
 

Definition at line 164 of file b_local.h.

Referenced by ClearPlayerAlertEvents(), and CorpsePhysics().

#define CHECK_360   2
 

Definition at line 166 of file b_local.h.

Referenced by NPC_BSFollowLeader(), NPC_CheckCanAttack(), NPC_CheckPossibleEnemy(), NPC_CheckVisibility(), NPC_PickAlly(), and NPC_PickEnemy().

#define CHECK_FOV   4
 

Definition at line 167 of file b_local.h.

Referenced by NPC_BSAdvanceFight(), NPC_BSFollowLeader(), NPC_BSHuntAndKill(), NPC_CheckCanAttack(), NPC_CheckPossibleEnemy(), NPC_CheckVisibility(), and NPC_PickEnemy().

#define CHECK_PVS   1
 

Definition at line 165 of file b_local.h.

Referenced by NPC_BSFollowLeader(), and NPC_CheckVisibility().

#define CHECK_SHOOT   8
 

Definition at line 168 of file b_local.h.

Referenced by NPC_BSFollowLeader(), NPC_BSHuntAndKill(), and NPC_CheckVisibility().

#define CHECK_VISRANGE   16
 

Definition at line 169 of file b_local.h.

Referenced by NPC_CheckVisibility(), NPC_PickAlly(), and NPC_PickEnemy().

#define COLLISION_RADIUS   32
 

Definition at line 133 of file b_local.h.

#define CP_ANY   0
 

Definition at line 243 of file b_local.h.

Referenced by ST_Commander().

#define CP_APPROACH_ENEMY   0x00000200
 

Definition at line 253 of file b_local.h.

Referenced by NPC_FindCombatPoint(), ST_Commander(), and ST_GetCPFlags().

#define CP_AVOID   0x00000100
 

Definition at line 252 of file b_local.h.

Referenced by NPC_FindCombatPoint(), NPC_StartFlee(), ST_Commander(), and ST_GetCPFlags().

#define CP_AVOID_ENEMY   0x00000020
 

Definition at line 249 of file b_local.h.

Referenced by NPC_FindCombatPoint(), and ST_Commander().

#define CP_CLEAR   0x00000002
 

Definition at line 245 of file b_local.h.

Referenced by NPC_FindCombatPoint(), ST_ApproachEnemy(), ST_Commander(), and ST_GetCPFlags().

#define CP_CLOSEST   0x00000400
 

Definition at line 254 of file b_local.h.

Referenced by ST_ApproachEnemy(), ST_Commander(), and ST_GetCPFlags().

#define CP_COVER   0x00000001
 

Definition at line 244 of file b_local.h.

Referenced by NPC_FindCombatPoint(), NPC_StartFlee(), ST_Commander(), and ST_GetCPFlags().

#define CP_DUCK   0x00000008
 

Definition at line 247 of file b_local.h.

Referenced by ST_Commander().

#define CP_FLANK   0x00000800
 

Definition at line 255 of file b_local.h.

Referenced by NPC_FindCombatPoint(), ST_Commander(), and ST_GetCPFlags().

#define CP_FLEE   0x00000004
 

Definition at line 246 of file b_local.h.

Referenced by ST_Commander(), and ST_GetCPFlags().

#define CP_HAS_ROUTE   0x00001000
 

Definition at line 256 of file b_local.h.

Referenced by NPC_FindCombatPoint(), NPC_StartFlee(), and ST_Commander().

#define CP_HORZ_DIST_COLL   0x00008000
 

Definition at line 259 of file b_local.h.

Referenced by NPC_FindCombatPoint().

#define CP_INVESTIGATE   0x00000040
 

Definition at line 250 of file b_local.h.

Referenced by ST_Commander().

#define CP_NEAREST   0x00000010
 

Definition at line 248 of file b_local.h.

Referenced by NPC_FindCombatPoint(), ST_Commander(), and ST_GetCPFlags().

#define CP_NO_PVS   0x00010000
 

Definition at line 260 of file b_local.h.

Referenced by NPC_FindCombatPoint(), and NPC_StartFlee().

#define CP_RETREAT   0x00020000
 

Definition at line 261 of file b_local.h.

Referenced by NPC_FindCombatPoint(), ST_Commander(), and ST_GetCPFlags().

#define CP_SAFE   0x00004000
 

Definition at line 258 of file b_local.h.

Referenced by ST_Commander(), and ST_GetCPFlags().

#define CP_SNIPE   0x00002000
 

Definition at line 257 of file b_local.h.

#define CP_SQUAD   0x00000080
 

Definition at line 251 of file b_local.h.

Referenced by ST_Commander().

#define CPF_DUCK   0x00000001
 

Definition at line 264 of file b_local.h.

Referenced by ST_Commander().

#define CPF_FLEE   0x00000002
 

Definition at line 265 of file b_local.h.

#define CPF_INVESTIGATE   0x00000004
 

Definition at line 266 of file b_local.h.

#define CPF_LEAN   0x00000010
 

Definition at line 268 of file b_local.h.

#define CPF_NONE   0
 

Definition at line 263 of file b_local.h.

#define CPF_SNIPE   0x00000020
 

Definition at line 269 of file b_local.h.

#define CPF_SQUAD   0x00000008
 

Definition at line 267 of file b_local.h.

Referenced by NPC_FindSquadPoint().

#define DEBUG_LEVEL_DETAIL   4
 

Definition at line 22 of file b_local.h.

Referenced by Debug_NPCPrintf(), and Debug_Printf().

#define DEBUG_LEVEL_ERROR   1
 

Definition at line 25 of file b_local.h.

Referenced by Debug_NPCPrintf(), and Debug_Printf().

#define DEBUG_LEVEL_INFO   3
 

Definition at line 23 of file b_local.h.

Referenced by Debug_NPCPrintf(), Debug_Printf(), and NPC_PickEnemy().

#define DEBUG_LEVEL_NONE   0
 

Definition at line 26 of file b_local.h.

#define DEBUG_LEVEL_WARNING   2
 

Definition at line 24 of file b_local.h.

Referenced by Debug_NPCPrintf(), and Debug_Printf().

#define MAX_COMBAT_POINT_CHECK   32
 

Definition at line 271 of file b_local.h.

#define MAX_GOAL_REACHED_DIST_SQUARED   256
 

Definition at line 28 of file b_local.h.

#define MIN_ANGLE_ERROR   0.01f
 

Definition at line 29 of file b_local.h.

Referenced by NPC_BSJump(), and NPC_UpdateAngles().

#define MIN_ROCKET_DIST_SQUARED   16384
 

Definition at line 31 of file b_local.h.

Referenced by Boba_FireDecide(), NPC_BSST_Attack(), and ST_Commander().

#define NAVF_DUCK   0x00000001
 

Definition at line 17 of file b_local.h.

#define NAVF_HOLD   0x00000004
 

Definition at line 19 of file b_local.h.

#define NAVF_JUMP   0x00000002
 

Definition at line 18 of file b_local.h.

#define NAVF_SLOW   0x00000008
 

Definition at line 20 of file b_local.h.

#define NIF_BLOCKED   0x00000008
 

Definition at line 306 of file b_local.h.

Referenced by NAV_AvoidCollision(), and NAV_TestForBlocked().

#define NIF_COLLISION   0x00000004
 

Definition at line 305 of file b_local.h.

Referenced by NAV_AvoidCollision(), and NAVNEW_AvoidCollision().

#define NIF_FAILED   0x00000001
 

Definition at line 303 of file b_local.h.

#define NIF_MACRO_NAV   0x00000002
 

Definition at line 304 of file b_local.h.

Referenced by NPC_GetMoveDirection(), and NPC_GetMoveDirectionAltRoute().

#define NIF_NONE   0x00000000
 

Definition at line 302 of file b_local.h.

#define NUM_POSITIONS   30
 

Definition at line 134 of file b_local.h.

#define SFB_CINEMATIC   32
 

Definition at line 147 of file b_local.h.

Referenced by NPC_Begin(), and NPC_SetMiscDefaultData().

#define SFB_DRILL   16
 

Definition at line 145 of file b_local.h.

#define SFB_GUN   4
 

Definition at line 142 of file b_local.h.

#define SFB_NOTSOLID   64
 

Definition at line 148 of file b_local.h.

Referenced by NPC_Begin().

#define SFB_OLDBORG   2
 

Definition at line 140 of file b_local.h.

#define SFB_PHASER   4
 

Definition at line 141 of file b_local.h.

Referenced by NPC_WeaponsForTeam().

#define SFB_RIFLEMAN   2
 

Definition at line 139 of file b_local.h.

Referenced by NPC_WeaponsForTeam().

#define SFB_SMALLHULL   1
 

Definition at line 137 of file b_local.h.

#define SFB_STARTINSOLID   128
 

Definition at line 149 of file b_local.h.

Referenced by NPC_Begin().

#define SFB_TASER   8
 

Definition at line 144 of file b_local.h.

#define SFB_TRICORDER   8
 

Definition at line 143 of file b_local.h.


Typedef Documentation

typedef struct navInfo_s navInfo_t
 

Referenced by AI_CheckEnemyCollision(), NAV_AvoidCollision(), NAV_GetLastMove(), NAV_MoveToGoal(), NAVNEW_AvoidCollision(), NAVNEW_MoveToGoal(), and NPC_GetMoveDirectionAltRoute().


Function Documentation

void CalcEntitySpot const gentity_t ent,
const spot_t  spot,
vec3_t  point
 

Definition at line 20 of file NPC_utils.c.

References entityShared_t::absmax, entityShared_t::absmin, AngleVectors(), CalcMuzzlePoint(), CLASS_ATST, gentity_s::client, entityShared_t::currentOrigin, trace_t::endpos, renderInfo_s::eyePoint, trace_t::fraction, gentity_t, entityState_s::groundEntityNum, MASK_PLAYERSOLID, entityShared_t::maxs, entityShared_t::mins, gentity_s::NPC, gclient_s::NPC_class, entityState_s::number, gclient_s::ps, gentity_s::r, gclient_s::renderInfo, gentity_s::s, gNPC_t::shootAngles, SPOT_CHEST, SPOT_GROUND, SPOT_HEAD, SPOT_HEAD_LEAN, SPOT_LEGS, SPOT_ORIGIN, SPOT_WEAPON, trap_Trace(), vec3_origin, vec3_t, VectorCopy, VectorMA, VectorSubtract, playerState_s::viewangles, and playerState_s::viewheight.

Referenced by Boba_FireDecide(), CanSee(), CanShoot(), G_CheckAlertEvents(), G_ClearLOS2(), G_ClearLOS3(), G_ClearLOS4(), G_ClearLOS5(), G_FindLocalInterestPoint(), ImperialProbe_FireBlaster(), InFOV(), InFOV2(), InVisrange(), Mark1_FireBlaster(), Mark1_FireRocket(), Mark2_FireBlaster(), NPC_BSAdvanceFight(), NPC_BSCinematic(), NPC_BSFollowLeader(), NPC_BSGM_Attack(), NPC_BSPointShoot(), NPC_CheckCanAttack(), NPC_ClearShot(), NPC_FaceEntity(), NPC_FacePosition(), NPC_ShotEntity(), Remote_Fire(), Seeker_Fire(), Sniper_FaceEnemy(), and Sniper_UpdateEnemyPos().

00021 {
00022         vec3_t  forward, up, right;
00023         vec3_t  start, end;
00024         trace_t tr;
00025 
00026         if ( !ent )
00027         {
00028                 return;
00029         }
00030         switch ( spot ) 
00031         {
00032         case SPOT_ORIGIN:
00033                 if(VectorCompare(ent->r.currentOrigin, vec3_origin))
00034                 {//brush
00035                         VectorSubtract(ent->r.absmax, ent->r.absmin, point);//size
00036                         VectorMA(ent->r.absmin, 0.5, point, point);
00037                 }
00038                 else
00039                 {
00040                         VectorCopy ( ent->r.currentOrigin, point );
00041                 }
00042                 break;
00043 
00044         case SPOT_CHEST:
00045         case SPOT_HEAD:
00046                 if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) /*&& (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD)*/ )
00047                 {//Actual tag_head eyespot!
00048                         //FIXME: Stasis aliens may have a problem here...
00049                         VectorCopy( ent->client->renderInfo.eyePoint, point );
00050                         if ( ent->client->NPC_class == CLASS_ATST )
00051                         {//adjust up some
00052                                 point[2] += 28;//magic number :)
00053                         }
00054                         if ( ent->NPC )
00055                         {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
00056                                 point[0] = ent->r.currentOrigin[0];
00057                                 point[1] = ent->r.currentOrigin[1];
00058                         }
00059                         /*
00060                         else if (ent->s.eType == ET_PLAYER )
00061                         {
00062                                 SubtractLeanOfs( ent, point );
00063                         }
00064                         */
00065                 }
00066                 else
00067                 {
00068                         VectorCopy ( ent->r.currentOrigin, point );
00069                         if ( ent->client ) 
00070                         {
00071                                 point[2] += ent->client->ps.viewheight;
00072                         }
00073                 }
00074                 if ( spot == SPOT_CHEST && ent->client )
00075                 {
00076                         if ( ent->client->NPC_class != CLASS_ATST )
00077                         {//adjust up some
00078                                 point[2] -= ent->r.maxs[2]*0.2f;
00079                         }
00080                 }
00081                 break;
00082 
00083         case SPOT_HEAD_LEAN:
00084                 if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) /*&& (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD*/ )
00085                 {//Actual tag_head eyespot!
00086                         //FIXME: Stasis aliens may have a problem here...
00087                         VectorCopy( ent->client->renderInfo.eyePoint, point );
00088                         if ( ent->client->NPC_class == CLASS_ATST )
00089                         {//adjust up some
00090                                 point[2] += 28;//magic number :)
00091                         }
00092                         if ( ent->NPC )
00093                         {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
00094                                 point[0] = ent->r.currentOrigin[0];
00095                                 point[1] = ent->r.currentOrigin[1];
00096                         }
00097                         /*
00098                         else if ( ent->s.eType == ET_PLAYER )
00099                         {
00100                                 SubtractLeanOfs( ent, point );
00101                         }
00102                         */
00103                         //NOTE: automatically takes leaning into account!
00104                 }
00105                 else
00106                 {
00107                         VectorCopy ( ent->r.currentOrigin, point );
00108                         if ( ent->client ) 
00109                         {
00110                                 point[2] += ent->client->ps.viewheight;
00111                         }
00112                         //AddLeanOfs ( ent, point );
00113                 }
00114                 break;
00115 
00116         //FIXME: implement...
00117         //case SPOT_CHEST:
00118                 //Returns point 3/4 from tag_torso to tag_head?
00119                 //break;
00120 
00121         case SPOT_LEGS:
00122                 VectorCopy ( ent->r.currentOrigin, point );
00123                 point[2] += (ent->r.mins[2] * 0.5);
00124                 break;
00125 
00126         case SPOT_WEAPON:
00127                 if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles ))
00128                 {
00129                         AngleVectors( ent->NPC->shootAngles, forward, right, up );
00130                 }
00131                 else
00132                 {
00133                         AngleVectors( ent->client->ps.viewangles, forward, right, up );
00134                 }
00135                 CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point );
00136                 //NOTE: automatically takes leaning into account!
00137                 break;
00138 
00139         case SPOT_GROUND:
00140                 // if entity is on the ground, just use it's absmin
00141                 if ( ent->s.groundEntityNum != -1 ) 
00142                 {
00143                         VectorCopy( ent->r.currentOrigin, point );
00144                         point[2] = ent->r.absmin[2];
00145                         break;
00146                 }
00147 
00148                 // if it is reasonably close to the ground, give the point underneath of it
00149                 VectorCopy( ent->r.currentOrigin, start );
00150                 start[2] = ent->r.absmin[2];
00151                 VectorCopy( start, end );
00152                 end[2] -= 64;
00153                 trap_Trace( &tr, start, ent->r.mins, ent->r.maxs, end, ent->s.number, MASK_PLAYERSOLID );
00154                 if ( tr.fraction < 1.0 ) 
00155                 {
00156                         VectorCopy( tr.endpos, point);
00157                         break;
00158                 }
00159 
00160                 // otherwise just use the origin
00161                 VectorCopy( ent->r.currentOrigin, point );
00162                 break;
00163 
00164         default:
00165                 VectorCopy ( ent->r.currentOrigin, point );
00166                 break;
00167         }
00168 }

qboolean CanSee gentity_t ent  ) 
 

Definition at line 47 of file NPC_senses.c.

References CalcEntitySpot(), trace_t::fraction, gentity_t, MASK_OPAQUE, NPC, NULL, entityState_s::number, qboolean, qfalse, qtrue, gentity_s::s, ShotThroughGlass(), SPOT_HEAD, SPOT_HEAD_LEAN, SPOT_LEGS, SPOT_ORIGIN, trap_Trace(), and vec3_t.

Referenced by NPC_CheckVisibility().

00048 {
00049         trace_t         tr;
00050         vec3_t          eyes;
00051         vec3_t          spot;
00052 
00053         CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
00054 
00055         CalcEntitySpot( ent, SPOT_ORIGIN, spot );
00056         trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
00057         ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
00058         if ( tr.fraction == 1.0 ) 
00059         {
00060                 return qtrue;
00061         }
00062 
00063         CalcEntitySpot( ent, SPOT_HEAD, spot );
00064         trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
00065         ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
00066         if ( tr.fraction == 1.0 ) 
00067         {
00068                 return qtrue;
00069         }
00070 
00071         CalcEntitySpot( ent, SPOT_LEGS, spot );
00072         trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
00073         ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
00074         if ( tr.fraction == 1.0 ) 
00075         {
00076                 return qtrue;
00077         }
00078 
00079         return qfalse;
00080 }

qboolean CanShoot gentity_t ent,
gentity_t shooter
 

Definition at line 1150 of file NPC_combat.c.

References CalcEntitySpot(), gentity_s::client, trace_t::endpos, trace_t::entityNum, g_entities, gentity_t, gentity_s::health, MASK_SHOT, gentity_s::NPC, NULL, entityState_s::number, gclient_s::playerTeam, qboolean, qfalse, qtrue, random, gentity_s::s, ShotThroughGlass(), SPOT_HEAD, SPOT_ORIGIN, SPOT_WEAPON, trace_t::startsolid, gNPC_t::touchedByPlayer, trap_Trace(), vec3_t, and VectorSubtract.

Referenced by NPC_CheckVisibility().

01151 {
01152         trace_t         tr;
01153         vec3_t          muzzle;
01154         vec3_t          spot, diff;
01155         gentity_t       *traceEnt;
01156 
01157         CalcEntitySpot( shooter, SPOT_WEAPON, muzzle );
01158         CalcEntitySpot( ent, SPOT_ORIGIN, spot );               //FIXME preferred target locations for some weapons (feet for R/L)
01159 
01160         trap_Trace ( &tr, muzzle, NULL, NULL, spot, shooter->s.number, MASK_SHOT );
01161         traceEnt = &g_entities[ tr.entityNum ];
01162 
01163         // point blank, baby!
01164         if (tr.startsolid && (shooter->NPC) && (shooter->NPC->touchedByPlayer) ) 
01165         {
01166                 traceEnt = shooter->NPC->touchedByPlayer;
01167         }
01168         
01169         if ( ShotThroughGlass( &tr, ent, spot, MASK_SHOT ) )
01170         {
01171                 traceEnt = &g_entities[ tr.entityNum ];
01172         }
01173 
01174         // shot is dead on
01175         if ( traceEnt == ent ) 
01176         {
01177                 return qtrue;
01178         }
01179 //MCG - Begin
01180         else
01181         {//ok, can't hit them in center, try their head
01182                 CalcEntitySpot( ent, SPOT_HEAD, spot );
01183                 trap_Trace ( &tr, muzzle, NULL, NULL, spot, shooter->s.number, MASK_SHOT );
01184                 traceEnt = &g_entities[ tr.entityNum ];
01185                 if ( traceEnt == ent) 
01186                 {
01187                         return qtrue;
01188                 }
01189         }
01190 
01191         //Actually, we should just check to fire in dir we're facing and if it's close enough,
01192         //and we didn't hit someone on our own team, shoot
01193         VectorSubtract(spot, tr.endpos, diff);
01194         if(VectorLength(diff) < random() * 32)
01195         {
01196                 return qtrue;
01197         }
01198 //MCG - End
01199         // shot would hit a non-client
01200         if ( !traceEnt->client ) 
01201         {
01202                 return qfalse;
01203         }
01204 
01205         // shot is blocked by another player
01206 
01207         // he's already dead, so go ahead
01208         if ( traceEnt->health <= 0 ) 
01209         {
01210                 return qtrue;
01211         }
01212 
01213         // don't deliberately shoot a teammate
01214         if ( traceEnt->client && ( traceEnt->client->playerTeam == shooter->client->playerTeam ) ) 
01215         {
01216                 return qfalse;
01217         }
01218 
01219         // he's just in the wrong place, go ahead
01220         return qtrue;
01221 }

int ChooseBestWeapon void   ) 
 

void ClearPlayerAlertEvents void   ) 
 

Definition at line 660 of file NPC_senses.c.

Referenced by G_RunFrame().

00661 {
00662         int curNumAlerts = level.numAlertEvents;
00663         int i;
00664         //loop through them all (max 32)
00665         for ( i = 0; i < curNumAlerts; i++ )
00666         {
00667                 //see if the event is old enough to delete
00668                 if ( level.alertEvents[i].timestamp && level.alertEvents[i].timestamp + ALERT_CLEAR_TIME < level.time )
00669                 {//this event has timed out
00670                         //drop the count
00671                         level.numAlertEvents--;
00672                         //shift the rest down
00673                         if ( level.numAlertEvents > 0 )
00674                         {//still have more in the array
00675                                 if ( (i+1) < MAX_ALERT_EVENTS )
00676                                 {
00677                                         memmove( &level.alertEvents[i], &level.alertEvents[i+1], sizeof(alertEvent_t)*(MAX_ALERT_EVENTS-(i+1) ) );
00678                                 }
00679                         }
00680                         else
00681                         {//just clear this one... or should we clear the whole array?
00682                                 memset( &level.alertEvents[i], 0, sizeof( alertEvent_t ) );
00683                         }
00684                 }
00685         }
00686         //make sure this never drops below zero... if it does, something very very bad happened
00687         assert( level.numAlertEvents >= 0 );
00688 
00689         if ( eventClearTime < level.time )
00690         {//this is just a 200ms debouncer so things that generate constant alerts (like corpses and missiles) add an alert every 200 ms
00691                 eventClearTime = level.time + ALERT_CLEAR_TIME;
00692         }
00693 }

void Debug_NPCPrintf gentity_t printNPC,
vmCvar_t cv,
int  debugLevel,
char *  fmt,
... 
 

Definition at line 41 of file NPC_misc.c.

References COLOR_GREEN, COLOR_RED, COLOR_WHITE, COLOR_YELLOW, Com_Printf(), DEBUG_LEVEL_DETAIL, DEBUG_LEVEL_ERROR, DEBUG_LEVEL_INFO, DEBUG_LEVEL_WARNING, gentity_t, level, Q_COLOR_ESCAPE, gentity_s::targetname, level_locals_t::time, va_end, va_list, va_start, vmCvar_t::value, and vsprintf().

00042 {
00043         int                     color;
00044         va_list         argptr;
00045         char            msg[1024];
00046 
00047         if (cv->value < debugLevel)
00048         {
00049                 return;
00050         }
00051 
00052 //      if ( debugNPCName.string[0] && Q_stricmp( debugNPCName.string, printNPC->targetname) != 0 ) 
00053 //      {
00054 //              return;
00055 //      }
00056 
00057         if (debugLevel == DEBUG_LEVEL_DETAIL)
00058                 color = COLOR_WHITE;
00059         else if (debugLevel == DEBUG_LEVEL_INFO)
00060                 color = COLOR_GREEN;
00061         else if (debugLevel == DEBUG_LEVEL_WARNING)
00062                 color = COLOR_YELLOW;
00063         else if (debugLevel == DEBUG_LEVEL_ERROR)
00064                 color = COLOR_RED;
00065         else
00066                 color = COLOR_RED;
00067 
00068         va_start (argptr,fmt);
00069         vsprintf (msg, fmt, argptr);
00070         va_end (argptr);
00071 
00072         Com_Printf ("%c%c%5i (%s) %s", Q_COLOR_ESCAPE, color, level.time, printNPC->targetname, msg);
00073 }

void Debug_Printf vmCvar_t cv,
int  level,
char *  fmt,
... 
 

Definition at line 10 of file NPC_misc.c.

References Com_Printf(), DEBUG_LEVEL_DETAIL, DEBUG_LEVEL_ERROR, DEBUG_LEVEL_INFO, DEBUG_LEVEL_WARNING, level, S_COLOR_GREEN, S_COLOR_RED, S_COLOR_WHITE, S_COLOR_YELLOW, level_locals_t::time, va_end, va_list, va_start, vmCvar_t::value, and vsprintf().

Referenced by NPC_PickEnemy().

00011 {
00012         char            *color;
00013         va_list         argptr;
00014         char            msg[1024];
00015 
00016         if (cv->value < debugLevel)
00017                 return;
00018 
00019         if (debugLevel == DEBUG_LEVEL_DETAIL)
00020                 color = S_COLOR_WHITE;
00021         else if (debugLevel == DEBUG_LEVEL_INFO)
00022                 color = S_COLOR_GREEN;
00023         else if (debugLevel == DEBUG_LEVEL_WARNING)
00024                 color = S_COLOR_YELLOW;
00025         else if (debugLevel == DEBUG_LEVEL_ERROR)
00026                 color = S_COLOR_RED;
00027         else
00028                 color = S_COLOR_RED;
00029 
00030         va_start (argptr,fmt);
00031         vsprintf (msg, fmt, argptr);
00032         va_end (argptr);
00033 
00034         Com_Printf("%s%5i:%s", color, level.time, msg);
00035 }

qboolean EntIsGlass gentity_t check  ) 
 

Definition at line 1114 of file NPC_combat.c.

References gentity_s::classname, gentity_s::count, gentity_t, gentity_s::health, Q_stricmp(), qboolean, qfalse, and qtrue.

Referenced by G_ClearLineOfSight(), NPC_CheckCanAttack(), and ShotThroughGlass().

01115 {
01116         if(check->classname &&
01117                 !Q_stricmp("func_breakable", check->classname) &&
01118                 check->count == 1 && check->health <= 100)
01119         {
01120                 return qtrue;
01121         }
01122 
01123         return qfalse;
01124 }

qboolean G_ActivateBehavior gentity_t self,
int  bset
 

Definition at line 851 of file NPC_utils.c.

00852 {
00853         bState_t        bSID = (bState_t)-1;
00854         char *bs_name = NULL;
00855 
00856         if ( !self )
00857         {
00858                 return qfalse;
00859         }
00860 
00861         bs_name = self->behaviorSet[bset];
00862         
00863         if( !(VALIDSTRING( bs_name )) )
00864         {
00865                 return qfalse;
00866         }
00867 
00868         if ( self->NPC )
00869         {
00870                 bSID = (bState_t)(GetIDForString( BSTable, bs_name ));
00871         }
00872 
00873         if(bSID > -1)
00874         {
00875                 self->NPC->tempBehavior = BS_DEFAULT;
00876                 self->NPC->behaviorState = bSID;
00877         }
00878         else
00879         {
00880                 /*
00881                 char                    newname[MAX_FILENAME_LENGTH];           
00882                 sprintf((char *) &newname, "%s/%s", Q3_SCRIPT_DIR, bs_name );
00883                 */
00884                 
00885                 //FIXME: between here and actually getting into the ICARUS_RunScript function, the stack gets blown!
00886                 //if ( ( ICARUS_entFilter == -1 ) || ( ICARUS_entFilter == self->s.number ) )
00887                 if (0)
00888                 {
00889                         G_DebugPrint( WL_VERBOSE, "%s attempting to run bSet %s (%s)\n", self->targetname, GetStringForID( BSETTable, bset ), bs_name );
00890                 }
00891                 trap_ICARUS_RunScript( self, va( "%s/%s", Q3_SCRIPT_DIR, bs_name ) );
00892         }
00893         return qtrue;
00894 }

void G_AlertTeam gentity_t victim,
gentity_t attacker,
float  radius,
float  soundDist
 

Definition at line 1769 of file g_combat.c.

References gentity_s::client, entityShared_t::currentOrigin, gentity_s::enemy, g_entities, G_SetEnemy(), gentity_t, gentity_s::health, gNPCstats_e::hfov, InFOV(), gentity_s::NPC, NPC_ClearLOS2(), NULL, gclient_s::playerTeam, gentity_s::r, SCF_IGNORE_ALERTS, SCF_LOOK_FOR_ENEMIES, SCF_NO_GROUPS, gNPC_t::scriptFlags, gNPC_t::stats, trap_EntitiesInBox(), trap_InPVS(), vec3_t, and gNPCstats_e::vfov.

Referenced by G_AngerAlert(), and G_DeathAlert().

01770 {
01771         int                     radiusEnts[ 128 ];
01772         gentity_t       *check;
01773         vec3_t          mins, maxs;
01774         int                     numEnts;
01775         int                     i;
01776         float           distSq, sndDistSq = (soundDist*soundDist);
01777 
01778         if ( attacker == NULL || attacker->client == NULL )
01779                 return;
01780 
01781         //Setup the bbox to search in
01782         for ( i = 0; i < 3; i++ )
01783         {
01784                 mins[i] = victim->r.currentOrigin[i] - radius;
01785                 maxs[i] = victim->r.currentOrigin[i] + radius;
01786         }
01787 
01788         //Get the number of entities in a given space
01789         numEnts = trap_EntitiesInBox( mins, maxs, radiusEnts, 128 );
01790 
01791         //Cull this list
01792         for ( i = 0; i < numEnts; i++ )
01793         {
01794                 check = &g_entities[radiusEnts[i]];
01795 
01796                 //Validate clients
01797                 if ( check->client == NULL )
01798                         continue;
01799 
01800                 //only want NPCs
01801                 if ( check->NPC == NULL )
01802                         continue;
01803 
01804                 //Don't bother if they're ignoring enemies
01805 //              if ( check->svFlags & SVF_IGNORE_ENEMIES )
01806 //                      continue;
01807 
01808                 //This NPC specifically flagged to ignore alerts
01809                 if ( check->NPC->scriptFlags & SCF_IGNORE_ALERTS )
01810                         continue;
01811 
01812                 //This NPC specifically flagged to ignore alerts
01813                 if ( !(check->NPC->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
01814                         continue;
01815 
01816                 //this ent does not participate in group AI
01817                 if ( (check->NPC->scriptFlags&SCF_NO_GROUPS) )
01818                         continue;
01819 
01820                 //Skip the requested avoid check if present
01821                 if ( check == victim )
01822                         continue;
01823 
01824                 //Skip the attacker
01825                 if ( check == attacker )
01826                         continue;
01827 
01828                 //Must be on the same team
01829                 if ( check->client->playerTeam != victim->client->playerTeam )
01830                         continue;
01831 
01832                 //Must be alive
01833                 if ( check->health <= 0 )
01834                         continue;
01835 
01836                 if ( check->enemy == NULL )
01837                 {//only do this if they're not already mad at someone
01838                         distSq = DistanceSquared( check->r.currentOrigin, victim->r.currentOrigin );
01839                         if ( distSq > 16384 /*128 squared*/ && !trap_InPVS( victim->r.currentOrigin, check->r.currentOrigin ) )
01840                         {//not even potentially visible/hearable
01841                                 continue;
01842                         }
01843                         //NOTE: this allows sound alerts to still go through doors/PVS if the teammate is within 128 of the victim...
01844                         if ( soundDist <= 0 || distSq > sndDistSq )
01845                         {//out of sound range
01846                                 if ( !InFOV( victim, check, check->NPC->stats.hfov, check->NPC->stats.vfov ) 
01847                                         ||  !NPC_ClearLOS2( check, victim->r.currentOrigin ) )
01848                                 {//out of FOV or no LOS
01849                                         continue;
01850                                 }
01851                         }
01852 
01853                         //FIXME: This can have a nasty cascading effect if setup wrong...
01854                         G_SetEnemy( check, attacker );
01855                 }
01856         }
01857 }

qboolean G_BoundsOverlap const vec3_t  mins1,
const vec3_t  maxs1,
const vec3_t  mins2,
const vec3_t  maxs2
 

Definition at line 94 of file NPC_goal.c.

00095 {//NOTE: flush up against counts as overlapping
00096         if(mins1[0]>maxs2[0])
00097                 return qfalse;
00098 
00099         if(mins1[1]>maxs2[1])
00100                 return qfalse;
00101 
00102         if(mins1[2]>maxs2[2])
00103                 return qfalse;
00104 
00105         if(maxs1[0]<mins2[0])
00106                 return qfalse;
00107 
00108         if(maxs1[1]<mins2[1])
00109                 return qfalse;
00110 
00111         if(maxs1[2]<mins2[2])
00112                 return qfalse;
00113 
00114         return qtrue;
00115 }

qboolean G_CheckInSolid gentity_t self,
qboolean  fix
 

Definition at line 1965 of file g_utils.c.

01966 {
01967         trace_t trace;
01968         vec3_t  end, mins;
01969 
01970         VectorCopy(self->r.currentOrigin, end);
01971         end[2] += self->r.mins[2];
01972         VectorCopy(self->r.mins, mins);
01973         mins[2] = 0;
01974 
01975         trap_Trace(&trace, self->r.currentOrigin, mins, self->r.maxs, end, self->s.number, self->clipmask);
01976         if(trace.allsolid || trace.startsolid)
01977         {
01978                 return qtrue;
01979         }
01980         
01981         if(trace.fraction < 1.0)
01982         {
01983                 if(fix)
01984                 {//Put them at end of trace and check again
01985                         vec3_t  neworg;
01986 
01987                         VectorCopy(trace.endpos, neworg);
01988                         neworg[2] -= self->r.mins[2];
01989                         G_SetOrigin(self, neworg);
01990                         trap_LinkEntity(self);
01991 
01992                         return G_CheckInSolid(self, qfalse);
01993                 }
01994                 else
01995                 {
01996                         return qtrue;
01997                 }
01998         }
01999                 
02000         return qfalse;
02001 }

void G_ClearEnemy gentity_t self  ) 
 

Definition at line 17 of file NPC_combat.c.

References gentity_s::client, gentity_s::enemy, gentity_t, gNPC_t::goalEntity, renderInfo_s::lookTarget, gentity_s::NPC, NPC_CheckLookTarget(), NPC_ClearLookTarget(), NULL, entityState_s::number, gclient_s::renderInfo, and gentity_s::s.

Referenced by ForceTelepathyCheckDirectNPCTarget(), G_SetEnemy(), NPC_BSFollowLeader(), NPC_CheckCharmed(), NPC_CheckEnemy(), NPC_ExecuteBState(), NPC_FindEnemy(), NPC_Grenadier_PlayConfusionSound(), NPC_LostEnemyDecideChase(), NPC_PlayConfusionSound(), NPC_Sniper_PlayConfusionSound(), and ST_Commander().

00018 {
00019         NPC_CheckLookTarget( self );
00020 
00021         if ( self->enemy )
00022         {
00023                 if(     self->client && self->client->renderInfo.lookTarget == self->enemy->s.number )
00024                 {
00025                         NPC_ClearLookTarget( self );
00026                 }
00027 
00028                 if ( self->NPC && self->enemy == self->NPC->goalEntity )
00029                 {
00030                         self->NPC->goalEntity = NULL;
00031                 }
00032                 //FIXME: set last enemy?
00033         }
00034 
00035         self->enemy = NULL;
00036 }

void G_MaintainFormations gentity_t self  ) 
 

void G_StartFlee gentity_t self,
gentity_t enemy,
vec3_t  dangerPoint,
int  dangerLevel,
int  fleeTimeMin,
int  fleeTimeMax
 

Definition at line 1636 of file NPC_behavior.c.

References gentity_t, gentity_s::NPC, NPC_StartFlee(), RestoreNPCGlobals(), SaveNPCGlobals(), SetNPCGlobals(), and vec3_t.

Referenced by ST_StartFlee().

01637 {
01638         if ( !self->NPC )
01639         {//player
01640                 return;
01641         }
01642         SaveNPCGlobals();
01643         SetNPCGlobals( self );
01644 
01645         NPC_StartFlee( enemy, dangerPoint, dangerLevel, fleeTimeMin, fleeTimeMax );
01646 
01647         RestoreNPCGlobals();
01648 }

qboolean HaveWeapon int  weapon  ) 
 

Definition at line 1109 of file NPC_combat.c.

References client, gclient_s::ps, qboolean, STAT_WEAPONS, and playerState_s::stats.

01110 {
01111         return ( client->ps.stats[STAT_WEAPONS] & ( 1 << weapon ) );
01112 }

float IdealDistance gentity_t self  ) 
 

Definition at line 2475 of file NPC_combat.c.

References gNPCstats_e::aggression, gentity_t, NPC, NPCInfo, gentity_s::s, gNPC_t::stats, entityState_s::weapon, WP_BLASTER, WP_BRYAR_PISTOL, WP_ROCKET_LAUNCHER, WP_SABER, and WP_THERMAL.

Referenced by NPC_BSHuntAndKill().

02476 {
02477         float   ideal;
02478 
02479         ideal = 225 - 20 * NPCInfo->stats.aggression;
02480         switch ( NPC->s.weapon ) 
02481         {
02482         case WP_ROCKET_LAUNCHER:
02483                 ideal += 200;
02484                 break;
02485 
02486         case WP_THERMAL:
02487                 ideal += 50;
02488                 break;
02489 
02490 /*      case WP_TRICORDER:
02491                 ideal = 0;
02492                 break;
02493 */
02494         case WP_SABER:
02495         case WP_BRYAR_PISTOL:
02496 //      case WP_BLASTER_PISTOL:
02497         case WP_BLASTER:
02498         default:
02499                 break;
02500         }
02501 
02502         return ideal;
02503 }

qboolean InFOV gentity_t ent,
gentity_t from,
int  hFOV,
int  vFOV
 

Definition at line 149 of file NPC_senses.c.

References AngleDelta(), entityState_s::angles, CalcEntitySpot(), gentity_s::client, renderInfo_s::eyeAngles, fabs(), gentity_t, PITCH, gclient_s::ps, qboolean, qfalse, qtrue, gclient_s::renderInfo, gentity_s::s, SPOT_HEAD, SPOT_HEAD_LEAN, SPOT_LEGS, SPOT_ORIGIN, vec3_origin, vec3_t, vectoangles(), VectorCopy, VectorSubtract, playerState_s::viewangles, and YAW.

Referenced by G_AlertTeam(), Jedi_CheckAmbushPlayer(), NPC_CheckEnemyStealth(), NPC_CheckSurrender(), NPC_CheckVisibility(), NPC_ShySpawn(), NPC_SomeoneLookingAtMe(), and NPC_TargetVisible().

00150 {
00151         vec3_t  eyes;
00152         vec3_t  spot;
00153         vec3_t  deltaVector;
00154         vec3_t  angles, fromAngles;
00155         vec3_t  deltaAngles;
00156 
00157         if( from->client )
00158         {
00159                 if( !VectorCompare( from->client->renderInfo.eyeAngles, vec3_origin ) )
00160                 {//Actual facing of tag_head!
00161                         //NOTE: Stasis aliens may have a problem with this?
00162                         VectorCopy( from->client->renderInfo.eyeAngles, fromAngles );
00163                 }
00164                 else
00165                 {
00166                         VectorCopy( from->client->ps.viewangles, fromAngles );
00167                 }
00168         }
00169         else
00170         {
00171                 VectorCopy(from->s.angles, fromAngles);
00172         }
00173 
00174         CalcEntitySpot( from, SPOT_HEAD_LEAN, eyes );
00175 
00176         CalcEntitySpot( ent, SPOT_ORIGIN, spot );
00177         VectorSubtract ( spot, eyes, deltaVector);
00178 
00179         vectoangles ( deltaVector, angles );
00180         deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
00181         deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
00182         if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) 
00183         {
00184                 return qtrue;
00185         }
00186 
00187         CalcEntitySpot( ent, SPOT_HEAD, spot );
00188         VectorSubtract ( spot, eyes, deltaVector);
00189         vectoangles ( deltaVector, angles );
00190         deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
00191         deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
00192         if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) 
00193         {
00194                 return qtrue;
00195         }
00196 
00197         CalcEntitySpot( ent, SPOT_LEGS, spot );
00198         VectorSubtract ( spot, eyes, deltaVector);
00199         vectoangles ( deltaVector, angles );
00200         deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
00201         deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
00202         if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) 
00203         {
00204                 return qtrue;
00205         }
00206 
00207         return qfalse;
00208 }

qboolean InFOV2 vec3_t  origin,
gentity_t from,
int  hFOV,
int  vFOV
 

Definition at line 129 of file NPC_senses.c.

References entityState_s::angles, CalcEntitySpot(), gentity_s::client, gentity_t, InFOV3(), gclient_s::ps, qboolean, gentity_s::s, SPOT_HEAD, vec3_t, VectorCopy, and playerState_s::viewangles.

00130 {
00131         vec3_t  fromAngles, eyes;
00132 
00133         if( from->client )
00134         {
00135                 VectorCopy(from->client->ps.viewangles, fromAngles);
00136         }
00137         else
00138         {
00139                 VectorCopy(from->s.angles, fromAngles);
00140         }
00141 
00142         CalcEntitySpot( from, SPOT_HEAD, eyes );
00143 
00144         return InFOV3( origin, eyes, fromAngles, hFOV, vFOV );
00145 }

qboolean InFOV3 vec3_t  spot,
vec3_t  from,
vec3_t  fromAngles,
int  hFOV,
int  vFOV
 

Definition at line 109 of file NPC_senses.c.

00110 {
00111         vec3_t  deltaVector, angles, deltaAngles;
00112 
00113         VectorSubtract ( spot, from, deltaVector );
00114         vectoangles ( deltaVector, angles );
00115         
00116         deltaAngles[PITCH]      = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
00117         deltaAngles[YAW]        = AngleDelta ( fromAngles[YAW], angles[YAW] );
00118 
00119         if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) 
00120         {
00121                 return qtrue;
00122         }
00123 
00124         return qfalse;
00125 }

qboolean InVisrange gentity_t ent  ) 
 

Definition at line 210 of file NPC_senses.c.

References CalcEntitySpot(), gentity_t, NPC, NPCInfo, qboolean, qfalse, qtrue, SPOT_HEAD_LEAN, SPOT_ORIGIN, gNPC_t::stats, vec3_t, VectorSubtract, and gNPCstats_e::visrange.

Referenced by NPC_CheckVisibility(), and NPC_PickEnemy().

00211 {//FIXME: make a calculate visibility for ents that takes into account
00212         //lighting, movement, turning, crouch/stand up, other anims, hide brushes, etc.
00213         vec3_t  eyes;
00214         vec3_t  spot;
00215         vec3_t  deltaVector;
00216         float   visrange = (NPCInfo->stats.visrange*NPCInfo->stats.visrange);
00217 
00218         CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
00219 
00220         CalcEntitySpot( ent, SPOT_ORIGIN, spot );
00221         VectorSubtract ( spot, eyes, deltaVector);
00222 
00223         /*if(ent->client)
00224         {
00225                 float   vel, avel;
00226                 if(ent->client->ps.velocity[0] || ent->client->ps.velocity[1] || ent->client->ps.velocity[2])
00227                 {
00228                         vel = VectorLength(ent->client->ps.velocity);
00229                         if(vel > 128)
00230                         {
00231                                 visrange += visrange * (vel/256);
00232                         }
00233                 }
00234 
00235                 if(ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
00236                 {//FIXME: shouldn't they need to have line of sight to you to detect this?
00237                         avel = VectorLength(ent->avelocity);
00238                         if(avel > 15)
00239                         {
00240                                 visrange += visrange * (avel/60);
00241                         }
00242                 }
00243         }*/
00244 
00245         if(VectorLengthSquared(deltaVector) > visrange)
00246         {
00247                 return qfalse;
00248         }
00249 
00250         return qtrue;
00251 }

qboolean NAV_AvoidCollision gentity_t self,
gentity_t goal,
navInfo_t info
 

Definition at line 902 of file g_nav.c.

References gNPC_t::aiFlags, navInfo_s::blocker, CONTENTS_BODY, entityShared_t::currentOrigin, navInfo_s::direction, navInfo_s::distance, EDGE_PATH, trace_t::entityNum, navInfo_s::flags, G_DrawEdge(), g_entities, gentity_t, MAX_COLL_AVOID_DIST, NAV_CheckAhead(), NAV_ClearBlockedInfo(), NAV_ResolveEntityCollision(), NAV_TestForBlocked(), NAVDEBUG_showCollision, navInfo_t, NIF_BLOCKED, NIF_COLLISION, NPC, gentity_s::NPC, NPCAI_NO_COLL_AVOID, navInfo_s::pathDirection, qboolean, qfalse, qtrue, gentity_s::r, navInfo_s::trace, vec3_t, VectorCopy, and VectorMA.

Referenced by NPC_GetMoveDirection().

00903 {
00904         vec3_t  movedir;
00905         vec3_t  movepos;
00906 
00907         //Clear our block info for this frame
00908         NAV_ClearBlockedInfo( NPC );
00909 
00910         //Cap our distance
00911         if ( info->distance > MAX_COLL_AVOID_DIST )
00912         {
00913                 info->distance = MAX_COLL_AVOID_DIST;
00914         }
00915 
00916         //Get an end position
00917         VectorMA( self->r.currentOrigin, info->distance, info->direction, movepos );
00918         VectorCopy( info->direction, movedir );
00919 
00920         if ( self && self->NPC && (self->NPC->aiFlags&NPCAI_NO_COLL_AVOID) )
00921         {//pretend there's no-one in the way
00922                 return qtrue;
00923         }
00924         //Now test against entities
00925         if ( NAV_CheckAhead( self, movepos, &info->trace, CONTENTS_BODY ) == qfalse )
00926         {
00927                 //Get the blocker
00928                 info->blocker = &g_entities[ info->trace.entityNum ];
00929                 info->flags |= NIF_COLLISION;
00930 
00931                 //Ok to hit our goal entity
00932                 if ( goal == info->blocker )
00933                         return qtrue;
00934 
00935                 //See if we're moving along with them
00936                 //if ( NAV_TrueCollision( self, info.blocker, movedir, info.direction ) == qfalse )
00937                 //      return qtrue;
00938 
00939                 //Test for blocking by standing on goal
00940                 if ( NAV_TestForBlocked( self, goal, info->blocker, info->distance, &info->flags ) == qtrue )
00941                         return qfalse;
00942 
00943                 //If the above function said we're blocked, don't do the extra checks
00944                 if ( info->flags & NIF_BLOCKED )
00945                         return qtrue;
00946 
00947                 //See if we can get that entity to move out of our way
00948                 if ( NAV_ResolveEntityCollision( self, info->blocker, movedir, info->pathDirection ) == qfalse )
00949                         return qfalse;
00950 
00951                 VectorCopy( movedir, info->direction );
00952                 
00953                 return qtrue;
00954         }
00955 
00956         //Our path is clear, just move there
00957         if ( NAVDEBUG_showCollision )
00958         {
00959                 G_DrawEdge( self->r.currentOrigin, movepos, EDGE_PATH );
00960         }
00961 
00962         return qtrue;
00963 }

qboolean NAV_CheckAhead gentity_t self,
vec3_t  end,
trace_t trace,
int  clipmask
 

Definition at line 494 of file g_nav.c.

References trace_t::allsolid, gentity_s::classname, trace_t::contents, CONTENTS_BOTCLIP, entityShared_t::currentOrigin, trace_t::endpos, trace_t::entityNum, ENTITYNUM_WORLD, fabs(), trace_t::fraction, G_EntIsUnlockedDoor(), g_entities, gentity_t, entityShared_t::maxs, MIN_DOOR_BLOCK_DIST_SQR, entityShared_t::mins, entityState_s::number, qboolean, qfalse, qtrue, gentity_s::r, gentity_s::s, trace_t::startsolid, STEPSIZE, trap_Trace(), VALIDSTRING, vec3_t, and VectorSet.

00495 {
00496         vec3_t  mins;
00497         float   radius;
00498         float   dist;
00499         float   tFrac;
00500 
00501         //Offset the step height
00502         VectorSet( mins, self->r.mins[0], self->r.mins[1], self->r.mins[2] + STEPSIZE );
00503         
00504         trap_Trace( trace, self->r.currentOrigin, mins, self->r.maxs, end, self->s.number, clipmask );
00505 
00506         if ( trace->startsolid&&(trace->contents&CONTENTS_BOTCLIP) )
00507         {//started inside do not enter, so ignore them
00508                 clipmask &= ~CONTENTS_BOTCLIP;
00509                 trap_Trace( trace, self->r.currentOrigin, mins, self->r.maxs, end, self->s.number, clipmask );
00510         }
00511         //Do a simple check
00512         if ( ( trace->allsolid == qfalse ) && ( trace->startsolid == qfalse ) && ( trace->fraction == 1.0f ) )
00513                 return qtrue;
00514 
00515         //See if we're too far above
00516         if ( fabs( self->r.currentOrigin[2] - end[2] ) > 48 )
00517                 return qfalse;
00518 
00519         //This is a work around
00520         radius = ( self->r.maxs[0] > self->r.maxs[1] ) ? self->r.maxs[0] : self->r.maxs[1];
00521         dist = Distance( self->r.currentOrigin, end );
00522         tFrac = 1.0f - ( radius / dist );
00523 
00524         if ( trace->fraction >= tFrac )
00525                 return qtrue;
00526 
00527         //Do a special check for doors
00528         if ( trace->entityNum < ENTITYNUM_WORLD )
00529         {
00530                 gentity_t       *blocker = &g_entities[trace->entityNum];
00531                 
00532                 if VALIDSTRING( blocker->classname )
00533                 {
00534                         if ( G_EntIsUnlockedDoor( blocker->s.number ) )
00535                         //if ( Q_stricmp( blocker->classname, "func_door" ) == 0 )
00536                         {
00537                                 //We're too close, try and avoid the door (most likely stuck on a lip)
00538                                 if ( DistanceSquared( self->r.currentOrigin, trace->endpos ) < MIN_DOOR_BLOCK_DIST_SQR )
00539                                         return qfalse;
00540 
00541                                 return qtrue;
00542                         }
00543                 }
00544         }
00545 
00546         return qfalse;
00547 }

qboolean NAV_ClearPathToPoint gentity_t self,
vec3_t  pmins,
vec3_t  pmaxs,
vec3_t  point,
int  clipmask,
int  okToHitEnt
 

Definition at line 222 of file g_nav.c.

00223 {
00224 //      trace_t trace;
00225 //      return NAV_CheckAhead( self, point, trace, clipmask|CONTENTS_BOTCLIP );
00226 
00227         vec3_t  mins, maxs;
00228         trace_t trace;
00229 
00230         //Test if they're even conceivably close to one another
00231         if ( !trap_InPVS( self->r.currentOrigin, point ) )
00232                 return qfalse;
00233 
00234         if ( self->flags & FL_NAVGOAL )
00235         {
00236                 if ( !self->parent )
00237                 {
00238                         //SHOULD NEVER HAPPEN!!!
00239                         assert(self->parent);
00240                         return qfalse;
00241                 }
00242                 VectorCopy( self->parent->r.mins, mins );
00243                 VectorCopy( self->parent->r.maxs, maxs );
00244         }
00245         else
00246         {
00247                 VectorCopy( pmins, mins );
00248                 VectorCopy( pmaxs, maxs );
00249         }
00250         
00251         if ( self->client || ( self->flags & FL_NAVGOAL ) )
00252         {
00253                 //Clients can step up things, or if this is a navgoal check, a client will be using this info
00254                 mins[2] += STEPSIZE;
00255 
00256                 //don't let box get inverted
00257                 if ( mins[2] > maxs[2] )
00258                 {       
00259                         mins[2] = maxs[2];
00260                 }
00261         }
00262 
00263         if ( self->flags & FL_NAVGOAL )
00264         {
00265                 //Trace from point to navgoal
00266                 trap_Trace( &trace, point, mins, maxs, self->r.currentOrigin, self->parent->s.number, (clipmask|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP)&~CONTENTS_BODY );
00267                 if ( trace.startsolid&&(trace.contents&CONTENTS_BOTCLIP) )
00268                 {//started inside do not enter, so ignore them
00269                         clipmask &= ~CONTENTS_BOTCLIP;
00270                         trap_Trace( &trace, point, mins, maxs, self->r.currentOrigin, self->parent->s.number, (clipmask|CONTENTS_MONSTERCLIP)&~CONTENTS_BODY );
00271                 }
00272                 
00273                 if ( trace.startsolid || trace.allsolid )
00274                 {
00275                         return qfalse;
00276                 }
00277                 
00278                 //Made it
00279                 if ( trace.fraction == 1.0 )
00280                 {
00281                         return qtrue;
00282                 }
00283                 
00284                 if ( okToHitEntNum != ENTITYNUM_NONE && trace.entityNum == okToHitEntNum )
00285                 {
00286                         return qtrue;
00287                 }
00288 
00289                 //Okay, didn't get all the way there, let's see if we got close enough:
00290                 if ( NAV_HitNavGoal( self->r.currentOrigin, self->parent->r.mins, self->parent->r.maxs, trace.endpos, NPCInfo->goalRadius, FlyingCreature( self->parent ) ) )
00291                 {
00292                         return qtrue;
00293                 }
00294                 else
00295                 {
00296                         if ( NAVDEBUG_showCollision )
00297                         {
00298                                 if ( trace.entityNum < ENTITYNUM_WORLD && (&g_entities[trace.entityNum] != NULL) && g_entities[trace.entityNum].s.eType != ET_MOVER )
00299                                 {
00300                                         vec3_t  p1, p2;
00301                                         G_DrawEdge( point, trace.endpos, EDGE_PATH );
00302                                         VectorAdd(g_entities[trace.entityNum].r.mins, g_entities[trace.entityNum].r.currentOrigin, p1);
00303                                         VectorAdd(g_entities[trace.entityNum].r.maxs, g_entities[trace.entityNum].r.currentOrigin, p2);
00304                                         G_CubeOutline( p1, p2, FRAMETIME, 0x0000ff, 0.5 );
00305                                 }
00306                                 //FIXME: if it is a bmodel, light up the surf?
00307                         }
00308                 }
00309         }
00310         else
00311         {
00312                 trap_Trace( &trace, self->r.currentOrigin, mins, maxs, point, self->s.number, clipmask|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP);
00313                 if ( trace.startsolid&&(trace.contents&CONTENTS_BOTCLIP) )
00314                 {//started inside do not enter, so ignore them
00315                         clipmask &= ~CONTENTS_BOTCLIP;
00316                         trap_Trace( &trace, self->r.currentOrigin, mins, maxs, point, self->s.number, clipmask|CONTENTS_MONSTERCLIP);
00317                 }
00318 
00319                 if( ( ( trace.startsolid == qfalse ) && ( trace.allsolid == qfalse ) ) && ( trace.fraction == 1.0f ) )
00320                 {//FIXME: check for drops
00321                         return qtrue;
00322                 }
00323 
00324                 if ( okToHitEntNum != ENTITYNUM_NONE && trace.entityNum == okToHitEntNum )
00325                 {
00326                         return qtrue;
00327                 }
00328 
00329                 if ( NAVDEBUG_showCollision )
00330                 {
00331                         if ( trace.entityNum < ENTITYNUM_WORLD && (&g_entities[trace.entityNum] != NULL) && g_entities[trace.entityNum].s.eType != ET_MOVER )
00332                         {
00333                                 vec3_t  p1, p2;
00334                                 G_DrawEdge( self->r.currentOrigin, trace.endpos, EDGE_PATH );
00335                                 VectorAdd(g_entities[trace.entityNum].r.mins, g_entities[trace.entityNum].r.currentOrigin, p1);
00336                                 VectorAdd(g_entities[trace.entityNum].r.maxs, g_entities[trace.entityNum].r.currentOrigin, p2);
00337                                 G_CubeOutline( p1, p2, FRAMETIME, 0x0000ff, 0.5 );
00338                         }
00339                         //FIXME: if it is a bmodel, light up the surf?
00340                 }
00341         }
00342 
00343         return qfalse;
00344 }

int NAV_FindClosestWaypointForEnt gentity_t ent,
int  targWp
 

Definition at line 352 of file g_nav.c.

References gentity_t, NF_CLEAR_PATH, trap_Nav_GetNearestNode(), and gentity_s::waypoint.

Referenced by AI_SortGroupByPathCostToEnemy(), NPC_BSSearch(), NPC_BSSearchStart(), NPC_BSWander(), and ST_Commander().

00353 {
00354         //FIXME: Take the target into account
00355         return trap_Nav_GetNearestNode( ent, ent->waypoint, NF_CLEAR_PATH, targWp );
00356 }

void NAV_GetLastMove navInfo_t info  ) 
 

Definition at line 149 of file NPC_move.c.

References frameNavInfo, and navInfo_t.

Referenced by AI_CheckEnemyCollision().

00150 {
00151         *info = frameNavInfo;
00152 }

qboolean NAV_HitNavGoal vec3_t  point,
vec3_t  mins,
vec3_t  maxs,
vec3_t  dest,
int  radius,
qboolean  flying
 

Definition at line 167 of file g_nav.c.

References fabs(), G_BoundsOverlap(), NAVGOAL_USE_RADIUS, qboolean, vec3_t, VectorAdd, VectorSet, and VectorSubtract.

Referenced by NAV_ClearPathToPoint(), NAV_TestForBlocked(), NPC_BSST_Investigate(), NPC_ClearPathToGoal(), and ReachedGoal().

00168 {
00169         vec3_t  dmins, dmaxs, pmins, pmaxs;
00170 
00171         if ( radius & NAVGOAL_USE_RADIUS )
00172         {
00173                 radius &= ~NAVGOAL_USE_RADIUS;
00174                 //NOTE:  This needs to do a DistanceSquared on navgoals that had
00175                 //                      a radius manually set! We can't do the smaller navgoals against
00176                 //                      walls to get around this because player-sized traces to them
00177                 //                      from angles will not work... - MCG
00178                 if ( !flying )
00179                 {//Allow for a little z difference
00180                         vec3_t  diff;
00181                         VectorSubtract( point, dest, diff );
00182                         if ( fabs(diff[2]) <= 24 )
00183                         {
00184                                 diff[2] = 0;
00185                         }
00186                         return ( VectorLengthSquared( diff ) <= (radius*radius) );
00187                 }
00188                 else
00189                 {//must hit exactly
00190                         return ( DistanceSquared(dest, point) <= (radius*radius) );
00191                 }
00192                 //There is probably a better way to do this, either by preserving the original
00193                 //              mins and maxs of the navgoal and doing this check ONLY if the radius 
00194                 //              is non-zero (like the original implementation) or some boolean to
00195                 //              tell us to do this check rather than the fake bbox overlap check...
00196         }
00197         else
00198         {
00199                 //Construct a dummy bounding box from our radius value
00200                 VectorSet( dmins, -radius, -radius, -radius );
00201                 VectorSet( dmaxs,  radius,  radius,  radius );
00202 
00203                 //Translate it
00204                 VectorAdd( dmins, dest, dmins );
00205                 VectorAdd( dmaxs, dest, dmaxs );
00206 
00207                 //Translate the starting box
00208                 VectorAdd( point, mins, pmins );
00209                 VectorAdd( point, maxs, pmaxs );
00210 
00211                 //See if they overlap
00212                 return G_BoundsOverlap( pmins, pmaxs, dmins, dmaxs );
00213         }
00214 }

int NAV_MoveToGoal gentity_t self,
navInfo_t info
 

Definition at line 1113 of file g_nav.c.

References gentity_s::clipmask, CONTENTS_BODY, CONTENTS_BOTCLIP, entityShared_t::currentOrigin, navInfo_s::direction, navInfo_s::distance, G_DrawNode(), G_FindClosestPointOnLineSegment(), gentity_t, gNPC_t::goalEntity, gentity_s::lastWaypoint, NAV_CheckAhead(), NAV_GetNearestNode(), NAV_TestBestNode(), NAVDEBUG_showEnemyPath, navInfo_t, NODE_GOAL, NODE_NONE, NODE_START, gentity_s::NPC, NULL, entityState_s::number, navInfo_s::pathDirection, qfalse, gentity_s::r, gentity_s::s, navInfo_s::trace, trap_Nav_GetBestNode(), trap_Nav_GetNodePosition(), trap_Nav_ShowPath(), vec3_t, VectorNormalize(), VectorSubtract, gentity_s::waypoint, and WAYPOINT_NONE.

Referenced by NPC_GetMoveDirection().

01114 {
01115         int bestNode;
01116         vec3_t  origin, end;
01117 
01118         //Must have a goal entity to move there
01119         if( self->NPC->goalEntity == NULL )
01120                 return WAYPOINT_NONE;
01121 
01122         //Check special player optimizations
01123         if ( self->NPC->goalEntity->s.number == 0 )
01124         {
01125                 //If we couldn't find the point, then we won't be able to this turn
01126                 if ( self->NPC->goalEntity->waypoint == WAYPOINT_NONE )
01127                         return WAYPOINT_NONE;
01128 
01129                 //NOTENOTE: Otherwise trust this waypoint for the whole frame (reduce all unnecessary calculations)
01130         }
01131         else
01132         {
01133                 //Find the target's waypoint
01134                 if ( ( self->NPC->goalEntity->waypoint = NAV_GetNearestNode( self->NPC->goalEntity, self->NPC->goalEntity->waypoint ) ) == WAYPOINT_NONE )
01135                         return WAYPOINT_NONE;
01136         }
01137 
01138         //Find our waypoint
01139         if ( ( self->waypoint = NAV_GetNearestNode( self, self->lastWaypoint ) ) == WAYPOINT_NONE )
01140                 return WAYPOINT_NONE;
01141 
01142         bestNode = trap_Nav_GetBestNode( self->waypoint, self->NPC->goalEntity->waypoint, NODE_NONE );
01143 
01144         if ( bestNode == WAYPOINT_NONE )
01145         {
01146                 if ( NAVDEBUG_showEnemyPath )
01147                 {
01148                         vec3_t  origin, torigin;
01149 
01150                         trap_Nav_GetNodePosition( self->NPC->goalEntity->waypoint, torigin );
01151                         trap_Nav_GetNodePosition( self->waypoint, origin );
01152 
01153                         G_DrawNode( torigin, NODE_GOAL );
01154                         G_DrawNode( origin, NODE_GOAL );
01155                         G_DrawNode( self->NPC->goalEntity->r.currentOrigin, NODE_START );
01156                 }
01157                 
01158                 return WAYPOINT_NONE;
01159         }
01160 
01161         //Check this node
01162         bestNode = NAV_TestBestNode( self, bestNode, self->NPC->goalEntity->waypoint, qfalse );
01163 
01164         //trace_t       trace;
01165 
01166         //Get this position
01167         trap_Nav_GetNodePosition( bestNode, origin );
01168         trap_Nav_GetNodePosition( self->waypoint, end );
01169 
01170         //Basically, see if the path we have isn't helping
01171         //if ( NAV_MicroError( origin, end ) )
01172         //      return WAYPOINT_NONE;
01173 
01174         //Test the path connection from our current position to the best node
01175         if ( NAV_CheckAhead( self, origin, &info->trace, (self->clipmask&~CONTENTS_BODY)|CONTENTS_BOTCLIP ) == qfalse )
01176         {
01177                 //First attempt to move to the closest point on the line between the waypoints
01178                 G_FindClosestPointOnLineSegment( origin, end, self->r.currentOrigin, origin );
01179 
01180                 //See if we can go there
01181                 if ( NAV_CheckAhead( self, origin, &info->trace, (self->clipmask&~CONTENTS_BODY)|CONTENTS_BOTCLIP ) == qfalse )
01182                 {
01183                         //Just move towards our current waypoint
01184                         bestNode = self->waypoint;
01185                         trap_Nav_GetNodePosition( bestNode, origin );
01186                 }
01187         }
01188 
01189         //Setup our new move information
01190         VectorSubtract( origin, self->r.currentOrigin, info->direction );
01191         info->distance = VectorNormalize( info->direction );
01192 
01193         VectorSubtract( end, origin, info->pathDirection );
01194         VectorNormalize( info->pathDirection );
01195 
01196         //Draw any debug info, if requested
01197         if ( NAVDEBUG_showEnemyPath )
01198         {
01199                 vec3_t  dest, start;
01200 
01201                 //Get the positions
01202                 trap_Nav_GetNodePosition( self->NPC->goalEntity->waypoint, dest );
01203                 trap_Nav_GetNodePosition( bestNode, start );
01204 
01205                 //Draw the route
01206                 G_DrawNode( start, NODE_START );
01207                 G_DrawNode( dest, NODE_GOAL );
01208                 trap_Nav_ShowPath( self->waypoint, self->NPC->goalEntity->waypoint );
01209         }
01210 
01211         return bestNode;
01212 }

void NPC_AimWiggle vec3_t  enemy_org  ) 
 

Definition at line 519 of file NPC_utils.c.

References gNPC_t::aimErrorDebounceTime, gNPC_t::aimOfs, gentity_s::enemy, flrand(), level, entityShared_t::maxs, entityShared_t::mins, NPC, NPCInfo, gentity_s::r, level_locals_t::time, vec3_t, and VectorAdd.

Referenced by NPC_BSFollowLeader(), and NPC_CheckCanAttack().

00520 {
00521         //shoot for somewhere between the head and torso
00522         //NOTE: yes, I know this looks weird, but it works
00523         if ( NPCInfo->aimErrorDebounceTime < level.time )
00524         {
00525                 NPCInfo->aimOfs[0] = 0.3*flrand(NPC->enemy->r.mins[0], NPC->enemy->r.maxs[0]);
00526                 NPCInfo->aimOfs[1] = 0.3*flrand(NPC->enemy->r.mins[1], NPC->enemy->r.maxs[1]);
00527                 if ( NPC->enemy->r.maxs[2] > 0 )
00528                 {
00529                         NPCInfo->aimOfs[2] = NPC->enemy->r.maxs[2]*flrand(0.0f, -1.0f);
00530                 }
00531         }
00532         VectorAdd( enemy_org, NPCInfo->aimOfs, enemy_org );
00533 }

void NPC_ApplyWeaponFireDelay void   ) 
 

Definition at line 878 of file NPC_combat.c.

References gentity_s::attackDebounceTime, client, playerState_s::clientNum, level, NPC, gclient_s::ps, level_locals_t::time, playerState_s::weapon, playerState_s::weaponTime, WP_STUN_BATON, and WP_THERMAL.

Referenced by ShootThink().

00879 {       
00880         if ( NPC->attackDebounceTime > level.time )
00881         {//Just fired, if attacking again, must be a burst fire, so don't add delay
00882                 //NOTE: Borg AI uses attackDebounceTime "incorrectly", so this will always return for them!
00883                 return;
00884         }
00885         
00886         switch(client->ps.weapon)
00887         {
00888                 /*
00889         case WP_BOT_LASER:
00890                 NPCInfo->burstCount = 0;
00891                 client->ps.weaponTime = 500;
00892                 break;
00893                 */ //rwwFIXMEFIXME: support for this
00894 
00895         case WP_THERMAL:
00896                 if ( client->ps.clientNum )
00897                 {//NPCs delay... 
00898                         //FIXME: player should, too, but would feel weird in 1st person, even though it
00899                         //                      would look right in 3rd person.  Really should have a wind-up anim
00900                         //                      for player as he holds down the fire button to throw, then play
00901                         //                      the actual throw when he lets go...
00902                         client->ps.weaponTime = 700;
00903                 }
00904                 break;
00905 
00906         case WP_STUN_BATON:
00907                 //if ( !PM_DroidMelee( client->NPC_class ) )
00908                 if (1) //rwwFIXMEFIXME: ...
00909                 {//FIXME: should be unique per melee anim
00910                         client->ps.weaponTime = 300;
00911                 }
00912                 break;
00913 
00914         default:
00915                 client->ps.weaponTime = 0;
00916                 break;
00917         }
00918 }

int NPC_AttackDebounceForWeapon void   ) 
 

Definition at line 1288 of file NPC_combat.c.

References gNPC_t::burstSpacing, gentity_s::client, NPC, NPCInfo, gclient_s::ps, playerState_s::weapon, and WP_SABER.

Referenced by ShootThink().

01289 {
01290         switch ( NPC->client->ps.weapon ) 
01291         {
01292 /*
01293         case WP_BLASTER://scav rifle
01294                 return 1000;
01295                 break;
01296 
01297         case WP_BRYAR_PISTOL://prifle
01298                 return 3000;
01299                 break;
01300 
01301         case WP_SABER:
01302                 return 100;
01303                 break;
01304         
01305 
01306         case WP_TRICORDER:
01307                 return 0;//tricorder
01308                 break;
01309 */
01310         case WP_SABER:
01311                 return 0;
01312                 break;
01313 
01314                 /*
01315         case WP_BOT_LASER:
01316                 
01317                 if ( g_spskill.integer == 0 )
01318                         return 2000;
01319 
01320                 if ( g_spskill.integer == 1 )
01321                         return 1500;
01322 
01323                 return 1000;
01324                 break;
01325                 */
01326                 //rwwFIXMEFIXME: support
01327         default:
01328                 return NPCInfo->burstSpacing;//was 100 by default
01329                 break;
01330         }
01331 }

void NPC_BSAdvanceFight void   ) 
 

Definition at line 29 of file NPC_behavior.c.

References entityShared_t::absmin, AngleVectors(), CalcEntitySpot(), gNPC_t::captureGoal, CHECK_FOV, gentity_s::client, entityShared_t::currentOrigin, gNPC_t::desiredPitch, trace_t::endpos, gentity_s::enemy, gNPC_t::enemyLastVisibility, gclient_s::enemyTeam, enemyVisibility, trace_t::entityNum, usercmd_s::forwardmove, g_entities, gentity_t, gNPC_t::goalTime, level, MASK_SHOT, entityShared_t::maxs, NPC, gentity_s::NPC, NPC_CheckAttack(), NPC_CheckEnemy(), NPC_CheckVisibility(), NPC_EnemyTooFar(), NPC_SetMoveGoal(), NPC_UpdateShootAngles(), NPCInfo, NULL, entityState_s::number, PITCH, gclient_s::playerTeam, gclient_s::ps, qboolean, qfalse, qtrue, gentity_s::r, random, usercmd_s::rightmove, gentity_s::s, gNPC_t::shootAngles, SPOT_HEAD, SPOT_WEAPON, TID_BSTATE, level_locals_t::time, trap_ICARUS_IsInitialized(), trap_ICARUS_TaskIDComplete(), trap_Trace(), ucmd, vec3_t, vectoangles(), VectorCopy, VectorMA, VectorNormalize(), VectorSubtract, playerState_s::viewangles, VIS_FOV, VIS_SHOOT, and WeaponThink().

Referenced by NPC_BehaviorSet_Default().

00030 {//FIXME: IMPLEMENT
00031 //Head to Goal if I can
00032 
00033         //Make sure we're still headed where we want to capture
00034         if ( NPCInfo->captureGoal )
00035         {//FIXME: if no captureGoal, what do we do?
00036                 //VectorCopy( NPCInfo->captureGoal->r.currentOrigin, NPCInfo->tempGoal->r.currentOrigin );
00037                 //NPCInfo->goalEntity = NPCInfo->tempGoal;
00038 
00039                 NPC_SetMoveGoal( NPC, NPCInfo->captureGoal->r.currentOrigin, 16, qtrue, -1, NULL );
00040 
00041 //              NAV_ClearLastRoute(NPC);
00042                 NPCInfo->goalTime = level.time + 100000;
00043         }
00044 
00045 //      NPC_BSRun();
00046 
00047         NPC_CheckEnemy(qtrue, qfalse, qtrue);
00048 
00049         //FIXME: Need melee code
00050         if( NPC->enemy )
00051         {//See if we can shoot him
00052                 vec3_t          delta, forward;
00053                 vec3_t          angleToEnemy;
00054                 vec3_t          hitspot, muzzle, diff, enemy_org, enemy_head;
00055                 float           distanceToEnemy;
00056                 qboolean        attack_ok = qfalse;
00057                 qboolean        dead_on = qfalse;
00058                 float           attack_scale = 1.0;
00059                 float           aim_off;
00060                 float           max_aim_off = 64;
00061 
00062                 //Yaw to enemy
00063                 VectorMA(NPC->enemy->r.absmin, 0.5, NPC->enemy->r.maxs, enemy_org);
00064                 CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
00065                 
00066                 VectorSubtract (enemy_org, muzzle, delta);
00067                 vectoangles ( delta, angleToEnemy );
00068                 distanceToEnemy = VectorNormalize(delta);
00069 
00070                 if(!NPC_EnemyTooFar(NPC->enemy, distanceToEnemy*distanceToEnemy, qtrue))
00071                 {
00072                         attack_ok = qtrue;
00073                 }
00074 
00075                 if(attack_ok)
00076                 {
00077                         NPC_UpdateShootAngles(angleToEnemy, qfalse, qtrue);
00078 
00079                         NPCInfo->enemyLastVisibility = enemyVisibility;
00080                         enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV);//CHECK_360|//CHECK_PVS|
00081 
00082                         if(enemyVisibility == VIS_FOV)
00083                         {//He's in our FOV
00084                                 
00085                                 attack_ok = qtrue;
00086                                 CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_head);
00087 
00088                                 if(attack_ok)
00089                                 {
00090                                         trace_t         tr;
00091                                         gentity_t       *traceEnt;
00092                                         //are we gonna hit him if we shoot at his center?
00093                                         trap_Trace ( &tr, muzzle, NULL, NULL, enemy_org, NPC->s.number, MASK_SHOT );
00094                                         traceEnt = &g_entities[tr.entityNum];
00095                                         if( traceEnt != NPC->enemy &&
00096                                                 (!traceEnt || !traceEnt->client || !NPC->client->enemyTeam || NPC->client->enemyTeam != traceEnt->client->playerTeam) )
00097                                         {//no, so shoot for the head
00098                                                 attack_scale *= 0.75;
00099                                                 trap_Trace ( &tr, muzzle, NULL, NULL, enemy_head, NPC->s.number, MASK_SHOT );
00100                                                 traceEnt = &g_entities[tr.entityNum];
00101                                         }
00102 
00103                                         VectorCopy( tr.endpos, hitspot );
00104 
00105                                         if( traceEnt == NPC->enemy || (traceEnt->client && NPC->client->enemyTeam && NPC->client->enemyTeam == traceEnt->client->playerTeam) )
00106                                         {
00107                                                 dead_on = qtrue;
00108                                         }
00109                                         else
00110                                         {
00111                                                 attack_scale *= 0.5;
00112                                                 if(NPC->client->playerTeam)
00113                                                 {
00114                                                         if(traceEnt && traceEnt->client && traceEnt->client->playerTeam)
00115                                                         {
00116                                                                 if(NPC->client->playerTeam == traceEnt->client->playerTeam)
00117                                                                 {//Don't shoot our own team
00118                                                                         attack_ok = qfalse;
00119                                                                 }
00120                                                         }
00121                                                 }
00122                                         }
00123                                 }
00124 
00125                                 if( attack_ok )
00126                                 {
00127                                         //ok, now adjust pitch aim
00128                                         VectorSubtract (hitspot, muzzle, delta);
00129                                         vectoangles ( delta, angleToEnemy );
00130                                         NPC->NPC->desiredPitch = angleToEnemy[PITCH];
00131                                         NPC_UpdateShootAngles(angleToEnemy, qtrue, qfalse);
00132 
00133                                         if( !dead_on )
00134                                         {//We're not going to hit him directly, try a suppressing fire
00135                                                 //see if where we're going to shoot is too far from his origin
00136                                                 AngleVectors (NPCInfo->shootAngles, forward, NULL, NULL);
00137                                                 VectorMA ( muzzle, distanceToEnemy, forward, hitspot);
00138                                                 VectorSubtract(hitspot, enemy_org, diff);
00139                                                 aim_off = VectorLength(diff);
00140                                                 if(aim_off > random() * max_aim_off)//FIXME: use aim value to allow poor aim?
00141                                                 {
00142                                                         attack_scale *= 0.75;
00143                                                         //see if where we're going to shoot is too far from his head
00144                                                         VectorSubtract(hitspot, enemy_head, diff);
00145                                                         aim_off = VectorLength(diff);
00146                                                         if(aim_off > random() * max_aim_off)
00147                                                         {
00148                                                                 attack_ok = qfalse;
00149                                                         }
00150                                                 }
00151                                                 attack_scale *= (max_aim_off - aim_off + 1)/max_aim_off;
00152                                         }
00153                                 }
00154                         }
00155                 }
00156 
00157                 if( attack_ok )
00158                 {
00159                         if( NPC_CheckAttack( attack_scale ))
00160                         {//check aggression to decide if we should shoot
00161                                 enemyVisibility = VIS_SHOOT;
00162                                 WeaponThink(qtrue);
00163                         }
00164                         else
00165                                 attack_ok = qfalse;
00166                 }
00167 //Don't do this- only for when stationary and trying to shoot an enemy
00168 //              else
00169 //                      NPC->cantHitEnemyCounter++;
00170         }
00171         else
00172         {//FIXME: 
00173                 NPC_UpdateShootAngles(NPC->client->ps.viewangles, qtrue, qtrue);
00174         }
00175 
00176         if(!ucmd.forwardmove && !ucmd.rightmove)
00177         {//We reached our captureGoal
00178                 if(trap_ICARUS_IsInitialized(NPC->s.number))
00179                 {
00180                         trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
00181                 }
00182         }
00183 }

void NPC_BSDefault void   ) 
 

Definition at line 712 of file NPC_AI_Default.c.

References AEL_DISCOVERED, level_locals_t::alertEvents, BUTTON_WALKING, usercmd_s::buttons, gentity_s::client, gNPC_t::combatMove, entityShared_t::currentOrigin, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gentity_s::enemy, gclient_s::enemyTeam, G_SetEnemy(), gNPC_t::goalEntity, gentity_s::health, alertEvent_s::ID, gNPC_t::lastAlertID, gclient_s::leader, level, alertEvent_s::level, NPC, NPC_BSFollowLeader(), NPC_BSST_Attack(), NPC_CheckAlertEvents(), NPC_CheckEnemy(), NPC_CheckGetNewWeapon(), NPC_ClearGoal(), NPC_MoveToGoal(), NPC_SetAnim(), NPC_SomeoneLookingAtMe(), NPC_UpdateAngles(), NPCInfo, alertEvent_s::owner, PITCH, gclient_s::playerTeam, gclient_s::ps, qboolean, qfalse, qtrue, gentity_s::r, SCF_FACE_MOVE_DIR, SCF_FIRE_WEAPON, SCF_FORCED_MARCH, SCF_IGNORE_ALERTS, SCF_LOOK_FOR_ENEMIES, SCF_RUNNING, SCF_WALKING, gNPC_t::scriptFlags, SETANIM_FLAG_HOLD, SETANIM_TORSO, TID_MOVE_NAV, TORSO_SURRENDER_START, playerState_s::torsoAnim, trap_ICARUS_TaskIDPending(), ucmd, UpdateGoal(), vec3_t, vectoangles(), VectorSubtract, WeaponThink(), and YAW.

Referenced by NPC_BehaviorSet_Charmed(), NPC_BehaviorSet_Default(), and NPC_RunBehavior().

00713 {
00714 //      vec3_t          enemyDir;
00715 //      float           enemyDist;
00716 //      float           shootDist;
00717 //      qboolean        enemyFOV = qfalse;
00718 //      qboolean        enemyShotFOV = qfalse;
00719 //      qboolean        enemyPVS = qfalse;
00720 //      vec3_t          enemyHead;
00721 //      vec3_t          muzzle;
00722 //      qboolean        enemyLOS = qfalse;
00723 //      qboolean        enemyCS = qfalse;
00724         qboolean        move = qtrue;
00725 //      qboolean        shoot = qfalse;
00726 
00727         
00728         if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
00729         {
00730                 WeaponThink( qtrue );
00731         }
00732 
00733         if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
00734         {//being forced to walk
00735                 if( NPC->client->ps.torsoAnim != TORSO_SURRENDER_START )
00736                 {
00737                         NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD );
00738                 }
00739         }
00740         //look for a new enemy if don't have one and are allowed to look, validate current enemy if have one
00741         NPC_CheckEnemy( (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES), qfalse, qtrue );
00742         if ( !NPC->enemy )
00743         {//still don't have an enemy
00744                 if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
00745                 {//check for alert events
00746                         //FIXME: Check Alert events, see if we should investigate or just look at it
00747                         int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED );
00748 
00749                         //There is an event to look at
00750                         if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
00751                         {//heard/saw something
00752                                 if ( level.alertEvents[alertEvent].level >= AEL_DISCOVERED && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
00753                                 {//was a big event
00754                                         if ( level.alertEvents[alertEvent].owner && level.alertEvents[alertEvent].owner->client && level.alertEvents[alertEvent].owner->health >= 0 && level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
00755                                         {//an enemy
00756                                                 G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
00757                                         }
00758                                 }
00759                                 else
00760                                 {//FIXME: investigate lesser events
00761                                 }
00762                         }
00763                         //FIXME: also check our allies' condition?
00764                 }
00765         }
00766 
00767         if ( NPC->enemy && !(NPCInfo->scriptFlags&SCF_FORCED_MARCH) )
00768         {
00769                 // just use the stormtrooper attack AI...
00770                 NPC_CheckGetNewWeapon();
00771                 if ( NPC->client->leader 
00772                         && NPCInfo->goalEntity == NPC->client->leader 
00773                         && !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
00774                 {
00775                         NPC_ClearGoal();
00776                 }
00777                         NPC_BSST_Attack();
00778                 return;
00779 /*
00780                 //have an enemy
00781                 //FIXME: if one of these fails, meaning we can't shoot, do we really need to do the rest?
00782                 VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, enemyDir );
00783                 enemyDist = VectorNormalize( enemyDir );
00784                 enemyDist *= enemyDist;
00785                 shootDist = NPC_MaxDistSquaredForWeapon();
00786 
00787                 enemyFOV = InFOV( NPC->enemy, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov );
00788                 enemyShotFOV = InFOV( NPC->enemy, NPC, 20, 20 );
00789                 enemyPVS = gi.inPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin );
00790 
00791                 if ( enemyPVS )
00792                 {//in the pvs
00793                         trace_t tr;
00794 
00795                         CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemyHead );
00796                         enemyHead[2] -= Q_flrand( 0.0f, NPC->enemy->maxs[2]*0.5f );
00797                         CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
00798                         enemyLOS = NPC_ClearLOS( muzzle, enemyHead );
00799 
00800                         gi.trace ( &tr, muzzle, vec3_origin, vec3_origin, enemyHead, NPC->s.number, MASK_SHOT );
00801                         enemyCS = NPC_EvaluateShot( tr.entityNum, qtrue );
00802                 }
00803                 else
00804                 {//skip thr 2 traces since they would have to fail
00805                         enemyLOS = qfalse;
00806                         enemyCS = qfalse;
00807                 }
00808                 
00809                 if ( enemyCS && enemyShotFOV )
00810                 {//can hit enemy if we want
00811                         NPC->cantHitEnemyCounter = 0;
00812                 }
00813                 else
00814                 {//can't hit
00815                         NPC->cantHitEnemyCounter++;
00816                 }
00817 
00818                 if ( enemyCS && enemyShotFOV && enemyDist < shootDist )
00819                 {//can shoot
00820                         shoot = qtrue;
00821                         if ( NPCInfo->goalEntity == NPC->enemy )
00822                         {//my goal is my enemy and I have a clear shot, no need to chase right now
00823                                 move = qfalse;
00824                         }
00825                 }
00826                 else
00827                 {//don't shoot yet, keep chasing
00828                         shoot = qfalse;
00829                         move = qtrue;
00830                 }
00831 
00832                 //shoot decision
00833                 if ( !(NPCInfo->scriptFlags&SCF_DONT_FIRE) )
00834                 {//try to shoot
00835                         if ( NPC->enemy )
00836                         {
00837                                 if ( shoot )
00838                                 {
00839                                         if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
00840                                         {
00841                                                 WeaponThink( qtrue );
00842                                         }
00843                                 }
00844                         }
00845                 }
00846 
00847                 //chase decision
00848                 if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00849                 {//go after him
00850                         NPCInfo->goalEntity = NPC->enemy;
00851                         //FIXME: don't need to chase when have a clear shot and in range?
00852                         if ( !enemyCS && NPC->cantHitEnemyCounter > 60 )
00853                         {//haven't been able to shoot enemy for about 6 seconds, need to do something
00854                                 //FIXME: combat points?  Just chase?
00855                                 if ( enemyPVS )
00856                                 {//in my PVS, just pick a combat point
00857                                         //FIXME: implement
00858                                 }
00859                                 else
00860                                 {//just chase him
00861                                 }
00862                         }
00863                         //FIXME: in normal behavior, should we use combat Points?  Do we care?  Is anyone actually going to ever use this AI?
00864                 }
00865                 else if ( NPC->cantHitEnemyCounter > 60 )
00866                 {//pick a new one
00867                         NPC_CheckEnemy( qtrue, qfalse, qtrue );
00868                 }
00869                 
00870                 if ( enemyPVS && enemyLOS )//&& !enemyShotFOV )
00871                 {//have a clear LOS to him//, but not looking at him
00872                         //Find the desired angles
00873                         vec3_t  angles;
00874 
00875                         GetAnglesForDirection( muzzle, enemyHead, angles );
00876 
00877                         NPCInfo->desiredYaw             = AngleNormalize180( angles[YAW] );
00878                         NPCInfo->desiredPitch   = AngleNormalize180( angles[PITCH] );
00879                 }
00880                 */
00881         }
00882 
00883         if ( UpdateGoal() )
00884         {//have a goal
00885                 if ( !NPC->enemy 
00886                         && NPC->client->leader 
00887                         && NPCInfo->goalEntity == NPC->client->leader 
00888                         && !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
00889                 {
00890                         NPC_BSFollowLeader();
00891                 }
00892                 else
00893                 {
00894                         //set angles
00895                         if ( (NPCInfo->scriptFlags & SCF_FACE_MOVE_DIR) || NPCInfo->goalEntity != NPC->enemy )
00896                         {//face direction of movement, NOTE: default behavior when not chasing enemy
00897                                 NPCInfo->combatMove = qfalse;
00898                         }
00899                         else
00900                         {//face goal.. FIXME: what if have a navgoal but want to face enemy while moving?  Will this do that?
00901                                 vec3_t  dir, angles;
00902 
00903                                 NPCInfo->combatMove = qfalse;
00904 
00905                                 VectorSubtract( NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir );
00906                                 vectoangles( dir, angles );
00907                                 NPCInfo->desiredYaw = angles[YAW];
00908                                 if ( NPCInfo->goalEntity == NPC->enemy )
00909                                 {
00910                                         NPCInfo->desiredPitch = angles[PITCH];
00911                                 }
00912                         }
00913 
00914                         //set movement
00915                         //override default walk/run behavior
00916                         //NOTE: redundant, done in NPC_ApplyScriptFlags
00917                         if ( NPCInfo->scriptFlags & SCF_RUNNING )
00918                         {
00919                                 ucmd.buttons &= ~BUTTON_WALKING;
00920                         }
00921                         else if ( NPCInfo->scriptFlags & SCF_WALKING )
00922                         {
00923                                 ucmd.buttons |= BUTTON_WALKING;
00924                         }
00925                         else if ( NPCInfo->goalEntity == NPC->enemy )
00926                         {
00927                                 ucmd.buttons &= ~BUTTON_WALKING;
00928                         }
00929                         else
00930                         {
00931                                 ucmd.buttons |= BUTTON_WALKING;
00932                         }
00933 
00934                         if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
00935                         {//being forced to walk
00936                                 //if ( g_crosshairEntNum != NPC->s.number )
00937                                 if (!NPC_SomeoneLookingAtMe(NPC))
00938                                 {//don't walk if player isn't aiming at me
00939                                         move = qfalse;
00940                                 }
00941                         }
00942 
00943                         if ( move )
00944                         {
00945                                 //move toward goal
00946                                 NPC_MoveToGoal( qtrue );
00947                         }
00948                 }
00949         }
00950         else if ( !NPC->enemy && NPC->client->leader )
00951         {
00952                 NPC_BSFollowLeader();
00953         }
00954 
00955         //update angles
00956         NPC_UpdateAngles( qtrue, qtrue );
00957 }

void NPC_BSFlee void   ) 
 

Definition at line 1444 of file NPC_behavior.c.

References BS_DEFAULT, BS_FLEE, BUTTON_WALKING, usercmd_s::buttons, entityShared_t::currentOrigin, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gNPC_t::distToGoal, DotProduct, flrand(), usercmd_s::forwardmove, gentity_t, gNPC_t::goalEntity, gNPC_t::investigateGoal, gNPC_t::lastGoalEntity, gentity_s::lastWaypoint, NAV_GetNearestNode(), NPC, NPC_CheckGetNewWeapon(), NPC_CheckSurrender(), NPC_MoveToGoal(), NPC_SetMoveGoal(), NPC_Surrender(), NPC_UpdateAngles(), NPCInfo, NULL, qboolean, qfalse, qtrue, gentity_s::r, gentity_s::s, SQUAD_IDLE, gNPC_t::squadState, gNPC_t::tempBehavior, gNPC_t::tempGoal, TIMER_Done(), trap_Nav_GetNodeEdge(), trap_Nav_GetNodeNumEdges(), trap_Nav_GetNodePosition(), ucmd, vec3_t, VectorNormalize(), VectorSubtract, vectoyaw(), gentity_s::waypoint, WAYPOINT_NONE, entityState_s::weapon, and WP_NONE.

Referenced by NPC_BehaviorSet_Charmed(), NPC_BehaviorSet_Default(), and NPC_RunBehavior().

01445 {//FIXME: keep checking for danger
01446         gentity_t *goal;
01447 
01448         if ( TIMER_Done( NPC, "flee" ) && NPCInfo->tempBehavior == BS_FLEE )
01449         {
01450                 NPCInfo->tempBehavior = BS_DEFAULT;
01451                 NPCInfo->squadState = SQUAD_IDLE;
01452                 //FIXME: should we set some timer to make him stay in this spot for a bit, 
01453                 //so he doesn't just suddenly turn around and come back at the enemy?
01454                 //OR, just stop running toward goal for last second or so of flee?
01455         }
01456         if ( NPC_CheckSurrender() )
01457         {
01458                 return;
01459         }
01460         goal = NPCInfo->goalEntity;
01461         if ( !goal )
01462         {
01463                 goal = NPCInfo->lastGoalEntity;
01464                 if ( !goal )
01465                 {//???!!!
01466                         goal = NPCInfo->tempGoal;
01467                 }
01468         }
01469 
01470         if ( goal )
01471         {
01472                 qboolean moved;
01473                 qboolean reverseCourse = qtrue;
01474 
01475                 //FIXME: if no weapon, find one and run to pick it up?
01476 
01477                 //Let's try to find a waypoint that gets me away from this thing
01478                 if ( NPC->waypoint == WAYPOINT_NONE )
01479                 {
01480                         NPC->waypoint = NAV_GetNearestNode( NPC, NPC->lastWaypoint );
01481                 }
01482                 if ( NPC->waypoint != WAYPOINT_NONE )
01483                 {
01484                         int     numEdges = trap_Nav_GetNodeNumEdges( NPC->waypoint );
01485 
01486                         if ( numEdges != WAYPOINT_NONE )
01487                         {
01488                                 vec3_t  dangerDir;
01489                                 int             nextWp;
01490                                 int             branchNum;
01491 
01492                                 VectorSubtract( NPCInfo->investigateGoal, NPC->r.currentOrigin, dangerDir );
01493                                 VectorNormalize( dangerDir );
01494 
01495                                 for ( branchNum = 0; branchNum < numEdges; branchNum++ )
01496                                 {
01497                                         vec3_t  branchPos, runDir;
01498 
01499                                         nextWp = trap_Nav_GetNodeEdge( NPC->waypoint, branchNum );
01500                                         trap_Nav_GetNodePosition( nextWp, branchPos );
01501 
01502                                         VectorSubtract( branchPos, NPC->r.currentOrigin, runDir );
01503                                         VectorNormalize( runDir );
01504                                         if ( DotProduct( runDir, dangerDir ) > flrand( 0, 0.5 ) )
01505                                         {//don't run toward danger
01506                                                 continue;
01507                                         }
01508                                         //FIXME: don't want to ping-pong back and forth
01509                                         NPC_SetMoveGoal( NPC, branchPos, 0, qtrue, -1, NULL );
01510                                         reverseCourse = qfalse;
01511                                         break;
01512                                 }
01513                         }
01514                 }
01515 
01516                 moved = NPC_MoveToGoal( qfalse );//qtrue? (do try to move straight to (away from) goal)
01517 
01518                 if ( NPC->s.weapon == WP_NONE && (moved == qfalse || reverseCourse) )
01519                 {//No weapon and no escape route... Just cower?  Need anim.
01520                         NPC_Surrender();
01521                         NPC_UpdateAngles( qtrue, qtrue );
01522                         return;
01523                 }
01524                 //If our move failed, then just run straight away from our goal
01525                 //FIXME: We really shouldn't do this.
01526                 if ( moved == qfalse )
01527                 {
01528                         vec3_t  dir;
01529                         float   dist;
01530                         if ( reverseCourse )
01531                         {
01532                                 VectorSubtract( NPC->r.currentOrigin, goal->r.currentOrigin, dir );
01533                         }
01534                         else
01535                         {
01536                                 VectorSubtract( goal->r.currentOrigin, NPC->r.currentOrigin, dir );
01537                         }
01538                         NPCInfo->distToGoal     = dist = VectorNormalize( dir );
01539                         NPCInfo->desiredYaw = vectoyaw( dir );
01540                         NPCInfo->desiredPitch = 0;
01541                         ucmd.forwardmove = 127;
01542                 }
01543                 else if ( reverseCourse )
01544                 {
01545                         //ucmd.forwardmove *= -1;
01546                         //ucmd.rightmove *= -1;
01547                         //VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir );
01548                         NPCInfo->desiredYaw *= -1;
01549                 }
01550                 //FIXME: can stop after a safe distance?
01551                 //ucmd.upmove = 0;
01552                 ucmd.buttons &= ~BUTTON_WALKING;
01553                 //FIXME: what do we do once we've gotten to our goal?
01554         }
01555         NPC_UpdateAngles( qtrue, qtrue );
01556 
01557         NPC_CheckGetNewWeapon();
01558 }

void NPC_BSFollowLeader void   ) 
 

Definition at line 524 of file NPC_behavior.c.

References AEL_MINOR, AEL_SUSPICIOUS, level_locals_t::alertEvents, gentity_s::alliedTeam, BOTH_ATTACK1, BOTH_ATTACK2, BOTH_ATTACK3, BOTH_MELEE1, BOTH_MELEE2, BS_DEFAULT, BS_FOLLOW_LEADER, BS_HUNT_AND_KILL, BS_STAND_GUARD, BUTTON_WALKING, usercmd_s::buttons, CalcEntitySpot(), CHECK_360, CHECK_FOV, CHECK_PVS, CHECK_SHOOT, gentity_s::client, gNPC_t::confusionTime, entityShared_t::currentOrigin, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gentity_s::enemy, gNPC_t::enemyCheckDebounceTime, gNPC_t::enemyLastSeenTime, gclient_s::enemyTeam, enemyVisibility, FL_NOTARGET, gentity_s::flags, gNPC_t::followDist, usercmd_s::forwardmove, G_ClearEnemy(), G_SetEnemy(), gNPC_t::goalEntity, gentity_s::health, gNPCstats_e::hfov, alertEvent_s::ID, gNPC_t::lastAlertID, gclient_s::leader, playerState_s::legsAnim, level, alertEvent_s::level, playerState_s::moveDir, NPC, gentity_s::NPC, NPC_AimAdjust(), NPC_AimWiggle(), NPC_BSStandGuard(), NPC_CheckAlertEvents(), NPC_CheckEnemy(), NPC_CheckVisibility(), NPC_GetHFOVPercentage(), NPC_MoveDirClear(), NPC_SlideMoveToGoal(), NPC_UpdateAngles(), NPC_UpdateFiringAngles(), NPCInfo, alertEvent_s::owner, PITCH, gclient_s::playerTeam, gclient_s::ps, Q_irand(), qfalse, qtrue, gentity_s::r, usercmd_s::rightmove, SCF_IGNORE_ALERTS, SCF_LOOK_FOR_ENEMIES, gNPC_t::scriptFlags, SPOT_HEAD, SPOT_WEAPON, gNPC_t::stats, gNPC_t::tempBehavior, level_locals_t::time, TIMER_Set(), ucmd, vec3_origin, vec3_t, vectoangles(), VectorNormalize(), VectorScale, VectorSubtract, gNPCstats_e::vfov, playerState_s::viewangles, VIS_PVS, VIS_SHOOT, visibility_t, playerState_s::weapon, WeaponThink(), WP_SABER, and YAW.

Referenced by NPC_BehaviorSet_Charmed(), NPC_BehaviorSet_Default(), NPC_BSDefault(), and NPC_BSJedi_FollowLeader().

00525 {
00526         vec3_t          vec;
00527         float           leaderDist;
00528         visibility_t    leaderVis;
00529         int                     curAnim;
00530 
00531         if ( !NPC->client->leader )
00532         {//ok, stand guard until we find an enemy
00533                 if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
00534                 {
00535                         NPCInfo->tempBehavior = BS_DEFAULT;
00536                 }
00537                 else
00538                 {
00539                         NPCInfo->tempBehavior = BS_STAND_GUARD;
00540                         NPC_BSStandGuard();
00541                 }
00542                 return;
00543         }
00544 
00545         if ( !NPC->enemy  )
00546         {//no enemy, find one
00547                 NPC_CheckEnemy( NPCInfo->confusionTime<level.time, qfalse, qtrue );//don't find new enemy if this is tempbehav
00548                 if ( NPC->enemy )
00549                 {//just found one
00550                         NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
00551                 }
00552                 else
00553                 {
00554                         if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
00555                         {
00556                                 int eventID = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_MINOR );
00557                                 if ( level.alertEvents[eventID].level >= AEL_SUSPICIOUS && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
00558                                 {
00559                                         NPCInfo->lastAlertID = level.alertEvents[eventID].ID;
00560                                         if ( !level.alertEvents[eventID].owner || 
00561                                                 !level.alertEvents[eventID].owner->client || 
00562                                                 level.alertEvents[eventID].owner->health <= 0 ||
00563                                                 level.alertEvents[eventID].owner->client->playerTeam != NPC->client->enemyTeam )
00564                                         {//not an enemy
00565                                         }
00566                                         else
00567                                         {
00568                                                 //FIXME: what if can't actually see enemy, don't know where he is... should we make them just become very alert and start looking for him?  Or just let combat AI handle this... (act as if you lost him)
00569                                                 G_SetEnemy( NPC, level.alertEvents[eventID].owner );
00570                                                 NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
00571                                                 NPCInfo->enemyLastSeenTime = level.time;
00572                                                 TIMER_Set( NPC, "attackDelay", Q_irand( 500, 1000 ) );
00573                                         }
00574                                 }
00575 
00576                         }
00577                 }
00578                 if ( !NPC->enemy )
00579                 {
00580                         if ( NPC->client->leader 
00581                                 && NPC->client->leader->enemy 
00582                                 && NPC->client->leader->enemy != NPC
00583                                 && ( (NPC->client->leader->enemy->client&&NPC->client->leader->enemy->client->playerTeam==NPC->client->enemyTeam)
00584                                         ||(/*NPC->client->leader->enemy->r.svFlags&SVF_NONNPC_ENEMY*/0&&NPC->client->leader->enemy->alliedTeam==NPC->client->enemyTeam) )
00585                                 && NPC->client->leader->enemy->health > 0 )
00586                         { //rwwFIXMEFIXME: use SVF_NONNPC_ENEMY?
00587                                 G_SetEnemy( NPC, NPC->client->leader->enemy );
00588                                 NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
00589                                 NPCInfo->enemyLastSeenTime = level.time;
00590                         }
00591                 }
00592         }
00593         else 
00594         {
00595                 if ( NPC->enemy->health <= 0 || (NPC->enemy->flags&FL_NOTARGET) )
00596                 {
00597                         G_ClearEnemy( NPC );
00598                         if ( NPCInfo->enemyCheckDebounceTime > level.time + 1000 )
00599                         {
00600                                 NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 1000, 2000 );
00601                         }
00602                 }
00603                 else if ( NPC->client->ps.weapon && NPCInfo->enemyCheckDebounceTime < level.time )
00604                 {
00605                         NPC_CheckEnemy( (NPCInfo->confusionTime<level.time||NPCInfo->tempBehavior!=BS_FOLLOW_LEADER), qfalse, qtrue );//don't find new enemy if this is tempbehav
00606                 }
00607         }
00608         
00609         if ( NPC->enemy && NPC->client->ps.weapon )
00610         {//If have an enemy, face him and fire
00611                 if ( NPC->client->ps.weapon == WP_SABER )//|| NPCInfo->confusionTime>level.time )
00612                 {//lightsaber user or charmed enemy
00613                         if ( NPCInfo->tempBehavior != BS_FOLLOW_LEADER )
00614                         {//not already in a temp bState
00615                                 //go after the guy
00616                                 NPCInfo->tempBehavior = BS_HUNT_AND_KILL;
00617                                 NPC_UpdateAngles(qtrue, qtrue);
00618                                 return;
00619                         }
00620                 }
00621 
00622                 enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );//CHECK_360|CHECK_PVS|
00623                 if ( enemyVisibility > VIS_PVS )
00624                 {//face
00625                         vec3_t  enemy_org, muzzle, delta, angleToEnemy;
00626                         float   distanceToEnemy;
00627 
00628                         CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org );
00629                         NPC_AimWiggle( enemy_org );
00630 
00631                         CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
00632                         
00633                         VectorSubtract( enemy_org, muzzle, delta);
00634                         vectoangles( delta, angleToEnemy );
00635                         distanceToEnemy = VectorNormalize( delta );
00636 
00637                         NPCInfo->desiredYaw = angleToEnemy[YAW];
00638                         NPCInfo->desiredPitch = angleToEnemy[PITCH];
00639                         NPC_UpdateFiringAngles( qtrue, qtrue );
00640 
00641                         if ( enemyVisibility >= VIS_SHOOT )
00642                         {//shoot
00643                                 NPC_AimAdjust( 2 );
00644                                 if ( NPC_GetHFOVPercentage( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.hfov ) > 0.6f 
00645                                         && NPC_GetHFOVPercentage( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.vfov ) > 0.5f )
00646                                 {//actually withing our front cone
00647                                         WeaponThink( qtrue );
00648                                 }
00649                         }
00650                         else
00651                         {
00652                                 NPC_AimAdjust( 1 );
00653                         }
00654                         
00655                         //NPC_CheckCanAttack(1.0, qfalse);
00656                 }
00657                 else
00658                 {
00659                         NPC_AimAdjust( -1 );
00660                 }
00661         }
00662         else
00663         {//FIXME: combine with vector calc below
00664                 vec3_t  head, leaderHead, delta, angleToLeader;
00665 
00666                 CalcEntitySpot( NPC->client->leader, SPOT_HEAD, leaderHead );
00667                 CalcEntitySpot( NPC, SPOT_HEAD, head );
00668                 VectorSubtract (leaderHead, head, delta);
00669                 vectoangles ( delta, angleToLeader );
00670                 VectorNormalize(delta);
00671                 NPC->NPC->desiredYaw = angleToLeader[YAW];
00672                 NPC->NPC->desiredPitch = angleToLeader[PITCH];
00673                 
00674                 NPC_UpdateAngles(qtrue, qtrue);
00675         }
00676 
00677         //leader visible?
00678         leaderVis = NPC_CheckVisibility( NPC->client->leader, CHECK_PVS|CHECK_360|CHECK_SHOOT );//                      ent->e_UseFunc = useF_NULL;
00679 
00680 
00681         //Follow leader, stay within visibility and a certain distance, maintain a distance from.
00682         curAnim = NPC->client->ps.legsAnim;
00683         if ( curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 )
00684         {//Don't move toward leader if we're in a full-body attack anim
00685                 //FIXME, use IdealDistance to determine if we need to close distance
00686                 float   followDist = 96.0f;//FIXME:  If there are enmies, make this larger?
00687                 float   backupdist, walkdist, minrundist;
00688                 float   leaderHDist;
00689 
00690                 if ( NPCInfo->followDist )
00691                 {
00692                         followDist = NPCInfo->followDist;
00693                 }
00694                 backupdist = followDist/2.0f;
00695                 walkdist = followDist*0.83;
00696                 minrundist = followDist*1.33;
00697 
00698                 VectorSubtract(NPC->client->leader->r.currentOrigin, NPC->r.currentOrigin, vec);
00699                 leaderDist = VectorLength( vec );//FIXME: make this just nav distance?
00700                 //never get within their radius horizontally
00701                 vec[2] = 0;
00702                 leaderHDist = VectorLength( vec );
00703                 if( leaderHDist > backupdist && (leaderVis != VIS_SHOOT || leaderDist > walkdist) )
00704                 {//We should close in?
00705                         NPCInfo->goalEntity = NPC->client->leader;
00706 
00707                         NPC_SlideMoveToGoal();
00708                         if ( leaderVis == VIS_SHOOT && leaderDist < minrundist )
00709                         {
00710                                 ucmd.buttons |= BUTTON_WALKING;
00711                         }
00712                 }
00713                 else if ( leaderDist < backupdist )
00714                 {//We should back off?
00715                         NPCInfo->goalEntity = NPC->client->leader;
00716                         NPC_SlideMoveToGoal();
00717 
00718                         //reversing direction
00719                         ucmd.forwardmove = -ucmd.forwardmove;
00720                         ucmd.rightmove   = -ucmd.rightmove;
00721                         VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir );
00722                 }//otherwise, stay where we are
00723                 //check for do not enter and stop if there's one there...
00724                 if ( ucmd.forwardmove || ucmd.rightmove || VectorCompare( vec3_origin, NPC->client->ps.moveDir ) )
00725                 {
00726                         NPC_MoveDirClear( ucmd.forwardmove, ucmd.rightmove, qtrue );
00727                 }
00728         }
00729 }

void NPC_BSFormation void   ) 
 

void NPC_BSHuntAndKill void   ) 
 

(ucmd.buttons & BUTTON_ATTACK) ||

Definition at line 232 of file NPC_AI_Default.c.

References BOTH_ATTACK1, BOTH_ATTACK2, BOTH_ATTACK3, BOTH_MELEE1, BOTH_MELEE2, BS_DEFAULT, BS_HUNT_AND_KILL, BS_STAND_GUARD, BUTTON_WALKING, usercmd_s::buttons, CHECK_FOV, CHECK_SHOOT, gentity_s::client, entityShared_t::currentOrigin, gentity_s::enemy, enemyDist, enemyVisibility, usercmd_s::forwardmove, gNPC_t::goalEntity, gNPC_t::goalRadius, IdealDistance(), playerState_s::legsAnim, playerState_s::moveDir, NPC, NPC_BSStandGuard(), NPC_CheckCanAttack(), NPC_CheckEnemy(), NPC_CheckVisibility(), NPC_EnemyTooFar(), NPC_MaxDistSquaredForWeapon(), NPC_MoveToGoal(), NPC_UpdateAngles(), NPCInfo, gclient_s::ps, qboolean, qfalse, qtrue, gentity_s::r, usercmd_s::rightmove, gNPC_t::tempBehavior, ucmd, vec3_t, VectorScale, VectorSubtract, VIS_PVS, VIS_SHOOT, and visibility_t.

00233 {
00234         qboolean        turned = qfalse;
00235         vec3_t          vec;
00236         float           enemyDist;
00237         visibility_t    oEVis;
00238         int                     curAnim;
00239 
00240         NPC_CheckEnemy( NPCInfo->tempBehavior != BS_HUNT_AND_KILL, qfalse, qtrue );//don't find new enemy if this is tempbehav
00241 
00242         if ( NPC->enemy )
00243         {
00244                 oEVis = enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );//CHECK_360|//CHECK_PVS|
00245                 if(enemyVisibility > VIS_PVS)
00246                 {
00247                         if ( !NPC_EnemyTooFar( NPC->enemy, 0, qtrue ) )
00248                         {//Enemy is close enough to shoot - FIXME: this next func does this also, but need to know here for info on whether ot not to turn later
00249                                 NPC_CheckCanAttack( 1.0, qfalse );
00250                                 turned = qtrue;
00251                         }
00252                 }
00253 
00254                 curAnim = NPC->client->ps.legsAnim;
00255                 if(curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 )
00256                 {//Don't move toward enemy if we're in a full-body attack anim
00257                         //FIXME, use IdealDistance to determin if we need to close distance
00258                         VectorSubtract(NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, vec);
00259                         enemyDist = VectorLength(vec);
00260                         if( enemyDist > 48 && ((enemyDist*1.5)*(enemyDist*1.5) >= NPC_MaxDistSquaredForWeapon() ||
00261                                 oEVis != VIS_SHOOT ||
00263                                 enemyDist > IdealDistance(NPC)*3 ) )
00264                         {//We should close in?
00265                                 NPCInfo->goalEntity = NPC->enemy;
00266 
00267                                 NPC_MoveToGoal( qtrue );
00268                         }
00269                         else if(enemyDist < IdealDistance(NPC))
00270                         {//We should back off?
00271                                 //if(ucmd.buttons & BUTTON_ATTACK)
00272                                 {
00273                                         NPCInfo->goalEntity = NPC->enemy;
00274                                         NPCInfo->goalRadius = 12;
00275                                         NPC_MoveToGoal( qtrue );
00276 
00277                                         ucmd.forwardmove *= -1;
00278                                         ucmd.rightmove *= -1;
00279                                         VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir );
00280 
00281                                         ucmd.buttons |= BUTTON_WALKING;
00282                                 }
00283                         }//otherwise, stay where we are
00284                 }
00285         }
00286         else 
00287         {//ok, stand guard until we find an enemy
00288                 if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
00289                 {
00290                         NPCInfo->tempBehavior = BS_DEFAULT;
00291                 }
00292                 else
00293                 {
00294                         NPCInfo->tempBehavior = BS_STAND_GUARD;
00295                         NPC_BSStandGuard();
00296                 }
00297                 return;
00298         }
00299 
00300         if(!turned)
00301         {
00302                 NPC_UpdateAngles(qtrue, qtrue);
00303         }
00304 }

void NPC_BSIdle void   ) 
 

Definition at line 161 of file NPC_AI_Default.c.

References BUTTON_WALKING, usercmd_s::buttons, usercmd_s::forwardmove, NPC_MoveToGoal(), NPC_UpdateAngles(), qtrue, usercmd_s::rightmove, ucmd, UpdateGoal(), and usercmd_s::upmove.

Referenced by ATST_Idle(), ImperialProbe_Idle(), Interrogator_Idle(), Mark1_Idle(), Mark2_Idle(), Remote_Idle(), and Sentry_Idle().

00162 {
00163         //FIXME if there is no nav data, we need to do something else
00164         // if we're stuck, try to move around it
00165         if ( UpdateGoal() )
00166         {
00167                 NPC_MoveToGoal( qtrue );
00168         }
00169 
00170         if ( ( ucmd.forwardmove == 0 ) && ( ucmd.rightmove == 0 ) && ( ucmd.upmove == 0 ) )
00171         {
00172 //              NPC_StandIdle();
00173         }
00174 
00175         NPC_UpdateAngles( qtrue, qtrue );
00176         ucmd.buttons |= BUTTON_WALKING;
00177 }

void NPC_BSInvestigate void   ) 
 

Definition at line 252 of file NPC_behavior.c.

00253 {
00254 /*
00255         //FIXME: maybe allow this to be set as a tempBState in a script?  Just specify the
00256         //investigateGoal, investigateDebounceTime and investigateCount? (Needs a macro)
00257         vec3_t          invDir, invAngles, spot;
00258         gentity_t       *saveGoal;
00259         //BS_INVESTIGATE would turn toward goal, maybe take a couple steps towards it,
00260         //look for enemies, then turn away after your investigate counter was down-
00261         //investigate counter goes up every time you set it...
00262 
00263         if(level.time > NPCInfo->enemyCheckDebounceTime)
00264         {
00265                 NPCInfo->enemyCheckDebounceTime = level.time + (NPCInfo->stats.vigilance * 1000);
00266                 NPC_CheckEnemy(qtrue, qfalse);
00267                 if(NPC->enemy)
00268                 {//FIXME: do anger script
00269                         NPCInfo->goalEntity = NPC->enemy;
00270 //                      NAV_ClearLastRoute(NPC);
00271                         NPCInfo->behaviorState = BS_RUN_AND_SHOOT;
00272                         NPCInfo->tempBehavior = BS_DEFAULT;
00273                         NPC_AngerSound();
00274                         return;
00275                 }
00276         }
00277 
00278         NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL );
00279 
00280         if(NPCInfo->stats.vigilance <= 1.0 && NPCInfo->eventOwner)
00281         {
00282                 VectorCopy(NPCInfo->eventOwner->r.currentOrigin, NPCInfo->investigateGoal);
00283         }
00284 
00285         saveGoal = NPCInfo->goalEntity;
00286         if(     level.time > NPCInfo->walkDebounceTime )
00287         {
00288                 vec3_t  vec;
00289 
00290                 VectorSubtract(NPCInfo->investigateGoal, NPC->r.currentOrigin, vec);
00291                 vec[2] = 0;
00292                 if(VectorLength(vec) > 64)
00293                 {
00294                         if(Q_irand(0, 100) < NPCInfo->investigateCount)
00295                         {//take a full step
00296                                 //NPCInfo->walkDebounceTime = level.time + 1400;
00297                                 //actually finds length of my BOTH_WALK anim
00298                                 NPCInfo->walkDebounceTime = PM_AnimLength( NPC->client->clientInfo.animFileIndex, BOTH_WALK1 );
00299                         }
00300                 }
00301         }
00302 
00303         if(     level.time < NPCInfo->walkDebounceTime )
00304         {//walk toward investigateGoal
00305                 
00306                 /*
00307                 NPCInfo->goalEntity = NPCInfo->tempGoal;
00308 //              NAV_ClearLastRoute(NPC);
00309                 VectorCopy(NPCInfo->investigateGoal, NPCInfo->tempGoal->r.currentOrigin);
00310                 */
00311 
00312 /*              NPC_SetMoveGoal( NPC, NPCInfo->investigateGoal, 16, qtrue );
00313 
00314                 NPC_MoveToGoal( qtrue );
00315 
00316                 //FIXME: walk2?
00317                 NPC_SetAnim(NPC,SETANIM_LEGS,BOTH_WALK1,SETANIM_FLAG_NORMAL);
00318 
00319                 ucmd.buttons |= BUTTON_WALKING;
00320         }
00321         else
00322         {
00323 
00324                 NPC_SetAnim(NPC,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
00325 
00326                 if(NPCInfo->hlookCount > 30)
00327                 {
00328                         if(Q_irand(0, 10) > 7) 
00329                         {
00330                                 NPCInfo->hlookCount = 0;
00331                         }
00332                 }
00333                 else if(NPCInfo->hlookCount < -30)
00334                 {
00335                         if(Q_irand(0, 10) > 7) 
00336                         {
00337                                 NPCInfo->hlookCount = 0;
00338                         }
00339                 }
00340                 else if(NPCInfo->hlookCount == 0)
00341                 {
00342                         NPCInfo->hlookCount = Q_irand(-1, 1);
00343                 }
00344                 else if(Q_irand(0, 10) > 7) 
00345                 {
00346                         if(NPCInfo->hlookCount > 0)
00347                         {
00348                                 NPCInfo->hlookCount++;
00349                         }
00350                         else//lookCount < 0
00351                         {
00352                                 NPCInfo->hlookCount--;
00353                         }
00354                 }
00355 
00356                 if(NPCInfo->vlookCount >= 15)
00357                 {
00358                         if(Q_irand(0, 10) > 7) 
00359                         {
00360                                 NPCInfo->vlookCount = 0;
00361                         }
00362                 }
00363                 else if(NPCInfo->vlookCount <= -15)
00364                 {
00365                         if(Q_irand(0, 10) > 7) 
00366                         {
00367                                 NPCInfo->vlookCount = 0;
00368                         }
00369                 }
00370                 else if(NPCInfo->vlookCount == 0)
00371                 {
00372                         NPCInfo->vlookCount = Q_irand(-1, 1);
00373                 }
00374                 else if(Q_irand(0, 10) > 8) 
00375                 {
00376                         if(NPCInfo->vlookCount > 0)
00377                         {
00378                                 NPCInfo->vlookCount++;
00379                         }
00380                         else//lookCount < 0
00381                         {
00382                                 NPCInfo->vlookCount--;
00383                         }
00384                 }
00385 
00386                 //turn toward investigateGoal
00387                 CalcEntitySpot( NPC, SPOT_HEAD, spot );
00388                 VectorSubtract(NPCInfo->investigateGoal, spot, invDir);
00389                 VectorNormalize(invDir);
00390                 vectoangles(invDir, invAngles);
00391                 NPCInfo->desiredYaw = AngleNormalize360(invAngles[YAW] + NPCInfo->hlookCount);
00392                 NPCInfo->desiredPitch = AngleNormalize360(invAngles[PITCH] + NPCInfo->hlookCount);
00393         }
00394 
00395         NPC_UpdateAngles(qtrue, qtrue);
00396 
00397         NPCInfo->goalEntity = saveGoal;
00398 //      NAV_ClearLastRoute(NPC);
00399 
00400         if(level.time > NPCInfo->investigateDebounceTime)
00401         {
00402                 NPCInfo->tempBehavior = BS_DEFAULT;
00403         }
00404 
00405         NPC_CheckSoundEvents();
00406         */
00407 }

void NPC_BSJump void   ) 
 

Definition at line 733 of file NPC_behavior.c.

References gNPC_t::aiFlags, AngleDelta(), AngleNormalize360(), APEX_HEIGHT, BOTH_CROUCH1, BOTH_INAIR1, BOTH_LAND1, gentity_s::client, entityShared_t::currentOrigin, gNPC_t::desiredPitch, gNPC_t::desiredYaw, ENTITYNUM_NONE, FL_NO_KNOCKBACK, gentity_s::flags, usercmd_s::forwardmove, G_Cube(), gNPC_t::goalEntity, gNPC_t::goalTime, playerState_s::gravity, entityState_s::groundEntityNum, JS_CROUCHING, JS_FACING, JS_JUMPING, JS_LANDING, JS_WAITING, gNPC_t::jumpState, playerState_s::legsTimer, level, gNPC_t::lockedDesiredPitch, gNPC_t::lockedDesiredYaw, entityShared_t::maxs, MIN_ANGLE_ERROR, entityShared_t::mins, NPC, NPC_ClearGoal(), NPC_SetAnim(), NPC_UpdateAngles(), NPCAI_MOVING, NPCDEBUG_BLUE, NPCInfo, PITCH, gentity_s::pos1, gclient_s::ps, qtrue, gentity_s::r, gentity_s::s, SETANIM_BOTH, SETANIM_FLAG_HOLD, SETANIM_FLAG_OVERRIDE, SETANIM_LEGS, showBBoxes, sqrt(), TID_MOVE_NAV, level_locals_t::time, trap_ICARUS_TaskIDComplete(), ucmd, vec3_t, vectoangles(), VectorAdd, VectorClear, VectorCopy, VectorMA, VectorNormalize(), VectorScale, VectorSubtract, playerState_s::velocity, playerState_s::viewangles, and YAW.

Referenced by NPC_BehaviorSet_Default().

00734 {
00735         vec3_t          dir, angles, p1, p2, apex;
00736         float           time, height, forward, z, xy, dist, yawError, apexHeight;
00737 
00738         if( !NPCInfo->goalEntity )
00739         {//Should have task completed the navgoal
00740                 return;
00741         }
00742 
00743         if ( NPCInfo->jumpState != JS_JUMPING && NPCInfo->jumpState != JS_LANDING )
00744         {
00745                 //Face navgoal
00746                 VectorSubtract(NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir);
00747                 vectoangles(dir, angles);
00748                 NPCInfo->desiredPitch = NPCInfo->lockedDesiredPitch = AngleNormalize360(angles[PITCH]);
00749                 NPCInfo->desiredYaw = NPCInfo->lockedDesiredYaw = AngleNormalize360(angles[YAW]);
00750         }
00751 
00752         NPC_UpdateAngles ( qtrue, qtrue );
00753         yawError = AngleDelta ( NPC->client->ps.viewangles[YAW], NPCInfo->desiredYaw );
00754         //We don't really care about pitch here
00755 
00756         switch ( NPCInfo->jumpState )
00757         {
00758         case JS_FACING:
00759                 if ( yawError < MIN_ANGLE_ERROR )
00760                 {//Facing it, Start crouching
00761                         NPC_SetAnim(NPC, SETANIM_LEGS, BOTH_CROUCH1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
00762                         NPCInfo->jumpState = JS_CROUCHING;
00763                 }
00764                 break;
00765         case JS_CROUCHING:
00766                 if ( NPC->client->ps.legsTimer > 0 )
00767                 {//Still playing crouching anim
00768                         return;
00769                 }
00770 
00771                 //Create a parabola
00772 
00773                 if ( NPC->r.currentOrigin[2] > NPCInfo->goalEntity->r.currentOrigin[2] )
00774                 {
00775                         VectorCopy( NPC->r.currentOrigin, p1 );
00776                         VectorCopy( NPCInfo->goalEntity->r.currentOrigin, p2 );
00777                 }
00778                 else if ( NPC->r.currentOrigin[2] < NPCInfo->goalEntity->r.currentOrigin[2] )
00779                 {
00780                         VectorCopy( NPCInfo->goalEntity->r.currentOrigin, p1 );
00781                         VectorCopy( NPC->r.currentOrigin, p2 );
00782                 }
00783                 else
00784                 {
00785                         VectorCopy( NPC->r.currentOrigin, p1 );
00786                         VectorCopy( NPCInfo->goalEntity->r.currentOrigin, p2 );
00787                 }
00788 
00789                 //z = xy*xy
00790                 VectorSubtract( p2, p1, dir );
00791                 dir[2] = 0;
00792 
00793                 //Get xy and z diffs
00794                 xy = VectorNormalize( dir );
00795                 z = p1[2] - p2[2];
00796 
00797                 apexHeight = APEX_HEIGHT/2;
00798                 /*
00799                 //Determine most desirable apex height
00800                 apexHeight = (APEX_HEIGHT * PARA_WIDTH/xy) + (APEX_HEIGHT * z/128);
00801                 if ( apexHeight < APEX_HEIGHT * 0.5 )
00802                 {
00803                         apexHeight = APEX_HEIGHT*0.5;
00804                 }
00805                 else if ( apexHeight > APEX_HEIGHT * 2 )
00806                 {
00807                         apexHeight = APEX_HEIGHT*2;
00808                 }
00809                 */
00810 
00811                 //FIXME: length of xy will change curve of parabola, need to account for this
00812                 //somewhere... PARA_WIDTH
00813                 
00814                 z = (sqrt(apexHeight + z) - sqrt(apexHeight));
00815 
00816                 assert(z >= 0);
00817 
00818 //              Com_Printf("apex is %4.2f percent from p1: ", (xy-z)*0.5/xy*100.0f);
00819 
00820                 xy -= z;
00821                 xy *= 0.5;
00822                 
00823                 assert(xy > 0);
00824 
00825                 VectorMA( p1, xy, dir, apex );
00826                 apex[2] += apexHeight;
00827         
00828                 VectorCopy(apex, NPC->pos1);
00829                 
00830                 //Now we have the apex, aim for it
00831                 height = apex[2] - NPC->r.currentOrigin[2];
00832                 time = sqrt( height / ( .5 * NPC->client->ps.gravity ) );
00833                 if ( !time ) 
00834                 {
00835 //                      Com_Printf("ERROR no time in jump\n");
00836                         return;
00837                 }
00838 
00839                 // set s.origin2 to the push velocity
00840                 VectorSubtract ( apex, NPC->r.currentOrigin, NPC->client->ps.velocity );
00841                 NPC->client->ps.velocity[2] = 0;
00842                 dist = VectorNormalize( NPC->client->ps.velocity );
00843 
00844                 forward = dist / time;
00845                 VectorScale( NPC->client->ps.velocity, forward, NPC->client->ps.velocity );
00846 
00847                 NPC->client->ps.velocity[2] = time * NPC->client->ps.gravity;
00848 
00849 //              Com_Printf( "%s jumping %s, gravity at %4.0f percent\n", NPC->targetname, vtos(NPC->client->ps.velocity), NPC->client->ps.gravity/8.0f );
00850 
00851                 NPC->flags |= FL_NO_KNOCKBACK;
00852                 NPCInfo->jumpState = JS_JUMPING;
00853                 //FIXME: jumpsound?
00854                 break;
00855         case JS_JUMPING:
00856 
00857                 if ( showBBoxes )
00858                 {
00859                         VectorAdd(NPC->r.mins, NPC->pos1, p1);
00860                         VectorAdd(NPC->r.maxs, NPC->pos1, p2);
00861                         G_Cube( p1, p2, NPCDEBUG_BLUE, 0.5 );
00862                 }
00863 
00864                 if ( NPC->s.groundEntityNum != ENTITYNUM_NONE)
00865                 {//Landed, start landing anim
00866                         //FIXME: if the 
00867                         VectorClear(NPC->client->ps.velocity);
00868                         NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_LAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
00869                         NPCInfo->jumpState = JS_LANDING;
00870                         //FIXME: landsound?
00871                 }
00872                 else if ( NPC->client->ps.legsTimer > 0 )
00873                 {//Still playing jumping anim
00874                         //FIXME: apply jump velocity here, a couple frames after start, not right away
00875                         return;
00876                 }
00877                 else
00878                 {//still in air, but done with jump anim, play inair anim
00879                         NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_OVERRIDE);
00880                 }
00881                 break;
00882         case JS_LANDING:
00883                 if ( NPC->client->ps.legsTimer > 0 )
00884                 {//Still playing landing anim
00885                         return;
00886                 }
00887                 else
00888                 {
00889                         NPCInfo->jumpState = JS_WAITING;
00890 
00891                         
00892                         //task complete no matter what...  
00893                         NPC_ClearGoal();
00894                         NPCInfo->goalTime = level.time;
00895                         NPCInfo->aiFlags &= ~NPCAI_MOVING;
00896                         ucmd.forwardmove = 0;
00897                         NPC->flags &= ~FL_NO_KNOCKBACK;
00898                         //Return that the goal was reached
00899                         trap_ICARUS_TaskIDComplete( NPC, TID_MOVE_NAV );
00900                         
00901                         //Or should we keep jumping until reached goal?
00902                         
00903                         /*
00904                         NPCInfo->goalEntity = UpdateGoal();
00905                         if ( !NPCInfo->goalEntity )
00906                         {
00907                                 NPC->flags &= ~FL_NO_KNOCKBACK;
00908                                 Q3_TaskIDComplete( NPC, TID_MOVE_NAV );
00909                         }
00910                         */
00911                         
00912                 }
00913                 break;
00914         case JS_WAITING:
00915         default:
00916                 NPCInfo->jumpState = JS_FACING;
00917                 break;
00918         }
00919 }

void NPC_BSPatrol void   ) 
 

Definition at line 664 of file NPC_AI_Default.c.

References gNPC_t::behaviorState, BS_HUNT_AND_KILL, BUTTON_WALKING, usercmd_s::buttons, gentity_s::enemy, gNPC_t::enemyCheckDebounceTime, gNPC_t::investigateSoundDebounceTime, level, NPC, NPC_CheckEnemy(), NPC_MoveToGoal(), NPC_UpdateAngles(), NPCInfo, qfalse, qtrue, gNPC_t::stats, level_locals_t::time, ucmd, UpdateGoal(), and gNPCstats_e::vigilance.

Referenced by NPC_BSST_Attack().

00665 {
00666         //int   alertEventNum;
00667 
00668         if(level.time > NPCInfo->enemyCheckDebounceTime)
00669         {
00670                 NPCInfo->enemyCheckDebounceTime = level.time + (NPCInfo->stats.vigilance * 1000);
00671                 NPC_CheckEnemy(qtrue, qfalse, qtrue);
00672                 if(NPC->enemy)
00673                 {//FIXME: do anger script
00674                         NPCInfo->behaviorState = BS_HUNT_AND_KILL;
00675                         //NPC_AngerSound();
00676                         return;
00677                 }
00678         }
00679 
00680         //FIXME: Implement generic sound alerts
00681         /*
00682         alertEventNum = NPC_CheckAlertEvents( qtrue, qtrue );
00683         if( alertEventNum != -1 )
00684         {//If we heard something, see if we should check it out
00685                 if ( NPC_CheckInvestigate( alertEventNum ) )
00686                 {
00687                         return;
00688                 }
00689         }
00690         */
00691 
00692         NPCInfo->investigateSoundDebounceTime = 0;
00693         //FIXME if there is no nav data, we need to do something else
00694         // if we're stuck, try to move around it
00695         if ( UpdateGoal() )
00696         {
00697                 NPC_MoveToGoal( qtrue );
00698         }
00699 
00700         NPC_UpdateAngles( qtrue, qtrue );
00701 
00702         ucmd.buttons |= BUTTON_WALKING;
00703 }

void NPC_BSPointShoot qboolean  shoot  ) 
 

Definition at line 505 of file NPC_AI_Default.c.

References gNPC_t::aimTime, AngleDelta(), AngleNormalize360(), BUTTON_ATTACK, usercmd_s::buttons, CalcEntitySpot(), gentity_s::client, client, DEG2RAD, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gentity_s::enemy, gentity_s::health, gentity_s::inuse, gNPC_t::lockedDesiredPitch, gNPC_t::lockedDesiredYaw, entityShared_t::maxs, entityShared_t::mins, NPC, gentity_s::NPC, NPC_UpdateAngles(), NPCInfo, PITCH, gclient_s::ps, qtrue, gentity_s::r, SPOT_HEAD, SPOT_WEAPON, tan(), TID_BSTATE, trap_ICARUS_TaskIDComplete(), ucmd, vec3_t, vectoangles(), VectorSubtract, playerState_s::viewangles, playerState_s::weapon, WP_NONE, WP_SABER, WP_STUN_BATON, and YAW.

00506 {//FIXME: doesn't check for clear shot...
00507         vec3_t  muzzle, dir, angles, org;
00508 
00509         if ( !NPC->enemy || !NPC->enemy->inuse || (NPC->enemy->NPC && NPC->enemy->health <= 0) )
00510         {//FIXME: should still keep shooting for a second or two after they actually die...
00511                 trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
00512                 goto finished;
00513                 return;
00514         }
00515 
00516         CalcEntitySpot(NPC, SPOT_WEAPON, muzzle);
00517         CalcEntitySpot(NPC->enemy, SPOT_HEAD, org);//Was spot_org
00518         //Head is a little high, so let's aim for the chest:
00519         if ( NPC->enemy->client )
00520         {
00521                 org[2] -= 12;//NOTE: is this enough?
00522         }
00523 
00524         VectorSubtract(org, muzzle, dir);
00525         vectoangles(dir, angles);
00526 
00527         switch( NPC->client->ps.weapon )
00528         {
00529         case WP_NONE:
00530 //      case WP_TRICORDER:
00531         case WP_STUN_BATON:
00532         case WP_SABER:
00533                 //don't do any pitch change if not holding a firing weapon
00534                 break;
00535         default:
00536                 NPCInfo->desiredPitch = NPCInfo->lockedDesiredPitch = AngleNormalize360(angles[PITCH]);
00537                 break;
00538         }
00539 
00540         NPCInfo->desiredYaw = NPCInfo->lockedDesiredYaw = AngleNormalize360(angles[YAW]);
00541 
00542         if ( NPC_UpdateAngles ( qtrue, qtrue ) )
00543         {//FIXME: if angles clamped, this may never work!
00544                 //NPCInfo->shotTime = NPC->attackDebounceTime = 0;
00545 
00546                 if ( shoot )
00547                 {//FIXME: needs to hold this down if using a weapon that requires it, like phaser...
00548                         ucmd.buttons |= BUTTON_ATTACK;
00549                 }
00550                 
00551                 //if ( !shoot || !(NPC->svFlags & SVF_LOCKEDENEMY) )
00552                 if (1)
00553                 {//If locked_enemy is on, dont complete until it is destroyed...
00554                         trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
00555                         goto finished;
00556                 }
00557         }
00558         //else if ( shoot && (NPC->svFlags & SVF_LOCKEDENEMY) )
00559         if (0)
00560         {//shooting them till their dead, not aiming right at them yet...
00561                 /*
00562                 qboolean movingTarget = qfalse;
00563 
00564                 if ( NPC->enemy->client )
00565                 {
00566                         if ( VectorLengthSquared( NPC->enemy->client->ps.velocity ) )
00567                         {
00568                                 movingTarget = qtrue;
00569                         }
00570                 }
00571                 else if ( VectorLengthSquared( NPC->enemy->s.pos.trDelta ) )
00572                 {
00573                         movingTarget = qtrue;
00574                 }
00575 
00576                 if (movingTarget )
00577                 */
00578                 {
00579                         float   dist = VectorLength( dir );
00580                         float   yawMiss, yawMissAllow = NPC->enemy->r.maxs[0];
00581                         float   pitchMiss, pitchMissAllow = (NPC->enemy->r.maxs[2] - NPC->enemy->r.mins[2])/2;
00582                         
00583                         if ( yawMissAllow < 8.0f )
00584                         {
00585                                 yawMissAllow = 8.0f;
00586                         }
00587 
00588                         if ( pitchMissAllow < 8.0f )
00589                         {
00590                                 pitchMissAllow = 8.0f;
00591                         }
00592 
00593                         yawMiss = tan(DEG2RAD(AngleDelta ( NPC->client->ps.viewangles[YAW], NPCInfo->desiredYaw ))) * dist;
00594                         pitchMiss = tan(DEG2RAD(AngleDelta ( NPC->client->ps.viewangles[PITCH], NPCInfo->desiredPitch))) * dist;
00595 
00596                         if ( yawMissAllow >= yawMiss && pitchMissAllow > pitchMiss )
00597                         {
00598                                 ucmd.buttons |= BUTTON_ATTACK;
00599                         }
00600                 }
00601         }
00602         
00603         return;
00604                 
00605 finished:
00606         NPCInfo->desiredYaw = client->ps.viewangles[YAW];
00607         NPCInfo->desiredPitch = client->ps.viewangles[PITCH];
00608 
00609         NPCInfo->aimTime = 0;//ok to turn normally now
00610 }

void NPC_BSRemove void   ) 
 

Definition at line 921 of file NPC_behavior.c.

References entityShared_t::contents, entityShared_t::currentOrigin, EF_NODRAW, entityState_s::eFlags, ET_INVISIBLE, entityState_s::eType, FRAMETIME, g_entities, G_FreeEntity(), G_UseTargets2(), gentity_s::health, level, gentity_s::nextthink, NPC, NPC_UpdateAngles(), NULL, qtrue, gentity_s::r, gentity_s::s, gentity_s::target3, gentity_s::targetname, gentity_s::think, level_locals_t::time, and trap_InPVS().

Referenced by NPC_BehaviorSet_Charmed(), and NPC_BehaviorSet_Default().

00922 {
00923         NPC_UpdateAngles ( qtrue, qtrue );
00924         if( !trap_InPVS( NPC->r.currentOrigin, g_entities[0].r.currentOrigin ) )//FIXME: use cg.vieworg?
00925         { //rwwFIXMEFIXME: Care about all clients instead of just 0?
00926                 G_UseTargets2( NPC, NPC, NPC->target3 );
00927                 NPC->s.eFlags |= EF_NODRAW;
00928                 NPC->s.eType = ET_INVISIBLE;
00929                 NPC->r.contents = 0;
00930                 NPC->health = 0;
00931                 NPC->targetname = NULL;
00932 
00933                 //Disappear in half a second
00934                 NPC->think = G_FreeEntity;
00935                 NPC->nextthink = level.time + FRAMETIME;
00936         }//FIXME: else allow for out of FOV???
00937 }

void NPC_BSRunAndShoot void   ) 
 

Definition at line 394 of file NPC_AI_Default.c.

References gNPCstats_e::aggression, usercmd_s::angles, BS_DEFAULT, BS_HUNT_AND_KILL, BUTTON_ATTACK, usercmd_s::buttons, gentity_s::cantHitEnemyCounter, entityShared_t::currentOrigin, gNPC_t::duckDebounceTime, gentity_s::enemy, gNPC_t::goalEntity, gNPC_t::goalRadius, level, NPC, NPC_CheckCanAttack(), NPC_CheckEnemy(), NPC_LostEnemyDecideChase(), NPC_MoveToGoal(), NPC_StandTrackAndShoot(), NPC_UpdateAngles(), NPCInfo, PITCH, qfalse, qtrue, gentity_s::r, gNPC_t::stats, gNPC_t::tempBehavior, level_locals_t::time, ucmd, usercmd_s::upmove, vec3_t, VectorSubtract, and YAW.

Referenced by NPC_BSSearch().

00395 {
00396         /*if(NPC->playerTeam && NPC->enemyTeam)
00397         {
00398                 //FIXME: don't realize this right away- or else enemies show up and we're standing around
00399                 if( teamNumbers[NPC->enemyTeam] == 0 )
00400                 {//ok, stand guard until we find another enemy
00401                         //reset our rush counter
00402                         teamCounter[NPC->playerTeam] = 0;
00403                         NPCInfo->tempBehavior = BS_STAND_GUARD;
00404                         NPC_BSStandGuard();
00405                         return;
00406                 }
00407         }*/
00408 
00409         //NOTE: are we sure we want ALL run and shoot people to move this way?
00410         //Shouldn't it check to see if we have an enemy and our enemy is our goal?!
00411         //Moved that check into NPC_MoveToGoal
00412         //NPCInfo->combatMove = qtrue;
00413 
00414         NPC_CheckEnemy( qtrue, qfalse, qtrue );
00415         
00416         if ( NPCInfo->duckDebounceTime > level.time ) // && NPCInfo->hidingGoal )
00417         {
00418                 ucmd.upmove = -127;
00419                 if ( NPC->enemy )
00420                 {
00421                         NPC_CheckCanAttack( 1.0, qfalse );
00422                 }
00423                 return;         
00424         }
00425 
00426         if ( NPC->enemy )
00427         {
00428                 int monitor = NPC->cantHitEnemyCounter;
00429                 NPC_StandTrackAndShoot( NPC, qfalse );//(NPCInfo->hidingGoal != NULL) );
00430 
00431                 if ( !(ucmd.buttons & BUTTON_ATTACK) && ucmd.upmove >= 0 && NPC->cantHitEnemyCounter > monitor )
00432                 {//not crouching and not firing
00433                         vec3_t  vec;
00434 
00435                         VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, vec );
00436                         vec[2] = 0;
00437                         if ( VectorLength( vec ) > 128 || NPC->cantHitEnemyCounter >= 10 )
00438                         {//run at enemy if too far away
00439                                 //The cantHitEnemyCounter getting high has other repercussions
00440                                 //100 (10 seconds) will make you try to pick a new enemy... 
00441                                 //But we're chasing, so we clamp it at 50 here
00442                                 if ( NPC->cantHitEnemyCounter > 60 )
00443                                 {
00444                                         NPC->cantHitEnemyCounter = 60;
00445                                 }
00446                                 
00447                                 if ( NPC->cantHitEnemyCounter >= (NPCInfo->stats.aggression+1) * 10 )
00448                                 {
00449                                         NPC_LostEnemyDecideChase();
00450                                 }
00451 
00452                                 //chase and face
00453                                 ucmd.angles[YAW] = 0;
00454                                 ucmd.angles[PITCH] = 0;
00455                                 NPCInfo->goalEntity = NPC->enemy;
00456                                 NPCInfo->goalRadius = 12;
00457                                 //NAV_ClearLastRoute(NPC);
00458                                 NPC_MoveToGoal( qtrue );
00459                                 NPC_UpdateAngles(qtrue, qtrue);
00460                         }
00461                         else
00462                         {
00463                                 //FIXME: this could happen if they're just on the other side
00464                                 //of a thin wall or something else blocking out shot.  That
00465                                 //would make us just stand there and not go around it...
00466                                 //but maybe it's okay- might look like we're waiting for
00467                                 //him to come out...?  
00468                                 //Current solution: runs around if cantHitEnemyCounter gets
00469                                 //to 10 (1 second).  
00470                         }
00471                 }
00472                 else
00473                 {//Clear the can't hit enemy counter here
00474                         NPC->cantHitEnemyCounter = 0;
00475                 }
00476         }
00477         else
00478         {
00479                 if ( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
00480                 {//lost him, go back to what we were doing before
00481                         NPCInfo->tempBehavior = BS_DEFAULT;
00482                         return;
00483                 }
00484 
00485 //              NPC_BSRun();//only moves if we have a goal
00486         }
00487 }

void NPC_BSSearch void   ) 
 

Definition at line 939 of file NPC_behavior.c.

References gNPC_t::aiFlags, AngleNormalize360(), gNPC_t::behaviorState, BOTH_GUARD_IDLE1, BOTH_GUARD_LOOKAROUND1, BS_DEFAULT, BS_HUNT_AND_KILL, BS_SEARCH, BS_STAND_GUARD, BSET_LOSTENEMY, entityShared_t::currentOrigin, gNPC_t::desiredYaw, gentity_s::enemy, flrand(), G_ActivateBehavior(), gNPC_t::goalEntity, gNPC_t::homeWp, gNPC_t::investigateDebounceTime, level, NAV_FindClosestWaypointForEnt(), NPC, NPC_BSRunAndShoot(), NPC_CheckEnemy(), NPC_MoveToGoal(), NPC_SetAnim(), NPC_UpdateAngles(), NPCAI_ENROUTE_TO_HOMEWP, NPCInfo, Q_irand(), qfalse, qtrue, gentity_s::r, SETANIM_BOTH, SETANIM_FLAG_NORMAL, gNPC_t::tempBehavior, gNPC_t::tempGoal, level_locals_t::time, trap_Nav_GetNodeEdge(), trap_Nav_GetNodeNumEdges(), trap_Nav_GetNodePosition(), vec3_t, VectorSubtract, vectoyaw(), gentity_s::waypoint, and WAYPOINT_NONE.

Referenced by NPC_BehaviorSet_Charmed(), NPC_BehaviorSet_Default(), and NPC_BSWampa_Default().

00940 {
00941         NPC_CheckEnemy(qtrue, qfalse, qtrue);
00942         //Look for enemies, if find one:
00943         if ( NPC->enemy )
00944         {
00945                 if( NPCInfo->tempBehavior == BS_SEARCH )
00946                 {//if tempbehavior, set tempbehavior to default
00947                         NPCInfo->tempBehavior = BS_DEFAULT;
00948                 }
00949                 else
00950                 {//if bState, change to run and shoot
00951                         NPCInfo->behaviorState = BS_HUNT_AND_KILL;
00952                         NPC_BSRunAndShoot();
00953                 }
00954                 return;
00955         }
00956 
00957         //FIXME: what if our goalEntity is not NULL and NOT our tempGoal - they must
00958         //want us to do something else?  If tempBehavior, just default, else set
00959         //to run and shoot...?
00960 
00961         //FIXME: Reimplement
00962 
00963         if ( !NPCInfo->investigateDebounceTime )
00964         {//On our way to a tempGoal
00965                 float   minGoalReachedDistSquared = 32*32;
00966                 vec3_t  vec;
00967 
00968                 //Keep moving toward our tempGoal
00969                 NPCInfo->goalEntity = NPCInfo->tempGoal;
00970 
00971                 VectorSubtract ( NPCInfo->tempGoal->r.currentOrigin, NPC->r.currentOrigin, vec);
00972                 if ( vec[2] < 24 )
00973                 {
00974                         vec[2] = 0;
00975                 }
00976 
00977                 if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE )
00978                 {
00979                         /*
00980                         //FIXME: can't get the radius...
00981                         float   wpRadSq = waypoints[NPCInfo->tempGoal->waypoint].radius * waypoints[NPCInfo->tempGoal->waypoint].radius;
00982                         if ( minGoalReachedDistSquared > wpRadSq )
00983                         {
00984                                 minGoalReachedDistSquared = wpRadSq;
00985                         }
00986                         */
00987 
00988                         minGoalReachedDistSquared = 32*32;//12*12;
00989                 }
00990 
00991                 if ( VectorLengthSquared( vec ) < minGoalReachedDistSquared )
00992                 {
00993                         //Close enough, just got there
00994                         NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
00995 
00996                         if ( ( NPCInfo->homeWp == WAYPOINT_NONE ) || ( NPC->waypoint == WAYPOINT_NONE ) )
00997                         {
00998                                 //Heading for or at an invalid waypoint, get out of this bState
00999                                 if( NPCInfo->tempBehavior == BS_SEARCH )
01000                                 {//if tempbehavior, set tempbehavior to default
01001                                         NPCInfo->tempBehavior = BS_DEFAULT;
01002                                 }
01003                                 else
01004                                 {//if bState, change to stand guard
01005                                         NPCInfo->behaviorState = BS_STAND_GUARD;
01006                                         NPC_BSRunAndShoot();
01007                                 }
01008                                 return;
01009                         }
01010 
01011                         if ( NPC->waypoint == NPCInfo->homeWp )
01012                         {
01013                                 //Just Reached our homeWp, if this is the first time, run your lostenemyscript
01014                                 if ( NPCInfo->aiFlags & NPCAI_ENROUTE_TO_HOMEWP )
01015                                 {
01016                                         NPCInfo->aiFlags &= ~NPCAI_ENROUTE_TO_HOMEWP;
01017                                         G_ActivateBehavior( NPC, BSET_LOSTENEMY );
01018                                 }
01019 
01020                         }
01021 
01022                         //Com_Printf("Got there.\n");
01023                         //Com_Printf("Looking...");
01024                         if( !Q_irand(0, 1) )
01025                         {
01026                                 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL);
01027                         }
01028                         else
01029                         {
01030                                 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL);
01031                         }
01032                         NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000);
01033                 }
01034                 else
01035                 {
01036                         NPC_MoveToGoal( qtrue );
01037                 }
01038         }
01039         else
01040         {
01041                 //We're there
01042                 if ( NPCInfo->investigateDebounceTime > level.time )
01043                 {
01044                         //Still waiting around for a bit
01045                         //Turn angles every now and then to look around
01046                         if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE )
01047                         {
01048                                 if ( !Q_irand( 0, 30 ) )
01049                                 {
01050                                         int     numEdges = trap_Nav_GetNodeNumEdges( NPCInfo->tempGoal->waypoint );
01051 
01052                                         if ( numEdges != WAYPOINT_NONE )
01053                                         {
01054                                                 int branchNum = Q_irand( 0, numEdges - 1 );
01055 
01056                                                 vec3_t  branchPos, lookDir;
01057 
01058                                                 int nextWp = trap_Nav_GetNodeEdge( NPCInfo->tempGoal->waypoint, branchNum );
01059                                                 trap_Nav_GetNodePosition( nextWp, branchPos );
01060 
01061                                                 VectorSubtract( branchPos, NPCInfo->tempGoal->r.currentOrigin, lookDir );
01062                                                 NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + flrand( -45, 45 ) );
01063                                         }
01064 
01065                                         //pick an angle +-45 degrees off of the dir of a random branch
01066                                         //from NPCInfo->tempGoal->waypoint
01067                                         //int branch = Q_irand( 0, (waypoints[NPCInfo->tempGoal->waypoint].numNeighbors - 1) );
01068                                         //int   nextWp = waypoints[NPCInfo->tempGoal->waypoint].nextWaypoint[branch][NPC->client->moveType];
01069                                         //vec3_t        lookDir;
01070 
01071                                         //VectorSubtract( waypoints[nextWp].origin, NPCInfo->tempGoal->r.currentOrigin, lookDir );
01072                                         //Look in that direction +- 45 degrees
01073                                         //NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + Q_flrand( -45, 45 ) );
01074                                 }
01075                         }
01076                         //Com_Printf(".");
01077                 }
01078                 else
01079                 {//Just finished waiting
01080                         NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
01081                         
01082                         if ( NPC->waypoint == NPCInfo->homeWp )
01083                         {
01084                                 int     numEdges = trap_Nav_GetNodeNumEdges( NPCInfo->tempGoal->waypoint );
01085 
01086                                 if ( numEdges != WAYPOINT_NONE )
01087                                 {
01088                                         int branchNum = Q_irand( 0, numEdges - 1 );
01089 
01090                                         int nextWp = trap_Nav_GetNodeEdge( NPCInfo->homeWp, branchNum );
01091                                         trap_Nav_GetNodePosition( nextWp, NPCInfo->tempGoal->r.currentOrigin );
01092                                         NPCInfo->tempGoal->waypoint = nextWp;
01093                                 }
01094 
01095                                 /*
01096                                 //Pick a random branch
01097                                 int branch = Q_irand( 0, (waypoints[NPCInfo->homeWp].numNeighbors - 1) );
01098                                 int     nextWp = waypoints[NPCInfo->homeWp].nextWaypoint[branch][NPC->client->moveType];
01099 
01100                                 VectorCopy( waypoints[nextWp].origin, NPCInfo->tempGoal->r.currentOrigin );
01101                                 NPCInfo->tempGoal->waypoint = nextWp;
01102                                 //Com_Printf("\nHeading for wp %d...\n", waypoints[NPCInfo->homeWp].nextWaypoint[branch][NPC->client->moveType]);
01103                                 */
01104                         }
01105                         else
01106                         {//At a branch, so return home
01107                                 trap_Nav_GetNodePosition( NPCInfo->homeWp, NPCInfo->tempGoal->r.currentOrigin );
01108                                 NPCInfo->tempGoal->waypoint = NPCInfo->homeWp;
01109                                 /*
01110                                 VectorCopy( waypoints[NPCInfo->homeWp].origin, NPCInfo->tempGoal->r.currentOrigin );
01111                                 NPCInfo->tempGoal->waypoint = NPCInfo->homeWp;
01112                                 //Com_Printf("\nHeading for wp %d...\n", NPCInfo->homeWp);
01113                                 */
01114                         }
01115 
01116                         NPCInfo->investigateDebounceTime = 0;
01117                         //Start moving toward our tempGoal
01118                         NPCInfo->goalEntity = NPCInfo->tempGoal;
01119                         NPC_MoveToGoal( qtrue );
01120                 }
01121         }
01122 
01123         NPC_UpdateAngles( qtrue, qtrue );
01124 }

void NPC_BSSearchStart int  homeWp,
bState_t  bState
 

Definition at line 1132 of file NPC_behavior.c.

References gNPC_t::aiFlags, entityShared_t::currentOrigin, gNPC_t::homeWp, gNPC_t::investigateDebounceTime, NAV_FindClosestWaypointForEnt(), NPC, NPCAI_ENROUTE_TO_HOMEWP, NPCInfo, gentity_s::r, gNPC_t::tempBehavior, gNPC_t::tempGoal, trap_Nav_GetNodePosition(), gentity_s::waypoint, and WAYPOINT_NONE.

Referenced by NPC_BSWampa_Default(), NPC_LostEnemyDecideChase(), and ST_Commander().

01133 {
01134         //FIXME: Reimplement
01135         if ( homeWp == WAYPOINT_NONE )
01136         {
01137                 homeWp = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
01138                 if( NPC->waypoint == WAYPOINT_NONE )
01139                 {
01140                         NPC->waypoint = homeWp;
01141                 }
01142         }
01143         NPCInfo->homeWp = homeWp;
01144         NPCInfo->tempBehavior = bState;
01145         NPCInfo->aiFlags |= NPCAI_ENROUTE_TO_HOMEWP;
01146         NPCInfo->investigateDebounceTime = 0;
01147         trap_Nav_GetNodePosition( homeWp, NPCInfo->tempGoal->r.currentOrigin );
01148         NPCInfo->tempGoal->waypoint = homeWp;
01149         //Com_Printf("\nHeading for wp %d...\n", NPCInfo->homeWp);
01150 }

void NPC_BSSleep void   ) 
 

Definition at line 500 of file NPC_behavior.c.

References AEL_MINOR, BSET_AWAKE, G_ActivateBehavior(), NPC, NPC_CheckAlertEvents(), qfalse, and qtrue.

Referenced by NPC_BehaviorSet_Default().

00501 {
00502         int alertEvent = NPC_CheckAlertEvents( qtrue, qfalse, -1, qfalse, AEL_MINOR );
00503 
00504         //There is an event to look at
00505         if ( alertEvent >= 0 )
00506         {
00507                 G_ActivateBehavior(NPC, BSET_AWAKE);
00508                 return;
00509         }
00510 
00511         /*
00512         if ( level.time > NPCInfo->enemyCheckDebounceTime )
00513         {
00514                 if ( NPC_CheckSoundEvents() != -1 )
00515                 {//only 1 alert per second per 0.1 of vigilance
00516                         NPCInfo->enemyCheckDebounceTime = level.time + (NPCInfo->stats.vigilance * 10000);
00517                         G_ActivateBehavior(NPC, BSET_AWAKE);
00518                 }
00519         }
00520         */
00521 }

void NPC_BSStandAndShoot void   ) 
 

Definition at line 306 of file NPC_AI_Default.c.

References gentity_s::client, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gNPC_t::duckDebounceTime, gentity_s::enemy, gclient_s::enemyTeam, level, NPC, NPC_CheckCanAttack(), NPC_CheckEnemy(), NPC_StandTrackAndShoot(), NPC_UpdateAngles(), NPCInfo, PITCH, gclient_s::playerTeam, gclient_s::ps, qfalse, qtrue, level_locals_t::time, ucmd, usercmd_s::upmove, playerState_s::viewangles, playerState_s::weapon, WP_SABER, and YAW.

00307 {
00308         //FIXME:
00309         //When our numbers outnumber enemies 3 to 1, or only one of them,
00310         //go into hunt and kill mode
00311 
00312         //FIXME:
00313         //When they're all dead, go to some script or wander off to sickbay?
00314         
00315         if(NPC->client->playerTeam && NPC->client->enemyTeam)
00316         {
00317                 //FIXME: don't realize this right away- or else enemies show up and we're standing around
00318                 /*
00319                 if( teamNumbers[NPC->enemyTeam] == 0 )
00320                 {//ok, stand guard until we find another enemy
00321                         //reset our rush counter
00322                         teamCounter[NPC->playerTeam] = 0;
00323                         NPCInfo->tempBehavior = BS_STAND_GUARD;
00324                         NPC_BSStandGuard();
00325                         return;
00326                 }*/
00327                 /*
00328                 //FIXME: whether to do this or not should be settable
00329                 else if( NPC->playerTeam != TEAM_BORG )//Borg don't rush
00330                 {
00331                 //FIXME: In case reinforcements show up, we should wait a few seconds
00332                 //and keep checking before rushing!
00333                 //Also: what if not everyone on our team is going after playerTeam?
00334                 //Also: our team count includes medics!
00335                         if(NPC->health > 25)
00336                         {//Can we rush the enemy?
00337                                 if(teamNumbers[NPC->enemyTeam] == 1 ||
00338                                         teamNumbers[NPC->playerTeam] >= teamNumbers[NPC->enemyTeam]*3)
00339                                 {//Only one of them or we outnumber 3 to 1
00340                                         if(teamStrength[NPC->playerTeam] >= 75 ||
00341                                                 (teamStrength[NPC->playerTeam] >= 50 && teamStrength[NPC->playerTeam] > teamStrength[NPC->enemyTeam]))
00342                                         {//Our team is strong enough to rush
00343                                                 teamCounter[NPC->playerTeam]++;
00344                                                 if(teamNumbers[NPC->playerTeam] * 17 <= teamCounter[NPC->playerTeam])
00345                                                 {//ok, we waited 1.7 think cycles on average and everyone is go, let's do it!
00346                                                         //FIXME: Should we do this to everyone on our team?
00347                                                         NPCInfo->behaviorState = BS_HUNT_AND_KILL;
00348                                                         //FIXME: if the tide changes, we should retreat!
00349                                                         //FIXME: when do we reset the counter?
00350                                                         NPC_BSHuntAndKill ();
00351                                                         return;
00352                                                 }
00353                                         }
00354                                         else//Oops!  Something's wrong, reset the counter to rush
00355                                                 teamCounter[NPC->playerTeam] = 0;
00356                                 }
00357                                 else//Oops!  Something's wrong, reset the counter to rush
00358                                         teamCounter[NPC->playerTeam] = 0;
00359                         }
00360                 }
00361                 */
00362         }
00363 
00364         NPC_CheckEnemy(qtrue, qfalse, qtrue);
00365         
00366         if(NPCInfo->duckDebounceTime > level.time && NPC->client->ps.weapon != WP_SABER )
00367         {
00368                 ucmd.upmove = -127;
00369                 if(NPC->enemy)
00370                 {
00371                         NPC_CheckCanAttack(1.0, qtrue);
00372                 }
00373                 return;         
00374         }
00375 
00376         if(NPC->enemy)
00377         {
00378                 if(!NPC_StandTrackAndShoot( NPC, qtrue ))
00379                 {//That func didn't update our angles
00380                         NPCInfo->desiredYaw = NPC->client->ps.viewangles[YAW];
00381                         NPCInfo->desiredPitch = NPC->client->ps.viewangles[PITCH];
00382                         NPC_UpdateAngles(qtrue, qtrue);
00383                 }
00384         }
00385         else
00386         {
00387                 NPCInfo->desiredYaw = NPC->client->ps.viewangles[YAW];
00388                 NPCInfo->desiredPitch = NPC->client->ps.viewangles[PITCH];
00389                 NPC_UpdateAngles(qtrue, qtrue);
00390 //              NPC_BSIdle();//only moves if we have a goal
00391         }
00392 }

void NPC_BSStandGuard void   ) 
 

Definition at line 191 of file NPC_AI_Default.c.

References gNPC_t::behaviorState, BS_DEFAULT, BS_STAND_AND_SHOOT, BS_STAND_GUARD, gentity_s::cantHitEnemyCounter, gentity_s::client, gentity_s::enemy, gclient_s::enemyTeam, G_SetEnemy(), gentity_t, NPC, NPC_PickEnemy(), NPC_UpdateAngles(), NPCInfo, NPCTEAM_PLAYER, NULL, qtrue, random, and gNPC_t::tempBehavior.

Referenced by NPC_BSFollowLeader(), and NPC_BSHuntAndKill().

00192 {
00193         //FIXME: Use Snapshot info
00194         if ( NPC->enemy == NULL )
00195         {//Possible to pick one up by being shot
00196                 if( random() < 0.5 )
00197                 {
00198                         if(NPC->client->enemyTeam)
00199                         {
00200                                 gentity_t *newenemy = NPC_PickEnemy(NPC, NPC->client->enemyTeam, (NPC->cantHitEnemyCounter < 10), (NPC->client->enemyTeam == NPCTEAM_PLAYER), qtrue);
00201                                 //only checks for vis if couldn't hit last enemy
00202                                 if(newenemy)
00203                                 {
00204                                         G_SetEnemy( NPC, newenemy );
00205                                 }
00206                         }
00207                 }
00208         }
00209 
00210         if ( NPC->enemy != NULL )
00211         {
00212                 if( NPCInfo->tempBehavior == BS_STAND_GUARD )
00213                 {
00214                         NPCInfo->tempBehavior = BS_DEFAULT;
00215                 }
00216                 
00217                 if( NPCInfo->behaviorState == BS_STAND_GUARD )
00218                 {
00219                         NPCInfo->behaviorState = BS_STAND_AND_SHOOT;
00220                 }
00221         }
00222 
00223         NPC_UpdateAngles( qtrue, qtrue );
00224 }

void NPC_BSWait void   ) 
 

Definition at line 246 of file NPC_behavior.c.

References NPC_UpdateAngles(), and qtrue.

Referenced by NPC_BehaviorSet_Default().

00247 {
00248         NPC_UpdateAngles( qtrue, qtrue );
00249 }

void NPC_BSWander void   ) 
 

Definition at line 1193 of file NPC_behavior.c.

References AngleNormalize360(), BOTH_GUARD_IDLE1, BOTH_GUARD_LOOKAROUND1, entityShared_t::currentOrigin, gNPC_t::desiredYaw, flrand(), gNPC_t::goalEntity, gNPC_t::investigateDebounceTime, level, NAV_FindClosestWaypointForEnt(), NPC, NPC_MoveToGoal(), NPC_SetAnim(), NPC_UpdateAngles(), NPCInfo, Q_irand(), qtrue, gentity_s::r, SETANIM_BOTH, SETANIM_FLAG_NORMAL, gNPC_t::tempGoal, level_locals_t::time, trap_Nav_GetNodeEdge(), trap_Nav_GetNodeNumEdges(), trap_Nav_GetNodePosition(), vec3_t, VectorSubtract, vectoyaw(), gentity_s::waypoint, and WAYPOINT_NONE.

Referenced by NPC_BehaviorSet_Charmed(), NPC_BehaviorSet_Default(), and NPC_BSWampa_Default().

01194 {//FIXME: don't actually go all the way to the next waypoint, just move in fits and jerks...?
01195         if ( !NPCInfo->investigateDebounceTime )
01196         {//Starting out
01197                 float   minGoalReachedDistSquared = 64;//32*32;
01198                 vec3_t  vec;
01199 
01200                 //Keep moving toward our tempGoal
01201                 NPCInfo->goalEntity = NPCInfo->tempGoal;
01202 
01203                 VectorSubtract ( NPCInfo->tempGoal->r.currentOrigin, NPC->r.currentOrigin, vec);
01204 
01205                 if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE )
01206                 {
01207                         minGoalReachedDistSquared = 64;
01208                 }
01209 
01210                 if ( VectorLengthSquared( vec ) < minGoalReachedDistSquared )
01211                 {
01212                         //Close enough, just got there
01213                         NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
01214 
01215                         if( !Q_irand(0, 1) )
01216                         {
01217                                 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL);
01218                         }
01219                         else
01220                         {
01221                                 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL);
01222                         }
01223                         //Just got here, so Look around for a while
01224                         NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000);
01225                 }
01226                 else
01227                 {
01228                         //Keep moving toward goal
01229                         NPC_MoveToGoal( qtrue );
01230                 }
01231         }
01232         else
01233         {
01234                 //We're there
01235                 if ( NPCInfo->investigateDebounceTime > level.time )
01236                 {
01237                         //Still waiting around for a bit
01238                         //Turn angles every now and then to look around
01239                         if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE )
01240                         {
01241                                 if ( !Q_irand( 0, 30 ) )
01242                                 {
01243                                         int     numEdges = trap_Nav_GetNodeNumEdges( NPCInfo->tempGoal->waypoint );
01244 
01245                                         if ( numEdges != WAYPOINT_NONE )
01246                                         {
01247                                                 int branchNum = Q_irand( 0, numEdges - 1 );
01248 
01249                                                 vec3_t  branchPos, lookDir;
01250 
01251                                                 int     nextWp = trap_Nav_GetNodeEdge( NPCInfo->tempGoal->waypoint, branchNum );
01252                                                 trap_Nav_GetNodePosition( nextWp, branchPos );
01253 
01254                                                 VectorSubtract( branchPos, NPCInfo->tempGoal->r.currentOrigin, lookDir );
01255                                                 NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + flrand( -45, 45 ) );
01256                                         }
01257                                 }
01258                         }
01259                 }
01260                 else
01261                 {//Just finished waiting
01262                         NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
01263                         
01264                         if ( NPC->waypoint != WAYPOINT_NONE )
01265                         {
01266                                 int     numEdges = trap_Nav_GetNodeNumEdges( NPC->waypoint );
01267 
01268                                 if ( numEdges != WAYPOINT_NONE )
01269                                 {
01270                                         int branchNum = Q_irand( 0, numEdges - 1 );
01271 
01272                                         int nextWp = trap_Nav_GetNodeEdge( NPC->waypoint, branchNum );
01273                                         trap_Nav_GetNodePosition( nextWp, NPCInfo->tempGoal->r.currentOrigin );
01274                                         NPCInfo->tempGoal->waypoint = nextWp;
01275                                 }
01276 
01277                                 NPCInfo->investigateDebounceTime = 0;
01278                                 //Start moving toward our tempGoal
01279                                 NPCInfo->goalEntity = NPCInfo->tempGoal;
01280                                 NPC_MoveToGoal( qtrue );
01281                         }
01282                 }
01283         }
01284 
01285         NPC_UpdateAngles( qtrue, qtrue );
01286 }

void NPC_CalcClosestFormationSpot gentity_t self  ) 
 

void NPC_ChangeWeapon int  newWeapon  ) 
 

Definition at line 844 of file NPC_combat.c.

Referenced by ATST_Attack(), Boba_ChangeWeapon(), Boba_FireDecide(), NPC_Begin(), NPC_BSGM_Attack(), NPC_BSGrenadier_Attack(), NPC_BSSniper_Attack(), and NPC_BSST_Attack().

00845 {
00846         /*
00847         qboolean        changing = qfalse;
00848         if ( newWeapon != NPC->client->ps.weapon )
00849         {
00850                 changing = qtrue;
00851         }
00852         if ( changing && NPC->weaponModel[0] > 9 )
00853         {
00854                 trap_G2API_RemoveGhoul2Model( NPC->ghoul2, NPC->weaponModel[0] );
00855         }
00856         ChangeWeapon( NPC, newWeapon );
00857         if ( changing && NPC->client->ps.weapon != WP_NONE )
00858         {
00859                 if ( NPC->client->ps.weapon == WP_SABER )
00860                 {
00861                         G_CreateG2AttachedWeaponModel( NPC, NPC->client->ps.saber[0].model, NPC->handRBolt, 0 );
00862                         if ( NPC->client->ps.dualSabers )
00863                         {
00864                                 G_CreateG2AttachedWeaponModel( NPC, NPC->client->ps.saber[1].model, NPC->handLBolt, 0 );
00865                         }
00866                 }
00867                 else
00868                 {
00869                         G_CreateG2AttachedWeaponModel( NPC, weaponData[NPC->client->ps.weapon].weaponMdl, NPC->handRBolt, 0 );
00870                 }
00871         }*/
00872         //rwwFIXMEFIXME: Change the same way as players, all this stuff is just crazy.
00873 }

int NPC_CheckAlertEvents qboolean  checkSight,
qboolean  checkSound,
int  ignoreAlert,
qboolean  mustHaveOwner,
int  minAlertLevel
 

Definition at line 532 of file NPC_senses.c.

References gNPCstats_e::earshot, G_CheckAlertEvents(), NPC, NPCInfo, gNPC_t::stats, and gNPCstats_e::visrange.

Referenced by Jedi_CheckDanger(), NPC_BSDefault(), NPC_BSFollowLeader(), NPC_BSGrenadier_Attack(), NPC_BSGrenadier_Patrol(), NPC_BSSleep(), NPC_BSSniper_Attack(), NPC_BSSniper_Patrol(), NPC_BSST_Attack(), NPC_BSST_Investigate(), NPC_BSST_Patrol(), NPC_BSST_Sleep(), NPC_PickEnemyExt(), and ST_Commander().

00533 {
00534         return G_CheckAlertEvents( NPC, checkSight, checkSound, NPCInfo->stats.visrange, NPCInfo->stats.earshot, ignoreAlert, mustHaveOwner, minAlertLevel );
00535 }

qboolean NPC_CheckAttack float  scale  ) 
 

Definition at line 2228 of file NPC_combat.c.

References gNPCstats_e::aggression, flrand(), level, NPCInfo, qboolean, qfalse, qtrue, gNPC_t::shotTime, gNPC_t::stats, and level_locals_t::time.

Referenced by NPC_BSAdvanceFight(), and NPC_CheckCanAttack().

02229 {
02230         if(!scale)
02231                 scale = 1.0;
02232 
02233         if(((float)NPCInfo->stats.aggression) * scale < flrand(0, 4))
02234         {
02235                 return qfalse;
02236         }
02237 
02238         if(NPCInfo->shotTime > level.time)
02239                 return qfalse;
02240 
02241         return qtrue;
02242 }

qboolean NPC_CheckCanAttack float  attack_scale,
qboolean  stationary
 

Definition at line 2263 of file NPC_combat.c.

References gNPCstats_e::aim, AngleVectors(), BUTTON_ATTACK, gclient_s::buttons, CalcEntitySpot(), CHECK_360, CHECK_FOV, client, gentity_s::client, entityShared_t::currentOrigin, gNPC_t::desiredPitch, gNPC_t::desiredYaw, trace_t::endpos, gentity_s::enemy, gNPC_t::enemyLastVisibility, gclient_s::enemyTeam, enemyVisibility, EntIsGlass(), trace_t::entityNum, FL_NOTARGET, gentity_s::flags, g_entities, gentity_t, gentity_s::health, MASK_SHOT, NPC, gentity_s::NPC, NPC_AimWiggle(), NPC_CheckAttack(), NPC_CheckDefend(), NPC_CheckVisibility(), NPC_EnemyTooFar(), NPC_UpdateFiringAngles(), NPCInfo, NULL, entityState_s::number, PITCH, gclient_s::playerTeam, gclient_s::ps, qboolean, qfalse, qtrue, gentity_s::r, random, gentity_s::s, SCF_DONT_FIRE, gNPC_t::scriptFlags, ShotThroughGlass(), gentity_s::splashRadius, SPOT_HEAD, SPOT_WEAPON, gNPC_t::stats, trap_Trace(), ucmd, usercmd_s::upmove, vec3_t, vectoangles(), VectorCopy, VectorMA, VectorNormalize(), VectorSubtract, playerState_s::viewangles, VIS_FOV, VIS_SHOOT, WeaponThink(), playerState_s::weaponTime, and YAW.

Referenced by NPC_BSHuntAndKill(), NPC_BSMove(), NPC_BSRunAndShoot(), NPC_BSStandAndShoot(), and NPC_StandTrackAndShoot().

02264 {
02265         vec3_t          delta, forward;
02266         vec3_t          angleToEnemy;
02267         vec3_t          hitspot, muzzle, diff, enemy_org;//, enemy_head;
02268         float           distanceToEnemy;
02269         qboolean        attack_ok = qfalse;
02270 //      qboolean        duck_ok = qfalse;
02271         qboolean        dead_on = qfalse;
02272         float           aim_off;
02273         float           max_aim_off = 128 - (16 * (float)NPCInfo->stats.aim);
02274         trace_t         tr;
02275         gentity_t       *traceEnt = NULL;
02276 
02277         if(NPC->enemy->flags & FL_NOTARGET)
02278         {
02279                 return qfalse;
02280         }
02281 
02282         //FIXME: only check to see if should duck if that provides cover from the
02283         //enemy!!!
02284         if(!attack_scale)
02285         {
02286                 attack_scale = 1.0;
02287         }
02288         //Yaw to enemy
02289         CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org );
02290         NPC_AimWiggle( enemy_org );
02291 
02292         CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
02293         
02294         VectorSubtract (enemy_org, muzzle, delta);
02295         vectoangles ( delta, angleToEnemy );
02296         distanceToEnemy = VectorNormalize(delta);
02297 
02298         NPC->NPC->desiredYaw = angleToEnemy[YAW];
02299         NPC_UpdateFiringAngles(qfalse, qtrue);
02300 
02301         if( NPC_EnemyTooFar(NPC->enemy, distanceToEnemy*distanceToEnemy, qtrue) )
02302         {//Too far away?  Do not attack
02303                 return qfalse;
02304         }
02305 
02306         if(client->ps.weaponTime > 0)
02307         {//already waiting for a shot to fire
02308                 NPC->NPC->desiredPitch = angleToEnemy[PITCH];
02309                 NPC_UpdateFiringAngles(qtrue, qfalse);
02310                 return qfalse;
02311         }
02312 
02313         if(NPCInfo->scriptFlags & SCF_DONT_FIRE)
02314         {
02315                 return qfalse;
02316         }
02317 
02318         NPCInfo->enemyLastVisibility = enemyVisibility;
02319         //See if they're in our FOV and we have a clear shot to them
02320         enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_360|CHECK_FOV);
02321 
02322         if(enemyVisibility >= VIS_FOV)
02323         {//He's in our FOV
02324                 
02325                 attack_ok = qtrue;
02326                 //CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_head);
02327 
02328                 //Check to duck
02329                 if ( NPC->enemy->client )
02330                 {
02331                         if ( NPC->enemy->enemy == NPC )
02332                         {
02333                                 if ( NPC->enemy->client->buttons & BUTTON_ATTACK )
02334                                 {//FIXME: determine if enemy fire angles would hit me or get close
02335                                         if ( NPC_CheckDefend( 1.0 ) )//FIXME: Check self-preservation?  Health?
02336                                         {//duck and don't shoot
02337                                                 attack_ok = qfalse;
02338                                                 ucmd.upmove = -127;
02339                                         }
02340                                 }
02341                         }
02342                 }
02343 
02344                 if(attack_ok)
02345                 {
02346                         //are we gonna hit him
02347                         //NEW: use actual forward facing
02348                         AngleVectors( client->ps.viewangles, forward, NULL, NULL );
02349                         VectorMA( muzzle, distanceToEnemy, forward, hitspot );
02350                         trap_Trace( &tr, muzzle, NULL, NULL, hitspot, NPC->s.number, MASK_SHOT );
02351                         ShotThroughGlass( &tr, NPC->enemy, hitspot, MASK_SHOT );
02352                         /*
02353                         //OLD: trace regardless of facing
02354                         trap_Trace ( &tr, muzzle, NULL, NULL, enemy_org, NPC->s.number, MASK_SHOT );
02355                         ShotThroughGlass(&tr, NPC->enemy, enemy_org, MASK_SHOT);
02356                         */
02357 
02358                         traceEnt = &g_entities[tr.entityNum];
02359 
02360                         /*
02361                         if( traceEnt != NPC->enemy &&//FIXME: if someone on our team is in the way, suggest that they duck if possible
02362                                 (!traceEnt || !traceEnt->client || !NPC->client->enemyTeam || NPC->client->enemyTeam != traceEnt->client->playerTeam) )
02363                         {//no, so shoot for somewhere between the head and torso
02364                                 //NOTE: yes, I know this looks weird, but it works
02365                                 enemy_org[0] += 0.3*Q_flrand(NPC->enemy->r.mins[0], NPC->enemy->r.maxs[0]);
02366                                 enemy_org[1] += 0.3*Q_flrand(NPC->enemy->r.mins[1], NPC->enemy->r.maxs[1]);
02367                                 enemy_org[2] -= NPC->enemy->r.maxs[2]*Q_flrand(0.0f, 1.0f);
02368 
02369                                 attack_scale *= 0.75;
02370                                 trap_Trace ( &tr, muzzle, NULL, NULL, enemy_org, NPC->s.number, MASK_SHOT );
02371                                 ShotThroughGlass(&tr, NPC->enemy, enemy_org, MASK_SHOT);
02372                                 traceEnt = &g_entities[tr.entityNum];
02373                         }
02374                         */
02375 
02376                         VectorCopy( tr.endpos, hitspot );
02377 
02378                         if( traceEnt == NPC->enemy || (traceEnt->client && NPC->client->enemyTeam && NPC->client->enemyTeam == traceEnt->client->playerTeam) )
02379                         {
02380                                 dead_on = qtrue;
02381                         }
02382                         else
02383                         {
02384                                 attack_scale *= 0.5;
02385                                 if(NPC->client->playerTeam)
02386                                 {
02387                                         if(traceEnt && traceEnt->client && traceEnt->client->playerTeam)
02388                                         {
02389                                                 if(NPC->client->playerTeam == traceEnt->client->playerTeam)
02390                                                 {//Don't shoot our own team
02391                                                         attack_ok = qfalse;
02392                                                 }
02393                                         }
02394                                 }
02395                         }
02396                 }
02397 
02398                 if( attack_ok )
02399                 {
02400                         //ok, now adjust pitch aim
02401                         VectorSubtract (hitspot, muzzle, delta);
02402                         vectoangles ( delta, angleToEnemy );
02403                         NPC->NPC->desiredPitch = angleToEnemy[PITCH];
02404                         NPC_UpdateFiringAngles(qtrue, qfalse);
02405 
02406                         if( !dead_on )
02407                         {//We're not going to hit him directly, try a suppressing fire
02408                                 //see if where we're going to shoot is too far from his origin
02409                                 if(traceEnt && (traceEnt->health <= 30 || EntIsGlass(traceEnt)))
02410                                 {//easy to kill - go for it
02411                                         //if(traceEnt->die == ExplodeDeath_Wait && traceEnt->splashDamage)
02412                                         if (0) //rwwFIXMEFIXME: ExplodeDeath_Wait?
02413                                         {//going to explode, don't shoot if close to self
02414                                                 VectorSubtract(NPC->r.currentOrigin, traceEnt->r.currentOrigin, diff);
02415                                                 if(VectorLengthSquared(diff) < traceEnt->splashRadius*traceEnt->splashRadius)
02416                                                 {//Too close to shoot!
02417                                                         attack_ok = qfalse;
02418                                                 }
02419                                                 else 
02420                                                 {//Hey, it might kill him, do it!
02421                                                         attack_scale *= 2;//
02422                                                 }
02423                                         }
02424                                 }
02425                                 else
02426                                 {
02427                                         AngleVectors (client->ps.viewangles, forward, NULL, NULL);
02428                                         VectorMA ( muzzle, distanceToEnemy, forward, hitspot);
02429                                         VectorSubtract(hitspot, enemy_org, diff);
02430                                         aim_off = VectorLength(diff);
02431                                         if(aim_off > random() * max_aim_off)//FIXME: use aim value to allow poor aim?
02432                                         {
02433                                                 attack_scale *= 0.75;
02434                                                 //see if where we're going to shoot is too far from his head
02435                                                 VectorSubtract(hitspot, enemy_org, diff);
02436                                                 aim_off = VectorLength(diff);
02437                                                 if(aim_off > random() * max_aim_off)
02438                                                 {
02439                                                         attack_ok = qfalse;
02440                                                 }
02441                                         }
02442                                         attack_scale *= (max_aim_off - aim_off + 1)/max_aim_off;
02443                                 }
02444                         }
02445                 }
02446         }
02447         else
02448         {//Update pitch anyway
02449                 NPC->NPC->desiredPitch = angleToEnemy[PITCH];
02450                 NPC_UpdateFiringAngles(qtrue, qfalse);
02451         }
02452 
02453         if( attack_ok )
02454         {
02455                 if( NPC_CheckAttack( attack_scale ))
02456                 {//check aggression to decide if we should shoot
02457                         enemyVisibility = VIS_SHOOT;
02458                         WeaponThink(qtrue);
02459                 }
02460                 else
02461                         attack_ok = qfalse;
02462         }
02463 
02464         return attack_ok;
02465 }

qboolean NPC_CheckCanAttackExt void   ) 
 

Definition at line 1588 of file NPC_utils.c.

References gentity_s::enemy, NPC, NPC_ClearShot(), NPC_FaceEnemy(), NPCInfo, qboolean, qfalse, qtrue, SCF_DONT_FIRE, and gNPC_t::scriptFlags.

01589 {
01590         //We don't want them to shoot
01591         if( NPCInfo->scriptFlags & SCF_DONT_FIRE )
01592                 return qfalse;
01593 
01594         //Turn to face
01595         if ( NPC_FaceEnemy( qtrue ) == qfalse )
01596                 return qfalse;
01597 
01598         //Must have a clear line of sight to the target
01599         if ( NPC_ClearShot( NPC->enemy ) == qfalse )
01600                 return qfalse;
01601 
01602         return qtrue;
01603 }

qboolean NPC_CheckDefend float  scale  ) 
 

Definition at line 2250 of file NPC_combat.c.

References gNPCstats_e::evasion, NPCInfo, qboolean, qfalse, qtrue, random, and gNPC_t::stats.

Referenced by NPC_CheckCanAttack(), and NPC_StandTrackAndShoot().

02251 {
02252         if(!scale)
02253                 scale = 1.0;
02254 
02255         if((float)(NPCInfo->stats.evasion) > random() * 4 * scale)
02256                 return qtrue;
02257 
02258         return qfalse;
02259 }

gentity_t* NPC_CheckEnemy qboolean  findNew,
qboolean  tooFarOk,
qboolean  setEnemy
 

Definition at line 1895 of file NPC_combat.c.

References gentity_s::cantHitEnemyCounter, gentity_s::client, entityShared_t::currentOrigin, gNPC_t::defendEnt, gentity_s::enemy, gclient_s::enemyTeam, FL_NOTARGET, gentity_s::flags, G_ClearEnemy(), G_SetEnemy(), gentity_t, gentity_s::health, gclient_s::hiddenDist, gentity_s::inuse, gentity_s::lastEnemy, NPC, NPC_EnemyTooFar(), NPC_LostEnemyDecideChase(), NPC_PickEnemy(), NPCInfo, NPCTEAM_NEUTRAL, NULL, gclient_s::playerTeam, qboolean, qfalse, qtrue, gentity_s::r, and trap_InPVS().

Referenced by NPC_BSAdvanceFight(), NPC_BSDefault(), NPC_BSFollowLeader(), NPC_BSHuntAndKill(), NPC_BSJedi_Default(), NPC_BSMove(), NPC_BSPatrol(), NPC_BSRancor_Default(), NPC_BSRunAndShoot(), NPC_BSSearch(), NPC_BSStandAndShoot(), and NPC_BSWampa_Default().

01896 {
01897         qboolean        forcefindNew = qfalse;
01898         gentity_t       *closestTo;
01899         gentity_t       *newEnemy = NULL;
01900         //FIXME: have a "NPCInfo->persistance" we can set to determine how long to try to shoot
01901         //someone we can't hit?  Rather than hard-coded 10?
01902 
01903         //FIXME they shouldn't recognize enemy's death instantly
01904 
01905         //TEMP FIX:
01906         //if(NPC->enemy->client)
01907         //{
01908         //      NPC->enemy->health = NPC->enemy->client->ps.stats[STAT_HEALTH];
01909         //}
01910 
01911         if ( NPC->enemy )
01912         {
01913                 if ( !NPC->enemy->inuse )//|| NPC->enemy == NPC )//wtf?  NPCs should never get mad at themselves!
01914                 {
01915                         if ( setEnemy )
01916                         {
01917                                 G_ClearEnemy( NPC );
01918                         }
01919                 }
01920         }
01921 
01922         //if ( NPC->svFlags & SVF_IGNORE_ENEMIES )
01923         if (0) //rwwFIXMEFIXME: support for this flag
01924         {//We're ignoring all enemies for now
01925                 if ( setEnemy )
01926                 {
01927                         G_ClearEnemy( NPC );
01928                 }
01929                 return NULL;
01930         }
01931 
01932         //rwwFIXMEFIXME: support for this flag
01933         /*
01934         if ( NPC->svFlags & SVF_LOCKEDENEMY )
01935         {//keep this enemy until dead
01936                 if ( NPC->enemy )
01937                 {
01938                         if ( (!NPC->NPC && !(NPC->svFlags & SVF_NONNPC_ENEMY) ) || NPC->enemy->health > 0 )
01939                         {//Enemy never had health (a train or info_not_null, etc) or did and is now dead (NPCs, turrets, etc)
01940                                 return NULL;
01941                         }
01942                 }
01943                 NPC->svFlags &= ~SVF_LOCKEDENEMY;
01944         }
01945         */
01946 
01947         if ( NPC->enemy )
01948         {
01949                 if ( NPC_EnemyTooFar(NPC->enemy, 0, qfalse) )
01950                 {
01951                         if(findNew)
01952                         {//See if there is a close one and take it if so, else keep this one
01953                                 forcefindNew = qtrue;
01954                         }
01955                         else if(!tooFarOk)//FIXME: don't need this extra bool any more
01956                         {
01957                                 if ( setEnemy )
01958                                 {
01959                                         G_ClearEnemy( NPC );
01960                                 }
01961                         }
01962                 }
01963                 else if ( !trap_InPVS(NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ) )
01964                 {//FIXME: should this be a line-of site check?
01965                         //FIXME: a lot of things check PVS AGAIN when deciding whether 
01966                         //or not to shoot, redundant!
01967                         //Should we lose the enemy?
01968                         //FIXME: if lose enemy, run lostenemyscript
01969                         if ( NPC->enemy->client && NPC->enemy->client->hiddenDist )
01970                         {//He ducked into shadow while we weren't looking
01971                                 //Drop enemy and see if we should search for him
01972                                 NPC_LostEnemyDecideChase();
01973                         }
01974                         else
01975                         {//If we're not chasing him, we need to lose him
01976                                 //NOTE: since we no longer have bStates, really, this logic doesn't work, so never give him up
01977 
01978                                 /*
01979                                 switch( NPCInfo->behaviorState )
01980                                 {
01981                                 case BS_HUNT_AND_KILL:
01982                                         //Okay to lose PVS, we're chasing them
01983                                         break;
01984                                 case BS_RUN_AND_SHOOT:
01985                                 //FIXME: only do this if !(NPCInfo->scriptFlags&SCF_CHASE_ENEMY)
01986                                         //If he's not our goalEntity, we're running somewhere else, so lose him
01987                                         if ( NPC->enemy != NPCInfo->goalEntity )
01988                                         {
01989                                                 G_ClearEnemy( NPC );
01990                                         }
01991                                         break;
01992                                 default:
01993                                         //We're not chasing him, so lose him as an enemy
01994                                         G_ClearEnemy( NPC );
01995                                         break;
01996                                 }
01997                                 */
01998                         }
01999                 }
02000         }
02001 
02002         if ( NPC->enemy )
02003         {
02004                 if ( NPC->enemy->health <= 0 || NPC->enemy->flags & FL_NOTARGET )
02005                 {
02006                         if ( setEnemy )
02007                         {
02008                                 G_ClearEnemy( NPC );
02009                         }
02010                 }
02011         }
02012 
02013         closestTo = NPC;
02014         //FIXME: check your defendEnt, if you have one, see if their enemy is different 
02015         //than yours, or, if they don't have one, pick the closest enemy to THEM?
02016         if ( NPCInfo->defendEnt )
02017         {//Trying to protect someone
02018                 if ( NPCInfo->defendEnt->health > 0 )
02019                 {//Still alive, We presume we're close to them, navigation should handle this?
02020                         if ( NPCInfo->defendEnt->enemy )
02021                         {//They were shot or acquired an enemy
02022                                 if ( NPC->enemy != NPCInfo->defendEnt->enemy )
02023                                 {//They have a different enemy, take it!
02024                                         newEnemy = NPCInfo->defendEnt->enemy;
02025                                         if ( setEnemy )
02026                                         {
02027                                                 G_SetEnemy( NPC, NPCInfo->defendEnt->enemy );
02028                                         }
02029                                 }
02030                         }
02031                         else if ( NPC->enemy == NULL )
02032                         {//We don't have an enemy, so find closest to defendEnt
02033                                 closestTo = NPCInfo->defendEnt;
02034                         }
02035                 }
02036         }
02037 
02038         if (!NPC->enemy || ( NPC->enemy && NPC->enemy->health <= 0 ) || forcefindNew )
02039         {//FIXME: NPCs that are moving after an enemy should ignore the can't hit enemy counter- that should only be for NPCs that are standing still
02040                 //NOTE: cantHitEnemyCounter >= 100 means we couldn't hit enemy for a full
02041                 //      10 seconds, so give up.  This means even if we're chasing him, we would
02042                 //      try to find another enemy after 10 seconds (assuming the cantHitEnemyCounter
02043                 //      is allowed to increment in a chasing bState)
02044                 qboolean        foundenemy = qfalse;
02045 
02046                 if(!findNew)
02047                 {
02048                         if ( setEnemy )
02049                         {
02050                                 NPC->lastEnemy = NPC->enemy;
02051                                 G_ClearEnemy(NPC);
02052                         }
02053                         return NULL;
02054                 }
02055 
02056                 //If enemy dead or unshootable, look for others on out enemy's team
02057                 if ( NPC->client->enemyTeam != NPCTEAM_NEUTRAL )
02058                 {
02059                         //NOTE:  this only checks vis if can't hit enemy for 10 tries, which I suppose
02060                         //                      means they need to find one that in more than just PVS
02061                         //newenemy = NPC_PickEnemy( closestTo, NPC->client->enemyTeam, (NPC->cantHitEnemyCounter > 10), qfalse, qtrue );//3rd parm was (NPC->enemyTeam == TEAM_STARFLEET)
02062                         //For now, made it so you ALWAYS have to check VIS
02063                         newEnemy = NPC_PickEnemy( closestTo, NPC->client->enemyTeam, qtrue, qfalse, qtrue );//3rd parm was (NPC->enemyTeam == TEAM_STARFLEET)
02064                         if ( newEnemy )
02065                         {
02066                                 foundenemy = qtrue;
02067                                 if ( setEnemy )
02068                                 {
02069                                         G_SetEnemy( NPC, newEnemy );
02070                                 }
02071                         }
02072                 }
02073                 
02074                 if ( !forcefindNew )
02075                 {
02076                         if ( !foundenemy )
02077                         {
02078                                 if ( setEnemy )
02079                                 {
02080                                         NPC->lastEnemy = NPC->enemy;
02081                                         G_ClearEnemy(NPC);
02082                                 }
02083                         }
02084                         
02085                         NPC->cantHitEnemyCounter = 0;
02086                 }
02087                 //FIXME: if we can't find any at all, go into INdependant NPC AI, pursue and kill
02088         }
02089 
02090         if ( NPC->enemy && NPC->enemy->client ) 
02091         {
02092                 if(NPC->enemy->client->playerTeam)
02093                 {
02094 //                      assert( NPC->client->playerTeam != NPC->enemy->client->playerTeam);
02095                         if( NPC->client->playerTeam != NPC->enemy->client->playerTeam )
02096                         {
02097                                 NPC->client->enemyTeam = NPC->enemy->client->playerTeam;
02098                         }
02099                 }
02100         }
02101         return newEnemy;
02102 }

qboolean NPC_CheckEnemyExt qboolean  checkAlerts  ) 
 

Definition at line 1469 of file NPC_utils.c.

References NPC_FindEnemy(), and qboolean.

Referenced by ATST_Attack(), Howler_Patrol(), ImperialProbe_AttackDecision(), Interrogator_Attack(), Mark1_AttackDecision(), MineMonster_Patrol(), NPC_BSEmplaced(), NPC_BSGM_Attack(), NPC_BSGrenadier_Attack(), NPC_BSRancor_Default(), NPC_BSSniper_Attack(), NPC_BSST_Attack(), NPC_BSWampa_Default(), Rancor_Patrol(), Remote_Attack(), Sentry_AttackDecision(), and Wampa_Patrol().

01470 {
01471         //Make sure we're ready to think again
01472 /*
01473         if ( NPCInfo->enemyCheckDebounceTime > level.time )
01474                 return qfalse;
01475 
01476         //Get our next think time
01477         NPCInfo->enemyCheckDebounceTime = level.time + NPC_GetCheckDelta();
01478 
01479         //Attempt to find an enemy
01480         return NPC_FindEnemy();
01481 */
01482         return NPC_FindEnemy( checkAlerts );
01483 }

qboolean NPC_CheckForDanger int  alertEvent  ) 
 

Definition at line 568 of file NPC_senses.c.

References G_CheckForDanger(), NPC, and qboolean.

Referenced by NPC_BSGrenadier_Attack(), NPC_BSGrenadier_Patrol(), NPC_BSSniper_Attack(), NPC_BSSniper_Patrol(), NPC_BSST_Attack(), NPC_BSST_Investigate(), and ST_Commander().

00569 {//FIXME: more bStates need to call this?
00570         return G_CheckForDanger( NPC, alertEvent );
00571 }

qboolean NPC_CheckInvestigate int  alertEventNum  ) 
 

Definition at line 409 of file NPC_behavior.c.

References level_locals_t::alertEvents, gNPC_t::behaviorState, BS_HUNT_AND_KILL, BS_INVESTIGATE, BSET_AWAKE, gentity_s::client, entityShared_t::currentOrigin, gNPCstats_e::earshot, EF_NODRAW, gentity_s::enemy, ET_NPC, ET_PLAYER, entityState_s::eType, gNPC_t::eventOwner, FL_NOTARGET, G_ActivateBehavior(), G_SetEnemy(), gentity_t, gNPC_t::goalEntity, gNPC_t::goalRadius, gNPC_t::investigateCount, gNPC_t::investigateDebounceTime, gNPC_t::investigateGoal, level, alertEvent_s::level, NPC, NPCInfo, alertEvent_s::owner, gclient_s::playerTeam, alertEvent_s::position, qboolean, qfalse, qtrue, gentity_s::r, alertEvent_s::radius, gentity_s::s, gNPC_t::stats, gNPC_t::tempBehavior, level_locals_t::time, trap_InPVS(), ValidEnemy(), vec3_t, VectorCopy, and gNPCstats_e::vigilance.

00410 {
00411         gentity_t       *owner = level.alertEvents[alertEventNum].owner;
00412         int             invAdd = level.alertEvents[alertEventNum].level;
00413         vec3_t  soundPos;
00414         float   soundRad = level.alertEvents[alertEventNum].radius;
00415         float   earshot = NPCInfo->stats.earshot;
00416 
00417         VectorCopy( level.alertEvents[alertEventNum].position, soundPos );
00418 
00419         //NOTE: Trying to preserve previous investigation behavior
00420         if ( !owner )
00421         {
00422                 return qfalse;
00423         }
00424 
00425         if ( owner->s.eType != ET_PLAYER && owner->s.eType != ET_NPC && owner == NPCInfo->goalEntity ) 
00426         {
00427                 return qfalse;
00428         }
00429 
00430         if ( owner->s.eFlags & EF_NODRAW ) 
00431         {
00432                 return qfalse;
00433         }
00434 
00435         if ( owner->flags & FL_NOTARGET ) 
00436         {
00437                 return qfalse;
00438         }
00439 
00440         if ( soundRad < earshot )
00441         {
00442                 return qfalse;
00443         }
00444 
00445         //if(!trap_InPVSIgnorePortals(ent->r.currentOrigin, NPC->r.currentOrigin))//should we be able to hear through areaportals?
00446         if ( !trap_InPVS( soundPos, NPC->r.currentOrigin ) )
00447         {//can hear through doors?
00448                 return qfalse;
00449         }
00450 
00451         if ( owner->client && owner->client->playerTeam && NPC->client->playerTeam && owner->client->playerTeam != NPC->client->playerTeam )
00452         {
00453                 if( (float)NPCInfo->investigateCount >= (NPCInfo->stats.vigilance*200) && owner )
00454                 {//If investigateCount == 10, just take it as enemy and go
00455                         if ( ValidEnemy( owner ) )
00456                         {//FIXME: run angerscript
00457                                 G_SetEnemy( NPC, owner );
00458                                 NPCInfo->goalEntity = NPC->enemy;
00459                                 NPCInfo->goalRadius = 12;
00460                                 NPCInfo->behaviorState = BS_HUNT_AND_KILL;
00461                                 return qtrue;
00462                         }
00463                 }
00464                 else
00465                 {
00466                         NPCInfo->investigateCount += invAdd;
00467                 }
00468                 //run awakescript
00469                 G_ActivateBehavior(NPC, BSET_AWAKE);
00470 
00471                 /*
00472                 if ( Q_irand(0, 10) > 7 )
00473                 {
00474                         NPC_AngerSound();
00475                 }
00476                 */
00477 
00478                 //NPCInfo->hlookCount = NPCInfo->vlookCount = 0;
00479                 NPCInfo->eventOwner = owner;
00480                 VectorCopy( soundPos, NPCInfo->investigateGoal );
00481                 if ( NPCInfo->investigateCount > 20 )
00482                 {
00483                         NPCInfo->investigateDebounceTime = level.time + 10000;
00484                 }
00485                 else
00486                 {
00487                         NPCInfo->investigateDebounceTime = level.time + (NPCInfo->investigateCount*500);
00488                 }
00489                 NPCInfo->tempBehavior = BS_INVESTIGATE;
00490                 return qtrue;
00491         }
00492 
00493         return qfalse;
00494 }

void NPC_CheckPossibleEnemy gentity_t other,
visibility_t  vis
 

Definition at line 1229 of file NPC_combat.c.

References CHECK_360, CHECK_FOV, entityShared_t::currentOrigin, gentity_s::enemy, gNPC_t::enemyLastHeardLocation, gNPC_t::enemyLastHeardTime, gNPC_t::enemyLastSeenLocation, gNPC_t::enemyLastSeenTime, enemyVisibility, FL_NOTARGET, gentity_s::flags, G_SetEnemy(), gentity_t, level, NPC, NPC_CheckVisibility(), NPCInfo, gentity_s::r, level_locals_t::time, VectorClear, VectorCopy, VIS_FOV, and VIS_UNKNOWN.

01230 {
01231         // is he is already our enemy?
01232         if ( other == NPC->enemy ) 
01233                 return;
01234 
01235         if ( other->flags & FL_NOTARGET ) 
01236                 return;
01237 
01238         // we already have an enemy and this guy is in our FOV, see if this guy would be better
01239         if ( NPC->enemy && vis == VIS_FOV ) 
01240         {
01241                 if ( NPCInfo->enemyLastSeenTime - level.time < 2000 ) 
01242                 {
01243                         return;
01244                 }
01245                 if ( enemyVisibility == VIS_UNKNOWN ) 
01246                 {
01247                         enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_360|CHECK_FOV );
01248                 }
01249                 if ( enemyVisibility == VIS_FOV ) 
01250                 {
01251                         return;
01252                 }
01253         }
01254 
01255         if ( !NPC->enemy )
01256         {//only take an enemy if you don't have one yet
01257                 G_SetEnemy( NPC, other );
01258         }
01259 
01260         if ( vis == VIS_FOV ) 
01261         {
01262                 NPCInfo->enemyLastSeenTime = level.time;
01263                 VectorCopy( other->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
01264                 NPCInfo->enemyLastHeardTime = 0;
01265                 VectorClear( NPCInfo->enemyLastHeardLocation );
01266         } 
01267         else 
01268         {
01269                 NPCInfo->enemyLastSeenTime = 0;
01270                 VectorClear( NPCInfo->enemyLastSeenLocation );
01271                 NPCInfo->enemyLastHeardTime = level.time;
01272                 VectorCopy( other->r.currentOrigin, NPCInfo->enemyLastHeardLocation );
01273         }
01274 }

visibility_t NPC_CheckVisibility gentity_t ent,
int  flags
 

Definition at line 257 of file NPC_senses.c.

References CanSee(), CanShoot(), CHECK_360, CHECK_FOV, CHECK_PVS, CHECK_SHOOT, CHECK_VISRANGE, entityShared_t::currentOrigin, gentity_t, gNPCstats_e::hfov, InFOV(), InVisrange(), NPC, NPCInfo, gentity_s::r, gNPC_t::stats, trap_InPVS(), gNPCstats_e::vfov, VIS_360, VIS_FOV, VIS_NOT, VIS_PVS, VIS_SHOOT, and visibility_t.

Referenced by NPC_BSAdvanceFight(), NPC_BSFollowLeader(), NPC_BSHuntAndKill(), NPC_CheckCanAttack(), NPC_CheckPossibleEnemy(), NPC_PickAlly(), and NPC_PickEnemy().

00258 {
00259         // flags should never be 0
00260         if ( !flags ) 
00261         {
00262                 return VIS_NOT;
00263         }
00264 
00265         // check PVS
00266         if ( flags & CHECK_PVS ) 
00267         {
00268                 if ( !trap_InPVS ( ent->r.currentOrigin, NPC->r.currentOrigin ) ) 
00269                 {
00270                         return VIS_NOT;
00271                 }
00272         }
00273         if ( !(flags & (CHECK_360|CHECK_FOV|CHECK_SHOOT)) ) 
00274         {
00275                 return VIS_PVS;
00276         }
00277 
00278         // check within visrange
00279         if (flags & CHECK_VISRANGE)
00280         {
00281                 if( !InVisrange ( ent ) ) 
00282                 {
00283                         return VIS_PVS;
00284                 }
00285         }
00286 
00287         // check 360 degree visibility
00288         //Meaning has to be a direct line of site
00289         if ( flags & CHECK_360 ) 
00290         {
00291                 if ( !CanSee ( ent ) ) 
00292                 {
00293                         return VIS_PVS;
00294                 }
00295         }
00296         if ( !(flags & (CHECK_FOV|CHECK_SHOOT)) ) 
00297         {
00298                 return VIS_360;
00299         }
00300 
00301         // check FOV
00302         if ( flags & CHECK_FOV ) 
00303         {
00304                 if ( !InFOV ( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov) ) 
00305                 {
00306                         return VIS_360;
00307                 }
00308         }
00309 
00310         if ( !(flags & CHECK_SHOOT) ) 
00311         {
00312                 return VIS_FOV;
00313         }
00314 
00315         // check shootability
00316         if ( flags & CHECK_SHOOT ) 
00317         {
00318                 if ( !CanShoot ( ent, NPC ) ) 
00319                 {
00320                         return VIS_FOV;
00321                 }
00322         }
00323 
00324         return VIS_SHOOT;
00325 }

void NPC_ClearGoal void   ) 
 

Definition at line 65 of file NPC_goal.c.

References EF_NODRAW, entityState_s::eFlags, gentity_t, gentity_s::inuse, gNPC_t::lastGoalEntity, NPCInfo, NULL, gentity_s::s, and SetGoal().

Referenced by NPC_Begin(), NPC_BSDefault(), NPC_BSJump(), NPC_ReachedGoal(), and UpdateGoal().

00066 {
00067         gentity_t       *goal;
00068 
00069         if ( !NPCInfo->lastGoalEntity ) 
00070         {
00071                 SetGoal( NULL, 0.0 );
00072                 return;
00073         }
00074 
00075         goal = NPCInfo->lastGoalEntity;
00076         NPCInfo->lastGoalEntity = NULL;
00077 //      NAV_ClearLastRoute(NPC);
00078         if ( goal->inuse && !(goal->s.eFlags & EF_NODRAW) ) 
00079         {
00080 //              Debug_NPCPrintf( NPC, debugNPCAI, DEBUG_LEVEL_INFO, "NPC_ClearGoal: pop %s\n", goal->classname );
00081                 SetGoal( goal, 0 );//, NPCInfo->lastGoalEntityNeed
00082                 return;
00083         }
00084 
00085         SetGoal( NULL, 0.0 );
00086 }

qboolean NPC_ClearLOS const vec3_t  start,
const vec3_t  end
 

Definition at line 1069 of file NPC_utils.c.

References G_ClearLOS(), NPC, qboolean, and vec3_t.

Referenced by NPC_FindCombatPoint(), and Wampa_Combat().

01070 {
01071         return G_ClearLOS( NPC, start, end );
01072 }

qboolean NPC_ClearLOS2 gentity_t ent,
const vec3_t  end
 

Definition at line 1085 of file NPC_utils.c.

01086 {
01087         return G_ClearLOS2( NPC, ent, end );
01088 }

qboolean NPC_ClearLOS3 const vec3_t  start,
gentity_t ent
 

Definition at line 1081 of file NPC_utils.c.

References G_ClearLOS3(), gentity_t, NPC, qboolean, and vec3_t.

Referenced by NPC_FindCombatPoint().

01082 {
01083         return G_ClearLOS3( NPC, start, ent );
01084 }

qboolean NPC_ClearLOS4 gentity_t ent  ) 
 

Definition at line 1077 of file NPC_utils.c.

References G_ClearLOS4(), gentity_t, NPC, and qboolean.

Referenced by ATST_Attack(), Boba_FireDecide(), Howler_Combat(), ImperialProbe_AttackDecision(), Interrogator_Attack(), Jedi_CheckAmbushPlayer(), Mark1_AttackDecision(), Mark2_AttackDecision(), MineMonster_Combat(), NPC_BSEmplaced(), NPC_BSGM_Attack(), NPC_BSGrenadier_Attack(), NPC_BSJedi_FollowLeader(), NPC_BSSniper_Attack(), NPC_BSST_Attack(), NPC_CheckEnemyStealth(), NPC_TargetVisible(), Rancor_Combat(), Remote_Attack(), Seeker_Attack(), Seeker_FindEnemy(), Sentry_AttackDecision(), and ST_Commander().

01078 {
01079         return G_ClearLOS4( NPC, ent );
01080 }

qboolean NPC_ClearLOS5 const vec3_t  end  ) 
 

Definition at line 1073 of file NPC_utils.c.

References G_ClearLOS5(), NPC, qboolean, and vec3_t.

01074 {
01075         return G_ClearLOS5( NPC, end );
01076 }

qboolean NPC_ClearPathToGoal vec3_t  dir,
gentity_t goal
 

Definition at line 27 of file NPC_move.c.

References gentity_s::clipmask, CONTENTS_BODY, CONTENTS_BOTCLIP, entityShared_t::currentOrigin, trace_t::endpos, fabs(), FL_NAVGOAL, gentity_s::flags, FlyingCreature(), trace_t::fraction, gentity_t, gNPC_t::goalRadius, entityShared_t::maxs, entityShared_t::mins, NAV_CheckAhead(), NAV_HitNavGoal(), NPC, NPCInfo, qboolean, qfalse, qtrue, gentity_s::r, and vec3_t.

Referenced by NPC_GetMoveDirection(), and NPC_GetMoveDirectionAltRoute().

00028 {
00029         trace_t trace;
00030         float radius, dist, tFrac;
00031 
00032         //FIXME: What does do about area portals?  THIS IS BROKEN
00033         //if ( gi.inPVS( NPC->r.currentOrigin, goal->r.currentOrigin ) == qfalse )
00034         //      return qfalse;
00035 
00036         //Look ahead and see if we're clear to move to our goal position
00037         if ( NAV_CheckAhead( NPC, goal->r.currentOrigin, &trace, ( NPC->clipmask & ~CONTENTS_BODY )|CONTENTS_BOTCLIP ) )
00038         {
00039                 //VectorSubtract( goal->r.currentOrigin, NPC->r.currentOrigin, dir );
00040                 return qtrue;
00041         }
00042 
00043         if (!FlyingCreature(NPC))
00044         {
00045                 //See if we're too far above
00046                 if ( fabs( NPC->r.currentOrigin[2] - goal->r.currentOrigin[2] ) > 48 )
00047                         return qfalse;
00048         }
00049 
00050         //This is a work around
00051         radius = ( NPC->r.maxs[0] > NPC->r.maxs[1] ) ? NPC->r.maxs[0] : NPC->r.maxs[1];
00052         dist = Distance( NPC->r.currentOrigin, goal->r.currentOrigin );
00053         tFrac = 1.0f - ( radius / dist );
00054 
00055         if ( trace.fraction >= tFrac )
00056                 return qtrue;
00057 
00058         //See if we're looking for a navgoal
00059         if ( goal->flags & FL_NAVGOAL )
00060         {
00061                 //Okay, didn't get all the way there, let's see if we got close enough:
00062                 if ( NAV_HitNavGoal( trace.endpos, NPC->r.mins, NPC->r.maxs, goal->r.currentOrigin, NPCInfo->goalRadius, FlyingCreature( NPC ) ) )
00063                 {
00064                         //VectorSubtract(goal->r.currentOrigin, NPC->r.currentOrigin, dir);
00065                         return qtrue;
00066                 }
00067         }
00068 
00069         return qfalse;
00070 }

qboolean NPC_ClearShot gentity_t ent  ) 
 

Definition at line 2110 of file NPC_combat.c.

References trace_t::allsolid, CalcEntitySpot(), entityShared_t::currentOrigin, trace_t::entityNum, gentity_t, MASK_SHOT, NPC, NULL, entityState_s::number, qboolean, qfalse, qtrue, gentity_s::r, gentity_s::s, SPOT_WEAPON, trace_t::startsolid, trap_Trace(), vec3_t, entityState_s::weapon, and WP_BLASTER.

Referenced by NPC_CheckCanAttackExt().

02111 {
02112         vec3_t  muzzle;
02113         trace_t tr;
02114 
02115         if ( ( NPC == NULL ) || ( ent == NULL ) )
02116                 return qfalse;
02117 
02118         CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
02119 
02120         // add aim error
02121         // use weapon instead of specific npc types, although you could add certain npc classes if you wanted
02122 //      if ( NPC->client->playerTeam == TEAM_SCAVENGERS )
02123         if( NPC->s.weapon == WP_BLASTER /*|| NPC->s.weapon == WP_BLASTER_PISTOL*/ ) // any other guns to check for?
02124         {
02125                 vec3_t  mins = { -2, -2, -2 };
02126                 vec3_t  maxs = {  2,  2,  2 };
02127 
02128                 trap_Trace ( &tr, muzzle, mins, maxs, ent->r.currentOrigin, NPC->s.number, MASK_SHOT );
02129         }
02130         else
02131         {
02132                 trap_Trace ( &tr, muzzle, NULL, NULL, ent->r.currentOrigin, NPC->s.number, MASK_SHOT );
02133         }
02134         
02135         if ( tr.startsolid || tr.allsolid )
02136         {
02137                 return qfalse;
02138         }
02139 
02140         if ( tr.entityNum == ent->s.number ) 
02141                 return qtrue;
02142         
02143         return qfalse;
02144 }

void NPC_CreateFormation gentity_t self  ) 
 

void NPC_DeleteFromFormation gentity_t self  ) 
 

void NPC_DropFormation gentity_t self  ) 
 

qboolean NPC_EnemyTooFar gentity_t enemy,
float  dist,
qboolean  toShoot
 

Definition at line 1462 of file NPC_combat.c.

References gentity_s::client, entityShared_t::currentOrigin, gentity_t, NPC, NPC_MaxDistSquaredForWeapon(), gclient_s::ps, qboolean, qfalse, qtrue, gentity_s::r, vec3_t, VectorSubtract, playerState_s::weapon, and WP_SABER.

Referenced by NPC_BSAdvanceFight(), NPC_BSHuntAndKill(), NPC_CheckCanAttack(), NPC_CheckEnemy(), and NPC_PickEnemy().

01463 {
01464         vec3_t  vec;
01465 
01466         
01467         if ( !toShoot )
01468         {//Not trying to actually press fire button with this check
01469                 if ( NPC->client->ps.weapon == WP_SABER )
01470                 {//Just have to get to him
01471                         return qfalse;
01472                 }
01473         }
01474         
01475 
01476         if(!dist)
01477         {
01478                 VectorSubtract(NPC->r.currentOrigin, enemy->r.currentOrigin, vec);
01479                 dist = VectorLengthSquared(vec);
01480         }
01481 
01482         if(dist > NPC_MaxDistSquaredForWeapon())
01483                 return qtrue;
01484 
01485         return qfalse;
01486 }

qboolean NPC_EvaluateShot int  hit,
qboolean  glassOK
 

Definition at line 2208 of file NPC_combat.c.

References gentity_s::enemy, g_entities, NPC, NULL, entityState_s::number, qboolean, qfalse, qtrue, gentity_s::r, gentity_s::s, SVF_GLASS_BRUSH, and entityShared_t::svFlags.

02209 {
02210         if ( !NPC->enemy )
02211         {
02212                 return qfalse;
02213         }
02214 
02215         if ( hit == NPC->enemy->s.number || (&g_entities[hit] != NULL && (g_entities[hit].r.svFlags&SVF_GLASS_BRUSH)) )
02216         {//can hit enemy or will hit glass, so shoot anyway
02217                 return qtrue;
02218         }
02219         return qfalse;
02220 }

qboolean NPC_FaceEnemy qboolean  doPitch  ) 
 

Definition at line 1571 of file NPC_utils.c.

References gentity_s::enemy, NPC, NPC_FaceEntity(), NULL, qboolean, and qfalse.

Referenced by ATST_Attack(), Howler_Combat(), ImperialProbe_AttackDecision(), Interrogator_Attack(), Interrogator_Hunt(), Mark1_AttackDecision(), Mark1_Hunt(), Mark2_AttackDecision(), Mark2_Hunt(), MineMonster_Combat(), NPC_BSEmplaced(), NPC_BSGM_Attack(), NPC_BSGrenadier_Attack(), NPC_BSRancor_Default(), NPC_BSST_Attack(), NPC_BSWampa_Default(), NPC_CheckCanAttackExt(), Rancor_Combat(), Seeker_Hunt(), Sentry_AttackDecision(), Sentry_MaintainHeight(), and Wampa_Combat().

01572 {
01573         if ( NPC == NULL )
01574                 return qfalse;
01575 
01576         if ( NPC->enemy == NULL )
01577                 return qfalse;
01578 
01579         return NPC_FaceEntity( NPC->enemy, doPitch );
01580 }

qboolean NPC_FaceEntity gentity_t ent,
qboolean  doPitch
 

Definition at line 1555 of file NPC_utils.c.

References CalcEntitySpot(), gentity_t, NPC_FacePosition(), qboolean, SPOT_HEAD_LEAN, and vec3_t.

Referenced by NAV_ResolveBlock(), NAV_ResolveEntityCollision(), NAV_TestForBlocked(), NPC_BSJedi_FollowLeader(), and NPC_FaceEnemy().

01556 {
01557         vec3_t          entPos;
01558 
01559         //Get the positions
01560         CalcEntitySpot( ent, SPOT_HEAD_LEAN, entPos );
01561 
01562         return NPC_FacePosition( entPos, doPitch );
01563 }

qboolean NPC_FacePosition vec3_t  position,
qboolean  doPitch
 

Definition at line 1491 of file NPC_utils.c.

References AngleNormalize360(), usercmd_s::angles, CalcEntitySpot(), CLASS_ATST, CLASS_GALAKMECH, CLASS_RANCOR, CLASS_WAMPA, gentity_s::client, client, playerState_s::delta_angles, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gentity_s::enemy, fabs(), flrand(), GetAnglesForDirection(), level, entityShared_t::maxs, NPC, gclient_s::NPC_class, NPC_UpdateAngles(), NPCInfo, PITCH, gclient_s::ps, qboolean, qfalse, qtrue, gentity_s::r, SHORT2ANGLE, sin(), SPOT_HEAD_LEAN, SPOT_ORIGIN, SPOT_WEAPON, level_locals_t::time, ucmd, VALID_ATTACK_CONE, vec3_t, and YAW.

Referenced by NPC_FaceEntity().

01492 {
01493         vec3_t          muzzle;
01494         vec3_t          angles;
01495         float           yawDelta;
01496         qboolean        facing = qtrue;
01497 
01498         //Get the positions
01499         if ( NPC->client && (NPC->client->NPC_class == CLASS_RANCOR || NPC->client->NPC_class == CLASS_WAMPA) )// || NPC->client->NPC_class == CLASS_SAND_CREATURE) )
01500         {
01501                 CalcEntitySpot( NPC, SPOT_ORIGIN, muzzle );
01502                 muzzle[2] += NPC->r.maxs[2] * 0.75f;
01503         }
01504         else if ( NPC->client && NPC->client->NPC_class == CLASS_GALAKMECH )
01505         {
01506                 CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
01507         }
01508         else
01509         {
01510                 CalcEntitySpot( NPC, SPOT_HEAD_LEAN, muzzle );//SPOT_HEAD
01511         }
01512 
01513         //Find the desired angles
01514         GetAnglesForDirection( muzzle, position, angles );
01515 
01516         NPCInfo->desiredYaw             = AngleNormalize360( angles[YAW] );
01517         NPCInfo->desiredPitch   = AngleNormalize360( angles[PITCH] );
01518 
01519         if ( NPC->enemy && NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_ATST )
01520         {
01521                 // FIXME: this is kind of dumb, but it was the easiest way to get it to look sort of ok
01522                 NPCInfo->desiredYaw     += flrand( -5, 5 ) + sin( level.time * 0.004f ) * 7;
01523                 NPCInfo->desiredPitch += flrand( -2, 2 );
01524         }
01525         //Face that yaw
01526         NPC_UpdateAngles( qtrue, qtrue );
01527 
01528         //Find the delta between our goal and our current facing
01529         yawDelta = AngleNormalize360( NPCInfo->desiredYaw - ( SHORT2ANGLE( ucmd.angles[YAW] + client->ps.delta_angles[YAW] ) ) );
01530         
01531         //See if we are facing properly
01532         if ( fabs( yawDelta ) > VALID_ATTACK_CONE )
01533                 facing = qfalse;
01534 
01535         if ( doPitch )
01536         {
01537                 //Find the delta between our goal and our current facing
01538                 float currentAngles = ( SHORT2ANGLE( ucmd.angles[PITCH] + client->ps.delta_angles[PITCH] ) );
01539                 float pitchDelta = NPCInfo->desiredPitch - currentAngles;
01540                 
01541                 //See if we are facing properly
01542                 if ( fabs( pitchDelta ) > VALID_ATTACK_CONE )
01543                         facing = qfalse;
01544         }
01545 
01546         return facing;
01547 }

float NPC_FindClosestTeammate gentity_t self  ) 
 

int NPC_FindCombatPoint const vec3_t  position,
const vec3_t  avoidPosition,
vec3_t  enemyPosition,
const int  flags,
const float  avoidDist,
const int  ignorePoint
 

Definition at line 2657 of file NPC_combat.c.

References trace_t::allsolid, gentity_s::clipmask, level_locals_t::combatPoints, CP_APPROACH_ENEMY, CP_AVOID, CP_AVOID_ENEMY, CP_CLEAR, CP_COLLECT_RADIUS, CP_COVER, CP_FLANK, CP_HAS_ROUTE, CP_HORZ_DIST_COLL, CP_NEAREST, CP_NO_PVS, CP_RETREAT, entityShared_t::currentOrigin, DistanceHorizontalSquared(), DotProduct, gentity_s::enemy, ENTITYNUM_NONE, combatPt_t::index, gentity_s::lastWaypoint, level, MAX_COMBAT_POINTS, entityShared_t::maxs, MIN_AVOID_DISTANCE_SQUARED, MIN_AVOID_DOT, entityShared_t::mins, NAV_ClearPathToPoint(), NAV_GetNearestNode(), NODE_NONE, NPC, NPC_ClearLOS(), NPC_ClearLOS3(), NPCInfo, entityState_s::number, combatPoint_t::origin, Q3_INFINITE, qfalse, qtrue, gentity_s::r, gentity_s::s, trace_t::startsolid, gNPC_t::stats, trap_Nav_GetBestNodeAltRoute2(), trap_Nav_GetPathCost(), trap_Trace(), vec3_t, VectorCopy, VectorNormalize(), VectorSubtract, gNPCstats_e::visrange, gentity_s::waypoint, combatPoint_t::waypoint, WAYPOINT_NONE, entityState_s::weapon, and WP_THERMAL.

Referenced by NPC_StartFlee(), and ST_Commander().

02658 {
02659         combatPt_t              points[MAX_COMBAT_POINTS];
02660         int                             best = -1, cost, bestCost = Q3_INFINITE, waypoint = WAYPOINT_NONE;
02661         float                   dist;
02662         trace_t                 tr;
02663         float                   collRad = CP_COLLECT_RADIUS;
02664         int                             numPoints;
02665         int                             j = 0;
02666         float                   modifiedAvoidDist = avoidDist;
02667 
02668         if ( modifiedAvoidDist <= 0 )
02669         {
02670                 modifiedAvoidDist = MIN_AVOID_DISTANCE_SQUARED;
02671         }
02672         else
02673         {
02674                 modifiedAvoidDist *= modifiedAvoidDist;
02675         }
02676 
02677         if ( (flags & CP_HAS_ROUTE) || (flags & CP_NEAREST) )
02678         {//going to be doing macro nav tests
02679                 if ( NPC->waypoint == WAYPOINT_NONE )
02680                 {
02681                         waypoint = NAV_GetNearestNode( NPC, NPC->lastWaypoint );
02682                 }
02683                 else
02684                 {
02685                         waypoint = NPC->waypoint;
02686                 }
02687         }
02688 
02689         //Collect our nearest points
02690         if ( flags & CP_NO_PVS )
02691         {//much larger radius since most will be dropped?
02692                 collRad = CP_COLLECT_RADIUS*4;
02693         }
02694         numPoints = NPC_CollectCombatPoints( enemyPosition, collRad, points, flags );//position
02695 
02696 
02697         for ( j = 0; j < numPoints; j++ )
02698         {
02699                 //const int i = (*cpi).second;
02700                 const int i = points[j].index;
02701                 const float pdist = points[j].dist;
02702 
02703                 //Must not be one we want to ignore
02704                 if ( i == ignorePoint )
02705                         continue;
02706 
02707                 //FIXME: able to mark certain ones as too dangerous to go to for now?  Like a tripmine/thermal/detpack is near or something?
02708                 //If we need a cover point, check this point
02709                 if ( ( flags & CP_COVER ) && ( NPC_ClearLOS( level.combatPoints[i].origin, enemyPosition ) == qtrue ) )//Used to use NPC->enemy
02710                         continue;
02711 
02712                 //Need a clear LOS to our target... and be within shot range to enemy position (FIXME: make this a separate CS_ flag? and pass in a range?)
02713                 if ( flags & CP_CLEAR )
02714                 {
02715                         if ( NPC_ClearLOS3( level.combatPoints[i].origin, NPC->enemy ) == qfalse )
02716                         {
02717                                 continue;
02718                         }
02719                         if ( NPC->s.weapon == WP_THERMAL )
02720                         {//horizontal
02721                                 dist = DistanceHorizontalSquared( level.combatPoints[i].origin, NPC->enemy->r.currentOrigin );
02722                         }
02723                         else
02724                         {//actual
02725                                 dist = DistanceSquared( level.combatPoints[i].origin, NPC->enemy->r.currentOrigin );
02726                         }
02727                         if ( dist > (NPCInfo->stats.visrange*NPCInfo->stats.visrange) )
02728                         {
02729                                 continue;
02730                         }
02731                 }
02732 
02733                 //Avoid this position?
02734                 if ( ( flags & CP_AVOID ) && ( DistanceSquared( level.combatPoints[i].origin, position ) < modifiedAvoidDist ) )//was using MIN_AVOID_DISTANCE_SQUARED, not passed in modifiedAvoidDist
02735                         continue;
02736 
02737                 //Try to find a point closer to the enemy than where we are
02738                 if ( flags & CP_APPROACH_ENEMY )
02739                 {
02740                         if ( flags&CP_HORZ_DIST_COLL )
02741                         {
02742                                 if ( pdist > DistanceHorizontalSquared( position, enemyPosition ) )
02743                                 {
02744                                         continue;
02745                                 }
02746                         }
02747                         else 
02748                         {
02749                                 if ( pdist > DistanceSquared( position, enemyPosition ) )
02750                                 {
02751                                         continue;
02752                                 }
02753                         }
02754                 }
02755                 //Try to find a point farther from the enemy than where we are
02756                 if ( flags & CP_RETREAT )
02757                 {
02758                         if ( flags&CP_HORZ_DIST_COLL )
02759                         {
02760                                 if ( pdist < DistanceHorizontalSquared( position, enemyPosition ) )
02761                                 {//it's closer, don't use it
02762                                         continue;
02763                                 }
02764                         }
02765                         else
02766                         {
02767                                 if ( pdist < DistanceSquared( position, enemyPosition ) )
02768                                 {//it's closer, don't use it
02769                                         continue;
02770                                 }
02771                         }
02772                 }
02773 
02774                 //We want a point on other side of the enemy from current pos
02775                 if ( flags & CP_FLANK  )
02776                 {
02777                         vec3_t  eDir2Me, eDir2CP;
02778                         float dot;
02779                         
02780                         VectorSubtract( position, enemyPosition, eDir2Me );
02781                         VectorNormalize( eDir2Me );
02782 
02783                         VectorSubtract( level.combatPoints[i].origin, enemyPosition, eDir2CP );
02784                         VectorNormalize( eDir2CP );
02785 
02786                         dot = DotProduct( eDir2Me, eDir2CP );
02787                         
02788                         //Not far enough behind enemy from current pos
02789                         if ( dot >= 0.4 )
02790                                 continue;
02791                 }
02792 
02793                 //See if we're trying to avoid our enemy
02794                 //FIXME: this needs to check for the waypoint you'll be taking to get to that combat point
02795                 if ( flags & CP_AVOID_ENEMY  )
02796                 {
02797                         vec3_t  eDir, gDir;
02798                         vec3_t  wpOrg;
02799                         float dot;
02800                         
02801                         VectorSubtract( position, enemyPosition, eDir );
02802                         VectorNormalize( eDir );
02803 
02804                         /*
02805                         NAV_FindClosestWaypointForEnt( NPC, level.combatPoints[i].waypoint );
02806                         if ( NPC->waypoint != WAYPOINT_NONE && NPC->waypoint != level.combatPoints[i].waypoint )
02807                         {
02808                                 trap_Nav_GetNodePosition( NPC->waypoint, wpOrg );
02809                         }
02810                         else
02811                         */
02812                         {
02813                                 VectorCopy( level.combatPoints[i].origin, wpOrg );
02814                         }
02815                         VectorSubtract( position, wpOrg, gDir );
02816                         VectorNormalize( gDir );
02817 
02818                         dot = DotProduct( gDir, eDir );
02819                         
02820                         //Don't want to run at enemy
02821                         if ( dot >= MIN_AVOID_DOT )
02822                                 continue;
02823 
02824                         //Can't be too close to the enemy
02825                         if ( DistanceSquared( wpOrg, enemyPosition ) < modifiedAvoidDist )
02826                                 continue;
02827                 }
02828                 
02829                 //Okay, now make sure it's not blocked
02830                 trap_Trace( &tr, level.combatPoints[i].origin, NPC->r.mins, NPC->r.maxs, level.combatPoints[i].origin, NPC->s.number, NPC->clipmask );
02831                 if ( tr.allsolid || tr.startsolid )
02832                 {
02833                         continue;
02834                 }
02835 
02836                 //we must have a route to the combat point
02837                 if ( flags & CP_HAS_ROUTE )
02838                 {
02839                         /*
02840                         if ( level.combatPoints[i].waypoint == WAYPOINT_NONE )
02841                         {
02842                                 level.combatPoints[i].waypoint = NAV_FindClosestWaypointForPoint( level.combatPoints[i].origin );
02843                         }
02844                         */
02845 
02846                         if ( waypoint == WAYPOINT_NONE || level.combatPoints[i].waypoint == WAYPOINT_NONE || trap_Nav_GetBestNodeAltRoute2( waypoint, level.combatPoints[i].waypoint, NODE_NONE ) == WAYPOINT_NONE )
02847                         {//can't possibly have a route to any OR can't possibly have a route to this one OR don't have a route to this one
02848                                 if ( !NAV_ClearPathToPoint( NPC, NPC->r.mins, NPC->r.maxs, level.combatPoints[i].origin, NPC->clipmask, ENTITYNUM_NONE ) )
02849                                 {//don't even have a clear straight path to this one
02850                                         continue;
02851                                 }
02852                         }
02853                 }
02854 
02855                 //We want the one with the shortest path from current pos
02856                 if ( flags & CP_NEAREST && waypoint != WAYPOINT_NONE && level.combatPoints[i].waypoint != WAYPOINT_NONE )
02857                 {
02858                         cost = trap_Nav_GetPathCost( waypoint, level.combatPoints[i].waypoint );
02859                         if ( cost < bestCost )
02860                         {
02861                                 bestCost = cost;
02862                                 best = i;
02863                         }
02864                         continue;
02865                 }
02866 
02867                 //we want the combat point closest to the enemy
02868                 //if ( flags & CP_CLOSEST )
02869                 //they are sorted by this distance, so the first one to get this far is the closest
02870                 return i;
02871         }
02872 
02873         return best;
02874 }

qboolean NPC_FindPlayer void   ) 
 

Definition at line 1356 of file NPC_utils.c.

References g_entities, NPC_TargetVisible(), and qboolean.

01357 {
01358         return NPC_TargetVisible( &g_entities[0] );
01359 }

int NPC_FindSquadPoint vec3_t  position  ) 
 

Definition at line 2882 of file NPC_combat.c.

References level_locals_t::combatPoints, CPF_SQUAD, combatPoint_t::flags, level, level_locals_t::numCombatPoints, combatPoint_t::occupied, combatPoint_t::origin, qfalse, qtrue, vec3_t, and WORLD_SIZE.

02883 {
02884         float   dist, nearestDist = (float)WORLD_SIZE*(float)WORLD_SIZE;
02885         int             nearestPoint = -1;
02886         int             i;
02887 
02888         //float                 playerDist = DistanceSquared( g_entities[0].currentOrigin, NPC->r.currentOrigin );
02889 
02890         for ( i = 0; i < level.numCombatPoints; i++ )
02891         {
02892                 //Squad points are only valid if we're looking for them
02893                 if ( ( level.combatPoints[i].flags & CPF_SQUAD ) == qfalse )
02894                         continue;
02895 
02896                 //Must be vacant
02897                 if ( level.combatPoints[i].occupied == qtrue )
02898                         continue;
02899                 
02900                 dist = DistanceSquared( position, level.combatPoints[i].origin );
02901 
02902                 //The point cannot take us past the player
02903                 //if ( dist > ( playerDist * DotProduct( dirToPlayer, playerDir ) ) )   //FIXME: Retain this
02904                 //      continue;
02905 
02906                 //See if this is closer than the others
02907                 if ( dist < nearestDist )
02908                 {
02909                         nearestPoint = i;
02910                         nearestDist = dist;
02911                 }
02912         }
02913 
02914         return nearestPoint;
02915 }

qboolean NPC_FreeCombatPoint int  combatPointID,
qboolean  failed
 

Definition at line 2945 of file NPC_combat.c.

References level_locals_t::combatPoints, gNPC_t::lastFailedCombatPoint, level, NPCInfo, level_locals_t::numCombatPoints, combatPoint_t::occupied, qboolean, qfalse, and qtrue.

Referenced by NPC_SetCombatPoint(), player_die(), ST_ApproachEnemy(), ST_HuntEnemy(), and ST_TrackEnemy().

02946 {
02947         if ( failed )
02948         {//remember that this one failed for us
02949                 NPCInfo->lastFailedCombatPoint = combatPointID;
02950         }
02951         //Make sure it's valid
02952         if ( combatPointID > level.numCombatPoints )
02953                 return qfalse;
02954 
02955         //Make sure it's currently occupied
02956         if ( level.combatPoints[combatPointID].occupied == qfalse )
02957                 return qfalse;
02958 
02959         //Free it
02960         level.combatPoints[combatPointID].occupied = qfalse;
02961         
02962         return qtrue;
02963 }

float NPC_GetPainChance gentity_t self,
int  damage
 

Definition at line 157 of file NPC_reactions.c.

00158 {
00159         float pain_chance;
00160         if ( !self->enemy )
00161         {//surprised, always take pain
00162                 return 1.0f;
00163         }
00164 
00165         if (!self->client)
00166         {
00167                 return 1.0f;
00168         }
00169 
00170         //if ( damage > self->max_health/2.0f )
00171         if (damage > self->client->ps.stats[STAT_MAX_HEALTH]/2.0f)
00172         {
00173                 return 1.0f;
00174         }
00175 
00176         pain_chance = (float)(self->client->ps.stats[STAT_MAX_HEALTH]-self->health)/(self->client->ps.stats[STAT_MAX_HEALTH]*2.0f) + (float)damage/(self->client->ps.stats[STAT_MAX_HEALTH]/2.0f);
00177         switch ( g_spskill.integer )
00178         {
00179         case 0: //easy
00180                 //return 0.75f;
00181                 break;
00182 
00183         case 1://med
00184                 pain_chance *= 0.5f;
00185                 //return 0.35f;
00186                 break;
00187 
00188         case 2://hard
00189         default:
00190                 pain_chance *= 0.1f;
00191                 //return 0.05f;
00192                 break;
00193         }
00194         //Com_Printf( "%s: %4.2f\n", self->NPC_type, pain_chance );
00195         return pain_chance;
00196 }

void NPC_InsertIntoFormation gentity_t self  ) 
 

void NPC_LoadParms void   ) 
 

Definition at line 3241 of file NPC_stats.c.

References COM_Compress(), Com_Printf(), fileHandle_t, FS_READ, G_Error(), MAX_NPC_DATA_SIZE, NPCParms, npcParseBuffer, NULL, qfalse, strcat(), strlen(), trap_FS_FCloseFile(), trap_FS_FOpenFile(), trap_FS_GetFileList(), trap_FS_Read(), and va().

Referenced by NPC_InitGame().

03242 {
03243         int                     len, totallen, npcExtFNLen, mainBlockLen, fileCnt, i;
03244 //      const char      *filename = "ext_data/NPC2.cfg";
03245         char             *holdChar, *marker;
03246         char            npcExtensionListBuf[2048];                      //      The list of file names read in
03247         fileHandle_t f;
03248         len = 0;
03249 
03250         //remember where to store the next one
03251         totallen = mainBlockLen = len;
03252         marker = NPCParms+totallen;
03253         *marker = 0;
03254 
03255         //now load in the extra .npc extensions
03256         fileCnt = trap_FS_GetFileList("ext_data/NPCs", ".npc", npcExtensionListBuf, sizeof(npcExtensionListBuf) );
03257 
03258 #ifdef _XBOX
03259         npcParseBuffer = (char *) Z_Malloc(MAX_NPC_DATA_SIZE, TAG_TEMP_WORKSPACE, qfalse, 4);
03260 #endif
03261 
03262         holdChar = npcExtensionListBuf;
03263         for ( i = 0; i < fileCnt; i++, holdChar += npcExtFNLen + 1 ) 
03264         {
03265                 npcExtFNLen = strlen( holdChar );
03266 
03267 //              Com_Printf( "Parsing %s\n", holdChar );
03268 
03269                 len = trap_FS_FOpenFile(va( "ext_data/NPCs/%s", holdChar), &f, FS_READ);
03270 
03271                 if ( len == -1 ) 
03272                 {
03273                         Com_Printf( "error reading file\n" );
03274                 }
03275                 else
03276                 {
03277                         if ( totallen + len >= MAX_NPC_DATA_SIZE ) {
03278                                 G_Error( "NPC extensions (*.npc) are too large" );
03279                         }
03280                         trap_FS_Read(npcParseBuffer, len, f);
03281                         npcParseBuffer[len] = 0;
03282 
03283                         len = COM_Compress( npcParseBuffer );
03284 
03285                         strcat( marker, npcParseBuffer );
03286                         strcat(marker, "\n");
03287                         len++;
03288                         trap_FS_FCloseFile(f);
03289 
03290                         totallen += len;
03291                         marker = NPCParms+totallen;
03292                         //*marker = 0; //rww - make sure this is null or strcat will not append to the correct place
03293                         //rww  12/19/02-actually the probelm was npcParseBuffer not being nul-term'd, which could cause issues in the strcat too
03294                 }
03295         }
03296 
03297 #ifdef _XBOX
03298         Z_Free(npcParseBuffer);
03299         npcParseBuffer = NULL;
03300 #endif
03301 
03302 }

void NPC_LostEnemyDecideChase void   ) 
 

Definition at line 18 of file NPC_AI_Default.c.

References gNPC_t::behaviorState, BS_HUNT_AND_KILL, BS_SEARCH, gentity_s::enemy, G_ClearEnemy(), gNPC_t::goalEntity, gentity_s::lastWaypoint, NPC, NPC_BSSearchStart(), NPCInfo, and WAYPOINT_NONE.

Referenced by NPC_BSRunAndShoot(), NPC_CheckEnemy(), and NPC_HandleAIFlags().

00019 {
00020         switch( NPCInfo->behaviorState )
00021         {
00022         case BS_HUNT_AND_KILL:
00023                 //We were chasing him and lost him, so try to find him
00024                 if ( NPC->enemy == NPCInfo->goalEntity && NPC->enemy->lastWaypoint != WAYPOINT_NONE )
00025                 {//Remember his last valid Wp, then check it out
00026                         //FIXME: Should we only do this if there's no other enemies or we've got LOCKED_ENEMY on?
00027                         NPC_BSSearchStart( NPC->enemy->lastWaypoint, BS_SEARCH );
00028                 }
00029                 //If he's not our goalEntity, we're running somewhere else, so lose him
00030                 break;
00031         default:
00032                 break;
00033         }
00034         G_ClearEnemy( NPC );
00035 }

float NPC_MaxDistSquaredForWeapon void   ) 
 

Definition at line 1334 of file NPC_combat.c.

Referenced by NPC_BSHuntAndKill(), NPC_BSSniper_Attack(), NPC_CheckAttackHold(), and NPC_EnemyTooFar().

01335 {
01336         if(NPCInfo->stats.shootDistance > 0)
01337         {//overrides default weapon dist
01338                 return NPCInfo->stats.shootDistance * NPCInfo->stats.shootDistance;
01339         }
01340 
01341         switch ( NPC->s.weapon ) 
01342         {
01343         case WP_BLASTER://scav rifle
01344                 return 1024 * 1024;//should be shorter?
01345                 break;
01346 
01347         case WP_BRYAR_PISTOL://prifle
01348                 return 1024 * 1024;
01349                 break;
01350 
01351                 /*
01352         case WP_BLASTER_PISTOL://prifle
01353                 return 1024 * 1024;
01354                 break;
01355                 */
01356 
01357         case WP_DISRUPTOR://disruptor
01358                 if ( NPCInfo->scriptFlags & SCF_ALT_FIRE )
01359                 {
01360                         return ( 4096 * 4096 );
01361                 }
01362                 else
01363                 {
01364                         return 1024 * 1024;
01365                 }
01366                 break;
01367 /*
01368         case WP_SABER:
01369                 return 1024 * 1024;
01370                 break;
01371         
01372 
01373         case WP_TRICORDER:
01374                 return 0;//tricorder
01375                 break;
01376 */
01377         case WP_SABER:
01378                 if ( NPC->client && NPC->client->saber[0].blade[0].lengthMax )
01379                 {//FIXME: account for whether enemy and I are heading towards each other!
01380                         return (NPC->client->saber[0].blade[0].lengthMax + NPC->r.maxs[0]*1.5)*(NPC->client->saber[0].blade[0].lengthMax + NPC->r.maxs[0]*1.5);
01381                 }
01382                 else
01383                 {
01384                         return 48*48;
01385                 }
01386                 break;
01387 
01388         default:
01389                 return 1024 * 1024;//was 0
01390                 break;
01391         }
01392 }

qboolean NPC_MoveToGoal qboolean  tryStraight  ) 
 

Definition at line 382 of file NPC_move.c.

References AngleNormalize360(), BOTH_PAIN1, BOTH_PAIN18, BUTTON_WALKING, usercmd_s::buttons, gentity_s::client, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gNPC_t::distToGoal, EF2_FLYING, playerState_s::eFlags2, usercmd_s::forwardmove, G_UcmdMoveForDir(), GetTime(), gNPC_t::lastPathAngles, entityState_s::legsAnim, NPC, NPC_CheckCombatMove(), NPC_GetMoveDirection(), NPC_GetMoveDirectionAltRoute(), NPCInfo, PITCH, PM_InKnockDown(), gclient_s::ps, qboolean, qfalse, qtrue, gNPCstats_e::runSpeed, gentity_s::s, playerState_s::speed, startTime, gNPC_t::stats, ucmd, vec3_t, vectoangles(), playerState_s::velocity, gNPCstats_e::walkSpeed, and YAW.

Referenced by ATST_Hunt(), ATST_Patrol(), Droid_Patrol(), Howler_Combat(), Howler_Move(), Howler_Patrol(), ImperialProbe_Patrol(), Mark1_Hunt(), Mark1_Patrol(), Mark2_Hunt(), Mark2_Patrol(), MineMonster_Combat(), MineMonster_Idle(), MineMonster_Move(), MineMonster_Patrol(), NPC_BSCinematic(), NPC_BSDefault(), NPC_BSFlee(), NPC_BSGM_Patrol(), NPC_BSGrenadier_Patrol(), NPC_BSHuntAndKill(), NPC_BSIdle(), NPC_BSJedi_FollowLeader(), NPC_BSPatrol(), NPC_BSRun(), NPC_BSRunAndShoot(), NPC_BSSearch(), NPC_BSSniper_Patrol(), NPC_BSST_Investigate(), NPC_BSST_Patrol(), NPC_BSWander(), NPC_Sentry_Patrol(), NPC_SlideMoveToGoal(), Rancor_Combat(), Rancor_Idle(), Rancor_Move(), Rancor_Patrol(), Remote_Patrol(), Seeker_FollowOwner(), Wampa_Idle(), Wampa_Move(), and Wampa_Patrol().

00383 {
00384         float   distance;
00385         vec3_t  dir;
00386 
00387 #if     AI_TIMERS
00388         int     startTime = GetTime(0);
00389 #endif//        AI_TIMERS
00390         //If taking full body pain, don't move
00391         if ( PM_InKnockDown( &NPC->client->ps ) || ( ( NPC->s.legsAnim >= BOTH_PAIN1 ) && ( NPC->s.legsAnim <= BOTH_PAIN18 ) ) )
00392         {
00393                 return qtrue;
00394         }
00395 
00396         /*
00397         if( NPC->s.eFlags & EF_LOCKED_TO_WEAPON )
00398         {//If in an emplaced gun, never try to navigate!
00399                 return qtrue;
00400         }
00401         */
00402         //rwwFIXMEFIXME: emplaced support
00403 
00404         //FIXME: if can't get to goal & goal is a target (enemy), try to find a waypoint that has line of sight to target, at least?
00405         //Get our movement direction
00406 #if 1
00407         if ( NPC_GetMoveDirectionAltRoute( dir, &distance, tryStraight ) == qfalse )
00408 #else
00409         if ( NPC_GetMoveDirection( dir, &distance ) == qfalse )
00410 #endif
00411                 return qfalse;
00412 
00413         NPCInfo->distToGoal             = distance;
00414 
00415         //Convert the move to angles
00416         vectoangles( dir, NPCInfo->lastPathAngles );
00417         if ( (ucmd.buttons&BUTTON_WALKING) )
00418         {
00419                 NPC->client->ps.speed = NPCInfo->stats.walkSpeed;
00420         }
00421         else
00422         {
00423                 NPC->client->ps.speed = NPCInfo->stats.runSpeed;
00424         }
00425 
00426         //FIXME: still getting ping-ponging in certain cases... !!!  Nav/avoidance error?  WTF???!!!
00427         //If in combat move, then move directly towards our goal
00428         if ( NPC_CheckCombatMove() )
00429         {//keep current facing
00430                 G_UcmdMoveForDir( NPC, &ucmd, dir );
00431         }
00432         else
00433         {//face our goal
00434                 //FIXME: strafe instead of turn if change in dir is small and temporary
00435                 NPCInfo->desiredPitch   = 0.0f;
00436                 NPCInfo->desiredYaw             = AngleNormalize360( NPCInfo->lastPathAngles[YAW] );
00437                 
00438                 //Pitch towards the goal and also update if flying or swimming
00439                 if ( (NPC->client->ps.eFlags2&EF2_FLYING) )//moveType == MT_FLYSWIM )
00440                 {
00441                         NPCInfo->desiredPitch = AngleNormalize360( NPCInfo->lastPathAngles[PITCH] );
00442                         
00443                         if ( dir[2] )
00444                         {
00445                                 float scale = (dir[2] * distance);
00446                                 if ( scale > 64 )
00447                                 {
00448                                         scale = 64;
00449                                 }
00450                                 else if ( scale < -64 )
00451                                 {
00452                                         scale = -64;
00453                                 }
00454                                 NPC->client->ps.velocity[2] = scale;
00455                                 //NPC->client->ps.velocity[2] = (dir[2] > 0) ? 64 : -64;
00456                         }
00457                 }
00458 
00459                 //Set any final info
00460                 ucmd.forwardmove = 127;
00461         }
00462 
00463 #if     AI_TIMERS
00464         navTime += GetTime( startTime );
00465 #endif//        AI_TIMERS
00466         return qtrue;
00467 }

void NPC_Pain gentity_t self,
gentity_t attacker,
int  damage
 

Definition at line 363 of file NPC_reactions.c.

Referenced by NPC_PainFunc().

00364 {
00365         team_t otherTeam = TEAM_FREE;
00366         int             voiceEvent = -1;
00367         gentity_t *other = attacker;
00368         int mod = gPainMOD;
00369         int hitLoc = gPainHitLoc;
00370         vec3_t point;
00371 
00372         VectorCopy(gPainPoint, point);
00373 
00374         if ( self->NPC == NULL ) 
00375                 return;
00376 
00377         if ( other == NULL ) 
00378                 return;
00379 
00380         //or just remove ->pain in player_die?
00381         if ( self->client->ps.pm_type == PM_DEAD )
00382                 return;
00383 
00384         if ( other == self ) 
00385                 return;
00386 
00387         //MCG: Ignore damage from your own team for now
00388         if ( other->client )
00389         {
00390                 otherTeam = other->client->playerTeam;
00391         //      if ( otherTeam == TEAM_DISGUISE )
00392         //      {
00393         //              otherTeam = TEAM_PLAYER;
00394         //      }
00395         }
00396 
00397         if ( self->client->playerTeam 
00398                 && other->client 
00399                 && otherTeam == self->client->playerTeam 
00400         /*      && (!player->client->ps.viewEntity || other->s.number != player->client->ps.viewEntity)*/) 
00401         //rwwFIXMEFIXME: Will need modification when player controllable npcs are done
00402         {//hit by a teammate
00403                 if ( other != self->enemy && self != other->enemy )
00404                 {//we weren't already enemies
00405                         if ( self->enemy || other->enemy 
00406                                 
00407                                 //|| (other->s.number&&other->s.number!=player->client->ps.viewEntity) 
00408                                 //rwwFIXMEFIXME: same
00409 
00410                                 /*|| (!other->s.number&&Q_irand( 0, 3 ))*/ )
00411                         {//if one of us actually has an enemy already, it's okay, just an accident OR wasn't hit by player or someone controlled by player OR player hit ally and didn't get 25% chance of getting mad (FIXME:accumulate anger+base on diff?)
00412                                 //FIXME: player should have to do a certain amount of damage to ally or hit them several times to make them mad
00413                                 //Still run pain and flee scripts
00414                                 if ( self->client && self->NPC )
00415                                 {//Run any pain instructions
00416                                         if ( self->health <= (self->client->ps.stats[STAT_MAX_HEALTH]/3) && G_ActivateBehavior(self, BSET_FLEE) )
00417                                         {
00418                                                 
00419                                         }
00420                                         else// if( VALIDSTRING( self->behaviorSet[BSET_PAIN] ) )
00421                                         {
00422                                                 G_ActivateBehavior(self, BSET_PAIN);
00423                                         }
00424                                 }
00425                                 if ( damage != -1 )
00426                                 {//-1 == don't play pain anim
00427                                         //Set our proper pain animation
00428                                         if ( Q_irand( 0, 1 ) )
00429                                         {
00430                                                 NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc, EV_FFWARN );
00431                                         }
00432                                         else
00433                                         {
00434                                                 NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc, -1 );
00435                                         }
00436                                 }
00437                                 return;
00438                         }
00439                         else if ( self->NPC && !other->s.number )//should be assumed, but...
00440                         {//dammit, stop that!
00441                                 if ( self->NPC->charmedTime )
00442                                 {//mindtricked
00443                                         return;
00444                                 }
00445                                 else if ( self->NPC->ffireCount < 3+((2-g_spskill.integer)*2) )
00446                                 {//not mad enough yet
00447                                         //Com_Printf( "chck: %d < %d\n", self->NPC->ffireCount, 3+((2-g_spskill.integer)*2) );
00448                                         if ( damage != -1 )
00449                                         {//-1 == don't play pain anim
00450                                                 //Set our proper pain animation
00451                                                 if ( Q_irand( 0, 1 ) )
00452                                                 {
00453                                                         NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc, EV_FFWARN );
00454                                                 }
00455                                                 else
00456                                                 {
00457                                                         NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc, -1 );
00458                                                 }
00459                                         }
00460                                         return;
00461                                 }
00462                                 else if ( G_ActivateBehavior( self, BSET_FFIRE ) )
00463                                 {//we have a specific script to run, so do that instead
00464                                         return;
00465                                 }
00466                                 else
00467                                 {//okay, we're going to turn on our ally, we need to set and lock our enemy and put ourselves in a bstate that lets us attack him (and clear any flags that would stop us)
00468                                         self->NPC->blockedSpeechDebounceTime = 0;
00469                                         voiceEvent = EV_FFTURN;
00470                                         self->NPC->behaviorState = self->NPC->tempBehavior = self->NPC->defaultBehavior = BS_DEFAULT;
00471                                         other->flags &= ~FL_NOTARGET;
00472                                         //self->svFlags &= ~(SVF_IGNORE_ENEMIES|SVF_ICARUS_FREEZE|SVF_NO_COMBAT_SOUNDS);
00473                                         self->r.svFlags &= ~SVF_ICARUS_FREEZE;
00474                                         G_SetEnemy( self, other );
00475                                         //self->svFlags |= SVF_LOCKEDENEMY; //rwwFIXMEFIXME: proper support for these flags.
00476                                         self->NPC->scriptFlags &= ~(SCF_DONT_FIRE|SCF_CROUCHED|SCF_WALKING|SCF_NO_COMBAT_TALK|SCF_FORCED_MARCH);
00477                                         self->NPC->scriptFlags |= (SCF_CHASE_ENEMIES|SCF_NO_MIND_TRICK);
00478                                         //NOTE: we also stop ICARUS altogether
00479                                         //stop_icarus = qtrue;
00480                                         //rwwFIXMEFIXME: stop icarus?
00481                                         if ( !killPlayerTimer )
00482                                         {
00483                                                 killPlayerTimer = level.time + 10000;
00484                                         }
00485                                 }
00486                         }
00487                 }
00488         }
00489 
00490         SaveNPCGlobals();
00491         SetNPCGlobals( self );
00492 
00493         //Do extra bits
00494         if ( NPCInfo->ignorePain == qfalse )
00495         {
00496                 NPCInfo->confusionTime = 0;//clear any charm or confusion, regardless
00497                 if ( damage != -1 )
00498                 {//-1 == don't play pain anim
00499                         //Set our proper pain animation
00500                         NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc, voiceEvent );
00501                 }
00502                 //Check to take a new enemy
00503                 if ( NPC->enemy != other && NPC != other )
00504                 {//not already mad at them
00505                         NPC_CheckAttacker( other, mod );
00506                 }
00507         }
00508 
00509         //Attempt to run any pain instructions
00510         if ( self->client && self->NPC )
00511         {
00512                 //FIXME: This needs better heuristics perhaps
00513                 if(self->health <= (self->client->ps.stats[STAT_MAX_HEALTH]/3) && G_ActivateBehavior(self, BSET_FLEE) )
00514                 {
00515                 }
00516                 else //if( VALIDSTRING( self->behaviorSet[BSET_PAIN] ) )
00517                 {
00518                         G_ActivateBehavior(self, BSET_PAIN);
00519                 }
00520         }
00521 
00522         //Attempt to fire any paintargets we might have
00523         if( self->paintarget && self->paintarget[0] )
00524         {
00525                 G_UseTargets2(self, other, self->paintarget);
00526         }
00527 
00528         RestoreNPCGlobals();
00529 }

qboolean NPC_ParseParms const char *  NPCName,
gentity_t NPC
 

*WP_BLASTER_PISTOL*/WP_SABER ) //?!

Definition at line 974 of file NPC_stats.c.

References gNPCstats_e::acceleration, gNPCstats_e::aggression, gNPCstats_e::aim, playerState_s::ammo, atoi(), BG_ParseLiteral(), BG_TempAlloc(), BG_TempFree(), saberInfo_t::blade, bState_t, class_t, CLASS_VEHICLE, ClassTable, gentity_s::client, bladeInfo_t::color, COM_BeginParseSession(), COM_ParseExt(), COM_ParseFloat(), COM_ParseInt(), COM_ParseString(), Com_Printf(), Com_sprintf(), CROUCH_MAXS_2, playerState_s::crouchheight, entityShared_t::currentOrigin, playerState_s::customRGBA, DEFAULT_MAXS_2, DEFAULT_MINS_2, gNPC_t::defaultBehavior, gNPCstats_e::earshot, EF2_FLYING, playerState_s::eFlags2, gclient_s::enemyTeam, gNPCstats_e::evasion, playerState_s::fd, forcedata_s::forcePowerLevel, forcedata_s::forcePowerMax, forcedata_s::forcePowersKnown, FP_FIRST, FPTable, gentity_s::fullName, G_ModelIndex(), G_NewString(), G_SetOrigin(), gentity_t, GetIDForString(), gNPCstats_t, renderInfo_s::headPitchRangeDown, renderInfo_s::headPitchRangeUp, renderInfo_s::headYawRangeLeft, renderInfo_s::headYawRangeRight, gNPCstats_e::health, gNPCstats_e::hfov, playerState_s::iModelScale, gNPCstats_e::intelligence, bladeInfo_t::lengthMax, gentity_s::m_pVehicle, Vehicle_s::m_pVehicleInfo, MAX_BLADES, MAX_QPATH, clientPersistant_t::maxHealth, entityShared_t::maxs, memset(), entityShared_t::mins, gentity_s::modelScale, gNPCstats_e::move, NPC, gentity_s::NPC, gclient_s::NPC_class, entityState_s::NPC_class, NPC_Precache(), gentity_s::NPC_type, NPCFile, NPCParms, entityState_s::npcSaber1, entityState_s::npcSaber2, NULL, NUM_BSTATES, NUM_FORCE_POWERS, entityState_s::number, entityState_s::origin, playerState_s::origin, gclient_s::pers, playerMaxs, playerMins, gclient_s::playerTeam, gclient_s::ps, Q_irand(), Q_strcat(), Q_stricmp(), Q_strncpyz(), qboolean, qfalse, qtrue, gentity_s::r, gentity_s::radius, bladeInfo_t::radius, gNPC_t::rank, gNPCstats_e::reactions, gclient_s::renderInfo, renderInfo_t, gNPCstats_e::runSpeed, gentity_s::s, S_COLOR_RED, S_COLOR_YELLOW, gclient_s::saber, saber_colors_t, forcedata_s::saberAnimLevel, saberInfo_t::saberFlags, SCF_ALT_FIRE, gNPC_t::scriptFlags, SetupGameGhoul2Model(), SFL_TWO_HANDED, gNPCstats_e::shootDistance, SkipBracedSection(), SkipRestOfLine(), playerState_s::standheight, STAT_MAX_HEALTH, STAT_WEAPONS, gNPC_t::stats, playerState_s::stats, strcpy(), strstr(), SVF_NO_BASIC_SOUNDS, SVF_NO_COMBAT_SOUNDS, SVF_NO_EXTRA_SOUNDS, entityShared_t::svFlags, team_t, entityState_s::teamowner, TeamTable, renderInfo_s::torsoPitchRangeDown, renderInfo_s::torsoPitchRangeUp, renderInfo_s::torsoYawRangeLeft, renderInfo_s::torsoYawRangeRight, TranslateSaberColor(), trap_LinkEntity(), vehicleInfo_t::type, va(), vec3_t, VectorCopy, VectorSet, gNPCstats_e::vfov, VH_FIGHTER, gNPCstats_e::vigilance, gNPCstats_e::visrange, gNPCstats_e::walkSpeed, playerState_s::weapon, weaponData, WP_NONE, WP_NUM_WEAPONS, WP_RemoveSaber(), WP_SaberParseParms(), WPTable, and gNPCstats_e::yawSpeed.

Referenced by NPC_Spawn_Do().

00975 {
00976         const char      *token;
00977         const char      *value;
00978         const char      *p;
00979         int             n;
00980         float   f;
00981         char    *patch;
00982         char    sound[MAX_QPATH];
00983         char    playerModel[MAX_QPATH];
00984         char    customSkin[MAX_QPATH];
00985         renderInfo_t    *ri = &NPC->client->renderInfo;
00986         gNPCstats_t             *stats = NULL;
00987         qboolean        md3Model = qtrue;
00988         char    surfOff[1024];
00989         char    surfOn[1024];
00990         qboolean parsingPlayer = qfalse;
00991         vec3_t playerMins;
00992         vec3_t playerMaxs;
00993         int npcSaber1 = 0;
00994         int npcSaber2 = 0;
00995 
00996         VectorSet(playerMins, -15, -15, DEFAULT_MINS_2);
00997         VectorSet(playerMaxs, 15, 15, DEFAULT_MAXS_2);
00998 
00999         strcpy(customSkin,"default");
01000         if ( !NPCName || !NPCName[0]) 
01001         {
01002                 NPCName = "Player";
01003         }
01004 
01005         if ( !NPC->s.number && NPC->client != NULL )
01006         {//player, only want certain data
01007                 parsingPlayer = qtrue;
01008         }
01009 
01010         if ( NPC->NPC )
01011         {
01012                 stats = &NPC->NPC->stats;
01013 /*
01014         NPC->NPC->allWeaponOrder[0]     = WP_BRYAR_PISTOL;
01015         NPC->NPC->allWeaponOrder[1]     = WP_SABER;
01016         NPC->NPC->allWeaponOrder[2]     = WP_IMOD;
01017         NPC->NPC->allWeaponOrder[3]     = WP_SCAVENGER_RIFLE;
01018         NPC->NPC->allWeaponOrder[4]     = WP_TRICORDER;
01019         NPC->NPC->allWeaponOrder[6]     = WP_NONE;
01020         NPC->NPC->allWeaponOrder[6]     = WP_NONE;
01021         NPC->NPC->allWeaponOrder[7]     = WP_NONE;
01022 */
01023                 // fill in defaults
01024                 stats->aggression       = 3;
01025                 stats->aim                      = 3;
01026                 stats->earshot          = 1024;
01027                 stats->evasion          = 3;
01028                 stats->hfov                     = 90;
01029                 stats->intelligence     = 3;
01030                 stats->move                     = 3;
01031                 stats->reactions        = 3;
01032                 stats->vfov                     = 60;
01033                 stats->vigilance        = 0.1f;
01034                 stats->visrange         = 1024;
01035 
01036                 stats->health           = 0;
01037 
01038                 stats->yawSpeed         = 90;
01039                 stats->walkSpeed        = 90;
01040                 stats->runSpeed         = 300;
01041                 stats->acceleration     = 15;//Increase/descrease speed this much per frame (20fps)
01042         }
01043         else
01044         {
01045                 stats = NULL;
01046         }
01047 
01048         //Set defaults
01049         //FIXME: should probably put default torso and head models, but what about enemies
01050         //that don't have any- like Stasis?
01051         //Q_strncpyz( ri->headModelName,        DEFAULT_HEADMODEL,  sizeof(ri->headModelName),  qtrue);
01052         //Q_strncpyz( ri->torsoModelName, DEFAULT_TORSOMODEL, sizeof(ri->torsoModelName),       qtrue);
01053         //Q_strncpyz( ri->legsModelName,        DEFAULT_LEGSMODEL,  sizeof(ri->legsModelName),  qtrue);
01054         //FIXME: should we have one for weapon too?
01055         memset( (char *)surfOff, 0, sizeof(surfOff) );
01056         memset( (char *)surfOn, 0, sizeof(surfOn) );
01057         
01058         /*
01059         ri->headYawRangeLeft = 50;
01060         ri->headYawRangeRight = 50;
01061         ri->headPitchRangeUp = 40;
01062         ri->headPitchRangeDown = 50;
01063         ri->torsoYawRangeLeft = 60;
01064         ri->torsoYawRangeRight = 60;
01065         ri->torsoPitchRangeUp = 30;
01066         ri->torsoPitchRangeDown = 70;
01067         */
01068 
01069         ri->headYawRangeLeft = 80;
01070         ri->headYawRangeRight = 80;
01071         ri->headPitchRangeUp = 45;
01072         ri->headPitchRangeDown = 45;
01073         ri->torsoYawRangeLeft = 60;
01074         ri->torsoYawRangeRight = 60;
01075         ri->torsoPitchRangeUp = 30;
01076         ri->torsoPitchRangeDown = 50;
01077 
01078         VectorCopy(playerMins, NPC->r.mins);
01079         VectorCopy(playerMaxs, NPC->r.maxs);
01080         NPC->client->ps.crouchheight = CROUCH_MAXS_2;
01081         NPC->client->ps.standheight = DEFAULT_MAXS_2;
01082 
01083         //rwwFIXMEFIXME: ...
01084         /*
01085         NPC->client->moveType           = MT_RUNJUMP;
01086 
01087         NPC->client->dismemberProbHead = 100;
01088         NPC->client->dismemberProbArms = 100;
01089         NPC->client->dismemberProbHands = 100;
01090         NPC->client->dismemberProbWaist = 100;
01091         NPC->client->dismemberProbLegs = 100;
01092 
01093         NPC->s.modelScale[0] = NPC->s.modelScale[1] = NPC->s.modelScale[2] = 1.0f;
01094         */
01095 
01096         NPC->client->ps.customRGBA[0]=255;
01097         NPC->client->ps.customRGBA[1]=255;
01098         NPC->client->ps.customRGBA[2]=255;
01099         NPC->client->ps.customRGBA[3]=255;
01100 
01101         if ( !Q_stricmp( "random", NPCName ) )
01102         {//Randomly assemble a starfleet guy
01103                 //NPC_BuildRandom( NPC );
01104                 Com_Printf("RANDOM NPC NOT SUPPORTED IN MP\n");
01105                 return qfalse;
01106         }
01107         else
01108         {
01109                 int fp;
01110 
01111                 p = NPCParms;
01112                 COM_BeginParseSession(NPCFile);
01113 
01114                 // look for the right NPC
01115                 while ( p ) 
01116                 {
01117                         token = COM_ParseExt( &p, qtrue );
01118                         if ( token[0] == 0 )
01119                         {
01120                                 return qfalse;
01121                         }
01122 
01123                         if ( !Q_stricmp( token, NPCName ) ) 
01124                         {
01125                                 break;
01126                         }
01127 
01128                         SkipBracedSection( &p );
01129                 }
01130                 if ( !p ) 
01131                 {
01132                         return qfalse;
01133                 }
01134 
01135                 if ( BG_ParseLiteral( &p, "{" ) ) 
01136                 {
01137                         return qfalse;
01138                 }
01139                         
01140                 // parse the NPC info block
01141                 while ( 1 ) 
01142                 {
01143                         token = COM_ParseExt( &p, qtrue );
01144                         if ( !token[0] ) 
01145                         {
01146                                 Com_Printf( S_COLOR_RED"ERROR: unexpected EOF while parsing '%s'\n", NPCName );
01147                                 return qfalse;
01148                         }
01149 
01150                         if ( !Q_stricmp( token, "}" ) ) 
01151                         {
01152                                 break;
01153                         }
01154         //===MODEL PROPERTIES===========================================================
01155                         // custom color
01156                         if ( !Q_stricmp( token, "customRGBA" ) ) 
01157                         {
01158                                 if ( COM_ParseString( &p, &value ) ) 
01159                                 {
01160                                         continue;
01161                                 }
01162                                 if ( !Q_stricmp( value, "random") )
01163                                 {
01164                                         NPC->client->ps.customRGBA[0]=Q_irand(0,255);
01165                                         NPC->client->ps.customRGBA[1]=Q_irand(0,255);
01166                                         NPC->client->ps.customRGBA[2]=Q_irand(0,255);
01167                                         NPC->client->ps.customRGBA[3]=255;
01168                                 } 
01169                                 else 
01170                                 {
01171                                         NPC->client->ps.customRGBA[0]=atoi(value);
01172                                         
01173                                         if ( COM_ParseInt( &p, &n ) ) 
01174                                         {
01175                                                 continue;
01176                                         }
01177                                         NPC->client->ps.customRGBA[1]=n;
01178                                         
01179                                         if ( COM_ParseInt( &p, &n ) ) 
01180                                         {
01181                                                 continue;
01182                                         }
01183                                         NPC->client->ps.customRGBA[2]=n;
01184                                         
01185                                         if ( COM_ParseInt( &p, &n ) ) 
01186                                         {
01187                                                 continue;
01188                                         }
01189                                         NPC->client->ps.customRGBA[3]=n;
01190                                 }
01191                                 continue;
01192                         }
01193 
01194                         // headmodel
01195                         if ( !Q_stricmp( token, "headmodel" ) ) 
01196                         {
01197                                 if ( COM_ParseString( &p, &value ) ) 
01198                                 {
01199                                         continue;
01200                                 }
01201 
01202                                 if(!Q_stricmp("none", value))
01203                                 {
01204                                         //Zero the head clamp range so the torso & legs don't lag behind
01205                                         ri->headYawRangeLeft = 
01206                                         ri->headYawRangeRight = 
01207                                         ri->headPitchRangeUp = 
01208                                         ri->headPitchRangeDown = 0;
01209                                 }
01210                                 continue;
01211                         }
01212                         
01213                         // torsomodel
01214                         if ( !Q_stricmp( token, "torsomodel" ) ) 
01215                         {
01216                                 if ( COM_ParseString( &p, &value ) ) 
01217                                 {
01218                                         continue;
01219                                 }
01220 
01221                                 if(!Q_stricmp("none", value))
01222                                 {
01223                                         //Zero the torso clamp range so the legs don't lag behind
01224                                         ri->torsoYawRangeLeft = 
01225                                         ri->torsoYawRangeRight = 
01226                                         ri->torsoPitchRangeUp = 
01227                                         ri->torsoPitchRangeDown = 0;
01228                                 }
01229                                 continue;
01230                         }
01231 
01232                         // legsmodel
01233                         if ( !Q_stricmp( token, "legsmodel" ) ) 
01234                         {
01235                                 if ( COM_ParseString( &p, &value ) ) 
01236                                 {
01237                                         continue;
01238                                 }
01239                                 /*
01240                                 Q_strncpyz( ri->legsModelName, value, sizeof(ri->legsModelName), qtrue);                        
01241                                 //Need to do this here to get the right index
01242                                 G_ParseAnimFileSet( ri->legsModelName, ri->legsModelName, &ci->animFileIndex );
01243                                 */
01244                                 continue;
01245                         }
01246 
01247                         // playerModel
01248                         if ( !Q_stricmp( token, "playerModel" ) ) 
01249                         {
01250                                 if ( COM_ParseString( &p, &value ) ) 
01251                                 {
01252                                         continue;
01253                                 }
01254                                 Q_strncpyz( playerModel, value, sizeof(playerModel));                   
01255                                 md3Model = qfalse;
01256                                 continue;
01257                         }
01258                         
01259                         // customSkin
01260                         if ( !Q_stricmp( token, "customSkin" ) ) 
01261                         {
01262                                 if ( COM_ParseString( &p, &value ) ) 
01263                                 {
01264                                         continue;
01265                                 }
01266                                 Q_strncpyz( customSkin, value, sizeof(customSkin));                     
01267                                 continue;
01268                         }
01269 
01270                         // surfOff
01271                         if ( !Q_stricmp( token, "surfOff" ) ) 
01272                         {
01273                                 if ( COM_ParseString( &p, &value ) ) 
01274                                 {
01275                                         continue;
01276                                 }
01277                                 if ( surfOff[0] )
01278                                 {
01279                                         Q_strcat( (char *)surfOff, sizeof(surfOff), "," );
01280                                         Q_strcat( (char *)surfOff, sizeof(surfOff), value );
01281                                 }
01282                                 else
01283                                 {
01284                                         Q_strncpyz( surfOff, value, sizeof(surfOff));
01285                                 }
01286                                 continue;
01287                         }
01288                         
01289                         // surfOn
01290                         if ( !Q_stricmp( token, "surfOn" ) ) 
01291                         {
01292                                 if ( COM_ParseString( &p, &value ) ) 
01293                                 {
01294                                         continue;
01295                                 }
01296                                 if ( surfOn[0] )
01297                                 {
01298                                         Q_strcat( (char *)surfOn, sizeof(surfOn), "," );
01299                                         Q_strcat( (char *)surfOn, sizeof(surfOn), value );
01300                                 }
01301                                 else
01302                                 {
01303                                         Q_strncpyz( surfOn, value, sizeof(surfOn));
01304                                 }
01305                                 continue;
01306                         }
01307                         
01308                         //headYawRangeLeft
01309                         if ( !Q_stricmp( token, "headYawRangeLeft" ) ) 
01310                         {
01311                                 if ( COM_ParseInt( &p, &n ) ) 
01312                                 {
01313                                         SkipRestOfLine( &p );
01314                                         continue;
01315                                 }
01316                                 if ( n < 0 ) 
01317                                 {
01318                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01319                                         continue;
01320                                 }
01321                                 ri->headYawRangeLeft = n;
01322                                 continue;
01323                         }
01324 
01325                         //headYawRangeRight
01326                         if ( !Q_stricmp( token, "headYawRangeRight" ) ) 
01327                         {
01328                                 if ( COM_ParseInt( &p, &n ) ) 
01329                                 {
01330                                         SkipRestOfLine( &p );
01331                                         continue;
01332                                 }
01333                                 if ( n < 0 ) 
01334                                 {
01335                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01336                                         continue;
01337                                 }
01338                                 ri->headYawRangeRight = n;
01339                                 continue;
01340                         }
01341 
01342                         //headPitchRangeUp
01343                         if ( !Q_stricmp( token, "headPitchRangeUp" ) ) 
01344                         {
01345                                 if ( COM_ParseInt( &p, &n ) ) 
01346                                 {
01347                                         SkipRestOfLine( &p );
01348                                         continue;
01349                                 }
01350                                 if ( n < 0 ) 
01351                                 {
01352                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01353                                         continue;
01354                                 }
01355                                 ri->headPitchRangeUp = n;
01356                                 continue;
01357                         }
01358                         
01359                         //headPitchRangeDown
01360                         if ( !Q_stricmp( token, "headPitchRangeDown" ) ) 
01361                         {
01362                                 if ( COM_ParseInt( &p, &n ) ) 
01363                                 {
01364                                         SkipRestOfLine( &p );
01365                                         continue;
01366                                 }
01367                                 if ( n < 0 ) 
01368                                 {
01369                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01370                                         continue;
01371                                 }
01372                                 ri->headPitchRangeDown = n;
01373                                 continue;
01374                         }
01375 
01376                         //torsoYawRangeLeft
01377                         if ( !Q_stricmp( token, "torsoYawRangeLeft" ) ) 
01378                         {
01379                                 if ( COM_ParseInt( &p, &n ) ) 
01380                                 {
01381                                         SkipRestOfLine( &p );
01382                                         continue;
01383                                 }
01384                                 if ( n < 0 ) 
01385                                 {
01386                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01387                                         continue;
01388                                 }
01389                                 ri->torsoYawRangeLeft = n;
01390                                 continue;
01391                         }
01392 
01393                         //torsoYawRangeRight
01394                         if ( !Q_stricmp( token, "torsoYawRangeRight" ) ) 
01395                         {
01396                                 if ( COM_ParseInt( &p, &n ) ) 
01397                                 {
01398                                         SkipRestOfLine( &p );
01399                                         continue;
01400                                 }
01401                                 if ( n < 0 ) 
01402                                 {
01403                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01404                                         continue;
01405                                 }
01406                                 ri->torsoYawRangeRight = n;
01407                                 continue;
01408                         }
01409 
01410                         //torsoPitchRangeUp
01411                         if ( !Q_stricmp( token, "torsoPitchRangeUp" ) ) 
01412                         {
01413                                 if ( COM_ParseInt( &p, &n ) ) 
01414                                 {
01415                                         SkipRestOfLine( &p );
01416                                         continue;
01417                                 }
01418                                 if ( n < 0 ) 
01419                                 {
01420                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01421                                         continue;
01422                                 }
01423                                 ri->torsoPitchRangeUp = n;
01424                                 continue;
01425                         }
01426 
01427                         //torsoPitchRangeDown
01428                         if ( !Q_stricmp( token, "torsoPitchRangeDown" ) ) 
01429                         {
01430                                 if ( COM_ParseInt( &p, &n ) ) 
01431                                 {
01432                                         SkipRestOfLine( &p );
01433                                         continue;
01434                                 }
01435                                 if ( n < 0 ) 
01436                                 {
01437                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01438                                         continue;
01439                                 }
01440                                 ri->torsoPitchRangeDown = n;
01441                                 continue;
01442                         }
01443 
01444                         // Uniform XYZ scale
01445                         if ( !Q_stricmp( token, "scale" ) ) 
01446                         {
01447                                 if ( COM_ParseInt( &p, &n ) ) 
01448                                 {
01449                                         SkipRestOfLine( &p );
01450                                         continue;
01451                                 }
01452                                 if ( n < 0 ) 
01453                                 {
01454                                         Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01455                                         continue;
01456                                 }
01457                                 if (n != 100)
01458                                 {
01459                                         NPC->client->ps.iModelScale = n; //so the client knows
01460                                         if (n >= 1024)
01461                                         {
01462                                                 Com_Printf("WARNING: MP does not support scaling up to or over 1024%\n");
01463                                                 n = 1023;
01464                                         }
01465 
01466                                         NPC->modelScale[0] = NPC->modelScale[1] = NPC->modelScale[2] = n/100.0f;
01467                                 }
01468                                 continue;
01469                         }
01470 
01471                         //X scale
01472                         if ( !Q_stricmp( token, "scaleX" ) ) 
01473                         {
01474                                 if ( COM_ParseInt( &p, &n ) ) 
01475                                 {
01476                                         SkipRestOfLine( &p );
01477                                         continue;
01478                                 }
01479                                 if ( n < 0 ) 
01480                                 {
01481                                         Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01482                                         continue;
01483                                 }
01484                                 if (n != 100)
01485                                 {
01486                                         Com_Printf("MP doesn't support xyz scaling, use 'scale'.\n");
01487                                         //NPC->s.modelScale[0] = n/100.0f;
01488                                 }
01489                                 continue;
01490                         }
01491 
01492                         //Y scale
01493                         if ( !Q_stricmp( token, "scaleY" ) ) 
01494                         {
01495                                 if ( COM_ParseInt( &p, &n ) ) 
01496                                 {
01497                                         SkipRestOfLine( &p );
01498                                         continue;
01499                                 }
01500                                 if ( n < 0 ) 
01501                                 {
01502                                         Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01503                                         continue;
01504                                 }
01505                                 if (n != 100)
01506                                 {
01507                                         Com_Printf("MP doesn't support xyz scaling, use 'scale'.\n");
01508                                         //NPC->s.modelScale[1] = n/100.0f;
01509                                 }
01510                                 continue;
01511                         }
01512 
01513                         //Z scale
01514                         if ( !Q_stricmp( token, "scaleZ" ) ) 
01515                         {
01516                                 if ( COM_ParseInt( &p, &n ) ) 
01517                                 {
01518                                         SkipRestOfLine( &p );
01519                                         continue;
01520                                 }
01521                                 if ( n < 0 ) 
01522                                 {
01523                                         Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01524                                         continue;
01525                                 }
01526                                 if (n != 100)
01527                                 {
01528                                         Com_Printf("MP doesn't support xyz scaling, use 'scale'.\n");
01529                                 //      NPC->s.modelScale[2] = n/100.0f;
01530                                 }
01531                                 continue;
01532                         }
01533 
01534         //===AI STATS=====================================================================
01535                         if ( !parsingPlayer )
01536                         {
01537                                 // aggression
01538                                 if ( !Q_stricmp( token, "aggression" ) ) {
01539                                         if ( COM_ParseInt( &p, &n ) ) {
01540                                                 SkipRestOfLine( &p );
01541                                                 continue;
01542                                         }
01543                                         if ( n < 1 || n > 5 ) {
01544                                                 Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01545                                                 continue;
01546                                         }
01547                                         if ( NPC->NPC )
01548                                         {
01549                                                 stats->aggression = n;
01550                                         }
01551                                         continue;
01552                                 }
01553 
01554                                 // aim
01555                                 if ( !Q_stricmp( token, "aim" ) ) {
01556                                         if ( COM_ParseInt( &p, &n ) ) {
01557                                                 SkipRestOfLine( &p );
01558                                                 continue;
01559                                         }
01560                                         if ( n < 1 || n > 5 ) {
01561                                                 Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01562                                                 continue;
01563                                         }
01564                                         if ( NPC->NPC )
01565                                         {
01566                                                 stats->aim = n;
01567                                         }
01568                                         continue;
01569                                 }
01570 
01571                                 // earshot
01572                                 if ( !Q_stricmp( token, "earshot" ) ) {
01573                                         if ( COM_ParseFloat( &p, &f ) ) {
01574                                                 SkipRestOfLine( &p );
01575                                                 continue;
01576                                         }
01577                                         if ( f < 0.0f ) 
01578                                         {
01579                                                 Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01580                                                 continue;
01581                                         }
01582                                         if ( NPC->NPC )
01583                                         {
01584                                                 stats->earshot = f;
01585                                         }
01586                                         continue;
01587                                 }
01588 
01589                                 // evasion
01590                                 if ( !Q_stricmp( token, "evasion" ) ) 
01591                                 {
01592                                         if ( COM_ParseInt( &p, &n ) ) 
01593                                         {
01594                                                 SkipRestOfLine( &p );
01595                                                 continue;
01596                                         }
01597                                         if ( n < 1 || n > 5 ) 
01598                                         {
01599                                                 Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01600                                                 continue;
01601                                         }
01602                                         if ( NPC->NPC )
01603                                         {
01604                                                 stats->evasion = n;
01605                                         }
01606                                         continue;
01607                                 }
01608 
01609                                 // hfov
01610                                 if ( !Q_stricmp( token, "hfov" ) ) {
01611                                         if ( COM_ParseInt( &p, &n ) ) {
01612                                                 SkipRestOfLine( &p );
01613                                                 continue;
01614                                         }
01615                                         if ( n < 30 || n > 180 ) {
01616                                                 Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01617                                                 continue;
01618                                         }
01619                                         if ( NPC->NPC )
01620                                         {
01621                                                 stats->hfov = n;// / 2; //FIXME: Why was this being done?!
01622                                         }
01623                                         continue;
01624                                 }
01625 
01626                                 // intelligence
01627                                 if ( !Q_stricmp( token, "intelligence" ) ) {
01628                                         if ( COM_ParseInt( &p, &n ) ) {
01629                                                 SkipRestOfLine( &p );
01630                                                 continue;
01631                                         }
01632                                         if ( n < 1 || n > 5 ) {
01633                                                 Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01634                                                 continue;
01635                                         }
01636                                         if ( NPC->NPC )
01637                                         {
01638                                                 stats->intelligence = n;
01639                                         }
01640                                         continue;
01641                                 }
01642                                 
01643                                 // move
01644                                 if ( !Q_stricmp( token, "move" ) ) {
01645                                         if ( COM_ParseInt( &p, &n ) ) {
01646                                                 SkipRestOfLine( &p );
01647                                                 continue;
01648                                         }
01649                                         if ( n < 1 || n > 5 ) {
01650                                                 Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01651                                                 continue;
01652                                         }
01653                                         if ( NPC->NPC )
01654                                         {
01655                                                 stats->move = n;
01656                                         }
01657                                         continue;
01658                                 }
01659 
01660                                 // reactions
01661                                 if ( !Q_stricmp( token, "reactions" ) ) {
01662                                         if ( COM_ParseInt( &p, &n ) ) {
01663                                                 SkipRestOfLine( &p );
01664                                                 continue;
01665                                         }
01666                                         if ( n < 1 || n > 5 ) {
01667                                                 Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01668                                                 continue;
01669                                         }
01670                                         if ( NPC->NPC )
01671                                         {
01672                                                 stats->reactions = n;
01673                                         }
01674                                         continue;
01675                                 }
01676 
01677                                 // shootDistance
01678                                 if ( !Q_stricmp( token, "shootDistance" ) ) {
01679                                         if ( COM_ParseFloat( &p, &f ) ) {
01680                                                 SkipRestOfLine( &p );
01681                                                 continue;
01682                                         }
01683                                         if ( f < 0.0f ) 
01684                                         {
01685                                                 Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01686                                                 continue;
01687                                         }
01688                                         if ( NPC->NPC )
01689                                         {
01690                                                 stats->shootDistance = f;
01691                                         }
01692                                         continue;
01693                                 }
01694 
01695                                 // vfov
01696                                 if ( !Q_stricmp( token, "vfov" ) ) {
01697                                         if ( COM_ParseInt( &p, &n ) ) {
01698                                                 SkipRestOfLine( &p );
01699                                                 continue;
01700                                         }
01701                                         if ( n < 30 || n > 180 ) {
01702                                                 Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
01703                                                 continue;
01704                                         }
01705                                         if ( NPC->NPC )
01706                                         {
01707                                                 stats->vfov = n / 2;
01708                                         }
01709                                         continue;
01710                                 }
01711 
01712                                 // vigilance
01713                                 if ( !Q_stricmp( token, "vigilance" ) ) {
01714                                         if ( COM_ParseFloat( &p, &f ) ) {
01715                                                 SkipRestOfLine( &p );
01716                                                 continue;
01717                                         }
01718                                         if ( f < 0.0f ) 
01719                                         {
01720                                                 Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01721                                                 continue;
01722                                         }
01723                                         if ( NPC->NPC )
01724                                         {
01725                                                 stats->vigilance = f;
01726                                         }
01727                                         continue;
01728                                 }
01729 
01730                                 // visrange
01731                                 if ( !Q_stricmp( token, "visrange" ) ) {
01732                                         if ( COM_ParseFloat( &p, &f ) ) {
01733                                                 SkipRestOfLine( &p );
01734                                                 continue;
01735                                         }
01736                                         if ( f < 0.0f ) 
01737                                         {
01738                                                 Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01739                                                 continue;
01740                                         }
01741                                         if ( NPC->NPC )
01742                                         {
01743                                                 stats->visrange = f;
01744                                         }
01745                                         continue;
01746                                 }
01747 
01748                                 // race
01749                 //              if ( !Q_stricmp( token, "race" ) ) 
01750                 //              {
01751                 //                      if ( COM_ParseString( &p, &value ) ) 
01752                 //                      {
01753                 //                              continue;
01754                 //                      }
01755                 //                      NPC->client->race = TranslateRaceName(value);
01756                 //                      continue;
01757                 //              }
01758 
01759                                 // rank
01760                                 if ( !Q_stricmp( token, "rank" ) ) 
01761                                 {
01762                                         if ( COM_ParseString( &p, &value ) ) 
01763                                         {
01764                                                 continue;
01765                                         }
01766                                         if ( NPC->NPC )
01767                                         {
01768                                                 NPC->NPC->rank = TranslateRankName(value);
01769                                         }
01770                                         continue;
01771                                 }
01772                         }
01773 
01774                         // health
01775                         if ( !Q_stricmp( token, "health" ) ) 
01776                         {
01777                                 if ( COM_ParseInt( &p, &n ) ) 
01778                                 {
01779                                         SkipRestOfLine( &p );
01780                                         continue;
01781                                 }
01782                                 if ( n < 0 ) 
01783                                 {
01784                                         Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
01785                                         continue;
01786                                 }
01787                                 if ( NPC->NPC )
01788                                 {
01789                                         stats->health = n;
01790                                 }
01791                                 else if ( parsingPlayer )
01792                                 {
01793                                         NPC->client->ps.stats[STAT_MAX_HEALTH] = NPC->client->pers.maxHealth = n;
01794                                 }
01795                                 continue;
01796                         }
01797 
01798                         // fullName
01799                         if ( !Q_stricmp( token, "fullName" ) ) 
01800                         {
01801                                 if ( COM_ParseString( &p, &value ) ) 
01802                                 {
01803                                         continue;
01804                                 }
01805                                 NPC->fullName = G_NewString(value);
01806                                 continue;
01807                         }
01808 
01809                         // playerTeam
01810                         if ( !Q_stricmp( token, "playerTeam" ) ) 
01811                         {
01812                                 char tk[4096]; //rww - hackilicious!
01813 
01814                                 if ( COM_ParseString( &p, &value ) ) 
01815                                 {
01816                                         continue;
01817                                 }
01818                                 Com_sprintf(tk, sizeof(tk), "NPC%s", token);
01819                                 NPC->client->playerTeam = NPC->s.teamowner = (team_t)GetIDForString( TeamTable, tk );//TranslateTeamName(value);
01820                                 continue;
01821                         }
01822 
01823                         // enemyTeam
01824                         if ( !Q_stricmp( token, "enemyTeam" ) ) 
01825                         {
01826                                 char tk[4096]; //rww - hackilicious!
01827 
01828                                 if ( COM_ParseString( &p, &value ) ) 
01829                                 {
01830                                         continue;
01831                                 }
01832                                 Com_sprintf(tk, sizeof(tk), "NPC%s", token);
01833                                 NPC->client->enemyTeam = (team_t)GetIDForString( TeamTable, tk );//TranslateTeamName(value);
01834                                 continue;
01835                         }
01836 
01837                         // class
01838                         if ( !Q_stricmp( token, "class" ) ) 
01839                         {
01840                                 if ( COM_ParseString( &p, &value ) ) 
01841                                 {
01842                                         continue;
01843                                 }
01844                                 NPC->client->NPC_class = (class_t)GetIDForString( ClassTable, value );
01845                                 NPC->s.NPC_class = NPC->client->NPC_class; //we actually only need this value now, but at the moment I don't feel like changing the 200+ references to client->NPC_class.
01846 
01847                                 // No md3's for vehicles.
01848                                 if ( NPC->client->NPC_class == CLASS_VEHICLE )
01849                                 {
01850                                         if ( !NPC->m_pVehicle )
01851                                         {//you didn't spawn this guy right!
01852                                                 Com_Printf ( S_COLOR_RED "ERROR: Tried to spawn a vehicle NPC (%s) without using NPC_Vehicle or 'NPC spawn vehicle <vehiclename>'!!!  Bad, bad, bad!  Shame on you!\n", NPCName );
01853                                                 return qfalse;
01854                                         }
01855                                         md3Model = qfalse;
01856                                 }
01857 
01858                                 continue;
01859                         }
01860 
01861                         // dismemberment probability for head
01862                         if ( !Q_stricmp( token, "dismemberProbHead" ) ) {
01863                                 if ( COM_ParseInt( &p, &n ) ) {
01864                                         SkipRestOfLine( &p );
01865                                         continue;
01866                                 }
01867                                 if ( n < 0 ) 
01868                                 {
01869                                         Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01870                                         continue;
01871                                 }
01872                                 if ( NPC->NPC )
01873                                 {
01874                                 //      NPC->client->dismemberProbHead = n;
01875                                         //rwwFIXMEFIXME: support for this?
01876                                 }
01877                                 continue;
01878                         }
01879 
01880                         // dismemberment probability for arms
01881                         if ( !Q_stricmp( token, "dismemberProbArms" ) ) {
01882                                 if ( COM_ParseInt( &p, &n ) ) {
01883                                         SkipRestOfLine( &p );
01884                                         continue;
01885                                 }
01886                                 if ( n < 0 ) 
01887                                 {
01888                                         Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01889                                         continue;
01890                                 }
01891                                 if ( NPC->NPC )
01892                                 {
01893                                 //      NPC->client->dismemberProbArms = n;
01894                                 }
01895                                 continue;
01896                         }
01897 
01898                         // dismemberment probability for hands
01899                         if ( !Q_stricmp( token, "dismemberProbHands" ) ) {
01900                                 if ( COM_ParseInt( &p, &n ) ) {
01901                                         SkipRestOfLine( &p );
01902                                         continue;
01903                                 }
01904                                 if ( n < 0 ) 
01905                                 {
01906                                         Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01907                                         continue;
01908                                 }
01909                                 if ( NPC->NPC )
01910                                 {
01911                                 //      NPC->client->dismemberProbHands = n;
01912                                 }
01913                                 continue;
01914                         }
01915 
01916                         // dismemberment probability for waist
01917                         if ( !Q_stricmp( token, "dismemberProbWaist" ) ) {
01918                                 if ( COM_ParseInt( &p, &n ) ) {
01919                                         SkipRestOfLine( &p );
01920                                         continue;
01921                                 }
01922                                 if ( n < 0 ) 
01923                                 {
01924                                         Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01925                                         continue;
01926                                 }
01927                                 if ( NPC->NPC )
01928                                 {
01929                                 //      NPC->client->dismemberProbWaist = n;
01930                                 }
01931                                 continue;
01932                         }
01933 
01934                         // dismemberment probability for legs
01935                         if ( !Q_stricmp( token, "dismemberProbLegs" ) ) {
01936                                 if ( COM_ParseInt( &p, &n ) ) {
01937                                         SkipRestOfLine( &p );
01938                                         continue;
01939                                 }
01940                                 if ( n < 0 ) 
01941                                 {
01942                                         Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
01943                                         continue;
01944                                 }
01945                                 if ( NPC->NPC )
01946                                 {
01947                                 //      NPC->client->dismemberProbLegs = n;
01948                                 }
01949                                 continue;
01950                         }
01951 
01952         //===MOVEMENT STATS============================================================
01953                         
01954                         if ( !Q_stricmp( token, "width" ) ) 
01955                         {
01956                                 if ( COM_ParseInt( &p, &n ) ) 
01957                                 {
01958                                         continue;
01959                                 }
01960 
01961                                 NPC->r.mins[0] = NPC->r.mins[1] = -n;
01962                                 NPC->r.maxs[0] = NPC->r.maxs[1] = n;
01963                                 continue;
01964                         }
01965 
01966                         if ( !Q_stricmp( token, "height" ) ) 
01967                         {
01968                                 if ( COM_ParseInt( &p, &n ) ) 
01969                                 {
01970                                         continue;
01971                                 }
01972                                 if ( NPC->client->NPC_class == CLASS_VEHICLE
01973                                         && NPC->m_pVehicle
01974                                         && NPC->m_pVehicle->m_pVehicleInfo
01975                                         && NPC->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
01976                                 {//a flying vehicle's origin must be centered in bbox and it should spawn on the ground
01977                                         //trace_t               tr;
01978                                         //vec3_t                bottom;
01979                                         //float         adjust = 32.0f;
01980                                         NPC->r.maxs[2] = NPC->client->ps.standheight = (n/2.0f);
01981                                         NPC->r.mins[2] = -NPC->r.maxs[2];
01982                                         NPC->s.origin[2] += (DEFAULT_MINS_2-NPC->r.mins[2])+0.125f;
01983                                         VectorCopy(NPC->s.origin, NPC->client->ps.origin);
01984                                         VectorCopy(NPC->s.origin, NPC->r.currentOrigin);
01985                                         G_SetOrigin( NPC, NPC->s.origin );
01986                                         trap_LinkEntity(NPC);
01987                                         //now trace down
01988                                         /*
01989                                         VectorCopy( NPC->s.origin, bottom );
01990                                         bottom[2] -= adjust;
01991                                         trap_Trace( &tr, NPC->s.origin, NPC->r.mins, NPC->r.maxs, bottom, NPC->s.number, MASK_NPCSOLID );
01992                                         if ( !tr.allsolid && !tr.startsolid )
01993                                         {
01994                                                 G_SetOrigin( NPC, tr.endpos );
01995                                                 trap_LinkEntity(NPC);
01996                                         }
01997                                         */
01998                                 }
01999                                 else
02000                                 {
02001                                         NPC->r.mins[2] = DEFAULT_MINS_2;//Cannot change
02002                                         NPC->r.maxs[2] = NPC->client->ps.standheight = n + DEFAULT_MINS_2;
02003                                 }
02004                                 NPC->radius = n;
02005                                 continue;
02006                         }
02007 
02008                         if ( !Q_stricmp( token, "crouchheight" ) ) 
02009                         {
02010                                 if ( COM_ParseInt( &p, &n ) ) 
02011                                 {
02012                                         continue;
02013                                 }
02014 
02015                                 NPC->client->ps.crouchheight = n + DEFAULT_MINS_2;
02016                                 continue;
02017                         }
02018 
02019                         if ( !parsingPlayer )
02020                         {
02021                                 if ( !Q_stricmp( token, "movetype" ) ) 
02022                                 {
02023                                         if ( COM_ParseString( &p, &value ) ) 
02024                                         {
02025                                                 continue;
02026                                         }
02027                                         if ( Q_stricmp( "flyswim", value ) == 0 )
02028                                         {
02029                                                 NPC->client->ps.eFlags2 |= EF2_FLYING;
02030                                         }
02031                                         //NPC->client->moveType = (movetype_t)MoveTypeNameToEnum(value);
02032                                         //rwwFIXMEFIXME: support for movetypes
02033                                         continue;
02034                                 }
02035                                         
02036                                 // yawSpeed
02037                                 if ( !Q_stricmp( token, "yawSpeed" ) ) {
02038                                         if ( COM_ParseInt( &p, &n ) ) {
02039                                                 SkipRestOfLine( &p );
02040                                                 continue;
02041                                         }
02042                                         if ( n <= 0) {
02043                                                 Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
02044                                                 continue;
02045                                         }
02046                                         if ( NPC->NPC )
02047                                         {
02048                                                 stats->yawSpeed = ((float)(n));
02049                                         }
02050                                         continue;
02051                                 }
02052 
02053                                 // walkSpeed
02054                                 if ( !Q_stricmp( token, "walkSpeed" ) ) 
02055                                 {
02056                                         if ( COM_ParseInt( &p, &n ) ) 
02057                                         {
02058                                                 SkipRestOfLine( &p );
02059                                                 continue;
02060                                         }
02061                                         if ( n < 0 ) 
02062                                         {
02063                                                 Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
02064                                                 continue;
02065                                         }
02066                                         if ( NPC->NPC )
02067                                         {
02068                                                 stats->walkSpeed = n;
02069                                         }
02070                                         continue;
02071                                 }
02072                                 
02073                                 //runSpeed
02074                                 if ( !Q_stricmp( token, "runSpeed" ) ) 
02075                                 {
02076                                         if ( COM_ParseInt( &p, &n ) ) 
02077                                         {
02078                                                 SkipRestOfLine( &p );
02079                                                 continue;
02080                                         }
02081                                         if ( n < 0 ) 
02082                                         {
02083                                                 Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
02084                                                 continue;
02085                                         }
02086                                         if ( NPC->NPC )
02087                                         {
02088                                                 stats->runSpeed = n;
02089                                         }
02090                                         continue;
02091                                 }
02092 
02093                                 //acceleration
02094                                 if ( !Q_stricmp( token, "acceleration" ) ) 
02095                                 {
02096                                         if ( COM_ParseInt( &p, &n ) ) 
02097                                         {
02098                                                 SkipRestOfLine( &p );
02099                                                 continue;
02100                                         }
02101                                         if ( n < 0 ) 
02102                                         {
02103                                                 Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
02104                                                 continue;
02105                                         }
02106                                         if ( NPC->NPC )
02107                                         {
02108                                                 stats->acceleration = n;
02109                                         }
02110                                         continue;
02111                                 }
02112                                 //sex - skip in MP
02113                                 if ( !Q_stricmp( token, "sex" ) ) 
02114                                 {
02115                                         SkipRestOfLine( &p );
02116                                         continue;
02117                                 }
02118 //===MISC===============================================================================
02119                                 // default behavior
02120                                 if ( !Q_stricmp( token, "behavior" ) ) 
02121                                 {
02122                                         if ( COM_ParseInt( &p, &n ) ) 
02123                                         {
02124                                                 SkipRestOfLine( &p );
02125                                                 continue;
02126                                         }
02127                                         if ( n < BS_DEFAULT || n >= NUM_BSTATES ) 
02128                                         {
02129                                                 Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
02130                                                 continue;
02131                                         }
02132                                         if ( NPC->NPC )
02133                                         {
02134                                                 NPC->NPC->defaultBehavior = (bState_t)(n);
02135                                         }
02136                                         continue;
02137                                 }
02138                         }
02139 
02140                         // snd
02141                         if ( !Q_stricmp( token, "snd" ) ) 
02142                         {
02143                                 if ( COM_ParseString( &p, &value ) ) 
02144                                 {
02145                                         continue;
02146                                 }
02147                                 if ( !(NPC->r.svFlags&SVF_NO_BASIC_SOUNDS) )
02148                                 {
02149                                         //FIXME: store this in some sound field or parse in the soundTable like the animTable...
02150                                         Q_strncpyz( sound, value, sizeof( sound ) );
02151                                         patch = strstr( sound, "/" );
02152                                         if ( patch ) 
02153                                         {
02154                                                 *patch = 0;
02155                                         }
02156                                 //      ci->customBasicSoundDir = G_NewString( sound );
02157                                         //rwwFIXMEFIXME: Hooray for violating client server rules
02158                                 }
02159                                 continue;
02160                         }
02161 
02162                         // sndcombat
02163                         if ( !Q_stricmp( token, "sndcombat" ) ) 
02164                         {
02165                                 if ( COM_ParseString( &p, &value ) ) 
02166                                 {
02167                                         continue;
02168                                 }
02169                                 if ( !(NPC->r.svFlags&SVF_NO_COMBAT_SOUNDS) )
02170                                 {
02171                                         //FIXME: store this in some sound field or parse in the soundTable like the animTable...
02172                                         Q_strncpyz( sound, value, sizeof( sound ) );
02173                                         patch = strstr( sound, "/" );
02174                                         if ( patch ) 
02175                                         {
02176                                                 *patch = 0;
02177                                         }
02178                                 //      ci->customCombatSoundDir = G_NewString( sound );
02179                                 }
02180                                 continue;
02181                         }
02182                         
02183                         // sndextra
02184                         if ( !Q_stricmp( token, "sndextra" ) ) 
02185                         {
02186                                 if ( COM_ParseString( &p, &value ) ) 
02187                                 {
02188                                         continue;
02189                                 }
02190                                 if ( !(NPC->r.svFlags&SVF_NO_EXTRA_SOUNDS) )
02191                                 {
02192                                         //FIXME: store this in some sound field or parse in the soundTable like the animTable...
02193                                         Q_strncpyz( sound, value, sizeof( sound ) );
02194                                         patch = strstr( sound, "/" );
02195                                         if ( patch ) 
02196                                         {
02197                                                 *patch = 0;
02198                                         }
02199                                 //      ci->customExtraSoundDir = G_NewString( sound );
02200                                 }
02201                                 continue;
02202                         }
02203 
02204                         // sndjedi
02205                         if ( !Q_stricmp( token, "sndjedi" ) ) 
02206                         {
02207                                 if ( COM_ParseString( &p, &value ) ) 
02208                                 {
02209                                         continue;
02210                                 }
02211                                 if ( !(NPC->r.svFlags&SVF_NO_EXTRA_SOUNDS) )
02212                                 {
02213                                         //FIXME: store this in some sound field or parse in the soundTable like the animTable...
02214                                         Q_strncpyz( sound, value, sizeof( sound ) );
02215                                         patch = strstr( sound, "/" );
02216                                         if ( patch ) 
02217                                         {
02218                                                 *patch = 0;
02219                                         }
02220                                         //ci->customJediSoundDir = G_NewString( sound );
02221                                 }
02222                                 continue;
02223                         }
02224 
02225                         //New NPC/jedi stats:
02226                         //starting weapon
02227                         if ( !Q_stricmp( token, "weapon" ) ) 
02228                         {
02229                                 int weap;
02230 
02231                                 if ( COM_ParseString( &p, &value ) ) 
02232                                 {
02233                                         continue;
02234                                 }
02235                                 //FIXME: need to precache the weapon, too?  (in above func)
02236                                 weap = GetIDForString( WPTable, value );
02237                                 if ( weap >= WP_NONE && weap <= WP_NUM_WEAPONS )
02238                                 {
02239                                         NPC->client->ps.weapon = weap;
02240                                         NPC->client->ps.stats[STAT_WEAPONS] |= ( 1 << NPC->client->ps.weapon );
02241                                         if ( weap > WP_NONE )
02242                                         {
02243                                         //      RegisterItem( FindItemForWeapon( (weapon_t)(NPC->client->ps.weapon) ) );        //precache the weapon
02244                                                 NPC->client->ps.ammo[weaponData[NPC->client->ps.weapon].ammoIndex] = 100;//FIXME: max ammo!
02245                                         }
02246                                 }
02247                                 continue;
02248                         }
02249                         
02250                         if ( !parsingPlayer )
02251                         {
02252                                 //altFire
02253                                 if ( !Q_stricmp( token, "altFire" ) ) 
02254                                 {
02255                                         if ( COM_ParseInt( &p, &n ) ) 
02256                                         {
02257                                                 SkipRestOfLine( &p );
02258                                                 continue;
02259                                         }
02260                                         if ( NPC->NPC )
02261                                         {
02262                                                 if ( n != 0 )
02263                                                 {
02264                                                         NPC->NPC->scriptFlags |= SCF_ALT_FIRE;
02265                                                 }
02266                                         }
02267                                         continue;
02268                                 }
02269                                 //Other unique behaviors/numbers that are currently hardcoded?
02270                         }
02271 
02272                         //force powers
02273                         fp = GetIDForString( FPTable, token );
02274                         if ( fp >= FP_FIRST && fp < NUM_FORCE_POWERS )
02275                         {
02276                                 if ( COM_ParseInt( &p, &n ) ) 
02277                                 {
02278                                         SkipRestOfLine( &p );
02279                                         continue;
02280                                 }
02281                                 //FIXME: need to precache the fx, too?  (in above func)
02282                                 //cap
02283                                 if ( n > 5 )
02284                                 {
02285                                         n = 5;
02286                                 }
02287                                 else if ( n < 0 )
02288                                 {
02289                                         n = 0;
02290                                 }
02291                                 if ( n )
02292                                 {//set
02293                                         NPC->client->ps.fd.forcePowersKnown |= ( 1 << fp );
02294                                 }
02295                                 else
02296                                 {//clear
02297                                         NPC->client->ps.fd.forcePowersKnown &= ~( 1 << fp );
02298                                 }
02299                                 NPC->client->ps.fd.forcePowerLevel[fp] = n;
02300                                 continue;
02301                         }
02302 
02303                         //max force power
02304                         if ( !Q_stricmp( token, "forcePowerMax" ) ) 
02305                         {
02306                                 if ( COM_ParseInt( &p, &n ) ) 
02307                                 {
02308                                         SkipRestOfLine( &p );
02309                                         continue;
02310                                 }
02311                                 NPC->client->ps.fd.forcePowerMax = n;
02312                                 continue;
02313                         }
02314 
02315                         //force regen rate - default is 100ms
02316                         if ( !Q_stricmp( token, "forceRegenRate" ) ) 
02317                         {
02318                                 if ( COM_ParseInt( &p, &n ) ) 
02319                                 {
02320                                         SkipRestOfLine( &p );
02321                                         continue;
02322                                 }
02323                                 //NPC->client->ps.forcePowerRegenRate = n;
02324                                 //rwwFIXMEFIXME: support this?
02325                                 continue;
02326                         }
02327 
02328                         //force regen amount - default is 1 (points per second)
02329                         if ( !Q_stricmp( token, "forceRegenAmount" ) ) 
02330                         {
02331                                 if ( COM_ParseInt( &p, &n ) ) 
02332                                 {
02333                                         SkipRestOfLine( &p );
02334                                         continue;
02335                                 }
02336                                 //NPC->client->ps.forcePowerRegenAmount = n;
02337                                 //rwwFIXMEFIXME: support this?
02338                                 continue;
02339                         }
02340 
02341                         //have a sabers.cfg and just name your saber in your NPCs.cfg/ICARUS script
02342                         //saber name
02343                         if ( !Q_stricmp( token, "saber" ) ) 
02344                         {
02345                                 char *saberName;
02346 
02347                                 if ( COM_ParseString( &p, &value ) ) 
02348                                 {
02349                                         continue;
02350                                 }
02351 
02352                                 saberName = (char *)BG_TempAlloc(4096);//G_NewString( value );
02353                                 strcpy(saberName, value);
02354 
02355                                 WP_SaberParseParms( saberName, &NPC->client->saber[0] );
02356                                 npcSaber1 = G_ModelIndex(va("@%s", saberName));
02357 
02358                                 BG_TempFree(4096);
02359                                 continue;
02360                         }
02361                         
02362                         //second saber name
02363                         if ( !Q_stricmp( token, "saber2" ) ) 
02364                         {
02365                                 if ( COM_ParseString( &p, &value ) ) 
02366                                 {
02367                                         continue;
02368                                 }
02369 
02370                                 if ( !(NPC->client->saber[0].saberFlags&SFL_TWO_HANDED) )
02371                                 {//can't use a second saber if first one is a two-handed saber...?
02372                                         char *saberName = (char *)BG_TempAlloc(4096);//G_NewString( value );
02373                                         strcpy(saberName, value);
02374 
02375                                         WP_SaberParseParms( saberName, &NPC->client->saber[1] );
02376                                         if ( (NPC->client->saber[1].saberFlags&SFL_TWO_HANDED) )
02377                                         {//tsk tsk, can't use a twoHanded saber as second saber
02378                                                 WP_RemoveSaber( NPC->client->saber, 1 );
02379                                         }
02380                                         else
02381                                         {
02382                                                 //NPC->client->ps.dualSabers = qtrue;
02383                                                 npcSaber2 = G_ModelIndex(va("@%s", saberName));
02384                                         }
02385                                         BG_TempFree(4096);
02386                                 }
02387                                 continue;
02388                         }
02389 
02390                         // saberColor
02391                         if ( !Q_stricmp( token, "saberColor" ) ) 
02392                         {
02393                                 if ( COM_ParseString( &p, &value ) ) 
02394                                 {
02395                                         continue;
02396                                 }
02397                                 if ( NPC->client )
02398                                 {
02399                                         saber_colors_t color = TranslateSaberColor( value );
02400                                         for ( n = 0; n < MAX_BLADES; n++ )
02401                                         {
02402                                                 NPC->client->saber[0].blade[n].color = color;
02403                                         }
02404                                 }
02405                                 continue;
02406                         }
02407 
02408                         if ( !Q_stricmp( token, "saberColor2" ) ) 
02409                         {
02410                                 if ( COM_ParseString( &p, &value ) ) 
02411                                 {
02412                                         continue;
02413                                 }
02414                                 if ( NPC->client )
02415                                 {
02416                                         NPC->client->saber[0].blade[1].color = TranslateSaberColor( value );
02417                                 }
02418                                 continue;
02419                         }
02420 
02421                         if ( !Q_stricmp( token, "saberColor3" ) ) 
02422                         {
02423                                 if ( COM_ParseString( &p, &value ) ) 
02424                                 {
02425                                         continue;
02426                                 }
02427                                 if ( NPC->client )
02428                                 {
02429                                         NPC->client->saber[0].blade[2].color = TranslateSaberColor( value );
02430                                 }
02431                                 continue;
02432                         }
02433 
02434                         if ( !Q_stricmp( token, "saberColor4" ) ) 
02435                         {
02436                                 if ( COM_ParseString( &p, &value ) ) 
02437                                 {
02438                                         continue;
02439                                 }
02440                                 if ( NPC->client )
02441                                 {
02442                                         NPC->client->saber[0].blade[3].color = TranslateSaberColor( value );
02443                                 }
02444                                 continue;
02445                         }
02446 
02447                         if ( !Q_stricmp( token, "saberColor5" ) ) 
02448                         {
02449                                 if ( COM_ParseString( &p, &value ) ) 
02450                                 {
02451                                         continue;
02452                                 }
02453                                 if ( NPC->client )
02454                                 {
02455                                         NPC->client->saber[0].blade[4].color = TranslateSaberColor( value );
02456                                 }
02457                                 continue;
02458                         }
02459 
02460                         if ( !Q_stricmp( token, "saberColor6" ) ) 
02461                         {
02462                                 if ( COM_ParseString( &p, &value ) ) 
02463                                 {
02464                                         continue;
02465                                 }
02466                                 if ( NPC->client )
02467                                 {
02468                                         NPC->client->saber[0].blade[5].color = TranslateSaberColor( value );
02469                                 }
02470                                 continue;
02471                         }
02472 
02473                         if ( !Q_stricmp( token, "saberColor7" ) ) 
02474                         {
02475                                 if ( COM_ParseString( &p, &value ) ) 
02476                                 {
02477                                         continue;
02478                                 }
02479                                 if ( NPC->client )
02480                                 {
02481                                         NPC->client->saber[0].blade[6].color = TranslateSaberColor( value );
02482                                 }
02483                                 continue;
02484                         }
02485 
02486                         if ( !Q_stricmp( token, "saberColor8" ) ) 
02487                         {
02488                                 if ( COM_ParseString( &p, &value ) ) 
02489                                 {
02490                                         continue;
02491                                 }
02492                                 if ( NPC->client )
02493                                 {
02494                                         NPC->client->saber[0].blade[7].color = TranslateSaberColor( value );
02495                                 }
02496                                 continue;
02497                         }
02498 
02499                         if ( !Q_stricmp( token, "saber2Color" ) ) 
02500                         {
02501                                 if ( COM_ParseString( &p, &value ) ) 
02502                                 {
02503                                         continue;
02504                                 }
02505                                 if ( NPC->client )
02506                                 {
02507                                         saber_colors_t color = TranslateSaberColor( value );
02508                                         for ( n = 0; n < MAX_BLADES; n++ )
02509                                         {
02510                                                 NPC->client->saber[1].blade[n].color = color;
02511                                         }
02512                                 }
02513                                 continue;
02514                         }
02515 
02516                         if ( !Q_stricmp( token, "saber2Color2" ) ) 
02517                         {
02518                                 if ( COM_ParseString( &p, &value ) ) 
02519                                 {
02520                                         continue;
02521                                 }
02522                                 if ( NPC->client )
02523                                 {
02524                                         NPC->client->saber[1].blade[1].color = TranslateSaberColor( value );
02525                                 }
02526                                 continue;
02527                         }
02528 
02529                         if ( !Q_stricmp( token, "saber2Color3" ) ) 
02530                         {
02531                                 if ( COM_ParseString( &p, &value ) ) 
02532                                 {
02533                                         continue;
02534                                 }
02535                                 if ( NPC->client )
02536                                 {
02537                                         NPC->client->saber[1].blade[2].color = TranslateSaberColor( value );
02538                                 }
02539                                 continue;
02540                         }
02541 
02542                         if ( !Q_stricmp( token, "saber2Color4" ) ) 
02543                         {
02544                                 if ( COM_ParseString( &p, &value ) ) 
02545                                 {
02546                                         continue;
02547                                 }
02548                                 if ( NPC->client )
02549                                 {
02550                                         NPC->client->saber[1].blade[3].color = TranslateSaberColor( value );
02551                                 }
02552                                 continue;
02553                         }
02554 
02555                         if ( !Q_stricmp( token, "saber2Color5" ) ) 
02556                         {
02557                                 if ( COM_ParseString( &p, &value ) ) 
02558                                 {
02559                                         continue;
02560                                 }
02561                                 if ( NPC->client )
02562                                 {
02563                                         NPC->client->saber[1].blade[4].color = TranslateSaberColor( value );
02564                                 }
02565                                 continue;
02566                         }
02567 
02568                         if ( !Q_stricmp( token, "saber2Color6" ) ) 
02569                         {
02570                                 if ( COM_ParseString( &p, &value ) ) 
02571                                 {
02572                                         continue;
02573                                 }
02574                                 if ( NPC->client )
02575                                 {
02576                                         NPC->client->saber[1].blade[5].color = TranslateSaberColor( value );
02577                                 }
02578                                 continue;
02579                         }
02580 
02581                         if ( !Q_stricmp( token, "saber2Color7" ) ) 
02582                         {
02583                                 if ( COM_ParseString( &p, &value ) ) 
02584                                 {
02585                                         continue;
02586                                 }
02587                                 if ( NPC->client )
02588                                 {
02589                                         NPC->client->saber[1].blade[6].color = TranslateSaberColor( value );
02590                                 }
02591                                 continue;
02592                         }
02593 
02594                         if ( !Q_stricmp( token, "saber2Color8" ) ) 
02595                         {
02596                                 if ( COM_ParseString( &p, &value ) ) 
02597                                 {
02598                                         continue;
02599                                 }
02600                                 if ( NPC->client )
02601                                 {
02602                                         NPC->client->saber[1].blade[7].color = TranslateSaberColor( value );
02603                                 }
02604                                 continue;
02605                         }
02606 
02607                         //saber length
02608                         if ( !Q_stricmp( token, "saberLength" ) ) 
02609                         {
02610                                 if ( COM_ParseFloat( &p, &f ) ) 
02611                                 {
02612                                         SkipRestOfLine( &p );
02613                                         continue;
02614                                 }
02615                                 //cap
02616                                 if ( f < 4.0f )
02617                                 {
02618                                         f = 4.0f;
02619                                 }
02620 
02621                                 for ( n = 0; n < MAX_BLADES; n++ )
02622                                 {
02623                                         NPC->client->saber[0].blade[n].lengthMax = f;
02624                                 }
02625                                 continue;
02626                         }
02627 
02628                         if ( !Q_stricmp( token, "saberLength2" ) ) 
02629                         {
02630                                 if ( COM_ParseFloat( &p, &f ) ) 
02631                                 {
02632                                         SkipRestOfLine( &p );
02633                                         continue;
02634                                 }
02635                                 //cap
02636                                 if ( f < 4.0f )
02637                                 {
02638                                         f = 4.0f;
02639                                 }
02640                                 NPC->client->saber[0].blade[1].lengthMax = f;
02641                                 continue;
02642                         }
02643 
02644                         if ( !Q_stricmp( token, "saberLength3" ) ) 
02645                         {
02646                                 if ( COM_ParseFloat( &p, &f ) ) 
02647                                 {
02648                                         SkipRestOfLine( &p );
02649                                         continue;
02650                                 }
02651                                 //cap
02652                                 if ( f < 4.0f )
02653                                 {
02654                                         f = 4.0f;
02655                                 }
02656                                 NPC->client->saber[0].blade[2].lengthMax = f;
02657                                 continue;
02658                         }
02659 
02660                         if ( !Q_stricmp( token, "saberLength4" ) ) 
02661                         {
02662                                 if ( COM_ParseFloat( &p, &f ) ) 
02663                                 {
02664                                         SkipRestOfLine( &p );
02665                                         continue;
02666                                 }
02667                                 //cap
02668                                 if ( f < 4.0f )
02669                                 {
02670                                         f = 4.0f;
02671                                 }
02672                                 NPC->client->saber[0].blade[3].lengthMax = f;
02673                                 continue;
02674                         }
02675 
02676                         if ( !Q_stricmp( token, "saberLength5" ) ) 
02677                         {
02678                                 if ( COM_ParseFloat( &p, &f ) ) 
02679                                 {
02680                                         SkipRestOfLine( &p );
02681                                         continue;
02682                                 }
02683                                 //cap
02684                                 if ( f < 4.0f )
02685                                 {
02686                                         f = 4.0f;
02687                                 }
02688                                 NPC->client->saber[0].blade[4].lengthMax = f;
02689                                 continue;
02690                         }
02691 
02692                         if ( !Q_stricmp( token, "saberLength6" ) ) 
02693                         {
02694                                 if ( COM_ParseFloat( &p, &f ) ) 
02695                                 {
02696                                         SkipRestOfLine( &p );
02697                                         continue;
02698                                 }
02699                                 //cap
02700                                 if ( f < 4.0f )
02701                                 {
02702                                         f = 4.0f;
02703                                 }
02704                                 NPC->client->saber[0].blade[5].lengthMax = f;
02705                                 continue;
02706                         }
02707 
02708                         if ( !Q_stricmp( token, "saberLength7" ) ) 
02709                         {
02710                                 if ( COM_ParseFloat( &p, &f ) ) 
02711                                 {
02712                                         SkipRestOfLine( &p );
02713                                         continue;
02714                                 }
02715                                 //cap
02716                                 if ( f < 4.0f )
02717                                 {
02718                                         f = 4.0f;
02719                                 }
02720                                 NPC->client->saber[0].blade[6].lengthMax = f;
02721                                 continue;
02722                         }
02723 
02724                         if ( !Q_stricmp( token, "saberLength8" ) ) 
02725                         {
02726                                 if ( COM_ParseFloat( &p, &f ) ) 
02727                                 {
02728                                         SkipRestOfLine( &p );
02729                                         continue;
02730                                 }
02731                                 //cap
02732                                 if ( f < 4.0f )
02733                                 {
02734                                         f = 4.0f;
02735                                 }
02736                                 NPC->client->saber[0].blade[7].lengthMax = f;
02737                                 continue;
02738                         }
02739 
02740                         if ( !Q_stricmp( token, "saber2Length" ) ) 
02741                         {
02742                                 if ( COM_ParseFloat( &p, &f ) ) 
02743                                 {
02744                                         SkipRestOfLine( &p );
02745                                         continue;
02746                                 }
02747                                 //cap
02748                                 if ( f < 4.0f )
02749                                 {
02750                                         f = 4.0f;
02751                                 }
02752                                 for ( n = 0; n < MAX_BLADES; n++ )
02753                                 {
02754                                         NPC->client->saber[1].blade[n].lengthMax = f;
02755                                 }
02756                                 continue;
02757                         }
02758 
02759                         if ( !Q_stricmp( token, "saber2Length2" ) ) 
02760                         {
02761                                 if ( COM_ParseFloat( &p, &f ) ) 
02762                                 {
02763                                         SkipRestOfLine( &p );
02764                                         continue;
02765                                 }
02766                                 //cap
02767                                 if ( f < 4.0f )
02768                                 {
02769                                         f = 4.0f;
02770                                 }
02771                                 NPC->client->saber[1].blade[1].lengthMax = f;
02772                                 continue;
02773                         }
02774 
02775                         if ( !Q_stricmp( token, "saber2Length3" ) ) 
02776                         {
02777                                 if ( COM_ParseFloat( &p, &f ) ) 
02778                                 {
02779                                         SkipRestOfLine( &p );
02780                                         continue;
02781                                 }
02782                                 //cap
02783                                 if ( f < 4.0f )
02784                                 {
02785                                         f = 4.0f;
02786                                 }
02787                                 NPC->client->saber[1].blade[2].lengthMax = f;
02788                                 continue;
02789                         }
02790 
02791                         if ( !Q_stricmp( token, "saber2Length4" ) ) 
02792                         {
02793                                 if ( COM_ParseFloat( &p, &f ) ) 
02794                                 {
02795                                         SkipRestOfLine( &p );
02796                                         continue;
02797                                 }
02798                                 //cap
02799                                 if ( f < 4.0f )
02800                                 {
02801                                         f = 4.0f;
02802                                 }
02803                                 NPC->client->saber[1].blade[3].lengthMax = f;
02804                                 continue;
02805                         }
02806 
02807                         if ( !Q_stricmp( token, "saber2Length5" ) ) 
02808                         {
02809                                 if ( COM_ParseFloat( &p, &f ) ) 
02810                                 {
02811                                         SkipRestOfLine( &p );
02812                                         continue;
02813                                 }
02814                                 //cap
02815                                 if ( f < 4.0f )
02816                                 {
02817                                         f = 4.0f;
02818                                 }
02819                                 NPC->client->saber[1].blade[4].lengthMax = f;
02820                                 continue;
02821                         }
02822 
02823                         if ( !Q_stricmp( token, "saber2Length6" ) ) 
02824                         {
02825                                 if ( COM_ParseFloat( &p, &f ) ) 
02826                                 {
02827                                         SkipRestOfLine( &p );
02828                                         continue;
02829                                 }
02830                                 //cap
02831                                 if ( f < 4.0f )
02832                                 {
02833                                         f = 4.0f;
02834                                 }
02835                                 NPC->client->saber[1].blade[5].lengthMax = f;
02836                                 continue;
02837                         }
02838 
02839                         if ( !Q_stricmp( token,