realm.cpp 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. // realm.cpp
  19. // Project: Postal
  20. //
  21. // This module impliments the CRealm class.
  22. //
  23. // History:
  24. // 12/31/96 MJR Started.
  25. //
  26. // 01/20/97 MJR Changed Update loop to save a temporary iterator so
  27. // objects that delete themselves during the update
  28. // loop will still have a valid iterator to get the next
  29. // item in the Things list.
  30. //
  31. // 01/23/97 JMI Added instantiation of static member ms_asAttribToLayer[].
  32. //
  33. // 01/27/97 JMI Now 4 bits are used from the attribute to determine the
  34. // proper layer for dudes.
  35. //
  36. // 01/28/97 JMI Added ms_apszLayerNames[] to access the names of the
  37. // layers.
  38. //
  39. // 01/29/97 JMI Reordered ms_apszLayerNames to match the new order in the
  40. // CRealm::Layers enum (fixed to the way Steve is doing it
  41. // (the opaque layers are now before alpha equivalents)).
  42. //
  43. // 02/03/97 JMI The Load(char*) will now Open a SAK with the same base name
  44. // as the *.rlm file, if one exists. If it does not exist,
  45. // m_resmgr's BasePath is set to the No SAK Dir. Once the
  46. // load is complete, no more new resources are to be requested
  47. // from m_resmgr. To enfore this, m_resmgr's base path is
  48. // set to a condescending message. If you are not careful, it
  49. // may taunt you a second time-a!
  50. //
  51. // 02/04/97 BRH Temporarily (or permanently) took out timeout check in
  52. // Startup because it caused problems debugging startup
  53. // code.
  54. //
  55. // 02/04/97 BRH Added ms_pCurrentNavNet to the realm, moving it from
  56. // its previous position in gameedit.
  57. //
  58. // 02/10/97 JMI Now all loops iterate to the next iterator in the STL list
  59. // before dooing iterative processing just in case the current
  60. // iterator is invalidated during processing.
  61. //
  62. // 02/13/97 JMI Added hood pointer to CRealm.
  63. //
  64. // 02/17/97 JMI Added set lists to CRealm. Now there is a list for each
  65. // set of Things. The sets are enumerated in CThing. Now
  66. // new'ed in CRealm() and delete'ed in ~CRealm().
  67. //
  68. // 02/18/97 JMI Changed uses of CThing::ThingSet to CThing::Things.
  69. //
  70. // 02/19/97 JMI Got rid of stuff regarding old collision sets and added
  71. // new CSmashitorium usage.
  72. //
  73. // 02/23/97 JMI Added m_iNext and m_bUpdating so RemoveThing() and
  74. // AddThing() can make the necessary adjustments to the next
  75. // iterator processed in the Update() loop.
  76. //
  77. // 02/23/97 MJR Added call to class-based Preload() in CRealm::Load().
  78. //
  79. // 02/24/97 JMI Added same protection for Render() that we implemented for
  80. // Update() on 02/23/97.
  81. //
  82. // 02/25/97 JMI The way I had done the parens for *m_iNext++ for Render()
  83. // and Update() was scaring me so I separated it out more.
  84. //
  85. // 03/13/97 JMI Load() now passes the file version number to the CThing
  86. // Load()'s.
  87. //
  88. // 03/13/97 JMI Now, instead of the file's version number having to
  89. // exactly match FileVersion, it must just be less than or
  90. // equal. If a file's version is greater than the current
  91. // version known by the code, the CThing that caused the
  92. // version number to increase will have more, less, or
  93. // different data to load that it cannot possibly know about.
  94. //
  95. // 03/25/97 JMI Load() no longer opens a SAK with the same title as the
  96. // .rlm file (This is now opened by the CHood).
  97. //
  98. // 04/09/97 BRH Added RMultiGrid for the multi layer attribute maps, but
  99. // haven't taken out the RAttributeMap yet so that the
  100. // game will continue to work until we completely switch over.
  101. //
  102. // 04/16/97 BRH Added Jon's template class CListNode head and tail nodes
  103. // to CRealm to replace the STL containers that provided the
  104. // linked lists of CThings. Once the new methods are proven
  105. // to work, we will get rid of the m_everthing and m_apthings
  106. // arrays of STL lists.
  107. //
  108. // 04/17/97 BRH Took timeout check out of Shutdown and Startup (it was
  109. // already commented out of Startup). The 1 second timeout
  110. // when calling shutdown caused large levels like parade
  111. // to abort before saving everything.
  112. //
  113. // 04/18/97 JMI Now Suspend() suspends the game time and Resume() resumes
  114. // it. This should alleviate the need for the CThing derived
  115. // objects to do their own time compensation.
  116. //
  117. // 04/21/97 JMI Added m_sNumSuspends and IsSuspended().
  118. //
  119. // 05/04/97 BRH Removed STL references since the CListNode lists seem
  120. // to be working properly.
  121. //
  122. // 05/13/97 JMI Added IsPathClear() map functions to determine if a path
  123. // is clear of terrain that cannot be surmounted.
  124. //
  125. // 05/16/97 JMI Changed layer bits to be 8 bits (instead of 4). This
  126. // caused the REALM_ATTR_EFFECT_MASK to lose 4 bits and the
  127. // REALM_ATTR_LIGHT_BIT to move up 4 bits (0x0010 to 0x0100).
  128. // Also, the table ms_asAttribToLayer to be increased from
  129. // 16 entries to 256 entries.
  130. // Also, removed GetLayerFromAttrib() which was left over from
  131. // the pre RMultiGrid days.
  132. //
  133. // 05/17/97 JMI In EditUpdate() no argument list was supplied in the line:
  134. // pCur->m_powner->EditUpdate;
  135. // Added a set of parens.
  136. //
  137. // 05/26/97 JMI Added DrawStatus() function.
  138. //
  139. // 05/27/97 JMI Changed format of DrawStatus() output string.
  140. //
  141. // 05/29/97 JMI Changed occurences of m_pHeightMap to m_pTerrainMap.
  142. // Changed occurences of m_pAttrMap to m_pLayerMap.
  143. // Also, removed occurences of m_pAttribMap.
  144. // Also, added CreateLayerMap() that creates the attribute
  145. // to layer map now that it is so huge.
  146. //
  147. // 06/04/97 BRH Turned off drawing lines in IsPathClear function.
  148. //
  149. // 06/09/97 JMI Changed wording of realm status.
  150. // Also, Suspend() and Resume() pause and resume the sound.
  151. //
  152. // 06/10/97 JMI Now Resume() does not let you 'over'-resume.
  153. //
  154. // 06/17/97 MJR Moved some vars that were CPylon statics into the realm
  155. // so they could be instantiated on a realm-by-realm basis.
  156. //
  157. // 06/20/97 JRD Replaced code needed to manage allocation and deallocation
  158. // for the new CSmashatorium
  159. //
  160. // 06/26/97 JMI Moved Map2DTo3D from reality.h to here.
  161. // When converting to 2D, Y is now scaled based on the view
  162. // angle. This was not applied to Z. It could be a bug, but
  163. // it just looked better this way. I suspect it is a
  164. // bug/feature of the way most of the code was written
  165. // (probably a product of being more aware of the noticable
  166. // difference Y coord has when mapped to 2D when comparing 45
  167. // to 90 degree type levels while originally designing/coding
  168. // CThings, the editor, and CScene).
  169. //
  170. // 06/28/97 JMI Moved attribute access functions from realm.h to realm.cpp
  171. // while we're getting all the conversion from 3D to the X/Z
  172. // plane stuff right. Compiling the entire project for any
  173. // tweak just doesn't sound very fun. Hopefully, though,
  174. // there'll won't be many tweaks.
  175. // Also, added ScaleZ() functions to scale just Z (useful
  176. // for attribute map access).
  177. // Changed references to GetWorldRotX() to GetRealmRotX().
  178. //
  179. // 06/29/97 JMI Added version of ScaleZ() that takes shorts.
  180. // Changed both versions of ScaleZ() to MapZ3DtoY2D()
  181. // and added two versions of MapY2DtoZ3D().
  182. //
  183. // 06/30/97 JMI Now uses CRealm's new GetRealmWidth() and *Height()
  184. // for dimensions of realm's X/Z plane.
  185. //
  186. // 06/30/97 JMI Added bCheckExtents parm to IsPathClear(). See proto for
  187. // details.
  188. //
  189. // 07/01/97 JMI Added MapY2DtoY3D() and MapY3DtoY2D().
  190. // Also, GetHeight() now scales the height into realm
  191. // coordinates.
  192. //
  193. // 07/01/97 JMI IsPathClear() had 'step-up' logic (TM) for land based
  194. // things (like doofuses) that did not work for hovering
  195. // things (like missiles). Fixed.
  196. //
  197. // 07/01/97 JMI Changed the file version for the Hood so it can load and
  198. // save whether to use the attribute map heights with or
  199. // without scaling based on the view angle.
  200. // Also, added function to scale heights that checks the
  201. // hood value.
  202. //
  203. // 07/07/97 BRH Added EditModify function to process the realm properties
  204. // dialog box where you can select the scoring mode and
  205. // play mode for the realm.
  206. //
  207. // 07/08/97 BRH Added loading and saving of properties as of version 27.
  208. //
  209. // 07/09/97 JMI Added function to get the full path for a 2D resource
  210. // based on the current hood setting for 'Use top-view 2Ds'.
  211. //
  212. // 07/09/97 JMI Moved m_s2dResPathIndex from CHood to CRealm b/c of order
  213. // issues when loading.
  214. //
  215. // 07/10/97 BRH Added cases to IsEndOfLevelGoalMet for the different
  216. // scoring modes.
  217. //
  218. // 07/11/97 JMI Minor change in IsEndOfLevelGoalMet() to make it so, if
  219. // there are zero births, the goal is considered met. A
  220. // special case for example levels that have no enemies.
  221. //
  222. // 07/11/97 BRH Added time calculations here for expiration date. Checked
  223. // in again to update source safe with correct date.
  224. //
  225. // 07/12/97 JMI Added m_bMultiplayer, which signifies whether this is
  226. // a multi or single player game.
  227. // Also, added Init(). See proto for details.
  228. // Moved things that were being initialized both in CRealm()
  229. // and in Clear() to Init() which is called by these two
  230. // functions.
  231. // Also, moved the initialization of the population statistics
  232. // into Init() even though they were only being done on a
  233. // Clear(). This might be bad but it did not seem like it
  234. // could hurt.
  235. //
  236. // 07/12/97 BRH Made minor change to the EditModify dialog so that the
  237. // seconds always appears as two digits.
  238. //
  239. // 07/14/97 JMI Moved initialization of Pylon stuff into Init() and also
  240. // no m_ucNextPylonId is 1 instead of 0.
  241. //
  242. // 07/14/97 BRH Fixed a bug that caused the score timer to always count up.
  243. // Fixed problems with the goal stopping conditions and added
  244. // m_sFlagbaseCaptured to keep track of flags that were
  245. // successfully returned to their base.
  246. //
  247. // 07/15/97 BRH Added the end of level key flag as a parameter to
  248. // the end of level goal check.
  249. //
  250. // 07/16/97 BRH Fixed a problem with standard scoring mode on levels
  251. // that didn't have any enemies, they would end right
  252. // away using the new method of checking the end of
  253. // the level.
  254. //
  255. // 07/17/97 BRH Added the time adjustment to the mac version.
  256. //
  257. // 07/27/97 BRH Added m_lScoreInitialTime and set it to the same
  258. // initial value as m_lScoreDisplayTimer. This was required
  259. // in order to calculate the time elapsed for the high
  260. // scores.
  261. //
  262. // 07/30/97 BRH Added a string to hold the path of the realm which will
  263. // be used by the high score function to identify the
  264. // current realm file.
  265. //
  266. // 08/05/97 JMI Now pauses only active sounds so that new sounds can
  267. // continue to play while the realm is suspended.
  268. //
  269. // 08/05/97 JMI Added CRealm::Flags struct and an instance in CRealm,
  270. // m_flags.
  271. //
  272. // 08/08/97 JMI Now displays a useful message about the thing that failed
  273. // to load should one do so.
  274. //
  275. // 08/09/97 JMI Added progress callback and components. There is now a
  276. // callback member that is called (when non-zero) that passes
  277. // the number of items processed so far and the total number of
  278. // items to process. The return value of the callback dictates
  279. // whether to proceed with the operation. Although, this is
  280. // meant to be generic so we can use it for any type of
  281. // process, it is currently only used by Load() and Save().
  282. //
  283. // 08/11/97 BRH Changed both Capture the flag goals to flagbases captured
  284. // rather than flags captured.
  285. //
  286. // 08/19/97 BRH Fixed end of level goal for the checkpoint scoring so
  287. // if a specific number of flags is not set in the realm
  288. // file, it will just continue until the time runs out.
  289. //
  290. // 08/20/97 JMI Made ms_apsz2dResPaths[] a static class member (was just
  291. // defined at realm.cpp file scope) and added enum macro for
  292. // number of elements.
  293. //
  294. // 08/28/97 BRH Changed level goals for challenge scoring modes to use
  295. // the population numbers rather than just the hostile numbers
  296. // so that the victims count also.
  297. //
  298. // 08/30/97 JMI IsEndOfLevelGoalMet() now allows a player to go on in
  299. // Timed and Checkpoint, if they hit the 'next level' key.
  300. //
  301. // 08/30/97 JMI Now initializes hostile deaths and population deaths in
  302. // Startup().
  303. //
  304. // 09/02/97 JMI Now resets all population statistics in Startup().
  305. //
  306. // 09/04/97 BRH Realm::Load now attempts several paths. It first tries
  307. // the path passed in for the case where the user
  308. // specified a full path using the open dialog. Then it
  309. // tries the HD path and then the CD path if those fail.
  310. // This would allow us to send updated realm files
  311. // that could be loaded instead of the ones on the CD
  312. // just by putting them in the mirror path on the HD.
  313. //
  314. // 09/07/97 BRH Changed end of level goal to never end the MPFrag if
  315. // the kills goal is set to zero. That will mean no
  316. // frag limit.
  317. //
  318. // 09/09/97 JMI Now checks number of flags against the actual number of
  319. // flags when using CheckPoint.
  320. //
  321. // 09/11/97 MJR Added DoesFileExist(), which uses the same logic as Load()
  322. // to determine if a realm file exists. Also added Open()
  323. // as a common function for DoesFileExist() and Load() to use.
  324. //
  325. // 09/12/97 JMI Now, if ENABLE_PLAY_SPECIFIC_REALMS_ONLY is defined, we
  326. // try to load the .RLM out of memory using
  327. // GetMemFileResource().
  328. //
  329. // 09/12/97 MJR Now Open() will detect an empty filename as an error,
  330. // which avoids the ASSERT() that the Microsoft Runtime
  331. // library does when you pass open() and empty string.
  332. //
  333. // 11/21/97 JMI Added bCoopMode flag indicating whether we're in cooperative
  334. // or deathmatch mode when in multiplayer.
  335. //
  336. ////////////////////////////////////////////////////////////////////////////////
  337. #define REALM_CPP
  338. #include "RSPiX.h"
  339. #include "realm.h"
  340. #include "game.h"
  341. #include "reality.h"
  342. #include "score.h"
  343. #include <time.h>
  344. #include "MemFileFest.h"
  345. //#define RSP_PROFILE_ON
  346. #include "ORANGE/Debug/profile.h"
  347. ////////////////////////////////////////////////////////////////////////////////
  348. // Macros/types/etc.
  349. ////////////////////////////////////////////////////////////////////////////////
  350. // Sets the specified value into the data pointed, if the ptr is not NULL.
  351. #define SET(ptr, val) ( (ptr != NULL) ? *ptr = val : val)
  352. // Time, in ms, between status updates.
  353. #define STATUS_UPDATE_INTERVAL 1000
  354. #define STATUS_PRINT_X 0
  355. #define STATUS_PRINT_Y 0
  356. #define STATUS_FONT_SIZE 19
  357. #define STATUS_FONT_FORE_INDEX 2
  358. #define STATUS_FONT_BACK_INDEX 0
  359. #define STATUS_FONT_SHADOW_INDEX 0
  360. // Determines the number of elements in the passed array at compile time.
  361. #define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]) )
  362. #define MAX_SMASH_DIAMETER 20
  363. #define REALM_DIALOG_FILE "res/editor/realm.gui"
  364. #define TIMER_MIN_EDIT_ID 201
  365. #define TIMER_SEC_EDIT_ID 202
  366. #define KILLS_NUM_EDIT_ID 203
  367. #define KILLS_PCT_EDIT_ID 204
  368. #define FLAGS_NUM_EDIT_ID 205
  369. #define SCORE_MODE_LB_ID 99
  370. #define SCORE_MODE_LIST_BASE 100
  371. ////////////////////////////////////////////////////////////////////////////////
  372. // Variables/data
  373. ////////////////////////////////////////////////////////////////////////////////
  374. // File counter
  375. short CRealm::ms_sFileCount;
  376. // Maps the layer portion of an attribute to the appropriate
  377. // layer.
  378. // Now that this table is 32K, we generate it table at run time to avoid adding
  379. // an extra 32K of uncompressable space to the exe.
  380. short CRealm::ms_asAttribToLayer[CRealm::LayerAttribMask + 1];
  381. // Names of layers. Use Layer enum values to index.
  382. char* CRealm::ms_apszLayerNames[TotalLayers] =
  383. {
  384. "Background",
  385. "Sprite1",
  386. "Opaque1",
  387. "Sprite2",
  388. "Alpha1",
  389. "Sprite3",
  390. "Opaque2",
  391. "Sprite4",
  392. "Alpha2",
  393. "Sprite5",
  394. "Opaque3",
  395. "Sprite6",
  396. "Alpha3",
  397. "Sprite7",
  398. "Opaque4",
  399. "Sprite8",
  400. "Alpha4",
  401. "Sprite9",
  402. "Opaque5",
  403. "Sprite10",
  404. "Alpha5",
  405. "Sprite11",
  406. "Opaque6",
  407. "Sprite12",
  408. "Alpha6",
  409. "Sprite13",
  410. "Opaque7",
  411. "Sprite14",
  412. "Alpha7",
  413. "Sprite15",
  414. "Opaque8",
  415. "Sprite16",
  416. };
  417. // These are the various 2d paths that we currently support. Eventually, if
  418. // there's more than two, this can be presented in listbox form (instead of
  419. // checkbox form).
  420. char* CRealm::ms_apsz2dResPaths[Num2dPaths] =
  421. {
  422. "2d/Top/",
  423. "2d/Side/",
  424. "2d/SideBright/",
  425. };
  426. // Used for CRealm oriented drawing tasks.
  427. static RPrint ms_print;
  428. ////////////////////////////////////////////////////////////////////////////////
  429. // Function prototypes
  430. ////////////////////////////////////////////////////////////////////////////////
  431. ////////////////////////////////////////////////////////////////////////////////
  432. // Non-member functions.
  433. ////////////////////////////////////////////////////////////////////////////////
  434. ///////////////////////////////////////////////////////////////////////////////
  435. // Maps a 3D coordinate onto the viewing plane provided the view angle
  436. // (~angle of projection).
  437. ///////////////////////////////////////////////////////////////////////////////
  438. template <class TIn, class TOut>
  439. void Map3Dto2D( // Returns nothing.
  440. TIn tX, // In.
  441. TIn tY, // In.
  442. TIn tZ, // In.
  443. TOut* ptX, // Out.
  444. TOut* ptY, // Out.
  445. short sViewAngle) // In: View angle in degrees.
  446. {
  447. *ptX = tX;
  448. *ptY = SINQ[sViewAngle] * tZ - COSQ[sViewAngle] * tY;
  449. }
  450. ///////////////////////////////////////////////////////////////////////////////
  451. // Scales a Z coordinate onto the viewing plane provided the
  452. // view angle (~angle of projection).
  453. ///////////////////////////////////////////////////////////////////////////////
  454. template <class TIn, class TOut>
  455. void MapZ3DtoY2D( // Returns nothing.
  456. TIn tZIn, // In.
  457. TOut* ptYOut, // Out.
  458. short sViewAngle) // In: View angle in degrees.
  459. {
  460. ASSERT(sViewAngle >= 0 && sViewAngle < 360);
  461. *ptYOut = SINQ[sViewAngle] * tZIn;
  462. }
  463. ///////////////////////////////////////////////////////////////////////////////
  464. // Scales a Y coordinate from the viewing plane provided the
  465. // view angle (~angle of projection).
  466. ///////////////////////////////////////////////////////////////////////////////
  467. template <class TIn, class TOut>
  468. void MapY2DtoZ3D( // Returns nothing.
  469. TIn tYIn, // In.
  470. TOut* ptZOut, // Out.
  471. short sViewAngle) // In: View angle in degrees.
  472. {
  473. ASSERT(sViewAngle >= 0 && sViewAngle < 360);
  474. REAL rSin = SINQ[sViewAngle];
  475. if (rSin != 0.0)
  476. {
  477. *ptZOut = tYIn / rSin;
  478. }
  479. else
  480. {
  481. *ptZOut = 0;
  482. }
  483. }
  484. ///////////////////////////////////////////////////////////////////////////////
  485. // Scales a Y coordinate onto the viewing plane provided the
  486. // view angle (~angle of projection).
  487. ///////////////////////////////////////////////////////////////////////////////
  488. template <class TIn, class TOut>
  489. void MapY3DtoY2D( // Returns nothing.
  490. TIn tYIn, // In.
  491. TOut* ptYOut, // Out.
  492. short sViewAngle) // In: View angle in degrees.
  493. {
  494. ASSERT(sViewAngle >= 0 && sViewAngle < 360);
  495. *ptYOut = COSQ[sViewAngle] * tYIn;
  496. }
  497. ///////////////////////////////////////////////////////////////////////////////
  498. // Scales a Y coordinate from the viewing plane provided the
  499. // view angle (~angle of projection).
  500. ///////////////////////////////////////////////////////////////////////////////
  501. template <class TIn, class TOut>
  502. void MapY2DtoY3D( // Returns nothing.
  503. TIn tYIn, // In.
  504. TOut* ptYOut, // Out.
  505. short sViewAngle) // In: View angle in degrees.
  506. {
  507. ASSERT(sViewAngle >= 0 && sViewAngle < 360);
  508. REAL rCos = COSQ[sViewAngle];
  509. if (rCos != 0.0)
  510. {
  511. *ptYOut = tYIn / rCos;
  512. }
  513. else
  514. {
  515. *ptYOut = 0;
  516. }
  517. }
  518. ////////////////////////////////////////////////////////////////////////////////
  519. // Default (and only) constructor
  520. ////////////////////////////////////////////////////////////////////////////////
  521. CRealm::CRealm()
  522. {
  523. time_t lTime;
  524. time(&lTime);
  525. #ifndef WIN32
  526. // Mac version time adjusment back to UTC time.
  527. lTime -= ((365 * 70UL) + 17) * 24 * 60 * 60; // time_fudge 1900->1970
  528. #endif
  529. g_lRegValue = lTime - g_lRegTime;
  530. g_lExpValue = g_lExpTime - lTime;
  531. CreateLayerMap();
  532. // Setup render object (it's constructor was automatically called)
  533. m_scene.SetLayers(TotalLayers);
  534. // Set attribute map to a safe (but invalid) value
  535. m_pTerrainMap = 0;
  536. m_pLayerMap = 0;
  537. m_pTriggerMap = 0;
  538. m_pTriggerMapHolder = 0;
  539. // Set Hood ptr to a safe (but invalid) value.
  540. m_phood = NULL;
  541. /*
  542. // Create a container of things for each element in the array
  543. short s;
  544. for (s = 0; s < CThing::TotalIDs; s++)
  545. m_apthings[s] = new CThing::Things;
  546. */
  547. // Initialize current Navigation Net pointer
  548. m_pCurrentNavNet = NULL;
  549. // Not currently updating.
  550. m_bUpdating = false;
  551. // Initialize dummy nodes for linked lists of CThings
  552. m_everythingHead.m_pnNext = &m_everythingTail;
  553. m_everythingHead.m_pnPrev = NULL;
  554. m_everythingHead.m_powner = NULL;
  555. m_everythingTail.m_pnNext = NULL;
  556. m_everythingTail.m_pnPrev = &m_everythingHead;
  557. m_everythingTail.m_powner = NULL;
  558. short i;
  559. for (i = 0; i < CThing::TotalIDs; i++)
  560. {
  561. m_aclassHeads[i].m_pnNext = &(m_aclassTails[i]);
  562. m_aclassHeads[i].m_pnPrev = NULL;
  563. m_aclassHeads[i].m_powner = NULL;
  564. m_aclassTails[i].m_pnNext = NULL;
  565. m_aclassTails[i].m_pnPrev = &(m_aclassHeads[i]);
  566. m_aclassTails[i].m_powner = NULL;
  567. m_asClassNumThings[i] = 0;
  568. }
  569. m_sNumThings = 0;
  570. m_sNumSuspends = 0;
  571. // Setup print.
  572. ms_print.SetFont(STATUS_FONT_SIZE, &g_fontBig);
  573. ms_print.SetColor(
  574. STATUS_FONT_FORE_INDEX,
  575. STATUS_FONT_BACK_INDEX,
  576. STATUS_FONT_SHADOW_INDEX);
  577. // Initialize flags to defaults for safety.
  578. // This might be a bad idea if we want to guarantee they get set in which
  579. // case we should maybe set them to absurd values.
  580. m_flags.bMultiplayer = false;
  581. m_flags.bCoopMode = false;
  582. m_flags.bEditing = false;
  583. m_flags.bEditPlay = false;
  584. m_flags.sDifficulty = 5;
  585. m_fnProgress = NULL;
  586. m_bPressedEndLevelKey = false;
  587. // Initialize.
  588. Init();
  589. }
  590. ////////////////////////////////////////////////////////////////////////////////
  591. // Destructor
  592. ////////////////////////////////////////////////////////////////////////////////
  593. CRealm::~CRealm()
  594. {
  595. // Clear the realm (in case this hasn't been done yet)
  596. Clear();
  597. // Double-check to be sure there's nothing left
  598. if (m_everythingHead.m_pnNext != &m_everythingTail)
  599. TRACE("CRealm::~CRealm(): There are still %d CThing's in this realm!\n", m_sNumThings);
  600. }
  601. ////////////////////////////////////////////////////////////////////////////////
  602. // This will set all values that are to be set on construction and during
  603. // a Clear(). This is called by CRealm() and Clear(). This gives us one
  604. // spot to implement these, rather than having to do it twice.
  605. ////////////////////////////////////////////////////////////////////////////////
  606. void CRealm::Init(void) // Returns nothing. Cannot fail.
  607. {
  608. m_dKillsPercentGoal = 80.0;
  609. m_sKillsGoal = 0;
  610. m_sFlagsGoal = 0;
  611. m_lScoreTimeDisplay = 0;
  612. m_lScoreInitialTime = 0;
  613. m_ScoringMode = Standard;
  614. m_sFlagsCaptured = 0;
  615. m_sFlagbaseCaptured = 0;
  616. // Reset the Population statistics
  617. m_sPopulationBirths = 0;
  618. m_sPopulation = 0;
  619. m_sPopulationDeaths = 0;
  620. m_sHostiles = 0;
  621. m_sHostileBirths = 0;
  622. m_sHostileKills = 0;
  623. // Initial index for 2D resoruce paths array.
  624. m_s2dResPathIndex = 1;
  625. // Reset timer.
  626. m_lLastStatusDrawTime = -STATUS_UPDATE_INTERVAL;
  627. // Pylon stuff
  628. short i;
  629. for (i=0;i < 256;i++)
  630. m_asPylonUIDs[i] = 0; // clear the Pylon UIDs!
  631. m_sNumPylons = 0;
  632. m_ucNextPylonID = 1;
  633. }
  634. ////////////////////////////////////////////////////////////////////////////////
  635. // Clear the realm
  636. ////////////////////////////////////////////////////////////////////////////////
  637. void CRealm::Clear()
  638. {
  639. // Shutdown the realm (in case this hasn't been done yet)
  640. Shutdown();
  641. // Destroy all the objects. We use a copy of the iterator to avoid being
  642. // stuck with an invalid iterator once the object is gone.
  643. CListNode<CThing>* pCur;
  644. CListNode<CThing>* pNext = m_everythingHead.m_pnNext;
  645. while (pNext->m_powner != NULL)
  646. {
  647. pCur = pNext;
  648. pNext = pNext->m_pnNext;
  649. delete (pCur->m_powner);
  650. }
  651. // Clear out any sprites that didn't already remove themselves
  652. m_scene.RemoveAllSprites();
  653. // Clear out any residue IDs. Shouldn't need to, but . . .
  654. m_idbank.Reset();
  655. // Reset smashatorium.
  656. #ifdef NEW_SMASH // need to become final at some point...
  657. m_smashatorium.Destroy();
  658. #else
  659. m_smashatorium.Reset();
  660. #endif
  661. // Re-Initialize.
  662. Init();
  663. }
  664. ////////////////////////////////////////////////////////////////////////////////
  665. // Determine if specified file exists according to same rules used by Load()
  666. ////////////////////////////////////////////////////////////////////////////////
  667. // static
  668. bool CRealm::DoesFileExist( // Returns true if file exists, false otherwise
  669. const char* pszFileName) // In: Name of file
  670. {
  671. bool bResult = false;
  672. RFile file;
  673. if (Open(pszFileName, &file) == 0)
  674. {
  675. file.Close();
  676. bResult = true;
  677. }
  678. return bResult;
  679. }
  680. ////////////////////////////////////////////////////////////////////////////////
  681. // Open the specified realm file
  682. ////////////////////////////////////////////////////////////////////////////////
  683. // static
  684. short CRealm::Open( // Returns 0 if successfull, non-zero otherwise
  685. const char* pszFileName, // In: Name of file to load from
  686. RFile* pfile) // I/O: RFile to be used
  687. {
  688. short sResult = 0;
  689. if (strlen(pszFileName) > 0)
  690. {
  691. #if !defined(ENABLE_PLAY_SPECIFIC_REALMS_ONLY)
  692. // Try the given path first since it may already have a full path in the
  693. // case of loading a level, then try the path with the HD path prepended,
  694. // then try the CD path.
  695. sResult = pfile->Open(rspPathToSystem((char*)pszFileName), "rb", RFile::LittleEndian);
  696. if (sResult != 0)
  697. {
  698. char pszFullPath[RSP_MAX_PATH];
  699. strcpy(pszFullPath, FullPathHD((char*) pszFileName));
  700. sResult = pfile->Open((char*)pszFullPath, "rb", RFile::LittleEndian);
  701. if (sResult != 0)
  702. {
  703. strcpy(pszFullPath, FullPathCD((char*) pszFileName));
  704. sResult = pfile->Open((char*)pszFullPath, "rb", RFile::LittleEndian);
  705. }
  706. }
  707. #else
  708. // There's only one place it can possibly be and, if it's not there,
  709. // no realm for you!
  710. sResult = GetMemFileResource(pszFileName, RFile::LittleEndian, pfile);
  711. #endif // ENABLE_PLAY_SPECIFIC_REALMS_ONLY
  712. }
  713. else
  714. {
  715. sResult = -1;
  716. TRACE("CRealm::Open(): Empty file name!\n");
  717. }
  718. return sResult;
  719. }
  720. ////////////////////////////////////////////////////////////////////////////////
  721. // Load the realm
  722. ////////////////////////////////////////////////////////////////////////////////
  723. short CRealm::Load( // Returns 0 if successfull, non-zero otherwise
  724. const char* pszFileName, // In: Name of file to load from
  725. bool bEditMode) // In: Use true for edit mode, false otherwise
  726. {
  727. short sResult = 0;
  728. // Copy the name to use later for high score purposes
  729. m_rsRealmString = pszFileName;
  730. // Open file
  731. RFile file;
  732. sResult = Open(pszFileName, &file);
  733. if (sResult == 0)
  734. {
  735. // Use alternate load to do most of the work
  736. sResult = Load(&file, bEditMode);
  737. file.Close();
  738. }
  739. else
  740. {
  741. sResult = -1;
  742. TRACE("CRealm::Load(): Couldn't open file: %s !\n", pszFileName);
  743. }
  744. return sResult;
  745. }
  746. ////////////////////////////////////////////////////////////////////////////////
  747. // Load realm
  748. ////////////////////////////////////////////////////////////////////////////////
  749. short CRealm::Load( // Returns 0 if successfull, non-zero otherwise
  750. RFile* pFile, // In: File to load from
  751. bool bEditMode) // In: Use true for edit mode, false otherwise
  752. {
  753. short sResult = 0;
  754. // Clear the realm before loading this new stuff
  755. Clear();
  756. // Increment file count
  757. ms_sFileCount++;
  758. // Read & validate file ID
  759. ULONG ulFileID;
  760. if (pFile->Read(&ulFileID) == 1)
  761. {
  762. if (ulFileID == CRealm::FileID)
  763. {
  764. // Read & validate file version
  765. ULONG ulFileVersion;
  766. if (pFile->Read(&ulFileVersion) == 1)
  767. {
  768. // If a known version . . .
  769. if (ulFileVersion <= CRealm::FileVersion)
  770. {
  771. // Read properties for the realm
  772. switch (ulFileVersion)
  773. {
  774. default:
  775. case 30:
  776. pFile->Read(&m_s2dResPathIndex);
  777. case 29:
  778. pFile->Read(&m_ScoringMode);
  779. case 28:
  780. case 27:
  781. {
  782. short sUp;
  783. pFile->Read(&m_lScoreTimeDisplay);
  784. m_lScoreInitialTime = m_lScoreTimeDisplay;
  785. pFile->Read(&sUp);
  786. if (sUp == 1)
  787. m_bScoreTimerCountsUp = true;
  788. else
  789. m_bScoreTimerCountsUp = false;
  790. pFile->Read(&m_sKillsGoal);
  791. pFile->Read(&m_sFlagsGoal);
  792. pFile->Read(&m_dKillsPercentGoal);
  793. break;
  794. }
  795. case 26:
  796. case 25:
  797. case 24:
  798. case 23:
  799. case 22:
  800. case 21:
  801. case 20:
  802. case 19:
  803. case 18:
  804. case 17:
  805. case 16:
  806. case 15:
  807. case 14:
  808. case 13:
  809. case 12:
  810. case 11:
  811. case 10:
  812. case 9:
  813. case 8:
  814. case 7:
  815. case 6:
  816. case 5:
  817. case 4:
  818. case 3:
  819. case 2:
  820. case 1:
  821. case 0:
  822. break;
  823. }
  824. // Scan through class info structs and for each non-0 preload func,
  825. // call it to give that class a chance to preload stuff. The intention
  826. // is to give classes whose objects don't exist at the start of a level
  827. // a chance to preload resources now rather than during gameplay.
  828. for (short sPre = 0; sPre < CThing::TotalIDs; sPre++)
  829. {
  830. CThing::FuncPreload func = CThing::ms_aClassInfo[sPre].funcPreload;
  831. if (func != 0)
  832. {
  833. sResult = (*func)(this);
  834. if (sResult != 0)
  835. {
  836. TRACE("CRealm::Load(): Error reported by Preload() for CThing class ID = %hd\n", (short)sPre);
  837. break;
  838. }
  839. }
  840. }
  841. if (sResult == 0)
  842. {
  843. // Read number of things that were written to file (could be 0!)
  844. short sCount;
  845. if (pFile->Read(&sCount) == 1)
  846. {
  847. CThing::ClassIDType idLastThingLoaded = CThing::TotalIDs;
  848. // If there's a callback . . .
  849. if (m_fnProgress)
  850. {
  851. // Call it . . .
  852. if (m_fnProgress(0, sCount) == true)
  853. {
  854. // Callback is happy to continue.
  855. }
  856. else
  857. {
  858. // Callback has decided to end this operation.
  859. sResult = 1;
  860. }
  861. }
  862. // Load each object that was written to the file (could be 0!)
  863. for (short s = 0; (s < sCount) && !sResult; s++)
  864. {
  865. // Read class ID of next object in file
  866. CThing::ClassIDType id;
  867. if (pFile->Read(&id) == 1)
  868. {
  869. // Create object based on class ID
  870. CThing* pThing;
  871. sResult = CThing::Construct(id, this, &pThing);
  872. if (!sResult)
  873. {
  874. // Load object assocated with this class ID
  875. sResult = pThing->Load(pFile, bEditMode, ms_sFileCount, ulFileVersion);
  876. // If successful . . .
  877. if (sResult == 0)
  878. {
  879. // Store last thing to successfully load.
  880. idLastThingLoaded = id;
  881. // If there's a callback . . .
  882. if (m_fnProgress)
  883. {
  884. // Call it . . .
  885. if (m_fnProgress(s + 1, sCount) == true)
  886. {
  887. // Callback is happy to continue.
  888. }
  889. else
  890. {
  891. // Callback has decided to end this operation.
  892. sResult = 1;
  893. }
  894. }
  895. }
  896. else
  897. {
  898. TRACE("CRealm::Load(): Load() failed for thing of type %s; ",
  899. CThing::ms_aClassInfo[id].pszClassName);
  900. if (idLastThingLoaded != CThing::TotalIDs)
  901. {
  902. STRACE("The last thing to successfully loaded was a %s.\n",
  903. CThing::ms_aClassInfo[idLastThingLoaded].pszClassName);
  904. }
  905. else
  906. {
  907. STRACE("This was the first thing to load.\n");
  908. }
  909. }
  910. }
  911. }
  912. else
  913. {
  914. sResult = -1;
  915. TRACE("CRealm::Load(): Error reading class ID!\n");
  916. }
  917. }
  918. // Check for I/O errors (only matters if no errors were reported so far)
  919. if (!sResult && pFile->Error())
  920. {
  921. sResult = -1;
  922. TRACE("CRealm::Load(): Error reading file!\n");
  923. }
  924. // If any errors occurred . . .
  925. if (sResult)
  926. {
  927. // Better clean up stuff that did load.
  928. Clear();
  929. }
  930. }
  931. else
  932. {
  933. sResult = -1;
  934. TRACE("CRealm::Load(): Error reading count of objects in file!\n");
  935. }
  936. }
  937. }
  938. else
  939. {
  940. sResult = -1;
  941. TRACE("CRealm::Load(): Incorrect file version (should be 0x%lx or less, was 0x%lx)!\n", CRealm::FileVersion, ulFileVersion);
  942. }
  943. }
  944. else
  945. {
  946. sResult = -1;
  947. TRACE("CRealm::Load(): Error reading file version!\n");
  948. }
  949. }
  950. else
  951. {
  952. sResult = -1;
  953. TRACE("CRealm::Load(): Incorrect file ID (should be 0x%lx, was 0x%lx)!\n", CRealm::FileID, ulFileID);
  954. }
  955. }
  956. else
  957. {
  958. sResult = -1;
  959. TRACE("CRealm::Load(): Error reading file ID!\n");
  960. }
  961. #ifdef NEW_SMASH
  962. if (sResult == 0) // a success....
  963. {
  964. /* For now, let's see if this is necessary...
  965. // Allocate the Smashatorium:
  966. // Kill old...*
  967. short sOldW = m_smashatorium.m_sWorldW;
  968. short sOldH = m_smashatorium.m_sWorldH;
  969. short sOldTileW = m_smashatorium.m_sTileW;
  970. short sOldTileH = m_smashatorium.m_sTileH;
  971. if (m_smashatorium.m_pGrid) m_smashatorium.Destroy();
  972. if (m_smashatorium.Alloc(sOldW,sOldH,sOldTileW,sOldTileH) != SUCCESS)
  973. {
  974. TRACE("CRealm::Load(): Error reallocating the smashatorium!\n");
  975. sResult = -1;
  976. }
  977. */
  978. }
  979. #endif
  980. return sResult;
  981. }
  982. ////////////////////////////////////////////////////////////////////////////////
  983. // Save the realm
  984. ////////////////////////////////////////////////////////////////////////////////
  985. short CRealm::Save( // Returns 0 if successfull, non-zero otherwise
  986. const char* pszFile) // In: Name of file to save to
  987. {
  988. short sResult = 0;
  989. // Open file
  990. RFile file;
  991. sResult = file.Open((char*)pszFile, "wb", RFile::LittleEndian);
  992. if (sResult == 0)
  993. {
  994. // Use alternate save to do most of the work
  995. sResult = Save(&file);
  996. file.Close();
  997. // Would this be an appropriate time to build the SAK file???
  998. }
  999. else
  1000. {
  1001. sResult = -1;
  1002. TRACE("CRealm::Save(): Couldn't open file: %s !\n", pszFile);
  1003. }
  1004. return sResult;
  1005. }
  1006. ////////////////////////////////////////////////////////////////////////////////
  1007. // Save the realm
  1008. ////////////////////////////////////////////////////////////////////////////////
  1009. short CRealm::Save( // Returns 0 if successfull, non-zero otherwise
  1010. RFile* pFile) // In: File to save to
  1011. {
  1012. short sResult = 0;
  1013. // Increment file count
  1014. ms_sFileCount++;
  1015. // Write out file ID and version
  1016. pFile->Write((unsigned long)CRealm::FileID);
  1017. pFile->Write((unsigned long)CRealm::FileVersion);
  1018. // Save properties for the realm
  1019. pFile->Write(m_s2dResPathIndex);
  1020. pFile->Write(&m_ScoringMode);
  1021. pFile->Write(&m_lScoreTimeDisplay);
  1022. short sUp = 0;
  1023. if (m_bScoreTimerCountsUp)
  1024. sUp = 1;
  1025. pFile->Write(&sUp);
  1026. pFile->Write(&m_sKillsGoal);
  1027. pFile->Write(&m_sFlagsGoal);
  1028. pFile->Write(&m_dKillsPercentGoal);
  1029. // Write out number of objects
  1030. pFile->Write(m_sNumThings);
  1031. // If there's a callback . . .
  1032. if (m_fnProgress)
  1033. {
  1034. // Call it . . .
  1035. if (m_fnProgress(0, m_sNumThings) == true)
  1036. {
  1037. // Callback is happy to continue.
  1038. }
  1039. else
  1040. {
  1041. // Callback has decided to end this operation.
  1042. sResult = 1;
  1043. }
  1044. }
  1045. // Do this for all of the objects
  1046. CListNode<CThing>* pCur;
  1047. CListNode<CThing>* pNext = m_everythingHead.m_pnNext;
  1048. short sCurItemNum = 0;
  1049. while (pNext->m_powner != NULL && !sResult)
  1050. {
  1051. pCur = pNext;
  1052. pNext = pNext->m_pnNext;
  1053. // Write out object's class ID (so we know waht kind it is when we load it)
  1054. pFile->Write(pCur->m_powner->GetClassID());
  1055. // Let object save itself
  1056. sResult = pCur->m_powner->Save(pFile, ms_sFileCount);
  1057. if (sResult)
  1058. break;
  1059. else
  1060. {
  1061. sCurItemNum++;
  1062. // If there's a callback . . .
  1063. if (m_fnProgress)
  1064. {
  1065. // Call it . . .
  1066. if (m_fnProgress(sCurItemNum, m_sNumThings) == true)
  1067. {
  1068. // Callback is happy to continue.
  1069. }
  1070. else
  1071. {
  1072. // Callback has decided to end this operation.
  1073. sResult = 1;
  1074. }
  1075. }
  1076. }
  1077. }
  1078. // Check for I/O errors (only matters if no errors were reported so far)
  1079. if (!sResult && pFile->Error())
  1080. {
  1081. sResult = -1;
  1082. TRACE("CRealm::Save(): Error writing file!\n");
  1083. }
  1084. return sResult;
  1085. }
  1086. ////////////////////////////////////////////////////////////////////////////////
  1087. // Startup the realm
  1088. ////////////////////////////////////////////////////////////////////////////////
  1089. short CRealm::Startup(void) // Returns 0 if successfull, non-zero otherwise
  1090. {
  1091. short sResult = 0;
  1092. // Initialize Population statistics b/c anyone killed already was not done so
  1093. // by the player.
  1094. m_sPopulationBirths = 0;
  1095. m_sPopulation = 0;
  1096. m_sPopulationDeaths = 0;
  1097. m_sHostiles = 0;
  1098. m_sHostileBirths = 0;
  1099. m_sHostileKills = 0;
  1100. // The idea is to only call Startup() for those objects that were Load()'ed,
  1101. // and NOT for any other objects in the realm. The m_sCallStartup flags are
  1102. // set during the CRealm::Load() process to ensure that only those objects
  1103. // are called here. I'm no longer sure I like this idea, so perhaps we
  1104. // should do what Shutdown() does, which is to call Startup() for every
  1105. // object. The original reasoning was that once the game gets going, any
  1106. // newly created objects will NOT have their Startup() called, so Startup()
  1107. // was seen as a special service for Load()'ed objects so they could interact
  1108. // with other objects once all the objects are Load()'ed. Actually, that
  1109. // sounds pretty good! I guess I'll keep it this way pending suggestions.
  1110. // This loop is specifically designed so that it will not end until all of
  1111. // the objects have been scanned in a single pass and none have their flags
  1112. // set. Keep in mind that since objects may be interacting with one another,
  1113. // calling one object may result in indirectly changing another's flag!
  1114. short sDone;
  1115. long lPassNum = 0;
  1116. do {
  1117. // Always assume this will be the last pass. If it isn't, this flag will
  1118. // be reset to 0, and we'll do the whole thing again.
  1119. sDone = 1;
  1120. // Do this for all the objects. We use a copy of the iterator to avoid being
  1121. // stuck with an invalid iterator once the object is gone.
  1122. CListNode<CThing>* pCur;
  1123. CListNode<CThing>* pNext = m_everythingHead.m_pnNext;
  1124. // Go through all objects, calling Startup() for those that have the flag set
  1125. while (pNext->m_powner != NULL && !sResult)
  1126. {
  1127. pCur = pNext;
  1128. pNext = pNext->m_pnNext;
  1129. if (pCur->m_powner->m_sCallStartup)
  1130. {
  1131. sDone = 0;
  1132. pCur->m_powner->m_sCallStartup = 0;
  1133. sResult = pCur->m_powner->Startup();
  1134. }
  1135. }
  1136. // Increment pass number for testing/debugging
  1137. lPassNum++;
  1138. // Setup the previous time for the start of the game.
  1139. m_lPrevTime = m_time.GetGameTime();
  1140. } while (!sDone && !sResult);
  1141. return sResult;
  1142. }
  1143. ////////////////////////////////////////////////////////////////////////////////
  1144. // Shutdown the realm
  1145. ////////////////////////////////////////////////////////////////////////////////
  1146. short CRealm::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
  1147. {
  1148. short sResult = 0;
  1149. // This loop is specifically designed so that it will not end until all of
  1150. // the objects have been scanned in a single pass and none have their flags
  1151. // set. Keep in mind that since objects may be interacting with one another,
  1152. // calling one object may result in indirectly changing another's flag!
  1153. short sDone;
  1154. short sFirstTime = 1;
  1155. long lPassNum = 0;
  1156. do {
  1157. // Always assume this will be the last pass. If it isn't, this flag will
  1158. // be reset to 0, and we'll do the whole thing again.
  1159. sDone = 1;
  1160. // Do this for all the objects. We use a copy of the iterator to avoid being
  1161. // stuck with an invalid iterator once the object is gone.
  1162. CListNode<CThing>* pCur;
  1163. CListNode<CThing>* pNext = m_everythingHead.m_pnNext;
  1164. while (pNext->m_powner != NULL && !sResult)
  1165. {
  1166. pCur = pNext;
  1167. pNext = pNext->m_pnNext;
  1168. if (pCur->m_powner->m_sCallShutdown || sFirstTime)
  1169. {
  1170. sDone = 0;
  1171. pCur->m_powner->m_sCallShutdown = 0;
  1172. sResult = pCur->m_powner->Shutdown();
  1173. }
  1174. }
  1175. // Clear first-time flag
  1176. sFirstTime = 0;
  1177. // Increment pass number for testing/debugging
  1178. lPassNum++;
  1179. } while (!sDone && !sResult);
  1180. return sResult;
  1181. }
  1182. ////////////////////////////////////////////////////////////////////////////////
  1183. // Suspend the realm
  1184. ////////////////////////////////////////////////////////////////////////////////
  1185. void CRealm::Suspend(void)
  1186. {
  1187. m_sNumSuspends++;
  1188. // Do this for all the objects. We use a copy of the iterator to avoid being
  1189. // stuck with an invalid iterator once the object is gone.
  1190. CListNode<CThing>* pCur;
  1191. CListNode<CThing>* pNext = m_everythingHead.m_pnNext;
  1192. while (pNext->m_powner != NULL)
  1193. {
  1194. pCur = pNext;
  1195. pNext = pNext->m_pnNext;
  1196. pCur->m_powner->Suspend();
  1197. }
  1198. // Suspend the game time. I don't think it matters whether this is done
  1199. // before or after the CThing->Suspend() calls since game time never actually
  1200. // advances unless CTime->Update() is called (when not suspended, of course).
  1201. m_time.Suspend();
  1202. // Suspend active sounds.
  1203. PauseAllSamples();
  1204. }
  1205. ////////////////////////////////////////////////////////////////////////////////
  1206. // Resume the realm
  1207. ////////////////////////////////////////////////////////////////////////////////
  1208. void CRealm::Resume(void)
  1209. {
  1210. if (m_sNumSuspends > 0)
  1211. {
  1212. // Do this for all the objects. We use a copy of the iterator to avoid being
  1213. // stuck with an invalid iterator once the object is gone.
  1214. CListNode<CThing>* pCur;
  1215. CListNode<CThing>* pNext = m_everythingHead.m_pnNext;
  1216. while (pNext->m_powner != NULL)
  1217. {
  1218. pCur = pNext;
  1219. pNext = pNext->m_pnNext;
  1220. pCur->m_powner->Resume();
  1221. }
  1222. // Resume the game time. I don't think it matters whether this is done
  1223. // before or after the CThing->Resume() calls since game time never actually
  1224. // advances unless CTime->Update() is called (when not suspended, of course).
  1225. m_time.Resume();
  1226. m_sNumSuspends--;
  1227. ASSERT(m_sNumSuspends >= 0);
  1228. // Resume active sounds.
  1229. ResumeAllSamples();
  1230. }
  1231. }
  1232. ////////////////////////////////////////////////////////////////////////////////
  1233. // Update the realm
  1234. ////////////////////////////////////////////////////////////////////////////////
  1235. void CRealm::Update(void)
  1236. {
  1237. // We want to do this for every CThing in the realm. We use iNext to get
  1238. // the next iterator before calling the current item to avoid
  1239. // a problem that could otherwise occur where iCur becomes invalidated during
  1240. // the (*iCur)->Update() (like when a CThing deletes itself).
  1241. // This causes an additional problem which is relatively easy to fix. Since
  1242. // we always insert at the end, if iCur is the last thing (meaning iNext is the
  1243. // .end()), and that call adds a new thing at the end, that thing will never
  1244. // get called b/c iNext already points to .end(). We effectively skip the
  1245. // newly added item.
  1246. // To avoid this, we simply, at the end, check to make sure iNext-- produces
  1247. // iCur. If not, we process iNext--.
  1248. // Further, if the last item added two things, we need to go back two.
  1249. rspStartProfile("Realm Update");
  1250. // Entering update loop.
  1251. m_bUpdating = true;
  1252. // Do this for everything.
  1253. CThing* pthing;
  1254. m_pNext = m_everythingHead.m_pnNext;
  1255. while (m_pNext->m_powner != NULL)
  1256. {
  1257. pthing = m_pNext->m_powner;
  1258. m_pNext = m_pNext->m_pnNext;
  1259. pthing->Update();
  1260. }
  1261. // Update the display timer
  1262. m_lThisTime = m_time.GetGameTime();
  1263. m_lElapsedTime = m_lThisTime - m_lPrevTime;
  1264. if (m_bScoreTimerCountsUp)
  1265. m_lScoreTimeDisplay += m_lElapsedTime;
  1266. else
  1267. m_lScoreTimeDisplay -= m_lElapsedTime;
  1268. m_lPrevTime = m_lThisTime;
  1269. // Leaving update loop.
  1270. m_bUpdating = false;
  1271. rspEndProfile("Realm Update");
  1272. }
  1273. ////////////////////////////////////////////////////////////////////////////////
  1274. // Render the realm
  1275. ////////////////////////////////////////////////////////////////////////////////
  1276. void CRealm::Render(void)
  1277. {
  1278. // Entering update loop.
  1279. m_bUpdating = true;
  1280. // Do this for everything.
  1281. CThing* pthing;
  1282. m_pNext = m_everythingHead.m_pnNext;
  1283. while (m_pNext->m_powner != NULL)
  1284. {
  1285. pthing = m_pNext->m_powner;
  1286. m_pNext = m_pNext->m_pnNext;
  1287. pthing->Render();
  1288. }
  1289. // Leaving update loop.
  1290. m_bUpdating = false;
  1291. }
  1292. // This old way probably doesn't make sense any more since we're going to allow
  1293. // for multiple views of a realm. I don't think we'd want to tell each object
  1294. // to render itself for each different view -- not unless the enter concept of
  1295. // what each item does to "render" itself changes. Right now, objects merely
  1296. // update their representations in the scene, which really doesn't take too
  1297. // long, and needs to get done no matter what view we're talking about.
  1298. /*
  1299. void CRealm::Render(
  1300. short sViewX, // In: X coord of view
  1301. short sViewY, // In: Y coord of view
  1302. short sViewW, // In: Width of view
  1303. short sViewH, // In: Height of view
  1304. RImage* pimDst, // In: Image to render to
  1305. short sDstX, // In: X coord to draw to
  1306. short sDstY) // In: Y coord to draw to
  1307. {
  1308. // It may turn out that some objects want to do their own clipping to see if
  1309. // they need to add themselves to the scene. I would guess this would be
  1310. // the exception rather than the rule, so when (or if) such a requirement
  1311. // comes up, we shouldn't pass the view info to each object, but instead
  1312. // should create a function that objects can call to get the view info, the
  1313. // idea being that this would be faster overall than passing all that data
  1314. // to each object only to have it ignored most of the time.
  1315. // Do this for everything
  1316. for (CThing::Things::iterator i = m_everything.begin(); i != m_everything.end(); i++)
  1317. (*i)->Render();
  1318. // Render specified view to specified position in specified image
  1319. m_scene.Render(
  1320. sViewX,
  1321. sViewY,
  1322. sViewW,
  1323. sViewH,
  1324. pimDst,
  1325. sDstX,
  1326. sDstY);
  1327. }
  1328. */
  1329. ////////////////////////////////////////////////////////////////////////////////
  1330. // Edit mode: Update the realm
  1331. ////////////////////////////////////////////////////////////////////////////////
  1332. void CRealm::EditUpdate(void)
  1333. {
  1334. // Do this for all the objects. We use a copy of the iterator to avoid being
  1335. // stuck with an invalid iterator once the object is gone.
  1336. CListNode<CThing>* pCur;
  1337. CListNode<CThing>* pNext = m_everythingHead.m_pnNext;
  1338. while (pNext->m_powner != NULL)
  1339. {
  1340. pCur = pNext;
  1341. pNext = pNext->m_pnNext;
  1342. pCur->m_powner->EditUpdate();
  1343. }
  1344. }
  1345. ////////////////////////////////////////////////////////////////////////////////
  1346. // Edit mode: Render the realm
  1347. ////////////////////////////////////////////////////////////////////////////////
  1348. void CRealm::EditRender(void)
  1349. {
  1350. // Do this for all the objects. We use a copy of the iterator to avoid being
  1351. // stuck with an invalid iterator once the object is gone.
  1352. CListNode<CThing>* pCur;
  1353. CListNode<CThing>* pNext = m_everythingHead.m_pnNext;
  1354. while (pNext->m_powner != NULL)
  1355. {
  1356. pCur = pNext;
  1357. pNext = pNext->m_pnNext;
  1358. pCur->m_powner->EditRender();
  1359. }
  1360. }
  1361. // This old way probably doesn't make sense any more since we're going to allow
  1362. // for multiple views of a realm. I don't think we'd want to tell each object
  1363. // to render itself for each different view -- not unless the enter concept of
  1364. // what each item does to "render" itself changes. Right now, objects merely
  1365. // update their representations in the scene, which really doesn't take too
  1366. // long, and needs to get done no matter what view we're talking about.
  1367. /*
  1368. void CRealm::EditRender(
  1369. short sViewX, // In: X coord of view
  1370. short sViewY, // In: Y coord of view
  1371. short sViewW, // In: Width of view
  1372. short sViewH, // In: Height of view
  1373. RImage* pimDst, // In: Image to render to
  1374. short sDstX, // In: X coord to draw to
  1375. short sDstY) // In: Y coord to draw to
  1376. {
  1377. // It may turn out that some objects want to do their own clipping to see if
  1378. // they need to add themselves to the render. I would guess this would be
  1379. // the exception rather than the rule, so when (or if) such a requirement
  1380. // comes up, we shouldn't pass the view info to each object, but instead
  1381. // should create a function that objects can call to get the view info, the
  1382. // idea being that this would be faster overall than passing all that data
  1383. // to each object only to have it ignored most of the time.
  1384. // Do this for everything
  1385. for (CThing::Things::iterator i = m_everything.begin(); i != m_everything.end(); i++)
  1386. (*i)->EditRender();
  1387. // Render specified view to specified position in specified image
  1388. m_scene.Render(
  1389. sViewX,
  1390. sViewY,
  1391. sViewW,
  1392. sViewH,
  1393. pimDst,
  1394. sDstX,
  1395. sDstY);
  1396. }
  1397. */
  1398. ////////////////////////////////////////////////////////////////////////////////
  1399. // EditModify - Run dialog for realm scoring and play options
  1400. ////////////////////////////////////////////////////////////////////////////////
  1401. void CRealm::EditModify(void)
  1402. {
  1403. RGuiItem* pguiRoot = RGuiItem::LoadInstantiate(FullPathVD(REALM_DIALOG_FILE));
  1404. RProcessGui guiDialog;
  1405. if (pguiRoot != NULL)
  1406. {
  1407. RGuiItem* pguiOk = pguiRoot->GetItemFromId(1);
  1408. RGuiItem* pguiCancel = pguiRoot->GetItemFromId(2);
  1409. REdit* peditMinutes = (REdit*) pguiRoot->GetItemFromId(TIMER_MIN_EDIT_ID);
  1410. REdit* peditSeconds = (REdit*) pguiRoot->GetItemFromId(TIMER_SEC_EDIT_ID);
  1411. REdit* peditKillsNum = (REdit*) pguiRoot->GetItemFromId(KILLS_NUM_EDIT_ID);
  1412. REdit* peditKillsPct = (REdit*) pguiRoot->GetItemFromId(KILLS_PCT_EDIT_ID);
  1413. REdit* peditFlagsNum = (REdit*) pguiRoot->GetItemFromId(FLAGS_NUM_EDIT_ID);
  1414. RListBox* plbScoreModes = (RListBox*) pguiRoot->GetItemFromId(SCORE_MODE_LB_ID);
  1415. RGuiItem* pguiItem = NULL;
  1416. long lMinutes;
  1417. long lSeconds;
  1418. if (peditMinutes != NULL && peditSeconds != NULL && peditKillsNum != NULL &&
  1419. peditKillsPct != NULL && peditFlagsNum != NULL && plbScoreModes != NULL)
  1420. {
  1421. ASSERT(peditMinutes->m_type == RGuiItem::Edit);
  1422. ASSERT(peditSeconds->m_type == RGuiItem::Edit);
  1423. ASSERT(peditKillsNum->m_type == RGuiItem::Edit);
  1424. ASSERT(peditKillsPct->m_type == RGuiItem::Edit);
  1425. ASSERT(peditFlagsNum->m_type == RGuiItem::Edit);
  1426. ASSERT(plbScoreModes->m_type == RGuiItem::ListBox);
  1427. lMinutes = m_lScoreTimeDisplay / 60000;
  1428. lSeconds = (m_lScoreTimeDisplay / 1000) % 60;
  1429. peditMinutes->SetText("%ld", lMinutes);
  1430. peditSeconds->SetText("%2.2ld", lSeconds);
  1431. peditKillsNum->SetText("%d", m_sKillsGoal);
  1432. peditKillsPct->SetText("%3.1f", m_dKillsPercentGoal);
  1433. peditFlagsNum->SetText("%d", m_sFlagsGoal);
  1434. peditMinutes->Compose();
  1435. peditSeconds->Compose();
  1436. peditKillsNum->Compose();
  1437. peditKillsPct->Compose();
  1438. peditFlagsNum->Compose();
  1439. pguiItem = plbScoreModes->GetItemFromId(SCORE_MODE_LIST_BASE + m_ScoringMode);
  1440. if (pguiItem != NULL)
  1441. {
  1442. plbScoreModes->SetSel(pguiItem);
  1443. plbScoreModes->AdjustContents();
  1444. plbScoreModes->EnsureVisible(pguiItem);
  1445. }
  1446. if (guiDialog.DoModal(pguiRoot, pguiOk, pguiCancel) == 1)
  1447. {
  1448. lMinutes = peditMinutes->GetVal();
  1449. lSeconds = peditSeconds->GetVal() % 60;
  1450. m_lScoreInitialTime = m_lScoreTimeDisplay = (lMinutes * 60000) + (lSeconds * 1000);
  1451. if (m_lScoreTimeDisplay == 0)
  1452. m_bScoreTimerCountsUp = true;
  1453. else
  1454. m_bScoreTimerCountsUp = false;
  1455. m_sKillsGoal = (short) peditKillsNum->GetVal();
  1456. m_sFlagsGoal = (short) peditFlagsNum->GetVal();
  1457. m_dKillsPercentGoal = (double) peditKillsPct->GetVal();
  1458. pguiItem = plbScoreModes->GetSel();
  1459. if (pguiItem != NULL)
  1460. m_ScoringMode = pguiItem->m_lId - SCORE_MODE_LIST_BASE;
  1461. }
  1462. }
  1463. }
  1464. }
  1465. #ifdef MOBILE
  1466. extern "C"
  1467. {
  1468. #include "android/android.h"
  1469. }
  1470. #endif
  1471. ////////////////////////////////////////////////////////////////////////////////
  1472. // IsEndOfLevelGoalMet - check to see if level is complete based on the
  1473. // scoring and game play mode.
  1474. ////////////////////////////////////////////////////////////////////////////////
  1475. bool CRealm::IsEndOfLevelGoalMet(bool bEndLevelKey)
  1476. {
  1477. #ifdef MOBILE
  1478. bool showAndroidKey = true;
  1479. switch (m_ScoringMode)
  1480. {
  1481. case Standard:
  1482. if (m_sHostileBirths != 0)
  1483. if (((m_sHostileKills * 100) / m_sHostileBirths < m_dKillsPercentGoal))
  1484. showAndroidKey = false;
  1485. break;
  1486. default: //Hide the next key for anything else for the moment
  1487. showAndroidKey = false;
  1488. }
  1489. AndroidSetShowEndLevelKey(showAndroidKey);
  1490. #endif
  1491. bool bEnd = true;
  1492. if (m_bPressedEndLevelKey)
  1493. {
  1494. m_bPressedEndLevelKey = false;
  1495. // Hack: don't let the level end immediately if the player is using the debug level skip
  1496. if (m_time.GetGameTime() > 1000)
  1497. bEndLevelKey = true;
  1498. }
  1499. switch (m_ScoringMode)
  1500. {
  1501. // In a standard level, the user is done when the percentage of hostiles killed
  1502. // is greater than the minimum set in the level and the user presses the
  1503. // 'next level' key.
  1504. case Standard:
  1505. if (m_sHostileBirths != 0)
  1506. {
  1507. if (((m_sHostileKills * 100) / m_sHostileBirths < m_dKillsPercentGoal) || !bEndLevelKey)
  1508. bEnd = false;
  1509. }
  1510. else
  1511. {
  1512. if (!bEndLevelKey)
  1513. bEnd = false;
  1514. }
  1515. break;
  1516. // In a timed level, the user is done when the time runs out, the population
  1517. // runs out, or the user presses the 'next level' key.
  1518. case Timed:
  1519. if (m_lScoreTimeDisplay > 0 && m_sPopulation > 0 && !bEndLevelKey)
  1520. bEnd = false;
  1521. break;
  1522. // In a timed goal level, the user must meet the goal within the specified
  1523. // time.
  1524. case TimedGoal:
  1525. if (m_lScoreTimeDisplay > 0 && m_sPopulationDeaths < m_sKillsGoal)
  1526. bEnd = false;
  1527. break;
  1528. // In a timed flag level, the user must get the flag to a base before
  1529. // the goal is considered met.
  1530. case TimedFlag:
  1531. case MPTimedFlag:
  1532. // if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_sFlagsGoal)
  1533. if (m_lScoreTimeDisplay > 0 && m_sFlagbaseCaptured < m_sFlagsGoal)
  1534. bEnd = false;
  1535. break;
  1536. // In a capture the flag level, a user must capture a flag and return it
  1537. // to a base to complete the level.
  1538. case CaptureFlag:
  1539. case MPCaptureFlag:
  1540. if (m_sFlagbaseCaptured < m_sFlagsGoal)
  1541. bEnd = false;
  1542. break;
  1543. // In a goal level, the user can only be done when they meet the goal.
  1544. case Goal:
  1545. if (m_sPopulationDeaths < m_sKillsGoal)
  1546. bEnd = false;
  1547. break;
  1548. // In a checkpoint level, the user collects as many flags as possible and
  1549. // can choose to end the level whenever they want (they'll just get a lower
  1550. // score, if they have not gotten all the flags).
  1551. case Checkpoint:
  1552. if (m_sFlagsGoal == 0)
  1553. {
  1554. if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_asClassNumThings[CThing::CFlagID])
  1555. bEnd = false;
  1556. }
  1557. else
  1558. {
  1559. if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_sFlagsGoal && !bEndLevelKey)
  1560. bEnd = false;
  1561. }
  1562. break;
  1563. case MPFrag:
  1564. // Get highest number of kills from score module and
  1565. if ((m_sKillsGoal < 1) || (ScoreHighestKills(this) < m_sKillsGoal))
  1566. bEnd = false;
  1567. break;
  1568. case MPTimedFrag:
  1569. if (m_lScoreTimeDisplay > 0 && ScoreHighestKills(this) < m_sKillsGoal)
  1570. bEnd = false;
  1571. break;
  1572. case MPLastMan:
  1573. // if (ScorePlayersRemaining() > 1)
  1574. bEnd = false;
  1575. break;
  1576. case MPTimed:
  1577. if (m_lScoreTimeDisplay > 0)
  1578. bEnd = false;
  1579. break;
  1580. }
  1581. #if defined(DEBUG_LEVEL_CHEAT)
  1582. bEnd = bEndLevelKey;
  1583. #endif
  1584. return bEnd;
  1585. }
  1586. ////////////////////////////////////////////////////////////////////////////////
  1587. // Determine if a path is clear of terrain.
  1588. ////////////////////////////////////////////////////////////////////////////////
  1589. bool CRealm::IsPathClear( // Returns true, if the entire path is clear.
  1590. // Returns false, if only a portion of the path is clear.
  1591. // (see *psX, *psY, *psZ).
  1592. short sX, // In: Starting X.
  1593. short sY, // In: Starting Y.
  1594. short sZ, // In: Starting Z.
  1595. short sRotY, // In: Rotation around y axis (direction on X/Z plane).
  1596. double dCrawlRate, // In: Rate at which to scan ('crawl') path in pixels per
  1597. // iteration.
  1598. // NOTE: Values less than 1.0 are inefficient.
  1599. // NOTE: We scan terrain using GetHeight()
  1600. // at only one pixel.
  1601. // NOTE: We could change this to a speed in pixels per second
  1602. // where we'd assume a certain frame rate.
  1603. short sDistanceXZ, // In: Distance on X/Z plane.
  1604. short sVerticalTolerance /*= 0*/, // In: Max traverser can step up.
  1605. short* psX /*= NULL*/, // Out: If not NULL, last clear point on path.
  1606. short* psY /*= NULL*/, // Out: If not NULL, last clear point on path.
  1607. short* psZ /*= NULL*/, // Out: If not NULL, last clear point on path.
  1608. bool bCheckExtents /*= true*/) // In: If true, will consider the edge of the realm a path
  1609. // inhibitor. If false, reaching the edge of the realm
  1610. // indicates a clear path.
  1611. {
  1612. bool bEntirelyClear = false; // Assume entire path is not clear.
  1613. ////////////////////////// Traverse path ///////////////////////////////////
  1614. // Get most efficient increments that won't miss any attributes.
  1615. // For the rates we use trig with a hypotenuse of 1 which will give
  1616. // us a rate <= 1.0 and then multiply by the the crawl for
  1617. // a reasonable increase in the speed of this alg.
  1618. // sAngle must be between 0 and 359.
  1619. sRotY = rspMod360(sRotY);
  1620. float fRateX = COSQ[sRotY] * dCrawlRate;
  1621. float fRateZ = -SINQ[sRotY] * dCrawlRate;
  1622. float fRateY = 0.0; // If we ever want vertical movement . . .
  1623. // Set initial position to first point to check (NEVER checks original position).
  1624. float fPosX = sX + fRateX;
  1625. float fPosY = sY + fRateY;
  1626. float fPosZ = sZ + fRateZ;
  1627. // Determine amount traveled per iteration on X/Z plane just once.
  1628. float fIterDistXZ = rspSqrt(ABS2(fRateX, fRateZ) );
  1629. float fTotalDistXZ = 0.0F;
  1630. // Store extents.
  1631. short sMaxX = GetRealmWidth();
  1632. short sMaxZ = GetRealmHeight();
  1633. short sMinX = 0;
  1634. short sMinZ = 0;
  1635. short sCurH;
  1636. bool bInsurmountableHeight = false;
  1637. // Scan while in realm.
  1638. while (
  1639. fPosX > sMinX
  1640. && fPosZ > sMinZ
  1641. && fPosX < sMaxX
  1642. && fPosZ < sMaxZ
  1643. && fTotalDistXZ < sDistanceXZ)
  1644. {
  1645. sCurH = GetHeight((short)fPosX, (short)fPosZ);
  1646. // If too big a height difference . . .
  1647. if (sCurH - fPosY > sVerticalTolerance)
  1648. {
  1649. bInsurmountableHeight = true;
  1650. break;
  1651. }
  1652. // Update position.
  1653. fPosX += fRateX;
  1654. fPosY = MAX(fPosY, (float)sCurH);
  1655. fPosZ += fRateZ;
  1656. // Update distance travelled on X/Z plane.
  1657. fTotalDistXZ += fIterDistXZ;
  1658. }
  1659. // Set end pt.
  1660. SET(psX, fPosX);
  1661. SET(psY, fPosY);
  1662. SET(psZ, fPosZ);
  1663. // If we made it the whole way . . .
  1664. if (fTotalDistXZ >= sDistanceXZ)
  1665. {
  1666. bEntirelyClear = true;
  1667. }
  1668. // Else, if we didn't hit any terrain . . .
  1669. else if (bInsurmountableHeight == false)
  1670. {
  1671. // Only clear if we are not checking extents.
  1672. bEntirelyClear = !bCheckExtents;
  1673. }
  1674. #if 0
  1675. // FEEDBACK.
  1676. // Create a line sprite.
  1677. CSpriteLine2d* psl2d = new CSpriteLine2d;
  1678. if (psl2d != NULL)
  1679. {
  1680. Map3Dto2D(
  1681. sX,
  1682. sY,
  1683. sZ,
  1684. &(psl2d->m_sX2),
  1685. &(psl2d->m_sY2) );
  1686. Map3Dto2D(
  1687. fPosX,
  1688. fPosY,
  1689. fPosZ,
  1690. &(psl2d->m_sX2End),
  1691. &(psl2d->m_sY2End) );
  1692. psl2d->m_sPriority = sZ;
  1693. psl2d->m_sLayer = GetLayerViaAttrib(GetLayer(sX, sZ));
  1694. psl2d->m_u8Color = (bEntirelyClear == false) ? 249 : 250;
  1695. // Destroy when done.
  1696. psl2d->m_sInFlags = CSprite::InDeleteOnRender;
  1697. // Put 'er there.
  1698. m_scene.UpdateSprite(psl2d);
  1699. }
  1700. #endif
  1701. return bEntirelyClear;
  1702. }
  1703. ////////////////////////////////////////////////////////////////////////////////
  1704. // Determine if a path is clear of terrain.
  1705. ////////////////////////////////////////////////////////////////////////////////
  1706. bool CRealm::IsPathClear( // Returns true, if the entire path is clear.
  1707. // Returns false, if only a portion of the path is clear.
  1708. // (see *psX, *psY, *psZ).
  1709. short sX, // In: Starting X.
  1710. short sY, // In: Starting Y.
  1711. short sZ, // In: Starting Z.
  1712. double dCrawlRate, // In: Rate at which to scan ('crawl') path in pixels per
  1713. // iteration.
  1714. // NOTE: Values less than 1.0 are inefficient.
  1715. // NOTE: We scan terrain using GetHeight()
  1716. // at only one pixel.
  1717. // NOTE: We could change this to a speed in pixels per second
  1718. // where we'd assume a certain frame rate.
  1719. short sDstX, // In: Destination X.
  1720. short sDstZ, // In: Destination Z.
  1721. short sVerticalTolerance /*= 0*/, // In: Max traverser can step up.
  1722. short* psX /*= NULL*/, // Out: If not NULL, last clear point on path.
  1723. short* psY /*= NULL*/, // Out: If not NULL, last clear point on path.
  1724. short* psZ /*= NULL*/, // Out: If not NULL, last clear point on path.
  1725. bool bCheckExtents /*= true*/) // In: If true, will consider the edge of the realm a path
  1726. // inhibitor. If false, reaching the edge of the realm
  1727. // indicates a clear path.
  1728. {
  1729. short sDistanceXZ = rspSqrt(ABS2(sDstX - sX, sZ - sDstZ) );
  1730. short sRotY = rspATan(sZ - sDstZ, sDstX - sX);
  1731. return IsPathClear( // Returns true, if the entire path is clear.
  1732. // Returns false, if only a portion of the path is clear.
  1733. // (see *psX, *psY, *psZ).
  1734. sX, // In: Starting X.
  1735. sY, // In: Starting Y.
  1736. sZ, // In: Starting Z.
  1737. sRotY, // In: Rotation around y axis (direction on X/Z plane).
  1738. dCrawlRate, // In: Rate at which to scan ('crawl') path in pixels per
  1739. // iteration.
  1740. // NOTE: Values less than 1.0 are inefficient.
  1741. // NOTE: We scan terrain using GetHeight()
  1742. // at only one pixel.
  1743. // NOTE: We could change this to a speed in pixels per second
  1744. // where we'd assume a certain frame rate.
  1745. sDistanceXZ, // In: Distance on X/Z plane.
  1746. sVerticalTolerance, // In: Max traverser can step up.
  1747. psX, // Out: If not NULL, last clear point on path.
  1748. psY, // Out: If not NULL, last clear point on path.
  1749. psZ, // Out: If not NULL, last clear point on path.
  1750. bCheckExtents); // In: If true, will consider the edge of the realm a path
  1751. // inhibitor. If false, reaching the edge of the realm
  1752. // indicates a clear path.
  1753. }
  1754. ////////////////////////////////////////////////////////////////////////////////
  1755. // Gives this realm an opportunity and drawing surface to display its
  1756. // current status.
  1757. ////////////////////////////////////////////////////////////////////////////////
  1758. void CRealm::DrawStatus( // Returns nothing.
  1759. RImage* pim, // In: Image in which to draw status.
  1760. RRect* prc) // In: Rectangle in which to draw status. Clips to.
  1761. {
  1762. long lCurTime = m_time.GetGameTime();
  1763. if (lCurTime > m_lLastStatusDrawTime + STATUS_UPDATE_INTERVAL)
  1764. {
  1765. // Set print/clip to area.
  1766. RRect rcDst;
  1767. rcDst.sX = prc->sX + STATUS_PRINT_X;
  1768. rcDst.sY = prc->sY + STATUS_PRINT_Y;
  1769. rcDst.sW = prc->sW - STATUS_PRINT_X;
  1770. rcDst.sH = prc->sH - STATUS_PRINT_Y;
  1771. // Clear.
  1772. rspRect(RSP_BLACK_INDEX, pim, rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
  1773. ms_print.SetDestination(pim, &rcDst);
  1774. ms_print.print(
  1775. pim,
  1776. rcDst.sX,
  1777. rcDst.sY,
  1778. " Population %d Body Count %d (%d%%) Goal %d%%",
  1779. m_sPopulationBirths,
  1780. m_sPopulationDeaths,
  1781. m_sPopulationDeaths * 100 / ((m_sPopulationBirths != 0) ? m_sPopulationBirths : 1),
  1782. (short)m_dKillsPercentGoal
  1783. );
  1784. m_lLastStatusDrawTime = lCurTime;
  1785. }
  1786. }
  1787. ////////////////////////////////////////////////////////////////////////////////
  1788. // Maps a 3D coordinate onto the viewing plane provided the view angle
  1789. // (~angle of projection).
  1790. ////////////////////////////////////////////////////////////////////////////////
  1791. void CRealm::Map3Dto2D( // Returns nothing.
  1792. short sX, // In.
  1793. short sY, // In.
  1794. short sZ, // In.
  1795. short* psX, // Out.
  1796. short* psY) // Out.
  1797. {
  1798. ::Map3Dto2D(sX, sY, sZ, psX, psY, m_phood->GetRealmRotX() );
  1799. }
  1800. ////////////////////////////////////////////////////////////////////////////////
  1801. // Maps a 3D coordinate onto the viewing plane provided the view angle
  1802. // (~angle of projection).
  1803. ////////////////////////////////////////////////////////////////////////////////
  1804. void CRealm::Map3Dto2D( // Returns nothing.
  1805. double dX, // In.
  1806. double dY, // In.
  1807. double dZ, // In.
  1808. double* pdX, // Out.
  1809. double* pdY) // Out.
  1810. {
  1811. ::Map3Dto2D(dX, dY, dZ, pdX, pdY, m_phood->GetRealmRotX() );
  1812. }
  1813. ////////////////////////////////////////////////////////////////////////////////
  1814. // Scales a Z coordinate onto the viewing plane using the
  1815. // view angle (~angle of projection).
  1816. ////////////////////////////////////////////////////////////////////////////////
  1817. void CRealm::MapZ3DtoY2D( // Returns nothing.
  1818. double dZIn, // In.
  1819. double* pdYOut) // Out.
  1820. {
  1821. ::MapZ3DtoY2D(dZIn, pdYOut, m_phood->GetRealmRotX() );
  1822. }
  1823. ////////////////////////////////////////////////////////////////////////////////
  1824. // Scales a Z coordinate onto the viewing plane using the
  1825. // view angle (~angle of projection).
  1826. ////////////////////////////////////////////////////////////////////////////////
  1827. void CRealm::MapZ3DtoY2D( // Returns nothing.
  1828. short sZIn, // In.
  1829. short* psYOut) // Out.
  1830. {
  1831. ::MapZ3DtoY2D(sZIn, psYOut, m_phood->GetRealmRotX() );
  1832. }
  1833. ////////////////////////////////////////////////////////////////////////////////
  1834. // Scales a Y coordinate from the viewing plane using the
  1835. // view angle (~angle of projection).
  1836. ////////////////////////////////////////////////////////////////////////////////
  1837. void CRealm::MapY2DtoZ3D( // Returns nothing.
  1838. double dYIn, // In.
  1839. double* pdZOut) // Out.
  1840. {
  1841. ::MapY2DtoZ3D(dYIn, pdZOut, m_phood->GetRealmRotX() );
  1842. }
  1843. ////////////////////////////////////////////////////////////////////////////////
  1844. // Scales a Y coordinate from the viewing plane using the
  1845. // view angle (~angle of projection).
  1846. ////////////////////////////////////////////////////////////////////////////////
  1847. void CRealm::MapY2DtoZ3D( // Returns nothing.
  1848. short sYIn, // In.
  1849. short* psZOut) // Out.
  1850. {
  1851. ::MapY2DtoZ3D(sYIn, psZOut, m_phood->GetRealmRotX() );
  1852. }
  1853. ////////////////////////////////////////////////////////////////////////////////
  1854. // Scales a Y coordinate onto the viewing plane using the
  1855. // view angle (~angle of projection).
  1856. ////////////////////////////////////////////////////////////////////////////////
  1857. void CRealm::MapY3DtoY2D( // Returns nothing.
  1858. double dYIn, // In.
  1859. double* pdYOut) // Out.
  1860. {
  1861. ::MapY3DtoY2D(dYIn, pdYOut, m_phood->GetRealmRotX() );
  1862. }
  1863. ////////////////////////////////////////////////////////////////////////////////
  1864. // Scales a Y coordinate onto the viewing plane using the
  1865. // view angle (~angle of projection).
  1866. ////////////////////////////////////////////////////////////////////////////////
  1867. void CRealm::MapY3DtoY2D( // Returns nothing.
  1868. short sYIn, // In.
  1869. short* psYOut) // Out.
  1870. {
  1871. ::MapY3DtoY2D(sYIn, psYOut, m_phood->GetRealmRotX() );
  1872. }
  1873. ////////////////////////////////////////////////////////////////////////////////
  1874. // Scales a Y coordinate from the viewing plane using the
  1875. // view angle (~angle of projection).
  1876. ////////////////////////////////////////////////////////////////////////////////
  1877. void CRealm::MapY2DtoY3D( // Returns nothing.
  1878. double dYIn, // In.
  1879. double* pdYOut) // Out.
  1880. {
  1881. ::MapY2DtoY3D(dYIn, pdYOut, m_phood->GetRealmRotX() );
  1882. }
  1883. ////////////////////////////////////////////////////////////////////////////////
  1884. // Scales a Y coordinate from the viewing plane using the
  1885. // view angle (~angle of projection).
  1886. ////////////////////////////////////////////////////////////////////////////////
  1887. void CRealm::MapY2DtoY3D( // Returns nothing.
  1888. short sYIn, // In.
  1889. short* psYOut) // Out.
  1890. {
  1891. ::MapY2DtoY3D(sYIn, psYOut, m_phood->GetRealmRotX() );
  1892. }
  1893. ////////////////////////////////////////////////////////////////////////////////
  1894. // If enabled, scales the specified height based on the view angle.
  1895. ////////////////////////////////////////////////////////////////////////////////
  1896. void CRealm::MapAttribHeight( // Returns nothing.
  1897. short sHIn, // In.
  1898. short* psHOut) // Out.
  1899. {
  1900. // If scaling attrib map heights . . .
  1901. if (m_phood->m_sScaleAttribHeights != FALSE)
  1902. {
  1903. short sRotX = m_phood->GetRealmRotX();
  1904. // Scale into realm.
  1905. ::MapY2DtoY3D(sHIn, psHOut, sRotX);
  1906. }
  1907. else
  1908. {
  1909. *psHOut = sHIn;
  1910. }
  1911. }
  1912. ////////////////////////////////////////////////////////////////////////////////
  1913. //// Terrrain map access functions /////////////////////////////////////////////
  1914. ////////////////////////////////////////////////////////////////////////////////
  1915. // Note these had no comments describing their function so I made some very
  1916. // vague comments that I hope were accurate -- JMI 06/28/97.
  1917. // Get the terrain height at an x/z position.
  1918. // Zero, if off map.
  1919. short CRealm::GetHeight(short sX, short sZ)
  1920. {
  1921. short sRotX = m_phood->GetRealmRotX();
  1922. // Scale the Z based on the view angle.
  1923. ::MapZ3DtoY2D(sZ, &sZ, sRotX);
  1924. short sH = 4 * (m_pTerrainMap->GetVal(sX, sZ, 0x0000) & REALM_ATTR_HEIGHT_MASK);
  1925. // Scale into realm.
  1926. MapAttribHeight(sH, &sH);
  1927. return sH;
  1928. }
  1929. // Get the height and 'not walkable' status at the specified location.
  1930. // 'No walk', if off map.
  1931. short CRealm::GetHeightAndNoWalk( // Returns height at new location.
  1932. short sX, // In: X position to check on map.
  1933. short sZ, // In: Z position to check on map.
  1934. bool* pbNoWalk) // Out: true, if 'no walk'.
  1935. {
  1936. short sRotX = m_phood->GetRealmRotX();
  1937. // Scale the Z based on the view angle.
  1938. ::MapZ3DtoY2D(sZ, &sZ, sRotX);
  1939. U16 u16Attrib = m_pTerrainMap->GetVal(sX, sZ, REALM_ATTR_NOT_WALKABLE);
  1940. short sH = 4 * (u16Attrib & REALM_ATTR_HEIGHT_MASK);
  1941. // Scale into realm.
  1942. MapAttribHeight(sH, &sH);
  1943. // Get 'no walk'.
  1944. if (u16Attrib & REALM_ATTR_NOT_WALKABLE)
  1945. {
  1946. *pbNoWalk = true;
  1947. }
  1948. else
  1949. {
  1950. *pbNoWalk = false;
  1951. }
  1952. return sH;
  1953. }
  1954. // Get the terrain attributes at an x/z position.
  1955. // 'No walk', if off map.
  1956. short CRealm::GetTerrainAttributes(short sX, short sZ)
  1957. {
  1958. // Scale the Z based on the view angle.
  1959. ::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX() );
  1960. return m_pTerrainMap->GetVal(sX, sZ, REALM_ATTR_NOT_WALKABLE);
  1961. }
  1962. // Get the floor attributes at an x/z position.
  1963. // Zero, if off map.
  1964. short CRealm::GetFloorAttribute(short sX, short sZ)
  1965. {
  1966. // Scale the Z based on the view angle.
  1967. ::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX() );
  1968. return m_pTerrainMap->GetVal(sX, sZ, 0) & REALM_ATTR_FLOOR_MASK;
  1969. }
  1970. // Get the floor value at an x/z position.
  1971. // sMask, if off map.
  1972. short CRealm::GetFloorMapValue(short sX, short sZ, short sMask/* = 0x007f*/)
  1973. {
  1974. // Scale the Z based on the view angle.
  1975. ::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX() );
  1976. return m_pTerrainMap->GetVal(sX, sZ, sMask);
  1977. }
  1978. // Get the all alpha and opaque layer bits at an x/z position.
  1979. // Zero, if off map.
  1980. short CRealm::GetLayer(short sX, short sZ)
  1981. {
  1982. // Scale the Z based on the view angle.
  1983. ::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX() );
  1984. return m_pLayerMap->GetVal(sX, sZ, 0) & REALM_ATTR_LAYER_MASK;
  1985. }
  1986. // Get effect attributes at an x/z position.
  1987. // Zero, if off map.
  1988. short CRealm::GetEffectAttribute(short sX, short sZ)
  1989. {
  1990. // Scale the Z based on the view angle.
  1991. ::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX() );
  1992. return m_pTerrainMap->GetVal(sX, sZ, 0) & REALM_ATTR_EFFECT_MASK;
  1993. }
  1994. // Get effect value at an x/z position.
  1995. // Zero, if off map.
  1996. short CRealm::GetEffectMapValue(short sX, short sZ)
  1997. {
  1998. // Scale the Z based on the view angle.
  1999. ::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX() );
  2000. return m_pTerrainMap->GetVal(sX, sZ, 0);
  2001. }
  2002. ////////////////////////////////////////////////////////////////////////////////
  2003. ////////////////////////////////////////////////////////////////////////////////
  2004. ////////////////////////////////////////////////////////////////////////////////
  2005. // Makes a 2D path based on the current hood setting for 'Use top-view 2Ds'.
  2006. // Note that this function returns to you a ptr to its one and only static
  2007. // string of length RSP_MAX_PATH. Do not write to this string and do not
  2008. // store this string. It is best to just use this call to pass a string to
  2009. // a function that will just use it right away (i.e., will not store it or
  2010. // modify it).
  2011. ////////////////////////////////////////////////////////////////////////////////
  2012. const char* CRealm::Make2dResPath( // Returns a ptr to an internal static buffer
  2013. // containing the passed string, pszResName,
  2014. // preceded by the appropriate directory based
  2015. // on the current hood settings.
  2016. const char* pszResName) // In: Resource name to prepend path to.
  2017. {
  2018. static char szFullPath[RSP_MAX_PATH];
  2019. ASSERT(m_s2dResPathIndex < NUM_ELEMENTS(ms_apsz2dResPaths) );
  2020. // Get resource path.
  2021. char* pszPath = ms_apsz2dResPaths[m_s2dResPathIndex];
  2022. ASSERT(strlen(pszPath) + strlen(pszResName) < sizeof(szFullPath) );
  2023. strcpy(szFullPath, pszPath);
  2024. strcat(szFullPath, pszResName);
  2025. return szFullPath;
  2026. }
  2027. ////////////////////////////////////////////////////////////////////////////////
  2028. // Creates the layer map, if it has not already been done.
  2029. // Now that the layer map needs to be 32K of uncompressable data, we create it
  2030. // at run time.
  2031. // (static)
  2032. ////////////////////////////////////////////////////////////////////////////////
  2033. void CRealm::CreateLayerMap(void)
  2034. {
  2035. // If table needs to be built . . .
  2036. if (ms_asAttribToLayer[0] != LayerSprite16)
  2037. {
  2038. long l;
  2039. for (l = 0; l < NUM_ELEMENTS(ms_asAttribToLayer); l++)
  2040. {
  2041. if (l & 0x0001)
  2042. ms_asAttribToLayer[l] = LayerSprite1;
  2043. else if (l & 0x0002)
  2044. ms_asAttribToLayer[l] = LayerSprite2;
  2045. else if (l & 0x0004)
  2046. ms_asAttribToLayer[l] = LayerSprite3;
  2047. else if (l & 0x0008)
  2048. ms_asAttribToLayer[l] = LayerSprite4;
  2049. else if (l & 0x0010)
  2050. ms_asAttribToLayer[l] = LayerSprite5;
  2051. else if (l & 0x0020)
  2052. ms_asAttribToLayer[l] = LayerSprite6;
  2053. else if (l & 0x0040)
  2054. ms_asAttribToLayer[l] = LayerSprite7;
  2055. else if (l & 0x0080)
  2056. ms_asAttribToLayer[l] = LayerSprite8;
  2057. else if (l & 0x0100)
  2058. ms_asAttribToLayer[l] = LayerSprite9;
  2059. else if (l & 0x0200)
  2060. ms_asAttribToLayer[l] = LayerSprite10;
  2061. else if (l & 0x0400)
  2062. ms_asAttribToLayer[l] = LayerSprite11;
  2063. else if (l & 0x0800)
  2064. ms_asAttribToLayer[l] = LayerSprite12;
  2065. else if (l & 0x1000)
  2066. ms_asAttribToLayer[l] = LayerSprite13;
  2067. else if (l & 0x2000)
  2068. ms_asAttribToLayer[l] = LayerSprite14;
  2069. else if (l & 0x4000)
  2070. ms_asAttribToLayer[l] = LayerSprite15;
  2071. else
  2072. ms_asAttribToLayer[l] = LayerSprite16;
  2073. }
  2074. }
  2075. }
  2076. ////////////////////////////////////////////////////////////////////////////////
  2077. // EOF
  2078. ////////////////////////////////////////////////////////////////////////////////