Beispiel #1
0
        /// <summary>
        /// Applies the effects of the ability being used on all of it's targets.
        /// </summary>
        /// <param name="attacker">The entity performing the ability.</param>
        /// <param name="ability">The ability to apply the effects of.</param>
        /// <param name="targets">The targets of the ability.</param>
        private void ApplyEffects(CombatEntity attacker, Ability ability, IEnumerable <CombatEntity> targets)
        {
            bool isCrit = IsCritical(attacker, ability);

            foreach (var target in targets)
            {
                var damage  = DamageCalculator.GetTotalDamage(attacker, target, ability, isCrit);
                var healing = DamageCalculator.GetHeal(attacker, ability, isCrit);
                healing += target.Resources.MaxHealth * ability.PercentHeal / 100;

                target.Resources.CurrentHealth += healing;
                target.Resources.CurrentHealth -= DamageCalculator.GetDamageTypesAsInt(damage);
                if (target.Resources.CurrentHealth > 0)
                {
                    _statusEffectManager.Apply(target, attacker, ability.AppliedStatusEffects, isCrit);
                    if (target.Resources.CurrentHealth > target.Resources.MaxHealth)
                    {
                        target.Resources.CurrentHealth = target.Resources.MaxHealth;
                    }
                }
                else
                {
                    _statusEffectManager.RemoveAll(target);
                    target.Resources.CurrentHealth = 0;
                }
            }

            if (ability.SelfAppliedStatusEffects.Any())
            {
                _statusEffectManager.Apply(attacker, attacker, ability.SelfAppliedStatusEffects, isCrit);
            }
        }
        /// <summary>
        /// Applies a StatusEffect onto a CombatEntity.
        /// </summary>
        /// <param name="recipient">The CombatEntity who is receiving the StatusEffect.</param>
        /// <param name="applicator">The CombatEntity applying the StatusEffect on the receiver.</param>
        /// <param name="statusEffect">The StatusEffect to apply onto the receiver.</param>
        /// <param name="isCrit">If true, will include critical damage in the calculations.</param>
        public void Apply(CombatEntity recipient,
                          CombatEntity applicator,
                          StatusEffect statusEffect,
                          bool isCrit)
        {
            var status = recipient.StatusEffects.FirstOrDefault(se => se.BaseStatus.Id == statusEffect.Id);

            if (status == null)
            {
                var appliedStatusEffect = CreateAppliedStatus(applicator, recipient, statusEffect);
                ApplyStatEffects(recipient, statusEffect);
                recipient.StatusEffects.Add(appliedStatusEffect);
            }
            else
            {
                // Apply another stack of StatusEffect
                if (statusEffect.StackSize > 1 && status.CurrentStacks < statusEffect.StackSize)
                {
                    status.CumulativeDamage += DamageCalculator.GetDamage(applicator, statusEffect, isCrit);
                    status.CumulativeHeal   += DamageCalculator.GetHeal(applicator, statusEffect, isCrit);
                    status.CurrentStacks++;
                    status.Duration = statusEffect.Duration;
                }
                // Can't apply another stack, refresh duration instead
                else
                {
                    status.Duration = statusEffect.Duration;
                }
            }
        }
        /// <summary>
        /// Creates an instance of an AppliedStatusEffect from the given StatusEffect.
        /// </summary>
        /// <param name="applicator">The CombatEntity applying this instance of the AppliedStatusEffect.</param>
        /// <param name="statusEffect">The base to use for the AppliedStatusEffect.</param>
        /// <returns>Returns an instance of an AppliedStatusEffect that keeps track of a StatusEffect on a CombatEntity.</returns>
        private AppliedStatusEffect CreateAppliedStatus(CombatEntity applicator, CombatEntity recipient, StatusEffect statusEffect)
        {
            var appliedStatus = new AppliedStatusEffect();

            appliedStatus.BaseStatus       = statusEffect;
            appliedStatus.CumulativeDamage = DamageCalculator.GetDamage(applicator, statusEffect);
            appliedStatus.CumulativeHeal   = DamageCalculator.GetHeal(applicator, statusEffect);
            appliedStatus.CumulativeHeal  += DamageCalculator.GetPercentageHeal(recipient.Resources.MaxHealth, statusEffect.PercentHealPerTurn);

            appliedStatus.CurrentStacks = 1;
            appliedStatus.Duration      = statusEffect.Duration;

            return(appliedStatus);
        }
Beispiel #4
0
        /// <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);
        }
Beispiel #5
0
        /// <summary>
        /// Returns a FormationEvaluation for the given CombatEntity which will use the given Ability on
        /// the provided Formation.
        /// <para>The FormationEvaluation will contain the best target location for the given Ability depending
        /// on the AiRandomnesFactor for the ai Formation.</para>
        /// </summary>
        /// <param name="activeEntity">The CombatEntity that will be acting.</param>
        /// <param name="ability">The Ability being evaluated.</param>
        /// <param name="formation">The Formation being targeted.</param>
        /// <param name="isEnemyFormation">True if the provided Formation is an enemy Formation. False if allied.</param>
        /// <returns></returns>
        private FormationEvaluation EvaluateFormation(CombatEntity activeEntity,
                                                      Ability ability,
                                                      Formation formation,
                                                      bool isEnemyFormation)
        {
            bool isOffensive = ability.IsOffensive.GetValueOrDefault();

            // Only allows offensive abilities against enemy formations and defensive abilities on allied formations
            if ((!isOffensive && isEnemyFormation) || (isOffensive && !isEnemyFormation))
            {
                return(null);
            }

            var comparer    = new DuplicateKeyComparer();
            var evaluations = new SortedList <int, FormationEvaluation>(comparer);

            // Calculate total ai weight modifier for this ability
            int aiWeightModifier = ability.SelfAppliedStatusEffects.Concat(ability.AppliedStatusEffects)
                                   .Select(status => status.AiWeightModifier)
                                   .Sum();

            aiWeightModifier += ability.AiWeightModifier;

            // Evaluate every target in the formation and record the evaluations in the SortedList
            for (int i = 0; i < formation.Positions.Length; i++)
            {
                for (int j = 0; j < formation.Positions[i].Length; j++)
                {
                    int newCenter = i * GameplayConstants.MaxFormationRows + j + 1;
                    var targets   = FormationTargeter.GetTargets(ability, newCenter, formation);

                    // No valid targets, skip this position
                    if (targets == null || targets.Count() <= 0)
                    {
                        continue;
                    }

                    int totalThreat = targets.Select(entity => entity.Threat)
                                      .Sum();

                    // Calculate total value using damage if using the ability offensively
                    if (isEnemyFormation && isOffensive)
                    {
                        // Ignore dead entities
                        targets = targets.Where(entity => entity.Resources.CurrentHealth > 0).ToList();
                        if (targets.Count() == 0)
                        {
                            continue;
                        }

                        // Gets total potential damage dealt to each target as a percentage
                        int damagePercentage = targets.Select(entity =>
                        {
                            int damage          = DamageCalculator.GetTotalDamageAsInt(activeEntity, entity, ability);
                            int damageAsPercent = damage * 100 / entity.Resources.MaxHealth;

                            return(damageAsPercent + DamageCalculator.GetPercentageDamageAsInt(entity, ability));
                        }).Sum();

                        int totalValue = totalThreat + damagePercentage + aiWeightModifier;

                        evaluations.Add(totalValue, new FormationEvaluation
                        {
                            TargetLocation = newCenter,
                            TotalValue     = totalValue
                        });
                    }

                    // Calculate total value using healing if using the ability defensively
                    else if (!isEnemyFormation && !isOffensive)
                    {
                        // Ignore dead entities, update later when adding resurrection spells
                        targets = targets.Where(entity => entity.Resources.CurrentHealth > 0).ToList();
                        if (targets.Count() == 0)
                        {
                            continue;
                        }

                        int healPercentage = targets.Select(entity =>
                        {
                            // Value cannot exceed max heal amount, prevents ai from wanting to overheal
                            int maxHealPercent = 100 - (entity.Resources.CurrentHealth * 100 / entity.Resources.MaxHealth);
                            int healPercent    = DamageCalculator.GetHeal(activeEntity, ability) * 100 / entity.Resources.MaxHealth;
                            healPercent       += ability.PercentHeal;

                            return((healPercent > maxHealPercent) ? maxHealPercent : healPercent);
                        }).Sum();

                        int totalValue = totalThreat + healPercentage + aiWeightModifier;

                        evaluations.Add(totalValue, new FormationEvaluation
                        {
                            TargetLocation = newCenter,
                            TotalValue     = totalValue
                        });
                    }
                }
            }

            if (evaluations.Count == 0)
            {
                return(null);
            }

            // Returns best target evaluation if no randomness
            if (_myFormation.AiRandomness <= 0)
            {
                return(evaluations.Last().Value);
            }

            // If any ai randomness, choose randomly between the best targets
            else
            {
                var highestValues = evaluations.TakeLast(_myFormation.AiRandomness + 1)
                                    .Select(kvp => kvp.Value);

                var random = _rand.Next(highestValues.Count());

                return(highestValues.ElementAt(random));
            }
        }