codemp/game/NPC_AI_Sniper.c File Reference

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

Go to the source code of this file.

Defines

#define SPF_NO_HIDE   2
#define MAX_VIEW_DIST   1024
#define MAX_VIEW_SPEED   250
#define MAX_LIGHT_INTENSITY   255
#define MIN_LIGHT_THRESHOLD   0.1
#define DISTANCE_SCALE   0.25f
#define DISTANCE_THRESHOLD   0.075f
#define SPEED_SCALE   0.25f
#define FOV_SCALE   0.5f
#define LIGHT_SCALE   0.25f
#define REALIZE_THRESHOLD   0.6f
#define CAUTIOUS_THRESHOLD   ( REALIZE_THRESHOLD * 0.75 )

Enumerations

enum  { LSTATE_NONE = 0, LSTATE_UNDERFIRE, LSTATE_INVESTIGATE }

Functions

void G_AddVoiceEvent (gentity_t *self, int event, int speakDebounceTime)
void G_SoundOnEnt (gentity_t *ent, soundChannel_t channel, const char *soundPath)
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)
qboolean FlyingCreature (gentity_t *ent)
qboolean NPC_CheckPlayerTeamStealth (void)
void Sniper_ClearTimers (gentity_t *ent)
void NPC_Sniper_PlayConfusionSound (gentity_t *self)
void NPC_Sniper_Pain (gentity_t *self, gentity_t *attacker, int damage)
void NPC_BSSniper_Patrol (void)
qboolean Sniper_EvaluateShot (int hit)
void Sniper_FaceEnemy (void)
void Sniper_UpdateEnemyPos (void)
void Sniper_StartHide (void)
void NPC_BSSniper_Attack (void)
void NPC_BSSniper_Default (void)


Define Documentation

#define CAUTIOUS_THRESHOLD   ( REALIZE_THRESHOLD * 0.75 )
 

Definition at line 25 of file NPC_AI_Sniper.c.

#define DISTANCE_SCALE   0.25f
 

Definition at line 18 of file NPC_AI_Sniper.c.

#define DISTANCE_THRESHOLD   0.075f
 

Definition at line 19 of file NPC_AI_Sniper.c.

#define FOV_SCALE   0.5f
 

Definition at line 21 of file NPC_AI_Sniper.c.

#define LIGHT_SCALE   0.25f
 

Definition at line 22 of file NPC_AI_Sniper.c.

#define MAX_LIGHT_INTENSITY   255
 

Definition at line 15 of file NPC_AI_Sniper.c.

#define MAX_VIEW_DIST   1024
 

Definition at line 13 of file NPC_AI_Sniper.c.

#define MAX_VIEW_SPEED   250
 

Definition at line 14 of file NPC_AI_Sniper.c.

#define MIN_LIGHT_THRESHOLD   0.1
 

Definition at line 16 of file NPC_AI_Sniper.c.

#define REALIZE_THRESHOLD   0.6f
 

Definition at line 24 of file NPC_AI_Sniper.c.

#define SPEED_SCALE   0.25f
 

Definition at line 20 of file NPC_AI_Sniper.c.

#define SPF_NO_HIDE   2
 

Definition at line 11 of file NPC_AI_Sniper.c.

Referenced by NPC_BSSniper_Attack().


Enumeration Type Documentation

anonymous enum
 

Enumeration values:
LSTATE_NONE 
LSTATE_UNDERFIRE 
LSTATE_INVESTIGATE 

Definition at line 37 of file NPC_AI_Sniper.c.

00038 {
00039         LSTATE_NONE = 0,
00040         LSTATE_UNDERFIRE,
00041         LSTATE_INVESTIGATE,
00042 };


Function Documentation

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.

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 }

void G_SoundOnEnt gentity_t ent,
soundChannel_t  channel,
const char *  soundPath
 

void NPC_BSSniper_Attack void   ) 
 

NPC->enemy )//

Definition at line 640 of file NPC_AI_Sniper.c.

References AEL_DANGER, trace_t::allsolid, AngleVectors(), gentity_s::attackDebounceTime, BUTTON_ALT_ATTACK, BUTTON_ATTACK, usercmd_s::buttons, CalcMuzzlePoint(), CHAN_WEAPON, gentity_s::client, gentity_s::clipmask, entityShared_t::currentOrigin, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gentity_s::enemy, gNPC_t::enemyLastSeenLocation, gNPC_t::enemyLastSeenTime, trace_t::entityNum, gentity_s::fly_sound_debounce_time, trace_t::fraction, G_SoundOnEnt(), gNPC_t::goalEntity, gNPC_t::lastPathAngles, level, MASK_SHOT, entityShared_t::maxs, entityShared_t::mins, NPC, NPC_BSSniper_Patrol(), NPC_ChangeWeapon(), NPC_CheckAlertEvents(), NPC_CheckEnemyExt(), NPC_CheckForDanger(), NPC_ClearLOS4(), NPC_MaxDistSquaredForWeapon(), NPC_UpdateAngles(), NPCInfo, NULL, entityState_s::number, gentity_s::painDebounceTime, gclient_s::ps, Q_irand(), qfalse, qtrue, gentity_s::r, gentity_s::s, SCF_ALT_FIRE, SCF_DONT_FIRE, gNPC_t::scriptFlags, gNPC_t::shotTime, Sniper_EvaluateShot(), Sniper_FaceEnemy(), Sniper_StartHide(), Sniper_UpdateEnemyPos(), gentity_s::spawnflags, SPF_NO_HIDE, trace_t::startsolid, level_locals_t::time, TIMER_Done(), TIMER_Get(), TIMER_Set(), trap_Trace(), ucmd, usercmd_s::upmove, vec3_t, VectorCopy, VectorMA, playerState_s::viewangles, playerState_s::weapon, WeaponThink(), WP_DISRUPTOR, and YAW.

Referenced by NPC_BSSniper_Default().

00641 {
00642         //Don't do anything if we're hurt
00643         if ( NPC->painDebounceTime > level.time )
00644         {
00645                 NPC_UpdateAngles( qtrue, qtrue );
00646                 return;
00647         }
00648 
00649         //NPC_CheckEnemy( qtrue, qfalse );
00650         //If we don't have an enemy, just idle
00651         if ( NPC_CheckEnemyExt(qfalse) == qfalse )
00652         {
00653                 NPC->enemy = NULL;
00654                 NPC_BSSniper_Patrol();//FIXME: or patrol?
00655                 return;
00656         }
00657 
00658         if ( TIMER_Done( NPC, "flee" ) && NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) )
00659         {//going to run
00660                 NPC_UpdateAngles( qtrue, qtrue );
00661                 return;
00662         }
00663 
00664         if ( !NPC->enemy )
00665         {//WTF?  somehow we lost our enemy?
00666                 NPC_BSSniper_Patrol();//FIXME: or patrol?
00667                 return;
00668         }
00669 
00670         enemyLOS2 = enemyCS2 = qfalse;
00671         move2 = qtrue;
00672         faceEnemy2 = qfalse;
00673         shoot2 = qfalse;
00674         enemyDist2 = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
00675         if ( enemyDist2 < 16384 )//128 squared
00676         {//too close, so switch to primary fire
00677                 if ( NPC->client->ps.weapon == WP_DISRUPTOR )
00678                 {//sniping... should be assumed
00679                         if ( NPCInfo->scriptFlags & SCF_ALT_FIRE )
00680                         {//use primary fire
00681                                 trace_t trace;
00682                                 trap_Trace ( &trace, NPC->enemy->r.currentOrigin, NPC->enemy->r.mins, NPC->enemy->r.maxs, NPC->r.currentOrigin, NPC->enemy->s.number, NPC->enemy->clipmask );
00683                                 if ( !trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->s.number ) )
00684                                 {//he can get right to me
00685                                         NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
00686                                         //reset fire-timing variables
00687                                         NPC_ChangeWeapon( WP_DISRUPTOR );
00688                                         NPC_UpdateAngles( qtrue, qtrue );
00689                                         return;
00690                                 }
00691                         }
00692                         //FIXME: switch back if he gets far away again?
00693                 }
00694         }
00695         else if ( enemyDist2 > 65536 )//256 squared
00696         {
00697                 if ( NPC->client->ps.weapon == WP_DISRUPTOR )
00698                 {//sniping... should be assumed
00699                         if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) )
00700                         {//use primary fire
00701                                 NPCInfo->scriptFlags |= SCF_ALT_FIRE;
00702                                 //reset fire-timing variables
00703                                 NPC_ChangeWeapon( WP_DISRUPTOR );
00704                                 NPC_UpdateAngles( qtrue, qtrue );
00705                                 return;
00706                         }
00707                 }
00708         }
00709 
00710         Sniper_UpdateEnemyPos();
00711         //can we see our target?
00712         if ( NPC_ClearLOS4( NPC->enemy ) )//|| (NPCInfo->stats.aim >= 5 && gi.inPVS( NPC->client->renderInfo.eyePoint, NPC->enemy->currentOrigin )) )
00713         {
00714                 float maxShootDist;
00715 
00716                 NPCInfo->enemyLastSeenTime = level.time;
00717                 VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
00718                 enemyLOS2 = qtrue;
00719                 maxShootDist = NPC_MaxDistSquaredForWeapon();
00720                 if ( enemyDist2 < maxShootDist )
00721                 {
00722                         vec3_t fwd, right, up, muzzle, end;
00723                         trace_t tr;
00724                         int hit;
00725 
00726                         AngleVectors( NPC->client->ps.viewangles, fwd, right, up );
00727                         CalcMuzzlePoint( NPC, fwd, right, up, muzzle );
00728                         VectorMA( muzzle, 8192, fwd, end );
00729                         trap_Trace ( &tr, muzzle, NULL, NULL, end, NPC->s.number, MASK_SHOT );
00730 
00731                         hit = tr.entityNum;
00732                         //can we shoot our target?
00733                         if ( Sniper_EvaluateShot( hit ) )
00734                         {
00735                                 enemyCS2 = qtrue;
00736                         }
00737                 }
00738         }
00739         /*
00740         else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
00741         {
00742                 NPCInfo->enemyLastSeenTime = level.time;
00743                 faceEnemy2 = qtrue;
00744         }
00745         */
00746 
00747         if ( enemyLOS2 )
00748         {//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
00749                 faceEnemy2 = qtrue;
00750         }
00751         if ( enemyCS2 )
00752         {
00753                 shoot2 = qtrue;
00754         }
00755         else if ( level.time - NPCInfo->enemyLastSeenTime > 3000 )
00756         {//Hmm, have to get around this bastard... FIXME: this NPCInfo->enemyLastSeenTime builds up when ducked seems to make them want to run when they uncrouch
00757                 Sniper_ResolveBlockedShot();
00758         }
00759 
00760         //Check for movement to take care of
00761         Sniper_CheckMoveState();
00762 
00763         //See if we should override shooting decision with any special considerations
00764         Sniper_CheckFireState();
00765 
00766         if ( move2 )
00767         {//move toward goal
00768                 if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist2 > 10000 ) )//100 squared
00769                 {
00770                         move2 = Sniper_Move();
00771                 }
00772                 else
00773                 {
00774                         move2 = qfalse;
00775                 }
00776         }
00777 
00778         if ( !move2 )
00779         {
00780                 if ( !TIMER_Done( NPC, "duck" ) )
00781                 {
00782                         if ( TIMER_Done( NPC, "watch" ) )
00783                         {//not while watching
00784                                 ucmd.upmove = -127;
00785                         }
00786                 }
00787                 //FIXME: what about leaning?
00788                 //FIXME: also, when stop ducking, start looking, if enemy can see me, chance of ducking back down again
00789         }
00790         else
00791         {//stop ducking!
00792                 TIMER_Set( NPC, "duck", -1 );
00793         }
00794 
00795         if ( TIMER_Done( NPC, "duck" ) 
00796                 && TIMER_Done( NPC, "watch" ) 
00797                 && (TIMER_Get( NPC, "attackDelay" )-level.time) > 1000 
00798                 && NPC->attackDebounceTime < level.time )
00799         {
00800                 if ( enemyLOS2 && (NPCInfo->scriptFlags&SCF_ALT_FIRE) )
00801                 {
00802                         if ( NPC->fly_sound_debounce_time < level.time )
00803                         {
00804                                 NPC->fly_sound_debounce_time = level.time + 2000;
00805                         }
00806                 }
00807         }
00808 
00809         if ( !faceEnemy2 )
00810         {//we want to face in the dir we're running
00811                 if ( move2 )
00812                 {//don't run away and shoot
00813                         NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
00814                         NPCInfo->desiredPitch = 0;
00815                         shoot2 = qfalse;
00816                 }
00817                 NPC_UpdateAngles( qtrue, qtrue );
00818         }
00819         else// if ( faceEnemy2 )
00820         {//face the enemy
00821                 Sniper_FaceEnemy();
00822         }
00823 
00824         if ( NPCInfo->scriptFlags&SCF_DONT_FIRE )
00825         {
00826                 shoot2 = qfalse;
00827         }
00828 
00829         //FIXME: don't shoot right away!
00830         if ( shoot2 )
00831         {//try to shoot if it's time
00832                 if ( TIMER_Done( NPC, "attackDelay" ) )
00833                 {
00834                         WeaponThink( qtrue );
00835                         if ( ucmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK) )
00836                         {
00837                                 G_SoundOnEnt( NPC, CHAN_WEAPON, "sound/null.wav" );
00838                         }
00839 
00840                         //took a shot, now hide
00841                         if ( !(NPC->spawnflags&SPF_NO_HIDE) && !Q_irand( 0, 1 ) )
00842                         {
00843                                 //FIXME: do this if in combat point and combat point has duck-type cover... also handle lean-type cover
00844                                 Sniper_StartHide();
00845                         }
00846                         else
00847                         {
00848                                 TIMER_Set( NPC, "attackDelay", NPCInfo->shotTime-level.time );
00849                         }
00850                 }
00851         }
00852 }

void NPC_BSSniper_Default void   ) 
 

Definition at line 854 of file NPC_AI_Sniper.c.

References gentity_s::enemy, NPC, NPC_BSSniper_Attack(), and NPC_BSSniper_Patrol().

00855 {
00856         if( !NPC->enemy )
00857         {//don't have an enemy, look for one
00858                 NPC_BSSniper_Patrol();
00859         }
00860         else//if ( NPC->enemy )
00861         {//have an enemy
00862                 NPC_BSSniper_Attack();
00863         }
00864 }

void NPC_BSSniper_Patrol void   ) 
 

Definition at line 185 of file NPC_AI_Sniper.c.

References AEL_DISCOVERED, AEL_SUSPICIOUS, gNPCstats_e::aim, level_locals_t::alertEvents, BUTTON_WALKING, usercmd_s::buttons, gentity_s::client, gNPC_t::confusionTime, gentity_s::count, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gclient_s::enemyTeam, renderInfo_s::eyePoint, G_SetEnemy(), gentity_s::health, alertEvent_s::ID, gNPC_t::investigateDebounceTime, gNPC_t::investigateGoal, gNPC_t::lastAlertID, alertEvent_s::level, level, NPC, NPC_CheckAlertEvents(), NPC_CheckForDanger(), NPC_CheckPlayerTeamStealth(), NPC_MoveToGoal(), NPC_UpdateAngles(), NPCInfo, alertEvent_s::owner, PITCH, gclient_s::playerTeam, alertEvent_s::position, Q_irand(), qfalse, qtrue, gclient_s::renderInfo, SCF_IGNORE_ALERTS, SCF_LOOK_FOR_ENEMIES, gNPC_t::scriptFlags, gNPC_t::stats, level_locals_t::time, TIMER_Set(), ucmd, UpdateGoal(), vec3_t, vectoangles(), VectorCopy, VectorSubtract, and YAW.

Referenced by NPC_BSSniper_Attack(), and NPC_BSSniper_Default().

00186 {//FIXME: pick up on bodies of dead buddies?
00187         NPC->count = 0;
00188 
00189         if ( NPCInfo->confusionTime < level.time )
00190         {
00191                 //Look for any enemies
00192                 if ( NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES )
00193                 {
00194                         if ( NPC_CheckPlayerTeamStealth() )
00195                         {
00196                                 //NPCInfo->behaviorState = BS_HUNT_AND_KILL;//Should be auto now
00197                                 //NPC_AngerSound();
00198                                 NPC_UpdateAngles( qtrue, qtrue );
00199                                 return;
00200                         }
00201                 }
00202 
00203                 if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
00204                 {
00205                         //Is there danger nearby
00206                         int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_SUSPICIOUS );
00207                         if ( NPC_CheckForDanger( alertEvent ) )
00208                         {
00209                                 NPC_UpdateAngles( qtrue, qtrue );
00210                                 return;
00211                         }
00212                         else
00213                         {//check for other alert events
00214                                 //There is an event to look at
00215                                 if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
00216                                 {
00217                                         NPCInfo->lastAlertID = level.alertEvents[alertEvent].ID;
00218                                         if ( level.alertEvents[alertEvent].level == AEL_DISCOVERED )
00219                                         {
00220                                                 if ( level.alertEvents[alertEvent].owner && 
00221                                                         level.alertEvents[alertEvent].owner->client && 
00222                                                         level.alertEvents[alertEvent].owner->health >= 0 &&
00223                                                         level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
00224                                                 {//an enemy
00225                                                         G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
00226                                                         //NPCInfo->enemyLastSeenTime = level.time;
00227                                                         TIMER_Set( NPC, "attackDelay", Q_irand( (6-NPCInfo->stats.aim)*100, (6-NPCInfo->stats.aim)*500 ) );
00228                                                 }
00229                                         }
00230                                         else
00231                                         {//FIXME: get more suspicious over time?
00232                                                 //Save the position for movement (if necessary)
00233                                                 //FIXME: sound?
00234                                                 VectorCopy( level.alertEvents[alertEvent].position, NPCInfo->investigateGoal );
00235                                                 NPCInfo->investigateDebounceTime = level.time + Q_irand( 500, 1000 );
00236                                                 if ( level.alertEvents[alertEvent].level == AEL_SUSPICIOUS )
00237                                                 {//suspicious looks longer
00238                                                         NPCInfo->investigateDebounceTime += Q_irand( 500, 2500 );
00239                                                 }
00240                                         }
00241                                 }
00242                         }
00243 
00244                         if ( NPCInfo->investigateDebounceTime > level.time )
00245                         {//FIXME: walk over to it, maybe?  Not if not chase enemies flag
00246                                 //NOTE: stops walking or doing anything else below
00247                                 vec3_t  dir, angles;
00248                                 float   o_yaw, o_pitch;
00249                                 
00250                                 VectorSubtract( NPCInfo->investigateGoal, NPC->client->renderInfo.eyePoint, dir );
00251                                 vectoangles( dir, angles );
00252                                 
00253                                 o_yaw = NPCInfo->desiredYaw;
00254                                 o_pitch = NPCInfo->desiredPitch;
00255                                 NPCInfo->desiredYaw = angles[YAW];
00256                                 NPCInfo->desiredPitch = angles[PITCH];
00257                                 
00258                                 NPC_UpdateAngles( qtrue, qtrue );
00259 
00260                                 NPCInfo->desiredYaw = o_yaw;
00261                                 NPCInfo->desiredPitch = o_pitch;
00262                                 return;
00263                         }
00264                 }
00265         }
00266 
00267         //If we have somewhere to go, then do that
00268         if ( UpdateGoal() )
00269         {
00270                 ucmd.buttons |= BUTTON_WALKING;
00271                 NPC_MoveToGoal( qtrue );
00272         }
00273 
00274         NPC_UpdateAngles( qtrue, qtrue );
00275 }

qboolean NPC_CheckPlayerTeamStealth void   ) 
 

void NPC_Sniper_Pain gentity_t self,
gentity_t attacker,
int  damage
 

Definition at line 85 of file NPC_AI_Sniper.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().

00086 {
00087         self->NPC->localState = LSTATE_UNDERFIRE;
00088 
00089         TIMER_Set( self, "duck", -1 );
00090         TIMER_Set( self, "stand", 2000 );
00091 
00092         NPC_Pain( self, attacker, damage );
00093 
00094         if ( !damage && self->health > 0 )
00095         {//FIXME: better way to know I was pushed
00096                 G_AddVoiceEvent( self, Q_irand(EV_PUSHED1, EV_PUSHED3), 2000 );
00097         }
00098 }

void NPC_Sniper_PlayConfusionSound gentity_t self  ) 
 

Definition at line 60 of file NPC_AI_Sniper.c.

References BS_DEFAULT, EV_CONFUSE1, EV_CONFUSE3, G_AddVoiceEvent(), G_ClearEnemy(), gentity_t, gentity_s::health, gNPC_t::investigateCount, gentity_s::NPC, Q_irand(), SQUAD_IDLE, gNPC_t::squadState, gNPC_t::tempBehavior, and TIMER_Set().

00061 {//FIXME: make this a custom sound in sound set
00062         if ( self->health > 0 )
00063         {
00064                 G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
00065         }
00066         //reset him to be totally unaware again
00067         TIMER_Set( self, "enemyLastVisible", 0 );
00068         TIMER_Set( self, "flee", 0 );
00069         self->NPC->squadState = SQUAD_IDLE;
00070         self->NPC->tempBehavior = BS_DEFAULT;
00071 
00072         //self->NPC->behaviorState = BS_PATROL;
00073         G_ClearEnemy( self );//FIXME: or just self->enemy = NULL;?
00074 
00075         self->NPC->investigateCount = 0;
00076 }

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

Definition at line 661 of file NPC_reactions.c.

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 Sniper_ClearTimers gentity_t ent  ) 
 

Definition at line 44 of file NPC_AI_Sniper.c.

References gentity_t, and TIMER_Set().

00045 {
00046         TIMER_Set( ent, "chatter", 0 );
00047         TIMER_Set( ent, "duck", 0 );
00048         TIMER_Set( ent, "stand", 0 );
00049         TIMER_Set( ent, "shuffleTime", 0 );
00050         TIMER_Set( ent, "sleepTime", 0 );
00051         TIMER_Set( ent, "enemyLastVisible", 0 );
00052         TIMER_Set( ent, "roamTime", 0 );
00053         TIMER_Set( ent, "hideTime", 0 );
00054         TIMER_Set( ent, "attackDelay", 0 );     //FIXME: Slant for difficulty levels
00055         TIMER_Set( ent, "stick", 0 );
00056         TIMER_Set( ent, "scoutTime", 0 );
00057         TIMER_Set( ent, "flee", 0 );
00058 }

qboolean Sniper_EvaluateShot int  hit  ) 
 

Definition at line 488 of file NPC_AI_Sniper.c.

References gentity_s::client, gentity_s::enemy, gclient_s::enemyTeam, g_entities, gentity_t, gentity_s::health, NPC, entityState_s::number, gclient_s::playerTeam, qboolean, qfalse, qtrue, gentity_s::r, gentity_s::s, SVF_GLASS_BRUSH, entityShared_t::svFlags, gentity_s::takedamage, entityState_s::weapon, and WP_EMPLACED_GUN.

Referenced by NPC_BSSniper_Attack(), and Sniper_FaceEnemy().

00489 {
00490         gentity_t *hitEnt;
00491 
00492         if ( !NPC->enemy )
00493         {
00494                 return qfalse;
00495         }
00496 
00497         hitEnt = &g_entities[hit];
00498         if ( hit == NPC->enemy->s.number 
00499                 || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
00500                 || ( hitEnt && hitEnt->takedamage && ((hitEnt->r.svFlags&SVF_GLASS_BRUSH)||hitEnt->health < 40||NPC->s.weapon == WP_EMPLACED_GUN) )
00501                 || ( hitEnt && (hitEnt->r.svFlags&SVF_GLASS_BRUSH)) )
00502         {//can hit enemy or will hit glass, so shoot anyway
00503                 return qtrue;
00504         }
00505         return qfalse;
00506 }

void Sniper_FaceEnemy void   ) 
 

Definition at line 508 of file NPC_AI_Sniper.c.

References gNPCstats_e::aim, AngleNormalize360(), AngleVectors(), CalcEntitySpot(), CalcMuzzlePoint(), gentity_s::client, gentity_s::count, gNPC_t::desiredPitch, gNPC_t::desiredYaw, gentity_s::enemy, ENEMY_POS_LAG_STEPS, gNPC_t::enemyLaggedPos, trace_t::entityNum, flrand(), g_spskill, GetAnglesForDirection(), vmCvar_t::integer, level, MASK_SHOT, entityShared_t::maxs, entityShared_t::mins, NPC, NPC_UpdateAngles(), NPCInfo, entityState_s::number, PITCH, gclient_s::ps, Q_irand(), qboolean, qfalse, qtrue, gentity_s::r, gentity_s::s, gNPC_t::shotTime, Sniper_EvaluateShot(), SPOT_ORIGIN, gNPC_t::stats, level_locals_t::time, TIMER_Done(), trap_Trace(), vec3_origin, vec3_t, VectorCopy, VectorMA, playerState_s::viewangles, and YAW.

Referenced by NPC_BSSniper_Attack().

00509 {
00510         //FIXME: the ones behind kill holes are facing some arbitrary direction and not firing
00511         //FIXME: If actually trying to hit enemy, don't fire unless enemy is at least in front of me?
00512         //FIXME: need to give designers option to make them not miss first few shots
00513         if ( NPC->enemy )
00514         {
00515                 vec3_t  muzzle, target, angles, forward, right, up;
00516                 //Get the positions
00517                 AngleVectors( NPC->client->ps.viewangles, forward, right, up );
00518                 CalcMuzzlePoint( NPC, forward, right, up, muzzle );
00519                 //CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
00520                 CalcEntitySpot( NPC->enemy, SPOT_ORIGIN, target );
00521 
00522                 if ( enemyDist2 > 65536 && NPCInfo->stats.aim < 5 )//is 256 squared, was 16384 (128*128)
00523                 {
00524                         if ( NPC->count < (5-NPCInfo->stats.aim) )
00525                         {//miss a few times first
00526                                 if ( shoot2 && TIMER_Done( NPC, "attackDelay" ) && level.time >= NPCInfo->shotTime )
00527                                 {//ready to fire again
00528                                         qboolean        aimError = qfalse;
00529                                         qboolean        hit = qtrue;
00530                                         int                     tryMissCount = 0;
00531                                         trace_t         trace;
00532 
00533                                         GetAnglesForDirection( muzzle, target, angles );
00534                                         AngleVectors( angles, forward, right, up );
00535 
00536                                         while ( hit && tryMissCount < 10 )
00537                                         {
00538                                                 tryMissCount++;
00539                                                 if ( !Q_irand( 0, 1 ) )
00540                                                 {
00541                                                         aimError = qtrue;
00542                                                         if ( !Q_irand( 0, 1 ) )
00543                                                         {
00544                                                                 VectorMA( target, NPC->enemy->r.maxs[2]*flrand(1.5, 4), right, target );
00545                                                         }
00546                                                         else
00547                                                         {
00548                                                                 VectorMA( target, NPC->enemy->r.mins[2]*flrand(1.5, 4), right, target );
00549                                                         }
00550                                                 }
00551                                                 if ( !aimError || !Q_irand( 0, 1 ) )
00552                                                 {
00553                                                         if ( !Q_irand( 0, 1 ) )
00554                                                         {
00555                                                                 VectorMA( target, NPC->enemy->r.maxs[2]*flrand(1.5, 4), up, target );
00556                                                         }
00557                                                         else
00558                                                         {
00559                                                                 VectorMA( target, NPC->enemy->r.mins[2]*flrand(1.5, 4), up, target );
00560                                                         }
00561                                                 }
00562                                                 trap_Trace( &trace, muzzle, vec3_origin, vec3_origin, target, NPC->s.number, MASK_SHOT );
00563                                                 hit = Sniper_EvaluateShot( trace.entityNum );
00564                                         }
00565                                         NPC->count++;
00566                                 }
00567                                 else
00568                                 {
00569                                         if ( !enemyLOS2 )
00570                                         {
00571                                                 NPC_UpdateAngles( qtrue, qtrue );
00572                                                 return;
00573                                         }
00574                                 }
00575                         }
00576                         else
00577                         {//based on distance, aim value, difficulty and enemy movement, miss
00578                                 //FIXME: incorporate distance as a factor?
00579                                 int missFactor = 8-(NPCInfo->stats.aim+g_spskill.integer) * 3;
00580                                 if ( missFactor > ENEMY_POS_LAG_STEPS )
00581                                 {
00582                                         missFactor = ENEMY_POS_LAG_STEPS;
00583                                 }
00584                                 else if ( missFactor < 0 )
00585                                 {//???
00586                                         missFactor = 0 ;
00587                                 }
00588                                 VectorCopy( NPCInfo->enemyLaggedPos[missFactor], target );
00589                         }
00590                         GetAnglesForDirection( muzzle, target, angles );
00591                 }
00592                 else
00593                 {
00594                         target[2] += flrand( 0, NPC->enemy->r.maxs[2] );
00595                         //CalcEntitySpot( NPC->enemy, SPOT_HEAD_LEAN, target );
00596                         GetAnglesForDirection( muzzle, target, angles );
00597                 }
00598 
00599                 NPCInfo->desiredYaw             = AngleNormalize360( angles[YAW] );
00600                 NPCInfo->desiredPitch   = AngleNormalize360( angles[PITCH] );
00601         }
00602         NPC_UpdateAngles( qtrue, qtrue );
00603 }

void Sniper_StartHide void   ) 
 

Definition at line 631 of file NPC_AI_Sniper.c.

References NPC, Q_irand(), and TIMER_Set().

Referenced by NPC_BSSniper_Attack().

00632 {
00633         int duckTime = Q_irand( 2000, 5000 );
00634         
00635         TIMER_Set( NPC, "duck", duckTime );
00636         TIMER_Set( NPC, "watch", 500 );
00637         TIMER_Set( NPC, "attackDelay", duckTime + Q_irand( 500, 2000 ) );
00638 }

void Sniper_UpdateEnemyPos void   ) 
 

Definition at line 605 of file NPC_AI_Sniper.c.

References CalcEntitySpot(), gentity_s::enemy, ENEMY_POS_LAG_INTERVAL, gNPC_t::enemyLaggedPos, flrand(), MAX_ENEMY_POS_LAG, NPC, NPCInfo, SPOT_HEAD_LEAN, and VectorCopy.

Referenced by NPC_BSSniper_Attack().

00606 {
00607         int index;
00608         int i;
00609 
00610         for ( i = MAX_ENEMY_POS_LAG-ENEMY_POS_LAG_INTERVAL; i >= 0; i -= ENEMY_POS_LAG_INTERVAL )
00611         {
00612                 index = i/ENEMY_POS_LAG_INTERVAL;
00613                 if ( !index )
00614                 {
00615                         CalcEntitySpot( NPC->enemy, SPOT_HEAD_LEAN, NPCInfo->enemyLaggedPos[index] );
00616                         NPCInfo->enemyLaggedPos[index][2] -= flrand( 2, 16 );
00617                 }
00618                 else
00619                 {
00620                         VectorCopy( NPCInfo->enemyLaggedPos[index-1], NPCInfo->enemyLaggedPos[index] );
00621                 }
00622         }
00623 }