GameplayScreen.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // GameplayScreen.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections.Generic;
  12. using Microsoft.Xna.Framework;
  13. using Microsoft.Xna.Framework.Graphics;
  14. using GameStateManagement;
  15. using Microsoft.Xna.Framework.GamerServices;
  16. using System.Xml.Linq;
  17. using Microsoft.Xna.Framework.Input.Touch;
  18. #endregion
  19. namespace MemoryMadness
  20. {
  21. class GameplayScreen : GameScreen
  22. {
  23. #region Fields
  24. private bool isActive;
  25. private bool isLevelChange;
  26. /// <summary>
  27. /// Sets/gets whether or not the game is active. Set operations propogate
  28. /// to the current level.
  29. /// </summary>
  30. public new bool IsActive
  31. {
  32. get { return isActive; }
  33. set
  34. {
  35. isActive = value;
  36. if (null != currentLevel)
  37. currentLevel.IsActive = value;
  38. }
  39. }
  40. bool moveToHighScore = false;
  41. // Gameplay variables
  42. public Level currentLevel;
  43. int currentLevelNumber;
  44. int movesPerformed = 0;
  45. int maxLevelNumber;
  46. // Rendering variables
  47. SpriteFont levelNumberFont;
  48. SpriteFont textFont;
  49. Texture2D background;
  50. Texture2D buttonsTexture;
  51. // Input related variables
  52. TimeSpan inputTimeMeasure;
  53. TimeSpan inputGracePeriod = TimeSpan.FromMilliseconds(150);
  54. TouchInputState inputState = TouchInputState.Idle;
  55. List<TouchLocation> lastPressInput;
  56. #endregion
  57. #region Initializations
  58. public GameplayScreen(int levelNumber, int movesPerformed)
  59. : this(levelNumber)
  60. {
  61. this.movesPerformed = movesPerformed;
  62. }
  63. public GameplayScreen(int levelNumber)
  64. {
  65. TransitionOnTime = TimeSpan.FromSeconds(0.0);
  66. TransitionOffTime = TimeSpan.FromSeconds(0.0);
  67. currentLevelNumber = levelNumber;
  68. }
  69. #endregion
  70. #region Loading
  71. /// <summary>
  72. /// Load the game content
  73. /// </summary>
  74. public override void LoadContent()
  75. {
  76. XDocument doc = XDocument.Load(@"Content\Gameplay\LevelDefinitions.xml");
  77. var levels = doc.Document.Descendants(XName.Get("Level"));
  78. foreach (var level in levels)
  79. {
  80. maxLevelNumber++;
  81. }
  82. // Resolution for a possible situation which can occur while debugging the
  83. // game. The game may remember it is on a level which is higher than the
  84. // highest available level, following a change to the definition file.
  85. if (currentLevelNumber > maxLevelNumber)
  86. currentLevelNumber = 1;
  87. InitializeLevel();
  88. base.LoadContent();
  89. }
  90. /// <summary>
  91. /// Initialize the level portrayed by the gameplay screen.
  92. /// </summary>
  93. private void InitializeLevel()
  94. {
  95. currentLevel = new Level(ScreenManager.Game,
  96. ScreenManager.SpriteBatch,
  97. currentLevelNumber, movesPerformed, buttonsTexture);
  98. currentLevel.IsActive = true;
  99. ScreenManager.Game.Components.Add(currentLevel);
  100. }
  101. /// <summary>
  102. /// Load assets used by the gameplay screen.
  103. /// </summary>
  104. public void LoadAssets()
  105. {
  106. levelNumberFont =
  107. ScreenManager.Game.Content.Load<SpriteFont>(@"Fonts\GameplayLargeFont");
  108. textFont =
  109. ScreenManager.Game.Content.Load<SpriteFont>(@"Fonts\GameplaySmallFont");
  110. background =
  111. ScreenManager.Game.Content.Load<Texture2D>(
  112. @"Textures\Backgrounds\gameplayBG");
  113. buttonsTexture =
  114. ScreenManager.Game.Content.Load<Texture2D>(@"Textures\ButtonStates");
  115. }
  116. #endregion
  117. #region Update
  118. /// <summary>
  119. /// Handle user input.
  120. /// </summary>
  121. /// <param name="input">The input to handle.</param>
  122. public override void HandleInput(InputState input)
  123. {
  124. if (IsActive)
  125. {
  126. if (input == null)
  127. throw new ArgumentNullException("input");
  128. if (input.IsPauseGame(null))
  129. {
  130. PauseCurrentGame();
  131. }
  132. if (input.TouchState.Count > 0)
  133. {
  134. // We are about to handle touch input
  135. switch (inputState)
  136. {
  137. case TouchInputState.Idle:
  138. // We have yet to receive input, start grace period
  139. inputTimeMeasure = TimeSpan.Zero;
  140. inputState = TouchInputState.GracePeriod;
  141. lastPressInput = new List<TouchLocation>();
  142. foreach (var touch in input.TouchState)
  143. {
  144. if (touch.State == TouchLocationState.Pressed)
  145. {
  146. lastPressInput.Add(touch);
  147. }
  148. }
  149. break;
  150. case TouchInputState.GracePeriod:
  151. // Do nothing during the grace period other than remembering
  152. // additional presses
  153. foreach (var touch in input.TouchState)
  154. {
  155. if (touch.State == TouchLocationState.Pressed)
  156. {
  157. lastPressInput.Add(touch);
  158. }
  159. }
  160. break;
  161. default:
  162. break;
  163. }
  164. }
  165. }
  166. }
  167. /// <summary>
  168. /// Update all the game component
  169. /// </summary>
  170. /// <param name="gameTime"></param>
  171. /// <param name="otherScreenHasFocus"></param>
  172. /// <param name="coveredByOtherScreen"></param>
  173. public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
  174. {
  175. // Do not advance to the highscore screen if sounds are playing
  176. if (moveToHighScore && !AudioManager.AreSoundsPlaying())
  177. {
  178. ScreenManager.Game.Components.Remove(currentLevel);
  179. foreach (GameScreen screen in ScreenManager.GetScreens())
  180. screen.ExitScreen();
  181. ScreenManager.AddScreen(new BackgroundScreen(true), null);
  182. ScreenManager.AddScreen(new HighScoreScreen(), null);
  183. }
  184. // Do not perform advance update logic if the game is inactive or we are
  185. // moving to the highscore screen
  186. if (!IsActive || moveToHighScore)
  187. {
  188. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  189. return;
  190. }
  191. if ((inputState == TouchInputState.GracePeriod) && (isActive))
  192. {
  193. inputTimeMeasure += gameTime.ElapsedGameTime;
  194. // if the input grace period is over, handle the touch input
  195. if (inputTimeMeasure >= inputGracePeriod)
  196. {
  197. currentLevel.RegisterTouch(lastPressInput);
  198. inputState = TouchInputState.Idle;
  199. }
  200. }
  201. // If the user passed the level, advance to the next or finish the game if
  202. // the current level was last
  203. if (currentLevel.CurrentState == LevelState.FinishedOk && isActive)
  204. {
  205. AudioManager.PlaySound("success");
  206. if (currentLevelNumber < maxLevelNumber)
  207. {
  208. currentLevelNumber++;
  209. isLevelChange = true;
  210. }
  211. else
  212. {
  213. FinishCurrentGame();
  214. }
  215. }
  216. // If the user failed to pass the level, revert to level one, allowing the
  217. // user to register a highscore if he reached a high enough level
  218. else if (currentLevel.CurrentState == LevelState.FinishedFail)
  219. {
  220. isActive = false;
  221. if (HighScoreScreen.IsInHighscores(currentLevelNumber))
  222. {
  223. // The player has a highscore - show the device's keyboard
  224. Guide.BeginShowKeyboardInput(PlayerIndex.One,
  225. Constants.HighscorePopupTitle, Constants.HighscorePopupText,
  226. Constants.HighscorePopupDefault, ShowHighscorePromptEnded,
  227. false);
  228. }
  229. else
  230. {
  231. AudioManager.PlaySound("fail");
  232. isActive = true;
  233. currentLevelNumber = 1;
  234. isLevelChange = true;
  235. }
  236. }
  237. if (isLevelChange)
  238. {
  239. ScreenManager.Game.Components.Remove(currentLevel);
  240. currentLevel = new Level(ScreenManager.Game,
  241. ScreenManager.SpriteBatch,
  242. currentLevelNumber, buttonsTexture);
  243. currentLevel.IsActive = true;
  244. ScreenManager.Game.Components.Add(currentLevel);
  245. isLevelChange = false;
  246. }
  247. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  248. }
  249. #endregion
  250. #region Render
  251. /// <summary>
  252. /// Draw The gameplay screen
  253. /// </summary>
  254. /// <param name="gameTime"></param>
  255. public override void Draw(GameTime gameTime)
  256. {
  257. ScreenManager.GraphicsDevice.Clear(Color.CornflowerBlue);
  258. ScreenManager.SpriteBatch.Begin();
  259. ScreenManager.SpriteBatch.Draw(background, Vector2.Zero, Color.White);
  260. if (IsActive)
  261. {
  262. string text;
  263. Vector2 size;
  264. Vector2 position;
  265. if (currentLevel.CurrentState == LevelState.NotReady)
  266. {
  267. text = "Preparing...";
  268. size = textFont.MeasureString(text);
  269. position = new Vector2((ScreenManager.GraphicsDevice.Viewport.Width - size.X) / 2,
  270. (ScreenManager.GraphicsDevice.Viewport.Height - size.Y) / 2);
  271. position.X += 20f;
  272. ScreenManager.SpriteBatch.DrawString(textFont, text,
  273. position, Color.White, 0f, Vector2.Zero, 0.9f, SpriteEffects.None, 0f);
  274. }
  275. else
  276. {
  277. // Draw the current level text, with the text color representing the
  278. // game's current state
  279. Color levelColor = Color.White;
  280. switch (currentLevel.CurrentState)
  281. {
  282. case LevelState.NotReady:
  283. case LevelState.Ready:
  284. break;
  285. case LevelState.Flashing:
  286. levelColor = Color.Yellow;
  287. break;
  288. case LevelState.Started:
  289. case LevelState.Success:
  290. case LevelState.InProcess:
  291. case LevelState.FinishedOk:
  292. levelColor = Color.LimeGreen;
  293. break;
  294. case LevelState.Fault:
  295. case LevelState.FinishedFail:
  296. levelColor = Color.Red;
  297. break;
  298. default:
  299. break;
  300. }
  301. // Draw "Level" text
  302. text = "Level";
  303. size = textFont.MeasureString(text);
  304. position = new Vector2(70, (
  305. ScreenManager.GraphicsDevice.Viewport.Height - size.Y) / 2);
  306. ScreenManager.SpriteBatch.DrawString(
  307. textFont, text, position, levelColor);
  308. // Draw level number
  309. text = currentLevelNumber.ToString("D2");
  310. size = levelNumberFont.MeasureString(text);
  311. position = new Vector2(290, (
  312. ScreenManager.GraphicsDevice.Viewport.Height - size.Y) / 2);
  313. ScreenManager.SpriteBatch.DrawString(
  314. levelNumberFont, text, position, levelColor);
  315. }
  316. }
  317. ScreenManager.SpriteBatch.End();
  318. base.Draw(gameTime);
  319. }
  320. #endregion
  321. #region Private functions
  322. /// <summary>
  323. /// Finish the current game
  324. /// </summary>
  325. private void FinishCurrentGame()
  326. {
  327. isActive = false;
  328. if (HighScoreScreen.IsInHighscores(currentLevelNumber))
  329. {
  330. // Show the device's keyboard to enter a name for the highscore
  331. Guide.BeginShowKeyboardInput(PlayerIndex.One,
  332. Constants.HighscorePopupTitle, Constants.HighscorePopupText,
  333. Constants.HighscorePopupDefault, ShowHighscorePromptEnded, true);
  334. }
  335. else
  336. {
  337. moveToHighScore = true;
  338. }
  339. }
  340. /// <summary>
  341. /// Asynchronous handler for the highscore player name popup messagebox.
  342. /// </summary>
  343. /// <param name="result">The popup messagebox result. The result's
  344. /// AsyncState property should be true if the user successfully finished
  345. /// the game, or false otherwise.</param>
  346. private void ShowHighscorePromptEnded(IAsyncResult result)
  347. {
  348. string playerName = Guide.EndShowKeyboardInput(result);
  349. bool finishedGame = (bool)result.AsyncState;
  350. if (playerName != null)
  351. {
  352. if (playerName.Length > 15)
  353. playerName = playerName.Substring(0, 15);
  354. HighScoreScreen.PutHighScore(playerName, currentLevelNumber);
  355. }
  356. if (finishedGame)
  357. {
  358. moveToHighScore = true;
  359. }
  360. else
  361. {
  362. AudioManager.PlaySound("fail");
  363. isActive = true;
  364. currentLevelNumber = 1;
  365. isLevelChange = true;
  366. }
  367. }
  368. /// <summary>
  369. /// Pause the game.
  370. /// </summary>
  371. private void PauseCurrentGame()
  372. {
  373. IsActive = false;
  374. // Pause sounds
  375. AudioManager.PauseResumeSounds(false);
  376. ScreenManager.AddScreen(new BackgroundScreen(false), null);
  377. ScreenManager.AddScreen(new PauseScreen(false), null);
  378. }
  379. #endregion
  380. }
  381. }