codemp/game/NPC_combat.c File Reference

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

Go to the source code of this file.

Data Structures

struct  combatPt_t

Defines

#define ANGER_ALERT_RADIUS   512
#define ANGER_ALERT_SOUND_RADIUS   256
#define MIN_AVOID_DOT   0.75f
#define MIN_AVOID_DISTANCE   128
#define MIN_AVOID_DISTANCE_SQUARED   ( MIN_AVOID_DISTANCE * MIN_AVOID_DISTANCE )
#define CP_COLLECT_RADIUS   512.0f

Functions

void G_AddVoiceEvent (gentity_t *self, int event, int speakDebounceTime)
void G_SetEnemy (gentity_t *self, gentity_t *enemy)
qboolean NPC_CheckLookTarget (gentity_t *self)
void NPC_ClearLookTarget (gentity_t *self)
void NPC_Jedi_RateNewEnemy (gentity_t *self, gentity_t *enemy)
int NAV_FindClosestWaypointForPoint2 (vec3_t point)
int NAV_GetNearestNode (gentity_t *self, int lastNode)
void G_CreateG2AttachedWeaponModel (gentity_t *ent, const char *weaponModel, int boltNum, int weaponNum)
qboolean PM_DroidMelee (int npc_class)
void ChangeWeapon (gentity_t *ent, int newWeapon)
void G_ClearEnemy (gentity_t *self)
void G_AngerAlert (gentity_t *self)
qboolean G_TeamEnemy (gentity_t *self)
void G_AttackDelay (gentity_t *self, gentity_t *enemy)
void G_ForceSaberOn (gentity_t *ent)
void G_AimSet (gentity_t *self, int aim)
void NPC_ChangeWeapon (int newWeapon)
void NPC_ApplyWeaponFireDelay (void)
void ShootThink (void)
void WeaponThink (qboolean inCombat)
qboolean HaveWeapon (int weapon)
qboolean EntIsGlass (gentity_t *check)
qboolean ShotThroughGlass (trace_t *tr, gentity_t *target, vec3_t spot, int mask)
qboolean CanShoot (gentity_t *ent, gentity_t *shooter)
void NPC_CheckPossibleEnemy (gentity_t *other, visibility_t vis)
int NPC_AttackDebounceForWeapon (void)
float NPC_MaxDistSquaredForWeapon (void)
qboolean ValidEnemy (gentity_t *ent)
qboolean NPC_EnemyTooFar (gentity_t *enemy, float dist, qboolean toShoot)
gentity_tNPC_PickEnemy (gentity_t *closestTo, int enemyTeam, qboolean checkVis, qboolean findPlayersFirst, qboolean findClosest)
gentity_tNPC_PickAlly (qboolean facingEachOther, float range, qboolean ignoreGroup, qboolean movingOnly)
gentity_tNPC_CheckEnemy (qboolean findNew, qboolean tooFarOk, qboolean setEnemy)
qboolean NPC_ClearShot (gentity_t *ent)
int NPC_ShotEntity (gentity_t *ent, vec3_t impactPos)
qboolean NPC_EvaluateShot (int hit, qboolean glassOK)
qboolean NPC_CheckAttack (float scale)
qboolean NPC_CheckDefend (float scale)
qboolean NPC_CheckCanAttack (float attack_scale, qboolean stationary)
float IdealDistance (gentity_t *self)
void SP_point_combat (gentity_t *self)
void CP_FindCombatPointWaypoints (void)
int NPC_FindCombatPoint (const vec3_t position, const vec3_t avoidPosition, vec3_t enemyPosition, const int flags, const float avoidDist, const int ignorePoint)
int NPC_FindSquadPoint (vec3_t position)
qboolean NPC_ReserveCombatPoint (int combatPointID)
qboolean NPC_FreeCombatPoint (int combatPointID, qboolean failed)
qboolean NPC_SetCombatPoint (int combatPointID)
qboolean CheckItemCanBePickedUpByNPC (gentity_t *item, gentity_t *pickerupper)
gentity_tNPC_SearchForWeapons (void)
void NPC_SetPickUpGoal (gentity_t *foundWeap)
void NPC_CheckGetNewWeapon (void)
void NPC_AimAdjust (int change)


Define Documentation

#define ANGER_ALERT_RADIUS   512
 

Definition at line 44 of file NPC_combat.c.

Referenced by G_AngerAlert().

#define ANGER_ALERT_SOUND_RADIUS   256
 

Definition at line 45 of file NPC_combat.c.

Referenced by G_AngerAlert().

#define CP_COLLECT_RADIUS   512.0f
 

Definition at line 2655 of file NPC_combat.c.

Referenced by NPC_FindCombatPoint().

#define MIN_AVOID_DISTANCE   128
 

Definition at line 2653 of file NPC_combat.c.

#define MIN_AVOID_DISTANCE_SQUARED   ( MIN_AVOID_DISTANCE * MIN_AVOID_DISTANCE )
 

Definition at line 2654 of file NPC_combat.c.

Referenced by NPC_FindCombatPoint().

#define MIN_AVOID_DOT   0.75f
 

Definition at line 2652 of file NPC_combat.c.

Referenced by NPC_FindCombatPoint().


Function Documentation

qboolean CanShoot gentity_t ent,
gentity_t shooter
 

Definition at line 1150 of file NPC_combat.c.

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

Referenced by NPC_CheckVisibility().

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

void ChangeWeapon gentity_t ent,
int  newWeapon
 

qboolean CheckItemCanBePickedUpByNPC gentity_t item,
gentity_t pickerupper
 

Definition at line 2336 of file g_items.c.

References gentity_s::activator, gentity_s::enemy, FL_DROPPED_ITEM, gentity_s::flags, g_entities, gentity_t, level, gentity_s::NPC, entityState_s::number, gentity_s::painDebounceTime, qboolean, qfalse, qtrue, gentity_s::s, SCF_FORCED_MARCH, gNPC_t::scriptFlags, gNPC_t::surrenderTime, entityState_s::time, level_locals_t::time, entityState_s::weapon, and WP_NONE.

Referenced by NPC_SearchForWeapons(), and Touch_Item().

02337 {
02338         if ( (item->flags&FL_DROPPED_ITEM) 
02339                 && item->activator != &g_entities[0] 
02340                 && pickerupper->s.number 
02341                 && pickerupper->s.weapon == WP_NONE 
02342                 && pickerupper->enemy 
02343                 && pickerupper->painDebounceTime < level.time
02344                 && pickerupper->NPC && pickerupper->NPC->surrenderTime < level.time //not surrendering
02345                 && !(pickerupper->NPC->scriptFlags&SCF_FORCED_MARCH) //not being forced to march
02346                 /*&& item->item->giTag != INV_SECURITY_KEY*/ )
02347         {//non-player, in combat, picking up a dropped item that does NOT belong to the player and it *not* a security key
02348                 if ( level.time - item->s.time < 3000 )//was 5000
02349                 {
02350                         return qfalse;
02351                 }
02352                 return qtrue;
02353         }
02354         return qfalse;
02355 }

void CP_FindCombatPointWaypoints void   ) 
 

Definition at line 2548 of file NPC_combat.c.

References Com_Printf(), level_locals_t::combatPoints, level, NAV_FindClosestWaypointForPoint2(), level_locals_t::numCombatPoints, combatPoint_t::origin, S_COLOR_RED, vtos(), combatPoint_t::waypoint, and WAYPOINT_NONE.

Referenced by G_InitGame(), and vmMain().

02549 {
02550         int i;
02551 
02552         for ( i = 0; i < level.numCombatPoints; i++ )
02553         {
02554                 level.combatPoints[i].waypoint = NAV_FindClosestWaypointForPoint2( level.combatPoints[i].origin );
02555 #ifndef FINAL_BUILD
02556                 if ( level.combatPoints[i].waypoint == WAYPOINT_NONE )
02557                 {
02558                         Com_Printf( S_COLOR_RED"ERROR: Combat Point at %s has no waypoint!\n", vtos(level.combatPoints[i].origin) );
02559                 }
02560 #endif
02561         }
02562 }

qboolean EntIsGlass gentity_t check  ) 
 

Definition at line 1114 of file NPC_combat.c.

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

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

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

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 }

void G_AimSet gentity_t self,
int  aim
 

Definition at line 3131 of file NPC_combat.c.

References gNPC_t::currentAim, g_spskill, gentity_t, vmCvar_t::integer, gentity_s::NPC, Q_irand(), and TIMER_Set().

Referenced by G_SetEnemy().

03132 {
03133         if ( self->NPC )
03134         {
03135                 int debounce;
03136 
03137                 self->NPC->currentAim = aim;
03138                 //Com_Printf( "%s new aim = %d\n", self->NPC_type, self->NPC->currentAim );
03139 
03140                 debounce = 500+(3-g_spskill.integer)*100;
03141                 TIMER_Set( self, "aimDebounce", Q_irand( debounce,debounce+1000 ) );
03142         //      int debounce = 1000+(3-g_spskill.integer)*500;
03143         //      TIMER_Set( self, "aimDebounce", Q_irand( debounce,debounce+2000 ) );
03144         }
03145 }

void G_AngerAlert gentity_t self  ) 
 

Definition at line 47 of file NPC_combat.c.

References ANGER_ALERT_RADIUS, ANGER_ALERT_SOUND_RADIUS, gentity_s::enemy, G_AlertTeam(), gentity_t, gentity_s::NPC, SCF_NO_GROUPS, gNPC_t::scriptFlags, and TIMER_Done().

Referenced by G_SetEnemy().

00048 {
00049         if ( self && self->NPC && (self->NPC->scriptFlags&SCF_NO_GROUPS) )
00050         {//I'm not a team playa...
00051                 return;
00052         }
00053         if ( !TIMER_Done( self, "interrogating" ) )
00054         {//I'm interrogating, don't wake everyone else up yet... FIXME: this may never wake everyone else up, though!
00055                 return;
00056         }
00057         //FIXME: hmm.... with all the other new alerts now, is this still neccesary or even a good idea...?
00058         G_AlertTeam( self, self->enemy, ANGER_ALERT_RADIUS, ANGER_ALERT_SOUND_RADIUS ); 
00059 }

void G_AttackDelay gentity_t self,
gentity_t enemy
 

Definition at line 117 of file NPC_combat.c.

References AngleVectors(), CLASS_ATST, CLASS_GALAKMECH, CLASS_IMPERIAL, CLASS_IMPWORKER, CLASS_INTERROGATOR, CLASS_JAN, CLASS_JAWA, CLASS_LANDO, CLASS_MARK1, CLASS_MARK2, CLASS_MINEMONSTER, CLASS_MURJJ, CLASS_PRISONER, CLASS_PROBE, CLASS_REBEL, CLASS_REELO, CLASS_REMOTE, CLASS_SEEKER, CLASS_SENTRY, CLASS_STORMTROOPER, CLASS_SWAMPTROOPER, CLASS_TRANDOSHAN, CLASS_UGNAUGHT, gentity_s::client, entityShared_t::currentOrigin, DotProduct, renderInfo_s::eyeAngles, renderInfo_s::eyePoint, floor(), g_spskill, gentity_t, vmCvar_t::integer, gentity_s::NPC, gclient_s::NPC_class, NPCTEAM_PLAYER, NULL, gclient_s::playerTeam, Q_irand(), gentity_s::r, gNPC_t::rank, RANK_LT, gclient_s::renderInfo, gentity_s::s, SCF_ALT_FIRE, gNPC_t::scriptFlags, TIMER_Set(), vec3_t, VectorNormalize(), VectorSubtract, entityState_s::weapon, WP_BLASTER, WP_BOWCASTER, WP_BRYAR_PISTOL, WP_DISRUPTOR, WP_EMPLACED_GUN, WP_FLECHETTE, WP_NONE, WP_REPEATER, WP_ROCKET_LAUNCHER, WP_SABER, WP_STUN_BATON, WP_THERMAL, and WP_TURRET.

Referenced by G_SetEnemy().

00118 {
00119         if ( enemy && self->client && self->NPC )
00120         {//delay their attack based on how far away they're facing from enemy
00121                 vec3_t          fwd, dir;
00122                 int                     attDelay;
00123 
00124                 VectorSubtract( self->client->renderInfo.eyePoint, enemy->r.currentOrigin, dir );//purposely backwards
00125                 VectorNormalize( dir );
00126                 AngleVectors( self->client->renderInfo.eyeAngles, fwd, NULL, NULL );
00127                 //dir[2] = fwd[2] = 0;//ignore z diff?
00128                 
00129                 attDelay = (4-g_spskill.integer)*500;//initial: from 1000ms delay on hard to 2000ms delay on easy
00130                 if ( self->client->playerTeam == NPCTEAM_PLAYER )
00131                 {//invert
00132                         attDelay = 2000-attDelay;
00133                 }
00134                 attDelay += floor( (DotProduct( fwd, dir )+1.0f) * 2000.0f );//add up to 4000ms delay if they're facing away
00135 
00136                 //FIXME: should distance matter, too?
00137 
00138                 //Now modify the delay based on NPC_class, weapon, and team
00139                 //NOTE: attDelay should be somewhere between 1000 to 6000 milliseconds
00140                 switch ( self->client->NPC_class )
00141                 {
00142                 case CLASS_IMPERIAL://they give orders and hang back
00143                         attDelay += Q_irand( 500, 1500 );
00144                         break;
00145                 case CLASS_STORMTROOPER://stormtroopers shoot sooner
00146                         if ( self->NPC->rank >= RANK_LT )
00147                         {//officers shoot even sooner
00148                                 attDelay -= Q_irand( 500, 1500 );
00149                         }
00150                         else
00151                         {//normal stormtroopers don't have as fast reflexes as officers
00152                                 attDelay -= Q_irand( 0, 1000 );
00153                         }
00154                         break;
00155                 case CLASS_SWAMPTROOPER://shoot very quickly?  What about guys in water?
00156                         attDelay -= Q_irand( 1000, 2000 );
00157                         break;
00158                 case CLASS_IMPWORKER://they panic, don't fire right away
00159                         attDelay += Q_irand( 1000, 2500 );
00160                         break;
00161                 case CLASS_TRANDOSHAN:
00162                         attDelay -= Q_irand( 500, 1500 );
00163                         break;
00164                 case CLASS_JAN:                         
00165                 case CLASS_LANDO:                       
00166                 case CLASS_PRISONER:
00167                 case CLASS_REBEL:
00168                         attDelay -= Q_irand( 500, 1500 );
00169                         break;
00170                 case CLASS_GALAKMECH:   
00171                 case CLASS_ATST:                
00172                         attDelay -= Q_irand( 1000, 2000 );
00173                         break;
00174                 case CLASS_REELO:
00175                 case CLASS_UGNAUGHT:
00176                 case CLASS_JAWA:
00177                         return;
00178                         break;
00179                 case CLASS_MINEMONSTER:
00180                 case CLASS_MURJJ:
00181                         return;
00182                         break;
00183                 case CLASS_INTERROGATOR:
00184                 case CLASS_PROBE:               
00185                 case CLASS_MARK1:               
00186                 case CLASS_MARK2:               
00187                 case CLASS_SENTRY:              
00188                         return;
00189                         break;
00190                 case CLASS_REMOTE:
00191                 case CLASS_SEEKER:
00192                         return;
00193                         break;
00194                 /*
00195                 case CLASS_GRAN:
00196                 case CLASS_RODIAN:
00197                 case CLASS_WEEQUAY:
00198                         break;
00199                 case CLASS_JEDI:                                
00200                 case CLASS_SHADOWTROOPER:
00201                 case CLASS_TAVION:
00202                 case CLASS_REBORN:
00203                 case CLASS_LUKE:                                
00204                 case CLASS_DESANN:                      
00205                         break;
00206                 */
00207                 }
00208 
00209                 switch ( self->s.weapon )
00210                 {
00211                 case WP_NONE:
00212                 case WP_SABER:
00213                         return;
00214                         break;
00215                 case WP_BRYAR_PISTOL:
00216                         break;
00217                 case WP_BLASTER:
00218                         if ( self->NPC->scriptFlags & SCF_ALT_FIRE )
00219                         {//rapid-fire blasters
00220                                 attDelay += Q_irand( 0, 500 );
00221                         }
00222                         else
00223                         {//regular blaster
00224                                 attDelay -= Q_irand( 0, 500 );
00225                         }
00226                         break;
00227                 case WP_BOWCASTER:
00228                         attDelay += Q_irand( 0, 500 );
00229                         break;
00230                 case WP_REPEATER:
00231                         if ( !(self->NPC->scriptFlags&SCF_ALT_FIRE) )
00232                         {//rapid-fire blasters
00233                                 attDelay += Q_irand( 0, 500 );
00234                         }
00235                         break;
00236                 case WP_FLECHETTE:
00237                         attDelay += Q_irand( 500, 1500 );
00238                         break;
00239                 case WP_ROCKET_LAUNCHER:
00240                         attDelay += Q_irand( 500, 1500 );
00241                         break;
00242 //              case WP_BLASTER_PISTOL: // apparently some enemy only version of the blaster
00243 //                      attDelay -= Q_irand( 500, 1500 );
00244 //                      break;
00245                         //rwwFIXMEFIXME: Have this weapon for NPCs?
00246                 case WP_DISRUPTOR://sniper's don't delay?
00247                         return;
00248                         break;
00249                 case WP_THERMAL://grenade-throwing has a built-in delay
00250                         return;
00251                         break;
00252                 case WP_STUN_BATON:                     // Any ol' melee attack
00253                         return;
00254                         break;
00255                 case WP_EMPLACED_GUN:
00256                         return;
00257                         break;
00258                 case WP_TURRET:                 // turret guns 
00259                         return;
00260                         break;
00261 //              case WP_BOT_LASER:              // Probe droid  - Laser blast
00262 //                      return; //rwwFIXMEFIXME: Have this weapon for NPCs?
00263                         break;
00264                 /*
00265                 case WP_DEMP2:
00266                         break;
00267                 case WP_TRIP_MINE:
00268                         break;
00269                 case WP_DET_PACK:
00270                         break;
00271                 case WP_STUN_BATON:
00272                         break;
00273                 case WP_ATST_MAIN:
00274                         break;
00275                 case WP_ATST_SIDE:
00276                         break;
00277                 case WP_TIE_FIGHTER:
00278                         break;
00279                 case WP_RAPID_FIRE_CONC:
00280                         break;
00281                 */
00282                 }
00283 
00284                 if ( self->client->playerTeam == NPCTEAM_PLAYER )
00285                 {//clamp it
00286                         if ( attDelay > 2000 )
00287                         {
00288                                 attDelay = 2000;
00289                         }
00290                 }
00291 
00292                 //don't shoot right away
00293                 if ( attDelay > 4000+((2-g_spskill.integer)*3000) )
00294                 {
00295                         attDelay = 4000+((2-g_spskill.integer)*3000);
00296                 }
00297                 TIMER_Set( self, "attackDelay", attDelay );//Q_irand( 1500, 4500 ) );
00298                 //don't move right away either
00299                 if ( attDelay > 4000 )
00300                 {
00301                         attDelay = 4000 - Q_irand(500, 1500);
00302                 }
00303                 else
00304                 {
00305                         attDelay -= Q_irand(500, 1500);
00306                 }
00307 
00308                 TIMER_Set( self, "roamTime", attDelay );//was Q_irand( 1000, 3500 );
00309         }
00310 }

void G_ClearEnemy gentity_t self  ) 
 

Definition at line 17 of file NPC_combat.c.

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

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

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

void G_CreateG2AttachedWeaponModel gentity_t ent,
const char *  weaponModel,
int  boltNum,
int  weaponNum
 

void G_ForceSaberOn gentity_t ent  ) 
 

Definition at line 312 of file NPC_combat.c.

References CHAN_AUTO, gentity_s::client, G_Sound(), gentity_t, gclient_s::ps, gclient_s::saber, playerState_s::saberHolstered, playerState_s::saberInFlight, saberInfo_t::soundOn, playerState_s::weapon, and WP_SABER.

Referenced by G_SetEnemy().

00313 {
00314         if (ent->client->ps.saberInFlight)
00315         { //alright, can't turn it on now in any case, so forget it.
00316                 return;
00317         }
00318 
00319         if (!ent->client->ps.saberHolstered)
00320         { //it's already on!
00321                 return;
00322         }
00323 
00324         if (ent->client->ps.weapon != WP_SABER)
00325         { //This probably should never happen. But if it does we'll just return without complaining.
00326                 return;
00327         }
00328 
00329         //Well then, turn it on.
00330         ent->client->ps.saberHolstered = 0;
00331 
00332         if (ent->client->saber[0].soundOn)
00333         {
00334                 G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOn);
00335         }
00336         if (ent->client->saber[1].soundOn)
00337         {
00338                 G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOn);
00339         }
00340 }

void G_SetEnemy gentity_t self,
gentity_t enemy
 

Referenced by AI_CheckEnemyCollision(), G_AlertTeam(), Howler_Patrol(), Jedi_CheckAmbushPlayer(), Jedi_CheckDanger(), MineMonster_Patrol(), NPC_Blocked(), NPC_BSDefault(), NPC_BSFollowLeader(), NPC_BSGrenadier_Patrol(), NPC_BSJedi_Default(), NPC_BSRancor_Default(), NPC_BSSniper_Patrol(), NPC_BSST_Sleep(), NPC_BSStandGuard(), NPC_BSWampa_Default(), NPC_CheckEnemy(), NPC_CheckEnemyStealth(), NPC_CheckInvestigate(), NPC_CheckPossibleEnemy(), NPC_FindEnemy(), NPC_Pain(), NPC_Rancor_Pain(), NPC_StartFlee(), NPC_Touch(), NPC_Wampa_Pain(), TurretG2Pain(), TurretPain(), and WP_SaberStartMissileBlockCheck().

qboolean G_TeamEnemy gentity_t self  ) 
 

Definition at line 67 of file NPC_combat.c.

References gentity_s::client, gentity_s::enemy, g_entities, gentity_t, gentity_s::health, level, gentity_s::NPC, level_locals_t::num_entities, gclient_s::playerTeam, qboolean, qfalse, qtrue, SCF_NO_GROUPS, gNPC_t::scriptFlags, and TEAM_FREE.

Referenced by G_SetEnemy().

00068 {//FIXME: Probably a better way to do this, is a linked list of your teammates already available?
00069         int     i;
00070         gentity_t       *ent;
00071 
00072         if ( !self->client || self->client->playerTeam == TEAM_FREE )
00073         {
00074                 return qfalse;
00075         }
00076         if ( self && self->NPC && (self->NPC->scriptFlags&SCF_NO_GROUPS) )
00077         {//I'm not a team playa...
00078                 return qfalse;
00079         }
00080 
00081         for( i = 1; i < level.num_entities; i++ )
00082         {
00083                 ent = &g_entities[i];
00084 
00085                 if ( ent == self )
00086                 {
00087                         continue;
00088                 }
00089 
00090                 if ( ent->health <= 0 )
00091                 {
00092                         continue;
00093                 }
00094 
00095                 if ( !ent->client )
00096                 {
00097                         continue;
00098                 }
00099 
00100                 if ( ent->client->playerTeam != self->client->playerTeam )
00101                 {//ent is not on my team
00102                         continue;
00103                 }
00104 
00105                 if ( ent->enemy )
00106                 {//they have an enemy
00107                         if ( !ent->enemy->client || ent->enemy->client->playerTeam != self->client->playerTeam )
00108                         {//the ent's enemy is either a normal ent or is a player/NPC that is not on my team
00109                                 return qtrue;
00110                         }
00111                 }
00112         }
00113 
00114         return qfalse;
00115 }

qboolean HaveWeapon int  weapon  ) 
 

Definition at line 1109 of file NPC_combat.c.

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

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

float IdealDistance gentity_t self  ) 
 

Definition at line 2475 of file NPC_combat.c.

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

Referenced by NPC_BSHuntAndKill().

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

int NAV_FindClosestWaypointForPoint2 vec3_t  point  ) 
 

Definition at line 384 of file g_nav.c.

References gentity_s::clipmask, G_FreeEntity(), G_SetOrigin(), G_Spawn(), gentity_t, MASK_NPCSOLID, entityShared_t::maxs, entityShared_t::mins, NF_CLEAR_PATH, gentity_s::r, trap_Nav_GetNearestNode(), vec3_t, VectorSet, gentity_s::waypoint, and WAYPOINT_NONE.

Referenced by CP_FindCombatPointWaypoints().

00385 {
00386         int     bestWP;
00387         //FIXME: can we make this a static ent?
00388         gentity_t *marker = G_Spawn();
00389         
00390         if ( !marker )
00391         {
00392                 return WAYPOINT_NONE;
00393         }
00394 
00395         G_SetOrigin( marker, point );
00396 
00397         VectorSet( marker->r.mins, -16, -16, -6 );//includes stepsize
00398         VectorSet( marker->r.maxs, 16, 16, 32 );
00399 
00400         marker->clipmask = MASK_NPCSOLID;
00401         marker->waypoint = WAYPOINT_NONE;
00402 
00403         bestWP = trap_Nav_GetNearestNode( marker, marker->waypoint, NF_CLEAR_PATH, WAYPOINT_NONE );
00404 
00405         G_FreeEntity( marker );
00406 
00407         return bestWP;
00408 }

int NAV_GetNearestNode gentity_t self,
int  lastNode
 

Definition at line 1083 of file g_nav.c.

References gentity_t, NF_CLEAR_PATH, trap_Nav_GetNearestNode(), and WAYPOINT_NONE.

Referenced by NAV_MoveToGoal(), NPC_BSFlee(), and NPC_FindCombatPoint().

01084 {
01085         return trap_Nav_GetNearestNode( self, lastNode, NF_CLEAR_PATH, WAYPOINT_NONE );
01086 }

void NPC_AimAdjust int  change  ) 
 

Definition at line 3098 of file NPC_combat.c.

References gNPCstats_e::aim, gNPC_t::currentAim, g_spskill, vmCvar_t::integer, NPC, NPCInfo, Q_irand(), gNPC_t::stats, TIMER_Done(), TIMER_Exists(), and TIMER_Set().

Referenced by NPC_BSEmplaced(), NPC_BSFollowLeader(), NPC_BSGM_Attack(), NPC_BSGrenadier_Attack(), and NPC_BSST_Attack().

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_ApplyWeaponFireDelay void   ) 
 

Definition at line 878 of file NPC_combat.c.

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

Referenced by ShootThink().

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

int NPC_AttackDebounceForWeapon void   ) 
 

Definition at line 1288 of file NPC_combat.c.

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

Referenced by ShootThink().

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

void NPC_ChangeWeapon int  newWeapon  ) 
 

Definition at line 844 of file NPC_combat.c.

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

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

qboolean NPC_CheckAttack float  scale  ) 
 

Definition at line 2228 of file NPC_combat.c.

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

Referenced by NPC_BSAdvanceFight(), and NPC_CheckCanAttack().

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

qboolean NPC_CheckCanAttack float  attack_scale,
qboolean  stationary
 

Definition at line 2263 of file NPC_combat.c.

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

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

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

qboolean NPC_CheckDefend float  scale  ) 
 

Definition at line 2250 of file NPC_combat.c.

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

Referenced by NPC_CheckCanAttack(), and NPC_StandTrackAndShoot().

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

gentity_t* NPC_CheckEnemy qboolean  findNew,
qboolean  tooFarOk,
qboolean  setEnemy
 

Definition at line 1895 of file NPC_combat.c.

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

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

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

void NPC_CheckGetNewWeapon void   ) 
 

Definition at line 3060 of file NPC_combat.c.

References gentity_s::enemy, gentity_t, gNPC_t::goalEntity, gentity_s::inuse, NPC, NPC_SearchForWeapons(), NPC_SetPickUpGoal(), NPCInfo, NULL, gentity_s::s, gNPC_t::tempGoal, TIMER_Done(), entityState_s::weapon, and WP_NONE.

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_CheckLookTarget gentity_t self  ) 
 

Definition at line 1653 of file NPC_utils.c.

01654 {
01655         if ( self->client )
01656         {
01657                 if ( self->client->renderInfo.lookTarget >= 0 && self->client->renderInfo.lookTarget < ENTITYNUM_WORLD )
01658                 {//within valid range
01659                         if ( (&g_entities[self->client->renderInfo.lookTarget] == NULL) || !g_entities[self->client->renderInfo.lookTarget].inuse )
01660                         {//lookTarget not inuse or not valid anymore
01661                                 NPC_ClearLookTarget( self );
01662                         }
01663                         else if ( self->client->renderInfo.lookTargetClearTime && self->client->renderInfo.lookTargetClearTime < level.time )
01664                         {//Time to clear lookTarget
01665                                 NPC_ClearLookTarget( self );
01666                         }
01667                         else if ( g_entities[self->client->renderInfo.lookTarget].client && self->enemy && (&g_entities[self->client->renderInfo.lookTarget] != self->enemy) )
01668                         {//should always look at current enemy if engaged in battle... FIXME: this could override certain scripted lookTargets...???
01669                                 NPC_ClearLookTarget( self );
01670                         }
01671                         else
01672                         {
01673                                 return qtrue;
01674                         }
01675                 }
01676         }
01677 
01678         return qfalse;
01679 }

void NPC_CheckPossibleEnemy gentity_t other,
visibility_t  vis
 

Definition at line 1229 of file NPC_combat.c.

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

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

void NPC_ClearLookTarget gentity_t self  ) 
 

Definition at line 1611 of file NPC_utils.c.

References gentity_s::client, EF2_HELD_BY_MONSTER, playerState_s::eFlags2, ENTITYNUM_NONE, gentity_t, renderInfo_s::lookTarget, renderInfo_s::lookTargetClearTime, gclient_s::ps, and gclient_s::renderInfo.

Referenced by G_ClearEnemy(), and NPC_CheckLookTarget().

01612 {
01613         if ( !self->client )
01614         {
01615                 return;
01616         }
01617 
01618         if ( (self->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
01619         {//lookTarget is set by and to the monster that's holding you, no other operations can change that
01620                 return;
01621         }
01622 
01623         self->client->renderInfo.lookTarget = ENTITYNUM_NONE;//ENTITYNUM_WORLD;
01624         self->client->renderInfo.lookTargetClearTime = 0;
01625 }

qboolean NPC_ClearShot gentity_t ent  ) 
 

Definition at line 2110 of file NPC_combat.c.

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

Referenced by NPC_CheckCanAttackExt().

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

qboolean NPC_EnemyTooFar gentity_t enemy,
float  dist,
qboolean  toShoot
 

Definition at line 1462 of file NPC_combat.c.

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

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

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

qboolean NPC_EvaluateShot int  hit,
qboolean  glassOK
 

Definition at line 2208 of file NPC_combat.c.

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

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

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

Definition at line 2657 of file NPC_combat.c.

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

Referenced by NPC_StartFlee(), and ST_Commander().

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

int NPC_FindSquadPoint vec3_t  position  ) 
 

Definition at line 2882 of file NPC_combat.c.

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

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

qboolean NPC_FreeCombatPoint int  combatPointID,
qboolean  failed
 

Definition at line 2945 of file NPC_combat.c.

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

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

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

void NPC_Jedi_RateNewEnemy gentity_t self,
gentity_t enemy
 

Definition at line 914 of file NPC_AI_Jedi.c.

References gNPCstats_e::aggression, ceil(), entityShared_t::currentOrigin, gentity_t, gentity_s::health, gentity_s::NPC, Q_irand(), gentity_s::r, gentity_s::s, gNPC_t::stats, TIMER_Set(), entityState_s::weapon, WP_BLASTER, and WP_SABER.

Referenced by G_SetEnemy().

00915 {
00916         float healthAggression;
00917         float weaponAggression;
00918         int newAggression;
00919 
00920         switch( enemy->s.weapon )
00921         {
00922         case WP_SABER:
00923                 healthAggression = (float)self->health/200.0f*6.0f;
00924                 weaponAggression = 7;//go after him
00925                 break;
00926         case WP_BLASTER:
00927                 if ( DistanceSquared( self->r.currentOrigin, enemy->r.currentOrigin ) < 65536 )//256 squared
00928                 {
00929                         healthAggression = (float)self->health/200.0f*8.0f;
00930                         weaponAggression = 8;//go after him
00931                 }
00932                 else
00933                 {
00934                         healthAggression = 8.0f - ((float)self->health/200.0f*8.0f);
00935                         weaponAggression = 2;//hang back for a second
00936                 }
00937                 break;
00938         default:
00939                 healthAggression = (float)self->health/200.0f*8.0f;
00940                 weaponAggression = 6;//approach
00941                 break;
00942         }
00943         //Average these with current aggression
00944         newAggression = ceil( (healthAggression + weaponAggression + (float)self->NPC->stats.aggression )/3.0f);
00945         //Com_Printf( "(%d) new agg %d - new enemy\n", level.time, newAggression );
00946         Jedi_Aggression( self, newAggression - self->NPC->stats.aggression );
00947 
00948         //don't taunt right away
00949         TIMER_Set( self, "chatter", Q_irand( 4000, 7000 ) );
00950 }

float NPC_MaxDistSquaredForWeapon void   ) 
 

Definition at line 1334 of file NPC_combat.c.

References saberInfo_t::blade, gentity_s::client, bladeInfo_t::lengthMax, entityShared_t::maxs, NPC, NPCInfo, gentity_s::r, gentity_s::s, gclient_s::saber, SCF_ALT_FIRE, gNPC_t::scriptFlags, gNPCstats_e::shootDistance, gNPC_t::stats, entityState_s::weapon, WP_BLASTER, WP_BRYAR_PISTOL, WP_DISRUPTOR, and WP_SABER.

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

gentity_t* NPC_PickAlly qboolean  facingEachOther,
float  range,
qboolean  ignoreGroup,
qboolean  movingOnly
 

Definition at line 1804 of file NPC_combat.c.

References AngleVectors(), CHECK_360, CHECK_VISRANGE, gentity_s::client, entityShared_t::currentOrigin, DotProduct, g_entities, gentity_t, gentity_s::health, gclient_s::leader, level, NPC, NPC_CheckVisibility(), NPCTEAM_ENEMY, NULL, level_locals_t::num_entities, gclient_s::playerTeam, gclient_s::ps, gentity_s::r, trap_InPVS(), vec3_t, VectorNormalize(), VectorSubtract, playerState_s::velocity, playerState_s::viewangles, and VIS_360.

01805 {
01806         gentity_t       *ally = NULL;
01807         gentity_t       *closestAlly = NULL;
01808         int                     entNum;
01809         vec3_t          diff;
01810         float           relDist;
01811         float           bestDist = range;
01812 
01813         for ( entNum = 0; entNum < level.num_entities; entNum++ )
01814         {
01815                 ally = &g_entities[entNum];
01816 
01817                 if ( ally->client )
01818                 {
01819                         if ( ally->health > 0 )
01820                         {
01821                                 if ( ally->client && ( ally->client->playerTeam == NPC->client->playerTeam ||
01822                                          NPC->client->playerTeam == NPCTEAM_ENEMY ) )// && ally->client->playerTeam == TEAM_DISGUISE ) ) )
01823                                 {//if on same team or if player is disguised as your team
01824                                         if ( ignoreGroup )
01825                                         {
01826                                                 if ( ally == NPC->client->leader )
01827                                                 {
01828                                                         //reject
01829                                                         continue;
01830                                                 }
01831                                                 if ( ally->client && ally->client->leader && ally->client->leader == NPC )
01832                                                 {
01833                                                         //reject
01834                                                         continue;
01835                                                 }
01836                                         }
01837 
01838                                         if(!trap_InPVS(ally->r.currentOrigin, NPC->r.currentOrigin))
01839                                         {
01840                                                 continue;
01841                                         }
01842 
01843                                         if ( movingOnly && ally->client && NPC->client )
01844                                         {//They have to be moving relative to each other
01845                                                 if ( !DistanceSquared( ally->client->ps.velocity, NPC->client->ps.velocity ) )
01846                                                 {
01847                                                         continue;
01848                                                 }
01849                                         }
01850 
01851                                         VectorSubtract( NPC->r.currentOrigin, ally->r.currentOrigin, diff );
01852                                         relDist = VectorNormalize( diff );
01853                                         if ( relDist < bestDist )
01854                                         {
01855                                                 if ( facingEachOther )
01856                                                 {
01857                                                         vec3_t  vf;
01858                                                         float   dot;
01859 
01860                                                         AngleVectors( ally->client->ps.viewangles, vf, NULL, NULL );
01861                                                         VectorNormalize(vf);
01862                                                         dot = DotProduct(diff, vf);
01863 
01864                                                         if ( dot < 0.5 )
01865                                                         {//Not facing in dir to me
01866                                                                 continue;
01867                                                         }
01868                                                         //He's facing me, am I facing him?
01869                                                         AngleVectors( NPC->client->ps.viewangles, vf, NULL, NULL );
01870                                                         VectorNormalize(vf);
01871                                                         dot = DotProduct(diff, vf);
01872 
01873                                                         if ( dot > -0.5 )
01874                                                         {//I'm not facing opposite of dir to me
01875                                                                 continue;
01876                                                         }
01877                                                         //I am facing him
01878                                                 }
01879 
01880                                                 if ( NPC_CheckVisibility ( ally, CHECK_360|CHECK_VISRANGE ) >= VIS_360 )
01881                                                 {
01882                                                         bestDist = relDist;
01883                                                         closestAlly = ally;
01884                                                 }
01885                                         }
01886                                 }
01887                         }
01888                 }
01889         }
01890 
01891         
01892         return closestAlly;
01893 }

gentity_t* NPC_PickEnemy gentity_t closestTo,
int  enemyTeam,
qboolean  checkVis,
qboolean  findPlayersFirst,
qboolean  findClosest
 

Definition at line 1505 of file NPC_combat.c.

References gentity_s::alliedTeam, gNPC_t::behaviorState, BS_HUNT_AND_KILL, BS_INVESTIGATE, BS_PATROL, BS_STAND_AND_SHOOT, CHECK_360, CHECK_FOV, CHECK_VISRANGE, gentity_s::client, entityShared_t::currentOrigin, DEBUG_LEVEL_INFO, Debug_Printf(), debugNPCAI, DotProduct, EF_NODRAW, entityState_s::eFlags, gentity_s::enemy, FL_NOTARGET, gentity_s::flags, g_entities, gentity_t, gentity_s::health, gclient_s::hiddenDir, gclient_s::hiddenDist, InVisrange(), gentity_s::lastEnemy, level, NPC, NPC_CheckVisibility(), NPC_EnemyTooFar(), NPC_ValidEnemy(), NPCInfo, NPCTEAM_NEUTRAL, NPCTEAM_PLAYER, NULL, level_locals_t::num_entities, entityState_s::number, gclient_s::playerTeam, Q3_INFINITE, qboolean, qfalse, qtrue, gentity_s::r, rand(), gentity_s::s, gentity_s::targetname, trap_InPVS(), vec3_t, VectorNormalize(), VectorSubtract, VIS_360, VIS_FOV, and vtos().

Referenced by NPC_BSStandGuard(), and NPC_CheckEnemy().

01506 {
01507         int                     num_choices = 0;
01508         int                     choice[128];//FIXME: need a different way to determine how many choices?
01509         gentity_t       *newenemy = NULL;
01510         gentity_t       *closestEnemy = NULL;
01511         int                     entNum;
01512         vec3_t          diff;
01513         float           relDist;
01514         float           bestDist = Q3_INFINITE;
01515         qboolean        failed = qfalse;
01516         int                     visChecks = (CHECK_360|CHECK_FOV|CHECK_VISRANGE);
01517         int                     minVis = VIS_FOV;
01518 
01519         if ( enemyTeam == NPCTEAM_NEUTRAL )
01520         {
01521                 return NULL;
01522         }
01523 
01524         if ( NPCInfo->behaviorState == BS_STAND_AND_SHOOT || 
01525                 NPCInfo->behaviorState == BS_HUNT_AND_KILL ) 
01526         {//Formations guys don't require inFov to pick up a target
01527                 //These other behavior states are active battle states and should not
01528                 //use FOV.  FOV checks are for enemies who are patrolling, guarding, etc.
01529                 visChecks &= ~CHECK_FOV;
01530                 minVis = VIS_360;
01531         }
01532 
01533         if( findPlayersFirst )
01534         {//try to find a player first
01535                 newenemy = &g_entities[0];
01536                 if( newenemy->client && !(newenemy->flags & FL_NOTARGET) && !(newenemy->s.eFlags & EF_NODRAW))
01537                 {
01538                         if( newenemy->health > 0 )
01539                         {
01540                                 if( NPC_ValidEnemy( newenemy) )//enemyTeam == TEAM_PLAYER || newenemy->client->playerTeam == enemyTeam || ( enemyTeam == TEAM_PLAYER ) )
01541                                 {//FIXME:  check for range and FOV or vis?
01542                                         if( newenemy != NPC->lastEnemy )
01543                                         {//Make sure we're not just going back and forth here
01544                                                 if ( trap_InPVS(newenemy->r.currentOrigin, NPC->r.currentOrigin) )
01545                                                 {
01546                                                         if(NPCInfo->behaviorState == BS_INVESTIGATE ||  NPCInfo->behaviorState == BS_PATROL)
01547                                                         {
01548                                                                 if(!NPC->enemy)
01549                                                                 {
01550                                                                         if(!InVisrange(newenemy))
01551                                                                         {
01552                                                                                 failed = qtrue;
01553                                                                         }
01554                                                                         else if(NPC_CheckVisibility ( newenemy, CHECK_360|CHECK_FOV|CHECK_VISRANGE ) != VIS_FOV)
01555                                                                         {
01556                                                                                 failed = qtrue;
01557                                                                         }
01558                                                                 }
01559                                                         }
01560 
01561                                                         if ( !failed )
01562                                                         {
01563                                                                 VectorSubtract( closestTo->r.currentOrigin, newenemy->r.currentOrigin, diff );
01564                                                                 relDist = VectorLengthSquared(diff);
01565                                                                 if ( newenemy->client->hiddenDist > 0 )
01566                                                                 {
01567                                                                         if( relDist > newenemy->client->hiddenDist*newenemy->client->hiddenDist )
01568                                                                         {
01569                                                                                 //out of hidden range
01570                                                                                 if ( VectorLengthSquared( newenemy->client->hiddenDir ) )
01571                                                                                 {//They're only hidden from a certain direction, check
01572                                                                                         float   dot;
01573                                                                                         VectorNormalize( diff );
01574                                                                                         dot = DotProduct( newenemy->client->hiddenDir, diff ); 
01575                                                                                         if ( dot > 0.5 )
01576                                                                                         {//I'm not looking in the right dir toward them to see them 
01577                                                                                                 failed = qtrue;
01578                                                                                         }
01579                                                                                         else
01580                                                                                         {
01581                                                                                                 Debug_Printf(&debugNPCAI, DEBUG_LEVEL_INFO, "%s saw %s trying to hide - hiddenDir %s targetDir %s dot %f\n", NPC->targetname, newenemy->targetname, vtos(newenemy->client->hiddenDir), vtos(diff), dot );
01582                                                                                         }
01583                                                                                 }
01584                                                                                 else
01585                                                                                 {
01586                                                                                         failed = qtrue;
01587                                                                                 }
01588                                                                         }
01589                                                                         else
01590                                                                         {
01591                                                                                 Debug_Printf(&debugNPCAI, DEBUG_LEVEL_INFO, "%s saw %s trying to hide - hiddenDist %f\n", NPC->targetname, newenemy->targetname, newenemy->client->hiddenDist );
01592                                                                         }
01593                                                                 }
01594 
01595                                                                 if(!failed)
01596                                                                 {
01597                                                                         if(findClosest)
01598                                                                         {
01599                                                                                 if(relDist < bestDist)
01600                                                                                 {
01601                                                                                         if(!NPC_EnemyTooFar(newenemy, relDist, qfalse))
01602                                                                                         {
01603                                                                                                 if(checkVis)
01604                                                                                                 {
01605                                                                                                         if( NPC_CheckVisibility ( newenemy, visChecks ) == minVis )
01606                                                                                                         {
01607                                                                                                                 bestDist = relDist;
01608                                                                                                                 closestEnemy = newenemy;
01609                                                                                                         }
01610                                                                                                 }
01611                                                                                                 else
01612                                                                                                 {
01613                                                                                                         bestDist = relDist;
01614                                                                                                         closestEnemy = newenemy;
01615                                                                                                 }
01616                                                                                         }
01617                                                                                 }
01618                                                                         }
01619                                                                         else if(!NPC_EnemyTooFar(newenemy, 0, qfalse))
01620                                                                         {
01621                                                                                 if(checkVis)
01622                                                                                 {
01623                                                                                         if( NPC_CheckVisibility ( newenemy, CHECK_360|CHECK_FOV|CHECK_VISRANGE ) == VIS_FOV )
01624                                                                                         {
01625                                                                                                 choice[num_choices++] = newenemy->s.number;
01626                                                                                         }
01627                                                                                 }
01628                                                                                 else
01629                                                                                 {
01630                                                                                         choice[num_choices++] = newenemy->s.number;
01631                                                                                 }
01632                                                                         }
01633                                                                 }
01634                                                         }
01635                                                 }
01636                                         }
01637                                 }
01638                         }
01639                 }
01640         }
01641 
01642         if (findClosest && closestEnemy)
01643         {
01644                 return closestEnemy;
01645         }
01646 
01647         if (num_choices)
01648         {
01649                 return &g_entities[ choice[rand() % num_choices] ];
01650         }
01651 
01652         /*
01653         //FIXME: used to have an option to look *only* for the player... now...?  Still need it?
01654         if ( enemyTeam == TEAM_PLAYER )
01655         {//couldn't find the player
01656                 return NULL;
01657         }
01658         */
01659 
01660         num_choices = 0;
01661         bestDist = Q3_INFINITE;
01662         closestEnemy = NULL;
01663 
01664         for ( entNum = 0; entNum < level.num_entities; entNum++ )
01665         {
01666                 newenemy = &g_entities[entNum];
01667 
01668                 if ( newenemy != NPC && (newenemy->client /*|| newenemy->svFlags & SVF_NONNPC_ENEMY*/) && !(newenemy->flags & FL_NOTARGET) && !(newenemy->s.eFlags & EF_NODRAW))
01669                 {
01670                         if ( newenemy->health > 0 )
01671                         {
01672                                 if ( (newenemy->client && NPC_ValidEnemy( newenemy))
01673                                         || (!newenemy->client && newenemy->alliedTeam == enemyTeam) )
01674                                 {//FIXME:  check for range and FOV or vis?
01675                                         if ( NPC->client->playerTeam == NPCTEAM_PLAYER && enemyTeam == NPCTEAM_PLAYER )
01676                                         {//player allies turning on ourselves?  How?
01677                                                 if ( newenemy->s.number )
01678                                                 {//only turn on the player, not other player allies
01679                                                         continue;
01680                                                 }
01681                                         }
01682 
01683                                         if ( newenemy != NPC->lastEnemy )
01684                                         {//Make sure we're not just going back and forth here
01685                                                 if(!trap_InPVS(newenemy->r.currentOrigin, NPC->r.currentOrigin))
01686                                                 {
01687                                                         continue;
01688                                                 }
01689 
01690                                                 if ( NPCInfo->behaviorState == BS_INVESTIGATE || NPCInfo->behaviorState == BS_PATROL )
01691                                                 {
01692                                                         if ( !NPC->enemy )
01693                                                         {
01694                                                                 if ( !InVisrange( newenemy ) )
01695                                                                 {
01696                                                                         continue;
01697                                                                 }
01698                                                                 else if ( NPC_CheckVisibility ( newenemy, CHECK_360|CHECK_FOV|CHECK_VISRANGE ) != VIS_FOV )
01699                                                                 {
01700                                                                         continue;
01701                                                                 }
01702                                                         }
01703                                                 }
01704 
01705                                                 VectorSubtract( closestTo->r.currentOrigin, newenemy->r.currentOrigin, diff );
01706                                                 relDist = VectorLengthSquared(diff);
01707                                                 if ( newenemy->client && newenemy->client->hiddenDist > 0 )
01708                                                 {
01709                                                         if( relDist > newenemy->client->hiddenDist*newenemy->client->hiddenDist )
01710                                                         {
01711                                                                 //out of hidden range
01712                                                                 if ( VectorLengthSquared( newenemy->client->hiddenDir ) )
01713                                                                 {//They're only hidden from a certain direction, check
01714                                                                         float   dot;
01715 
01716                                                                         VectorNormalize( diff );
01717                                                                         dot = DotProduct( newenemy->client->hiddenDir, diff ); 
01718                                                                         if ( dot > 0.5 )
01719                                                                         {//I'm not looking in the right dir toward them to see them 
01720                                                                                 continue;
01721                                                                         }
01722                                                                         else
01723                                                                         {
01724                                                                                 Debug_Printf(&debugNPCAI, DEBUG_LEVEL_INFO, "%s saw %s trying to hide - hiddenDir %s targetDir %s dot %f\n", NPC->targetname, newenemy->targetname, vtos(newenemy->client->hiddenDir), vtos(diff), dot );
01725                                                                         }
01726                                                                 }
01727                                                                 else
01728                                                                 {
01729                                                                         continue;
01730                                                                 }
01731                                                         }
01732                                                         else
01733                                                         {
01734                                                                 Debug_Printf(&debugNPCAI, DEBUG_LEVEL_INFO, "%s saw %s trying to hide - hiddenDist %f\n", NPC->targetname, newenemy->targetname, newenemy->client->hiddenDist );
01735                                                         }
01736                                                 }
01737 
01738                                                 if ( findClosest )
01739                                                 {
01740                                                         if ( relDist < bestDist )
01741                                                         {
01742                                                                 if ( !NPC_EnemyTooFar( newenemy, relDist, qfalse ) )
01743                                                                 {
01744                                                                         if ( checkVis )
01745                                                                         {
01746                                                                                 //FIXME: NPCs need to be able to pick up other NPCs behind them,
01747                                                                                 //but for now, commented out because it was picking up enemies it shouldn't
01748                                                                                 //if ( NPC_CheckVisibility ( newenemy, CHECK_360|CHECK_VISRANGE ) >= VIS_360 )
01749                                                                                 if ( NPC_CheckVisibility ( newenemy, visChecks ) == minVis )
01750                                                                                 {
01751                                                                                         bestDist = relDist;
01752                                                                                         closestEnemy = newenemy;
01753                                                                                 }
01754                                                                         }
01755                                                                         else
01756                                                                         {
01757                                                                                 bestDist = relDist;
01758                                                                                 closestEnemy = newenemy;
01759                                                                         }
01760                                                                 }
01761                                                         }
01762                                                 }
01763                                                 else if ( !NPC_EnemyTooFar( newenemy, 0, qfalse ) )
01764                                                 {
01765                                                         if ( checkVis )
01766                                                         {
01767                                                                 //if( NPC_CheckVisibility ( newenemy, CHECK_360|CHECK_FOV|CHECK_VISRANGE ) == VIS_FOV )
01768                                                                 if ( NPC_CheckVisibility ( newenemy, CHECK_360|CHECK_VISRANGE ) >= VIS_360 )
01769                                                                 {
01770                                                                         choice[num_choices++] = newenemy->s.number;
01771                                                                 }
01772                                                         }
01773                                                         else
01774                                                         {
01775                                                                 choice[num_choices++] = newenemy->s.number;
01776                                                         }
01777                                                 }
01778                                         }
01779                                 }
01780                         }
01781                 }
01782         }
01783 
01784         
01785         if (findClosest)
01786         {//FIXME: you can pick up an enemy around a corner this way.
01787                 return closestEnemy;
01788         }
01789 
01790         if (!num_choices)
01791         {
01792                 return NULL;
01793         }
01794 
01795         return &g_entities[ choice[rand() % num_choices] ];
01796 }

qboolean NPC_ReserveCombatPoint int  combatPointID  ) 
 

Definition at line 2923 of file NPC_combat.c.

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

Referenced by NPC_SetCombatPoint().

02924 {
02925         //Make sure it's valid
02926         if ( combatPointID > level.numCombatPoints )
02927                 return qfalse;
02928 
02929         //Make sure it's not already occupied
02930         if ( level.combatPoints[combatPointID].occupied )
02931                 return qfalse;
02932 
02933         //Reserve it
02934         level.combatPoints[combatPointID].occupied = qtrue;
02935 
02936         return qtrue;
02937 }

gentity_t* NPC_SearchForWeapons void   ) 
 

Definition at line 2988 of file NPC_combat.c.

References CheckItemCanBePickedUpByNPC(), gentity_s::clipmask, entityShared_t::currentOrigin, EF_NODRAW, entityState_s::eFlags, ENTITYNUM_NONE, ET_ITEM, entityState_s::eType, g_entities, gentity_t, gitem_s::giType, gentity_s::inuse, IT_WEAPON, gentity_s::item, level, entityShared_t::maxs, entityShared_t::mins, NAV_ClearPathToPoint(), NF_CLEAR_PATH, NODE_NONE, NPC, NULL, level_locals_t::num_entities, Q3_INFINITE, gentity_s::r, gentity_s::s, trap_InPVS(), trap_Nav_GetBestNodeAltRoute2(), trap_Nav_GetBestPathBetweenEnts(), gentity_s::waypoint, and WAYPOINT_NONE.

Referenced by NPC_CheckGetNewWeapon().

02989 {
02990         gentity_t *found = g_entities, *bestFound = NULL;
02991         float           dist, bestDist = Q3_INFINITE;
02992         int i;
02993 //      for ( found = g_entities; found < &g_entities[globals.num_entities] ; found++)
02994         for ( i = 0; i<level.num_entities; i++)
02995         {
02996 //              if ( !found->inuse )
02997 //              {
02998 //                      continue;
02999 //              }
03000                 if(!g_entities[i].inuse)
03001                         continue;
03002                 
03003                 found=&g_entities[i];
03004                 
03005                 //FIXME: Also look for ammo_racks that have weapons on them?
03006                 if ( found->s.eType != ET_ITEM )
03007                 {
03008                         continue;
03009                 }
03010                 if ( found->item->giType != IT_WEAPON )
03011                 {
03012                         continue;
03013                 }
03014                 if ( found->s.eFlags & EF_NODRAW )
03015                 {
03016                         continue;
03017                 }
03018                 if ( CheckItemCanBePickedUpByNPC( found, NPC ) )
03019                 {
03020                         if ( trap_InPVS( found->r.currentOrigin, NPC->r.currentOrigin ) )
03021                         {
03022                                 dist = DistanceSquared( found->r.currentOrigin, NPC->r.currentOrigin );
03023                                 if ( dist < bestDist )
03024                                 {
03025                                         if ( !trap_Nav_GetBestPathBetweenEnts( NPC, found, NF_CLEAR_PATH ) 
03026                                                 || trap_Nav_GetBestNodeAltRoute2( NPC->waypoint, found->waypoint, NODE_NONE ) == WAYPOINT_NONE )
03027                                         {//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
03028                                                 if ( NAV_ClearPathToPoint( NPC, NPC->r.mins, NPC->r.maxs, found->r.currentOrigin, NPC->clipmask, ENTITYNUM_NONE ) )
03029                                                 {//have a clear straight path to this one
03030                                                         bestDist = dist;
03031                                                         bestFound = found;
03032                                                 }
03033                                         }
03034                                         else
03035                                         {//can nav to it
03036                                                 bestDist = dist;
03037                                                 bestFound = found;
03038                                         }
03039                                 }
03040                         }
03041                 }
03042         }
03043 
03044         return bestFound;
03045 }

qboolean NPC_SetCombatPoint int  combatPointID  ) 
 

Definition at line 2971 of file NPC_combat.c.

References gNPC_t::combatPoint, NPC_FreeCombatPoint(), NPC_ReserveCombatPoint(), NPCInfo, qboolean, qfalse, and qtrue.

Referenced by NPC_StartFlee(), and ST_Commander().

02972 {
02973         //Free a combat point if we already have one
02974         if ( NPCInfo->combatPoint != -1 )
02975         {
02976                 NPC_FreeCombatPoint( NPCInfo->combatPoint, qfalse );
02977         }
02978 
02979         if ( NPC_ReserveCombatPoint( combatPointID ) == qfalse )
02980                 return qfalse;
02981 
02982         NPCInfo->combatPoint = combatPointID;
02983 
02984         return qtrue;
02985 }

void NPC_SetPickUpGoal gentity_t foundWeap  ) 
 

Definition at line 3047 of file NPC_combat.c.

References BS_DEFAULT, entityShared_t::currentOrigin, gentity_t, entityShared_t::maxs, entityShared_t::mins, NPC, NPC_SetMoveGoal(), NPCInfo, qfalse, gentity_s::r, SQUAD_TRANSITION, gNPC_t::squadState, gNPC_t::tempBehavior, gNPC_t::tempGoal, vec3_t, VectorCopy, and gentity_s::waypoint.

Referenced by NPC_CheckGetNewWeapon().

03048 {
03049         vec3_t org;
03050 
03051         //NPCInfo->goalEntity = foundWeap;
03052         VectorCopy( foundWeap->r.currentOrigin, org );
03053         org[2] += 24 - (foundWeap->r.mins[2]*-1);//adjust the origin so that I am on the ground
03054         NPC_SetMoveGoal( NPC, org, foundWeap->r.maxs[0]*0.75, qfalse, -1, foundWeap );
03055         NPCInfo->tempGoal->waypoint = foundWeap->waypoint;
03056         NPCInfo->tempBehavior = BS_DEFAULT;
03057         NPCInfo->squadState = SQUAD_TRANSITION;
03058 }

int NPC_ShotEntity gentity_t ent,
vec3_t  impactPos
 

Definition at line 2152 of file NPC_combat.c.

References AngleVectors(), CalcEntitySpot(), gentity_s::client, trace_t::endpos, trace_t::entityNum, gentity_t, MASK_SHOT, NPC, NULL, entityState_s::number, gclient_s::ps, qfalse, gentity_s::s, SPOT_CHEST, SPOT_HEAD, SPOT_WEAPON, trap_Trace(), vec3_origin, vec3_t, VectorCopy, VectorMA, VectorSet, playerState_s::viewangles, entityState_s::weapon, WP_BLASTER, and WP_THERMAL.

Referenced by Boba_FireDecide(), NPC_BSEmplaced(), NPC_BSGM_Attack(), NPC_BSGrenadier_Attack(), and NPC_BSST_Attack().

02153 {
02154         vec3_t  muzzle;
02155         vec3_t targ;
02156         trace_t tr;
02157 
02158         if ( ( NPC == NULL ) || ( ent == NULL ) )
02159                 return qfalse;
02160 
02161         if ( NPC->s.weapon == WP_THERMAL )
02162         {//thermal aims from slightly above head
02163                 //FIXME: what about low-angle shots, rolling the thermal under something?
02164                 vec3_t  angles, forward, end;
02165 
02166                 CalcEntitySpot( NPC, SPOT_HEAD, muzzle );
02167                 VectorSet( angles, 0, NPC->client->ps.viewangles[1], 0 );
02168                 AngleVectors( angles, forward, NULL, NULL );
02169                 VectorMA( muzzle, 8, forward, end );
02170                 end[2] += 24;
02171                 trap_Trace ( &tr, muzzle, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
02172                 VectorCopy( tr.endpos, muzzle );
02173         }
02174         else
02175         {
02176                 CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
02177         }
02178         CalcEntitySpot( ent, SPOT_CHEST, targ );
02179         
02180         // add aim error
02181         // use weapon instead of specific npc types, although you could add certain npc classes if you wanted
02182 //      if ( NPC->client->playerTeam == TEAM_SCAVENGERS )
02183         if( NPC->s.weapon == WP_BLASTER /*|| NPC->s.weapon == WP_BLASTER_PISTOL*/ ) // any other guns to check for?
02184         {
02185                 vec3_t  mins = { -2, -2, -2 };
02186                 vec3_t  maxs = {  2,  2,  2 };
02187 
02188                 trap_Trace ( &tr, muzzle, mins, maxs, targ, NPC->s.number, MASK_SHOT );
02189         }
02190         else
02191         {
02192                 trap_Trace ( &tr, muzzle, NULL, NULL, targ, NPC->s.number, MASK_SHOT );
02193         }
02194         //FIXME: if using a bouncing weapon like the bowcaster, should we check the reflection of the wall, too?
02195         if ( impactPos )
02196         {//they want to know *where* the hit would be, too
02197                 VectorCopy( tr.endpos, impactPos );
02198         }
02199 /* // NPCs should be able to shoot even if the muzzle would be inside their target
02200         if ( tr.startsolid || tr.allsolid )
02201         {
02202                 return ENTITYNUM_NONE;
02203         }
02204 */
02205         return tr.entityNum;
02206 }

qboolean PM_DroidMelee int  npc_class  ) 
 

void ShootThink void   ) 
 

Definition at line 925 of file NPC_combat.c.

References gNPC_t::aiFlags, playerState_s::ammo, gentity_s::attackDebounceTime, gNPC_t::burstCount, gNPC_t::burstMax, gNPC_t::burstMin, gNPC_t::burstSpacing, BUTTON_ATTACK, usercmd_s::buttons, client, gNPC_t::currentAmmo, g_spskill, vmCvar_t::integer, level, NPC, NPC_ApplyWeaponFireDelay(), NPC_AttackDebounceForWeapon(), NPCAI_BURST_WEAPON, NPCInfo, gentity_s::parent, gclient_s::ps, Q_irand(), gentity_s::random, gNPC_t::shotTime, level_locals_t::time, ucmd, playerState_s::weapon, WEAPON_FIRING, WEAPON_IDLE, WEAPON_READY, weaponData, playerState_s::weaponstate, WP_EMPLACED_GUN, and WP_NONE.

Referenced by WeaponThink().

00926 {
00927         int                     delay;
00928 
00929         ucmd.buttons &= ~BUTTON_ATTACK;
00930 /*
00931         if ( enemyVisibility != VIS_SHOOT) 
00932                 return;
00933 */
00934 
00935         if ( client->ps.weapon == WP_NONE )
00936                 return;
00937 
00938         if ( client->ps.weaponstate != WEAPON_READY && client->ps.weaponstate != WEAPON_FIRING && client->ps.weaponstate != WEAPON_IDLE) 
00939                 return;
00940 
00941         if ( level.time < NPCInfo->shotTime ) 
00942         {
00943                 return;
00944         }
00945 
00946         ucmd.buttons |= BUTTON_ATTACK;
00947 
00948         NPCInfo->currentAmmo = client->ps.ammo[weaponData[client->ps.weapon].ammoIndex];        // checkme
00949 
00950         NPC_ApplyWeaponFireDelay();
00951 
00952         if ( NPCInfo->aiFlags & NPCAI_BURST_WEAPON ) 
00953         {
00954                 if ( !NPCInfo->burstCount ) 
00955                 {
00956                         NPCInfo->burstCount = Q_irand( NPCInfo->burstMin, NPCInfo->burstMax );
00957                         /*
00958                         NPCInfo->burstCount = erandom( NPCInfo->burstMean );
00959                         if ( NPCInfo->burstCount < NPCInfo->burstMin ) 
00960                         {
00961                                 NPCInfo->burstCount = NPCInfo->burstMin;
00962                         }
00963                         else if ( NPCInfo->burstCount > NPCInfo->burstMax ) 
00964                         {
00965                                 NPCInfo->burstCount = NPCInfo->burstMax;
00966                         }
00967                         */
00968                         delay = 0;
00969                 }
00970                 else 
00971                 {
00972                         NPCInfo->burstCount--;
00973                         if ( NPCInfo->burstCount == 0 ) 
00974                         {
00975                                 delay = NPCInfo->burstSpacing;
00976                         }
00977                         else 
00978                         {
00979                                 delay = 0;
00980                         }
00981                 }
00982 
00983                 if ( !delay )
00984                 {
00985                         // HACK: dirty little emplaced bits, but is done because it would otherwise require some sort of new variable...
00986                         if ( client->ps.weapon == WP_EMPLACED_GUN )
00987                         {
00988                                 if ( NPC->parent ) // try and get the debounce values from the chair if we can
00989                                 {
00990                                         if ( g_spskill.integer == 0 )
00991                                         {
00992                                                 delay = NPC->parent->random + 150;
00993                                         }
00994                                         else if ( g_spskill.integer == 1 )
00995                                         {
00996                                                 delay = NPC->parent->random + 100;
00997                                         }
00998                                         else 
00999                                         {
01000                                                 delay = NPC->parent->random;
01001                                         }
01002                                 }
01003                                 else
01004                                 {
01005                                         if ( g_spskill.integer == 0 )
01006                                         {
01007                                                 delay = 350;
01008                                         }
01009                                         else if ( g_spskill.integer == 1 )
01010                                         {
01011                                                 delay = 300;
01012                                         }
01013                                         else 
01014                                         {
01015                                                 delay = 200;
01016                                         }
01017                                 }
01018                         }
01019                 }
01020         }
01021         else 
01022         {
01023                 delay = NPCInfo->burstSpacing;
01024         }
01025 
01026         NPCInfo->shotTime = level.time + delay;
01027         NPC->attackDebounceTime = level.time + NPC_AttackDebounceForWeapon();
01028 }

qboolean ShotThroughGlass trace_t tr,
gentity_t target,
vec3_t  spot,
int  mask
 

Definition at line 1126 of file NPC_combat.c.

References trace_t::endpos, EntIsGlass(), trace_t::entityNum, g_entities, gentity_t, NULL, entityState_s::number, qboolean, qfalse, qtrue, gentity_s::s, trap_Trace(), vec3_t, and VectorCopy.

Referenced by CanSee(), CanShoot(), and NPC_CheckCanAttack().

01127 {
01128         gentity_t       *hit = &g_entities[ tr->entityNum ];
01129         if(hit != target && EntIsGlass(hit))
01130         {//ok to shoot through breakable glass
01131                 int                     skip = hit->s.number;
01132                 vec3_t          muzzle;
01133 
01134                 VectorCopy(tr->endpos, muzzle);
01135                 trap_Trace (tr, muzzle, NULL, NULL, spot, skip, mask );
01136                 return qtrue;
01137         }
01138 
01139         return qfalse;
01140 }

void SP_point_combat gentity_t self  ) 
 

Definition at line 2516 of file NPC_combat.c.

References Com_Printf(), level_locals_t::combatPoints, entityShared_t::currentOrigin, G_CheckInSolid(), G_FreeEntity(), G_SetOrigin(), gentity_t, level, MAX_COMBAT_POINTS, level_locals_t::numCombatPoints, entityState_s::origin, qfalse, qtrue, gentity_s::r, gentity_s::s, S_COLOR_RED, gentity_s::spawnflags, trap_LinkEntity(), VectorCopy, and vtos().

02517 {
02518         if(level.numCombatPoints >= MAX_COMBAT_POINTS)
02519         {
02520 #ifndef FINAL_BUILD
02521                 Com_Printf(S_COLOR_RED"ERROR:  Too many combat points, limit is %d\n", MAX_COMBAT_POINTS);
02522 #endif
02523                 G_FreeEntity(self);
02524                 return;
02525         }
02526 
02527         self->s.origin[2] += 0.125;
02528         G_SetOrigin(self, self->s.origin);
02529         trap_LinkEntity(self);
02530 
02531         if ( G_CheckInSolid( self, qtrue ) )
02532         {
02533 #ifndef FINAL_BUILD
02534                 Com_Printf( S_COLOR_RED"ERROR: combat point at %s in solid!\n", vtos(self->r.currentOrigin) );
02535 #endif
02536         }
02537 
02538         VectorCopy( self->r.currentOrigin, level.combatPoints[level.numCombatPoints].origin );
02539         
02540         level.combatPoints[level.numCombatPoints].flags = self->spawnflags;
02541         level.combatPoints[level.numCombatPoints].occupied = qfalse;
02542 
02543         level.numCombatPoints++;
02544 
02545         G_FreeEntity(self);
02546 }

qboolean ValidEnemy gentity_t ent  ) 
 

Definition at line 1400 of file NPC_combat.c.

References gentity_s::client, gclient_s::enemyTeam, FL_NOTARGET, gentity_s::flags, gentity_t, gentity_s::health, gentity_s::NPC, NPC, NPCTEAM_ENEMY, NPCTEAM_FREE, NPCTEAM_NEUTRAL, NPCTEAM_PLAYER, NULL, gclient_s::playerTeam, qboolean, qfalse, qtrue, gclient_s::sess, clientSession_t::sessionTeam, TEAM_BLUE, TEAM_FREE, TEAM_RED, and TEAM_SPECTATOR.

Referenced by NPC_BSRancor_Default(), NPC_BSWampa_Default(), and NPC_CheckInvestigate().

01401 {
01402         if ( ent == NULL )
01403                 return qfalse;
01404 
01405         if ( ent == NPC )
01406                 return qfalse;
01407 
01408         //if team_free, maybe everyone is an enemy?
01409         //if ( !NPC->client->enemyTeam )
01410         //      return qfalse;
01411 
01412         if ( !(ent->flags & FL_NOTARGET) )
01413         {
01414                 if( ent->health > 0 )
01415                 {
01416                         if( !ent->client )
01417                         {
01418                                 return qtrue;
01419                         }
01420                         else if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR )
01421                         {//don't go after spectators
01422                                 return qfalse;
01423                         }
01424                         else
01425                         {
01426                                 int entTeam = TEAM_FREE;
01427                                 if ( ent->NPC && ent->client )
01428                                 {
01429                                         entTeam = ent->client->playerTeam;
01430                                 }
01431                                 else if ( ent->client )
01432                                 {
01433                                         if ( ent->client->sess.sessionTeam == TEAM_BLUE )
01434                                         {
01435                                                 entTeam = NPCTEAM_PLAYER;
01436                                         }
01437                                         else if ( ent->client->sess.sessionTeam == TEAM_RED )
01438                                         {
01439                                                 entTeam = NPCTEAM_ENEMY;
01440                                         }
01441                                         else
01442                                         {
01443                                                 entTeam = NPCTEAM_NEUTRAL;
01444                                         }
01445                                 }
01446                                 if( entTeam == NPCTEAM_FREE 
01447                                         || NPC->client->enemyTeam == NPCTEAM_FREE 
01448                                         || entTeam == NPC->client->enemyTeam )
01449                                 {
01450                                         if ( entTeam != NPC->client->playerTeam )
01451                                         {
01452                                                 return qtrue;
01453                                         }
01454                                 }
01455                         }
01456                 }
01457         }
01458 
01459         return qfalse;
01460 }

void WeaponThink qboolean  inCombat  ) 
 

Definition at line 1036 of file NPC_combat.c.

References Add_Ammo(), playerState_s::ammo, BUTTON_ATTACK, usercmd_s::buttons, gentity_s::client, client, NPC, gclient_s::ps, ShootThink(), ucmd, playerState_s::weapon, usercmd_s::weapon, WEAPON_DROPPING, WEAPON_RAISING, weaponData, and playerState_s::weaponstate.

Referenced by Boba_FireDecide(), NPC_BSAdvanceFight(), NPC_BSCinematic(), NPC_BSDefault(), NPC_BSEmplaced(), NPC_BSFollowLeader(), NPC_BSGM_Attack(), NPC_BSGM_Default(), NPC_BSGrenadier_Attack(), NPC_BSGrenadier_Default(), NPC_BSShoot(), NPC_BSSniper_Attack(), NPC_BSST_Attack(), NPC_BSST_Default(), NPC_BSST_Investigate(), and NPC_CheckCanAttack().

01037 {
01038         if ( client->ps.weaponstate == WEAPON_RAISING || client->ps.weaponstate == WEAPON_DROPPING ) 
01039         {
01040                 ucmd.weapon = client->ps.weapon;
01041                 ucmd.buttons &= ~BUTTON_ATTACK;
01042                 return;
01043         }
01044 
01045 //MCG - Begin
01046         //For now, no-one runs out of ammo      
01047         if(NPC->client->ps.ammo[ weaponData[client->ps.weapon].ammoIndex ] < 10)        // checkme      
01048 //      if(NPC->client->ps.ammo[ client->ps.weapon ] < 10)
01049         {
01050                 Add_Ammo (NPC, client->ps.weapon, 100);
01051         }
01052 
01053         /*if ( NPC->playerTeam == TEAM_BORG )
01054         {//HACK!!!
01055                 if(!(NPC->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_BORG_WEAPON )))
01056                         NPC->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BORG_WEAPON );
01057 
01058                 if ( client->ps.weapon != WP_BORG_WEAPON ) 
01059                 {
01060                         NPC_ChangeWeapon( WP_BORG_WEAPON );
01061                         Add_Ammo (NPC, client->ps.weapon, 10);
01062                         NPCInfo->currentAmmo = client->ps.ammo[client->ps.weapon];
01063                 }
01064         }
01065         else */
01066         
01067         /*if ( NPC->client->playerTeam == TEAM_SCAVENGERS )
01068         {//HACK!!!
01069                 if(!(NPC->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_BLASTER )))
01070                         NPC->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BLASTER );
01071 
01072                 if ( client->ps.weapon != WP_BLASTER )
01073                          
01074                 {
01075                         NPC_ChangeWeapon( WP_BLASTER );
01076                         Add_Ammo (NPC, client->ps.weapon, 10);
01077 //                      NPCInfo->currentAmmo = client->ps.ammo[client->ps.weapon];
01078                         NPCInfo->currentAmmo = client->ps.ammo[weaponData[client->ps.weapon].ammoIndex];        // checkme
01079                 }
01080         }
01081         else*/
01082 //MCG - End
01083         {
01084                 // if the gun in our hands is out of ammo, we need to change
01085                 /*if ( client->ps.ammo[client->ps.weapon] == 0 ) 
01086                 {
01087                         NPCInfo->aiFlags |= NPCAI_CHECK_WEAPON;
01088                 }
01089 
01090                 if ( NPCInfo->aiFlags & NPCAI_CHECK_WEAPON ) 
01091                 {
01092                         NPCInfo->aiFlags &= ~NPCAI_CHECK_WEAPON;
01093                         bestWeapon = ChooseBestWeapon();
01094                         if ( bestWeapon != client->ps.weapon ) 
01095                         {
01096                                 NPC_ChangeWeapon( bestWeapon );
01097                         }
01098                 }*/
01099         }
01100 
01101         ucmd.weapon = client->ps.weapon;
01102         ShootThink();
01103 }