virtual public void AccumulateStats() { if (m_Stats == null) { m_Stats = new StatsDK(); } m_Stats.Strength = m_DKStats.Strength; m_Stats.Stamina = m_DKStats.Stamina; m_Stats.PhysicalHaste = m_DKStats.PhysicalHaste; m_Stats.CritRating = m_DKStats.CritRating; m_Stats.PhysicalHit = m_DKStats.PhysicalHit; m_Stats.BonusDamageMultiplier = m_DKStats.BonusDamageMultiplier; m_Stats.Expertise = StatConversion.GetExpertiseFromDodgeParryReduc(m_DKStats.PhysicalHit); if (m_Talents.GlyphofRaiseDead) { m_Stats.Strength += (m_DKStats.Strength * .4f); m_Stats.Stamina += (m_DKStats.Stamina * .4f); } m_Stats.DamageTakenReductionMultiplier = 1f - (1f - m_Stats.DamageTakenReductionMultiplier) * (1f - .90f); // 90% passive AOE damage reduction. // Apply ratings and such. m_Stats.AttackPower = (float)Math.Floor(m_Stats.Strength * 2); m_Stats.PhysicalCrit = StatConversion.GetCritFromRating(m_Stats.CritRating); m_Stats.SpellCrit = StatConversion.GetCritFromRating(m_Stats.CritRating); }
virtual public void AccumulateStats() { if (m_Stats == null) m_Stats = new StatsDK(); m_Stats.Strength = m_DKStats.Strength; m_Stats.Stamina = m_DKStats.Stamina; m_Stats.PhysicalHaste = m_DKStats.PhysicalHaste; m_Stats.CritRating = m_DKStats.CritRating; m_Stats.PhysicalHit = m_DKStats.PhysicalHit; m_Stats.BonusDamageMultiplier = m_DKStats.BonusDamageMultiplier; m_Stats.Expertise = StatConversion.GetExpertiseFromDodgeParryReduc(m_DKStats.PhysicalHit); if (m_Talents.GlyphofRaiseDead) { m_Stats.Strength += (m_DKStats.Strength * .4f); m_Stats.Stamina += (m_DKStats.Stamina * .4f); } m_Stats.DamageTakenReductionMultiplier = 1f - (1f - m_Stats.DamageTakenReductionMultiplier) * (1f - .90f); // 90% passive AOE damage reduction. // Apply ratings and such. m_Stats.AttackPower = (float)Math.Floor(m_Stats.Strength * 2); m_Stats.PhysicalCrit = StatConversion.GetCritFromRating(m_Stats.CritRating); m_Stats.SpellCrit = StatConversion.GetCritFromRating(m_Stats.CritRating); }
public DKCombatTable(Character c, StatsDK stats, CharacterCalculationsBase calcs, ICalculationOptionBase calcOpts, BossOptions bossOpts) { m_CState = new CombatState(); if (c != null) { m_CState.m_Char = c; m_CState.m_Talents = c.DeathKnightTalents; m_CState.m_Spec = CalculationsDPSDK.GetSpec(c.DeathKnightTalents); } m_CState.m_Stats = stats; // TODO: Put in check here for null. m_Calcs = calcs as CharacterCalculationsDPSDK; m_Opts = calcOpts as CalculationOptionsDPSDK; m_CState.m_Presence = Presence.Frost; if (calcOpts != null && m_Opts == null) { //throw new Exception("Opts not converted properly."); m_Opts = new CalculationOptionsDPSDK(); } try { m_CState.m_Presence = m_Opts.presence; } catch { } // pass stay w/ default m_BO = bossOpts; if (m_BO == null) { m_BO = new BossOptions(); } // JOTHAY TODO: Kind of an Ugly Hack to do this, but it will give them a value m_CState.m_NumberOfTargets = m_BO.MultiTargs ? m_BO.DynamicCompiler_MultiTargs.GetAverageTargetGroupSize(m_BO.BerserkTimer) : 1f; // m_CState.m_bAttackingFromBehind = m_BO.InBack; m_CState.fBossArmor = m_BO.Armor; SetupExpertise(c); }
public Pet(StatsDK dkstats, DeathKnightTalents t, BossOptions bo, Presence p) { m_BO = bo; m_DKStats = dkstats; m_Talents = t; m_Presence = p; AccumulateStats(); }
public Gargoyle(StatsDK dkstats, DeathKnightTalents t, BossOptions bo, Presence p) { m_BO = bo; m_DKStats = dkstats; m_Talents = t; m_Presence = p; AccumulateStats(); DamageType = ItemDamageType.Physical; }
/// <summary> /// Process the Stat modifier values /// </summary> /// <param name="statsTotal">[in/out] Stats object for the total character stats.</param> /// <param name="iBladedArmor">[in] character.talent.BladedArmor</param> private void ProcessStatModifiers(StatsDK statsTotal, int iBladedArmor, Character c) { statsTotal.Strength = StatConversion.ApplyMultiplier(statsTotal.Strength, statsTotal.BonusStrengthMultiplier); statsTotal.Agility = StatConversion.ApplyMultiplier(statsTotal.Agility, statsTotal.BonusAgilityMultiplier); // The stamina value is floor in game for the calculation statsTotal.Stamina = StatConversion.ApplyMultiplier(statsTotal.Stamina, statsTotal.BonusStaminaMultiplier); statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina); statsTotal.Armor = StatConversion.ApplyMultiplier(statsTotal.Armor, statsTotal.BaseArmorMultiplier); statsTotal.AttackPower = StatConversion.ApplyMultiplier(statsTotal.AttackPower, statsTotal.BonusAttackPowerMultiplier); statsTotal.BonusArmor = StatConversion.ApplyMultiplier(statsTotal.BonusArmor, statsTotal.BonusArmorMultiplier); statsTotal.Armor += statsTotal.BonusArmor; statsTotal.Health += StatConversion.GetHealthFromStamina(statsTotal.Stamina); statsTotal.Health = statsTotal.Health * (1 + statsTotal.BonusHealthMultiplier); // Talent: BladedArmor ////////////////////////////////////////////////////////////// if (iBladedArmor > 0) { statsTotal.AttackPower += (statsTotal.Armor / 180f) * (float)iBladedArmor; } // AP, crit, etc. already being factored in w/ multiplier. statsTotal.AttackPower += StatConversion.ApplyMultiplier((statsTotal.Strength * 2), statsTotal.BonusAttackPowerMultiplier); }
/// <summary> /// Get Character Stats for multiple calls. Allowing means by which to stack different sets/Special effects. /// </summary> /// <param name="character"></param> /// <param name="additionalItem"></param> /// <param name="sType">Enum describing which set of stats we want.</param> /// <returns></returns> private StatsDK GetCharacterStats(Character character, Item additionalItem, StatType sType, TankDKChar TDK, Rotation rot = null) { StatsDK statsTotal = new StatsDK(); if (null == character.CalculationOptions) { // Possibly put some error text here. return statsTotal; } // Warning TDK can be blank at this point. TDK.Char = character; TDK.calcOpts = character.CalculationOptions as CalculationOptionsTankDK; TDK.bo = character.BossOptions; // Start populating data w/ Basic racial & class baseline. Stats BStats = BaseStats.GetBaseStats(character); statsTotal.Accumulate(BStats); statsTotal.BaseAgility = BStats.Agility; AccumulateItemStats(statsTotal, character, additionalItem); // Stack only the info we care about. statsTotal = GetRelevantStatsLocal(statsTotal); AccumulateBuffsStats(statsTotal, character.ActiveBuffs); AccumulateSetBonusStats(statsTotal, character.SetBonusCount); #region Tier Bonuses: Tank #region T11 int tierCount; if (character.SetBonusCount.TryGetValue("Magma Plated Battlearmor", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T11_Tank = true; } if (tierCount >= 4) { statsTotal.b4T11_Tank = true; } } if (statsTotal.b4T11_Tank) statsTotal.AddSpecialEffect(_SE_IBF[1]); else statsTotal.AddSpecialEffect(_SE_IBF[0]); #endregion #region T12 if (character.SetBonusCount.TryGetValue("Elementium Deathplate Battlearmor", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T12_Tank = true; } if (tierCount >= 4) { statsTotal.b4T12_Tank = true; } } if (statsTotal.b2T12_Tank) { // Your melee attacks cause Burning Blood on your target, // which deals 800 Fire damage every 2 for 6 sec and // causes your abilities to behave as if you had 2 diseases // present on the target. // Implemented in CombatState DiseaseCount statsTotal.FireDamage = 800 / 2; } if (statsTotal.b4T12_Tank) { // Your when your Dancing Rune Weapon expires, it grants 15% additional parry chance for 12 sec. // Implemented in DRW talent Static Special Effect. } #endregion #region T13 if (character.SetBonusCount.TryGetValue("Necrotic Boneplate Armor", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T13_Tank = true; } if (tierCount >= 4) { statsTotal.b4T13_Tank = true; } } if (statsTotal.b2T13_Tank) { // When an attack drops your health below 35%, one of your Blood Runes // will immediately activate and convert into a Death Rune for the next // 20 sec. This effect cannot occur more than once every 45 sec. } if (statsTotal.b4T13_Tank) { // Your Vampiric Blood ability also affects all party and raid members // for 50% of the effect it has on you. } #endregion #endregion Rawr.DPSDK.CalculationsDPSDK.RemoveDuplicateRunes(statsTotal, character, true/*statsTotal.bDW*/); Rawr.DPSDK.CalculationsDPSDK.AccumulateTalents(statsTotal, character); Rawr.DPSDK.CalculationsDPSDK.AccumulatePresenceStats(statsTotal, Presence.Blood, character.DeathKnightTalents); statsTotal.ArcaneResistance += statsTotal.ArcaneResistanceBuff; statsTotal.ArcaneResistanceBuff = 0f; statsTotal.FireResistance += statsTotal.FireResistanceBuff; statsTotal.FireResistanceBuff = 0f; statsTotal.FrostResistance += statsTotal.FrostResistanceBuff; statsTotal.FrostResistanceBuff = 0f; statsTotal.NatureResistance += statsTotal.NatureResistanceBuff; statsTotal.NatureResistanceBuff = 0f; statsTotal.ShadowResistance += statsTotal.ShadowResistanceBuff; statsTotal.ShadowResistanceBuff = 0f; /* At this point, we're combined all the data from gear and talents and all that happy jazz. * However, we haven't applied any special effects nor have we applied any multipliers. * Many special effects are now getting dependant upon combat info (rotations). */ StatsDK PreRatingsBase = statsTotal.Clone() as StatsDK; // Apply the ratings to actual stats. ProcessRatings(statsTotal); ProcessAvoidance(statsTotal, TDK.bo.Level, TDK.Char, PreRatingsBase); statsTotal.EffectiveParry = 0; if (character.MainHand != null) { statsTotal.EffectiveParry = statsTotal.Parry; } float fChanceToGetHit = 1f - Math.Min(1f, statsTotal.Miss + statsTotal.Dodge + statsTotal.EffectiveParry); // Now comes the special handling for secondary stats passes that are dependant upon Boss & Rotation values. if (sType != StatType.Unbuffed && (null != TDK.bo && null != rot)) // Make sure we have the rotation and Boss info. { #region Special Effects #region Talent: Bone Shield if (character.DeathKnightTalents.BoneShield > 0) { int BSStacks = 4; // The number of bones by default. if (Rawr.Properties.GeneralSettings.Default.PTRMode) BSStacks = 6; // The number of bones by default. float BoneLossRate = Math.Max(2f, TDK.bo.DynamicCompiler_Attacks.AttackSpeed / fChanceToGetHit); // 2 sec internal cooldown on loosing bones so the DK can't get spammed to death. float moveVal = character.DeathKnightTalents.GlyphofBoneShield ? 0.15f : 0f; SpecialEffect primary = new SpecialEffect(Trigger.Use, new Stats() { DamageTakenReductionMultiplier = 0.20f, BonusDamageMultiplier = 0.02f, MovementSpeed = moveVal, }, BoneLossRate * BSStacks, 60) {BypassCache = true,}; statsTotal.AddSpecialEffect(primary); } #endregion #region Vengeance // Vengence has the chance to increase AP. int iVengenceMax = (int)(statsTotal.Stamina + (BaseStats.GetBaseStats(character).Health) * .1); int iAttackPowerMax = (int)statsTotal.AttackPower + iVengenceMax; float mitigatedDPS = TDK.bo.GetDPSByType(TDK.role, 0, statsTotal.DamageTakenReductionMultiplier, 0, .14f, statsTotal.Miss, statsTotal.Dodge, statsTotal.EffectiveParry, 0, 0, 0, 0, 0, 0, 0); //statsTotal.ArcaneResistance, statsTotal.FireResistance, statsTotal.FrostResistance, statsTotal.NatureResistance, statsTotal.ShadowResistance); mitigatedDPS = mitigatedDPS * (1 - (float)StatConversion.GetArmorDamageReduction(TDK.bo.Level, statsTotal.Armor, 0f, 0f)); float APStackSingle = mitigatedDPS * 0.05f * TDK.bo.DynamicCompiler_Attacks.AttackSpeed; int APStackCountMax = (int)Math.Floor(iVengenceMax / APStackSingle); SpecialEffect seVeng = new SpecialEffect(Trigger.DamageTaken, new Stats() { AttackPower = APStackSingle }, 2 * 10, 0, 1, APStackCountMax) { BypassCache = true, }; Dictionary<Trigger, float> triggerInterval = new Dictionary<Trigger,float>(); Dictionary<Trigger, float> triggerChance = new Dictionary<Trigger,float>(); triggerInterval.Add(Trigger.DamageTaken, TDK.bo.DynamicCompiler_Attacks.AttackSpeed); triggerChance.Add(Trigger.DamageTaken, 1f); // MitigatedDPS already factors in avoidance. statsTotal.VengenceAttackPower = seVeng.GetAverageStats(triggerInterval, triggerChance).AttackPower; statsTotal.AttackPower += statsTotal.VengenceAttackPower * TDK.calcOpts.VengeanceWeight; #endregion statsTotal.AddSpecialEffect(_SE_DeathPact); // For now we just factor them in once. Rawr.DPSDK.StatsSpecialEffects se = new Rawr.DPSDK.StatsSpecialEffects(rot.m_CT, rot, TDK.bo); StatsDK statSE = new StatsDK(); foreach (SpecialEffect effect in statsTotal.SpecialEffects()) { if (HasRelevantStats(effect.Stats)) { statSE.Accumulate(se.getSpecialEffects(effect)); // statsTotal.Accumulate(se.getSpecialEffects(effect)); // This is done further down. } } // Darkmoon card greatness procs if (statSE.HighestStat > 0 || statSE.Paragon > 0) { if (statSE.Strength >= statSE.Agility) { statSE.Strength += statSE.HighestStat + statSE.Paragon; } else if (statSE.Agility > statSE.Strength) { statSE.Agility += statSE.HighestStat + statSE.Paragon; } statSE.HighestStat = 0; statSE.Paragon = 0; } // Any Modifiers from stats need to be applied to statSE statSE.Strength = StatConversion.ApplyMultiplier(statSE.Strength, statsTotal.BonusStrengthMultiplier); statSE.Agility = StatConversion.ApplyMultiplier(statSE.Agility, statsTotal.BonusAgilityMultiplier); statSE.Stamina = StatConversion.ApplyMultiplier(statSE.Stamina, statsTotal.BonusStaminaMultiplier); // statSE.Stamina = (float)Math.Floor(statSE.Stamina); statSE.Armor = StatConversion.ApplyMultiplier(statSE.Armor, statsTotal.BaseArmorMultiplier); statSE.AttackPower = StatConversion.ApplyMultiplier(statSE.AttackPower, statsTotal.BonusAttackPowerMultiplier); statSE.BonusArmor = StatConversion.ApplyMultiplier(statSE.BonusArmor, statsTotal.BonusArmorMultiplier); statSE.Armor += statSE.BonusArmor; statSE.Health += StatConversion.GetHealthFromStamina(statSE.Stamina) + statSE.BattlemasterHealthProc; statSE.Health = statSE.Health * (1 + statSE.BonusHealthMultiplier); statsTotal.BonusHealthMultiplier = ((1 + statsTotal.BonusHealthMultiplier) * (1 + statSE.BonusHealthMultiplier)) - 1 ; if (character.DeathKnightTalents.BladedArmor > 0) { statSE.AttackPower += (statSE.Armor / 180f) * (float)character.DeathKnightTalents.BladedArmor; } statSE.AttackPower += StatConversion.ApplyMultiplier((statSE.Strength * 2), statsTotal.BonusAttackPowerMultiplier); statSE.ParryRating += statSE.Strength * 0.27f; // Any Modifiers from statSE need to be applied to stats statsTotal.Strength = StatConversion.ApplyMultiplier(statsTotal.Strength, statSE.BonusStrengthMultiplier); statsTotal.Agility = StatConversion.ApplyMultiplier(statsTotal.Agility, statSE.BonusAgilityMultiplier); statsTotal.Stamina = StatConversion.ApplyMultiplier(statsTotal.Stamina, statSE.BonusStaminaMultiplier); // stats.Stamina = (float)Math.Floor(stats.Stamina); statsTotal.Armor = StatConversion.ApplyMultiplier(statsTotal.Armor, statSE.BaseArmorMultiplier); statsTotal.AttackPower = StatConversion.ApplyMultiplier(statsTotal.AttackPower, statSE.BonusAttackPowerMultiplier); statsTotal.BonusArmor = StatConversion.ApplyMultiplier(statsTotal.BonusArmor, statSE.BonusArmorMultiplier); statsTotal.Accumulate(statSE); PreRatingsBase.Miss += statSE.Miss; PreRatingsBase.Dodge += statSE.Dodge; PreRatingsBase.Parry += statSE.Parry; #if DEBUG if (float.IsNaN(statsTotal.Stamina)) throw new Exception("Something very wrong in stats."); #endif #endregion // Special effects } // Apply the Multipliers ProcessStatModifiers(statsTotal, character.DeathKnightTalents.BladedArmor, character); ProcessAvoidance(statsTotal, TDK.bo.Level, TDK.Char, PreRatingsBase); if (character.MainHand != null) { statsTotal.EffectiveParry = statsTotal.Parry; } return (statsTotal); }
private CharacterCalculationsTankDK GetCharacterCalculations(TankDKChar TDK, StatsDK stats, Rotation rot, bool isBurstCalc, bool needsDisplayCalcs) { CharacterCalculationsTankDK calcs = new CharacterCalculationsTankDK(); // Level differences. int iLevelDiff = Math.Max(TDK.bo.Level - TDK.Char.Level, 0); float fChanceToGetHit = 1f - Math.Min(1f, stats.Miss + stats.Dodge + stats.EffectiveParry); float ArmorDamageReduction = (float)StatConversion.GetArmorDamageReduction(TDK.bo.Level, stats.Armor, 0f, 0f); #region **** Setup Fight parameters **** // Get the values of each type of damage in %. // So first we get each type of damage in the same units: DPS. // Get the total DPS. float[] fCurrentDTPS = new float[3]; fCurrentDTPS[(int)SurvivalSub.Physical] = 0f; fCurrentDTPS[(int)SurvivalSub.Bleed] = 0f; fCurrentDTPS[(int)SurvivalSub.Magic] = 0f; float[] fCurrentDmgBiggestHit = new float[3]; fCurrentDmgBiggestHit[(int)SurvivalSub.Physical] = 0f; fCurrentDmgBiggestHit[(int)SurvivalSub.Bleed] = 0f; fCurrentDmgBiggestHit[(int)SurvivalSub.Magic] = 0f; float[] fCurrentDTPSPerc = new float[3]; fCurrentDTPSPerc[(int)SurvivalSub.Physical] = 1f; fCurrentDTPSPerc[(int)SurvivalSub.Bleed] = 0f; fCurrentDTPSPerc[(int)SurvivalSub.Magic] = 0f; float fTotalDTPS = 0; float fAvoidanceTotal = 1f - fChanceToGetHit; // We want to start getting the Boss Handler stuff going on. // Setup initial Boss data. // How much of what kind of damage does this boss deal with? #region ** Incoming Boss Dam ** // Let's make sure this is even valid float DPHit = 0; float DPTick = 0; switch (TDK.calcOpts.PlayerRole) { case 0: TDK.role = PLAYER_ROLES.MainTank; break; case 1: TDK.role = PLAYER_ROLES.OffTank; break; case 2: TDK.role = PLAYER_ROLES.TertiaryTank; break; default: TDK.role = PLAYER_ROLES.MainTank; break; } TDK.role = PLAYER_ROLES.MainTank; foreach (Attack a in TDK.bo.Attacks) { // PlayerRole on calcOpts is MT=0, OT=1, TT=2, Any Tank = 3 // Any Tank means it should be affected by anything that affects a tanking role if (a.AffectsRole[PLAYER_ROLES.MainTank] && (TDK.calcOpts.PlayerRole == 0 || TDK.calcOpts.PlayerRole == 3) || a.AffectsRole[PLAYER_ROLES.OffTank] && (TDK.calcOpts.PlayerRole == 1 || TDK.calcOpts.PlayerRole == 3) || a.AffectsRole[PLAYER_ROLES.TertiaryTank] && (TDK.calcOpts.PlayerRole == 2 || TDK.calcOpts.PlayerRole == 3)) { // TODO: Figure out a way to get the phase changes handled. DPHit = a.DamagePerHit; DPTick = a.DamagePerTick; if (a.DamageIsPerc) { #if DEBUG if ((a.DamagePerHit >= 1f) || (a.DamagePerTick >= 1f)) throw new Exception("Percentage Damage is >= 100%."); #endif DPHit = a.DamagePerHit * stats.Health; DPTick = a.DamagePerTick * stats.Health; } // Bleeds vs Magic vs Physical if (a.DamageType == ItemDamageType.Physical) { // Bleed or Physical // Need to figure out how to determine bleed vs. physical hits. // Also need to balance out the physical hits and balance the hit rate. // JOTHAY NOTE: Bleeds are DoTs if (a.IsDoT) { fCurrentDTPS[(int)SurvivalSub.Bleed] += GetDPS(DPHit, a.AttackSpeed) + GetDPS(DPTick, a.TickInterval); if (fCurrentDmgBiggestHit[(int)SurvivalSub.Bleed] < DPHit + (DPTick * a.NumTicks)) fCurrentDmgBiggestHit[(int)SurvivalSub.Bleed] = DPHit + (DPTick * a.NumTicks); } else { fCurrentDTPS[(int)SurvivalSub.Physical] += GetDPS(DPHit, a.AttackSpeed); if (fCurrentDmgBiggestHit[(int)SurvivalSub.Physical] < DPHit) fCurrentDmgBiggestHit[(int)SurvivalSub.Physical] = DPHit; } } else { // Magic now covering magical dots. fCurrentDTPS[(int)SurvivalSub.Magic] += GetDPS(DPHit, a.AttackSpeed) + GetDPS(DPTick, a.TickInterval); if (fCurrentDmgBiggestHit[(int)SurvivalSub.Magic] < DPHit + (DPTick * a.NumTicks)) fCurrentDmgBiggestHit[(int)SurvivalSub.Magic] = DPHit + (DPTick * a.NumTicks); } } } fTotalDTPS += fCurrentDTPS[(int)SurvivalSub.Physical]; fTotalDTPS += fCurrentDTPS[(int)SurvivalSub.Bleed]; fTotalDTPS += fCurrentDTPS[(int)SurvivalSub.Magic]; if (fTotalDTPS > 0) { fCurrentDTPSPerc[(int)SurvivalSub.Physical] = fCurrentDTPS[(int)SurvivalSub.Physical] / fTotalDTPS; fCurrentDTPSPerc[(int)SurvivalSub.Bleed] = fCurrentDTPS[(int)SurvivalSub.Bleed] / fTotalDTPS; fCurrentDTPSPerc[(int)SurvivalSub.Magic] = fCurrentDTPS[(int)SurvivalSub.Magic] / fTotalDTPS; } #endregion // Set the Fight Duration to no larger than the Berserk Timer // Question: What is the units for Berserk & Speed Timer? MS/S/M? #endregion #region ***** Survival Rating ***** // Magical damage: // if there is a max resistance, then it's likely they are stacking for that resistance. So factor in that Max resistance. float fMaxResist = Math.Max(stats.ArcaneResistance, stats.FireResistance); fMaxResist = Math.Max(fMaxResist, stats.FrostResistance); fMaxResist = Math.Max(fMaxResist, stats.NatureResistance); fMaxResist = Math.Max(fMaxResist, stats.ShadowResistance); float fMagicDR = StatConversion.GetAverageResistance(TDK.bo.Level, TDK.Char.Level, fMaxResist, 0f); calcs.MagicDamageReduction = fMagicDR; float[] SurvivalResults = new float [EnumHelper.GetCount(typeof(SurvivalSub))]; SurvivalResults = GetSurvival(ref TDK, stats, fCurrentDTPSPerc, ArmorDamageReduction, fMagicDR); calcs.ArmorDamageReduction = ArmorDamageReduction; calcs.PhysicalSurvival = SoftCapSurvival(TDK, fCurrentDmgBiggestHit[(int)SurvivalSub.Physical], SurvivalResults[(int)SurvivalSub.Physical], isBurstCalc); calcs.BleedSurvival = SoftCapSurvival(TDK, fCurrentDmgBiggestHit[(int)SurvivalSub.Bleed], SurvivalResults[(int)SurvivalSub.Bleed], isBurstCalc); calcs.MagicSurvival = SoftCapSurvival(TDK, fCurrentDmgBiggestHit[(int)SurvivalSub.Magic], SurvivalResults[(int)SurvivalSub.Magic], isBurstCalc); calcs.HitsToSurvive = TDK.calcOpts.HitsToSurvive; #endregion #region ***** Threat Rating ***** rot.TotalDamage += (int)(stats.FireDamage * (1 + stats.BonusFireDamageMultiplier) * rot.CurRotationDuration); rot.TotalThreat += (int)(stats.FireDamage * (1 + stats.BonusFireDamageMultiplier) * rot.CurRotationDuration) * 2; calcs.RotationTime = rot.CurRotationDuration; // Display the rot in secs. calcs.Threat = rot.m_TPS; calcs.DPS = rot.m_DPS; calcs.Blood = rot.m_BloodRunes; calcs.Frost = rot.m_FrostRunes; calcs.Unholy = rot.m_UnholyRunes; calcs.Death = rot.m_DeathRunes; calcs.RP = rot.m_RunicPower; calcs.TotalThreat = (int)rot.TotalThreat; calcs.ThreatWeight = TDK.calcOpts.ThreatWeight; if (needsDisplayCalcs) { TDK.calcOpts.szRotReport = rot.ReportRotation(); } #endregion #region ***** Mitigation Rating ***** float[] fCurrentDTPSNoAvoid = new float[3]; fCurrentDTPSNoAvoid = fCurrentDTPS.Clone() as float[]; float[] fCurrentMitigation = GetMitigation(ref TDK, stats, rot, (stats.CritChanceReduction / .06f), ArmorDamageReduction, fCurrentDTPS, fMagicDR); calcs.ArmorMitigation = fCurrentMitigation[(int)MitigationSub.Armor]; calcs.AvoidanceMitigation = fCurrentMitigation[(int)MitigationSub.Avoidance]; calcs.CritMitigation = fCurrentMitigation[(int)MitigationSub.Crit]; calcs.DamageTakenMitigation = fCurrentMitigation[(int)MitigationSub.DamageReduction]; calcs.DamageTakenMitigation += fCurrentMitigation[(int)MitigationSub.Haste]; calcs.HealsMitigation = fCurrentMitigation[(int)MitigationSub.Heals]; calcs.ImpedenceMitigation = fCurrentMitigation[(int)MitigationSub.Impedences]; calcs.MagicDamageReductedByAmount = fCurrentMitigation[(int)MitigationSub.AMS]; calcs.MagicDamageReductedByAmount += fCurrentMitigation[(int)MitigationSub.Magic]; calcs.Crit = (.06f - stats.CritChanceReduction); calcs.DTPS = 0; calcs.DTPSNoAvoidance = 0; foreach (float f in fCurrentDTPS) { // These are sometimes coming back as negative. // Assuming we are just 100% absorbing the attack, no damage if (f > 0) { calcs.DTPS += f; } } if (TDK.calcOpts.b_RecoveryInclAvoidance == false) { GetMitigation(ref TDK, stats, rot, (stats.CritChanceReduction / .06f), ArmorDamageReduction, fCurrentDTPSNoAvoid, fMagicDR, false); foreach (float f in fCurrentDTPSNoAvoid) { // These are sometimes coming back as negative. // Assuming we are just 100% absorbing the attack, no damage if (f > 0) { calcs.DTPSNoAvoidance += f; } } } // Have to ensure we don't divide by 0 calcs.Mitigation = StatConversion.MitigationScaler / (Math.Max(1f, calcs.DTPS) / fTotalDTPS); #endregion return calcs; }
public void AbilityDK_PlagueStrike_BP_Basic() { // Needs AP passed in Stats FFTestStats = new StatsDK(); FFTestStats.AttackPower = 100; Item i = new Item("Test", ItemQuality.Common, ItemType.Dagger, 1, "", ItemSlot.MainHand, "", false, new Stats(), new Stats(), ItemSlot.None, ItemSlot.None, ItemSlot.None, 10, 20, ItemDamageType.Physical, 2, ""); Rawr.TankDK.CalculationOptionsTankDK c = new Rawr.TankDK.CalculationOptionsTankDK(); //c.talents = new DeathKnightTalents(); //Weapon w = new Weapon(i, FFTestStats, c, 0f); CombatState combatState = new CombatState(); //combatState.m_Stats = FFTestStats; //combatState.MH = w; //combatState.m_Talents = c.talents; //AbilityDK_PlagueStrike PS = new AbilityDK_PlagueStrike(combatState); // Blood Plauge application. //AbilityDK_BloodPlague BP = new AbilityDK_BloodPlague(combatState); // A disease dealing [0 + AP * 0.055 * 1.15] Shadow damage every 3 sec . // Base damage 0 // Bonus from attack power [AP * 0.055 * 1.15] // Plague Strike Checking /* Assert.IsTrue(PS.szName == "Plague Strike", "Name"); Assert.AreEqual(PS.AbilityCost[(int)DKCostTypes.Blood], 0, "Blood Rune"); Assert.AreEqual(PS.AbilityCost[(int)DKCostTypes.Frost], 0, "Frost Rune"); Assert.AreEqual(PS.AbilityCost[(int)DKCostTypes.UnHoly], 1, "UnHoly Rune"); Assert.AreEqual(PS.AbilityCost[(int)DKCostTypes.RunicPower], -10, "RP"); Assert.AreEqual(Math.Floor((378 + PS.wMH.damage) / 2), PS.uBaseDamage, "BaseDamage"); Assert.AreEqual(Math.Floor((378 + PS.wMH.damage) / 2), PS.uBaseDamage, "Total Damage"); Assert.AreEqual(PS.uRange, AbilityDK_Base.MELEE_RANGE, "Range"); Assert.AreEqual(PS.tDamageType, ItemDamageType.Physical, "Damage Type"); Assert.AreEqual(PS.Cooldown, 1500u, "Cooldown"); // Blood Plague Checking Assert.IsTrue(BP.szName == "Blood Plague", "Name"); Assert.AreEqual(BP.AbilityCost[(int)DKCostTypes.Blood], 0, "Blood Rune"); Assert.AreEqual(BP.AbilityCost[(int)DKCostTypes.Frost], 0, "Frost Rune"); Assert.AreEqual(BP.AbilityCost[(int)DKCostTypes.UnHoly], 0, "UnHoly Rune"); Assert.AreEqual(BP.AbilityCost[(int)DKCostTypes.RunicPower], 0, "Runic Power"); Assert.AreEqual(BP.uBaseDamage, 0u, "Damage"); Assert.AreEqual(BP.tDamageType, ItemDamageType.Shadow, "Damage Type"); // Not sure if this actually needs a Cooldown. // Assert.AreEqual(BP.Cooldown, 0u, "Cooldown"); Assert.AreEqual(BP.uDuration, 15000u, "Duration"); Assert.AreEqual(BP.uTickRate, 3000u, "TickRate"); Assert.AreEqual((int)(FFTestStats.AttackPower * 0.055f * 1.15f), BP.GetTickDamage(), 0.1, "GetTickDamage"); Assert.AreEqual((int)(BP.GetTickDamage() * (15 / 3)), BP.GetTotalDamage(), 0.1, "GetTotalDamage"); * */ }
public static void AccumulatePresenceStats(StatsDK PresenceStats, Presence p, DeathKnightTalents t) { switch(p) { case Presence.Blood: { if (t.ImprovedBloodPresence > 0) { PresenceStats.CritChanceReduction += 0.03f * t.ImprovedBloodPresence; PresenceStats.BonusRuneRegeneration += .1f * t.ImprovedBloodPresence; } else if (t.ImprovedFrostPresence > 0) PresenceStats.BonusRPMultiplier += .02f * t.ImprovedFrostPresence; else if (t.ImprovedUnholyPresence == 1) PresenceStats.MovementSpeed += .08f; else if (t.ImprovedUnholyPresence == 2) PresenceStats.MovementSpeed += .15f; PresenceStats.BonusStaminaMultiplier += .08125f; if (Rawr.Properties.GeneralSettings.Default.PTRMode) PresenceStats.BaseArmorMultiplier += 0.55f; else PresenceStats.BaseArmorMultiplier += 0.3f; PresenceStats.DamageTakenReductionMultiplier = 1f - (1f - PresenceStats.DamageTakenReductionMultiplier) * (1f - 0.08f); // Threat bonus. PresenceStats.ThreatIncreaseMultiplier += 1f; break; } case Presence.Frost: { if (t.ImprovedBloodPresence > 0) PresenceStats.DamageTakenReductionMultiplier = 1f - (1f - PresenceStats.DamageTakenReductionMultiplier) * (1f - 0.02f * t.ImprovedBloodPresence); else if (t.ImprovedUnholyPresence == 1) PresenceStats.MovementSpeed += .08f; else if (t.ImprovedUnholyPresence == 2) PresenceStats.MovementSpeed += .15f; PresenceStats.BonusDamageMultiplier += 0.1f; PresenceStats.BonusRPMultiplier += 0.1f; PresenceStats.ThreatReductionMultiplier += .20f; // Wowhead has this as effect #3 break; } case Presence.Unholy: { if (t.ImprovedBloodPresence > 0) PresenceStats.DamageTakenReductionMultiplier = 1f - (1f - PresenceStats.DamageTakenReductionMultiplier) * (1f - 0.02f * t.ImprovedBloodPresence); else if (t.ImprovedFrostPresence > 0) PresenceStats.BonusRPMultiplier += .02f * t.ImprovedFrostPresence; else if (t.ImprovedUnholyPresence > 0) PresenceStats.PhysicalHaste = AddStatMultiplierStat(PresenceStats.PhysicalHaste, (.025f * t.ImprovedUnholyPresence)); PresenceStats.PhysicalHaste = AddStatMultiplierStat(PresenceStats.PhysicalHaste, .1f); //PresenceStats.BonusRuneRegeneration += .1f; PresenceStats.MovementSpeed += .15f; PresenceStats.ThreatReductionMultiplier += .20f; // Wowhead has this as effect #3 break; } } }
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); } } }
private StatsDK GetRelevantStatsLocal(Stats stats) { StatsDK s = new StatsDK(); s.Accumulate(GetRelevantStats(stats)); return s; }
private float[] GetMitigation(ref TankDKChar TDK, StatsDK stats, Rotation rot, float fPercentCritMitigation, float ArmorDamageReduction, float[] fCurrentDTPS, float fMagicDR) { return GetMitigation(ref TDK, stats, rot, fPercentCritMitigation, ArmorDamageReduction, fCurrentDTPS, fMagicDR, true); }
/// <summary> /// With Triggers already setup, just pass into the Accumulate function and return the values /// </summary> /// <param name="effect"></param> /// <returns></returns> public StatsDK getSpecialEffects(SpecialEffect effect) { StatsDK statsAverage = new StatsDK(); triggerIntervals[Trigger.Use] = effect.Cooldown; if (float.IsInfinity(effect.Cooldown)) triggerIntervals[Trigger.Use] = m_bo.BerserkTimer; effect.AccumulateAverageStats(statsAverage, triggerIntervals, triggerChances, unhastedAttackSpeed, m_bo.BerserkTimer); return statsAverage; }
/// <summary> /// GetCharacterStats is the 2nd-most calculation intensive method in a model. Here the model will /// combine all of the information about the character, including race, gear, enchants, buffs, /// calculationoptions, etc., to form a single combined Stats object. Three of the methods below /// can be called from this method to help total up stats: GetItemStats(character, additionalItem), /// GetEnchantsStats(character), and GetBuffsStats(character.ActiveBuffs). /// </summary> /// <param name="character">The character whose stats should be totaled.</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 Stats object containing the final totaled values of all character stats.</returns> public override Stats GetCharacterStats(Character character, Item additionalItem) { StatsDK statsTotal = new StatsDK(); if (null == character) { #if DEBUG throw new Exception("Character is Null"); #else return statsTotal; #endif } CalculationOptionsDPSDK calcOpts = character.CalculationOptions as CalculationOptionsDPSDK; if (null == calcOpts) { calcOpts = new CalculationOptionsDPSDK(); } DeathKnightTalents talents = character.DeathKnightTalents; if (null == talents) { return statsTotal; } statsTotal.Accumulate(GetRaceStats(character)); AccumulateItemStats(statsTotal, character, additionalItem); statsTotal = GetRelevantStats(statsTotal) as StatsDK; // GetRel removes any stats specific to the StatsDK object. statsTotal.bDW = false; if (character.MainHand != null && character.OffHand != null) statsTotal.bDW = true; RemoveDuplicateRunes(statsTotal, character, statsTotal.bDW); AccumulateBuffsStats(statsTotal, character.ActiveBuffs); #region Tank #region T11 int tierCount; if (character.SetBonusCount.TryGetValue("Magma Plated Battlearmor", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T11_Tank = true; } if (tierCount >= 4) { statsTotal.b4T11_Tank = true; } } if (statsTotal.b4T11_Tank) statsTotal.AddSpecialEffect(_SE_IBF[1]); else statsTotal.AddSpecialEffect(_SE_IBF[0]); #endregion #region T12 if (character.SetBonusCount.TryGetValue("Elementium Deathplate Battlearmor", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T12_Tank = true; } if (tierCount >= 4) { statsTotal.b4T12_Tank = true; } } if (statsTotal.b2T12_Tank) { // Your melee attacks cause Burning Blood on your target, // which deals 800 Fire damage every 2 for 6 sec and // causes your abilities to behave as if you had 2 diseases // present on the target. // Implemented in CombatState DiseaseCount statsTotal.FireDamage = 800 / 2; } if (statsTotal.b4T12_Tank) { // Your Dancing Rune Weapon grants 15% additional parry chance. // Implemented in DRW talent Static Special Effect. } #endregion #region T13 if (character.SetBonusCount.TryGetValue("Necrotic Boneplate Armor", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T13_Tank = true; } if (tierCount >= 4) { statsTotal.b4T13_Tank = true; } } if (statsTotal.b2T13_Tank) { // When an attack drops your health below 35%, one of your Blood Runes // will immediately activate and convert into a Death Rune for the next // 20 sec. This effect cannot occur more than once every 45 sec. } if (statsTotal.b4T13_Tank) { // Your Vampiric Blood ability also affects all party and raid members // for 50% of the effect it has on you. } #endregion #endregion #region DPS #region T11 if (character.SetBonusCount.TryGetValue("Magma Plated Battlegear", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T11_DPS = true; if (tierCount >= 4) { statsTotal.b4T11_DPS = true; } } if (statsTotal.b2T11_DPS) { // increase the crit chance of your DeathCoil & FS by 5% statsTotal.BonusCritChanceDeathCoil += .05f; statsTotal.BonusCritChanceFrostStrike += .05f; } if (statsTotal.b4T11_DPS) { // Each time you gain a Death Rune or trigger your Killing Machine talent, // you also gain 1% increased attack power for 30 sec. Stacks up to 3 times. statsTotal.AddSpecialEffect(new SpecialEffect(Trigger.DeathRuneGained, new Stats() { BonusAttackPowerMultiplier = 0.01f, }, 30, 0, 1f, 3)); statsTotal.AddSpecialEffect(new SpecialEffect(Trigger.KillingMachine, new Stats() { BonusAttackPowerMultiplier = 0.01f, }, 30, 0, 1f, 3)); } } #endregion #region T12 if (character.SetBonusCount.TryGetValue("Elementium Deathplate Battlegear", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T12_DPS = true; if (tierCount >= 4) { statsTotal.b4T12_DPS = true; } } } if (statsTotal.b2T12_DPS) { // Horn of Winter also grats 3 RPp5 statsTotal.RPp5 += 3; } if (statsTotal.b4T12_DPS) { // Your Obliterate and Scourge Strike abilities instantly deal 6% additional damage as Fire damage. // Implemented in Oblit and SS classes. } #endregion #region T13 if (character.SetBonusCount.TryGetValue("Necrotic Boneplate Battlegear", out tierCount)) { if (tierCount >= 2) { statsTotal.b2T13_DPS = true; } if (tierCount >= 4) { statsTotal.b4T13_DPS = true; } } if (statsTotal.b2T13_DPS) { // Sudden Doom has a 30% chance and Rime has a 60% chance // to grant 2 charges when triggered instead of 1. } if (statsTotal.b4T13_DPS) { // Runic Empowerment has a 25% chance and Runic Corruption // has a 40% chance to also grant 710 mastery rating for 12 sec when activated. } #endregion #endregion AccumulateTalents(statsTotal, character); AccumulatePresenceStats(statsTotal, calcOpts.presence, talents); return (statsTotal); }
/// <summary>Build the talent effects.</summary> public static void AccumulateTalents(StatsDK FullCharacterStats, Character character) { Stats newStats = new Stats(); FullCharacterStats.Mastery += StatConversion.GetMasteryFromRating(FullCharacterStats.MasteryRating); // Runic Focus: FullCharacterStats.SpellHit += 0.09f; // Which talent tree focus? #region Talent Speciality Rotation.Type r = GetSpec(character.DeathKnightTalents); switch (r) { case Rotation.Type.Blood: { // Special abilities for being blood // Heart Strike // Veteran of the third war // Stamina +9% FullCharacterStats.BonusStaminaMultiplier += .09f; // Expertise +6 FullCharacterStats.Expertise += 6; // Blood Rites // Whenever you hit with Death Strike or Obliterate, the Frost and Unholy Runes // will become Death Runes when they activate. Death Runes count as a Blood, Frost or Unholy Rune. // Vengence // Each time you take damage, you gain 5% of the damage taken as attack power, up to a maximum of 10% of your health. // Mastery: Blood Shield // Each Time you heal yourself w/ DS you gain a shield worth 50% of the amount healed // Each Point of Mastery increases the shield by 6.25% break; } case Rotation.Type.Frost: { // Special abilities for being Frost // Frost Strike // Icy Talons // Melee Attack speed +20% FullCharacterStats.PhysicalHaste = AddStatMultiplierStat(FullCharacterStats.PhysicalHaste, .2f); FullCharacterStats.BonusRuneRegeneration -= .2f; // Only this haste doesn't affect Rune Regen. // Blood of the North // Blood runes are death runes. // Mastery: Frozen Heart // Increases all frost damage by 16%. // Each point of mastery increases frost damage by an additional 2.0% FullCharacterStats.BonusFrostDamageMultiplier += .16f + (.02f * FullCharacterStats.Mastery); break; } case Rotation.Type.Unholy: { // Special abilities for being Unholy // Scourge Strike // Master of Ghouls // Reduces the CD on Raise dead by 60 sec. // The ghoul summoned is considered your pet w/o a limited duration. // Reaping // Whenever you hit with Blood strike, pestilence, or Festering strike, the runes spent will // become death runes when they activate. // Unholy Might // Str +25% FullCharacterStats.BonusStrengthMultiplier += .25f; // Mastery: Dreadblade. // Increases shadow damage by 20% + // Each point of mastery increases shadow damage by an additional 2.5% FullCharacterStats.BonusShadowDamageMultiplier += .2f + (.025f * FullCharacterStats.Mastery); break; } } #endregion #region Blood Talents // Butchery // 1RPp5 per Point if (character.DeathKnightTalents.Butchery > 0) { FullCharacterStats.RPp5 += 1 * character.DeathKnightTalents.Butchery; } // Blade Barrier // Reduce damage by 2% per point for 10 sec. if (character.DeathKnightTalents.BladeBarrier > 0) { // If you don't have your Blood Runes on CD, you're doing it wrong. FullCharacterStats.DamageTakenReductionMultiplier = 1f - (1f - FullCharacterStats.DamageTakenReductionMultiplier) * (1f - .02f * character.DeathKnightTalents.BladeBarrier); } // Bladed Armor // 2 AP per point per 180 Armor if (character.DeathKnightTalents.BladedArmor > 0) { // If you don't have your Blood Runes on CD, you're doing it wrong. FullCharacterStats.AttackPower += (2 * character.DeathKnightTalents.BladedArmor) * (FullCharacterStats.Armor / 180); } // Improved Blood Tap // Reduces Blood tap CD by 15 sec * pts. // Scent of Blood // 15% after Dodge, Parry or damage received causing 1 melee hit per point to generate 10 runic power. // Implemented in WhiteSwing. // Scarlet Fever // Those hit by BB do have a 50/100% chance to reduce damage dealt by 10% for 30 sec. if (character.DeathKnightTalents.ScarletFever > 0) { // Like Blade Barrier, this should always be up. // Be sure to put this in the rotation solver for Tanking Rotations. // JOTHAY TODO: Isn't this conflicting with the Buffs pane? FullCharacterStats.DamageTakenReductionMultiplier = 1f - (1f - FullCharacterStats.DamageTakenReductionMultiplier) * (1f - .05f * character.DeathKnightTalents.ScarletFever); } // Hand of Doom // Reduces the CD for Strangulate by 30/60 sec. if (r == Rotation.Type.Blood) { // Blood-Caked Blade // 10% chance per point to cause Blood-Caked strike // Implmented in WhiteDamage ability file. // Bone Shield // 6 Bones // Takes 20% less damage from all sources // Does 2% more damage to target // Each damaging attack consumes a bone. // Lasts 5 mins // Toughness // Increases Armor Value from items by 3% per point. if (character.DeathKnightTalents.Toughness > 0) { FullCharacterStats.BaseArmorMultiplier = AddStatMultiplierStat(FullCharacterStats.BaseArmorMultiplier, (.03f * character.DeathKnightTalents.Toughness)); } // Abominations Might // increase AP by 5%/10% of raid. // 1% per point increase to str. if (character.DeathKnightTalents.AbominationsMight > 0) { // This happens no matter what: FullCharacterStats.BonusStrengthMultiplier += (0.01f * character.DeathKnightTalents.AbominationsMight); // This happens only if there isn't Trueshot Aura/Unleashed Rage/Abom's might available: if (!(character.ActiveBuffsContains("Trueshot Aura") || character.ActiveBuffsContains("Unleashed Rage") || character.ActiveBuffsContains("Abomination's Might"))) { FullCharacterStats.BonusAttackPowerMultiplier += (.05f * character.DeathKnightTalents.AbominationsMight); } } // Sanguine Fortitude // Buff's IBF: // While Active, your IBF reduces Dam taken by 15/30% and costs 50/100% less RP to activate. // CD duration? 3min suggested on pwnwear. // Cost? This is a CD stacker. // TODO: look into CD stacking code./max v average values. // Blood Parasite // Melee Attacks have 5% * PTS chance of spawning a blood worm. // Blood worm attacks your enemies, gorging itself on blood // until it bursts to heal nearby allies. Lasts 20 sec. if (character.DeathKnightTalents.BloodParasite > 0) { float fDamageDone = 200f; // TODO: Put this in a way to increase DPS. float fBWAttackSpeed = 1.4f; // TODO: Validate this speed. float fBWDuration = 20f; float avgstacks = 5; // TODO: Figure out the best way to determine avg Stacks of BloodGorged float WormHealth = (FullCharacterStats.Health * 0.35f); _SE_Bloodworms = new SpecialEffect[] { null, new SpecialEffect(Trigger.MeleeAttack, new Stats() { Healed = ((avgstacks * WormHealth * .05f) / fBWDuration), PhysicalDamage = (fDamageDone/fBWAttackSpeed) }, fBWDuration, 0, .05f * 1), new SpecialEffect(Trigger.MeleeAttack, new Stats() { Healed = ((avgstacks * WormHealth * .05f) / fBWDuration), PhysicalDamage = (fDamageDone/fBWAttackSpeed) }, fBWDuration, 0, .05f * 2), }; FullCharacterStats.AddSpecialEffect(_SE_Bloodworms[character.DeathKnightTalents.BloodParasite]); } // Improved Blood Presence // Reduces chance to be critically hit while in blood presence by 3/6% // In addition while in Frost or Unholy, retain the 2/4% Dam reduction. // Implemented in AccumulatePresenceStats() // Will of the Necropolis // Dam that takes you below 35% health or while at less than 35% is reduced by 5% per point. if (character.DeathKnightTalents.WillOfTheNecropolis > 0) { // Need to factor in the damage taken aspect of the trigger. // Using the assumption that the tank will be at < 35% health about that % of the time. FullCharacterStats.AddSpecialEffect(_SE_WillOfTheNecropolis[character.DeathKnightTalents.WillOfTheNecropolis]); } // Rune Tap // Convert 1 BR to 10% health. if (character.DeathKnightTalents.RuneTap > 0) { FullCharacterStats.AddSpecialEffect(_SE_RuneTap); } // Vampiric Blood // temp 15% of max health and // increases health generated by 25% for 10 sec. // 1 min CD. if (character.DeathKnightTalents.VampiricBlood > 0) { FullCharacterStats.AddSpecialEffect(_SE_VampiricBlood[character.DeathKnightTalents.GlyphofVampiricBlood ? 1 : 0]); } // Improved Death Strike if (character.DeathKnightTalents.ImprovedDeathStrike > 0) { FullCharacterStats.BonusDamageDeathStrike += (.40f * character.DeathKnightTalents.ImprovedDeathStrike); // Also improves DS Healing. Implemented in TankDK heals section. } // Crimson Scourge // Increases the damage dealt by your Blood Boil by 20/40%, and when you Plague Strike a target that is already // infected with your Blood Plague, there is a 50/100% chance that your next Blood Boil will consume no runes. if (character.DeathKnightTalents.CrimsonScourge > 0) { // Part 1 implmented in BB Ability // TODO: Part 2 implement in rotation. } // Dancing Rune Weapon if (character.DeathKnightTalents.DancingRuneWeapon > 0) { uint u4T12 = FullCharacterStats.b4T12_Tank ? 1u : 0u; uint uGDRW = character.DeathKnightTalents.GlyphofDancingRuneWeapon ? 1u : 0u; FullCharacterStats.AddSpecialEffect(_SE_DRW[u4T12][uGDRW]); } } #endregion #region Frost Talents // Runic Power Mastery // Increases Max RP by 10 per point if (character.DeathKnightTalents.RunicPowerMastery > 0) { FullCharacterStats.BonusMaxRunicPower += 10 * character.DeathKnightTalents.RunicPowerMastery; } // Icy Reach // Increases range of IT & CoI and HB by 5 yards per point. // Nerves of Cold Steel // Increase hit w/ 1H weapons by 1% per point // Increase damage done by off hand weapons by 8/16/25% per point // Implement off-hand weapon buff in combat shot roation if (character.MainHand != null && (character.MainHand.Slot == ItemSlot.MainHand || character.MainHand.Slot == ItemSlot.OneHand)) { FullCharacterStats.PhysicalHit += (character.DeathKnightTalents.NervesOfColdSteel * .01f); } // Lichborne // for 10 sec, immune to charm, fear, sleep // CD 2 Mins // On a Pale Horse // Reduce duration of Movement slowing effects by 15% per point // increase mount speed by 10% per point if (character.DeathKnightTalents.OnAPaleHorse > 0) { // Now w/ boss handler, reduce the duration of the movement slowing effects. } // Endless Winter // Mind Freeze RP cost is reduced by 50% per point. if (character.DeathKnightTalents.EndlessWinter > 0) { } if (r == Rotation.Type.Frost) { // Merciless Combat // addtional 6% per point damage for IT, HB, Oblit, and FS // on targets of less than 35% health. if (character.DeathKnightTalents.MercilessCombat > 0) { // implemented in the abilities section (increase the damage done by 15% * 35%) } // Chill of the Grave // CoI, HB, IT and Oblit generate 5 RP per point. if (character.DeathKnightTalents.ChillOfTheGrave > 0) { // Implemented in the abilities. } // Killing Machine // Melee attacks have a chance to make OB, or FS a crit. // increased proc per point. // Research Suggests 5 PPM at 3/3 if (character.DeathKnightTalents.KillingMachine > 0) { // FullCharacterStats.AddSpecialEffect(_KM[character.DeathKnightTalents.KillingMachine]); // Implemented in Frost Rotation } // Rime // Oblit has a 15% per point your next IT or HB consumes no runes if (character.DeathKnightTalents.Rime > 0) { // Implemented in FrostRotation. } // Pillar of Frost // 1 min CD, 20 sec dur // Str +20% if (character.DeathKnightTalents.PillarOfFrost > 0) { FullCharacterStats.AddSpecialEffect(_SE_PillarOfFrost); } // Improved Icy Talons // increases the melee haste of the group/raid by 10% // increases your haste by 5% all the time. if (character.DeathKnightTalents.ImprovedIcyTalons > 0) { FullCharacterStats.PhysicalHaste = AddStatMultiplierStat(FullCharacterStats.PhysicalHaste, 0.05f); if (!character.ActiveBuffsContains("Improved Icy Talons") && !character.ActiveBuffsContains("Windfury Totem")) { FullCharacterStats.PhysicalHaste = AddStatMultiplierStat(FullCharacterStats.PhysicalHaste, .1f); FullCharacterStats.RangedHaste += .1f; } } // Brittle Bones: // Str +2% per point // FF chills the bones of its victims increasing damage taken by 2% per point. if (character.DeathKnightTalents.BrittleBones > 0) { FullCharacterStats.BonusStrengthMultiplier += .02f * character.DeathKnightTalents.BrittleBones; FullCharacterStats.BonusDamageMultiplier += .02f * character.DeathKnightTalents.BrittleBones; } // Chilblains // FF victimes are movement reduced 25% per point // Hungering Cold // Spell that freezes all enemies w/ 10 yards. // Improved Frost Presence // Increases your bonus damage while in Frost Presence by an additional 2% per point. // In addition, while in Blood Presence or Unholy Presence, you retain 2% per point increased runic power generation from Frost Presence. if (character.DeathKnightTalents.ImprovedFrostPresence > 0) { FullCharacterStats.BonusDamageMultiplier += (0.02f * character.DeathKnightTalents.ImprovedFrostPresence); } // Threat of Thassarian: // When dual-wielding, your Death Strikes, Obliterates, Plague Strikes, // Blood Strikes and Frost Strikes and Rune Strike (as of 3.2.2) have a 30/60/100% chance // to also deal damage with your off-hand weapon. if (character.DeathKnightTalents.ThreatOfThassarian > 0) { // implemented in the abilities } // Might of the Frozen Wastes // When wielding a two-handed weapon, your autoattacks have a 15% chance to generate 10 Runic Power. if (character.DeathKnightTalents.MightOfTheFrozenWastes > 0) { // WhiteDamage Bonus in WhiteDamage Ability. if (character.MainHand != null && character.MainHand.Slot == ItemSlot.TwoHand) FullCharacterStats.BonusPhysicalDamageMultiplier += (.1f/3) * character.DeathKnightTalents.MightOfTheFrozenWastes; } // Howling Blast. } #endregion #region UnHoly Talents // Unholy Command // reduces CD of DG by 5 sec per point // Virulence // if (character.DeathKnightTalents.Virulence > 0) { FullCharacterStats.BonusDiseaseDamageMultiplier += (.1f * character.DeathKnightTalents.Virulence); } // Epidemic // Increases Duration of BP and FF by 4 sec per point // Implemented in the abilities page. // Desecration // PS and SS cause Desecrated Ground effect. // Targets are slowed by 25% per point // Not Implemented. // Resilient Infection // When your diseases are dispelled you have a 50/100% to activate a // Frost rune if FF was dispelled // Unholy rune if BP was dispelled // Not Implemented // Morbidity // increases dam & healing of DC by 5% per point // increases dam of DnD by 10% sec per point if (character.DeathKnightTalents.Morbidity > 0) { // implemented in abilities. } if (r == Rotation.Type.Unholy) { // Runic Corruption // Reduces the cost of your Death Coil by 3 per point, and causes // your Runic Empowerment ability to no longer refresh a depleted // rune, but instead to increase your rune regeneration rate by 50/100% for 3 sec. if (character.DeathKnightTalents.RunicCorruption > 0) { // Implmented in rotation. } // Unholy Frenzy // Induces a friendly unit into a killing frenzy for 30 sec. // The target is Enraged, which increases their melee and ranged haste by 20%, // but causes them to lose health equal to 2% of their maximum health every 3 sec. // TODO: // Contagion // Increases the damage of your diseases spread via Pestilence by 50/100%. if (character.DeathKnightTalents.Contagion > 0) FullCharacterStats.BonusDiseaseDamageMultiplier += .5f * character.DeathKnightTalents.Contagion; // Shadow Infusion // When you cast Death Coil, you have a 33% per point chance to empower your active Ghoul, // increasing its damage dealt by 10% for 30 sec. Stacks up to 5 times. // TODO: Implement in Rotation & Ghoul // Magic Suppression // AMS absorbs additional 8, 16, 25% of spell damage. // Rage of Rivendare // Increases the damage of your Plague Strike, Scourge Strike, and Festering Strike abilities by 15% per point. if (character.DeathKnightTalents.RageOfRivendare > 0) { // Implemented in the abilities. } // Unholy Blight // Causes the victims of your Death Coil to be surrounded by a vile swarm of unholy insects, // taking 10% of the damage done by the Death Coil over 10 sec, and preventing any diseases on the victim from being dispelled. // Implemented in the DeathCoil ability. // AntiMagic Zone // Creates a zone where party/raid members take 75% less spell damage // Lasts 10 secs or X damage. if (character.DeathKnightTalents.AntiMagicZone > 0) { FullCharacterStats.AddSpecialEffect(_SE_AntiMagicZone); } // Improved Unholy Presence // Grants you an additional 2% haste while in Unholy Presence. // In addition, while in Blood Presence or Frost Presence, you retain 8% increased movement speed from Unholy Presence. // Implemented in Presence Stats. // Dark Transformation // Consume 5 charges of Shadow Infusion on your Ghoul to transform it into a powerful // undead monstrosity for 30 sec. The Ghoul's abilities are empowered and take on new // functions while the transformation is active. // TODO: implement in Ghoul // Ebon Plaguebringer // Your Plague Strike, Icy Touch, Chains of Ice, and Outbreak abilities also infect // their target with Ebon Plague, which increases damage taken from your diseases // by 15/30% and all magic damage taken by an additional 8%. if (character.DeathKnightTalents.EbonPlaguebringer > 0) { if (!character.ActiveBuffsContains("Earth and Moon") && !character.ActiveBuffsContains("Curse of the Elements") && !character.ActiveBuffsContains("Ebon Plaguebringer")) { float fBonus = .08f; FullCharacterStats.BonusArcaneDamageMultiplier += fBonus; FullCharacterStats.BonusFireDamageMultiplier += fBonus; FullCharacterStats.BonusFrostDamageMultiplier += fBonus; FullCharacterStats.BonusHolyDamageMultiplier += fBonus; FullCharacterStats.BonusNatureDamageMultiplier += fBonus; FullCharacterStats.BonusShadowDamageMultiplier += fBonus; } FullCharacterStats.BonusDiseaseDamageMultiplier += (.15f * character.DeathKnightTalents.EbonPlaguebringer); } // Sudden Doom // Your auto attacks have a 5% per point chance to make your next Death Coil cost no runic power. // TODO: To Implment in DeathCoil // Summon Gargoyle } #endregion }
/// <summary> /// Process All the ratings score to their base values. /// </summary> /// <param name="s"></param> private void ProcessRatings(StatsDK statsTotal) { // Expertise Rating -> Expertise: statsTotal.Expertise += StatConversion.GetExpertiseFromRating(statsTotal.ExpertiseRating); // Mastery Rating Handled during AccumulateTalents. // statsTotal.Mastery += StatConversion.GetMasteryFromRating(statsTotal.MasteryRating); statsTotal.PhysicalHit += StatConversion.GetPhysicalHitFromRating(statsTotal.HitRating); statsTotal.PhysicalCrit += StatConversion.GetPhysicalCritFromRating(statsTotal.CritRating); statsTotal.PhysicalCrit += StatConversion.GetPhysicalCritFromAgility(statsTotal.Agility, CharacterClass.DeathKnight); statsTotal.PhysicalHaste += StatConversion.GetPhysicalHasteFromRating(statsTotal.HasteRating, CharacterClass.DeathKnight); statsTotal.SpellHit += StatConversion.GetSpellHitFromRating(statsTotal.HitRating); statsTotal.SpellCrit += StatConversion.GetSpellCritFromRating(statsTotal.CritRating); statsTotal.SpellCrit += StatConversion.GetSpellCritFromIntellect(statsTotal.Intellect); statsTotal.SpellCrit += statsTotal.SpellCritOnTarget; statsTotal.SpellHaste += StatConversion.GetSpellHasteFromRating(statsTotal.HasteRating, CharacterClass.DeathKnight); }
/// <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; }
private void ProcessAvoidance(StatsDK statsTotal, int iTargetLevel, Character c, StatsDK PreRatingsBase) { // Key point to get avoidance bases. // Ratings with change with multiple passes, // but the actual values need to return back to the base. statsTotal.Miss = PreRatingsBase.Miss; statsTotal.Dodge = PreRatingsBase.Dodge; statsTotal.Parry = PreRatingsBase.Parry; // Get all the character avoidance numbers including deminishing returns. // Iterate through each hit type. and use fAvoidance array w/ the hitresult enum. float[] fAvoidance = new float[HitResultCount]; for (uint i = 0; i < HitResultCount; i++) { // GetDRAvoidanceChance returns a dec. percentage. // Since CurrentAvoidance is a percent, need to multiply by 100. fAvoidance[i] = (StatConversion.GetDRAvoidanceChance(c, statsTotal, (HitResult)i, iTargetLevel)); } // So let's populate the miss, dodge and parry values for the UI display as well as pulling them out of the avoidance number. // Cap and floor is already factored in as part of the GetDRAvoidanceChance. statsTotal.Miss = fAvoidance[(int)HitResult.Miss]; statsTotal.Dodge = fAvoidance[(int)HitResult.Dodge]; statsTotal.Parry = fAvoidance[(int)HitResult.Parry]; }
private float[] GetSurvival(ref TankDKChar TDK, StatsDK stats, float[] DamagePercentages, float ArmorDamageReduction, float fMagicDR) { // For right now Survival Rating == Effective Health will be HP + Armor/Resistance mitigation values. // Everything else is really mitigating damage based on RNG. // The health bonus from Frost presence is now include in the character by default. float fPhysicalSurvival = stats.Health; float fBleedSurvival = stats.Health; float fMagicalSurvival = stats.Health; // Physical damage: fPhysicalSurvival = GetEffectiveHealth(stats.Health, ArmorDamageReduction, DamagePercentages[(int)SurvivalSub.Physical]); // Bleed damage: fBleedSurvival = GetEffectiveHealth(stats.Health, 0, DamagePercentages[(int)SurvivalSub.Bleed]); // Magic Dam: fMagicalSurvival = GetEffectiveHealth(stats.Health, fMagicDR, DamagePercentages[(int)SurvivalSub.Magic]); // Since Armor plays a role in Survival, so shall the other damage taken adjusters. // Note, it's expected that (at least for tanks) that DamageTakenMultiplier will be Negative. // JOTHAY NOTE: The above is no longer true. DamageTakenReductionMultiplier will now be positive, but must be handled multiplicatively and inversely // JOTHAY NOTE: YOUR FORMULAS HAVE BEEN UPDATED TO REFLECT THIS CHANGE // So the next line should INCREASE survival because // fPhysicalSurvival * (1 - [some negative value] * (1 - [0 or some negative value]) // will look like: // fPhysicalSurvival * 1.xx * 1.xx fPhysicalSurvival = fPhysicalSurvival / ((1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier)); fBleedSurvival = fBleedSurvival / ((1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier)); fMagicalSurvival = fMagicalSurvival / ((1f - stats.DamageTakenReductionMultiplier) * (1f - stats.SpellDamageTakenReductionMultiplier )); float[] SurvivalResults = new float[EnumHelper.GetCount(typeof(SurvivalSub))]; SurvivalResults[(int)SurvivalSub.Physical] = fPhysicalSurvival; SurvivalResults[(int)SurvivalSub.Bleed] = fBleedSurvival; SurvivalResults[(int)SurvivalSub.Magic] = fMagicalSurvival; return SurvivalResults; }
private void ApplyRatings(StatsDK statsTotal) { // Apply ratings. statsTotal.Expertise += (float)StatConversion.GetExpertiseFromRating(statsTotal.ExpertiseRating); statsTotal.Strength += statsTotal.HighestStat + statsTotal.Paragon; statsTotal.Agility = (float)Math.Floor(statsTotal.Agility * (1 + statsTotal.BonusAgilityMultiplier)); statsTotal.Strength = (float)Math.Floor(statsTotal.Strength * (1 + statsTotal.BonusStrengthMultiplier)); statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina * (1 + statsTotal.BonusStaminaMultiplier)); statsTotal.Health += StatConversion.GetHealthFromStamina(statsTotal.Stamina); statsTotal.Health *= 1 + statsTotal.BonusHealthMultiplier; statsTotal.AttackPower = (float)Math.Floor(statsTotal.AttackPower + statsTotal.Strength * 2); statsTotal.Armor = (float)Math.Floor(StatConversion.ApplyMultiplier(statsTotal.Armor, statsTotal.BaseArmorMultiplier) + StatConversion.ApplyMultiplier(statsTotal.BonusArmor, statsTotal.BonusArmorMultiplier)); statsTotal.AttackPower *= 1f + statsTotal.BonusAttackPowerMultiplier; float HighestSecondaryStatValue = statsTotal.HighestSecondaryStat; // how much HighestSecondaryStat to add statsTotal.HighestSecondaryStat = 0f; // remove HighestSecondaryStat stat, since it's not needed if (statsTotal.CritRating > statsTotal.HasteRating && statsTotal.CritRating > statsTotal.MasteryRating) { statsTotal.CritRating += HighestSecondaryStatValue; } else if (statsTotal.HasteRating > statsTotal.CritRating && statsTotal.HasteRating > statsTotal.MasteryRating) { statsTotal.HasteRating += HighestSecondaryStatValue; } else /*if (statsTotal.MasteryRating > statsTotal.CritRating && statsTotal.MasteryRating > statsTotal.HasteRating)*/ { statsTotal.MasteryRating += HighestSecondaryStatValue; } statsTotal.PhysicalHit += StatConversion.GetPhysicalHitFromRating(statsTotal.HitRating); statsTotal.PhysicalCrit += StatConversion.GetPhysicalCritFromRating(statsTotal.CritRating); statsTotal.PhysicalCrit += StatConversion.GetPhysicalCritFromAgility(statsTotal.Agility, CharacterClass.DeathKnight); statsTotal.PhysicalHaste = AddStatMultiplierStat(statsTotal.PhysicalHaste, StatConversion.GetPhysicalHasteFromRating(statsTotal.HasteRating, CharacterClass.DeathKnight)); statsTotal.SpellHit += StatConversion.GetSpellHitFromRating(statsTotal.HitRating); statsTotal.SpellCrit += StatConversion.GetSpellCritFromRating(statsTotal.CritRating); statsTotal.SpellCrit += StatConversion.GetSpellCritFromIntellect(statsTotal.Intellect); statsTotal.SpellCrit += statsTotal.SpellCritOnTarget; statsTotal.SpellHaste += StatConversion.GetSpellHasteFromRating(statsTotal.HasteRating, CharacterClass.DeathKnight); }
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 override Stats GetRelevantStats(Stats stats) { StatsDK s = new StatsDK() { // Core stats Strength = stats.Strength, Agility = stats.Agility, Stamina = stats.Stamina, ExpertiseRating = stats.ExpertiseRating, AttackPower = stats.AttackPower, MasteryRating = stats.MasteryRating, // Other Base Stats HasteRating = stats.HasteRating, HitRating = stats.HitRating, CritRating = stats.CritRating, Armor = stats.Armor, BonusArmor = stats.BonusArmor, Resilience = stats.Resilience, // Secondary Stats Health = stats.Health, SpellHaste = stats.SpellHaste, PhysicalCrit = stats.PhysicalCrit, PhysicalHaste = stats.PhysicalHaste, PhysicalHit = stats.PhysicalHit, SpellCrit = stats.SpellCrit, SpellCritOnTarget = stats.SpellCritOnTarget, SpellHit = stats.SpellHit, SpellPenetration = stats.SpellPenetration, // Dam stats WeaponDamage = stats.WeaponDamage, PhysicalDamage = stats.PhysicalDamage, ShadowDamage = stats.ShadowDamage, ArcaneDamage = stats.ArcaneDamage, FireDamage = stats.FireDamage, FrostDamage = stats.FrostDamage, HolyDamage = stats.HolyDamage, NatureDamage = stats.NatureDamage, // Bonus to stat BonusHealthMultiplier = stats.BonusHealthMultiplier, BonusStrengthMultiplier = stats.BonusStrengthMultiplier, BonusStaminaMultiplier = stats.BonusStaminaMultiplier, BonusAgilityMultiplier = stats.BonusAgilityMultiplier, BonusCritDamageMultiplier = stats.BonusCritDamageMultiplier, BonusAttackPowerMultiplier = stats.BonusAttackPowerMultiplier, // Bonus to Dam // *Dam BonusWhiteDamageMultiplier = stats.BonusWhiteDamageMultiplier, BonusDamageMultiplier = stats.BonusDamageMultiplier, BonusPhysicalDamageMultiplier = stats.BonusPhysicalDamageMultiplier, BonusShadowDamageMultiplier = stats.BonusShadowDamageMultiplier, BonusFrostDamageMultiplier = stats.BonusFrostDamageMultiplier, BonusDiseaseDamageMultiplier = stats.BonusDiseaseDamageMultiplier, // +Dam BonusFrostWeaponDamage = stats.BonusFrostWeaponDamage, BonusDamageScourgeStrike = stats.BonusDamageScourgeStrike, BonusDamageBloodStrike = stats.BonusDamageBloodStrike, BonusDamageDeathCoil = stats.BonusDamageDeathCoil, BonusDamageDeathStrike = stats.BonusDamageDeathStrike, BonusDamageFrostStrike = stats.BonusDamageFrostStrike, BonusDamageHeartStrike = stats.BonusDamageHeartStrike, BonusDamageIcyTouch = stats.BonusDamageIcyTouch, BonusDamageObliterate = stats.BonusDamageObliterate, // Crit BonusCritChanceDeathCoil = stats.BonusCritChanceDeathCoil, BonusCritChanceFrostStrike = stats.BonusCritChanceFrostStrike, BonusCritChanceObliterate = stats.BonusCritChanceObliterate, // Other CinderglacierProc = stats.CinderglacierProc, HighestStat = stats.HighestStat, HighestSecondaryStat = stats.HighestSecondaryStat, Paragon = stats.Paragon, ThreatIncreaseMultiplier = stats.ThreatIncreaseMultiplier, ThreatReductionMultiplier = stats.ThreatReductionMultiplier, TargetArmorReduction = stats.TargetArmorReduction, // BossHandler SnareRootDurReduc = stats.SnareRootDurReduc, FearDurReduc = stats.FearDurReduc, StunDurReduc = stats.StunDurReduc, MovementSpeed = stats.MovementSpeed, }; foreach (SpecialEffect effect in stats.SpecialEffects()) { if (HasRelevantStats(effect.Stats)) { if (effect.Trigger == Trigger.DamageDone || effect.Trigger == Trigger.DamageOrHealingDone || effect.Trigger == Trigger.DamageSpellCast || effect.Trigger == Trigger.DamageSpellCrit || effect.Trigger == Trigger.DamageSpellHit || effect.Trigger == Trigger.SpellCast || effect.Trigger == Trigger.SpellCrit || effect.Trigger == Trigger.SpellHit || effect.Trigger == Trigger.DoTTick || effect.Trigger == Trigger.MeleeCrit || effect.Trigger == Trigger.MeleeHit || effect.Trigger == Trigger.CurrentHandHit || effect.Trigger == Trigger.MainHandHit || effect.Trigger == Trigger.OffHandHit || effect.Trigger == Trigger.PhysicalCrit || effect.Trigger == Trigger.PhysicalHit || effect.Trigger == Trigger.PhysicalAttack || effect.Trigger == Trigger.BloodStrikeHit || effect.Trigger == Trigger.HeartStrikeHit || effect.Trigger == Trigger.ScourgeStrikeHit || effect.Trigger == Trigger.ObliterateHit || effect.Trigger == Trigger.DeathStrikeHit || effect.Trigger == Trigger.IcyTouchHit || effect.Trigger == Trigger.PlagueStrikeHit || effect.Trigger == Trigger.RuneStrikeHit || effect.Trigger == Trigger.DeathRuneGained || effect.Trigger == Trigger.Use) { s.AddSpecialEffect(effect); } } } return s; }