CELL.CPP 140 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: /CounterStrike/CELL.CPP 4 3/14/97 1:15p Joe_b $ */
  15. /***********************************************************************************************
  16. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : CELL.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : April 29, 1994 *
  26. * *
  27. * Last Update : October 6, 1996 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. *
  32. * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level *
  33. * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. *
  34. * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. *
  35. * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. *
  36. * CellClass::CellClass -- Constructor for cell objects. *
  37. * CellClass::Cell_Building -- Return with building at specified cell. *
  38. * CellClass::Cell_Color -- Determine what radar color to use for this cell. *
  39. * CellClass::Cell_Coord -- Returns the coordinate of this cell. *
  40. * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell *
  41. * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. *
  42. * CellClass::Cell_Object -- Returns with clickable object in cell. *
  43. * CellClass::Cell_Techno -- Return with the unit/building at specified cell. *
  44. * CellClass::Cell_Terrain -- Determines terrain object in cell. *
  45. * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. *
  46. * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. *
  47. * CellClass::Clear_Icon -- Calculates what the clear icon number should be. *
  48. * CellClass::Closest_Free_Spot -- returns free spot closest to given coord *
  49. * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. *
  50. * CellClass::Draw_It -- Draws the cell imagery at the location specified. *
  51. * CellClass::Flag_Place -- Places a house flag down on the cell. *
  52. * CellClass::Flag_Remove -- Removes the house flag from the cell. *
  53. * CellClass::Goodie_Check -- Performs crate discovery logic. *
  54. * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. *
  55. * CellClass::Incoming -- Causes objects in cell to "run for cover". *
  56. * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. *
  57. * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. *
  58. * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel *
  59. * CellClass::Occupy_Down -- Flag occupation of specified cell. *
  60. * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. *
  61. * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (over*
  62. * CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. *
  63. * CellClass::Overlap_Up -- Removes overlap flag for the cell. *
  64. * CellClass::Read -- Reads a particular cell value from a save game file. *
  65. * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. *
  66. * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. *
  67. * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. *
  68. * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. *
  69. * CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. *
  70. * CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
  71. * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE *
  72. * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. *
  73. * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. *
  74. * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. *
  75. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  76. #include "function.h"
  77. #include "vortex.h"
  78. /*
  79. ** New sidebar for GlyphX multiplayer. ST - 8/2/2019 2:50PM
  80. */
  81. #include "SidebarGlyphx.h"
  82. /***********************************************************************************************
  83. * CellClass::CellClass -- Constructor for cell objects. *
  84. * *
  85. * A cell object is constructed into an empty state. It contains no specific objects, *
  86. * templates, or overlays. *
  87. * *
  88. * INPUT: none *
  89. * *
  90. * OUTPUT: none *
  91. * *
  92. * WARNINGS: none *
  93. * *
  94. * HISTORY: *
  95. * 08/09/1994 JLB : Created. *
  96. * 02/20/1996 JLB : Uses initializer list. *
  97. *=============================================================================================*/
  98. CellClass::CellClass(void) :
  99. ID(Map.ID(this)),
  100. IsPlot(false),
  101. IsCursorHere(false),
  102. IsMapped(false),
  103. IsVisible(false),
  104. IsWaypoint(false),
  105. IsRadarCursor(false),
  106. IsFlagged(false),
  107. IsToShroud(false),
  108. Jammed(0),
  109. Trigger(NULL),
  110. TType(TEMPLATE_NONE),
  111. TIcon(0),
  112. Overlay(OVERLAY_NONE),
  113. OverlayData(0),
  114. Smudge(SMUDGE_NONE),
  115. SmudgeData(0),
  116. Owner(HOUSE_NONE),
  117. InfType(HOUSE_NONE),
  118. OccupierPtr(0),
  119. Land(LAND_CLEAR),
  120. OverrideLand(LAND_NONE),
  121. IsMappedByPlayerMask(0),
  122. IsVisibleByPlayerMask(0),
  123. CTFFlag(NULL)
  124. {
  125. for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) {
  126. Zones[zone] = 0;
  127. }
  128. Flag.Composite = 0;
  129. for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
  130. Overlapper[index] = 0;
  131. }
  132. }
  133. /***********************************************************************************************
  134. * CellClass::Cell_Color -- Determine what radar color to use for this cell. *
  135. * *
  136. * Use this routine to determine what radar color to render a radar *
  137. * pixel with. This routine is called many many times to render the *
  138. * radar map, so it must be fast. *
  139. * *
  140. * INPUT: none *
  141. * *
  142. * OUTPUT: Returns with the color to display the radar pixel with. *
  143. * *
  144. * WARNINGS: none *
  145. * *
  146. * HISTORY: *
  147. * 03/01/1994 JLB : Created. *
  148. * 04/30/1994 JLB : Converted to member function. *
  149. * 05/31/1994 JLB : Takes into account any stealth characteristics of object. *
  150. *=============================================================================================*/
  151. int CellClass::Cell_Color(bool override) const
  152. {
  153. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  154. BuildingClass * object = Cell_Building();
  155. if (object && !object->Class->IsInvisible) {
  156. return(ColorRemaps[object->House->RemapColor].Bar);
  157. }
  158. if (override) {
  159. return(TBLACK);
  160. }
  161. if (LastTheater == THEATER_SNOW) {
  162. return(::SnowColor[Land_Type()]);
  163. } else {
  164. return(::GroundColor[Land_Type()]);
  165. }
  166. }
  167. /***********************************************************************************************
  168. * CellClass::Cell_Techno -- Return with the unit/building at specified cell. *
  169. * *
  170. * Returns an object located in the cell. If there is a *
  171. * building present, it returns a pointer to that, otherwise it returns *
  172. * a pointer to one of the units there. If nothing is present in the *
  173. * specified cell, then it returns NULL. *
  174. * *
  175. * INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting *
  176. * the desired object within the cell. *
  177. * *
  178. * OUTPUT: Returns a pointer to a building or unit located in cell. If *
  179. * nothing present, just returns NULL. *
  180. * *
  181. * WARNINGS: none *
  182. * *
  183. * HISTORY: *
  184. * 08/05/1992 JLB : Created. *
  185. * 04/30/1994 JLB : Converted to member function. *
  186. *=============================================================================================*/
  187. TechnoClass * CellClass::Cell_Techno(int x, int y) const
  188. {
  189. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  190. ObjectClass * object;
  191. COORDINATE click; // Coordinate of click relative to cell corner.
  192. TechnoClass * close = NULL;
  193. long distance = 0; // Recorded closest distance.
  194. /*
  195. ** Create a coordinate value that represent the pixel location within the cell. This is
  196. ** actually the lower significant bits (leptons) of a regular coordinate value.
  197. */
  198. click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y));
  199. if (Cell_Occupier()) {
  200. object = Cell_Occupier();
  201. while (object && object->IsActive) {
  202. if (object->Is_Techno()) {
  203. COORDINATE coord = Coord_Fraction(object->Center_Coord());
  204. long dist = Distance(coord, click);
  205. if (!close || dist < distance) {
  206. close = (TechnoClass *)object;
  207. distance = dist;
  208. }
  209. }
  210. object = object->Next;
  211. }
  212. }
  213. return(close);
  214. }
  215. /***************************************************************************
  216. * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell *
  217. * *
  218. * INPUT: RTTIType the RTTI type we are searching for *
  219. * *
  220. * OUTPUT: none *
  221. * *
  222. * WARNINGS: none *
  223. * *
  224. * HISTORY: *
  225. * 03/17/1995 PWG : Created. *
  226. * 06/12/1995 JLB : Returns object class pointer. *
  227. *=========================================================================*/
  228. ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const
  229. {
  230. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  231. assert(rtti != RTTI_NONE);
  232. ObjectClass * object = Cell_Occupier();
  233. while (object != NULL && object->IsActive) {
  234. if (object->What_Am_I() == rtti) {
  235. return(object);
  236. }
  237. object = object->Next;
  238. }
  239. return(NULL);
  240. }
  241. /***********************************************************************************************
  242. * CellClass::Cell_Building -- Return with building at specified cell. *
  243. * *
  244. * Given a cell, determine if there is a building associated *
  245. * and return with a pointer to this building. *
  246. * *
  247. * INPUT: none *
  248. * *
  249. * OUTPUT: Returns with a pointer to the building associated with the *
  250. * cell. If there is no building associated, then NULL is *
  251. * returned. *
  252. * *
  253. * WARNINGS: none *
  254. * *
  255. * HISTORY: *
  256. * 08/05/1992 JLB : Created. *
  257. * 04/30/1994 JLB : Converted to member function. *
  258. *=============================================================================================*/
  259. BuildingClass * CellClass::Cell_Building(void) const
  260. {
  261. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  262. return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING));
  263. }
  264. /***********************************************************************************************
  265. * CellClass::Cell_Terrain -- Determines terrain object in cell. *
  266. * *
  267. * This routine is used to determine the terrain object (if any) that *
  268. * overlaps this cell. *
  269. * *
  270. * INPUT: none *
  271. * *
  272. * OUTPUT: Returns with a pointer to the terrain object that overlaps *
  273. * this cell. If there is no terrain object present, then NULL *
  274. * is returned. *
  275. * *
  276. * WARNINGS: none *
  277. * *
  278. * HISTORY: *
  279. * 05/18/1994 JLB : Created. *
  280. *=============================================================================================*/
  281. TerrainClass * CellClass::Cell_Terrain(void) const
  282. {
  283. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  284. return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN));
  285. }
  286. /***********************************************************************************************
  287. * CellClass::Cell_Object -- Returns with clickable object in cell. *
  288. * *
  289. * This routine is used to determine which object is to be selected *
  290. * by a player click upon the cell. Not all objects that overlap the *
  291. * cell are selectable by the player. This routine sorts out which *
  292. * is which and returns with the appropriate object pointer. *
  293. * *
  294. * INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when *
  295. * selecting the object within the cell. This plays a role in those cases *
  296. * where several objects (such as infantry) exist within the same cell. *
  297. * *
  298. * OUTPUT: Returns with pointer to the object clickable within the *
  299. * cell. NULL is returned if there is no clickable object *
  300. * present. *
  301. * *
  302. * WARNINGS: none *
  303. * *
  304. * HISTORY: *
  305. * 05/13/1994 JLB : Created. *
  306. *=============================================================================================*/
  307. ObjectClass * CellClass::Cell_Object(int x, int y) const
  308. {
  309. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  310. ObjectClass * ptr;
  311. /*
  312. ** Hack so that aircraft landed on helipads can still be selected if directly
  313. ** clicked on.
  314. */
  315. ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT);
  316. if (ptr) {
  317. return(ptr);
  318. }
  319. ptr = Cell_Techno(x, y);
  320. if (ptr) {
  321. return(ptr);
  322. }
  323. ptr = Cell_Terrain();
  324. if (ptr) return(ptr);
  325. return(ptr);
  326. }
  327. /***********************************************************************************************
  328. * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. *
  329. * *
  330. * This is a low level routine that marks all objects that overlap this *
  331. * cell to be redrawn. It is necessary to call this routine whenever *
  332. * the underlying icon has to be redrawn. *
  333. * *
  334. * INPUT: forced -- Should this redraw be forced even if flags *
  335. * indicate that it would be redundant? *
  336. * *
  337. * OUTPUT: none *
  338. * *
  339. * WARNINGS: none *
  340. * *
  341. * HISTORY: *
  342. * 05/18/1994 JLB : Created. *
  343. * 06/20/1994 JLB : Simplified to use object pointers. *
  344. * 12/24/1994 JLB : Only checks if cell is in view and not flagged already. *
  345. *=============================================================================================*/
  346. void CellClass::Redraw_Objects(bool forced)
  347. {
  348. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  349. CELL cell = Cell_Number();
  350. if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) {
  351. /*
  352. ** Flag the icon to be redrawn.
  353. */
  354. Map.Flag_Cell(cell);
  355. /*
  356. ** Flag the main object in the cell to be redrawn.
  357. */
  358. if (Cell_Occupier() != NULL) {
  359. ObjectClass * optr = Cell_Occupier();
  360. while (optr != NULL && optr->IsActive) {
  361. #ifdef SORTDRAW
  362. if (optr->Is_Techno() && ((TechnoClass *)optr)->Visual_Character() != VISUAL_NORMAL) {
  363. optr->Mark(MARK_CHANGE);
  364. }
  365. #else
  366. optr->Mark(MARK_CHANGE);
  367. #endif
  368. if (optr->Next != NULL && !optr->Next->IsActive) {
  369. optr->Next = NULL;
  370. }
  371. optr = optr->Next;
  372. }
  373. }
  374. #ifdef SORTDRAW
  375. /*
  376. ** Flag any overlapping object in this cell to be redrawn.
  377. */
  378. for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
  379. if (Overlapper[index]) {
  380. assert(Overlapper[index]->IsActive);
  381. if (Overlapper[index]->Is_Techno() && ((TechnoClass *)Overlapper[index])->Visual_Character() != VISUAL_NORMAL) {
  382. Overlapper[index]->Mark(MARK_CHANGE);
  383. }
  384. }
  385. }
  386. #else
  387. /*
  388. ** Flag any overlapping object in this cell to be redrawn.
  389. */
  390. for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
  391. if (Overlapper[index] != NULL) {
  392. if (!Overlapper[index]->IsActive) {
  393. Overlapper[index] = NULL;
  394. } else {
  395. Overlapper[index]->Mark(MARK_CHANGE);
  396. }
  397. }
  398. }
  399. #endif
  400. }
  401. }
  402. /***********************************************************************************************
  403. * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. *
  404. * *
  405. * This determines if the cell can become a proper foundation for *
  406. * building placement. *
  407. * *
  408. * INPUT: loco -- The locomotion of the object trying to consider if this cell is *
  409. * generally clear. Buildings use the value of SPEED_NONE. *
  410. * *
  411. * OUTPUT: bool; Is this cell generally clear (usually for building purposes)? *
  412. * *
  413. * WARNINGS: none *
  414. * *
  415. * HISTORY: *
  416. * 05/18/1994 JLB : Created. *
  417. * 06/25/1996 JLB : Handles different locomotion types. *
  418. * 10/05/1996 JLB : Checks for crushable walls and crushable object. *
  419. *=============================================================================================*/
  420. bool CellClass::Is_Clear_To_Build(SpeedType loco) const
  421. {
  422. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  423. /*
  424. ** During scenario initialization, passability is always guaranteed.
  425. */
  426. if (ScenarioInit) return(true);
  427. /*
  428. ** If there is an object there, then don't allow building.
  429. */
  430. if (Cell_Object() != NULL) {
  431. return(false);
  432. }
  433. /*
  434. ** Prevents a building from being placed over a flag object.
  435. */
  436. #ifdef FIXIT_FLAG_CHECK
  437. if (IsFlagged) {
  438. return(false);
  439. }
  440. #endif
  441. /*
  442. ** Walls are always considered to block the terrain for general passability
  443. ** purposes. In normal game mode, all overlays are not buildable.
  444. */
  445. if (Overlay != OVERLAY_NONE && (Overlay == OVERLAY_FLAG_SPOT || !Debug_Map || OverlayTypeClass::As_Reference(Overlay).IsWall)) {
  446. return(false);
  447. }
  448. /*
  449. ** Building over a bib is not allowed.
  450. */
  451. if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib /* && Owner != HOUSE_NONE*/) {
  452. return(false);
  453. }
  454. /*
  455. ** Building on certain kinds of terrain is prohibited -- bridges in particular.
  456. ** If the locomotion type is SPEED_NONE, then this check is presumed to be
  457. ** for the purposes of building.
  458. */
  459. if (loco == SPEED_NONE) {
  460. if (Is_Bridge_Here()) {
  461. return(false);
  462. }
  463. return(::Ground[Land_Type()].Build);
  464. } else {
  465. if (::Ground[Land_Type()].Cost[loco] == fixed(0)) {
  466. // if (::Ground[Land_Type()].Cost[SPEED_TRACK] == fixed(0)) {
  467. return(false);
  468. }
  469. return(true);
  470. }
  471. }
  472. /***********************************************************************************************
  473. * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. *
  474. * *
  475. * This routine recalculates the ground type in the cell. The speeds the find path *
  476. * algorithm and other determinations of the cell type. *
  477. * *
  478. * INPUT: none *
  479. * *
  480. * OUTPUT: none *
  481. * *
  482. * WARNINGS: none *
  483. * *
  484. * HISTORY: *
  485. * 05/29/1994 JLB : Created. *
  486. * 06/20/1994 JLB : Knows about template pointer in cell object. *
  487. *=============================================================================================*/
  488. void CellClass::Recalc_Attributes(void)
  489. {
  490. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  491. /*
  492. ** Special override for interior terrain set so that a non-template or a clear template
  493. ** is equivalent to impassable rock.
  494. */
  495. if (LastTheater == THEATER_INTERIOR) {
  496. if (TType == TEMPLATE_NONE || TType == TEMPLATE_CLEAR1) {
  497. Land = LAND_ROCK;
  498. return;
  499. }
  500. }
  501. /*
  502. ** Check for wall effects.
  503. */
  504. if (Overlay != OVERLAY_NONE) {
  505. Land = OverlayTypeClass::As_Reference(Overlay).Land;
  506. if (Land != LAND_CLEAR) return;
  507. }
  508. /*
  509. ** If there is a template associated with this cell, then fetch the
  510. ** land type given the template type and icon number.
  511. */
  512. if (TType != TEMPLATE_NONE && TType != 255) {
  513. TemplateTypeClass const * ttype = &TemplateTypeClass::As_Reference(TType);
  514. Land = ttype->Land_Type(TIcon);
  515. return;
  516. }
  517. /*
  518. ** No template is the same as clear terrain.
  519. */
  520. Land = LAND_CLEAR;
  521. }
  522. /***********************************************************************************************
  523. * CellClass::Occupy_Down -- Flag occupation of specified cell. *
  524. * *
  525. * This routine is used to mark the cell as being occupied by the specified object. *
  526. * *
  527. * INPUT: object -- The object that is to occupy the cell *
  528. * *
  529. * OUTPUT: none *
  530. * *
  531. * WARNINGS: none *
  532. * *
  533. * HISTORY: *
  534. * 07/18/1994 JLB : Created. *
  535. * 11/29/1994 JLB : Simplified. *
  536. *=============================================================================================*/
  537. void CellClass::Occupy_Down(ObjectClass * object)
  538. {
  539. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  540. assert(object != NULL && object->IsActive);
  541. ObjectClass * optr;
  542. if (object == NULL) return;
  543. /*
  544. ** Always add buildings to the end of the occupation chain. This is necessary because
  545. ** the occupation chain is a single list even though buildings occupy more than one
  546. ** cell. If more than one building is allowed to occupy the same cell, then this chain
  547. ** logic will fail.
  548. */
  549. if (object->What_Am_I() == RTTI_BUILDING && Cell_Occupier()) {
  550. optr = Cell_Occupier();
  551. while (optr->Next != NULL) {
  552. assert(optr != object);
  553. assert(optr->What_Am_I() != RTTI_BUILDING);
  554. optr = optr->Next;
  555. }
  556. optr->Next = object;
  557. object->Next = 0;
  558. } else {
  559. object->Next = Cell_Occupier();
  560. OccupierPtr = object;
  561. }
  562. Map.Radar_Pixel(Cell_Number());
  563. /*
  564. ** If being placed down on a visible square, then flag this
  565. ** techno object as being revealed to the player.
  566. */
  567. // Changes for client/server multiplayer. ST - 8/2/2019 2:51PM
  568. //if (IsMapped || Session.Type != GAME_NORMAL) {
  569. // object->Revealed(PlayerPtr);
  570. //}
  571. if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
  572. if (IsMapped || Session.Type != GAME_NORMAL) {
  573. object->Revealed(PlayerPtr);
  574. }
  575. } else {
  576. for (int i = 0; i < Session.Players.Count(); i++) {
  577. HousesType house_type = Session.Players[i]->Player.ID;
  578. if (Is_Visible(house_type)) {
  579. HouseClass *house = HouseClass::As_Pointer(house_type);
  580. object->Revealed(house);
  581. }
  582. }
  583. }
  584. /*
  585. ** Special occupy bit set.
  586. */
  587. switch (object->What_Am_I()) {
  588. case RTTI_BUILDING:
  589. Flag.Occupy.Building = true;
  590. break;
  591. case RTTI_VESSEL:
  592. case RTTI_AIRCRAFT:
  593. case RTTI_UNIT:
  594. Flag.Occupy.Vehicle = true;
  595. break;
  596. case RTTI_TERRAIN:
  597. Flag.Occupy.Monolith = true;
  598. break;
  599. default:
  600. break;
  601. }
  602. }
  603. /***********************************************************************************************
  604. * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. *
  605. * *
  606. * This routine will lift the object from the cell and free the cell to be occupied by *
  607. * another object. Only if the cell was previously marked with the object specified, will *
  608. * the object be lifted off. This routine is the counterpart to Occupy_Down(). *
  609. * *
  610. * INPUT: object -- The object that is being lifted off. *
  611. * *
  612. * OUTPUT: none *
  613. * *
  614. * WARNINGS: none *
  615. * *
  616. * HISTORY: *
  617. * 07/18/1994 JLB : Created. *
  618. * 11/29/1994 JLB : Fixed to handle next pointer in previous object. *
  619. *=============================================================================================*/
  620. void CellClass::Occupy_Up(ObjectClass * object)
  621. {
  622. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  623. assert(object != NULL && object->IsActive);
  624. if (object == NULL) return;
  625. ObjectClass * optr = Cell_Occupier(); // Working pointer to the objects in the chain.
  626. if (optr == object) {
  627. OccupierPtr = object->Next;
  628. object->Next = 0;
  629. } else {
  630. bool found = false;
  631. while (optr != NULL) {
  632. if (optr->Next == object) {
  633. optr->Next = object->Next;
  634. object->Next = 0;
  635. found = true;
  636. break;
  637. }
  638. optr = optr->Next;
  639. }
  640. // assert(found);
  641. }
  642. Map.Radar_Pixel(Cell_Number());
  643. /*
  644. ** Special occupy bit clear.
  645. */
  646. switch (object->What_Am_I()) {
  647. case RTTI_BUILDING:
  648. Flag.Occupy.Building = false;
  649. break;
  650. case RTTI_VESSEL:
  651. case RTTI_AIRCRAFT:
  652. case RTTI_UNIT:
  653. Flag.Occupy.Vehicle = false;
  654. break;
  655. case RTTI_TERRAIN:
  656. Flag.Occupy.Monolith = false;
  657. break;
  658. default:
  659. break;
  660. }
  661. }
  662. /***********************************************************************************************
  663. * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla*
  664. * *
  665. * Most game objects can often have their graphic imagery spill into more than one cell *
  666. * even though they are considered to "occupy" only one cell. All cells overlapped are *
  667. * flagged by this routine. Using this information it is possible to keep the tactical map *
  668. * display correct. *
  669. * *
  670. * INPUT: object -- The object to mark as overlapping this cell. *
  671. * *
  672. * OUTPUT: none *
  673. * *
  674. * WARNINGS: none *
  675. * *
  676. * HISTORY: *
  677. * 07/18/1994 JLB : Created. *
  678. * 07/04/1995 JLB : Ensures that buildings are always marked down. *
  679. *=============================================================================================*/
  680. void CellClass::Overlap_Down(ObjectClass * object)
  681. {
  682. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  683. assert(object != NULL && object->IsActive);
  684. ObjectClass ** ptr = 0;
  685. if (!object) return;
  686. int index;
  687. for (index = 0; index < ARRAY_SIZE(Overlapper); index++) {
  688. if (Overlapper[index] == object) return;
  689. if (!Overlapper[index]) ptr = &Overlapper[index];
  690. }
  691. /*
  692. ** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody
  693. ** else out in this case.
  694. */
  695. if (!ptr && object->What_Am_I() == RTTI_BUILDING) {
  696. for (index = 0; index < ARRAY_SIZE(Overlapper); index++) {
  697. switch (Overlapper[index]->What_Am_I()) {
  698. case RTTI_BUILDING:
  699. case RTTI_TERRAIN:
  700. break;
  701. default:
  702. Overlapper[index] = object;
  703. index = ARRAY_SIZE(Overlapper);
  704. break;
  705. }
  706. }
  707. }
  708. if (ptr) *ptr = object;
  709. /*
  710. ** If being placed down on a visible square, then flag this
  711. ** techno object as being revealed to the player.
  712. */
  713. if (IsMapped) {
  714. object->Revealed(PlayerPtr);
  715. }
  716. }
  717. /***********************************************************************************************
  718. * CellClass::Overlap_Up -- Removes overlap flag for the cell. *
  719. * *
  720. * This is the counterpart to Overlap_Down and is used to remove the overlap flag for the *
  721. * specified unit on the cell. *
  722. * *
  723. * INPUT: object -- The object to remove the overlap flag for. *
  724. * *
  725. * OUTPUT: none *
  726. * *
  727. * WARNINGS: none *
  728. * *
  729. * HISTORY: *
  730. * 07/18/1994 JLB : Created. *
  731. *=============================================================================================*/
  732. void CellClass::Overlap_Up(ObjectClass * object)
  733. {
  734. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  735. assert(object != NULL && object->IsActive);
  736. for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
  737. if (Overlapper[index] == object) {
  738. Overlapper[index] = 0;
  739. break;
  740. }
  741. }
  742. }
  743. /***********************************************************************************************
  744. * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. *
  745. * *
  746. * This routine will determine if a unit is occupying the cell and if so, return a pointer *
  747. * to it. If there is no unit occupying the cell, then NULL is returned. *
  748. * *
  749. * INPUT: none *
  750. * *
  751. * OUTPUT: Returns with pointer to unit occupying cell, else NULL. *
  752. * *
  753. * WARNINGS: none *
  754. * *
  755. * HISTORY: *
  756. * 07/18/1994 JLB : Created. *
  757. *=============================================================================================*/
  758. UnitClass * CellClass::Cell_Unit(void) const
  759. {
  760. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  761. return((UnitClass*)Cell_Find_Object(RTTI_UNIT));
  762. }
  763. /***********************************************************************************************
  764. * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. *
  765. * *
  766. * Call this routine to query and return a pointer to a vessel located in the cell. If *
  767. * there is no vessel present, then this routine will return NULL. *
  768. * *
  769. * INPUT: none *
  770. * *
  771. * OUTPUT: Returns with a pointer to the vessel class object if one is present. *
  772. * *
  773. * WARNINGS: none *
  774. * *
  775. * HISTORY: *
  776. * 05/20/1996 JLB : Created. *
  777. *=============================================================================================*/
  778. VesselClass * CellClass::Cell_Vessel(void) const
  779. {
  780. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  781. return((VesselClass*)Cell_Find_Object(RTTI_VESSEL));
  782. }
  783. /***********************************************************************************************
  784. * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. *
  785. * *
  786. * This routine examines the cell and returns a pointer to the first infantry unit *
  787. * that occupies it. If there is no infantry unit in the cell, then NULL is returned. *
  788. * *
  789. * INPUT: none *
  790. * *
  791. * OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are *
  792. * present. *
  793. * *
  794. * WARNINGS: none *
  795. * *
  796. * HISTORY: *
  797. * 12/21/1994 JLB : Created. *
  798. *=============================================================================================*/
  799. InfantryClass * CellClass::Cell_Infantry(void) const
  800. {
  801. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  802. return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY));
  803. }
  804. #ifdef SORTDRAW
  805. static bool _Calc_Partial_Window(int cellx, int celly, int & drawx, int & drawy)
  806. {
  807. int & px = WindowList[WINDOW_PARTIAL][WINDOWX];
  808. int & py = WindowList[WINDOW_PARTIAL][WINDOWY];
  809. int & pw = WindowList[WINDOW_PARTIAL][WINDOWWIDTH];
  810. int & ph = WindowList[WINDOW_PARTIAL][WINDOWHEIGHT];
  811. int & tx = WindowList[WINDOW_TACTICAL][WINDOWX];
  812. int & ty = WindowList[WINDOW_TACTICAL][WINDOWY];
  813. int & tw = WindowList[WINDOW_TACTICAL][WINDOWWIDTH];
  814. int & th = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT];
  815. px = cellx + tx;
  816. py = celly + ty;
  817. pw = CELL_PIXEL_W;
  818. ph = CELL_PIXEL_H;
  819. if (px < tx) {
  820. pw -= tx - px;
  821. px = tx;
  822. }
  823. if (pw < 1) return(false);
  824. if (py < ty) {
  825. ph -= ty - py;
  826. py = ty;
  827. }
  828. if (ph < 1) return(false);
  829. if (px + pw > tx + tw) {
  830. pw -= (px + pw) - (tx + tw);
  831. }
  832. if (pw < 1) return(false);
  833. if (py + ph > ty + th) {
  834. ph -= (py + ph) - (ty + th);
  835. }
  836. if (ph < 1) return(false);
  837. drawx = drawx - (px-tx);
  838. drawy = drawy - (py-ty);
  839. return(true);
  840. }
  841. static int _ocompare(const void * left, const void * right)
  842. {
  843. COORDINATE lcoord = (*((ObjectClass **)left))->Sort_Y();
  844. COORDINATE rcoord = (*((ObjectClass **)right))->Sort_Y();
  845. if (lcoord < rcoord) return(-1);
  846. if (lcoord > rcoord) return(1);
  847. return(0);
  848. }
  849. #endif
  850. /***********************************************************************************************
  851. * CellClass::Get_Template_Info -- Get some info about a template for external use *
  852. * *
  853. * *
  854. * *
  855. * *
  856. * INPUT: Ref to info required *
  857. * *
  858. * OUTPUT: True if image info available *
  859. * *
  860. * *
  861. * WARNINGS: none *
  862. * *
  863. * HISTORY: *
  864. * 1/10/2019 5:57PM ST : Created. *
  865. *=============================================================================================*/
  866. bool CellClass::Get_Template_Info(char *template_name, int &icon, void *&image_data)
  867. {
  868. TemplateTypeClass const *ttype = NULL;
  869. if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { // Not sure why it's checking for 255 here since that's a valid tile type. ST - 6/4/2019
  870. ttype = &TemplateTypeClass::As_Reference(TType);
  871. icon = TIcon;
  872. }
  873. else {
  874. ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1);
  875. icon = Clear_Icon();
  876. }
  877. if (ttype) {
  878. strcpy(template_name, ttype->IniName);
  879. image_data = (void*)ttype->ImageData;
  880. return true;
  881. }
  882. return false;
  883. }
  884. /***********************************************************************************************
  885. * CellClass::Draw_It -- Draws the cell imagery at the location specified. *
  886. * *
  887. * This is the gruntwork cell rendering code. It draws the cell at the screen location *
  888. * specified. This routine doesn't draw any overlapping or occupying units. It only *
  889. * deals with the ground (cell) layer -- icon level. *
  890. * *
  891. * INPUT: x,y -- The screen coordinates to render the cell imagery at. *
  892. * *
  893. * OUTPUT: none *
  894. * *
  895. * WARNINGS: none *
  896. * *
  897. * HISTORY: *
  898. * 07/18/1994 JLB : Created. *
  899. * 08/21/1994 JLB : Revised for simple template objects. *
  900. * 11/01/1994 BRR : Updated placement cursor; draws actual object *
  901. * 11/14/1994 BRR : Added remapping code to show passable areas *
  902. * 12/02/1994 BRR : Added trigger display *
  903. * 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. *
  904. * 04/25/1995 JLB : Smudges drawn BELOW overlays. *
  905. * 07/22/1996 JLB : Objects added to draw process. *
  906. *=============================================================================================*/
  907. void CellClass::Draw_It(int x, int y, bool objects) const
  908. {
  909. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  910. if (!objects) {
  911. BStart(BENCH_CELL);
  912. TemplateTypeClass const * ttype = 0;
  913. int icon; // The icon number to use from the template set.
  914. CELL cell = Cell_Number();
  915. void * remap = NULL;
  916. #ifdef SCENARIO_EDITOR
  917. TemplateTypeClass * tptr;
  918. // TriggerClass * trig;
  919. int i;
  920. char waypt[3];
  921. #endif
  922. CellCount++;
  923. /*
  924. ** Fetch a pointer to the template type associated with this cell.
  925. */
  926. if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) {
  927. ttype = &TemplateTypeClass::As_Reference(TType);
  928. icon = TIcon;
  929. } else {
  930. ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1);
  931. icon = Clear_Icon();
  932. }
  933. #ifdef CHEAT_KEYS
  934. /*
  935. ** Draw the stamp of the template.
  936. */
  937. if (Debug_Icon) {
  938. LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254));
  939. FontXSpacing -= 2;
  940. Fancy_Text_Print("%02X%02X\r%d%d%d\r%d %d", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, &GreyScheme, TBLACK, TPF_EFNT|TPF_CENTER|TPF_BRIGHT_COLOR|TPF_FULLSHADOW,
  941. Cell_Y(cell), Cell_X(cell),
  942. //(CurrentObject.Count() && CurrentObject[0]->Is_Techno()) ? ((TechnoClass *)CurrentObject[0])->House->Which_Zone(cell) : -1,
  943. Zones[MZONE_NORMAL],Zones[MZONE_CRUSHER],Zones[MZONE_DESTROYER],
  944. Overlay, OverlayData
  945. );
  946. FontXSpacing += 2;
  947. } else {
  948. #endif
  949. #ifdef SCENARIO_EDITOR
  950. /*
  951. ** Set up the remap table for this icon.
  952. */
  953. if (Debug_Map && Debug_Passable) {
  954. if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL &&
  955. Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable
  956. remap = DisplayClass::FadingRed;
  957. } else {
  958. if (::Ground[Land].Cost[0] > fixed(1, 3)) { // pretty passable
  959. remap = DisplayClass::FadingGreen;
  960. } else {
  961. remap = DisplayClass::FadingYellow; // moderately passable
  962. }
  963. }
  964. }
  965. #endif
  966. /*
  967. ** This is the underlying terrain icon.
  968. */
  969. if (ttype->Get_Image_Data()) {
  970. LogicPage->Draw_Stamp(ttype->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL);
  971. if (remap) {
  972. LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap);
  973. }
  974. }
  975. #ifdef SCENARIO_EDITOR
  976. /*
  977. ** Draw the map editor's "current" cell. This is the cell that can be
  978. ** assigned attributes such as tag labels.
  979. ** This must be draw before the placement cursor, but after drawing the
  980. ** objects in the cell.
  981. */
  982. if (Debug_Map && CurrentCell == Cell_Number()) {
  983. LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW);
  984. }
  985. #endif
  986. /*
  987. ** Redraw any smudge.
  988. */
  989. if (Smudge != SMUDGE_NONE) {
  990. SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData);
  991. }
  992. /*
  993. ** Draw the overlay object.
  994. */
  995. if (Overlay != OVERLAY_NONE) {
  996. OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay);
  997. IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific
  998. CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow);
  999. IsTheaterShape = false;
  1000. }
  1001. #ifdef SCENARIO_EDITOR
  1002. if (Debug_Map) {
  1003. /*
  1004. ** Draw the cell's Trigger mnemonic, if it has a trigger
  1005. */
  1006. if (Trigger.Is_Valid()) {
  1007. Fancy_Text_Print(Trigger->Class->IniName, x+Map.TacPixelX, y+Map.TacPixelY, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
  1008. }
  1009. /*
  1010. ** Draw the cell's Waypoint designation if there is one.
  1011. */
  1012. if (IsWaypoint) {
  1013. for (i = 0; i < WAYPT_HOME; i++) {
  1014. if (Scen.Waypoint[i] == Cell_Number()) {
  1015. if (i < 26) {
  1016. waypt[0] = 'A' + i;
  1017. waypt[1] = 0;
  1018. } else {
  1019. waypt[0] = 'A' + (i/26)-1;
  1020. waypt[1] = 'A' + (i % 26);
  1021. waypt[2] = 0;
  1022. }
  1023. Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2,
  1024. Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3,
  1025. &ColorRemaps[PCOLOR_RED], TBLACK,
  1026. TPF_EFNT | TPF_CENTER|TPF_FULLSHADOW);
  1027. break;
  1028. }
  1029. }
  1030. if (Scen.Waypoint[WAYPT_HOME] == Cell_Number()) {
  1031. Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7,
  1032. &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
  1033. }
  1034. if (Scen.Waypoint[WAYPT_REINF] == Cell_Number()) {
  1035. Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7,
  1036. &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
  1037. }
  1038. }
  1039. }
  1040. #endif
  1041. /*
  1042. ** Draw the placement cursor:
  1043. ** - First, draw the hash-mark cursor, so it will appear underneath
  1044. ** any cursor being drawn
  1045. ** - If the PendingObject is a template, overlay, or smudge, draw it
  1046. ** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it
  1047. */
  1048. if (IsCursorHere) {
  1049. SpeedType loco = SPEED_NONE;
  1050. if (Map.PendingObjectPtr) {
  1051. if (Map.PendingObjectPtr->What_Am_I() == RTTI_BUILDING) {
  1052. BuildingClass * obj = (BuildingClass *)(Map.PendingObjectPtr);
  1053. loco = obj->Class->Speed;
  1054. // if (*obj == STRUCT_SUB_PEN || *obj == STRUCT_SHIP_YARD ||
  1055. // *obj == STRUCT_FAKE_PEN || *obj == STRUCT_FAKE_YARD) loco = SPEED_FLOAT;
  1056. }
  1057. }
  1058. /*
  1059. ** Draw the hash-mark cursor:
  1060. */
  1061. if (Map.ProximityCheck && Is_Clear_To_Build(loco)) {
  1062. LogicPage->Draw_Stamp(DisplayClass::TransIconset, 0, x, y, NULL, WINDOW_TACTICAL);
  1063. } else {
  1064. LogicPage->Draw_Stamp(DisplayClass::TransIconset, 2, x, y, NULL, WINDOW_TACTICAL);
  1065. }
  1066. #ifdef SCENARIO_EDITOR
  1067. if (Debug_Map && Map.PendingObject) {
  1068. switch (Map.PendingObject->What_Am_I()) {
  1069. /*
  1070. ** Draw a template:
  1071. ** - Compute the icon offset of this cell for this template, using
  1072. ** ZoneCell+ZoneOffset to get the upper-left corner of the placement
  1073. ** cursor
  1074. ** - Draw the icon
  1075. */
  1076. case RTTI_TEMPLATETYPE:
  1077. tptr = (TemplateTypeClass *)Map.PendingObject;
  1078. if (tptr->Get_Image_Data()) {
  1079. icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) +
  1080. (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) *
  1081. tptr->Width;
  1082. LogicPage->Draw_Stamp(tptr->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL);
  1083. }
  1084. break;
  1085. /*
  1086. ** Draw an overlay; just use the existing 'OverlayData' even though
  1087. ** it means nothing.
  1088. */
  1089. case RTTI_OVERLAYTYPE:
  1090. OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData);
  1091. break;
  1092. /*
  1093. ** Draw a smudge
  1094. */
  1095. case RTTI_SMUDGETYPE:
  1096. SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0);
  1097. break;
  1098. default:
  1099. break;
  1100. }
  1101. }
  1102. #endif
  1103. }
  1104. #ifdef CHEAT_KEYS
  1105. }
  1106. #endif
  1107. BEnd(BENCH_CELL);
  1108. }
  1109. #ifdef SORTDRAW
  1110. if (objects) {
  1111. BStart(BENCH_OBJECTS);
  1112. /*
  1113. ** Build a list of objects to draw into a working buffer. There is a
  1114. ** big presumption here -- it is presumed that if the cell is to be
  1115. ** redrawn, then all objects in the cell should properly be flagged to
  1116. ** be redrawn as well. Normally, this isn't a problem, but for subs
  1117. ** the IsToDisplay flag MUST REMAIN SET. This is because there is a
  1118. ** hack overpass after the cells are redrawn so that subs can be
  1119. ** redrawn separately.
  1120. */
  1121. static DynamicVectorClass<ObjectClass*> optr(20 + ARRAY_SIZE(Overlapper));
  1122. optr.Delete_All();
  1123. ObjectClass * object = Cell_Occupier();
  1124. while (object != NULL) {
  1125. if (!object->IsActive) break;
  1126. optr.Add(object);
  1127. object->IsToDisplay = true;
  1128. object = object->Next;
  1129. }
  1130. for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
  1131. object = Overlapper[index];
  1132. if (object != NULL && object->IsActive) {
  1133. object->IsToDisplay = true;
  1134. optr.Add(object);
  1135. }
  1136. }
  1137. /*
  1138. ** Sort the object list so that objects will be drawn from
  1139. ** back to front.
  1140. */
  1141. switch (optr.Count()) {
  1142. /*
  1143. ** If there are zero or one object, then sorting is
  1144. ** unnecessary.
  1145. */
  1146. case 0:
  1147. case 1:
  1148. break;
  1149. /*
  1150. ** Two objects can be sorted with a single compare and swap.
  1151. */
  1152. case 2:
  1153. if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) {
  1154. swap(optr[0], optr[1]);
  1155. }
  1156. break;
  1157. /*
  1158. ** Three objects can be sorted with three compares and swaps.
  1159. */
  1160. case 3:
  1161. if (optr[0]->Sort_Y() > optr[2]->Sort_Y()) {
  1162. swap(optr[0], optr[2]);
  1163. }
  1164. if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) {
  1165. swap(optr[0], optr[1]);
  1166. }
  1167. if (optr[1]->Sort_Y() > optr[2]->Sort_Y()) {
  1168. swap(optr[1], optr[2]);
  1169. }
  1170. break;
  1171. /*
  1172. ** Large number of objects can be effeciently sorted by using
  1173. ** a quicksort.
  1174. */
  1175. default:
  1176. qsort(&optr[0], optr.Count(), sizeof(ObjectClass*), _ocompare);
  1177. break;
  1178. }
  1179. /*
  1180. ** Draw any objects that happen to be in or overlapping this cell.
  1181. */
  1182. for (int index = 0; index < optr.Count(); index++) {
  1183. object = optr[index];
  1184. int xx,yy;
  1185. if (object->IsToDisplay && (!object->Is_Techno() || ((TechnoClass *)object)->Visual_Character() == VISUAL_NORMAL) && Map.Coord_To_Pixel(object->Render_Coord(), xx, yy)) {
  1186. if (_Calc_Partial_Window(x, y, xx, yy)) {
  1187. object->Draw_It(xx, yy, WINDOW_PARTIAL);
  1188. if (Debug_Map) {
  1189. object->IsToDisplay = true;
  1190. } else {
  1191. object->IsToDisplay = false;
  1192. }
  1193. }
  1194. object->IsToDisplay = false;
  1195. }
  1196. }
  1197. BEnd(BENCH_OBJECTS);
  1198. }
  1199. #endif
  1200. }
  1201. /***********************************************************************************************
  1202. * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. *
  1203. * *
  1204. * This routine examines the cells around the current one and from this, determines what *
  1205. * concrete icon shape to use (if any). The cell data is adjusted and the cell is marked *
  1206. * for redraw if the icon changed. *
  1207. * *
  1208. * INPUT: none *
  1209. * *
  1210. * OUTPUT: none *
  1211. * *
  1212. * WARNINGS: none *
  1213. * *
  1214. * HISTORY: *
  1215. * 08/01/1994 JLB : Created. *
  1216. *=============================================================================================*/
  1217. void CellClass::Concrete_Calc(void)
  1218. {
  1219. #ifdef OBSOLETE
  1220. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1221. static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW};
  1222. static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S};
  1223. FacingType * ptr; // Working pointer into adjacent cell list.
  1224. int index; // Constructed bit index.
  1225. int icon; // Icon number.
  1226. bool isodd; // Is this for the odd column?
  1227. #define OF_N 0x01
  1228. #define OF_NE 0x02
  1229. #define OF_E 0x04
  1230. #define OF_SE 0x08
  1231. #define OF_S 0x10
  1232. #define EF_N 0x01
  1233. #define EF_NW 0x10
  1234. #define EF_W 0x08
  1235. #define EF_SW 0x04
  1236. #define EF_S 0x02
  1237. /*
  1238. ** Determine if the even or odd row logic is necessary.
  1239. */
  1240. isodd = ((Cell_Number() & 0x01) != 0);
  1241. /*
  1242. ** Fetch correct pointer depending on whether this is for an
  1243. ** odd or even row.
  1244. */
  1245. ptr = (isodd) ? _odd : _even;
  1246. /*
  1247. ** Build an index according to the presence of concrete in the special
  1248. ** adjacent cells. This is a short list of adjacent cell flags since
  1249. ** only 5 adjacent cells need to be examined. The choice of which 5
  1250. ** depends on whether this is for an even or odd column.
  1251. */
  1252. index = 0;
  1253. for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) {
  1254. CellClass & cellptr = Adjacent_Cell(*ptr++);
  1255. if (cellptr.Overlay == OVERLAY_CONCRETE) {
  1256. index |= (1<<i);
  1257. }
  1258. }
  1259. /*
  1260. ** Special logic occurs for cells that are concrete filled.
  1261. */
  1262. if (Overlay == OVERLAY_CONCRETE) {
  1263. /*
  1264. ** Process the index value and place the appropriate concrete icon
  1265. ** in the cell.
  1266. */
  1267. if (isodd) {
  1268. switch (index) {
  1269. case OF_NE:
  1270. case OF_N|OF_NE:
  1271. case OF_E|OF_N:
  1272. case OF_E|OF_NE:
  1273. case OF_N|OF_NE|OF_E:
  1274. case OF_S|OF_N|OF_NE:
  1275. icon = C_RIGHT_UP; // right - up
  1276. break;
  1277. case OF_SE:
  1278. case OF_E|OF_SE:
  1279. case OF_S|OF_SE:
  1280. case OF_S|OF_E:
  1281. case OF_S|OF_SE|OF_E:
  1282. case OF_S|OF_SE|OF_N:
  1283. icon = C_RIGHT_DOWN; // right - down
  1284. break;
  1285. case OF_SE|OF_NE:
  1286. case OF_SE|OF_NE|OF_N:
  1287. case OF_SE|OF_NE|OF_S:
  1288. case OF_SE|OF_NE|OF_S|OF_N:
  1289. case OF_SE|OF_E|OF_N:
  1290. case OF_SE|OF_E|OF_NE|OF_N:
  1291. case OF_S|OF_E|OF_N:
  1292. case OF_S|OF_E|OF_NE:
  1293. case OF_S|OF_E|OF_NE|OF_N:
  1294. case OF_S|OF_SE|OF_E|OF_N:
  1295. case OF_S|OF_SE|OF_E|OF_NE|OF_N:
  1296. case OF_S|OF_SE|OF_E|OF_NE:
  1297. icon = C_RIGHT_UPDOWN; // right - up - down
  1298. break;
  1299. default:
  1300. icon = C_RIGHT; // right
  1301. break;
  1302. }
  1303. } else {
  1304. switch (index) {
  1305. case EF_NW:
  1306. case EF_NW|EF_N:
  1307. case EF_W|EF_N:
  1308. case EF_NW|EF_W|EF_N:
  1309. case EF_NW|EF_W:
  1310. case EF_NW|EF_S|EF_N:
  1311. icon = C_LEFT_UP; // left - up
  1312. break;
  1313. case EF_SW:
  1314. case EF_SW|EF_S:
  1315. case EF_W|EF_S:
  1316. case EF_W|EF_SW|EF_S:
  1317. case EF_W|EF_SW:
  1318. case EF_SW|EF_S|EF_N:
  1319. icon = C_LEFT_DOWN; // left - down
  1320. break;
  1321. case EF_NW|EF_SW:
  1322. case EF_NW|EF_SW|EF_N:
  1323. case EF_NW|EF_SW|EF_S:
  1324. case EF_NW|EF_SW|EF_S|EF_N:
  1325. case EF_W|EF_S|EF_N:
  1326. case EF_W|EF_SW|EF_N:
  1327. case EF_W|EF_SW|EF_S|EF_N:
  1328. case EF_NW|EF_W|EF_S:
  1329. case EF_NW|EF_W|EF_S|EF_N:
  1330. case EF_NW|EF_W|EF_SW|EF_S|EF_N:
  1331. case EF_NW|EF_W|EF_SW|EF_N:
  1332. case EF_NW|EF_W|EF_SW|EF_S:
  1333. icon = C_LEFT_UPDOWN; // left - up - down
  1334. break;
  1335. default:
  1336. icon = C_LEFT; // left
  1337. break;
  1338. }
  1339. }
  1340. } else {
  1341. // Presume that no concrete piece is needed.
  1342. icon = C_NONE;
  1343. if (isodd) {
  1344. index &= ~(OF_NE|OF_SE); // Ignore diagonals.
  1345. switch (index) {
  1346. case OF_N|OF_E:
  1347. icon = C_UP_RIGHT; // up right
  1348. break;
  1349. case OF_E|OF_S:
  1350. icon = C_DOWN_RIGHT; // down right
  1351. break;
  1352. case OF_N|OF_E|OF_S:
  1353. icon = C_UPDOWN_RIGHT; // up/down right
  1354. break;
  1355. default:
  1356. break;
  1357. }
  1358. } else {
  1359. index &= ~(EF_NW|EF_SW); // Ignore diagonals.
  1360. switch (index) {
  1361. case EF_N|EF_W:
  1362. icon = C_UP_LEFT; // up left
  1363. break;
  1364. case EF_W|EF_S:
  1365. icon = C_DOWN_LEFT; // down left
  1366. break;
  1367. case EF_N|EF_W|EF_S:
  1368. icon = C_UPDOWN_LEFT; // up/down left
  1369. break;
  1370. default:
  1371. break;
  1372. }
  1373. }
  1374. /*
  1375. ** If any kind of fixup piece is needed, then add concrete
  1376. ** to this location RECURSIVELY!
  1377. */
  1378. if (icon != C_NONE) {
  1379. OverlayTypeClass::As_Reference(OVERLAY_CONCRETE).Create_And_Place(Cell_Number());
  1380. icon = C_NONE;
  1381. }
  1382. }
  1383. /*
  1384. ** Update the icon on the map.
  1385. */
  1386. if (icon != C_NONE && OverlayData != icon) {
  1387. OverlayData = icon;
  1388. //Array[cell].Base = 0;
  1389. Redraw_Objects();
  1390. }
  1391. #endif
  1392. }
  1393. /***********************************************************************************************
  1394. * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. *
  1395. * *
  1396. * This routine will examine the cell and the adjacent cells to determine what the wall *
  1397. * should look like with the cell. It will then update the wall's imagery value and flag *
  1398. * the cell to be redrawn if necessary. This routine should be called whenever the wall *
  1399. * or an adjacent wall is created or destroyed. *
  1400. * *
  1401. * INPUT: none *
  1402. * *
  1403. * OUTPUT: none *
  1404. * *
  1405. * WARNINGS: none *
  1406. * *
  1407. * HISTORY: *
  1408. * 09/19/1994 JLB : Created. *
  1409. * 09/19/1994 BWG : Updated to handle partially-damaged walls. *
  1410. *=============================================================================================*/
  1411. void CellClass::Wall_Update(void)
  1412. {
  1413. if (Overlay == OVERLAY_NONE) {
  1414. return;
  1415. }
  1416. OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay);
  1417. if (!wall.IsWall) {
  1418. return;
  1419. }
  1420. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1421. static FacingType _offsets[5] = {FACING_N, FACING_E, FACING_S, FACING_W, FACING_NONE};
  1422. for (unsigned index = 0; index < (sizeof(_offsets)/sizeof(_offsets[0])); index++) {
  1423. CellClass * newcell = Adjacent_Cell(_offsets[index]);
  1424. if (newcell && newcell->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(newcell->Overlay).IsWall) {
  1425. int icon = 0;
  1426. /*
  1427. ** Build the icon number according to walls located in the adjacent
  1428. ** cells.
  1429. */
  1430. for (unsigned i = 0; i < 4; i++) {
  1431. CellClass * adjcell = newcell->Adjacent_Cell(_offsets[i]);
  1432. if (adjcell && adjcell->Overlay == newcell->Overlay) {
  1433. icon |= 1 << i;
  1434. }
  1435. }
  1436. newcell->OverlayData = (newcell->OverlayData & 0xFFF0) | icon;
  1437. /*
  1438. ** Handle special cases for the incomplete damaged wall sets. If a damage stage
  1439. ** is calculated, but there is no artwork for it, then consider the wall to be
  1440. ** completely destroyed.
  1441. */
  1442. if (newcell->Overlay == OVERLAY_BRICK_WALL && newcell->OverlayData == 48) {
  1443. newcell->Overlay = OVERLAY_NONE;
  1444. newcell->OverlayData = 0;
  1445. Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
  1446. }
  1447. if (newcell->Overlay == OVERLAY_SANDBAG_WALL && newcell->OverlayData == 16) {
  1448. newcell->Overlay = OVERLAY_NONE;
  1449. newcell->OverlayData = 0;
  1450. Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
  1451. }
  1452. if (newcell->Overlay == OVERLAY_CYCLONE_WALL && newcell->OverlayData == 32) {
  1453. newcell->Overlay = OVERLAY_NONE;
  1454. newcell->OverlayData = 0;
  1455. Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
  1456. }
  1457. if (newcell->Overlay == OVERLAY_FENCE && (newcell->OverlayData == 16 || newcell->OverlayData == 32)) {
  1458. newcell->Overlay = OVERLAY_NONE;
  1459. newcell->OverlayData = 0;
  1460. Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
  1461. }
  1462. if (newcell->Overlay == OVERLAY_BARBWIRE_WALL && newcell->OverlayData == 16) {
  1463. newcell->Overlay = OVERLAY_NONE;
  1464. newcell->OverlayData = 0;
  1465. Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
  1466. }
  1467. newcell->Recalc_Attributes();
  1468. newcell->Redraw_Objects();
  1469. }
  1470. }
  1471. }
  1472. /***********************************************************************************************
  1473. * CellClass::Cell_Coord -- Returns the coordinate of this cell. *
  1474. * *
  1475. * This support function will determine the coordinate of this cell and return it. *
  1476. * *
  1477. * INPUT: none *
  1478. * *
  1479. * OUTPUT: Returns with coordinate value of cell. *
  1480. * *
  1481. * WARNINGS: none *
  1482. * *
  1483. * HISTORY: *
  1484. * 09/19/1994 JLB : Created. *
  1485. *=============================================================================================*/
  1486. COORDINATE CellClass::Cell_Coord(void) const
  1487. {
  1488. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1489. return(::Cell_Coord(Cell_Number()));
  1490. }
  1491. /***********************************************************************************************
  1492. * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. *
  1493. * *
  1494. * This routine will lower the tiberium level in the cell. It is used by the harvesting *
  1495. * process as well as by combat damage to the tiberium fields. *
  1496. * *
  1497. * INPUT: levels -- The number of levels to reduce the tiberium. *
  1498. * *
  1499. * OUTPUT: bool; Was the tiberium level reduced by at least one level? *
  1500. * *
  1501. * WARNINGS: none *
  1502. * *
  1503. * HISTORY: *
  1504. * 09/19/1994 JLB : Created. *
  1505. *=============================================================================================*/
  1506. int CellClass::Reduce_Tiberium(int levels)
  1507. {
  1508. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1509. int reducer = 0;
  1510. if (levels > 0 && Land == LAND_TIBERIUM) {
  1511. if (OverlayData+1 > levels) {
  1512. OverlayData -= levels;
  1513. reducer = levels;
  1514. } else {
  1515. Overlay = OVERLAY_NONE;
  1516. reducer = OverlayData;
  1517. OverlayData = 0;
  1518. Recalc_Attributes();
  1519. }
  1520. }
  1521. return(reducer);
  1522. }
  1523. /***********************************************************************************************
  1524. * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. *
  1525. * *
  1526. * This routine will change the wall shape used for a wall if it's damaged. *
  1527. * *
  1528. * INPUT: damage -- The number of damage points the wall was hit with. If this value is *
  1529. * -1, then the entire wall at this cell will be destroyed. *
  1530. * *
  1531. * OUTPUT: bool; Was the wall destroyed? *
  1532. * *
  1533. * WARNINGS: none *
  1534. * *
  1535. * HISTORY: *
  1536. * 03/15/1995 BWG : Created. *
  1537. * 03/19/1995 JLB : Updates cell information if wall was destroyed. *
  1538. * 10/06/1996 JLB : Updates zone as necessary. *
  1539. *=============================================================================================*/
  1540. int CellClass::Reduce_Wall(int damage)
  1541. {
  1542. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1543. if (Overlay != OVERLAY_NONE) {
  1544. bool destroyed = false;
  1545. OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay);
  1546. if (wall.IsWall) {
  1547. /*
  1548. ** If the damage was great enough to ensure wall destruction, reduce the wall by one
  1549. ** level (no more). Otherwise determine wall reduction based on a percentage chance
  1550. ** proportional to the damage received and the wall's strength.
  1551. */
  1552. if (damage == -1 || damage >= wall.DamagePoints) {
  1553. destroyed = true;
  1554. } else {
  1555. destroyed = Random_Pick(0, wall.DamagePoints) < damage;
  1556. }
  1557. /*
  1558. ** If the wall is destroyed, destroy it and check for any adjustments to
  1559. ** adjacent walls.
  1560. */
  1561. if (destroyed) {
  1562. OverlayData+=16;
  1563. if (damage == -1 ||
  1564. (OverlayData>>4) >= wall.DamageLevels ||
  1565. ((OverlayData>>4) == wall.DamageLevels-1 && (OverlayData & 0xF)==0) ) {
  1566. Owner = HOUSE_NONE;
  1567. Overlay = OVERLAY_NONE;
  1568. OverlayData = 0;
  1569. Recalc_Attributes();
  1570. Redraw_Objects();
  1571. CellClass * ncell = Adjacent_Cell(FACING_N);
  1572. if (ncell) ncell->Wall_Update();
  1573. CellClass * wcell = Adjacent_Cell(FACING_W);
  1574. if (wcell) wcell->Wall_Update();
  1575. CellClass * scell = Adjacent_Cell(FACING_S);
  1576. if (scell) scell->Wall_Update();
  1577. CellClass * ecell = Adjacent_Cell(FACING_E);
  1578. if (ecell) ecell->Wall_Update();
  1579. Detach_This_From_All(As_Target());
  1580. /*
  1581. ** The zone calculation changes now for non-crushable zone sensitive
  1582. ** travellers.
  1583. */
  1584. if (wall.IsCrushable) {
  1585. Map.Zone_Reset(MZONEF_NORMAL);
  1586. } else {
  1587. Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL);
  1588. }
  1589. return(true);
  1590. }
  1591. }
  1592. }
  1593. }
  1594. return(false);
  1595. }
  1596. /***********************************************************************************************
  1597. * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE *
  1598. * *
  1599. * INPUT: *
  1600. * coord COORDINATE to compute index for *
  1601. * *
  1602. * OUTPUT: *
  1603. * index into StoppingCoord that's closest to this coord *
  1604. * *
  1605. * WARNINGS: *
  1606. * none. *
  1607. * *
  1608. * HISTORY: *
  1609. * 11/21/1994 BR : Created. *
  1610. * 12/10/1994 JLB : Uses alternate sub-position algorithm. *
  1611. *=============================================================================================*/
  1612. int CellClass::Spot_Index(COORDINATE coord)
  1613. {
  1614. COORDINATE rel = Coord_Fraction(coord); // Sub coordinate value within cell.
  1615. /*
  1616. ** If the coordinate is close enough to the center of the cell, then return
  1617. ** the center position index.
  1618. */
  1619. if (Distance(rel, (COORDINATE)0x00800080L) < 60) {
  1620. return(0);
  1621. }
  1622. /*
  1623. ** Since the center cell position has been eliminated, a simple comparison
  1624. ** as related to the center of the cell can be used to determine the sub
  1625. ** position. Take advantage of the fact that the sub positions are organized
  1626. ** from left to right, top to bottom.
  1627. */
  1628. int index = 0;
  1629. if (Coord_X(rel) > 0x80) index |= 0x01;
  1630. if (Coord_Y(rel) > 0x80) index |= 0x02;
  1631. return(index+1);
  1632. }
  1633. /***********************************************************************************************
  1634. * CellClass::Closest_Free_Spot -- returns free spot closest to given coord *
  1635. * *
  1636. * Similar to the CellClass::Free_Spot; this routine finds the spot in *
  1637. * the cell closest to the given coordinate, and returns the COORDINATE of *
  1638. * that spot if it's available, NULL if it's not. *
  1639. * *
  1640. * INPUT: *
  1641. * coord coordinate to check (only sub cell position examined) *
  1642. * *
  1643. * any -- If only the closest spot is desired regardless of whether it is free or *
  1644. * not, then this parameter will be true. *
  1645. * *
  1646. * OUTPUT: *
  1647. * COORDINATE of free spot, NULL if none. The coordinate return value does not alter the cell *
  1648. * coordinate data portions of the coordinate passed in. Only the lower sub-cell *
  1649. * data is altered. *
  1650. * *
  1651. * WARNINGS: *
  1652. * none. *
  1653. * *
  1654. * HISTORY: *
  1655. * 11/08/1994 BR : Created. *
  1656. * 12/10/1994 JLB : Picks best of closest stopping positions. *
  1657. * 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. *
  1658. *=============================================================================================*/
  1659. COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const
  1660. {
  1661. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1662. int spot_index = Spot_Index(coord);
  1663. /*
  1664. ** This precalculated sequence table records the closest spots to any given spot. Sequential
  1665. ** examination of these spots for availability ensures that the closest available one is
  1666. ** discovered first.
  1667. */
  1668. static unsigned char _sequence[5][4] = {
  1669. {1,2,3,4},
  1670. {0,2,3,4},
  1671. {0,1,4,3},
  1672. {0,1,4,2},
  1673. {0,2,3,1}
  1674. };
  1675. /*
  1676. ** In the case of the center coordinate being requested, but is occupied, then all other
  1677. ** sublocations are equidistant. Instead of picking a static sequence of examination, the
  1678. ** order is mixed up by way of this table.
  1679. */
  1680. static unsigned char _alternate[4][4] = {
  1681. {1,2,3,4},
  1682. {2,3,4,1},
  1683. {3,4,1,2},
  1684. {4,1,2,3},
  1685. };
  1686. coord = Coord_Whole(coord);
  1687. /*
  1688. ** Cells occupied by buildings or vehicles don't have any free spots.
  1689. */
  1690. if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) {
  1691. return(NULL);
  1692. }
  1693. /*
  1694. ** If just the nearest position is desired regardless of whether occupied or not,
  1695. ** then just return with the stopping coordinate value.
  1696. */
  1697. if (any || Is_Spot_Free(spot_index)) {
  1698. return(Coord_Add(coord, StoppingCoordAbs[spot_index]));
  1699. }
  1700. /*
  1701. ** Scan through all available sub-locations in the cell in order to determine
  1702. ** the closest one to the coordinate requested. Use precalculated table so that
  1703. ** when the first free position is found, bail.
  1704. */
  1705. unsigned char * sequence;
  1706. if (spot_index == 0) {
  1707. sequence = &_alternate[Random_Pick(0, 3)][0];
  1708. } else {
  1709. sequence = &_sequence[spot_index][0];
  1710. }
  1711. for (int index = 0; index < 4; index++) {
  1712. int pos = *sequence++;
  1713. if (Is_Spot_Free(pos)) {
  1714. return(Coord_Add(coord, StoppingCoordAbs[pos]));
  1715. }
  1716. }
  1717. /*
  1718. ** No free spot could be found so return a NULL coordinate.
  1719. */
  1720. return(0x00000000L);
  1721. }
  1722. /***********************************************************************************************
  1723. * CellClass::Clear_Icon -- Calculates what the clear icon number should be. *
  1724. * *
  1725. * This support routine will determine what the clear icon number would be for the cell. *
  1726. * The icon number is determined by converting the cell number into an index into a *
  1727. * lookup table. This yields what appears to be a randomized map without the necessity of *
  1728. * generating and recording randomized map numbers. *
  1729. * *
  1730. * INPUT: none *
  1731. * *
  1732. * OUTPUT: Returns with the icon number for clear terrain if it were displayed at the *
  1733. * cell. *
  1734. * *
  1735. * WARNINGS: none *
  1736. * *
  1737. * HISTORY: *
  1738. * 12/26/1994 JLB : Created. *
  1739. * 06/09/1995 JLB : Uses 16 entry scramble algorithm. *
  1740. *=============================================================================================*/
  1741. int CellClass::Clear_Icon(void) const
  1742. {
  1743. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1744. CELL cell = Cell_Number();
  1745. return((Cell_X(cell) & 0x03) | ((Cell_Y(cell) & 0x03) << 2));
  1746. // return((cell & 0x03) | ((unsigned(cell)>>5) & 0x0C));
  1747. }
  1748. /***********************************************************************************************
  1749. * CellClass::Incoming -- Causes objects in cell to "run for cover". *
  1750. * *
  1751. * This routine is called whenever a great, but slow moving, threat is presented to the *
  1752. * occupants of a cell. The occupants will, in most cases, stop what they are doing and *
  1753. * try to get out of the way. *
  1754. * *
  1755. * INPUT: threat -- The coordinate source of the threat. *
  1756. * *
  1757. * forced -- If this threat is so major that the occupants should stop what *
  1758. * they are doing, then this parameter should be set to true. *
  1759. * *
  1760. * nokidding -- Override the scatter to also affect human controlled objects. *
  1761. * *
  1762. * OUTPUT: none *
  1763. * *
  1764. * WARNINGS: none *
  1765. * *
  1766. * HISTORY: *
  1767. * 01/10/1995 JLB : Created. *
  1768. * 08/02/1996 JLB : Added the "nokidding" parameter. *
  1769. *=============================================================================================*/
  1770. void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding)
  1771. {
  1772. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1773. ObjectClass * object = NULL;
  1774. object = Cell_Occupier();
  1775. while (object != NULL) {
  1776. /*
  1777. ** Special check to make sure that friendly units never scatter.
  1778. */
  1779. if (nokidding || Rule.IsScatter || (object->Is_Techno() && ((TechnoClass *)object)->House->IQ >= Rule.IQScatter)) {
  1780. object->Scatter(threat, forced, nokidding);
  1781. }
  1782. object = object->Next;
  1783. }
  1784. }
  1785. /***********************************************************************************************
  1786. * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. *
  1787. * *
  1788. * Use this routine to return a reference to the adjacent cell in the direction specified. *
  1789. * *
  1790. * INPUT: face -- The direction to use when determining the adjacent cell. *
  1791. * *
  1792. * OUTPUT: Returns with a reference to the adjacent cell. *
  1793. * *
  1794. * WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. *
  1795. * *
  1796. * HISTORY: *
  1797. * 03/19/1995 JLB : Created. *
  1798. *=============================================================================================*/
  1799. CellClass const * CellClass::Adjacent_Cell(FacingType face) const
  1800. {
  1801. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1802. if (face == FACING_NONE) {
  1803. return(this);
  1804. }
  1805. if ((unsigned)face >= FACING_COUNT) {
  1806. return(NULL);
  1807. }
  1808. CELL newcell = ::Adjacent_Cell(Cell_Number(), face);
  1809. if ((unsigned)newcell >= MAP_CELL_TOTAL) {
  1810. return(NULL);
  1811. }
  1812. return &Map[newcell];
  1813. }
  1814. /***************************************************************************
  1815. * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level *
  1816. * *
  1817. * INPUT: *
  1818. * *
  1819. * OUTPUT: *
  1820. * *
  1821. * WARNINGS: *
  1822. * *
  1823. * HISTORY: *
  1824. * 04/24/1995 PWG : Created. *
  1825. *=========================================================================*/
  1826. void CellClass::Adjust_Threat(HousesType house, int threat_value)
  1827. {
  1828. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1829. int region = Map.Cell_Region(Cell_Number());
  1830. for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) {
  1831. if (lp == house) continue;
  1832. HouseClass * house_ptr = HouseClass::As_Pointer(lp);
  1833. if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) {
  1834. house_ptr->Adjust_Threat(region, threat_value);
  1835. }
  1836. }
  1837. if (Debug_Threat) {
  1838. Map.Flag_To_Redraw(true);
  1839. }
  1840. }
  1841. /***********************************************************************************************
  1842. * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. *
  1843. * *
  1844. * This routine will adjust the level of the Tiberium in the cell so that it will *
  1845. * smoothly blend with the adjacent Tiberium. This routine should only be called for *
  1846. * new Tiberium cells. Existing cells that contain Tiberium follow a different growth *
  1847. * pattern. *
  1848. * *
  1849. * INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay *
  1850. * used. *
  1851. * *
  1852. * OUTPUT: Returns with the added Tiberium value that is now available for harvesting. *
  1853. * *
  1854. * WARNINGS: The return value is only valid for the initial placement. Tiberium growth will *
  1855. * increase the net worth of the existing Tiberium. *
  1856. * *
  1857. * HISTORY: *
  1858. * 05/16/1995 JLB : Created. *
  1859. * 02/20/1996 JLB : Takes into account the ore type. *
  1860. *=============================================================================================*/
  1861. long CellClass::Tiberium_Adjust(bool pregame)
  1862. {
  1863. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1864. if (Overlay != OVERLAY_NONE) {
  1865. if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) {
  1866. static int _adj[9] = {0,1,3,4,6,7,8,10,11};
  1867. static int _adjgem[9] = {0,0,0,1,1,1,2,2,2};
  1868. int count = 0;
  1869. /*
  1870. ** Mixup the Tiberium overlays so that they don't look the same.
  1871. ** Since the type of ore is known, also record the nominal
  1872. ** value per step of that ore type.
  1873. */
  1874. bool gems = false;
  1875. int value = 0;
  1876. if (pregame) {
  1877. switch (Overlay) {
  1878. case OVERLAY_GOLD1:
  1879. case OVERLAY_GOLD2:
  1880. case OVERLAY_GOLD3:
  1881. case OVERLAY_GOLD4:
  1882. value = Rule.GoldValue;
  1883. Overlay = Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4);
  1884. break;
  1885. case OVERLAY_GEMS1:
  1886. case OVERLAY_GEMS2:
  1887. case OVERLAY_GEMS3:
  1888. case OVERLAY_GEMS4:
  1889. gems = true;
  1890. value = Rule.GemValue*4;
  1891. Overlay = Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4);
  1892. break;
  1893. default:
  1894. break;
  1895. }
  1896. }
  1897. /*
  1898. ** Add up all adjacent cells that contain tiberium.
  1899. ** (Skip those cells which aren't on the map)
  1900. */
  1901. for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) {
  1902. if ((unsigned)::Adjacent_Cell(Cell_Number(), face) >= MAP_CELL_TOTAL) continue;
  1903. CellClass * adj = Adjacent_Cell(face);
  1904. if (adj && adj->Overlay != OVERLAY_NONE &&
  1905. OverlayTypeClass::As_Reference(adj->Overlay).Land == LAND_TIBERIUM) {
  1906. count++;
  1907. }
  1908. }
  1909. if (gems) {
  1910. OverlayData = _adjgem[count];
  1911. OverlayData = min(OverlayData, 2);
  1912. } else {
  1913. OverlayData = _adj[count];
  1914. }
  1915. return((OverlayData+1) * value);
  1916. }
  1917. }
  1918. return(0);
  1919. }
  1920. extern bool MPSuperWeaponDisable;
  1921. /***********************************************************************************************
  1922. * CellClass::Goodie_Check -- Performs crate discovery logic. *
  1923. * *
  1924. * Call this routine whenever an object enters a cell. It will check for the existence *
  1925. * of a crate and generate any "goodie" it might contain. *
  1926. * *
  1927. * INPUT: object -- Pointer to the object that is triggering this crate. *
  1928. * *
  1929. * OUTPUT: Can the object continue to enter this cell? A false return value means that the *
  1930. * cell is now occupied and must not be entered. *
  1931. * *
  1932. * WARNINGS: none *
  1933. * *
  1934. * HISTORY: *
  1935. * 05/22/1995 JLB : Created. *
  1936. * 07/08/1995 JLB : Added a bunch of goodies to the crates. *
  1937. * 06/17/1996 JLB : Revamped for Red Alert *
  1938. *=============================================================================================*/
  1939. bool CellClass::Goodie_Check(FootClass * object)
  1940. {
  1941. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  1942. if (object != NULL && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) {
  1943. bool force_mcv = false;
  1944. int force_money = 0;
  1945. int damage;
  1946. COORDINATE coord;
  1947. /*
  1948. ** Determine the total number of shares for all the crate powerups. This is used as
  1949. ** the base pool to determine the odds from.
  1950. */
  1951. int total_shares = 0;
  1952. for (int index = CRATE_FIRST; index < CRATE_COUNT; index++) {
  1953. total_shares += CrateShares[index];
  1954. }
  1955. /*
  1956. ** Pick a random crate powerup according to the shares allotted to each powerup.
  1957. ** In solo play, the bonus item is dependant upon the rules control.
  1958. */
  1959. CrateType powerup;
  1960. if (Session.Type == GAME_NORMAL) {
  1961. /*
  1962. ** Solo play has money amount determined by rules.ini file.
  1963. */
  1964. force_money = Rule.SoloCrateMoney;
  1965. if (Overlay == OVERLAY_STEEL_CRATE) {
  1966. powerup = Rule.SilverCrate;
  1967. }
  1968. if (Overlay == OVERLAY_WOOD_CRATE) {
  1969. powerup = Rule.WoodCrate;
  1970. }
  1971. if (Overlay == OVERLAY_WATER_CRATE) {
  1972. //Mono_Printf("%d-%s.\n", __LINE__, __FILE__);
  1973. powerup = Rule.WaterCrate;
  1974. }
  1975. } else {
  1976. int pick = Random_Pick(1, total_shares);
  1977. int share_count = 0;
  1978. for (powerup = CRATE_FIRST; powerup < CRATE_COUNT; powerup++) {
  1979. share_count += CrateShares[powerup];
  1980. if (pick <= share_count) break;
  1981. }
  1982. assert(powerup != CRATE_COUNT);
  1983. /*
  1984. ** Depending on what was picked, there might be an alternate goodie if the selected
  1985. ** goodie would have no effect.
  1986. */
  1987. switch (powerup) {
  1988. case CRATE_UNIT:
  1989. if (object->House->CurUnits > 50) powerup = CRATE_MONEY;
  1990. break;
  1991. case CRATE_SQUAD:
  1992. if (object->House->CurInfantry > 100) powerup = CRATE_MONEY;
  1993. break;
  1994. case CRATE_DARKNESS:
  1995. if (object->House->IsGPSActive) powerup = CRATE_MONEY;
  1996. break;
  1997. case CRATE_ARMOR:
  1998. if (object->ArmorBias != 1) powerup = CRATE_MONEY;
  1999. break;
  2000. case CRATE_SPEED:
  2001. if (object->SpeedBias != 1 || object->What_Am_I() == RTTI_AIRCRAFT) powerup = CRATE_MONEY;
  2002. break;
  2003. case CRATE_FIREPOWER:
  2004. if (object->FirepowerBias != 1 || !object->Is_Weapon_Equipped()) powerup = CRATE_MONEY;
  2005. break;
  2006. case CRATE_REVEAL:
  2007. if (object->House->IsVisionary) {
  2008. if (object->House->IsGPSActive) {
  2009. powerup = CRATE_MONEY;
  2010. } else {
  2011. powerup = CRATE_DARKNESS;
  2012. }
  2013. }
  2014. break;
  2015. case CRATE_CLOAK:
  2016. if (object->IsCloakable) powerup = CRATE_MONEY;
  2017. break;
  2018. // case CRATE_HEAL_BASE:
  2019. // if (object->House->BScan == 0) powerup = CRATE_UNIT;
  2020. case CRATE_MONEY:
  2021. break;
  2022. case CRATE_ICBM:
  2023. case CRATE_PARA_BOMB:
  2024. case CRATE_SONAR:
  2025. if (Session.Type != GAME_NORMAL) {
  2026. if (MPSuperWeaponDisable) {
  2027. powerup = CRATE_MONEY;
  2028. }
  2029. }
  2030. break;
  2031. case CRATE_TIMEQUAKE:
  2032. /*
  2033. ** For the time quake crate, scan through and count up all the
  2034. ** units (and infantry and ships and aircraft) and if either
  2035. ** side has very few, allow the time quake. Otherwise,
  2036. ** change the crate to money or something. Only do this for
  2037. ** multiplay - for solo play, they get what they get. First,
  2038. ** check for time - the chance for getting a time quake crate
  2039. ** should be very very low when they first start the mission,
  2040. ** but as time goes on the chance goes up.
  2041. */
  2042. if (Session.Type != GAME_NORMAL) {
  2043. int i,ucount;
  2044. int minunits = 1000;
  2045. bool found = false;
  2046. unsigned long minutes = (Score.ElapsedTime / TIMER_MINUTE);
  2047. if (minutes > 100) minutes = 100;
  2048. if (Random_Pick(0,100-(int)minutes) == 0) {
  2049. for (i=0; i < (Session.Players.Count() + Session.Options.AIPlayers); i++) {
  2050. ucount = 0;
  2051. HouseClass * hptr = Houses.Ptr(i + HOUSE_MULTI1);
  2052. if (hptr != NULL && !hptr->IsDefeated) {
  2053. int j;
  2054. for( j=0; j < UNIT_COUNT; j++) {
  2055. ucount += hptr->QuantityU(j);
  2056. }
  2057. for( j=0; j < INFANTRY_COUNT; j++) {
  2058. ucount += hptr->QuantityI(j);
  2059. }
  2060. for( j=0; j < AIRCRAFT_COUNT; j++) {
  2061. ucount += hptr->QuantityA(j);
  2062. }
  2063. for( j=0; j < VESSEL_COUNT; j++) {
  2064. ucount += hptr->QuantityV(j);
  2065. }
  2066. int bcount = 0;
  2067. for( j=0; j < STRUCT_COUNT; j++) {
  2068. bcount += hptr->QuantityB(j);
  2069. }
  2070. ucount += bcount/2; // weight buildings less
  2071. minunits = min(minunits, ucount);
  2072. }
  2073. }
  2074. if (Random_Pick(0, minunits) == minunits) {
  2075. found = true;
  2076. }
  2077. }
  2078. if (!found) {
  2079. powerup = CRATE_MONEY;
  2080. }
  2081. }
  2082. break;
  2083. }
  2084. /*
  2085. ** Possibly force it to be an MCV if there is
  2086. ** sufficient money and no buildings left.
  2087. */
  2088. if ( object->House->BScan == 0 &&
  2089. object->House->Available_Money() > ( (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost) * object->House->CostBias) &&
  2090. Session.Options.Bases &&
  2091. !(object->House->UScan & UNITF_MCV)) {
  2092. powerup = CRATE_UNIT;
  2093. force_mcv = true;
  2094. }
  2095. /*
  2096. ** If the powerup is money but there is insufficient money to build a refinery but there is a construction
  2097. ** yard available, then force the money to be enough to rebuild the refinery.
  2098. */
  2099. if (powerup == CRATE_MONEY && (object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST &&
  2100. object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias) {
  2101. force_money = BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias;
  2102. }
  2103. /*
  2104. ** Special override for water crates so that illegal goodies items
  2105. ** won't appear.
  2106. */
  2107. if (Overlay == OVERLAY_WATER_CRATE) {
  2108. switch (powerup) {
  2109. case CRATE_UNIT:
  2110. case CRATE_SQUAD:
  2111. powerup = CRATE_MONEY;
  2112. break;
  2113. default:
  2114. break;
  2115. }
  2116. }
  2117. }
  2118. /*
  2119. ** Keep track of the number of each type of crate found
  2120. */
  2121. if (Session.Type == GAME_INTERNET) {
  2122. object->House->TotalCrates->Increment_Unit_Total(powerup);
  2123. }
  2124. /*
  2125. ** Remove the crate from the map.
  2126. */
  2127. Map.Remove_Crate(Cell_Number());
  2128. // Map[Cell_Number()].Overlay = OVERLAY_NONE;
  2129. if (Session.Type != GAME_NORMAL && Rule.IsMPCrates) {
  2130. Map.Place_Random_Crate();
  2131. }
  2132. /*
  2133. ** Generate any corresponding animation associated with this crate powerup.
  2134. */
  2135. if (CrateAnims[powerup] != ANIM_NONE) {
  2136. new AnimClass(CrateAnims[powerup], Cell_Coord());
  2137. }
  2138. /*
  2139. ** Create the effect requested.
  2140. */
  2141. bool tospeak = false;
  2142. switch (powerup) {
  2143. case CRATE_TIMEQUAKE:
  2144. TimeQuake = true;
  2145. break;
  2146. /*
  2147. ** Give the player money.
  2148. */
  2149. case CRATE_MONEY:
  2150. crate_money:
  2151. if (force_money > 0) {
  2152. object->House->Refund_Money(force_money);
  2153. } else {
  2154. object->House->Refund_Money(Random_Pick(CrateData[powerup], CrateData[powerup]+900));
  2155. }
  2156. break;
  2157. /*
  2158. ** Shroud the world in blackness.
  2159. */
  2160. case CRATE_DARKNESS:
  2161. /*
  2162. ** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM
  2163. */
  2164. if (object->House->IsHuman) {
  2165. Map.Shroud_The_Map(object->House);
  2166. }
  2167. break;
  2168. /*
  2169. ** Reveal the entire map.
  2170. */
  2171. case CRATE_REVEAL:
  2172. /*
  2173. ** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM
  2174. */
  2175. object->House->IsVisionary = true;
  2176. if (object->House->IsHuman) {
  2177. for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
  2178. Map.Map_Cell(cell, object->House);
  2179. }
  2180. Map.Flag_To_Redraw(true);
  2181. }
  2182. break;
  2183. /*
  2184. ** Try to create a unit where the crate was.
  2185. */
  2186. case CRATE_UNIT: {
  2187. UnitTypeClass const * utp = NULL;
  2188. /*
  2189. ** Give the player an MCV if he has no base left but does have more than enough
  2190. ** money to rebuild a new base. Of course, if he already has an MCV, then don't
  2191. ** give him another one.
  2192. */
  2193. if (force_mcv) {
  2194. utp = &UnitTypeClass::As_Reference(UNIT_MCV);
  2195. }
  2196. /*
  2197. ** If the player has a base and a refinery, but no harvester, then give him
  2198. ** a free one.
  2199. */
  2200. if (utp == NULL && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) {
  2201. utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER);
  2202. }
  2203. /*
  2204. ** Check for special unit type override value.
  2205. */
  2206. if (Rule.UnitCrateType != UNIT_NONE) {
  2207. utp = &UnitTypeClass::As_Reference(Rule.UnitCrateType);
  2208. }
  2209. /*
  2210. ** If no unit type has been determined, then pick one at random.
  2211. */
  2212. while (utp == NULL) {
  2213. #ifdef FIXIT_ANTS
  2214. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  2215. UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_RA_COUNT-1 -3));
  2216. #else
  2217. UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1 -3));
  2218. #endif
  2219. #else
  2220. UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1));
  2221. #endif
  2222. if (utype != UNIT_MCV || Session.Options.Bases) {
  2223. utp = &UnitTypeClass::As_Reference(utype);
  2224. if (utp->IsCrateGoodie && (utp->Ownable & (1 << HouseClass::As_Pointer(object->Owner())->ActLike))) {
  2225. break;
  2226. }
  2227. utp = NULL;
  2228. }
  2229. }
  2230. if (utp != NULL) {
  2231. UnitClass * goodie_unit = (UnitClass *)utp->Create_One_Of(object->House);
  2232. if (goodie_unit != NULL) {
  2233. if (goodie_unit->Unlimbo(Cell_Coord())) {
  2234. return(false);
  2235. }
  2236. /*
  2237. ** Try to place the object into a nearby cell if something is preventing
  2238. ** placement at the crate location.
  2239. */
  2240. CELL cell = Map.Nearby_Location(Cell_Number(), goodie_unit->Class->Speed);
  2241. if (goodie_unit->Unlimbo(::Cell_Coord(cell))) {
  2242. return(false);
  2243. }
  2244. delete goodie_unit;
  2245. goto crate_money;
  2246. }
  2247. }
  2248. }
  2249. break;
  2250. /*
  2251. ** Create a squad of miscellaneous composition.
  2252. */
  2253. case CRATE_SQUAD:
  2254. for (int index = 0; index < 5; index++) {
  2255. static InfantryType _inf[] = {
  2256. INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,
  2257. INFANTRY_E2,
  2258. INFANTRY_E3,
  2259. INFANTRY_RENOVATOR
  2260. };
  2261. if (!InfantryTypeClass::As_Reference(_inf[Random_Pick(0, ARRAY_SIZE(_inf)-1)]).Create_And_Place(Cell_Number(), object->Owner())) {
  2262. if (index == 0) {
  2263. goto crate_money;
  2264. }
  2265. }
  2266. }
  2267. return(false);
  2268. /*
  2269. ** A one para-bomb mission.
  2270. */
  2271. case CRATE_PARA_BOMB:
  2272. if (object->House->SuperWeapon[SPC_PARA_BOMB].Enable(true)) {
  2273. // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM
  2274. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  2275. if (object->House->IsHuman) {
  2276. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, object->House);
  2277. }
  2278. } else {
  2279. if (object->IsOwnedByPlayer) {
  2280. Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB);
  2281. Map.Column[1].Flag_To_Redraw();
  2282. }
  2283. }
  2284. }
  2285. break;
  2286. /*
  2287. ** A one time sonar pulse
  2288. */
  2289. case CRATE_SONAR:
  2290. if (object->House->SuperWeapon[SPC_SONAR_PULSE].Enable(true)) {
  2291. // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM
  2292. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  2293. if (object->House->IsHuman) {
  2294. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SONAR_PULSE, object->House);
  2295. }
  2296. } else {
  2297. if (object->IsOwnedByPlayer) {
  2298. Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE);
  2299. Map.Column[1].Flag_To_Redraw();
  2300. }
  2301. }
  2302. }
  2303. break;
  2304. /*
  2305. ** A group of explosions are triggered around the crate.
  2306. */
  2307. case CRATE_EXPLOSION:
  2308. if (object != NULL) {
  2309. int d = CrateData[powerup];
  2310. object->Take_Damage(d, 0, WARHEAD_HE, 0, true);
  2311. }
  2312. for (int index = 0; index < 5; index++) {
  2313. COORDINATE frag_coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200));
  2314. new AnimClass(ANIM_FBALL1, frag_coord);
  2315. damage = CrateData[powerup];
  2316. Explosion_Damage(frag_coord, damage, NULL, WARHEAD_HE);
  2317. }
  2318. break;
  2319. /*
  2320. ** A napalm blast is triggered.
  2321. */
  2322. case CRATE_NAPALM:
  2323. coord = Coord_Mid(Cell_Coord(), object->Center_Coord());
  2324. new AnimClass(ANIM_NAPALM3, coord);
  2325. if (object != NULL) {
  2326. int d = CrateData[powerup];
  2327. object->Take_Damage(d, 0, WARHEAD_FIRE, 0, true);
  2328. }
  2329. damage = CrateData[powerup];
  2330. Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE);
  2331. break;
  2332. /*
  2333. ** All objects within a certain range will gain the ability to cloak.
  2334. */
  2335. case CRATE_CLOAK:
  2336. for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
  2337. ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
  2338. if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) {
  2339. ((TechnoClass *)obj)->IsCloakable = true;
  2340. }
  2341. }
  2342. break;
  2343. /*
  2344. ** All of the player's objects heal up.
  2345. */
  2346. case CRATE_HEAL_BASE:
  2347. if (object->IsOwnedByPlayer) {
  2348. Sound_Effect(VOC_HEAL, object->Center_Coord());
  2349. }
  2350. for (int index = 0; index < Logic.Count(); index++) {
  2351. ObjectClass * obj = Logic[index];
  2352. if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) {
  2353. obj->Strength = obj->Class_Of().MaxStrength;
  2354. }
  2355. }
  2356. break;
  2357. case CRATE_ICBM:
  2358. if (object->House->SuperWeapon[SPC_NUCLEAR_BOMB].Enable(true)) {
  2359. // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM
  2360. if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
  2361. if (object->House->IsHuman) {
  2362. Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, object->House);
  2363. }
  2364. } else {
  2365. if (object->IsOwnedByPlayer) {
  2366. Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB);
  2367. Map.Column[1].Flag_To_Redraw();
  2368. }
  2369. }
  2370. }
  2371. break;
  2372. case CRATE_ARMOR:
  2373. for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
  2374. ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
  2375. if (obj != NULL && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->ArmorBias == 1) {
  2376. fixed val = ((TechnoClass *)obj)->ArmorBias * Inverse(fixed(CrateData[powerup], 256));
  2377. ((TechnoClass *)obj)->ArmorBias = val;
  2378. if (obj->Owner() == PlayerPtr->Class->House) tospeak = true;
  2379. }
  2380. }
  2381. if (tospeak) Speak(VOX_UPGRADE_ARMOR);
  2382. break;
  2383. case CRATE_SPEED:
  2384. for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
  2385. ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
  2386. if (obj && obj->Is_Foot() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((FootClass *)obj)->SpeedBias == 1 && obj->What_Am_I() != RTTI_AIRCRAFT) {
  2387. FootClass * foot = (FootClass *)obj;
  2388. fixed val = foot->SpeedBias * fixed(CrateData[powerup], 256);
  2389. foot->SpeedBias = val;
  2390. if (foot->IsOwnedByPlayer) tospeak = true;
  2391. }
  2392. }
  2393. if (tospeak) Speak(VOX_UPGRADE_SPEED);
  2394. break;
  2395. case CRATE_FIREPOWER:
  2396. for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
  2397. ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
  2398. if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->FirepowerBias == 1) {
  2399. fixed val = ((TechnoClass *)obj)->FirepowerBias * fixed(CrateData[powerup], 256);
  2400. ((TechnoClass *)obj)->FirepowerBias = val;
  2401. if (obj->Owner() == PlayerPtr->Class->House) tospeak = true;
  2402. }
  2403. }
  2404. if (tospeak) Speak(VOX_UPGRADE_FIREPOWER);
  2405. break;
  2406. case CRATE_INVULN:
  2407. for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
  2408. ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
  2409. if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) {
  2410. ((TechnoClass *)obj)->IronCurtainCountDown = (TICKS_PER_MINUTE * fixed(CrateData[powerup], 256));
  2411. obj->Mark(MARK_CHANGE);
  2412. }
  2413. }
  2414. break;
  2415. /*
  2416. ** A chronal vortex appears targetted at the triggering object.
  2417. */
  2418. case CRATE_VORTEX:
  2419. if ( !ChronalVortex.Is_Active()) {
  2420. ChronalVortex.Appear ( Cell_Coord() );
  2421. ChronalVortex.Set_Target ( (ObjectClass*) object );
  2422. Sound_Effect(VOC_TESLA_ZAP, object->Center_Coord());
  2423. }
  2424. break;
  2425. default:
  2426. break;
  2427. }
  2428. }
  2429. return(true);
  2430. }
  2431. /***********************************************************************************************
  2432. * CellClass::Flag_Place -- Places a house flag down on the cell. *
  2433. * *
  2434. * This routine will place the house flag at this cell location. *
  2435. * *
  2436. * INPUT: house -- The house that is having its flag placed here. *
  2437. * *
  2438. * OUTPUT: Was the flag successfully placed here? *
  2439. * *
  2440. * WARNINGS: Failure to place means that the cell is impassable for some reason. *
  2441. * *
  2442. * HISTORY: *
  2443. * 05/23/1995 JLB : Created. *
  2444. *=============================================================================================*/
  2445. bool CellClass::Flag_Place(HousesType house)
  2446. {
  2447. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  2448. if (!IsFlagged && Is_Clear_To_Move(SPEED_TRACK, false, false)) {
  2449. IsFlagged = true;
  2450. Owner = house;
  2451. Flag_Update();
  2452. Redraw_Objects();
  2453. return(true);
  2454. }
  2455. return(false);
  2456. }
  2457. /***********************************************************************************************
  2458. * CellClass::Flag_Remove -- Removes the house flag from the cell. *
  2459. * *
  2460. * This routine will free the cell of any house flag that may be located there. *
  2461. * *
  2462. * INPUT: none *
  2463. * *
  2464. * OUTPUT: Was there a flag here that was removed? *
  2465. * *
  2466. * WARNINGS: none *
  2467. * *
  2468. * HISTORY: *
  2469. * 05/23/1995 JLB : Created. *
  2470. *=============================================================================================*/
  2471. bool CellClass::Flag_Remove(void)
  2472. {
  2473. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  2474. if (IsFlagged) {
  2475. IsFlagged = false;
  2476. Owner = HOUSE_NONE;
  2477. Flag_Update();
  2478. Redraw_Objects();
  2479. return(true);
  2480. }
  2481. return(false);
  2482. }
  2483. void CellClass::Flag_Update(void)
  2484. {
  2485. if (IsFlagged && !CTFFlag) {
  2486. Flag_Create();
  2487. } else if (!IsFlagged && CTFFlag) {
  2488. Flag_Destroy();
  2489. }
  2490. }
  2491. void CellClass::Flag_Create(void)
  2492. {
  2493. if (!CTFFlag) {
  2494. CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord());
  2495. if (CTFFlag == NULL) {
  2496. for (int i = 0; i < Anims.Count(); ++i) {
  2497. AnimClass* anim = Anims.Ptr(i);
  2498. if (*anim != ANIM_FLAG) {
  2499. delete anim;
  2500. break;
  2501. }
  2502. }
  2503. CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord());
  2504. }
  2505. assert(CTFFlag != NULL);
  2506. CTFFlag->Set_Owner(Owner);
  2507. }
  2508. }
  2509. void CellClass::Flag_Destroy(void)
  2510. {
  2511. delete CTFFlag;
  2512. CTFFlag = NULL;
  2513. }
  2514. /***********************************************************************************************
  2515. * CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
  2516. * *
  2517. * This routine is called when some event would cause a momentary disruption in the *
  2518. * cloaking device. All objects that are cloaked in the cell will have their cloaking *
  2519. * device shimmer. *
  2520. * *
  2521. * INPUT: none *
  2522. * *
  2523. * OUTPUT: none *
  2524. * *
  2525. * WARNINGS: none *
  2526. * *
  2527. * HISTORY: *
  2528. * 07/29/1995 JLB : Created. *
  2529. *=============================================================================================*/
  2530. void CellClass::Shimmer(void)
  2531. {
  2532. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  2533. ObjectClass * object = Cell_Occupier();
  2534. while (object) {
  2535. object->Do_Shimmer();
  2536. object = object->Next;
  2537. }
  2538. }
  2539. /***********************************************************************************************
  2540. * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel *
  2541. * *
  2542. * This routine is called when determining general passability for purposes of zone *
  2543. * calculation. Only blockages that cannot be circumvented are considered to make a cell *
  2544. * impassable. All other obstructions can either be destroyed or are temporary. *
  2545. * *
  2546. * INPUT: loco -- The locomotion type to use when determining passablility. *
  2547. * *
  2548. * ignoreinfantry -- Should infantry in the cell be ignored for movement purposes? *
  2549. * *
  2550. * ignorevehicles -- If vehicles should be ignored, then this flag will be true. *
  2551. * *
  2552. * zone -- If specified, the zone must match this value or else movement is *
  2553. * presumed disallowed. *
  2554. * *
  2555. * check -- This specifies the zone type that this check applies to. *
  2556. * *
  2557. * OUTPUT: Is the cell generally passable to ground targeting? *
  2558. * *
  2559. * WARNINGS: none *
  2560. * *
  2561. * HISTORY: *
  2562. * 09/25/1995 JLB : Created. *
  2563. * 06/25/1996 JLB : Uses tracked vehicles as a basis for zone check. *
  2564. * 10/05/1996 JLB : Allows checking for crushable blockages. *
  2565. *=============================================================================================*/
  2566. bool CellClass::Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone, MZoneType check) const
  2567. {
  2568. assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
  2569. /*
  2570. ** Flying objects always consider every cell passable since they can fly over everything.
  2571. */
  2572. if (loco == SPEED_WINGED) {
  2573. return(true);
  2574. }
  2575. /*
  2576. ** If a zone was specified, then see if the cell is in a legal
  2577. ** zone to allow movement.
  2578. */
  2579. if (zone != -1) {
  2580. if (zone != Zones[check]) {
  2581. return(false);
  2582. }
  2583. }
  2584. /*
  2585. ** Check the occupy bits for passable legality. If ignore infantry is true, then
  2586. ** don't consider infnatry.
  2587. */
  2588. int composite = Flag.Composite;
  2589. if (ignoreinfantry) {
  2590. composite &= 0xE0; // Drop the infantry occupation bits.
  2591. }
  2592. if (ignorevehicles) {
  2593. composite &= 0x5F; // Drop the vehicle/building bit.
  2594. }
  2595. if (composite != 0) {
  2596. return(false);
  2597. }
  2598. /*
  2599. ** Fetch the land type of the cell -- to be modified and used later.
  2600. */
  2601. LandType land = Land_Type();
  2602. /*
  2603. ** Walls are always considered to block the terrain for general passability
  2604. ** purposes unless this is a wall crushing check or if the checking object
  2605. ** can destroy walls.
  2606. */
  2607. OverlayTypeClass const * overlay = NULL;
  2608. if (Overlay != OVERLAY_NONE) {
  2609. overlay = &OverlayTypeClass::As_Reference(Overlay);
  2610. }
  2611. if (overlay != NULL && overlay->IsWall) {
  2612. if (check != MZONE_DESTROYER && (check != MZONE_CRUSHER || !overlay->IsCrushable)) {
  2613. return(false);
  2614. }
  2615. /*
  2616. ** Crushing objects consider crushable walls as clear rather than the
  2617. ** typical LAND_WALL setting.
  2618. */
  2619. land = LAND_CLEAR;
  2620. }
  2621. /*
  2622. ** See if the ground type is impassable to this locomotion type and if
  2623. ** so, return the error condition.
  2624. */
  2625. if (::Ground[land].Cost[loco] == 0) {
  2626. return(false);
  2627. }
  2628. /*
  2629. ** All checks passed, so this cell must be passable.
  2630. */
  2631. return(true);
  2632. }
  2633. /***********************************************************************************************
  2634. * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. *
  2635. * *
  2636. * This routine will examine this cell and if there is a bridge here, it will return *
  2637. * true. *
  2638. * *
  2639. * INPUT: none *
  2640. * *
  2641. * OUTPUT: bool; Is there a bridge located in this cell? *
  2642. * *
  2643. * WARNINGS: none *
  2644. * *
  2645. * HISTORY: *
  2646. * 07/30/1996 JLB : Created. *
  2647. *=============================================================================================*/
  2648. bool CellClass::Is_Bridge_Here(void) const
  2649. {
  2650. switch (TType) {
  2651. case TEMPLATE_BRIDGE1:
  2652. case TEMPLATE_BRIDGE1H:
  2653. case TEMPLATE_BRIDGE1D:
  2654. case TEMPLATE_BRIDGE2:
  2655. case TEMPLATE_BRIDGE2H:
  2656. case TEMPLATE_BRIDGE2D:
  2657. case TEMPLATE_BRIDGE_1A:
  2658. case TEMPLATE_BRIDGE_1B:
  2659. case TEMPLATE_BRIDGE_2A:
  2660. case TEMPLATE_BRIDGE_2B:
  2661. case TEMPLATE_BRIDGE_3A:
  2662. case TEMPLATE_BRIDGE_3B:
  2663. case TEMPLATE_BRIDGE_3C:
  2664. case TEMPLATE_BRIDGE_3D:
  2665. case TEMPLATE_BRIDGE_3E:
  2666. case TEMPLATE_BRIDGE_3F:
  2667. return(true);
  2668. }
  2669. return(false);
  2670. }
  2671. /***********************************************************************************************
  2672. * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. *
  2673. * *
  2674. * This checks the cell to see if Tiberium can grow at least one level in it. Tiberium can *
  2675. * grow only if there is Tiberium already present. It can only grow to a certain level *
  2676. * and then all further growth is suspended. *
  2677. * *
  2678. * INPUT: none *
  2679. * *
  2680. * OUTPUT: bool; Can Tiberium grow in this cell? *
  2681. * *
  2682. * WARNINGS: none *
  2683. * *
  2684. * HISTORY: *
  2685. * 08/14/1996 JLB : Created. *
  2686. *=============================================================================================*/
  2687. bool CellClass::Can_Tiberium_Grow(void) const
  2688. {
  2689. if (!Rule.IsTGrowth) return(false);
  2690. if (Session.Type != GAME_NORMAL) {
  2691. if(!Session.Options.Tiberium) return(false);
  2692. }
  2693. if (Land_Type() != LAND_TIBERIUM) return(false);
  2694. if (OverlayData >= 11) return(false);
  2695. if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false);
  2696. return(true);
  2697. }
  2698. /***********************************************************************************************
  2699. * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. *
  2700. * *
  2701. * This routine will examine the cell and determine if there is sufficient Tiberium *
  2702. * present that Tiberium spores will spread to adjacent cells. If the Tiberium level is *
  2703. * too low, spreading will not occur. *
  2704. * *
  2705. * INPUT: none *
  2706. * *
  2707. * OUTPUT: bool; Can Tiberium spread from this cell into adjacent cells? *
  2708. * *
  2709. * WARNINGS: This routine does not check to see if, in fact, there are any adjacent cells *
  2710. * available to spread to. *
  2711. * *
  2712. * HISTORY: *
  2713. * 08/14/1996 JLB : Created. *
  2714. *=============================================================================================*/
  2715. bool CellClass::Can_Tiberium_Spread(void) const
  2716. {
  2717. if (!Rule.IsTSpread) return(false);
  2718. if (Session.Type != GAME_NORMAL) {
  2719. if(!Session.Options.Tiberium) return(false);
  2720. }
  2721. if (Land_Type() != LAND_TIBERIUM) return(false);
  2722. if (OverlayData <= 6) return(false);
  2723. if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false);
  2724. return(true);
  2725. }
  2726. /***********************************************************************************************
  2727. * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. *
  2728. * *
  2729. * This routine will cause the tiberium to grow in the cell. *
  2730. * *
  2731. * INPUT: none *
  2732. * *
  2733. * OUTPUT: bool; Did Tiberium grow in the cell? *
  2734. * *
  2735. * WARNINGS: none *
  2736. * *
  2737. * HISTORY: *
  2738. * 08/14/1996 JLB : Created. *
  2739. *=============================================================================================*/
  2740. bool CellClass::Grow_Tiberium(void)
  2741. {
  2742. if (Can_Tiberium_Grow()) {
  2743. OverlayData++;
  2744. Redraw_Objects();
  2745. return(true);
  2746. }
  2747. return(false);
  2748. }
  2749. /***********************************************************************************************
  2750. * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. *
  2751. * *
  2752. * This routine will cause the Tiberium to spread from this cell into an adjacent (random) *
  2753. * cell. *
  2754. * *
  2755. * INPUT: none *
  2756. * *
  2757. * OUTPUT: bool; Did the Tiberium spread? *
  2758. * *
  2759. * WARNINGS: If there are no adjacent cells that the tiberium can spread to, then this *
  2760. * routine will fail. *
  2761. * *
  2762. * HISTORY: *
  2763. * 08/14/1996 JLB : Created. *
  2764. *=============================================================================================*/
  2765. bool CellClass::Spread_Tiberium(bool forced)
  2766. {
  2767. if (!forced) {
  2768. if (!Can_Tiberium_Spread()) return(false);
  2769. }
  2770. FacingType offset = Random_Pick(FACING_N, FACING_NW);
  2771. for (FacingType index = FACING_N; index < FACING_COUNT; index++) {
  2772. CellClass * newcell = Adjacent_Cell(index+offset);
  2773. if (newcell != NULL && newcell->Can_Tiberium_Germinate()) {
  2774. new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number());
  2775. newcell->OverlayData = 0;
  2776. return(true);
  2777. }
  2778. }
  2779. return(false);
  2780. }
  2781. /***********************************************************************************************
  2782. * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. *
  2783. * *
  2784. * This routine will examine the cell and determine if Tiberium can start growth in it. *
  2785. * *
  2786. * INPUT: none *
  2787. * *
  2788. * OUTPUT: bool; Can Tiberium grow in this cell? *
  2789. * *
  2790. * WARNINGS: none *
  2791. * *
  2792. * HISTORY: *
  2793. * 08/14/1996 JLB : Created. *
  2794. *=============================================================================================*/
  2795. bool CellClass::Can_Tiberium_Germinate(void) const
  2796. {
  2797. if (!Map.In_Radar(Cell_Number())) return(false);
  2798. if (Is_Bridge_Here()) return(false);
  2799. /*
  2800. ** Don't allow Tiberium to grow on a cell with a building unless that building is
  2801. ** invisible. In such a case, the Tiberium must grow or else the location of the
  2802. ** building will be revealed.
  2803. */
  2804. BuildingClass const * building = Cell_Building();
  2805. if (building != NULL && !building->Class->IsInvisible) return(false);
  2806. if (!Ground[Land_Type()].Build) return(false);
  2807. if (Overlay != OVERLAY_NONE) return(false);
  2808. return(true);
  2809. }
  2810. /*
  2811. ** Additions to CellClass to track visibility per-player. ST - 8/2/2019 2:59PM
  2812. **
  2813. **
  2814. **
  2815. **
  2816. **
  2817. */
  2818. /***********************************************************************************************
  2819. * CellClass::Set_Mapped -- Set the cell mapped for the given player *
  2820. * *
  2821. * *
  2822. * HISTORY: *
  2823. * 3/5/2019 3:09PM - ST *
  2824. *=============================================================================================*/
  2825. void CellClass::Set_Mapped(HousesType house, bool set)
  2826. {
  2827. int shift = (int) house;
  2828. if (set) {
  2829. IsMappedByPlayerMask |= (1 << shift);
  2830. } else {
  2831. IsMappedByPlayerMask &= ~(1 << shift);
  2832. }
  2833. }
  2834. /***********************************************************************************************
  2835. * CellClass::Set_Mapped -- Set the cell mapped for the given player *
  2836. * *
  2837. * *
  2838. * HISTORY: *
  2839. * 3/5/2019 3:09PM - ST *
  2840. *=============================================================================================*/
  2841. void CellClass::Set_Mapped(HouseClass *player, bool set)
  2842. {
  2843. if (player && player->Class) {
  2844. Set_Mapped(player->Class->House, set);
  2845. if (Session.Type == GAME_NORMAL && player->IsHuman) {
  2846. IsMapped = set; // Also set the regular flag in single player
  2847. }
  2848. }
  2849. }
  2850. /***********************************************************************************************
  2851. * CellClass::Is_Mapped -- Is the cell mapped for the given player *
  2852. * *
  2853. * *
  2854. * HISTORY: *
  2855. * 3/5/2019 3:13PM - ST *
  2856. *=============================================================================================*/
  2857. bool CellClass::Is_Mapped(HousesType house) const
  2858. {
  2859. int shift = (int) house;
  2860. return (IsMappedByPlayerMask & (1 << shift)) ? true : false;
  2861. }
  2862. /***********************************************************************************************
  2863. * CellClass::Is_Mapped -- Is the cell mapped for the given player *
  2864. * *
  2865. * *
  2866. * HISTORY: *
  2867. * 3/5/2019 3:13PM - ST *
  2868. *=============================================================================================*/
  2869. bool CellClass::Is_Mapped(HouseClass *player) const
  2870. {
  2871. if (player && player->Class) {
  2872. return Is_Mapped(player->Class->House);
  2873. }
  2874. return false;
  2875. }
  2876. /***********************************************************************************************
  2877. * CellClass::Set_Visible -- Set the cell visible for the given player *
  2878. * *
  2879. * *
  2880. * HISTORY: *
  2881. * 3/5/2019 3:16PM - ST *
  2882. *=============================================================================================*/
  2883. void CellClass::Set_Visible(HousesType house, bool set)
  2884. {
  2885. int shift = (int) house;
  2886. if (set) {
  2887. IsVisibleByPlayerMask |= (1 << shift);
  2888. } else {
  2889. IsVisibleByPlayerMask &= ~(1 << shift);
  2890. }
  2891. }
  2892. /***********************************************************************************************
  2893. * CellClass::Set_Visible -- Set the cell visible for the given player *
  2894. * *
  2895. * *
  2896. * HISTORY: *
  2897. * 3/5/2019 3:16PM - ST *
  2898. *=============================================================================================*/
  2899. void CellClass::Set_Visible(HouseClass *player, bool set)
  2900. {
  2901. if (player && player->Class) {
  2902. Set_Visible(player->Class->House, set);
  2903. if (Session.Type == GAME_NORMAL && player->IsHuman) {
  2904. IsVisible = set; // Also set the regular flag in single player. This is needed for rendering
  2905. }
  2906. }
  2907. }
  2908. /***********************************************************************************************
  2909. * CellClass::Is_Visible -- Is the cell visible for the given player *
  2910. * *
  2911. * *
  2912. * HISTORY: *
  2913. * 3/5/2019 3:16PM - ST *
  2914. *=============================================================================================*/
  2915. bool CellClass::Is_Visible(HousesType house) const
  2916. {
  2917. int shift = (int) house;
  2918. return (IsVisibleByPlayerMask & (1 << shift)) ? true : false;
  2919. }
  2920. /***********************************************************************************************
  2921. * CellClass::Is_Visible -- Is the cell visible for the given player *
  2922. * *
  2923. * *
  2924. * HISTORY: *
  2925. * 3/5/2019 3:16PM - ST *
  2926. *=============================================================================================*/
  2927. bool CellClass::Is_Visible(HouseClass *player) const
  2928. {
  2929. if (player && player->Class) {
  2930. return Is_Visible(player->Class->House);
  2931. }
  2932. return false;
  2933. }
  2934. bool CellClass::Is_Jamming(HousesType house) const
  2935. {
  2936. int shift = (int)house;
  2937. return (Jammed & (1 << shift)) ? true : false;
  2938. }
  2939. bool CellClass::Is_Jamming(HouseClass *player) const
  2940. {
  2941. if (player && player->Class) {
  2942. return Is_Jamming(player->Class->House);
  2943. }
  2944. return false;
  2945. }
  2946. void CellClass::Override_Land_Type(LandType type)
  2947. {
  2948. OverrideLand = type;
  2949. }