codemp/game/NPC_AI_Stormtrooper.c File Reference

#include "b_local.h"
#include "g_nav.h"
#include "anims.h"

Go to the source code of this file.

Defines

#define MAX_VIEW_DIST   1024
#define MAX_VIEW_SPEED   250
#define MAX_LIGHT_INTENSITY   255
#define MIN_LIGHT_THRESHOLD   0.1
#define ST_MIN_LIGHT_THRESHOLD   30
#define ST_MAX_LIGHT_THRESHOLD   180
#define DISTANCE_THRESHOLD   0.075f
#define DISTANCE_SCALE   0.35f
#define FOV_SCALE   0.40f
#define LIGHT_SCALE   0.25f
#define SPEED_SCALE   0.25f
#define TURNING_SCALE   0.25f
#define REALIZE_THRESHOLD   0.6f
#define CAUTIOUS_THRESHOLD   ( REALIZE_THRESHOLD * 0.75 )
#define MAX_CHECK_THRESHOLD   1

Enumerations

enum  { LSTATE_NONE = 0, LSTATE_UNDERFIRE, LSTATE_INVESTIGATE }
enum  {
  SPEECH_CHASE, SPEECH_CONFUSED, SPEECH_COVER, SPEECH_DETECTED,
  SPEECH_GIVEUP, SPEECH_LOOK, SPEECH_LOST, SPEECH_OUTFLANK,
  SPEECH_ESCAPING, SPEECH_SIGHT, SPEECH_SOUND, SPEECH_SUSPICIOUS,
  SPEECH_YELL, SPEECH_PUSHED
}

Functions

void G_AddVoiceEvent (gentity_t *self, int event, int speakDebounceTime)
void AI_GroupUpdateSquadstates (AIGroupInfo_t *group, gentity_t *member, int newSquadState)
qboolean AI_GroupContainsEntNum (AIGroupInfo_t *group, int entNum)
void AI_GroupUpdateEnemyLastSeen (AIGroupInfo_t *group, vec3_t spot)
void AI_GroupUpdateClearShotTime (AIGroupInfo_t *group)
void NPC_TempLookTarget (gentity_t *self, int lookEntNum, int minLookTime, int maxLookTime)
qboolean G_ExpandPointToBBox (vec3_t point, const vec3_t mins, const vec3_t maxs, int ignore, int clipmask)
void ChangeWeapon (gentity_t *ent, int newWeapon)
void NPC_CheckGetNewWeapon (void)
int GetTime (int lastTime)
void NPC_AimAdjust (int change)
qboolean FlyingCreature (gentity_t *ent)
qboolean NPC_CheckPlayerTeamStealth (void)
void ST_AggressionAdjust (gentity_t *self, int change)
void ST_ClearTimers (gentity_t *ent)
void ST_MarkToCover (gentity_t *self)
void ST_StartFlee (gentity_t *self, gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int minTime, int maxTime)
void NPC_ST_Pain (gentity_t *self, gentity_t *attacker, int damage)
void NPC_ST_SayMovementSpeech (void)
void NPC_ST_StoreMovementSpeech (int speech, float chance)
void ST_TransferMoveGoal (gentity_t *self, gentity_t *other)
void NPC_BSST_Sleep (void)
qboolean NPC_CheckEnemyStealth (gentity_t *target)
void NPC_BSST_Investigate (void)
void NPC_BSST_Patrol (void)
void ST_ResolveBlockedShot (int hit)
void ST_TrackEnemy (gentity_t *self, vec3_t enemyPos)
int ST_ApproachEnemy (gentity_t *self)
void ST_HuntEnemy (gentity_t *self)
void ST_TransferTimers (gentity_t *self, gentity_t *other)
int ST_GetCPFlags (void)
void ST_Commander (void)
void NPC_BSST_Attack (void)
void NPC_BSST_Default (void)

Variables

vmCvar_t d_asynchronousGroupAI
int groupSpeechDebounceTime [TEAM_NUM_TEAMS]


Define Documentation

#define CAUTIOUS_THRESHOLD   ( REALIZE_THRESHOLD * 0.75 )
 

Definition at line 36 of file NPC_AI_Stormtrooper.c.

#define DISTANCE_SCALE   0.35f
 

Definition at line 28 of file NPC_AI_Stormtrooper.c.

#define DISTANCE_THRESHOLD   0.075f
 

Definition at line 26 of file NPC_AI_Stormtrooper.c.

#define FOV_SCALE   0.40f
 

Definition at line 29 of file NPC_AI_Stormtrooper.c.

#define LIGHT_SCALE   0.25f
 

Definition at line 30 of file NPC_AI_Stormtrooper.c.

#define MAX_CHECK_THRESHOLD   1
 

Definition at line 764 of file NPC_AI_Stormtrooper.c.

#define MAX_LIGHT_INTENSITY   255
 

Definition at line 22 of file NPC_AI_Stormtrooper.c.

#define MAX_VIEW_DIST   1024
 

Definition at line 20 of file NPC_AI_Stormtrooper.c.

#define MAX_VIEW_SPEED   250
 

Definition at line 21 of file NPC_AI_Stormtrooper.c.

#define MIN_LIGHT_THRESHOLD   0.1
 

Definition at line 23 of file NPC_AI_Stormtrooper.c.

#define REALIZE_THRESHOLD   0.6f
 

Definition at line 35 of file NPC_AI_Stormtrooper.c.

#define SPEED_SCALE   0.25f
 

Definition at line 32 of file NPC_AI_Stormtrooper.c.

#define ST_MAX_LIGHT_THRESHOLD   180
 

Definition at line 25 of file NPC_AI_Stormtrooper.c.

#define ST_MIN_LIGHT_THRESHOLD   30
 

Definition at line 24 of file NPC_AI_Stormtrooper.c.

#define TURNING_SCALE   0.25f
 

Definition at line 33 of file NPC_AI_Stormtrooper.c.

Referenced by NPC_CheckEnemyStealth().


Enumeration Type Documentation

anonymous enum
 

Enumeration values:
LSTATE_NONE 
LSTATE_UNDERFIRE 
LSTATE_INVESTIGATE 

Definition at line 53 of file NPC_AI_Stormtrooper.c.

00054 {
00055         LSTATE_NONE = 0,
00056         LSTATE_UNDERFIRE,
00057         LSTATE_INVESTIGATE,
00058 };

anonymous enum
 

Enumeration values:
SPEECH_CHASE 
SPEECH_CONFUSED 
SPEECH_COVER 
SPEECH_DETECTED 
SPEECH_GIVEUP 
SPEECH_LOOK 
SPEECH_LOST 
SPEECH_OUTFLANK 
SPEECH_ESCAPING 
SPEECH_SIGHT 
SPEECH_SOUND 
SPEECH_SUSPICIOUS 
SPEECH_YELL 
SPEECH_PUSHED 

Definition at line 106 of file NPC_AI_Stormtrooper.c.

00107 {
00108         SPEECH_CHASE,
00109         SPEECH_CONFUSED,
00110         SPEECH_COVER,
00111         SPEECH_DETECTED,
00112         SPEECH_GIVEUP,
00113         SPEECH_LOOK,
00114         SPEECH_LOST,
00115         SPEECH_OUTFLANK,
00116         SPEECH_ESCAPING,
00117         SPEECH_SIGHT,
00118         SPEECH_SOUND,
00119         SPEECH_SUSPICIOUS,
00120         SPEECH_YELL,
00121         SPEECH_PUSHED
00122 };


Function Documentation

qboolean AI_GroupContainsEntNum AIGroupInfo_t group,
int  entNum
 

Definition at line 982 of file NPC_AI_Utils.c.

References AIGroupInfo_t, AIGroupInfo_s::member, AIGroupMember_s::number, AIGroupInfo_s::numGroup, qboolean, qfalse, and qtrue.

Referenced by ST_ResolveBlockedShot().

00983 {
00984         int i;
00985 
00986         if ( !group )
00987         {
00988                 return qfalse;
00989         }
00990         for ( i = 0; i < group->numGroup; i++ )
00991         {
00992                 if ( group->member[i].number == entNum )
00993                 {
00994                         return qtrue;
00995                 }
00996         }
00997         return qfalse;
00998 }

void AI_GroupUpdateClearShotTime AIGroupInfo_t group  ) 
 

Definition at line 709 of file NPC_AI_Utils.c.

References AIGroupInfo_t, AIGroupInfo_s::lastClearShotTime, level, and level_locals_t::time.

Referenced by NPC_BSST_Attack().

00710 {
00711         if ( !group )
00712         {
00713                 return;
00714         }
00715         group->lastClearShotTime = level.time;
00716 }

void AI_GroupUpdateEnemyLastSeen AIGroupInfo_t group,
vec3_t  spot
 

Definition at line 699 of file NPC_AI_Utils.c.

References AIGroupInfo_t, AIGroupInfo_s::enemyLastSeenPos, AIGroupInfo_s::lastSeenEnemyTime, level, level_locals_t::time, vec3_t, and VectorCopy.

Referenced by NPC_BSST_Attack().

00700 {
00701         if ( !group )
00702         {
00703                 return;
00704         }
00705         group->lastSeenEnemyTime = level.time;
00706         VectorCopy( spot, group->enemyLastSeenPos );
00707 }

void AI_GroupUpdateSquadstates AIGroupInfo_t group,
gentity_t member,
int  newSquadState
 

Definition at line 718 of file NPC_AI_Utils.c.

References AIGroupInfo_t, gentity_t, AIGroupInfo_s::member, gentity_s::NPC, AIGroupMember_s::number, entityState_s::number, AIGroupInfo_s::numGroup, AIGroupInfo_s::numState, gentity_s::s, and gNPC_t::squadState.

Referenced by ST_Commander(), and ST_TransferMoveGoal().

00719 {
00720         int i;
00721 
00722         if ( !group )
00723         {
00724                 member->NPC->squadState = newSquadState;
00725                 return;
00726         }
00727 
00728         for ( i = 0; i < group->numGroup; i++ )
00729         {
00730                 if ( group->member[i].number == member->s.number )
00731                 {
00732                         group->numState[member->NPC->squadState]--;
00733                         member->NPC->squadState = newSquadState;
00734                         group->numState[member->NPC->squadState]++;
00735                         return;
00736                 }
00737         }
00738 }

void ChangeWeapon gentity_t ent,
int  newWeapon
 

qboolean FlyingCreature gentity_t ent  ) 
 

Definition at line 46 of file g_nav.c.

00047 {
00048         if (ent->client && ent->client->ps.gravity <= 0)
00049         {
00050                 return qtrue;
00051         }
00052         return qfalse;
00053 }

void G_AddVoiceEvent gentity_t self,
int  event,
int  speakDebounceTime
 

Definition at line 23 of file NPC_sounds.c.

00024 {
00025         if ( !self->NPC )
00026         {
00027                 return;
00028         }
00029 
00030         if ( !self->client || self->client->ps.pm_type >= PM_DEAD )
00031         {
00032                 return;
00033         }
00034 
00035         if ( self->NPC->blockedSpeechDebounceTime > level.time )
00036         {
00037                 return;
00038         }
00039 
00040         if ( trap_ICARUS_TaskIDPending( self, TID_CHAN_VOICE ) )
00041         {
00042                 return;
00043         }
00044 
00045         
00046         if ( (self->NPC->scriptFlags&SCF_NO_COMBAT_TALK) && ( (event >= EV_ANGER1 && event <= EV_VICTORY3) || (event >= EV_CHASE1 && event <= EV_SUSPICIOUS5) ) )//(event < EV_FF_1A || event > EV_FF_3C) && (event < EV_RESPOND1 || event > EV_MISSION3) )
00047         {
00048                 return;
00049         }
00050         
00051         if ( (self->NPC->scriptFlags&SCF_NO_ALERT_TALK) && (event >= EV_GIVEUP1 && event <= EV_SUSPICIOUS5) )
00052         {
00053                 return;
00054         }
00055         //FIXME: Also needs to check for teammates. Don't want
00056         //              everyone babbling at once
00057 
00058         //NOTE: was losing too many speech events, so we do it directly now, screw networking!
00059         //G_AddEvent( self, event, 0 );
00060         G_SpeechEvent( self, event );
00061 
00062         //won't speak again for 5 seconds (unless otherwise specified)
00063         self->NPC->blockedSpeechDebounceTime = level.time + ((speakDebounceTime==0) ? 5000 : speakDebounceTime);
00064 }

qboolean G_ExpandPointToBBox vec3_t  point,
const vec3_t  mins,
const vec3_t  maxs,
int  ignore,
int  clipmask
 

Definition at line 2087 of file g_utils.c.

References trace_t::allsolid, trace_t::fraction, qboolean, qfalse, qtrue, trace_t::startsolid, trap_Trace(), vec3_origin, vec3_t, and VectorCopy.

02088 {
02089         trace_t tr;
02090         vec3_t  start, end;
02091         int i;
02092 
02093         VectorCopy( point, start );
02094         
02095         for ( i = 0; i < 3; i++ )
02096         {
02097                 VectorCopy( start, end );
02098                 end[i] += mins[i];
02099                 trap_Trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask );
02100                 if ( tr.allsolid || tr.startsolid )
02101                 {
02102                         return qfalse;
02103                 }
02104                 if ( tr.fraction < 1.0 )
02105                 {
02106                         VectorCopy( start, end );
02107                         end[i] += maxs[i]-(mins[i]*tr.fraction);
02108                         trap_Trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask );
02109                         if ( tr.allsolid || tr.startsolid )
02110                         {
02111                                 return qfalse;
02112                         }
02113                         if ( tr.fraction < 1.0 )
02114                         {
02115                                 return qfalse;
02116                         }
02117                         VectorCopy( end, start );
02118                 }
02119         }
02120         //expanded it, now see if it's all clear
02121         trap_Trace( &tr, start, mins, maxs, start, ignore, clipmask );
02122         if ( tr.allsolid || tr.startsolid )
02123         {
02124                 return qfalse;
02125         }
02126         VectorCopy( start, point );
02127         return qtrue;
02128 }

int GetTime int  lastTime  ) 
 

void NPC_AimAdjust int  change  ) 
 

Definition at line 3098 of file NPC_combat.c.

03099 {
03100         if ( !TIMER_Exists( NPC, "aimDebounce" ) )
03101         {
03102                 int debounce = 500+(3-g_spskill.integer)*100;
03103                 TIMER_Set( NPC, "aimDebounce", Q_irand( debounce,debounce+1000 ) );
03104                 //int debounce = 1000+(3-g_spskill.integer)*500;
03105                 //TIMER_Set( NPC, "aimDebounce", Q_irand( debounce, debounce+2000 ) );
03106                 return;
03107         }
03108         if ( TIMER_Done( NPC, "aimDebounce" ) )
03109         {
03110                 int debounce;
03111 
03112                 NPCInfo->currentAim += change;
03113                 if ( NPCInfo->currentAim > NPCInfo->stats.aim )
03114                 {//can never be better than max aim
03115                         NPCInfo->currentAim = NPCInfo->stats.aim;
03116                 }
03117                 else if ( NPCInfo->currentAim < -30 )
03118                 {//can never be worse than this
03119                         NPCInfo->currentAim = -30;
03120                 }
03121 
03122                 //Com_Printf( "%s new aim = %d\n", NPC->NPC_type, NPCInfo->currentAim );
03123 
03124                 debounce = 500+(3-g_spskill.integer)*100;
03125                 TIMER_Set( NPC, "aimDebounce", Q_irand( debounce,debounce+1000 ) );
03126                 //int debounce = 1000+(3-g_spskill.integer)*500;
03127                 //TIMER_Set( NPC, "aimDebounce", Q_irand( debounce, debounce+2000 ) );
03128         }
03129 }

void NPC_BSST_Attack void   ) 
 

NPC->enemy )//

Definition at line 2409 of file NPC_AI_Stormtrooper.c.

References AEL_DANGER, AI_GetGroup(), AI_GroupUpdateClearShotTime(), AI_GroupUpdateEnemyLastSeen(), AngleVectors(), BUTTON_ALT_ATTACK, BUTTON_ATTACK, usercmd_s::buttons, gentity_s::client, entityShared_t::currentOrigin, gNPC_t::desiredPitch, gNPC_t::desiredYaw, DotProduct, gentity_s::enemy, gNPC_t::enemyLastSeenLocation, gNPC_t::enemyLastSeenTime, gclient_s::enemyTeam, g_entities, g_spskill, gentity_t, GetTime(), gNPC_t::goalEntity, gNPC_t::group, gentity_s::health, vmCvar_t::integer, gNPC_t::lastPathAngles, level, MIN_ROCKET_DIST_SQUARED, NPC, NPC_AimAdjust(), NPC_BSPatrol(), NPC_BSST_Patrol(), NPC_ChangeWeapon(), NPC_CheckAlertEvents(), NPC_CheckEnemyExt(), NPC_CheckForDanger(), NPC_ClearLOS4(), NPC_FaceEnemy(), NPC_ShotEntity(), NPC_UpdateAngles(), NPCInfo, NPCTEAM_PLAYER, NULL, entityState_s::number, gentity_s::painDebounceTime, gclient_s::playerTeam, AIGroupInfo_s::processed, gclient_s::ps, Q_irand(), qfalse, qtrue, gentity_s::r, gentity_s::s, S_COLOR_GREEN, S_COLOR_RED, S_COLOR_YELLOW, SCF_ALT_FIRE, SCF_CHASE_ENEMIES, SCF_DONT_FIRE, SCF_FIRE_WEAPON, gNPC_t::scriptFlags, SPEECH_COVER, ST_Commander(), ST_ResolveBlockedShot(), startTime, SVF_GLASS_BRUSH, entityShared_t::svFlags, gentity_s::takedamage, level_locals_t::time, TIMER_Done(), TIMER_Set(), trap_InPVS(), ucmd, usercmd_s::upmove, vec3_t, VectorClear, VectorCopy, VectorNormalize(), VectorSubtract, playerState_s::viewangles, entityState_s::weapon, playerState_s::weapon, WeaponThink(), playerState_s::weaponTime, WP_DISRUPTOR, WP_EMPLACED_GUN, WP_FLECHETTE, WP_NONE, WP_REPEATER, WP_ROCKET_LAUNCHER, WP_SABER, and YAW.

Referenced by NPC_BSDefault(), and NPC_BSST_Default().

02410 {
02411         vec3_t  enemyDir, shootDir;
02412         float dot;
02413 
02414         //Don't do anything if we're hurt
02415         if ( NPC->painDebounceTime > level.time )
02416         {
02417                 NPC_UpdateAngles( qtrue, qtrue );
02418                 return;
02419         }
02420 
02421         //NPC_CheckEnemy( qtrue, qfalse );
02422         //If we don't have an enemy, just idle
02423         if ( NPC_CheckEnemyExt(qfalse) == qfalse )
02424         {
02425                 NPC->enemy = NULL;
02426                 if( NPC->client->playerTeam == NPCTEAM_PLAYER )
02427                 {
02428                         NPC_BSPatrol();
02429                 }
02430                 else
02431                 {
02432                         NPC_BSST_Patrol();//FIXME: or patrol?
02433                 }
02434                 return;
02435         }
02436 
02437         //FIXME: put some sort of delay into the guys depending on how they saw you...?
02438 
02439         //Get our group info
02440         if ( TIMER_Done( NPC, "interrogating" ) )
02441         {
02442                 AI_GetGroup( NPC );//, 45, 512, NPC->enemy );
02443         }
02444         else
02445         {
02446                 //FIXME: when done interrogating, I should send out a team alert!
02447         }
02448 
02449         if ( NPCInfo->group )
02450         {//I belong to a squad of guys - we should *always* have a group
02451                 if ( !NPCInfo->group->processed )
02452                 {//I'm the first ent in my group, I'll make the command decisions
02453 #if     AI_TIMERS
02454                         int     startTime = GetTime(0);
02455 #endif//        AI_TIMERS
02456                         ST_Commander();
02457 #if     AI_TIMERS
02458                         int commTime = GetTime ( startTime );
02459                         if ( commTime > 20 )
02460                         {
02461                                 gi.Printf( S_COLOR_RED"ERROR: Commander time: %d\n", commTime );
02462                         }
02463                         else if ( commTime > 10 )
02464                         {
02465                                 gi.Printf( S_COLOR_YELLOW"WARNING: Commander time: %d\n", commTime );
02466                         }
02467                         else if ( commTime > 2 )
02468                         {
02469                                 gi.Printf( S_COLOR_GREEN"Commander time: %d\n", commTime );
02470                         }
02471 #endif//        AI_TIMERS
02472                 }
02473         }
02474         else if ( TIMER_Done( NPC, "flee" ) && NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) )
02475         {//not already fleeing, and going to run
02476                 ST_Speech( NPC, SPEECH_COVER, 0 );
02477                 NPC_UpdateAngles( qtrue, qtrue );
02478                 return;
02479         }
02480 
02481         if ( !NPC->enemy )
02482         {//WTF?  somehow we lost our enemy?
02483                 NPC_BSST_Patrol();//FIXME: or patrol?
02484                 return;
02485         }
02486 
02487         enemyLOS = enemyCS = enemyInFOV = qfalse;
02488         move = qtrue;
02489         faceEnemy = qfalse;
02490         shoot = qfalse;
02491         hitAlly = qfalse;
02492         VectorClear( impactPos );
02493         enemyDist = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
02494 
02495         VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, enemyDir );
02496         VectorNormalize( enemyDir );
02497         AngleVectors( NPC->client->ps.viewangles, shootDir, NULL, NULL );
02498         dot = DotProduct( enemyDir, shootDir );
02499         if ( dot > 0.5f ||( enemyDist * (1.0f-dot)) < 10000 )
02500         {//enemy is in front of me or they're very close and not behind me
02501                 enemyInFOV = qtrue;
02502         }
02503 
02504         if ( enemyDist < MIN_ROCKET_DIST_SQUARED )//128
02505         {//enemy within 128
02506                 if ( (NPC->client->ps.weapon == WP_FLECHETTE || NPC->client->ps.weapon == WP_REPEATER) && 
02507                         (NPCInfo->scriptFlags & SCF_ALT_FIRE) )
02508                 {//shooting an explosive, but enemy too close, switch to primary fire
02509                         NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
02510                         //FIXME: we can never go back to alt-fire this way since, after this, we don't know if we were initially supposed to use alt-fire or not...
02511                 }
02512         }
02513         else if ( enemyDist > 65536 )//256 squared
02514         {
02515                 if ( NPC->client->ps.weapon == WP_DISRUPTOR )
02516                 {//sniping... should be assumed
02517                         if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) )
02518                         {//use primary fire
02519                                 NPCInfo->scriptFlags |= SCF_ALT_FIRE;
02520                                 //reset fire-timing variables
02521                                 NPC_ChangeWeapon( WP_DISRUPTOR );
02522                                 NPC_UpdateAngles( qtrue, qtrue );
02523                                 return;
02524                         }
02525                 }
02526         }
02527 
02528         //can we see our target?
02529         if ( NPC_ClearLOS4( NPC->enemy ) )
02530         {
02531                 AI_GroupUpdateEnemyLastSeen( NPCInfo->group, NPC->enemy->r.currentOrigin );
02532                 NPCInfo->enemyLastSeenTime = level.time;
02533                 enemyLOS = qtrue;
02534 
02535                 if ( NPC->client->ps.weapon == WP_NONE )
02536                 {
02537                         enemyCS = qfalse;//not true, but should stop us from firing
02538                         NPC_AimAdjust( -1 );//adjust aim worse longer we have no weapon
02539                 }
02540                 else
02541                 {//can we shoot our target?
02542                         if ( (NPC->client->ps.weapon == WP_ROCKET_LAUNCHER || (NPC->client->ps.weapon == WP_FLECHETTE && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist < MIN_ROCKET_DIST_SQUARED )//128*128
02543                         {
02544                                 enemyCS = qfalse;//not true, but should stop us from firing
02545                                 hitAlly = qtrue;//us!
02546                                 //FIXME: if too close, run away!
02547                         }
02548                         else if ( enemyInFOV )
02549                         {//if enemy is FOV, go ahead and check for shooting
02550                                 int hit = NPC_ShotEntity( NPC->enemy, impactPos );
02551                                 gentity_t *hitEnt = &g_entities[hit];
02552 
02553                                 if ( hit == NPC->enemy->s.number 
02554                                         || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
02555                                         || ( hitEnt && hitEnt->takedamage && ((hitEnt->r.svFlags&SVF_GLASS_BRUSH)||hitEnt->health < 40||NPC->s.weapon == WP_EMPLACED_GUN) ) )
02556                                 {//can hit enemy or enemy ally or will hit glass or other minor breakable (or in emplaced gun), so shoot anyway
02557                                         AI_GroupUpdateClearShotTime( NPCInfo->group );
02558                                         enemyCS = qtrue;
02559                                         NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
02560                                         VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
02561                                 }
02562                                 else
02563                                 {//Hmm, have to get around this bastard
02564                                         NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy
02565                                         ST_ResolveBlockedShot( hit );
02566                                         if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam )
02567                                         {//would hit an ally, don't fire!!!
02568                                                 hitAlly = qtrue;
02569                                         }
02570                                         else
02571                                         {//Check and see where our shot *would* hit... if it's not close to the enemy (within 256?), then don't fire
02572                                         }
02573                                 }
02574                         }
02575                         else
02576                         {
02577                                 enemyCS = qfalse;//not true, but should stop us from firing
02578                         }
02579                 }
02580         }
02581         else if ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) )
02582         {
02583                 NPCInfo->enemyLastSeenTime = level.time;
02584                 faceEnemy = qtrue;
02585                 NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
02586         }
02587 
02588         if ( NPC->client->ps.weapon == WP_NONE )
02589         {
02590                 faceEnemy = qfalse;
02591                 shoot = qfalse;
02592         }
02593         else
02594         {
02595                 if ( enemyLOS )
02596                 {//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
02597                         faceEnemy = qtrue;
02598                 }
02599                 if ( enemyCS )
02600                 {
02601                         shoot = qtrue;
02602                 }
02603         }
02604 
02605         //Check for movement to take care of
02606         ST_CheckMoveState();
02607 
02608         //See if we should override shooting decision with any special considerations
02609         ST_CheckFireState();
02610 
02611         if ( faceEnemy )
02612         {//face the enemy
02613                 NPC_FaceEnemy( qtrue );
02614         }
02615 
02616         if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )
02617         {//not supposed to chase my enemies
02618                 if ( NPCInfo->goalEntity == NPC->enemy )
02619                 {//goal is my entity, so don't move
02620                         move = qfalse;
02621                 }
02622         }
02623 
02624         if ( NPC->client->ps.weaponTime > 0 && NPC->s.weapon == WP_ROCKET_LAUNCHER )
02625         {
02626                 move = qfalse;
02627         }
02628 
02629         if ( move )
02630         {//move toward goal
02631                 if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist > 10000 ) )//100 squared
02632                 {
02633                         move = ST_Move();
02634                 }
02635                 else
02636                 {
02637                         move = qfalse;
02638                 }
02639         }
02640 
02641         if ( !move )
02642         {
02643                 if ( !TIMER_Done( NPC, "duck" ) )
02644                 {
02645                         ucmd.upmove = -127;
02646                 }
02647                 //FIXME: what about leaning?
02648         }
02649         else
02650         {//stop ducking!
02651                 TIMER_Set( NPC, "duck", -1 );
02652         }
02653 
02654         if ( !TIMER_Done( NPC, "flee" ) )
02655         {//running away
02656                 faceEnemy = qfalse;
02657         }
02658 
02659         //FIXME: check scf_face_move_dir here?
02660 
02661         if ( !faceEnemy )
02662         {//we want to face in the dir we're running
02663                 if ( !move )
02664                 {//if we haven't moved, we should look in the direction we last looked?
02665                         VectorCopy( NPC->client->ps.viewangles, NPCInfo->lastPathAngles );
02666                 }
02667                 NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
02668                 NPCInfo->desiredPitch = 0;
02669                 NPC_UpdateAngles( qtrue, qtrue );
02670                 if ( move )
02671                 {//don't run away and shoot
02672                         shoot = qfalse;
02673                 }
02674         }
02675 
02676         if ( NPCInfo->scriptFlags & SCF_DONT_FIRE )
02677         {
02678                 shoot = qfalse;
02679         }
02680 
02681         if ( NPC->enemy && NPC->enemy->enemy )
02682         {
02683                 if ( NPC->enemy->s.weapon == WP_SABER && NPC->enemy->enemy->s.weapon == WP_SABER )
02684                 {//don't shoot at an enemy jedi who is fighting another jedi, for fear of injuring one or causing rogue blaster deflections (a la Obi Wan/Vader duel at end of ANH)
02685                         shoot = qfalse;
02686                 }
02687         }
02688         //FIXME: don't shoot right away!
02689         if ( NPC->client->ps.weaponTime > 0 )
02690         {
02691                 if ( NPC->s.weapon == WP_ROCKET_LAUNCHER )
02692                 {
02693                         if ( !enemyLOS || !enemyCS )
02694                         {//cancel it
02695                                 NPC->client->ps.weaponTime = 0;
02696                         }
02697                         else
02698                         {//delay our next attempt
02699                                 TIMER_Set( NPC, "attackDelay", Q_irand( 3000, 5000 ) );
02700                         }
02701                 }
02702         }
02703         else if ( shoot )
02704         {//try to shoot if it's time
02705                 if ( TIMER_Done( NPC, "attackDelay" ) )
02706                 {
02707                         if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
02708                         {
02709                                 WeaponThink( qtrue );
02710                         }
02711                         //NASTY
02712                         if ( NPC->s.weapon == WP_ROCKET_LAUNCHER 
02713                                 && (ucmd.buttons&BUTTON_ATTACK) 
02714                                 && !move
02715                                 && g_spskill.integer > 1 
02716                                 && !Q_irand( 0, 3 ) )
02717                         {//every now and then, shoot a homing rocket
02718                                 ucmd.buttons &= ~BUTTON_ATTACK;
02719                                 ucmd.buttons |= BUTTON_ALT_ATTACK;
02720                                 NPC->client->ps.weaponTime = Q_irand( 1000, 2500 );
02721                         }
02722                 }
02723         }
02724 }

void NPC_BSST_Default void   ) 
 

Definition at line 2726 of file NPC_AI_Stormtrooper.c.

References gentity_s::enemy, NPC, NPC_BSST_Attack(), NPC_BSST_Patrol(), NPC_CheckGetNewWeapon(), NPCInfo, qtrue, SCF_FIRE_WEAPON, gNPC_t::scriptFlags, and WeaponThink().

Referenced by NPC_BehaviorSet_Stormtrooper().

02727 {
02728         if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
02729         {
02730                 WeaponThink( qtrue );
02731         }
02732         
02733         if( !NPC->enemy )
02734         {//don't have an enemy, look for one
02735                 NPC_BSST_Patrol();
02736         }
02737         else //if ( NPC->enemy )
02738         {//have an enemy
02739                 NPC_CheckGetNewWeapon();
02740                 NPC_BSST_Attack();
02741         }
02742 }

void NPC_BSST_Investigate void   ) 
 

Definition at line 978 of file NPC_AI_Stormtrooper.c.

References AEL_MINOR, AI_GetGroup(), level_locals_t::alertEvents, BS_DEFAULT, BUTTON_WALKING, usercmd_s::buttons, gNPC_t::confusionTime, entityShared_t::currentOrigin, FlyingCreature(), gNPC_t::goalEntity, alertEvent_s::ID, gNPC_t::investigateCount, gNPC_t::investigateDebounceTime, gNPC_t::lastAlertID, level, gNPC_t::localState, LSTATE_INVESTIGATE, LSTATE_NONE, entityShared_t::maxs, entityShared_t::mins, NAV_HitNavGoal(), NPC, NPC_CheckAlertEvents(), NPC_CheckForDanger(), NPC_CheckPlayerTeamStealth(), NPC_MoveToGoal(), NPC_UpdateAngles(), NPCInfo, NULL, gNPC_t::pauseTime, qfalse, qtrue, gentity_s::r, SCF_FIRE_WEAPON, SCF_IGNORE_ALERTS, SCF_LOOK_FOR_ENEMIES, gNPC_t::scriptFlags, SPEECH_COVER, SPEECH_DETECTED, SPEECH_GIVEUP, gNPC_t::tempBehavior, level_locals_t::time, ucmd, UpdateGoal(), and WeaponThink().

Referenced by NPC_BehaviorSet_Stormtrooper().

00979 {
00980         //get group- mainly for group speech debouncing, but may use for group scouting/investigating AI, too
00981         AI_GetGroup( NPC );
00982 
00983         if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
00984         {
00985                 WeaponThink( qtrue );
00986         }
00987 
00988         if ( NPCInfo->confusionTime < level.time )
00989         {
00990                 if ( NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES )
00991                 {
00992                         //Look for an enemy
00993                         if ( NPC_CheckPlayerTeamStealth() )
00994                         {
00995                                 //NPCInfo->behaviorState        = BS_HUNT_AND_KILL;//should be auto now
00996                                 ST_Speech( NPC, SPEECH_DETECTED, 0 );
00997                                 NPCInfo->tempBehavior   = BS_DEFAULT;
00998                                 NPC_UpdateAngles( qtrue, qtrue );
00999                                 return;
01000                         }
01001                 }
01002         }
01003 
01004         if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
01005         {
01006                 int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, NPCInfo->lastAlertID, qfalse, AEL_MINOR );
01007 
01008                 //There is an event to look at
01009                 if ( alertEvent >= 0 )
01010                 {
01011                         if ( NPCInfo->confusionTime < level.time )
01012                         {
01013                                 if ( NPC_CheckForDanger( alertEvent ) )
01014                                 {//running like hell
01015                                         ST_Speech( NPC, SPEECH_COVER, 0 );//FIXME: flee sound?
01016                                         return;
01017                                 }
01018                         }
01019 
01020                         if ( level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
01021                         {
01022                                 NPC_ST_InvestigateEvent( alertEvent, qtrue );
01023                         }
01024                 }
01025         }
01026 
01027         //If we're done looking, then just return to what we were doing
01028         if ( ( NPCInfo->investigateDebounceTime + NPCInfo->pauseTime ) < level.time )
01029         {
01030                 NPCInfo->tempBehavior = BS_DEFAULT;
01031                 NPCInfo->goalEntity = UpdateGoal();
01032                 
01033                 NPC_UpdateAngles( qtrue, qtrue );
01034                 //Say something
01035                 ST_Speech( NPC, SPEECH_GIVEUP, 0 );
01036                 return;
01037         }
01038 
01039         //FIXME: else, look for new alerts
01040 
01041         //See if we're searching for the noise's origin
01042         if ( NPCInfo->localState == LSTATE_INVESTIGATE && (NPCInfo->goalEntity!=NULL) )
01043         {
01044                 //See if we're there
01045                 if ( NAV_HitNavGoal( NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPCInfo->goalEntity->r.currentOrigin, 32, FlyingCreature( NPC ) ) == qfalse )
01046                 {
01047                         ucmd.buttons |= BUTTON_WALKING;
01048 
01049                         //Try and move there
01050                         if ( NPC_MoveToGoal( qtrue )  )
01051                         {
01052                                 //Bump our times
01053                                 NPCInfo->investigateDebounceTime        = NPCInfo->investigateCount * 5000;
01054                                 NPCInfo->pauseTime                                      = level.time;
01055 
01056                                 NPC_UpdateAngles( qtrue, qtrue );
01057                                 return;
01058                         }
01059                 }
01060 
01061                 //Otherwise we're done or have given up
01062                 //Say something
01063                 //ST_Speech( NPC, SPEECH_LOOK, 0.33f );
01064                 NPCInfo->localState = LSTATE_NONE;
01065         }
01066 
01067         //Look around
01068         ST_LookAround();
01069 }

void NPC_BSST_Patrol void   ) 
 

Definition at line 1077 of file NPC_AI_Stormtrooper.c.

References AEL_MINOR, AI_GetGroup(), entityState_s::angles, BOTH_STAND4, BUTTON_WALKING, usercmd_s::buttons, ChangeWeapon(), CLASS_IMPERIAL, CLASS_IMPWORKER, gentity_s::client, gNPC_t::confusionTime, gNPC_t::desiredPitch, gNPC_t::desiredYaw, usercmd_s::forwardmove, playerState_s::legsAnim, playerState_s::legsTimer, level, NPC, NPC_CheckAlertEvents(), NPC_CheckPlayerTeamStealth(), gclient_s::NPC_class, NPC_MoveToGoal(), NPC_SetAnim(), NPC_UpdateAngles(), NPCInfo, gclient_s::ps, Q_irand(), qfalse, qtrue, usercmd_s::rightmove, gentity_s::s, SCF_IGNORE_ALERTS, SCF_LOOK_FOR_ENEMIES, SCF_RUNNING, gNPC_t::scriptFlags, SETANIM_BOTH, SETANIM_FLAG_HOLD, SETANIM_FLAG_OVERRIDE, SETANIM_TORSO, level_locals_t::time, TIMER_Done(), playerState_s::torsoAnim, playerState_s::torsoTimer, ucmd, UpdateGoal(), usercmd_s::upmove, playerState_s::weapon, WEAPON_READY, playerState_s::weaponstate, and WP_NONE.

Referenced by NPC_BSJedi_Default(), NPC_BSST_Attack(), and NPC_BSST_Default().

01078 {//FIXME: pick up on bodies of dead buddies?
01079         
01080         //get group- mainly for group speech debouncing, but may use for group scouting/investigating AI, too
01081         AI_GetGroup( NPC );
01082 
01083         if ( NPCInfo->confusionTime < level.time )
01084         {
01085                 //Look for any enemies
01086                 if ( NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES )
01087                 {
01088                         if ( NPC_CheckPlayerTeamStealth() )
01089                         {
01090                                 //NPCInfo->behaviorState = BS_HUNT_AND_KILL;//should be auto now
01091                                 //NPC_AngerSound();
01092                                 NPC_UpdateAngles( qtrue, qtrue );
01093                                 return;
01094                         }
01095                 }
01096         }
01097 
01098         if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
01099         {
01100                 int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_MINOR );
01101 
01102                 //There is an event to look at
01103                 if ( alertEvent >= 0 )
01104                 {
01105                         if ( NPC_ST_InvestigateEvent( alertEvent, qfalse ) )
01106                         {//actually going to investigate it
01107                                 NPC_UpdateAngles( qtrue, qtrue );
01108                                 return;
01109                         }
01110                 }
01111         }
01112 
01113         //If we have somewhere to go, then do that
01114         if ( UpdateGoal() )
01115         {
01116                 ucmd.buttons |= BUTTON_WALKING;
01117                 //ST_Move( NPCInfo->goalEntity );
01118                 NPC_MoveToGoal( qtrue );
01119         }
01120         else// if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
01121         {
01122                 if ( NPC->client->NPC_class != CLASS_IMPERIAL && NPC->client->NPC_class != CLASS_IMPWORKER )
01123                 {//imperials do not look around
01124                         if ( TIMER_Done( NPC, "enemyLastVisible" ) )
01125                         {//nothing suspicious, look around
01126                                 if ( !Q_irand( 0, 30 ) )
01127                                 {
01128                                         NPCInfo->desiredYaw = NPC->s.angles[1] + Q_irand( -90, 90 );
01129                                 }
01130                                 if ( !Q_irand( 0, 30 ) )
01131                                 {
01132                                         NPCInfo->desiredPitch = Q_irand( -20, 20 );
01133                                 }
01134                         }
01135                 }
01136         }
01137 
01138         NPC_UpdateAngles( qtrue, qtrue );
01139         //TEMP hack for Imperial stand anim
01140         if ( NPC->client->NPC_class == CLASS_IMPERIAL || NPC->client->NPC_class == CLASS_IMPWORKER )
01141         {//hack
01142                 if ( ucmd.forwardmove || ucmd.rightmove || ucmd.upmove )
01143                 {//moving
01144         
01145                         if( (NPC->client->ps.torsoTimer <= 0) || (NPC->client->ps.torsoAnim == BOTH_STAND4) )                   
01146                         {
01147                                 if ( (ucmd.buttons&BUTTON_WALKING) && !(NPCInfo->scriptFlags&SCF_RUNNING) )
01148                                 {//not running, only set upper anim
01149                                         //  No longer overrides scripted anims
01150                                         NPC_SetAnim( NPC, SETANIM_TORSO, BOTH_STAND4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
01151                                         NPC->client->ps.torsoTimer = 200;
01152                                 }
01153                         }
01154                 }
01155                 else
01156                 {//standing still, set both torso and legs anim
01157                         //  No longer overrides scripted anims
01158                         if( ( NPC->client->ps.torsoTimer <= 0 || (NPC->client->ps.torsoAnim == BOTH_STAND4) ) &&
01159                                 ( NPC->client->ps.legsTimer <= 0  || (NPC->client->ps.legsAnim == BOTH_STAND4) ) )
01160                         {
01161                                 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
01162                                 NPC->client->ps.torsoTimer = NPC->client->ps.legsTimer = 200;
01163                         }
01164                 }
01165                 //FIXME: this is a disgusting hack that is supposed to make the Imperials start with their weapon holstered- need a better way
01166                 if ( NPC->client->ps.weapon != WP_NONE )
01167                 {
01168                         ChangeWeapon( NPC, WP_NONE );
01169                         NPC->client->ps.weapon = WP_NONE;
01170                         NPC->client->ps.weaponstate = WEAPON_READY;
01171                         /*
01172                         if ( NPC->weaponModel[0] > 0 )
01173                         {
01174                                 gi.G2API_RemoveGhoul2Model( NPC->ghoul2, NPC->weaponModel[0] );
01175                                 NPC->weaponModel[0] = -1;
01176                         }
01177                         */
01178                         //rwwFIXMEFIXME: Do this?
01179                 }
01180         }
01181 }

void NPC_BSST_Sleep void   ) 
 

Definition at line 447 of file NPC_AI_Stormtrooper.c.

References AEL_DISCOVERED, AEL_MINOR, level_locals_t::alertEvents, g_entities, G_SetEnemy(), gentity_s::health, alertEvent_s::level, level, NPC, NPC_CheckAlertEvents(), NPCInfo, qfalse, qtrue, SCF_LOOK_FOR_ENEMIES, and gNPC_t::scriptFlags.

Referenced by NPC_BehaviorSet_Stormtrooper().

00448 {
00449         int alertEvent = NPC_CheckAlertEvents( qfalse, qtrue, -1, qfalse, AEL_MINOR );//only check sounds since we're alseep!
00450 
00451         //There is an event we heard
00452         if ( alertEvent >= 0 )
00453         {
00454                 //See if it was enough to wake us up
00455                 if ( level.alertEvents[alertEvent].level == AEL_DISCOVERED && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
00456                 { //rwwFIXMEFIXME: Care about all clients not just 0
00457                         if ( &g_entities[0] && g_entities[0].health > 0 )
00458                         {
00459                                 G_SetEnemy( NPC, &g_entities[0] );
00460                                 return;
00461                         }
00462                 }
00463 
00464                 //Otherwise just stir a bit
00465                 NPC_ST_SleepShuffle();
00466                 return;
00467         }
00468 }

qboolean NPC_CheckEnemyStealth gentity_t target  ) 
 

Definition at line 476 of file NPC_AI_Stormtrooper.c.

References CAUTIOUS_THRESHOLD, CLASS_ATST, CLASS_SWAMPTROOPER, gentity_s::client, clientPersistant_t::cmd, CONTENTS_FOG, CONTENTS_WATER, entityShared_t::currentOrigin, DISTANCE_SCALE, DISTANCE_THRESHOLD, gentity_s::enemy, gNPC_t::enemyLastSeenTime, renderInfo_s::eyeAngles, renderInfo_s::eyePoint, FL_NOTARGET, gentity_s::flags, FOV_SCALE, G_SetEnemy(), gentity_t, gentity_s::health, gNPCstats_e::hfov, InFOV(), level, LIGHT_SCALE, MAX_LIGHT_INTENSITY, MAX_VIEW_DIST, MAX_VIEW_SPEED, entityShared_t::maxs, MIN_LIGHT_THRESHOLD, NPC, gclient_s::NPC_class, NPC_ClearLOS4(), NPC_GetHFOVPercentage(), NPC_GetVFOVPercentage(), NPC_TempLookTarget(), NPCInfo, NULL, entityState_s::number, gclient_s::pers, playerState_s::pm_flags, PMF_DUCKED, gclient_s::ps, Q_irand(), qboolean, qfalse, qtrue, gentity_s::r, gNPC_t::rank, RANK_LT, REALIZE_THRESHOLD, gclient_s::renderInfo, gentity_s::s, playerState_s::saberHolstered, playerState_s::saberInFlight, SCF_IGNORE_ALERTS, SCF_LOOK_FOR_ENEMIES, gNPC_t::scriptFlags, SPEECH_SIGHT, SPEECH_SUSPICIOUS, SPEED_SCALE, gNPC_t::stats, level_locals_t::time, TIMER_Done(), TIMER_Get(), TIMER_Set(), trap_PointContents(), TURNING_SCALE, usercmd_s::upmove, vec3_t, VectorSet, playerState_s::velocity, gNPCstats_e::vfov, gNPCstats_e::visrange, playerState_s::weapon, and WP_SABER.

00477 {
00478         float           target_dist, minDist = 40;//any closer than 40 and we definitely notice
00479         float           maxViewDist;
00480         qboolean        clearLOS;
00481 
00482         //In case we aquired one some other way
00483         if ( NPC->enemy != NULL )
00484                 return qtrue;
00485 
00486         //Ignore notarget
00487         if ( target->flags & FL_NOTARGET )
00488                 return qfalse;
00489 
00490         if ( target->health <= 0 )
00491         {
00492                 return qfalse;
00493         }
00494 
00495         if ( target->client->ps.weapon == WP_SABER && !target->client->ps.saberHolstered && !target->client->ps.saberInFlight )
00496         {//if target has saber in hand and activated, we wake up even sooner even if not facing him
00497                 minDist = 100;
00498         }
00499 
00500         target_dist = DistanceSquared( target->r.currentOrigin, NPC->r.currentOrigin );
00501 
00502         //If the target is this close, then wake up regardless
00503         if ( !(target->client->ps.pm_flags&PMF_DUCKED)
00504                 && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES)
00505                 && (target_dist) < (minDist*minDist) )
00506         {
00507                 G_SetEnemy( NPC, target );
00508                 NPCInfo->enemyLastSeenTime = level.time;
00509                 TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
00510                 return qtrue;
00511         }
00512 
00513         maxViewDist                     = MAX_VIEW_DIST;
00514 
00515         if ( NPCInfo->stats.visrange > maxViewDist )
00516         {//FIXME: should we always just set maxViewDist to this?
00517                 maxViewDist = NPCInfo->stats.visrange;
00518         }
00519 
00520         if ( target_dist > (maxViewDist*maxViewDist) )
00521         {//out of possible visRange
00522                 return qfalse;
00523         }
00524 
00525         //Check FOV first
00526         if ( InFOV( target, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse )
00527                 return qfalse;
00528 
00529         //clearLOS = ( target->client->ps.leanofs ) ? NPC_ClearLOS5( target->client->renderInfo.eyePoint ) : NPC_ClearLOS4( target );
00530         clearLOS = NPC_ClearLOS4( target );
00531 
00532         //Now check for clear line of vision
00533         if ( clearLOS )
00534         {
00535                 vec3_t  targ_org;
00536                 float   hAngle_perc;
00537                 float   vAngle_perc;
00538                 float   target_speed;
00539                 int             target_crouching;
00540                 float   dist_rating;
00541                 float   speed_rating;
00542                 float   turning_rating;
00543                 float   light_level;
00544                 float   FOV_perc;
00545                 float   vis_rating;
00546                 float   dist_influence;
00547                 float   fov_influence;
00548                 float   light_influence;
00549                 float   target_rating;
00550                 int             contents;
00551                 float   realize, cautious;
00552 
00553                 if ( target->client->NPC_class == CLASS_ATST )
00554                 {//can't miss 'em!
00555                         G_SetEnemy( NPC, target );
00556                         TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
00557                         return qtrue;
00558                 }
00559                 VectorSet(targ_org, target->r.currentOrigin[0],target->r.currentOrigin[1],target->r.currentOrigin[2]+target->r.maxs[2]-4);
00560                 hAngle_perc                     = NPC_GetHFOVPercentage( targ_org, NPC->client->renderInfo.eyePoint, NPC->client->renderInfo.eyeAngles, NPCInfo->stats.hfov );
00561                 vAngle_perc                     = NPC_GetVFOVPercentage( targ_org, NPC->client->renderInfo.eyePoint, NPC->client->renderInfo.eyeAngles, NPCInfo->stats.vfov );
00562 
00563                 //Scale them vertically some, and horizontally pretty harshly
00564                 vAngle_perc *= vAngle_perc;//( vAngle_perc * vAngle_perc );
00565                 hAngle_perc *= ( hAngle_perc * hAngle_perc );
00566 
00567                 //Cap our vertical vision severely
00568                 //if ( vAngle_perc <= 0.3f ) // was 0.5f
00569                 //      return qfalse;
00570 
00571                 //Assess the player's current status
00572                 target_dist                     = Distance( target->r.currentOrigin, NPC->r.currentOrigin );
00573 
00574                 target_speed            = VectorLength( target->client->ps.velocity );
00575                 target_crouching        = ( target->client->pers.cmd.upmove < 0 );
00576                 dist_rating                     = ( target_dist / maxViewDist );
00577                 speed_rating            = ( target_speed / MAX_VIEW_SPEED );
00578                 turning_rating          = 5.0f;//AngleDelta( target->client->ps.viewangles[PITCH], target->lastAngles[PITCH] )/180.0f + AngleDelta( target->client->ps.viewangles[YAW], target->lastAngles[YAW] )/180.0f;
00579                 light_level                     = (255/MAX_LIGHT_INTENSITY); //( target->lightLevel / MAX_LIGHT_INTENSITY );
00580                 FOV_perc                        = 1.0f - ( hAngle_perc + vAngle_perc ) * 0.5f;  //FIXME: Dunno about the average...
00581                 vis_rating                      = 0.0f;
00582                 
00583                 //Too dark
00584                 if ( light_level < MIN_LIGHT_THRESHOLD )
00585                         return qfalse;
00586 
00587                 //Too close?
00588                 if ( dist_rating < DISTANCE_THRESHOLD )
00589                 {
00590                         G_SetEnemy( NPC, target );
00591                         TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
00592                         return qtrue;
00593                 }
00594 
00595                 //Out of range
00596                 if ( dist_rating > 1.0f )
00597                         return qfalse;
00598                 
00599                 //Cap our speed checks
00600                 if ( speed_rating > 1.0f )
00601                         speed_rating = 1.0f;
00602                 
00603 
00604                 //Calculate the distance, fov and light influences
00605                 //...Visibilty linearly wanes over distance
00606                 dist_influence  = DISTANCE_SCALE * ( ( 1.0f - dist_rating ) );
00607                 //...As the percentage out of the FOV increases, straight perception suffers on an exponential scale
00608                 fov_influence           = FOV_SCALE * ( 1.0f - FOV_perc );
00609                 //...Lack of light hides, abundance of light exposes
00610                 light_influence = ( light_level - 0.5f ) * LIGHT_SCALE;
00611                 
00612                 //Calculate our base rating
00613                 target_rating           = dist_influence + fov_influence + light_influence;
00614                 
00615                 //Now award any final bonuses to this number
00616                 contents = trap_PointContents( targ_org, target->s.number );
00617                 if ( contents&CONTENTS_WATER )
00618                 {
00619                         int myContents = trap_PointContents( NPC->client->renderInfo.eyePoint, NPC->s.number );
00620                         if ( !(myContents&CONTENTS_WATER) )
00621                         {//I'm not in water
00622                                 if ( NPC->client->NPC_class == CLASS_SWAMPTROOPER )
00623                                 {//these guys can see in in/through water pretty well
00624                                         vis_rating = 0.10f;//10% bonus
00625                                 }
00626                                 else
00627                                 {
00628                                         vis_rating = 0.35f;//35% bonus
00629                                 }
00630                         }
00631                         else
00632                         {//else, if we're both in water
00633                                 if ( NPC->client->NPC_class == CLASS_SWAMPTROOPER )
00634                                 {//I can see him just fine
00635                                 }
00636                                 else
00637                                 {
00638                                         vis_rating = 0.15f;//15% bonus
00639                                 }
00640                         }
00641                 }
00642                 else 
00643                 {//not in water
00644                         if ( contents&CONTENTS_FOG )
00645                         {
00646                                 vis_rating = 0.15f;//15% bonus
00647                         }
00648                 }
00649 
00650                 target_rating *= (1.0f - vis_rating);
00651 
00652                 //...Motion draws the eye quickly
00653                 target_rating += speed_rating * SPEED_SCALE;
00654                 target_rating += turning_rating * TURNING_SCALE;
00655                 //FIXME: check to see if they're animating, too?  But can we do something as simple as frame != oldframe?
00656 
00657                 //...Smaller targets are harder to indentify
00658                 if ( target_crouching )
00659                 {
00660                         target_rating *= 0.9f;  //10% bonus
00661                 }       
00662         
00663                 //If he's violated the threshold, then realize him
00664                 //float difficulty_scale = 1.0f + (2.0f-g_spskill.value);//if playing on easy, 20% harder to be seen...?
00665                 if ( NPC->client->NPC_class == CLASS_SWAMPTROOPER )
00666                 {//swamptroopers can see much better
00667                         realize = (float)CAUTIOUS_THRESHOLD;
00668                         cautious = (float)CAUTIOUS_THRESHOLD * 0.75f;
00669                 }
00670                 else
00671                 {
00672                         realize = (float)REALIZE_THRESHOLD;
00673                         cautious = (float)CAUTIOUS_THRESHOLD * 0.75f;
00674                 }
00675 
00676                 if ( target_rating > realize && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
00677                 {
00678                         G_SetEnemy( NPC, target );
00679                         NPCInfo->enemyLastSeenTime = level.time;
00680                         TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
00681                         return qtrue;
00682                 }
00683 
00684                 //If he's above the caution threshold, then realize him in a few seconds unless he moves to cover
00685                 if ( target_rating > cautious && !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
00686                 {//FIXME: ambushing guys should never talk
00687                         if ( TIMER_Done( NPC, "enemyLastVisible" ) )
00688                         {//If we haven't already, start the counter
00689                                 int     lookTime = Q_irand( 4500, 8500 );
00690                                 //NPCInfo->timeEnemyLastVisible = level.time + 2000;
00691                                 TIMER_Set( NPC, "enemyLastVisible", lookTime );
00692                                 //TODO: Play a sound along the lines of, "Huh?  What was that?"
00693                                 ST_Speech( NPC, SPEECH_SIGHT, 0 );
00694                                 NPC_TempLookTarget( NPC, target->s.number, lookTime, lookTime );
00695                                 //FIXME: set desired yaw and pitch towards this guy?
00696                         }
00697                         else if ( TIMER_Get( NPC, "enemyLastVisible" ) <= level.time + 500 && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )     //FIXME: Is this reliable?
00698                         {
00699                                 if ( NPCInfo->rank < RANK_LT && !Q_irand( 0, 2 ) )
00700                                 {
00701                                         int     interrogateTime = Q_irand( 2000, 4000 );
00702                                         ST_Speech( NPC, SPEECH_SUSPICIOUS, 0 );
00703                                         TIMER_Set( NPC, "interrogating", interrogateTime );
00704                                         G_SetEnemy( NPC, target );
00705                                         NPCInfo->enemyLastSeenTime = level.time;
00706                                         TIMER_Set( NPC, "attackDelay", interrogateTime );
00707                                         TIMER_Set( NPC, "stand", interrogateTime );
00708                                 }
00709                                 else
00710                                 {
00711                                         G_SetEnemy( NPC, target );
00712                                         NPCInfo->enemyLastSeenTime = level.time;
00713                                         //FIXME: ambush guys (like those popping out of water) shouldn't delay...
00714                                         TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
00715                                         TIMER_Set( NPC, "stand", Q_irand( 500, 2500 ) );
00716                                 }
00717                                 return qtrue;
00718                         }
00719 
00720                         return qfalse;
00721                 }
00722         }
00723 
00724         return qfalse;
00725 }

void NPC_CheckGetNewWeapon void   ) 
 

Definition at line 3060 of file NPC_combat.c.

03061 {
03062         if ( NPC->s.weapon == WP_NONE && NPC->enemy )
03063         {//if running away because dropped weapon...
03064                 if ( NPCInfo->goalEntity 
03065                         && NPCInfo->goalEntity == NPCInfo->tempGoal
03066                         && NPCInfo->goalEntity->enemy
03067                         && !NPCInfo->goalEntity->enemy->inuse )
03068                 {//maybe was running at a weapon that was picked up
03069                         NPCInfo->goalEntity = NULL;
03070                 }
03071                 if ( TIMER_Done( NPC, "panic" ) && NPCInfo->goalEntity == NULL )
03072                 {//need a weapon, any lying around?
03073                         gentity_t *foundWeap = NPC_SearchForWeapons();
03074                         if ( foundWeap )
03075                         {//try to nav to it
03076                                 /*
03077                                 if ( !trap_Nav_GetBestPathBetweenEnts( NPC, foundWeap, NF_CLEAR_PATH ) 
03078                                         || trap_Nav_GetBestNodeAltRoute( NPC->waypoint, foundWeap->waypoint ) == WAYPOINT_NONE )
03079                                 {//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
03080                                         if ( !NAV_ClearPathToPoint( NPC, NPC->r.mins, NPC->r.maxs, foundWeap->r.currentOrigin, NPC->clipmask, ENTITYNUM_NONE ) )
03081                                         {//don't even have a clear straight path to this one
03082                                         }
03083                                         else
03084                                         {
03085                                                 NPC_SetPickUpGoal( foundWeap );
03086                                         }
03087                                 }
03088                                 else
03089                                 */
03090                                 {
03091                                         NPC_SetPickUpGoal( foundWeap );
03092                                 }
03093                         }
03094                 }
03095         }
03096 }

qboolean NPC_CheckPlayerTeamStealth void   ) 
 

void NPC_ST_Pain gentity_t self,
gentity_t attacker,
int  damage
 

Definition at line 260 of file NPC_AI_Stormtrooper.c.

References EV_PUSHED1, EV_PUSHED3, G_AddVoiceEvent(), gentity_t, gentity_s::health, gNPC_t::localState, LSTATE_UNDERFIRE, gentity_s::NPC, NPC_Pain(), Q_irand(), and TIMER_Set().

Referenced by NPC_PainFunc().

00261 {
00262         self->NPC->localState = LSTATE_UNDERFIRE;
00263 
00264         TIMER_Set( self, "duck", -1 );
00265         TIMER_Set( self, "hideTime", -1 );
00266         TIMER_Set( self, "stand", 2000 );
00267 
00268         NPC_Pain( self, attacker, damage );
00269 
00270         if ( !damage && self->health > 0 )
00271         {//FIXME: better way to know I was pushed
00272                 G_AddVoiceEvent( self, Q_irand(EV_PUSHED1, EV_PUSHED3), 2000 );
00273         }
00274 }

void NPC_ST_SayMovementSpeech void   ) 
 

Definition at line 304 of file NPC_AI_Stormtrooper.c.

References CLASS_IMPERIAL, gentity_s::client, AIGroupInfo_s::commander, gNPC_t::group, gNPC_t::movementSpeech, gNPC_t::movementSpeechChance, NPC, gclient_s::NPC_class, NPCInfo, and Q_irand().

00305 {
00306         if ( !NPCInfo->movementSpeech )
00307         {
00308                 return;
00309         }
00310         if ( NPCInfo->group && 
00311                 NPCInfo->group->commander && 
00312                 NPCInfo->group->commander->client && 
00313                 NPCInfo->group->commander->client->NPC_class == CLASS_IMPERIAL && 
00314                 !Q_irand( 0, 3 ) )
00315         {//imperial (commander) gives the order
00316                 ST_Speech( NPCInfo->group->commander, NPCInfo->movementSpeech, NPCInfo->movementSpeechChance );
00317         }
00318         else
00319         {//really don't want to say this unless we can actually get there...
00320                 ST_Speech( NPC, NPCInfo->movementSpeech, NPCInfo->movementSpeechChance );
00321         }
00322 
00323         NPCInfo->movementSpeech = 0;
00324         NPCInfo->movementSpeechChance = 0.0f;
00325 }

void NPC_ST_StoreMovementSpeech int  speech,
float  chance
 

Definition at line 327 of file NPC_AI_Stormtrooper.c.

References gNPC_t::movementSpeech, gNPC_t::movementSpeechChance, and NPCInfo.

Referenced by ST_Commander().

00328 {
00329         NPCInfo->movementSpeech = speech;
00330         NPCInfo->movementSpeechChance = chance;
00331 }

void NPC_TempLookTarget gentity_t self,
int  lookEntNum,
int  minLookTime,
int  maxLookTime
 

Definition at line 661 of file NPC_reactions.c.

References gentity_s::client, EF2_HELD_BY_MONSTER, playerState_s::eFlags2, gentity_t, level, NPC_CheckLookTarget(), NPC_SetLookTarget(), gclient_s::ps, Q_irand(), and level_locals_t::time.

Referenced by NPC_CheckEnemyStealth(), and NPC_Respond().

00662 {
00663         if ( !self->client )
00664         {
00665                 return;
00666         }
00667 
00668         if ( (self->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
00669         {//lookTarget is set by and to the monster that's holding you, no other operations can change that
00670                 return;
00671         }
00672 
00673         if ( !minLookTime )
00674         {
00675                 minLookTime = 1000;
00676         }
00677 
00678         if ( !maxLookTime )
00679         {
00680                 maxLookTime = 1000;
00681         }
00682 
00683         if ( !NPC_CheckLookTarget( self ) )
00684         {//Not already looking at something else
00685                 //Look at him for 1 to 3 seconds
00686                 NPC_SetLookTarget( self, lookEntNum, level.time + Q_irand( minLookTime, maxLookTime ) );
00687         }
00688 }

void ST_AggressionAdjust gentity_t self,
int  change
 

Definition at line 60 of file NPC_AI_Stormtrooper.c.

References gNPCstats_e::aggression, gentity_s::client, gentity_t, gentity_s::NPC, NPCTEAM_PLAYER, gclient_s::playerTeam, and gNPC_t::stats.

Referenced by AI_GroupMemberKilled(), and ST_MarkToCover().

00061 {
00062         int     upper_threshold, lower_threshold;
00063 
00064         self->NPC->stats.aggression += change;
00065         
00066         //FIXME: base this on initial NPC stats
00067         if ( self->client->playerTeam == NPCTEAM_PLAYER )
00068         {//good guys are less aggressive
00069                 upper_threshold = 7;
00070                 lower_threshold = 1;
00071         }
00072         else
00073         {//bad guys are more aggressive
00074                 upper_threshold = 10;
00075                 lower_threshold = 3;
00076         }
00077 
00078         if ( self->NPC->stats.aggression > upper_threshold )
00079         {
00080                 self->NPC->stats.aggression = upper_threshold;
00081         }
00082         else if ( self->NPC->stats.aggression < lower_threshold )
00083         {
00084                 self->NPC->stats.aggression = lower_threshold;
00085         }
00086 }

int ST_ApproachEnemy gentity_t self  ) 
 

Definition at line 1550 of file NPC_AI_Stormtrooper.c.

References gNPC_t::combatPoint, CP_CLEAR, CP_CLOSEST, gentity_t, level, gentity_s::NPC, NPC_FreeCombatPoint(), Q_irand(), qfalse, level_locals_t::time, TIMER_Get(), and TIMER_Set().

Referenced by ST_Commander().

01551 {
01552         TIMER_Set( self, "attackDelay", Q_irand( 250, 500 ) );
01553         //TIMER_Set( self, "duck", -1 );
01554         TIMER_Set( self, "stick", Q_irand( 1000, 2000 ) );
01555         TIMER_Set( self, "stand", -1 );
01556         TIMER_Set( self, "scoutTime", TIMER_Get( self, "stick" )-level.time+Q_irand(5000, 10000) );
01557         //leave my combat point
01558         NPC_FreeCombatPoint( self->NPC->combatPoint, qfalse );
01559         //return the relevant combat point flags
01560         return (CP_CLEAR|CP_CLOSEST);
01561 }

void ST_ClearTimers gentity_t ent  ) 
 

Definition at line 88 of file NPC_AI_Stormtrooper.c.

References gentity_t, and TIMER_Set().

Referenced by NPC_SetMiscDefaultData().

00089 {
00090         TIMER_Set( ent, "chatter", 0 );
00091         TIMER_Set( ent, "duck", 0 );
00092         TIMER_Set( ent, "stand", 0 );
00093         TIMER_Set( ent, "shuffleTime", 0 );
00094         TIMER_Set( ent, "sleepTime", 0 );
00095         TIMER_Set( ent, "enemyLastVisible", 0 );
00096         TIMER_Set( ent, "roamTime", 0 );
00097         TIMER_Set( ent, "hideTime", 0 );
00098         TIMER_Set( ent, "attackDelay", 0 );     //FIXME: Slant for difficulty levels
00099         TIMER_Set( ent, "stick", 0 );
00100         TIMER_Set( ent, "scoutTime", 0 );
00101         TIMER_Set( ent, "flee", 0 );
00102         TIMER_Set( ent, "interrogating", 0 );
00103         TIMER_Set( ent, "verifyCP", 0 );
00104 }

void ST_Commander void   ) 
 

Q_irand( 0, 2 ) )

runner &&

Definition at line 1724 of file NPC_AI_Stormtrooper.c.

References AIGroupInfo_s::activeMemberNum, AEL_DANGER, AEL_DANGER_GREAT, AI_GroupUpdateSquadstates(), gNPC_t::aiFlags, AIGroupInfo_t, gNPC_t::behaviorState, gNPC_t::blockedSpeechDebounceTime, gNPC_t::blockingEntNum, BS_DEFAULT, BS_SEARCH, gentity_s::client, gNPC_t::combatPoint, level_locals_t::combatPoints, AIGroupInfo_s::commander, CP_ANY, CP_APPROACH_ENEMY, CP_AVOID, CP_AVOID_ENEMY, CP_CLEAR, CP_CLOSEST, CP_COVER, CP_DUCK, CP_FLANK, CP_FLEE, CP_HAS_ROUTE, CP_INVESTIGATE, CP_NEAREST, CP_RETREAT, CP_SAFE, CP_SQUAD, CPF_DUCK, entityShared_t::currentOrigin, d_asynchronousGroupAI, DotProduct, gentity_s::enemy, AIGroupInfo_s::enemy, AIGroupInfo_s::enemyLastSeenPos, ET_ITEM, entityState_s::eType, G_ClearEnemy(), g_entities, gentity_t, gNPC_t::goalEntity, gNPC_t::group, gentity_s::health, vmCvar_t::integer, AIGroupInfo_s::lastClearShotTime, gNPC_t::lastFailedCombatPoint, AIGroupInfo_s::lastSeenEnemyTime, level, gNPC_t::localState, LSTATE_NONE, LSTATE_UNDERFIRE, AIGroupInfo_s::member, MIN_ROCKET_DIST_SQUARED, AIGroupInfo_s::morale, NAV_FindClosestWaypointForEnt(), gentity_s::NPC, NPC, NPC_BSSearchStart(), NPC_CheckAlertEvents(), NPC_CheckForDanger(), NPC_ClearLOS4(), NPC_FindCombatPoint(), NPC_SetCombatPoint(), NPC_SetMoveGoal(), NPC_ST_StoreMovementSpeech(), NPC_StartFlee(), NPCAI_BLOCKED, NPCInfo, NULL, AIGroupMember_s::number, AIGroupInfo_s::numGroup, AIGroupInfo_s::numState, combatPoint_t::origin, AIGroupInfo_s::processed, gclient_s::ps, Q3_INFINITE, Q_irand(), qboolean, qfalse, qtrue, gentity_s::r, gNPC_t::rank, RANK_ENSIGN, RestoreNPCGlobals(), gentity_s::s, playerState_s::saberHolstered, SaveNPCGlobals(), SCF_CHASE_ENEMIES, SCF_USE_CP_NEAREST, gNPC_t::scriptFlags, SetNPCGlobals(), SPEECH_CHASE, SPEECH_COVER, SPEECH_ESCAPING, SPEECH_LOST, SPEECH_OUTFLANK, SPEECH_YELL, SQUAD_IDLE, SQUAD_POINT, SQUAD_RETREAT, SQUAD_SCOUT, SQUAD_TRANSITION, gNPC_t::squadState, ST_ApproachEnemy(), ST_GetCPFlags(), ST_HuntEnemy(), ST_TrackEnemy(), ST_TransferMoveGoal(), gNPC_t::tempGoal, TID_MOVE_NAV, level_locals_t::time, TIMER_Done(), TIMER_Set(), trap_ICARUS_TaskIDPending(), trap_InPVS(), trap_Nav_GetPathCost(), vec3_t, VectorNormalize(), VectorSubtract, gentity_s::waypoint, WAYPOINT_NONE, playerState_s::weapon, entityState_s::weapon, WP_BLASTER, WP_NONE, WP_ROCKET_LAUNCHER, and WP_SABER.

Referenced by NPC_BSST_Attack().

01725 {
01726         int             i, j;
01727         int             cp, cpFlags_org, cpFlags;
01728         AIGroupInfo_t   *group = NPCInfo->group;
01729         gentity_t       *member;//, *buddy;
01730         qboolean        runner = qfalse;
01731         qboolean        enemyLost = qfalse;
01732         qboolean        enemyProtected = qfalse;
01733         qboolean        scouting = qfalse;
01734         int                     squadState;
01735         int                     curMemberNum, lastMemberNum;
01736         float           avoidDist;
01737 
01738         group->processed = qtrue;
01739 
01740         if ( group->enemy == NULL || group->enemy->client == NULL )
01741         {//hmm, no enemy...?!
01742                 return;
01743         }
01744 
01745         //FIXME: have this group commander check the enemy group (if any) and see if they have 
01746         //              superior numbers.  If they do, fall back rather than advance.  If you have
01747         //              superior numbers, advance on them.
01748         //FIXME: find the group commander and have him occasionally give orders when there is speech
01749         //FIXME: start fleeing when only a couple of you vs. a lightsaber, possibly give up if the only one left
01750 
01751         SaveNPCGlobals();
01752 
01753         if ( group->lastSeenEnemyTime < level.time - 180000 )
01754         {//dissolve the group
01755                 ST_Speech( NPC, SPEECH_LOST, 0.0f );
01756                 group->enemy->waypoint = NAV_FindClosestWaypointForEnt( group->enemy, WAYPOINT_NONE );
01757                 for ( i = 0; i < group->numGroup; i++ )
01758                 {
01759                         member = &g_entities[group->member[i].number];
01760                         SetNPCGlobals( member );
01761                         if ( trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
01762                         {//running somewhere that a script requires us to go, don't break from that
01763                                 continue;
01764                         }
01765                         if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )
01766                         {//not allowed to move on my own
01767                                 continue;
01768                         }
01769                         //Lost enemy for three minutes?  go into search mode?
01770                         G_ClearEnemy( NPC );
01771                         NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, group->enemy->waypoint );
01772                         if ( NPC->waypoint == WAYPOINT_NONE )
01773                         {
01774                                 NPCInfo->behaviorState = BS_DEFAULT;//BS_PATROL;
01775                         }
01776                         else if ( group->enemy->waypoint == WAYPOINT_NONE || (trap_Nav_GetPathCost( NPC->waypoint, group->enemy->waypoint ) >= Q3_INFINITE) )
01777                         {
01778                                 NPC_BSSearchStart( NPC->waypoint, BS_SEARCH );
01779                         }
01780                         else
01781                         {
01782                                 NPC_BSSearchStart( group->enemy->waypoint, BS_SEARCH );
01783                         }
01784                 }
01785                 group->enemy = NULL;
01786                 RestoreNPCGlobals();
01787                 return;
01788         }
01789 
01790 
01791         //See if anyone in our group is not alerted and alert them
01792         /*
01793         for ( i = 0; i < group->numGroup; i++ )
01794         {
01795                 member = &g_entities[group->member[i].number];
01796                 if ( !member->enemy )
01797                 {//he's not mad, so get him mad
01798                         //Have his buddy tell him to get mad
01799                         if ( group->member[i].closestBuddy != ENTITYNUM_NONE )
01800                         {
01801                                 buddy = &g_entities[group->member[i].closestBuddy];
01802                                 if ( buddy->enemy == group->enemy )
01803                                 {
01804                                         SetNPCGlobals( buddy );
01805                                         ST_Speech( NPC, SPEECH_CHARGE, 0.7f );
01806                                 }
01807                         }
01808                         SetNPCGlobals( member );
01809                         G_SetEnemy( member, group->enemy );
01810                 }
01811         }
01812         */
01813         //Okay, everyone is mad
01814 
01815         //see if anyone is running
01816         if ( group->numState[SQUAD_SCOUT] > 0 || 
01817                 group->numState[SQUAD_TRANSITION] > 0 || 
01818                 group->numState[SQUAD_RETREAT] > 0 )
01819         {//someone is running
01820                 runner = qtrue;
01821         }
01822 
01823         if (  group->lastSeenEnemyTime > level.time - 32000 && group->lastSeenEnemyTime < level.time - 30000 )
01824         {//no-one has seen the enemy for 30 seconds// and no-one is running after him
01825                 if ( group->commander && !Q_irand( 0, 1 ) )
01826                 {
01827                         ST_Speech( group->commander, SPEECH_ESCAPING, 0.0f );
01828                 }
01829                 else
01830                 {
01831                         ST_Speech( NPC, SPEECH_ESCAPING, 0.0f );
01832                 }
01833                 //don't say this again
01834                 NPCInfo->blockedSpeechDebounceTime = level.time + 3000;
01835         }
01836 
01837         if ( group->lastSeenEnemyTime < level.time - 10000 )
01838         {//no-one has seen the enemy for at least 10 seconds!  Should send a scout
01839                 enemyLost = qtrue;
01840         }
01841 
01842         if ( group->lastClearShotTime < level.time - 5000 )
01843         {//no-one has had a clear shot for 5 seconds!
01844                 enemyProtected = qtrue;
01845         }
01846 
01847         //Go through the list:
01848 
01849         //Everyone should try to get to a combat point if possible
01850         if ( d_asynchronousGroupAI.integer )
01851         {//do one member a turn
01852                 group->activeMemberNum++;
01853                 if ( group->activeMemberNum >= group->numGroup )
01854                 {
01855                         group->activeMemberNum = 0;
01856                 }
01857                 curMemberNum = group->activeMemberNum;
01858                 lastMemberNum = curMemberNum + 1;
01859         }
01860         else
01861         {
01862                 curMemberNum = 0;
01863                 lastMemberNum = group->numGroup;
01864         }
01865         for ( i = curMemberNum; i < lastMemberNum; i++ )
01866         {
01867                 //reset combat point flags
01868                 cp = -1;
01869                 cpFlags = 0;
01870                 squadState = SQUAD_IDLE;
01871                 avoidDist = 0;
01872                 scouting = qfalse;
01873 
01874                 //get the next guy
01875                 member = &g_entities[group->member[i].number];
01876                 if ( !member->enemy )
01877                 {//don't include guys that aren't angry
01878                         continue;
01879                 }
01880                 SetNPCGlobals( member );
01881 
01882                 if ( !TIMER_Done( NPC, "flee" ) )
01883                 {//running away
01884                         continue;
01885                 }
01886 
01887                 if ( trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
01888                 {//running somewhere that a script requires us to go
01889                         continue;
01890                 }
01891 
01892                 if ( NPC->s.weapon == WP_NONE 
01893                         && NPCInfo->goalEntity 
01894                         && NPCInfo->goalEntity == NPCInfo->tempGoal
01895                         && NPCInfo->goalEntity->enemy
01896                         && NPCInfo->goalEntity->enemy->s.eType == ET_ITEM )
01897                 {//running to pick up a gun, don't do other logic
01898                         continue;
01899                 }
01900 
01901                 //see if this member should start running (only if have no officer... FIXME: should always run from AEL_DANGER_GREAT?)
01902                 if ( !group->commander || group->commander->NPC->rank < RANK_ENSIGN )
01903                 {
01904                         if ( NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) )
01905                         {//going to run
01906                                 ST_Speech( NPC, SPEECH_COVER, 0 );
01907                                 continue;
01908                         }
01909                 }
01910                 
01911                 if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )
01912                 {//not allowed to do combat-movement
01913                         continue;
01914                 }
01915 
01916                 //check the local state
01917                 if ( NPCInfo->squadState != SQUAD_RETREAT )
01918                 {//not already retreating
01919                         if ( NPC->client->ps.weapon == WP_NONE )
01920                         {//weaponless, should be hiding
01921                                 if ( NPCInfo->goalEntity == NULL || NPCInfo->goalEntity->enemy == NULL || NPCInfo->goalEntity->enemy->s.eType != ET_ITEM )
01922                                 {//not running after a pickup
01923                                         if ( TIMER_Done( NPC, "hideTime" ) || (DistanceSquared( group->enemy->r.currentOrigin, NPC->r.currentOrigin ) < 65536 && NPC_ClearLOS4( NPC->enemy )) )
01924                                         {//done hiding or enemy near and can see us
01925                                                 //er, start another flee I guess?
01926                                                 NPC_StartFlee( NPC->enemy, NPC->enemy->r.currentOrigin, AEL_DANGER_GREAT, 5000, 10000 );
01927                                         }//else, just hang here
01928                                 }
01929                                 continue;
01930                         }
01931                         if ( TIMER_Done( NPC, "roamTime" ) && TIMER_Done( NPC, "hideTime" ) && NPC->health > 10 && !trap_InPVS( group->enemy->r.currentOrigin, NPC->r.currentOrigin ) )
01932                         {//cant even see enemy
01933                                 //better go after him
01934                                 cpFlags |= (CP_CLEAR|CP_COVER);
01935                         }
01936                         else if ( NPCInfo->localState == LSTATE_UNDERFIRE )
01937                         {//we've been shot
01938                                 switch( group->enemy->client->ps.weapon )
01939                                 {
01940                                 case WP_SABER:
01941                                         if ( DistanceSquared( group->enemy->r.currentOrigin, NPC->r.currentOrigin ) < 65536 )//256 squared
01942                                         {
01943                                                 cpFlags |= (CP_AVOID_ENEMY|CP_COVER|CP_AVOID|CP_RETREAT);
01944                                                 if ( !group->commander || group->commander->NPC->rank  < RANK_ENSIGN )
01945                                                 {
01946                                                         squadState = SQUAD_RETREAT;
01947                                                 }
01948                                                 avoidDist = 256;
01949                                         }
01950                                         break;
01951                                 default:
01952                                 case WP_BLASTER:
01953                                         cpFlags |= (CP_COVER);
01954                                         break;
01955                                 }
01956                                 if ( NPC->health <= 10 )
01957                                 {
01958                                         if ( !group->commander || group->commander->NPC->rank < RANK_ENSIGN )
01959                                         {
01960                                                 cpFlags |= (CP_FLEE|CP_AVOID|CP_RETREAT);
01961                                                 squadState = SQUAD_RETREAT;
01962                                         }
01963                                 }
01964                         }
01965                         else
01966                         {//not hit, see if there are other reasons we should run
01967                                 if ( trap_InPVS( NPC->r.currentOrigin, group->enemy->r.currentOrigin ) )
01968                                 {//in the same room as enemy
01969                                         if ( NPC->client->ps.weapon == WP_ROCKET_LAUNCHER &&
01970                                                 DistanceSquared( group->enemy->r.currentOrigin, NPC->r.currentOrigin ) < MIN_ROCKET_DIST_SQUARED &&
01971                                                 NPCInfo->squadState != SQUAD_TRANSITION )
01972                                         {//too close for me to fire my weapon and I'm not already on the move
01973                                                 cpFlags |= (CP_AVOID_ENEMY|CP_CLEAR|CP_AVOID);
01974                                                 avoidDist = 256;
01975                                         }
01976                                         else
01977                                         {
01978                                                 switch( group->enemy->client->ps.weapon )
01979                                                 {
01980                                                 case WP_SABER:
01981                                                         //if ( group->enemy->client->ps.SaberLength() > 0 )
01982                                                         if (!group->enemy->client->ps.saberHolstered)
01983                                                         {
01984                                                                 if ( DistanceSquared( group->enemy->r.currentOrigin, NPC->r.currentOrigin ) < 65536 )
01985                                                                 {
01986                                                                         if ( TIMER_Done( NPC, "hideTime" ) )
01987                                                                         {
01988                                                                                 if ( NPCInfo->squadState != SQUAD_TRANSITION )
01989                                                                                 {//not already moving: FIXME: we need to see if where we're going is good now?
01990                                                                                         cpFlags |= (CP_AVOID_ENEMY|CP_CLEAR|CP_AVOID);
01991                                                                                         avoidDist = 256;
01992                                                                                 }
01993                                                                         }
01994                                                                 }
01995                                                         }
01996                                                 default:
01997                                                         break;
01998                                                 }
01999                                         }
02000                                 }
02001                         }
02002                 }
02003 
02004                 if ( !cpFlags )
02005                 {//okay, we have no new enemy-driven reason to run... let's use tactics now
02006                         if ( runner && NPCInfo->combatPoint != -1 )
02007                         {//someone is running and we have a combat point already
02008                                 if ( NPCInfo->squadState != SQUAD_SCOUT &&
02009                                         NPCInfo->squadState != SQUAD_TRANSITION &&
02010                                         NPCInfo->squadState != SQUAD_RETREAT )
02011                                 {//it's not us
02012                                         if ( TIMER_Done( NPC, "verifyCP" ) && DistanceSquared( NPC->r.currentOrigin, level.combatPoints[NPCInfo->combatPoint].origin ) > 64*64 )
02013                                         {//1 - 3 seconds have passed since you chose a CP, see if you're there since, for some reason, you've stopped running...
02014                                                 //uh, WTF, we're not on our combat point?
02015                                                 //er, try again, I guess?
02016                                                 cp = NPCInfo->combatPoint;
02017                                                 cpFlags |= ST_GetCPFlags();
02018                                         }
02019                                         else
02020                                         {//cover them
02021                                                 //stop ducking
02022                                                 TIMER_Set( NPC, "duck", -1 );
02023                                                 //start shooting
02024                                                 TIMER_Set( NPC, "attackDelay", -1 );
02025                                                 //AI should take care of the rest - fire at enemy
02026                                         }
02027                                 }
02028                                 else
02029                                 {//we're running
02030                                         //see if we're blocked
02031                                         if ( NPCInfo->aiFlags & NPCAI_BLOCKED )
02032                                         {//dammit, something is in our way
02033                                                 //see if it's one of ours
02034                                                 for ( j = 0; j < group->numGroup; j++ )
02035                                                 {
02036                                                         if ( group->member[j].number == NPCInfo->blockingEntNum )
02037                                                         {//we're being blocked by one of our own, pass our goal onto them and I'll stand still
02038                                                                 ST_TransferMoveGoal( NPC, &g_entities[group->member[j].number] );
02039                                                                 break;
02040                                                         }
02041                                                 }
02042                                         }
02043                                         //we don't need to do anything else
02044                                         continue;
02045                                 }
02046                         }
02047                         else
02048                         {//okay no-one is running, use some tactics
02049                                 if ( NPCInfo->combatPoint != -1 )
02050                                 {//we have a combat point we're supposed to be running to
02051                                         if ( NPCInfo->squadState != SQUAD_SCOUT &&
02052                                                 NPCInfo->squadState != SQUAD_TRANSITION &&
02053                                                 NPCInfo->squadState != SQUAD_RETREAT )
02054                                         {//but we're not running
02055                                                 if ( TIMER_Done( NPC, "verifyCP" ) )
02056                                                 {//1 - 3 seconds have passed since you chose a CP, see if you're there since, for some reason, you've stopped running...
02057                                                         if ( DistanceSquared( NPC->r.currentOrigin, level.combatPoints[NPCInfo->combatPoint].origin ) > 64*64 )
02058                                                         {//uh, WTF, we're not on our combat point?
02059                                                                 //er, try again, I guess?
02060                                                                 cp = NPCInfo->combatPoint;
02061                                                                 cpFlags |= ST_GetCPFlags();
02062                                                         }
02063                                                 }
02064                                         }
02065                                 }
02066                                 if ( enemyLost )
02067                                 {//if no-one has seen the enemy for a while, send a scout
02068                                         //ask where he went
02069                                         if ( group->numState[SQUAD_SCOUT] <= 0 )
02070                                         {
02071                                                 scouting = qtrue;
02072                                                 NPC_ST_StoreMovementSpeech( SPEECH_CHASE, 0.0f );
02073                                         }
02074                                         //Since no-one else has done this, I should be the closest one, so go after him...
02075                                         ST_TrackEnemy( NPC, group->enemyLastSeenPos );
02076                                         //set me into scout mode
02077                                         AI_GroupUpdateSquadstates( group, NPC, SQUAD_SCOUT );
02078                                         //we're not using a cp, so we need to set runner to true right here
02079                                         runner = qtrue;
02080                                 }
02081                                 else if ( enemyProtected )
02082                                 {//if no-one has a clear shot at the enemy, someone should go after him
02083                                         //FIXME: if I'm in an area where no safe combat points have a clear shot at me, they don't come after me... they should anyway, though after some extra hesitation.
02084                                         //ALSO: seem to give up when behind an area portal?
02085                                         //since no-one else here has done this, I should be the closest one
02086                                         if ( TIMER_Done( NPC, "roamTime" ) && !Q_irand( 0, group->numGroup) )
02087                                         {//only do this if we're ready to move again and we feel like it
02088                                                 cpFlags |= ST_ApproachEnemy( NPC );
02089                                                 //set me into scout mode
02090                                                 AI_GroupUpdateSquadstates( group, NPC, SQUAD_SCOUT );
02091                                         }
02092                                 }
02093                                 else
02094                                 {//group can see and has been shooting at the enemy
02095                                         //see if we should do something fancy?
02096                                         
02097                                         {//we're ready to move
02098                                                 if ( NPCInfo->combatPoint == -1 )
02099                                                 {//we're not on a combat point
02100                                                         if ( 1 )
02101                                                         {//we should go for a combat point
02102                                                                 cpFlags |= ST_GetCPFlags();
02103                                                         }
02104                                                         else
02105                                                         {
02106                                                                 TIMER_Set( NPC, "stick", Q_irand( 2000, 4000 ) );
02107                                                                 TIMER_Set( NPC, "roamTime", Q_irand( 1000, 3000 ) );
02108                                                         }
02109                                                 }
02110                                                 else if ( TIMER_Done( NPC, "roamTime" ) )
02111                                                 {//we are already on a combat point
02112                                                         if ( i == 0 )
02113                                                         {//we're the closest
02114                                                                 if ( (group->morale-group->numGroup>0) && !Q_irand( 0, 4 ) )
02115                                                                 {//try to outflank him
02116                                                                         cpFlags |= (CP_CLEAR|CP_COVER|CP_FLANK|CP_APPROACH_ENEMY);
02117                                                                 }
02118                                                                 else if ( (group->morale-group->numGroup<0) )
02119                                                                 {//better move!
02120                                                                         cpFlags |= ST_GetCPFlags();
02121                                                                 }
02122                                                                 else
02123                                                                 {//If we're point, then get down
02124                                                                         TIMER_Set( NPC, "roamTime", Q_irand( 2000, 5000 ) );
02125                                                                         TIMER_Set( NPC, "stick", Q_irand( 2000, 5000 ) );
02126                                                                         //FIXME: what if we can't shoot from a ducked pos?
02127                                                                         TIMER_Set( NPC, "duck", Q_irand( 3000, 4000 ) );
02128                                                                         AI_GroupUpdateSquadstates( group, NPC, SQUAD_POINT );
02129                                                                 }
02130                                                         }
02131                                                         else if ( i == group->numGroup - 1 )
02132                                                         {//farthest from the enemy
02133                                                                 if ( (group->morale-group->numGroup<0) )
02134                                                                 {//low morale, just hang here
02135                                                                         TIMER_Set( NPC, "roamTime", Q_irand( 2000, 5000 ) );
02136                                                                         TIMER_Set( NPC, "stick", Q_irand( 2000, 5000 ) );
02137                                                                 }
02138                                                                 else if ( (group->morale-group->numGroup>0) )
02139                                                                 {//try to move in on the enemy
02140                                                                         cpFlags |= ST_ApproachEnemy( NPC );
02141                                                                         //set me into scout mode
02142                                                                         AI_GroupUpdateSquadstates( group, NPC, SQUAD_SCOUT );
02143                                                                 }
02144                                                                 else
02145                                                                 {//use normal decision making process
02146                                                                         cpFlags |= ST_GetCPFlags();
02147                                                                 }
02148                                                         }
02149                                                         else
02150                                                         {//someone in-between
02151                                                                 if ( (group->morale-group->numGroup<0) || !Q_irand( 0, 4 ) )
02152                                                                 {//do something
02153                                                                         cpFlags |= ST_GetCPFlags();
02154                                                                 }
02155                                                                 else
02156                                                                 {
02157                                                                         TIMER_Set( NPC, "stick", Q_irand( 2000, 4000 ) );
02158                                                                         TIMER_Set( NPC, "roamTime", Q_irand( 2000, 4000 ) );
02159                                                                 }
02160                                                         }
02161                                                 }
02162                                         }
02163                                         if ( !cpFlags )
02164                                         {//still not moving
02165                                                 //see if we should say something?
02166                                                 /*
02167                                                 if ( NPC->attackDebounceTime < level.time - 2000 )
02168                                                 {//we, personally, haven't shot for 2 seconds
02169                                                         //maybe yell at the enemy?
02170                                                         ST_Speech( NPC, SPEECH_CHARGE, 0.9f );
02171                                                 }
02172                                                 */
02173 
02174                                                 //see if we should do other fun stuff
02175                                                 //toy with ducking
02176                                                 if ( TIMER_Done( NPC, "duck" ) )
02177                                                 {//not ducking
02178                                                         if ( TIMER_Done( NPC, "stand" ) )
02179                                                         {//don't have to keep standing
02180                                                                 if ( NPCInfo->combatPoint == -1 || (level.combatPoints[NPCInfo->combatPoint].flags&CPF_DUCK) )
02181                                                                 {//okay to duck here
02182                                                                         if ( !Q_irand( 0, 3 ) )
02183                                                                         {
02184                                                                                 TIMER_Set( NPC, "duck", Q_irand( 1000, 3000 ) );
02185                                                                         }
02186                                                                 }
02187                                                         }
02188                                                 }
02189                                                 //FIXME: what about CPF_LEAN?
02190                                         }
02191                                 }
02192                         }
02193                 }
02194 
02195                 //clear the local state
02196                 NPCInfo->localState = LSTATE_NONE;
02197 
02198                 if ( NPCInfo->scriptFlags&SCF_USE_CP_NEAREST )
02199                 {
02200                         cpFlags &= ~(CP_FLANK|CP_APPROACH_ENEMY|CP_CLOSEST);
02201                         cpFlags |= CP_NEAREST;
02202                 }
02203                 //Assign combat points
02204                 if ( cpFlags )
02205                 {//we want to run to a combat point
02206                         /*
02207                         if ( NPCInfo->combatPoint != -1 )
02208                         {//if we're on a combat point, we obviously don't want the one we're closest to
02209                                 cpFlags |= CP_AVOID;
02210                         }
02211                         */
02212 
02213                         if ( group->enemy->client->ps.weapon == WP_SABER && /*group->enemy->client->ps.SaberLength() > 0*/!group->enemy->client->ps.saberHolstered )
02214                         {//we obviously want to avoid the enemy if he has a saber
02215                                 cpFlags |= CP_AVOID_ENEMY;
02216                                 avoidDist = 256;
02217                         }
02218 
02219                         //remember what we *wanted* to do...
02220                         cpFlags_org = cpFlags;
02221 
02222                         //now get a combat point
02223                         if ( cp == -1 )
02224                         {//may have had sone set above
02225                                 cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, group->enemy->r.currentOrigin, cpFlags|CP_HAS_ROUTE, avoidDist, NPCInfo->lastFailedCombatPoint );
02226                         }
02227                         while ( cp == -1 && cpFlags != CP_ANY )
02228                         {//start "OR"ing out certain flags to see if we can find *any* point
02229                                 if ( cpFlags & CP_INVESTIGATE )
02230                                 {//don't need to investigate
02231                                         cpFlags &= ~CP_INVESTIGATE;
02232                                 }
02233                                 else if ( cpFlags & CP_SQUAD )
02234                                 {//don't need to stick to squads
02235                                         cpFlags &= ~CP_SQUAD;
02236                                 }
02237                                 else if ( cpFlags & CP_DUCK )
02238                                 {//don't need to duck
02239                                         cpFlags &= ~CP_DUCK;
02240                                 }
02241                                 else if ( cpFlags & CP_NEAREST )
02242                                 {//don't need closest one to me
02243                                         cpFlags &= ~CP_NEAREST;
02244                                 }
02245                                 else if ( cpFlags & CP_FLANK )
02246                                 {//don't need to flank enemy
02247                                         cpFlags &= ~CP_FLANK;
02248                                 }
02249                                 else if ( cpFlags & CP_SAFE )
02250                                 {//don't need one that hasn't been shot at recently
02251                                         cpFlags &= ~CP_SAFE;
02252                                 }
02253                                 else if ( cpFlags & CP_CLOSEST )
02254                                 {//don't need to get closest to enemy
02255                                         cpFlags &= ~CP_CLOSEST;
02256                                         //but let's try to approach at least
02257                                         cpFlags |= CP_APPROACH_ENEMY;
02258                                 }
02259                                 else if ( cpFlags & CP_APPROACH_ENEMY )
02260                                 {//don't need to approach enemy
02261                                         cpFlags &= ~CP_APPROACH_ENEMY;
02262                                 }
02263                                 else if ( cpFlags & CP_COVER )
02264                                 {//don't need cover
02265                                         cpFlags &= ~CP_COVER;
02266                                         //but let's pick one that makes us duck
02267                                         cpFlags |= CP_DUCK;
02268                                 }
02269                                 else if ( cpFlags & CP_CLEAR )
02270                                 {//don't need a clear shot to enemy
02271                                         cpFlags &= ~CP_CLEAR;
02272                                 }
02273                                 else if ( cpFlags & CP_AVOID_ENEMY )
02274                                 {//don't need to avoid enemy
02275                                         cpFlags &= ~CP_AVOID_ENEMY;
02276                                 }
02277                                 else if ( cpFlags & CP_RETREAT )
02278                                 {//don't need to retreat
02279                                         cpFlags &= ~CP_RETREAT;
02280                                 }
02281                                 else if ( cpFlags &CP_FLEE )
02282                                 {//don't need to flee
02283                                         cpFlags &= ~CP_FLEE;
02284                                         //but at least avoid enemy and pick one that gives cover
02285                                         cpFlags |= (CP_COVER|CP_AVOID_ENEMY);
02286                                 }
02287                                 else if ( cpFlags & CP_AVOID )
02288                                 {//okay, even pick one right by me
02289                                         cpFlags &= ~CP_AVOID;
02290                                 }
02291                                 else
02292                                 {
02293                                         cpFlags = CP_ANY;
02294                                 }
02295                                 //now try again
02296                                 cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, group->enemy->r.currentOrigin, cpFlags|CP_HAS_ROUTE, avoidDist, -1 );
02297                         } 
02298                         //see if we got a valid one
02299                         if ( cp != -1 )
02300                         {//found a combat point
02301                                 //let others know that someone is now running
02302                                 runner = qtrue;
02303                                 //don't change course again until we get to where we're going
02304                                 TIMER_Set( NPC, "roamTime", Q3_INFINITE );
02305                                 TIMER_Set( NPC, "verifyCP", Q_irand( 1000, 3000 ) );//don't make sure you're in your CP for 1 - 3 seconds
02306                                 NPC_SetCombatPoint( cp );
02307                                 NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp, NULL );
02308                                 //okay, try a move right now to see if we can even get there
02309 
02310                                 //if ( ST_Move() )
02311                                 {//we actually can get to it, so okay to say you're going there.
02312                                         //FIXME: Hmm... any way we can store this move info so we don't have to do it again
02313                                         //              when our turn to think comes up?
02314 
02315                                         //set us up so others know we're on the move
02316                                         if ( squadState != SQUAD_IDLE )
02317                                         {
02318                                                 AI_GroupUpdateSquadstates( group, NPC, squadState );
02319                                         }
02320                                         else if ( cpFlags&CP_FLEE )
02321                                         {//outright running for your life
02322                                                 AI_GroupUpdateSquadstates( group, NPC, SQUAD_RETREAT );
02323                                         }
02324                                         else
02325                                         {//any other kind of transition between combat points
02326                                                 AI_GroupUpdateSquadstates( group, NPC, SQUAD_TRANSITION );
02327                                         }
02328                                         
02329                                         //unless we're trying to flee, walk slowly
02330                                         if ( !(cpFlags_org&CP_FLEE) )
02331                                         {
02332                                                 //ucmd.buttons |= BUTTON_CAREFUL;
02333                                         }
02334 
02335                                         /*
02336                                         if ( scouting )
02337                                         {//successfully chasing enemy
02338                                                 ST_Speech( NPC, SPEECH_CHASE, 0.0f );
02339                                                 //don't say this again
02340                                                 //group->speechDebounceTime = level.time + 5000;
02341                                         }
02342                                         //flanking:
02343                                         else */if ( cpFlags & CP_FLANK )
02344                                         {
02345                                                 if ( group->numGroup > 1 )
02346                                                 {
02347                                                         NPC_ST_StoreMovementSpeech( SPEECH_OUTFLANK, -1 );
02348                                                 }
02349                                         }
02350                                         else 
02351                                         {//okay, let's cheat
02352                                                 if ( group->numGroup > 1 )
02353                                                 {
02354                                                         float   dot = 1.0f;
02355                                                         if ( !Q_irand( 0, 3 ) )
02356                                                         {//25% of the time, see if we're flanking the enemy
02357                                                                 vec3_t  eDir2Me, eDir2CP;
02358                                                                 
02359                                                                 VectorSubtract( NPC->r.currentOrigin, group->enemy->r.currentOrigin, eDir2Me );
02360                                                                 VectorNormalize( eDir2Me );
02361 
02362                                                                 VectorSubtract( level.combatPoints[NPCInfo->combatPoint].origin, group->enemy->r.currentOrigin, eDir2CP );
02363                                                                 VectorNormalize( eDir2CP );
02364 
02365                                                                 dot = DotProduct( eDir2Me, eDir2CP );
02366                                                         }
02367                                                         
02368                                                         if ( dot < 0.4 )
02369                                                         {//flanking!
02370                                                                 NPC_ST_StoreMovementSpeech( SPEECH_OUTFLANK, -1 );
02371                                                         }
02372                                                         else if ( !Q_irand( 0, 10 ) )
02373                                                         {//regular movement
02374                                                                 NPC_ST_StoreMovementSpeech( SPEECH_YELL, 0.2f );//was SPEECH_COVER
02375                                                         }
02376                                                 }
02377                                         }
02378                                         /*
02379                                         else if ( cpFlags & CP_CLOSEST || cpFlags & CP_APPROACH_ENEMY )
02380                                         {
02381                                                 if ( group->numGroup > 1 )
02382                                                 {
02383                                                         NPC_ST_StoreMovementSpeech( SPEECH_CHASE, 0.4f );
02384                                                 }
02385                                         }
02386                                         */
02387                                 }//else: nothing, a failed move should clear the combatPoint and you can try again next frame
02388                         }
02389                         else if ( NPCInfo->squadState == SQUAD_SCOUT )
02390                         {//we couldn't find a combatPoint by the player, so just go after him directly
02391                                 ST_HuntEnemy( NPC );
02392                                 //set me into scout mode
02393                                 AI_GroupUpdateSquadstates( group, NPC, SQUAD_SCOUT );
02394                                 //AI should take care of rest
02395                         }
02396                 }
02397         }
02398 
02399         RestoreNPCGlobals();
02400         return;
02401 }

int ST_GetCPFlags void   ) 
 

Definition at line 1628 of file NPC_AI_Stormtrooper.c.

References CLASS_IMPERIAL, gentity_s::client, AIGroupInfo_s::commander, CP_APPROACH_ENEMY, CP_AVOID, CP_CLEAR, CP_CLOSEST, CP_COVER, CP_FLANK, CP_FLEE, CP_NEAREST, CP_RETREAT, CP_SAFE, gNPC_t::group, AIGroupInfo_s::morale, NPC, gclient_s::NPC_class, NPCInfo, AIGroupInfo_s::numGroup, Q_irand(), SCF_USE_CP_NEAREST, gNPC_t::scriptFlags, SPEECH_CHASE, and SPEECH_YELL.

Referenced by ST_Commander().

01629 {
01630         int cpFlags = 0;
01631         if ( NPC && NPCInfo->group )
01632         {
01633                 if ( NPC == NPCInfo->group->commander && NPC->client->NPC_class == CLASS_IMPERIAL )
01634                 {//imperials hang back and give orders
01635                         if ( NPCInfo->group->numGroup > 1 && Q_irand( -3, NPCInfo->group->numGroup ) > 1 )
01636                         {//FIXME: make sure he;s giving orders with these lines
01637                                 if ( Q_irand( 0, 1 ) )
01638                                 {
01639                                         ST_Speech( NPC, SPEECH_CHASE, 0.5 );
01640                                 }
01641                                 else
01642                                 {
01643                                         ST_Speech( NPC, SPEECH_YELL, 0.5 );
01644                                 }
01645                         }
01646                         cpFlags = (CP_CLEAR|CP_COVER|CP_AVOID|CP_SAFE|CP_RETREAT);
01647                 }
01648                 else if ( NPCInfo->group->morale < 0 )
01649                 {//hide
01650                         cpFlags = (CP_COVER|CP_AVOID|CP_SAFE|CP_RETREAT);
01651                 }
01652                 else if ( NPCInfo->group->morale < NPCInfo->group->numGroup )
01653                 {//morale is low for our size
01654                         int moraleDrop = NPCInfo->group->numGroup - NPCInfo->group->morale;
01655                         if ( moraleDrop < -6 )
01656                         {//flee (no clear shot needed)
01657                                 cpFlags = (CP_FLEE|CP_RETREAT|CP_COVER|CP_AVOID|CP_SAFE);
01658                         }
01659                         else if ( moraleDrop < -3 )
01660                         {//retreat (no clear shot needed)
01661                                 cpFlags = (CP_RETREAT|CP_COVER|CP_AVOID|CP_SAFE);
01662                         }
01663                         else if ( moraleDrop < 0 )
01664                         {//cover (no clear shot needed)
01665                                 cpFlags = (CP_COVER|CP_AVOID|CP_SAFE);
01666                         }
01667                 }
01668                 else
01669                 {
01670                         int moraleBoost = NPCInfo->group->morale - NPCInfo->group->numGroup;
01671                         if ( moraleBoost > 20 )
01672                         {//charge to any one and outflank (no cover needed)
01673                                 cpFlags = (CP_CLEAR|CP_FLANK|CP_APPROACH_ENEMY);
01674                         }
01675                         else if ( moraleBoost > 15 )
01676                         {//charge to closest one (no cover needed)
01677                                 cpFlags = (CP_CLEAR|CP_CLOSEST|CP_APPROACH_ENEMY);
01678                         }
01679                         else if ( moraleBoost > 10 )
01680                         {//charge closer (no cover needed)
01681                                 cpFlags = (CP_CLEAR|CP_APPROACH_ENEMY);
01682                         }
01683                 }
01684         }
01685         if ( !cpFlags )
01686         {
01687                 //at some medium level of morale
01688                 switch( Q_irand( 0, 3 ) )
01689                 {
01690                 case 0://just take the nearest one
01691                         cpFlags = (CP_CLEAR|CP_COVER|CP_NEAREST);
01692                         break;
01693                 case 1://take one closer to the enemy
01694                         cpFlags = (CP_CLEAR|CP_COVER|CP_APPROACH_ENEMY);
01695                         break;
01696                 case 2://take the one closest to the enemy
01697                         cpFlags = (CP_CLEAR|CP_COVER|CP_CLOSEST|CP_APPROACH_ENEMY);
01698                         break;
01699                 case 3://take the one on the other side of the enemy
01700                         cpFlags = (CP_CLEAR|CP_COVER|CP_FLANK|CP_APPROACH_ENEMY);
01701                         break;
01702                 }
01703         }
01704         if ( NPC && (NPCInfo->scriptFlags&SCF_USE_CP_NEAREST) )
01705         {
01706                 cpFlags &= ~(CP_FLANK|CP_APPROACH_ENEMY|CP_CLOSEST);
01707                 cpFlags |= CP_NEAREST;
01708         }
01709         return cpFlags;
01710 }

void ST_HuntEnemy gentity_t self  ) 
 

Definition at line 1563 of file NPC_AI_Stormtrooper.c.

References gNPC_t::combatPoint, gentity_s::enemy, gentity_t, gNPC_t::goalEntity, level, gentity_s::NPC, NPC, NPC_FreeCombatPoint(), NPCInfo, Q_irand(), qfalse, SCF_CHASE_ENEMIES, gNPC_t::scriptFlags, level_locals_t::time, TIMER_Get(), and TIMER_Set().

Referenced by ST_Commander().

01564 {
01565         //TIMER_Set( NPC, "attackDelay", Q_irand( 250, 500 ) );//Disabled this for now, guys who couldn't hunt would never attack
01566         //TIMER_Set( NPC, "duck", -1 );
01567         TIMER_Set( NPC, "stick", Q_irand( 250, 1000 ) );
01568         TIMER_Set( NPC, "stand", -1 );
01569         TIMER_Set( NPC, "scoutTime", TIMER_Get( NPC, "stick" )-level.time+Q_irand(5000, 10000) );
01570         //leave my combat point
01571         NPC_FreeCombatPoint( NPCInfo->combatPoint, qfalse );
01572         //go directly after the enemy
01573         if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
01574         {
01575                 self->NPC->goalEntity = NPC->enemy;
01576         }
01577 }

void ST_MarkToCover gentity_t self  ) 
 

Definition at line 227 of file NPC_AI_Stormtrooper.c.

References gentity_t, gNPC_t::group, gNPC_t::localState, LSTATE_UNDERFIRE, gentity_s::NPC, AIGroupInfo_s::numGroup, Q_irand(), SPEECH_COVER, ST_AggressionAdjust(), and TIMER_Set().

Referenced by AI_GroupMemberKilled().

00228 {
00229         if ( !self || !self->NPC )
00230         {
00231                 return;
00232         }
00233         self->NPC->localState = LSTATE_UNDERFIRE;
00234         TIMER_Set( self, "attackDelay", Q_irand( 500, 2500 ) );
00235         ST_AggressionAdjust( self, -3 );
00236         if ( self->NPC->group && self->NPC->group->numGroup > 1 )
00237         {
00238                 ST_Speech( self, SPEECH_COVER, 0 );//FIXME: flee sound?
00239         }
00240 }

void ST_ResolveBlockedShot int  hit  ) 
 

Definition at line 1360 of file NPC_AI_Stormtrooper.c.

References AI_GroupContainsEntNum(), g_entities, gentity_t, gNPC_t::group, level, NPC, NPCInfo, Q_irand(), level_locals_t::time, TIMER_Done(), TIMER_Get(), and TIMER_Set().

Referenced by NPC_BSST_Attack().

01361 {
01362         int     stuckTime;
01363         //figure out how long we intend to stand here, max
01364         if ( TIMER_Get( NPC, "roamTime" ) > TIMER_Get( NPC, "stick" ) )
01365         {
01366                 stuckTime = TIMER_Get( NPC, "roamTime" )-level.time;
01367         }
01368         else
01369         {
01370                 stuckTime = TIMER_Get( NPC, "stick" )-level.time;
01371         }
01372 
01373         if ( TIMER_Done( NPC, "duck" ) )
01374         {//we're not ducking
01375                 if ( AI_GroupContainsEntNum( NPCInfo->group, hit ) )
01376                 {
01377                         gentity_t *member = &g_entities[hit];
01378                         if ( TIMER_Done( member, "duck" ) )
01379                         {//they aren't ducking
01380                                 if ( TIMER_Done( member, "stand" ) )
01381                                 {//they're not being forced to stand
01382                                         //tell them to duck at least as long as I'm not moving
01383                                         TIMER_Set( member, "duck", stuckTime );
01384                                         return;
01385                                 }
01386                         }
01387                 }
01388         }
01389         else
01390         {//maybe we should stand
01391                 if ( TIMER_Done( NPC, "stand" ) )
01392                 {//stand for as long as we'll be here
01393                         TIMER_Set( NPC, "stand", stuckTime );
01394                         return;
01395                 }
01396         }
01397         //Hmm, can't resolve this by telling them to duck or telling me to stand
01398         //We need to move!
01399         TIMER_Set( NPC, "roamTime", -1 );
01400         TIMER_Set( NPC, "stick", -1 );
01401         TIMER_Set( NPC, "duck", -1 );
01402         TIMER_Set( NPC, "attakDelay", Q_irand( 1000, 3000 ) );
01403 }

void ST_StartFlee gentity_t self,
gentity_t enemy,
vec3_t  dangerPoint,
int  dangerLevel,
int  minTime,
int  maxTime
 

Definition at line 242 of file NPC_AI_Stormtrooper.c.

References G_StartFlee(), gentity_t, gNPC_t::group, gentity_s::NPC, AIGroupInfo_s::numGroup, SPEECH_COVER, and vec3_t.

Referenced by AI_GroupMemberKilled().

00243 {
00244         if ( !self || !self->NPC )
00245         {
00246                 return;
00247         }
00248         G_StartFlee( self, enemy, dangerPoint, dangerLevel, minTime, maxTime );
00249         if ( self->NPC->group && self->NPC->group->numGroup > 1 )
00250         {
00251                 ST_Speech( self, SPEECH_COVER, 0 );//FIXME: flee sound?
00252         }
00253 }

void ST_TrackEnemy gentity_t self,
vec3_t  enemyPos
 

Definition at line 1536 of file NPC_AI_Stormtrooper.c.

References gNPC_t::combatPoint, gentity_t, level, gentity_s::NPC, NPC_FreeCombatPoint(), NPC_SetMoveGoal(), NULL, Q_irand(), qfalse, level_locals_t::time, TIMER_Get(), TIMER_Set(), and vec3_t.

Referenced by ST_Commander().

01537 {
01538         //clear timers
01539         TIMER_Set( self, "attackDelay", Q_irand( 1000, 2000 ) );
01540         //TIMER_Set( self, "duck", -1 );
01541         TIMER_Set( self, "stick", Q_irand( 500, 1500 ) );
01542         TIMER_Set( self, "stand", -1 );
01543         TIMER_Set( self, "scoutTime", TIMER_Get( self, "stick" )-level.time+Q_irand(5000, 10000) );
01544         //leave my combat point
01545         NPC_FreeCombatPoint( self->NPC->combatPoint, qfalse );
01546         //go after his last seen pos
01547         NPC_SetMoveGoal( self, enemyPos, 16, qfalse, -1, NULL );
01548 }

void ST_TransferMoveGoal gentity_t self,
gentity_t other
 

Definition at line 1595 of file NPC_AI_Stormtrooper.c.

References AI_GroupUpdateSquadstates(), gNPC_t::combatPoint, entityShared_t::currentOrigin, FL_NAVGOAL, gentity_s::flags, gentity_t, gNPC_t::goalEntity, gNPC_t::goalRadius, gNPC_t::group, gNPC_t::lastFailedCombatPoint, gentity_s::NPC, NPC_SetMoveGoal(), NPCInfo, NULL, Q_irand(), gentity_s::r, SQUAD_STAND_AND_SHOOT, gNPC_t::squadState, ST_TransferTimers(), gNPC_t::tempGoal, TID_MOVE_NAV, TIMER_Set(), and trap_ICARUS_TaskIDPending().

Referenced by ST_Commander().

01596 {
01597         if ( trap_ICARUS_TaskIDPending( self, TID_MOVE_NAV ) )
01598         {//can't transfer movegoal when a script we're running is waiting to complete
01599                 return;
01600         }
01601         if ( self->NPC->combatPoint != -1 )
01602         {//I've got a combatPoint I'm going to, give it to him
01603                 self->NPC->lastFailedCombatPoint = other->NPC->combatPoint = self->NPC->combatPoint;
01604                 self->NPC->combatPoint = -1;
01605         }
01606         else
01607         {//I must be going for a goal, give that to him instead
01608                 if ( self->NPC->goalEntity == self->NPC->tempGoal )
01609                 {
01610                         NPC_SetMoveGoal( other, self->NPC->tempGoal->r.currentOrigin, self->NPC->goalRadius, ((self->NPC->tempGoal->flags&FL_NAVGOAL)?qtrue:qfalse), -1, NULL );
01611                 }
01612                 else
01613                 {
01614                         other->NPC->goalEntity = self->NPC->goalEntity;
01615                 }
01616         }
01617         //give him my squadstate
01618         AI_GroupUpdateSquadstates( self->NPC->group, other, NPCInfo->squadState );
01619 
01620         //give him my timers and clear mine
01621         ST_TransferTimers( self, other );
01622 
01623         //now make me stand around for a second or two at least
01624         AI_GroupUpdateSquadstates( self->NPC->group, self, SQUAD_STAND_AND_SHOOT );
01625         TIMER_Set( self, "stand", Q_irand( 1000, 3000 ) );
01626 }

void ST_TransferTimers gentity_t self,
gentity_t other
 

Definition at line 1579 of file NPC_AI_Stormtrooper.c.

References gentity_t, level, level_locals_t::time, TIMER_Get(), and TIMER_Set().

Referenced by ST_TransferMoveGoal().

01580 {
01581         TIMER_Set( other, "attackDelay", TIMER_Get( self, "attackDelay" )-level.time );
01582         TIMER_Set( other, "duck", TIMER_Get( self, "duck" )-level.time );
01583         TIMER_Set( other, "stick", TIMER_Get( self, "stick" )-level.time );
01584         TIMER_Set( other, "scoutTime", TIMER_Get( self, "scout" )-level.time );
01585         TIMER_Set( other, "roamTime", TIMER_Get( self, "roamTime" )-level.time );
01586         TIMER_Set( other, "stand", TIMER_Get( self, "stand" )-level.time );
01587         TIMER_Set( self, "attackDelay", -1 );
01588         TIMER_Set( self, "duck", -1 );
01589         TIMER_Set( self, "stick", -1 );
01590         TIMER_Set( self, "scoutTime", -1 );
01591         TIMER_Set( self, "roamTime", -1 );
01592         TIMER_Set( self, "stand", -1 );
01593 }


Variable Documentation

vmCvar_t d_asynchronousGroupAI
 

Definition at line 18 of file NPC_AI_Stormtrooper.c.

Referenced by ST_Commander().

int groupSpeechDebounceTime[TEAM_NUM_TEAMS]
 

Definition at line 50 of file NPC_AI_Stormtrooper.c.