/// <summary> /// Get the spell damage value. /// </summary> /// <param name="source"> /// The source /// </param> /// <param name="target"> /// The target /// </param> /// <param name="spellSlot"> /// The spell slot /// </param> /// <param name="stage"> /// The stage /// </param> /// <returns> /// The <see cref="double" /> value of damage. /// </returns> public static double GetSpellDamage( this Obj_AI_Hero source, Obj_AI_Base target, SpellSlot spellSlot, DamageStage stage = DamageStage.Default) { if (source == null || !source.IsValid || target == null || !target.IsValid) { return(0); } ChampionDamage value; if (!DamageCollection.TryGetValue(source.ChampionName, out value)) { return(0); } var spellData = value.GetSlot(spellSlot)?.FirstOrDefault(e => e.Stage == stage)?.SpellData; if (spellData == null) { return(0); } var spellLevel = source.Spellbook.GetSpell(spellData.ScaleSlot != SpellSlot.Unknown ? spellData.ScaleSlot : spellSlot) .Level; if (spellLevel == 0) { return(0); } bool alreadyAdd1 = false, alreadyAdd2 = false; var targetHero = target as Obj_AI_Hero; var targetMinion = target as Obj_AI_Minion; double dmgBase = 0, dmgBonus = 0, dmgPassive = 0; var dmgReduce = 1d; if (spellData.DamagesPerLvl?.Count > 0) { dmgBase = spellData.DamagesPerLvl[Math.Min(source.Level - 1, spellData.DamagesPerLvl.Count - 1)]; } else if (spellData.Damages?.Count > 0) { dmgBase = spellData.Damages[Math.Min(spellLevel - 1, spellData.Damages.Count - 1)]; if (!string.IsNullOrEmpty(spellData.ScalingBuff)) { var buffCount = (spellData.ScalingBuffTarget == DamageScalingTarget.Source ? source : target).GetBuffCount( spellData.ScalingBuff); dmgBase = buffCount > 0 ? dmgBase * (buffCount + spellData.ScalingBuffOffset) : 0; } } if (dmgBase > 0) { if (targetMinion != null && spellData.BonusDamageOnMinion?.Count > 0) { dmgBase += spellData.BonusDamageOnMinion[Math.Min(spellLevel - 1, spellData.BonusDamageOnMinion.Count - 1)]; } if (spellData.IsApplyOnHit || spellData.IsModifiedDamage || spellData.SpellEffectType == SpellEffectType.Single) { if (source.HasBuff("Serrated")) { dmgBase += 15; } if (targetHero != null) { if (!spellData.IsApplyOnHit && Items.HasItem((int)ItemId.Dorans_Shield, targetHero)) { dmgBase -= 8; } } else if (targetMinion != null) { var savagery = source.GetCunning(Cunning.Savagery); if (savagery.IsValid()) { dmgBase += savagery.Points; } } alreadyAdd1 = true; } dmgBase = source.CalculateDamage(target, spellData.DamageType, dmgBase); if (spellData.IsModifiedDamage && spellData.DamageType == DamageType.Physical && targetHero != null && targetHero.ChampionName == "Fizz") { dmgBase -= 4 + (2 * Math.Floor((targetHero.Level - 1) / 3d)); alreadyAdd2 = true; } } if (spellData.BonusDamages?.Count > 0) { foreach (var bonusDmg in spellData.BonusDamages) { var dmg = source.ResolveBonusSpellDamage(target, bonusDmg, spellLevel - 1); if (dmg <= 0) { continue; } if (!alreadyAdd1 && (spellData.IsModifiedDamage || spellData.SpellEffectType == SpellEffectType.Single)) { if (source.HasBuff("Serrated")) { dmg += 15; } if (targetHero != null) { if (Items.HasItem((int)ItemId.Dorans_Shield, targetHero)) { dmg -= 8; } } else if (targetMinion == null) { var savagery = source.GetCunning(Cunning.Savagery); if (savagery.IsValid()) { dmg += savagery.Points; } } alreadyAdd1 = true; } dmgBonus += source.CalculateDamage(target, bonusDmg.DamageType, dmg); if (!alreadyAdd2 && spellData.IsModifiedDamage && bonusDmg.DamageType == DamageType.Physical && targetHero != null && targetHero.ChampionName == "Fizz") { dmgBonus -= 4 + (2 * Math.Floor((targetHero.Level - 1) / 3d)); alreadyAdd2 = true; } } } var totalDamage = dmgBase + dmgBonus; if (totalDamage > 0) { if (spellData.ScalePerTargetMissHealth > 0) { totalDamage *= (target.MaxHealth - target.Health) / target.MaxHealth * spellData.ScalePerTargetMissHealth + 1; } if (target is Obj_AI_Minion && spellData.MaxDamageOnMinion?.Count > 0) { totalDamage = Math.Min( totalDamage, spellData.MaxDamageOnMinion[Math.Min(spellLevel - 1, spellData.MaxDamageOnMinion.Count - 1)]); } if (spellData.IsApplyOnHit || spellData.IsModifiedDamage) { dmgPassive += source.GetPassiveDamageInfo(target, false).Value; if (targetHero != null) { if (spellData.IsModifiedDamage && new[] { 3047, 1316, 1318, 1315, 1317 }.Any(i => Items.HasItem(i, targetHero))) { dmgReduce *= 0.9; } if (source.GetFerocity(Ferocity.FervorofBattle).IsValid()) { var fervorBuffCount = source.GetBuffCount("MasteryOnHitDamageStacker"); if (fervorBuffCount > 0) { dmgPassive += source.CalculateDamage( target, DamageType.Physical, (0.13 + (0.77 * source.Level)) * fervorBuffCount); } } } } } return (Math.Max( Math.Floor( totalDamage * dmgReduce + (spellData.IsApplyOnHit || spellData.IsModifiedDamage ? source.PassiveFlatMod(target) : 0) + dmgPassive), 0)); }
/// <summary> /// Apples passive percent mod calculations towards the given amount of damage, a modifier onto the amount based on /// passive percent effects. /// </summary> /// <param name="source"> /// The source /// </param> /// <param name="target"> /// The target /// </param> /// <param name="amount"> /// The amount /// </param> /// <param name="damageType"> /// The damage Type. /// </param> /// <returns> /// The damage after passive percent modifier calculations. /// </returns> private static double PassivePercentMod( this Obj_AI_Base source, Obj_AI_Base target, double amount, DamageType damageType) { if (source is Obj_AI_Turret) { var minion = target as Obj_AI_Minion; if (minion != null) { var minionType = minion.GetMinionType(); if (minionType.HasFlag(MinionTypes.Siege)) { // Siege minions and super minions receive 70% damage from turrets. amount *= 0.7; } else if (minionType.HasFlag(MinionTypes.Normal)) { // Normal minions take 114% more damage from towers. amount *= 1.14285714285714; } } } var hero = source as Obj_AI_Hero; var targetHero = target as Obj_AI_Hero; if (hero != null) { // DoubleEdgedSword if (damageType != DamageType.True && hero.GetFerocity(Ferocity.DoubleEdgedSword).IsValid()) { amount *= hero.IsMelee() ? 1.03 : 1.02; } // Oppressor if (hero.GetFerocity(Ferocity.Oppressor).IsValid() && target.IsMoveImpaired()) { amount *= 1.025; } if (targetHero != null) { // Expose Weakness var exposeweakBuff = targetHero.GetBuff("ExposeWeaknessDebuff"); if (exposeweakBuff != null && hero.Team == exposeweakBuff.Caster.Team && !exposeweakBuff.Caster.Compare(hero)) { amount *= 1.03; } // Assassin if (hero.GetCunning(Cunning.Assassin).IsValid() && !GameObjects.Heroes.Any( h => h.Team == hero.Team && !h.Compare(hero) && h.Distance(hero) < 800)) { amount *= 1.02; } // Merciless var merciless = hero.GetCunning(Cunning.Merciless); if (merciless.IsValid() && targetHero.HealthPercent < 40) { amount *= 1 + (merciless.Points / 100); } // Giant Slayer - Lord Dominik's Regards if ((Items.HasItem(3036, hero) || Items.HasItem(3034, hero)) && hero.MaxHealth < targetHero.MaxHealth && damageType == DamageType.Physical) { amount *= 1 + Math.Min(targetHero.MaxHealth - hero.MaxHealth, 500) / 50 * (Items.HasItem(3036, hero) ? 0.015 : 0.01); } } } // DoubleEdgedSword if (targetHero != null && damageType != DamageType.True && targetHero.GetFerocity(Ferocity.DoubleEdgedSword).IsValid()) { amount *= targetHero.IsMelee() ? 1.015 : 1.02; } return(amount); }
/// <summary> /// Gets the source auto attack damage on the target. /// </summary> /// <param name="source"> /// The source /// </param> /// <param name="target"> /// The target /// </param> /// <returns> /// The estimated auto attack damage. /// </returns> public static double GetAutoAttackDamage(this Obj_AI_Base source, Obj_AI_Base target) { double dmgPhysical = source.TotalAttackDamage, dmgMagical = 0, dmgPassive = 0; var dmgReduce = 1d; var hero = source as Obj_AI_Hero; var targetHero = target as Obj_AI_Hero; var isMixDmg = false; if (hero != null) { // Spoils Of War if (hero.IsMelee() && target is Obj_AI_Minion && target.Team != GameObjectTeam.Neutral && hero.GetBuffCount("TalentReaper") > 0) { var eyeEquinox = Items.HasItem(2303, hero); if ( GameObjects.Heroes.Any( h => h.Team == hero.Team && !h.Compare(hero) && h.Distance(hero) < (eyeEquinox ? 850 : 1050))) { var spoilwarDmg = 200; if (Items.HasItem((int)ItemId.Targons_Brace, hero)) { spoilwarDmg = 240; } else if (Items.HasItem((int)ItemId.Face_of_the_Mountain, hero) || eyeEquinox) { spoilwarDmg = 400; } if (target.Health < spoilwarDmg) { return(float.MaxValue); } } } // Bonus Damage (Passive) var passiveInfo = hero.GetPassiveDamageInfo(target); dmgPassive += passiveInfo.Value; if (passiveInfo.Override) { return(dmgPassive); } switch (hero.ChampionName) { case "Kalista": dmgPhysical *= 0.9; break; case "Jhin": dmgPhysical += Math.Round( (new[] { 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40 }[ hero.Level - 1] +(Math.Round((hero.Crit * 100 / 10) * 4)) + (Math.Round(((hero.AttackSpeedMod - 1) * 100 / 10) * 2.5))) / 100 * dmgPhysical); break; case "Corki": dmgPhysical /= 2; dmgMagical = dmgPhysical; isMixDmg = true; break; case "Quinn": if (target.HasBuff("quinnw")) { dmgPhysical += 10 + (5 * hero.Level) + (0.14 + (0.02 * hero.Level)) * hero.TotalAttackDamage; } break; } // Serrated Dirk if (hero.HasBuff("Serrated")) { if (!isMixDmg) { dmgPhysical += 15; } else { dmgPhysical += 7.5; dmgMagical += 7.5; } } if (targetHero != null) { // Dorans Shield if (Items.HasItem((int)ItemId.Dorans_Shield, targetHero)) { var subDmg = (dmgPhysical + dmgMagical) - 8; dmgPhysical = !isMixDmg ? subDmg : (dmgMagical = subDmg / 2); } // Fervor Of Battle if (hero.GetFerocity(Ferocity.FervorofBattle).IsValid()) { var fervorBuffCount = hero.GetBuffCount("MasteryOnHitDamageStacker"); if (fervorBuffCount > 0) { dmgPassive += hero.CalculatePhysicalDamage( target, (0.13 + (0.77 * hero.Level)) * fervorBuffCount); } } } else if (target is Obj_AI_Minion) { // Savagery var savagery = hero.GetCunning(Cunning.Savagery); if (savagery.IsValid()) { dmgPhysical += savagery.Points; } // RiftHerald P if (!hero.IsMelee() && target.Team == GameObjectTeam.Neutral && target.Name == "SRU_RiftHerald") { dmgReduce *= 0.65; } } } // Ninja Tabi if (targetHero != null && !(source is Obj_AI_Turret) && new[] { 3047, 1316, 1318, 1315, 1317 }.Any(i => Items.HasItem(i, targetHero))) { dmgReduce *= 0.9; } dmgPhysical = source.CalculatePhysicalDamage(target, dmgPhysical); dmgMagical = source.CalculateMagicDamage(target, dmgMagical); // Fizz P if (targetHero != null && targetHero.ChampionName == "Fizz") { dmgPhysical -= 4 + (2 * Math.Floor((targetHero.Level - 1) / 3d)); } // This formula is right, work out the math yourself if you don't believe me return (Math.Max( Math.Floor((dmgPhysical + dmgMagical) * dmgReduce + source.PassiveFlatMod(target) + dmgPassive), 0)); }