/// <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>
        /// Applies healing then damage from a delayed action.
        /// </summary>
        /// <param name="action">The delayed action causing the healing and damage.</param>
        private void ApplyHealingAndDamage(DelayedAction action)
        {
            var targets = new List <Character>(AllCharacters.Where(
                                                   character => IsTargetableCharacter(character, action.BaseAction, action.Targets)));

            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 startingHealth = targets[i].CurrentHealth;
                preHealthChangedDict.Add(targets[i].Id, startingHealth);
                int totalDamage       = DamageCalculator.GetTotalDamage(action.TotalDamage, targets[i]);
                int percentHealAmount = targets[i].CurrentMaxHealth * action.HealPercentage;

                _characterController.ModifyCurrentHealth(targets[i], percentHealAmount);
                _characterController.ModifyCurrentHealth(targets[i], action.HealAmount);
                _characterController.ModifyCurrentHealth(targets[i], totalDamage);
                _threatController.ApplyThreat(action.Actor,
                                              targets[i],
                                              percentHealAmount + action.HealAmount + totalDamage,
                                              action.BaseAction.Threat,
                                              action.BaseAction.ThreatMultiplier);
                int modifiedHealth = targets[i].CurrentHealth - startingHealth;
                changeAmount.Add(targets[i].Id, modifiedHealth);
                loggableData.Add(new KeyValuePair <string, int>(targets[i].Name, modifiedHealth));
                postHealthChangedDict.Add(targets[i].Id, targets[i].CurrentHealth);
            }

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

                CharactersHealthChanged?.Invoke(this, new CharactersHealthChangedEventArgs()
                {
                    PostCharactersChanged = postHealthChangedDict,
                    PreCharactersChanged  = preHealthChangedDict,
                    ChangeAmount          = changeAmount,
                    LogMessage            = logMessage
                });
            }

            CheckForDeadTargets(targets);
        }