ChestScreen.cs 21 KB

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