public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations) { #region Setup CharacterCalculationsTankDK basecalcs = new CharacterCalculationsTankDK(); CharacterCalculationsTankDK calcs = new CharacterCalculationsTankDK(); TankDKChar TDK = new TankDKChar(); if (character == null) { return calcs; } TDK.calcOpts = character.CalculationOptions as CalculationOptionsTankDK; if (TDK.calcOpts == null) { return calcs; } TDK.Char = character; TDK.bo = character.BossOptions; // Make sure there is at least one attack in the list. // If there's not, add a Default Melee Attack for processing if (TDK.bo.Attacks.Count < 1) { TDK.Char.IsLoading = true; TDK.bo.DamagingTargs = true; TDK.bo.Attacks.Add(BossHandler.ADefaultMeleeAttack); TDK.Char.IsLoading = false; } // Make sure there is a default melee attack // If the above processed, there will be one so this won't have to process // If the above didn't process and there isn't one, add one now if (TDK.bo.DefaultMeleeAttack == null) { TDK.Char.IsLoading = true; TDK.bo.DamagingTargs = true; TDK.bo.Attacks.Add(BossHandler.ADefaultMeleeAttack); TDK.Char.IsLoading = false; } // Since the above forced there to be an attack it's safe to do this without a null check // Attack bossAttack = TDK.bo.DefaultMeleeAttack; #endregion #region Stats // Get base stats that will be used for paperdoll: StatsDK stats = GetCharacterStats(TDK.Char, additionalItem) as StatsDK; // validate that we get a stats object; if (null == stats) { return calcs; } // This is the point that SHOULD have the right values according to the paper-doll. StatsDK sPaperDoll = stats.Clone() as StatsDK; #endregion #region Evaluation Rawr.DPSDK.CharacterCalculationsDPSDK DPSCalcs = new Rawr.DPSDK.CharacterCalculationsDPSDK(); Rawr.DPSDK.CalculationOptionsDPSDK DPSopts = new Rawr.DPSDK.CalculationOptionsDPSDK(); DPSopts.presence = Presence.Blood; DKCombatTable ct = new DKCombatTable(TDK.Char, stats, DPSCalcs, DPSopts, TDK.bo); Rotation rot = new Rotation(ct, true); rot.PRE_BloodDiseased(); // Base calculation values. This will give us Mitigation, and Survival w/ base stats. basecalcs = GetCharacterCalculations(TDK, stats, rot, false, needsDisplayCalculations); // Setup max values w/ everything turned on. stats = GetCharacterStats(TDK.Char, additionalItem, StatType.Maximum, TDK, rot); calcs.SEStats = stats.Clone() as StatsDK; ct = new DKCombatTable(TDK.Char, stats, DPSCalcs, DPSopts, TDK.bo); rot = new Rotation(ct, true); rot.PRE_BloodDiseased(); calcs = GetCharacterCalculations(TDK, stats, rot, true, needsDisplayCalculations); #region Burst // Burst as On-Use Abilties. calcs.Burst = 0; calcs.BurstWeight = TDK.calcOpts.BurstWeight; if (calcs.BurstWeight > 0) { calcs.Burst += calcs.Survivability - basecalcs.Survivability; if (calcs.Burst < 0 || float.IsNaN(calcs.Burst)) { calcs.Burst = 0; } // This should never happen but just in case calcs.Burst += calcs.Mitigation - basecalcs.Mitigation; if (calcs.Burst < 0 || float.IsNaN(calcs.Burst)) { calcs.Burst = 0; } // This should never happen but just in case // Survival calcs.PhysicalSurvival = basecalcs.PhysicalSurvival; calcs.MagicSurvival = basecalcs.MagicSurvival; calcs.BleedSurvival = basecalcs.BleedSurvival; // Mitigation calcs.Mitigation = basecalcs.Mitigation; } #endregion #region **** Recovery: DS & Blood Shield **** float minDSHeal = stats.Health * .07f; // 4.1: DS Heals for 20% of Dam Taken over the last 5 secs. float DTPSFactor = calcs.DTPS * 5f; if (TDK.calcOpts.b_RecoveryInclAvoidance == false) { DTPSFactor = calcs.DTPSNoAvoidance * 5f; } float DamDSHeal = (DTPSFactor * .20f) * (1 + .15f * TDK.Char.DeathKnightTalents.ImprovedDeathStrike); // IDS increases heals by .15 * level float DSHeal = Math.Max(minDSHeal, DamDSHeal); calcs.DSHeal = DSHeal; calcs.DSOverHeal = DSHeal * TDK.calcOpts.pOverHealing; calcs.DSCount = TDK.bo.BerserkTimer * rot.m_DSperSec; float BloodShield = (DSHeal * .5f) * (1 + (stats.Mastery * .0625f)); calcs.BShield = BloodShield; // 4.3 Removing Hitchance for healing float DSHealsPSec = (DSHeal * rot.m_DSperSec * (1f - TDK.calcOpts.pOverHealing)); calcs.TotalDShealed = DSHealsPSec * TDK.bo.BerserkTimer; float BShieldPSec = BloodShield * rot.m_DSperSec; // A new shield w/ each DS. calcs.TotalBShield = BShieldPSec * TDK.bo.BerserkTimer; calcs.Recovery = BloodShield + (DSHeal * (1f - TDK.calcOpts.pOverHealing)); calcs.HPS += DSHealsPSec; calcs.DTPS -= BShieldPSec; calcs.RecoveryWeight = TDK.calcOpts.RecoveryWeight; #endregion #endregion #region Key Data Validation if (float.IsNaN(calcs.Threat) || float.IsNaN(calcs.Survivability) || float.IsNaN(calcs.Burst) || float.IsNaN(calcs.Recovery) || float.IsNaN(calcs.Mitigation) || float.IsNaN(calcs.OverallPoints)) { #if DEBUG throw new Exception("One of the Subpoints are Invalid."); #endif } #endregion #region Display only work calcs.Miss = sPaperDoll.Miss; calcs.Dodge = sPaperDoll.Dodge; calcs.Parry = sPaperDoll.Parry; calcs.BasicStats = sPaperDoll; calcs.SEStats = stats.Clone() as StatsDK; // The full character data. calcs.TargetLevel = TDK.bo.Level; if (null != rot.m_CT.MH) { calcs.TargetDodge = rot.m_CT.MH.chanceDodged; calcs.TargetMiss = rot.m_CT.MH.chanceMissed; calcs.TargetParry = rot.m_CT.MH.chanceParried; calcs.Expertise = rot.m_CT.m_Calcs.MHExpertise; } #endregion return calcs; }
/// <summary> /// /// </summary> /// <param name="ct">Combat Table</param> /// <param name="bThreat">True if you want the results/sorting by Threat (rather Dam)</param> public Rotation(DKCombatTable ct, bool bThreat = false) { m_CT = ct; m_bThreat = bThreat; }
private void AccumulateSpecialEffectStats(StatsDK s, Character c, CalculationOptionsDPSDK calcOpts, DKCombatTable t, Rotation rot) { StatsSpecialEffects se = new StatsSpecialEffects(t, rot, c.BossOptions ); StatsDK statSE = new StatsDK(); foreach (SpecialEffect effect in s.SpecialEffects()) { if (HasRelevantStats(effect.Stats)) { statSE = se.getSpecialEffects(effect); s.Accumulate(statSE); } } }
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 }
/// <summary> /// GetCharacterCalculations is the primary method of each model, where a majority of the calculations /// and formulae will be used. GetCharacterCalculations should call GetCharacterStats(), and based on /// those total stats for the character, and any calculationoptions on the character, perform all the /// calculations required to come up with the final calculations defined in /// CharacterDisplayCalculationLabels, including an Overall rating, and all Sub ratings defined in /// SubPointNameColors. /// </summary> /// <param name="character">The character to perform calculations for.</param> /// <param name="additionalItem">An additional item to treat the character as wearing. /// This is used for gems, which don't have a slot on the character to fit in, so are just /// added onto the character, in order to get gem calculations.</param> /// <returns>A custom CharacterCalculations object which inherits from CharacterCalculationsBase, /// containing all of the final calculations defined in CharacterDisplayCalculationLabels. See /// CharacterCalculationsBase comments for more details.</returns> public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations) { // First things first, we need to ensure that we aren't using bad data CharacterCalculationsDPSDK calc = new CharacterCalculationsDPSDK(); if (character == null) { return calc; } CalculationOptionsDPSDK calcOpts = character.CalculationOptions as CalculationOptionsDPSDK; if (calcOpts == null) { return calc; } // StatsDK stats = new StatsDK(); DeathKnightTalents talents = character.DeathKnightTalents; // Setup initial Boss data. // Get Boss from BossOptions data. BossOptions hBossOptions = character.BossOptions; if (hBossOptions == null) hBossOptions = new BossOptions(); int targetLevel = hBossOptions.Level; stats = GetCharacterStats(character, additionalItem) as StatsDK; calc.BasicStats = stats.Clone() as StatsDK; ApplyRatings(calc.BasicStats); DKCombatTable combatTable = new DKCombatTable(character, calc.BasicStats, calc, calcOpts, hBossOptions); if (needsDisplayCalculations) combatTable.PostAbilitiesSingleUse(false); Rotation rot = new Rotation(combatTable); Rotation.Type RotT = rot.GetRotationType(character.DeathKnightTalents); // TODO: Fix this so we're not using pre-set rotations/priorities. if (RotT == Rotation.Type.Frost) rot.PRE_Frost(); else if (RotT == Rotation.Type.Unholy) rot.PRE_Unholy(); else if (RotT == Rotation.Type.Blood) rot.PRE_BloodDiseased(); else rot.Solver(); //TODO: This may need to be handled special since it's to update stats. AccumulateSpecialEffectStats(stats, character, calcOpts, combatTable, rot); // Now add in the special effects. ApplyRatings(stats); #region Cinderglacier if (stats.CinderglacierProc > 0) { // How many frost & shadow abilities do we have per min.? float CGabs = ((rot.m_FrostSpecials + rot.m_ShadowSpecials) / rot.CurRotationDuration) * 60f; float effCG = 0; if (CGabs > 0) // Since 3 of those abilities get the 20% buff // Get the effective ammount of CinderGlacier that would be applied across each ability. // it is a proc after all. effCG = 3 / CGabs; stats.BonusFrostDamageMultiplier += (.2f * effCG); stats.BonusShadowDamageMultiplier += (.2f * effCG); } #endregion // refresh w/ updated stats. combatTable = new DKCombatTable(character, stats, calc, calcOpts, hBossOptions); combatTable.PostAbilitiesSingleUse(false); rot = new Rotation(combatTable); RotT = rot.GetRotationType(character.DeathKnightTalents); // TODO: Fix this so we're not using pre-set rotations. if (RotT == Rotation.Type.Frost) rot.PRE_Frost(); else if (RotT == Rotation.Type.Unholy) rot.PRE_Unholy(); else if (RotT == Rotation.Type.Blood) rot.PRE_BloodDiseased(); else rot.Solver(); #region Pet Handling // For UH, this is valid. For Frost/Blood, we need to have this be 1/3 of the value since it has an uptime of 1 min for every 3. float ghouluptime = 1f; calc.dpsSub[(int)DKability.Gargoyle] = 0; if (RotT != Rotation.Type.Unholy) ghouluptime = 1f / 3f; else { // Unholy will also have gargoyles. Pet Gar = new Gargoyle(stats, talents, hBossOptions, calcOpts.presence); float garuptime = .5f/3f; calc.dpsSub[(int)DKability.Gargoyle] = Gar.DPS * garuptime; calc.damSub[(int)DKability.Gargoyle] = Gar.DPS * 30f; // Duration 30 seconds. } Pet ghoul = new Ghoul(stats, talents, hBossOptions, calcOpts.presence); calc.dpsSub[(int)DKability.Ghoul] = ghoul.DPS * ghouluptime; calc.damSub[(int)DKability.Ghoul] = ghoul.DPS * 60f; // Duration 1 min. #endregion // Stats as Fire damage additive value proc. if (stats.ArcaneDamage > 1) calc.dpsSub[(int)DKability.OtherArcane] += stats.ArcaneDamage; if (stats.FireDamage > 1) calc.dpsSub[(int)DKability.OtherFire] += stats.FireDamage; if (stats.FrostDamage > 1) calc.dpsSub[(int)DKability.OtherFrost] += stats.FrostDamage; if (stats.HolyDamage > 1) calc.dpsSub[(int)DKability.OtherHoly] += stats.HolyDamage; if (stats.NatureDamage > 1) calc.dpsSub[(int)DKability.OtherNature] += stats.NatureDamage; if (stats.ShadowDamage > 1) calc.dpsSub[(int)DKability.OtherShadow] += stats.ShadowDamage; // Fire Dam Multiplier. calc.RotationTime = rot.CurRotationDuration; calc.Blood = rot.m_BloodRunes; calc.Frost = rot.m_FrostRunes; calc.Unholy = rot.m_UnholyRunes; calc.Death = rot.m_DeathRunes; calc.RP = rot.m_RunicPower; calc.FreeRERunes = rot.m_FreeRunesFromRE; calc.EffectiveArmor = stats.Armor; calc.OverallPoints = calc.DPSPoints = rot.m_DPS // Add in supplemental damage from other sources + calc.dpsSub[(int)DKability.Ghoul] + calc.dpsSub[(int)DKability.Gargoyle] + calc.dpsSub[(int)DKability.OtherArcane] + calc.dpsSub[(int)DKability.OtherFire] + calc.dpsSub[(int)DKability.OtherFrost] + calc.dpsSub[(int)DKability.OtherHoly] + calc.dpsSub[(int)DKability.OtherNature] + calc.dpsSub[(int)DKability.OtherShadow]; if (needsDisplayCalculations) { AbilityDK_Base a = rot.GetAbilityOfType(DKability.White); if (rot.ml_Rot.Count > 1) { AbilityDK_Base b; b = rot.GetAbilityOfType(DKability.ScourgeStrike); if (b == null) b = rot.GetAbilityOfType(DKability.FrostStrike); if (b == null) b = rot.GetAbilityOfType(DKability.DeathStrike); calc.YellowHitChance = b.HitChance; } calc.WhiteHitChance = (a == null ? 0 : a.HitChance + a.CritChance + .23f); // + glancing calc.MHWeaponDPS = (a == null ? 0 : rot.GetAbilityOfType(DKability.White).DPS); if (null != combatTable.MH) { calc.MHWeaponDamage = combatTable.MH.damage; calc.MHAttackSpeed = combatTable.MH.hastedSpeed; calc.DodgedAttacks = combatTable.MH.chanceDodged; calc.AvoidedAttacks = combatTable.MH.chanceDodged; if (!hBossOptions.InBack) calc.AvoidedAttacks += combatTable.MH.chanceParried; calc.MissedAttacks = combatTable.MH.chanceMissed; } if (null != combatTable.OH) { a = rot.GetAbilityOfType(DKability.WhiteOH); calc.OHWeaponDPS = (a == null ? 0 : rot.GetAbilityOfType(DKability.WhiteOH).DPS); calc.OHWeaponDamage = combatTable.OH.damage; calc.OHAttackSpeed = combatTable.OH.hastedSpeed; } calcOpts.szRotReport = rot.ReportRotation(); calc.m_RuneCD = (float)rot.m_SingleRuneCD / 1000; calc.DPSBreakdown(rot); } return calc; }