score.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467
  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. // Score.cpp
  19. // Project: Postal
  20. //
  21. // Description:
  22. // This module will be used to collect and display the scoring for the
  23. // game. Characters that die will call a function to register the kill
  24. // giving their ID and the ID of the character that killed them. Play
  25. // will call a function to update the display for the score and depending
  26. // on the mode of scoring, this module will draw the score into the given
  27. // image. The first scoring mode will be for multiplayer where all 8
  28. // players will have the number of kills displayed with their names.
  29. // Single player score may show something like Number of victims and
  30. // number of enemies. A timed challenge level may show the kills and the
  31. // time remaining.
  32. //
  33. // History:
  34. //
  35. // 06/11/97 BRH Started this module for scoring of different types.
  36. //
  37. // 06/12/97 BRH Added Scoring display for multiplayer and moved the
  38. // single player score from Realm to the same display
  39. // function here in score.
  40. //
  41. // 06/13/97 BRH Fixed the formatting for multiplayer mode.
  42. //
  43. // 06/15/97 BRH Took out temporary code that forced it into multiplayer
  44. // display for testing. Put back the normal code to
  45. // work for either single or multiplayer mode.
  46. //
  47. // 06/16/97 BRH Added a reset function to reset the scores and
  48. // display time.
  49. //
  50. // 06/17/97 BRH Fixed the display area for the score.
  51. //
  52. // 06/26/97 BRH Changed the score so that the population number goes
  53. // down when someone is killed.
  54. //
  55. // 06/29/97 BRH Separated the score and status lines so that the mission
  56. // goal can be displayed for a short period of time while
  57. // the score displays constantly.
  58. //
  59. // 07/04/97 BRH Added scoring displays for some of the other types
  60. // of games, timed, goal, capture the flag, etc.
  61. //
  62. // 07/06/97 BRH Added scoring displays and staus lines for the challenge
  63. // levels.
  64. //
  65. // 07/07/97 JMI Now ScoreUpdateDisplay() returns true if it drew into
  66. // the passed in buffer and false otherwise.
  67. //
  68. // 07/08/97 BRH Changed the check for scoring mode from the scoreboard
  69. // to the realm's enumerated type.
  70. //
  71. // 07/14/97 BRH Fixed clock format to use %2.2d to correctly display
  72. // 2 digits for seconds.
  73. //
  74. // 07/22/97 BRH Added ScoreDisplayHighScores function which is called
  75. // from Play.cpp after the goal level has been met. This
  76. // will check the player's score against the top 5 stored
  77. // scores for the level and if they place, it will allow
  78. // them to enter their name. The initial version of this
  79. // will just skip the dialog for now, but will soon
  80. // do a switch statement for each scoring mode.
  81. //
  82. // 07/26/97 BRH Added loading and saving of high scores in a prefs type
  83. // file. Now the only thing that needs to be changed is
  84. // to use the function that returns the realm name or
  85. // descriptive string which will be used as the section
  86. // name to find the scores particular to this realm.
  87. //
  88. // 07/27/97 BRH Made use of the new realm variable m_lScoreIntialTime
  89. // to calculate the elapsed time for two of the scoring
  90. // modes.
  91. //
  92. // 07/30/97 BRH Used the realm's string to identify the current realm
  93. // being played so that the high scores for this realm
  94. // can be loaded and saved.
  95. //
  96. // 08/10/97 JRD Modified ScoreUpdateDisplay to accept a hood pointer so
  97. // it could create a graphical back drop.
  98. //
  99. // 09/10/97 JRD Added color matching and shadow drop to the upper score bar.
  100. //
  101. // 08/14/97 BRH Changed location of gui for high scores, moved from
  102. // /game/ to /shell directory so that the textures used
  103. // for it could be shared easier with the other shell gui's
  104. //
  105. // 08/17/97 MJR Now uses g_resmgrShell to load gui's. Also now frees
  106. // the resources (ooooohhh Bill!)
  107. //
  108. // 08/19/97 BRH Fixed the problem with checkpoint scoring, probably
  109. // some typo that Mike put in by accident.
  110. //
  111. // 08/19/97 JMI There was a missing '}' ala Mike via Bill.
  112. //
  113. // 08/20/97 BRH No thanks to Mike or Jon, I had to fix the multiplayer
  114. // score displays so that new time information would
  115. // fit on a third line.
  116. //
  117. // 08/25/97 JMI Moved gsStatus* to toolbar.h.
  118. //
  119. // 08/28/97 JMI Added GetRes/ReleaseRes callbacks to dialogs, PalTrans,
  120. // Postal Font, and sound to name edit field.
  121. //
  122. // 08/29/97 BRH Changed challenge modes to use Population numbers
  123. // rather than just hostile numbers so that the victims
  124. // count also.
  125. //
  126. // 08/29/97 JMI Now ScoreDisplayHighScores() quits immediately for
  127. // scoring modes that don't track high scores (currently,
  128. // only standard).
  129. //
  130. // 08/30/97 JMI Now uses population deaths instead of hostile deaths
  131. // for timed challenge scoring.
  132. // Also, sets the palette every time the dialog is shown.
  133. //
  134. // 09/01/97 JMI Now your high score is entered in the high score dialog
  135. // itself. Also, now uses a listbox for high scores so
  136. // that we could edit merely one high score item and fill
  137. // the listbox with instances of that same item.
  138. //
  139. // 09/01/97 JMI Now displays new highscore in different color.
  140. //
  141. // 09/02/97 JMI Now ScoreDisplayHighScores() makes sure you're not playing
  142. // a demo before displaying the highscores.
  143. //
  144. // 09/02/97 JMI Now has separate cases for determing scoring for every
  145. // mode in ScoreDisplayHighScores().
  146. //
  147. // 09/04/97 BRH Fixed font size for the MPFrag scoring mode.
  148. //
  149. // 09/07/97 JMI Added ability to display high scores for multiplayer.
  150. // Also, added optional high score display timeout.
  151. //
  152. // 09/07/97 BRH Fixed problem with long multi player names wrapping
  153. // to the next line.
  154. //
  155. // 09/07/97 JMI Now colors this player's score in MP mode. Also, dialog
  156. // is much smaller so name length is restricted more. But
  157. // does allow up to 16 scores now.
  158. //
  159. // 09/08/97 BRH Fixed Goal and TimedGoal to display the Remaining: as
  160. // the number remaining in the goal, not the number of
  161. // people remaining in the realm.
  162. //
  163. // 09/08/97 BRH Adjusted the posiiton of the MP mission goals since
  164. // the font size is smaller, they weren't centered properly.
  165. //
  166. // 09/08/97 JMI Changed "Congratulations! Please enter your name for your
  167. // score." to "Please enter your name, Asshole." but the end
  168. // clips off (just kidding).
  169. //
  170. // 09/08/97 JMI Now sorts multiplayer names by score.
  171. //
  172. // 06/04/98 JMI Strings used for storage and sorting of multiplayer names
  173. // were sized at MAX_PLAYER_NAME_LEN (to fit the high score
  174. // GUI without overlapping the score) but Postal's net
  175. // client (from which the names are querried) allows longer
  176. // names (specifically Net::MaxPlayerNameSize). Now only
  177. // copies first MAX_PLAYER_NAME_LEN chars from the player's
  178. // name.
  179. //
  180. //////////////////////////////////////////////////////////////////////////////
  181. #define SCORE_CPP
  182. //////////////////////////////////////////////////////////////////////////////
  183. // C Headers
  184. //////////////////////////////////////////////////////////////////////////////
  185. #include "RSPiX.h"
  186. #include "score.h"
  187. #include "dude.h"
  188. #include "toolbar.h"
  189. #include "update.h"
  190. //////////////////////////////////////////////////////////////////////////////
  191. // Macros
  192. //////////////////////////////////////////////////////////////////////////////
  193. // Time, in ms, between status updates.
  194. #define SCORE_UPDATE_INTERVAL 1000
  195. #define STATUS_DISPLAY_TIMEOUT 8000
  196. #define STATUS_PRINT_X 2
  197. #define STATUS_PRINT_Y 0
  198. #define STATUS_PRINT_Y2 14
  199. #define STATUS_PRINT_Y3 28
  200. #define STATUS_FONT_SIZE 19
  201. #define STATUS_DISPLAY_HEIGHT 40
  202. #define MP_FONT_SIZE 11 //15
  203. #define MP_PRINT_X 2
  204. #define MP_PRINT_Y1 0
  205. #define MP_PRINT_Y2 11
  206. #define MP_PRINT_Y3 22
  207. //------------------------ These are color matched in the toolbar module
  208. #define HIGHSCORE_NAMEDIALOG_FILE "menu/addname.gui"
  209. #define HIGHSCORE_DIALOG_FILE "menu/hiscore.gui"
  210. #define HIGHSCORE_ITEM_FILE "menu/HiScoreItem.gui"
  211. #define HIGHSCORE_SCORES_FILE "res/savegame/high.ini"
  212. #define TEXT_SHADOW_COLOR 220
  213. #define TEXT_HIGHLIGHT_COLOR 9
  214. // This path gets prepended to the resource path before passing the
  215. // GUI res request to the resource manager. This makes it easy to
  216. // keep the path for the file simple while in the GUI Editor so the default
  217. // handling can take care of it there. The GUI Editor does not use the resmgr,
  218. // it simply loads it blindly with no regard to its current directory.
  219. #define GUI_RES_DIR "menu/"
  220. // Maximum name length.
  221. #define MAX_PLAYER_NAME_LEN 17
  222. #define MAX_HIGH_SCORES 16
  223. //////////////////////////////////////////////////////////////////////////////
  224. // Variables
  225. //////////////////////////////////////////////////////////////////////////////
  226. CScoreboard g_scoreboard;
  227. RPrint ms_print;
  228. static long ms_lScoreMaxTimeOut; // Optional score timeout (max time spent
  229. // on score screen).
  230. //////////////////////////////////////////////////////////////////////////////
  231. // Functions.
  232. //////////////////////////////////////////////////////////////////////////////
  233. //////////////////////////////////////////////////////////////////////////////
  234. // Helper to make a time value.
  235. //////////////////////////////////////////////////////////////////////////////
  236. inline char* CreateTimeString( // Returns time string. No failures.
  237. long lTimeVal) // In: Time value in milliseconds.
  238. {
  239. static char szTime[100];
  240. short sMinutes = lTimeVal / 60000;
  241. short sSeconds = (lTimeVal / 1000) % 60;
  242. sprintf(szTime, "%2.2d:%2.2d", sMinutes, sSeconds);
  243. return szTime;
  244. }
  245. //////////////////////////////////////////////////////////////////////////////
  246. // GuiReleaseRes - Release a resource that the requesting GUI wants to
  247. // discard.
  248. //////////////////////////////////////////////////////////////////////////////
  249. static void GuiReleaseRes( // Returns nothing.
  250. RGuiItem* pgui) // In: Requesting GUI.
  251. {
  252. rspReleaseResource(&g_resmgrShell, &pgui->m_pimBkdRes);
  253. }
  254. //////////////////////////////////////////////////////////////////////////////
  255. // GuiGetRes - Get a resource that the requesting GUI wants to use for its
  256. // background or state image.
  257. //////////////////////////////////////////////////////////////////////////////
  258. static short GuiGetRes( // Returns 0 on success; non-zero on failure.
  259. RGuiItem* pgui) // In: Requesting GUI.
  260. {
  261. short sResult = 0;
  262. // Release resources first (just in case)
  263. GuiReleaseRes(pgui);
  264. // Allocate and load new resources. We get the name of the file (which
  265. // is ASSUMED to have NO PATH!!) from the gui itself, then tack on the
  266. // path we need and get the resource from the resource manager.
  267. char szFile[RSP_MAX_PATH * 2];
  268. sprintf(szFile, "%s%s", GUI_RES_DIR, pgui->m_szBkdResName);
  269. if (rspGetResource(&g_resmgrShell, szFile, &pgui->m_pimBkdRes) == 0)
  270. {
  271. // Set palette via resource.
  272. ASSERT(pgui->m_pimBkdRes->m_pPalette != NULL);
  273. ASSERT(pgui->m_pimBkdRes->m_pPalette->m_type == RPal::PDIB);
  274. rspSetPaletteEntries(
  275. 0,
  276. 230,
  277. pgui->m_pimBkdRes->m_pPalette->Red(0),
  278. pgui->m_pimBkdRes->m_pPalette->Green(0),
  279. pgui->m_pimBkdRes->m_pPalette->Blue(0),
  280. pgui->m_pimBkdRes->m_pPalette->m_sPalEntrySize);
  281. // Update hardware palette.
  282. rspUpdatePalette();
  283. }
  284. else
  285. {
  286. sResult = -1;
  287. TRACE("GuiGetRes(): Failed to open file '%s'\n", szFile);
  288. }
  289. return sResult;
  290. }
  291. //////////////////////////////////////////////////////////////////////////////
  292. // EditInputUserFeedback -- Whines when the user causes an input
  293. // disgruntlement.
  294. //////////////////////////////////////////////////////////////////////////////
  295. static void EditInputUserFeedback( // Called when a user input notification
  296. // should occur.
  297. REdit* pedit) // In: Edit field.
  298. {
  299. PlaySample(g_smidEmptyWeapon, SampleMaster::UserFeedBack);
  300. }
  301. ////////////////////////////////////////////////////////////////////////////////
  302. //
  303. // Callback from RProcessGui for system update.
  304. //
  305. ////////////////////////////////////////////////////////////////////////////////
  306. static long SysUpdate( // Returns a non-zero ID to abort or zero
  307. // to continue.
  308. RInputEvent* pie) // Out: Next input event to process.
  309. {
  310. long lIdRes = 0; // Assume no GUI ID pressed (i.e., continue).
  311. UpdateSystem();
  312. rspGetNextInputEvent(pie);
  313. // If timeout has expired . . .
  314. if (rspGetMilliseconds() > ms_lScoreMaxTimeOut)
  315. {
  316. // Auto push OK.
  317. lIdRes = 1;
  318. }
  319. return lIdRes;
  320. }
  321. //////////////////////////////////////////////////////////////////////////////
  322. // ScoreInit - Set up the RPrint for the score
  323. //////////////////////////////////////////////////////////////////////////////
  324. void ScoreInit(void)
  325. {
  326. // Setup print.
  327. ms_print.SetFont(STATUS_FONT_SIZE, &g_fontBig);
  328. ms_print.SetColor(
  329. gsStatusFontForeIndex,
  330. gsStatusFontBackIndex,
  331. gsStatusFontShadowIndex);
  332. // Make sure shadow is one off:
  333. ms_print.SetEffectAbs(RPrint::SHADOW_X,+1);
  334. ms_print.SetEffectAbs(RPrint::SHADOW_Y,+1);
  335. // Warning! This may need to be set back again for gui items - if so, use
  336. // my OWN print!
  337. }
  338. //////////////////////////////////////////////////////////////////////////////
  339. // ScoreReset - Reset the scores and the display time
  340. //////////////////////////////////////////////////////////////////////////////
  341. void ScoreReset(void)
  342. {
  343. g_scoreboard.Reset();
  344. }
  345. //////////////////////////////////////////////////////////////////////////////
  346. // ScoreResetDisplay - Reset the timer for the display before the start
  347. // of each realm
  348. //////////////////////////////////////////////////////////////////////////////
  349. void ScoreResetDisplay(void)
  350. {
  351. g_scoreboard.m_lLastStatusDrawTime = 0;
  352. g_scoreboard.m_lLastScoreDrawTime = 0;
  353. }
  354. //////////////////////////////////////////////////////////////////////////////
  355. // ScoreRegisterKill - Characters should call this when they die
  356. //////////////////////////////////////////////////////////////////////////////
  357. void ScoreRegisterKill(CRealm* pRealm, U16 u16DeadGuy, U16 u16Killer)
  358. {
  359. CThing* pShooter = NULL;
  360. pRealm->m_idbank.GetThingByID(&pShooter, u16Killer);
  361. if (pShooter && pShooter->GetClassID() == CThing::CDudeID)
  362. {
  363. if (u16DeadGuy == u16Killer)
  364. g_scoreboard.SubtractOne(((CDude*) pShooter)->m_sDudeNum);
  365. else
  366. g_scoreboard.AddOne(((CDude*) pShooter)->m_sDudeNum);
  367. }
  368. }
  369. //////////////////////////////////////////////////////////////////////////////
  370. // ScoreUpdateDisplay - Display the score for the current mode
  371. // Returns true, if pImage was updated; false otherwise.
  372. //////////////////////////////////////////////////////////////////////////////
  373. bool ScoreUpdateDisplay(RImage* pim, RRect* prc, CRealm* pRealm, CNetClient* pclient,
  374. short sDstX,short sDstY,CHood* pHood)
  375. {
  376. RRect rcBox;
  377. RRect rcDst;
  378. rcDst.sX = prc->sX + STATUS_PRINT_X;
  379. rcDst.sY = prc->sY + STATUS_PRINT_Y;
  380. rcDst.sW = prc->sW - 2 * STATUS_PRINT_X;
  381. rcDst.sH = prc->sH - STATUS_PRINT_Y;
  382. short sMinutes = pRealm->m_lScoreTimeDisplay / 60000;
  383. short sSeconds = (pRealm->m_lScoreTimeDisplay / 1000) % 60;
  384. bool bDrew = false; // Assume we do not draw.
  385. long lCurTime = pRealm->m_time.GetGameTime();
  386. if (lCurTime > g_scoreboard.m_lLastScoreDrawTime + SCORE_UPDATE_INTERVAL)
  387. {
  388. ms_print.SetColor( // set current color
  389. gsStatusFontForeIndex,
  390. gsStatusFontBackIndex,
  391. gsStatusFontShadowIndex);
  392. // Set print/clip to area.
  393. // instead of clearing, drop the special backdrop.
  394. //rspRect(RSP_BLACK_INDEX, pim, rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
  395. if (pHood) rspBlit(pHood->m_pimTopBar,pim,0,0,sDstX,sDstY,
  396. pHood->m_pimTopBar->m_sWidth,pHood->m_pimTopBar->m_sHeight);
  397. short sNumDudes = pRealm->m_asClassNumThings[CThing::CDudeID];
  398. short i;
  399. switch (pRealm->m_ScoringMode)
  400. {
  401. case CRealm::MPFrag:
  402. ms_print.SetFont(MP_FONT_SIZE, &g_fontBig);
  403. rcBox.sY = prc->sY + MP_PRINT_Y1;
  404. rcBox.sH = MP_FONT_SIZE + 1;
  405. rcBox.sW = rcDst.sW / sNumDudes;
  406. ms_print.SetJustifyLeft();
  407. ms_print.SetWordWrap(FALSE);
  408. for (i = 0; i < sNumDudes; i++)
  409. {
  410. rcBox.sX = rcDst.sX + (i * rcBox.sW);
  411. rcBox.sY = prc->sY + STATUS_PRINT_Y;
  412. ms_print.SetDestination(pim, &rcBox);
  413. ms_print.print(
  414. rcBox.sX,
  415. rcBox.sY,
  416. "%s",
  417. pclient->GetPlayerName(i)
  418. );
  419. rcBox.sY = prc->sY + STATUS_PRINT_Y2;
  420. ms_print.SetDestination(pim, &rcBox);
  421. ms_print.print(
  422. rcBox.sX,
  423. rcBox.sY,
  424. "%d",
  425. g_scoreboard.m_asScores[i]
  426. );
  427. }
  428. break;
  429. // These have the same display, but different ending conditions
  430. case CRealm::MPTimed:
  431. case CRealm::MPTimedFrag:
  432. ms_print.SetFont(MP_FONT_SIZE, &g_fontBig);
  433. rcBox.sY = prc->sY + MP_PRINT_Y1;
  434. rcBox.sH = MP_FONT_SIZE + 1;
  435. rcBox.sW = rcDst.sW / sNumDudes;
  436. ms_print.SetJustifyLeft();
  437. ms_print.SetWordWrap(FALSE);
  438. for (i = 0; i < sNumDudes; i++)
  439. {
  440. rcBox.sX = rcDst.sX + (i * rcBox.sW);
  441. rcBox.sY = prc->sY + MP_PRINT_Y1;
  442. ms_print.SetDestination(pim, &rcBox);
  443. ms_print.print(
  444. rcBox.sX,
  445. rcBox.sY,
  446. "%s",
  447. pclient->GetPlayerName(i)
  448. );
  449. rcBox.sY = prc->sY + MP_PRINT_Y2;
  450. ms_print.SetDestination(pim, &rcBox);
  451. ms_print.print(
  452. rcBox.sX,
  453. rcBox.sY,
  454. "%d",
  455. g_scoreboard.m_asScores[i]
  456. );
  457. }
  458. rcBox.sY = prc->sY + MP_PRINT_Y3;
  459. rcBox.sX = prc->sX;
  460. ms_print.SetDestination(pim, &rcBox);
  461. ms_print.print(
  462. rcDst.sX,
  463. rcDst.sY,
  464. // "Time Remaining %d:%2.2d",
  465. g_apszScoreDisplayText[pRealm->m_ScoringMode],
  466. sMinutes,
  467. sSeconds);
  468. break;
  469. case CRealm::MPLastMan:
  470. rcBox.sY = prc->sY + STATUS_PRINT_Y;
  471. rcBox.sH = STATUS_FONT_SIZE + 1;
  472. rcBox.sW = rcDst.sW / sNumDudes;
  473. ms_print.SetJustifyLeft();
  474. ms_print.SetWordWrap(FALSE);
  475. for (i = 0; i < sNumDudes; i++)
  476. {
  477. // If this player is dead, set the color to dead color
  478. /*
  479. ms_print.SetColor( // set current color
  480. gsStatusFontForeDeadIndex,
  481. gsStatusFontBackIndex,
  482. gsStatusFontShadowIndex);
  483. else
  484. ms_print.SetColor( // set current color
  485. gsStatusFontForeIndex,
  486. gsStatusFontBackIndex,
  487. gsStatusFontShadowIndex);
  488. */
  489. rcBox.sX = rcDst.sX + (i * rcBox.sW);
  490. rcBox.sY = prc->sY + STATUS_PRINT_Y;
  491. ms_print.SetDestination(pim, &rcBox);
  492. ms_print.print(
  493. rcBox.sX,
  494. rcBox.sY,
  495. "%s",
  496. pclient->GetPlayerName(i)
  497. );
  498. rcBox.sY = prc->sY + STATUS_PRINT_Y2;
  499. ms_print.SetDestination(pim, &rcBox);
  500. ms_print.print(
  501. rcBox.sX,
  502. rcBox.sY,
  503. "%d",
  504. g_scoreboard.m_asScores[i]
  505. );
  506. }
  507. break;
  508. case CRealm::MPLastManTimed:
  509. break;
  510. case CRealm::MPLastManFrag:
  511. break;
  512. case CRealm::MPLastManTimedFrag:
  513. break;
  514. case CRealm::Standard:
  515. ms_print.SetDestination(pim, &rcDst);
  516. ms_print.print(
  517. pim,
  518. rcDst.sX,
  519. rcDst.sY,
  520. // " Population %d Hostiles %d Killed %d (%d%% / %d%%)",
  521. g_apszScoreDisplayText[pRealm->m_ScoringMode],
  522. pRealm->m_sPopulation,
  523. pRealm->m_sHostiles,
  524. pRealm->m_sHostileKills,
  525. pRealm->m_sHostileKills * 100 / ((pRealm->m_sHostileBirths != 0) ? pRealm->m_sHostileBirths : 1),
  526. (short) pRealm->m_dKillsPercentGoal
  527. );
  528. break;
  529. case CRealm::Timed:
  530. ms_print.SetDestination(pim, &rcDst);
  531. ms_print.print(
  532. pim,
  533. rcDst.sX,
  534. rcDst.sY,
  535. // " Time Remaining %d:%2.2d Kills %d",
  536. g_apszScoreDisplayText[pRealm->m_ScoringMode],
  537. sMinutes,
  538. sSeconds,
  539. pRealm->m_sPopulationDeaths
  540. );
  541. break;
  542. case CRealm::TimedGoal:
  543. ms_print.SetDestination(pim, &rcDst);
  544. ms_print.print(
  545. pim,
  546. rcDst.sX,
  547. rcDst.sY,
  548. // " Time Remaining %d:%2.2d Kills %d Remaining %d / %d",
  549. g_apszScoreDisplayText[pRealm->m_ScoringMode],
  550. sMinutes,
  551. sSeconds,
  552. pRealm->m_sPopulationDeaths,
  553. pRealm->m_sKillsGoal - pRealm->m_sPopulationDeaths, pRealm->m_sPopulation
  554. );
  555. break;
  556. case CRealm::TimedFlag:
  557. ms_print.SetDestination(pim, &rcDst);
  558. ms_print.print(
  559. pim,
  560. rcDst.sX,
  561. rcDst.sY,
  562. // " Time Remaining %d:%2.2d",
  563. g_apszScoreDisplayText[pRealm->m_ScoringMode],
  564. sMinutes,
  565. sSeconds
  566. );
  567. break;
  568. case CRealm::Goal:
  569. ms_print.SetDestination(pim, &rcDst);
  570. ms_print.print(
  571. pim,
  572. rcDst.sX,
  573. rcDst.sY,
  574. // " Kills %d Remaining %d Time Elapsed %d:%2.2d",
  575. g_apszScoreDisplayText[pRealm->m_ScoringMode],
  576. pRealm->m_sPopulationDeaths,
  577. pRealm->m_sKillsGoal - pRealm->m_sPopulationDeaths /*pRealm->m_sPopulation*/,
  578. sMinutes,
  579. sSeconds
  580. );
  581. break;
  582. case CRealm::CaptureFlag:
  583. ms_print.SetDestination(pim, &rcDst);
  584. ms_print.print(
  585. pim,
  586. rcDst.sX,
  587. rcDst.sY,
  588. // " Time Elapsed %d:%2.2d",
  589. g_apszScoreDisplayText[pRealm->m_ScoringMode],
  590. sMinutes,
  591. sSeconds
  592. );
  593. break;
  594. case CRealm::Checkpoint:
  595. ms_print.SetDestination(pim, &rcDst);
  596. ms_print.print(
  597. pim,
  598. rcDst.sX,
  599. rcDst.sY,
  600. // " Clock %d:%2.2d You have %d flags Flags Remaining %d",
  601. g_apszScoreDisplayText[pRealm->m_ScoringMode],
  602. sMinutes,
  603. sSeconds,
  604. pRealm->m_sFlagsCaptured,
  605. pRealm->m_asClassNumThings[CThing::CFlagID] - pRealm->m_sFlagsCaptured
  606. );
  607. break;
  608. default:
  609. break;
  610. }
  611. g_scoreboard.m_lLastScoreDrawTime = lCurTime;
  612. // Display the status or mission statement line
  613. if (lCurTime < g_scoreboard.m_lLastStatusDrawTime + STATUS_DISPLAY_TIMEOUT)
  614. {
  615. switch (pRealm->m_ScoringMode)
  616. {
  617. case CRealm::MPFrag:
  618. rcDst.sY = prc->sY + MP_PRINT_Y3;
  619. if (pRealm->m_sKillsGoal < 1)
  620. {
  621. rcDst.sX = prc->sX + 0;
  622. ms_print.SetDestination(pim, &rcDst);
  623. ms_print.print(
  624. rcDst.sX,
  625. rcDst.sY,
  626. // " There are no time or kill limits on this game - play as long as you like"
  627. g_apszScoreGoalText[CRealm::MPLastManTimedFrag] // Cheating since this is
  628. // Really none of the scoring modes
  629. );
  630. }
  631. else
  632. {
  633. rcDst.sX = prc->sX + 220;
  634. ms_print.SetDestination(pim, &rcDst);
  635. ms_print.print(
  636. rcDst.sX,
  637. rcDst.sY,
  638. // " The first player to get %d kills wins",
  639. g_apszScoreGoalText[pRealm->m_ScoringMode],
  640. pRealm->m_sKillsGoal
  641. );
  642. }
  643. break;
  644. case CRealm::MPTimed:
  645. rcDst.sY = prc->sY + MP_PRINT_Y3;
  646. rcDst.sX = prc->sX + 180;
  647. ms_print.SetDestination(pim, &rcDst);
  648. ms_print.print(
  649. rcDst.sX,
  650. rcDst.sY,
  651. // " The player with the most kills when time expires is the winner"
  652. g_apszScoreGoalText[pRealm->m_ScoringMode]
  653. );
  654. break;
  655. case CRealm::MPTimedFrag:
  656. rcDst.sY = prc->sY + MP_PRINT_Y3;
  657. rcDst.sX = prc->sX + 220;
  658. ms_print.SetDestination(pim, &rcDst);
  659. ms_print.print(
  660. rcDst.sX,
  661. rcDst.sY,
  662. // " Try to reach %d kills before time expires",
  663. g_apszScoreGoalText[pRealm->m_ScoringMode],
  664. pRealm->m_sKillsGoal
  665. );
  666. break;
  667. case CRealm::Standard:
  668. rcDst.sY = prc->sY + STATUS_PRINT_Y2;
  669. ms_print.SetDestination(pim, &rcDst);
  670. ms_print.print(
  671. rcDst.sX,
  672. rcDst.sY,
  673. // " You must kill %d%% of the hostiles.",
  674. g_apszScoreGoalText[pRealm->m_ScoringMode],
  675. (short) pRealm->m_dKillsPercentGoal
  676. );
  677. break;
  678. case CRealm::Timed:
  679. rcDst.sY = prc->sY + STATUS_PRINT_Y2;
  680. ms_print.SetDestination(pim, &rcDst);
  681. ms_print.print(
  682. rcDst.sX,
  683. rcDst.sY,
  684. // " Score as many kills as possible in the time remaining."
  685. g_apszScoreGoalText[pRealm->m_ScoringMode]
  686. );
  687. break;
  688. case CRealm::TimedGoal:
  689. rcDst.sY = prc->sY + STATUS_PRINT_Y2;
  690. ms_print.SetDestination(pim, &rcDst);
  691. ms_print.print(
  692. rcDst.sX,
  693. rcDst.sY,
  694. // " Kill everyone before time runs out."
  695. g_apszScoreGoalText[pRealm->m_ScoringMode]
  696. );
  697. break;
  698. case CRealm::TimedFlag:
  699. rcDst.sY = prc->sY + STATUS_PRINT_Y2;
  700. ms_print.SetDestination(pim, &rcDst);
  701. ms_print.print(
  702. rcDst.sX,
  703. rcDst.sY,
  704. // " Capture the flag before time runs out."
  705. g_apszScoreGoalText[pRealm->m_ScoringMode]
  706. );
  707. break;
  708. case CRealm::Goal:
  709. rcDst.sY = prc->sY + STATUS_PRINT_Y2;
  710. ms_print.SetDestination(pim, &rcDst);
  711. ms_print.print(
  712. rcDst.sX,
  713. rcDst.sY,
  714. // " Kill %d People in as little time as possible.",
  715. g_apszScoreGoalText[pRealm->m_ScoringMode],
  716. pRealm->m_sKillsGoal
  717. );
  718. break;
  719. case CRealm::CaptureFlag:
  720. rcDst.sY = prc->sY + STATUS_PRINT_Y2;
  721. ms_print.SetDestination(pim, &rcDst);
  722. ms_print.print(
  723. rcDst.sX,
  724. rcDst.sY,
  725. // " Capture the flag in as little time as possible."
  726. g_apszScoreGoalText[pRealm->m_ScoringMode]
  727. );
  728. break;
  729. case CRealm::Checkpoint:
  730. rcDst.sY = prc->sY + STATUS_PRINT_Y2;
  731. ms_print.SetDestination(pim, &rcDst);
  732. ms_print.print(
  733. rcDst.sX,
  734. rcDst.sY,
  735. // " Grab as many flags as possible before time runs out."
  736. g_apszScoreGoalText[pRealm->m_ScoringMode]
  737. );
  738. break;
  739. default:
  740. break;
  741. }
  742. }
  743. // Note that we drew.
  744. bDrew = true;
  745. }
  746. return bDrew;
  747. }
  748. //////////////////////////////////////////////////////////////////////////////
  749. // ScoreDisplayStatus
  750. //
  751. // Sets the status display timer so it will show for a few seconds
  752. //////////////////////////////////////////////////////////////////////////////
  753. void ScoreDisplayStatus(CRealm* pRealm)
  754. {
  755. g_scoreboard.m_lLastStatusDrawTime = pRealm->m_time.GetGameTime();
  756. }
  757. //////////////////////////////////////////////////////////////////////////////
  758. // ScoreSetMode - Set mode of scoring
  759. //////////////////////////////////////////////////////////////////////////////
  760. void ScoreSetMode(CScoreboard::ScoringMode Mode)
  761. {
  762. g_scoreboard.SetScoringMode(Mode);
  763. }
  764. //////////////////////////////////////////////////////////////////////////////
  765. // ScoreDisplayHighScores
  766. //////////////////////////////////////////////////////////////////////////////
  767. void ScoreDisplayHighScores( // Returns nothing.
  768. CRealm* pRealm, // In: Realm won.
  769. CNetClient* pclient, // In: Client ptr for MP mode, or NULL in SP mode.
  770. long lMaxTimeOut) // In: Max time on score screen (quits after this
  771. // duration, if not -1).
  772. {
  773. RGuiItem* pguiRoot;
  774. RGuiItem::ms_print.SetFont(15, &g_fontPostal);
  775. RProcessGui guiDialog;
  776. short sResult;
  777. char szScoringExplanation[512] = "";
  778. long alScores[MAX_HIGH_SCORES];
  779. char astrNames[MAX_HIGH_SCORES][MAX_PLAYER_NAME_LEN + 1];
  780. char szKeyName[256];
  781. RPrefs scores;
  782. typedef enum
  783. {
  784. Value,
  785. Time
  786. } ValType;
  787. ValType vtScoringUnit = Value;
  788. // Let's just not do any of this for modes that have no scoring . . .
  789. if (pRealm->m_ScoringMode >= CRealm::Timed && pRealm->m_ScoringMode <= CRealm::MPLastManTimedFrag && GetInputMode() != INPUT_MODE_PLAYBACK)
  790. {
  791. // Determine player's score and note how we determined it in a string
  792. // for the user.
  793. long lPlayerScore = 0;
  794. switch (pRealm->m_ScoringMode)
  795. {
  796. case CRealm::Standard:
  797. // No high scores for this mode
  798. break;
  799. case CRealm::Timed:
  800. sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode], CreateTimeString(pRealm->m_lScoreInitialTime) );
  801. // Number of deaths.
  802. lPlayerScore = pRealm->m_sPopulationDeaths;
  803. vtScoringUnit = Value;
  804. break;
  805. case CRealm::TimedGoal:
  806. sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode], pRealm->m_sKillsGoal);
  807. // Elapsed time, if goal met.
  808. if (pRealm->m_sPopulationDeaths >= pRealm->m_sKillsGoal)
  809. {
  810. lPlayerScore = pRealm->m_lScoreInitialTime - pRealm->m_lScoreTimeDisplay;
  811. }
  812. else
  813. {
  814. // Really bad elapsed time.
  815. lPlayerScore = LONG_MAX;
  816. }
  817. vtScoringUnit = Time;
  818. break;
  819. case CRealm::TimedFlag:
  820. sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode], pRealm->m_sKillsGoal);
  821. // Elapsed time, if goal met.
  822. if (pRealm->m_sFlagbaseCaptured >= pRealm->m_sFlagsGoal)
  823. {
  824. lPlayerScore = pRealm->m_lScoreInitialTime - pRealm->m_lScoreTimeDisplay;
  825. }
  826. else
  827. {
  828. // Really bad elapsed time.
  829. lPlayerScore = LONG_MAX;
  830. }
  831. vtScoringUnit = Time;
  832. break;
  833. case CRealm::CaptureFlag:
  834. sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode], pRealm->m_sKillsGoal);
  835. // Time left, if goal met.
  836. if (pRealm->m_sFlagbaseCaptured >= pRealm->m_sFlagsGoal)
  837. {
  838. lPlayerScore = pRealm->m_lScoreTimeDisplay;
  839. }
  840. else
  841. {
  842. // Really bad time.
  843. lPlayerScore = LONG_MIN;
  844. }
  845. vtScoringUnit = Time;
  846. break;
  847. case CRealm::Goal:
  848. sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode], pRealm->m_sKillsGoal);
  849. // Time left, if goal met.
  850. if (pRealm->m_sPopulationDeaths >= pRealm->m_sKillsGoal)
  851. {
  852. lPlayerScore = pRealm->m_lScoreTimeDisplay;
  853. }
  854. else
  855. {
  856. // Really bad time.
  857. lPlayerScore = LONG_MIN;
  858. }
  859. vtScoringUnit = Time;
  860. break;
  861. case CRealm::Checkpoint:
  862. sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode], 0);
  863. // Number of flags captured.
  864. lPlayerScore = pRealm->m_sFlagsCaptured;
  865. vtScoringUnit = Value;
  866. break;
  867. case CRealm::MPTimed:
  868. case CRealm::MPFrag:
  869. case CRealm::MPLastMan:
  870. case CRealm::MPCaptureFlag:
  871. case CRealm::MPTimedFlag:
  872. case CRealm::MPTimedFrag:
  873. case CRealm::MPLastManFrag:
  874. case CRealm::MPLastManTimed:
  875. case CRealm::MPLastManTimedFrag:
  876. sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode], MAX_HIGH_SCORES);
  877. vtScoringUnit = Value;
  878. break;
  879. }
  880. // Get the name or description string for this realm file and use that as
  881. // the prefs section name from which to get the high scores. Scores are
  882. // stored as longs whether they be times in seconds or counts and the names
  883. // are stored as strings.
  884. // Temporarily I will base the section name on the scoring method
  885. // until the realm description function is available.
  886. short sPlayersScorePosition = -1; // Valid score index once/if we find a slot
  887. // for this player's score.
  888. // If not a multiplayer scoring . . .
  889. if (pRealm->m_flags.bMultiplayer == false)
  890. {
  891. // Try to open the file, but if it doesn't exist, then we simply won't be
  892. // getting any values from it.
  893. short sOpenRes = scores.Open(FullPathHD(HIGHSCORE_SCORES_FILE), "r");
  894. // Read in the scores file
  895. short sSrcIndex;
  896. short sDstIndex;
  897. for (sSrcIndex = 0, sDstIndex = 0; sDstIndex < MAX_HIGH_SCORES; sDstIndex++)
  898. {
  899. sprintf(szKeyName, "Player%d", sSrcIndex);
  900. if (sOpenRes == 0)
  901. scores.GetVal((char*) pRealm->m_rsRealmString, szKeyName, "<Empty>", astrNames[sDstIndex]);
  902. else
  903. strcpy(astrNames[sDstIndex], "<Empty>");
  904. // Determine if our score beat this score.
  905. bool bPlayerBeatThisScore = false;
  906. sprintf(szKeyName, "Score%d", sSrcIndex);
  907. // Some scoring modes need to default to zero scores, but the timed levels
  908. // need to default to some high, easy to beat time if there is no saved score
  909. if (vtScoringUnit == Value)
  910. {
  911. if (sOpenRes == 0)
  912. scores.GetVal((char*) pRealm->m_rsRealmString, szKeyName, (long) 0, &(alScores[sDstIndex]));
  913. else
  914. alScores[sDstIndex] = 0;
  915. // Did we get a higher value than in score?
  916. if (lPlayerScore > alScores[sDstIndex])
  917. {
  918. bPlayerBeatThisScore = true;
  919. }
  920. }
  921. else
  922. {
  923. if (sOpenRes == 0)
  924. scores.GetVal((char*) pRealm->m_rsRealmString, szKeyName, (long) 3600000, &(alScores[sDstIndex]));
  925. else
  926. alScores[sDstIndex] = (long) 3600000;
  927. // Did we get a better time than read in score?
  928. if (lPlayerScore < alScores[sDstIndex] )
  929. {
  930. bPlayerBeatThisScore = true;
  931. }
  932. }
  933. // If we beat this score and haven't yet found a score position . . .
  934. if (bPlayerBeatThisScore == true && sPlayersScorePosition < 0)
  935. {
  936. // Remember player's score.
  937. alScores[sDstIndex] = lPlayerScore;
  938. // Clear player's name.
  939. astrNames[sDstIndex][0] = '\0';
  940. // Remember player's score position.
  941. sPlayersScorePosition = sDstIndex;
  942. // Don't increment the source index.
  943. }
  944. else
  945. {
  946. // Move to the next source score val and name from the INI.
  947. sSrcIndex++;
  948. }
  949. }
  950. scores.Close();
  951. }
  952. else
  953. {
  954. ASSERT(pclient);
  955. long alTempScores[MAX_HIGH_SCORES];
  956. char astrTempNames[MAX_HIGH_SCORES][MAX_PLAYER_NAME_LEN + 1];
  957. short sIndex;
  958. for (sIndex = 0; sIndex < MAX_HIGH_SCORES ; sIndex++)
  959. {
  960. if (sIndex < pRealm->m_asClassNumThings[CThing::CDudeID])
  961. {
  962. strncpy(astrTempNames[sIndex], pclient->GetPlayerName(sIndex), MAX_PLAYER_NAME_LEN);
  963. // Strncpy does not NULL terminate if the 'n' is less than or equal to the length
  964. // of the src string.
  965. astrTempNames[sIndex][MAX_PLAYER_NAME_LEN] = '\0';
  966. alTempScores[sIndex] = g_scoreboard.m_asScores[sIndex];
  967. }
  968. else
  969. {
  970. astrTempNames[sIndex][0] = '\0';
  971. alTempScores[sIndex] = LONG_MIN + 1;
  972. }
  973. }
  974. // Find the largest score (most frags in all modes) and put it at the
  975. // next position.
  976. short sDstIndex;
  977. short sSrcIndex;
  978. short sHighestScoreIndex;
  979. long lHighestScore;
  980. // This declaration relies on false being zero!!
  981. ASSERT(false == 0);
  982. bool abAlreadyCopied[MAX_HIGH_SCORES] = { false, };
  983. for (sDstIndex = 0; sDstIndex < MAX_HIGH_SCORES; sDstIndex++)
  984. {
  985. sHighestScoreIndex = -1;
  986. lHighestScore = LONG_MIN;
  987. // Find the highest score of the ones not yet copied.
  988. for (sSrcIndex = 0; sSrcIndex < MAX_HIGH_SCORES; sSrcIndex++)
  989. {
  990. // If not yet copied . . .
  991. if (abAlreadyCopied[sSrcIndex] == false)
  992. {
  993. // If this score is higher . . .
  994. if (alTempScores[sSrcIndex] > lHighestScore)
  995. {
  996. sHighestScoreIndex = sSrcIndex;
  997. lHighestScore = alTempScores[sSrcIndex];
  998. }
  999. }
  1000. }
  1001. // Use the highest score.
  1002. ASSERT(sHighestScoreIndex != -1);
  1003. alScores[sDstIndex] = alTempScores[sHighestScoreIndex];
  1004. // This copy is safe b/c astrTempNames[] and astrNames[] are the
  1005. // same length.
  1006. // Check for safety (future changes).
  1007. ASSERT(sizeof(astrNames[0]) >= sizeof(astrTempNames[0]) );
  1008. strcpy(astrNames[sDstIndex], astrTempNames[sHighestScoreIndex] );
  1009. // Note that this score is already placed.
  1010. abAlreadyCopied[sHighestScoreIndex] = true;
  1011. // If this is us . . .
  1012. if (sHighestScoreIndex == pclient->GetID() )
  1013. {
  1014. // Remember our position (placement) so we can highlight it.
  1015. sPlayersScorePosition = sDstIndex;
  1016. }
  1017. }
  1018. // Note that Player's score position stays -1 since there's no name to enter.
  1019. }
  1020. short i;
  1021. if (rspGetResource(&g_resmgrShell, HIGHSCORE_DIALOG_FILE, (RDlg**)&pguiRoot) == 0)
  1022. {
  1023. RGuiItem* pguiOk = pguiRoot->GetItemFromId(1);
  1024. RGuiItem* pguiCancel = pguiRoot->GetItemFromId(2);
  1025. RTxt* ptextExplain1 = (RTxt*) pguiRoot->GetItemFromId(50);
  1026. RTxt* ptextExplain2 = (RTxt*) pguiRoot->GetItemFromId(51);
  1027. RListBox* plbScores = (RListBox*) pguiRoot->GetItemFromId(1000);
  1028. // Set to the input field if the player gets a high score.
  1029. RGuiItem* pguiPlayersName = NULL;
  1030. // Create and add all score items.
  1031. short sScoreIndex;
  1032. bool bGotAllScoreItems = true;
  1033. if (plbScores)
  1034. {
  1035. ASSERT(plbScores->m_type == RGuiItem::ListBox);
  1036. for (sScoreIndex = 0; sScoreIndex < MAX_HIGH_SCORES && bGotAllScoreItems; sScoreIndex++)
  1037. {
  1038. // If there's an associated name or this is the one we're adding . . .
  1039. if (astrNames[sScoreIndex][0] != '\0' || sPlayersScorePosition == sScoreIndex)
  1040. {
  1041. RGuiItem* pguiItem;
  1042. if (rspGetResourceInstance(&g_resmgrShell, HIGHSCORE_ITEM_FILE, &pguiItem) == 0)
  1043. {
  1044. // Get the two settable items.
  1045. RGuiItem* pguiName = pguiItem->GetItemFromId(100);
  1046. RGuiItem* pguiScore = pguiItem->GetItemFromId(101);
  1047. RGuiItem* pguiPlace = pguiItem->GetItemFromId(102);
  1048. if (pguiName && pguiScore)
  1049. {
  1050. // Add shadow attributes.
  1051. pguiName->m_sTextEffects |= RGuiItem::Shadow;
  1052. pguiName->m_u32TextShadowColor = TEXT_SHADOW_COLOR;
  1053. pguiScore->m_sTextEffects |= RGuiItem::Shadow;
  1054. pguiScore->m_u32TextShadowColor = TEXT_SHADOW_COLOR;
  1055. // If this is the place for the new name . . .
  1056. if (sPlayersScorePosition == sScoreIndex)
  1057. {
  1058. // Set the focus to this item.
  1059. pguiName->SetFocus();
  1060. // Limit input text to the space in our storage area.
  1061. // Must be edit field for this op.
  1062. ASSERT(pguiName->m_type == RGuiItem::Edit);
  1063. ((REdit*)pguiName)->m_sMaxText = sizeof(astrNames[0]) - 1;
  1064. // Highlight this entry.
  1065. pguiName->m_u32TextColor = TEXT_HIGHLIGHT_COLOR;
  1066. // Remember which one so we can get the name later.
  1067. pguiPlayersName = pguiName;
  1068. }
  1069. else
  1070. {
  1071. // Deactivate all others.
  1072. pguiName->m_sActive = FALSE;
  1073. }
  1074. // Set placement.
  1075. pguiPlace->SetText("%d)", sScoreIndex + 1);
  1076. pguiPlace->Compose();
  1077. // Set name.
  1078. pguiName->SetText("%s", astrNames[sScoreIndex]);
  1079. pguiName->Compose();
  1080. // Set score.
  1081. // There are two types of scores.
  1082. if (vtScoringUnit == Value)
  1083. {
  1084. // Value.
  1085. pguiScore->SetText("%ld %s", alScores[sScoreIndex], g_apszScoreUnits[pRealm->m_ScoringMode] );
  1086. }
  1087. else
  1088. {
  1089. // Time.
  1090. pguiScore->SetText("%s %s", CreateTimeString(alScores[sScoreIndex] ), g_apszScoreUnits[pRealm->m_ScoringMode] );
  1091. }
  1092. pguiScore->Compose();
  1093. // Mark item as an encapsulator. When an item is marked this way the listbox
  1094. // knows it's okay to move it around and stuff.
  1095. RListBox::MakeEncapsulator(pguiItem);
  1096. // Add to the list box . . .
  1097. if (plbScores->AddItem(pguiItem) )
  1098. {
  1099. // Success.
  1100. }
  1101. else
  1102. {
  1103. TRACE("ScoreDisplayHighScores(): Unable to add item to listbox.\n");
  1104. bGotAllScoreItems = false;
  1105. }
  1106. }
  1107. else
  1108. {
  1109. TRACE("ScoreDisplayHighScores(): Missing items in this instance of \"%d\".\n",
  1110. HIGHSCORE_ITEM_FILE);
  1111. bGotAllScoreItems = false;
  1112. }
  1113. }
  1114. else
  1115. {
  1116. TRACE("ScoreDisplayHighScores(): Failed to get instance of \"%d\".\n",
  1117. HIGHSCORE_ITEM_FILE);
  1118. bGotAllScoreItems = false;
  1119. }
  1120. }
  1121. }
  1122. // Repaginate now.
  1123. plbScores->AdjustContents();
  1124. // If we have an entry . . .
  1125. if (pguiPlayersName)
  1126. {
  1127. // Make sure it's visible . . .
  1128. plbScores->EnsureVisible(pguiPlayersName, RListBox::Bottom);
  1129. }
  1130. }
  1131. if (ptextExplain1 != NULL &&
  1132. ptextExplain2 != NULL &&
  1133. plbScores != NULL &&
  1134. bGotAllScoreItems == true)
  1135. {
  1136. // Get some colors free.
  1137. PalTranOn();
  1138. // Set the callbacks for the resource load and discard (note that it
  1139. // already tried to load it during the above rspGetResource() and failed
  1140. // b/c the default implementation has no clue about the resmgr).
  1141. pguiRoot->m_fnGetRes = GuiGetRes;
  1142. pguiRoot->m_fnReleaseRes = GuiReleaseRes;
  1143. pguiRoot->ReleaseRes();
  1144. // Recompose the root item (does not recompose children). This time
  1145. // it should successfully find the resources via the Get callback.
  1146. pguiRoot->Compose();
  1147. // Let us handle updates.
  1148. guiDialog.m_fnUpdate = SysUpdate;
  1149. // Center the GUI root.
  1150. pguiRoot->Move(
  1151. g_pimScreenBuf->m_sWidth / 2 - pguiRoot->m_im.m_sWidth / 2,
  1152. g_pimScreenBuf->m_sHeight / 2 - pguiRoot->m_im.m_sHeight / 2);
  1153. // Make the explanation texts shadowed.
  1154. ptextExplain1->m_sTextEffects |= RGuiItem::Shadow;
  1155. ptextExplain1->m_u32TextShadowColor = TEXT_SHADOW_COLOR;
  1156. ptextExplain2->m_sTextEffects |= RGuiItem::Shadow;
  1157. ptextExplain2->m_u32TextShadowColor = TEXT_SHADOW_COLOR;
  1158. ptextExplain2->Compose();
  1159. // Store current mouse show level so we can restore it.
  1160. short sOrigShowLevel = rspGetMouseCursorShowLevel();
  1161. // Make sure it's visible.
  1162. // Let's not do this and instead try to insinuate keyboard use.
  1163. // rspSetMouseCursorShowLevel(1);
  1164. // Make sure there's no timeout on while player is adding their name.
  1165. ms_lScoreMaxTimeOut = LONG_MAX;
  1166. // If we want a high score from this player . . .
  1167. if (sPlayersScorePosition >= 0 && pRealm->m_flags.bMultiplayer == false)
  1168. {
  1169. // Ask the player for their name.
  1170. ptextExplain1->SetText("Please enter your name.\n");
  1171. ptextExplain1->Compose();
  1172. // Don't clean the screen when done so we can have a smooth transition
  1173. // to the next DoModal().
  1174. guiDialog.m_sFlags = RProcessGui::NoCleanScreen;
  1175. // Do the dialog once to get the name.
  1176. guiDialog.DoModal(pguiRoot, pguiOk, pguiCancel);
  1177. // Clear the focus.
  1178. RGuiItem::SetFocus(NULL);
  1179. ASSERT(pguiPlayersName);
  1180. // Get the user's name for saving.
  1181. pguiPlayersName->GetText(astrNames[sPlayersScorePosition], sizeof(astrNames[sPlayersScorePosition]) );
  1182. }
  1183. // Set the explanation text.
  1184. ptextExplain1->SetText("%s", szScoringExplanation);
  1185. ptextExplain1->Compose();
  1186. // Don't set time out time until after player has entered name for
  1187. // reduced frustration.
  1188. // If timeout specified . . .
  1189. if (lMaxTimeOut >= 0)
  1190. {
  1191. ms_lScoreMaxTimeOut = rspGetMilliseconds() + lMaxTimeOut;
  1192. }
  1193. else
  1194. {
  1195. ms_lScoreMaxTimeOut = LONG_MAX;
  1196. }
  1197. // Set the focus to the listbox's vertical scrollbar so that the arrows will work.
  1198. plbScores->m_sbVert.SetFocus();
  1199. // This time we want the screen cleared.
  1200. guiDialog.m_sFlags = 0;
  1201. // Display the high scores.
  1202. guiDialog.DoModal(pguiRoot, pguiOk, pguiCancel);
  1203. // Restore mouse cursor show level.
  1204. rspSetMouseCursorShowLevel(sOrigShowLevel);
  1205. // If we got a high score . . .
  1206. if (sPlayersScorePosition >= 0 && pRealm->m_flags.bMultiplayer == false)
  1207. {
  1208. RPrefs prefsScores;
  1209. // Save the scores to the file. First we open it in read+ mode, which is
  1210. // safe if the file already exists. If that fails, we assume the file does
  1211. // NOT exist and we try to open it in write+ mode, which will clobber the
  1212. // contents of the file if it does exist.
  1213. sResult = prefsScores.Open(FullPathHD(HIGHSCORE_SCORES_FILE), "r+");
  1214. if (sResult != SUCCESS)
  1215. sResult = prefsScores.Open(FullPathHD(HIGHSCORE_SCORES_FILE), "w+");
  1216. if (sResult == SUCCESS)
  1217. {
  1218. for (i = 0; i < MAX_HIGH_SCORES; i++)
  1219. {
  1220. sprintf(szKeyName, "Player%d", i);
  1221. prefsScores.SetVal((char*) pRealm->m_rsRealmString, szKeyName, astrNames[i]);
  1222. sprintf(szKeyName, "Score%d", i);
  1223. prefsScores.SetVal((char*) pRealm->m_rsRealmString, szKeyName, alScores[i]);
  1224. }
  1225. }
  1226. prefsScores.Close();
  1227. }
  1228. // Put the colors back.
  1229. PalTranOff();
  1230. }
  1231. if (plbScores)
  1232. {
  1233. #if 0
  1234. // Get rid of all score item instances.
  1235. RGuiItem* pguiScoreItem = plbScores->GetFirst();
  1236. while (pguiScoreItem)
  1237. {
  1238. // Remove the listbox's encapsulator property.
  1239. pguiScoreItem->RemoveProp(ENCAPSULATOR_PROP_KEY);
  1240. // Release the current one.
  1241. rspReleaseResourceInstance(&g_resmgrShell, &pguiScoreItem);
  1242. // Set the next one as the current.
  1243. pguiScoreItem = plbScores->GetNext();
  1244. }
  1245. #else
  1246. plbScores->RemoveAll();
  1247. #endif
  1248. }
  1249. rspReleaseResource(&g_resmgrShell, &pguiRoot);
  1250. }
  1251. }
  1252. }
  1253. //////////////////////////////////////////////////////////////////////////////
  1254. // ScoreHighestKills
  1255. //
  1256. // Return the highest number of kills among all of the players. This will
  1257. // be called to determine if a frag limit level is over.
  1258. //
  1259. //////////////////////////////////////////////////////////////////////////////
  1260. short ScoreHighestKills(CRealm* pRealm)
  1261. {
  1262. short sHighest = 0;
  1263. short sNumDudes = pRealm->m_asClassNumThings[CThing::CDudeID];
  1264. short i;
  1265. for (i = 0; i < sNumDudes; i++)
  1266. {
  1267. if (g_scoreboard.m_asScores[i] > sHighest)
  1268. sHighest = g_scoreboard.m_asScores[i];
  1269. }
  1270. return sHighest;
  1271. }
  1272. //////////////////////////////////////////////////////////////////////////////
  1273. // EOF
  1274. //////////////////////////////////////////////////////////////////////////////