AI_SaberDroid.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
  2. #include "g_headers.h"
  3. // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
  4. #include "g_headers.h"
  5. #include "b_local.h"
  6. #include "g_nav.h"
  7. #include "anims.h"
  8. #include "g_navigator.h"
  9. #include "wp_saber.h"
  10. //extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
  11. extern void WP_DeactivateSaber( gentity_t *self, qboolean clearLength = qfalse );
  12. extern int PM_AnimLength( int index, animNumber_t anim );
  13. qboolean NPC_CheckPlayerTeamStealth( void );
  14. static qboolean enemyLOS;
  15. static qboolean enemyCS;
  16. static qboolean faceEnemy;
  17. static qboolean move;
  18. static qboolean shoot;
  19. static float enemyDist;
  20. /*
  21. void NPC_SaberDroid_PlayConfusionSound( gentity_t *self )
  22. {//FIXME: make this a custom sound in sound set
  23. if ( self->health > 0 )
  24. {
  25. G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
  26. }
  27. //reset him to be totally unaware again
  28. TIMER_Set( self, "enemyLastVisible", 0 );
  29. TIMER_Set( self, "flee", 0 );
  30. self->NPC->squadState = SQUAD_IDLE;
  31. self->NPC->tempBehavior = BS_DEFAULT;
  32. //self->NPC->behaviorState = BS_PATROL;
  33. G_ClearEnemy( self );//FIXME: or just self->enemy = NULL;?
  34. self->NPC->investigateCount = 0;
  35. }
  36. */
  37. /*
  38. -------------------------
  39. ST_Move
  40. -------------------------
  41. */
  42. static qboolean SaberDroid_Move( void )
  43. {
  44. NPCInfo->combatMove = qtrue;//always move straight toward our goal
  45. UpdateGoal();
  46. if ( !NPCInfo->goalEntity )
  47. {
  48. NPCInfo->goalEntity = NPC->enemy;
  49. }
  50. NPCInfo->goalRadius = 30.0f;
  51. qboolean moved = NPC_MoveToGoal( qtrue );
  52. // navInfo_t info;
  53. //Get the move info
  54. // NAV_GetLastMove( info );
  55. //FIXME: if we bump into another one of our guys and can't get around him, just stop!
  56. //If we hit our target, then stop and fire!
  57. // if ( info.flags & NIF_COLLISION )
  58. // {
  59. // if ( info.blocker == NPC->enemy )
  60. // {
  61. // SaberDroid_HoldPosition();
  62. // }
  63. // }
  64. //If our move failed, then reset
  65. /*
  66. if ( moved == qfalse )
  67. {//couldn't get to enemy
  68. //just hang here
  69. SaberDroid_HoldPosition();
  70. }
  71. */
  72. return moved;
  73. }
  74. /*
  75. -------------------------
  76. NPC_BSSaberDroid_Patrol
  77. -------------------------
  78. */
  79. void NPC_BSSaberDroid_Patrol( void )
  80. {//FIXME: pick up on bodies of dead buddies?
  81. if ( NPCInfo->confusionTime < level.time )
  82. {
  83. //Look for any enemies
  84. if ( NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES )
  85. {
  86. if ( NPC_CheckPlayerTeamStealth() )
  87. {
  88. //NPCInfo->behaviorState = BS_HUNT_AND_KILL;//should be automatic now
  89. //NPC_AngerSound();
  90. NPC_UpdateAngles( qtrue, qtrue );
  91. return;
  92. }
  93. }
  94. if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
  95. {
  96. //Is there danger nearby
  97. int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_SUSPICIOUS );
  98. //There is an event to look at
  99. if ( alertEvent >= 0 )//&& level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
  100. {
  101. //NPCInfo->lastAlertID = level.alertEvents[alertEvent].ID;
  102. if ( level.alertEvents[alertEvent].level >= AEL_DISCOVERED )
  103. {
  104. if ( level.alertEvents[alertEvent].owner &&
  105. level.alertEvents[alertEvent].owner->client &&
  106. level.alertEvents[alertEvent].owner->health >= 0 &&
  107. level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
  108. {//an enemy
  109. G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
  110. //NPCInfo->enemyLastSeenTime = level.time;
  111. TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
  112. }
  113. }
  114. else
  115. {//FIXME: get more suspicious over time?
  116. //Save the position for movement (if necessary)
  117. VectorCopy( level.alertEvents[alertEvent].position, NPCInfo->investigateGoal );
  118. NPCInfo->investigateDebounceTime = level.time + Q_irand( 500, 1000 );
  119. if ( level.alertEvents[alertEvent].level == AEL_SUSPICIOUS )
  120. {//suspicious looks longer
  121. NPCInfo->investigateDebounceTime += Q_irand( 500, 2500 );
  122. }
  123. }
  124. }
  125. if ( NPCInfo->investigateDebounceTime > level.time )
  126. {//FIXME: walk over to it, maybe? Not if not chase enemies
  127. //NOTE: stops walking or doing anything else below
  128. vec3_t dir, angles;
  129. float o_yaw, o_pitch;
  130. VectorSubtract( NPCInfo->investigateGoal, NPC->client->renderInfo.eyePoint, dir );
  131. vectoangles( dir, angles );
  132. o_yaw = NPCInfo->desiredYaw;
  133. o_pitch = NPCInfo->desiredPitch;
  134. NPCInfo->desiredYaw = angles[YAW];
  135. NPCInfo->desiredPitch = angles[PITCH];
  136. NPC_UpdateAngles( qtrue, qtrue );
  137. NPCInfo->desiredYaw = o_yaw;
  138. NPCInfo->desiredPitch = o_pitch;
  139. return;
  140. }
  141. }
  142. }
  143. //If we have somewhere to go, then do that
  144. if ( UpdateGoal() )
  145. {
  146. ucmd.buttons |= BUTTON_WALKING;
  147. NPC_MoveToGoal( qtrue );
  148. }
  149. else if ( !NPC->client->ps.weaponTime
  150. && TIMER_Done( NPC, "attackDelay" )
  151. && TIMER_Done( NPC, "inactiveDelay" ) )
  152. {
  153. if ( NPC->client->ps.SaberActive() )
  154. {
  155. WP_DeactivateSaber( NPC, qfalse );
  156. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURNOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  157. }
  158. }
  159. NPC_UpdateAngles( qtrue, qtrue );
  160. }
  161. int SaberDroid_PowerLevelForSaberAnim( gentity_t *self )
  162. {
  163. switch ( self->client->ps.legsAnim )
  164. {
  165. case BOTH_A2_TR_BL:
  166. if ( self->client->ps.torsoAnimTimer <= 200 )
  167. {//end of anim
  168. return FORCE_LEVEL_0;
  169. }
  170. else if ( PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim ) - self->client->ps.torsoAnimTimer < 200 )
  171. {//beginning of anim
  172. return FORCE_LEVEL_0;
  173. }
  174. return FORCE_LEVEL_2;
  175. break;
  176. case BOTH_A1_BL_TR:
  177. if ( self->client->ps.torsoAnimTimer <= 300 )
  178. {//end of anim
  179. return FORCE_LEVEL_0;
  180. }
  181. else if ( PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim ) - self->client->ps.torsoAnimTimer < 200 )
  182. {//beginning of anim
  183. return FORCE_LEVEL_0;
  184. }
  185. return FORCE_LEVEL_1;
  186. break;
  187. case BOTH_A1__L__R:
  188. if ( self->client->ps.torsoAnimTimer <= 250 )
  189. {//end of anim
  190. return FORCE_LEVEL_0;
  191. }
  192. else if ( PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim ) - self->client->ps.torsoAnimTimer < 150 )
  193. {//beginning of anim
  194. return FORCE_LEVEL_0;
  195. }
  196. return FORCE_LEVEL_1;
  197. break;
  198. case BOTH_A3__L__R:
  199. if ( self->client->ps.torsoAnimTimer <= 200 )
  200. {//end of anim
  201. return FORCE_LEVEL_0;
  202. }
  203. else if ( PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim ) - self->client->ps.torsoAnimTimer < 300 )
  204. {//beginning of anim
  205. return FORCE_LEVEL_0;
  206. }
  207. return FORCE_LEVEL_3;
  208. break;
  209. }
  210. return FORCE_LEVEL_0;
  211. }
  212. /*
  213. -------------------------
  214. NPC_BSSaberDroid_Attack
  215. -------------------------
  216. */
  217. void NPC_SaberDroid_PickAttack( void )
  218. {
  219. int attackAnim = Q_irand( 0, 3 );
  220. switch ( attackAnim )
  221. {
  222. case 0:
  223. default:
  224. attackAnim = BOTH_A2_TR_BL;
  225. NPC->client->ps.saberMove = LS_A_TR2BL;
  226. NPC->client->ps.saberAnimLevel = SS_MEDIUM;
  227. break;
  228. case 1:
  229. attackAnim = BOTH_A1_BL_TR;
  230. NPC->client->ps.saberMove = LS_A_BL2TR;
  231. NPC->client->ps.saberAnimLevel = SS_FAST;
  232. break;
  233. case 2:
  234. attackAnim = BOTH_A1__L__R;
  235. NPC->client->ps.saberMove = LS_A_L2R;
  236. NPC->client->ps.saberAnimLevel = SS_FAST;
  237. break;
  238. case 3:
  239. attackAnim = BOTH_A3__L__R;
  240. NPC->client->ps.saberMove = LS_A_L2R;
  241. NPC->client->ps.saberAnimLevel = SS_STRONG;
  242. break;
  243. }
  244. NPC->client->ps.saberBlocking = saberMoveData[NPC->client->ps.saberMove].blocking;
  245. if ( saberMoveData[NPC->client->ps.saberMove].trailLength > 0 )
  246. {
  247. NPC->client->ps.SaberActivateTrail( saberMoveData[NPC->client->ps.saberMove].trailLength ); // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter
  248. }
  249. else
  250. {
  251. NPC->client->ps.SaberDeactivateTrail( 0 );
  252. }
  253. NPC_SetAnim( NPC, SETANIM_BOTH, attackAnim, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE );
  254. NPC->client->ps.torsoAnim = NPC->client->ps.legsAnim;//need to do this because we have no anim split but saber code checks torsoAnim
  255. NPC->client->ps.weaponTime = NPC->client->ps.torsoAnimTimer = NPC->client->ps.legsAnimTimer;
  256. NPC->client->ps.weaponstate = WEAPON_FIRING;
  257. }
  258. void NPC_BSSaberDroid_Attack( void )
  259. {
  260. //Don't do anything if we're hurt
  261. if ( NPC->painDebounceTime > level.time )
  262. {
  263. NPC_UpdateAngles( qtrue, qtrue );
  264. return;
  265. }
  266. //NPC_CheckEnemy( qtrue, qfalse );
  267. //If we don't have an enemy, just idle
  268. if ( NPC_CheckEnemyExt() == qfalse )//!NPC->enemy )//
  269. {
  270. NPC->enemy = NULL;
  271. NPC_BSSaberDroid_Patrol();//FIXME: or patrol?
  272. return;
  273. }
  274. if ( !NPC->enemy )
  275. {//WTF? somehow we lost our enemy?
  276. NPC_BSSaberDroid_Patrol();//FIXME: or patrol?
  277. return;
  278. }
  279. enemyLOS = enemyCS = qfalse;
  280. move = qtrue;
  281. faceEnemy = qfalse;
  282. shoot = qfalse;
  283. enemyDist = DistanceSquared( NPC->enemy->currentOrigin, NPC->currentOrigin );
  284. //can we see our target?
  285. if ( NPC_ClearLOS( NPC->enemy ) )
  286. {
  287. NPCInfo->enemyLastSeenTime = level.time;
  288. enemyLOS = qtrue;
  289. if ( enemyDist <= 4096 && InFOV( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 90, 45 ) )//within 64 & infront
  290. {
  291. VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation );
  292. enemyCS = qtrue;
  293. }
  294. }
  295. /*
  296. else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
  297. {
  298. NPCInfo->enemyLastSeenTime = level.time;
  299. faceEnemy = qtrue;
  300. }
  301. */
  302. if ( enemyLOS )
  303. {//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
  304. faceEnemy = qtrue;
  305. }
  306. if ( !TIMER_Done( NPC, "taunting" ) )
  307. {
  308. move = qfalse;
  309. }
  310. else if ( enemyCS )
  311. {
  312. shoot = qtrue;
  313. if ( enemyDist < (NPC->maxs[0]+NPC->enemy->maxs[0]+32)*(NPC->maxs[0]+NPC->enemy->maxs[0]+32) )
  314. {//close enough
  315. move = qfalse;
  316. }
  317. }//this should make him chase enemy when out of range...?
  318. if ( NPC->client->ps.legsAnimTimer
  319. && NPC->client->ps.legsAnim != BOTH_A3__L__R )//this one is a running attack
  320. {//in the middle of a held, stationary anim, can't move
  321. move = qfalse;
  322. }
  323. if ( move )
  324. {//move toward goal
  325. move = SaberDroid_Move();
  326. if ( move )
  327. {//if we had to chase him, be sure to attack as soon as possible
  328. TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime );
  329. }
  330. }
  331. if ( !faceEnemy )
  332. {//we want to face in the dir we're running
  333. if ( move )
  334. {//don't run away and shoot
  335. NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
  336. NPCInfo->desiredPitch = 0;
  337. shoot = qfalse;
  338. }
  339. NPC_UpdateAngles( qtrue, qtrue );
  340. }
  341. else// if ( faceEnemy )
  342. {//face the enemy
  343. NPC_FaceEnemy();
  344. }
  345. if ( NPCInfo->scriptFlags&SCF_DONT_FIRE )
  346. {
  347. shoot = qfalse;
  348. }
  349. //FIXME: need predicted blocking?
  350. //FIXME: don't shoot right away!
  351. if ( shoot )
  352. {//try to shoot if it's time
  353. if ( TIMER_Done( NPC, "attackDelay" ) )
  354. {
  355. if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
  356. {
  357. NPC_SaberDroid_PickAttack();
  358. if ( NPCInfo->rank > RANK_CREWMAN )
  359. {
  360. TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime+Q_irand(0, 1000) );
  361. }
  362. else
  363. {
  364. TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime+Q_irand( 0, 1000 )+(Q_irand( 0, (3-g_spskill->integer)*2 )*500) );
  365. }
  366. }
  367. }
  368. }
  369. }
  370. void NPC_BSSD_Default( void )
  371. {
  372. if( !NPC->enemy )
  373. {//don't have an enemy, look for one
  374. NPC_BSSaberDroid_Patrol();
  375. }
  376. else//if ( NPC->enemy )
  377. {//have an enemy
  378. if ( !NPC->client->ps.SaberActive() )
  379. {
  380. NPC->client->ps.SaberActivate();
  381. if ( NPC->client->ps.legsAnim == BOTH_TURNOFF
  382. || NPC->client->ps.legsAnim == BOTH_STAND1 )
  383. {
  384. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURNON, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  385. }
  386. }
  387. NPC_BSSaberDroid_Attack();
  388. TIMER_Set( NPC, "inactiveDelay", Q_irand( 2000, 4000 ) );
  389. }
  390. if ( !NPC->client->ps.weaponTime )
  391. {
  392. NPC->client->ps.saberMove = LS_READY;
  393. NPC->client->ps.saberBlocking = saberMoveData[LS_READY].blocking;
  394. NPC->client->ps.SaberDeactivateTrail( 0 );
  395. NPC->client->ps.saberAnimLevel = SS_MEDIUM;
  396. NPC->client->ps.weaponstate = WEAPON_READY;
  397. }
  398. }