UNIT.CPP 173 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051
  1. /*
  2. ** Command & Conquer(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: F:\projects\c&c\vcs\code\unit.cpv 2.17 16 Oct 1995 16:48:28 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 : UNIT.CPP *
  26. * *
  27. * Programmer : Joe L. Bostic *
  28. * *
  29. * Start Date : September 10, 1993 *
  30. * *
  31. * Last Update : August 16, 1995 [JLB] *
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * Recoil_Adjust -- Adjust pixel values in direction specified. *
  36. * Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. *
  37. * UnitClass::AI -- AI processing for the unit. *
  38. * UnitClass::APC_Close_Door -- Closes an APC door. *
  39. * UnitClass::APC_Open_Door -- Opens an APC door. *
  40. * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possib*
  41. * UnitClass::As_Target -- Returns the unit as a target value. *
  42. * UnitClass::Blocking_Object -- Determines how a object blocks a unit *
  43. * UnitClass::Can_Enter_Building -- Determines building entry legality. *
  44. * UnitClass::Can_Fire -- Determines if this unit can fire. *
  45. * UnitClass::Can_Player_Move -- Determines if the player is legally allowed to move it. *
  46. * UnitClass::Click_With -- Handles player map clicking while this unit is selected. *
  47. * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. *
  48. * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. *
  49. * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. *
  50. * UnitClass::Draw_It -- Draws a unit object. *
  51. * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. *
  52. * UnitClass::Find_LZ -- Maintenance function for transport units. *
  53. * UnitClass::Flag_Attach -- Attaches a house flag to this unit. *
  54. * UnitClass::Flag_Remove -- Removes the house flag from this unit. *
  55. * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. *
  56. * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. *
  57. * UnitClass::Harvesting -- Harvests tiberium at the current location. *
  58. * UnitClass::Init -- Clears all units for scenario preparation. *
  59. * UnitClass::Limbo -- Prepares vehicle and then limbos it. *
  60. * UnitClass::Look -- Perform map revelation from a unit's position. *
  61. * UnitClass::Mission_Attack -- Handles the mission attack logic. *
  62. * UnitClass::Mission_Guard -- Special guard mission override processor. *
  63. * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. *
  64. * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. *
  65. * UnitClass::Mission_Move -- Handles special move mission overrides. *
  66. * UnitClass::Mission_Unload -- Handles unloading cargo. *
  67. * UnitClass::Overlap_List -- Determines overlap list for units. *
  68. * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. *
  69. * UnitClass::Pip_Count -- Fetchs the number of pips to display on unit. *
  70. * UnitClass::Random_Animate -- Handles random idle animation for the unit. *
  71. * UnitClass::Read_INI -- Reads units from scenario INI file. *
  72. * UnitClass::Receive_Message -- Handles receiving a radio message. *
  73. * UnitClass::Remap_Table -- Fetches the remap table to use for this object. *
  74. * UnitClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. *
  75. * UnitClass::Response_Move -- Voice feedback when ordering the unit to move. *
  76. * UnitClass::Response_Select -- Voice feedback when selecting the unit. *
  77. * UnitClass::Scatter -- Causes the unit to travel to a nearby safe cell. *
  78. * UnitClass::Set_Speed -- Initiate unit movement physics. *
  79. * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. *
  80. * UnitClass::Start_Driver -- Starts driving and reserves destination cell. *
  81. * UnitClass::Stop_Driver -- Handles removing occupation bits when driving stops. *
  82. * UnitClass::Stun -- Stuns the unit in preparation for unit removal. *
  83. * UnitClass::Take_Damage -- Inflicts damage points on a unit. *
  84. * UnitClass::Target_Coord -- The coordinate to use when targeting this unit. *
  85. * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. *
  86. * UnitClass::UnitClass -- Constructor for units. *
  87. * UnitClass::Unlimbo -- Removes unit from stasis. *
  88. * UnitClass::Unload_Hovercraft_Process -- Handles unloading hovercraft transport. *
  89. * UnitClass::Validate -- validates unit pointer. *
  90. * UnitClass::What_Action -- Determines what action would occur if clicked on object. *
  91. * UnitClass::What_Am_I -- Returns with the RTTI type this object is. *
  92. * UnitClass::Write_INI -- Writes all the units out to an INI file. *
  93. * UnitClass::delete -- Deletion operator for units. *
  94. * UnitClass::new -- Allocate a unit slot and adjust access arrays. *
  95. * UnitClass::~UnitClass -- Destructor for unit objects. *
  96. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  97. #include "function.h"
  98. /*
  99. ** This contains the value of the Virtual Function Table Pointer
  100. */
  101. void * UnitClass::VTable;
  102. /***********************************************************************************************
  103. * UnitClass::Validate -- validates unit pointer. *
  104. * *
  105. * INPUT: *
  106. * none. *
  107. * *
  108. * OUTPUT: *
  109. * 1 = ok, 0 = error *
  110. * *
  111. * WARNINGS: *
  112. * none. *
  113. * *
  114. * HISTORY: *
  115. * 08/09/1995 BRR : Created. *
  116. *=============================================================================================*/
  117. #ifdef CHEAT_KEYS
  118. int UnitClass::Validate(void) const
  119. {
  120. int num;
  121. num = Units.ID(this);
  122. if (num < 0 || num >= UNIT_MAX) {
  123. Validate_Error("UNIT");
  124. return (0);
  125. }
  126. else
  127. return (1);
  128. }
  129. #else
  130. #define Validate()
  131. #endif
  132. /***********************************************************************************************
  133. * Recoil_Adjust -- Adjust pixel values in direction specified. *
  134. * *
  135. * This is a helper routine that modifies the pixel coordinates provided according to the *
  136. * direction specified. The effect is the simulate recoil effects by moving an object 'back'*
  137. * one pixel. Since the pixels moved depend on facing, this routine handles the pixel *
  138. * adjustment quickly. *
  139. * *
  140. * INPUT: dir -- The direction to base the recoil on. *
  141. * *
  142. * x,y -- References to the pixel coordinates that will be adjusted. *
  143. * *
  144. * OUTPUT: none *
  145. * *
  146. * WARNINGS: none *
  147. * *
  148. * HISTORY: *
  149. * 05/08/1995 JLB : Created. *
  150. *=============================================================================================*/
  151. void Recoil_Adjust(DirType dir, int &x, int &y)
  152. {
  153. static struct {
  154. signed char X,Y;
  155. } _adjust[32] = {
  156. {0,1}, // N
  157. {0,1},
  158. {0,1},
  159. {-1,1},
  160. {-1,1}, // NE
  161. {-1,1},
  162. {-1,0},
  163. {-1,0},
  164. {-1,0}, // E
  165. {-1,0},
  166. {-1,-1},
  167. {-1,-1},
  168. {-1,-1}, // SE
  169. {-1,-1},
  170. {-1,-1},
  171. {0,-1},
  172. {0,-1}, // S
  173. {0,-1},
  174. {0,-1},
  175. {1,-1},
  176. {1,-1}, // SW
  177. {1,-1},
  178. {1,0},
  179. {1,0},
  180. {1,0}, // W
  181. {1,0},
  182. {1,1},
  183. {1,1},
  184. {1,1}, // NW
  185. {1,1},
  186. {0,1},
  187. {0,1}
  188. };
  189. int index = Facing_To_32(dir);
  190. x += _adjust[index].X;
  191. y += _adjust[index].Y;
  192. }
  193. /***********************************************************************************************
  194. * Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. *
  195. * *
  196. * This routine adjusts the pixel coordinates specified to account for the displacement of *
  197. * the turret on the MLRS and MSAM vehicles. *
  198. * *
  199. * INPUT: dir -- The direction of the body of the vehicle. *
  200. * *
  201. * x,y -- References to the turret center pixel position. These will be modified as *
  202. * necessary. *
  203. * *
  204. * OUTPUT: none *
  205. * *
  206. * WARNINGS: none *
  207. * *
  208. * HISTORY: *
  209. * 05/08/1995 JLB : Created. *
  210. *=============================================================================================*/
  211. void Turret_Adjust(DirType dir, int &x, int &y)
  212. {
  213. static struct {
  214. signed char X,Y;
  215. } _adjust[32] = {
  216. {1,2}, // N
  217. {-1,1},
  218. {-2,0},
  219. {-3,0},
  220. {-3,1}, // NW
  221. {-4,-1},
  222. {-4,-1},
  223. {-5,-2},
  224. {-5,-3}, // W
  225. {-5,-3},
  226. {-3,-3},
  227. {-3,-4},
  228. {-3,-4}, // SW
  229. {-3,-5},
  230. {-2,-5},
  231. {-1,-5},
  232. {0,-5}, // S
  233. {1,-6},
  234. {2,-5},
  235. {3,-5},
  236. {4,-5}, // SE
  237. {6,-4},
  238. {6,-3},
  239. {6,-3},
  240. {6,-3}, // E
  241. {5,-1},
  242. {5,-1},
  243. {4,0},
  244. {3,0}, // NE
  245. {2,0},
  246. {2,1},
  247. {1,2}
  248. };
  249. int index = Facing_To_32(dir);
  250. x += _adjust[index].X;
  251. y += _adjust[index].Y;
  252. }
  253. /***********************************************************************************************
  254. * UnitClass::As_Target -- Returns the unit as a target value. *
  255. * *
  256. * This routine will convert the unit into a target value that is then returned. Target *
  257. * values are typically used for navigation and other computer uses. *
  258. * *
  259. * INPUT: none *
  260. * *
  261. * OUTPUT: Returns with target value of unit. *
  262. * *
  263. * WARNINGS: none *
  264. * *
  265. * HISTORY: *
  266. * 09/19/1994 JLB : Created. *
  267. *=============================================================================================*/
  268. TARGET UnitClass::As_Target(void) const
  269. {
  270. Validate();
  271. return(Build_Target(KIND_UNIT, Units.ID(this)));
  272. }
  273. #ifdef CHEAT_KEYS
  274. /***********************************************************************************************
  275. * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. *
  276. * *
  277. * This displays the current status of the unit class to the mono monitor. By this display *
  278. * bugs may be tracked down or prevented. *
  279. * *
  280. * INPUT: none *
  281. * *
  282. * OUTPUT: none *
  283. * *
  284. * WARNINGS: none *
  285. * *
  286. * HISTORY: *
  287. * 06/02/1994 JLB : Created. *
  288. *=============================================================================================*/
  289. void UnitClass::Debug_Dump(MonoClass *mono) const
  290. {
  291. Validate();
  292. mono->Set_Cursor(0, 0);
  293. mono->Print(
  294. "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂNavCom:ÂRadio:ÂCoord:ÄÄÂHeadTo:ÄÂSt:Ä¿\n"
  295. "³ ³ ³ ³ ³ ³ ³ ³ ³\n"
  296. "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂBody:ÂTurret:ÂSpeed:ÂPath:ÁÄÄÄÄÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n"
  297. "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n"
  298. "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"
  299. "³Owned.........³ ³ ³Last Message: ³\n"
  300. "³Discovered....³ ³ ÃTimer:ÂArm:ÂTrack:ÂTiberium:ÂFlash:ÂStage:ÂTeam:ÄÄÄÄÂArch:´\n"
  301. "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³\n"
  302. "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÙ\n"
  303. "³Locked on Map.³ ³ ³ \n"
  304. "³Turret Locked.³ ³ ³ \n"
  305. "³Is A Loaner...³ ³ ³ \n"
  306. "³Deploying.....³ ³ ³ \n"
  307. "³Rotating......³ ³ ³ \n"
  308. "³Firing........³ ³ ³ \n"
  309. "³Driving.......³ ³ ³ \n"
  310. "³To Look.......³ ³ ³ \n"
  311. "³Recoiling.....³ ³ ³ \n"
  312. "³To Display....³ ³ ³ \n"
  313. "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n");
  314. mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName);
  315. CargoClass::Debug_Dump(mono);
  316. MissionClass::Debug_Dump(mono);
  317. TarComClass::Debug_Dump(mono);
  318. }
  319. #endif
  320. /***********************************************************************************************
  321. * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. *
  322. * *
  323. * This routine is used by the rendering system in order to sort the *
  324. * game objects in a back to front order. This is now the correct *
  325. * overlap effect is achieved. *
  326. * *
  327. * INPUT: none *
  328. * *
  329. * OUTPUT: Returns with a coordinate value that can be used for sorting. *
  330. * *
  331. * WARNINGS: none *
  332. * *
  333. * HISTORY: *
  334. * 05/17/1994 JLB : Created. *
  335. *=============================================================================================*/
  336. COORDINATE UnitClass::Sort_Y(void) const
  337. {
  338. Validate();
  339. if (IsTethered && *this == UNIT_HOVER) {
  340. return(Coord_Add(Coord, 0xFF800000L));
  341. }
  342. return(Coord_Add(Coord, 0x00800000L));
  343. }
  344. /***********************************************************************************************
  345. * UnitClass::AI -- AI processing for the unit. *
  346. * *
  347. * This routine will perform the AI processing necessary for the unit. These are non- *
  348. * graphic related operations. *
  349. * *
  350. * INPUT: none *
  351. * *
  352. * OUTPUT: none *
  353. * *
  354. * WARNINGS: none *
  355. * *
  356. * HISTORY: *
  357. * 05/31/1994 JLB : Created. *
  358. *=============================================================================================*/
  359. void UnitClass::AI(void)
  360. {
  361. Validate();
  362. /*
  363. ** Act on new orders if the unit is at a good position to do so.
  364. */
  365. if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) {
  366. Commence();
  367. }
  368. TarComClass::AI();
  369. /*
  370. ** Delete this unit if it finds itself off the edge of the map and it is in
  371. ** guard or other static mission mode.
  372. */
  373. if (!Team && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) {
  374. Stun();
  375. delete this;
  376. return;
  377. }
  378. /*
  379. ** Rocket launchers will reload every so often.
  380. */
  381. if (*this == UNIT_MSAM && Ammo < Class->MaxAmmo) {
  382. if (IsDriving) {
  383. Reload = Reload + 1;
  384. } else {
  385. if (Reload.Expired()) {
  386. Ammo++;
  387. if (Ammo < Class->MaxAmmo) {
  388. Reload = TICKS_PER_SECOND*30;
  389. }
  390. Mark(MARK_CHANGE);
  391. }
  392. }
  393. }
  394. /*
  395. ** Hover landers always are flagged to redraw since they don't record themselves
  396. ** on the map in the normal fashion.
  397. */
  398. if (*this == UNIT_HOVER) {
  399. // Mark_For_Redraw();
  400. //if (IsDown) Mono_Printf("*");
  401. Mark(MARK_CHANGE);
  402. }
  403. /*
  404. ** If this is a vehicle that heals itself (e.g., Mammoth Tank), then it will perform
  405. ** the heal logic here.
  406. */
  407. if (*this == UNIT_HTANK && (Frame % 16) == 0 && Health_Ratio() < 0x0080) {
  408. Strength++;
  409. Mark(MARK_CHANGE);
  410. }
  411. if (*this == UNIT_VICE && Map[Coord_Cell(Coord)].Land_Type() == LAND_TIBERIUM && Health_Ratio() < 0x0100 && (Frame % 16) == 0) {
  412. Strength++;
  413. Mark(MARK_CHANGE);
  414. }
  415. /*
  416. ** Transporters require special logic handled here since there isn't a MISSION_WAIT_FOR_PASSENGERS
  417. ** mission that they can follow. Passenger loading is merely a part of their normal operation.
  418. */
  419. if (Class->IsTransporter) {
  420. /*
  421. ** Double check that there is a passenger that is trying to load or unload.
  422. ** If not, then close the door.
  423. */
  424. if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER) {
  425. APC_Close_Door();
  426. }
  427. }
  428. /*
  429. ** Don't start a new mission unless the vehicle is in the center of
  430. ** a cell (not driving) and the door (if any) is closed.
  431. */
  432. if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) {
  433. Commence();
  434. }
  435. /*
  436. ** Handle recoil recovery here.
  437. */
  438. if (IsInRecoilState) {
  439. IsInRecoilState = false;
  440. Mark(MARK_CHANGE);
  441. }
  442. /*
  443. ** For animating objects such as Visceroids, max-out their animation
  444. ** stages here
  445. */
  446. if (Class->IsAnimating) {
  447. if (!Fetch_Rate()) Set_Rate(2);
  448. StageClass::Graphic_Logic();
  449. if (Fetch_Stage() >= Get_Build_Frame_Count(Class->Get_Image_Data())-1) {
  450. Set_Stage(0);
  451. }
  452. }
  453. /*
  454. ** for Jurassic objects, animate them if they're walking
  455. */
  456. if (Class->IsPieceOfEight && Special.IsJurassic && AreThingiesEnabled) {
  457. // Only animate if they're walking
  458. if (IsDriving || IsFiring) {
  459. if (!Fetch_Rate()) {
  460. Set_Rate(Options.Normalize_Delay(2));
  461. Set_Stage(0);
  462. }
  463. StageClass::Graphic_Logic();
  464. if (Fetch_Stage() >= ( (IsDriving || *this == UNIT_TREX || *this == UNIT_RAPT) ? 8 : 12) ) {
  465. Set_Stage(0);
  466. if (IsFiring) {
  467. Set_Rate(0);
  468. IsFiring = false;
  469. }
  470. }
  471. }
  472. }
  473. /*
  474. ** A cloaked object that is carrying the flag will always shimmer.
  475. */
  476. if (Cloak == CLOAKED && Flagged != HOUSE_NONE) {
  477. Do_Shimmer();
  478. }
  479. }
  480. /***********************************************************************************************
  481. * UnitClass::Can_Fire -- Determines if this unit can fire. *
  482. * *
  483. * INPUT: target -- The target that is desired to fire upon. *
  484. * *
  485. * which -- The weapon (primary=0, secondary=1) to fire. *
  486. * *
  487. * OUTPUT: Returns with the fire error number if it cannot fire or else FIRE_OK. *
  488. * *
  489. * WARNINGS: none *
  490. * *
  491. * HISTORY: *
  492. * 05/08/1995 JLB : Created. *
  493. *=============================================================================================*/
  494. FireErrorType UnitClass::Can_Fire(TARGET target, int which) const
  495. {
  496. Validate();
  497. FireErrorType cf;
  498. cf = TarComClass::Can_Fire(target, which);
  499. if (cf == FIRE_OK) {
  500. /*
  501. ** If it's a dinosaur, when it's OK to fire we should start the firing
  502. ** animation, but wait for the proper attack stage before starting the
  503. ** bullet, so things won't die prematurely.
  504. */
  505. if (Class->IsFireAnim) {
  506. if (!IsFiring) {
  507. UnitClass *nonconst;
  508. nonconst = (UnitClass *)this;
  509. nonconst->Set_Rate(Options.Normalize_Delay(2));
  510. nonconst->Set_Stage(0);
  511. IsFiring = true;
  512. cf = FIRE_BUSY;
  513. } else {
  514. if (Fetch_Stage() < 4) cf = FIRE_BUSY;
  515. }
  516. }
  517. }
  518. return(cf);
  519. }
  520. /***********************************************************************************************
  521. * UnitClass::Receive_Message -- Handles receiving a radio message. *
  522. * *
  523. * This is the handler function for when a unit receives a radio *
  524. * message. Typical use of this is when a unit unloads from a hover *
  525. * class so that clearing of the transport is successful. *
  526. * *
  527. * INPUT: from -- Pointer to the originator of the message. *
  528. * *
  529. * message -- The radio message received. *
  530. * *
  531. * param -- Reference to an optional parameter the might be needed to return *
  532. * information back to the originator of the message. *
  533. * *
  534. * OUTPUT: Returns with the radio message response. *
  535. * *
  536. * WARNINGS: none *
  537. * *
  538. * HISTORY: *
  539. * 05/22/1994 JLB : Created. *
  540. *=============================================================================================*/
  541. RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
  542. {
  543. Validate();
  544. switch (message) {
  545. /*
  546. ** Asks if the passenger can load on this transport.
  547. */
  548. case RADIO_CAN_LOAD:
  549. if (Class->IsTransporter && How_Many() < Class->Max_Passengers() && from && House->Class->House == from->Owner()) {
  550. return(RADIO_ROGER);
  551. }
  552. return(RADIO_NEGATIVE);
  553. /*
  554. ** The refinery has told this harvester that it should begin the backup procedure
  555. ** so that proper unloading may take place.
  556. */
  557. case RADIO_BACKUP_NOW:
  558. TarComClass::Receive_Message(from, message, param);
  559. if (!IsRotating && PrimaryFacing != DIR_SW) {
  560. Do_Turn(DIR_SW);
  561. } else {
  562. if (!IsDriving) {
  563. Force_Track(BACKUP_INTO_REFINERY, Adjacent_Cell(Center_Coord(), FACING_N));
  564. Set_Speed(128);
  565. }
  566. }
  567. return(RADIO_ROGER);
  568. /*
  569. ** This message is sent by the passenger when it determines that it has
  570. ** entered the transport.
  571. */
  572. case RADIO_IM_IN:
  573. if (How_Many() == Class->Max_Passengers()) {
  574. APC_Close_Door();
  575. }
  576. return(RADIO_ATTACH);
  577. /*
  578. ** Docking maintenance message received. Check to see if new orders should be given
  579. ** to the impatient unit.
  580. */
  581. case RADIO_DOCKING:
  582. if (Class->IsTransporter && *this == UNIT_APC && How_Many() < Class->Max_Passengers()) {
  583. TarComClass::Receive_Message(from, message, param);
  584. if (!IsDriving && !IsRotating && !IsTethered) {
  585. /*
  586. ** If the potential passenger needs someplace to go, then figure out a good
  587. ** spot and tell it to go.
  588. */
  589. if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) {
  590. CELL cell;
  591. DirType dir = Desired_Load_Dir(from, cell);
  592. /*
  593. ** If no adjacent free cells are detected, then passenger loading
  594. ** cannot occur. Break radio contact.
  595. */
  596. if (cell == 0) {
  597. Transmit_Message(RADIO_OVER_OUT, from);
  598. } else {
  599. param = (long)::As_Target(cell);
  600. Do_Turn(dir);
  601. /*
  602. ** If it is now facing the correct direction, then open the
  603. ** transport doors. Close the doors if the transport is or needs
  604. ** to rotate.
  605. */
  606. if (IsRotating) {
  607. if (!Is_Door_Closed()) {
  608. APC_Close_Door();
  609. }
  610. } else {
  611. if (!Is_Door_Open()) {
  612. APC_Open_Door();
  613. }
  614. }
  615. /*
  616. ** Tell the potential passenger where it should go. If the passenger is
  617. ** already at the staging location, then tell it to move onto the transport
  618. ** directly.
  619. */
  620. if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) {
  621. if (Is_Door_Open()) {
  622. param = (long)As_Target();
  623. Transmit_Message(RADIO_TETHER);
  624. if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) {
  625. Transmit_Message(RADIO_OVER_OUT, from);
  626. } else {
  627. Contact_With_Whom()->Unselect();
  628. }
  629. }
  630. }
  631. }
  632. }
  633. }
  634. return(RADIO_ROGER);
  635. }
  636. break;
  637. /*
  638. ** When this message is received, it means that the other object
  639. ** has already turned its radio off. Turn this radio off as well.
  640. */
  641. case RADIO_OVER_OUT:
  642. if (Mission == MISSION_RETURN) {
  643. Assign_Mission(MISSION_GUARD);
  644. }
  645. TarComClass::Receive_Message(from, message, param);
  646. return(RADIO_ROGER);
  647. }
  648. return(TarComClass::Receive_Message(from, message, param));
  649. }
  650. /***********************************************************************************************
  651. * UnitClass::Unlimbo -- Removes unit from stasis. *
  652. * *
  653. * This routine will place a unit into the game and out of its limbo *
  654. * state. This occurs whenever a unit is unloaded from a transport. *
  655. * *
  656. * INPUT: coord -- The coordinate to make the unit appear. *
  657. * *
  658. * dir -- The initial facing to impart upon the unit. *
  659. * *
  660. * OUTPUT: bool; Was the unit unlimboed successfully? If the desired *
  661. * coordinate is illegal, then this might very well return *
  662. * false. *
  663. * *
  664. * WARNINGS: none *
  665. * *
  666. * HISTORY: *
  667. * 05/22/1994 JLB : Created. *
  668. *=============================================================================================*/
  669. bool UnitClass::Unlimbo(COORDINATE coord, DirType dir)
  670. {
  671. Validate();
  672. /*
  673. ** All units must start out facing one of the 8 major directions.
  674. */
  675. dir = Facing_Dir(Dir_Facing(dir));
  676. if (TarComClass::Unlimbo(coord, dir)) {
  677. /*
  678. ** Ensure that the owning house knows about the
  679. ** new object.
  680. */
  681. House->UScan |= (1L << Class->Type);
  682. House->ActiveUScan |= (1L << Class->Type);
  683. /*
  684. ** If it starts off the edge of the map, then it already starts cloaked.
  685. */
  686. if (IsCloakable && !IsLocked) Cloak = CLOAKED;
  687. /*
  688. ** Units default to no special animation.
  689. */
  690. Set_Rate(0);
  691. Set_Stage(0);
  692. /*
  693. ** A gun boat and a hover craft are allowed to exit the map thus we
  694. ** flag them so they can. This undoes the work of Techno::Unlimbo which
  695. ** stole their IsALoaner flag.
  696. */
  697. if (*this == UNIT_GUNBOAT || *this == UNIT_HOVER) {
  698. IsALoaner = true;
  699. }
  700. /*
  701. ** Start the gunboat animating when it is unlimboed.
  702. */
  703. if (*this == UNIT_GUNBOAT) {
  704. Set_Rate(2);
  705. Set_Stage(0);
  706. }
  707. return(true);
  708. }
  709. return(false);
  710. }
  711. /***********************************************************************************************
  712. * UnitClass::Take_Damage -- Inflicts damage points on a unit. *
  713. * *
  714. * This routine will inflict the specified number of damage points on *
  715. * the given unit. If the unit is destroyed, then this routine will *
  716. * remove the unit cleanly from the game. The return value indicates *
  717. * whether the unit was destroyed. This will allow appropriate death *
  718. * animation or whatever. *
  719. * *
  720. * INPUT: damage-- The number of damage points to inflict. *
  721. * *
  722. * distance -- The distance from the damage center point to the object's center point.*
  723. * *
  724. * warhead--The type of damage to inflict. *
  725. * *
  726. * source -- Who is responsible for this damage? *
  727. * *
  728. * OUTPUT: Returns the result of the damage process. This can range from RESULT_NONE up to *
  729. * RESULT_DESTROYED. *
  730. * *
  731. * WARNINGS: none *
  732. * *
  733. * HISTORY: *
  734. * 05/30/1991 JLB : Created. *
  735. * 07/12/1991 JLB : Script initiated by unit destruction. *
  736. * 04/15/1994 JLB : Converted to member function. *
  737. * 04/16/1994 JLB : Warhead modifier. *
  738. * 06/03/1994 JLB : Added the source of the damage target value. *
  739. * 06/20/1994 JLB : Source is a base class pointer. *
  740. * 11/22/1994 JLB : Shares base damage handler for techno objects. *
  741. * 06/30/1995 JLB : Lasers do maximum damage against gunboat. *
  742. * 08/16/1995 JLB : Harvester crushing doesn't occur on early missions. *
  743. *=============================================================================================*/
  744. ResultType UnitClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source)
  745. {
  746. Validate();
  747. ResultType res = RESULT_NONE;
  748. /*
  749. ** Special case: If this is a laser attacking a gunboat, then the gunboat is ALWAYS toasted.
  750. */
  751. if (*this == UNIT_GUNBOAT && warhead == WARHEAD_LASER) {
  752. damage = Strength*3;
  753. }
  754. /*
  755. ** Remember if this object was selected. If it was and it gets destroyed and it has
  756. ** passengers that pop out, then the passengers will inherit the select state.
  757. */
  758. bool select = (IsSelected && IsOwnedByPlayer);
  759. /*
  760. ** In order for a this to be damaged, it must either be a unit
  761. ** with a crew or a sandworm.
  762. */
  763. res = TarComClass::Take_Damage(damage, distance, warhead, source);
  764. if (res == RESULT_DESTROYED) {
  765. Death_Announcement(source);
  766. if (Class->Explosion != ANIM_NONE) {
  767. AnimType anim = Class->Explosion;
  768. /*
  769. ** SSM launchers will really explode big if they are carrying
  770. ** missiles at the time of the explosion.
  771. */
  772. if (*this == UNIT_MSAM && Ammo) {
  773. anim = ANIM_NAPALM3;
  774. }
  775. if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) {
  776. Sound_Effect(VOC_DINODIE1, Coord);
  777. }
  778. new AnimClass(anim, Coord);
  779. /*
  780. ** When the flame tank blows up, it really blows up. It is
  781. ** equivalent to a napalm strike.
  782. */
  783. if (Class->Explosion == ANIM_NAPALM3) {
  784. Explosion_Damage(Coord, 200, source, WARHEAD_FIRE);
  785. }
  786. /*
  787. ** Very strong units that have an explosion will also rock the
  788. ** screen when they are destroyed.
  789. */
  790. if (Class->MaxStrength > 400) {
  791. Shake_Screen(3);
  792. }
  793. }
  794. /*
  795. ** Possibly have the crew member run away.
  796. */
  797. Mark(MARK_UP);
  798. if (Class->IsCrew && !Class->IsTransporter) {
  799. if (Random_Pick(0, 1) == 0) {
  800. InfantryClass * i = 0;
  801. if (Class->Primary == WEAPON_NONE) {
  802. i = new InfantryClass(INFANTRY_C1, House->Class->House);
  803. i->IsTechnician = true;
  804. } else {
  805. i = new InfantryClass(INFANTRY_E1, House->Class->House);
  806. }
  807. if (i) {
  808. if (i->Unlimbo(Coord, DIR_N)) {
  809. i->Strength = Random_Pick(5, (int)i->Class->MaxStrength/2);
  810. i->Scatter(0, true);
  811. if (!House->IsHuman) {
  812. i->Assign_Mission(MISSION_HUNT);
  813. } else {
  814. i->Assign_Mission(MISSION_GUARD);
  815. }
  816. if (select) i->Select();
  817. } else {
  818. delete i;
  819. }
  820. }
  821. }
  822. } else {
  823. if (*this != UNIT_HOVER) {
  824. while (Is_Something_Attached()) {
  825. FootClass * object = Detach_Object();
  826. if (!object) break; // How can this happen?
  827. /*
  828. ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure
  829. ** thing.
  830. */
  831. if (object->Is_Infantry() && object->Unlimbo(Coord, DIR_N)) {
  832. // object->Strength = Random_Pick(5, (int)((InfantryClass*)object)->Class->MaxStrength/2);
  833. object->Scatter(0, true);
  834. if (select) object->Select();
  835. } else {
  836. object->Record_The_Kill(source);
  837. delete object;
  838. }
  839. }
  840. } else {
  841. Kill_Cargo(source);
  842. }
  843. }
  844. /*
  845. ** When the mobile head quarters blows up, the entire side blows up.
  846. */
  847. if (*this == UNIT_MHQ) {
  848. House->Flag_To_Die();
  849. }
  850. /*
  851. ** Finally, delete the vehicle.
  852. */
  853. delete this;
  854. } else {
  855. /*
  856. ** When damaged and below half strength, start smoking if
  857. ** it isn't already smoking.
  858. */
  859. if (Health_Ratio() < 0x0080 && !IsAnimAttached && *this != UNIT_VICE && *this != UNIT_STEG && *this != UNIT_TREX && *this != UNIT_TRIC && *this != UNIT_RAPT) {
  860. AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8)));
  861. if (anim) anim->Attach_To(this);
  862. }
  863. /*
  864. ** If at half damage, then start smoking or burning as appropriate.
  865. */
  866. if (res == RESULT_HALF) {
  867. if (*this == UNIT_GUNBOAT) {
  868. AnimClass * anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Add(Coord, XYP_Coord(Random_Pick(0, 16)-8, -2)));
  869. if (anim) anim->Attach_To(this);
  870. }
  871. }
  872. /*
  873. ** Try to crush anyone that fires on this unit if possible. The harvester
  874. ** typically is the only one that will qualify here.
  875. */
  876. if (!Team && source && !IsTethered && !House->Is_Ally(source) && (!House->IsHuman || Special.IsSmartDefense)) {
  877. /*
  878. ** Try to crush the attacker if it can be crushed by this unit and this unit is
  879. ** not equipped with a flame type weapon. If this unit has a weapon and the target
  880. ** is not very close, then fire on it instead. In easy mode, they never run over the
  881. ** player. In hard mode, they always do. In normal mode, they only overrun past
  882. ** mission #8.
  883. */
  884. if ((Class->Primary == WEAPON_NONE || (Distance(source) < 0x0180 && BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead != WARHEAD_FIRE)) &&
  885. (GameToPlay != GAME_NORMAL ||
  886. *this != UNIT_HARVESTER ||
  887. BuildLevel > 8 ||
  888. Special.IsDifficult) &&
  889. !Special.IsEasy &&
  890. Class->IsCrusher && source->Is_Techno() && ((TechnoTypeClass const &)source->Class_Of()).IsCrushable) {
  891. Assign_Destination(source->As_Target());
  892. Assign_Mission(MISSION_MOVE);
  893. } else {
  894. /*
  895. ** Try to return to base if possible.
  896. */
  897. if (*this == UNIT_HARVESTER && Pip_Count() && Health_Ratio() < 0x0080) {
  898. /*
  899. ** Find nearby refinery and head to it?
  900. */
  901. BuildingClass * building = Find_Docking_Bay(STRUCT_REFINERY, false);
  902. /*
  903. ** Since the refinery said it was ok to load, establish radio
  904. ** contact with the refinery and then await docking orders.
  905. */
  906. if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) {
  907. Assign_Mission(MISSION_ENTER);
  908. }
  909. }
  910. }
  911. }
  912. /*
  913. ** Computer controlled harvester will radio for help if they are attacked.
  914. */
  915. if (*this == UNIT_HARVESTER && !House->IsHuman && source) {
  916. Base_Is_Attacked(source);
  917. }
  918. }
  919. return(res);
  920. }
  921. /***********************************************************************************************
  922. * UnitClass::new -- Allocate a unit slot and adjust access arrays. *
  923. * *
  924. * This routine will allocate a unit from the available unit pool and *
  925. * fixup all the access lists to match. It will allocate a unit slot *
  926. * from within the range allowed for the specified unit type. If no *
  927. * slot was found, then it will fail. *
  928. * *
  929. * INPUT: none *
  930. * *
  931. * OUTPUT: Returns with a pointer to the allocated unit. *
  932. * *
  933. * WARNINGS: none *
  934. * *
  935. * HISTORY: *
  936. * 04/11/1994 JLB : Created. *
  937. * 04/21/1994 JLB : Converted to operator new. *
  938. *=============================================================================================*/
  939. void * UnitClass::operator new(size_t)
  940. {
  941. void * ptr = (UnitClass *)Units.Allocate();
  942. if (ptr) {
  943. ((UnitClass *)ptr)->IsActive = true;
  944. }
  945. return(ptr);
  946. }
  947. /***********************************************************************************************
  948. * UnitClass::delete -- Deletion operator for units. *
  949. * *
  950. * This removes the unit from the local allocation system. Since this *
  951. * is a fixed block of memory, not much has to be done to delete the *
  952. * unit. Merely marking it as inactive is enough. *
  953. * *
  954. * INPUT: ptr -- Pointer to the unit to delete. *
  955. * *
  956. * OUTPUT: none *
  957. * *
  958. * WARNINGS: none *
  959. * *
  960. * HISTORY: *
  961. * 04/21/1994 JLB : Created. *
  962. *=============================================================================================*/
  963. void UnitClass::operator delete(void *ptr)
  964. {
  965. if (ptr) {
  966. ((UnitClass *)ptr)->IsActive = false;
  967. }
  968. Units.Free((UnitClass *)ptr);
  969. }
  970. /***********************************************************************************************
  971. * UnitClass::~UnitClass -- Destructor for unit objects. *
  972. * *
  973. * This destructor will lower the unit count for the owning house as well as inform any *
  974. * other units in communication, that this unit is about to leave reality. *
  975. * *
  976. * INPUT: none *
  977. * *
  978. * OUTPUT: none *
  979. * *
  980. * WARNINGS: none *
  981. * *
  982. * HISTORY: *
  983. * 08/15/1994 JLB : Created. *
  984. *=============================================================================================*/
  985. UnitClass::~UnitClass(void)
  986. {
  987. if (GameActive && Class) {
  988. /*
  989. ** If there are any cargo members, delete them.
  990. */
  991. while (Is_Something_Attached()) {
  992. delete Detach_Object();
  993. }
  994. Limbo();
  995. }
  996. if (GameActive && Team) Team->Remove(this);
  997. }
  998. /***********************************************************************************************
  999. * UnitClass::UnitClass -- Constructor for units. *
  1000. * *
  1001. * This constructor for units will initialize the unit into the game *
  1002. * system. It will be placed in all necessary tracking lists. The initial condition will *
  1003. * be in a state of limbo. *
  1004. * *
  1005. * INPUT: classid -- The type of unit to create. *
  1006. * *
  1007. * house -- The house owner of this unit. *
  1008. * *
  1009. * OUTPUT: none *
  1010. * *
  1011. * WARNINGS: none *
  1012. * *
  1013. * HISTORY: *
  1014. * 04/21/1994 JLB : Created. *
  1015. *=============================================================================================*/
  1016. UnitClass::UnitClass(UnitType classid, HousesType house) :
  1017. TarComClass(classid, house)
  1018. {
  1019. Flagged = HOUSE_NONE;
  1020. Reload = 0;
  1021. Ammo = Class->MaxAmmo;
  1022. IsCloakable = Class->IsCloakable;
  1023. if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3));
  1024. /*
  1025. ** Keep count of the number of units created.
  1026. */
  1027. if (GameToPlay == GAME_INTERNET){
  1028. House->UnitTotals->Increment_Unit_Total((int)classid);
  1029. }
  1030. }
  1031. /***********************************************************************************************
  1032. * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possible*
  1033. * *
  1034. * This routine intercepts the active click operation. It check to see if this is a self *
  1035. * deployment request (MCV's have this ability). If it is, then the object is initiated *
  1036. * to self deploy. In the other cases, it passes the operation down to the lower *
  1037. * classes for processing. *
  1038. * *
  1039. * INPUT: action -- The action requested of the unit. *
  1040. * *
  1041. * object -- The object that the mouse pointer is over. *
  1042. * *
  1043. * OUTPUT: none *
  1044. * *
  1045. * WARNINGS: none *
  1046. * *
  1047. * HISTORY: *
  1048. * 03/10/1995 JLB : Created. *
  1049. *=============================================================================================*/
  1050. void UnitClass::Active_Click_With(ActionType action, ObjectClass * object)
  1051. {
  1052. Validate();
  1053. if (action != What_Action(object)) {
  1054. switch (action) {
  1055. case ACTION_SABOTAGE:
  1056. case ACTION_CAPTURE:
  1057. action = ACTION_ATTACK;
  1058. break;
  1059. case ACTION_ENTER:
  1060. action = ACTION_MOVE;
  1061. break;
  1062. default:
  1063. action = ACTION_NONE;
  1064. break;
  1065. }
  1066. }
  1067. TarComClass::Active_Click_With(action, object);
  1068. }
  1069. void UnitClass::Active_Click_With(ActionType action, CELL cell) {TarComClass::Active_Click_With(action, cell);};
  1070. /***********************************************************************************************
  1071. * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. *
  1072. * *
  1073. * This routine is called when the unit completes one mission but does not have a clear *
  1074. * follow up mission to perform. In such a case, the unit should enter a default idle *
  1075. * state. This idle state varies depending on what the current internal computer *
  1076. * settings of the unit is as well as what kind of unit it is. *
  1077. * *
  1078. * INPUT: initial -- Is this called when the unit just leaves a factory or is initially *
  1079. * or is initially placed on the map? *
  1080. * *
  1081. * OUTPUT: none *
  1082. * *
  1083. * WARNINGS: none *
  1084. * *
  1085. * HISTORY: *
  1086. * 05/31/1994 JLB : Created. *
  1087. * 06/03/1994 JLB : Fixed to handle non-combat vehicles. *
  1088. * 06/18/1995 JLB : Allows a harvester to stop harvesting. *
  1089. *=============================================================================================*/
  1090. void UnitClass::Enter_Idle_Mode(bool initial)
  1091. {
  1092. Validate();
  1093. MissionType order;
  1094. /*
  1095. ** A movement mission without a NavCom would be pointless to have a radio contact since
  1096. ** no radio coordination occurs on a just a simple movement mission.
  1097. */
  1098. if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) {
  1099. Transmit_Message(RADIO_OVER_OUT);
  1100. }
  1101. if (Class->Primary == WEAPON_NONE) {
  1102. if (Class->IsToHarvest) {
  1103. if (!In_Radio_Contact() && Mission != MISSION_HARVEST) {
  1104. if (initial || !House->IsHuman || Map[Coord_Cell(Coord)].Land_Type() == LAND_TIBERIUM) {
  1105. order = MISSION_HARVEST;
  1106. } else {
  1107. order = MISSION_GUARD;
  1108. }
  1109. Assign_Target(TARGET_NONE);
  1110. Assign_Destination(TARGET_NONE);
  1111. } else {
  1112. return;
  1113. }
  1114. } else {
  1115. if (IsALoaner && Class->IsTransporter && Is_Something_Attached() && !Team) {
  1116. order = MISSION_UNLOAD;
  1117. } else {
  1118. order = MISSION_GUARD;
  1119. Assign_Target(TARGET_NONE);
  1120. Assign_Destination(TARGET_NONE);
  1121. }
  1122. }
  1123. } else {
  1124. if (Target_Legal(TarCom)) {
  1125. order = MISSION_ATTACK;
  1126. } else {
  1127. if (Target_Legal(NavCom)) {
  1128. order = MISSION_MOVE;
  1129. } else {
  1130. if (GameToPlay == GAME_NORMAL || House->IsHuman) {
  1131. order = MISSION_GUARD;
  1132. } else {
  1133. if (GameToPlay != GAME_NORMAL) {
  1134. order = MISSION_TIMED_HUNT;
  1135. } else {
  1136. order = MISSION_HUNT;
  1137. }
  1138. }
  1139. }
  1140. }
  1141. }
  1142. Assign_Mission(order);
  1143. }
  1144. /***********************************************************************************************
  1145. * UnitClass::Find_LZ -- Maintenance function for transport units. *
  1146. * *
  1147. * This is a maintenance function for transport units. It checks to see if it is loaded *
  1148. * with cargo, but it doesn't know where to put it. In such a case, the unit must look *
  1149. * for a landing zone (LZ) to unload the cargo. This routine should be called every so *
  1150. * often. Typically, calling this routine is controlled by the scripts. *
  1151. * *
  1152. * INPUT: none *
  1153. * *
  1154. * OUTPUT: none *
  1155. * *
  1156. * WARNINGS: none *
  1157. * *
  1158. * HISTORY: *
  1159. * 05/31/1994 JLB : Created. *
  1160. *=============================================================================================*/
  1161. void UnitClass::Find_LZ(void)
  1162. {
  1163. Validate();
  1164. CELL cell; // Map exit cell number.
  1165. if (*this == UNIT_HOVER) {
  1166. if (!IsRotating && Is_Something_Attached() && !Target_Legal(NavCom)) {
  1167. cell = Map.Calculated_Cell(SOURCE_BEACH, House->Class->House);
  1168. Assign_Destination(::As_Target(cell));
  1169. }
  1170. }
  1171. }
  1172. /***********************************************************************************************
  1173. * UnitClass::Unload_Hovercraft_Process -- Handles unloading hovercraft transport. *
  1174. * *
  1175. * This is a maintenance routine to handle the special operations necessary for a *
  1176. * hovercraft transport. This routine checks to see if special unloading operations are *
  1177. * necessary and performs them. These operations can include unloading the transported *
  1178. * object, finding a new beach cell, and rotating to a convenient unloading facing. *
  1179. * *
  1180. * INPUT: none *
  1181. * *
  1182. * OUTPUT: bool; Has the transport finished its unloading mission? This is true after the *
  1183. * hovercraft has completely dispatched its cargo. *
  1184. * *
  1185. * WARNINGS: none *
  1186. * *
  1187. * HISTORY: *
  1188. * 05/31/1994 JLB : Created. *
  1189. * 07/31/1995 JLB : Second infantry unloaded MUST be the one tethered. *
  1190. *=============================================================================================*/
  1191. bool UnitClass::Unload_Hovercraft_Process(void)
  1192. {
  1193. Validate();
  1194. bool unloaded = false;
  1195. FootClass *unit; // The unit to be unloaded.
  1196. CELL cell; // Cell to unload to.
  1197. /*
  1198. ** If the hovercraft is currently waiting for the last unit
  1199. ** to completely unload, then don't do anything.
  1200. */
  1201. if (IsTethered || IsRotating) {
  1202. return(false);
  1203. }
  1204. if (Is_Something_Attached()) {
  1205. /*
  1206. ** Only unload if the hovercraft has reached the beach.
  1207. */
  1208. if (!Target_Legal(NavCom)) {
  1209. cell = Coord_Cell(Adjacent_Cell(Coord, Dir_Facing(PrimaryFacing.Current())));
  1210. unit = (FootClass *)Attached_Object();
  1211. Mark(MARK_UP);
  1212. if (Map.In_Radar(cell) && !Map[cell].Cell_Unit()) {
  1213. if (unit->Can_Enter_Cell(cell, FACING_NONE) == MOVE_OK) {
  1214. /*
  1215. ** Place all the transported units onto the map.
  1216. */
  1217. int count = 0;
  1218. bool first = true;
  1219. FootClass * secondary = 0;
  1220. while (Attached_Object()) {
  1221. FootClass * u = (FootClass *)Detach_Object();
  1222. if (!first && !secondary) secondary = u;
  1223. first = false;
  1224. /*
  1225. ** Place the unit on the map and start it heading in the right direction.
  1226. */
  1227. ScenarioInit++;
  1228. if (u->Unlimbo(Coord_Add(Coord & 0xFF00FF00L, StoppingCoordAbs[count]), DIR_N)) {
  1229. u->Assign_Mission(MISSION_MOVE);
  1230. u->NavCom = ::As_Target(cell);
  1231. u->Path[0] = Dir_Facing(u->PrimaryFacing.Current());
  1232. u->Path[1] = FACING_NONE;
  1233. u->Set_Speed(0x80);
  1234. u->IsUnloading = true;
  1235. } else {
  1236. /*
  1237. ** Couldn't unlimbo for some strange reason. Kill the unit off.
  1238. */
  1239. delete u;
  1240. }
  1241. ScenarioInit--;
  1242. count++;
  1243. }
  1244. if (secondary) unit = secondary;
  1245. /*
  1246. ** Establish radio contact bond with the transport
  1247. ** hovercraft. This bond tells the hovercraft to
  1248. ** not move until the unit has completely unloaded.
  1249. */
  1250. if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) {
  1251. Transmit_Message(RADIO_UNLOAD);
  1252. }
  1253. Mark(MARK_DOWN);
  1254. Map.Layer[LAYER_GROUND].Sort();
  1255. Map.Layer[LAYER_GROUND].Sort();
  1256. Map.Layer[LAYER_GROUND].Sort();
  1257. return(false);
  1258. } else {
  1259. /*
  1260. ** If the attached object cannot unload because the desired destination
  1261. ** cell is impassable, then abort the landing. This is faked by pretending
  1262. ** that the unload was successful. The controlling routine will cause
  1263. ** the transport to leave.
  1264. */
  1265. Mark(MARK_DOWN);
  1266. return(false);
  1267. // return(true);
  1268. }
  1269. }
  1270. Mark(MARK_DOWN);
  1271. }
  1272. } else {
  1273. return(true);
  1274. }
  1275. return(unloaded);
  1276. }
  1277. /***********************************************************************************************
  1278. * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. *
  1279. * *
  1280. * This routine is used by the MCV to find a clear spot to deploy. If a clear spot *
  1281. * is found, then the MCV will assign that location to its navigation computer. This only *
  1282. * occurs if the MCV isn't already heading toward a spot. *
  1283. * *
  1284. * INPUT: none *
  1285. * *
  1286. * OUTPUT: bool; Is the located at a spot where it can deploy? *
  1287. * *
  1288. * WARNINGS: none *
  1289. * *
  1290. * HISTORY: *
  1291. * 06/27/1994 JLB : Created. *
  1292. *=============================================================================================*/
  1293. bool UnitClass::Goto_Clear_Spot(void)
  1294. {
  1295. Validate();
  1296. Mark(MARK_UP);
  1297. if (!Target_Legal(NavCom) && BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Coord))) {
  1298. Mark(MARK_DOWN);
  1299. return(true);
  1300. }
  1301. if (!Target_Legal(NavCom)) {
  1302. /*
  1303. ** This scan table is skewed to north scanning only. This should
  1304. ** probably be converted to a more flexible method.
  1305. */
  1306. static int _offsets[] = {
  1307. -MAP_CELL_W*1,
  1308. -MAP_CELL_W*2,
  1309. -(MAP_CELL_W*2)+1,
  1310. -(MAP_CELL_W*2)-1,
  1311. -MAP_CELL_W*3,
  1312. -(MAP_CELL_W*3)+1,
  1313. -(MAP_CELL_W*3)-1,
  1314. -(MAP_CELL_W*3)+2,
  1315. -(MAP_CELL_W*3)-2,
  1316. -MAP_CELL_W*4,
  1317. -(MAP_CELL_W*4)+1,
  1318. -(MAP_CELL_W*4)-1,
  1319. -(MAP_CELL_W*4)+2,
  1320. -(MAP_CELL_W*4)-2,
  1321. 0
  1322. };
  1323. int *ptr;
  1324. ptr = &_offsets[0];
  1325. while (*ptr) {
  1326. CELL cell = Coord_Cell(Coord)+*ptr++;
  1327. if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) {
  1328. Assign_Destination(::As_Target(cell));
  1329. break;
  1330. }
  1331. }
  1332. }
  1333. Mark(MARK_DOWN);
  1334. return(false);
  1335. }
  1336. /***********************************************************************************************
  1337. * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. *
  1338. * *
  1339. * Certain units have the ability to deploy into a building. When this routine is called *
  1340. * for one of those units, it will attempt to deploy at its current location. If the unit *
  1341. * is in motion to a destination or it isn't one of the special units that can deploy or *
  1342. * it isn't allowed to deploy at this location for some reason it won't deploy. In all *
  1343. * other cases, it will begin to deploy and once it begins only a player abort action will *
  1344. * stop it. *
  1345. * *
  1346. * INPUT: none *
  1347. * *
  1348. * OUTPUT: bool; Was deployment begun? *
  1349. * *
  1350. * WARNINGS: none *
  1351. * *
  1352. * HISTORY: *
  1353. * 06/18/1994 JLB : Created. *
  1354. *=============================================================================================*/
  1355. bool UnitClass::Try_To_Deploy(void)
  1356. {
  1357. Validate();
  1358. if (!Target_Legal(NavCom) && !IsRotating) {
  1359. if (*this == UNIT_MCV) {
  1360. /*
  1361. ** Determine if it is legal to deploy at this location. If not, tell the
  1362. ** player.
  1363. */
  1364. Mark(MARK_UP);
  1365. if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) {
  1366. if (PlayerPtr == House) {
  1367. Speak(VOX_DEPLOY);
  1368. }
  1369. Mark(MARK_DOWN);
  1370. IsDeploying = false;
  1371. return(false);
  1372. }
  1373. Mark(MARK_DOWN);
  1374. /*
  1375. ** If the unit is not facing the correct direction, then start it rotating
  1376. ** toward the right facing, but still flag it as if it had deployed. This is
  1377. ** because it will deploy as soon as it reaches the correct facing.
  1378. */
  1379. if (PrimaryFacing.Current() != DIR_SW) {
  1380. Do_Turn(DIR_SW);
  1381. // PrimaryFacing.Set_Desired(DIR_SW);
  1382. IsDeploying = true;
  1383. return(true);
  1384. }
  1385. /*
  1386. ** Since the unit is already facing the correct direction, actually do the
  1387. ** deploy logic. If for some reason this cannot occur, then don't delete the
  1388. ** unit, just mark it as not deploying.
  1389. */
  1390. Mark(MARK_UP);
  1391. BuildingClass * building = new BuildingClass(STRUCT_CONST, House->Class->House);
  1392. if (building) {
  1393. if (building->Unlimbo(Adjacent_Cell(Coord, FACING_NW))) {
  1394. /*
  1395. ** Always reveal the construction yard to the player that owned the
  1396. ** mobile construction vehicle.
  1397. */
  1398. building->Revealed(House);
  1399. /*
  1400. ** Force the newly placed construction yard to be in the same strength
  1401. ** ratio as the MCV that deployed into it.
  1402. */
  1403. int ratio = Health_Ratio();
  1404. building->Strength = Fixed_To_Cardinal(building->Class->MaxStrength, ratio);
  1405. /*
  1406. ** Force the MCV to drop any flag it was carrying. This will also set
  1407. ** the owner house's flag home cell (since the house's FlagHome is
  1408. ** presumably 0 at this point).
  1409. */
  1410. Stun();
  1411. delete this;
  1412. return(true);
  1413. } else {
  1414. /*
  1415. ** Could not deploy the construction yard at this location! Just revert
  1416. ** back to normal "just sitting there" mode and await further instructions.
  1417. */
  1418. delete building;
  1419. }
  1420. }
  1421. Mark(MARK_DOWN);
  1422. IsDeploying = false;
  1423. }
  1424. }
  1425. return(false);
  1426. }
  1427. /***********************************************************************************************
  1428. * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. *
  1429. * *
  1430. * This routine will perform the operations necessary that occur when a unit is at the *
  1431. * center of a cell. These operations could entail deploying into a construction yard, *
  1432. * radioing a transport unit, and looking around for the enemy. *
  1433. * *
  1434. * INPUT: center -- Is the unit safely at the center of a cell? If it is merely "close" *
  1435. * to the center, then this parameter will be false. *
  1436. * *
  1437. * OUTPUT: none *
  1438. * *
  1439. * WARNINGS: none *
  1440. * *
  1441. * HISTORY: *
  1442. * 06/18/1994 JLB : Created. *
  1443. * 06/17/1995 JLB : Handles case when building says "NO!" *
  1444. * 06/30/1995 JLB : Gunboats head back and forth now. *
  1445. *=============================================================================================*/
  1446. void UnitClass::Per_Cell_Process(bool center)
  1447. {
  1448. Validate();
  1449. CELL cell = Coord_Cell(Coord);
  1450. TechnoClass * whom;
  1451. HousesType house;
  1452. /*
  1453. ** If this is a unit that is driving onto a building then the unit must enter
  1454. ** the building as the final step.
  1455. */
  1456. whom = Contact_With_Whom();
  1457. if (IsTethered && whom && center) {
  1458. if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) {
  1459. if (whom == Map[cell].Cell_Building()) {
  1460. switch (Transmit_Message(RADIO_IM_IN, whom)) {
  1461. case RADIO_ROGER:
  1462. break;
  1463. case RADIO_ATTACH:
  1464. Mark(MARK_UP);
  1465. SpecialFlag = true;
  1466. Limbo();
  1467. SpecialFlag = false;
  1468. whom->Attach(this);
  1469. return;
  1470. default:
  1471. Scatter(true);
  1472. break;
  1473. }
  1474. }
  1475. }
  1476. }
  1477. /*
  1478. ** When breaking away from a transport object or building, possibly
  1479. ** scatter or otherwise begin normal unit operations.
  1480. */
  1481. if (IsTethered && center && Mission != MISSION_ENTER) {
  1482. if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) {
  1483. if (!Target_Legal(NavCom)) {
  1484. Scatter(0, true);
  1485. }
  1486. } else {
  1487. if (*this == UNIT_HARVESTER) {
  1488. if (Target_Legal(ArchiveTarget)) {
  1489. Assign_Mission(MISSION_HARVEST);
  1490. Assign_Destination(ArchiveTarget);
  1491. ArchiveTarget = TARGET_NONE;
  1492. } else {
  1493. /*
  1494. ** Since there is no place to go, move away to clear
  1495. ** the pad for another harvester.
  1496. */
  1497. if (!Target_Legal(NavCom)) {
  1498. Scatter(0, true);
  1499. }
  1500. }
  1501. }
  1502. }
  1503. }
  1504. #ifdef OBSOLETE
  1505. /*
  1506. ** If this unit is on a teather, then cut it at this time so that
  1507. ** the "parent" unit is free to proceed. Note that the parent
  1508. ** unit might actually be a building.
  1509. */
  1510. if (IsTethered && center && !Map[cell].Cell_Building()) {
  1511. if (!Tiberium || *this != UNIT_HARVESTER) {
  1512. Transmit_Message(RADIO_UNLOADED);
  1513. if (*this == UNIT_HARVESTER) {
  1514. if (Target_Legal(ArchiveTarget)) {
  1515. Assign_Mission(MISSION_HARVEST);
  1516. Assign_Destination(ArchiveTarget);
  1517. ArchiveTarget = TARGET_NONE;
  1518. } else {
  1519. /*
  1520. ** Since there is no place to go, move away to clear
  1521. ** the pad for another harvester.
  1522. */
  1523. Scatter(0, true);
  1524. }
  1525. }
  1526. }
  1527. }
  1528. #endif
  1529. #ifdef OBSOLETE
  1530. /*
  1531. ** If the unit is at the center of the repair facility, and that was his
  1532. ** destination, then start him repairing.
  1533. */
  1534. if (center && !IsRepairing) {
  1535. BuildingClass * b = As_Building(NavCom);
  1536. if (b && *b==STRUCT_REPAIR && Coord == b->Center_Coord()) {
  1537. NavCom = 0;
  1538. IsRepairing = true;
  1539. Transmit_Message(RADIO_REPAIR_BEGIN_ANIM);
  1540. }
  1541. }
  1542. #endif
  1543. /*
  1544. ** Check to see if this is merely the end of a rotation for the MCV as it is
  1545. ** preparing to deploy. In this case, it should begin its deploy process.
  1546. */
  1547. if (center && IsDeploying) {
  1548. Try_To_Deploy();
  1549. if (!IsActive) return; // Unit no longer exists -- bail.
  1550. }
  1551. /*
  1552. ** If this is a loaner unit and is is off the edge of the
  1553. ** map, then it gets eliminated. That is, unless it is carrying cargo. This means that
  1554. ** it is probably carrying an incoming reinforcement and it should not be eliminated.
  1555. */
  1556. if (center && IsALoaner && !Map.In_Radar(cell)) {
  1557. if (IsReturning || !Is_Something_Attached()) {
  1558. if (*this == UNIT_GUNBOAT) {
  1559. CELL cell = Coord_Cell(Coord);
  1560. if (Cell_X(cell) <= Map.MapCellX) {
  1561. Assign_Mission(MISSION_HUNT);
  1562. Assign_Destination(::As_Target(XY_Cell(Map.MapCellX+Map.MapCellWidth, Cell_Y(cell))));
  1563. Set_Speed(255);
  1564. PrimaryFacing = DIR_E;
  1565. } else {
  1566. Assign_Mission(MISSION_HUNT);
  1567. Assign_Destination(::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(cell))));
  1568. Set_Speed(255);
  1569. PrimaryFacing = DIR_W;
  1570. }
  1571. Mark(MARK_DOWN);
  1572. } else {
  1573. Mark(MARK_DOWN);
  1574. Stun();
  1575. delete this;
  1576. return;
  1577. }
  1578. }
  1579. }
  1580. #ifdef OBSOLETE
  1581. /*
  1582. ** Destroy any crushable wall that is driven over by a tracked vehicle.
  1583. */
  1584. CellClass * cellptr = &Map[cell];
  1585. if (center && Class->Speed == SPEED_TRACK && cellptr->Overlay != OVERLAY_NONE) {
  1586. OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);
  1587. if (optr->IsCrushable) {
  1588. cellptr->Reduce_Wall(100);
  1589. cellptr->Reduce_Wall(100);
  1590. cellptr->Reduce_Wall(100);
  1591. cellptr->Reduce_Wall(100);
  1592. cellptr->Reduce_Wall(100);
  1593. cellptr->Reduce_Wall(100);
  1594. }
  1595. }
  1596. #endif
  1597. /*
  1598. ** Check to see if crushing of any unfortunate infantry is warranted.
  1599. */
  1600. Overrun_Square(Coord_Cell(Coord), false);
  1601. /*
  1602. ** The unit performs looking around at this time. If the
  1603. ** unit moved further than one square during the last track
  1604. ** move, don't do an incremental look. Do a full look around
  1605. ** instead.
  1606. */
  1607. if (IsPlanningToLook) {
  1608. IsPlanningToLook = false;
  1609. Look(false);
  1610. } else {
  1611. Look(true);
  1612. }
  1613. /*
  1614. ** Act on new orders if the unit is at a good position to do so.
  1615. */
  1616. if (center) {
  1617. Commence();
  1618. }
  1619. /*
  1620. ** Certain units require some setup time after they come to a halt.
  1621. */
  1622. if (Special.IsDefenderAdvantage && /*center &&*/ !Target_Legal(NavCom) && Path[0] == FACING_NONE) {
  1623. if (*this == UNIT_MLRS || *this == UNIT_ARTY || *this == UNIT_MSAM) {
  1624. Arm = Rearm_Delay(false)*2;
  1625. }
  1626. }
  1627. /*
  1628. ** If there is a house flag here, then this unit just might pick it up.
  1629. */
  1630. if (center && Flagged == HOUSE_NONE) {
  1631. if (Map[cell].IsFlagged && Map[cell].Owner != House->Class->House) {
  1632. HouseClass::As_Pointer(Map[cell].Owner)->Flag_Attach(this);
  1633. }
  1634. }
  1635. /*
  1636. ** If this is the unit's own flag-home-cell and the unit is carrying
  1637. ** a flag, destroy the house of the flag the unit is carrying.
  1638. */
  1639. if (Flagged != HOUSE_NONE) {
  1640. /*
  1641. ** If this vehicle is carrying your flag, then it will reveal the
  1642. ** map for you as well as itself. This gives you and opportunity to
  1643. ** attack the unit.
  1644. */
  1645. if (!IsOwnedByPlayer && Flagged == PlayerPtr->Class->House) {
  1646. Map.Sight_From(Coord_Cell(Coord), Class->SightRange, true);
  1647. }
  1648. /*
  1649. ** If the flag reaches the home cell for the player, then the flag's
  1650. ** owner will be destroyed.
  1651. */
  1652. if (cell == HouseClass::As_Pointer(Owner())->FlagHome && center) {
  1653. house = Flagged; // Flag_Remove will clear 'Flagged', so save it
  1654. HouseClass::As_Pointer(house)->Flag_Remove(As_Target(),true);
  1655. HouseClass::As_Pointer(house)->Flag_To_Die();
  1656. }
  1657. }
  1658. TarComClass::Per_Cell_Process(center);
  1659. }
  1660. /***********************************************************************************************
  1661. * UnitClass::Draw_It -- Draws a unit object. *
  1662. * *
  1663. * This routine is the one that actually draws a unit object. It displays the unit *
  1664. * according to its current state flags and centered at the location specified. *
  1665. * *
  1666. * INPUT: x,y -- The X and Y coordinate of where to draw the unit. *
  1667. * *
  1668. * window -- The clipping window to use. *
  1669. * *
  1670. * OUTPUT: none *
  1671. * *
  1672. * WARNINGS: none *
  1673. * *
  1674. * HISTORY: *
  1675. * 06/20/1994 JLB : Created. *
  1676. * 06/27/1994 JLB : Takes a window parameter. *
  1677. * 08/15/1994 JLB : Removed infantry support. *
  1678. * 01/07/1995 JLB : Harvester animation support. *
  1679. * 07/08/1995 JLB : Uses general purpose draw routine. *
  1680. *=============================================================================================*/
  1681. void UnitClass::Draw_It(int x, int y, WindowNumberType window)
  1682. {
  1683. Validate();
  1684. int shapenum; // Working shape number.
  1685. void const *shapefile; // Working shape file pointer.
  1686. int facing = Facing_To_32(PrimaryFacing.Current());
  1687. int tfacing = Facing_To_32(SecondaryFacing.Current());
  1688. /*
  1689. ** Verify the legality of the unit class.
  1690. */
  1691. shapefile = Class->Get_Image_Data();
  1692. if (!shapefile) return;
  1693. /*
  1694. ** If drawing of this unit is not explicitly prohibited, then proceed
  1695. ** with the render process.
  1696. */
  1697. if (Visual_Character() != VISUAL_HIDDEN) {
  1698. /*
  1699. ** For eight facing units, adjust the facing number accordingly.
  1700. */
  1701. if (Class->IsPieceOfEight) {
  1702. facing = Dir_Facing(PrimaryFacing.Current());
  1703. }
  1704. /*
  1705. ** Some units have only four facings, but are equipped with a turret
  1706. ** that has 32 facings.
  1707. */
  1708. if (Class->IsChunkyShape) {
  1709. /*
  1710. ** Special wake drawing occurs here.
  1711. */
  1712. if (*this == UNIT_GUNBOAT) {
  1713. int shapestart;
  1714. int xx,yy;
  1715. xx = x;
  1716. yy = y;
  1717. switch (Dir_Facing(PrimaryFacing.Current())) {
  1718. case FACING_NE:
  1719. case FACING_E:
  1720. case FACING_SE:
  1721. shapenum = UnitClass::BodyShape[tfacing]+96;
  1722. shapestart = 0;
  1723. //xx -= 4;
  1724. break;
  1725. case FACING_W:
  1726. default:
  1727. shapenum = UnitClass::BodyShape[tfacing];
  1728. shapestart = 6;
  1729. xx += 4;
  1730. break;
  1731. }
  1732. CC_Draw_Shape(UnitTypeClass::WakeShapes, shapestart + (Fetch_Stage() % 6), xx-1, yy+3, window, SHAPE_CENTER|SHAPE_WIN_REL);
  1733. if (Health_Ratio() < 0x0080) shapenum += 32;
  1734. if (Health_Ratio() < 0x0040) shapenum += 32;
  1735. } else {
  1736. /*
  1737. ** Special hovercraft shape is ALWAYS N/S.
  1738. */
  1739. shapenum = 0;
  1740. //shapenum = ((UnitClass::BodyShape[facing] + 4) / 8) & 0x03;
  1741. }
  1742. } else {
  1743. /*
  1744. ** Fetch the harvesting animation stage as appropriate.
  1745. */
  1746. if (IsHarvesting && !PrimaryFacing.Is_Rotating() && !NavCom && !IsDriving) {
  1747. static char _hstage[6] = {
  1748. 0, 1, 2, 3, 2, 1
  1749. };
  1750. shapenum = 32 + (((UnitClass::BodyShape[facing]+2)/4)*4)+_hstage[Fetch_Stage()%sizeof(_hstage)];
  1751. } else {
  1752. shapenum = UnitClass::BodyShape[facing];
  1753. if (Class->IsAnimating) {
  1754. shapenum = Fetch_Stage();
  1755. }
  1756. if (Class->IsPieceOfEight) {
  1757. shapenum = 0;
  1758. if (facing) shapenum = UnitClass::BodyShape[24+facing];
  1759. if (IsDriving) shapenum = Fetch_Stage() + 16 + shapenum*8;
  1760. if (IsFiring) shapenum = Fetch_Stage() + 80 + shapenum*( ((*this == UNIT_TREX) || (*this == UNIT_RAPT)) ? 8 : 12);
  1761. } else {
  1762. /*
  1763. ** Door opening and closing animation must be handled carefully. There are only
  1764. ** certain directions where this door animation will work.
  1765. */
  1766. if (!Is_Door_Closed() && (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE)) {
  1767. if (PrimaryFacing == DIR_NE) {
  1768. shapenum = 32;
  1769. } else {
  1770. if (PrimaryFacing == DIR_NW) {
  1771. shapenum = 35;
  1772. }
  1773. }
  1774. shapenum += Door_Stage();
  1775. }
  1776. }
  1777. }
  1778. }
  1779. /*
  1780. ** The artillery unit should have its entire body recoil when it fires.
  1781. */
  1782. if (*this == UNIT_ARTY && IsInRecoilState) {
  1783. Recoil_Adjust(PrimaryFacing.Current(), x, y);
  1784. }
  1785. /*
  1786. ** Actually perform the draw. Overlay an optional shimmer effect as necessary.
  1787. */
  1788. //if (*this == UNIT_HOVER) {
  1789. // Mono_Printf("Display hover %p %d.\n", shapefile, shapenum);
  1790. //}
  1791. Techno_Draw_Object(shapefile, shapenum, x, y, window);
  1792. /*
  1793. ** If there is a rotating radar dish, draw it now.
  1794. */
  1795. if (Class->IsRadarEquipped) {
  1796. shapenum = 32 + (Frame % 32);
  1797. Techno_Draw_Object(shapefile, shapenum, x, y-5, window);
  1798. }
  1799. /*
  1800. ** If there is a turret, then it must be rendered as well. This may include
  1801. ** firing animation if required.
  1802. */
  1803. if (!Class->IsChunkyShape && Class->IsTurretEquipped) {
  1804. int x1 = x;
  1805. int y1 = y;
  1806. /*
  1807. ** Determine which turret shape to use. This depends on if there
  1808. ** is any firing animation in progress.
  1809. */
  1810. shapenum = TechnoClass::BodyShape[tfacing]+32;
  1811. /*
  1812. ** The shape to use for the rocket launcher is dependant on the
  1813. ** ammo remaining.
  1814. */
  1815. if (*this == UNIT_MSAM) {
  1816. if (Ammo == 0) shapenum += 64;
  1817. if (Ammo == 1) shapenum += 32;
  1818. }
  1819. /*
  1820. ** A recoiling turret moves "backward" one pixel.
  1821. */
  1822. if (IsInRecoilState) {
  1823. Recoil_Adjust(SecondaryFacing.Current(), x1, y1);
  1824. }
  1825. /*
  1826. ** The Mobile SAM and the Missile Launchers need their turrets moved based
  1827. ** on the facing of the vehicle.
  1828. */
  1829. if (*this == UNIT_MSAM || *this == UNIT_MLRS) {
  1830. Turret_Adjust(PrimaryFacing, x1, y1);
  1831. }
  1832. if (*this == UNIT_JEEP || *this == UNIT_BUGGY) {
  1833. y1 -= 4;
  1834. }
  1835. /*
  1836. ** Actually perform the draw. Overlay an optional shimmer effect as necessary.
  1837. */
  1838. Techno_Draw_Object(shapefile, shapenum, x1, y1, window);
  1839. }
  1840. /*
  1841. ** If this unit has "piggy back" unit(s), then render it at the same time.
  1842. */
  1843. if (*this == UNIT_HOVER && Is_Something_Attached()) {
  1844. TechnoClass * u = (TechnoClass *)Attached_Object();
  1845. int counter = 0;
  1846. for (;;) {
  1847. int x1,y1;
  1848. if (Map.Coord_To_Pixel(Coord_Add(Coord_Add(Coord, 0xFF80FF80L), StoppingCoordAbs[counter++]), x1, y1)) {
  1849. u->Draw_It(x1, y1, WINDOW_TACTICAL);
  1850. }
  1851. if (!u->Next) break;
  1852. u = (TechnoClass *)u->Next;
  1853. }
  1854. }
  1855. }
  1856. /*
  1857. ** If this unit is carrying the flag, then draw that on top of everything else.
  1858. */
  1859. if (Flagged != HOUSE_NONE) {
  1860. CC_Draw_Shape(MixFileClass::Retrieve("FLAGFLY.SHP"), Frame % 14, x, y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, false), Map.UnitShadow);
  1861. }
  1862. TarComClass::Draw_It(x, y, window);
  1863. }
  1864. /***********************************************************************************************
  1865. * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. *
  1866. * *
  1867. * This routine is used to move a harvester to a place where it can load up with *
  1868. * Tiberium. It will return true only if it can start harvesting. Otherwise, it sets *
  1869. * the navigation computer toward the nearest Tiberium and lets the unit head there *
  1870. * automatically. *
  1871. * *
  1872. * INPUT: none *
  1873. * *
  1874. * OUTPUT: bool; Is it located directly over a Tiberium patch? *
  1875. * *
  1876. * WARNINGS: none *
  1877. * *
  1878. * HISTORY: *
  1879. * 07/18/1994 JLB : Created. *
  1880. *=============================================================================================*/
  1881. bool UnitClass::Tiberium_Check(CELL &center, int x, int y)
  1882. {
  1883. Validate();
  1884. /*
  1885. ** If the specified offset from the origin will cause it
  1886. ** to spill past the map edge, then abort this cell check.
  1887. */
  1888. if (Cell_X(center)+x < Map.MapCellX) return(false);
  1889. if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(false);
  1890. if (Cell_Y(center)+y < Map.MapCellY) return(false);
  1891. if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(false);
  1892. center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y);
  1893. if ((GameToPlay != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].IsVisible))) {
  1894. if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) {
  1895. return(true);
  1896. }
  1897. }
  1898. return(false);
  1899. }
  1900. bool UnitClass::Goto_Tiberium(void)
  1901. {
  1902. Validate();
  1903. if (!Target_Legal(NavCom)) {
  1904. CELL center = Coord_Cell(Center_Coord());
  1905. if (Map[center].Land_Type() == LAND_TIBERIUM) {
  1906. return(true);
  1907. } else {
  1908. /*
  1909. ** Perform a ring search outward from the center.
  1910. */
  1911. for (int radius = 1; radius < 32; radius++) {
  1912. for (int x = -radius; x <= radius; x++) {
  1913. CELL cell = center;
  1914. if (Tiberium_Check(cell, x, -radius)) {
  1915. Assign_Destination(::As_Target(cell));
  1916. return(false);
  1917. }
  1918. cell = center;
  1919. if (Tiberium_Check(cell, x, +radius)) {
  1920. Assign_Destination(::As_Target(cell));
  1921. return(false);
  1922. }
  1923. cell = center;
  1924. if (Tiberium_Check(cell, -radius, x)) {
  1925. Assign_Destination(::As_Target(cell));
  1926. return(false);
  1927. }
  1928. cell = center;
  1929. if (Tiberium_Check(cell, +radius, x)) {
  1930. Assign_Destination(::As_Target(cell));
  1931. return(false);
  1932. }
  1933. }
  1934. }
  1935. }
  1936. }
  1937. return(false);
  1938. }
  1939. /***********************************************************************************************
  1940. * UnitClass::Harvesting -- Harvests tiberium at the current location. *
  1941. * *
  1942. * This routine is used to by the harvester to harvest Tiberium at the current location. *
  1943. * When harvesting is complete, this routine will return true. *
  1944. * *
  1945. * INPUT: none *
  1946. * *
  1947. * OUTPUT: bool; Is harvesting complete? *
  1948. * *
  1949. * WARNINGS: none *
  1950. * *
  1951. * HISTORY: *
  1952. * 07/18/1994 JLB : Created. *
  1953. *=============================================================================================*/
  1954. bool UnitClass::Harvesting(void)
  1955. {
  1956. Validate();
  1957. CELL cell = Coord_Cell(Coord);
  1958. CellClass * ptr = &Map[cell];
  1959. /*
  1960. ** Keep waiting if still heading toward a spot to harvest.
  1961. */
  1962. if (Target_Legal(NavCom)) return(true);
  1963. if (Tiberium_Load() < 0x0100 && ptr->Land_Type() == LAND_TIBERIUM) {
  1964. /*
  1965. ** Lift some Tiberium from the ground. Try to lift a complete
  1966. ** "level" of Tiberium. A level happens to be 6 steps. If there
  1967. ** is a partial level, then lift that instead. Never lift more
  1968. ** than the harvester can carry.
  1969. */
  1970. int reducer = (ptr->OverlayData % 6) + 1;
  1971. reducer = ptr->Reduce_Tiberium(MIN(reducer, UnitTypeClass::STEP_COUNT-Tiberium));
  1972. Tiberium += reducer;
  1973. Set_Stage(0);
  1974. Set_Rate(2);
  1975. } else {
  1976. /*
  1977. ** If the harvester is stopped on a non Tiberium field and the harvester
  1978. ** isn't loaded with Tiberium, then no further action can be performed
  1979. ** by this logic routine. Bail with a failure and thus cause a branch to
  1980. ** a better suited logic processor.
  1981. */
  1982. Set_Stage(0);
  1983. Set_Rate(0);
  1984. return(false);
  1985. }
  1986. return(true);
  1987. }
  1988. /***********************************************************************************************
  1989. * UnitClass::Mission_Unload -- Handles unloading cargo. *
  1990. * *
  1991. * This is the AI control sequence for when a transport desires to unload its cargo and *
  1992. * then exit the map. *
  1993. * *
  1994. * INPUT: none *
  1995. * *
  1996. * OUTPUT: Returns with the delay before calling this routine again. *
  1997. * *
  1998. * WARNINGS: none *
  1999. * *
  2000. * HISTORY: *
  2001. * 07/18/1994 JLB : Created. *
  2002. *=============================================================================================*/
  2003. int UnitClass::Mission_Unload(void)
  2004. {
  2005. Validate();
  2006. enum {
  2007. INITIAL_CHECK,
  2008. MANEUVERING,
  2009. OPENING_DOOR,
  2010. UNLOADING,
  2011. CLOSING_DOOR
  2012. };
  2013. DirType dir;
  2014. CELL cell;
  2015. switch (Class->Type) {
  2016. case UNIT_APC:
  2017. switch (Status) {
  2018. case INITIAL_CHECK:
  2019. dir = Desired_Load_Dir(NULL, cell);
  2020. if (How_Many() && cell != 0) {
  2021. Do_Turn(dir);
  2022. Status = MANEUVERING;
  2023. return(1);
  2024. } else {
  2025. Assign_Mission(MISSION_GUARD);
  2026. }
  2027. break;
  2028. case MANEUVERING:
  2029. if (!IsRotating) {
  2030. APC_Open_Door();
  2031. if (Is_Door_Opening()) {
  2032. Status = OPENING_DOOR;
  2033. return(1);
  2034. }
  2035. }
  2036. break;
  2037. case OPENING_DOOR:
  2038. if (Is_Door_Open()) {
  2039. Status = UNLOADING;
  2040. return(1);
  2041. } else {
  2042. if (!Is_Door_Opening()) {
  2043. Status = INITIAL_CHECK;
  2044. }
  2045. }
  2046. break;
  2047. case UNLOADING:
  2048. if (How_Many()) {
  2049. FootClass * passenger = Detach_Object();
  2050. if (passenger) {
  2051. DirType toface = DIR_S + PrimaryFacing;
  2052. bool placed = false;
  2053. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  2054. DirType newface = toface + face;
  2055. CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
  2056. if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) {
  2057. ScenarioInit++;
  2058. passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface);
  2059. ScenarioInit--;
  2060. passenger->Assign_Mission(MISSION_MOVE);
  2061. passenger->Assign_Destination(::As_Target(newcell));
  2062. placed = true;
  2063. break;
  2064. }
  2065. }
  2066. /*
  2067. ** If the attached unit could NOT be deployed, then re-attach
  2068. ** it and then bail out of this deploy process.
  2069. */
  2070. if (!placed) {
  2071. Attach(passenger);
  2072. Status = CLOSING_DOOR;
  2073. }
  2074. }
  2075. } else {
  2076. Status = CLOSING_DOOR;
  2077. }
  2078. break;
  2079. /*
  2080. ** Close APC door in preparation for normal operation.
  2081. */
  2082. case CLOSING_DOOR:
  2083. if (Is_Door_Open()) {
  2084. APC_Close_Door();
  2085. }
  2086. if (Is_Door_Closed()) {
  2087. Assign_Mission(MISSION_GUARD);
  2088. }
  2089. break;
  2090. }
  2091. break;
  2092. case UNIT_MCV:
  2093. switch (Status) {
  2094. case 0:
  2095. Path[0] = FACING_NONE;
  2096. Status = 1;
  2097. break;
  2098. case 1:
  2099. if (!IsDriving) {
  2100. Try_To_Deploy();
  2101. if (IsDeploying) {
  2102. Status = 2;
  2103. } else {
  2104. Assign_Mission(MISSION_GUARD);
  2105. }
  2106. }
  2107. break;
  2108. case 2:
  2109. break;
  2110. }
  2111. return(1);
  2112. case UNIT_HOVER:
  2113. switch (Status) {
  2114. case 0:
  2115. if (Unload_Hovercraft_Process()) {
  2116. Status = 1;
  2117. }
  2118. break;
  2119. /*
  2120. ** Hovercraft always leave the map when they finish
  2121. ** unloading.
  2122. */
  2123. case 1:
  2124. if (Team) {
  2125. Team->Remove(this);
  2126. }
  2127. Exit_Map();
  2128. break;
  2129. }
  2130. break;
  2131. }
  2132. return(TICKS_PER_SECOND);
  2133. }
  2134. /***********************************************************************************************
  2135. * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. *
  2136. * *
  2137. * This is the AI process used by harvesters when they are doing their harvesting action. *
  2138. * This entails searching for nearby Tiberium, heading there, harvesting, and then *
  2139. * returning to a refinery for unloading. *
  2140. * *
  2141. * INPUT: none *
  2142. * *
  2143. * OUTPUT: Returns with the delay before calling this routine again. *
  2144. * *
  2145. * WARNINGS: none *
  2146. * *
  2147. * HISTORY: *
  2148. * 07/18/1994 JLB : Created. *
  2149. * 06/21/1995 JLB : Force guard mode if no Tiberium found. *
  2150. *=============================================================================================*/
  2151. int UnitClass::Mission_Harvest(void)
  2152. {
  2153. Validate();
  2154. enum {
  2155. LOOKING,
  2156. HARVESTING,
  2157. FINDHOME,
  2158. HEADINGHOME,
  2159. GOINGTOIDLE,
  2160. };
  2161. /*
  2162. ** A non-harvesting type unit will just sit still if it is given the harvest mission. This
  2163. ** allows combat units to act "brain dead".
  2164. */
  2165. if (!Class->IsToHarvest) return(TICKS_PER_SECOND*30);
  2166. switch (Status) {
  2167. /*
  2168. ** Go and find a Tiberium field to harvest.
  2169. */
  2170. case LOOKING:
  2171. IsHarvesting = false;
  2172. if (Goto_Tiberium()) {
  2173. IsHarvesting = true;
  2174. Set_Rate(2);
  2175. Set_Stage(0);
  2176. Status = HARVESTING;
  2177. return(1);
  2178. } else {
  2179. /*
  2180. ** If the harvester isn't on Tiberium and it is not heading toward Tiberium, then
  2181. ** force it to go into guard mode. This will prevent the harvester from repeatedly
  2182. ** searching for Tiberium.
  2183. */
  2184. if (!Target_Legal(NavCom)) {
  2185. Status = GOINGTOIDLE;
  2186. return(TICKS_PER_SECOND*15);
  2187. }
  2188. }
  2189. break;
  2190. /*
  2191. ** Harvest at current location until full or Tiberium exhausted.
  2192. */
  2193. case HARVESTING:
  2194. if (!Harvesting()) {
  2195. IsHarvesting = false;
  2196. if (Tiberium_Load() == 0x0100) {
  2197. Status = FINDHOME;
  2198. ArchiveTarget = ::As_Target(Coord_Cell(Coord));
  2199. } else {
  2200. if (!Goto_Tiberium() && !Target_Legal(NavCom)) {
  2201. ArchiveTarget = TARGET_NONE;
  2202. Status = FINDHOME;
  2203. } else {
  2204. Status = HARVESTING;
  2205. IsHarvesting = true;
  2206. }
  2207. }
  2208. return(1);
  2209. }
  2210. break;
  2211. /*
  2212. ** Find and head to refinery.
  2213. */
  2214. case FINDHOME:
  2215. if (!Target_Legal(NavCom)) {
  2216. /*
  2217. ** Find nearby refinery and head to it?
  2218. */
  2219. BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false);
  2220. /*
  2221. ** Since the refinery said it was ok to load, establish radio
  2222. ** contact with the refinery and then await docking orders.
  2223. */
  2224. if (nearest && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
  2225. Status = HEADINGHOME;
  2226. } else {
  2227. ScenarioInit++;
  2228. nearest = Find_Docking_Bay(STRUCT_REFINERY, false);
  2229. ScenarioInit--;
  2230. if (nearest) {
  2231. Assign_Destination(::As_Target(nearest->Nearby_Location(this)));
  2232. }
  2233. }
  2234. }
  2235. break;
  2236. /*
  2237. ** In communication with refinery so that it will successfully dock and
  2238. ** unload. If, for some reason, radio contact was lost, then hunt for
  2239. ** another refinery to unload at.
  2240. */
  2241. case HEADINGHOME:
  2242. Assign_Mission(MISSION_ENTER);
  2243. return(1);
  2244. case GOINGTOIDLE:
  2245. Assign_Mission(MISSION_GUARD);
  2246. break;
  2247. }
  2248. return(TICKS_PER_SECOND);
  2249. }
  2250. /***********************************************************************************************
  2251. * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. *
  2252. * *
  2253. * Computer controlled units must be intelligent enough to find enemies as well as to *
  2254. * attack them. This AI process will handle both the simple attack process as well as the *
  2255. * scanning for enemy units to attack. *
  2256. * *
  2257. * INPUT: none *
  2258. * *
  2259. * OUTPUT: Returns with the delay before calling this routine again. *
  2260. * *
  2261. * WARNINGS: none *
  2262. * *
  2263. * HISTORY: *
  2264. * 07/18/1994 JLB : Created. *
  2265. *=============================================================================================*/
  2266. int UnitClass::Mission_Hunt(void)
  2267. {
  2268. Validate();
  2269. if (*this == UNIT_MCV) {
  2270. switch (Status) {
  2271. /*
  2272. ** This stage handles locating a convenient spot, rotating to face the correct
  2273. ** direction and then commencing the deployment operation.
  2274. */
  2275. case 0:
  2276. if (Goto_Clear_Spot()) {
  2277. if (Try_To_Deploy()) {
  2278. Status = 1;
  2279. }
  2280. }
  2281. break;
  2282. /*
  2283. ** This stage watchdogs the deployment operation and if for some reason, the deployment
  2284. ** is aborted (the IsDeploying flag becomes false), then it reverts back to hunting for
  2285. ** a convenient spot to deploy.
  2286. */
  2287. case 1:
  2288. if (!IsDeploying) {
  2289. Status = 0;
  2290. }
  2291. break;
  2292. }
  2293. } else {
  2294. if (*this == UNIT_GUNBOAT) {
  2295. if (!Target_Legal(NavCom)) {
  2296. if (PrimaryFacing == DIR_W) {
  2297. Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(Coord_Cell(Coord)))) );
  2298. } else {
  2299. Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX+Map.MapCellWidth, Cell_Y(Coord_Cell(Coord)))) );
  2300. }
  2301. Set_Speed(255);
  2302. }
  2303. if (!Speed) {
  2304. Set_Speed(255);
  2305. }
  2306. if (!Target_Legal(TarCom) || !In_Range(TarCom, 0)) {
  2307. Target_Something_Nearby(THREAT_AREA);
  2308. }
  2309. } else {
  2310. return(TarComClass::Mission_Hunt());
  2311. }
  2312. }
  2313. return(TICKS_PER_SECOND);
  2314. }
  2315. /***********************************************************************************************
  2316. * UnitClass::Look -- Perform map revelation from a unit's position. *
  2317. * *
  2318. * Reveal the map around the specified unit with the sighting range *
  2319. * associated with the specified unit. *
  2320. * *
  2321. * INPUT: incremental -- If looking is to process only the cells in the *
  2322. * outer ring of the unit's search radius, then *
  2323. * set this parameter to true. This method is *
  2324. * quite a bit faster than processing all cells, *
  2325. * but must be used with caution. *
  2326. * *
  2327. * OUTPUT: none *
  2328. * *
  2329. * WARNINGS: none *
  2330. * *
  2331. * HISTORY: *
  2332. * 06/08/1992 JLB : Created. *
  2333. * 03/08/1994 JLB : Added incremental option. *
  2334. * 04/15/1994 JLB : Converted to member function. *
  2335. *=============================================================================================*/
  2336. void UnitClass::Look(bool incremental)
  2337. {
  2338. Validate();
  2339. if (!IsInLimbo && IsOwnedByPlayer) {
  2340. int sight = Class->SightRange;
  2341. if (sight) {
  2342. Map.Sight_From(Coord_Cell(Coord), sight, (*this == UNIT_GUNBOAT) ? false : incremental);
  2343. }
  2344. }
  2345. }
  2346. /***********************************************************************************************
  2347. * UnitClass::Overlap_List -- Determines overlap list for units. *
  2348. * *
  2349. * The unit overlap list is used to keep track of which cells are to *
  2350. * be marked for redraw. This is critical in order to keep the units *
  2351. * displayed correctly. *
  2352. * *
  2353. * INPUT: none *
  2354. * *
  2355. * OUTPUT: Returns with the overlap list pointer for the unit at its *
  2356. * present position. *
  2357. * *
  2358. * WARNINGS: none *
  2359. * *
  2360. * HISTORY: *
  2361. * 05/26/1994 JLB : Created. *
  2362. * 06/19/1994 JLB : Uses Coord_Spillable_List function. *
  2363. *=============================================================================================*/
  2364. short const * UnitClass::Overlap_List(void) const
  2365. {
  2366. Validate();
  2367. static short const _gunboat[] = {-3, -2, 2, 3, REFRESH_EOL};
  2368. int size;
  2369. /*
  2370. ** The gunboat is a special case.
  2371. */
  2372. if (*this == UNIT_GUNBOAT) {
  2373. return(&_gunboat[0]);
  2374. }
  2375. size = ICON_PIXEL_W;
  2376. if (IsSelected || IsFiring) {
  2377. size += 24;
  2378. }
  2379. if (IsSelected || Class->IsGigundo || IsAnimAttached) {
  2380. size = ICON_PIXEL_W*2;
  2381. }
  2382. return(Coord_Spillage_List(Coord, size)+1);
  2383. }
  2384. #ifdef NEVER
  2385. /********************************************************************************************* *
  2386. * UnitClass::Blocking_Object -- Determines how a object blocks a unit *
  2387. * *
  2388. * This routine is used by the Can_Enter_Cell logic when an object is in the desired cell *
  2389. * and it needs to know if that causes blockage. If blocked, this routine will return why. *
  2390. * *
  2391. * INPUT: TechnoClass * pointer to object that is blocking unit *
  2392. * *
  2393. * CELL the cell the unit is being blocked in *
  2394. * *
  2395. * OUTPUT: MoveBitType the way that the object is blocking the unit *
  2396. * *
  2397. * HISTORY: *
  2398. * 06/08/1995 PWG : Created. *
  2399. *=============================================================================================*/
  2400. MoveBitType UnitClass::Blocking_Object(TechnoClass const *techno, CELL cell) const
  2401. {
  2402. Validate();
  2403. /*
  2404. ** There are some extra checks we need to make if the techno is a unit
  2405. */
  2406. bool unit = (techno->What_Am_I() == RTTI_INFANTRY || techno->What_Am_I() == RTTI_UNIT);
  2407. CellClass const * cellptr = &Map[cell];
  2408. if (House->Is_Ally(techno)) {
  2409. if (techno == Contact_With_Whom() && IsTethered) {
  2410. return(MOVE_BIT_OK);
  2411. }
  2412. if (unit) {
  2413. /*
  2414. ** If the unit in question has a destination than we should
  2415. ** be prepared to wait for the unit to get out of our way.
  2416. */
  2417. if (((FootClass *)techno)->NavCom != TARGET_NONE) {
  2418. int face = Dir_Facing(PrimaryFacing);
  2419. int techface = Dir_Facing(((FootClass const *)techno)->PrimaryFacing) ^4;
  2420. if (face != techface && Distance((AbstractClass const *)techno) > 0x1FF) {
  2421. return(MOVE_BIT_MOVING_BLOCK);
  2422. } else {
  2423. // Mono_Printf("Move No!\r");
  2424. return(MOVE_BIT_NO);
  2425. }
  2426. }
  2427. return(MOVE_BIT_TEMP);
  2428. }
  2429. } else {
  2430. /*
  2431. ** If its an enemy unit, things are dealt with a little differently
  2432. */
  2433. if (unit) {
  2434. #ifdef NEVER
  2435. /*
  2436. ** If this is an enemy unit and we are not doing a find path then
  2437. ** we need to tell the unit to uncloak just in case it is a
  2438. ** stealth tank.
  2439. */
  2440. if (!IsFindPath) {
  2441. techno->Do_Uncloak();
  2442. }
  2443. #endif
  2444. /*
  2445. ** Can we just run it over?
  2446. */
  2447. if (techno->Class_Of().IsCrushable && (cellptr->Flag.Composite & 0xE0) == 0 && Class->IsCrusher) {
  2448. /*
  2449. ** Now lets run it over.
  2450. */
  2451. return(MOVE_BIT_OK);
  2452. }
  2453. /*
  2454. ** If the object is cloaked, then consider it passable for findpath purposes,
  2455. ** but not so for all other cases.
  2456. */
  2457. if (techno->Cloak == CLOAKED) {
  2458. if (House == techno->House) return(MOVE_BIT_NO);
  2459. if (IsFindPath) return(MOVE_BIT_OK);
  2460. return(MOVE_BIT_CLOAK);
  2461. }
  2462. /*
  2463. ** If our vehicle is weapon equipped, then report that the cell occupier
  2464. ** needs only to be destroyed in order to make the cell passable.
  2465. */
  2466. if (Class->Primary != WEAPON_NONE) {
  2467. return(MOVE_BIT_DESTROYABLE);
  2468. }
  2469. }
  2470. }
  2471. return(MOVE_BIT_NO);
  2472. }
  2473. #endif
  2474. /***********************************************************************************************
  2475. * UnitClass::Can_Enter_Cell -- Determines cell entry legality. *
  2476. * *
  2477. * Use this routine to determine if the unit can enter the cell *
  2478. * specified and given the direction of entry specified. Typically, *
  2479. * this is used when determining unit travel path. *
  2480. * *
  2481. * INPUT: cell -- The cell to examine. *
  2482. * *
  2483. * facing -- The facing that the unit would enter the specified *
  2484. * cell. If this value is -1, then don't consider *
  2485. * facing when performing the check. *
  2486. * *
  2487. * OUTPUT: Returns the reason why it couldn't enter the cell or MOVE_OK if movement is *
  2488. * allowed. *
  2489. * *
  2490. * WARNINGS: none *
  2491. * *
  2492. * HISTORY: *
  2493. * 09/07/1992 JLB : Created. *
  2494. * 04/16/1994 JLB : Converted to member function. *
  2495. * 07/04/1995 JLB : Allowed to drive on building trying to enter it. *
  2496. *=============================================================================================*/
  2497. MoveType UnitClass::Can_Enter_Cell(CELL cell, FacingType ) const
  2498. {
  2499. Validate();
  2500. CellClass const * cellptr = &Map[cell];
  2501. if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO);
  2502. /*
  2503. ** The gunboat can always move. This prevents it from trying to move around possible hover
  2504. ** craft blockage.
  2505. */
  2506. if (*this == UNIT_GUNBOAT) return(MOVE_OK);
  2507. /*
  2508. ** Moving off the edge of the map is not allowed unless
  2509. ** this is a loaner vehicle.
  2510. */
  2511. if (IsLocked && !IsALoaner && !ScenarioInit && !Map.In_Radar(cell)) {
  2512. return(MOVE_NO);
  2513. }
  2514. MoveType retval = MOVE_OK;
  2515. /*
  2516. ** Certain vehicles can drive over walls. Check for this case and
  2517. ** and return the appropriate flag. Other units treat walls as impassable.
  2518. */
  2519. if (cellptr->Overlay != OVERLAY_NONE) {
  2520. OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);
  2521. if (optr->IsCrate && !House->IsHuman) {
  2522. return(MOVE_NO);
  2523. }
  2524. if (optr->IsWall) {
  2525. if (Class->Primary != WEAPON_NONE) {
  2526. WarheadTypeClass const * whead = &Warheads[BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead];
  2527. if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) {
  2528. if (!House->IsHuman && !House->Is_Ally(cellptr->Owner)) {
  2529. if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
  2530. } else {
  2531. return(MOVE_NO);
  2532. }
  2533. } else {
  2534. return(MOVE_NO);
  2535. }
  2536. } else {
  2537. return(MOVE_NO);
  2538. }
  2539. }
  2540. }
  2541. /*
  2542. ** If the cell is out and out impassable because of underlying terrain, then
  2543. ** return this immutable fact.
  2544. */
  2545. #ifdef ADVANCED
  2546. if (retval != MOVE_DESTROYABLE && !Ground[cellptr->Land_Type()].Cost[Class->Speed]) {
  2547. #else
  2548. if (!Ground[cellptr->Land_Type()].Cost[Class->Speed]) {
  2549. #endif
  2550. return(MOVE_NO);
  2551. }
  2552. /*
  2553. ** Loop through all of the objects in the square setting a bit
  2554. ** for how they affect movement.
  2555. */
  2556. bool crushable = false;
  2557. ObjectClass *obj = cellptr->Cell_Occupier();
  2558. while (obj) {
  2559. if (obj != this) {
  2560. /*
  2561. ** Always allow entry if trying to move on a cell with
  2562. ** authorization from the occupier.
  2563. */
  2564. if (obj == Contact_With_Whom() && (IsTethered || (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_REPAIR))) {
  2565. return(MOVE_OK);
  2566. }
  2567. bool is_moving = (obj->What_Am_I() == RTTI_INFANTRY || obj->What_Am_I() == RTTI_UNIT) && Target_Legal(((FootClass *)obj)->NavCom);
  2568. if (House->Is_Ally(obj)) {
  2569. if (is_moving) {
  2570. int face = Dir_Facing(PrimaryFacing);
  2571. int techface = Dir_Facing(((FootClass const *)obj)->PrimaryFacing) ^4;
  2572. if (face == techface && Distance((AbstractClass const *)obj) <= 0x1FF) {
  2573. return(MOVE_NO);
  2574. }
  2575. if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK;
  2576. } else {
  2577. if (obj->What_Am_I() == RTTI_BUILDING) return(MOVE_NO);
  2578. if (retval < MOVE_TEMP) retval = MOVE_TEMP;
  2579. }
  2580. } else {
  2581. /*
  2582. ** Cloaked enemy objects are not considered if this is a Find_Path()
  2583. ** call.
  2584. */
  2585. if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) {
  2586. /*
  2587. ** If this unit can crush infantry, and there is an enemy infantry in the
  2588. ** cell, don't consider the cell impassible. This is true even if the unit
  2589. ** doesn't contain a legitimate weapon.
  2590. */
  2591. if (!Class->IsCrusher || !obj->Class_Of().IsCrushable) {
  2592. /*
  2593. ** Any non-allied blockage is considered impassible if the unit
  2594. ** is not equipped with a weapon.
  2595. */
  2596. if (Class->Primary == WEAPON_NONE) return(MOVE_NO);
  2597. /*
  2598. ** Some kinds of terrain are considered destroyable if the unit is equipped
  2599. ** with the weapon that can destroy it. Otherwise, the terrain is considered
  2600. ** impassable.
  2601. */
  2602. switch (obj->What_Am_I()) {
  2603. case RTTI_TERRAIN:
  2604. if (((TerrainClass *)obj)->Class->IsFlammable &&
  2605. BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead == WARHEAD_FIRE) {
  2606. if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
  2607. } else {
  2608. return(MOVE_NO);
  2609. }
  2610. break;
  2611. default:
  2612. if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
  2613. break;
  2614. }
  2615. } else {
  2616. crushable = true;
  2617. }
  2618. } else {
  2619. if (retval < MOVE_CLOAK) retval = MOVE_CLOAK;
  2620. }
  2621. }
  2622. }
  2623. /*
  2624. ** Move to next object in chain.
  2625. */
  2626. obj = obj->Next;
  2627. }
  2628. /*
  2629. ** If some allied object has reserved the cell, then consider the cell
  2630. ** as blocked by a moving object.
  2631. */
  2632. if (retval == MOVE_OK && !crushable && cellptr->Flag.Composite) {
  2633. /*
  2634. ** If reserved by a vehicle, then consider this blocked terrain.
  2635. */
  2636. if (cellptr->Flag.Occupy.Vehicle) {
  2637. retval = MOVE_MOVING_BLOCK;
  2638. } else {
  2639. if (cellptr->InfType != HOUSE_NONE && House->Is_Ally(cellptr->InfType)) {
  2640. retval = MOVE_MOVING_BLOCK;
  2641. } else {
  2642. /*
  2643. ** Enemy infantry have reserved the cell. If this unit can crush
  2644. ** infantry, consider the cell passable. If not, then consider the
  2645. ** cell destroyable if it has a weapon. If neither case applies, then
  2646. ** this vehicle should avoid the cell altogether.
  2647. */
  2648. if (!Class->IsCrusher) {
  2649. if (Class->Primary != WEAPON_NONE) {
  2650. retval = MOVE_DESTROYABLE;
  2651. } else {
  2652. return(MOVE_NO);
  2653. }
  2654. }
  2655. }
  2656. }
  2657. }
  2658. /*
  2659. ** If its ok to move into the cell because we can crush whats in the cell, then
  2660. ** make sure no one else is already moving into the cell to crush something.
  2661. */
  2662. if (retval == MOVE_OK && crushable && cellptr->Flag.Occupy.Vehicle) {
  2663. #ifdef ADVANCED
  2664. /*
  2665. ** However, if the cell is occupied by a crushable vehicle, then we can
  2666. ** never be sure if some other friendly vehicle is also trying to crush
  2667. ** the cell at the same time. In the case of a crushable vehicle in the
  2668. ** cell, then allow entry.
  2669. */
  2670. if (!cellptr->Cell_Unit() || !cellptr->Cell_Unit()->Class->IsCrushable) {
  2671. return(MOVE_MOVING_BLOCK);
  2672. }
  2673. #else
  2674. return(MOVE_MOVING_BLOCK);
  2675. #endif
  2676. }
  2677. /*
  2678. ** Return with the most severe reason why this cell would be impassable.
  2679. */
  2680. return(retval);
  2681. }
  2682. /***********************************************************************************************
  2683. * UnitClass::Init -- Clears all units for scenario preparation. *
  2684. * *
  2685. * This routine will zero out the unit list and unit objects. This routine is typically *
  2686. * used in preparation for a new scenario load. All units are guaranteed to be eliminated *
  2687. * by this routine. *
  2688. * *
  2689. * INPUT: none *
  2690. * *
  2691. * OUTPUT: none *
  2692. * *
  2693. * WARNINGS: none *
  2694. * *
  2695. * HISTORY: *
  2696. * 08/15/1994 JLB : Created. *
  2697. *=============================================================================================*/
  2698. void UnitClass::Init(void)
  2699. {
  2700. UnitClass * ptr;
  2701. Units.Free_All();
  2702. ptr = new UnitClass();
  2703. VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0];
  2704. delete ptr;
  2705. }
  2706. /***********************************************************************************************
  2707. * UnitClass::Target_Coord -- The coordinate to use when targeting this unit. *
  2708. * *
  2709. * Sometimes the coordinate to use when targeting an object is not the same as its center *
  2710. * coordinate. This is especially true for boats since leading their movement is needed *
  2711. * in order have any chance of hitting. *
  2712. * *
  2713. * INPUT: none *
  2714. * *
  2715. * OUTPUT: Returns with the coordinate to fire upon when attacking the unit. *
  2716. * *
  2717. * WARNINGS: none *
  2718. * *
  2719. * HISTORY: *
  2720. * 09/19/1994 JLB : Created. *
  2721. *=============================================================================================*/
  2722. COORDINATE UnitClass::Target_Coord(void) const
  2723. {
  2724. Validate();
  2725. // if (*this == UNIT_GUNBOAT) {
  2726. // return(Coord_Move(Coord, PrimaryFacing.Current(), 0x0080));
  2727. // }
  2728. return(TarComClass::Center_Coord());
  2729. }
  2730. /***********************************************************************************************
  2731. * UnitClass::Scatter -- Causes the unit to travel to a nearby safe cell. *
  2732. * *
  2733. * This routine is called when the unit discovers that it should get out of the "hot seat" *
  2734. * and move to an adjacent cell. Since the safety of the adjacent cell is not determined *
  2735. * before the move begins, it will appear that the unit is just scattering (which it *
  2736. * should). *
  2737. * *
  2738. * INPUT: threat -- The coordinate of the source of the threat. The unit will try to move *
  2739. * roughly away from the threat. *
  2740. * *
  2741. * OUTPUT: none *
  2742. * *
  2743. * WARNINGS: none *
  2744. * *
  2745. * HISTORY: *
  2746. * 09/25/1994 JLB : Created. *
  2747. *=============================================================================================*/
  2748. void UnitClass::Scatter(COORDINATE threat, bool forced)
  2749. {
  2750. Validate();
  2751. if (*this != UNIT_GUNBOAT && *this != UNIT_HOVER) {
  2752. if ((!Target_Legal(TarCom) && !Target_Legal(NavCom)) || forced || Random_Pick(1, 4) == 1) {
  2753. FacingType toface;
  2754. FacingType newface;
  2755. CELL newcell;
  2756. if (threat) {
  2757. toface = Dir_Facing(Direction8(threat, Coord));
  2758. toface = toface + (Random_Pick(0, 2)-1);
  2759. } else {
  2760. toface = Dir_Facing(PrimaryFacing.Current());
  2761. }
  2762. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  2763. newface = toface + face;
  2764. newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
  2765. if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) {
  2766. Assign_Destination(::As_Target(newcell));
  2767. }
  2768. }
  2769. }
  2770. }
  2771. }
  2772. /***********************************************************************************************
  2773. * UnitClass::Stop_Driver -- Handles removing occupation bits when driving stops. *
  2774. * *
  2775. * This routine will remove the "reservation" flag (if present) when the vehicle is *
  2776. * required to stop movement. *
  2777. * *
  2778. * INPUT: none *
  2779. * *
  2780. * OUTPUT: bool; Was the vehicle stopped? *
  2781. * *
  2782. * WARNINGS: none *
  2783. * *
  2784. * HISTORY: *
  2785. * 12/22/1994 JLB : Created. *
  2786. *=============================================================================================*/
  2787. bool UnitClass::Stop_Driver(void)
  2788. {
  2789. Validate();
  2790. /*
  2791. ** We only need to do something if the vehicle is actually going
  2792. ** somewhere.
  2793. */
  2794. if (Head_To_Coord()) {
  2795. /*
  2796. ** Safe off whether the vehicle is down or not so we know whether
  2797. ** we have to put it back down.
  2798. */
  2799. int temp = IsDown;
  2800. /*
  2801. ** If the vehicle is down, pick it up so it doesnt interfere with
  2802. ** our flags.
  2803. */
  2804. if (temp) {
  2805. Mark(MARK_UP);
  2806. }
  2807. /*
  2808. ** Call the drive class function which will let us release the
  2809. ** reserved track.
  2810. */
  2811. Mark_Track(Head_To_Coord(), MARK_UP);
  2812. /*
  2813. ** If it was down it should be down when we are done.
  2814. */
  2815. if (temp) {
  2816. Mark(MARK_DOWN);
  2817. }
  2818. }
  2819. return(TarComClass::Stop_Driver());
  2820. }
  2821. /***********************************************************************************************
  2822. * UnitClass::Start_Driver -- Starts driving and reserves destination cell. *
  2823. * *
  2824. * This routine will start the vehicle moving by marking the destination cell as *
  2825. * reserved. Cells must be reserved in this fashion or else they might become occupied as *
  2826. * the vehicle is proceeding toward it. *
  2827. * *
  2828. * INPUT: headto -- The location where the vehicle will be heading. *
  2829. * *
  2830. * OUTPUT: bool; Was the vehicle started to move? Failure could be the result of the cell *
  2831. * being occupied. *
  2832. * *
  2833. * WARNINGS: none *
  2834. * *
  2835. * HISTORY: *
  2836. * 12/22/1994 JLB : Created. *
  2837. *=============================================================================================*/
  2838. bool UnitClass::Start_Driver(COORDINATE & headto)
  2839. {
  2840. Validate();
  2841. if (TarComClass::Start_Driver(headto)) {
  2842. Mark_Track(headto, MARK_DOWN);
  2843. return(true);
  2844. }
  2845. return(false);
  2846. }
  2847. /***********************************************************************************************
  2848. * UnitClass::Limbo -- Prepares vehicle and then limbos it. *
  2849. * *
  2850. * This routine removes the occupation bits for the vehicle and also handles cleaning up *
  2851. * any vehicle reservation bits. After this, it then proceeds with limboing the unit. *
  2852. * *
  2853. * INPUT: none *
  2854. * *
  2855. * OUTPUT: bool; Was the vehicle limboed? *
  2856. * *
  2857. * WARNINGS: none *
  2858. * *
  2859. * HISTORY: *
  2860. * 12/22/1994 JLB : Created. *
  2861. *=============================================================================================*/
  2862. bool UnitClass::Limbo(void)
  2863. {
  2864. Validate();
  2865. if (!IsInLimbo) {
  2866. Stop_Driver();
  2867. }
  2868. return(TarComClass::Limbo());
  2869. }
  2870. /***********************************************************************************************
  2871. * UnitClass::Response_Select -- Voice feedback when selecting the unit. *
  2872. * *
  2873. * This is the voice to play when the unit is selected. *
  2874. * *
  2875. * INPUT: none *
  2876. * *
  2877. * OUTPUT: none *
  2878. * *
  2879. * WARNINGS: none *
  2880. * *
  2881. * HISTORY: *
  2882. * 12/30/1994 JLB : Created. *
  2883. *=============================================================================================*/
  2884. void UnitClass::Response_Select(void)
  2885. {
  2886. Validate();
  2887. static VocType _response[] = {
  2888. VOC_VEHIC,
  2889. VOC_UNIT,
  2890. VOC_YESSIR,
  2891. VOC_YESSIR,
  2892. VOC_YESSIR,
  2893. VOC_AWAIT
  2894. };
  2895. VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)];
  2896. if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) {
  2897. response = VOC_DINOYES;
  2898. }
  2899. if (AllowVoice) {
  2900. Sound_Effect(response, 0, -(Units.ID(this)+1));
  2901. }
  2902. }
  2903. /***********************************************************************************************
  2904. * UnitClass::Response_Move -- Voice feedback when ordering the unit to move. *
  2905. * *
  2906. * This plays the audio feedback when ordering this unit to move to a new destination. *
  2907. * *
  2908. * INPUT: none *
  2909. * *
  2910. * OUTPUT: none *
  2911. * *
  2912. * WARNINGS: none *
  2913. * *
  2914. * HISTORY: *
  2915. * 12/30/1994 JLB : Created. *
  2916. *=============================================================================================*/
  2917. void UnitClass::Response_Move(void)
  2918. {
  2919. Validate();
  2920. static VocType _response[] = {
  2921. VOC_MOVEOUT,
  2922. VOC_MOVEOUT,
  2923. VOC_MOVEOUT,
  2924. VOC_ACKNOWL,
  2925. VOC_AFFIRM,
  2926. VOC_AFFIRM
  2927. };
  2928. VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)];
  2929. if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) {
  2930. response = VOC_DINOMOUT;
  2931. }
  2932. if (AllowVoice) {
  2933. Sound_Effect(response, 0, -(Units.ID(this)+1));
  2934. }
  2935. }
  2936. /***********************************************************************************************
  2937. * UnitClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. *
  2938. * *
  2939. * This plays the audio feedback when ordering this unit to attack. *
  2940. * *
  2941. * INPUT: none *
  2942. * *
  2943. * OUTPUT: none *
  2944. * *
  2945. * WARNINGS: none *
  2946. * *
  2947. * HISTORY: *
  2948. * 12/30/1994 JLB : Created. *
  2949. *=============================================================================================*/
  2950. void UnitClass::Response_Attack(void)
  2951. {
  2952. Validate();
  2953. static VocType _response[] = {
  2954. VOC_AFFIRM,
  2955. VOC_ACKNOWL,
  2956. VOC_YESSIR,
  2957. VOC_YESSIR,
  2958. VOC_YESSIR
  2959. };
  2960. VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)];
  2961. if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) {
  2962. response = VOC_DINOMOUT;
  2963. }
  2964. if (AllowVoice) {
  2965. Sound_Effect(response, 0, -(Units.ID(this)+1));
  2966. }
  2967. }
  2968. /***********************************************************************************************
  2969. * UnitClass::What_Action -- Determines what action would occur if clicked on object. *
  2970. * *
  2971. * Use this function to determine what action would likely occur if the specified object *
  2972. * were clicked on while this unit was selected as current. This function controls, not *
  2973. * only the action to perform, but indirectly controls the cursor shape to use as well. *
  2974. * *
  2975. * INPUT: object -- The object that to check for against "this" object. *
  2976. * *
  2977. * OUTPUT: Returns with the default action to perform. If no clear action can be determined, *
  2978. * then ACTION_NONE is returned. *
  2979. * *
  2980. * WARNINGS: none *
  2981. * *
  2982. * HISTORY: *
  2983. * 01/11/1995 JLB : Created. *
  2984. *=============================================================================================*/
  2985. ActionType UnitClass::What_Action(ObjectClass * object) const
  2986. {
  2987. Validate();
  2988. ActionType action = TarComClass::What_Action(object);
  2989. /*
  2990. ** If the unit doesn't have a weapon, but can crush the object, then consider
  2991. ** the object as a movable location.
  2992. */
  2993. if (action == ACTION_ATTACK && !Can_Player_Fire()) {
  2994. if (Class->IsCrusher && object->Class_Of().IsCrushable) {
  2995. action = ACTION_MOVE;
  2996. } else {
  2997. action = ACTION_SELECT;
  2998. }
  2999. }
  3000. /*
  3001. ** Don't allow special deploy action unless there is something to deploy.
  3002. */
  3003. if (action == ACTION_SELF) {
  3004. if (*this != UNIT_MCV) {
  3005. if (!Class->IsTransporter || !How_Many()) {
  3006. action = ACTION_NONE;
  3007. }
  3008. } else {
  3009. ((ObjectClass &)(*this)).Mark(MARK_UP);
  3010. if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) {
  3011. action = ACTION_NONE;
  3012. }
  3013. ((ObjectClass &)(*this)).Mark(MARK_DOWN);
  3014. }
  3015. }
  3016. /*
  3017. ** Special return to friendly refinery action.
  3018. */
  3019. if (IsOwnedByPlayer && object->Is_Techno() && ((TechnoClass const *)object)->House->Is_Ally(this)) {
  3020. if (object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) {
  3021. action = ACTION_ENTER;
  3022. }
  3023. }
  3024. /*
  3025. ** Special return to friendly repair factory action.
  3026. */
  3027. if (IsOwnedByPlayer && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) {
  3028. BuildingClass * building = (BuildingClass *)object;
  3029. if (building->Class->Type == STRUCT_REPAIR && !building->In_Radio_Contact() && !building->Is_Something_Attached()) {
  3030. action = ACTION_MOVE;
  3031. }
  3032. }
  3033. return(action);
  3034. }
  3035. ActionType UnitClass::What_Action(CELL cell) const
  3036. {
  3037. Validate();
  3038. ActionType action = TarComClass::What_Action(cell);
  3039. if (action == ACTION_MOVE && Map[cell].Land_Type() == LAND_TIBERIUM && Class->IsToHarvest) {
  3040. return(ACTION_HARVEST);
  3041. }
  3042. return(action);
  3043. }
  3044. /***********************************************************************************************
  3045. * UnitClass::Can_Player_Move -- Determines if the player is legally allowed to move it. *
  3046. * *
  3047. * Use this routine to see if the player can move this object. If the player can move the *
  3048. * object, even only in theory, then this function returns true. In all other cases, such *
  3049. * as for enemy units, gunboats, or hovercraft, it returns false. *
  3050. * *
  3051. * INPUT: none *
  3052. * *
  3053. * OUTPUT: bool; Can the player give this object a movement order? *
  3054. * *
  3055. * WARNINGS: none *
  3056. * *
  3057. * HISTORY: *
  3058. * 01/19/1995 JLB : Created. *
  3059. *=============================================================================================*/
  3060. bool UnitClass::Can_Player_Move(void) const
  3061. {
  3062. Validate();
  3063. return(TarComClass::Can_Player_Move() && *this != UNIT_GUNBOAT && *this != UNIT_HOVER);
  3064. }
  3065. /***********************************************************************************************
  3066. * UnitClass::Read_INI -- Reads units from scenario INI file. *
  3067. * *
  3068. * This routine is used to read all the starting units from the *
  3069. * scenario control INI file. The units are created and placed on the *
  3070. * map by this routine. *
  3071. * *
  3072. * INI entry format: *
  3073. * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername *
  3074. * *
  3075. * INPUT: buffer -- Pointer to the loaded scenario INI file. *
  3076. * *
  3077. * OUTPUT: none *
  3078. * *
  3079. * WARNINGS: none *
  3080. * *
  3081. * HISTORY: *
  3082. * 05/24/1994 JLB : Created. *
  3083. *=============================================================================================*/
  3084. void UnitClass::Read_INI(char *buffer)
  3085. {
  3086. UnitClass *unit; // Working unit pointer.
  3087. char *tbuffer; // Accumulation buffer of unit IDs.
  3088. HousesType inhouse; // Unit house.
  3089. UnitType classid; // Unit class.
  3090. int len; // Length of data in buffer.
  3091. char buf[128];
  3092. len = strlen(buffer) + 2;
  3093. tbuffer = buffer + len;
  3094. WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer);
  3095. while (*tbuffer != '\0') {
  3096. WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer);
  3097. inhouse = HouseTypeClass::From_Name(strtok(buf, ","));
  3098. if (inhouse != HOUSE_NONE) {
  3099. classid = UnitTypeClass::From_Name(strtok(NULL, ","));
  3100. if (classid != UNIT_NONE) {
  3101. unit = new UnitClass(classid, inhouse);
  3102. if (unit) {
  3103. /*
  3104. ** Read the raw data.
  3105. */
  3106. int strength = atoi(strtok(NULL, ",\r\n"));
  3107. COORDINATE coord = Cell_Coord((CELL)atoi(strtok(NULL, ",\r\n")));
  3108. DirType dir = (DirType)atoi(strtok(NULL, ",\r\n"));
  3109. MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r"));
  3110. unit->Trigger = TriggerClass::As_Pointer(strtok(NULL,",\r\n"));
  3111. if (unit->Trigger) {
  3112. unit->Trigger->AttachCount++;
  3113. }
  3114. if (unit->Unlimbo(coord, dir)) {
  3115. unit->Strength = Fixed_To_Cardinal(unit->Class->MaxStrength, strength);
  3116. if (GameToPlay == GAME_NORMAL || unit->House->IsHuman) {
  3117. unit->Assign_Mission(mission);
  3118. unit->Commence();
  3119. } else {
  3120. unit->Enter_Idle_Mode();
  3121. }
  3122. /*
  3123. ** The gunboat is a special case: It must "drive" off the edge of the map.
  3124. ** Just pick the map edge that it is facing and set that as the destination
  3125. ** of the drive.
  3126. */
  3127. if (*unit == UNIT_GUNBOAT) {
  3128. unit->PrimaryFacing.Set_Desired(DIR_W);
  3129. unit->PrimaryFacing.Set_Current(DIR_W);
  3130. unit->Assign_Mission(MISSION_HUNT);
  3131. unit->Commence();
  3132. unit->Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(Coord_Cell(unit->Coord)))));
  3133. }
  3134. } else {
  3135. /*
  3136. ** If the unit could not be unlimboed, then this is a catastrophic error
  3137. ** condition. Delete the unit.
  3138. */
  3139. delete unit;
  3140. }
  3141. }
  3142. }
  3143. }
  3144. tbuffer += strlen(tbuffer)+1;
  3145. }
  3146. }
  3147. /***********************************************************************************************
  3148. * UnitClass::Write_INI -- Writes all the units out to an INI file. *
  3149. * *
  3150. * This routine writes all of the units in the game out to an INI file. This is used *
  3151. * in the scenario editor when the game needs to be saved. *
  3152. * *
  3153. * INI entry format: *
  3154. * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername *
  3155. * *
  3156. * INPUT: buffer -- A pointer to the loaded INI file staging area. *
  3157. * *
  3158. * OUTPUT: none *
  3159. * *
  3160. * WARNINGS: none *
  3161. * *
  3162. * HISTORY: *
  3163. * 05/28/1994 JLB : Created. *
  3164. *=============================================================================================*/
  3165. void UnitClass::Write_INI(char *buffer)
  3166. {
  3167. int index;
  3168. char uname[10];
  3169. char buf[128];
  3170. char *tbuffer; // Accumulation buffer of unit IDs.
  3171. /*
  3172. ** First, clear out all existing unit data from the ini file.
  3173. */
  3174. tbuffer = buffer + strlen(buffer) + 2;
  3175. WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer);
  3176. while (*tbuffer != '\0') {
  3177. WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer);
  3178. tbuffer += strlen(tbuffer)+1;
  3179. }
  3180. /*
  3181. ** Write the unit data out.
  3182. */
  3183. for (index = 0; index < Units.Count(); index++) {
  3184. UnitClass * unit;
  3185. unit = Units.Ptr(index);
  3186. if (!unit->IsInLimbo && unit->IsActive) {
  3187. sprintf(uname, "%03d", index);
  3188. sprintf(buf, "%s,%s,%d,%u,%d,%s,%s",
  3189. unit->House->Class->IniName,
  3190. unit->Class->IniName,
  3191. unit->Health_Ratio(),
  3192. Coord_Cell(unit->Coord),
  3193. unit->PrimaryFacing.Current(),
  3194. MissionClass::Mission_Name(unit->Mission),
  3195. unit->Trigger ? unit->Trigger->Get_Name() : "None"
  3196. );
  3197. WWWritePrivateProfileString(INI_Name(), uname, buf, buffer);
  3198. }
  3199. }
  3200. }
  3201. /***********************************************************************************************
  3202. * UnitClass::Exit_Repair -- Drive the unit off the repair facility. *
  3203. * *
  3204. * INPUT: none *
  3205. * *
  3206. * OUTPUT: none *
  3207. * *
  3208. * WARNINGS: none *
  3209. * *
  3210. * HISTORY: *
  3211. * 04/03/1995 BWG : Created. *
  3212. *=============================================================================================*/
  3213. #define XYCELL(x,y) (y*MAP_CELL_W+x)
  3214. void UnitClass::Exit_Repair(void)
  3215. {
  3216. Validate();
  3217. int i;
  3218. CELL cell;
  3219. bool found = false;
  3220. static short const ExitRepair[] = {
  3221. XYCELL(0,-2),
  3222. XYCELL(1,-1),
  3223. XYCELL(2, 0),
  3224. XYCELL(1, 1),
  3225. XYCELL(0, 2),
  3226. XYCELL(-1,1),
  3227. XYCELL(-2,0),
  3228. XYCELL(-1,-1)
  3229. };
  3230. cell = Coord_Cell(Coord) + ExitRepair[Dir_Facing(PrimaryFacing.Current())];
  3231. if (Can_Enter_Cell(cell) == MOVE_OK) found = true;
  3232. if (!found) for (i=0; i<8; i++) {
  3233. cell = Coord_Cell(Coord) + ExitRepair[i];
  3234. if (Can_Enter_Cell(cell) == MOVE_OK) {
  3235. found = true;
  3236. break;
  3237. }
  3238. }
  3239. if (found) {
  3240. DirType dir = Direction(cell);
  3241. Assign_Mission(MISSION_MOVE);
  3242. Assign_Destination(::As_Target(cell));
  3243. }
  3244. }
  3245. /***********************************************************************************************
  3246. * UnitClass::Mission_Guard -- Special guard mission override processor. *
  3247. * *
  3248. * This routine will intercept the guard mission and if it is for a hovercraft, assign *
  3249. * it the unload mission instead. This prevents the hovercraft from being stuck in the *
  3250. * water if something unexpected causes it to drop into guard mode. *
  3251. * *
  3252. * INPUT: none *
  3253. * *
  3254. * OUTPUT: Returns the time delay before this command is executed again. *
  3255. * *
  3256. * WARNINGS: none *
  3257. * *
  3258. * HISTORY: *
  3259. * 05/08/1995 JLB : Created. *
  3260. * 05/08/1995 JLB : Fixes gunboat problems. *
  3261. *=============================================================================================*/
  3262. int UnitClass::Mission_Guard(void)
  3263. {
  3264. Validate();
  3265. if (*this == UNIT_HOVER) {
  3266. if (Is_Something_Attached()) {
  3267. Assign_Mission(MISSION_UNLOAD);
  3268. Find_LZ();
  3269. } else {
  3270. Exit_Map();
  3271. }
  3272. return(TICKS_PER_SECOND);
  3273. }
  3274. if (*this == UNIT_GUNBOAT) {
  3275. Assign_Mission(MISSION_HUNT);
  3276. return(TICKS_PER_SECOND);
  3277. }
  3278. if (*this == UNIT_HARVESTER && !House->IsHuman) {
  3279. Assign_Mission(MISSION_HARVEST);
  3280. return(TICKS_PER_SECOND);
  3281. }
  3282. return(TarComClass::Mission_Guard());
  3283. }
  3284. /***********************************************************************************************
  3285. * UnitClass::Mission_Move -- Handles special move mission overrides. *
  3286. * *
  3287. * This routine intercepts the normal move mission and if a gunboat is being processed, *
  3288. * changes its mission to hunt. This is an attempt to keep the gunboat on the hunt mission *
  3289. * regardless of what the player did. *
  3290. * *
  3291. * INPUT: none *
  3292. * *
  3293. * OUTPUT: Returns the number of ticks before this routine should be called again. *
  3294. * *
  3295. * WARNINGS: none *
  3296. * *
  3297. * HISTORY: *
  3298. * 05/09/1995 JLB : Created. *
  3299. *=============================================================================================*/
  3300. int UnitClass::Mission_Move(void)
  3301. {
  3302. Validate();
  3303. IsHarvesting = false;
  3304. /*
  3305. ** Always make sure that that transport door is closed if the vehcile is moving.
  3306. */
  3307. if (!Is_Door_Closed()) {
  3308. APC_Close_Door();
  3309. }
  3310. /*
  3311. ** Gunboats must always have the hunt mission.
  3312. */
  3313. if (*this == UNIT_GUNBOAT) {
  3314. Assign_Mission(MISSION_HUNT);
  3315. return(TICKS_PER_SECOND);
  3316. }
  3317. return(TarComClass::Mission_Move());
  3318. }
  3319. /***********************************************************************************************
  3320. * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. *
  3321. * *
  3322. * This routine examines the unit and adjacent cells in order to find the best facing *
  3323. * for the transport and best staging cell for the potential passengers. This location is *
  3324. * modified by adjacent cell passability and direction of the potential passenger. *
  3325. * *
  3326. * INPUT: passenger -- Pointer to the potential passenger. *
  3327. * *
  3328. * moveto -- Reference to the cell number that specifies where the potential *
  3329. * passenger should move to first. *
  3330. * *
  3331. * OUTPUT: Returns with the direction the transport should face before opening the transport *
  3332. * door. *
  3333. * *
  3334. * WARNINGS: none *
  3335. * *
  3336. * HISTORY: *
  3337. * 05/23/1995 JLB : Created. *
  3338. *=============================================================================================*/
  3339. DirType UnitClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const
  3340. {
  3341. Validate();
  3342. /*
  3343. ** Determine the ideal facing that provides the least resistance. This would be the direction
  3344. ** of the potential passenger or the current transport facing if it is going to unload.
  3345. */
  3346. DirType faceto;
  3347. if (passenger) {
  3348. faceto = Direction(passenger);
  3349. } else {
  3350. faceto = PrimaryFacing.Current() + DIR_S;
  3351. }
  3352. /*
  3353. ** Sweep through the adjacent cells in order to find the best candidate.
  3354. */
  3355. FacingType bestdir;
  3356. int bestval = -1;
  3357. for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
  3358. int value = 0;
  3359. CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face);
  3360. /*
  3361. ** Base the initial value of the potential cell according to whether the passenger is
  3362. ** allowed to enter the cell. If it can't, then give such a negative value to the
  3363. ** cell so that it is prevented from ever choosing that cell for load/unload.
  3364. */
  3365. if (passenger) {
  3366. value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128;
  3367. } else {
  3368. CellClass * cell = &Map[cellnum];
  3369. if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) {
  3370. value = -128;
  3371. } else {
  3372. if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) {
  3373. value = -128;
  3374. } else {
  3375. value = 128;
  3376. }
  3377. }
  3378. }
  3379. /*
  3380. ** Give more weight to the cells that require the least rotation of the transport or the
  3381. ** least roundabout movement for the potential passenger.
  3382. */
  3383. value -= (int)ABS(Dir_Diff(Facing_Dir(face), faceto));
  3384. if (face == FACING_S) {
  3385. value -= 100;
  3386. }
  3387. if (face == FACING_SW || face == FACING_SE) value += 64;
  3388. /*
  3389. ** If the value for the potiential cell is greater than the last recorded potential
  3390. ** value, then record this cell as the best candidate.
  3391. */
  3392. if (bestval == -1 || value > bestval) {
  3393. bestval = value;
  3394. bestdir = face;
  3395. }
  3396. }
  3397. /*
  3398. ** If a suitable direction was found, then return with the direction value.
  3399. */
  3400. moveto = 0;
  3401. if (bestval > 0) {
  3402. static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE};
  3403. moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir);
  3404. return(_desired_to_actual[bestdir]);
  3405. }
  3406. return(DIR_S);
  3407. }
  3408. /***********************************************************************************************
  3409. * UnitClass::Mission_Attack -- Handles the mission attack logic. *
  3410. * *
  3411. * This routine intercepts the normal mission attack logic. If a gunboat is assigned the *
  3412. * attack mission then it must be converted back to a hunt mission. *
  3413. * *
  3414. * INPUT: none *
  3415. * *
  3416. * OUTPUT: Returns with the time before calling this routine again. *
  3417. * *
  3418. * WARNINGS: none *
  3419. * *
  3420. * HISTORY: *
  3421. * 05/23/1995 JLB : Created. *
  3422. *=============================================================================================*/
  3423. int UnitClass::Mission_Attack(void)
  3424. {
  3425. Validate();
  3426. if (*this == UNIT_GUNBOAT) {
  3427. Assign_Mission(MISSION_HUNT);
  3428. return(TICKS_PER_SECOND);
  3429. }
  3430. return(TarComClass::Mission_Attack());
  3431. }
  3432. /***********************************************************************************************
  3433. * UnitClass::Flag_Attach -- Attaches a house flag to this unit. *
  3434. * *
  3435. * This routine will attach a house flag to this unit. *
  3436. * *
  3437. * INPUT: house -- The house that is having its flag attached to it. *
  3438. * *
  3439. * OUTPUT: Was the house flag successfully attached to this unit? *
  3440. * *
  3441. * WARNINGS: A unit can only carry one flag at a time. This might be a reason for failure *
  3442. * of this routine. *
  3443. * *
  3444. * HISTORY: *
  3445. * 05/23/1995 JLB : Created. *
  3446. *=============================================================================================*/
  3447. bool UnitClass::Flag_Attach(HousesType house)
  3448. {
  3449. Validate();
  3450. if (house != HOUSE_NONE && Flagged == HOUSE_NONE) {
  3451. Flagged = house;
  3452. Mark(MARK_CHANGE);
  3453. return(true);
  3454. }
  3455. return(false);
  3456. }
  3457. /***********************************************************************************************
  3458. * UnitClass::Flag_Remove -- Removes the house flag from this unit. *
  3459. * *
  3460. * This routine will remove the house flag that is attached to this unit. *
  3461. * *
  3462. * INPUT: none *
  3463. * *
  3464. * OUTPUT: Was the flag successfully removed? *
  3465. * *
  3466. * WARNINGS: This routine doesn't put the flag into a new location. That operation must *
  3467. * be performed or else the house flag will cease to exist. *
  3468. * *
  3469. * HISTORY: *
  3470. * 05/23/1995 JLB : Created. *
  3471. *=============================================================================================*/
  3472. bool UnitClass::Flag_Remove(void)
  3473. {
  3474. Validate();
  3475. if (Flagged != HOUSE_NONE) {
  3476. Flagged = HOUSE_NONE;
  3477. Mark(MARK_CHANGE);
  3478. return(true);
  3479. }
  3480. return(false);
  3481. }
  3482. /***********************************************************************************************
  3483. * UnitClass::Stun -- Stuns the unit in preparation for unit removal. *
  3484. * *
  3485. * This routine intercepts the stun operation for the unit and if there is a house flag *
  3486. * attached, it will drop it to the ground. *
  3487. * *
  3488. * INPUT: none *
  3489. * *
  3490. * OUTPUT: none *
  3491. * *
  3492. * WARNINGS: none *
  3493. * *
  3494. * HISTORY: *
  3495. * 05/23/1995 JLB : Created. *
  3496. *=============================================================================================*/
  3497. void UnitClass::Stun(void)
  3498. {
  3499. Validate();
  3500. if (Flagged != HOUSE_NONE) {
  3501. HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord));
  3502. }
  3503. TarComClass::Stun();
  3504. }
  3505. /***********************************************************************************************
  3506. * UnitClass::Pip_Count -- Fetchs the number of pips to display on unit. *
  3507. * *
  3508. * This routine is used to fetch the number of "fullness" pips to display on the unit. *
  3509. * This will either be the number of passengers or the percentage full (in 1/5ths) of *
  3510. * a harvester. *
  3511. * *
  3512. * INPUT: none *
  3513. * *
  3514. * OUTPUT: Returns with the number of pips to draw on this unit. *
  3515. * *
  3516. * WARNINGS: none *
  3517. * *
  3518. * HISTORY: *
  3519. * 06/25/1995 JLB : Created. *
  3520. *=============================================================================================*/
  3521. int UnitClass::Pip_Count(void) const
  3522. {
  3523. Validate();
  3524. if (Class->IsTransporter) {
  3525. return(How_Many());
  3526. }
  3527. if (Class->IsToHarvest) {
  3528. return(Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS/100, Tiberium_Load()));
  3529. }
  3530. return(0);
  3531. }
  3532. /***********************************************************************************************
  3533. * UnitClass::APC_Close_Door -- Closes an APC door. *
  3534. * *
  3535. * This routine will initiate closing of the APC door. *
  3536. * *
  3537. * INPUT: none *
  3538. * *
  3539. * OUTPUT: none *
  3540. * *
  3541. * WARNINGS: none *
  3542. * *
  3543. * HISTORY: *
  3544. * 06/25/1995 JLB : Created. *
  3545. *=============================================================================================*/
  3546. void UnitClass::APC_Close_Door(void)
  3547. {
  3548. Validate();
  3549. Close_Door(10, 2);
  3550. }
  3551. /***********************************************************************************************
  3552. * UnitClass::APC_Open_Door -- Opens an APC door. *
  3553. * *
  3554. * This routine will initiate opening of the APC door. *
  3555. * *
  3556. * INPUT: none *
  3557. * *
  3558. * OUTPUT: none *
  3559. * *
  3560. * WARNINGS: none *
  3561. * *
  3562. * HISTORY: *
  3563. * 06/25/1995 JLB : Created. *
  3564. *=============================================================================================*/
  3565. void UnitClass::APC_Open_Door(void)
  3566. {
  3567. Validate();
  3568. if (!IsDriving && !IsRotating) {
  3569. if (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE) {
  3570. Open_Door(10, 2);
  3571. } else {
  3572. Open_Door(1, 2);
  3573. }
  3574. }
  3575. }
  3576. /***********************************************************************************************
  3577. * UnitClass::Remap_Table -- Fetches the remap table to use for this object. *
  3578. * *
  3579. * Use this routine to determine the rendering remap table to use for this object. The *
  3580. * remap table is normally the unit remap table, except for the MCV and the Harvestor. *
  3581. * These units use the building remap table since these units become part of the building *
  3582. * animation. *
  3583. * *
  3584. * INPUT: none *
  3585. * *
  3586. * OUTPUT: Returns with a pointer to the remap table to use for this unit. *
  3587. * *
  3588. * WARNINGS: none *
  3589. * *
  3590. * HISTORY: *
  3591. * 07/08/1995 JLB : Created. *
  3592. *=============================================================================================*/
  3593. void const * UnitClass::Remap_Table(void)
  3594. {
  3595. Validate();
  3596. if (*this == UNIT_MCV || *this == UNIT_HARVESTER) {
  3597. return(House->Remap_Table(IsBlushing, false));
  3598. }
  3599. return(TarComClass::Remap_Table());
  3600. }
  3601. /***********************************************************************************************
  3602. * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. *
  3603. * *
  3604. * When a unit is destroyed, a crew member might be generated. This routine will return *
  3605. * with the infantry type to produce for this unit. This routine will be called for every *
  3606. * survivor that is generated. *
  3607. * *
  3608. * INPUT: none *
  3609. * *
  3610. * OUTPUT: Returns with a suggested infantry type to generate as a survivor from this unit. *
  3611. * *
  3612. * WARNINGS: none *
  3613. * *
  3614. * HISTORY: *
  3615. * 08/13/1995 JLB : Created. *
  3616. *=============================================================================================*/
  3617. InfantryType UnitClass::Crew_Type(void) const
  3618. {
  3619. Validate();
  3620. if (Class->Primary == WEAPON_NONE) {
  3621. if (Random_Pick(0, 1) == 0) {
  3622. return(INFANTRY_C1);
  3623. } else {
  3624. return(INFANTRY_C7);
  3625. }
  3626. }
  3627. return(TarComClass::Crew_Type());
  3628. }
  3629. /***********************************************************************************************
  3630. * UnitClass::What_Am_I -- Returns with the RTTI type this object is. *
  3631. * *
  3632. * This will return that this is a normal vehicle unit type. Each object class overrides *
  3633. * this function in order to provide run time type identification support. *
  3634. * *
  3635. * INPUT: none *
  3636. * *
  3637. * OUTPUT: Returns the RTTI type that this object is (i.e., RTTI_UNIT). *
  3638. * *
  3639. * WARNINGS: none *
  3640. * *
  3641. * HISTORY: *
  3642. * 08/13/1995 JLB : Created. *
  3643. *=============================================================================================*/
  3644. RTTIType UnitClass::What_Am_I(void) const
  3645. {
  3646. Validate();
  3647. return(RTTI_UNIT);
  3648. }