AI_HazardTrooper.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572
  1. ////////////////////////////////////////////////////////////////////////////////////////
  2. // RAVEN SOFTWARE - STAR WARS: JK II
  3. // (c) 2002 Activision
  4. //
  5. // Troopers
  6. //
  7. // TODO
  8. // ----
  9. //
  10. //
  11. //
  12. //
  13. // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
  14. ////////////////////////////////////////////////////////////////////////////////////////
  15. #include "g_headers.h"
  16. ////////////////////////////////////////////////////////////////////////////////////////
  17. // Includes
  18. ////////////////////////////////////////////////////////////////////////////////////////
  19. #include "b_local.h"
  20. #if !defined(RAVL_VEC_INC)
  21. #include "..\Ravl\CVec.h"
  22. #endif
  23. #if !defined(RATL_ARRAY_VS_INC)
  24. #include "..\Ratl\array_vs.h"
  25. #endif
  26. #if !defined(RATL_VECTOR_VS_INC)
  27. #include "..\Ratl\vector_vs.h"
  28. #endif
  29. #if !defined(RATL_HANDLE_POOL_VS_INC)
  30. #include "..\Ratl\handle_pool_vs.h"
  31. #endif
  32. #if !defined(RUFL_HSTRING_INC)
  33. #include "..\Rufl\hstring.h"
  34. #endif
  35. ////////////////////////////////////////////////////////////////////////////////////////
  36. // Defines
  37. ////////////////////////////////////////////////////////////////////////////////////////
  38. #define MAX_TROOPS 100
  39. #define MAX_ENTS_PER_TROOP 7
  40. #define MAX_TROOP_JOIN_DIST2 1000000 //1000 units
  41. #define MAX_TROOP_MERGE_DIST2 250000 //500 units
  42. #define TARGET_POS_VISITED 10000 //100 units
  43. bool NPC_IsTrooper(gentity_t* actor);
  44. enum
  45. {
  46. SPEECH_CHASE,
  47. SPEECH_CONFUSED,
  48. SPEECH_COVER,
  49. SPEECH_DETECTED,
  50. SPEECH_GIVEUP,
  51. SPEECH_LOOK,
  52. SPEECH_LOST,
  53. SPEECH_OUTFLANK,
  54. SPEECH_ESCAPING,
  55. SPEECH_SIGHT,
  56. SPEECH_SOUND,
  57. SPEECH_SUSPICIOUS,
  58. SPEECH_YELL,
  59. SPEECH_PUSHED
  60. };
  61. extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
  62. static void HT_Speech( gentity_t *self, int speechType, float failChance )
  63. {
  64. if ( random() < failChance )
  65. {
  66. return;
  67. }
  68. if ( failChance >= 0 )
  69. {//a negative failChance makes it always talk
  70. if ( self->NPC->group )
  71. {//group AI speech debounce timer
  72. if ( self->NPC->group->speechDebounceTime > level.time )
  73. {
  74. return;
  75. }
  76. /*
  77. else if ( !self->NPC->group->enemy )
  78. {
  79. if ( groupSpeechDebounceTime[self->client->playerTeam] > level.time )
  80. {
  81. return;
  82. }
  83. }
  84. */
  85. }
  86. else if ( !TIMER_Done( self, "chatter" ) )
  87. {//personal timer
  88. return;
  89. }
  90. }
  91. TIMER_Set( self, "chatter", Q_irand( 2000, 4000 ) );
  92. if ( self->NPC->blockedSpeechDebounceTime > level.time )
  93. {
  94. return;
  95. }
  96. switch( speechType )
  97. {
  98. case SPEECH_CHASE:
  99. G_AddVoiceEvent( self, Q_irand(EV_CHASE1, EV_CHASE3), 2000 );
  100. break;
  101. case SPEECH_CONFUSED:
  102. G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
  103. break;
  104. case SPEECH_COVER:
  105. G_AddVoiceEvent( self, Q_irand(EV_COVER1, EV_COVER5), 2000 );
  106. break;
  107. case SPEECH_DETECTED:
  108. G_AddVoiceEvent( self, Q_irand(EV_DETECTED1, EV_DETECTED5), 2000 );
  109. break;
  110. case SPEECH_GIVEUP:
  111. G_AddVoiceEvent( self, Q_irand(EV_GIVEUP1, EV_GIVEUP4), 2000 );
  112. break;
  113. case SPEECH_LOOK:
  114. G_AddVoiceEvent( self, Q_irand(EV_LOOK1, EV_LOOK2), 2000 );
  115. break;
  116. case SPEECH_LOST:
  117. G_AddVoiceEvent( self, EV_LOST1, 2000 );
  118. break;
  119. case SPEECH_OUTFLANK:
  120. G_AddVoiceEvent( self, Q_irand(EV_OUTFLANK1, EV_OUTFLANK2), 2000 );
  121. break;
  122. case SPEECH_ESCAPING:
  123. G_AddVoiceEvent( self, Q_irand(EV_ESCAPING1, EV_ESCAPING3), 2000 );
  124. break;
  125. case SPEECH_SIGHT:
  126. G_AddVoiceEvent( self, Q_irand(EV_SIGHT1, EV_SIGHT3), 2000 );
  127. break;
  128. case SPEECH_SOUND:
  129. G_AddVoiceEvent( self, Q_irand(EV_SOUND1, EV_SOUND3), 2000 );
  130. break;
  131. case SPEECH_SUSPICIOUS:
  132. G_AddVoiceEvent( self, Q_irand(EV_SUSPICIOUS1, EV_SUSPICIOUS5), 2000 );
  133. break;
  134. case SPEECH_YELL:
  135. G_AddVoiceEvent( self, Q_irand( EV_ANGER1, EV_ANGER3 ), 2000 );
  136. break;
  137. case SPEECH_PUSHED:
  138. G_AddVoiceEvent( self, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 2000 );
  139. break;
  140. default:
  141. break;
  142. }
  143. self->NPC->blockedSpeechDebounceTime = level.time + 2000;
  144. }
  145. ////////////////////////////////////////////////////////////////////////////////////////
  146. // The Troop
  147. //
  148. // Troopers primarly derive their behavior from cooperation as a collective group of
  149. // individuals. They join Troops, each of which has a leader responsible for direcing
  150. // the movement of the rest of the group.
  151. //
  152. ////////////////////////////////////////////////////////////////////////////////////////
  153. class CTroop
  154. {
  155. ////////////////////////////////////////////////////////////////////////////////////
  156. // Various Troop Wide Data
  157. ////////////////////////////////////////////////////////////////////////////////////
  158. int mTroopHandle;
  159. int mTroopTeam;
  160. bool mTroopReform;
  161. float mFormSpacingFwd;
  162. float mFormSpacingRight;
  163. float mSurroundFanAngle;
  164. public:
  165. bool Empty() {return mActors.empty();}
  166. int Team() {return mTroopTeam;}
  167. int Handle() {return mTroopHandle;}
  168. ////////////////////////////////////////////////////////////////////////////////////
  169. // Initialize - Clear out all data, all actors, reset all variables
  170. ////////////////////////////////////////////////////////////////////////////////////
  171. void Initialize(int TroopHandle=0)
  172. {
  173. mActors.clear();
  174. mTarget = 0;
  175. mState = TS_NONE;
  176. mTroopHandle = TroopHandle;
  177. mTroopTeam = 0;
  178. mTroopReform = false;
  179. }
  180. ////////////////////////////////////////////////////////////////////////////////////
  181. // DistanceSq - Quick Operation to see how far an ent is from the rest of the troop
  182. ////////////////////////////////////////////////////////////////////////////////////
  183. float DistanceSq(gentity_t* ent)
  184. {
  185. if (mActors.size())
  186. {
  187. return DistanceSquared(ent->currentOrigin, mActors[0]->currentOrigin);
  188. }
  189. return 0.0f;
  190. }
  191. private:
  192. ////////////////////////////////////////////////////////////////////////////////////
  193. // The Actors
  194. //
  195. // Actors are all the troopers who belong to the group, their positions in this
  196. // vector affect their positions in the troop, whith the first actor as the leader
  197. ////////////////////////////////////////////////////////////////////////////////////
  198. ratl::vector_vs<gentity_t*, MAX_ENTS_PER_TROOP> mActors;
  199. ////////////////////////////////////////////////////////////////////////////////////
  200. // MakeActorLeader - Move A Given Index To A Leader Position
  201. ////////////////////////////////////////////////////////////////////////////////////
  202. void MakeActorLeader(int index)
  203. {
  204. if (index!=0)
  205. {
  206. mActors[0]->client->leader = 0;
  207. mActors.swap(index, 0);
  208. }
  209. mActors[0]->client->leader = mActors[0];
  210. if (mActors[0])
  211. {
  212. if (mActors[0]->client->NPC_class==CLASS_HAZARD_TROOPER)
  213. {
  214. mFormSpacingFwd = 75.0f;
  215. mFormSpacingRight = 50.0f;
  216. }
  217. else
  218. {
  219. mFormSpacingFwd = 75.0f;
  220. mFormSpacingRight = 20.0f;
  221. }
  222. }
  223. }
  224. public:
  225. ////////////////////////////////////////////////////////////////////////////////////
  226. // AddActor - Adds a new actor to the troop & automatically promote to leader
  227. ////////////////////////////////////////////////////////////////////////////////////
  228. void AddActor(gentity_t* actor)
  229. {
  230. assert(actor->NPC->troop==0 && !mActors.full());
  231. actor->NPC->troop = mTroopHandle;
  232. mActors.push_back(actor);
  233. mTroopReform = true;
  234. if ((mActors.size()==1) || (actor->NPC->rank > mActors[0]->NPC->rank))
  235. {
  236. MakeActorLeader(mActors.size()-1);
  237. }
  238. if (!mTroopTeam)
  239. {
  240. mTroopTeam = actor->client->playerTeam;
  241. }
  242. }
  243. ////////////////////////////////////////////////////////////////////////////////////
  244. // RemoveActor - Removes an actor from the troop & automatically promote leader
  245. ////////////////////////////////////////////////////////////////////////////////////
  246. void RemoveActor(gentity_t* actor)
  247. {
  248. assert(actor->NPC->troop==mTroopHandle);
  249. int bestNewLeader=-1;
  250. int numEnts = mActors.size();
  251. bool found = false;
  252. mTroopReform = true;
  253. // Find The Actor
  254. //----------------
  255. for (int i=0; i<numEnts; i++)
  256. {
  257. if (mActors[i]==actor)
  258. {
  259. found = true;
  260. mActors.erase_swap(i);
  261. numEnts --;
  262. if (i==0 && !mActors.empty())
  263. {
  264. bestNewLeader = 0;
  265. }
  266. }
  267. if (bestNewLeader>=0 && (mActors[i]->NPC->rank > mActors[bestNewLeader]->NPC->rank))
  268. {
  269. bestNewLeader = i;
  270. }
  271. }
  272. if (!mActors.empty() && bestNewLeader>=0)
  273. {
  274. MakeActorLeader(bestNewLeader);
  275. }
  276. assert(found);
  277. actor->NPC->troop = 0;
  278. }
  279. private:
  280. ////////////////////////////////////////////////////////////////////////////////////
  281. // Enemy
  282. //
  283. // The troop has a collective enemy that it knows about, which is updated by all
  284. // the members of the group;
  285. ////////////////////////////////////////////////////////////////////////////////////
  286. gentity_t* mTarget;
  287. bool mTargetVisable;
  288. int mTargetVisableStartTime;
  289. int mTargetVisableStopTime;
  290. CVec3 mTargetVisablePosition;
  291. int mTargetIndex;
  292. int mTargetLastKnownTime;
  293. CVec3 mTargetLastKnownPosition;
  294. bool mTargetLastKnownPositionVisited;
  295. ////////////////////////////////////////////////////////////////////////////////////
  296. // RegisterTarget - Records That the target is seen, when and where
  297. ////////////////////////////////////////////////////////////////////////////////////
  298. void RegisterTarget(gentity_t* target, int index, bool visable)
  299. {
  300. if (!mTarget)
  301. {
  302. HT_Speech(mActors[0], SPEECH_DETECTED, 0);
  303. }
  304. else if ((level.time - mTargetLastKnownTime)>8000)
  305. {
  306. HT_Speech(mActors[0], SPEECH_SIGHT, 0);
  307. }
  308. if (visable)
  309. {
  310. mTargetVisableStopTime = level.time;
  311. if (!mTargetVisable)
  312. {
  313. mTargetVisableStartTime = level.time;
  314. }
  315. CalcEntitySpot(target, SPOT_HEAD, mTargetVisablePosition.v);
  316. mTargetVisablePosition[2] -= 10.0f;
  317. }
  318. mTarget = target;
  319. mTargetVisable = visable;
  320. mTargetIndex = index;
  321. mTargetLastKnownTime = level.time;
  322. mTargetLastKnownPosition = target->currentOrigin;
  323. mTargetLastKnownPositionVisited = false;
  324. }
  325. ////////////////////////////////////////////////////////////////////////////////////
  326. // RegisterTarget - Records That the target is seen, when and where
  327. ////////////////////////////////////////////////////////////////////////////////////
  328. bool TargetLastKnownPositionVisited()
  329. {
  330. if (!mTargetLastKnownPositionVisited)
  331. {
  332. float dist = DistanceSquared(mTargetLastKnownPosition.v, mActors[0]->currentOrigin);
  333. mTargetLastKnownPositionVisited = (dist<TARGET_POS_VISITED);
  334. }
  335. return mTargetLastKnownPositionVisited;
  336. }
  337. float ClampScale(float val)
  338. {
  339. if (val>1.0f)
  340. {
  341. val = 1.0f;
  342. }
  343. if (val<0.0f)
  344. {
  345. val = 0.0f;
  346. }
  347. return val;
  348. }
  349. ////////////////////////////////////////////////////////////////////////////////////
  350. // Target Visibility
  351. //
  352. // Compute all factors that can add visibility to a target
  353. ////////////////////////////////////////////////////////////////////////////////////
  354. float TargetVisibility(gentity_t* target)
  355. {
  356. float Scale = 0.8f;
  357. if (target->client && target->client->ps.weapon==WP_SABER && target->client->ps.SaberActive())
  358. {
  359. Scale += 0.1f;
  360. }
  361. return ClampScale(Scale);
  362. }
  363. ////////////////////////////////////////////////////////////////////////////////////
  364. //
  365. ////////////////////////////////////////////////////////////////////////////////////
  366. float TargetNoiseLevel(gentity_t* target)
  367. {
  368. float Scale = 0.1f;
  369. Scale += target->resultspeed / (float)g_speed->integer;
  370. if (target->client && target->client->ps.weapon==WP_SABER && target->client->ps.SaberActive())
  371. {
  372. Scale += 0.2f;
  373. }
  374. return ClampScale(Scale);
  375. }
  376. ////////////////////////////////////////////////////////////////////////////////////
  377. // Scan For Enemies
  378. ////////////////////////////////////////////////////////////////////////////////////
  379. void ScanForTarget(int scannerIndex)
  380. {
  381. gentity_t* target;
  382. int targetIndex=0;
  383. int targetStop=ENTITYNUM_WORLD;
  384. CVec3 targetPos;
  385. CVec3 targetDirection;
  386. float targetDistance;
  387. float targetVisibility;
  388. float targetNoiseLevel;
  389. gentity_t* scanner = mActors[scannerIndex];
  390. gNPCstats_t* scannerStats = &(scanner->NPC->stats);
  391. float scannerMaxViewDist = scannerStats->visrange;
  392. float scannerMinVisability = 0.1f;//1.0f - scannerStats->vigilance;
  393. float scannerMaxHearDist = scannerStats->earshot;
  394. float scannerMinNoiseLevel = 0.3f;//1.0f - scannerStats->vigilance;
  395. CVec3 scannerPos(scanner->currentOrigin);
  396. CVec3 scannerFwd(scanner->currentAngles);
  397. scannerFwd.AngToVec();
  398. // If Existing Target, Only Check It
  399. //-----------------------------------
  400. if (mTarget)
  401. {
  402. targetIndex = mTargetIndex;
  403. targetStop = mTargetIndex+1;
  404. }
  405. SaveNPCGlobals();
  406. SetNPCGlobals(scanner);
  407. for (; targetIndex<targetStop; targetIndex++)
  408. {
  409. target = &g_entities[targetIndex];
  410. if (!NPC_ValidEnemy(target))
  411. {
  412. continue;
  413. }
  414. targetPos = target->currentOrigin;
  415. if (target->client && target->client->ps.leanofs)
  416. {
  417. targetPos = target->client->renderInfo.eyePoint;
  418. }
  419. targetDirection = (targetPos - scannerPos);
  420. targetDistance = targetDirection.SafeNorm();
  421. // Can The Scanner SEE The Target?
  422. //---------------------------------
  423. if (targetDistance<scannerMaxViewDist)
  424. {
  425. targetVisibility = TargetVisibility(target);
  426. targetVisibility *= targetDirection.Dot(scannerFwd);
  427. if (targetVisibility>scannerMinVisability)
  428. {
  429. if (NPC_ClearLOS(targetPos.v))
  430. {
  431. RegisterTarget(target, targetIndex, true);
  432. RestoreNPCGlobals();
  433. return;
  434. }
  435. }
  436. }
  437. // Can The Scanner HEAR The Target?
  438. //----------------------------------
  439. if (targetDistance<scannerMaxHearDist)
  440. {
  441. targetNoiseLevel = TargetNoiseLevel(target);
  442. targetNoiseLevel *= (1.0f - (targetDistance/scannerMaxHearDist)); // scale by distance
  443. if (targetNoiseLevel>scannerMinNoiseLevel)
  444. {
  445. RegisterTarget(target, targetIndex, false);
  446. RestoreNPCGlobals();
  447. return;
  448. }
  449. }
  450. }
  451. RestoreNPCGlobals();
  452. }
  453. private:
  454. ////////////////////////////////////////////////////////////////////////////////////
  455. // Troop State
  456. //
  457. // The troop as a whole can be acting under a number of different "behavior states"
  458. ////////////////////////////////////////////////////////////////////////////////////
  459. enum ETroopState
  460. {
  461. TS_NONE = 0, // No troop wide activity active
  462. TS_ADVANCE, // CHOOSE A NEW ADVANCE TACTIC
  463. TS_ADVANCE_REGROUP, // All ents move into squad position
  464. TS_ADVANCE_SEARCH, // Slow advance, looking left to right, in formation
  465. TS_ADVANCE_COVER, // One at a time moves forward, goes off path, provides cover
  466. TS_ADVANCE_FORMATION, // In formation jog to goal location
  467. TS_ATTACK, // CHOOSE A NEW ATTACK TACTIC
  468. TS_ATTACK_LINE, // Form 2 lines, front kneel, back stand
  469. TS_ATTACK_FLANK, // Same As Line, except scouting group attemts to get around other side of target
  470. TS_ATTACK_SURROUND, // Get on all sides of target
  471. TS_ATTACK_COVER, //
  472. TS_MAX
  473. };
  474. ETroopState mState;
  475. CVec3 mFormHead;
  476. CVec3 mFormFwd;
  477. CVec3 mFormRight;
  478. ////////////////////////////////////////////////////////////////////////////////////
  479. // TroopInFormation - A quick check to see if the troop is currently in formation
  480. ////////////////////////////////////////////////////////////////////////////////////
  481. bool TroopInFormation()
  482. {
  483. float maxActorRangeSq = ((mActors.size()/2) + 2) * mFormSpacingFwd;
  484. maxActorRangeSq *= maxActorRangeSq;
  485. for (int actorIndex=1; actorIndex<mActors.size(); actorIndex++)
  486. {
  487. if (DistanceSq(mActors[actorIndex])>maxActorRangeSq)
  488. {
  489. return false;
  490. }
  491. }
  492. return true;
  493. }
  494. ////////////////////////////////////////////////////////////////////////////////////
  495. // SActorOrder
  496. ////////////////////////////////////////////////////////////////////////////////////
  497. struct SActorOrder
  498. {
  499. CVec3 mPosition;
  500. int mCombatPoint;
  501. bool mKneelAndShoot;
  502. };
  503. ratl::array_vs<SActorOrder, MAX_ENTS_PER_TROOP> mOrders;
  504. ////////////////////////////////////////////////////////////////////////////////////
  505. // LeaderIssueAndUpdateOrders - Tell Everyone Where To Go
  506. ////////////////////////////////////////////////////////////////////////////////////
  507. void LeaderIssueAndUpdateOrders(ETroopState NextState)
  508. {
  509. int actorIndex;
  510. int actorCount = mActors.size();
  511. // Always Put Guys Closest To The Order Locations In Those Locations
  512. //-------------------------------------------------------------------
  513. for (int orderIndex=1; orderIndex<actorCount; orderIndex++)
  514. {
  515. // Don't re-assign points combat point related orders
  516. //----------------------------------------------------
  517. if (mOrders[orderIndex].mCombatPoint==-1)
  518. {
  519. int closestActorIndex = orderIndex;
  520. float closestActorDistance = DistanceSquared(mOrders[orderIndex].mPosition.v, mActors[orderIndex]->currentOrigin);
  521. float currentDistance = closestActorDistance;
  522. for (actorIndex=orderIndex+1; actorIndex<actorCount; actorIndex++)
  523. {
  524. currentDistance = DistanceSquared(mOrders[orderIndex].mPosition.v, mActors[actorIndex]->currentOrigin);
  525. if (currentDistance<closestActorDistance)
  526. {
  527. closestActorDistance = currentDistance;
  528. closestActorIndex = actorIndex;
  529. }
  530. }
  531. if (orderIndex!=closestActorIndex)
  532. {
  533. mActors.swap(orderIndex, closestActorIndex);
  534. }
  535. }
  536. }
  537. // Now Copy The Orders Out To The Actors
  538. //---------------------------------------
  539. for (actorIndex=1; actorIndex<actorCount; actorIndex++)
  540. {
  541. VectorCopy(mOrders[actorIndex].mPosition.v, mActors[actorIndex]->pos1);
  542. }
  543. // PHASE I - VOICE COMMANDS & ANIMATIONS
  544. //=======================================
  545. gentity_t* leader = mActors[0];
  546. if (NextState!=mState)
  547. {
  548. if (mActors.size()>0)
  549. {
  550. switch (NextState)
  551. {
  552. case (TS_ADVANCE_REGROUP) :
  553. {
  554. break;
  555. }
  556. case (TS_ADVANCE_SEARCH) :
  557. {
  558. HT_Speech(leader, SPEECH_LOOK, 0);
  559. break;
  560. }
  561. case (TS_ADVANCE_COVER) :
  562. {
  563. HT_Speech(leader, SPEECH_COVER, 0);
  564. NPC_SetAnim(leader, SETANIM_TORSO, TORSO_HANDSIGNAL4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLDLESS);
  565. break;
  566. }
  567. case (TS_ADVANCE_FORMATION) :
  568. {
  569. HT_Speech(leader, SPEECH_ESCAPING, 0);
  570. break;
  571. }
  572. case (TS_ATTACK_LINE) :
  573. {
  574. HT_Speech(leader, SPEECH_CHASE, 0);
  575. NPC_SetAnim(leader, SETANIM_TORSO, TORSO_HANDSIGNAL1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLDLESS);
  576. break;
  577. }
  578. case (TS_ATTACK_FLANK) :
  579. {
  580. HT_Speech(leader, SPEECH_OUTFLANK, 0);
  581. NPC_SetAnim(leader, SETANIM_TORSO, TORSO_HANDSIGNAL3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLDLESS);
  582. break;
  583. }
  584. case (TS_ATTACK_SURROUND) :
  585. {
  586. HT_Speech(leader, SPEECH_GIVEUP, 0);
  587. NPC_SetAnim(leader, SETANIM_TORSO, TORSO_HANDSIGNAL2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLDLESS);
  588. break;
  589. }
  590. case (TS_ATTACK_COVER) :
  591. {
  592. HT_Speech(leader, SPEECH_COVER, 0);
  593. break;
  594. }
  595. default:
  596. {
  597. }
  598. }
  599. }
  600. }
  601. // If Attacking, And Not Forced To Reform, Don't Recalculate Orders
  602. //------------------------------------------------------------------
  603. else if (NextState>TS_ATTACK && !mTroopReform)
  604. {
  605. return;
  606. }
  607. // PHASE II - COMPUTE THE NEW FORMATION HEAD, FORWARD, AND RIGHT VECTORS
  608. //=======================================================================
  609. CVec3 PreviousFwd = mFormFwd;
  610. mFormHead = leader->currentOrigin;
  611. mFormFwd = (NAV::HasPath(leader))?(NAV::NextPosition(leader)):(mTargetLastKnownPosition);
  612. mFormFwd -= mFormHead;
  613. mFormFwd[2] = 0;
  614. mFormFwd *= -1.0f; // Form Forward Goes Behind The Leader
  615. mFormFwd.Norm();
  616. mFormRight = mFormFwd;
  617. mFormRight.Cross(CVec3::mZ);
  618. // Scale Vectors By Spacing Distances
  619. //------------------------------------
  620. mFormFwd *= mFormSpacingFwd;
  621. mFormRight *= mFormSpacingRight;
  622. // If Attacking, Move Head Forward Some To Center On Target
  623. //----------------------------------------------------------
  624. if (NextState>TS_ATTACK)
  625. {
  626. if (!mTroopReform)
  627. {
  628. int FwdNum = ((actorCount/2)+1);
  629. for (int i=0; i<FwdNum; i++)
  630. {
  631. mFormHead -= mFormFwd;
  632. }
  633. }
  634. trace_t trace;
  635. mOrders[0].mPosition = mFormHead;
  636. gi.trace(&trace,
  637. mActors[0]->currentOrigin,
  638. mActors[0]->mins,
  639. mActors[0]->maxs,
  640. mOrders[0].mPosition.v,
  641. mActors[0]->s.number,
  642. mActors[0]->clipmask
  643. );
  644. if (trace.fraction<1.0f)
  645. {
  646. mOrders[0].mPosition = trace.endpos;
  647. }
  648. }
  649. else
  650. {
  651. mOrders[0].mPosition = mTargetLastKnownPosition;
  652. }
  653. VectorCopy(mOrders[0].mPosition.v, mActors[0]->pos1);
  654. CVec3 FormTgtToHead(mFormHead);
  655. FormTgtToHead -= mTargetLastKnownPosition;
  656. /*float FormTgtToHeadDist = */FormTgtToHead.SafeNorm();
  657. CVec3 BaseAngleToHead(FormTgtToHead);
  658. BaseAngleToHead.VecToAng();
  659. // int NumPerSide = mActors.size()/2;
  660. // float WidestAngle = FORMATION_SURROUND_FAN * (NumPerSide+1);
  661. // PHASE III - USE FORMATION VECTORS TO COMPUTE ORDERS FOR ALL ACTORS
  662. //====================================================================
  663. for (actorIndex=1; actorIndex<actorCount; actorIndex++)
  664. {
  665. SaveNPCGlobals();
  666. SetNPCGlobals(mActors[actorIndex]);
  667. SActorOrder& Order = mOrders[actorIndex];
  668. float FwdScale = (float)((int)((actorIndex+1)/2));
  669. float SideScale = ((actorIndex%2)==0)?(-1.0f):(1.0f);
  670. if (mActors[actorIndex]->NPC->combatPoint!=-1)
  671. {
  672. NPC_FreeCombatPoint(mActors[actorIndex]->NPC->combatPoint, false);
  673. mActors[actorIndex]->NPC->combatPoint = -1;
  674. }
  675. Order.mPosition = mFormHead;
  676. Order.mCombatPoint = -1;
  677. Order.mKneelAndShoot= false;
  678. // Advance Orders
  679. //----------------
  680. if (NextState<TS_ATTACK)
  681. {
  682. if ((NextState==TS_ADVANCE_REGROUP) || (NextState==TS_ADVANCE_SEARCH) || (NextState==TS_ADVANCE_FORMATION))
  683. {
  684. Order.mPosition.ScaleAdd(mFormFwd, FwdScale);
  685. Order.mPosition.ScaleAdd(mFormRight, SideScale);
  686. }
  687. else if (NextState==TS_ADVANCE_COVER)
  688. {
  689. // TODO: Take Turns Switching Who Is In Front
  690. Order.mPosition.ScaleAdd(mFormFwd, FwdScale);
  691. Order.mPosition.ScaleAdd(mFormRight, SideScale);
  692. }
  693. }
  694. // Setup Initial Attack Orders
  695. //-----------------------------
  696. else
  697. {
  698. if (NextState==TS_ATTACK_LINE || (NextState==TS_ATTACK_FLANK && actorIndex<4))
  699. {
  700. Order.mPosition.ScaleAdd(mFormFwd, FwdScale);
  701. Order.mPosition.ScaleAdd(mFormRight, SideScale);
  702. }
  703. else if (NextState==TS_ATTACK_FLANK && actorIndex>=4)
  704. {
  705. int cpFlags = (CP_HAS_ROUTE|CP_AVOID_ENEMY|CP_CLEAR|CP_COVER|CP_FLANK|CP_APPROACH_ENEMY);
  706. float avoidDist = 128.0f;
  707. Order.mCombatPoint = NPC_FindCombatPointRetry(
  708. mActors[actorIndex]->currentOrigin,
  709. mActors[actorIndex]->currentOrigin,
  710. mActors[actorIndex]->currentOrigin,
  711. &cpFlags,
  712. avoidDist,
  713. 0);
  714. if (Order.mCombatPoint!=-1 && (cpFlags&CP_CLEAR))
  715. {
  716. Order.mPosition = level.combatPoints[Order.mCombatPoint].origin;
  717. NPC_SetCombatPoint(Order.mCombatPoint);
  718. }
  719. else
  720. {
  721. Order.mPosition.ScaleAdd(mFormFwd, FwdScale);
  722. Order.mPosition.ScaleAdd(mFormRight, SideScale);
  723. }
  724. }
  725. else if (NextState==TS_ATTACK_SURROUND)
  726. {
  727. Order.mPosition.ScaleAdd(mFormFwd, FwdScale);
  728. Order.mPosition.ScaleAdd(mFormRight, SideScale);
  729. /* CVec3 FanAngles = BaseAngleToHead;
  730. FanAngles[YAW] += (SideScale * (WidestAngle-(FwdScale*FORMATION_SURROUND_FAN)));
  731. FanAngles.AngToVec();
  732. Order.mPosition = mTargetLastKnownPosition;
  733. Order.mPosition.ScaleAdd(FanAngles, FormTgtToHeadDist);
  734. */
  735. }
  736. else if (NextState==TS_ATTACK_COVER)
  737. {
  738. Order.mPosition.ScaleAdd(mFormFwd, FwdScale);
  739. Order.mPosition.ScaleAdd(mFormRight, SideScale);
  740. }
  741. }
  742. if (NextState>=TS_ATTACK)
  743. {
  744. trace_t trace;
  745. CVec3 OrderUp(Order.mPosition);
  746. OrderUp[2] += 10.0f;
  747. gi.trace(&trace,
  748. Order.mPosition.v,
  749. mActors[actorIndex]->mins,
  750. mActors[actorIndex]->maxs,
  751. OrderUp.v,
  752. mActors[actorIndex]->s.number,
  753. CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP);
  754. if (trace.startsolid || trace.allsolid)
  755. {
  756. int cpFlags = (CP_HAS_ROUTE|CP_AVOID_ENEMY|CP_CLEAR|CP_COVER|CP_FLANK|CP_APPROACH_ENEMY);
  757. float avoidDist = 128.0f;
  758. Order.mCombatPoint = NPC_FindCombatPointRetry(
  759. mActors[actorIndex]->currentOrigin,
  760. mActors[actorIndex]->currentOrigin,
  761. mActors[actorIndex]->currentOrigin,
  762. &cpFlags,
  763. avoidDist,
  764. 0);
  765. if (Order.mCombatPoint!=-1)
  766. {
  767. Order.mPosition = level.combatPoints[Order.mCombatPoint].origin;
  768. NPC_SetCombatPoint(Order.mCombatPoint);
  769. }
  770. else
  771. {
  772. Order.mPosition = mOrders[0].mPosition;
  773. }
  774. }
  775. }
  776. RestoreNPCGlobals();
  777. }
  778. mTroopReform = false;
  779. mState = NextState;
  780. }
  781. ////////////////////////////////////////////////////////////////////////////////////
  782. // SufficientCoverNearby - Look at nearby combat points, see if there is enough
  783. ////////////////////////////////////////////////////////////////////////////////////
  784. bool SufficientCoverNearby()
  785. {
  786. // TODO: Evaluate Available Combat Points
  787. return false;
  788. }
  789. public:
  790. ////////////////////////////////////////////////////////////////////////////////////
  791. // Update - This is the primary "think" function from the troop
  792. ////////////////////////////////////////////////////////////////////////////////////
  793. void Update()
  794. {
  795. if (mActors.empty())
  796. {
  797. return;
  798. }
  799. ScanForTarget(0 /*Q_irand(0, (mActors.size()-1))*/);
  800. if (mTarget)
  801. {
  802. ETroopState NextState = mState;
  803. int TimeSinceLastSeen = (level.time - mTargetVisableStopTime);
  804. // int TimeVisable = (mTargetVisableStopTime - mTargetVisableStartTime);
  805. bool Attack = (TimeSinceLastSeen<2000);
  806. if (Attack)
  807. {
  808. // If Not Currently Attacking, Or We Want To Pick A New Attack Tactic
  809. //--------------------------------------------------------------------
  810. if (mState<TS_ATTACK /*|| TODO: Timer To Pick New Tactic */)
  811. {
  812. if (TroopInFormation())
  813. {
  814. NextState = (mActors.size()>4)?(TS_ATTACK_FLANK):(TS_ATTACK_LINE);
  815. }
  816. else
  817. {
  818. NextState = (SufficientCoverNearby())?(TS_ATTACK_COVER):(TS_ATTACK_SURROUND);
  819. }
  820. }
  821. }
  822. else
  823. {
  824. if (!TroopInFormation())
  825. {
  826. NextState = TS_ADVANCE_REGROUP;
  827. }
  828. else
  829. {
  830. if (TargetLastKnownPositionVisited())
  831. {
  832. NextState = TS_ADVANCE_SEARCH;
  833. }
  834. else
  835. {
  836. NextState = (TimeSinceLastSeen<10000)?(TS_ADVANCE_COVER):(TS_ADVANCE_FORMATION);
  837. }
  838. }
  839. }
  840. LeaderIssueAndUpdateOrders(NextState);
  841. }
  842. }
  843. ////////////////////////////////////////////////////////////////////////////////////
  844. // MergeInto - Merges all actors into anther troop
  845. ////////////////////////////////////////////////////////////////////////////////////
  846. void MergeInto(CTroop& Other)
  847. {
  848. int numEnts = mActors.size();
  849. for (int i=0; i<numEnts; i++)
  850. {
  851. mActors[i]->client->leader = 0;
  852. mActors[i]->NPC->troop = 0;
  853. Other.AddActor(mActors[i]);
  854. }
  855. mActors.clear();
  856. if (!Other.mTarget && mTarget)
  857. {
  858. Other.mTarget = mTarget;
  859. Other.mTargetIndex = mTargetIndex;
  860. Other.mTargetLastKnownPosition = mTargetLastKnownPosition;
  861. Other.mTargetLastKnownPositionVisited = mTargetLastKnownPositionVisited;
  862. Other.mTargetLastKnownTime = mTargetLastKnownTime;
  863. Other.mTargetVisableStartTime = mTargetVisableStartTime;
  864. Other.mTargetVisableStopTime = mTargetVisableStopTime;
  865. Other.mTargetVisable = mTargetVisable;
  866. Other.mTargetVisablePosition = mTargetVisablePosition;
  867. Other.LeaderIssueAndUpdateOrders(mState);
  868. }
  869. }
  870. ////////////////////////////////////////////////////////////////////////////////////
  871. //
  872. ////////////////////////////////////////////////////////////////////////////////////
  873. gentity_t* TrackingTarget()
  874. {
  875. return mTarget;
  876. }
  877. ////////////////////////////////////////////////////////////////////////////////////
  878. //
  879. ////////////////////////////////////////////////////////////////////////////////////
  880. gentity_t* TroopLeader()
  881. {
  882. return mActors[0];
  883. }
  884. ////////////////////////////////////////////////////////////////////////////////////
  885. //
  886. ////////////////////////////////////////////////////////////////////////////////////
  887. int TimeSinceSeenTarget()
  888. {
  889. return (level.time - mTargetVisableStopTime);
  890. }
  891. ////////////////////////////////////////////////////////////////////////////////////
  892. //
  893. ////////////////////////////////////////////////////////////////////////////////////
  894. CVec3& TargetVisablePosition()
  895. {
  896. return mTargetVisablePosition;
  897. }
  898. ////////////////////////////////////////////////////////////////////////////////////
  899. //
  900. ////////////////////////////////////////////////////////////////////////////////////
  901. float FormSpacingFwd()
  902. {
  903. return mFormSpacingFwd;
  904. }
  905. ////////////////////////////////////////////////////////////////////////////////////
  906. //
  907. ////////////////////////////////////////////////////////////////////////////////////
  908. gentity_t* TooCloseToTroopMember(gentity_t* actor)
  909. {
  910. for (int i=0; i<mActors.size(); i++)
  911. {
  912. // Only avoid guys ahead of us in the formation
  913. //----------------------------------------------
  914. if (actor==mActors[i])
  915. {
  916. return 0;
  917. }
  918. // if (mActors[i]->resultspeed<10.0f)
  919. // {
  920. // continue;
  921. // }
  922. if (i==0)
  923. {
  924. if (Distance(actor->currentOrigin, mActors[i]->currentOrigin)<(mFormSpacingFwd*0.5f))
  925. {
  926. return mActors[i];
  927. }
  928. }
  929. else
  930. {
  931. if (Distance(actor->currentOrigin, mActors[i]->currentOrigin)<(mFormSpacingFwd*0.5f))
  932. {
  933. return mActors[i];
  934. }
  935. }
  936. }
  937. assert("Somehow this actor is not actually in the troop..."==0);
  938. return 0;
  939. }
  940. };
  941. typedef ratl::handle_pool_vs<CTroop, MAX_TROOPS> TTroopPool;
  942. TTroopPool mTroops;
  943. ////////////////////////////////////////////////////////////////////////////////////////
  944. // Erase All Data, Set To Default Vals Before Entities Spawn
  945. ////////////////////////////////////////////////////////////////////////////////////////
  946. void Troop_Reset()
  947. {
  948. mTroops.clear();
  949. }
  950. ////////////////////////////////////////////////////////////////////////////////////////
  951. // Entities Have Just Spawned, Initialize
  952. ////////////////////////////////////////////////////////////////////////////////////////
  953. void Troop_Initialize()
  954. {
  955. }
  956. ////////////////////////////////////////////////////////////////////////////////////////
  957. // Global Update Of All Troops
  958. ////////////////////////////////////////////////////////////////////////////////////////
  959. void Troop_Update()
  960. {
  961. for (TTroopPool::iterator i=mTroops.begin(); i!=mTroops.end(); i++)
  962. {
  963. i->Update();
  964. }
  965. }
  966. ////////////////////////////////////////////////////////////////////////////////////////
  967. // Erase All Data, Set To Default Vals Before Entities Spawn
  968. ////////////////////////////////////////////////////////////////////////////////////////
  969. void Trooper_UpdateTroop(gentity_t* actor)
  970. {
  971. // Try To Join A Troop
  972. //---------------------
  973. if (!actor->NPC->troop)
  974. {
  975. float curDist = 0;
  976. float closestDist = 0;
  977. TTroopPool::iterator closestTroop = mTroops.end();
  978. trace_t trace;
  979. for (TTroopPool::iterator iTroop=mTroops.begin(); iTroop!=mTroops.end(); iTroop++)
  980. {
  981. if (iTroop->Team()==actor->client->playerTeam)
  982. {
  983. curDist = iTroop->DistanceSq(actor);
  984. if (curDist<MAX_TROOP_JOIN_DIST2 && (!closestDist || curDist<closestDist))
  985. {
  986. // Only Join A Troop If You Can See The Leader
  987. //---------------------------------------------
  988. gi.trace(&trace,
  989. actor->currentOrigin,
  990. actor->mins,
  991. actor->maxs,
  992. iTroop->TroopLeader()->currentOrigin,
  993. actor->s.number,
  994. CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP);
  995. if (!trace.allsolid &&
  996. !trace.startsolid &&
  997. (trace.fraction>=1.0f || trace.entityNum==iTroop->TroopLeader()->s.number))
  998. {
  999. closestDist = curDist;
  1000. closestTroop = iTroop;
  1001. }
  1002. }
  1003. }
  1004. }
  1005. // If Found, Add The Actor To It
  1006. //--------------------------------
  1007. if (closestTroop!=mTroops.end())
  1008. {
  1009. closestTroop->AddActor(actor);
  1010. }
  1011. // If We Couldn't Find One, Create A New Troop
  1012. //---------------------------------------------
  1013. else if (!mTroops.full())
  1014. {
  1015. int nTroopHandle = mTroops.alloc();
  1016. mTroops[nTroopHandle].Initialize(nTroopHandle);
  1017. mTroops[nTroopHandle].AddActor(actor);
  1018. }
  1019. }
  1020. // If This Is A Leader, Then He Is Responsible For Merging Troops
  1021. //----------------------------------------------------------------
  1022. else if (actor->client->leader==actor)
  1023. {
  1024. float curDist = 0;
  1025. float closestDist = 0;
  1026. TTroopPool::iterator closestTroop = mTroops.end();
  1027. for (TTroopPool::iterator iTroop=mTroops.begin(); iTroop!=mTroops.end(); iTroop++)
  1028. {
  1029. curDist = iTroop->DistanceSq(actor);
  1030. if ((curDist<MAX_TROOP_MERGE_DIST2) &&
  1031. (!closestDist || curDist<closestDist) &&
  1032. (mTroops.index_to_handle(iTroop.index())!=actor->NPC->troop))
  1033. {
  1034. closestDist = curDist;
  1035. closestTroop = iTroop;
  1036. }
  1037. }
  1038. if (closestTroop!=mTroops.end())
  1039. {
  1040. int oldTroopNum = actor->NPC->troop;
  1041. mTroops[oldTroopNum].MergeInto(*closestTroop);
  1042. mTroops.free(oldTroopNum);
  1043. }
  1044. }
  1045. }
  1046. ////////////////////////////////////////////////////////////////////////////////////////
  1047. //
  1048. ////////////////////////////////////////////////////////////////////////////////////////
  1049. bool Trooper_UpdateSmackAway(gentity_t* actor, gentity_t* target)
  1050. {
  1051. if (actor->client->ps.legsAnim==BOTH_MELEE1)
  1052. {
  1053. if (TIMER_Done(actor, "Trooper_SmackAway"))
  1054. {
  1055. CVec3 ActorPos(actor->currentOrigin);
  1056. CVec3 ActorToTgt(target->currentOrigin);
  1057. ActorToTgt -= ActorPos;
  1058. float ActorToTgtDist = ActorToTgt.SafeNorm();
  1059. if (ActorToTgtDist<100.0f)
  1060. {
  1061. G_Throw(target, ActorToTgt.v, 200.0f);
  1062. }
  1063. }
  1064. return true;
  1065. }
  1066. return false;
  1067. }
  1068. ////////////////////////////////////////////////////////////////////////////////////////
  1069. //
  1070. ////////////////////////////////////////////////////////////////////////////////////////
  1071. void Trooper_SmackAway(gentity_t* actor, gentity_t* target)
  1072. {
  1073. assert(actor && actor->NPC);
  1074. if (actor->client->ps.legsAnim!=BOTH_MELEE1)
  1075. {
  1076. NPC_SetAnim(actor, SETANIM_BOTH, BOTH_MELEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
  1077. TIMER_Set(actor, "Trooper_SmackAway", actor->client->ps.torsoAnimTimer/4.0f);
  1078. }
  1079. }
  1080. ////////////////////////////////////////////////////////////////////////////////////////
  1081. //
  1082. ////////////////////////////////////////////////////////////////////////////////////////
  1083. bool Trooper_Kneeling(gentity_t* actor)
  1084. {
  1085. return (actor->NPC->aiFlags&NPCAI_KNEEL || actor->client->ps.legsAnim==BOTH_STAND_TO_KNEEL);
  1086. }
  1087. ////////////////////////////////////////////////////////////////////////////////////////
  1088. //
  1089. ////////////////////////////////////////////////////////////////////////////////////////
  1090. void Trooper_KneelDown(gentity_t* actor)
  1091. {
  1092. assert(actor && actor->NPC);
  1093. if (!Trooper_Kneeling(actor) && level.time>actor->NPC->kneelTime)
  1094. {
  1095. NPC_SetAnim(actor, SETANIM_BOTH, BOTH_STAND_TO_KNEEL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
  1096. actor->NPC->aiFlags |= NPCAI_KNEEL;
  1097. actor->NPC->kneelTime = level.time + Q_irand(3000, 6000);
  1098. }
  1099. }
  1100. ////////////////////////////////////////////////////////////////////////////////////////
  1101. //
  1102. ////////////////////////////////////////////////////////////////////////////////////////
  1103. void Trooper_StandUp(gentity_t* actor, bool always=false)
  1104. {
  1105. assert(actor && actor->NPC);
  1106. if (Trooper_Kneeling(actor) && (always || level.time>actor->NPC->kneelTime))
  1107. {
  1108. actor->NPC->aiFlags &= ~NPCAI_KNEEL;
  1109. NPC_SetAnim(actor, SETANIM_BOTH, BOTH_KNEEL_TO_STAND, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
  1110. actor->NPC->kneelTime = level.time + Q_irand(3000, 6000);
  1111. }
  1112. }
  1113. ////////////////////////////////////////////////////////////////////////////////////////
  1114. //
  1115. ////////////////////////////////////////////////////////////////////////////////////////
  1116. int Trooper_CanHitTarget(gentity_t* actor, gentity_t* target, CTroop& troop, float& MuzzleToTargetDistance, CVec3& MuzzleToTarget)
  1117. {
  1118. trace_t tr;
  1119. CVec3 MuzzlePoint(actor->currentOrigin);
  1120. CalcEntitySpot(actor, SPOT_WEAPON, MuzzlePoint.v);
  1121. MuzzleToTarget = troop.TargetVisablePosition();
  1122. MuzzleToTarget -= MuzzlePoint;
  1123. MuzzleToTargetDistance = MuzzleToTarget.SafeNorm();
  1124. CVec3 MuzzleDirection(actor->currentAngles);
  1125. MuzzleDirection.AngToVec();
  1126. // Aiming In The Right Direction?
  1127. //--------------------------------
  1128. if (MuzzleDirection.Dot(MuzzleToTarget)>0.95)
  1129. {
  1130. // Clear Line Of Sight To Target?
  1131. //--------------------------------
  1132. gi.trace(&tr, MuzzlePoint.v, NULL, NULL, troop.TargetVisablePosition().v, actor->s.number, MASK_SHOT);
  1133. if (tr.startsolid || tr.allsolid)
  1134. {
  1135. return ENTITYNUM_NONE;
  1136. }
  1137. if (tr.entityNum==target->s.number || tr.fraction>0.9f)
  1138. {
  1139. return target->s.number;
  1140. }
  1141. return tr.entityNum;
  1142. }
  1143. return ENTITYNUM_NONE;
  1144. }
  1145. ////////////////////////////////////////////////////////////////////////////////////////
  1146. // Run The Per Trooper Update
  1147. ////////////////////////////////////////////////////////////////////////////////////////
  1148. void Trooper_Think(gentity_t* actor)
  1149. {
  1150. gentity_t* target = (actor->NPC->troop)?(mTroops[actor->NPC->troop].TrackingTarget()):(0);
  1151. if (target)
  1152. {
  1153. G_SetEnemy(actor, target);
  1154. CTroop& troop = mTroops[actor->NPC->troop];
  1155. bool AtPos = STEER::Reached(actor, actor->pos1, 10.0f);
  1156. int traceTgt = ENTITYNUM_NONE;
  1157. bool traced = false;
  1158. bool inSmackAway = false;
  1159. float MuzzleToTargetDistance = 0.0f;
  1160. CVec3 MuzzleToTarget;
  1161. if (actor->NPC->combatPoint!=-1)
  1162. {
  1163. traceTgt = Trooper_CanHitTarget(actor, target, troop, MuzzleToTargetDistance, MuzzleToTarget);
  1164. traced = true;
  1165. if (traceTgt==target->s.number)
  1166. {
  1167. AtPos = true;
  1168. }
  1169. }
  1170. // Smack!
  1171. //-------
  1172. if (Trooper_UpdateSmackAway(actor, target))
  1173. {
  1174. traced = true;
  1175. AtPos = true;
  1176. inSmackAway = true;
  1177. }
  1178. if (false)
  1179. {
  1180. CG_DrawEdge(actor->currentOrigin, actor->pos1, EDGE_IMPACT_SAFE);
  1181. }
  1182. // If There, Stop Moving
  1183. //-----------------------
  1184. STEER::Activate(actor);
  1185. {
  1186. gentity_t* fleeFrom = troop.TooCloseToTroopMember(actor);
  1187. // If Too Close To The Leader, Get Out Of His Way
  1188. //------------------------------------------------
  1189. if (fleeFrom)
  1190. {
  1191. STEER::Flee(actor, fleeFrom->currentOrigin, 1.0f);
  1192. AtPos = false;
  1193. }
  1194. // If In Position, Stop Moving
  1195. //-----------------------------
  1196. if (AtPos)
  1197. {
  1198. NAV::ClearPath(actor);
  1199. STEER::Stop(actor);
  1200. }
  1201. // Otherwise, Try To Get To Position
  1202. //-----------------------------------
  1203. else
  1204. {
  1205. Trooper_StandUp(actor, true);
  1206. // If Close Enough, Persue Our Target Directly
  1207. //---------------------------------------------
  1208. bool moveSuccess = STEER::GoTo(NPC, actor->pos1, 10.0f, false);
  1209. // Otherwise
  1210. //-----------
  1211. if (!moveSuccess)
  1212. {
  1213. moveSuccess = NAV::GoTo(NPC, actor->pos1);
  1214. }
  1215. // If No Way To Get To Position, Stay Here
  1216. //-----------------------------------------
  1217. if (!moveSuccess || (level.time - actor->lastMoveTime)>4000)
  1218. {
  1219. AtPos = true;
  1220. }
  1221. }
  1222. }
  1223. STEER::DeActivate(actor, &ucmd);
  1224. // If There And Target Was Recently Visable
  1225. //------------------------------------------
  1226. if (AtPos && (troop.TimeSinceSeenTarget()<1500))
  1227. {
  1228. if (!traced)
  1229. {
  1230. traceTgt = Trooper_CanHitTarget(actor, target, troop, MuzzleToTargetDistance, MuzzleToTarget);
  1231. }
  1232. // Shoot!
  1233. //--------
  1234. if (traceTgt==target->s.number)
  1235. {
  1236. if (actor->s.weapon==WP_BLASTER)
  1237. {
  1238. ucmd.buttons |= BUTTON_ALT_ATTACK;
  1239. }
  1240. WeaponThink(qtrue);
  1241. }
  1242. else if (!inSmackAway)
  1243. {
  1244. // Otherwise, If Kneeling, Get Up!
  1245. //---------------------------------
  1246. if (Trooper_Kneeling(actor))
  1247. {
  1248. Trooper_StandUp(actor);
  1249. }
  1250. // If The Enemy Is Close Enough, Smack Him Away
  1251. //----------------------------------------------
  1252. else if (MuzzleToTargetDistance<40.0f)
  1253. {
  1254. Trooper_SmackAway(actor, target);
  1255. }
  1256. // If We Would Have It A Friend, Ask Him To Kneel
  1257. //------------------------------------------------
  1258. else if (traceTgt!=ENTITYNUM_NONE &&
  1259. traceTgt!=ENTITYNUM_WORLD &&
  1260. g_entities[traceTgt].client &&
  1261. g_entities[traceTgt].NPC &&
  1262. g_entities[traceTgt].client->playerTeam==actor->client->playerTeam &&
  1263. NPC_IsTrooper(&g_entities[traceTgt]) &&
  1264. g_entities[traceTgt].resultspeed<1.0f &&
  1265. !(g_entities[traceTgt].NPC->aiFlags & NPCAI_KNEEL))
  1266. {
  1267. Trooper_KneelDown(&g_entities[traceTgt]);
  1268. }
  1269. }
  1270. // Convert To Angles And Set That As Our Desired Look Direction
  1271. //--------------------------------------------------------------
  1272. if (MuzzleToTargetDistance>100)
  1273. {
  1274. MuzzleToTarget.VecToAng();
  1275. NPCInfo->desiredYaw = MuzzleToTarget[YAW];
  1276. NPCInfo->desiredPitch = MuzzleToTarget[PITCH];
  1277. }
  1278. else
  1279. {
  1280. MuzzleToTarget = troop.TargetVisablePosition();
  1281. MuzzleToTarget.v[2] -= 20.0f; // Aim Lower
  1282. MuzzleToTarget -= actor->currentOrigin;
  1283. MuzzleToTarget.SafeNorm();
  1284. MuzzleToTarget.VecToAng();
  1285. NPCInfo->desiredYaw = MuzzleToTarget[YAW];
  1286. NPCInfo->desiredPitch = MuzzleToTarget[PITCH];
  1287. }
  1288. }
  1289. NPC_UpdateFiringAngles( qtrue, qtrue );
  1290. NPC_UpdateAngles( qtrue, qtrue );
  1291. if (Trooper_Kneeling(actor))
  1292. {
  1293. ucmd.upmove = -127; // Set Crouch Height
  1294. }
  1295. }
  1296. else
  1297. {
  1298. NPC_BSST_Default();
  1299. }
  1300. }
  1301. ////////////////////////////////////////////////////////////////////////////////////////
  1302. /*
  1303. -------------------------
  1304. NPC_BehaviorSet_Trooper
  1305. -------------------------
  1306. */
  1307. ////////////////////////////////////////////////////////////////////////////////////////
  1308. void NPC_BehaviorSet_Trooper( int bState )
  1309. {
  1310. Trooper_UpdateTroop(NPC);
  1311. switch( bState )
  1312. {
  1313. case BS_STAND_GUARD:
  1314. case BS_PATROL:
  1315. case BS_STAND_AND_SHOOT:
  1316. case BS_HUNT_AND_KILL:
  1317. case BS_DEFAULT:
  1318. Trooper_Think(NPC);
  1319. break;
  1320. case BS_INVESTIGATE:
  1321. NPC_BSST_Investigate();
  1322. break;
  1323. case BS_SLEEP:
  1324. NPC_BSST_Sleep();
  1325. break;
  1326. default:
  1327. Trooper_Think(NPC);
  1328. break;
  1329. }
  1330. }
  1331. ////////////////////////////////////////////////////////////////////////////////////////
  1332. // IsTrooper - return true if you want a given actor to use trooper AI
  1333. ////////////////////////////////////////////////////////////////////////////////////////
  1334. bool NPC_IsTrooper(gentity_t* actor)
  1335. {
  1336. return (
  1337. actor &&
  1338. actor->NPC &&
  1339. actor->s.weapon &&
  1340. !!(actor->NPC->scriptFlags&SCF_NO_GROUPS)// &&
  1341. // !(actor->NPC->scriptFlags&SCF_CHASE_ENEMIES)
  1342. );
  1343. }
  1344. void NPC_LeaveTroop(gentity_t* actor)
  1345. {
  1346. assert(actor->NPC->troop);
  1347. int wasInTroop = actor->NPC->troop;
  1348. mTroops[actor->NPC->troop].RemoveActor(actor);
  1349. if (mTroops[wasInTroop].Empty())
  1350. {
  1351. mTroops.free(wasInTroop);
  1352. }
  1353. }