codemp/game/NPC_AI_Default.c File Reference

#include "b_local.h"
#include "g_nav.h"
#include "../icarus/Q3_Interface.h"

Go to the source code of this file.

Functions

qboolean NPC_SomeoneLookingAtMe (gentity_t *ent)
void NPC_LostEnemyDecideChase (void)
void NPC_StandIdle (void)
qboolean NPC_StandTrackAndShoot (gentity_t *NPC, qboolean canDuck)
void NPC_BSIdle (void)
void NPC_BSRun (void)
void NPC_BSStandGuard (void)
void NPC_BSHuntAndKill (void)
void NPC_BSStandAndShoot (void)
void NPC_BSRunAndShoot (void)
void NPC_BSFace (void)
void NPC_BSPointShoot (qboolean shoot)
void NPC_BSMove (void)
void NPC_BSShoot (void)
void NPC_BSPatrol (void)
void NPC_CheckGetNewWeapon (void)
void NPC_BSST_Attack (void)
void NPC_BSDefault (void)


Function Documentation

void NPC_BSDefault void   ) 
 

Definition at line 712 of file NPC_AI_Default.c.

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

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

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

void NPC_BSFace void   ) 
 

Definition at line 490 of file NPC_AI_Default.c.

References gNPC_t::aimTime, client, gNPC_t::desiredPitch, gNPC_t::desiredYaw, NPC, NPC_UpdateAngles(), NPCInfo, PITCH, gclient_s::ps, qtrue, TID_BSTATE, trap_ICARUS_TaskIDComplete(), playerState_s::viewangles, and YAW.

00491 {
00492         //FIXME: once you stop sending turning info, they reset to whatever their delta_angles was last????
00493         //Once this is over, it snaps back to what it was facing before- WHY???
00494         if( NPC_UpdateAngles ( qtrue, qtrue ) )
00495         {
00496                 trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
00497                 
00498                 NPCInfo->desiredYaw = client->ps.viewangles[YAW];
00499                 NPCInfo->desiredPitch = client->ps.viewangles[PITCH];
00500 
00501                 NPCInfo->aimTime = 0;//ok to turn normally now
00502         }
00503 }

void NPC_BSHuntAndKill void   ) 
 

(ucmd.buttons & BUTTON_ATTACK) ||

Definition at line 232 of file NPC_AI_Default.c.

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

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

void NPC_BSIdle void   ) 
 

Definition at line 161 of file NPC_AI_Default.c.

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

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

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

void NPC_BSMove void   ) 
 

Definition at line 616 of file NPC_AI_Default.c.

References gentity_s::enemy, gentity_t, NPC, NPC_CheckCanAttack(), NPC_CheckEnemy(), NPC_SlideMoveToGoal(), NPC_UpdateAngles(), NULL, qfalse, qtrue, and UpdateGoal().

00617 {
00618         gentity_t       *goal = NULL;
00619 
00620         NPC_CheckEnemy(qtrue, qfalse, qtrue);
00621         if(NPC->enemy)
00622         {
00623                 NPC_CheckCanAttack(1.0, qfalse);
00624         }
00625         else
00626         {
00627                 NPC_UpdateAngles(qtrue, qtrue);
00628         }
00629 
00630         goal = UpdateGoal();
00631         if(goal)
00632         {
00633 //              NPCInfo->moveToGoalMod = 1.0;
00634 
00635                 NPC_SlideMoveToGoal();
00636         }
00637 }

void NPC_BSPatrol void   ) 
 

Definition at line 664 of file NPC_AI_Default.c.

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

Referenced by NPC_BSST_Attack().

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

void NPC_BSPointShoot qboolean  shoot  ) 
 

Definition at line 505 of file NPC_AI_Default.c.

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

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

void NPC_BSRun void   ) 
 

Definition at line 179 of file NPC_AI_Default.c.

References NPC_MoveToGoal(), NPC_UpdateAngles(), qtrue, and UpdateGoal().

00180 {
00181         //FIXME if there is no nav data, we need to do something else
00182         // if we're stuck, try to move around it
00183         if ( UpdateGoal() )
00184         {
00185                 NPC_MoveToGoal( qtrue );
00186         }
00187 
00188         NPC_UpdateAngles( qtrue, qtrue );
00189 }

void NPC_BSRunAndShoot void   ) 
 

Definition at line 394 of file NPC_AI_Default.c.

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

Referenced by NPC_BSSearch().

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

void NPC_BSShoot void   ) 
 

Definition at line 644 of file NPC_AI_Default.c.

References client, enemyVisibility, gclient_s::ps, qtrue, VIS_SHOOT, WEAPON_FIRING, WEAPON_READY, playerState_s::weaponstate, and WeaponThink().

00645 {
00646 //      NPC_BSMove();
00647 
00648         enemyVisibility = VIS_SHOOT;
00649 
00650         if ( client->ps.weaponstate != WEAPON_READY && client->ps.weaponstate != WEAPON_FIRING ) 
00651         {
00652                 client->ps.weaponstate = WEAPON_READY;
00653         }
00654 
00655         WeaponThink(qtrue);
00656 }

void NPC_BSST_Attack void   ) 
 

NPC->enemy )//

Definition at line 2409 of file NPC_AI_Stormtrooper.c.

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

Referenced by NPC_BSDefault(), and NPC_BSST_Default().

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

void NPC_BSStandAndShoot void   ) 
 

Definition at line 306 of file NPC_AI_Default.c.

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

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

void NPC_BSStandGuard void   ) 
 

Definition at line 191 of file NPC_AI_Default.c.

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

Referenced by NPC_BSFollowLeader(), and NPC_BSHuntAndKill().

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

void NPC_CheckGetNewWeapon void   ) 
 

Definition at line 3060 of file NPC_combat.c.

Referenced by NPC_BSDefault(), NPC_BSFlee(), and NPC_BSST_Default().

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 }

void NPC_LostEnemyDecideChase void   ) 
 

Definition at line 18 of file NPC_AI_Default.c.

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

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

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

qboolean NPC_SomeoneLookingAtMe gentity_t ent  ) 
 

Definition at line 1042 of file NPC_utils.c.

01043 {
01044         int i = 0;
01045         gentity_t *pEnt;
01046 
01047         while (i < MAX_CLIENTS)
01048         {
01049                 pEnt = &g_entities[i];
01050 
01051                 if (pEnt && pEnt->inuse && pEnt->client && pEnt->client->sess.sessionTeam != TEAM_SPECTATOR &&
01052                         !(pEnt->client->ps.pm_flags & PMF_FOLLOW) && pEnt->s.weapon != WP_NONE)
01053                 {
01054                         if (trap_InPVS(ent->r.currentOrigin, pEnt->r.currentOrigin))
01055                         {
01056                                 if (InFOV( ent, pEnt, 30, 30 ))
01057                                 { //I'm in a 30 fov or so cone from this player.. that's enough I guess.
01058                                         return qtrue;
01059                                 }
01060                         }
01061                 }
01062 
01063                 i++;
01064         }
01065 
01066         return qfalse;
01067 }

void NPC_StandIdle void   ) 
 

Definition at line 42 of file NPC_AI_Default.c.

00043 {
00044 /*
00045         //Must be done with any other animations
00046         if ( NPC->client->ps.legsAnimTimer != 0 )
00047                 return;
00048 
00049         //Not ready to do another one
00050         if ( TIMER_Done( NPC, "idleAnim" ) == false )
00051                 return;
00052 
00053         int anim = NPC->client->ps.legsAnim;
00054 
00055         if ( anim != BOTH_STAND1 && anim != BOTH_STAND2 )
00056                 return;
00057 
00058         //FIXME: Account for STAND1 or STAND2 here and set the base anim accordingly
00059         int     baseSeq = ( anim == BOTH_STAND1 ) ? BOTH_STAND1_RANDOM1 : BOTH_STAND2_RANDOM1;
00060 
00061         //Must have at least one random idle animation
00062         //NOTENOTE: This relies on proper ordering of animations, which SHOULD be okay
00063         if ( PM_HasAnimation( NPC, baseSeq ) == false )
00064                 return;
00065 
00066         int     newIdle = Q_irand( 0, MAX_IDLE_ANIMS-1 );
00067 
00068         //FIXME: Technically this could never complete.. but that's not really too likely
00069         while( 1 )
00070         {
00071                 if ( PM_HasAnimation( NPC, baseSeq + newIdle ) )
00072                         break;
00073 
00074                 newIdle = Q_irand( 0, MAX_IDLE_ANIMS );
00075         }
00076         
00077         //Start that animation going
00078         NPC_SetAnim( NPC, SETANIM_BOTH, baseSeq + newIdle, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00079         
00080         int newTime = PM_AnimLength( NPC->client->clientInfo.animFileIndex, (animNumber_t) (baseSeq + newIdle) );
00081 
00082         //Don't do this again for a random amount of time
00083         TIMER_Set( NPC, "idleAnim", newTime + Q_irand( 2000, 10000 ) );
00084 */
00085 }

qboolean NPC_StandTrackAndShoot gentity_t NPC,
qboolean  canDuck
 

if ( NPC->client->playerTeam!= TEAM_BORG )

Definition at line 87 of file NPC_AI_Default.c.

References BUTTON_ATTACK, gclient_s::buttons, gentity_s::client, client, gNPC_t::duckDebounceTime, gentity_s::enemy, gentity_t, gentity_s::health, level, NPC, NPC_CheckCanAttack(), NPC_CheckDefend(), NPCInfo, gclient_s::ps, qboolean, qfalse, qtrue, random, level_locals_t::time, ucmd, usercmd_s::upmove, and playerState_s::weaponTime.

Referenced by NPC_BSRunAndShoot(), and NPC_BSStandAndShoot().

00088 {
00089         qboolean        attack_ok = qfalse;
00090         qboolean        duck_ok = qfalse;
00091         qboolean        faced = qfalse;
00092         float           attack_scale = 1.0;
00093 
00094         //First see if we're hurt bad- if so, duck
00095         //FIXME: if even when ducked, we can shoot someone, we should.
00096         //Maybe is can be shot even when ducked, we should run away to the nearest cover?
00097         if ( canDuck )
00098         {
00099                 if ( NPC->health < 20 )
00100                 {
00101                 //      if( NPC->svFlags&SVF_HEALING || random() )
00102                         if( random() )
00103                         {
00104                                 duck_ok = qtrue;
00105                         }
00106                 }
00107                 else if ( NPC->health < 40 )
00108                 {
00109 //                      if ( NPC->svFlags&SVF_HEALING )
00110 //                      {//Medic is on the way, get down!
00111 //                              duck_ok = qtrue;
00112 //                      }
00113                         // no more borg
00115 //                      {//Borg don't care if they're about to die
00116                                 //attack_scale will be a max of .66
00117 //                              attack_scale = NPC->health/60;
00118 //                      }
00119                 }
00120         }
00121 
00122         //NPC_CheckEnemy( qtrue, qfalse, qtrue );
00123 
00124         if ( !duck_ok )
00125         {//made this whole part a function call
00126                 attack_ok = NPC_CheckCanAttack( attack_scale, qtrue );
00127                 faced = qtrue;
00128         }
00129 
00130         if ( canDuck && (duck_ok || (!attack_ok && client->ps.weaponTime <= 0)) && ucmd.upmove != -127 )
00131         {//if we didn't attack check to duck if we're not already
00132                 if( !duck_ok )
00133                 {
00134                         if ( NPC->enemy->client )
00135                         {
00136                                 if ( NPC->enemy->enemy == NPC )
00137                                 {
00138                                         if ( NPC->enemy->client->buttons & BUTTON_ATTACK )
00139                                         {//FIXME: determine if enemy fire angles would hit me or get close
00140                                                 if ( NPC_CheckDefend( 1.0 ) )//FIXME: Check self-preservation?  Health?
00141                                                 {
00142                                                         duck_ok = qtrue;
00143                                                 }
00144                                         }
00145                                 }
00146                         }
00147                 }
00148 
00149                 if ( duck_ok )
00150                 {//duck and don't shoot
00151                         attack_ok = qfalse;
00152                         ucmd.upmove = -127;
00153                         NPCInfo->duckDebounceTime = level.time + 1000;//duck for a full second
00154                 }
00155         }
00156 
00157         return faced;
00158 }