MeleeCombatAction.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. //-----------------------------------------------------------------------------
  2. // MeleeCombatAction.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using Microsoft.Xna.Framework;
  9. using RolePlayingGameData;
  10. using Microsoft.Xna.Framework.Graphics;
  11. namespace RolePlaying
  12. {
  13. /// <summary>
  14. /// A melee-attack combat action, including related data and calculations.
  15. /// </summary>
  16. class MeleeCombatAction : CombatAction
  17. {
  18. /// <summary>
  19. /// Returns true if the action is offensive, targeting the opponents.
  20. /// </summary>
  21. public override bool IsOffensive
  22. {
  23. get { return true; }
  24. }
  25. /// <summary>
  26. /// Returns true if this action requires a target.
  27. /// </summary>
  28. public override bool IsTargetNeeded
  29. {
  30. get { return true; }
  31. }
  32. /// <summary>
  33. /// The speed at which the advancing character moves, in units per second.
  34. /// </summary>
  35. private const float advanceSpeed = 300f;
  36. /// <summary>
  37. /// The offset from the advance destination to the target position
  38. /// </summary>
  39. private static readonly Vector2 advanceOffset = new Vector2(85f, 0f);
  40. /// <summary>
  41. /// The direction of the advancement.
  42. /// </summary>
  43. private Vector2 advanceDirection;
  44. /// <summary>
  45. /// The distance covered so far by the advance/return action
  46. /// </summary>
  47. private float advanceDistanceCovered = 0f;
  48. /// <summary>
  49. /// The total distance between the original combatant position and the target.
  50. /// </summary>
  51. private float totalAdvanceDistance;
  52. /// <summary>
  53. /// Starts a new combat stage. Called right after the stage changes.
  54. /// </summary>
  55. /// <remarks>The stage never changes into NotStarted.</remarks>
  56. protected override void StartStage()
  57. {
  58. switch (stage)
  59. {
  60. case CombatActionStage.Preparing: // called from Start()
  61. {
  62. // play the animation
  63. combatant.CombatSprite.PlayAnimation("Idle");
  64. }
  65. break;
  66. case CombatActionStage.Advancing:
  67. {
  68. // play the animation
  69. combatant.CombatSprite.PlayAnimation("Walk");
  70. // calculate the advancing destination
  71. if (Target.Position.X > Combatant.Position.X)
  72. {
  73. advanceDirection = Target.Position -
  74. Combatant.OriginalPosition - advanceOffset;
  75. }
  76. else
  77. {
  78. advanceDirection = Target.Position -
  79. Combatant.OriginalPosition + advanceOffset;
  80. }
  81. totalAdvanceDistance = advanceDirection.Length();
  82. advanceDirection.Normalize();
  83. advanceDistanceCovered = 0f;
  84. }
  85. break;
  86. case CombatActionStage.Executing:
  87. {
  88. // play the animation
  89. combatant.CombatSprite.PlayAnimation("Attack");
  90. // play the audio
  91. Weapon weapon = combatant.Character.GetEquippedWeapon();
  92. if (weapon != null)
  93. {
  94. AudioManager.PlayCue(weapon.SwingCueName);
  95. }
  96. else
  97. {
  98. AudioManager.PlayCue("StaffSwing");
  99. }
  100. }
  101. break;
  102. case CombatActionStage.Returning:
  103. {
  104. // play the animation
  105. combatant.CombatSprite.PlayAnimation("Walk");
  106. // calculate the damage
  107. Int32Range damageRange = combatant.Character.TargetDamageRange +
  108. combatant.Statistics.PhysicalOffense;
  109. Int32Range defenseRange = Target.Character.HealthDefenseRange +
  110. Target.Statistics.PhysicalDefense;
  111. int damage = Math.Max(0,
  112. damageRange.GenerateValue(Session.Random) -
  113. defenseRange.GenerateValue(Session.Random));
  114. // apply the damage
  115. if (damage > 0)
  116. {
  117. // play the audio
  118. Weapon weapon = combatant.Character.GetEquippedWeapon();
  119. if (weapon != null)
  120. {
  121. AudioManager.PlayCue(weapon.HitCueName);
  122. }
  123. else
  124. {
  125. AudioManager.PlayCue("StaffHit");
  126. }
  127. // damage the target
  128. Target.DamageHealth(damage, 0);
  129. if ((weapon != null) && (weapon.Overlay != null))
  130. {
  131. weapon.Overlay.PlayAnimation(0);
  132. weapon.Overlay.ResetAnimation();
  133. }
  134. }
  135. }
  136. break;
  137. case CombatActionStage.Finishing:
  138. {
  139. // play the animation
  140. combatant.CombatSprite.PlayAnimation("Idle");
  141. }
  142. break;
  143. case CombatActionStage.Complete:
  144. {
  145. // play the animation
  146. combatant.CombatSprite.PlayAnimation("Idle");
  147. }
  148. break;
  149. }
  150. }
  151. /// <summary>
  152. /// Update the action for the current stage.
  153. /// </summary>
  154. /// <remarks>
  155. /// This function is guaranteed to be called at least once per stage.
  156. /// </remarks>
  157. protected override void UpdateCurrentStage(GameTime gameTime)
  158. {
  159. float elapsedSeconds = (float)gameTime.ElapsedGameTime.TotalSeconds;
  160. switch (stage)
  161. {
  162. case CombatActionStage.Advancing:
  163. {
  164. // move to the destination
  165. if (advanceDistanceCovered < totalAdvanceDistance)
  166. {
  167. advanceDistanceCovered = Math.Min(advanceDistanceCovered +
  168. advanceSpeed * elapsedSeconds, totalAdvanceDistance);
  169. }
  170. // update the combatant's position
  171. combatant.Position = combatant.OriginalPosition +
  172. advanceDirection * advanceDistanceCovered;
  173. }
  174. break;
  175. case CombatActionStage.Returning:
  176. {
  177. // move to the destination
  178. if (advanceDistanceCovered > 0f)
  179. {
  180. advanceDistanceCovered -= advanceSpeed * elapsedSeconds;
  181. }
  182. combatant.Position = combatant.OriginalPosition +
  183. advanceDirection * advanceDistanceCovered;
  184. }
  185. break;
  186. }
  187. }
  188. /// <summary>
  189. /// Returns true if the combat action is ready to proceed to the next stage.
  190. /// </summary>
  191. protected override bool IsReadyForNextStage
  192. {
  193. get
  194. {
  195. switch (stage)
  196. {
  197. case CombatActionStage.Preparing: // ready to advance?
  198. return true;
  199. case CombatActionStage.Advancing: // ready to execute?
  200. if (advanceDistanceCovered >= totalAdvanceDistance)
  201. {
  202. advanceDistanceCovered = totalAdvanceDistance;
  203. combatant.Position = combatant.OriginalPosition +
  204. advanceDirection * totalAdvanceDistance;
  205. return true;
  206. }
  207. else
  208. {
  209. return false;
  210. }
  211. case CombatActionStage.Executing: // ready to return?
  212. return combatant.CombatSprite.IsPlaybackComplete;
  213. case CombatActionStage.Returning: // ready to finish?
  214. if (advanceDistanceCovered <= 0f)
  215. {
  216. advanceDistanceCovered = 0f;
  217. combatant.Position = combatant.OriginalPosition;
  218. return true;
  219. }
  220. else
  221. {
  222. return false;
  223. }
  224. case CombatActionStage.Finishing: // ready to complete?
  225. return true;
  226. }
  227. // fall through to the base behavior
  228. return base.IsReadyForNextStage;
  229. }
  230. }
  231. /// <summary>
  232. /// The heuristic used to compare actions of this type to similar ones.
  233. /// </summary>
  234. public override int Heuristic
  235. {
  236. get
  237. {
  238. return combatant.Character.TargetDamageRange.Average;
  239. }
  240. }
  241. /// <summary>
  242. /// Constructs a new MeleeCombatAction object.
  243. /// </summary>
  244. /// <param name="character">The character performing the action.</param>
  245. public MeleeCombatAction(Combatant combatant)
  246. : base(combatant) { }
  247. /// <summary>
  248. /// Updates the action over time.
  249. /// </summary>
  250. public override void Update(GameTime gameTime)
  251. {
  252. // update the weapon animation
  253. float elapsedSeconds = (float)gameTime.ElapsedGameTime.TotalSeconds;
  254. Weapon weapon = Combatant.Character.GetEquippedWeapon();
  255. if ((weapon != null) && (weapon.Overlay != null))
  256. {
  257. weapon.Overlay.UpdateAnimation(elapsedSeconds);
  258. }
  259. // update the action
  260. base.Update(gameTime);
  261. }
  262. /// <summary>
  263. /// Draw any elements of the action that are independent of the character.
  264. /// </summary>
  265. public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
  266. {
  267. // draw the weapon overlay (typically blood)
  268. Weapon weapon = Combatant.Character.GetEquippedWeapon();
  269. if ((weapon != null) && (weapon.Overlay != null) &&
  270. !weapon.Overlay.IsPlaybackComplete)
  271. {
  272. weapon.Overlay.Draw(spriteBatch, Target.Position, 0f);
  273. }
  274. base.Draw(gameTime, spriteBatch);
  275. }
  276. }
  277. }