PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/RacingGameWindows1/RacingGame/RacingGame/GameScreens/Highscores.cs

https://bitbucket.org/ddreaper/racing-game-kit
C# | 403 lines | 260 code | 43 blank | 100 comment | 34 complexity | f995e2413ef21057944584943f7b411f MD5 | raw file
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // Highscores.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using directives
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Diagnostics;
  13. using System.Globalization;
  14. using System.IO;
  15. using System.Runtime.Serialization;
  16. using System.Text;
  17. using System.Threading;
  18. using RacingGame.GameLogic;
  19. using RacingGame.Graphics;
  20. using RacingGame.Helpers;
  21. using RacingGame.Properties;
  22. using Microsoft.Xna.Framework;
  23. using Microsoft.Xna.Framework.Graphics;
  24. using RacingGame.Sounds;
  25. #endregion
  26. namespace RacingGame.GameScreens
  27. {
  28. /// <summary>
  29. /// Highscores
  30. /// </summary>
  31. /// <returns>IGame screen</returns>
  32. class Highscores : IGameScreen
  33. {
  34. #region Highscore helper class
  35. /// <summary>
  36. /// Highscore helper class
  37. /// </summary>
  38. private struct HighscoreInLevel
  39. {
  40. #region Variables
  41. /// <summary>
  42. /// Player name
  43. /// </summary>
  44. public string name;
  45. /// <summary>
  46. /// Highscore points
  47. /// </summary>
  48. public int timeMilliseconds;
  49. #endregion
  50. #region Constructor
  51. /// <summary>
  52. /// Create highscore
  53. /// </summary>
  54. /// <param name="setName">Set name</param>
  55. /// <param name="setTimeMs">Set time ms</param>
  56. public HighscoreInLevel(string setName, int setTimeMs)
  57. {
  58. name = setName;
  59. timeMilliseconds = setTimeMs;
  60. }
  61. #endregion
  62. #region ToString
  63. /// <summary>
  64. /// To string
  65. /// </summary>
  66. /// <returns>String</returns>
  67. public override string ToString()
  68. {
  69. return name + ":" + timeMilliseconds;
  70. }
  71. #endregion
  72. }
  73. /// <summary>
  74. /// Number of highscores displayed in this screen.
  75. /// </summary>
  76. private const int NumOfHighscores = 10,
  77. NumOfHighscoreLevels = 3;
  78. /// <summary>
  79. /// List of remembered highscores.
  80. /// </summary>
  81. private static HighscoreInLevel[,] highscores = null;
  82. /// <summary>
  83. /// Write highscores to string. Used to save to highscores settings.
  84. /// </summary>
  85. private static void WriteHighscoresToSettings()
  86. {
  87. string saveString = "";
  88. for (int level = 0; level < NumOfHighscoreLevels; level++)
  89. {
  90. for (int num = 0; num < NumOfHighscores; num++)
  91. {
  92. saveString += (saveString.Length == 0 ? "" : ",") +
  93. highscores[level, num];
  94. }
  95. }
  96. GameSettings.Default.Highscores = saveString;
  97. ThreadPool.QueueUserWorkItem(new WaitCallback(SaveSettings), null);
  98. }
  99. /// <summary>
  100. /// Callback used for saving the settings from a worker thread
  101. /// </summary>
  102. /// <param name="replay">Not used, delegate signature requires it</param>
  103. private static void SaveSettings(object state)
  104. {
  105. GameSettings.Save();
  106. }
  107. /// <summary>
  108. /// Read highscores from settings
  109. /// </summary>
  110. /// <returns>True if reading succeeded, false otherwise.</returns>
  111. private static bool ReadHighscoresFromSettings()
  112. {
  113. if (String.IsNullOrEmpty(GameSettings.Default.Highscores))
  114. return false;
  115. string highscoreString = GameSettings.Default.Highscores;
  116. string[] allHighscores = highscoreString.Split(',');
  117. for (int level = 0; level < NumOfHighscoreLevels; level++)
  118. for (int num = 0; num < NumOfHighscores &&
  119. level * NumOfHighscores + num < allHighscores.Length; num++)
  120. {
  121. string[] oneHighscore =
  122. allHighscores[level * NumOfHighscores + num].
  123. Split(new char[] { ':' });
  124. highscores[level, num] = new HighscoreInLevel(
  125. oneHighscore[0], Convert.ToInt32(oneHighscore[1]));
  126. }
  127. return true;
  128. }
  129. #endregion
  130. #region Static constructor
  131. /// <summary>
  132. /// Create Highscores class, will basically try to load highscore list,
  133. /// if that fails we generate a standard highscore list!
  134. /// </summary>
  135. public static void Initialize()
  136. {
  137. // Init highscores
  138. highscores =
  139. new HighscoreInLevel[NumOfHighscoreLevels, NumOfHighscores];
  140. if (ReadHighscoresFromSettings() == false)
  141. {
  142. // Generate default lists
  143. for (int level = 0; level < NumOfHighscoreLevels; level++)
  144. {
  145. for (int rank = 0; rank < NumOfHighscores; rank++)
  146. {
  147. highscores[level, rank] =
  148. new HighscoreInLevel("Player " + (rank + 1).ToString(),
  149. (75000 + rank * 5000) * (level + 1));
  150. }
  151. }
  152. WriteHighscoresToSettings();
  153. }
  154. }
  155. #endregion
  156. #region Get top lap time
  157. /// <summary>
  158. /// Get top lap time
  159. /// </summary>
  160. /// <param name="level">Level</param>
  161. /// <returns>Best lap time</returns>
  162. public static float GetTopLapTime(int level)
  163. {
  164. return (float)highscores[level, 0].timeMilliseconds / 1000.0f;
  165. }
  166. #endregion
  167. #region Get top 5 rank lap times
  168. /// <summary>
  169. /// Get top 5 rank lap times
  170. /// </summary>
  171. /// <param name="level">Current level</param>
  172. /// <returns>Array of top 5 times</returns>
  173. public static int[] GetTop5LapTimes(int level)
  174. {
  175. return new int[]
  176. {
  177. highscores[level, 0].timeMilliseconds,
  178. highscores[level, 1].timeMilliseconds,
  179. highscores[level, 2].timeMilliseconds,
  180. highscores[level, 3].timeMilliseconds,
  181. highscores[level, 4].timeMilliseconds,
  182. };
  183. }
  184. #endregion
  185. #region Get rank from current score
  186. /// <summary>
  187. /// Get rank from current time.
  188. /// Used in game to determinate rank while flying around ^^
  189. /// </summary>
  190. /// <param name="level">Level</param>
  191. /// <param name="timeMilisec">Time ms</param>
  192. /// <returns>Int</returns>
  193. public static int GetRankFromCurrentTime(int level, int timeMilliseconds)
  194. {
  195. // Time must be at least 1 second
  196. if (timeMilliseconds < 1000)
  197. // Invalid time, return rank 11 (out of highscore)
  198. return NumOfHighscores;
  199. // Just compare with all highscores and return the rank we have reached.
  200. for (int num = 0; num < NumOfHighscores; num++)
  201. {
  202. if (timeMilliseconds <= highscores[level, num].timeMilliseconds)
  203. return num;
  204. }
  205. // No Rank found, use rank 11
  206. return NumOfHighscores;
  207. }
  208. #endregion
  209. #region Submit highscore after game
  210. /// <summary>
  211. /// Submit highscore. Done after each game is over (won or lost).
  212. /// New highscore will be added to the highscore screen.
  213. /// In the future: Also send highscores to the online server.
  214. /// </summary>
  215. /// <param name="score">Score</param>
  216. /// <param name="levelName">Level name</param>
  217. public static void SubmitHighscore(int level, int timeMilliseconds)
  218. {
  219. // Search which highscore rank we can replace
  220. for (int num = 0; num < NumOfHighscores; num++)
  221. {
  222. if (timeMilliseconds <= highscores[level, num].timeMilliseconds)
  223. {
  224. // Move all highscores up
  225. for (int moveUpNum = NumOfHighscores - 1; moveUpNum > num;
  226. moveUpNum--)
  227. {
  228. highscores[level, moveUpNum] = highscores[level, moveUpNum - 1];
  229. }
  230. // Add this highscore into the local highscore table
  231. highscores[level, num].name = GameSettings.Default.PlayerName;
  232. highscores[level, num].timeMilliseconds = timeMilliseconds;
  233. // And save that
  234. Highscores.WriteHighscoresToSettings();
  235. break;
  236. }
  237. }
  238. // Else no highscore was reached, we can't replace any rank.
  239. }
  240. #endregion
  241. #region Render
  242. int selectedLevel = 1;
  243. /// <summary>
  244. /// Render game screen. Called each frame.
  245. /// </summary>
  246. /// <returns>Bool</returns>
  247. public bool Render()
  248. {
  249. // This starts both menu and in game post screen shader!
  250. if (BaseGame.UI.PostScreenMenuShader != null)
  251. BaseGame.UI.PostScreenMenuShader.Start();
  252. // Render background
  253. BaseGame.UI.RenderMenuBackground();
  254. BaseGame.UI.RenderBlackBar(160, 498 - 160);
  255. // Highscores header
  256. int posX = 10;
  257. int posY = 18;
  258. if (Environment.OSVersion.Platform != PlatformID.Win32NT)
  259. {
  260. posX += 36;
  261. posY += 26;
  262. }
  263. BaseGame.UI.Headers.RenderOnScreenRelative1600(
  264. posX, posY, UIRenderer.HeaderHighscoresGfxRect);
  265. // Track selection
  266. int xPos = BaseGame.XToRes(512 - 160 * 3 / 2 + 25);
  267. int yPos = BaseGame.YToRes(182);
  268. int lineHeight = BaseGame.YToRes(27);
  269. // Beginner track
  270. bool inBox = Input.MouseInBox(new Rectangle(
  271. xPos, yPos, BaseGame.XToRes(125), lineHeight));
  272. TextureFont.WriteText(xPos, yPos, "Beginner",
  273. selectedLevel == 0 ? Color.Yellow :
  274. inBox ? Color.White : Color.LightGray);
  275. if (inBox && Input.MouseLeftButtonJustPressed)
  276. {
  277. Sound.Play(Sound.Sounds.ButtonClick);
  278. selectedLevel = 0;
  279. }
  280. xPos += BaseGame.XToRes(160 + 8);
  281. // Advanced track
  282. inBox = Input.MouseInBox(new Rectangle(
  283. xPos, yPos, BaseGame.XToRes(125), lineHeight));
  284. TextureFont.WriteText(xPos, yPos, "Advanced",
  285. selectedLevel == 1 ? Color.Yellow :
  286. inBox ? Color.White : Color.LightGray);
  287. if (inBox && Input.MouseLeftButtonJustPressed)
  288. {
  289. Sound.Play(Sound.Sounds.ButtonClick);
  290. selectedLevel = 1;
  291. }
  292. xPos += BaseGame.XToRes(160 + 30 - 8);
  293. // Expert track
  294. inBox = Input.MouseInBox(new Rectangle(
  295. xPos, yPos, BaseGame.XToRes(125), lineHeight));
  296. TextureFont.WriteText(xPos, yPos, "Expert",
  297. selectedLevel == 2 ? Color.Yellow :
  298. inBox ? Color.White : Color.LightGray);
  299. if (inBox && Input.MouseLeftButtonJustPressed)
  300. {
  301. Sound.Play(Sound.Sounds.ButtonClick);
  302. selectedLevel = 2;
  303. }
  304. // Also handle xbox controller input
  305. if (Input.GamePadLeftJustPressed ||
  306. Input.KeyboardLeftJustPressed)
  307. {
  308. Sound.Play(Sound.Sounds.ButtonClick);
  309. selectedLevel = (selectedLevel + 2) % 3;
  310. }
  311. else if (Input.GamePadRightJustPressed ||
  312. Input.KeyboardRightJustPressed)
  313. {
  314. Sound.Play(Sound.Sounds.ButtonClick);
  315. selectedLevel = (selectedLevel + 1) % 3;
  316. }
  317. int xPos1 = BaseGame.XToRes(300);
  318. int xPos2 = BaseGame.XToRes(350);
  319. int xPos3 = BaseGame.XToRes(640);
  320. // Draw seperation line
  321. yPos = BaseGame.YToRes(208);
  322. BaseGame.DrawLine(
  323. new Point(xPos1, yPos),
  324. new Point(xPos3 + TextureFont.GetTextWidth("5:67:89"), yPos),
  325. new Color(192, 192, 192, 128));
  326. // And another one, looks better with 2 pixel height
  327. BaseGame.DrawLine(
  328. new Point(xPos1, yPos + 1),
  329. new Point(xPos3 + TextureFont.GetTextWidth("5:67:89"), yPos + 1),
  330. new Color(192, 192, 192, 128));
  331. yPos = BaseGame.YToRes(220);
  332. // Go through all highscores
  333. for (int num = 0; num < NumOfHighscores; num++)
  334. {
  335. Rectangle lineRect = new Rectangle(
  336. 0, yPos, BaseGame.Width, lineHeight);
  337. Color col = Input.MouseInBox(lineRect) ?
  338. Color.White : new Color(200, 200, 200);
  339. TextureFont.WriteText(xPos1, yPos,
  340. (1 + num) + ".", col);
  341. TextureFont.WriteText(xPos2, yPos,
  342. highscores[selectedLevel, num].name, col);
  343. TextureFont.WriteGameTime(xPos3, yPos,
  344. highscores[selectedLevel, num].timeMilliseconds,
  345. Color.Yellow);
  346. yPos += lineHeight;
  347. }
  348. BaseGame.UI.RenderBottomButtons(true);
  349. if (Input.KeyboardEscapeJustPressed ||
  350. Input.GamePadBJustPressed ||
  351. Input.GamePadBackJustPressed ||
  352. Input.MouseLeftButtonJustPressed &&
  353. // Don't allow clicking on the controls to quit
  354. Input.MousePos.Y > yPos)
  355. return true;
  356. return false;
  357. }
  358. #endregion
  359. }
  360. }