TEAM.CPP 125 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: /CounterStrike/TEAM.CPP 1 3/03/97 10:25a Joe_bostic $ */
  15. /***********************************************************************************************
  16. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : TEAM.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : 12/11/94 *
  26. * *
  27. * Last Update : August 27, 1996 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * TeamClass::AI -- Process team logic. *
  32. * TeamClass::Add -- Adds specified object to team. *
  33. * TeamClass::Assign_Mission_Target -- Sets teams mission target and clears old target *
  34. * TeamClass::Calc_Center -- Determines average location of team members. *
  35. * TeamClass::Can_Add -- Determines if the specified object can be added to team. *
  36. * TeamClass::Control -- Updates control on a member unit. *
  37. * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. *
  38. * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. *
  39. * TeamClass::Coordinate_Do -- Handles the team performing specified mission. *
  40. * TeamClass::Coordinate_Move -- Handles team movement coordination. *
  41. * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). *
  42. * TeamClass::Debug_Dump -- Displays debug information about the team. *
  43. * TeamClass::Detach -- Removes specified target from team tracking. *
  44. * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. *
  45. * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. *
  46. * TeamClass::Init -- Initializes the team objects for scenario preparation. *
  47. * TeamClass::Is_A_Member -- Tests if a unit is a member of a team *
  48. * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map *
  49. * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. *
  50. * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. *
  51. * TeamClass::Remove -- Removes the specified object from the team. *
  52. * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. *
  53. * TeamClass::Suspend_Teams -- Suspends activity for low priority teams *
  54. * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. *
  55. * TeamClass::TMission_Attack -- Perform the team attack mission command. *
  56. * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. *
  57. * TeamClass::TMission_Formation -- Process team formation change command. *
  58. * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of tim*
  59. * TeamClass::TMission_Load -- Tells the team to load onto the transport now. *
  60. * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. *
  61. * TeamClass::TMission_Set_Global -- Performs a set global flag operation. *
  62. * TeamClass::TMission_Spy -- Perform the team spy mission. *
  63. * TeamClass::TMission_Unload -- Tells the team to unload passengers now. *
  64. * TeamClass::TeamClass -- Constructor for the team object type. *
  65. * TeamClass::Took_Damage -- Informs the team when the team member takes damage. *
  66. * TeamClass::operator delete -- Deallocates a team object. *
  67. * TeamClass::operator new -- Allocates a team object. *
  68. * TeamClass::~TeamClass -- Team object destructor. *
  69. * _Is_It_Breathing -- Checks to see if unit is an active team member. *
  70. * _Is_It_Playing -- Determines if unit is active and an initiated team member. *
  71. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  72. #include "function.h"
  73. #include "mission.h"
  74. /***********************************************************************************************
  75. * _Is_It_Breathing -- Checks to see if unit is an active team member. *
  76. * *
  77. * A unit could be a team member, but not be active. Such a case would occur when a *
  78. * reinforcement team is inside a transport. It could also occur if a unit is in the *
  79. * process of dying. Call this routine to ensure that the specified unit is a will and *
  80. * able participant in the team. *
  81. * *
  82. * INPUT: object -- Pointer to the unit/infantry/aircraft that is to be checked. *
  83. * *
  84. * OUTPUT: bool; Is the specified unit active and able to be given commands by the team? *
  85. * *
  86. * WARNINGS: none *
  87. * *
  88. * HISTORY: *
  89. * 03/11/1996 JLB : Created. *
  90. *=============================================================================================*/
  91. static inline bool _Is_It_Breathing(FootClass const * object)
  92. {
  93. /*
  94. ** If the object is not present or appears to be dead, then it
  95. ** certainly isn't an active member of the team.
  96. */
  97. if (object == NULL || !object->IsActive || object->Strength == 0) return(false);
  98. /*
  99. ** If the object is in limbo, then it isn't an active team member either. However, if the
  100. ** scenario init flag is on, then it is probably a reinforcement issue or scenario
  101. ** creation situation. In such a case, the members are considered active because they need to
  102. ** be given special orders and treatment.
  103. */
  104. if (!ScenarioInit && object->IsInLimbo) return(false);
  105. /*
  106. ** Nothing eliminated this object from being considered an active member of the team (i.e.,
  107. ** "breathing"), then return that it is ok.
  108. */
  109. return(true);
  110. }
  111. /***********************************************************************************************
  112. * _Is_It_Playing -- Determines if unit is active and an initiated team member. *
  113. * *
  114. * Use this routine to determine if the specified unit is an active participant of the *
  115. * team. When a unit is first recruited to the team, it must travel to the team's location *
  116. * before it can become an active player. Call this routine to determine if the specified *
  117. * unit can be considered an active player. *
  118. * *
  119. * INPUT: object -- Pointer to the object that is to be checked to see if it is an *
  120. * active player. *
  121. * *
  122. * OUTPUT: bool; Is the specified unit an active, living, initiated member of the team? *
  123. * *
  124. * WARNINGS: none *
  125. * *
  126. * HISTORY: *
  127. * 03/11/1996 JLB : Created. *
  128. *=============================================================================================*/
  129. static inline bool _Is_It_Playing(FootClass const * object)
  130. {
  131. /*
  132. ** If the object is not active, then it certainly can be a participating member of the
  133. ** team.
  134. */
  135. if (!_Is_It_Breathing(object)) return(false);
  136. /*
  137. ** Only members that have been "Initiated" are considered "playing" participants of the
  138. ** team. This results in the team members that are racing to regroup with the team (i.e.,
  139. ** not initiated), will continue to catch up to the team even while the initiated team members
  140. ** carry out their team specific orders.
  141. */
  142. if (!object->IsInitiated && object->What_Am_I() != RTTI_AIRCRAFT) return(false);
  143. /*
  144. ** If it reaches this point, then nothing appears to disqualify the specified object from
  145. ** being considered an active playing member of the team. In this case, return that
  146. ** information.
  147. */
  148. return(true);
  149. }
  150. #ifdef CHEAT_KEYS
  151. /***********************************************************************************************
  152. * TeamClass::Debug_Dump -- Displays debug information about the team. *
  153. * *
  154. * This routine will display information about the team. This is useful for debugging *
  155. * purposes. *
  156. * *
  157. * INPUT: mono -- Pointer to the monochrome screen that the debugging information will *
  158. * be displayed on. *
  159. * *
  160. * OUTPUT: none *
  161. * *
  162. * WARNINGS: none *
  163. * *
  164. * HISTORY: *
  165. * 03/11/1996 JLB : Created. *
  166. *=============================================================================================*/
  167. void TeamClass::Debug_Dump(MonoClass * mono) const
  168. {
  169. mono->Set_Cursor(1, 20);mono->Printf("%8.8s", Class->IniName);
  170. mono->Set_Cursor(10, 20);mono->Printf("%3d", Total);
  171. mono->Set_Cursor(17, 20);mono->Printf("%3d", Quantity[Class->ID]);
  172. if (CurrentMission != -1) {
  173. mono->Set_Cursor(1, 22);
  174. mono->Printf("%-29s", Class->MissionList[CurrentMission].Description(CurrentMission));
  175. }
  176. mono->Set_Cursor(40, 20);mono->Printf("%-10s", FormationName[Formation]);
  177. mono->Set_Cursor(22, 20);mono->Printf("%08X", Zone);
  178. mono->Set_Cursor(31, 20);mono->Printf("%08X", Target);
  179. mono->Fill_Attrib(53, 20, 12, 1, IsUnderStrength ? MonoClass::INVERSE : MonoClass::NORMAL);
  180. mono->Fill_Attrib(53, 21, 12, 1, IsFullStrength ? MonoClass::INVERSE : MonoClass::NORMAL);
  181. mono->Fill_Attrib(53, 22, 12, 1, IsHasBeen ? MonoClass::INVERSE : MonoClass::NORMAL);
  182. mono->Fill_Attrib(66, 20, 12, 1, IsMoving ? MonoClass::INVERSE : MonoClass::NORMAL);
  183. mono->Fill_Attrib(66, 21, 12, 1, IsForcedActive ? MonoClass::INVERSE : MonoClass::NORMAL);
  184. mono->Fill_Attrib(66, 22, 12, 1, IsReforming ? MonoClass::INVERSE : MonoClass::NORMAL);
  185. }
  186. #endif
  187. /***********************************************************************************************
  188. * TeamClass::Init -- Initializes the team objects for scenario preparation. *
  189. * *
  190. * This routine clears out the team object array in preparation for starting a new *
  191. * scenario. *
  192. * *
  193. * INPUT: none *
  194. * *
  195. * OUTPUT: none *
  196. * *
  197. * WARNINGS: none *
  198. * *
  199. * HISTORY: *
  200. * 12/29/1994 JLB : Created. *
  201. *=============================================================================================*/
  202. void TeamClass::Init(void)
  203. {
  204. Teams.Free_All();
  205. }
  206. /***********************************************************************************************
  207. * TeamClass::operator new -- Allocates a team object. *
  208. * *
  209. * This routine will allocate a team object from the team object pool. *
  210. * *
  211. * INPUT: size -- The size of the requested allocation. *
  212. * *
  213. * OUTPUT: Returns with a pointer to the freshly allocated team object. If an allocation *
  214. * could not be made, then NULL is returned. *
  215. * *
  216. * WARNINGS: none *
  217. * *
  218. * HISTORY: *
  219. * 09/21/1995 JLB : Created. *
  220. *=============================================================================================*/
  221. void * TeamClass::operator new(size_t)
  222. {
  223. void * ptr = Teams.Allocate();
  224. if (ptr != NULL) {
  225. ((TeamClass *)ptr)->Set_Active();
  226. }
  227. return(ptr);
  228. }
  229. /***********************************************************************************************
  230. * TeamClass::operator delete -- Deallocates a team object. *
  231. * *
  232. * This routine will return a team object to the team object pool. *
  233. * *
  234. * INPUT: ptr -- Pointer to the team object to deallocate. *
  235. * *
  236. * OUTPUT: none *
  237. * *
  238. * WARNINGS: none *
  239. * *
  240. * HISTORY: *
  241. * 09/21/1995 JLB : Created. *
  242. *=============================================================================================*/
  243. void TeamClass::operator delete(void * ptr)
  244. {
  245. if (ptr != NULL) {
  246. ((TeamClass *)ptr)->IsActive = false;
  247. }
  248. Teams.Free((TeamClass *)ptr);
  249. }
  250. /***********************************************************************************************
  251. * TeamClass::~TeamClass -- Team object destructor. *
  252. * *
  253. * This routine is called when a team object is destroyed. It handles updating the total *
  254. * number count for this team object type. *
  255. * *
  256. * INPUT: none *
  257. * *
  258. * OUTPUT: none *
  259. * *
  260. * WARNINGS: none *
  261. * *
  262. * HISTORY: *
  263. * 09/21/1995 JLB : Created. *
  264. * 07/04/1996 JLB : Keeps trigger if trigger still attached to objects. *
  265. *=============================================================================================*/
  266. TeamClass::~TeamClass(void)
  267. {
  268. if (GameActive && Class.Is_Valid()) {
  269. while (Member != NULL) {
  270. Remove(Member);
  271. }
  272. Class->Number--;
  273. /*
  274. ** When the team dies, any trigger associated with it, dies as well. This will only occur
  275. ** if there are no other objects linked to this trigger. Only player reinforcement
  276. ** members that broke off of the team earlier will have this occur.
  277. */
  278. if (Trigger.Is_Valid()) {
  279. if (Trigger->AttachCount == 0) {
  280. delete (TriggerClass *)Trigger;
  281. }
  282. Trigger = NULL;
  283. }
  284. }
  285. }
  286. /***********************************************************************************************
  287. * TeamClass::TeamClass -- Constructor for the team object type. *
  288. * *
  289. * This routine is called when the team object is created. *
  290. * *
  291. * INPUT: type -- Pointer to the team type to make this team object from. *
  292. * *
  293. * owner -- The owner of this team. *
  294. * *
  295. * OUTPUT: none *
  296. * *
  297. * WARNINGS: none *
  298. * *
  299. * HISTORY: *
  300. * 09/21/1995 JLB : Created. *
  301. *=============================================================================================*/
  302. TeamClass::TeamClass(TeamTypeClass const * type, HouseClass * owner) :
  303. AbstractClass(RTTI_TEAM, Teams.ID(this)),
  304. Class((TeamTypeClass *)type),
  305. House(owner),
  306. IsForcedActive(false),
  307. IsHasBeen(false),
  308. IsFullStrength(false),
  309. IsUnderStrength(true),
  310. IsReforming(false),
  311. IsLagging(false),
  312. IsAltered(true),
  313. JustAltered(false),
  314. IsMoving(false),
  315. IsNextMission(true),
  316. IsLeaveMap(false),
  317. Suspended(false),
  318. Trigger(NULL),
  319. Zone(TARGET_NONE),
  320. ClosestMember(TARGET_NONE),
  321. MissionTarget(TARGET_NONE),
  322. Target(TARGET_NONE),
  323. Total(0),
  324. Risk(0),
  325. Formation(FORMATION_NONE),
  326. SuspendTimer(0),
  327. CurrentMission(-1),
  328. TimeOut(0),
  329. Member(0)
  330. {
  331. assert(Class);
  332. assert(Class->IsActive);
  333. assert(Class->ClassCount > 0);
  334. if (owner == NULL) {
  335. House = HouseClass::As_Pointer(Class->House);
  336. }
  337. memset(Quantity, 0, sizeof(Quantity));
  338. if (Class->Origin != -1) {
  339. Zone = ::As_Target(Scen.Waypoint[Class->Origin]);
  340. }
  341. Class->Number++;
  342. /*
  343. ** If there is a trigger tightly associated with this team, then
  344. ** create an instance of that trigger and attach it to the team.
  345. */
  346. if (Class->Trigger.Is_Valid()) {
  347. Trigger = new TriggerClass(Class->Trigger);
  348. }
  349. }
  350. /***************************************************************************
  351. * TeamClass::Assign_Mission_Target -- Sets mission target and clears old *
  352. * *
  353. * INPUT: *
  354. * *
  355. * OUTPUT: *
  356. * *
  357. * WARNINGS: *
  358. * *
  359. * HISTORY: *
  360. * 05/16/1995 PWG : Created. *
  361. *=========================================================================*/
  362. void TeamClass::Assign_Mission_Target(TARGET new_target)
  363. {
  364. assert(IsActive);
  365. assert(Teams.ID(this) == ID);
  366. /*
  367. ** First go through and find anyone who is currently targeting
  368. ** the old mission target and clear their Tarcom.
  369. */
  370. FootClass * unit = Member;
  371. if (MissionTarget != TARGET_NONE) {
  372. while (unit != NULL) {
  373. bool tar = (unit->TarCom == MissionTarget);
  374. bool nav = (unit->NavCom == MissionTarget);
  375. if (tar || nav) {
  376. /*
  377. ** If the unit was doing something related to the team mission
  378. ** then we kick him into guard mode so that he is easy to change
  379. ** missions for.
  380. */
  381. unit->Assign_Mission(MISSION_GUARD);
  382. /*
  383. ** If the unit's tarcom is set to the old mission target, then
  384. ** clear it, so that it will be reset by whatever happens next.
  385. */
  386. if (nav) {
  387. unit->Assign_Destination(TARGET_NONE);
  388. }
  389. /*
  390. ** If the unit's navcom is set to the old mission target, then
  391. ** clear it, so that it will be reset by whatever happens next.
  392. */
  393. if (tar) {
  394. unit->Assign_Target(TARGET_NONE);
  395. }
  396. }
  397. unit = unit->Member;
  398. }
  399. }
  400. /*
  401. ** If there is not currently an override on the current mission target
  402. ** then assign both MissionTarget and Target to the new target. If
  403. ** there is an override, allow the team to keep fighting the override but
  404. ** make sure they pick up on the new mission when they are ready.
  405. */
  406. if (Target == MissionTarget || !Target_Legal(Target)) {
  407. MissionTarget = Target = new_target;
  408. } else {
  409. MissionTarget = new_target;
  410. }
  411. }
  412. /***********************************************************************************************
  413. * TeamClass::AI -- Process team logic. *
  414. * *
  415. * General purpose team logic is handled by this routine. It should be called once per *
  416. * active team per game tick. This routine handles recruitment and assigning orders to *
  417. * member units. *
  418. * *
  419. * INPUT: none *
  420. * *
  421. * OUTPUT: none *
  422. * *
  423. * WARNINGS: none *
  424. * *
  425. * HISTORY: *
  426. * 12/29/1994 JLB : Created. *
  427. * 01/06/1995 JLB : Choreographed gesture. *
  428. *=============================================================================================*/
  429. void TeamClass::AI(void)
  430. {
  431. assert(IsActive);
  432. assert(Teams.ID(this) == ID);
  433. int desired = 0;
  434. int old_under = IsUnderStrength;
  435. int old_full = IsFullStrength;
  436. /*
  437. ** If the team has been suspended then we need to check if it's time for
  438. ** us to reactivate the team. If not, no team logic will be processed
  439. ** for this team.
  440. */
  441. if (Suspended) {
  442. if (SuspendTimer != 0) {
  443. return;
  444. }
  445. Suspended = false;
  446. }
  447. /*
  448. ** If this team senses that its composition has been altered, then it should
  449. ** recalculate the under strength and full strength flags.
  450. */
  451. if (IsAltered) {
  452. /*
  453. ** Figure out the total number of objects that this team type requires.
  454. */
  455. for (int index = 0; index < Class->ClassCount; index++) {
  456. desired += Class->Members[index].Quantity;
  457. }
  458. assert(desired != 0);
  459. if (Total) {
  460. IsFullStrength = (Total == desired);
  461. if (IsFullStrength) {
  462. IsHasBeen = true;
  463. }
  464. /*
  465. ** Reinforceable teams will revert (or snap out of) the under strength
  466. ** mode when the members transition the magic 1/3 strength threshold.
  467. */
  468. if (Class->IsReinforcable) {
  469. if (desired > 2) {
  470. IsUnderStrength = (Total <= desired / 3);
  471. } else {
  472. IsUnderStrength = (Total < desired);
  473. }
  474. } else {
  475. /*
  476. ** Teams that are not flagged as reinforceable are never considered under
  477. ** strength if the team has already started its main mission. This
  478. ** ensures that once the team has started, it won't dally to pick up
  479. ** new members.
  480. */
  481. IsUnderStrength = !IsHasBeen;
  482. }
  483. IsAltered = JustAltered = false;
  484. } else {
  485. IsUnderStrength = true;
  486. IsFullStrength = false;
  487. Zone = TARGET_NONE;
  488. /*
  489. ** A team that exists on the player's side is automatically destroyed
  490. ** when there are no team members left. This team was created as a
  491. ** result of reinforcement logic and no longer needs to exist when there
  492. ** are no more team members.
  493. */
  494. if (IsHasBeen || Session.Type != GAME_NORMAL) {
  495. /*
  496. ** If this team had no members (i.e., the team object wasn't terminated by some
  497. ** outside means), then pass through the logic triggers to see if one that
  498. ** depends on this team leaving the map should be sprung.
  499. */
  500. if (IsLeaveMap) {
  501. for (int index = 0; index < LogicTriggers.Count(); index++) {
  502. TriggerClass * trig = LogicTriggers[index];
  503. if (trig->Spring(TEVENT_LEAVES_MAP)) {
  504. index--;
  505. if (LogicTriggers.Count() == 0) break;
  506. }
  507. }
  508. }
  509. delete this;
  510. return;
  511. }
  512. }
  513. /*
  514. ** If the team has gone from under strength to no longer under
  515. ** strength than the team needs to reform.
  516. */
  517. if (old_under != IsUnderStrength) {
  518. IsReforming = true;
  519. }
  520. }
  521. /*
  522. ** If the team is under strength, then flag it to regroup.
  523. */
  524. if (IsMoving && IsUnderStrength) {
  525. IsMoving = false;
  526. CurrentMission = -1;
  527. if (Total) {
  528. Calc_Center(Zone, ClosestMember);
  529. /*
  530. ** When a team is badly damaged and needs to regroup it should
  531. ** pick a friendly building to go and regroup at. Its first preference
  532. ** should be somewhere near repair factory. If it cannot find a repair
  533. ** factory then it should pick another structure that is friendly to
  534. ** its side.
  535. */
  536. CELL dest = As_Cell(Zone);
  537. int max = 0x7FFFFFFF;
  538. for (int index = 0; index < Buildings.Count(); index++) {
  539. BuildingClass * b = Buildings.Ptr(index);
  540. if (b != NULL && !b->IsInLimbo && b->House == House && b->Class->PrimaryWeapon == NULL) {
  541. CELL cell = Coord_Cell(b->Center_Coord());
  542. int dist = ::Distance(b->Center_Coord(), As_Coord(Zone)) * (Map.Cell_Threat(cell, House->Class->House) + 1);
  543. if (*b == STRUCT_REPAIR) {
  544. dist /= 2;
  545. }
  546. if (dist < max) {
  547. cell = Fetch_A_Leader()->Safety_Point(As_Cell(Zone), cell, 2, 4);
  548. // cell = Member->Safety_Point(As_Cell(Zone), cell, 2, 4);
  549. if (cell != -1) {
  550. max = dist;
  551. dest = cell;
  552. }
  553. }
  554. }
  555. }
  556. // Should calculate a regroup location.
  557. Target = ::As_Target(dest);
  558. Coordinate_Move();
  559. return;
  560. } else {
  561. Zone = TARGET_NONE;
  562. }
  563. }
  564. /*
  565. ** Flag this team into action when it gets to full strength. Human owned teams are only
  566. ** used for reinforcement purposes -- always consider them at full strength.
  567. */
  568. if (!IsMoving && (IsFullStrength || IsForcedActive)) {
  569. IsMoving = true;
  570. IsHasBeen = true;
  571. IsUnderStrength = false;
  572. /*
  573. ** Infantry can do a gesture when they start their mission. Pick
  574. ** a gesture at random.
  575. */
  576. FootClass * techno = Member;
  577. DoType doaction = Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2;
  578. while (techno) {
  579. if (_Is_It_Breathing(techno) && techno->What_Am_I() == RTTI_INFANTRY) {
  580. ((InfantryClass *)techno)->Do_Action(doaction);
  581. }
  582. if (IsReforming || IsForcedActive) {
  583. techno->IsInitiated = true;
  584. }
  585. techno = techno->Member;
  586. }
  587. CurrentMission = -1;
  588. IsNextMission = true;
  589. // IsForcedActive = false;
  590. }
  591. /*
  592. ** If the team is moving or if there is no center position for
  593. ** the team, then the center position must be recalculated.
  594. */
  595. if (IsReforming || IsMoving || Zone == TARGET_NONE || ClosestMember == TARGET_NONE) {
  596. Calc_Center(Zone, ClosestMember);
  597. }
  598. /*
  599. ** Try to recruit members if there is room to do so for this team.
  600. ** Only try to recruit members for a non player controlled team.
  601. */
  602. if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((!House->IsHuman || !IsHasBeen) && Session.Type == GAME_NORMAL)) {
  603. // if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((/*!House->IsHuman ||*/ !IsHasBeen) && Session.Type == GAME_NORMAL)) {
  604. for (int index = 0; index < Class->ClassCount; index++) {
  605. if (Quantity[index] < Class->Members[index].Quantity) {
  606. Recruit(index);
  607. }
  608. }
  609. }
  610. /*
  611. ** If there are no members of the team and the team has reached
  612. ** full strength at one time, then delete the team.
  613. */
  614. if (Member == NULL && IsHasBeen) {
  615. /*
  616. ** If this team had no members (i.e., the team object wasn't terminated by some
  617. ** outside means), then pass through the logic triggers to see if one that
  618. ** depends on this team leaving the map should be sprung.
  619. */
  620. if (IsLeaveMap) {
  621. for (int index = 0; index < LogicTriggers.Count(); index++) {
  622. TriggerClass * trig = LogicTriggers[index];
  623. if (trig->Spring(TEVENT_LEAVES_MAP)) {
  624. index--;
  625. if (LogicTriggers.Count() == 0) break;
  626. }
  627. }
  628. }
  629. delete this;
  630. return;
  631. }
  632. /*
  633. ** If the mission should be advanced to the next entry, then do so at
  634. ** this time. Various events may cause the mission to advance, but it
  635. ** all boils down to the following change-mission code.
  636. */
  637. if (IsMoving && !IsReforming && IsNextMission) {
  638. IsNextMission = false;
  639. CurrentMission++;
  640. if (CurrentMission < Class->MissionCount) {
  641. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  642. TimeOut = mission->Data.Value * (TICKS_PER_MINUTE/10);
  643. Target = TARGET_NONE;
  644. switch (mission->Mission) {
  645. case TMISSION_MOVECELL:
  646. Assign_Mission_Target(::As_Target((CELL)(mission->Data.Value)));
  647. break;
  648. case TMISSION_MOVE:
  649. if ((unsigned)mission->Data.Value < WAYPT_COUNT && Member != NULL) {
  650. FootClass * leader = Fetch_A_Leader();
  651. CELL movecell = Scen.Waypoint[mission->Data.Value];
  652. if (!Is_Leaving_Map()) {
  653. if (leader->Can_Enter_Cell(movecell) != MOVE_OK) {
  654. movecell = Map.Nearby_Location(movecell, leader->Techno_Type_Class()->Speed);
  655. }
  656. }
  657. Assign_Mission_Target(::As_Target(movecell));
  658. Target = ::As_Target(movecell);
  659. }
  660. break;
  661. case TMISSION_ATT_WAYPT:
  662. case TMISSION_PATROL:
  663. case TMISSION_SPY:
  664. if ((unsigned)mission->Data.Value < WAYPT_COUNT) {
  665. Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value]));
  666. }
  667. break;
  668. case TMISSION_ATTACKTARCOM:
  669. Assign_Mission_Target(mission->Data.Value);
  670. break;
  671. case TMISSION_UNLOAD:
  672. default:
  673. Assign_Mission_Target(TARGET_NONE);
  674. break;
  675. }
  676. } else {
  677. delete this;
  678. return;
  679. }
  680. }
  681. /*
  682. ** Perform mission of the team. This depends on the mission list.
  683. */
  684. if (Member != NULL && IsMoving && !IsReforming && !IsUnderStrength) {
  685. /*
  686. ** If the current Target has been dealt with but the mission target
  687. ** has not, then the current target needs to be reset to the mission
  688. ** target.
  689. */
  690. if (!Target_Legal(Target)) {
  691. Target = MissionTarget;
  692. }
  693. /*
  694. ** If the current mission is one that times out, then check for
  695. ** this case. If it has timed out then advance to the next
  696. ** mission in the list or disband the team.
  697. */
  698. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  699. // FootClass * member = Member;
  700. switch (mission->Mission) {
  701. case TMISSION_PATROL:
  702. TMission_Patrol();
  703. break;
  704. case TMISSION_FORMATION:
  705. TMission_Formation();
  706. break;
  707. case TMISSION_ATTACKTARCOM:
  708. case TMISSION_ATTACK:
  709. TMission_Attack();
  710. break;
  711. case TMISSION_LOAD:
  712. TMission_Load();
  713. break;
  714. case TMISSION_DEPLOY:
  715. TMission_Deploy();
  716. break;
  717. case TMISSION_UNLOAD:
  718. TMission_Unload();
  719. break;
  720. case TMISSION_MOVE:
  721. case TMISSION_MOVECELL:
  722. Coordinate_Move();
  723. break;
  724. /*
  725. ** All members of this team become invulnerable as if by magic.
  726. */
  727. case TMISSION_INVULNERABLE:
  728. TMission_Invulnerable();
  729. break;
  730. case TMISSION_GUARD:
  731. Coordinate_Regroup();
  732. break;
  733. case TMISSION_DO:
  734. Coordinate_Do();
  735. break;
  736. case TMISSION_SET_GLOBAL:
  737. TMission_Set_Global();
  738. break;
  739. case TMISSION_ATT_WAYPT:
  740. if (!Target_Legal(MissionTarget)) {
  741. Assign_Mission_Target(TARGET_NONE);
  742. IsNextMission = true;
  743. } else {
  744. Coordinate_Attack();
  745. }
  746. break;
  747. case TMISSION_SPY:
  748. TMission_Spy();
  749. break;
  750. case TMISSION_HOUND_DOG:
  751. TMission_Follow();
  752. break;
  753. case TMISSION_LOOP:
  754. TMission_Loop();
  755. break;
  756. }
  757. /*
  758. ** Check for mission time out condition. If the mission does in fact time out, then
  759. ** flag it so that the team mission list will advance.
  760. */
  761. switch (mission->Mission) {
  762. // case TMISSION_UNLOAD:
  763. case TMISSION_GUARD:
  764. if (TimeOut == 0) {
  765. IsNextMission = true;
  766. }
  767. break;
  768. }
  769. } else {
  770. if (IsMoving) {
  771. IsReforming = !Coordinate_Regroup();
  772. } else {
  773. Coordinate_Move();
  774. }
  775. }
  776. }
  777. /***********************************************************************************************
  778. * TeamClass::Add -- Adds specified object to team. *
  779. * *
  780. * Use this routine to add the specified object to the team. The object is checked to make *
  781. * sure that it can be assigned to the team. If it can't, then the object will be left *
  782. * alone and false will be returned. *
  783. * *
  784. * INPUT: obj -- Pointer to the object that is to be assigned to this team. *
  785. * *
  786. * OUTPUT: bool; Was the unit added to the team? *
  787. * *
  788. * WARNINGS: none *
  789. * *
  790. * HISTORY: *
  791. * 12/29/1994 JLB : Created. *
  792. * 01/02/1995 JLB : Initiation flag setup. *
  793. * 08/06/1995 JLB : Allows member stealing from lesser priority teams. *
  794. *=============================================================================================*/
  795. bool TeamClass::Add(FootClass * obj)
  796. {
  797. assert(IsActive);
  798. assert(Teams.ID(this) == ID);
  799. if (!obj) return(false);
  800. int typeindex;
  801. if (!Can_Add(obj, typeindex)) return(false);
  802. /*
  803. ** All is ok to add the object to the team, but if the object is already part of
  804. ** another team, then it must be removed from that team first.
  805. */
  806. if (obj->Team.Is_Valid()) {
  807. obj->Team->Remove(obj);
  808. }
  809. /*
  810. ** Actually add the object to the team.
  811. */
  812. Quantity[typeindex]++;
  813. obj->IsInitiated = (Member == NULL);
  814. obj->Member = Member;
  815. Member = obj;
  816. obj->Team = this;
  817. /*
  818. ** If a common trigger is designated for this team type, then attach the
  819. ** trigger to this team member.
  820. */
  821. if (Trigger.Is_Valid()) {
  822. obj->Attach_Trigger(Trigger);
  823. }
  824. Total++;
  825. Risk += obj->Risk();
  826. if (Zone == TARGET_NONE) {
  827. Calc_Center(Zone, ClosestMember);
  828. }
  829. /*
  830. ** Return with success, since the object was added to the team.
  831. */
  832. IsAltered = JustAltered = true;
  833. return(true);
  834. }
  835. /***********************************************************************************************
  836. * TeamClass::Can_Add -- Determines if the specified object can be added to team. *
  837. * *
  838. * This routine will examine the team and determine if the specified object can be *
  839. * properly added to this team. This is a security check to filter out those objects that *
  840. * should not be added because of conflicting priorities or other restrictions. *
  841. * *
  842. * INPUT: obj -- Pointer to the candidate object that is being checked for legal *
  843. * adding to this team. *
  844. * *
  845. * typeindex-- The class index number (according to the team type's class array) that *
  846. * the candidate object is classified as. The routine processes much *
  847. * faster if you can provide this information, but if you don't, the *
  848. * routine will figure it out. *
  849. * *
  850. * OUTPUT: bool; Can the specified object be added to this team? *
  851. * *
  852. * WARNINGS: none *
  853. * *
  854. * HISTORY: *
  855. * 02/27/1996 JLB : Created. *
  856. *=============================================================================================*/
  857. bool TeamClass::Can_Add(FootClass * obj, int & typeindex) const
  858. {
  859. assert(IsActive);
  860. assert(Teams.ID(this) == ID);
  861. /*
  862. ** Trying to add the team member to itself is an error condition.
  863. */
  864. if (obj->Team == this) {
  865. return(false);
  866. }
  867. /*
  868. ** The object must be active, a member of this house. A special dispensation is given to
  869. ** units that are in radio contact. It is presumed that they are very busy and should
  870. ** not be disturbed.
  871. */
  872. if (!_Is_It_Breathing(obj) || obj->In_Radio_Contact() || obj->House != House) {
  873. return(false);
  874. }
  875. /*
  876. ** If the object is doing some mission that precludes it from joining
  877. ** a team then don't add it.
  878. */
  879. if (obj->Mission != MISSION_NONE && !MissionClass::Is_Recruitable_Mission(obj->Mission)) {
  880. return(false);
  881. }
  882. /*
  883. ** If this object is part of another team, then check to make sure that it
  884. ** is permitted to leave the other team in order to join this one. If not,
  885. ** then no further processing is allowed -- bail.
  886. */
  887. if (obj->Team.Is_Valid() && (obj->Team->Class->RecruitPriority >= Class->RecruitPriority)) {
  888. return(false);
  889. }
  890. /*
  891. ** Aircraft that have no ammo for their weapons cannot be recruited into a team.
  892. */
  893. if (obj->What_Am_I() == RTTI_AIRCRAFT && obj->Techno_Type_Class()->PrimaryWeapon != NULL && !obj->Ammo) {
  894. return(false);
  895. }
  896. /*
  897. ** Search for the exact member index that the candidate object matches.
  898. ** If no match could be found, then adding the object to the team cannot
  899. ** occur.
  900. */
  901. for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) {
  902. if (Class->Members[typeindex].Class == &obj->Class_Of()) {
  903. break;
  904. }
  905. }
  906. if (typeindex == Class->ClassCount) {
  907. return(false);
  908. }
  909. /*
  910. ** If the team is already full of this type, then adding the object is not allowed.
  911. ** Return with a failure flag in this case.
  912. */
  913. if (Quantity[typeindex] >= Class->Members[typeindex].Quantity) {
  914. return(false);
  915. }
  916. return(true);
  917. }
  918. /***********************************************************************************************
  919. * TeamClass::Remove -- Removes the specified object from the team. *
  920. * *
  921. * Use this routine to remove an object from a team. Objects removed from the team are *
  922. * then available to be recruited by other teams, or even by the same team at a later time. *
  923. * *
  924. * INPUT: obj -- Pointer to the object that is to be removed from this team. *
  925. * *
  926. * typeindex-- Optional index of where this object type is specified in the type *
  927. * type class. This parameter can be omitted. It only serves to make *
  928. * the removal process faster. *
  929. * *
  930. * OUTPUT: bool; Was the object removed from this team? *
  931. * *
  932. * WARNINGS: none *
  933. * *
  934. * HISTORY: *
  935. * 12/29/1994 JLB : Created. *
  936. * 01/02/1995 JLB : Initiation tracking and team captain selection. *
  937. *=============================================================================================*/
  938. bool TeamClass::Remove(FootClass * obj, int typeindex)
  939. {
  940. assert(IsActive);
  941. assert(Teams.ID(this) == ID);
  942. /*
  943. ** Make sure that the object is in fact a member of this team. If not, then it can't
  944. ** be removed. Return success because the end result is the same.
  945. */
  946. if (this != obj->Team) {
  947. return(true);
  948. }
  949. /*
  950. ** Detach the common trigger for this team type. Only current and active members of the
  951. ** team have that trigger attached. The exception is for player team members that
  952. ** get removed from a reinforcement team.
  953. */
  954. if (obj->Trigger == Trigger && !obj->House->IsPlayerControl) {
  955. obj->Attach_Trigger(NULL);
  956. }
  957. /*
  958. ** If the proper team index was not provided, then find it in the type type class. The
  959. ** team type class will not be set if the appropriate type could not be found
  960. ** for this object. This indicates that the object was illegally added. Continue to
  961. ** process however, since removing this object from the team is a good idea.
  962. */
  963. if (typeindex == -1) {
  964. for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) {
  965. if (Class->Members[typeindex].Class == &obj->Class_Of()) {
  966. break;
  967. }
  968. }
  969. }
  970. /*
  971. ** Decrement the counter for the team class. There is now one less of this object type.
  972. */
  973. if (typeindex < Class->ClassCount) {
  974. Quantity[typeindex]--;
  975. }
  976. /*
  977. ** Actually remove the object from the team. Scan through the team members
  978. ** looking for the one that matches the one specified. If it is found, it
  979. ** is unlinked from the member chain. During this scan, a check is made to
  980. ** ensure that at least one remaining member is still initiated. If not, then
  981. ** a new team captain must be chosen.
  982. */
  983. bool initiated = false;
  984. FootClass * prev = 0;
  985. FootClass * curr = Member;
  986. bool found = false;
  987. while (curr != NULL && (!found || !initiated)) {
  988. if (curr == obj) {
  989. if (prev != NULL) {
  990. prev->Member = curr->Member;
  991. } else {
  992. Member = curr->Member;
  993. }
  994. FootClass * temp = curr->Member;
  995. curr->Member = 0;
  996. curr->Team = 0;
  997. curr->SuspendedMission = MISSION_NONE;
  998. curr->SuspendedNavCom = TARGET_NONE;
  999. curr->SuspendedTarCom = TARGET_NONE;
  1000. curr = temp;
  1001. Total--;
  1002. found = true;
  1003. Risk -= obj->Risk();
  1004. continue;
  1005. }
  1006. /*
  1007. ** If this (remaining) member is initiated, then keep a record of this.
  1008. */
  1009. initiated |= curr->IsInitiated;
  1010. prev = curr;
  1011. curr = curr->Member;
  1012. }
  1013. /*
  1014. ** A unit that breaks off of a team will enter idle mode.
  1015. */
  1016. obj->Enter_Idle_Mode();
  1017. /*
  1018. ** If, after removing the team member, there are no initiated members left
  1019. ** in the team, then just make the first remaining member of the team the
  1020. ** team captain. Mark the center location of the team as invalid so that
  1021. ** it will be centered around the captain.
  1022. */
  1023. if (!initiated && Member != NULL) {
  1024. Member->IsInitiated = true;
  1025. Zone = TARGET_NONE;
  1026. }
  1027. /*
  1028. ** Must record that the team composition has changed. At the next opportunity,
  1029. ** the team members will be counted and appropriate AI adjustments made.
  1030. */
  1031. IsAltered = JustAltered = true;
  1032. return(true);
  1033. }
  1034. /***********************************************************************************************
  1035. * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. *
  1036. * *
  1037. * This routine will take the given index ID and scan for available objects of that type *
  1038. * to recruit to the team. Recruiting will continue until that object type has either *
  1039. * been exhausted or if the team's requirement for that type has been filled. *
  1040. * *
  1041. * INPUT: typeindex -- The index for the object type to recruit. The index is used to *
  1042. * look into the type type's array of object types that make up this *
  1043. * team. *
  1044. * *
  1045. * OUTPUT: Returns with the number of objects added to this team. *
  1046. * *
  1047. * WARNINGS: none *
  1048. * *
  1049. * HISTORY: *
  1050. * 12/29/1994 JLB : Created. *
  1051. * 04/10/1995 JLB : Scans for units too. *
  1052. *=============================================================================================*/
  1053. int TeamClass::Recruit(int typeindex)
  1054. {
  1055. assert(IsActive);
  1056. assert(Teams.ID(this) == ID);
  1057. COORDINATE center = As_Coord(Zone);
  1058. if (Class->Origin != -1) {
  1059. center = Cell_Coord(Scen.Waypoint[Class->Origin]);
  1060. }
  1061. int added = 0; // Total number added to team.
  1062. /*
  1063. ** Quick check to see if recruiting is really allowed for this index or not.
  1064. */
  1065. if (Class->Members[typeindex].Quantity > Quantity[typeindex]) {
  1066. switch (Class->Members[typeindex].Class->What_Am_I()) {
  1067. /*
  1068. ** For infantry objects, sweep through the infantry in the game looking for
  1069. ** ones owned by the house that owns the team. When found, try to add.
  1070. */
  1071. case RTTI_INFANTRYTYPE:
  1072. case RTTI_INFANTRY:
  1073. {
  1074. InfantryClass * best = 0;
  1075. int bestdist = -1;
  1076. for (int index = 0; index < Infantry.Count(); index++) {
  1077. InfantryClass * infantry = Infantry.Ptr(index);
  1078. int d = infantry->Distance(center);
  1079. if ((d < bestdist || bestdist == -1) && Can_Add(infantry, typeindex)) {
  1080. best = infantry;
  1081. bestdist = d;
  1082. }
  1083. }
  1084. if (best) {
  1085. best->Assign_Target(TARGET_NONE);
  1086. Add(best);
  1087. added++;
  1088. }
  1089. }
  1090. break;
  1091. case RTTI_AIRCRAFTTYPE:
  1092. case RTTI_AIRCRAFT:
  1093. {
  1094. AircraftClass * best = 0;
  1095. int bestdist = -1;
  1096. for (int index = 0; index < Aircraft.Count(); index++) {
  1097. AircraftClass * aircraft = Aircraft.Ptr(index);
  1098. int d = aircraft->Distance(center);
  1099. if ((d < bestdist || bestdist == -1) && Can_Add(aircraft, typeindex)) {
  1100. best = aircraft;
  1101. bestdist = d;
  1102. }
  1103. }
  1104. if (best) {
  1105. best->Assign_Target(TARGET_NONE);
  1106. Add(best);
  1107. added++;
  1108. }
  1109. }
  1110. break;
  1111. case RTTI_UNITTYPE:
  1112. case RTTI_UNIT:
  1113. {
  1114. UnitClass * best = 0;
  1115. int bestdist = -1;
  1116. for (int index = 0; index < Units.Count(); index++) {
  1117. UnitClass * unit = Units.Ptr(index);
  1118. int d = unit->Distance(center);
  1119. if (unit->House == House && unit->Class == Class->Members[typeindex].Class) {
  1120. if ((d < bestdist || bestdist == -1) && Can_Add(unit, typeindex)) {
  1121. best = unit;
  1122. bestdist = d;
  1123. }
  1124. }
  1125. if (best) {
  1126. best->Assign_Target(TARGET_NONE);
  1127. Add(best);
  1128. added++;
  1129. /*
  1130. ** If a transport is added to the team, the occupants
  1131. ** are added by default.
  1132. */
  1133. FootClass * f = best->Attached_Object();
  1134. while (f) {
  1135. Add(f);
  1136. f = (FootClass *)(ObjectClass *)f->Next;
  1137. }
  1138. }
  1139. }
  1140. }
  1141. break;
  1142. case RTTI_VESSELTYPE:
  1143. case RTTI_VESSEL:
  1144. {
  1145. VesselClass * best = 0;
  1146. int bestdist = -1;
  1147. for (int index = 0; index < Vessels.Count(); index++) {
  1148. VesselClass * vessel = Vessels.Ptr(index);
  1149. int d = vessel->Distance(center);
  1150. if (vessel->House == House && vessel->Class == Class->Members[typeindex].Class) {
  1151. if ((d < bestdist || bestdist == -1) && Can_Add(vessel, typeindex)) {
  1152. best = vessel;
  1153. bestdist = d;
  1154. }
  1155. }
  1156. if (best) {
  1157. best->Assign_Target(TARGET_NONE);
  1158. Add(best);
  1159. added++;
  1160. /*
  1161. ** If a transport is added to the team, the occupants
  1162. ** are added by default.
  1163. */
  1164. FootClass * f = best->Attached_Object();
  1165. while (f) {
  1166. Add(f);
  1167. f = (FootClass *)(ObjectClass *)f->Next;
  1168. }
  1169. }
  1170. }
  1171. }
  1172. break;
  1173. }
  1174. }
  1175. return(added);
  1176. }
  1177. /***********************************************************************************************
  1178. * TeamClass::Detach -- Removes specified target from team tracking. *
  1179. * *
  1180. * When a target object is about to be removed from the game (e.g., it was killed), then *
  1181. * any team that is looking at that target must abort from that target. *
  1182. * *
  1183. * INPUT: target -- The target object that is going to be removed from the game. *
  1184. * *
  1185. * all -- Is the target going away for good as opposed to just cloaking/hiding? *
  1186. * *
  1187. * OUTPUT: none *
  1188. * *
  1189. * WARNINGS: none *
  1190. * *
  1191. * HISTORY: *
  1192. * 12/29/1994 JLB : Created. *
  1193. *=============================================================================================*/
  1194. void TeamClass::Detach(TARGET target, bool )
  1195. {
  1196. assert(IsActive);
  1197. assert(Teams.ID(this) == ID);
  1198. /*
  1199. ** If the target to detach matches the target of this team, then remove
  1200. ** the target from this team's Tar/Nav com and let the chips fall
  1201. ** where they may.
  1202. */
  1203. if (Target == target) {
  1204. Target = TARGET_NONE;
  1205. }
  1206. if (MissionTarget == target) {
  1207. MissionTarget = TARGET_NONE;
  1208. }
  1209. if (Trigger.Is_Valid() && Trigger->As_Target() == target) {
  1210. Trigger = 0;
  1211. }
  1212. }
  1213. /***********************************************************************************************
  1214. * TeamClass::Calc_Center -- Determines average location of team members. *
  1215. * *
  1216. * Use this routine to calculate the "center" location of the team. This is the average *
  1217. * position of all members of the team. Using this center value it is possible to tell *
  1218. * if a team member is too far away and where to head to in order to group up. *
  1219. * *
  1220. * INPUT: center -- Average center target location of the team. Only initiated members *
  1221. * will be considered. *
  1222. * *
  1223. * close_member--Location (as target) of the unit that is closest to the team's *
  1224. * target. *
  1225. * *
  1226. * OUTPUT: none *
  1227. * *
  1228. * WARNINGS: none *
  1229. * *
  1230. * HISTORY: *
  1231. * 12/29/1994 JLB : Created. *
  1232. *=============================================================================================*/
  1233. void TeamClass::Calc_Center(TARGET & center, TARGET & close_member) const
  1234. {
  1235. assert(IsActive);
  1236. assert(Teams.ID(this) == ID);
  1237. /*
  1238. ** Presume there is no center. This will be confirmed in the following scanning
  1239. ** operation.
  1240. */
  1241. close_member = TARGET_NONE;
  1242. center = TARGET_NONE;
  1243. FootClass const * team_member = Member; // Working team member pointer.
  1244. /*
  1245. ** If there are no members of the team, then there can be no center point of the team.
  1246. */
  1247. if (team_member == NULL) return;
  1248. /*
  1249. ** If the team is supposed to follow a nearby friendly unit, then the
  1250. ** team's "center" will actually be that unit. Otherwise, calculated the
  1251. ** average center location for the team.
  1252. */
  1253. if (Class->MissionList[CurrentMission].Mission == TMISSION_HOUND_DOG) {
  1254. /*
  1255. ** First pick a member of the team. The closest friendly object to that member
  1256. ** will be picked.
  1257. */
  1258. if (!team_member) return;
  1259. FootClass const * closest = NULL; // Current closest friendly object.
  1260. int distance = -1; // Record of last closest distance calc.
  1261. /*
  1262. ** Scan through all vehicles.
  1263. */
  1264. for (int unit_index = 0; unit_index < Units.Count(); unit_index++) {
  1265. FootClass const * trial_unit = Units.Ptr(unit_index);
  1266. if (_Is_It_Breathing(trial_unit) && trial_unit->House->Is_Ally(House) && trial_unit->Team != this) {
  1267. int trial_distance = team_member->Distance(trial_unit);
  1268. if (distance == -1 || trial_distance < distance) {
  1269. distance = trial_distance;
  1270. closest = trial_unit;
  1271. }
  1272. }
  1273. }
  1274. /*
  1275. ** Scan through all infantry.
  1276. */
  1277. for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) {
  1278. FootClass const * trial_infantry = Infantry.Ptr(infantry_index);
  1279. if (_Is_It_Breathing(trial_infantry) && trial_infantry->House->Is_Ally(House) && trial_infantry->Team != this) {
  1280. int trial_distance = team_member->Distance(trial_infantry);
  1281. if (distance == -1 || trial_distance < distance) {
  1282. distance = trial_distance;
  1283. closest = trial_infantry;
  1284. }
  1285. }
  1286. }
  1287. /*
  1288. ** Scan through all vessels.
  1289. */
  1290. for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) {
  1291. FootClass const * trial_vessel = Vessels.Ptr(vessel_index);
  1292. if (_Is_It_Breathing(trial_vessel) && trial_vessel->House->Is_Ally(House) && trial_vessel->Team != this) {
  1293. int trial_distance = team_member->Distance(trial_vessel);
  1294. if (distance == -1 || trial_distance < distance) {
  1295. distance = trial_distance;
  1296. closest = trial_vessel;
  1297. }
  1298. }
  1299. }
  1300. /*
  1301. ** Set the center location as actually the friendly object that is closest. If there
  1302. ** is no friendly object, then don't set any center location at all.
  1303. */
  1304. if (closest) {
  1305. center = closest->As_Target();
  1306. close_member = Member->As_Target();
  1307. }
  1308. } else {
  1309. long x = 0; // Accumulated X coordinate.
  1310. long y = 0; // Accumulated Y coordinate.
  1311. int dist = 0; // Closest recorded distance to team target.
  1312. int quantity = 0; // Number of team members counted.
  1313. FootClass const * closest = 0; // Closest member to target.
  1314. /*
  1315. ** Scan through all team members and accumulate the X and Y component of their
  1316. ** location. Only team members that are active will be considered. Also keep
  1317. ** track of the team member that is closest to the team's target.
  1318. */
  1319. while (team_member != NULL) {
  1320. if (_Is_It_Playing(team_member)) {
  1321. /*
  1322. ** Accumulate X and Y components of qualified team members.
  1323. */
  1324. x += Coord_X(team_member->Coord);
  1325. y += Coord_Y(team_member->Coord);
  1326. quantity++;
  1327. /*
  1328. ** Keep a record of the team member that is nearest to the team's
  1329. ** target.
  1330. */
  1331. int try_dist = team_member->Distance(Target);
  1332. if (!dist || try_dist < dist) {
  1333. dist = try_dist;
  1334. closest = team_member;
  1335. }
  1336. }
  1337. team_member = team_member->Member;
  1338. }
  1339. /*
  1340. ** If there were any qualifying members, then the team's center point can be
  1341. ** determined.
  1342. */
  1343. if (quantity) {
  1344. x /= quantity;
  1345. y /= quantity;
  1346. COORDINATE coord = XY_Coord((int)x, (int)y);
  1347. center = ::As_Target(coord);
  1348. /*
  1349. ** If the center location is impassable, then just pick the location of
  1350. ** one of the team members.
  1351. */
  1352. if (!closest->Can_Enter_Cell(As_Cell(center))) {
  1353. // if (Class->Origin != -1) {
  1354. // center = ::As_Target(Scen.Waypoint[Class->Origin]);
  1355. // } else {
  1356. center = ::As_Target(Coord_Cell(closest->Center_Coord()));
  1357. // }
  1358. }
  1359. }
  1360. /*
  1361. ** Record the position of the closest member to the team's target and
  1362. ** that will be used as the regroup point.
  1363. */
  1364. if (closest != NULL) {
  1365. close_member = ::As_Target(Coord_Cell(closest->Center_Coord()));
  1366. }
  1367. }
  1368. }
  1369. /***********************************************************************************************
  1370. * TeamClass::Took_Damage -- Informs the team when the team member takes damage. *
  1371. * *
  1372. * This routine is used when a team member takes damage. Usually the team will react in *
  1373. * some fashion to the attack. This reaction can range from running away to assigning this *
  1374. * new target as the team's target. *
  1375. * *
  1376. * INPUT: obj -- The team member that was damaged. *
  1377. * *
  1378. * result -- The severity of the damage taken. *
  1379. * *
  1380. * source -- The perpetrator of the damage. *
  1381. * *
  1382. * OUTPUT: none *
  1383. * *
  1384. * WARNINGS: none *
  1385. * *
  1386. * HISTORY: *
  1387. * 12/29/1994 JLB : Created. *
  1388. *=============================================================================================*/
  1389. void TeamClass::Took_Damage(FootClass * , ResultType result, TechnoClass * source)
  1390. {
  1391. assert(IsActive);
  1392. assert(Teams.ID(this) == ID);
  1393. if ((result != RESULT_NONE) && (!Class->IsSuicide)) {
  1394. if (!IsMoving) {
  1395. // TCTCTC
  1396. // Should run to a better hiding place or disband into a group of hunting units.
  1397. } else {
  1398. /*
  1399. ** Respond to the attack, but not if we're an aircraft or a LST.
  1400. */
  1401. if (source && !Is_A_Member(source) && Member && Member->What_Am_I() != RTTI_AIRCRAFT && (Member->What_Am_I() != RTTI_VESSEL || *(VesselClass *)((FootClass *)Member) != VESSEL_TRANSPORT)) {
  1402. if (Target != source->As_Target()) {
  1403. /*
  1404. ** Don't change target if the team's target is one that can fire as well. There is
  1405. ** no point in endlessly shuffling between targets that have firepower.
  1406. */
  1407. if (Target_Legal(Target)) {
  1408. TechnoClass * techno = As_Techno(Target);
  1409. if (techno && ((TechnoTypeClass const &)techno->Class_Of()).PrimaryWeapon != NULL) {
  1410. if (techno->In_Range(As_Coord(Zone), 0)) {
  1411. return;
  1412. }
  1413. }
  1414. }
  1415. /*
  1416. ** Don't change target to aggressor if the aggressor cannot normally be attacked.
  1417. */
  1418. if (source->What_Am_I() == RTTI_AIRCRAFT || (source->What_Am_I() == RTTI_VESSEL && (Member->What_Am_I() == RTTI_UNIT || Member->What_Am_I() == RTTI_INFANTRY))) {
  1419. return;
  1420. }
  1421. Target = source->As_Target();
  1422. }
  1423. }
  1424. }
  1425. }
  1426. }
  1427. /***********************************************************************************************
  1428. * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. *
  1429. * *
  1430. * This function is called when the team knows what it should attack. This routine will *
  1431. * give the necessary orders to the members of the team. *
  1432. * *
  1433. * INPUT: none *
  1434. * *
  1435. * OUTPUT: none *
  1436. * *
  1437. * WARNINGS: none *
  1438. * *
  1439. * HISTORY: *
  1440. * 04/06/1995 JLB : Created. *
  1441. *=============================================================================================*/
  1442. void TeamClass::Coordinate_Attack(void)
  1443. {
  1444. assert(IsActive);
  1445. assert(Teams.ID(this) == ID);
  1446. if (!Target_Legal(Target)) {
  1447. Target = MissionTarget;
  1448. }
  1449. /*
  1450. ** Check if they're attacking a cell. If the contents of the cell are
  1451. ** a bridge or a building/unit/techno, then it's a valid target. Otherwise,
  1452. ** the target is invalid. This only applies to non-aircraft teams. An aircraft team
  1453. ** can "attack" an empty cell and this is perfectly ok (paratrooper drop and parabombs
  1454. ** are prime examples).
  1455. */
  1456. if (Is_Target_Cell(Target) && Member != NULL && Fetch_A_Leader()->What_Am_I() != RTTI_AIRCRAFT) {
  1457. CellClass *cellptr = &Map[As_Cell(Target)];
  1458. TemplateType tt = cellptr->TType;
  1459. if (cellptr->Cell_Object()) {
  1460. Target = cellptr->Cell_Object()->As_Target();
  1461. } else {
  1462. if (tt != TEMPLATE_BRIDGE1 && tt != TEMPLATE_BRIDGE2 &&
  1463. tt != TEMPLATE_BRIDGE1H && tt != TEMPLATE_BRIDGE2H &&
  1464. tt != TEMPLATE_BRIDGE_1A && tt != TEMPLATE_BRIDGE_1B &&
  1465. tt != TEMPLATE_BRIDGE_2A && tt != TEMPLATE_BRIDGE_2B &&
  1466. tt != TEMPLATE_BRIDGE_3A && tt != TEMPLATE_BRIDGE_3B ) {
  1467. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1468. FootClass *unit = Member;
  1469. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  1470. if(unit->What_Am_I() != RTTI_UNIT ||
  1471. *(UnitClass *)unit != UNIT_CHRONOTANK ||
  1472. mission->Mission != TMISSION_SPY)
  1473. #endif
  1474. Target = 0; // invalidize the target so it'll go to next mission.
  1475. }
  1476. }
  1477. }
  1478. if (!Target_Legal(Target)) {
  1479. IsNextMission = true;
  1480. } else {
  1481. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  1482. FootClass * unit = Member;
  1483. while (unit != NULL) {
  1484. Coordinate_Conscript(unit);
  1485. if (_Is_It_Playing(unit)) {
  1486. if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)unit == INFANTRY_SPY) {
  1487. unit->Assign_Mission(MISSION_CAPTURE);
  1488. unit->Assign_Target(Target);
  1489. } else {
  1490. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1491. if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_CHRONOTANK) {
  1492. UnitClass *tank = (UnitClass *)unit;
  1493. tank->Teleport_To(::As_Cell(Target));
  1494. tank->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE;
  1495. Scen.Do_BW_Fade();
  1496. Sound_Effect(VOC_CHRONOTANK1, unit->Coord);
  1497. tank->Assign_Target(TARGET_NONE);
  1498. tank->Assign_Mission(MISSION_GUARD);
  1499. } else {
  1500. #endif
  1501. if (unit->Mission != MISSION_ATTACK && unit->Mission != MISSION_ENTER && unit->Mission != MISSION_CAPTURE) {
  1502. unit->Transmit_Message(RADIO_OVER_OUT);
  1503. unit->Assign_Mission(MISSION_ATTACK);
  1504. unit->Assign_Target(TARGET_NONE);
  1505. unit->Assign_Destination(TARGET_NONE);
  1506. }
  1507. }
  1508. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1509. }
  1510. #endif
  1511. if (unit->TarCom != Target) {
  1512. unit->Assign_Target(Target);
  1513. }
  1514. }
  1515. unit = unit->Member;
  1516. }
  1517. }
  1518. }
  1519. /***********************************************************************************************
  1520. * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). *
  1521. * *
  1522. * This routine is called when the team must delay at its current location. Team members *
  1523. * are grouped together by this function. It is called when the team needs to sit and *
  1524. * wait. *
  1525. * *
  1526. * INPUT: none *
  1527. * *
  1528. * OUTPUT: bool; Has the team completely regrouped? *
  1529. * *
  1530. * WARNINGS: none *
  1531. * *
  1532. * HISTORY: *
  1533. * 04/06/1995 JLB : Created. *
  1534. *=============================================================================================*/
  1535. bool TeamClass::Coordinate_Regroup(void)
  1536. {
  1537. assert(IsActive);
  1538. assert(Teams.ID(this) == ID);
  1539. FootClass * unit = Member;
  1540. bool retval = true;
  1541. /*
  1542. ** Regroup default logic.
  1543. */
  1544. while (unit != NULL) {
  1545. Coordinate_Conscript(unit);
  1546. if (_Is_It_Playing(unit)) {
  1547. if (unit->Distance(Zone) > Rule.StrayDistance && (unit->Mission != MISSION_GUARD_AREA || !Target_Legal(unit->TarCom))) {
  1548. if (!Target_Legal(unit->NavCom)) {
  1549. // TCTCTC
  1550. // if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, Zone) > Rule.StrayDistance) {
  1551. unit->Assign_Mission(MISSION_MOVE);
  1552. unit->Assign_Destination(Zone);
  1553. retval = false;
  1554. if (!unit->IsFormationMove) {
  1555. unit->Assign_Mission(MISSION_MOVE);
  1556. CELL dest = unit->Adjust_Dest(As_Cell(Zone));
  1557. unit->Assign_Destination(::As_Target(dest));
  1558. } else {
  1559. retval = true; // formations are always considered regrouped.
  1560. }
  1561. }
  1562. } else {
  1563. /*
  1564. ** The team is regrouping, so just sit here and wait.
  1565. */
  1566. if (unit->Mission != MISSION_GUARD_AREA) {
  1567. unit->Assign_Mission(MISSION_GUARD);
  1568. unit->Assign_Destination(TARGET_NONE);
  1569. }
  1570. }
  1571. }
  1572. unit = unit->Member;
  1573. }
  1574. return(retval);
  1575. }
  1576. /***********************************************************************************************
  1577. * TeamClass::Coordinate_Do -- Handles the team performing specified mission. *
  1578. * *
  1579. * This will assign the specified mission to the team. If there are team members that are *
  1580. * too far away from the center of the team, then they will be told to move to the team's *
  1581. * location. *
  1582. * *
  1583. * INPUT: none *
  1584. * *
  1585. * OUTPUT: none *
  1586. * *
  1587. * WARNINGS: This only works if the special mission the team members are to perform does not *
  1588. * require extra parameters. The ATTACK and MOVE missions are particularly bad. *
  1589. * *
  1590. * HISTORY: *
  1591. * 05/11/1996 JLB : Created. *
  1592. *=============================================================================================*/
  1593. void TeamClass::Coordinate_Do(void)
  1594. {
  1595. assert(IsActive);
  1596. assert(Teams.ID(this) == ID);
  1597. FootClass * unit = Member;
  1598. MissionType do_mission = Class->MissionList[CurrentMission].Data.Mission;
  1599. /*
  1600. ** For each unit either head it back to the team center or give it the main
  1601. ** team mission order as appropriate.
  1602. */
  1603. while (unit != NULL) {
  1604. Coordinate_Conscript(unit);
  1605. if (_Is_It_Playing(unit)) {
  1606. if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Distance(Zone) > Rule.StrayDistance * 2) {
  1607. /*
  1608. ** Only if the unit isn't already heading to regroup with the team, will it
  1609. ** be given orders to do so.
  1610. */
  1611. unit->Assign_Mission(MISSION_MOVE);
  1612. unit->Assign_Destination(Zone);
  1613. unit->Assign_Mission(MISSION_MOVE);
  1614. CELL dest = unit->Adjust_Dest(As_Cell(Zone));
  1615. unit->Assign_Destination(::As_Target(dest));
  1616. } else {
  1617. /*
  1618. ** The team is regrouping, so just sit here and wait.
  1619. */
  1620. if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Mission != do_mission) {
  1621. unit->ArchiveTarget = TARGET_NONE;
  1622. unit->Assign_Mission(do_mission);
  1623. unit->Assign_Target(TARGET_NONE);
  1624. unit->Assign_Destination(TARGET_NONE);
  1625. }
  1626. }
  1627. }
  1628. unit = unit->Member;
  1629. }
  1630. }
  1631. /***********************************************************************************************
  1632. * TeamClass::Coordinate_Move -- Handles team movement coordination. *
  1633. * *
  1634. * This routine is called when the team must move to a new location. Movement and grouping *
  1635. * commands associated with this task are initiated here. *
  1636. * *
  1637. * INPUT: none *
  1638. * *
  1639. * OUTPUT: none *
  1640. * *
  1641. * WARNINGS: none *
  1642. * *
  1643. * HISTORY: *
  1644. * 04/06/1995 JLB : Created. *
  1645. *=============================================================================================*/
  1646. void TeamClass::Coordinate_Move(void)
  1647. {
  1648. assert(IsActive);
  1649. assert(Teams.ID(this) == ID);
  1650. FootClass * unit = Member;
  1651. bool finished = true;
  1652. bool found = false;
  1653. if (!Target_Legal(Target)) {
  1654. Target = MissionTarget;
  1655. }
  1656. if (Target_Legal(Target)) {
  1657. if (!Lagging_Units()) {
  1658. while (unit != NULL) {
  1659. /*
  1660. ** Tell the unit, if necessary, that it should regroup
  1661. ** with the main team location. If the unit is regrouping, then
  1662. ** the team should continue NOT qualify as fully reaching the desired
  1663. ** location.
  1664. */
  1665. if (Coordinate_Conscript(unit)) {
  1666. finished = false;
  1667. }
  1668. if (unit->Mission == MISSION_UNLOAD || unit->MissionQueue == MISSION_UNLOAD) {
  1669. finished = false;
  1670. }
  1671. if (_Is_It_Playing(unit) && unit->Mission != MISSION_UNLOAD && unit->MissionQueue != MISSION_UNLOAD) {
  1672. int stray = Rule.StrayDistance;
  1673. if (unit->What_Am_I() == RTTI_AIRCRAFT) {
  1674. stray *= 3;
  1675. }
  1676. if (unit->What_Am_I() == RTTI_INFANTRY && ((InfantryClass const *)unit)->Class->IsDog) {
  1677. if (Target_Legal(unit->TarCom)) stray = unit->Techno_Type_Class()->ThreatRange;
  1678. if (Target_Legal(unit->TarCom) && unit->Distance(unit->TarCom) > stray) {
  1679. unit->Assign_Target(TARGET_NONE);
  1680. }
  1681. }
  1682. found = true;
  1683. int dist = unit->Distance(Target);
  1684. if (unit->IsFormationMove) {
  1685. if (::As_Target(Coord_Cell(unit->Coord)) != unit->NavCom) {
  1686. dist = Rule.StrayDistance + 1; // formation moves must be exact.
  1687. }
  1688. }
  1689. if (dist > stray ||
  1690. (unit->What_Am_I() == RTTI_AIRCRAFT &&
  1691. // (unit->In_Which_Layer() == LAYER_TOP &&
  1692. ((AircraftClass *)unit)->Height > 0 &&
  1693. Coord_Cell(unit->Center_Coord()) != As_Cell(Target) &&
  1694. !((AircraftClass *)unit)->Class->IsFixedWing &&
  1695. Class->MissionList[CurrentMission+1].Mission != TMISSION_MOVE)) {
  1696. bool wasform = false;
  1697. if (unit->Mission != MISSION_MOVE) {
  1698. unit->Assign_Mission(MISSION_MOVE);
  1699. }
  1700. if (unit->NavCom != Target) {
  1701. /*
  1702. ** Check if this destination should be adjusted for
  1703. ** a formation move
  1704. */
  1705. if (Is_Target_Cell(Target) && unit->IsFormationMove) {
  1706. CELL newcell = unit->Adjust_Dest(As_Cell(Target));
  1707. if (Coord_Cell(unit->Coord) != newcell) {
  1708. unit->Assign_Destination(::As_Target(newcell));
  1709. } else {
  1710. unit->Assign_Mission(MISSION_GUARD);
  1711. unit->Assign_Destination(TARGET_NONE);
  1712. wasform = true;
  1713. }
  1714. } else {
  1715. unit->Assign_Destination(Target);
  1716. }
  1717. }
  1718. if (!wasform) {
  1719. finished = false;
  1720. }
  1721. } else {
  1722. if (unit->Mission == MISSION_MOVE && (!Target_Legal(unit->NavCom) || Distance(unit->NavCom) < CELL_LEPTON_W)) {
  1723. unit->Assign_Destination(TARGET_NONE);
  1724. unit->Enter_Idle_Mode();
  1725. }
  1726. }
  1727. /*
  1728. ** If any member still has a valid NavCom then consider this
  1729. ** movement mission to still be in progress. This will ensure
  1730. ** that the members come to a complete stop before the next
  1731. ** mission commences. Without this, the team will prematurely
  1732. ** start on the next mission even when all members aren't yet
  1733. ** in their proper spot.
  1734. */
  1735. if (Target_Legal(unit->NavCom)) {
  1736. finished = false;
  1737. }
  1738. }
  1739. unit = unit->Member;
  1740. }
  1741. } else {
  1742. finished = false;
  1743. }
  1744. }
  1745. /*
  1746. ** If there are no initiated members to this team, then it certainly
  1747. ** could not have managed to move to the target destination.
  1748. */
  1749. if (!found) {
  1750. finished = false;
  1751. }
  1752. /*
  1753. ** If all the team members are close enough to the desired destination, then
  1754. ** move to the next mission.
  1755. */
  1756. if (finished && IsMoving) {
  1757. IsNextMission = true;
  1758. }
  1759. }
  1760. /***********************************************************************************************
  1761. * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. *
  1762. * *
  1763. * This routine will examine the team and find any lagging units. The units are then *
  1764. * ordered to catch up to the team member that is closest to the team's destination. This *
  1765. * routine will not do anything unless lagging members are suspected. This fact is *
  1766. * indicated by setting the IsLagging flag. The flag is set by some outside agent. *
  1767. * *
  1768. * INPUT: none *
  1769. * *
  1770. * OUTPUT: none *
  1771. * *
  1772. * WARNINGS: Be sure to set IsLagging for the team if a lagging member is suspected. *
  1773. * *
  1774. * HISTORY: *
  1775. * 08/01/1995 PWG : Created. *
  1776. * 04/11/1996 JLB : Modified. *
  1777. *=============================================================================================*/
  1778. bool TeamClass::Lagging_Units(void)
  1779. {
  1780. assert(IsActive);
  1781. assert(Teams.ID(this) == ID);
  1782. FootClass * unit = Member;
  1783. bool lag = false;
  1784. //BG: HACK - if it's in a formation move, then disable the check for
  1785. // VG added NULL check laggers, 'cause they're all moving simultaneously.
  1786. if (unit != NULL && unit->IsFormationMove) IsLagging = false;
  1787. /*
  1788. ** If the IsLagging bit is not set, then obviously there are no lagging
  1789. ** units.
  1790. */
  1791. if (!IsLagging) return(false);
  1792. /*
  1793. ** Scan through all of the units, searching for units who are having
  1794. ** trouble keeping up with the pack.
  1795. */
  1796. while (unit != NULL) {
  1797. if (_Is_It_Playing(unit)) {
  1798. int stray = Rule.StrayDistance;
  1799. if (unit->What_Am_I() == RTTI_AIRCRAFT) {
  1800. stray *= 3;
  1801. }
  1802. /*
  1803. ** If we find a unit who has fallen too far away from the center of
  1804. ** the pack, then we need to order that unit to catch up with the
  1805. ** first unit.
  1806. */
  1807. if (unit->Distance(ClosestMember) > stray) {
  1808. // TCTCTC
  1809. if (!Target_Legal(unit->NavCom)) {
  1810. // if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, ClosestMember) > Rule.StrayDistance) {
  1811. unit->Assign_Mission(MISSION_MOVE);
  1812. unit->Assign_Destination(ClosestMember);
  1813. }
  1814. lag = true;
  1815. } else {
  1816. /*
  1817. ** We need to order all of the other units to hold their
  1818. ** position until all lagging units catch up.
  1819. */
  1820. if (unit->Mission != MISSION_GUARD) {
  1821. unit->Assign_Mission(MISSION_GUARD);
  1822. unit->Assign_Destination(TARGET_NONE);
  1823. }
  1824. }
  1825. }
  1826. unit = unit->Member;
  1827. }
  1828. /*
  1829. ** Once we have handled the loop we know whether there are any lagging
  1830. ** units or not.
  1831. */
  1832. IsLagging = lag;
  1833. return(lag);
  1834. }
  1835. /***********************************************************************************************
  1836. * TeamClass::TMission_Unload -- Tells the team to unload passengers now. *
  1837. * *
  1838. * This routine tells all transport vehicles to unload passengers now. *
  1839. * *
  1840. * INPUT: none *
  1841. * *
  1842. * OUTPUT: none *
  1843. * *
  1844. * WARNINGS: none *
  1845. * *
  1846. * HISTORY: *
  1847. * 06/14/1995 JLB : Created. *
  1848. *=============================================================================================*/
  1849. int TeamClass::TMission_Unload(void)
  1850. {
  1851. assert(IsActive);
  1852. assert(Teams.ID(this) == ID);
  1853. FootClass * unit = Member;
  1854. bool finished = true;
  1855. while (unit != NULL) {
  1856. Coordinate_Conscript(unit);
  1857. if (_Is_It_Playing(unit)) {
  1858. /*
  1859. ** Only assign the mission if the unit is carrying a passenger, OR
  1860. ** if the unit is a minelayer, with mines in it, and the cell it's
  1861. ** on doesn't have a building (read: mine) in it already.
  1862. */
  1863. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  1864. /* Also, allow unload if it's a MAD Tank. */
  1865. if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MAD )) {
  1866. #else
  1867. if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) ) {
  1868. #endif
  1869. if (unit->Is_Something_Attached()) {
  1870. /*
  1871. ** Passenger-carrying vehicles will always return false until
  1872. ** they've unloaded all passengers.
  1873. */
  1874. finished = false;
  1875. }
  1876. /*
  1877. ** The check for a building is located here because the mine layer may have
  1878. ** already unloaded the mine but is still in the process of retracting
  1879. ** the mine layer. During this time, it should not be considered to have
  1880. ** finished its unload mission.
  1881. */
  1882. if (Map[unit->Center_Coord()].Cell_Building() == NULL && unit->Mission != MISSION_UNLOAD) {
  1883. unit->Assign_Destination(TARGET_NONE);
  1884. unit->Assign_Target(TARGET_NONE);
  1885. unit->Assign_Mission(MISSION_UNLOAD);
  1886. finished = false;
  1887. }
  1888. } else {
  1889. /*
  1890. ** A loaner transport should vacate the map when all transported objects
  1891. ** have been offloaded.
  1892. */
  1893. if (unit->IsALoaner) {
  1894. Remove(unit);
  1895. unit->Assign_Mission(MISSION_RETREAT);
  1896. unit->Commence();
  1897. }
  1898. }
  1899. }
  1900. unit = unit->Member;
  1901. }
  1902. if (finished) {
  1903. IsNextMission = true;
  1904. }
  1905. return(1);
  1906. }
  1907. /***********************************************************************************************
  1908. * TeamClass::TMission_Load -- Tells the team to load onto the transport now. *
  1909. * *
  1910. * This routine tells all non-transport units in the team to climb onto the transport in the*
  1911. * team. Note the transport must be a member of this team. *
  1912. * *
  1913. * INPUT: none *
  1914. * *
  1915. * OUTPUT: none *
  1916. * *
  1917. * WARNINGS: none *
  1918. * *
  1919. * HISTORY: *
  1920. * 06/28/1996 BWG : Created. *
  1921. *=============================================================================================*/
  1922. int TeamClass::TMission_Load(void)
  1923. {
  1924. assert(IsActive);
  1925. assert(Teams.ID(this) == ID);
  1926. FootClass * unit = Member;
  1927. FootClass * trans = 0;
  1928. /*
  1929. ** First locate the transport in the team, if there is one. There should
  1930. ** only be one transport in the team.
  1931. */
  1932. while(unit != NULL && trans == NULL) {
  1933. if (unit->Techno_Type_Class()->Max_Passengers() > 0) {
  1934. trans = unit;
  1935. break;
  1936. }
  1937. unit = unit->Member;
  1938. }
  1939. /*
  1940. ** In the case of no transport available, then consider the mission complete
  1941. ** since it can never complete otherwise.
  1942. */
  1943. if (trans == NULL) {
  1944. IsNextMission = true;
  1945. return(1);
  1946. }
  1947. /*
  1948. ** If the transport is already in radio contact, then this means that
  1949. ** it is in the process of loading. During this time, don't bother to assign
  1950. ** the enter mission to the other team members.
  1951. */
  1952. if (trans->In_Radio_Contact()) {
  1953. return(1);
  1954. }
  1955. /*
  1956. ** Find a member to assign the entry logic for.
  1957. */
  1958. bool finished = true;
  1959. unit = Member; // re-point at the first member of the team again.
  1960. while (unit != NULL && Total > 1) {
  1961. Coordinate_Conscript(unit);
  1962. /*
  1963. ** Only assign the mission if the unit is not the transport.
  1964. */
  1965. if (_Is_It_Playing(unit) && unit != trans) {
  1966. if (unit->Mission != MISSION_ENTER) {
  1967. unit->Assign_Mission(MISSION_ENTER);
  1968. unit->Assign_Target(TARGET_NONE);
  1969. unit->Assign_Destination(trans->As_Target());
  1970. finished = false;
  1971. break;
  1972. }
  1973. finished = false;
  1974. }
  1975. unit = unit->Member;
  1976. }
  1977. if (finished) {
  1978. IsNextMission = true;
  1979. }
  1980. return(1);
  1981. }
  1982. /***********************************************************************************************
  1983. * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. *
  1984. * *
  1985. * This routine will give the movement orders to the conscript so that it will group *
  1986. * with the other members of the team. *
  1987. * *
  1988. * INPUT: unit -- Pointer to the conscript unit. *
  1989. * *
  1990. * OUTPUT: bool; Is the unit still scurrying to reach the team's current location? *
  1991. * *
  1992. * WARNINGS: none *
  1993. * *
  1994. * HISTORY: *
  1995. * 04/06/1995 JLB : Created. *
  1996. *=============================================================================================*/
  1997. bool TeamClass::Coordinate_Conscript(FootClass * unit)
  1998. {
  1999. assert(IsActive);
  2000. assert(Teams.ID(this) == ID);
  2001. if (_Is_It_Breathing(unit) && !unit->IsInitiated) {
  2002. if (unit->Distance(Zone) > Rule.StrayDistance) {
  2003. if (!Target_Legal(unit->NavCom)) {
  2004. unit->Assign_Mission(MISSION_MOVE);
  2005. unit->Assign_Target(TARGET_NONE);
  2006. unit->IsFormationMove = false;
  2007. unit->Assign_Destination(Zone);
  2008. }
  2009. return(true);
  2010. } else {
  2011. /*
  2012. ** This unit has gotten close enough to the team center so that it is
  2013. ** now considered initiated. An initiated unit is considered when calculating
  2014. ** the center of the team.
  2015. */
  2016. unit->IsInitiated = true;
  2017. }
  2018. }
  2019. return(false);
  2020. }
  2021. /***************************************************************************
  2022. * TeamClass::Is_A_Member -- Tests if a unit is a member of a team *
  2023. * *
  2024. * INPUT: none *
  2025. * *
  2026. * OUTPUT: none *
  2027. * *
  2028. * WARNINGS: *
  2029. * *
  2030. * HISTORY: *
  2031. * 05/16/1995 PWG : Created. *
  2032. *=========================================================================*/
  2033. bool TeamClass::Is_A_Member(void const * who) const
  2034. {
  2035. assert(IsActive);
  2036. assert(Teams.ID(this) == ID);
  2037. FootClass * unit = Member;
  2038. while (unit != NULL) {
  2039. if (unit == who) {
  2040. return(true);
  2041. }
  2042. unit = unit->Member;
  2043. }
  2044. return(false);
  2045. }
  2046. /***************************************************************************
  2047. * TeamClass::Suspend_Teams -- Suspends activity for low priority teams *
  2048. * *
  2049. * INPUT: int priority - determines what is considered low priority. *
  2050. * *
  2051. * OUTPUT: *
  2052. * *
  2053. * WARNINGS: *
  2054. * *
  2055. * HISTORY: *
  2056. * 06/19/1995 PWG : Created. *
  2057. *=========================================================================*/
  2058. void TeamClass::Suspend_Teams(int priority, HouseClass const * house)
  2059. {
  2060. for (int index = 0; index < Teams.Count(); index++) {
  2061. TeamClass * team = Teams.Ptr(index);
  2062. /*
  2063. ** If a team is below the "survival priority level", then it gets
  2064. ** destroyed. The team members are then free to be reassigned.
  2065. */
  2066. if (team != NULL && team->House == house && team->Class->RecruitPriority < priority) {
  2067. FootClass * unit = team->Member;
  2068. while (team->Member) {
  2069. team->Remove(team->Member);
  2070. }
  2071. team->IsAltered = team->JustAltered = true;
  2072. team->SuspendTimer = Rule.SuspendDelay * TICKS_PER_MINUTE;
  2073. team->Suspended = true;
  2074. }
  2075. }
  2076. }
  2077. /***********************************************************************************************
  2078. * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map *
  2079. * *
  2080. * This routine is used to see if the team is leaving the map. A team that is leaving the *
  2081. * map gives implicit permission for its members to leave the map. *
  2082. * *
  2083. * INPUT: none *
  2084. * *
  2085. * OUTPUT: bool; Is this team trying to leave the map? *
  2086. * *
  2087. * WARNINGS: none *
  2088. * *
  2089. * HISTORY: *
  2090. * 04/30/1996 JLB : Created. *
  2091. *=============================================================================================*/
  2092. bool TeamClass::Is_Leaving_Map(void) const
  2093. {
  2094. assert(IsActive);
  2095. assert(Teams.ID(this) == ID);
  2096. if (IsMoving && CurrentMission >= 0) {
  2097. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  2098. if (mission->Mission == TMISSION_MOVE && !Map.In_Radar(Scen.Waypoint[mission->Data.Value])) {
  2099. return(true);
  2100. }
  2101. }
  2102. return(false);
  2103. }
  2104. /***********************************************************************************************
  2105. * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. *
  2106. * *
  2107. * This will examine all team members and only if all of them have entered the map, will *
  2108. * it return true. This routine is used to recognize the case of a team that has been *
  2109. * generated off map and one that has already entered game play. This knowledge can lead *
  2110. * to more intelligent behavior regarding team and member disposition. *
  2111. * *
  2112. * INPUT: none *
  2113. * *
  2114. * OUTPUT: bool; Have all members of this team entered the map? *
  2115. * *
  2116. * WARNINGS: none *
  2117. * *
  2118. * HISTORY: *
  2119. * 07/26/1996 JLB : Created. *
  2120. *=============================================================================================*/
  2121. bool TeamClass::Has_Entered_Map(void) const
  2122. {
  2123. bool ok = true;
  2124. FootClass * foot = Member;
  2125. while (foot != NULL) {
  2126. if (!foot->IsLocked) {
  2127. ok = false;
  2128. break;
  2129. }
  2130. foot = (FootClass *)(ObjectClass *)(foot->Next);
  2131. }
  2132. return(ok);
  2133. }
  2134. /***********************************************************************************************
  2135. * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. *
  2136. * *
  2137. * This routine is used when one of the team members cannot get within range of the team's *
  2138. * target. In such a case, the team must be assigned a new target and all members of that *
  2139. * team must recognize that a restricted target scan is required. This is done by clearing *
  2140. * out the team's target so that it will be forced to search for a new one. Also, since the *
  2141. * members are flagged for short scanning, whichever team member is picked to scan for a *
  2142. * target will scan for one that is within range. *
  2143. * *
  2144. * INPUT: none *
  2145. * *
  2146. * OUTPUT: none *
  2147. * *
  2148. * WARNINGS: The team will reassign its target as a result of this routine. *
  2149. * *
  2150. * HISTORY: *
  2151. * 07/26/1996 JLB : Created. *
  2152. *=============================================================================================*/
  2153. void TeamClass::Scan_Limit(void)
  2154. {
  2155. Assign_Mission_Target(TARGET_NONE);
  2156. FootClass * foot = Member;
  2157. while (foot != NULL) {
  2158. foot->Assign_Target(TARGET_NONE);
  2159. foot->IsScanLimited = true;
  2160. foot = foot->Member;
  2161. }
  2162. }
  2163. /***********************************************************************************************
  2164. * TeamClass::TMission_Formation -- Process team formation change command. *
  2165. * *
  2166. * This routine will change the team's formation to that specified in the team command *
  2167. * parameter. It is presumed that the team will have further movement orders so that the *
  2168. * formation can serve some purpose. Merely changing the formation doesn't alter the *
  2169. * member's location. The team must be given a movement order before team member *
  2170. * repositioning will occur. *
  2171. * *
  2172. * INPUT: none *
  2173. * *
  2174. * OUTPUT: Returns with the time to delay before further team actions should occur. *
  2175. * *
  2176. * WARNINGS: none *
  2177. * *
  2178. * HISTORY: *
  2179. * 08/06/1996 JLB : Created. *
  2180. *=============================================================================================*/
  2181. int TeamClass::TMission_Formation(void)
  2182. {
  2183. FootClass * member = Member;
  2184. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  2185. Formation = mission->Data.Formation;
  2186. int group = ID + 10;
  2187. int xdir = 0;
  2188. int ydir = 0;
  2189. bool evenodd = 1;
  2190. HousesType house = (member != NULL) ? member->Owner() : HOUSE_NONE;
  2191. /*
  2192. ** Assign appropriate formation offsets for each of the members
  2193. ** of this team.
  2194. */
  2195. switch (Formation) {
  2196. case FORMATION_NONE:
  2197. while (member != NULL) {
  2198. member->Group = 0xFF;
  2199. member->XFormOffset = 0x80000000;
  2200. member->YFormOffset = 0x80000000;
  2201. member->IsFormationMove = false;
  2202. member = member->Member;
  2203. }
  2204. break;
  2205. case FORMATION_TIGHT:
  2206. while (member != NULL) {
  2207. member->Group = group;
  2208. member->XFormOffset = 0;
  2209. member->YFormOffset = 0;
  2210. member->IsFormationMove = true;
  2211. member = member->Member;
  2212. }
  2213. break;
  2214. case FORMATION_LOOSE:
  2215. break;
  2216. case FORMATION_WEDGE_N:
  2217. ydir = -(Total / 2);
  2218. xdir = 0;
  2219. while (member != NULL) {
  2220. member->Group = group;
  2221. member->XFormOffset = xdir;
  2222. member->YFormOffset = ydir;
  2223. member->IsFormationMove = true;
  2224. xdir = -xdir;
  2225. evenodd ^= 1;
  2226. if (!evenodd) {
  2227. xdir -= 2;
  2228. ydir += 2;
  2229. }
  2230. member = member->Member;
  2231. }
  2232. break;
  2233. case FORMATION_WEDGE_E:
  2234. xdir = (Total / 2);
  2235. ydir = 0;
  2236. while (member != NULL) {
  2237. member->Group = group;
  2238. member->XFormOffset = xdir;
  2239. member->YFormOffset = ydir;
  2240. member->IsFormationMove = true;
  2241. ydir = -ydir;
  2242. evenodd ^= 1;
  2243. if (!evenodd) {
  2244. xdir -= 2;
  2245. ydir -= 2;
  2246. }
  2247. member = member->Member;
  2248. }
  2249. break;
  2250. case FORMATION_WEDGE_S:
  2251. ydir = (Total / 2);
  2252. xdir = 0;
  2253. while (member != NULL) {
  2254. member->Group = group;
  2255. member->XFormOffset = xdir;
  2256. member->YFormOffset = ydir;
  2257. member->IsFormationMove = true;
  2258. xdir = -xdir;
  2259. evenodd ^= 1;
  2260. if (!evenodd) {
  2261. xdir -= 2;
  2262. ydir -= 2;
  2263. }
  2264. member = member->Member;
  2265. }
  2266. break;
  2267. case FORMATION_WEDGE_W:
  2268. xdir = -(Total / 2);
  2269. ydir = 0;
  2270. while (member != NULL) {
  2271. member->Group = group;
  2272. member->XFormOffset = xdir;
  2273. member->YFormOffset = ydir;
  2274. member->IsFormationMove = true;
  2275. ydir = -ydir;
  2276. evenodd ^= 1;
  2277. if (!evenodd) {
  2278. xdir += 2;
  2279. ydir -= 2;
  2280. }
  2281. member = member->Member;
  2282. }
  2283. break;
  2284. case FORMATION_LINE_NS:
  2285. ydir = -(Total/2);
  2286. while (member != NULL) {
  2287. member->Group = group;
  2288. member->XFormOffset = 0;
  2289. member->YFormOffset = ydir;
  2290. member->IsFormationMove = true;
  2291. member = member->Member;
  2292. ydir += 2;
  2293. }
  2294. break;
  2295. case FORMATION_LINE_EW:
  2296. xdir = -(Total/2);
  2297. while (member != NULL) {
  2298. member->Group = group;
  2299. member->XFormOffset = xdir;
  2300. member->YFormOffset = 0;
  2301. member->IsFormationMove = true;
  2302. member = member->Member;
  2303. xdir += 2;
  2304. }
  2305. break;
  2306. }
  2307. /*
  2308. ** Now calculate the group's movement type and speed
  2309. */
  2310. if (Formation != FORMATION_NONE && house != HOUSE_NONE) {
  2311. TeamFormDataStruct& team_form_data = TeamFormData[house];
  2312. team_form_data.TeamSpeed[group] = SPEED_WHEEL;
  2313. team_form_data.TeamMaxSpeed[group] = MPH_LIGHT_SPEED;
  2314. member = Member;
  2315. while (member != NULL) {
  2316. RTTIType mytype = member->What_Am_I();
  2317. SpeedType memspeed;
  2318. MPHType memmax;
  2319. bool speedcheck = false;
  2320. if (mytype == RTTI_INFANTRY) {
  2321. memspeed = SPEED_FOOT;
  2322. memmax = ((InfantryClass *)member)->Class->MaxSpeed;
  2323. speedcheck = true;
  2324. }
  2325. if (mytype == RTTI_UNIT) {
  2326. memspeed = ((UnitClass *)member)->Class->Speed;
  2327. memmax = ((UnitClass *)member)->Class->MaxSpeed;
  2328. speedcheck = true;
  2329. }
  2330. if (mytype == RTTI_VESSEL) {
  2331. memspeed = ((VesselClass *)member)->Class->Speed;
  2332. memmax = ((VesselClass *)member)->Class->MaxSpeed;
  2333. speedcheck = true;
  2334. }
  2335. if (speedcheck) {
  2336. if (memmax < team_form_data.TeamMaxSpeed[group]) {
  2337. team_form_data.TeamMaxSpeed[group] = memmax;
  2338. team_form_data.TeamSpeed[group] = memspeed;
  2339. }
  2340. }
  2341. member = member->Member;
  2342. }
  2343. /*
  2344. ** Now that it's all calculated, assign the movement type and
  2345. ** speed to every member of the team.
  2346. */
  2347. member = Member;
  2348. while (member != NULL) {
  2349. member->FormationSpeed = team_form_data.TeamSpeed[group];
  2350. member->FormationMaxSpeed = team_form_data.TeamMaxSpeed[group];
  2351. if (member->What_Am_I() == RTTI_INFANTRY) {
  2352. member->FormationSpeed = SPEED_FOOT;
  2353. member->FormationMaxSpeed = MPH_SLOW_ISH;
  2354. }
  2355. member = member->Member;
  2356. }
  2357. }
  2358. // Advance past the formation-setting command.
  2359. IsNextMission = true;
  2360. return(1);
  2361. }
  2362. /***********************************************************************************************
  2363. * TeamClass::TMission_Attack -- Perform the team attack mission command. *
  2364. * *
  2365. * This will tell the team to attack the quarry specified in the team command. If the team *
  2366. * already has a target, this it is presumed that this target take precidence and it won't *
  2367. * be changed. *
  2368. * *
  2369. * INPUT: none *
  2370. * *
  2371. * OUTPUT: Returns with the delay before the next team logic operation should occur. *
  2372. * *
  2373. * WARNINGS: none *
  2374. * *
  2375. * HISTORY: *
  2376. * 08/06/1996 JLB : Created. *
  2377. *=============================================================================================*/
  2378. int TeamClass::TMission_Attack(void)
  2379. {
  2380. if (!Target_Legal(MissionTarget) && Member != NULL) {
  2381. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  2382. /*
  2383. ** Pick a team leader that has a weapon. Only in the case of no
  2384. ** team members having any weapons, will a member without a weapon
  2385. ** be chosen.
  2386. */
  2387. FootClass const * candidate = Fetch_A_Leader();
  2388. /*
  2389. ** Have the team leader pick what the next team target will be.
  2390. */
  2391. switch (mission->Data.Quarry) {
  2392. case QUARRY_ANYTHING:
  2393. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL));
  2394. break;
  2395. case QUARRY_BUILDINGS:
  2396. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BUILDINGS));
  2397. break;
  2398. case QUARRY_HARVESTERS:
  2399. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_TIBERIUM));
  2400. break;
  2401. case QUARRY_INFANTRY:
  2402. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_INFANTRY));
  2403. break;
  2404. case QUARRY_VEHICLES:
  2405. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_VEHICLES));
  2406. break;
  2407. case QUARRY_FACTORIES:
  2408. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FACTORIES));
  2409. break;
  2410. case QUARRY_DEFENSE:
  2411. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BASE_DEFENSE));
  2412. break;
  2413. case QUARRY_THREAT:
  2414. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL));
  2415. break;
  2416. case QUARRY_POWER:
  2417. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_POWER));
  2418. break;
  2419. case QUARRY_FAKES:
  2420. Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FAKES));
  2421. break;
  2422. default:
  2423. break;
  2424. }
  2425. if (!Target_Legal(MissionTarget)) IsNextMission = true;
  2426. }
  2427. Coordinate_Attack();
  2428. return(1);
  2429. }
  2430. /***********************************************************************************************
  2431. * TeamClass::TMission_Spy -- Perform the team spy mission. *
  2432. * *
  2433. * This will give the team a spy mission to the location specified. It is presumed that *
  2434. * the location of the team mission actually resides under the building to be spied. If *
  2435. * no building exists at the location, then the spy operation is presumed to be a mere *
  2436. * move operation. *
  2437. * *
  2438. * INPUT: none *
  2439. * *
  2440. * OUTPUT: Returns with the delay before the next team logic operation should occur. *
  2441. * *
  2442. * WARNINGS: none *
  2443. * *
  2444. * HISTORY: *
  2445. * 08/06/1996 JLB : Created. *
  2446. *=============================================================================================*/
  2447. int TeamClass::TMission_Spy(void)
  2448. {
  2449. if (Is_Target_Cell(MissionTarget))
  2450. {
  2451. CELL cell = ::As_Cell(MissionTarget);
  2452. CellClass * cellptr = &Map[cell];
  2453. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2454. ObjectClass * bldg = cellptr->Cell_Building();
  2455. #else
  2456. ObjectClass * bldg = cellptr->Cell_Object();
  2457. #endif
  2458. if (bldg != NULL)
  2459. {
  2460. Assign_Mission_Target(bldg->As_Target());
  2461. Coordinate_Attack();
  2462. }
  2463. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2464. else
  2465. {
  2466. FootClass *member = Member;
  2467. if(member->What_Am_I() == RTTI_UNIT && *(UnitClass *)member == UNIT_CHRONOTANK)
  2468. {
  2469. bool finished = true;
  2470. while (member)
  2471. {
  2472. if ( !((UnitClass *)member)->MoebiusCountDown) finished = false;
  2473. member = member->Member;
  2474. }
  2475. if (!finished)
  2476. {
  2477. Coordinate_Attack();
  2478. }
  2479. else
  2480. {
  2481. Assign_Mission_Target(TARGET_NONE);
  2482. IsNextMission = true;
  2483. }
  2484. }
  2485. }
  2486. #endif
  2487. }
  2488. else
  2489. {
  2490. if (!Target_Legal(MissionTarget))
  2491. {
  2492. Assign_Mission_Target(TARGET_NONE);
  2493. IsNextMission = true;
  2494. }
  2495. else
  2496. {
  2497. Coordinate_Attack();
  2498. }
  2499. }
  2500. return(1);
  2501. }
  2502. /***********************************************************************************************
  2503. * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. *
  2504. * *
  2505. * This will cause the team members to search out and follow the nearest friendly object. *
  2506. * *
  2507. * INPUT: none *
  2508. * *
  2509. * OUTPUT: Returns with the delay before the next team logic operation should be performed. *
  2510. * *
  2511. * WARNINGS: none *
  2512. * *
  2513. * HISTORY: *
  2514. * 08/06/1996 JLB : Created. *
  2515. *=============================================================================================*/
  2516. int TeamClass::TMission_Follow(void)
  2517. {
  2518. Calc_Center(Zone, ClosestMember);
  2519. Target = Zone;
  2520. Coordinate_Move();
  2521. return(1);
  2522. }
  2523. /***********************************************************************************************
  2524. * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. *
  2525. * *
  2526. * This is equivalent to a jump or goto command. It will alter the team command processing *
  2527. * such that it will continue processing at the command number specified. *
  2528. * *
  2529. * INPUT: none *
  2530. * *
  2531. * OUTPUT: Returns with the delay before the next team logic operation should be performed. *
  2532. * *
  2533. * WARNINGS: none *
  2534. * *
  2535. * HISTORY: *
  2536. * 08/06/1996 JLB : Created. *
  2537. *=============================================================================================*/
  2538. int TeamClass::TMission_Loop(void)
  2539. {
  2540. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  2541. CurrentMission = mission->Data.Value-1;
  2542. IsNextMission = true;
  2543. return(1);
  2544. }
  2545. /***********************************************************************************************
  2546. * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of time *
  2547. * *
  2548. * This is a team mission that simulates the Iron Curtain device. It will make all team *
  2549. * members invlunerable for a temporary period of time. *
  2550. * *
  2551. * INPUT: none *
  2552. * *
  2553. * OUTPUT: Returns with the time delay before the next team logic operation should occur. *
  2554. * *
  2555. * WARNINGS: none *
  2556. * *
  2557. * HISTORY: *
  2558. * 08/06/1996 JLB : Created. *
  2559. *=============================================================================================*/
  2560. int TeamClass::TMission_Invulnerable(void)
  2561. {
  2562. FootClass * foot = Member;
  2563. while (foot != NULL) {
  2564. foot->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE;
  2565. foot->Mark(MARK_CHANGE);
  2566. foot = foot->Member;
  2567. }
  2568. IsNextMission = true;
  2569. return(1);
  2570. }
  2571. /***********************************************************************************************
  2572. * TeamClass::TMission_Set_Global -- Performs a set global flag operation. *
  2573. * *
  2574. * This routine is used by the team to set a global variable but otherwise perform no *
  2575. * visible effect on the team. By using this routine, sophisticated trigger dependencies *
  2576. * can be implemented. *
  2577. * *
  2578. * INPUT: none *
  2579. * *
  2580. * OUTPUT: Returns with the delay before the next team logic operation should occur. *
  2581. * *
  2582. * WARNINGS: none *
  2583. * *
  2584. * HISTORY: *
  2585. * 08/06/1996 JLB : Created. *
  2586. *=============================================================================================*/
  2587. int TeamClass::TMission_Set_Global(void)
  2588. {
  2589. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  2590. Scen.Set_Global_To(mission->Data.Value, true);
  2591. IsNextMission = true;
  2592. return(1);
  2593. }
  2594. /***********************************************************************************************
  2595. * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. *
  2596. * *
  2597. * A patrolling team will move to the designated waypoint, but along the way it will *
  2598. * periodically scan for nearby enemies. If an enemy is found, the patrol mission turns *
  2599. * into an attack mission until the target is destroyed -- after which it resumes its *
  2600. * patrol duties. *
  2601. * *
  2602. * INPUT: none *
  2603. * *
  2604. * OUTPUT: Returns with the delay before the next call to this routine is needed. *
  2605. * *
  2606. * WARNINGS: none *
  2607. * *
  2608. * HISTORY: *
  2609. * 08/12/1996 JLB : Created. *
  2610. *=============================================================================================*/
  2611. int TeamClass::TMission_Patrol(void)
  2612. {
  2613. /*
  2614. ** Reassign the movement destination if the target has been prematurely
  2615. ** cleared (probably because the object has been destroyed).
  2616. */
  2617. if (!Target_Legal(Target)) {
  2618. TeamMissionClass const * mission = &Class->MissionList[CurrentMission];
  2619. if ((unsigned)mission->Data.Value < WAYPT_COUNT) {
  2620. Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value]));
  2621. }
  2622. }
  2623. /*
  2624. ** Every so often, scan for a nearby enemy.
  2625. */
  2626. if (Frame % (Rule.PatrolTime * TICKS_PER_MINUTE) == 0) {
  2627. FootClass * leader = Fetch_A_Leader();
  2628. if (leader != NULL) {
  2629. TARGET target = leader->Greatest_Threat(THREAT_NORMAL|THREAT_RANGE);
  2630. if (Target_Legal(target)) {
  2631. Assign_Mission_Target(target);
  2632. } else {
  2633. Assign_Mission_Target(TARGET_NONE);
  2634. }
  2635. }
  2636. }
  2637. /*
  2638. ** If the mission target looks like it should be attacked, then do so, otherwise
  2639. ** treat it as a movement destination.
  2640. */
  2641. if (Is_Target_Object(Target)) {
  2642. Coordinate_Attack();
  2643. } else {
  2644. Coordinate_Move();
  2645. }
  2646. return(1);
  2647. }
  2648. int TeamClass::TMission_Deploy(void)
  2649. {
  2650. assert(IsActive);
  2651. assert(Teams.ID(this) == ID);
  2652. FootClass * unit = Member;
  2653. bool finished = true;
  2654. while (unit != NULL) {
  2655. Coordinate_Conscript(unit);
  2656. if (_Is_It_Playing(unit)) {
  2657. if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MCV) {
  2658. if (unit->Mission != MISSION_UNLOAD) {
  2659. unit->Assign_Destination(TARGET_NONE);
  2660. unit->Assign_Target(TARGET_NONE);
  2661. unit->Assign_Mission(MISSION_UNLOAD);
  2662. finished = false;
  2663. }
  2664. }
  2665. if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo != 0) {
  2666. /*
  2667. ** The check for a building is located here because the mine layer may have
  2668. ** already unloaded the mine but is still in the process of retracting
  2669. ** the mine layer. During this time, it should not be considered to have
  2670. ** finished its unload mission.
  2671. */
  2672. if (!Map[unit->Center_Coord()].Cell_Building() && unit->Mission != MISSION_UNLOAD) {
  2673. unit->Assign_Destination(TARGET_NONE);
  2674. unit->Assign_Target(TARGET_NONE);
  2675. unit->Assign_Mission(MISSION_UNLOAD);
  2676. finished = false;
  2677. }
  2678. }
  2679. }
  2680. unit = unit->Member;
  2681. }
  2682. if (finished) {
  2683. IsNextMission = true;
  2684. }
  2685. return(1);
  2686. }
  2687. /***********************************************************************************************
  2688. * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. *
  2689. * *
  2690. * This will scan through the team members looking for one that is suitable as a leader *
  2691. * type. A team can sometimes contain limboed or unarmed members. These members are not *
  2692. * suitable for leadership roles. *
  2693. * *
  2694. * INPUT: none *
  2695. * *
  2696. * OUTPUT: Returns with a suitable leader type unit. *
  2697. * *
  2698. * WARNINGS: none *
  2699. * *
  2700. * HISTORY: *
  2701. * 08/27/1996 JLB : Created. *
  2702. *=============================================================================================*/
  2703. FootClass * TeamClass::Fetch_A_Leader(void) const
  2704. {
  2705. FootClass * leader = Member;
  2706. /*
  2707. ** Scan through the team members trying to find one that is an active member and
  2708. ** is equipped with a weapon.
  2709. */
  2710. while (leader != NULL) {
  2711. if (_Is_It_Playing(leader) && leader->Is_Weapon_Equipped()) break;
  2712. leader = leader->Member;
  2713. }
  2714. /*
  2715. ** If no suitable leader was found, then just return with the first conveniently
  2716. ** accessable team member. This presumes that some member is better than no member
  2717. ** at all.
  2718. */
  2719. if (leader == NULL) {
  2720. leader = Member;
  2721. }
  2722. return(leader);
  2723. }