/// <summary>
        /// Calculates healing and damage, then checks and applies any critical strikes, then applies healing and damage.
        /// </summary>
        /// <param name="actor">The character applying the healing and damage.</param>
        /// <param name="action">The action being performed.</param>
        /// <param name="targets">The list of characters the action is targeting.</param>
        private void ApplyHealingAndDamage(Character actor, ActionBase action, IReadOnlyList <Character> targets)
        {
            var damageTypes = DamageCalculator.GetDamage(actor, action);

            int[] damage = damageTypes.AsArray();
            var   postHealthChangedDict = new Dictionary <int, int>();
            var   changeAmount          = new Dictionary <int, int>();
            var   loggableData          = new List <KeyValuePair <string, int> >();
            var   preHealthChangedDict  = new Dictionary <int, int>();

            for (int i = 0; i < targets.Count(); i++)
            {
                int startHealth = targets[i].CurrentHealth;
                preHealthChangedDict.Add(targets[i].Id, startHealth);
                // Calculate and apply healing
                int percentHealAmount  = targets[i].CurrentMaxHealth * DamageCalculator.GetHealingPercentage(actor, action);
                int modifiedHealAmount = DamageCalculator.GetHealing(actor, action);
                int totalDamage        = DamageCalculator.GetTotalDamage(damageTypes, targets[i]);

                // Determine if a critical happened
                int totalCritChance = action.CritChance > 0 ? action.CritChance + actor.CritChance : 0;
                int rand            = _random.Next(1, 101);
                if (totalCritChance >= rand)
                {
                    int multiplier = action.CritMultiplier + actor.CritMultiplier + 100;
                    modifiedHealAmount = modifiedHealAmount * multiplier / 100;
                    totalDamage        = totalDamage * multiplier / 100;
                }

                _characterController.ModifyCurrentHealth(targets[i], percentHealAmount);
                _characterController.ModifyCurrentHealth(targets[i], modifiedHealAmount);
                _characterController.ModifyCurrentHealth(targets[i], totalDamage);
                _threatController.ApplyThreat(actor,
                                              targets[i],
                                              percentHealAmount + modifiedHealAmount + totalDamage,
                                              action.Threat,
                                              action.ThreatMultiplier);

                // Prepare event data
                int healthChange = targets[i].CurrentHealth - startHealth;
                changeAmount.Add(targets[i].Id, healthChange);
                loggableData.Add(new KeyValuePair <string, int>(targets[i].Name, healthChange));
                postHealthChangedDict.Add(targets[i].Id, targets[i].CurrentHealth);
            }

            if (changeAmount.Values.Any(val => val != 0))
            {
                string logMessage = CombatMessenger.GetHealthChangedMessage(actor.Name,
                                                                            action.Name,
                                                                            loggableData);

                CharactersHealthChanged?.Invoke(this, new CharactersHealthChangedEventArgs()
                {
                    PostCharactersChanged = postHealthChangedDict,
                    PreCharactersChanged  = preHealthChangedDict,
                    ChangeAmount          = changeAmount,
                    LogMessage            = logMessage
                });
            }
        }
 /// <summary>
 /// Creates and returns an instance of an AppliedStatus.
 /// </summary>
 /// <param name="applicator">The character from which the status originated.</param>
 /// <param name="statusBase">The status which the AppliedStatus is based off.</param>
 /// <returns>A wrapper around a status effect, containing modified values and a turn counter.</returns>
 private AppliedStatus CreateAppliedStatus(Character applicator, StatusEffect statusBase)
 {
     return(new AppliedStatus()
     {
         Applicator = applicator,
         BaseStatus = statusBase,
         TotalDamage = DamageCalculator.GetDamage(applicator, statusBase),
         HealAmount = DamageCalculator.GetHealing(applicator, statusBase),
         HealPercentage = DamageCalculator.GetHealingPercentage(applicator, statusBase),
         TurnsRemaining = statusBase.Duration,
         CritChance = applicator.CritChance + statusBase.CritChance,
         CritMultiplier = applicator.CritMultiplier + statusBase.CritMultiplier,
         StackCount = 1
     });
 }
        /// <summary>
        /// Creates a delayed status instance, which will apply a status effect after a set amount of turns has passed.
        /// </summary>
        /// <param name="applicator">The character from which the spell originated.</param>
        /// <param name="statusBase">The status effect to use as the base for the delayed status.</param>
        /// <param name="target">A list of characters to apply to status effects to.</param>
        /// <param name="spellDelay">How many rounds until the buffs activate.</param>
        public void CreateDelayedStatus(Character applicator, StatusEffect statusBase, IReadOnlyList <int> targets, int spellDelay)
        {
            var delayedStatus = new DelayedStatus()
            {
                Applicator     = applicator,
                BaseStatus     = statusBase,
                TotalDamage    = DamageCalculator.GetDamage(applicator, statusBase),
                HealAmount     = DamageCalculator.GetHealing(applicator, statusBase),
                HealPercentage = DamageCalculator.GetHealingPercentage(applicator, statusBase),
                SpellDelay     = spellDelay,
                CritChance     = applicator.CritChance + statusBase.CritChance,
                CritMultiplier = applicator.CritMultiplier + statusBase.CritMultiplier,
                Targets        = targets
            };

            if (!_delayedStatuses.ContainsKey(applicator))
            {
                _delayedStatuses[applicator] = new List <DelayedStatus>();
            }
            _delayedStatuses[applicator].Add(delayedStatus);
        }
        /// <summary>
        /// Creates a delayed action that has it's effects applied after an amount of rounds has passed.
        /// </summary>
        /// <param name="actor">The character performing the action.</param>
        /// <param name="action">The action being performed.</param>
        /// <param name="targets">The list of target positions the action targets.</param>
        private void CreateDelayedAction(Character actor, ActionBase action, IReadOnlyList <int> targets)
        {
            if (!_delayedActions.ContainsKey(actor))
            {
                _delayedActions[actor] = new List <DelayedAction>();
            }

            DamageTypes totalDamage       = DamageCalculator.GetDamage(actor, action);
            int         totalHeal         = DamageCalculator.GetHealing(actor, action);
            int         percentageHealing = DamageCalculator.GetHealingPercentage(actor, action);

            int rand            = _random.Next(1, 101);
            int totalCritChance = action.CritChance > 0 ? action.CritChance + actor.CritChance : 0;

            if (totalCritChance > rand)
            {
                int critMultiplier = actor.CritMultiplier + action.CritMultiplier + 100;
                totalDamage = totalDamage * critMultiplier / 100;
                totalHeal   = totalHeal * critMultiplier / 100;
            }

            _delayedActions[actor].Add(new DelayedAction()
            {
                Actor          = actor,
                BaseAction     = action,
                TotalDamage    = totalDamage,
                HealAmount     = totalHeal,
                HealPercentage = percentageHealing,
                TurnsRemaining = action.Delay,
                Targets        = targets
            });

            DelayedActionBeginChannel?.Invoke(this, new CombatLoggableEventArgs()
            {
                LogMessage = CombatMessenger.GetBeginChannelMessage(actor.Name, action.Name)
            });
        }
        /// <summary>
        /// Applies a status effect on a character.
        /// </summary>
        /// <param name="applicator">The character that is applying the status effect.</param>
        /// <param name="status">The status effect being applied.</param>
        /// <param name="character">The character the status effect is being applied on.</param>
        public void ApplyStatus(Character applicator, StatusEffect status, Character character, bool invokeAppliedStatusEvent = true)
        {
            if (!_appliedStatuses.ContainsKey(character))
            {
                _appliedStatuses[character] = new List <AppliedStatus>();
            }
            // If the the same type of status is already on a character
            if (_appliedStatuses[character].Any(applied => applied.BaseStatus == status))
            {
                var matchingStatus = _appliedStatuses[character].First(applied => applied.BaseStatus == status);
                // If the status is stackable, refresh the duration and apply another layer of effects
                if (status.Stackable && matchingStatus.StackCount < status.StackSize)
                {
                    matchingStatus.TurnsRemaining  = status.Duration;
                    matchingStatus.TotalDamage    += DamageCalculator.GetDamage(applicator, status);
                    matchingStatus.HealAmount     += DamageCalculator.GetHealing(applicator, status);
                    matchingStatus.HealPercentage += DamageCalculator.GetHealingPercentage(applicator, status);
                    matchingStatus.CritChance      = status.CritChance + character.CritChance;
                    matchingStatus.CritMultiplier  = status.CritMultiplier + character.CritMultiplier;
                    ApplyStatusEffects(status, character);
                    matchingStatus.StackCount++;
                }
                // If the status is stackable but has reached its stack limit, refresh the duration only
                else if (status.Stackable)
                {
                    matchingStatus.TurnsRemaining = status.Duration;
                }
                // If the status isn't stackable, refresh the duration and reset the damage
                else
                {
                    matchingStatus.TurnsRemaining = status.Duration;
                    matchingStatus.TotalDamage    = DamageCalculator.GetDamage(applicator, status);
                    matchingStatus.HealAmount     = DamageCalculator.GetHealing(applicator, status);
                    matchingStatus.HealPercentage = DamageCalculator.GetHealingPercentage(applicator, status);
                    matchingStatus.CritChance     = status.CritChance + character.CritChance;
                    matchingStatus.CritMultiplier = status.CritMultiplier + character.CritMultiplier;
                }
            }
            // Create and apply a new status effect on a character
            else
            {
                ApplyStatusEffects(status, character);
                _appliedStatuses[character].Add(CreateAppliedStatus(applicator, status));
                if (status.IsDebuff)
                {
                    character.Debuffs.Add(status);
                }
                else
                {
                    character.Buffs.Add(status);
                }
            }

            if (invokeAppliedStatusEvent)
            {
                StatusEffectApplied?.Invoke(this, new StatusEffectAppliedEventArgs()
                {
                    AffectedCharacterIds = new List <int>()
                    {
                        character.Id
                    },
                    LogMessage = CombatMessenger.GetAffectedByStatusMessage(status.Name, character.Name)
                });
            }
        }