#region File Description //----------------------------------------------------------------------------- // MeleeCombatAction.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using Microsoft.Xna.Framework; using RolePlayingGameData; using Microsoft.Xna.Framework.Graphics; #endregion namespace RolePlaying { /// /// A melee-attack combat action, including related data and calculations. /// class MeleeCombatAction : CombatAction { #region State /// /// Returns true if the action is offensive, targeting the opponents. /// public override bool IsOffensive { get { return true; } } /// /// Returns true if this action requires a target. /// public override bool IsTargetNeeded { get { return true; } } #endregion #region Advancing/Returning Data /// /// The speed at which the advancing character moves, in units per second. /// private const float advanceSpeed = 300f; /// /// The offset from the advance destination to the target position /// private static readonly Vector2 advanceOffset = new Vector2(85f, 0f); /// /// The direction of the advancement. /// private Vector2 advanceDirection; /// /// The distance covered so far by the advance/return action /// private float advanceDistanceCovered = 0f; /// /// The total distance between the original combatant position and the target. /// private float totalAdvanceDistance; #endregion #region Combat Stage /// /// Starts a new combat stage. Called right after the stage changes. /// /// The stage never changes into NotStarted. protected override void StartStage() { switch (stage) { case CombatActionStage.Preparing: // called from Start() { // play the animation combatant.CombatSprite.PlayAnimation("Idle"); } break; case CombatActionStage.Advancing: { // play the animation combatant.CombatSprite.PlayAnimation("Walk"); // calculate the advancing destination if (Target.Position.X > Combatant.Position.X) { advanceDirection = Target.Position - Combatant.OriginalPosition - advanceOffset; } else { advanceDirection = Target.Position - Combatant.OriginalPosition + advanceOffset; } totalAdvanceDistance = advanceDirection.Length(); advanceDirection.Normalize(); advanceDistanceCovered = 0f; } break; case CombatActionStage.Executing: { // play the animation combatant.CombatSprite.PlayAnimation("Attack"); // play the audio Weapon weapon = combatant.Character.GetEquippedWeapon(); if (weapon != null) { AudioManager.PlayCue(weapon.SwingCueName); } else { AudioManager.PlayCue("StaffSwing"); } } break; case CombatActionStage.Returning: { // play the animation combatant.CombatSprite.PlayAnimation("Walk"); // calculate the damage Int32Range damageRange = combatant.Character.TargetDamageRange + combatant.Statistics.PhysicalOffense; Int32Range defenseRange = Target.Character.HealthDefenseRange + Target.Statistics.PhysicalDefense; int damage = Math.Max(0, damageRange.GenerateValue(Session.Random) - defenseRange.GenerateValue(Session.Random)); // apply the damage if (damage > 0) { // play the audio Weapon weapon = combatant.Character.GetEquippedWeapon(); if (weapon != null) { AudioManager.PlayCue(weapon.HitCueName); } else { AudioManager.PlayCue("StaffHit"); } // damage the target Target.DamageHealth(damage, 0); if ((weapon != null) && (weapon.Overlay != null)) { weapon.Overlay.PlayAnimation(0); weapon.Overlay.ResetAnimation(); } } } break; case CombatActionStage.Finishing: { // play the animation combatant.CombatSprite.PlayAnimation("Idle"); } break; case CombatActionStage.Complete: { // play the animation combatant.CombatSprite.PlayAnimation("Idle"); } break; } } /// /// Update the action for the current stage. /// /// /// This function is guaranteed to be called at least once per stage. /// protected override void UpdateCurrentStage(GameTime gameTime) { float elapsedSeconds = (float)gameTime.ElapsedGameTime.TotalSeconds; switch (stage) { case CombatActionStage.Advancing: { // move to the destination if (advanceDistanceCovered < totalAdvanceDistance) { advanceDistanceCovered = Math.Min(advanceDistanceCovered + advanceSpeed * elapsedSeconds, totalAdvanceDistance); } // update the combatant's position combatant.Position = combatant.OriginalPosition + advanceDirection * advanceDistanceCovered; } break; case CombatActionStage.Returning: { // move to the destination if (advanceDistanceCovered > 0f) { advanceDistanceCovered -= advanceSpeed * elapsedSeconds; } combatant.Position = combatant.OriginalPosition + advanceDirection * advanceDistanceCovered; } break; } } /// /// Returns true if the combat action is ready to proceed to the next stage. /// protected override bool IsReadyForNextStage { get { switch (stage) { case CombatActionStage.Preparing: // ready to advance? return true; case CombatActionStage.Advancing: // ready to execute? if (advanceDistanceCovered >= totalAdvanceDistance) { advanceDistanceCovered = totalAdvanceDistance; combatant.Position = combatant.OriginalPosition + advanceDirection * totalAdvanceDistance; return true; } else { return false; } case CombatActionStage.Executing: // ready to return? return combatant.CombatSprite.IsPlaybackComplete; case CombatActionStage.Returning: // ready to finish? if (advanceDistanceCovered <= 0f) { advanceDistanceCovered = 0f; combatant.Position = combatant.OriginalPosition; return true; } else { return false; } case CombatActionStage.Finishing: // ready to complete? return true; } // fall through to the base behavior return base.IsReadyForNextStage; } } #endregion #region Heuristic /// /// The heuristic used to compare actions of this type to similar ones. /// public override int Heuristic { get { return combatant.Character.TargetDamageRange.Average; } } #endregion #region Initialization /// /// Constructs a new MeleeCombatAction object. /// /// The character performing the action. public MeleeCombatAction(Combatant combatant) : base(combatant) { } #endregion #region Updating /// /// Updates the action over time. /// public override void Update(GameTime gameTime) { // update the weapon animation float elapsedSeconds = (float)gameTime.ElapsedGameTime.TotalSeconds; Weapon weapon = Combatant.Character.GetEquippedWeapon(); if ((weapon != null) && (weapon.Overlay != null)) { weapon.Overlay.UpdateAnimation(elapsedSeconds); } // update the action base.Update(gameTime); } #endregion #region Drawing /// /// Draw any elements of the action that are independent of the character. /// public override void Draw(GameTime gameTime, SpriteBatch spriteBatch) { // draw the weapon overlay (typically blood) Weapon weapon = Combatant.Character.GetEquippedWeapon(); if ((weapon != null) && (weapon.Overlay != null) && !weapon.Overlay.IsPlaybackComplete) { weapon.Overlay.Draw(spriteBatch, Target.Position, 0f); } base.Draw(gameTime, spriteBatch); } #endregion } }