AI_Default.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. #include "g_headers.h"
  2. #include "Q3_Interface.h"
  3. //#include "anims.h"
  4. //extern int PM_AnimLength( int index, animNumber_t anim );
  5. //extern qboolean PM_HasAnimation( gentity_t *ent, int animation );
  6. //extern int PM_AnimLength( int index, animNumber_t anim );
  7. //#define MAX_IDLE_ANIMS 8
  8. extern int g_crosshairEntNum;
  9. /*
  10. void NPC_LostEnemyDecideChase(void)
  11. We lost our enemy and want to drop him but see if we should chase him if we are in the proper bState
  12. */
  13. void NPC_LostEnemyDecideChase(void)
  14. {
  15. switch( NPCInfo->behaviorState )
  16. {
  17. case BS_HUNT_AND_KILL:
  18. //We were chasing him and lost him, so try to find him
  19. if ( NPC->enemy == NPCInfo->goalEntity && NPC->enemy->lastWaypoint != WAYPOINT_NONE )
  20. {//Remember his last valid Wp, then check it out
  21. //FIXME: Should we only do this if there's no other enemies or we've got LOCKED_ENEMY on?
  22. NPC_BSSearchStart( NPC->enemy->lastWaypoint, BS_SEARCH );
  23. }
  24. //If he's not our goalEntity, we're running somewhere else, so lose him
  25. break;
  26. default:
  27. break;
  28. }
  29. G_ClearEnemy( NPC );
  30. }
  31. /*
  32. -------------------------
  33. NPC_StandIdle
  34. -------------------------
  35. */
  36. void NPC_StandIdle( void )
  37. {
  38. /*
  39. //Must be done with any other animations
  40. if ( NPC->client->ps.legsAnimTimer != 0 )
  41. return;
  42. //Not ready to do another one
  43. if ( TIMER_Done( NPC, "idleAnim" ) == false )
  44. return;
  45. int anim = NPC->client->ps.legsAnim;
  46. if ( anim != BOTH_STAND1 && anim != BOTH_STAND2 )
  47. return;
  48. //FIXME: Account for STAND1 or STAND2 here and set the base anim accordingly
  49. int baseSeq = ( anim == BOTH_STAND1 ) ? BOTH_STAND1_RANDOM1 : BOTH_STAND2_RANDOM1;
  50. //Must have at least one random idle animation
  51. //NOTENOTE: This relies on proper ordering of animations, which SHOULD be okay
  52. if ( PM_HasAnimation( NPC, baseSeq ) == false )
  53. return;
  54. int newIdle = Q_irand( 0, MAX_IDLE_ANIMS-1 );
  55. //FIXME: Technically this could never complete.. but that's not really too likely
  56. while( 1 )
  57. {
  58. if ( PM_HasAnimation( NPC, baseSeq + newIdle ) )
  59. break;
  60. newIdle = Q_irand( 0, MAX_IDLE_ANIMS );
  61. }
  62. //Start that animation going
  63. NPC_SetAnim( NPC, SETANIM_BOTH, baseSeq + newIdle, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  64. int newTime = PM_AnimLength( NPC->client->clientInfo.animFileIndex, (animNumber_t) (baseSeq + newIdle) );
  65. //Don't do this again for a random amount of time
  66. TIMER_Set( NPC, "idleAnim", newTime + Q_irand( 2000, 10000 ) );
  67. */
  68. }
  69. qboolean NPC_StandTrackAndShoot (gentity_t *NPC, qboolean canDuck)
  70. {
  71. qboolean attack_ok = qfalse;
  72. qboolean duck_ok = qfalse;
  73. qboolean faced = qfalse;
  74. float attack_scale = 1.0;
  75. //First see if we're hurt bad- if so, duck
  76. //FIXME: if even when ducked, we can shoot someone, we should.
  77. //Maybe is can be shot even when ducked, we should run away to the nearest cover?
  78. if ( canDuck )
  79. {
  80. if ( NPC->health < 20 )
  81. {
  82. // if( NPC->svFlags&SVF_HEALING || random() )
  83. if( random() )
  84. {
  85. duck_ok = qtrue;
  86. }
  87. }
  88. else if ( NPC->health < 40 )
  89. {
  90. // if ( NPC->svFlags&SVF_HEALING )
  91. // {//Medic is on the way, get down!
  92. // duck_ok = qtrue;
  93. // }
  94. // no more borg
  95. /// if ( NPC->client->playerTeam!= TEAM_BORG )
  96. // {//Borg don't care if they're about to die
  97. //attack_scale will be a max of .66
  98. // attack_scale = NPC->health/60;
  99. // }
  100. }
  101. }
  102. //NPC_CheckEnemy( qtrue, qfalse );
  103. if ( !duck_ok )
  104. {//made this whole part a function call
  105. attack_ok = NPC_CheckCanAttack( attack_scale, qtrue );
  106. faced = qtrue;
  107. }
  108. if ( canDuck && (duck_ok || (!attack_ok && client->fireDelay == 0)) && ucmd.upmove != -127 )
  109. {//if we didn't attack check to duck if we're not already
  110. if( !duck_ok )
  111. {
  112. if ( NPC->enemy->client )
  113. {
  114. if ( NPC->enemy->enemy == NPC )
  115. {
  116. if ( NPC->enemy->client->buttons & BUTTON_ATTACK )
  117. {//FIXME: determine if enemy fire angles would hit me or get close
  118. if ( NPC_CheckDefend( 1.0 ) )//FIXME: Check self-preservation? Health?
  119. {
  120. duck_ok = qtrue;
  121. }
  122. }
  123. }
  124. }
  125. }
  126. if ( duck_ok )
  127. {//duck and don't shoot
  128. attack_ok = qfalse;
  129. ucmd.upmove = -127;
  130. NPCInfo->duckDebounceTime = level.time + 1000;//duck for a full second
  131. }
  132. }
  133. return faced;
  134. }
  135. void NPC_BSIdle( void )
  136. {
  137. //FIXME if there is no nav data, we need to do something else
  138. // if we're stuck, try to move around it
  139. if ( UpdateGoal() )
  140. {
  141. NPC_MoveToGoal( qtrue );
  142. }
  143. if ( ( ucmd.forwardmove == 0 ) && ( ucmd.rightmove == 0 ) && ( ucmd.upmove == 0 ) )
  144. {
  145. // NPC_StandIdle();
  146. }
  147. NPC_UpdateAngles( qtrue, qtrue );
  148. ucmd.buttons |= BUTTON_WALKING;
  149. }
  150. void NPC_BSRun (void)
  151. {
  152. //FIXME if there is no nav data, we need to do something else
  153. // if we're stuck, try to move around it
  154. if ( UpdateGoal() )
  155. {
  156. NPC_MoveToGoal( qtrue );
  157. }
  158. NPC_UpdateAngles( qtrue, qtrue );
  159. }
  160. void NPC_BSStandGuard (void)
  161. {
  162. //FIXME: Use Snapshot info
  163. if ( NPC->enemy == NULL )
  164. {//Possible to pick one up by being shot
  165. if( random() < 0.5 )
  166. {
  167. if(NPC->client->enemyTeam)
  168. {
  169. gentity_t *newenemy = NPC_PickEnemy(NPC, NPC->client->enemyTeam, (NPC->cantHitEnemyCounter < 10), (NPC->client->enemyTeam == TEAM_PLAYER), qtrue);
  170. //only checks for vis if couldn't hit last enemy
  171. if(newenemy)
  172. {
  173. G_SetEnemy( NPC, newenemy );
  174. }
  175. }
  176. }
  177. }
  178. if ( NPC->enemy != NULL )
  179. {
  180. if( NPCInfo->tempBehavior == BS_STAND_GUARD )
  181. {
  182. NPCInfo->tempBehavior = BS_DEFAULT;
  183. }
  184. if( NPCInfo->behaviorState == BS_STAND_GUARD )
  185. {
  186. NPCInfo->behaviorState = BS_STAND_AND_SHOOT;
  187. }
  188. }
  189. NPC_UpdateAngles( qtrue, qtrue );
  190. }
  191. /*
  192. -------------------------
  193. NPC_BSHuntAndKill
  194. -------------------------
  195. */
  196. void NPC_BSHuntAndKill( void )
  197. {
  198. qboolean turned = qfalse;
  199. vec3_t vec;
  200. float enemyDist;
  201. visibility_t oEVis;
  202. int curAnim;
  203. NPC_CheckEnemy( NPCInfo->tempBehavior != BS_HUNT_AND_KILL, qfalse );//don't find new enemy if this is tempbehav
  204. if ( NPC->enemy )
  205. {
  206. oEVis = enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );//CHECK_360|//CHECK_PVS|
  207. if(enemyVisibility > VIS_PVS)
  208. {
  209. if ( !NPC_EnemyTooFar( NPC->enemy, 0, qtrue ) )
  210. {//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
  211. NPC_CheckCanAttack( 1.0, qfalse );
  212. turned = qtrue;
  213. }
  214. }
  215. curAnim = NPC->client->ps.legsAnim;
  216. if(curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 )
  217. {//Don't move toward enemy if we're in a full-body attack anim
  218. //FIXME, use IdealDistance to determin if we need to close distance
  219. VectorSubtract(NPC->enemy->currentOrigin, NPC->currentOrigin, vec);
  220. enemyDist = VectorLength(vec);
  221. if( enemyDist > 48 && ((enemyDist*1.5)*(enemyDist*1.5) >= NPC_MaxDistSquaredForWeapon() ||
  222. oEVis != VIS_SHOOT ||
  223. //!(ucmd.buttons & BUTTON_ATTACK) ||
  224. enemyDist > IdealDistance(NPC)*3 ) )
  225. {//We should close in?
  226. NPCInfo->goalEntity = NPC->enemy;
  227. NPC_MoveToGoal( qtrue );
  228. }
  229. else if(enemyDist < IdealDistance(NPC))
  230. {//We should back off?
  231. //if(ucmd.buttons & BUTTON_ATTACK)
  232. {
  233. NPCInfo->goalEntity = NPC->enemy;
  234. NPCInfo->goalRadius = 12;
  235. NPC_MoveToGoal( qtrue );
  236. ucmd.forwardmove *= -1;
  237. ucmd.rightmove *= -1;
  238. VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir );
  239. ucmd.buttons |= BUTTON_WALKING;
  240. }
  241. }//otherwise, stay where we are
  242. }
  243. }
  244. else
  245. {//ok, stand guard until we find an enemy
  246. if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
  247. {
  248. NPCInfo->tempBehavior = BS_DEFAULT;
  249. }
  250. else
  251. {
  252. NPCInfo->tempBehavior = BS_STAND_GUARD;
  253. NPC_BSStandGuard();
  254. }
  255. return;
  256. }
  257. if(!turned)
  258. {
  259. NPC_UpdateAngles(qtrue, qtrue);
  260. }
  261. }
  262. void NPC_BSStandAndShoot (void)
  263. {
  264. //FIXME:
  265. //When our numbers outnumber enemies 3 to 1, or only one of them,
  266. //go into hunt and kill mode
  267. //FIXME:
  268. //When they're all dead, go to some script or wander off to sickbay?
  269. if(NPC->client->playerTeam && NPC->client->enemyTeam)
  270. {
  271. //FIXME: don't realize this right away- or else enemies show up and we're standing around
  272. /*
  273. if( teamNumbers[NPC->enemyTeam] == 0 )
  274. {//ok, stand guard until we find another enemy
  275. //reset our rush counter
  276. teamCounter[NPC->playerTeam] = 0;
  277. NPCInfo->tempBehavior = BS_STAND_GUARD;
  278. NPC_BSStandGuard();
  279. return;
  280. }*/
  281. /*
  282. //FIXME: whether to do this or not should be settable
  283. else if( NPC->playerTeam != TEAM_BORG )//Borg don't rush
  284. {
  285. //FIXME: In case reinforcements show up, we should wait a few seconds
  286. //and keep checking before rushing!
  287. //Also: what if not everyone on our team is going after playerTeam?
  288. //Also: our team count includes medics!
  289. if(NPC->health > 25)
  290. {//Can we rush the enemy?
  291. if(teamNumbers[NPC->enemyTeam] == 1 ||
  292. teamNumbers[NPC->playerTeam] >= teamNumbers[NPC->enemyTeam]*3)
  293. {//Only one of them or we outnumber 3 to 1
  294. if(teamStrength[NPC->playerTeam] >= 75 ||
  295. (teamStrength[NPC->playerTeam] >= 50 && teamStrength[NPC->playerTeam] > teamStrength[NPC->enemyTeam]))
  296. {//Our team is strong enough to rush
  297. teamCounter[NPC->playerTeam]++;
  298. if(teamNumbers[NPC->playerTeam] * 17 <= teamCounter[NPC->playerTeam])
  299. {//ok, we waited 1.7 think cycles on average and everyone is go, let's do it!
  300. //FIXME: Should we do this to everyone on our team?
  301. NPCInfo->behaviorState = BS_HUNT_AND_KILL;
  302. //FIXME: if the tide changes, we should retreat!
  303. //FIXME: when do we reset the counter?
  304. NPC_BSHuntAndKill ();
  305. return;
  306. }
  307. }
  308. else//Oops! Something's wrong, reset the counter to rush
  309. teamCounter[NPC->playerTeam] = 0;
  310. }
  311. else//Oops! Something's wrong, reset the counter to rush
  312. teamCounter[NPC->playerTeam] = 0;
  313. }
  314. }
  315. */
  316. }
  317. NPC_CheckEnemy(qtrue, qfalse);
  318. if(NPCInfo->duckDebounceTime > level.time && NPC->client->ps.weapon != WP_SABER )
  319. {
  320. ucmd.upmove = -127;
  321. if(NPC->enemy)
  322. {
  323. NPC_CheckCanAttack(1.0, qtrue);
  324. }
  325. return;
  326. }
  327. if(NPC->enemy)
  328. {
  329. if(!NPC_StandTrackAndShoot( NPC, qtrue ))
  330. {//That func didn't update our angles
  331. NPCInfo->desiredYaw = NPC->client->ps.viewangles[YAW];
  332. NPCInfo->desiredPitch = NPC->client->ps.viewangles[PITCH];
  333. NPC_UpdateAngles(qtrue, qtrue);
  334. }
  335. }
  336. else
  337. {
  338. NPCInfo->desiredYaw = NPC->client->ps.viewangles[YAW];
  339. NPCInfo->desiredPitch = NPC->client->ps.viewangles[PITCH];
  340. NPC_UpdateAngles(qtrue, qtrue);
  341. // NPC_BSIdle();//only moves if we have a goal
  342. }
  343. }
  344. void NPC_BSRunAndShoot (void)
  345. {
  346. /*if(NPC->playerTeam && NPC->enemyTeam)
  347. {
  348. //FIXME: don't realize this right away- or else enemies show up and we're standing around
  349. if( teamNumbers[NPC->enemyTeam] == 0 )
  350. {//ok, stand guard until we find another enemy
  351. //reset our rush counter
  352. teamCounter[NPC->playerTeam] = 0;
  353. NPCInfo->tempBehavior = BS_STAND_GUARD;
  354. NPC_BSStandGuard();
  355. return;
  356. }
  357. }*/
  358. //NOTE: are we sure we want ALL run and shoot people to move this way?
  359. //Shouldn't it check to see if we have an enemy and our enemy is our goal?!
  360. //Moved that check into NPC_MoveToGoal
  361. //NPCInfo->combatMove = qtrue;
  362. NPC_CheckEnemy( qtrue, qfalse );
  363. if ( NPCInfo->duckDebounceTime > level.time ) // && NPCInfo->hidingGoal )
  364. {
  365. ucmd.upmove = -127;
  366. if ( NPC->enemy )
  367. {
  368. NPC_CheckCanAttack( 1.0, qfalse );
  369. }
  370. return;
  371. }
  372. if ( NPC->enemy )
  373. {
  374. int monitor = NPC->cantHitEnemyCounter;
  375. NPC_StandTrackAndShoot( NPC, qfalse );//(NPCInfo->hidingGoal != NULL) );
  376. if ( !(ucmd.buttons & BUTTON_ATTACK) && ucmd.upmove >= 0 && NPC->cantHitEnemyCounter > monitor )
  377. {//not crouching and not firing
  378. vec3_t vec;
  379. VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, vec );
  380. vec[2] = 0;
  381. if ( VectorLength( vec ) > 128 || NPC->cantHitEnemyCounter >= 10 )
  382. {//run at enemy if too far away
  383. //The cantHitEnemyCounter getting high has other repercussions
  384. //100 (10 seconds) will make you try to pick a new enemy...
  385. //But we're chasing, so we clamp it at 50 here
  386. if ( NPC->cantHitEnemyCounter > 60 )
  387. {
  388. NPC->cantHitEnemyCounter = 60;
  389. }
  390. if ( NPC->cantHitEnemyCounter >= (NPCInfo->stats.aggression+1) * 10 )
  391. {
  392. NPC_LostEnemyDecideChase();
  393. }
  394. //chase and face
  395. ucmd.angles[YAW] = 0;
  396. ucmd.angles[PITCH] = 0;
  397. NPCInfo->goalEntity = NPC->enemy;
  398. NPCInfo->goalRadius = 12;
  399. NPC_MoveToGoal( qtrue );
  400. NPC_UpdateAngles(qtrue, qtrue);
  401. }
  402. else
  403. {
  404. //FIXME: this could happen if they're just on the other side
  405. //of a thin wall or something else blocking out shot. That
  406. //would make us just stand there and not go around it...
  407. //but maybe it's okay- might look like we're waiting for
  408. //him to come out...?
  409. //Current solution: runs around if cantHitEnemyCounter gets
  410. //to 10 (1 second).
  411. }
  412. }
  413. else
  414. {//Clear the can't hit enemy counter here
  415. NPC->cantHitEnemyCounter = 0;
  416. }
  417. }
  418. else
  419. {
  420. if ( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
  421. {//lost him, go back to what we were doing before
  422. NPCInfo->tempBehavior = BS_DEFAULT;
  423. return;
  424. }
  425. // NPC_BSRun();//only moves if we have a goal
  426. }
  427. }
  428. //Simply turn until facing desired angles
  429. void NPC_BSFace (void)
  430. {
  431. //FIXME: once you stop sending turning info, they reset to whatever their delta_angles was last????
  432. //Once this is over, it snaps back to what it was facing before- WHY???
  433. if( NPC_UpdateAngles ( qtrue, qtrue ) )
  434. {
  435. Q3_TaskIDComplete( NPC, TID_BSTATE );
  436. NPCInfo->desiredYaw = client->ps.viewangles[YAW];
  437. NPCInfo->desiredPitch = client->ps.viewangles[PITCH];
  438. NPCInfo->aimTime = 0;//ok to turn normally now
  439. }
  440. }
  441. void NPC_BSPointShoot (qboolean shoot)
  442. {//FIXME: doesn't check for clear shot...
  443. vec3_t muzzle, dir, angles, org;
  444. if ( !NPC->enemy || !NPC->enemy->inuse || (NPC->enemy->NPC && NPC->enemy->health <= 0) )
  445. {//FIXME: should still keep shooting for a second or two after they actually die...
  446. Q3_TaskIDComplete( NPC, TID_BSTATE );
  447. goto finished;
  448. return;
  449. }
  450. CalcEntitySpot(NPC, SPOT_WEAPON, muzzle);
  451. CalcEntitySpot(NPC->enemy, SPOT_HEAD, org);//Was spot_org
  452. //Head is a little high, so let's aim for the chest:
  453. if ( NPC->enemy->client )
  454. {
  455. org[2] -= 12;//NOTE: is this enough?
  456. }
  457. VectorSubtract(org, muzzle, dir);
  458. vectoangles(dir, angles);
  459. switch( NPC->client->ps.weapon )
  460. {
  461. case WP_NONE:
  462. // case WP_TRICORDER:
  463. case WP_MELEE:
  464. case WP_TUSKEN_STAFF:
  465. case WP_SABER:
  466. //don't do any pitch change if not holding a firing weapon
  467. break;
  468. default:
  469. NPCInfo->desiredPitch = NPCInfo->lockedDesiredPitch = AngleNormalize360(angles[PITCH]);
  470. break;
  471. }
  472. NPCInfo->desiredYaw = NPCInfo->lockedDesiredYaw = AngleNormalize360(angles[YAW]);
  473. if ( NPC_UpdateAngles ( qtrue, qtrue ) )
  474. {//FIXME: if angles clamped, this may never work!
  475. //NPCInfo->shotTime = NPC->attackDebounceTime = 0;
  476. if ( shoot )
  477. {//FIXME: needs to hold this down if using a weapon that requires it, like phaser...
  478. ucmd.buttons |= BUTTON_ATTACK;
  479. }
  480. if ( !shoot || !(NPC->svFlags & SVF_LOCKEDENEMY) )
  481. {//If locked_enemy is on, dont complete until it is destroyed...
  482. Q3_TaskIDComplete( NPC, TID_BSTATE );
  483. goto finished;
  484. }
  485. }
  486. else if ( shoot && (NPC->svFlags & SVF_LOCKEDENEMY) )
  487. {//shooting them till their dead, not aiming right at them yet...
  488. /*
  489. qboolean movingTarget = qfalse;
  490. if ( NPC->enemy->client )
  491. {
  492. if ( VectorLengthSquared( NPC->enemy->client->ps.velocity ) )
  493. {
  494. movingTarget = qtrue;
  495. }
  496. }
  497. else if ( VectorLengthSquared( NPC->enemy->s.pos.trDelta ) )
  498. {
  499. movingTarget = qtrue;
  500. }
  501. if (movingTarget )
  502. */
  503. {
  504. float dist = VectorLength( dir );
  505. float yawMiss, yawMissAllow = NPC->enemy->maxs[0];
  506. float pitchMiss, pitchMissAllow = (NPC->enemy->maxs[2] - NPC->enemy->mins[2])/2;
  507. if ( yawMissAllow < 8.0f )
  508. {
  509. yawMissAllow = 8.0f;
  510. }
  511. if ( pitchMissAllow < 8.0f )
  512. {
  513. pitchMissAllow = 8.0f;
  514. }
  515. yawMiss = tan(DEG2RAD(AngleDelta ( NPC->client->ps.viewangles[YAW], NPCInfo->desiredYaw ))) * dist;
  516. pitchMiss = tan(DEG2RAD(AngleDelta ( NPC->client->ps.viewangles[PITCH], NPCInfo->desiredPitch))) * dist;
  517. if ( yawMissAllow >= yawMiss && pitchMissAllow > pitchMiss )
  518. {
  519. ucmd.buttons |= BUTTON_ATTACK;
  520. }
  521. }
  522. }
  523. return;
  524. finished:
  525. NPCInfo->desiredYaw = client->ps.viewangles[YAW];
  526. NPCInfo->desiredPitch = client->ps.viewangles[PITCH];
  527. NPCInfo->aimTime = 0;//ok to turn normally now
  528. }
  529. /*
  530. void NPC_BSMove(void)
  531. Move in a direction, face another
  532. */
  533. void NPC_BSMove(void)
  534. {
  535. gentity_t *goal = NULL;
  536. NPC_CheckEnemy(qtrue, qfalse);
  537. if(NPC->enemy)
  538. {
  539. NPC_CheckCanAttack(1.0, qfalse);
  540. }
  541. else
  542. {
  543. NPC_UpdateAngles(qtrue, qtrue);
  544. }
  545. goal = UpdateGoal();
  546. if(goal)
  547. {
  548. // NPCInfo->moveToGoalMod = 1.0;
  549. NPC_SlideMoveToGoal();
  550. }
  551. }
  552. /*
  553. void NPC_BSShoot(void)
  554. Move in a direction, face another
  555. */
  556. void NPC_BSShoot(void)
  557. {
  558. // NPC_BSMove();
  559. enemyVisibility = VIS_SHOOT;
  560. if ( client->ps.weaponstate != WEAPON_READY && client->ps.weaponstate != WEAPON_FIRING )
  561. {
  562. client->ps.weaponstate = WEAPON_READY;
  563. }
  564. WeaponThink(qtrue);
  565. }
  566. /*
  567. void NPC_BSPatrol( void )
  568. Same as idle, but you look for enemies every "vigilance"
  569. using your angles, HFOV, VFOV and visrange, and listen for sounds within earshot...
  570. */
  571. void NPC_BSPatrol( void )
  572. {
  573. //int alertEventNum;
  574. if(level.time > NPCInfo->enemyCheckDebounceTime)
  575. {
  576. NPCInfo->enemyCheckDebounceTime = level.time + (NPCInfo->stats.vigilance * 1000);
  577. NPC_CheckEnemy(qtrue, qfalse);
  578. if(NPC->enemy)
  579. {//FIXME: do anger script
  580. NPCInfo->behaviorState = BS_HUNT_AND_KILL;
  581. //NPC_AngerSound();
  582. return;
  583. }
  584. }
  585. //FIXME: Implement generic sound alerts
  586. /*
  587. alertEventNum = NPC_CheckAlertEvents( qtrue, qtrue );
  588. if( alertEventNum != -1 )
  589. {//If we heard something, see if we should check it out
  590. if ( NPC_CheckInvestigate( alertEventNum ) )
  591. {
  592. return;
  593. }
  594. }
  595. */
  596. NPCInfo->investigateSoundDebounceTime = 0;
  597. //FIXME if there is no nav data, we need to do something else
  598. // if we're stuck, try to move around it
  599. if ( UpdateGoal() )
  600. {
  601. NPC_MoveToGoal( qtrue );
  602. }
  603. NPC_UpdateAngles( qtrue, qtrue );
  604. ucmd.buttons |= BUTTON_WALKING;
  605. }
  606. /*
  607. void NPC_BSDefault(void)
  608. uses various scriptflags to determine how an npc should behave
  609. */
  610. extern void NPC_CheckGetNewWeapon( void );
  611. extern void NPC_BSST_Attack( void );
  612. void NPC_BSDefault( void )
  613. {
  614. // vec3_t enemyDir;
  615. // float enemyDist;
  616. // float shootDist;
  617. // qboolean enemyFOV = qfalse;
  618. // qboolean enemyShotFOV = qfalse;
  619. // qboolean enemyPVS = qfalse;
  620. // vec3_t enemyHead;
  621. // vec3_t muzzle;
  622. // qboolean enemyLOS = qfalse;
  623. // qboolean enemyCS = qfalse;
  624. qboolean move = qtrue;
  625. // qboolean shoot = qfalse;
  626. if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
  627. {
  628. WeaponThink( qtrue );
  629. }
  630. if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
  631. {//being forced to walk
  632. if( NPC->client->ps.torsoAnim != TORSO_SURRENDER_START )
  633. {
  634. NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD );
  635. }
  636. }
  637. //look for a new enemy if don't have one and are allowed to look, validate current enemy if have one
  638. NPC_CheckEnemy( (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES), qfalse );
  639. if ( !NPC->enemy )
  640. {//still don't have an enemy
  641. if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
  642. {//check for alert events
  643. //FIXME: Check Alert events, see if we should investigate or just look at it
  644. int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED );
  645. //There is an event to look at
  646. if ( alertEvent >= 0 )//&& level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
  647. {//heard/saw something
  648. if ( level.alertEvents[alertEvent].level >= AEL_DISCOVERED && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
  649. {//was a big event
  650. if ( level.alertEvents[alertEvent].owner
  651. && level.alertEvents[alertEvent].owner != NPC
  652. && level.alertEvents[alertEvent].owner->client
  653. && level.alertEvents[alertEvent].owner->health >= 0
  654. && level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
  655. {//an enemy
  656. G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
  657. }
  658. }
  659. else
  660. {//FIXME: investigate lesser events
  661. }
  662. }
  663. //FIXME: also check our allies' condition?
  664. }
  665. }
  666. if ( NPC->enemy && !(NPCInfo->scriptFlags&SCF_FORCED_MARCH) )
  667. {
  668. // just use the stormtrooper attack AI...
  669. NPC_CheckGetNewWeapon();
  670. if ( NPC->client->leader
  671. && NPCInfo->goalEntity == NPC->client->leader
  672. && !Q3_TaskIDPending( NPC, TID_MOVE_NAV ) )
  673. {
  674. NPC_ClearGoal();
  675. }
  676. NPC_BSST_Attack();
  677. return;
  678. /*
  679. //have an enemy
  680. //FIXME: if one of these fails, meaning we can't shoot, do we really need to do the rest?
  681. VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, enemyDir );
  682. enemyDist = VectorNormalize( enemyDir );
  683. enemyDist *= enemyDist;
  684. shootDist = NPC_MaxDistSquaredForWeapon();
  685. enemyFOV = InFOV( NPC->enemy, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov );
  686. enemyShotFOV = InFOV( NPC->enemy, NPC, 20, 20 );
  687. enemyPVS = gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin );
  688. if ( enemyPVS )
  689. {//in the pvs
  690. trace_t tr;
  691. CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemyHead );
  692. enemyHead[2] -= Q_flrand( 0.0f, NPC->enemy->maxs[2]*0.5f );
  693. CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
  694. enemyLOS = NPC_ClearLOS( muzzle, enemyHead );
  695. gi.trace ( &tr, muzzle, vec3_origin, vec3_origin, enemyHead, NPC->s.number, MASK_SHOT );
  696. enemyCS = NPC_EvaluateShot( tr.entityNum, qtrue );
  697. }
  698. else
  699. {//skip thr 2 traces since they would have to fail
  700. enemyLOS = qfalse;
  701. enemyCS = qfalse;
  702. }
  703. if ( enemyCS && enemyShotFOV )
  704. {//can hit enemy if we want
  705. NPC->cantHitEnemyCounter = 0;
  706. }
  707. else
  708. {//can't hit
  709. NPC->cantHitEnemyCounter++;
  710. }
  711. if ( enemyCS && enemyShotFOV && enemyDist < shootDist )
  712. {//can shoot
  713. shoot = qtrue;
  714. if ( NPCInfo->goalEntity == NPC->enemy )
  715. {//my goal is my enemy and I have a clear shot, no need to chase right now
  716. move = qfalse;
  717. }
  718. }
  719. else
  720. {//don't shoot yet, keep chasing
  721. shoot = qfalse;
  722. move = qtrue;
  723. }
  724. //shoot decision
  725. if ( !(NPCInfo->scriptFlags&SCF_DONT_FIRE) )
  726. {//try to shoot
  727. if ( NPC->enemy )
  728. {
  729. if ( shoot )
  730. {
  731. if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
  732. {
  733. WeaponThink( qtrue );
  734. }
  735. }
  736. }
  737. }
  738. //chase decision
  739. if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
  740. {//go after him
  741. NPCInfo->goalEntity = NPC->enemy;
  742. //FIXME: don't need to chase when have a clear shot and in range?
  743. if ( !enemyCS && NPC->cantHitEnemyCounter > 60 )
  744. {//haven't been able to shoot enemy for about 6 seconds, need to do something
  745. //FIXME: combat points? Just chase?
  746. if ( enemyPVS )
  747. {//in my PVS, just pick a combat point
  748. //FIXME: implement
  749. }
  750. else
  751. {//just chase him
  752. }
  753. }
  754. //FIXME: in normal behavior, should we use combat Points? Do we care? Is anyone actually going to ever use this AI?
  755. }
  756. else if ( NPC->cantHitEnemyCounter > 60 )
  757. {//pick a new one
  758. NPC_CheckEnemy( qtrue, qfalse );
  759. }
  760. if ( enemyPVS && enemyLOS )//&& !enemyShotFOV )
  761. {//have a clear LOS to him//, but not looking at him
  762. //Find the desired angles
  763. vec3_t angles;
  764. GetAnglesForDirection( muzzle, enemyHead, angles );
  765. NPCInfo->desiredYaw = AngleNormalize180( angles[YAW] );
  766. NPCInfo->desiredPitch = AngleNormalize180( angles[PITCH] );
  767. }
  768. */
  769. }
  770. if ( UpdateGoal() )
  771. {//have a goal
  772. if ( !NPC->enemy
  773. && NPC->client->leader
  774. && NPCInfo->goalEntity == NPC->client->leader
  775. && !Q3_TaskIDPending( NPC, TID_MOVE_NAV ) )
  776. {
  777. NPC_BSFollowLeader();
  778. }
  779. else
  780. {
  781. //set angles
  782. if ( (NPCInfo->scriptFlags & SCF_FACE_MOVE_DIR) || NPCInfo->goalEntity != NPC->enemy )
  783. {//face direction of movement, NOTE: default behavior when not chasing enemy
  784. NPCInfo->combatMove = qfalse;
  785. }
  786. else
  787. {//face goal.. FIXME: what if have a navgoal but want to face enemy while moving? Will this do that?
  788. vec3_t dir, angles;
  789. NPCInfo->combatMove = qfalse;
  790. VectorSubtract( NPCInfo->goalEntity->currentOrigin, NPC->currentOrigin, dir );
  791. vectoangles( dir, angles );
  792. NPCInfo->desiredYaw = angles[YAW];
  793. if ( NPCInfo->goalEntity == NPC->enemy )
  794. {
  795. NPCInfo->desiredPitch = angles[PITCH];
  796. }
  797. }
  798. //set movement
  799. //override default walk/run behavior
  800. //NOTE: redundant, done in NPC_ApplyScriptFlags
  801. if ( NPCInfo->scriptFlags & SCF_RUNNING )
  802. {
  803. ucmd.buttons &= ~BUTTON_WALKING;
  804. }
  805. else if ( NPCInfo->scriptFlags & SCF_WALKING )
  806. {
  807. ucmd.buttons |= BUTTON_WALKING;
  808. }
  809. else if ( NPCInfo->goalEntity == NPC->enemy )
  810. {
  811. ucmd.buttons &= ~BUTTON_WALKING;
  812. }
  813. else
  814. {
  815. ucmd.buttons |= BUTTON_WALKING;
  816. }
  817. if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
  818. {//being forced to walk
  819. if ( g_crosshairEntNum != NPC->s.number )
  820. {//don't walk if player isn't aiming at me
  821. move = qfalse;
  822. }
  823. }
  824. if ( move )
  825. {
  826. //move toward goal
  827. NPC_MoveToGoal( qtrue );
  828. }
  829. }
  830. }
  831. else if ( !NPC->enemy && NPC->client->leader )
  832. {
  833. NPC_BSFollowLeader();
  834. }
  835. //update angles
  836. NPC_UpdateAngles( qtrue, qtrue );
  837. }