2
0

ChestScreen.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // ChestScreen.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 System.Collections.ObjectModel;
  13. using Microsoft.Xna.Framework;
  14. using Microsoft.Xna.Framework.Graphics;
  15. using Microsoft.Xna.Framework.Content;
  16. using RolePlayingGameData;
  17. #endregion
  18. namespace RolePlaying
  19. {
  20. class ChestScreen : InventoryScreen
  21. {
  22. #region Graphics Data
  23. /// <summary>
  24. /// The left-facing quantity arrow.
  25. /// </summary>
  26. private Texture2D leftQuantityArrow;
  27. /// <summary>
  28. /// The right-facing quantity arrow.
  29. /// </summary>
  30. private Texture2D rightQuantityArrow;
  31. #endregion
  32. #region Columns
  33. private const int nameColumnInterval = 80;
  34. private const int powerColumnInterval = 270;
  35. private const int quantityColumnInterval = 450;
  36. #endregion
  37. #region Data Access
  38. /// <summary>
  39. /// The chest entry that is displayed here.
  40. /// </summary>
  41. private MapEntry<Chest> chestEntry;
  42. /// <summary>
  43. /// Retrieve the list of gear shown in this menu.
  44. /// </summary>
  45. /// <returns></returns>
  46. public override ReadOnlyCollection<ContentEntry<Gear>> GetDataList()
  47. {
  48. if ((chestEntry == null) || (chestEntry.Content == null))
  49. {
  50. return null;
  51. }
  52. return chestEntry.Content.Entries.AsReadOnly();
  53. }
  54. /// <summary>
  55. /// The selected quantity of the current entry.
  56. /// </summary>
  57. private int selectedQuantity = 0;
  58. /// <summary>
  59. /// Resets the selected quantity to the maximum value for the selected entry.
  60. /// </summary>
  61. private void ResetSelectedQuantity()
  62. {
  63. // safety-check on the chest
  64. if ((chestEntry == null) || (chestEntry.Content == null))
  65. {
  66. selectedQuantity = 0;
  67. return;
  68. }
  69. // set the quantity to the maximum
  70. if (IsGoldSelected)
  71. {
  72. selectedQuantity = chestEntry.Content.Gold;
  73. }
  74. else if ((SelectedGearIndex >= 0) &&
  75. (SelectedGearIndex < chestEntry.Content.Entries.Count))
  76. {
  77. selectedQuantity = chestEntry.Content[SelectedGearIndex].Count;
  78. }
  79. }
  80. #endregion
  81. #region List Navigation
  82. /// <summary>
  83. /// If true, the phantom ContentEntry for the chest's gold is selected
  84. /// </summary>
  85. public bool IsGoldSelected
  86. {
  87. get
  88. {
  89. return ((chestEntry != null) && (chestEntry.Content != null) &&
  90. (chestEntry.Content.Gold > 0) && (SelectedIndex == 0));
  91. }
  92. }
  93. /// <summary>
  94. /// Retrieve the zero-based selection of the gear in the chestEntry.Content.
  95. /// </summary>
  96. /// <remarks>
  97. /// If there is gold in the chestEntry.Content, its phantom ContentEntry shifts
  98. /// ListScreen.SelectedIndex by one. If IsGoldSelected is true, this property
  99. /// return -1.
  100. /// </remarks>
  101. public int SelectedGearIndex
  102. {
  103. get
  104. {
  105. return ((chestEntry != null) && (chestEntry.Content != null) &&
  106. (chestEntry.Content.Gold > 0)) ? SelectedIndex - 1 : SelectedIndex;
  107. }
  108. }
  109. /// <summary>
  110. /// Move the current selection up one entry.
  111. /// </summary>
  112. protected override void MoveCursorUp()
  113. {
  114. base.MoveCursorUp();
  115. ResetSelectedQuantity();
  116. }
  117. /// <summary>
  118. /// Move the current selection down one entry.
  119. /// </summary>
  120. protected override void MoveCursorDown()
  121. {
  122. base.MoveCursorDown();
  123. ResetSelectedQuantity();
  124. }
  125. /// <summary>
  126. /// Decrease the selected quantity by one.
  127. /// </summary>
  128. protected override void MoveCursorLeft()
  129. {
  130. // decrement the quantity, looping around if necessary
  131. if (selectedQuantity > 0)
  132. {
  133. selectedQuantity--;
  134. }
  135. else if (IsGoldSelected)
  136. {
  137. selectedQuantity = chestEntry.Content.Gold;
  138. }
  139. else if (SelectedGearIndex < chestEntry.Content.Entries.Count)
  140. {
  141. selectedQuantity = chestEntry.Content[SelectedGearIndex].Count;
  142. }
  143. }
  144. /// <summary>
  145. /// Increase the selected quantity by one.
  146. /// </summary>
  147. protected override void MoveCursorRight()
  148. {
  149. int maximumQuantity = 0;
  150. // get the maximum quantity for the selected entry
  151. if (IsGoldSelected)
  152. {
  153. maximumQuantity = chestEntry.Content.Gold;
  154. }
  155. else if (SelectedGearIndex < chestEntry.Content.Entries.Count)
  156. {
  157. maximumQuantity = chestEntry.Content[SelectedGearIndex].Count;
  158. }
  159. else
  160. {
  161. return;
  162. }
  163. // loop to zero if the selected quantity is already at maximum.
  164. selectedQuantity = selectedQuantity < maximumQuantity ?
  165. selectedQuantity + 1 : 0;
  166. }
  167. #endregion
  168. #region Initialization
  169. /// <summary>
  170. /// Creates a new ChestScreen object.
  171. /// </summary>
  172. /// <param name="chestEntry">The chest entry shown in the screen</param>
  173. public ChestScreen(MapEntry<Chest> chestEntry)
  174. : base(true)
  175. {
  176. // check the parameter
  177. if ((chestEntry == null) || (chestEntry.Content == null))
  178. {
  179. throw new ArgumentNullException("chestEntry.Content");
  180. }
  181. this.chestEntry = chestEntry;
  182. // clean up any empty entries
  183. this.chestEntry.Content.Entries.RemoveAll(
  184. delegate(ContentEntry<Gear> contentEntry)
  185. {
  186. return (contentEntry.Count <= 0);
  187. });
  188. // sort the chest entries by name
  189. this.chestEntry.Content.Entries.Sort(
  190. delegate(ContentEntry<Gear> gearEntry1, ContentEntry<Gear> gearEntry2)
  191. {
  192. // handle null values
  193. if ((gearEntry1 == null) || (gearEntry1.Content == null))
  194. {
  195. return ((gearEntry2 == null) || (gearEntry2.Content == null) ?
  196. 0 : 1);
  197. }
  198. else if ((gearEntry2 == null) || (gearEntry2.Content == null))
  199. {
  200. return -1;
  201. }
  202. // sort by name
  203. return gearEntry1.Content.Name.CompareTo(gearEntry2.Content.Name);
  204. });
  205. // set up the initial selected-quantity values
  206. ResetSelectedQuantity();
  207. // configure the menu strings
  208. titleText = chestEntry.Content.Name;
  209. selectButtonText = "Take";
  210. backButtonText = "Close";
  211. xButtonText = String.Empty;
  212. yButtonText = "Take All";
  213. leftTriggerText = String.Empty;
  214. rightTriggerText = String.Empty;
  215. }
  216. /// <summary>
  217. /// Load the graphics content from the content manager.
  218. /// </summary>
  219. public override void LoadContent()
  220. {
  221. base.LoadContent();
  222. ContentManager content = ScreenManager.Game.Content;
  223. leftQuantityArrow =
  224. content.Load<Texture2D>(@"Textures\Buttons\QuantityArrowLeft");
  225. rightQuantityArrow =
  226. content.Load<Texture2D>(@"Textures\Buttons\QuantityArrowRight");
  227. }
  228. #endregion
  229. #region Input Handling
  230. /// <summary>
  231. /// Allows the screen to handle user input.
  232. /// </summary>
  233. public override void HandleInput()
  234. {
  235. // if the chestEntry.Content is empty, exit immediately
  236. if (chestEntry.Content.IsEmpty)
  237. {
  238. BackTriggered();
  239. return;
  240. }
  241. if (InputManager.IsActionTriggered(InputManager.Action.CursorUp))
  242. {
  243. MoveCursorUp();
  244. }
  245. else if (InputManager.IsActionTriggered(InputManager.Action.CursorDown))
  246. {
  247. MoveCursorDown();
  248. }
  249. else if (InputManager.IsActionTriggered(InputManager.Action.IncreaseAmount))
  250. {
  251. MoveCursorRight();
  252. }
  253. else if (InputManager.IsActionTriggered(InputManager.Action.DecreaseAmount))
  254. {
  255. MoveCursorLeft();
  256. }
  257. // Close is pressed
  258. else if (InputManager.IsActionTriggered(InputManager.Action.Back))
  259. {
  260. BackTriggered();
  261. }
  262. // Take is pressed
  263. else if (InputManager.IsActionTriggered(InputManager.Action.Ok))
  264. {
  265. if (IsGoldSelected)
  266. {
  267. SelectTriggered(null);
  268. }
  269. else
  270. {
  271. ReadOnlyCollection<ContentEntry<Gear>> dataList = GetDataList();
  272. SelectTriggered(dataList[SelectedGearIndex]);
  273. }
  274. }
  275. // Take All is pressed
  276. else if (InputManager.IsActionTriggered(InputManager.Action.TakeView))
  277. {
  278. ButtonYPressed(null); // take-all doesn't need an entry
  279. }
  280. }
  281. /// <summary>
  282. /// Respond to the triggering of the Back action.
  283. /// </summary>
  284. protected override void BackTriggered()
  285. {
  286. // clean up any empty entries
  287. chestEntry.Content.Entries.RemoveAll(
  288. delegate(ContentEntry<Gear> contentEntry)
  289. {
  290. return (contentEntry.Count <= 0);
  291. });
  292. // if the chestEntry.Content is empty, remove it from the game and exit
  293. if (chestEntry.Content.IsEmpty)
  294. {
  295. Session.RemoveChest(chestEntry);
  296. }
  297. else
  298. {
  299. // otherwise, store the modified chestEntry.Content
  300. Session.StoreModifiedChest(chestEntry);
  301. }
  302. // exit the screen
  303. base.BackTriggered();
  304. }
  305. /// <summary>
  306. /// Respond to the triggering of the Select action.
  307. /// </summary>
  308. protected override void SelectTriggered(ContentEntry<Gear> entry)
  309. {
  310. // if the quantity is zero, don't bother
  311. if (selectedQuantity <= 0)
  312. {
  313. return;
  314. }
  315. // check to see if gold is selected
  316. if (IsGoldSelected)
  317. {
  318. // play the "pick up gold" cue
  319. AudioManager.PlayCue("Money");
  320. // add the gold to the party
  321. Session.Party.PartyGold += selectedQuantity;
  322. chestEntry.Content.Gold -= selectedQuantity;
  323. if (chestEntry.Content.Gold > 0)
  324. {
  325. selectedQuantity =
  326. Math.Min(selectedQuantity, chestEntry.Content.Gold);
  327. }
  328. else
  329. {
  330. ResetSelectedQuantity();
  331. }
  332. }
  333. else
  334. {
  335. // remove the selected quantity of gear from the chest
  336. int quantity = selectedQuantity;
  337. if ((entry.Content != null) && (quantity > 0))
  338. {
  339. Session.Party.AddToInventory(entry.Content, quantity);
  340. entry.Count -= quantity;
  341. }
  342. if (entry.Count > 0)
  343. {
  344. selectedQuantity = Math.Min(entry.Count, selectedQuantity);
  345. }
  346. else
  347. {
  348. // if the entry is now empty, remove it from the chest
  349. chestEntry.Content.Entries.RemoveAt(SelectedGearIndex);
  350. ResetSelectedQuantity();
  351. }
  352. }
  353. }
  354. /// <summary>
  355. /// Respond to the triggering of the Y button (and related key).
  356. /// </summary>
  357. protected override void ButtonYPressed(ContentEntry<Gear> entry)
  358. {
  359. // add the entire amount of gold
  360. if (chestEntry.Content.Gold > 0)
  361. {
  362. AudioManager.PlayCue("Money");
  363. Session.Party.PartyGold += chestEntry.Content.Gold;
  364. chestEntry.Content.Gold = 0;
  365. }
  366. // add all items at full quantity
  367. // -- there is no limit to the party's inventory
  368. ReadOnlyCollection<ContentEntry<Gear>> entries = GetDataList();
  369. foreach (ContentEntry<Gear> gearEntry in entries)
  370. {
  371. Session.Party.AddToInventory(gearEntry.Content, gearEntry.Count);
  372. }
  373. // clear the entries, as they're all gone now
  374. chestEntry.Content.Entries.Clear();
  375. selectedQuantity = 0;
  376. }
  377. #endregion
  378. #region Drawing
  379. /// <summary>
  380. /// Draws the screen.
  381. /// </summary>
  382. public override void Draw(Microsoft.Xna.Framework.GameTime gameTime)
  383. {
  384. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  385. // get the content list
  386. ReadOnlyCollection<ContentEntry<Gear>> dataList = GetDataList();
  387. // fix the indices for the current list size
  388. int maximumCount = chestEntry.Content.Gold > 0 ? dataList.Count + 1 :
  389. dataList.Count;
  390. SelectedIndex = (int)MathHelper.Clamp(SelectedIndex, 0, maximumCount - 1);
  391. StartIndex = (int)MathHelper.Clamp(StartIndex, 0,
  392. maximumCount - MaximumListEntries);
  393. EndIndex = Math.Min(StartIndex + MaximumListEntries, maximumCount);
  394. spriteBatch.Begin();
  395. DrawBackground();
  396. if (dataList.Count > 0)
  397. {
  398. DrawListPosition(SelectedIndex + 1, maximumCount);
  399. }
  400. DrawButtons();
  401. DrawPartyGold();
  402. DrawColumnHeaders();
  403. DrawTitle();
  404. // draw each item currently shown
  405. Vector2 position = listEntryStartPosition +
  406. new Vector2(0f, listLineSpacing / 2);
  407. for (int index = StartIndex; index < EndIndex; index++)
  408. {
  409. if ((index == 0) && (chestEntry.Content.Gold > 0))
  410. {
  411. if (index == SelectedIndex)
  412. {
  413. DrawSelection(position);
  414. DrawGoldEntry(position, true);
  415. }
  416. else
  417. {
  418. DrawGoldEntry(position, false);
  419. }
  420. }
  421. else
  422. {
  423. int currentIndex = chestEntry.Content.Gold > 0 ? index - 1 : index;
  424. ContentEntry<Gear> entry = dataList[currentIndex];
  425. if (index == SelectedIndex)
  426. {
  427. DrawSelection(position);
  428. DrawEntry(entry, position, true);
  429. DrawSelectedDescription(entry);
  430. }
  431. else
  432. {
  433. DrawEntry(entry, position, false);
  434. }
  435. }
  436. position.Y += listLineSpacing;
  437. }
  438. spriteBatch.End();
  439. }
  440. /// <summary>
  441. /// Draw the chest's gold at the given position in the list.
  442. /// </summary>
  443. /// <param name="position">The position to draw the gold at.</param>
  444. /// <param name="isSelected">If true, the gold is selected.</param>
  445. protected virtual void DrawGoldEntry(Vector2 position, bool isSelected)
  446. {
  447. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  448. Vector2 drawPosition = position;
  449. // draw the icon
  450. spriteBatch.Draw(goldTexture,
  451. drawPosition + iconOffset + new Vector2(0f, 7f), Color.White);
  452. // draw the name
  453. Color color = isSelected ? Fonts.HighlightColor : Fonts.DisplayColor;
  454. drawPosition.Y += listLineSpacing / 4;
  455. drawPosition.X += nameColumnInterval;
  456. spriteBatch.DrawString(Fonts.GearInfoFont, "Gold", drawPosition, color);
  457. // skip the power text
  458. drawPosition.X += powerColumnInterval;
  459. // draw the quantity
  460. drawPosition.X += quantityColumnInterval;
  461. if (isSelected)
  462. {
  463. // draw the left selection arrow
  464. drawPosition.X -= leftQuantityArrow.Width;
  465. spriteBatch.Draw(leftQuantityArrow,
  466. new Vector2(drawPosition.X, drawPosition.Y - 4), Color.White);
  467. drawPosition.X += leftQuantityArrow.Width;
  468. // draw the selected quantity ratio
  469. string quantityText = selectedQuantity.ToString() + "/" +
  470. chestEntry.Content.Gold.ToString();
  471. spriteBatch.DrawString(Fonts.GearInfoFont, quantityText,
  472. drawPosition, color);
  473. drawPosition.X += Fonts.GearInfoFont.MeasureString(quantityText).X;
  474. // draw the right selection arrow
  475. spriteBatch.Draw(rightQuantityArrow,
  476. new Vector2(drawPosition.X, drawPosition.Y - 4), Color.White);
  477. drawPosition.X += rightQuantityArrow.Width;
  478. }
  479. else
  480. {
  481. // draw the remaining quantity
  482. spriteBatch.DrawString(Fonts.GearInfoFont,
  483. chestEntry.Content.Gold.ToString(), drawPosition, color);
  484. }
  485. }
  486. /// <summary>
  487. /// Draw the gear's content entry at the given position in the list.
  488. /// </summary>
  489. /// <param name="contentEntry">The content entry to draw.</param>
  490. /// <param name="position">The position to draw the entry at.</param>
  491. /// <param name="isSelected">If true, this item is selected.</param>
  492. protected override void DrawEntry(ContentEntry<Gear> entry, Vector2 position,
  493. bool isSelected)
  494. {
  495. // check the parameter
  496. if (entry == null)
  497. {
  498. throw new ArgumentNullException("entry");
  499. }
  500. Gear gear = entry.Content as Gear;
  501. if (gear == null)
  502. {
  503. return;
  504. }
  505. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  506. Vector2 drawPosition = position;
  507. // draw the icon
  508. spriteBatch.Draw(gear.IconTexture, drawPosition + iconOffset, Color.White);
  509. // draw the name
  510. Color color = isSelected ? Fonts.HighlightColor : Fonts.DisplayColor;
  511. drawPosition.Y += listLineSpacing / 4;
  512. drawPosition.X += nameColumnInterval;
  513. spriteBatch.DrawString(Fonts.GearInfoFont, gear.Name, drawPosition, color);
  514. // draw the power
  515. drawPosition.X += powerColumnInterval;
  516. string powerText = gear.GetPowerText();
  517. Vector2 powerTextSize = Fonts.GearInfoFont.MeasureString(powerText);
  518. Vector2 powerPosition = drawPosition;
  519. powerPosition.Y -= (float)Math.Ceiling((powerTextSize.Y - 30f) / 2);
  520. spriteBatch.DrawString(Fonts.GearInfoFont, powerText,
  521. powerPosition, color);
  522. // draw the quantity
  523. drawPosition.X += quantityColumnInterval;
  524. if (isSelected)
  525. {
  526. // draw the left selection arrow
  527. drawPosition.X -= leftQuantityArrow.Width;
  528. spriteBatch.Draw(leftQuantityArrow,
  529. new Vector2(drawPosition.X, drawPosition.Y - 4), Color.White);
  530. drawPosition.X += leftQuantityArrow.Width;
  531. // draw the selected quantity ratio
  532. string quantityText = selectedQuantity.ToString() + "/" +
  533. entry.Count.ToString();
  534. spriteBatch.DrawString(Fonts.GearInfoFont, quantityText,
  535. drawPosition, color);
  536. drawPosition.X += Fonts.GearInfoFont.MeasureString(quantityText).X;
  537. // draw the right selection arrow
  538. spriteBatch.Draw(rightQuantityArrow,
  539. new Vector2(drawPosition.X, drawPosition.Y - 4), Color.White);
  540. drawPosition.X += rightQuantityArrow.Width;
  541. }
  542. else
  543. {
  544. // draw the remaining quantity
  545. spriteBatch.DrawString(Fonts.GearInfoFont, entry.Count.ToString(),
  546. drawPosition, color);
  547. }
  548. }
  549. #endregion
  550. }
  551. }