CELL.CPP 128 KB

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