TEAM.CPP 124 KB

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