AI_Utils.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. // These utilities are meant for strictly non-player, non-team NPCs.
  2. // These functions are in their own file because they are only intended
  3. // for use with NPCs who's logic has been overriden from the original
  4. // AI code, and who's code resides in files with the AI_ prefix.
  5. // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
  6. #include "g_headers.h"
  7. #include "b_local.h"
  8. #include "g_nav.h"
  9. #include "g_navigator.h"
  10. #define MAX_RADIUS_ENTS 128
  11. #define DEFAULT_RADIUS 45
  12. //extern CNavigator navigator;
  13. extern cvar_t *d_noGroupAI;
  14. qboolean AI_ValidateGroupMember( AIGroupInfo_t *group, gentity_t *member );
  15. /*
  16. -------------------------
  17. AI_GetGroupSize
  18. -------------------------
  19. */
  20. int AI_GetGroupSize( vec3_t origin, int radius, team_t playerTeam, gentity_t *avoid )
  21. {
  22. gentity_t *radiusEnts[ MAX_RADIUS_ENTS ];
  23. vec3_t mins, maxs;
  24. int numEnts, realCount = 0;
  25. //Setup the bbox to search in
  26. for ( int i = 0; i < 3; i++ )
  27. {
  28. mins[i] = origin[i] - radius;
  29. maxs[i] = origin[i] + radius;
  30. }
  31. //Get the number of entities in a given space
  32. numEnts = gi.EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
  33. //Cull this list
  34. for ( int j = 0; j < numEnts; j++ )
  35. {
  36. //Validate clients
  37. if ( radiusEnts[ j ]->client == NULL )
  38. continue;
  39. //Skip the requested avoid ent if present
  40. if ( ( avoid != NULL ) && ( radiusEnts[ j ] == avoid ) )
  41. continue;
  42. //Must be on the same team
  43. if ( radiusEnts[ j ]->client->playerTeam != playerTeam )
  44. continue;
  45. //Must be alive
  46. if ( radiusEnts[ j ]->health <= 0 )
  47. continue;
  48. realCount++;
  49. }
  50. return realCount;
  51. }
  52. //Overload
  53. int AI_GetGroupSize( gentity_t *ent, int radius )
  54. {
  55. if ( ( ent == NULL ) || ( ent->client == NULL ) )
  56. return -1;
  57. return AI_GetGroupSize( ent->currentOrigin, radius, ent->client->playerTeam, ent );
  58. }
  59. void AI_SetClosestBuddy( AIGroupInfo_t *group )
  60. {
  61. int i, j;
  62. int dist, bestDist;
  63. for ( i = 0; i < group->numGroup; i++ )
  64. {
  65. group->member[i].closestBuddy = ENTITYNUM_NONE;
  66. bestDist = Q3_INFINITE;
  67. for ( j = 0; j < group->numGroup; j++ )
  68. {
  69. dist = DistanceSquared( g_entities[group->member[i].number].currentOrigin, g_entities[group->member[j].number].currentOrigin );
  70. if ( dist < bestDist )
  71. {
  72. bestDist = dist;
  73. group->member[i].closestBuddy = group->member[j].number;
  74. }
  75. }
  76. }
  77. }
  78. void AI_SortGroupByPathCostToEnemy( AIGroupInfo_t *group )
  79. {
  80. AIGroupMember_t bestMembers[MAX_GROUP_MEMBERS];
  81. int i, j, k;
  82. qboolean sort = qfalse;
  83. if ( group->enemy != NULL )
  84. {//FIXME: just use enemy->waypoint?
  85. group->enemyWP = NAV::GetNearestNode(group->enemy);
  86. }
  87. else
  88. {
  89. group->enemyWP = WAYPOINT_NONE;
  90. }
  91. for ( i = 0; i < group->numGroup; i++ )
  92. {
  93. if ( group->enemyWP == WAYPOINT_NONE )
  94. {//FIXME: just use member->waypoint?
  95. group->member[i].waypoint = WAYPOINT_NONE;
  96. group->member[i].pathCostToEnemy = Q3_INFINITE;
  97. }
  98. else
  99. {//FIXME: just use member->waypoint?
  100. group->member[i].waypoint = NAV::GetNearestNode(group->enemy);
  101. if ( group->member[i].waypoint != WAYPOINT_NONE )
  102. {
  103. group->member[i].pathCostToEnemy = NAV::EstimateCostToGoal( group->member[i].waypoint, group->enemyWP );
  104. //at least one of us has a path, so do sorting
  105. sort = qtrue;
  106. }
  107. else
  108. {
  109. group->member[i].pathCostToEnemy = Q3_INFINITE;
  110. }
  111. }
  112. }
  113. //Now sort
  114. if ( sort )
  115. {
  116. //initialize bestMembers data
  117. for ( j = 0; j < group->numGroup; j++ )
  118. {
  119. bestMembers[j].number = ENTITYNUM_NONE;
  120. }
  121. for ( i = 0; i < group->numGroup; i++ )
  122. {
  123. for ( j = 0; j < group->numGroup; j++ )
  124. {
  125. if ( bestMembers[j].number != ENTITYNUM_NONE )
  126. {//slot occupied
  127. if ( group->member[i].pathCostToEnemy < bestMembers[j].pathCostToEnemy )
  128. {//this guy has a shorter path than the one currenly in this spot, bump him and put myself in here
  129. for ( k = group->numGroup; k > j; k++ )
  130. {
  131. memcpy( &bestMembers[k], &bestMembers[k-1], sizeof( bestMembers[k] ) );
  132. }
  133. memcpy( &bestMembers[j], &group->member[i], sizeof( bestMembers[j] ) );
  134. break;
  135. }
  136. }
  137. else
  138. {//slot unoccupied, reached end of list, throw self in here
  139. memcpy( &bestMembers[j], &group->member[i], sizeof( bestMembers[j] ) );
  140. break;
  141. }
  142. }
  143. }
  144. //Okay, now bestMembers is a sorted list, just copy it into group->members
  145. for ( i = 0; i < group->numGroup; i++ )
  146. {
  147. memcpy( &group->member[i], &bestMembers[i], sizeof( group->member[i] ) );
  148. }
  149. }
  150. }
  151. qboolean AI_FindSelfInPreviousGroup( gentity_t *self )
  152. {//go through other groups made this frame and see if any of those contain me already
  153. int i, j;
  154. for ( i = 0; i < MAX_FRAME_GROUPS; i++ )
  155. {
  156. if ( level.groups[i].numGroup )//&& level.groups[i].enemy != NULL )
  157. {//check this one
  158. for ( j = 0; j < level.groups[i].numGroup; j++ )
  159. {
  160. if ( level.groups[i].member[j].number == self->s.number )
  161. {
  162. self->NPC->group = &level.groups[i];
  163. return qtrue;
  164. }
  165. }
  166. }
  167. }
  168. return qfalse;
  169. }
  170. void AI_InsertGroupMember( AIGroupInfo_t *group, gentity_t *member )
  171. {
  172. //okay, you know what? Check this damn group and make sure we're not already in here!
  173. for ( int i = 0; i < group->numGroup; i++ )
  174. {
  175. if ( group->member[i].number == member->s.number )
  176. {//already in here
  177. break;
  178. }
  179. }
  180. if ( i < group->numGroup )
  181. {//found him in group already
  182. }
  183. else
  184. {//add him in
  185. group->member[group->numGroup++].number = member->s.number;
  186. group->numState[member->NPC->squadState]++;
  187. }
  188. if ( !group->commander || (member->NPC->rank > group->commander->NPC->rank) )
  189. {//keep track of highest rank
  190. group->commander = member;
  191. }
  192. member->NPC->group = group;
  193. }
  194. qboolean AI_TryJoinPreviousGroup( gentity_t *self )
  195. {//go through other groups made this frame and see if any of those have the same enemy as me... if so, add me in!
  196. int i;
  197. for ( i = 0; i < MAX_FRAME_GROUPS; i++ )
  198. {
  199. if ( level.groups[i].numGroup
  200. && level.groups[i].numGroup < (MAX_GROUP_MEMBERS - 1)
  201. //&& level.groups[i].enemy != NULL
  202. && level.groups[i].enemy == self->enemy )
  203. {//has members, not full and has my enemy
  204. if ( AI_ValidateGroupMember( &level.groups[i], self ) )
  205. {//I am a valid member for this group
  206. AI_InsertGroupMember( &level.groups[i], self );
  207. return qtrue;
  208. }
  209. }
  210. }
  211. return qfalse;
  212. }
  213. qboolean AI_GetNextEmptyGroup( gentity_t *self )
  214. {
  215. if ( AI_FindSelfInPreviousGroup( self ) )
  216. {//already in one, no need to make a new one
  217. return qfalse;
  218. }
  219. if ( AI_TryJoinPreviousGroup( self ) )
  220. {//try to just put us in one that already exists
  221. return qfalse;
  222. }
  223. //okay, make a whole new one, then
  224. for ( int i = 0; i < MAX_FRAME_GROUPS; i++ )
  225. {
  226. if ( !level.groups[i].numGroup )
  227. {//make a new one
  228. self->NPC->group = &level.groups[i];
  229. return qtrue;
  230. }
  231. }
  232. //if ( i >= MAX_FRAME_GROUPS )
  233. {//WTF? Out of groups!
  234. self->NPC->group = NULL;
  235. return qfalse;
  236. }
  237. }
  238. qboolean AI_ValidateNoEnemyGroupMember( AIGroupInfo_t *group, gentity_t *member )
  239. {
  240. if ( !group )
  241. {
  242. return qfalse;
  243. }
  244. vec3_t center;
  245. if ( group->commander )
  246. {
  247. VectorCopy( group->commander->currentOrigin, center );
  248. }
  249. else
  250. {//hmm, just pick the first member
  251. if ( group->member[0].number < 0 || group->member[0].number >= ENTITYNUM_WORLD )
  252. {
  253. return qfalse;
  254. }
  255. VectorCopy( g_entities[group->member[0].number].currentOrigin, center );
  256. }
  257. //FIXME: maybe it should be based on the center of the mass of the group, not the commander?
  258. if ( DistanceSquared( center, member->currentOrigin ) > 147456/*384*384*/ )
  259. {
  260. return qfalse;
  261. }
  262. if ( !gi.inPVS( member->currentOrigin, center ) )
  263. {//not within PVS of the group enemy
  264. return qfalse;
  265. }
  266. return qtrue;
  267. }
  268. qboolean AI_ValidateGroupMember( AIGroupInfo_t *group, gentity_t *member )
  269. {
  270. //Validate ents
  271. if ( member == NULL )
  272. return qfalse;
  273. //Validate clients
  274. if ( member->client == NULL )
  275. return qfalse;
  276. //Validate NPCs
  277. if ( member->NPC == NULL )
  278. return qfalse;
  279. //must be aware
  280. if ( member->NPC->confusionTime > level.time )
  281. return qfalse;
  282. //must be allowed to join groups
  283. if ( member->NPC->scriptFlags&SCF_NO_GROUPS )
  284. return qfalse;
  285. //Must not be in another group
  286. if ( member->NPC->group != NULL && member->NPC->group != group )
  287. {//FIXME: if that group's enemy is mine, why not absorb that group into mine?
  288. return qfalse;
  289. }
  290. //Must be alive
  291. if ( member->health <= 0 )
  292. return qfalse;
  293. //can't be in an emplaced gun
  294. if( member->s.eFlags & EF_LOCKED_TO_WEAPON )
  295. return qfalse;
  296. if( member->s.eFlags & EF_HELD_BY_RANCOR )
  297. return qfalse;
  298. if( member->s.eFlags & EF_HELD_BY_SAND_CREATURE )
  299. return qfalse;
  300. if( member->s.eFlags & EF_HELD_BY_WAMPA )
  301. return qfalse;
  302. //Must be on the same team
  303. if ( member->client->playerTeam != group->team )
  304. return qfalse;
  305. if ( member->client->ps.weapon == WP_SABER ||//!= self->s.weapon )
  306. member->client->ps.weapon == WP_THERMAL ||
  307. member->client->ps.weapon == WP_DISRUPTOR ||
  308. member->client->ps.weapon == WP_EMPLACED_GUN ||
  309. member->client->ps.weapon == WP_BOT_LASER || // Probe droid - Laser blast
  310. member->client->ps.weapon == WP_MELEE ||
  311. member->client->ps.weapon == WP_TURRET || // turret guns
  312. member->client->ps.weapon == WP_ATST_MAIN ||
  313. member->client->ps.weapon == WP_ATST_SIDE ||
  314. member->client->ps.weapon == WP_TIE_FIGHTER )
  315. {//not really a squad-type guy
  316. return qfalse;
  317. }
  318. if ( member->client->NPC_class == CLASS_ATST ||
  319. member->client->NPC_class == CLASS_PROBE ||
  320. member->client->NPC_class == CLASS_SEEKER ||
  321. member->client->NPC_class == CLASS_REMOTE ||
  322. member->client->NPC_class == CLASS_SENTRY ||
  323. member->client->NPC_class == CLASS_INTERROGATOR ||
  324. member->client->NPC_class == CLASS_MINEMONSTER ||
  325. member->client->NPC_class == CLASS_HOWLER ||
  326. member->client->NPC_class == CLASS_RANCOR ||
  327. member->client->NPC_class == CLASS_MARK1 ||
  328. member->client->NPC_class == CLASS_MARK2 )
  329. {//these kinds of enemies don't actually use this group AI
  330. return qfalse;
  331. }
  332. //should have same enemy
  333. if ( member->enemy != group->enemy )
  334. {
  335. if ( member->enemy != NULL )
  336. {//he's fighting someone else, leave him out
  337. return qfalse;
  338. }
  339. if ( !gi.inPVS( member->currentOrigin, group->enemy->currentOrigin ) )
  340. {//not within PVS of the group enemy
  341. return qfalse;
  342. }
  343. }
  344. else if ( group->enemy == NULL )
  345. {//if the group is a patrol group, only take those within the room and radius
  346. if ( !AI_ValidateNoEnemyGroupMember( group, member ) )
  347. {
  348. return qfalse;
  349. }
  350. }
  351. //must be actually in combat mode
  352. if ( !TIMER_Done( member, "interrogating" ) )
  353. return qfalse;
  354. //FIXME: need to have a route to enemy and/or clear shot?
  355. return qtrue;
  356. }
  357. /*
  358. -------------------------
  359. AI_GetGroup
  360. -------------------------
  361. */
  362. //#define MAX_WAITERS 128
  363. void AI_GetGroup( gentity_t *self )
  364. {
  365. int i;
  366. gentity_t *member;//, *waiter;
  367. //int waiters[MAX_WAITERS];
  368. if ( !self || !self->NPC )
  369. {
  370. return;
  371. }
  372. if ( d_noGroupAI->integer )
  373. {
  374. self->NPC->group = NULL;
  375. return;
  376. }
  377. if ( !self->client )
  378. {
  379. self->NPC->group = NULL;
  380. return;
  381. }
  382. if ( self->NPC->scriptFlags&SCF_NO_GROUPS )
  383. {
  384. self->NPC->group = NULL;
  385. return;
  386. }
  387. if ( self->enemy && (!self->enemy->client || (level.time - self->NPC->enemyLastSeenTime > 7000 )))
  388. {
  389. self->NPC->group = NULL;
  390. return;
  391. }
  392. if ( !AI_GetNextEmptyGroup( self ) )
  393. {//either no more groups left or we're already in a group built earlier
  394. return;
  395. }
  396. //create a new one
  397. memset( self->NPC->group, 0, sizeof( AIGroupInfo_t ) );
  398. self->NPC->group->enemy = self->enemy;
  399. self->NPC->group->team = self->client->playerTeam;
  400. self->NPC->group->processed = qfalse;
  401. self->NPC->group->commander = self;
  402. self->NPC->group->memberValidateTime = level.time + 2000;
  403. self->NPC->group->activeMemberNum = 0;
  404. if ( self->NPC->group->enemy )
  405. {
  406. self->NPC->group->lastSeenEnemyTime = level.time;
  407. self->NPC->group->lastClearShotTime = level.time;
  408. VectorCopy( self->NPC->group->enemy->currentOrigin, self->NPC->group->enemyLastSeenPos );
  409. }
  410. // for ( i = 0, member = &g_entities[0]; i < globals.num_entities ; i++, member++)
  411. for ( i = 0; i < globals.num_entities ; i++)
  412. {
  413. if(!PInUse(i))
  414. continue;
  415. member = &g_entities[i];
  416. if ( !AI_ValidateGroupMember( self->NPC->group, member ) )
  417. {//FIXME: keep track of those who aren't angry yet and see if we should wake them after we assemble the core group
  418. continue;
  419. }
  420. //store it
  421. AI_InsertGroupMember( self->NPC->group, member );
  422. if ( self->NPC->group->numGroup >= (MAX_GROUP_MEMBERS - 1) )
  423. {//full
  424. break;
  425. }
  426. }
  427. /*
  428. //now go through waiters and see if any should join the group
  429. //NOTE: Some should hang back and probably not attack, so we can ambush
  430. //NOTE: only do this if calling for reinforcements?
  431. for ( i = 0; i < numWaiters; i++ )
  432. {
  433. waiter = &g_entities[waiters[i]];
  434. for ( j = 0; j < self->NPC->group->numGroup; j++ )
  435. {
  436. member = &g_entities[self->NPC->group->member[j];
  437. if ( gi.inPVS( waiter->currentOrigin, member->currentOrigin ) )
  438. {//this waiter is within PVS of a current member
  439. }
  440. }
  441. }
  442. */
  443. if ( self->NPC->group->numGroup <= 0 )
  444. {//none in group
  445. self->NPC->group = NULL;
  446. return;
  447. }
  448. AI_SortGroupByPathCostToEnemy( self->NPC->group );
  449. AI_SetClosestBuddy( self->NPC->group );
  450. }
  451. void AI_SetNewGroupCommander( AIGroupInfo_t *group )
  452. {
  453. gentity_t *member = NULL;
  454. group->commander = NULL;
  455. for ( int i = 0; i < group->numGroup; i++ )
  456. {
  457. member = &g_entities[group->member[i].number];
  458. if ( !group->commander || (member && member->NPC && group->commander->NPC && member->NPC->rank > group->commander->NPC->rank) )
  459. {//keep track of highest rank
  460. group->commander = member;
  461. }
  462. }
  463. }
  464. void AI_DeleteGroupMember( AIGroupInfo_t *group, int memberNum )
  465. {
  466. if ( group->commander && group->commander->s.number == group->member[memberNum].number )
  467. {
  468. group->commander = NULL;
  469. }
  470. if ( g_entities[group->member[memberNum].number].NPC )
  471. {
  472. g_entities[group->member[memberNum].number].NPC->group = NULL;
  473. }
  474. for ( int i = memberNum; i < (group->numGroup-1); i++ )
  475. {
  476. memcpy( &group->member[i], &group->member[i+1], sizeof( group->member[i] ) );
  477. }
  478. if ( memberNum < group->activeMemberNum )
  479. {
  480. group->activeMemberNum--;
  481. if ( group->activeMemberNum < 0 )
  482. {
  483. group->activeMemberNum = 0;
  484. }
  485. }
  486. group->numGroup--;
  487. if ( group->numGroup < 0 )
  488. {
  489. group->numGroup = 0;
  490. }
  491. AI_SetNewGroupCommander( group );
  492. }
  493. void AI_DeleteSelfFromGroup( gentity_t *self )
  494. {
  495. //FIXME: if killed, keep track of how many in group killed? To affect morale?
  496. for ( int i = 0; i < self->NPC->group->numGroup; i++ )
  497. {
  498. if ( self->NPC->group->member[i].number == self->s.number )
  499. {
  500. AI_DeleteGroupMember( self->NPC->group, i );
  501. return;
  502. }
  503. }
  504. }
  505. extern void ST_AggressionAdjust( gentity_t *self, int change );
  506. extern void ST_MarkToCover( gentity_t *self );
  507. extern void ST_StartFlee( gentity_t *self, gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int minTime, int maxTime );
  508. void AI_GroupMemberKilled( gentity_t *self )
  509. {
  510. /* AIGroupInfo_t *group = self->NPC->group;
  511. gentity_t *member;
  512. qboolean noflee = qfalse;
  513. if ( !group )
  514. {//what group?
  515. return;
  516. }
  517. if ( !self || !self->NPC || self->NPC->rank < RANK_ENSIGN )
  518. {//I'm not an officer, let's not really care for now
  519. return;
  520. }
  521. //temporarily drop group morale for a few seconds
  522. group->moraleAdjust -= self->NPC->rank;
  523. //go through and drop aggression on my teammates (more cover, worse aim)
  524. for ( int i = 0; i < group->numGroup; i++ )
  525. {
  526. member = &g_entities[group->member[i].number];
  527. if ( member == self )
  528. {
  529. continue;
  530. }
  531. if ( member->NPC->rank > RANK_ENSIGN )
  532. {//officers do not panic
  533. noflee = qtrue;
  534. }
  535. else
  536. {
  537. ST_AggressionAdjust( member, -1 );
  538. member->NPC->currentAim -= Q_irand( 0, 10 );//Q_irand( 0, 2);//drop their aim accuracy
  539. }
  540. }
  541. //okay, if I'm the group commander, make everyone else flee
  542. if ( group->commander != self )
  543. {//I'm not the commander... hmm, should maybe a couple flee... maybe those near me?
  544. return;
  545. }
  546. //now see if there is another of sufficient rank to keep them from fleeing
  547. if ( !noflee )
  548. {
  549. self->NPC->group->speechDebounceTime = 0;
  550. for ( int i = 0; i < group->numGroup; i++ )
  551. {
  552. member = &g_entities[group->member[i].number];
  553. if ( member == self )
  554. {
  555. continue;
  556. }
  557. if ( member->NPC->rank < RANK_ENSIGN )
  558. {//grunt
  559. if ( group->enemy && DistanceSquared( member->currentOrigin, group->enemy->currentOrigin ) < 65536 )//256*256
  560. {//those close to enemy run away!
  561. ST_StartFlee( member, group->enemy, member->currentOrigin, AEL_DANGER_GREAT, 3000, 5000 );
  562. }
  563. else if ( DistanceSquared( member->currentOrigin, self->currentOrigin ) < 65536 )
  564. {//those close to me run away!
  565. ST_StartFlee( member, group->enemy, member->currentOrigin, AEL_DANGER_GREAT, 3000, 5000 );
  566. }
  567. else
  568. {//else, maybe just a random chance
  569. if ( Q_irand( 0, self->NPC->rank ) > member->NPC->rank )
  570. {//lower rank they are, higher rank I am, more likely they are to flee
  571. ST_StartFlee( member, group->enemy, member->currentOrigin, AEL_DANGER_GREAT, 3000, 5000 );
  572. }
  573. else
  574. {
  575. ST_MarkToCover( member );
  576. }
  577. }
  578. member->NPC->currentAim -= Q_irand( 1, 15 ); //Q_irand( 1, 3 );//drop their aim accuracy even more
  579. }
  580. member->NPC->currentAim -= Q_irand( 1, 15 ); //Q_irand( 1, 3 );//drop their aim accuracy even more
  581. }
  582. }*/
  583. }
  584. void AI_GroupUpdateEnemyLastSeen( AIGroupInfo_t *group, vec3_t spot )
  585. {
  586. if ( !group )
  587. {
  588. return;
  589. }
  590. group->lastSeenEnemyTime = level.time;
  591. VectorCopy( spot, group->enemyLastSeenPos );
  592. }
  593. void AI_GroupUpdateClearShotTime( AIGroupInfo_t *group )
  594. {
  595. if ( !group )
  596. {
  597. return;
  598. }
  599. group->lastClearShotTime = level.time;
  600. }
  601. void AI_GroupUpdateSquadstates( AIGroupInfo_t *group, gentity_t *member, int newSquadState )
  602. {
  603. if ( !group )
  604. {
  605. member->NPC->squadState = newSquadState;
  606. return;
  607. }
  608. for ( int i = 0; i < group->numGroup; i++ )
  609. {
  610. if ( group->member[i].number == member->s.number )
  611. {
  612. group->numState[member->NPC->squadState]--;
  613. member->NPC->squadState = newSquadState;
  614. group->numState[member->NPC->squadState]++;
  615. return;
  616. }
  617. }
  618. }
  619. qboolean AI_RefreshGroup( AIGroupInfo_t *group )
  620. {
  621. gentity_t *member;
  622. int i;//, j;
  623. //see if we should merge with another group
  624. for ( i = 0; i < MAX_FRAME_GROUPS; i++ )
  625. {
  626. if ( &level.groups[i] == group )
  627. {
  628. break;
  629. }
  630. else
  631. {
  632. if ( level.groups[i].enemy == group->enemy )
  633. {//2 groups with same enemy
  634. if ( level.groups[i].numGroup+group->numGroup < (MAX_GROUP_MEMBERS - 1) )
  635. {//combining the members would fit in one group
  636. qboolean deleteWhenDone = qtrue;
  637. //combine the members of mine into theirs
  638. for ( int j = 0; j < group->numGroup; j++ )
  639. {
  640. member = &g_entities[group->member[j].number];
  641. if ( level.groups[i].enemy == NULL )
  642. {//special case for groups without enemies, must be in range
  643. if ( !AI_ValidateNoEnemyGroupMember( &level.groups[i], member ) )
  644. {
  645. deleteWhenDone = qfalse;
  646. continue;
  647. }
  648. }
  649. //remove this member from this group
  650. AI_DeleteGroupMember( group, j );
  651. //keep marker at same place since we deleted this guy and shifted everyone up one
  652. j--;
  653. //add them to the earlier group
  654. AI_InsertGroupMember( &level.groups[i], member );
  655. }
  656. //return and delete this group
  657. if ( deleteWhenDone )
  658. {
  659. return qfalse;
  660. }
  661. }
  662. }
  663. }
  664. }
  665. //clear numStates
  666. for ( i = 0; i < NUM_SQUAD_STATES; i++ )
  667. {
  668. group->numState[i] = 0;
  669. }
  670. //go through group and validate each membership
  671. group->commander = NULL;
  672. for ( i = 0; i < group->numGroup; i++ )
  673. {
  674. /*
  675. //this checks for duplicate copies of one member in a group
  676. for ( j = 0; j < group->numGroup; j++ )
  677. {
  678. if ( i != j )
  679. {
  680. if ( group->member[i].number == group->member[j].number )
  681. {
  682. break;
  683. }
  684. }
  685. }
  686. if ( j < group->numGroup )
  687. {//found a dupe!
  688. gi.Printf( S_COLOR_RED"ERROR: member %s(%d) a duplicate group member!!!\n", g_entities[group->member[i].number].targetname, group->member[i].number );
  689. AI_DeleteGroupMember( group, i );
  690. i--;
  691. continue;
  692. }
  693. */
  694. member = &g_entities[group->member[i].number];
  695. //Must be alive
  696. if ( member->health <= 0 )
  697. {
  698. AI_DeleteGroupMember( group, i );
  699. //keep marker at same place since we deleted this guy and shifted everyone up one
  700. i--;
  701. }
  702. else if ( group->memberValidateTime < level.time && !AI_ValidateGroupMember( group, member ) )
  703. {
  704. //remove this one from the group
  705. AI_DeleteGroupMember( group, i );
  706. //keep marker at same place since we deleted this guy and shifted everyone up one
  707. i--;
  708. }
  709. else
  710. {//membership is valid
  711. //keep track of squadStates
  712. group->numState[member->NPC->squadState]++;
  713. if ( !group->commander || member->NPC->rank > group->commander->NPC->rank )
  714. {//keep track of highest rank
  715. group->commander = member;
  716. }
  717. }
  718. }
  719. if ( group->memberValidateTime < level.time )
  720. {
  721. group->memberValidateTime = level.time + Q_irand( 500, 2500 );
  722. }
  723. //Now add any new guys as long as we're not full
  724. /*
  725. for ( i = 0, member = &g_entities[0]; i < globals.num_entities && group->numGroup < (MAX_GROUP_MEMBERS - 1); i++, member++)
  726. {
  727. if ( !AI_ValidateGroupMember( group, member ) )
  728. {//FIXME: keep track of those who aren't angry yet and see if we should wake them after we assemble the core group
  729. continue;
  730. }
  731. if ( member->NPC->group == group )
  732. {//DOH, already in our group
  733. continue;
  734. }
  735. //store it
  736. AI_InsertGroupMember( group, member );
  737. }
  738. */
  739. //calc the morale of this group
  740. group->morale = group->moraleAdjust;
  741. for ( i = 0; i < group->numGroup; i++ )
  742. {
  743. member = &g_entities[group->member[i].number];
  744. if ( member->NPC->rank < RANK_ENSIGN )
  745. {//grunts
  746. group->morale++;
  747. }
  748. else
  749. {
  750. group->morale += member->NPC->rank;
  751. }
  752. if ( group->commander && debugNPCAI->integer )
  753. {
  754. G_DebugLine( group->commander->currentOrigin, member->currentOrigin, FRAMETIME, 0x00ff00ff, qtrue );
  755. }
  756. }
  757. if ( group->enemy )
  758. {//modify morale based on enemy health and weapon
  759. if ( group->enemy->health < 10 )
  760. {
  761. group->morale += 10;
  762. }
  763. else if ( group->enemy->health < 25 )
  764. {
  765. group->morale += 5;
  766. }
  767. else if ( group->enemy->health < 50 )
  768. {
  769. group->morale += 2;
  770. }
  771. switch( group->enemy->s.weapon )
  772. {
  773. case WP_SABER:
  774. group->morale -= 5;
  775. break;
  776. case WP_BRYAR_PISTOL:
  777. case WP_BLASTER_PISTOL:
  778. group->morale += 3;
  779. break;
  780. case WP_DISRUPTOR:
  781. group->morale += 2;
  782. break;
  783. case WP_REPEATER:
  784. group->morale -= 1;
  785. break;
  786. case WP_FLECHETTE:
  787. group->morale -= 2;
  788. break;
  789. case WP_ROCKET_LAUNCHER:
  790. group->morale -= 10;
  791. break;
  792. case WP_CONCUSSION:
  793. group->morale -= 12;
  794. break;
  795. case WP_THERMAL:
  796. group->morale -= 5;
  797. break;
  798. case WP_TRIP_MINE:
  799. group->morale -= 3;
  800. break;
  801. case WP_DET_PACK:
  802. group->morale -= 10;
  803. break;
  804. case WP_MELEE: // Any ol' melee attack
  805. group->morale += 20;
  806. break;
  807. case WP_STUN_BATON:
  808. group->morale += 10;
  809. break;
  810. case WP_EMPLACED_GUN:
  811. group->morale -= 8;
  812. break;
  813. case WP_ATST_MAIN:
  814. group->morale -= 8;
  815. break;
  816. case WP_ATST_SIDE:
  817. group->morale -= 20;
  818. break;
  819. }
  820. }
  821. if ( group->moraleDebounce < level.time )
  822. {//slowly degrade whatever moraleAdjusters we may have
  823. if ( group->moraleAdjust > 0 )
  824. {
  825. group->moraleAdjust--;
  826. }
  827. else if ( group->moraleAdjust < 0 )
  828. {
  829. group->moraleAdjust++;
  830. }
  831. group->moraleDebounce = level.time + 1000;//FIXME: define?
  832. }
  833. //mark this group as not having been run this frame
  834. group->processed = qfalse;
  835. return (group->numGroup>0);
  836. }
  837. void AI_UpdateGroups( void )
  838. {
  839. if ( d_noGroupAI->integer )
  840. {
  841. return;
  842. }
  843. //Clear all Groups
  844. for ( int i = 0; i < MAX_FRAME_GROUPS; i++ )
  845. {
  846. if ( !level.groups[i].numGroup || AI_RefreshGroup( &level.groups[i] ) == qfalse )//level.groups[i].enemy == NULL ||
  847. {
  848. memset( &level.groups[i], 0, sizeof( level.groups[i] ) );
  849. }
  850. }
  851. }
  852. qboolean AI_GroupContainsEntNum( AIGroupInfo_t *group, int entNum )
  853. {
  854. if ( !group )
  855. {
  856. return qfalse;
  857. }
  858. for ( int i = 0; i < group->numGroup; i++ )
  859. {
  860. if ( group->member[i].number == entNum )
  861. {
  862. return qtrue;
  863. }
  864. }
  865. return qfalse;
  866. }
  867. //Overload
  868. /*
  869. void AI_GetGroup( AIGroupInfo_t &group, gentity_t *ent, int radius )
  870. {
  871. if ( ent->client == NULL )
  872. return;
  873. vec3_t temp, angles;
  874. //FIXME: This is specialized code.. move?
  875. if ( ent->enemy )
  876. {
  877. VectorSubtract( ent->enemy->currentOrigin, ent->currentOrigin, temp );
  878. VectorNormalize( temp ); //FIXME: Needed?
  879. vectoangles( temp, angles );
  880. }
  881. else
  882. {
  883. VectorCopy( ent->currentAngles, angles );
  884. }
  885. AI_GetGroup( group, ent->currentOrigin, ent->currentAngles, DEFAULT_RADIUS, radius, ent->client->playerTeam, ent, ent->enemy );
  886. }
  887. */
  888. /*
  889. -------------------------
  890. AI_DistributeAttack
  891. -------------------------
  892. */
  893. #define MAX_RADIUS_ENTS 128
  894. gentity_t *AI_DistributeAttack( gentity_t *attacker, gentity_t *enemy, team_t team, int threshold )
  895. {
  896. //Don't take new targets
  897. if ( NPC->svFlags & SVF_LOCKEDENEMY )
  898. return enemy;
  899. int numSurrounding = AI_GetGroupSize( enemy->currentOrigin, 48, team, attacker );
  900. //First, see if we should look for the player
  901. if ( enemy != &g_entities[0] )
  902. {
  903. int aroundPlayer = AI_GetGroupSize( g_entities[0].currentOrigin, 48, team, attacker );
  904. //See if we're above our threshold
  905. if ( aroundPlayer < threshold )
  906. {
  907. return &g_entities[0];
  908. }
  909. }
  910. //See if our current enemy is still ok
  911. if ( numSurrounding < threshold )
  912. return enemy;
  913. //Otherwise we need to take a new enemy if possible
  914. vec3_t mins, maxs;
  915. //Setup the bbox to search in
  916. for ( int i = 0; i < 3; i++ )
  917. {
  918. mins[i] = enemy->currentOrigin[i] - 512;
  919. maxs[i] = enemy->currentOrigin[i] + 512;
  920. }
  921. //Get the number of entities in a given space
  922. gentity_t *radiusEnts[ MAX_RADIUS_ENTS ];
  923. int numEnts = gi.EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
  924. //Cull this list
  925. for ( int j = 0; j < numEnts; j++ )
  926. {
  927. //Validate clients
  928. if ( radiusEnts[ j ]->client == NULL )
  929. continue;
  930. //Skip the requested avoid ent if present
  931. if ( ( radiusEnts[ j ] == enemy ) )
  932. continue;
  933. //Must be on the same team
  934. if ( radiusEnts[ j ]->client->playerTeam != enemy->client->playerTeam )
  935. continue;
  936. //Must be alive
  937. if ( radiusEnts[ j ]->health <= 0 )
  938. continue;
  939. //Must not be overwhelmed
  940. if ( AI_GetGroupSize( radiusEnts[j]->currentOrigin, 48, team, attacker ) > threshold )
  941. continue;
  942. return radiusEnts[j];
  943. }
  944. return NULL;
  945. }