private float[] GetMitigation(ref TankDKChar TDK, StatsDK stats, Rotation rot, float fPercentCritMitigation, float ArmorDamageReduction, float[] fCurrentDTPS, float fMagicDR, bool bFactorInAvoidance) { float[] fTotalMitigation = new float[EnumHelper.GetCount(typeof(MitigationSub))]; // Ensure the CurrentDTPS structure is the right size. if (fCurrentDTPS.Length < EnumHelper.GetCount(typeof(SurvivalSub))) { fCurrentDTPS = new float[EnumHelper.GetCount(typeof(SurvivalSub))]; } float fSegmentMitigation = 0f; float fSegmentDPS = 0f; float fPhyDamageDPS = fCurrentDTPS[(int)SurvivalSub.Physical]; float fMagicDamageDPS = fCurrentDTPS[(int)SurvivalSub.Magic]; float fBleedDamageDPS = fCurrentDTPS[(int)SurvivalSub.Bleed]; #region *** Dam Avoided (Crit, Haste, Avoidance) *** #region ** Crit Mitigation ** // Crit mitigation: // Crit mitigation work for Physical damage only. float fCritMultiplier = .12f; // Bleeds can't crit. // Neither can spells from bosses. (As per a Loading screen ToolTip.) float fCritDPS = fPhyDamageDPS * fCritMultiplier; fSegmentMitigation = (fCritDPS * fPercentCritMitigation); // Add in the value of crit mitigation. fTotalMitigation[(int)MitigationSub.Crit] = fSegmentMitigation; // The max damage at this point needs to include crit. fCurrentDTPS[(int)SurvivalSub.Physical] = fCurrentDTPS[(int)SurvivalSub.Physical] + (fCritDPS - fSegmentMitigation); #endregion #region ** Haste Mitigation ** // Placeholder for comparing differing DPS values related to haste. float fNewIncPhysDPS = 0; // Let's just look at Imp Icy Touch #region Improved Icy Touch // Get the new slowed AttackSpeed based on ImpIcyTouch // Factor in the base slow caused by FF (14% base). float fBossAttackSpeedReduction = 0.0f; if (rot.Contains(DKability.IcyTouch) || rot.Contains(DKability.FrostFever)) { fBossAttackSpeedReduction = 0.2f; } else if (stats.BossAttackSpeedReductionMultiplier > 0) // FF provided by someone else. { fBossAttackSpeedReduction = stats.BossAttackSpeedReductionMultiplier; } // Figure out what the new Physical DPS should be based on that. fSegmentDPS = TDK.bo.GetDPSByType(ATTACK_TYPES.AT_MELEE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); fNewIncPhysDPS = fSegmentDPS / (1 + fBossAttackSpeedReduction); // Send the difference to the Mitigation value. fSegmentMitigation = fSegmentDPS - fNewIncPhysDPS; fTotalMitigation[(int)MitigationSub.Haste] += fSegmentMitigation; fCurrentDTPS[(int)SurvivalSub.Physical] -= fSegmentMitigation; #endregion #endregion #region ** Avoidance Mitigation ** // Let's see how much damage was avoided. // Raise the total mitgation by that amount. if (bFactorInAvoidance) { fSegmentDPS = fNewIncPhysDPS; fNewIncPhysDPS = TDK.bo.GetDPSByType(ATTACK_TYPES.AT_MELEE, 0, 0, 0, fBossAttackSpeedReduction, stats.Miss, stats.Dodge, stats.EffectiveParry, 0, 0, 0, 0, 0, 0, 0); fSegmentMitigation = fSegmentDPS - fNewIncPhysDPS; fTotalMitigation[(int)MitigationSub.Avoidance] += fSegmentMitigation; fCurrentDTPS[(int)SurvivalSub.Physical] -= fSegmentMitigation; } #endregion #endregion #region *** Dam Reduced (AMS, Armor, Magic Resist, DamageTaken Modifiers) *** #region ** Anti-Magic Shell ** // TODO: This is a CD, so would only be in BURST. // Anti-Magic Shell. //////////////////////////////////////////////////////// // Talent: MagicSuppression increases AMS by 8/16/25% per point. // Glyph: GlyphofAntiMagicShell increases AMS by 2 sec. // AMS has a 45 sec CD. float amsDuration = (5f + (TDK.Char.DeathKnightTalents.GlyphofAntiMagicShell == true ? 2f : 0f)) * (1 + stats.DefensiveCooldownDurationMultiplier); float amsUptimePct = amsDuration / 45f; // AMS reduces damage taken by 75% up to a max of 50% health. float amsReduction = 0.75f * (1f + (TDK.Char.DeathKnightTalents.MagicSuppression * .25f / 3)); float amsReductionMax = stats.Health * 0.5f * (1 + stats.DefensiveCooldownReductionMultiplier); // up to 50% of health means that the amdDRvalue equates to the raw damage points removed. // This means that toon health and INC damage values from the options pane are going to affect this quite a bit. float amsDRvalue = (Math.Min(amsReductionMax, (fMagicDamageDPS * amsDuration) * amsReduction) * amsUptimePct); // Raise the TotalMitigation by that amount. fCurrentDTPS[(int)SurvivalSub.Magic] -= amsDRvalue; fTotalMitigation[(int)MitigationSub.AMS] += amsDRvalue; #endregion #region ** Armor Dam Mitigation ** // For any physical only damage reductions. // Factor in armor Dam Reduction fSegmentMitigation = fPhyDamageDPS * ArmorDamageReduction; // calcs.ArmorMitigation = fSegmentMitigation; fTotalMitigation[(int)MitigationSub.Armor] += fSegmentMitigation; fCurrentDTPS[(int)SurvivalSub.Physical] *= 1f - ArmorDamageReduction; #endregion #region ** Resistance Dam Mitigation ** // For any physical only damage reductions. // Factor in armor Dam Reduction fSegmentMitigation = fMagicDamageDPS * fMagicDR; fTotalMitigation[(int)MitigationSub.Magic] += fSegmentMitigation; fCurrentDTPS[(int)SurvivalSub.Magic] -= fCurrentDTPS[(int)SurvivalSub.Magic] * fMagicDR; #endregion #region ** Dam Taken Mitigation ** fTotalMitigation[(int)MitigationSub.DamageReduction] += fMagicDamageDPS * (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.SpellDamageTakenReductionMultiplier ); fTotalMitigation[(int)MitigationSub.DamageReduction] += fBleedDamageDPS * (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier); fTotalMitigation[(int)MitigationSub.DamageReduction] += fPhyDamageDPS * (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier); fCurrentDTPS[(int)SurvivalSub.Magic] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.SpellDamageTakenReductionMultiplier ); fCurrentDTPS[(int)SurvivalSub.Bleed] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier); fCurrentDTPS[(int)SurvivalSub.Physical] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier); #endregion #region ** Dam Absorbed ** fTotalMitigation[(int)MitigationSub.DamageReduction] += stats.DamageAbsorbed; // fCurrentDTPS[(int)SurvivalSub.Magic] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.SpellDamageTakenReductionMultiplier); // fCurrentDTPS[(int)SurvivalSub.Bleed] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier); // fCurrentDTPS[(int)SurvivalSub.Physical] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier); #endregion #endregion #region ** Boss Handler Mitigation (Impedences) ** float TotalDuration = 0; float ImprovedDuration = 0; float ImpedenceMitigation = 0; #region ** Fear Based Mitigation (fear reduction) ** if (TDK.bo.Fears.Count > 0) { foreach (Impedance m in TDK.bo.Fears) { TotalDuration += m.Duration; } ImprovedDuration = TotalDuration / (1 + stats.FearDurReduc); fSegmentMitigation = (TotalDuration - ImprovedDuration) /* Add some multiplier to this */; ImpedenceMitigation += fSegmentMitigation; } #endregion #region ** Movement Based Mitigation (run speed) ** if (TDK.bo.Moves.Count > 0) { TotalDuration = 0; foreach (Impedance m in TDK.bo.Moves) { TotalDuration += m.Duration; } ImprovedDuration = TotalDuration / (1 + stats.MovementSpeed); fSegmentMitigation = (TotalDuration - ImprovedDuration) /* Add some multiplier to this */; ImpedenceMitigation += fSegmentMitigation; } #endregion #region ** Disarm Based Mitigation (Disarm reduction) ** if (TDK.bo.Disarms.Count > 0) { TotalDuration = 0; foreach (Impedance m in TDK.bo.Disarms) { TotalDuration += m.Duration; } ImprovedDuration = TotalDuration / (1 + stats.DisarmDurReduc); fSegmentMitigation = (TotalDuration - ImprovedDuration) /* Add some multiplier to this */; ImpedenceMitigation += fSegmentMitigation; } #endregion #region ** Stun Based Mitigation (stun reduction) ** if (TDK.bo.Stuns.Count > 0) { TotalDuration = 0; foreach (Impedance m in TDK.bo.Stuns) { TotalDuration += m.Duration; } ImprovedDuration = TotalDuration / (1 + stats.StunDurReduc); fSegmentMitigation = (TotalDuration - ImprovedDuration) /* Add some multiplier to this */; ImpedenceMitigation += fSegmentMitigation; } #endregion // calcs.ImpedenceMitigation = ImpedenceMitigation; fTotalMitigation[(int)MitigationSub.Impedences] += ImpedenceMitigation; #endregion #region ** Heals not from DS ** fSegmentMitigation = StatConversion.ApplyMultiplier(stats.Healed, stats.HealingReceivedMultiplier); fSegmentMitigation += (StatConversion.ApplyMultiplier(stats.Hp5, stats.HealingReceivedMultiplier) / 5); fSegmentMitigation += StatConversion.ApplyMultiplier(stats.HealthRestore, stats.HealingReceivedMultiplier); // Health Returned by other sources than DS: if (stats.HealthRestoreFromMaxHealth > 0) fSegmentMitigation += (stats.HealthRestoreFromMaxHealth * stats.Health); fTotalMitigation[(int)MitigationSub.Heals] = fSegmentMitigation; #endregion return fTotalMitigation; }
public StatsSpecialEffects(DKCombatTable t, Rotation rot, BossOptions bo) { if (rot.ml_Rot == null || rot.ml_Rot.Count == 0) { #if DEBUG throw new Exception("Invalid or Incomplete rotation."); #endif } combatTable = t; m_Rot = rot; m_bo = bo; triggerIntervals = new Dictionary<Trigger, float>(); // Chance that trigger of correct type is produced (for example for /// SpellCrit trigger you would set triggerInterval to average time between hits and set /// triggerChance to crit chance) triggerChances = new Dictionary<Trigger, float>(); unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f); float unhastedAttackSpeedOH = (combatTable.OH != null ? combatTable.OH.baseSpeed : 2.0f); float unhastedAttackSpeedSpells = (combatTable.OH != null ? combatTable.OH.baseSpeed : 2.0f); #region Use triggerIntervals.Add(Trigger.Use, 0); triggerChances.Add(Trigger.Use, 1); #endregion #region Basic Hit float fMeleeHitTriggerInterval = (1f / ((m_Rot.getMeleeSpecialsPerSecond() * (combatTable.DW ? 2f : 1f)) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f))); float fMeleeHitTriggerInterval1ofDW = (1f / ((m_Rot.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f))); float fPhysicalHitChance = 1f - (combatTable.missedSpecial + combatTable.dodgedSpecial) * (1f - combatTable.totalMHMiss); triggerIntervals.Add(Trigger.MeleeHit, fMeleeHitTriggerInterval); triggerIntervals.Add(Trigger.PhysicalHit, fMeleeHitTriggerInterval); triggerIntervals.Add(Trigger.MeleeAttack, fMeleeHitTriggerInterval); triggerIntervals.Add(Trigger.PhysicalAttack, fMeleeHitTriggerInterval); triggerChances.Add(Trigger.MeleeHit, fPhysicalHitChance); triggerChances.Add(Trigger.PhysicalHit, fPhysicalHitChance); triggerChances.Add(Trigger.MeleeAttack, fPhysicalHitChance); triggerChances.Add(Trigger.PhysicalAttack, fPhysicalHitChance); // TODO: interval would be quicker since it should include DOTTick interval. triggerIntervals.Add(Trigger.DamageDone, fMeleeHitTriggerInterval); triggerChances.Add(Trigger.DamageDone, fPhysicalHitChance); triggerIntervals.Add(Trigger.DamageOrHealingDone, fMeleeHitTriggerInterval); triggerChances.Add(Trigger.DamageOrHealingDone, fPhysicalHitChance); #endregion #region Special Hit triggerIntervals.Add(Trigger.CurrentHandHit, fMeleeHitTriggerInterval1ofDW); triggerChances.Add(Trigger.CurrentHandHit, fPhysicalHitChance); triggerIntervals.Add(Trigger.MainHandHit, fMeleeHitTriggerInterval1ofDW); triggerChances.Add(Trigger.MainHandHit, fPhysicalHitChance); float fMeleeHitTriggerIntervalOH = (combatTable.OH == null ? 0 : fMeleeHitTriggerInterval1ofDW); triggerIntervals.Add(Trigger.OffHandHit, fMeleeHitTriggerIntervalOH); triggerChances.Add(Trigger.OffHandHit, fPhysicalHitChance); #endregion #region Basic Crit triggerIntervals.Add(Trigger.MeleeCrit, fMeleeHitTriggerInterval); triggerChances.Add(Trigger.MeleeCrit, combatTable.physCrits); triggerIntervals.Add(Trigger.PhysicalCrit, fMeleeHitTriggerInterval); triggerChances.Add(Trigger.PhysicalCrit, combatTable.physCrits); #endregion #region Spell Hit float fSpellHitInterval = 0; fSpellHitInterval = 1f / m_Rot.getSpellSpecialsPerSecond(); float fSpellHitChance = 0; float fSpellCritChance = 0; switch (m_Rot.curRotationType) { case Rotation.Type.Unholy: { // Unholy fSpellHitChance = m_Rot.GetAbilityOfType(DKability.DeathCoil).HitChance; fSpellCritChance = m_Rot.GetAbilityOfType(DKability.DeathCoil).CritChance; break; } case Rotation.Type.Frost: { if (m_Rot.Contains(DKability.HowlingBlast)) { fSpellHitChance = m_Rot.GetAbilityOfType(DKability.HowlingBlast).HitChance; fSpellCritChance = m_Rot.GetAbilityOfType(DKability.HowlingBlast).CritChance; } else { fSpellHitChance = m_Rot.GetAbilityOfType(DKability.IcyTouch).HitChance; fSpellCritChance = m_Rot.GetAbilityOfType(DKability.IcyTouch).CritChance; } break; } case Rotation.Type.Blood: { fSpellHitChance = m_Rot.GetAbilityOfType(DKability.IcyTouch).HitChance; fSpellCritChance = m_Rot.GetAbilityOfType(DKability.IcyTouch).CritChance; break; } } triggerIntervals.Add(Trigger.DamageSpellCast, fSpellHitInterval); triggerChances.Add(Trigger.DamageSpellCast, fSpellHitChance); triggerIntervals.Add(Trigger.SpellCast, fSpellHitInterval); triggerChances.Add(Trigger.SpellCast, fSpellHitChance); triggerIntervals.Add(Trigger.DamageSpellHit, fSpellHitInterval); triggerChances.Add(Trigger.DamageSpellHit, fSpellHitChance); triggerIntervals.Add(Trigger.SpellHit, fSpellHitInterval); triggerChances.Add(Trigger.SpellHit, fSpellHitChance); #endregion #region Spell Crit triggerIntervals.Add(Trigger.SpellCrit, fSpellHitInterval); triggerChances.Add(Trigger.SpellCrit, fSpellCritChance); triggerIntervals.Add(Trigger.DamageSpellCrit, fSpellHitInterval); triggerChances.Add(Trigger.DamageSpellCrit, fSpellCritChance); #endregion #region Specific Strikes // triggerIntervals.Add(Trigger.BloodStrikeHit, 0); // triggerChances.Add(Trigger.BloodStrikeHit, 0); if (m_Rot.HasTrigger(Trigger.BloodStrikeHit)) { triggerIntervals.Add(Trigger.BloodStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.BloodStrikeHit) * (combatTable.DW ? 2f : 1f))); triggerChances.Add(Trigger.BloodStrikeHit, m_Rot.GetAbilityOfType(DKability.BloodStrike).HitChance); } // triggerIntervals.Add(Trigger.HeartStrikeHit, 0); // triggerChances.Add(Trigger.HeartStrikeHit, 0); if (m_Rot.HasTrigger(Trigger.HeartStrikeHit)) { triggerIntervals.Add(Trigger.HeartStrikeHit, m_Rot.CurRotationDuration / m_Rot.CountTrigger(Trigger.HeartStrikeHit)); triggerChances.Add(Trigger.HeartStrikeHit, m_Rot.GetAbilityOfType(DKability.HeartStrike).HitChance); } // triggerIntervals.Add(Trigger.ObliterateHit, 0); // triggerChances.Add(Trigger.ObliterateHit, 0); if (m_Rot.HasTrigger(Trigger.ObliterateHit)) { triggerIntervals.Add(Trigger.ObliterateHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.ObliterateHit) * (combatTable.DW ? 2f : 1f))); triggerChances.Add(Trigger.ObliterateHit, m_Rot.GetAbilityOfType(DKability.Obliterate).HitChance); } // triggerIntervals.Add(Trigger.ScourgeStrikeHit, 0); // triggerChances.Add(Trigger.ScourgeStrikeHit, 0); if (m_Rot.HasTrigger(Trigger.ScourgeStrikeHit)) { triggerIntervals.Add(Trigger.ScourgeStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.ScourgeStrikeHit) * (combatTable.DW ? 2f : 1f))); triggerChances.Add(Trigger.ScourgeStrikeHit, m_Rot.GetAbilityOfType(DKability.ScourgeStrike).HitChance); } // triggerIntervals.Add(Trigger.DeathStrikeHit, 0); // triggerChances.Add(Trigger.DeathStrikeHit, 0); if (m_Rot.HasTrigger(Trigger.DeathStrikeHit)) { triggerIntervals.Add(Trigger.DeathStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.DeathStrikeHit) * (combatTable.DW ? 2f : 1f))); triggerChances.Add(Trigger.DeathStrikeHit, m_Rot.GetAbilityOfType(DKability.DeathStrike).HitChance); } // triggerIntervals.Add(Trigger.PlagueStrikeHit, 0); // triggerChances.Add(Trigger.PlagueStrikeHit, 0); if (m_Rot.HasTrigger(Trigger.PlagueStrikeHit)) { triggerIntervals.Add(Trigger.PlagueStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.PlagueStrikeHit) * (combatTable.DW ? 2f : 1f))); triggerChances.Add(Trigger.PlagueStrikeHit, m_Rot.GetAbilityOfType(DKability.PlagueStrike).HitChance); } // triggerIntervals.Add(Trigger.IcyTouchHit, 0); // triggerChances.Add(Trigger.IcyTouchHit, 0); if (m_Rot.HasTrigger(Trigger.IcyTouchHit)) { triggerIntervals.Add(Trigger.IcyTouchHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.IcyTouchHit) * (combatTable.DW ? 2f : 1f))); triggerChances.Add(Trigger.IcyTouchHit, m_Rot.GetAbilityOfType(DKability.IcyTouch).HitChance); } // triggerIntervals.Add(Trigger.RuneStrikeHit, 0); // triggerChances.Add(Trigger.RuneStrikeHit, 0); if (m_Rot.HasTrigger(Trigger.RuneStrikeHit)) { triggerIntervals.Add(Trigger.RuneStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.RuneStrikeHit) * (combatTable.DW ? 2f : 1f))); triggerChances.Add(Trigger.RuneStrikeHit, m_Rot.GetAbilityOfType(DKability.RuneStrike).HitChance); } #endregion #region Misc Offensive triggerIntervals.Add(Trigger.DeathRuneGained, (m_Rot.m_DeathRunes > 0) ? m_Rot.CurRotationDuration / (m_Rot.m_DeathRunes) : 0); triggerChances.Add(Trigger.DeathRuneGained, 1); triggerIntervals.Add(Trigger.KillingMachine, (combatTable.m_CState.m_Talents.KillingMachine > 0) ? ( 60 / (5 * combatTable.m_CState.m_Talents.KillingMachine / 3 ) ) : 0); // KM is a PPM triggerChances.Add(Trigger.KillingMachine, 1); triggerIntervals.Add(Trigger.DoTTick, 1); // TODO: assumes 2 diseases. but w/ Blood & unholy's chance for a 3rd plus UB could also tick.... should be dynamic. triggerChances.Add(Trigger.DoTTick, 1); #endregion #region Defensive triggerChances.Add(Trigger.DamageParried, Math.Min(1f, m_Rot.m_CT.m_CState.m_Stats.EffectiveParry)); triggerIntervals.Add(Trigger.DamageParried, m_bo.DynamicCompiler_FilteredAttacks(m_bo.GetFilteredAttackList(AVOIDANCE_TYPES.Parry)).AttackSpeed); float fAvoidance = (m_Rot.m_CT.m_CState.m_Stats.EffectiveParry + m_Rot.m_CT.m_CState.m_Stats.Dodge + m_Rot.m_CT.m_CState.m_Stats.Miss); triggerChances.Add(Trigger.DamageAvoided, Math.Min(1f, fAvoidance)); triggerIntervals.Add(Trigger.DamageAvoided, m_bo.DynamicCompiler_FilteredAttacks(m_bo.GetFilteredAttackList( AVOIDANCE_TYPES.Parry | AVOIDANCE_TYPES.Block | AVOIDANCE_TYPES.Dodge | AVOIDANCE_TYPES.Miss)).AttackSpeed); triggerChances.Add(Trigger.DamageTaken, Math.Min(1f, 1f - fAvoidance)); triggerIntervals.Add(Trigger.DamageTaken, m_bo.DynamicCompiler_Attacks.AttackSpeed); triggerChances.Add(Trigger.DamageTakenPhysical, Math.Min(1f, 1f - fAvoidance)); triggerIntervals.Add(Trigger.DamageTakenPhysical, m_bo.DynamicCompiler_FilteredAttacks(m_bo.GetFilteredAttackList(ItemDamageType.Physical)).AttackSpeed); triggerChances.Add(Trigger.DamageTakenPutsMeBelow35PercHealth, 0.35f); triggerIntervals.Add(Trigger.DamageTakenPutsMeBelow35PercHealth, m_bo.DynamicCompiler_Attacks.AttackSpeed); triggerChances.Add(Trigger.DamageTakenMagical, 1); List<Attack> attacks = new List<Attack>(); foreach (ItemDamageType i in EnumHelper.GetValues(typeof(ItemDamageType))) { if (i != ItemDamageType.Physical) { foreach (Attack a in m_bo.GetFilteredAttackList(i)) attacks.Add(a); } } triggerIntervals.Add(Trigger.DamageTakenMagical, m_bo.DynamicCompiler_FilteredAttacks(attacks).AttackSpeed); #endregion }