#include "b_local.h"#include "g_nav.h"Go to the source code of this file.
|
|
Definition at line 44 of file NPC_combat.c. Referenced by G_AngerAlert(). |
|
|
Definition at line 45 of file NPC_combat.c. Referenced by G_AngerAlert(). |
|
|
Definition at line 2655 of file NPC_combat.c. Referenced by NPC_FindCombatPoint(). |
|
|
Definition at line 2653 of file NPC_combat.c. |
|
|
Definition at line 2654 of file NPC_combat.c. Referenced by NPC_FindCombatPoint(). |
|
|
Definition at line 2652 of file NPC_combat.c. Referenced by NPC_FindCombatPoint(). |
|
||||||||||||
|
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 }
|
|
||||||||||||
|
|
|
||||||||||||
|
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 }
|
|
|
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 }
|
|
|
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().
|
|
||||||||||||||||
|
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 }
|
|
||||||||||||
|
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 }
|
|
|
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 }
|
|
||||||||||||
|
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 }
|
|
|
||||||||||||||||||||
|
|
|
|
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 }
|
|
||||||||||||
|
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
||||||||||||
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
|
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().
|
|
||||||||||||
|
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 }
|
|
|
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().
|
|
||||||||||||||||
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
||||||||||||
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
||||||||||||||||
|
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 }
|
|
||||||||||||
|
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 }
|
|
||||||||||||||||||||||||||||
|
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 }
|
|
|
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 }
|
|
||||||||||||
|
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 }
|
|
||||||||||||
|
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 }
|
|
|
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 }
|
|
||||||||||||||||||||
|
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 }
|
|
||||||||||||||||||||||||
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
||||||||||||
|
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 }
|
|
|
|
|
|
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 }
|
|
||||||||||||||||||||
|
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 }
|
|
|
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 }
|
|
|
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 }
|
|
|
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 }
|