/// <summary>
 /// Given an instance of a DelayedStatus, creates and returns an instance of an AppliedStatus.
 /// </summary>
 /// <param name="status">The DelayedStatus the AppliedStatus should use as a base.</param>
 /// <returns>A wrapper around a status effect, containing modified values and a turn counter.</returns>
 private AppliedStatus CreateAppliedStatus(DelayedStatus status)
 {
     return(new AppliedStatus()
     {
         Applicator = status.Applicator,
         BaseStatus = status.BaseStatus,
         TotalDamage = status.TotalDamage,
         HealAmount = status.HealAmount,
         HealPercentage = status.HealPercentage,
         TurnsRemaining = status.BaseStatus.Duration,
         CritChance = status.CritChance,
         CritMultiplier = status.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>
        /// Applys a delayed status effect to a group of characters.
        /// </summary>
        /// <param name="status">The delayed status to apply.</param>
        private void ApplyStatus(DelayedStatus status)
        {
            var livingTargets = new List <Character>(
                AllCharacters.Where(
                    character => status.Targets.Contains(character.Position)));

            livingTargets.RemoveAll(target => target.CurrentHealth <= 0);

            foreach (var target in livingTargets)
            {
                if (!_appliedStatuses.ContainsKey(target))
                {
                    _appliedStatuses[target] = new List <AppliedStatus>();
                }
                // If the the same type of status is already on a character
                if (_appliedStatuses[target].Any(applied => applied.BaseStatus == status.BaseStatus))
                {
                    var matchingStatus = _appliedStatuses[target].First(applied => applied.BaseStatus == status.BaseStatus);
                    // If the status is stackable, refresh the duration and apply another layer of effects
                    if (status.BaseStatus.Stackable && matchingStatus.StackCount < status.BaseStatus.StackSize)
                    {
                        matchingStatus.Applicator      = status.Applicator;
                        matchingStatus.TurnsRemaining  = status.BaseStatus.Duration;
                        matchingStatus.TotalDamage    += status.TotalDamage;
                        matchingStatus.HealAmount     += status.HealAmount;
                        matchingStatus.HealPercentage += status.HealPercentage;
                        ApplyStatusEffects(status.BaseStatus, target);
                        matchingStatus.StackCount++;
                    }
                    // If the status is stackable but has reached its stack limit, refresh the duration only
                    else if (status.BaseStatus.Stackable)
                    {
                        matchingStatus.Applicator     = status.Applicator;
                        matchingStatus.TurnsRemaining = status.BaseStatus.Duration;
                    }
                    // If the status isn't stackable, refresh the duration and reset the damage
                    else
                    {
                        matchingStatus.Applicator     = status.Applicator;
                        matchingStatus.TurnsRemaining = status.BaseStatus.Duration;
                        matchingStatus.TotalDamage    = status.TotalDamage;
                        matchingStatus.HealAmount     = status.HealAmount;
                        matchingStatus.HealPercentage = status.HealPercentage;
                    }
                }
                // Create and apply a new status effect on a character
                else
                {
                    ApplyStatusEffects(status.BaseStatus, target);
                    _appliedStatuses[target].Add(CreateAppliedStatus(status));
                    if (status.BaseStatus.IsDebuff)
                    {
                        target.Debuffs.Add(status.BaseStatus);
                    }
                    else
                    {
                        target.Buffs.Add(status.BaseStatus);
                    }
                }
            }

            StatusEffectApplied?.Invoke(this, new StatusEffectAppliedEventArgs()
            {
                AffectedCharacterIds = new List <int>(livingTargets.Select(chr => chr.Id)),
                LogMessage           = CombatMessenger.GetAffectedByStatusMessage(status.BaseStatus.Name,
                                                                                  livingTargets.Select(target => target.Name).ToList())
            });
        }