SpellbookScreen.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. //-----------------------------------------------------------------------------
  2. // SpellbookScreen.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. namespace RolePlaying
  15. {
  16. /// <summary>
  17. /// Lists the spells available to the character.
  18. /// </summary>
  19. class SpellbookScreen : ListScreen<Spell>
  20. {
  21. private readonly Vector2 spellDescriptionPosition = new Vector2(200, 550);
  22. private readonly Vector2 warningMessagePosition = new Vector2(200, 580);
  23. private string nameColumnText = "Name";
  24. private const int nameColumnInterval = 80;
  25. private string levelColumnText = "Level";
  26. private const int levelColumnInterval = 240;
  27. private string powerColumnText = "Power (min, max)";
  28. private const int powerColumnInterval = 110;
  29. private string magicCostColumnText = "MP";
  30. private const int magicCostColumnInterval = 380;
  31. /// <summary>
  32. /// The FightingCharacter object whose spells are displayed.
  33. /// </summary>
  34. private FightingCharacter fightingCharacter;
  35. /// <summary>
  36. /// The statistics of the character, for calculating the eligibility of spells.
  37. /// </summary>
  38. /// <remarks>
  39. /// Needed because combat statistics override character statistics.
  40. /// </remarks>
  41. private StatisticsValue statistics;
  42. /// <summary>
  43. /// Get the list that this screen displays.
  44. /// </summary>
  45. public override ReadOnlyCollection<Spell> GetDataList()
  46. {
  47. return fightingCharacter.Spells.AsReadOnly();
  48. }
  49. /// <summary>
  50. /// Creates a new SpellbookScreen object for the given player and statistics.
  51. /// </summary>
  52. public SpellbookScreen(FightingCharacter fightingCharacter,
  53. StatisticsValue statistics)
  54. : base()
  55. {
  56. // check the parameter
  57. if (fightingCharacter == null)
  58. {
  59. throw new ArgumentNullException("fightingCharacter");
  60. }
  61. this.fightingCharacter = fightingCharacter;
  62. this.statistics = statistics;
  63. // sort the player's spell
  64. this.fightingCharacter.Spells.Sort(
  65. delegate(Spell spell1, Spell spell2)
  66. {
  67. // handle null values
  68. if (spell1 == null)
  69. {
  70. return (spell2 == null ? 0 : 1);
  71. }
  72. else if (spell2 == null)
  73. {
  74. return -1;
  75. }
  76. // sort by name
  77. return spell1.Name.CompareTo(spell2.Name);
  78. });
  79. // configure the menu text
  80. titleText = "Spell Book";
  81. selectButtonText = "Cast";
  82. backButtonText = "Back";
  83. xButtonText = String.Empty;
  84. yButtonText = String.Empty;
  85. leftTriggerText = String.Empty;
  86. rightTriggerText = String.Empty;
  87. }
  88. /// <summary>
  89. /// Delegate for spell-selection events.
  90. /// </summary>
  91. public delegate void SpellSelectedHandler(Spell spell);
  92. /// <summary>
  93. /// Responds when an spell is selected by this menu.
  94. /// </summary>
  95. /// <remarks>
  96. /// Typically used by the calling menu, like the combat HUD menu,
  97. /// to respond to selection.
  98. /// </remarks>
  99. public event SpellSelectedHandler SpellSelected;
  100. /// <summary>
  101. /// Respond to the triggering of the Select action (and related key).
  102. /// </summary>
  103. protected override void SelectTriggered(Spell entry)
  104. {
  105. // check the parameter
  106. if (entry == null)
  107. {
  108. return;
  109. }
  110. // make sure the spell can be selected
  111. if (!CanSelectEntry(entry))
  112. {
  113. return;
  114. }
  115. // if the event is valid, fire it and exit this screen
  116. if (SpellSelected != null)
  117. {
  118. SpellSelected(entry);
  119. ExitScreen();
  120. return;
  121. }
  122. }
  123. /// <summary>
  124. /// Returns true if the specified spell can be selected.
  125. /// </summary>
  126. private bool CanSelectEntry(Spell entry)
  127. {
  128. if (entry == null)
  129. {
  130. return false;
  131. }
  132. return (statistics.MagicPoints >= entry.MagicPointCost) &&
  133. (!entry.IsOffensive || CombatEngine.IsActive);
  134. }
  135. /// <summary>
  136. /// Draw the spell at the given position in the list.
  137. /// </summary>
  138. /// <param name="contentEntry">The spell to draw.</param>
  139. /// <param name="position">The position to draw the entry at.</param>
  140. /// <param name="isSelected">If true, this item is selected.</param>
  141. protected override void DrawEntry(Spell entry, Vector2 position,
  142. bool isSelected)
  143. {
  144. // check the parameter
  145. if (entry == null)
  146. {
  147. throw new ArgumentNullException("entry");
  148. }
  149. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  150. Vector2 drawPosition = position;
  151. Color color = isSelected ? Fonts.HighlightColor : Fonts.DisplayColor;
  152. // draw the icon
  153. spriteBatch.Draw(entry.IconTexture, drawPosition + iconOffset, Color.White);
  154. // draw the name
  155. drawPosition.Y += listLineSpacing / 4;
  156. drawPosition.X += nameColumnInterval;
  157. spriteBatch.DrawString(Fonts.GearInfoFont, entry.Name, drawPosition, color);
  158. // draw the level
  159. drawPosition.X += levelColumnInterval;
  160. spriteBatch.DrawString(Fonts.GearInfoFont, entry.Level.ToString(),
  161. drawPosition, color);
  162. // draw the power
  163. drawPosition.X += powerColumnInterval;
  164. string powerText = entry.GetPowerText();
  165. Vector2 powerTextSize = Fonts.GearInfoFont.MeasureString(powerText);
  166. Vector2 powerPosition = drawPosition;
  167. powerPosition.Y -= (float)Math.Ceiling((powerTextSize.Y - 30f) / 2);
  168. spriteBatch.DrawString(Fonts.GearInfoFont, powerText,
  169. powerPosition, color);
  170. // draw the quantity
  171. drawPosition.X += magicCostColumnInterval;
  172. spriteBatch.DrawString(Fonts.GearInfoFont, entry.MagicPointCost.ToString(),
  173. drawPosition, color);
  174. // draw the cast button if needed
  175. if (isSelected)
  176. {
  177. selectButtonText = (CanSelectEntry(entry) && (SpellSelected != null)) ?
  178. "Cast" : String.Empty;
  179. }
  180. }
  181. /// <summary>
  182. /// Draw the description of the selected item.
  183. /// </summary>
  184. protected override void DrawSelectedDescription(Spell entry)
  185. {
  186. // check the parameter
  187. if (entry == null)
  188. {
  189. throw new ArgumentNullException("entry");
  190. }
  191. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  192. Vector2 position = descriptionTextPosition;
  193. // draw the insufficient-mp warning
  194. if (CombatEngine.IsActive && (entry.MagicPointCost > statistics.MagicPoints))
  195. {
  196. // draw the insufficient-mp warning
  197. spriteBatch.DrawString(Fonts.DescriptionFont,
  198. "Not enough MP to Cast Spell", warningMessagePosition,
  199. Color.Red);
  200. }
  201. // draw the description
  202. spriteBatch.DrawString(Fonts.DescriptionFont,
  203. Fonts.BreakTextIntoLines(entry.Description, 90, 3),
  204. spellDescriptionPosition, Fonts.DescriptionColor);
  205. }
  206. /// <summary>
  207. /// Draw the column headers above the list.
  208. /// </summary>
  209. protected override void DrawColumnHeaders()
  210. {
  211. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  212. Vector2 position = listEntryStartPosition;
  213. position.X += nameColumnInterval;
  214. if (!String.IsNullOrEmpty(nameColumnText))
  215. {
  216. spriteBatch.DrawString(Fonts.CaptionFont, nameColumnText, position,
  217. Fonts.CaptionColor);
  218. }
  219. position.X += levelColumnInterval;
  220. if (!String.IsNullOrEmpty(levelColumnText))
  221. {
  222. spriteBatch.DrawString(Fonts.CaptionFont, levelColumnText, position,
  223. Fonts.CaptionColor);
  224. }
  225. position.X += powerColumnInterval;
  226. if (!String.IsNullOrEmpty(powerColumnText))
  227. {
  228. spriteBatch.DrawString(Fonts.CaptionFont, powerColumnText, position,
  229. Fonts.CaptionColor);
  230. }
  231. position.X += magicCostColumnInterval;
  232. if (!String.IsNullOrEmpty(magicCostColumnText))
  233. {
  234. spriteBatch.DrawString(Fonts.CaptionFont, magicCostColumnText, position,
  235. Fonts.CaptionColor);
  236. }
  237. }
  238. }
  239. }