public void GetTargets_WithBlockedTargets_ReturnsEmpty(Ability ability,
                                                               int target,
                                                               Formation formation)
        {
            var entities = FormationTargeter.GetTargets(ability, target, formation);

            Assert.Empty(entities);
        }
        public void GetTargets_WithPointBlankTargets_ReturnsExpected(Ability ability,
                                                                     int target,
                                                                     Formation formation,
                                                                     IEnumerable <CombatEntity> expectedTargets)
        {
            var entities = FormationTargeter.GetTargets(ability, target, formation);

            // Contains all of the same items
            var expectations = !expectedTargets.Except(entities).Any() && expectedTargets.Count() == entities.Count();

            Assert.True(expectations);
        }
Пример #3
0
        /// <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);
        }
Пример #4
0
        /// <summary>
        /// Activates the effects of a DelayedAbility on its target formation, returning an IEnumerable of
        /// CombatEntities affected by the DelayedAbility.
        /// </summary>
        /// <param name="ability">The DelayedAbility to activate the effects of.</param>
        /// <returns></returns>
        public IEnumerable <CombatEntity> PerformDelayedAbility(DelayedAbility ability)
        {
            var affectedEntities = new List <CombatEntity>();
            var targetPosition   = ability.TargetPosition;

            if (ability.BaseAbility.IsPositionStatic)
            {
                targetPosition = ability.BaseAbility.CenterOfTargets;
            }

            var targets = FormationTargeter.GetTargets(ability.BaseAbility, targetPosition, ability.TargetFormation);

            if (targets == null || targets.Count() == 0)
            {
                return(affectedEntities);
            }

            ApplyEffects(ability, targets);

            return(targets);
        }
        public void GetTargets_WithSingleTarget_ReturnsNotNull(Ability ability, int target, Formation formation)
        {
            var entities = FormationTargeter.GetTargets(ability, target, formation);

            Assert.NotEmpty(entities);
        }
Пример #6
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));
            }
        }