protected override void Calculate() { float tableSize = 0.0f; #if (RAWR3) int targetLevel = BossOpts.Level; #else int targetLevel = CalcOpts.TargetLevel; #endif float SpellHitChance = Lookup.SpellHitChance(Character, Stats, targetLevel); float bonusExpertise = Lookup.BonusExpertisePercentage(Character, Stats); if (Lookup.IsSpell(Ability)) { // Miss Miss = Math.Min(1.0f - tableSize, 1.0f - SpellHitChance);// - bonusHit)); tableSize += Miss; // Crit Critical = Lookup.SpellCritChance(Character, Stats, targetLevel); tableSize += Critical; } else { float bonusHit = Lookup.HitChance(Character, Stats, targetLevel); // Miss // Miss = Math.Min(1.0f - tableSize, Math.Max(0.0f, Lookup.TargetAvoidanceChance(Character, Stats, HitResult.Miss) - bonusHit)); Miss = Math.Min(1.0f - tableSize, Math.Max(0.0f, 1.0f - bonusHit)); tableSize += Miss; // Avoidance if (Lookup.IsAvoidable(Ability)) { // Dodge Dodge = Math.Min(1.0f - tableSize, Math.Max(0.0f, Lookup.TargetAvoidanceChance(Character, Stats, HitResult.Dodge, targetLevel) - bonusExpertise)); tableSize += Dodge; // Parry Parry = Math.Min(1.0f - tableSize, Math.Max(0.0f, Lookup.TargetAvoidanceChance(Character, Stats, HitResult.Parry, targetLevel) - bonusExpertise)); tableSize += Parry; } // Glancing Blow if (Ability == Ability.None) { Glance = Math.Min(1.0f - tableSize, Math.Max(0.0f, Lookup.TargetAvoidanceChance(Character, Stats, HitResult.Glance, targetLevel))); tableSize += Glance; } // Block if (Ability == Ability.None || Ability == Ability.HammerOfTheRighteous) { Block = Math.Min(1.0f - tableSize, Math.Max(0.0f, Lookup.TargetAvoidanceChance(Character, Stats, HitResult.Block, targetLevel))); tableSize += Block; } // Critical Hit Critical = Math.Min(1.0f - tableSize, Lookup.BonusCritPercentage(Character, Stats, Ability, targetLevel, CalcOpts.TargetType)); tableSize += Critical;//FIXME: Tablesize must not change when critchance is negative (boss) } // Normal Hit Hit = Math.Max(0.0f, 1.0f - tableSize); // Partial Resist TODO: Partial Resists don't belong in the combat table, they're not an avoidance type but a damage multiplier. if (Lookup.HasPartials(Ability)) { Resist = Math.Min(1.0f - tableSize, Math.Max(0.0f, Lookup.TargetAvoidanceChance(Character, Stats, HitResult.Resist, targetLevel))); } }
private void CalculateDamage() { float baseDamage = 0.0f; float critMultiplier = 0.0f; float duration = 0.0f; float AP = Stats.AttackPower; float SP = Stats.SpellPower; #if (RAWR3) int targetLevel = BossOpts.Level; #else int targetLevel = CalcOpts.TargetLevel; #endif #region Ability Base Damage switch (Ability) { // White Damage case Ability.None: baseDamage = Stats.WeaponDamage; DamageMultiplier *= (1.0f + Stats.BonusPhysicalDamageMultiplier) * (1.0f - (Lookup.GlancingReduction(Character, targetLevel) * AttackTable.Glance)) * (1.0f - ArmorReduction); critMultiplier = 1.0f; break; case Ability.ShieldOfRighteousness: float blockValue = Stats.BlockValue + Stats.ShieldOfRighteousnessBlockValue + Stats.JudgementBlockValue + Stats.HolyShieldBlockValue; float blockValueDRStart = 30 * Character.Level; if (blockValue < blockValueDRStart) { baseDamage = blockValue; } else if (blockValue < 39.5 * Character.Level) { baseDamage = blockValueDRStart + (0.95f * (blockValue - blockValueDRStart)) - (0.000625f * (float)Math.Pow(blockValue - blockValueDRStart, 2)); } else { baseDamage = blockValueDRStart + (0.95f * 9.5f * Character.Level) - (0.000625f * (float)Math.Pow(9.5 * Character.Level, 2)); } baseDamage += 520f + Stats.BonusShieldOfRighteousnessDamage; DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier); critMultiplier = 1.0f; break; case Ability.HammerOfTheRighteous: if (Talents.HammerOfTheRighteous == 0 || Character.MainHand == null) { Damage = 0.0f; return; } baseDamage = (Stats.WeaponDamage / Character.MainHand.Speed) * 4.0f; DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (1.0f + Stats.BonusHammerOfTheRighteousMultiplier); critMultiplier = 1.0f; break; // Seal of Vengeance is the tiny damage that applies on each swing; Holy Vengeance is the DoT // While trivial threat and damage, it's modeled for compatibility with Seal of Righteousness case Ability.SealOfVengeance: baseDamage = Stats.WeaponDamage * 0.33f; DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (1.0f + 0.03f * Talents.SealsOfThePure) * (1.0f + Stats.BonusSealOfVengeanceDamageMultiplier); critMultiplier = 1.0f; break; // 5 stacks of Holy Vengeance are assumed // TODO: implement stacking mechanic for beginning-of-fight TPS case Ability.HolyVengeance: baseDamage = 5f * (0.016f * SP + 0.032f * AP); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (1.0f + 0.03f * Talents.SealsOfThePure) * (1.0f + Stats.BonusSealOfVengeanceDamageMultiplier); critMultiplier = 0.0f; break; // Judgement of Vengeance assumes 5 stacks of Holy Vengeance case Ability.JudgementOfVengeance: float holyVengeanceStacks = 5; baseDamage = (0.22f * SP + 0.14f * AP) * (1.0f + 0.1f * holyVengeanceStacks); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (1.0f + 0.03f * Talents.SealsOfThePure) * (Talents.GlyphOfJudgement ? 1.1f : 1.0f); critMultiplier = 1.0f; break; case Ability.SealOfRighteousness: baseDamage = Lookup.WeaponSpeed(Character, Stats) * ((0.022f * AP) + (0.044f * SP)); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (1.0f + 0.03f * Talents.SealsOfThePure) * (1.0f + Stats.BonusSealOfRighteousnessDamageMultiplier) * (Talents.GlyphOfSealOfRighteousness ? 1.1f : 1.0f); critMultiplier = 0.0f; break; case Ability.JudgementOfRighteousness: baseDamage = 1.0f + (0.2f * AP) + (0.32f * SP); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (1.0f + 0.03f * Talents.SealsOfThePure) * (Talents.GlyphOfJudgement ? 1.1f : 1.0f); critMultiplier = 1.0f; break; case Ability.HolyShield: if (Talents.HolyShield == 0) { Damage = 0.0f; return; } baseDamage = (211f + (0.056f * AP) + (0.09f * SP)) * 1.3f; DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier); critMultiplier = 0.0f; break; case Ability.Consecration: baseDamage = 113f + (0.04f * (SP + Stats.ConsecrationSpellPower)) + (0.04f * AP); duration = (Talents.GlyphOfConsecration ? 10.0f : 8.0f); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier); critMultiplier = 0.0f; break; case Ability.Exorcism: baseDamage = 1087f + (0.15f * SP) + (0.15f * AP); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (1.0f + Talents.SanctityOfBattle * 0.05f) * (Talents.GlyphOfExorcism ? 1.2f : 1.0f); critMultiplier = 0.5f; break; case Ability.AvengersShield: baseDamage = 1222f + (0.07f * SP) + (0.07f * AP); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (Talents.GlyphOfAvengersShield ? 2.0f : 1.0f); critMultiplier = 1.0f; break; case Ability.HolyWrath: baseDamage = 1142f + (AP * 0.07f) + (SP * 0.07f); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier); critMultiplier = 0.5f; break; case Ability.HammerOfWrath: baseDamage = 1198f + (AP * 0.15f) + (SP * 0.15f); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier); critMultiplier = 1.0f; break; case Ability.RetributionAura: baseDamage = 112f + (SP * 0.0666f); DamageMultiplier *= (1.0f + Stats.BonusHolyDamageMultiplier) * (1.0f + Talents.SanctifiedRetribution * 0.5f); critMultiplier = 0.0f; break; } #endregion // All damage multipliers, 1HWS, Armor etc...do we need to split buff/debuff ? baseDamage *= DamageMultiplier; #region Miss Chance, Avoidance Chance if (Lookup.IsSpell(Ability)) { if (Ability == Ability.Consecration) { // Probability calculation, since each tick can be resisted individually. baseDamage = Lookup.GetConsecrationTickChances(duration, baseDamage, AttackTable.Miss); } else { // Missed spell attacks // TODO: expand Ability Model to include a check for damage type, not only spell. baseDamage *= (1.0f - AttackTable.Miss); } } else { // Missed attacks if (Lookup.IsAvoidable(Ability)) { baseDamage *= (1.0f - AttackTable.AnyMiss); } else { baseDamage *= (1.0f - AttackTable.Miss); } } #endregion #region Partial Resists // Partial Resists if (Lookup.HasPartials(Ability)) { // Detailed table of Partial slices. float[] partialChanceTable = StatConversion.GetResistanceTable(Character.Level, targetLevel, 0.0f, Stats.SpellPenetration); // Here goes nothing, Damage averaged over the different partial slices that can happen. float partialDamage = 0.0f; for (int i = 0; i < 11; i++) { partialDamage += partialChanceTable[i] * (1.0f - 0.1f * (float)i) * baseDamage; } baseDamage = partialDamage; } #endregion // Average critical strike bonuses if (Lookup.CanCrit(Ability)) { baseDamage += baseDamage * critMultiplier * AttackTable.Critical; } // Final Damage the Ability deals Damage = baseDamage; }