/// <summary>
        ///     Calculates damage from given ability on given target
        /// </summary>
        /// <param name="ability">
        ///     The ability.
        /// </param>
        /// <param name="source">
        ///     The source.
        /// </param>
        /// <param name="target">
        ///     The target.
        /// </param>
        /// <param name="minusArmor">
        ///     The minus Armor.
        /// </param>
        /// <param name="minusDamageResistancePerc">
        ///     The minus Damage Resistance Percentage.
        /// </param>
        /// <param name="minusMagicResistancePerc">
        ///     The minus Magic Resistance Percentage.
        /// </param>
        /// <param name="minusHealth">
        ///     The minus Health.
        /// </param>
        /// <returns>
        ///     The <see cref="float" />.
        /// </returns>
        public static float CalculateDamage(
            Ability ability, 
            Unit source, 
            Unit target, 
            double minusArmor = 0d, 
            double minusDamageResistancePerc = 0d, 
            double minusMagicResistancePerc = 0d, 
            float minusHealth = 0f)
        {
            var name = ability.StoredName();
            var level = ability.Level;
            if (ability.AbilityType.HasFlag(AbilityType.Ultimate) && level > 0 && source.AghanimState())
            {
                level += 1;
            }

            AbilityInfo data;
            if (!dataDictionary.TryGetValue(ability, out data))
            {
                data = AbilityDatabase.Find(name);
                if (data != null)
                {
                    dataDictionary.Add(ability, data);
                }
            }

            if (data == null)
            {
                return 0;
            }

            var outgoingDamage = 0f;
            float bonusDamage = 0;
            Hero hero;
            double multi;
            float tempDmg;
            switch (name)
            {
                case "item_urn_of_shadows":
                    outgoingDamage = target.SpellDamageTaken(150, DamageType.Pure, source, name);
                    break;
                case "ember_spirit_sleight_of_fist":
                    outgoingDamage = source.MinimumDamage + source.BonusDamage;
                    if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                    {
                        bonusDamage = ability.GetAbilityData(data.BonusDamageString);
                        outgoingDamage += bonusDamage;
                        damageDictionary.Add(ability, bonusDamage);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        bonusDamage = ability.GetAbilityData(data.BonusDamageString);
                        damageDictionary[ability] = bonusDamage;
                        outgoingDamage += bonusDamage;
                    }
                    else
                    {
                        outgoingDamage += bonusDamage;
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        outgoingDamage, 
                        DamageType.Physical, 
                        source, 
                        name, 
                        true, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "doom_bringer_lvl_death":
                    if (!damageDictionary.TryGetValue(ability, out tempDmg))
                    {
                        tempDmg = ability.GetAbilityData(data.DamageString);
                        damageDictionary.Add(ability, tempDmg);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        tempDmg = ability.GetAbilityData(data.DamageString);
                        damageDictionary[ability] = tempDmg;
                    }

                    var multiplier = ability.GetAbilityData("lvl_bonus_multiple");
                    bonusDamage = ability.GetAbilityData("lvl_bonus_damage");
                    var levelc = target.Level / multiplier;
                    if (levelc == Math.Floor(levelc) || target.Level == 25)
                    {
                        tempDmg += target.MaximumHealth * (bonusDamage / 100);
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        tempDmg, 
                        DamageType.Magical, 
                        source, 
                        name, 
                        data.MagicImmunityPierce, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "phantom_assassin_phantom_strike":
                    var crit = source.Spellbook.SpellR;
                    if (crit.Level > 0)
                    {
                        float critMulti;
                        if (!damageDictionary.TryGetValue(crit, out critMulti))
                        {
                            critMulti = crit.GetAbilityData("crit_bonus");
                            damageDictionary.Add(crit, critMulti);
                            levelDictionary.Add(crit, crit.Level);
                        }
                        else if (levelDictionary[crit] != crit.Level)
                        {
                            levelDictionary[crit] = crit.Level;
                            critMulti = crit.GetAbilityData("crit_bonus");
                            damageDictionary[crit] = critMulti;
                        }

                        outgoingDamage = (source.MinimumDamage + source.BonusDamage) * (critMulti / 100);
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        outgoingDamage, 
                        DamageType.Physical, 
                        source, 
                        name, 
                        data.MagicImmunityPierce, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "tusk_walrus_punch":
                    if (!multiplierDictionary.TryGetValue(ability, out multi))
                    {
                        multi = ability.GetAbilityData("crit_multiplier");
                        multiplierDictionary.Add(ability, multi);
                    }

                    outgoingDamage =
                        target.SpellDamageTaken(
                            (float)((source.MinimumDamage + source.BonusDamage) * (multi / 100)), 
                            DamageType.Physical, 
                            source, 
                            name, 
                            data.MagicImmunityPierce, 
                            minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "necrolyte_reapers_scythe":
                    if (!multiplierDictionary.TryGetValue(ability, out multi))
                    {
                        multi = ability.GetAbilityData(data.DamageString);
                        multiplierDictionary.Add(ability, multi);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        multi = ability.GetAbilityData(data.DamageString);
                        multiplierDictionary[ability] = multi;
                    }

                    var missingHp = target.MaximumHealth - target.Health + minusHealth;
                    outgoingDamage = target.SpellDamageTaken(
                        (float)(missingHp * multi), 
                        DamageType.Magical, 
                        source, 
                        name, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "chaos_knight_reality_rift":
                    if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                    {
                        bonusDamage = ability.GetAbilityData(data.DamageString);
                        damageDictionary.Add(ability, bonusDamage);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        bonusDamage = ability.GetAbilityData(data.DamageString);
                        damageDictionary[ability] = bonusDamage;
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        source.MaximumDamage + source.BonusDamage + bonusDamage, 
                        DamageType.Physical, 
                        source, 
                        name, 
                        data.MagicImmunityPierce);
                    break;
                case "templar_assassin_meld":
                    if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                    {
                        bonusDamage = ability.GetAbilityData(data.DamageString);
                        damageDictionary.Add(ability, bonusDamage);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        bonusDamage = ability.GetAbilityData(data.DamageString);
                        damageDictionary[ability] = bonusDamage;
                    }

                    var minusArmors = new[] { -2, -4, -6, -8 };
                    var meldminusArmor = target.Armor + minusArmors[level - 1];
                    var damageIncrease = 1 - 0.06 * meldminusArmor / (1 + 0.06 * Math.Abs(meldminusArmor));
                    outgoingDamage =
                        (float)
                        (target.SpellDamageTaken(
                            source.MaximumDamage + source.BonusDamage, 
                            DamageType.Physical, 
                            source, 
                            name, 
                            data.MagicImmunityPierce, 
                            minusMagicResistancePerc: minusMagicResistancePerc) + bonusDamage * damageIncrease);
                    break;
                case "undying_decay":
                    var strengthSteal = ability.GetAbilityData("str_steal");
                    if (source.AghanimState())
                    {
                        strengthSteal = ability.GetAbilityData("str_Steal_scepter");
                    }

                    outgoingDamage = strengthSteal * 19
                                     + target.SpellDamageTaken(
                                         ability.GetAbilityData(data.DamageString), 
                                         DamageType.Magical, 
                                         source, 
                                         name, 
                                         false, 
                                         minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "visage_soul_assumption":
                    var dmg = ability.GetAbilityData(data.DamageString);
                    var modif = source.FindModifier("modifier_visage_soul_assumption");
                    if (modif != null)
                    {
                        dmg += modif.StackCount * ability.GetAbilityData("soul_charge_damage");
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        dmg, 
                        DamageType.Magical, 
                        source, 
                        name, 
                        false, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "morphling_adaptive_strike":
                    if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                    {
                        bonusDamage = ability.GetAbilityData(data.DamageString);
                        damageDictionary.Add(ability, bonusDamage);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        bonusDamage = ability.GetAbilityData(data.DamageString);
                        damageDictionary[ability] = bonusDamage;
                    }

                    hero = source as Hero;
                    var agi = Math.Floor(hero.TotalAgility);
                    var str = Math.Floor(hero.TotalStrength);
                    var difference = agi / str;
                    var multimin = ability.GetAbilityData("damage_min");
                    var multimax = ability.GetAbilityData("damage_max");
                    multi = multimin + (difference - 0.5) * (multimax - multimin);
                    if (difference > 1.5)
                    {
                        multi = multimax;
                    }
                    else if (difference < 0.5)
                    {
                        multi = multimin;
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        (float)(bonusDamage + agi * multi), 
                        DamageType.Magical, 
                        source, 
                        name, 
                        false, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "mirana_starfall":
                    var radiusMax = ability.GetAbilityData("starfall_secondary_radius");
                    if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                    {
                        bonusDamage =
                            Convert.ToSingle(
                                Game.FindKeyValues(name + "/AbilityDamage", KeyValueSource.Ability)
                                    .StringValue.Split(' ')[level - 1]);
                        damageDictionary.Add(ability, bonusDamage);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        bonusDamage =
                            Convert.ToSingle(
                                Game.FindKeyValues(name + "/AbilityDamage", KeyValueSource.Ability)
                                    .StringValue.Split(' ')[level - 1]);
                        damageDictionary[ability] = bonusDamage;
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        bonusDamage, 
                        DamageType.Magical, 
                        source, 
                        name, 
                        data.MagicImmunityPierce, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    if (source.Distance2D(target) < radiusMax)
                    {
                        outgoingDamage *= 2;
                    }

                    break;
                case "item_ethereal_blade":
                    hero = source as Hero;
                    var primaryAtt = hero.PrimaryAttribute;
                    if (primaryAtt == Attribute.Agility)
                    {
                        bonusDamage = 2f * hero.TotalAgility;
                    }
                    else if (primaryAtt == Attribute.Intelligence)
                    {
                        bonusDamage = 2f * hero.TotalIntelligence;
                    }
                    else if (primaryAtt == Attribute.Strength)
                    {
                        bonusDamage = 2f * hero.TotalStrength;
                    }

                    if (!damageDictionary.TryGetValue(ability, out tempDmg))
                    {
                        tempDmg = ability.GetAbilityData(data.DamageString);
                        damageDictionary.Add(ability, tempDmg);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        tempDmg = ability.GetAbilityData(data.DamageString);
                        damageDictionary[ability] = tempDmg;
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        tempDmg + bonusDamage, 
                        DamageType.Magical, 
                        source, 
                        name, 
                        false, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "nyx_assassin_mana_burn":
                    var intMultiplier = ability.GetAbilityData("float_multiplier");
                    hero = target as Hero;
                    outgoingDamage = target.ManaBurnSpellDamageTaken(
                        hero.TotalIntelligence * intMultiplier, 
                        1, 
                        DamageType.Magical, 
                        source, 
                        name, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "antimage_mana_void":
                    if (!damageDictionary.TryGetValue(ability, out tempDmg))
                    {
                        tempDmg = ability.GetAbilityData(data.DamageString);
                        damageDictionary.Add(ability, tempDmg);
                        levelDictionary.Add(ability, level);
                    }
                    else if (levelDictionary[ability] != level)
                    {
                        levelDictionary[ability] = level;
                        tempDmg = ability.GetAbilityData(data.DamageString);
                        damageDictionary[ability] = tempDmg;
                    }

                    hero = target as Hero;
                    var missingMana = hero.MaximumMana - hero.Mana;
                    outgoingDamage = target.SpellDamageTaken(
                        missingMana * tempDmg, 
                        DamageType.Magical, 
                        source, 
                        name, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "riki_blink_strike":
                    var damage = ability.GetAbilityData(data.DamageString);
                    var backstab = source.Spellbook.SpellE;
                    var agiMultiplier = backstab.GetAbilityData("damage_multiplier");
                    hero = source as Hero;
                    var blinkdamage = target.SpellDamageTaken(
                        damage, 
                        DamageType.Magical, 
                        source, 
                        name, 
                        data.MagicImmunityPierce, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    outgoingDamage = blinkdamage
                                     + target.SpellDamageTaken(
                                         agiMultiplier * hero.TotalAgility + (source.MinimumDamage + source.BonusDamage), 
                                         DamageType.Physical, 
                                         source, 
                                         name, 
                                         data.MagicImmunityPierce, 
                                         minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                case "undying_soul_rip":
                    var radius = ability.GetAbilityData("radius");
                    var nearUnits = ObjectManager.GetEntities<Unit>();
                    var damagePerUnit = ability.GetAbilityData("damage_per_unit");
                    var maxUnits = ability.GetAbilityData("max_units");
                    outgoingDamage =
                        Math.Min(
                            nearUnits.Count(
                                x =>
                                !x.Equals(source) && !x.Equals(target) && x.Distance2D(source) < radius + x.HullRadius
                                && (x.ClassID == ClassID.CDOTA_BaseNPC_Creep_Lane
                                    || x.ClassID == ClassID.CDOTA_BaseNPC_Creep
                                    || x.ClassID == ClassID.CDOTA_BaseNPC_Creep_Neutral
                                    || x.ClassID == ClassID.CDOTA_BaseNPC_Creep_Siege
                                    || x.ClassID == ClassID.CDOTA_BaseNPC_Creature
                                    || x.ClassID == ClassID.CDOTA_BaseNPC_Invoker_Forged_Spirit
                                    || x.ClassID == ClassID.CDOTA_Unit_Undying_Zombie
                                    || x.ClassID == ClassID.CDOTA_BaseNPC_Warlock_Golem
                                    || (x is Hero
                                        && (x.Team == source.Team
                                            || (x.Team == source.GetEnemyTeam() && !x.IsMagicImmune())))) && x.IsAlive
                                && x.IsVisible), 
                            maxUnits) * damagePerUnit;
                    outgoingDamage = target.SpellDamageTaken(
                        outgoingDamage, 
                        DamageType.Magical, 
                        source, 
                        name, 
                        data.MagicImmunityPierce, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
                default:
                    var damageString = data.DamageString;
                    if (damageString == null)
                    {
                        outgoingDamage = ability.GetDamage(level - 1);
                    }
                    else
                    {
                        if (ability.AbilityType.HasFlag(AbilityType.Ultimate) && level > 0 && source.AghanimState())
                        {
                            level -= 1;
                        }

                        if (data.SpellLevel != null)
                        {
                            var spellLevel = source.FindSpell(data.SpellLevel, true);
                            level = spellLevel.Level;
                        }

                        if (source.ClassID == ClassID.CDOTA_Unit_Hero_Invoker && level > 0 && source.AghanimState())
                        {
                            level += 1;
                        }

                        if (data.DamageScepterString != null && source.AghanimState())
                        {
                            outgoingDamage = ability.GetAbilityData(data.DamageScepterString, level);
                        }
                        else
                        {
                            outgoingDamage = ability.GetAbilityData(damageString, level);
                        }

                        if (data.DamageMultiplier > 0)
                        {
                            outgoingDamage = outgoingDamage * data.DamageMultiplier;
                        }
                    }

                    outgoingDamage = target.SpellDamageTaken(
                        outgoingDamage, 
                        GetDamageType(ability), 
                        source, 
                        name, 
                        data.MagicImmunityPierce, 
                        minusMagicResistancePerc: minusMagicResistancePerc);
                    break;
            }

            if (source.ClassID == ClassID.CDOTA_Unit_Hero_Zuus && !(ability is Item)
                && (source.Distance2D(target) <= 1200 || ability.StoredName() != "zuus_thundergods_wrath"))
            {
                var staticField = source.Spellbook.Spell3;
                if (staticField.Level <= 0)
                {
                    return outgoingDamage;
                }

                var bonusDmg = staticField.GetAbilityData("damage_health_pct") / 100 * (target.Health - minusHealth);
                bonusDmg = target.SpellDamageTaken(
                    bonusDmg, 
                    DamageType.Magical, 
                    source, 
                    name, 
                    minusMagicResistancePerc: minusMagicResistancePerc);
                outgoingDamage += bonusDmg;
            }

            return outgoingDamage;
        }
        /// <summary>
        ///     Calculates damage from given ability on given target
        /// </summary>
        /// <param name="ability">
        ///     The ability.
        /// </param>
        /// <param name="source">
        ///     The source.
        /// </param>
        /// <param name="target">
        ///     The target.
        /// </param>
        /// <param name="minusArmor">
        ///     The minus Armor.
        /// </param>
        /// <param name="minusDamageResistancePerc">
        ///     The minus Damage Resistance Percentage.
        /// </param>
        /// <param name="minusMagicResistancePerc">
        ///     The minus Magic Resistance Percentage.
        /// </param>
        /// <param name="minusHealth">
        ///     The minus Health.
        /// </param>
        /// <returns>
        ///     The <see cref="float" />.
        /// </returns>
        public static float CalculateDamage(
            Ability ability,
            Unit source,
            Unit target,
            double minusArmor = 0d,
            double minusDamageResistancePerc = 0d,
            double minusMagicResistancePerc  = 0d,
            float minusHealth = 0f)
        {
            var name  = ability.StoredName();
            var level = ability.Level;

            if (ability.AbilityType.HasFlag(AbilityType.Ultimate) && level > 0 && source.AghanimState())
            {
                level += 1;
            }

            AbilityInfo data;

            if (!dataDictionary.TryGetValue(ability, out data))
            {
                data = AbilityDatabase.Find(name);
                if (data != null)
                {
                    dataDictionary.Add(ability, data);
                }
            }

            if (data == null)
            {
                return(0);
            }

            var    outgoingDamage = 0f;
            float  bonusDamage    = 0;
            Hero   hero;
            double multi;
            float  tempDmg;

            switch (name)
            {
            case "item_urn_of_shadows":
                outgoingDamage = target.SpellDamageTaken(150, DamageType.Pure, source, name);
                break;

            case "ember_spirit_sleight_of_fist":
                outgoingDamage = source.MinimumDamage + source.BonusDamage;
                if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                {
                    bonusDamage     = ability.GetAbilityData(data.BonusDamageString);
                    outgoingDamage += bonusDamage;
                    damageDictionary.Add(ability, bonusDamage);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    bonusDamage = ability.GetAbilityData(data.BonusDamageString);
                    damageDictionary[ability] = bonusDamage;
                    outgoingDamage           += bonusDamage;
                }
                else
                {
                    outgoingDamage += bonusDamage;
                }

                outgoingDamage = target.SpellDamageTaken(
                    outgoingDamage,
                    DamageType.Physical,
                    source,
                    name,
                    true,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "doom_bringer_lvl_death":
                if (!damageDictionary.TryGetValue(ability, out tempDmg))
                {
                    tempDmg = ability.GetAbilityData(data.DamageString);
                    damageDictionary.Add(ability, tempDmg);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    tempDmg = ability.GetAbilityData(data.DamageString);
                    damageDictionary[ability] = tempDmg;
                }

                var multiplier = ability.GetAbilityData("lvl_bonus_multiple");
                bonusDamage = ability.GetAbilityData("lvl_bonus_damage");
                var levelc = target.Level / multiplier;
                if (levelc == Math.Floor(levelc) || target.Level == 25)
                {
                    tempDmg += target.MaximumHealth * (bonusDamage / 100);
                }

                outgoingDamage = target.SpellDamageTaken(
                    tempDmg,
                    DamageType.Magical,
                    source,
                    name,
                    data.MagicImmunityPierce,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "phantom_assassin_phantom_strike":
                var crit = source.Spellbook.SpellR;
                if (crit.Level > 0)
                {
                    float critMulti;
                    if (!damageDictionary.TryGetValue(crit, out critMulti))
                    {
                        critMulti = crit.GetAbilityData("crit_bonus");
                        damageDictionary.Add(crit, critMulti);
                        levelDictionary.Add(crit, crit.Level);
                    }
                    else if (levelDictionary[crit] != crit.Level)
                    {
                        levelDictionary[crit]  = crit.Level;
                        critMulti              = crit.GetAbilityData("crit_bonus");
                        damageDictionary[crit] = critMulti;
                    }

                    outgoingDamage = (source.MinimumDamage + source.BonusDamage) * (critMulti / 100);
                }

                outgoingDamage = target.SpellDamageTaken(
                    outgoingDamage,
                    DamageType.Physical,
                    source,
                    name,
                    data.MagicImmunityPierce,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "tusk_walrus_punch":
                if (!multiplierDictionary.TryGetValue(ability, out multi))
                {
                    multi = ability.GetAbilityData("crit_multiplier");
                    multiplierDictionary.Add(ability, multi);
                }

                outgoingDamage =
                    target.SpellDamageTaken(
                        (float)((source.MinimumDamage + source.BonusDamage) * (multi / 100)),
                        DamageType.Physical,
                        source,
                        name,
                        data.MagicImmunityPierce,
                        minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "necrolyte_reapers_scythe":
                if (!multiplierDictionary.TryGetValue(ability, out multi))
                {
                    multi = ability.GetAbilityData(data.DamageString);
                    multiplierDictionary.Add(ability, multi);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    multi = ability.GetAbilityData(data.DamageString);
                    multiplierDictionary[ability] = multi;
                }

                var missingHp = target.MaximumHealth - target.Health + minusHealth;
                outgoingDamage = target.SpellDamageTaken(
                    (float)(missingHp * multi),
                    DamageType.Magical,
                    source,
                    name,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "chaos_knight_reality_rift":
                if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                {
                    bonusDamage = ability.GetAbilityData(data.DamageString);
                    damageDictionary.Add(ability, bonusDamage);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    bonusDamage = ability.GetAbilityData(data.DamageString);
                    damageDictionary[ability] = bonusDamage;
                }

                outgoingDamage = target.SpellDamageTaken(
                    source.MaximumDamage + source.BonusDamage + bonusDamage,
                    DamageType.Physical,
                    source,
                    name,
                    data.MagicImmunityPierce);
                break;

            case "templar_assassin_meld":
                if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                {
                    bonusDamage = ability.GetAbilityData(data.DamageString);
                    damageDictionary.Add(ability, bonusDamage);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    bonusDamage = ability.GetAbilityData(data.DamageString);
                    damageDictionary[ability] = bonusDamage;
                }

                var minusArmors    = new[] { -2, -4, -6, -8 };
                var meldminusArmor = target.Armor + minusArmors[level - 1];
                var damageIncrease = 1 - 0.06 * meldminusArmor / (1 + 0.06 * Math.Abs(meldminusArmor));
                outgoingDamage =
                    (float)
                    (target.SpellDamageTaken(
                         source.MaximumDamage + source.BonusDamage,
                         DamageType.Physical,
                         source,
                         name,
                         data.MagicImmunityPierce,
                         minusMagicResistancePerc: minusMagicResistancePerc) + bonusDamage * damageIncrease);
                break;

            case "undying_decay":
                var strengthSteal = ability.GetAbilityData("str_steal");
                if (source.AghanimState())
                {
                    strengthSteal = ability.GetAbilityData("str_Steal_scepter");
                }

                outgoingDamage = strengthSteal * 19
                                 + target.SpellDamageTaken(
                    ability.GetAbilityData(data.DamageString),
                    DamageType.Magical,
                    source,
                    name,
                    false,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "visage_soul_assumption":
                var dmg   = ability.GetAbilityData(data.DamageString);
                var modif = source.FindModifier("modifier_visage_soul_assumption");
                if (modif != null)
                {
                    dmg += modif.StackCount * ability.GetAbilityData("soul_charge_damage");
                }

                outgoingDamage = target.SpellDamageTaken(
                    dmg,
                    DamageType.Magical,
                    source,
                    name,
                    false,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "morphling_adaptive_strike":
                if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                {
                    bonusDamage = ability.GetAbilityData(data.DamageString);
                    damageDictionary.Add(ability, bonusDamage);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    bonusDamage = ability.GetAbilityData(data.DamageString);
                    damageDictionary[ability] = bonusDamage;
                }

                hero = source as Hero;
                var agi        = Math.Floor(hero.TotalAgility);
                var str        = Math.Floor(hero.TotalStrength);
                var difference = agi / str;
                var multimin   = ability.GetAbilityData("damage_min");
                var multimax   = ability.GetAbilityData("damage_max");
                multi = multimin + (difference - 0.5) * (multimax - multimin);
                if (difference > 1.5)
                {
                    multi = multimax;
                }
                else if (difference < 0.5)
                {
                    multi = multimin;
                }

                outgoingDamage = target.SpellDamageTaken(
                    (float)(bonusDamage + agi * multi),
                    DamageType.Magical,
                    source,
                    name,
                    false,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "mirana_starfall":
                var radiusMax = ability.GetAbilityData("starfall_secondary_radius");
                if (!damageDictionary.TryGetValue(ability, out bonusDamage))
                {
                    bonusDamage =
                        Convert.ToSingle(
                            Game.FindKeyValues(name + "/AbilityDamage", KeyValueSource.Ability)
                            .StringValue.Split(' ')[level - 1]);
                    damageDictionary.Add(ability, bonusDamage);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    bonusDamage =
                        Convert.ToSingle(
                            Game.FindKeyValues(name + "/AbilityDamage", KeyValueSource.Ability)
                            .StringValue.Split(' ')[level - 1]);
                    damageDictionary[ability] = bonusDamage;
                }

                outgoingDamage = target.SpellDamageTaken(
                    bonusDamage,
                    DamageType.Magical,
                    source,
                    name,
                    data.MagicImmunityPierce,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                if (source.Distance2D(target) < radiusMax)
                {
                    outgoingDamage *= 2;
                }

                break;

            case "item_ethereal_blade":
                hero = source as Hero;
                var primaryAtt = hero.PrimaryAttribute;
                if (primaryAtt == Attribute.Agility)
                {
                    bonusDamage = 2f * hero.TotalAgility;
                }
                else if (primaryAtt == Attribute.Intelligence)
                {
                    bonusDamage = 2f * hero.TotalIntelligence;
                }
                else if (primaryAtt == Attribute.Strength)
                {
                    bonusDamage = 2f * hero.TotalStrength;
                }

                if (!damageDictionary.TryGetValue(ability, out tempDmg))
                {
                    tempDmg = ability.GetAbilityData(data.DamageString);
                    damageDictionary.Add(ability, tempDmg);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    tempDmg = ability.GetAbilityData(data.DamageString);
                    damageDictionary[ability] = tempDmg;
                }

                outgoingDamage = target.SpellDamageTaken(
                    tempDmg + bonusDamage,
                    DamageType.Magical,
                    source,
                    name,
                    false,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "nyx_assassin_mana_burn":
                var intMultiplier = ability.GetAbilityData("float_multiplier");
                hero           = target as Hero;
                outgoingDamage = target.ManaBurnSpellDamageTaken(
                    hero.TotalIntelligence * intMultiplier,
                    1,
                    DamageType.Magical,
                    source,
                    name,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "antimage_mana_void":
                if (!damageDictionary.TryGetValue(ability, out tempDmg))
                {
                    tempDmg = ability.GetAbilityData(data.DamageString);
                    damageDictionary.Add(ability, tempDmg);
                    levelDictionary.Add(ability, level);
                }
                else if (levelDictionary[ability] != level)
                {
                    levelDictionary[ability] = level;
                    tempDmg = ability.GetAbilityData(data.DamageString);
                    damageDictionary[ability] = tempDmg;
                }

                hero = target as Hero;
                var missingMana = hero.MaximumMana - hero.Mana;
                outgoingDamage = target.SpellDamageTaken(
                    missingMana * tempDmg,
                    DamageType.Magical,
                    source,
                    name,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "riki_blink_strike":
                var damage        = ability.GetAbilityData(data.DamageString);
                var backstab      = source.Spellbook.SpellE;
                var agiMultiplier = backstab.GetAbilityData("damage_multiplier");
                hero = source as Hero;
                var blinkdamage = target.SpellDamageTaken(
                    damage,
                    DamageType.Magical,
                    source,
                    name,
                    data.MagicImmunityPierce,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                outgoingDamage = blinkdamage
                                 + target.SpellDamageTaken(
                    agiMultiplier * hero.TotalAgility + (source.MinimumDamage + source.BonusDamage),
                    DamageType.Physical,
                    source,
                    name,
                    data.MagicImmunityPierce,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            case "undying_soul_rip":
                var radius        = ability.GetAbilityData("radius");
                var nearUnits     = ObjectManager.GetEntities <Unit>();
                var damagePerUnit = ability.GetAbilityData("damage_per_unit");
                var maxUnits      = ability.GetAbilityData("max_units");
                outgoingDamage =
                    Math.Min(
                        nearUnits.Count(
                            x =>
                            !x.Equals(source) && !x.Equals(target) && x.Distance2D(source) < radius + x.HullRadius &&
                            (x.ClassID == ClassID.CDOTA_BaseNPC_Creep_Lane ||
                             x.ClassID == ClassID.CDOTA_BaseNPC_Creep ||
                             x.ClassID == ClassID.CDOTA_BaseNPC_Creep_Neutral ||
                             x.ClassID == ClassID.CDOTA_BaseNPC_Creep_Siege ||
                             x.ClassID == ClassID.CDOTA_BaseNPC_Creature ||
                             x.ClassID == ClassID.CDOTA_BaseNPC_Invoker_Forged_Spirit ||
                             x.ClassID == ClassID.CDOTA_Unit_Undying_Zombie ||
                             x.ClassID == ClassID.CDOTA_BaseNPC_Warlock_Golem ||
                             (x is Hero &&
                              (x.Team == source.Team ||
                               (x.Team == source.GetEnemyTeam() && !x.IsMagicImmune())))) && x.IsAlive &&
                            x.IsVisible),
                        maxUnits) * damagePerUnit;
                outgoingDamage = target.SpellDamageTaken(
                    outgoingDamage,
                    DamageType.Magical,
                    source,
                    name,
                    data.MagicImmunityPierce,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;

            default:
                var damageString = data.DamageString;
                if (damageString == null)
                {
                    outgoingDamage = ability.GetDamage(level - 1);
                }
                else
                {
                    if (ability.AbilityType.HasFlag(AbilityType.Ultimate) && level > 0 && source.AghanimState())
                    {
                        level -= 1;
                    }

                    if (data.SpellLevel != null)
                    {
                        var spellLevel = source.FindSpell(data.SpellLevel, true);
                        level = spellLevel.Level;
                    }

                    if (source.ClassID == ClassID.CDOTA_Unit_Hero_Invoker && level > 0 && source.AghanimState())
                    {
                        level += 1;
                    }

                    if (data.DamageScepterString != null && source.AghanimState())
                    {
                        outgoingDamage = ability.GetAbilityData(data.DamageScepterString, level);
                    }
                    else
                    {
                        outgoingDamage = ability.GetAbilityData(damageString, level);
                    }

                    if (data.DamageMultiplier > 0)
                    {
                        outgoingDamage = outgoingDamage * data.DamageMultiplier;
                    }
                }

                outgoingDamage = target.SpellDamageTaken(
                    outgoingDamage,
                    GetDamageType(ability),
                    source,
                    name,
                    data.MagicImmunityPierce,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                break;
            }

            if (source.ClassID == ClassID.CDOTA_Unit_Hero_Zuus && !(ability is Item) &&
                (source.Distance2D(target) <= 1200 || ability.StoredName() != "zuus_thundergods_wrath"))
            {
                var staticField = source.Spellbook.Spell3;
                if (staticField.Level <= 0)
                {
                    return(outgoingDamage);
                }

                var bonusDmg = staticField.GetAbilityData("damage_health_pct") / 100 * (target.Health - minusHealth);
                bonusDmg = target.SpellDamageTaken(
                    bonusDmg,
                    DamageType.Magical,
                    source,
                    name,
                    minusMagicResistancePerc: minusMagicResistancePerc);
                outgoingDamage += bonusDmg;
            }

            return(outgoingDamage);
        }
        /// <summary>
        ///     Returns DamageType of ability
        /// </summary>
        /// <param name="ability">
        ///     The ability.
        /// </param>
        /// <returns>
        ///     The <see cref="DamageType" />.
        /// </returns>
        public static DamageType GetDamageType(Ability ability)
        {
            var type = ability.DamageType;
            var name = ability.StoredName();
            switch (name)
            {
                case "item_urn_of_shadows":
                    type = DamageType.Pure;
                    break;
                case "abaddon_aphotic_shield":
                    type = DamageType.Magical;
                    break;
                case "meepo_poof":
                    type = DamageType.Magical;
                    break;
                case "axe_culling_blade":
                    type = DamageType.Pure;
                    break;
                case "invoker_sun_strinke":
                    type = DamageType.Pure;
                    break;
                case "alchemist_unstable_concoction_throw":
                    type = DamageType.Physical;
                    break;
                case "centaur_stampede":
                    type = DamageType.Physical;
                    break;
                case "lina_laguna_blade":
                    if ((ability.Owner as Hero).AghanimState())
                    {
                        type = DamageType.Pure;
                    }

                    break;
                case "legion_commander_duel":
                    type = DamageType.Physical;
                    break;
                case "item_ethereal_blade":
                    type = DamageType.Magical;
                    break;
                case "tusk_walrus_kick":
                    type = DamageType.Magical;
                    break;
                case "tusk_walrus_punch":
                    type = DamageType.Physical;
                    break;
                case "item_shivas_guard":
                    type = DamageType.Magical;
                    break;
                case "chaos_knight_reality_rift":
                    type = DamageType.Physical;
                    break;
                case "item_veil_of_discord":
                    type = DamageType.Magical;
                    break;
            }

            if (type == DamageType.None)
            {
                type = DamageType.Magical;
            }

            if (name.StartsWith("item_dagon"))
            {
                type = DamageType.Magical;
            }

            return type;
        }
        /// <summary>
        ///     Returns DamageType of ability
        /// </summary>
        /// <param name="ability">
        ///     The ability.
        /// </param>
        /// <returns>
        ///     The <see cref="DamageType" />.
        /// </returns>
        public static DamageType GetDamageType(Ability ability)
        {
            var type = ability.DamageType;
            var name = ability.StoredName();

            switch (name)
            {
            case "item_urn_of_shadows":
                type = DamageType.Pure;
                break;

            case "abaddon_aphotic_shield":
                type = DamageType.Magical;
                break;

            case "meepo_poof":
                type = DamageType.Magical;
                break;

            case "axe_culling_blade":
                type = DamageType.Pure;
                break;

            case "invoker_sun_strinke":
                type = DamageType.Pure;
                break;

            case "alchemist_unstable_concoction_throw":
                type = DamageType.Physical;
                break;

            case "centaur_stampede":
                type = DamageType.Physical;
                break;

            case "lina_laguna_blade":
                if ((ability.Owner as Hero).AghanimState())
                {
                    type = DamageType.Pure;
                }

                break;

            case "legion_commander_duel":
                type = DamageType.Physical;
                break;

            case "item_ethereal_blade":
                type = DamageType.Magical;
                break;

            case "tusk_walrus_kick":
                type = DamageType.Magical;
                break;

            case "tusk_walrus_punch":
                type = DamageType.Physical;
                break;

            case "item_shivas_guard":
                type = DamageType.Magical;
                break;

            case "chaos_knight_reality_rift":
                type = DamageType.Physical;
                break;

            case "item_veil_of_discord":
                type = DamageType.Magical;
                break;
            }

            if (type == DamageType.None)
            {
                type = DamageType.Magical;
            }

            if (name.StartsWith("item_dagon"))
            {
                type = DamageType.Magical;
            }

            return(type);
        }