/// <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);
        }
 private void OnCharactersHealthChanged(object sender, CharactersHealthChangedEventArgs args)
 {
     CharactersHealthChanged?.Invoke(sender, args);
 }
        /// <summary>
        /// Applies damage and healing from status effects affecting a character at the start of its turn.
        /// If there are no turns remaining on the status, the status is removed. If the character dies from
        /// status effect damage, the character died event is invoked.
        /// </summary>
        /// <param name="character">The character starting its turn.</param>
        private void StartOfTurnEffects(Character character)
        {
            var removeStatuses = new List <AppliedStatus>();
            int startingHealth = character.CurrentHealth;
            int totalDamage    = 0;

            // Calculate damage and apply healing from each status effect
            foreach (var status in _appliedStatuses[character])
            {
                int damage = DamageCalculator.GetTotalDamage(status.TotalDamage, character);
                totalDamage += damage;
                int healAmount  = status.HealAmount;
                int percentHeal = character.CurrentMaxHealth * status.HealPercentage / 100;
                if (_random.Next(1, 101) <= status.CritChance)
                {
                    totalDamage = totalDamage * status.CritMultiplier / 100;
                    healAmount  = healAmount * status.CritMultiplier / 100;
                }

                character.CurrentHealth += percentHeal;
                character.CurrentHealth += healAmount;

                _threatController.ApplyThreat(status.Applicator,
                                              character,
                                              damage + healAmount + percentHeal,
                                              status.BaseStatus.Threat,
                                              status.BaseStatus.ThreatMultiplier);

                if (character.CurrentHealth > character.CurrentMaxHealth)
                {
                    character.CurrentHealth = character.CurrentMaxHealth;
                }
                status.TurnsRemaining--;
                if (status.TurnsRemaining == 0)
                {
                    removeStatuses.Add(status);
                }
            }
            character.CurrentHealth += totalDamage;

            int modifiedHealth = character.CurrentHealth - startingHealth;
            var names          = _appliedStatuses[character].Select(status => status.BaseStatus.Name).ToList();

            if (modifiedHealth != 0)
            {
                CharactersHealthChanged?.Invoke(this, new CharactersHealthChangedEventArgs()
                {
                    PostCharactersChanged = new Dictionary <int, int>()
                    {
                        { character.Id, character.CurrentHealth }
                    },
                    PreCharactersChanged = new Dictionary <int, int>()
                    {
                        { character.Id, startingHealth }
                    },
                    ChangeAmount = new Dictionary <int, int>()
                    {
                        { character.Id, modifiedHealth }
                    },
                    LogMessage = CombatMessenger.GetHealthChangedByStatusMessage(names, character.Name, modifiedHealth)
                });
            }

            var statusNames = removeStatuses.Select(st => st.BaseStatus.Name).ToList();

            // If a status is queued for removal, remove from the character's buff and debuff lists
            foreach (var status in removeStatuses)
            {
                if (status.BaseStatus.IsDebuff)
                {
                    character.Debuffs.Remove(status.BaseStatus);
                }
                else
                {
                    character.Buffs.Remove(status.BaseStatus);
                }
                RemoveStatusEffects(status, character);
            }
            _appliedStatuses[character].RemoveAll(status => removeStatuses.Contains(status));

            if (removeStatuses.Any())
            {
                StatusEffectsRemoved?.Invoke(this, new CombatLoggableEventArgs()
                {
                    LogMessage = CombatMessenger.GetRemoveStatusMessage(statusNames, character.Name)
                });
            }

            // Invoke characters dying event if a character died as a result of this status effect.
            if (character.CurrentHealth <= 0)
            {
                character.CurrentHealth = 0;
                RemoveAllStatuses(character);
                var characters = new List <Character>()
                {
                    character
                };
                CharactersDied?.Invoke(this, new CharactersDiedEventArgs()
                {
                    DyingCharacters = characters,
                    LogMessage      = CombatMessenger.GetCharactersDiedMessage(characters.Select(chr => chr.Name).ToList())
                });
            }
        }