/// <summary> /// Returns true if the provided CombatEntity has enough resources to use an Ability. /// </summary> /// <param name="attacker">The attacking CombatEntity.</param> /// <param name="ability">The ability being used by the CombatEntity.</param> /// <returns></returns> private bool HasEnoughResources(CombatEntity attacker, Ability ability, out string failureReason) { failureReason = ""; int actionPointCost = ResourceCalculator.GetTotalActionPointCost(attacker, ability); int manaCost = ResourceCalculator.GetTotalManaCost(attacker, ability); int healthCost = ResourceCalculator.GetTotalHealthCost(attacker, ability); if (attacker.Resources.CurrentActionPoints < actionPointCost) { failureReason = BattleErrorWriter.WriteInsufficientActionPoints(attacker, ability); return(false); } if (attacker.Resources.CurrentMana < manaCost) { failureReason = BattleErrorWriter.WriteInsufficientMana(attacker, ability); return(false); } if (attacker.Resources.CurrentHealth < healthCost) { failureReason = BattleErrorWriter.WriteInsufficientHealth(attacker, ability); return(false); } return(true); }
/// <summary> /// Performs an ability on a target location, applying damage, healing, and status effects to any targets. /// </summary> /// <param name="attacker">The attacker performing the ability.</param> /// <param name="ability">The ability used to attack the enemy.</param> /// <param name="targetPosition">The position to target with the ability being used.</param> /// <param name="targetFormation">The Formation that the CombatEntity is targeting with its action.</param> /// <returns></returns> public AbilityResult PerformAbility(CombatEntity attacker, Ability ability, int targetPosition, Formation targetFormation) { var result = new AbilityResult(); // Target position is out of bounds if (!ability.IsPointBlank && !ability.IsPositionStatic && (targetPosition > 9 || targetPosition < 1)) { result.FailureReason = BattleErrorWriter.WriteTargetPositionOutOfBounds(); return(result); } if (!HasEnoughResources(attacker, ability, out string failureReason)) { result.FailureReason = failureReason; return(result); } if (ability.IsPointBlank) { targetPosition = GetTargetPosition(attacker, targetFormation); } var targets = FormationTargeter.GetTargets(ability, targetPosition, targetFormation).ToList(); if (targets == null || targets.Count == 0) { result.FailureReason = BattleErrorWriter.WriteNoTargets(); return(result); } // Filter out all dead targets targets = targets.Where(entity => entity != null && entity.Resources.CurrentHealth > 0).ToList(); // Will be an invalid Ability usage if all targets are dead (later on will add ability to revive) if (targets.Count == 0) { result.FailureReason = BattleErrorWriter.WriteAllTargetsAreDead(); return(result); } ConsumeResources(attacker, ability); ApplyEffects(attacker, ability, targets); targets.Add(attacker); result.AffectedEntities = targets; return(result); }
/// <summary> /// Creates an instance of a DelayedAbility. /// </summary> /// <param name="attacker">The CombatEntity starting the effect of a DelayedAbility.</param> /// <param name="ability">The Ability to convert into a DelayedAbility.</param> /// <param name="action">The object containing details about the action being performed.</param> /// <param name="targetFormation">The Formation that the CombatEntity is targeting with its action.</param> /// <returns></returns> public DelayedAbilityResult CreateDelayedAbility(CombatEntity attacker, Ability ability, BattleAction action, Formation targetFormation) { var result = new DelayedAbilityResult(); // Target out of bounds if (!ability.IsPointBlank && !ability.IsPositionStatic && (action.TargetPosition > 9 || action.TargetPosition < 1)) { result.FailureReason = BattleErrorWriter.WriteTargetPositionOutOfBounds(); return(result); } if (!HasEnoughResources(attacker, ability, out string failureReason)) { result.FailureReason = failureReason; return(result); } int targetPosition = action.TargetPosition; if (ability.IsPointBlank) { targetPosition = GetTargetPosition(attacker, targetFormation); } ConsumeResources(attacker, ability); bool isCrit = IsCritical(attacker, ability); result.DelayedAbility = new DelayedAbility { Actor = attacker, BaseAbility = ability, StoredDamage = DamageCalculator.GetDamage(attacker, ability, isCrit), StoredHealing = DamageCalculator.GetHeal(attacker, ability, isCrit), TargetFormation = targetFormation, TargetPosition = action.TargetPosition, IsCrit = isCrit, TurnsLeft = ability.DelayedTurns }; return(result); }
/// <summary> /// Performs an ability using the specified parameters. /// </summary> /// <param name="action">The object containing the original battle commands.</param> /// <param name="actor">The CombatEntity performing the ability.</param> /// <param name="actorFormation">The Formation of the CombatEntity performing the Ability.</param> /// <param name="isAttacker">Set to true if the CombatEntity is on the attacking side in battle.</param> /// <param name="ability">The Ability being performed.</param> /// <param name="result">A reference to the BattleActionResult object.</param> /// <returns>Returns an IEnumerable of CombatEntities affected by the Ability.</returns> private IEnumerable <CombatEntity> PerformAbility(BattleAction action, CombatEntity actor, Formation actorFormation, bool isAttacker, Ability ability, ref BattleActionResult result) { IEnumerable <CombatEntity> affectedEntities; Item item = null; // If using an item, prepare item to have it's charges deducted later if (action.IsUsingItem) { item = actor.EquippedItems.FirstOrDefault(i => i.ConsumableAbility.Id == action.AbilityId); if (item == null) { item = actor.PlayerInventory .Items .FirstOrDefault(i => i != null && i.ConsumableAbility.Id == action.AbilityId); } if (item == null) { result.FailureReason = BattleErrorWriter.WriteItemDoesntExist(actor); result.IsSuccess = false; return(null); } ability = item.ConsumableAbility; } // Not an item, check to see if character is restricted from using Ability else { ability = actor.Abilities.FirstOrDefault(abi => abi.Id == action.AbilityId); if (ability == null) { result.FailureReason = BattleErrorWriter.WriteAbilityDoesntExist(actor); result.IsSuccess = false; return(null); } if (ability.IsSpell && actor.StatusEffects.Any(se => se.BaseStatus.IsSilenced)) { result.FailureReason = BattleErrorWriter.WriteEntityIsSilenced(actor, ability); result.IsSuccess = false; return(null); } if (!ability.IsSpell && actor.StatusEffects.Any(se => se.BaseStatus.IsRestricted)) { result.FailureReason = BattleErrorWriter.WriteEntityIsRestricted(actor, ability); result.IsSuccess = false; return(null); } } var targetFormation = GetFormation(action.TargetFormationId, out bool throwAway); // If the ability has a delay, create a DelayedAbility if (ability.DelayedTurns > 0) { var delayedAbilityResult = _abilityManager.CreateDelayedAbility(actor, ability, action, targetFormation); if (delayedAbilityResult.DelayedAbility == null) { result.FailureReason = delayedAbilityResult.FailureReason; result.IsSuccess = false; return(null); } if (isAttacker) { _battle.AttackerDelayedAbilities.Add(delayedAbilityResult.DelayedAbility); } else { _battle.DefenderDelayedAbilities.Add(delayedAbilityResult.DelayedAbility); } affectedEntities = new List <CombatEntity> { actor }; } // Not a delayed ability, try to apply effects immediately else { var abilityResult = _abilityManager.PerformAbility(actor, ability, action.TargetPosition, targetFormation); if (abilityResult.FailureReason != null) { result.FailureReason = abilityResult.FailureReason; result.IsSuccess = false; return(null); } affectedEntities = abilityResult.AffectedEntities; } CheckForDeadEntities(affectedEntities, targetFormation); // Action was successful, remove the actor from characters free to act _battle.ActionsLeftPerFormation[actorFormation].Remove(actor); // If Ability was granted by an item, reduce the charges of the item if (item != null) { _equipmentManager.ReduceCharges(actor, item); } return(affectedEntities); }
/// <summary> /// Performs an action and returns true if the action was a success. /// </summary> /// <param name="action">The action to perform, containing data about the actor and the abilities used.</param> /// <returns></returns> public async Task <BattleActionResult> PerformActionAsync(BattleAction action) { IEnumerable <CombatEntity> affectedEntities = null; CombatEntity nextActiveEntity = null; Ability ability = null; CombatEntity actor = null; var result = new BattleActionResult { IsSuccess = true }; lock (_key) { if (_battle == null) { result.FailureReason = BattleErrorWriter.WriteNotInitiated(); result.IsSuccess = false; return(result); } var actorFormation = GetFormation(action.OwnerId, out bool isAttacker); if (actorFormation == null) { result.FailureReason = BattleErrorWriter.WriteNotParticipating(); result.IsSuccess = false; return(result); } actor = actorFormation.Positions.FirstOrDefaultTwoD(entity => entity != null && entity.Id == action.ActorId); if (actor == null) { result.FailureReason = BattleErrorWriter.WriteActorNotFound(); result.IsSuccess = false; return(result); } // Not player's turn if (_battle.IsDefenderTurn != !isAttacker) { result.FailureReason = BattleErrorWriter.WriteNotPlayersTurn(); result.IsSuccess = false; return(result); } if (!_battle.ActionsLeftPerFormation.ContainsKey(actorFormation)) { result.FailureReason = BattleErrorWriter.WriteNoMoreActions(); result.IsSuccess = false; return(result); } if (!_battle.ActionsLeftPerFormation[actorFormation].Contains(actor)) { result.FailureReason = BattleErrorWriter.WriteEntityCannotAct(actor); result.IsSuccess = false; return(result); } if (actor.StatusEffects.Any(se => se.BaseStatus.IsStunned)) { result.FailureReason = BattleErrorWriter.WriteEntityIsStunned(actor); result.IsSuccess = false; return(result); } if (!action.IsDefending && !action.IsFleeing) { affectedEntities = PerformAbility(action, actor, actorFormation, isAttacker, ability, ref result); if (affectedEntities == null) { return(result); } } // Is defending or fleeing else { if (action.IsDefending) { affectedEntities = PerformDefend(actor, actorFormation, out nextActiveEntity); } else if (action.IsFleeing) { affectedEntities = PerformFlee(actor, actorFormation); } } } await Task.Run(() => SuccessfulActionEvent.Invoke(this, new SuccessfulActionEventArgs { Ability = ability, Action = action, Actor = actor, AffectedEntities = affectedEntities, ParticipantIds = _participantIds, NextActiveEntityId = nextActiveEntity != null ? nextActiveEntity.Id : -1 })); return(result); }