private void CalculateDamage(Ability ability, out double spell_damage, out int damage_type)
        {
            /*foreach (AbilityData data in ability.AbilityData)
             * {
             *  Log.SlowDebug("Ability: " + ability.Name + " - Data: " + data.Name + " : " + SpellDamageLibrary.GetAbilityValue(ability, data));
             * }*/

            spell_damage = 0;
            int damage_none = (int)DamageType.None;

            damage_type = damage_none;
            double tickInterval = 1.0;
            double duration     = 1.0;
            double bonusDamage  = 0.0;
            double spellDoT     = 0.0;
            int    i;

            if (ability is Item)
            {
                Item item = (Item)ability;
                //process charge based item: if it has no charge left, return
                if (item.IsRequiringCharges && item.CurrentCharges == 0)
                {
                    return;
                }

                //process ethereal blade, special treatment to ethereal blade and return
                if (ability.Name.Contains("item_ethereal_blade"))
                {
                    damage_type = (int)DamageType.Magical;
                    try
                    {
                        double attribute = HeroObj.PrimaryAttribute == Attribute.Strength ? HeroObj.TotalStrength
                                         : HeroObj.PrimaryAttribute == Attribute.Agility ? HeroObj.TotalAgility
                                         : HeroObj.TotalIntelligence;
                        spell_damage += attribute * SpellDamageLibrary.GetAbilityValue(ability, "blast_agility_multiplier") + SpellDamageLibrary.GetAbilityValue(ability, "blast_damage_base");
                    }
                    catch (NullReferenceException)
                    {
                        spell_damage += 0;
                    }
                    return;
                }

                //process pure item
                if (damage_type == damage_none)
                {
                    for (i = 0; i < ItemPureDamage.Length; ++i)
                    {
                        if (ability.Name.Contains(ItemPureDamage[i]))
                        {
                            damage_type = (int)DamageType.Pure;
                            break;
                        }
                    }
                }

                //process magical item
                if (damage_type == damage_none)
                {
                    for (i = 0; i < ItemMagicDamage.Length; ++i)
                    {
                        if (ability.Name.Contains(ItemMagicDamage[i]))
                        {
                            damage_type = (int)DamageType.Magical;
                            break;
                        }
                    }
                }

                //process physical item
                if (damage_type == damage_none)
                {
                    for (i = 0; i < ItemMagicDamage.Length; ++i)
                    {
                        if (ability.Name.Contains(ItemPhysicalDamage[i]))
                        {
                            damage_type = (int)DamageType.Physical;
                            break;
                        }
                    }
                }

                //stop calculation if item is not in whitelist
                if (damage_type == damage_none)
                {
                    return;
                }

                AbilityData data = ability.AbilityData.FirstOrDefault(x => x.Name != "bonus_damage" && x.Name.ToLower().Contains("damage") && !x.Name.ToLower().Contains("duration"));
                if (data != null)
                {
                    spell_damage += SpellDamageLibrary.GetAbilityValue(ability, data);
                }
                return;
            }

            switch (ability.Name)
            {
            case "terrorblade_sunder":
                HasSunderSpell      = true;
                SunderMinPercentage = SpellDamageLibrary.GetAbilityValue(ability, "hit_point_minimum_pct") / 100;
                return;

            case "bristleback_quill_spray":
                HasQuillSpraySpell    = true;
                QuillSprayStackDamage = SpellDamageLibrary.GetAbilityValue(ability, "quill_stack_damage");
                spell_damage         += SpellDamageLibrary.GetAbilityValue(ability, "quill_base_damage");
                QuillSprayDamageType  = damage_type = (int)ability.DamageType;
                return;

            case "ursa_fury_swipes":
                HasFurySwipesSpell    = true;
                FurySwipesStackDamage = SpellDamageLibrary.GetAbilityValue(ability, "damage_per_stack");
                damage_type           = (int)ability.DamageType;
                return;

            case "bristleback_bristleback":
                HasBrislebackSpell   = true;
                BristlebackSideBlock = SpellDamageLibrary.GetAbilityValue(ability, "side_damage_reduction");
                BristlebackBackBlock = SpellDamageLibrary.GetAbilityValue(ability, "back_damage_reduction");
                BristlebackSideAngle = SpellDamageLibrary.GetAbilityValue(ability, "side_angle");
                BristlebackBackAngle = SpellDamageLibrary.GetAbilityValue(ability, "back_angle");
                return;

            case "antimage_mana_void":
                HasManaVoid        = true;
                ManaVoidMultiplier = SpellDamageLibrary.GetAbilityValue(ability, "mana_void_damage_per_mana");
                ManaVoidDamageType = (int)ability.DamageType;
                return;

            case "necrolyte_reapers_scythe":
                HasNecrolyteReapersScythe       = true;
                NecrolyteReapersDamageMultipler = SpellDamageLibrary.GetAbilityValue(ability, HasScepter ? "damage_per_health_scepter" : "damage_per_health");
                NecrolyteReapersDamageType      = (int)ability.DamageType;
                break;

            case "doom_bringer_lvl_death":
                HasLvlDeath = true;
                LvlDeathAdditionalDamage  = SpellDamageLibrary.GetAbilityValue(ability, "lvl_bonus_damage");
                LvlDeathBonusHeroMultiple = (int)SpellDamageLibrary.GetAbilityValue(ability, "lvl_bonus_multiple");
                if (LvlDeathBonusHeroMultiple <= 0)
                {
                    LvlDeathBonusHeroMultiple = 1;
                }
                LvlDeathDamageType = (int)ability.DamageType;
                break;      //not return because lvl death base damage can be calculated

            case "undying_decay":
                double strSteal = SpellDamageLibrary.GetAbilityValue(ability, HasScepter ? "str_steal_scepter" : "str_steal");
                strSteal = strSteal * 19;      //calculate damage on strSteal and return
                TotalDamageArray[(int)DamageType.HealthRemoval] += strSteal;
                break;

            case "nyx_assassin_mana_burn":
                HasNyxManaBurn        = true;
                NyxManaBurnMultiplier = SpellDamageLibrary.GetAbilityValue(ability, "float_multiplier");
                NyxManaBurnDamageType = (int)ability.DamageType;
                return;

            case "undying_soul_rip":
                double radius         = SpellDamageLibrary.GetAbilityValue(ability, "radius");
                double damagePerUnit  = SpellDamageLibrary.GetAbilityValue(ability, "damage_per_unit");
                double maxUnits       = SpellDamageLibrary.GetAbilityValue(ability, "max_units");
                int    nearUnitsCount = ObjectMgr.GetEntities <Unit>().Count(
                    x => !x.Equals(HeroObj) &&       //it shouldn't be the caster
                    x.IsAlive && x.IsVisible && x.Distance2D(HeroObj) < (radius + x.HullRadius) &&          //it should be any unit that is alive, visible and within range
                    !(x.Team != HeroObj.Team && x.IsMagicImmune()) &&          //it shouldn't be magic immune on enemy team
                    (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)
                    ) - 1;          //and remove the target
                spell_damage += Math.Min(nearUnitsCount, maxUnits) * damagePerUnit;
                break;

            case "invoker_emp":
                uint wex = HeroObj.FindSpell("invoker_wex").Level - 1;
                spell_damage = SpellDamageLibrary.GetAbilityValue(ability, "mana_burned", wex) * SpellDamageLibrary.GetAbilityValue(ability, "damage_per_mana_pct", wex) / 100;
                break;

            case "huskar_life_break":
                HasLifeBreak        = true;
                LifeBreakMultiplier = SpellDamageLibrary.GetAbilityValue(ability, HasScepter ? "health_damage_scepter" : "health_damage");
                LifeBreakDamageType = (int)ability.DamageType;
                return;

            case "centaur_stampede":
                spell_damage += SpellDamageLibrary.GetAbilityValue(ability, "strength_damage") * HeroObj.TotalStrength;
                damage_type   = (int)DamageType.Magical;
                return;      //we can just break, but this spell is DamageType bugged

            case "ancient_apparition_ice_blast":
                HasIceBlast       = true;
                IceBlastThreshold = SpellDamageLibrary.GetAbilityValue(ability, "kill_pct") / 100;
                spell_damage     += SpellDamageLibrary.GetAbilityValue(ability, "dot_damage") * SpellDamageLibrary.GetAbilityValue(ability, HasScepter ? "frostbite_duration_scepter" : "frostbite_duration");
                break;

            case "axe_culling_blade":
                HasCullingBlade        = true;
                CullingBladeDamageType = (int)ability.DamageType;
                CullingBladeDamage     = SpellDamageLibrary.GetAbilityValue(ability, "damage");
                CullingBladeThreshold  = SpellDamageLibrary.GetAbilityValue(ability, HasScepter ? "kill_threshold_scepter" : "kill_threshold");
                return;

            case "spectre_dispersion":
                double spellAmplifier = SpellDamageLibrary.GetAbilityValue(ability, "damage_reflection_pct");
                IncommingDamageAmplifier *= 1.0 - spellAmplifier / 100;
                return;

            case "bounty_hunter_jinada":
            case "tusk_walrus_punch":
                double critMultiplier = SpellDamageLibrary.GetAbilityValue(ability, "crit_multiplier");
                spell_damage += AttackDamage * critMultiplier / 100;
                damage_type   = (int)DamageType.Physical;
                return;

            case "visage_soul_assumption":
                spell_damage += SpellDamageLibrary.GetAbilityValue(ability, "soul_base_damage") + SoulAssumption * SpellDamageLibrary.GetAbilityValue(ability, "soul_charge_damage");
                damage_type   = (int)ability.DamageType;
                return;

            case "morphling_adaptive_strike":
                double agiRate = (double)HeroObj.TotalAgility / (HeroObj.TotalStrength + HeroObj.TotalAgility);
                agiRate = Math.Min(Math.Max(agiRate, 0.4), 0.6);      //rate between 0.4 total and 0.6 total
                double minMultiplier = SpellDamageLibrary.GetAbilityValue(ability, "damage_min");
                double maxMultiplier = SpellDamageLibrary.GetAbilityValue(ability, "damage_max");
                agiRate       = ((agiRate - 0.4) / 0.2) * (maxMultiplier - minMultiplier) + minMultiplier; //convert from agiRate to agiDamageRate
                spell_damage += agiRate * HeroObj.TotalAgility + SpellDamageLibrary.GetAbilityValue(ability, "damage_base");
                damage_type   = (int)ability.DamageType;
                return;

            default:
                break;
            }

            if (ability.AbilityBehavior == AbilityBehavior.Passive)
            {
                return;
            }
            if (ability.DamageType == DamageType.Magical || ability.DamageType == DamageType.Physical || ability.DamageType == DamageType.Pure)
            {
                damage_type = (int)ability.DamageType;
            }
            else
            {
                damage_type = (int)DamageType.Magical;
            }

            if (ability.Name == "lina_laguna_blade" && HasScepter)
            {
                damage_type = (int)DamageType.Pure;
            }

            //TODO: meepo poof

            //get damage because spell.GetDamage is not working currently
            string lastAbilityWord = ability.Name;

            lastAbilityWord = lastAbilityWord.Substring(lastAbilityWord.LastIndexOf("_") + 1) + "_damage";
            //find ability damage
            var spellDamageData = ability.AbilityData.FirstOrDefault(x =>
                                                                     x.Name == "target_damage" || x.Name == "#AbilityDamage" || x.Name == "total_damage" || x.Name == "total_damage_tooltip" || x.Name == "hero_damage_tooltip" || x.Name == "bonus_damage" ||
                                                                     x.Name == lastAbilityWord
                                                                     );

            if (spellDamageData == null)
            {
                if (HasScepter)
                {
                    spellDamageData = ability.AbilityData.FirstOrDefault(x => x.Name == "damage_scepter");
                }
                if (!HasScepter || spellDamageData == null)
                {
                    spellDamageData = ability.AbilityData.FirstOrDefault(x => x.Name == "damage");
                }
            }

            double spellDamage = SpellDamageLibrary.GetAbilityValue(ability, spellDamageData);

            //if it's not any DOT white list, just leave
            if (!FullDOTSpellName.Contains(ability.Name) && !HalfDOTSpellName.Contains(ability.Name))
            {
                spell_damage += spellDamage;
                return;
            }

            string customSpellDamageName   = "damage";
            string customSpellDurationName = "duration";
            string customSpellIntervalName = "tick_interval";
            string customSpellBonusName    = "strike_damage";

            if (ability.Name == "bane_fiends_grip")
            {
                customSpellDamageName   = HasScepter ? "fiend_grip_damage_scepter" : "fiend_grip_damage";
                customSpellDurationName = HasScepter ? "fiend_grip_duration_scepter" : "fiend_grip_duration";
                customSpellIntervalName = "fiend_grip_tick_interval";
            }
            else if (ability.Name == "doom_bringer_doom")
            {
                customSpellDurationName = HasScepter ? "duration_scepter" : "duration";
            }
            else if (ability.Name == "disruptor_thunder_strike")
            {
                customSpellDurationName = "strikes";
            }
            else if (ability.Name == "enigma_malefice")
            {
                customSpellDurationName = "tooltip_stuns";
            }
            else if (ability.Name == "bane_nightmare")
            {
                customSpellIntervalName = "nightmare_dot_interval";
            }
            else if (ability.Name == "shredder_chakram" || ability.Name == "shredder_chakram_2")
            {
                customSpellBonusName = "pass_damage";
            }

            if (spellDamageData == null)
            {
                spellDamageData = ability.AbilityData.FirstOrDefault(x => x.Name == customSpellDamageName || DOTDamageName.Contains(x.Name));
            }
            if (spellDamageData != null)
            {
                spellDoT = SpellDamageLibrary.GetAbilityValue(ability, spellDamageData);
            }

            if (!HalfDOTSpellName.Contains(ability.Name))
            {
                //get duration
                spellDamageData = ability.AbilityData.FirstOrDefault(x => x.Name == customSpellDurationName || x.Name == "duration_tooltip" || x.Name == "tooltip_duration" || x.Name == "burn_duration");
                duration        = SpellDamageLibrary.GetAbilityValue(ability, spellDamageData);
                if (ability.Name == "huskar_burning_spear")
                {
                    duration = 8.0;
                }
                else if (ability.Name == "dazzle_poison_touch")
                {
                    duration -= SpellDamageLibrary.GetAbilityValue(ability, "set_time") + 1;
                }
            }

            //get tick interval
            spellDamageData = ability.AbilityData.FirstOrDefault(x => x.Name == customSpellIntervalName);
            tickInterval    = SpellDamageLibrary.GetAbilityValue(ability, spellDamageData);
            if (OneSecDOTSpellName.Contains(ability.Name))
            {
                tickInterval = 1.0;
            }
            else if (ability.Name == "gyrocopter_rocket_barrage")
            {
                tickInterval = 1.0 / SpellDamageLibrary.GetAbilityValue(ability, "rockets_per_second");
            }

            //get bonus damage
            bonusDamage = SpellDamageLibrary.GetAbilityValue(ability, customSpellBonusName);

            //calculate final result
            if (tickInterval < 0.001)
            {
                tickInterval = 1.0;
            }
            if (duration < 0.001)
            {
                duration = 1.0;
            }
            if (duration < tickInterval)
            {
                duration = tickInterval;                           //HalfDOT should be getting at least 1 interval damage
            }
            spell_damage += spellDoT * duration / tickInterval + bonusDamage;
        }
        public void CalculateCustomModifier()
        {
            Modifier modifier = null;
            Ability  data     = null;

            /*foreach (Modifier m in HeroObj.Modifiers)
             * {
             *  Log.SlowDebug(m.Name + " " + m.StackCount);
             * }*/

            //check for scepter
            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_item_ultimate_scepter" || x.Name == "modifier_item_ultimate_scepter_consumed");
            if (modifier != null)
            {
                HasScepter = true;
            }

            //calculate stack count based on modifier
            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_bristleback_quill_spray");
            if (modifier != null)
            {
                QuillSprayStack = modifier.StackCount;
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_ursa_fury_swipes_damage_increase");
            if (modifier != null)
            {
                FurySwipesStack = modifier.StackCount;
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_visage_soul_assumption");
            if (modifier != null)
            {
                SoulAssumption = modifier.StackCount;
            }

            //calculate amplified base on modifier
            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_item_mask_of_madness_berserk");
            if (modifier != null)
            {
                IncommingDamageAmplifier *= 1.0 + SpellDamageLibrary.GetBerserkExtraDamage(HeroObj) / 100;
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_bloodseeker_bloodrage");
            if (modifier != null)
            {
                Hero caster = ObjectMgr.GetEntities <Hero>().FirstOrDefault(x => (data = x.Spellbook.Spells.FirstOrDefault(y => y.Name == "bloodseeker_bloodrage")) != null);
                if (caster != null)
                {
                    double spellAmplifier = SpellDamageLibrary.GetAbilityValue(data, "damage_increase_pct") / 100;
                    IncommingDamageAmplifier *= 1.0 + spellAmplifier;
                    OutgoingDamageAmplifier  *= 1.0 + spellAmplifier;
                }
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_chen_penitence");
            if (modifier != null)
            {
                Hero caster = ObjectMgr.GetEntities <Hero>().FirstOrDefault(x => (data = x.Spellbook.Spells.FirstOrDefault(y => y.Name == "chen_penitence")) != null);
                if (caster != null)
                {
                    double spellAmplifier = SpellDamageLibrary.GetAbilityValue(data, "bonus_damage_taken") / 100;
                    IncommingDamageAmplifier *= 1.0 + spellAmplifier;
                }
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_shadow_demon_soul_catcher");
            if (modifier != null)
            {
                Hero caster = ObjectMgr.GetEntities <Hero>().FirstOrDefault(x => (data = x.Spellbook.Spells.FirstOrDefault(y => y.Name == "shadow_demon_soul_catcher")) != null);
                if (caster != null)
                {
                    double spellAmplifier = SpellDamageLibrary.GetAbilityValue(data, "bonus_damage_taken") / 100;
                    IncommingDamageAmplifier *= 1.0 + spellAmplifier;
                }
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_wisp_overcharge");
            if (modifier != null)
            {
                Hero caster = ObjectMgr.GetEntities <Hero>().FirstOrDefault(x => (data = x.Spellbook.Spells.FirstOrDefault(y => y.Name == "wisp_overcharge")) != null);
                if (caster != null)
                {
                    //double spellAmplifier = SpellDamageLibrary.GetAbilityValue(data, "bonus_damage_pct");
                    double spellAmplifier = SpellDamageLibrary.GetWispReduction(data.Level - 1) / 100;
                    IncommingDamageAmplifier *= 1.0 - spellAmplifier;
                }
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_centaur_stampede");
            if (modifier != null)
            {
                //for centaur: the caster have to has scepter and in the same team
                Hero caster = ObjectMgr.GetEntities <Hero>().FirstOrDefault(x => ((data = x.Spellbook.Spells.FirstOrDefault(y => y.Name == "centaur_stampede")) != null) && (x.Modifiers.FirstOrDefault(y => y.Name == "modifier_item_ultimate_scepter" || y.Name == "modifier_item_ultimate_scepter_consumed") != null) && (x.Team == HeroObj.Team));
                if (caster != null)
                {
                    double spellAmplifier = SpellDamageLibrary.GetAbilityValue(data, "damage_reduction") / 100;
                    IncommingDamageAmplifier *= 1.0 - spellAmplifier;
                }
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_silver_edge_debuff");
            if (modifier != null)
            {
                OutgoingDamageAmplifier *= 1.0 - SpellDamageLibrary.GetSilverEdgeDamageReduction() / 100;
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_ursa_enrage");
            if (modifier != null)
            {
                data = HeroObj.Spellbook.Spells.First(x => x.Name == "ursa_enrage");
                FurySwipesMultiplier      = SpellDamageLibrary.GetAbilityValue(data, "enrage_multiplier");
                IncommingDamageAmplifier *= 1.0 - SpellDamageLibrary.GetAbilityValue(data, "damage_reduction") / 100;
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_nyx_assassin_burrow");
            if (modifier != null)
            {
                data = HeroObj.Spellbook.Spells.First(x => x.Name == "nyx_assassin_burrow");
                IncommingDamageAmplifier *= 1.0 - SpellDamageLibrary.GetAbilityValue(data, "damage_reduction") / 100;
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_medusa_mana_shield");
            if (modifier != null)
            {
                HasManaShield            = true;
                data                     = HeroObj.Spellbook.Spells.First(x => x.Name == "medusa_mana_shield");
                ManaShieldReduction      = SpellDamageLibrary.GetAbilityValue(data, "absorption_tooltip");
                ManaShieldDamageAbsorbed = HeroObj.Mana * SpellDamageLibrary.GetAbilityValue(data, "damage_per_mana");
            }

            //calculate bonus damage from invi item
            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_item_invisibility_edge_windwalk");
            if (modifier != null)
            {
                TotalDamageArray[(int)DamageType.Physical] += SpellDamageLibrary.GetInviSwordDamage(HeroObj);
            }

            modifier = HeroObj.Modifiers.FirstOrDefault(x => x.Name == "modifier_item_silver_edge_windwalk");
            if (modifier != null)
            {
                TotalDamageArray[(int)DamageType.Physical] += SpellDamageLibrary.GetSilverEdgeDamage();
            }
        }