public void SpellMiss_PVE_SameLevel() { float expected = 0.04f; var levelsBelowTarget = 0; var miss = StatConversion.GetSpellMiss(levelsBelowTarget, false); Assert.AreEqual(expected, miss, TOLERANCE); }
public void SpellMiss_PVE_BelowTargetBy3() { float expected = 0.17f; int levelsBelowTarget = 3; var miss = StatConversion.GetSpellMiss(-levelsBelowTarget, false); Assert.AreEqual(expected, miss, TOLERANCE); }
public void SpellMiss_PVE_AboveTargetBy1() { float expected = 0.03f; var levelsAboveTarget = 1; var miss = StatConversion.GetSpellMiss(levelsAboveTarget, false); Assert.AreEqual(expected, miss, TOLERANCE); }
protected override void Calculate() { float tableSize = 0f; // Miss if (useSpellHit) { Miss = Math.Min(1f - tableSize, Math.Max(0f, StatConversion.GetSpellMiss(-LevelDelta, false) - StatS.SpellHit - StatConversion.GetSpellHitFromRating(StatS.HitRating))); } else { Miss = Math.Min(1f - tableSize, Math.Max(0f, StatConversion.YELLOW_MISS_CHANCE_CAP[LevelDelta] - StatS.PhysicalHit - StatConversion.GetSpellHitFromRating(StatS.HitRating))); } tableSize += Miss; // Dodge if (!useSpellHit) { Dodge = Math.Min(1f - tableSize, Math.Max(0f, StatConversion.YELLOW_DODGE_CHANCE_CAP[LevelDelta] - StatConversion.GetDodgeParryReducFromExpertise( StatConversion.GetExpertiseFromRating(StatS.ExpertiseRating, Char.Class), Char.Class)) ); tableSize += Dodge; } else { Dodge = 0f; } // Parry if (!useSpellHit) { Parry = Math.Min(1f - tableSize, Math.Max(0f, StatConversion.YELLOW_PARRY_CHANCE_CAP[LevelDelta] - StatConversion.GetDodgeParryReducFromExpertise( StatConversion.GetExpertiseFromRating(StatS.ExpertiseRating, Char.Class), Char.Class)) ); tableSize += Parry; } else { Parry = 0f; } // Block //if (isWhite || Abil.CanBeBlocked) { //Block = Math.Min(1f - tableSize, isMH ? combatFactors._c_mhblock : combatFactors._c_ohblock); //tableSize += Block; //} else { Block = 0f; } // Glancing Blow //if (isWhite) { //Glance = Math.Min(1f - tableSize, combatFactors._c_glance); //tableSize += Glance; //} else { Glance = 0f; } // Critical Hit Crit = 0; /*if (isWhite) { * float critValueToUse = (isMH ? combatFactors._c_mhwcrit : combatFactors._c_ohwcrit); * foreach (WeightedStat ws in combatFactors.critProcs) * { * float modCritChance = Math.Min(1f - tableSize, critValueToUse + StatConversion.GetCritFromRating(ws.Value, Char.Class)) + StatConversion.NPC_LEVEL_CRIT_MOD[calcOpts.TargetLevel - Char.Level]; + Crit += ws.Chance * modCritChance; + } + tableSize += Crit; + } else if (Abil.CanCrit) {*/ float critValueToUse = StatConversion.NPC_LEVEL_CRIT_MOD[LevelDelta] + (useSpellHit ? StatS.SpellCrit : StatS.PhysicalCrit); foreach (WeightedStat ws in critProcs) { float modCritChance = Math.Min(1f - tableSize, (critValueToUse + StatConversion.GetCritFromRating(ws.Value, Char.Class)) * (1f - Dodge - Miss)); Crit += ws.Chance * modCritChance; } if (Crit < 0) { Crit = 0f; } tableSize += Crit; //} // Normal Hit Hit = Math.Max(0f, 1f - tableSize); base.Calculate(); }
public override Dictionary <string, string> GetCharacterDisplayCalculationValues() { Dictionary <string, string> dictValues = new Dictionary <string, string>(); dictValues.Add("Overall Points", OverallPoints.ToString()); dictValues.Add("DPS Points", DPSPoints.ToString()); dictValues.Add("Survivability Points", SurvivabilityPoints.ToString()); float baseMiss = StatConversion.WHITE_MISS_CHANCE_CAP_DW[TargetLevel - 80] - BasicStats.PhysicalHit; float baseYellowMiss = StatConversion.YELLOW_MISS_CHANCE_CAP[TargetLevel - 80] - BasicStats.PhysicalHit; float basePoisonMiss = StatConversion.GetSpellMiss(80 - TargetLevel, false) - BasicStats.SpellHit; float baseDodge = StatConversion.WHITE_DODGE_CHANCE_CAP[TargetLevel - 80] - StatConversion.GetDodgeParryReducFromExpertise(BasicStats.Expertise); float baseParry = 0f;// StatConversion.WHITE_PARRY_CHANCE_CAP[TargetLevel - 80] - StatConversion.GetDodgeParryReducFromExpertise(BasicStats.Expertise); float baseWhiteMHCrit = CritChanceMHTotal; float baseWhiteOHCrit = CritChanceOHTotal; float capMiss = (float)Math.Ceiling(baseMiss * 100f * 32.78998947f); float capYellowMiss = (float)Math.Ceiling(baseYellowMiss * 100f * 32.78998947f); float capPoisonMiss = (float)Math.Ceiling(basePoisonMiss * 100f * 26.23f); float capDodge = (float)Math.Ceiling(baseDodge * 100f * 32.78998947f); float capParry = (float)Math.Ceiling(baseParry * 100f * 32.78998947f); // TODO: Check this value float capWhiteMHCrit = 100 - StatConversion.WHITE_GLANCE_CHANCE_CAP[TargetLevel - 80] * 100 - MissedWhiteAttacks - DodgedMHAttacks; float capWhiteOHCrit = 100 - StatConversion.WHITE_GLANCE_CHANCE_CAP[TargetLevel - 80] * 100 - MissedWhiteAttacks - DodgedOHAttacks; string tipMiss = "*White: "; if (BasicStats.HitRating > capMiss) { tipMiss += string.Format("Over the cap by {0} Hit Rating", BasicStats.HitRating - capMiss); } else if (BasicStats.HitRating < capMiss) { tipMiss += string.Format("Under the cap by {0} Hit Rating", capMiss - BasicStats.HitRating); } else { tipMiss += "Exactly at the cap"; } tipMiss += "\r\nYellow: "; if (BasicStats.HitRating > capYellowMiss) { tipMiss += string.Format("Over the cap by {0} Hit Rating", BasicStats.HitRating - capYellowMiss); } else if (BasicStats.HitRating < capYellowMiss) { tipMiss += string.Format("Under the cap by {0} Hit Rating", capYellowMiss - BasicStats.HitRating); } else { tipMiss += "Exactly at the cap"; } tipMiss += "\r\nPoison: "; if (BasicStats.HitRating > capPoisonMiss) { tipMiss += string.Format("Over the cap by {0} Hit Rating", BasicStats.HitRating - capPoisonMiss); } else if (BasicStats.HitRating < capPoisonMiss) { tipMiss += string.Format("Under the cap by {0} Hit Rating", capPoisonMiss - BasicStats.HitRating); } else { tipMiss += "Exactly at the cap"; } string tipDodge = string.Empty; if (BasicStats.ExpertiseRating > capDodge) { tipDodge = string.Format("*Over the cap by {0} Expertise Rating", BasicStats.ExpertiseRating - capDodge); } else if (BasicStats.ExpertiseRating < capDodge) { tipDodge = string.Format("*Under the cap by {0} Expertise Rating", capDodge - BasicStats.ExpertiseRating); } else { tipDodge = "*Exactly at the cap"; } string tipCrit = string.Format("Mainhand: {0}, ", CritChanceMH); if (CritChanceMHTotal > capWhiteMHCrit) { tipCrit += string.Format("over the Crit cap by {0}%", CritChanceMHTotal - capWhiteMHCrit); } else if (CritChanceMHTotal < capWhiteMHCrit) { tipCrit += string.Format("under the Crit cap by {0}%", capWhiteMHCrit - CritChanceMHTotal); } else { tipCrit += "exactly at the Crit cap"; } tipCrit += string.Format("\nOffhand: {0}, ", CritChanceOH); if (CritChanceOHTotal > capWhiteOHCrit) { tipCrit += string.Format("over the Crit cap by {0}%", CritChanceOHTotal - capWhiteOHCrit); } else if (CritChanceOHTotal < capWhiteOHCrit) { tipCrit += string.Format("under the Crit cap by {0}%", capWhiteOHCrit - CritChanceOHTotal); } else { tipCrit += "exactly at the Crit cap"; } dictValues.Add("Health", BasicStats.Health.ToString()); dictValues.Add("Attack Power", BasicStats.AttackPower.ToString()); dictValues.Add("Agility", BasicStats.Agility.ToString()); dictValues.Add("Strength", BasicStats.Strength.ToString()); dictValues.Add("Crit Rating", BasicStats.CritRating.ToString()); dictValues.Add("Hit Rating", BasicStats.HitRating.ToString() + tipMiss); dictValues.Add("Expertise Rating", BasicStats.ExpertiseRating.ToString() + tipDodge); dictValues.Add("Haste Rating", BasicStats.HasteRating.ToString()); dictValues.Add("Armor Penetration Rating", BasicStats.ArmorPenetrationRating.ToString()); dictValues.Add("Weapon Damage", "+" + BasicStats.WeaponDamage.ToString()); dictValues.Add("Avoided White Attacks", string.Format("{0}% / {1}%*Mainhand: {2}% Dodged, {3}% Missed\n Offhand: {4}% Dodged, {3}% Missed", AvoidedWhiteMHAttacks, AvoidedWhiteOHAttacks, DodgedMHAttacks, MissedWhiteAttacks, DodgedOHAttacks)); dictValues.Add("Avoided Yellow Attacks", string.Format("{0}%*{1}% Dodged, {2}% Missed", AvoidedAttacks, DodgedMHAttacks, MissedAttacks)); dictValues.Add("Avoided Poison Attacks", string.Format("{0}%*{1}% Missed", AvoidedPoisonAttacks, MissedPoisonAttacks)); dictValues.Add("Crit Chance", CritChanceYellow.ToString() + "%*" + tipCrit); dictValues.Add("MainHand Speed", MainHandSpeed.ToString() + "s"); dictValues.Add("OffHand Speed", OffHandSpeed.ToString() + "s"); dictValues.Add("Armor Mitigation MainHand", ArmorMitigationMH.ToString() + "%"); dictValues.Add("Armor Mitigation OffHand", ArmorMitigationOH.ToString() + "%"); dictValues.Add("Optimal Rotation", HighestDPSRotation.ToString()); dictValues.Add("Optimal Rotation DPS", HighestDPSRotation.DPS.ToString()); dictValues.Add("Custom Rotation DPS", CustomRotation.DPS.ToString()); float chanceWhiteMHNonAvoided = 1f - (AvoidedWhiteMHAttacks / 100f); float chanceWhiteOHNonAvoided = 1f - (AvoidedWhiteOHAttacks / 100f); float chanceNonAvoided = 1f - (AvoidedAttacks / 100f); float chancePoisonNonAvoided = 1f - (AvoidedPoisonAttacks / 100f); dictValues.Add("MainHand", MainHandStats.GetStatsTexts(HighestDPSRotation.MainHandCount, 0, HighestDPSRotation.TotalDamage, chanceWhiteMHNonAvoided, Duration)); dictValues.Add("OffHand", OffHandStats.GetStatsTexts(HighestDPSRotation.OffHandCount, 0, HighestDPSRotation.TotalDamage, chanceWhiteOHNonAvoided, Duration)); dictValues.Add("Backstab", BackstabStats.GetStatsTexts(HighestDPSRotation.BackstabCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Hemorrhage", HemoStats.GetStatsTexts(HighestDPSRotation.HemoCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Sinister Strike", SStrikeStats.GetStatsTexts(HighestDPSRotation.SStrikeCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Mutilate", MutiStats.GetStatsTexts(HighestDPSRotation.MutiCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Rupture", RuptStats.GetStatsTexts(HighestDPSRotation.RuptCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Slice and Dice", SnDStats.GetStatsTexts(HighestDPSRotation.SnDCount, HighestDPSRotation.SnDCP, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Eviscerate", EvisStats.GetStatsTexts(HighestDPSRotation.EvisCount, Math.Max(HighestDPSRotation.EvisCP, HighestDPSRotation.EnvenomCP), HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Envenom", EnvenomStats.GetStatsTexts(HighestDPSRotation.EnvenomCount, Math.Max(HighestDPSRotation.EvisCP, HighestDPSRotation.EnvenomCP), HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Instant Poison", IPStats.GetStatsTexts(HighestDPSRotation.IPCount, 0, HighestDPSRotation.TotalDamage, chancePoisonNonAvoided, Duration)); dictValues.Add("Deadly Poison", DPStats.GetStatsTexts(HighestDPSRotation.DPCount, 0, HighestDPSRotation.TotalDamage, chancePoisonNonAvoided, Duration)); dictValues.Add("Wound Poison", WPStats.GetStatsTexts(HighestDPSRotation.WPCount, 0, HighestDPSRotation.TotalDamage, chancePoisonNonAvoided, Duration)); dictValues.Add("Anesthetic Poison", APStats.GetStatsTexts(HighestDPSRotation.APCount, 0, HighestDPSRotation.TotalDamage, chancePoisonNonAvoided, Duration)); return(dictValues); }
// Now returns damage per cast to allow adjustments for fight length private float DoTreeCalcs(CharacterCalculationsMoonkin calcs, int playerLevel, int bossLevel, float effectiveNatureDamage, float treantLifespan) { float sunderPercent = calcs.BasicStats.TargetArmorReduction; float meleeHit = calcs.SpellHit * (StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel] / StatConversion.GetSpellMiss(playerLevel - bossLevel, false)); float physicalDamageMultiplierBonus = (1f + calcs.BasicStats.BonusDamageMultiplier) * (1f + calcs.BasicStats.BonusPhysicalDamageMultiplier); float physicalDamageMultiplierReduc = (1f - calcs.BasicStats.DamageTakenReductionMultiplier) * (1f - calcs.BasicStats.PhysicalDamageTakenReductionMultiplier); // 932 = base AP, 57% spell power scaling float attackPower = 932.0f + (float)Math.Floor(0.57f * effectiveNatureDamage); // 1.65 s base swing speed float baseAttackSpeed = 1.65f; float attackSpeed = baseAttackSpeed / (1 + calcs.BasicStats.PhysicalHaste); // 580 = base DPS float damagePerHit = (580f + attackPower / 14.0f) * baseAttackSpeed; // 5% base crit rate, inherit crit debuffs // Remove crit depression, as it doesn't appear to have an effect (unless it's base ~10% crit rate) float critRate = 0.05f; // White hit glancing rate float glancingRate = StatConversion.WHITE_GLANCE_CHANCE_CAP[bossLevel - playerLevel]; // Hit rate determined by the amount of melee hit, not by spell hit float missRate = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel] - meleeHit); // Since the trees inherit expertise from their hit, scale their hit rate such that when they are hit capped, they are expertise capped float dodgeRate = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[bossLevel - playerLevel] * (missRate / StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel])); // Armor damage reduction, including Sunder float damageReduction = StatConversion.GetArmorDamageReduction(playerLevel, StatConversion.NPC_ARMOR[bossLevel - playerLevel] * (1f - sunderPercent), 0, 0); // Final normal damage per swing damagePerHit *= 1.0f - damageReduction; damagePerHit *= physicalDamageMultiplierReduc; damagePerHit *= physicalDamageMultiplierBonus; // Damage per swing, including crits/glances/misses // This is a cheesy approximation of a true combat table, but because crit/miss/dodge rates will all be fairly low, I don't need to do the whole thing damagePerHit = (critRate * damagePerHit * 2.0f) + (glancingRate * damagePerHit * 0.75f) + ((1 - critRate - glancingRate - missRate - dodgeRate) * damagePerHit); // Total damage done in their estimated lifespan float damagePerTree = (treantLifespan * 30.0f / attackSpeed) * damagePerHit; return(3 * damagePerTree); }
protected override void Calculate() { float tableSize = 0f; // Miss if (useSpellHit) { Miss = Math.Min(1f - tableSize, Math.Max(StatConversion.GetSpellMiss(LevelDif, false) - (StatConversion.GetSpellHitFromRating(StatS.HitRating, Char.Class) + StatS.SpellHit), 0f)); } else { Miss = Math.Min(1f - tableSize, isWhite ? combatFactors.CWmiss : combatFactors.CYmiss); } tableSize += Miss; // Dodge if (!useRangedHit && (isWhite || Abil.CanBeDodged)) { Dodge = Math.Min(1f - tableSize, isMH ? combatFactors.CMHdodge : combatFactors.COhdodge); tableSize += Dodge; } else { Dodge = 0f; } // Parry if (!useRangedHit && (isWhite || Abil.CanBeParried)) { Parry = Math.Min(1f - tableSize, isMH ? combatFactors.CMHparry : combatFactors.COhparry); tableSize += Parry; } else { Parry = 0f; } // Block if (!useRangedHit && (isWhite || Abil.CanBeBlocked)) { Block = Math.Min(1f - tableSize, isMH ? combatFactors.CMHblock : combatFactors.COhblock); tableSize += Block; } else { Block = 0f; } // Glancing Blow if (!useRangedHit && isWhite) { Glance = Math.Min(1f - tableSize, combatFactors.CGlance); tableSize += Glance; } else { Glance = 0f; } // Critical Hit Crit = 0; if (isWhite) { float critValueToUse = (isMH ? combatFactors.CMHwcrit : combatFactors.COhwcrit) + StatConversion.NPC_LEVEL_CRIT_MOD[LevelDif]; foreach (WeightedStat ws in combatFactors.CritProcs) { float modCritChance = Math.Min(1f - tableSize, critValueToUse + StatConversion.GetCritFromRating(ws.Value, Char.Class)); Crit += ws.Chance * modCritChance; } tableSize += Crit; } else if (Abil.CanCrit) { float critValueToUse = StatConversion.NPC_LEVEL_CRIT_MOD[LevelDif] + (isMH ? combatFactors.CMHycrit : combatFactors.COhycrit) + Abil.BonusCritChance; foreach (WeightedStat ws in combatFactors.CritProcs) { float modCritChance = Math.Min(1f - tableSize, (critValueToUse + StatConversion.GetCritFromRating(ws.Value, Char.Class)) * (1f - Dodge - Miss)); Crit += ws.Chance * modCritChance; } tableSize += Crit; } // Normal Hit Hit = Math.Max(0f, 1f - tableSize); base.Calculate(); }
public float GetSpellMissChance() { return(StatConversion.GetSpellMiss(_calcOpts.TargetLevel - 80, false)); }
public override Dictionary <string, string> GetCharacterDisplayCalculationValues() { Dictionary <string, string> dictValues = new Dictionary <string, string>(); dictValues.Add("Overall Points", OverallPoints.ToString()); dictValues.Add("DPS Points", DPSPoints.ToString()); dictValues.Add("Survivability Points", SurvivabilityPoints.ToString()); float baseMiss = StatConversion.WHITE_MISS_CHANCE_CAP_DW[TargetLevel - 85] - BasicStats.PhysicalHit; float baseYellowMiss = StatConversion.WHITE_MISS_CHANCE_CAP[TargetLevel - 85] - BasicStats.PhysicalHit; float basePoisonMiss = StatConversion.GetSpellMiss(85 - TargetLevel, false) - BasicStats.SpellHit; float baseDodge = StatConversion.WHITE_DODGE_CHANCE_CAP[TargetLevel - 85] - StatConversion.GetDodgeParryReducFromExpertise(BasicStats.Expertise); float baseParry = 0f;// StatConversion.WHITE_PARRY_CHANCE_CAP[TargetLevel - 85] - StatConversion.GetDodgeParryReducFromExpertise(BasicStats.Expertise); float baseWhiteMHCrit = CritChanceMHTotal; float baseWhiteOHCrit = CritChanceOHTotal; float capMiss = (float)Math.Ceiling(baseMiss * StatConversion.RATING_PER_PHYSICALHIT); float capYellowMiss = (float)Math.Ceiling(baseYellowMiss * StatConversion.RATING_PER_PHYSICALHIT); float capPoisonMiss = (float)Math.Ceiling(basePoisonMiss * StatConversion.RATING_PER_SPELLHIT); float capDodge = (float)Math.Ceiling(baseDodge * 100f * StatConversion.RATING_PER_EXPERTISE / (StatConversion.RATING_PER_DODGEPARRYREDUC * 100f)); float capParry = (float)Math.Ceiling(baseParry * 100f * 32.78998947f); // TODO: Check this value float capWhiteMHCrit = 100 - StatConversion.WHITE_GLANCE_CHANCE_CAP[TargetLevel - 85] * 100 - MissedWhiteAttacks - DodgedMHAttacks; float capWhiteOHCrit = 100 - StatConversion.WHITE_GLANCE_CHANCE_CAP[TargetLevel - 85] * 100 - MissedWhiteAttacks - DodgedOHAttacks; string tipMiss = "*White: "; if (BasicStats.HitRating > capMiss) { tipMiss += string.Format("Over the cap ({1}) by {0} Hit Rating", BasicStats.HitRating - capMiss, capMiss); } else if (BasicStats.HitRating < capMiss) { tipMiss += string.Format("Under the cap ({1}) by {0} Hit Rating", capMiss - BasicStats.HitRating, capMiss); } else { tipMiss += string.Format("Exactly at the cap ({0})", capMiss); } tipMiss += "\r\nYellow: "; if (BasicStats.HitRating > capYellowMiss) { tipMiss += string.Format("Over the cap ({1}) by {0} Hit Rating", BasicStats.HitRating - capYellowMiss, capYellowMiss); } else if (BasicStats.HitRating < capYellowMiss) { tipMiss += string.Format("Under the cap ({1}) by {0} Hit Rating", capYellowMiss - BasicStats.HitRating, capYellowMiss); } else { tipMiss += string.Format("Exactly at the cap ({0})", capYellowMiss); } tipMiss += "\r\nPoison: "; if (BasicStats.HitRating > capPoisonMiss) { tipMiss += string.Format("Over the cap ({1}) by {0} Hit Rating", BasicStats.HitRating - capPoisonMiss, capPoisonMiss); } else if (BasicStats.HitRating < capPoisonMiss) { tipMiss += string.Format("Under the cap ({1}) by {0} Hit Rating", capPoisonMiss - BasicStats.HitRating, capPoisonMiss); } else { tipMiss += string.Format("Exactly at the cap ({0})", capPoisonMiss); } string tipDodge = string.Empty; if (BasicStats.ExpertiseRating > capDodge) { tipDodge = string.Format("*Over the cap ({1}) by {0} Expertise Rating", BasicStats.ExpertiseRating - capDodge, capDodge); } else if (BasicStats.ExpertiseRating < capDodge) { tipDodge = string.Format("*Under the cap ({1}) by {0} Expertise Rating", capDodge - BasicStats.ExpertiseRating, capDodge); } else { tipDodge = string.Format("*Exactly at the cap ({0})", capDodge); } string tipCrit = string.Format("Mainhand: {0}, ", CritChanceMH); if (CritChanceMHTotal > capWhiteMHCrit) { tipCrit += string.Format("over the Crit cap ({1}) by {0}%", CritChanceMHTotal - capWhiteMHCrit, capWhiteMHCrit); } else if (CritChanceMHTotal < capWhiteMHCrit) { tipCrit += string.Format("under the Crit cap ({1}) by {0}%", capWhiteMHCrit - CritChanceMHTotal, capWhiteMHCrit); } else { tipCrit += string.Format("exactly at the Crit cap ({0})", capWhiteMHCrit); } tipCrit += string.Format("\nOffhand: {0}, ", CritChanceOH); if (CritChanceOHTotal > capWhiteOHCrit) { tipCrit += string.Format("over the Crit cap ({1}) by {0}%", CritChanceOHTotal - capWhiteOHCrit, capWhiteOHCrit); } else if (CritChanceOHTotal < capWhiteOHCrit) { tipCrit += string.Format("under the Crit cap ({1}) by {0}%", capWhiteOHCrit - CritChanceOHTotal, capWhiteOHCrit); } else { tipCrit += string.Format("exactly at the Crit cap ({0})", capWhiteOHCrit); } string tipMastery = "*"; if (Spec == 0) { tipMastery += String.Format("{0}% increased Poison damage", BasicStats.MasteryRating * RV.Mastery.PotentPoisonsDmgMultPerMast); } else if (Spec == 1) { tipMastery += String.Format("{0}% chance on an extra mainhand attack", BasicStats.MasteryRating * RV.Mastery.MainGauchePerMast); } else { tipMastery += String.Format("{0}% increased finishing move damage and Slice and Dice effectiveness", BasicStats.MasteryRating * RV.Mastery.ExecutionerPerMast); } dictValues.Add("Health", BasicStats.Health.ToString()); dictValues.Add("Attack Power", BasicStats.AttackPower.ToString()); dictValues.Add("Agility", BasicStats.Agility.ToString()); dictValues.Add("Strength", BasicStats.Strength.ToString()); dictValues.Add("Crit Rating", BasicStats.CritRating.ToString()); dictValues.Add("Hit Rating", BasicStats.HitRating.ToString() + tipMiss); dictValues.Add("Expertise Rating", BasicStats.ExpertiseRating.ToString() + tipDodge); dictValues.Add("Haste Rating", BasicStats.HasteRating.ToString()); dictValues.Add("Mastery Rating", BasicStats.MasteryRating.ToString() + tipMastery); dictValues.Add("Armor Penetration", BasicStats.ArmorPenetration.ToString()); dictValues.Add("Weapon Damage", "+" + BasicStats.WeaponDamage.ToString()); dictValues.Add("Avoided White Attacks", string.Format("{0}% / {1}%*Mainhand: {2}% Dodged, {3}% Missed\n Offhand: {4}% Dodged, {3}% Missed", AvoidedWhiteMHAttacks, AvoidedWhiteOHAttacks, DodgedMHAttacks, MissedWhiteAttacks, DodgedOHAttacks)); dictValues.Add("Avoided Yellow Attacks", string.Format("{0}%*{1}% Dodged, {2}% Missed", AvoidedAttacks, DodgedMHAttacks, MissedAttacks)); dictValues.Add("Avoided Poison Attacks", string.Format("{0}%*{1}% Missed", AvoidedPoisonAttacks, MissedPoisonAttacks)); dictValues.Add("Crit Chance", CritChanceYellow.ToString() + "%*" + tipCrit); dictValues.Add("MainHand Speed", MainHandSpeed.ToString() + "s"); dictValues.Add("OffHand Speed", OffHandSpeed.ToString() + "s"); dictValues.Add("Armor Mitigation", ArmorMitigation.ToString() + "%"); dictValues.Add("Optimal Rotation", HighestDPSRotation.ToString()); dictValues.Add("Optimal Rotation DPS", HighestDPSRotation.DPS.ToString()); dictValues.Add("Custom Rotation DPS", CustomRotation.DPS.ToString()); float chanceWhiteMHNonAvoided = 1f - (AvoidedWhiteMHAttacks / 100f); float chanceWhiteOHNonAvoided = 1f - (AvoidedWhiteOHAttacks / 100f); float chanceNonAvoided = 1f - (AvoidedAttacks / 100f); float chancePoisonNonAvoided = 1f - (AvoidedPoisonAttacks / 100f); dictValues.Add("MainHand", MainHandStats.GetStatsTexts(HighestDPSRotation.MainHandCount, 0, HighestDPSRotation.TotalDamage, chanceWhiteMHNonAvoided, Duration)); dictValues.Add("OffHand", OffHandStats.GetStatsTexts(HighestDPSRotation.OffHandCount, 0, HighestDPSRotation.TotalDamage, chanceWhiteOHNonAvoided, Duration)); dictValues.Add("Backstab", BackstabStats.GetStatsTexts(HighestDPSRotation.BackstabCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Hemorrhage", HemoStats.GetStatsTexts(HighestDPSRotation.HemoCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Sinister Strike", SStrikeStats.GetStatsTexts(HighestDPSRotation.SStrikeCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Mutilate", MutiStats.GetStatsTexts(HighestDPSRotation.MutiCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Revealing Strike", RStrikeStats.GetStatsTexts(HighestDPSRotation.RStrikeCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Rupture", RuptStats.GetStatsTexts(HighestDPSRotation.RuptCount, HighestDPSRotation.RuptCP, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Slice and Dice", SnDStats.GetStatsTexts(HighestDPSRotation.SnDCount, HighestDPSRotation.SnDCP, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Eviscerate", EvisStats.GetStatsTexts(HighestDPSRotation.EvisCount, Math.Max(HighestDPSRotation.EvisCP, HighestDPSRotation.EnvenomCP), HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Envenom", EnvenomStats.GetStatsTexts(HighestDPSRotation.EnvenomCount, Math.Max(HighestDPSRotation.EvisCP, HighestDPSRotation.EnvenomCP), HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); dictValues.Add("Instant Poison", IPStats.GetStatsTexts(HighestDPSRotation.IPCount, 0, HighestDPSRotation.TotalDamage, chancePoisonNonAvoided, Duration)); dictValues.Add("Deadly Poison", DPStats.GetStatsTexts(HighestDPSRotation.DPCount, 0, HighestDPSRotation.TotalDamage, chancePoisonNonAvoided, Duration)); dictValues.Add("Wound Poison", WPStats.GetStatsTexts(HighestDPSRotation.WPCount, 0, HighestDPSRotation.TotalDamage, chancePoisonNonAvoided, Duration)); dictValues.Add("Venomous Wounds", VenomousWoundsStats.GetStatsTexts(HighestDPSRotation.VenomousWoundsCount, 0, HighestDPSRotation.TotalDamage, 1f, Duration)); dictValues.Add("Main Gauche", MainGaucheStats.GetStatsTexts(HighestDPSRotation.MGCount, 0, HighestDPSRotation.TotalDamage, chanceNonAvoided, Duration)); return(dictValues); }
public override Dictionary <string, string> GetCharacterDisplayCalculationValues() { Dictionary <string, string> dictValues = new Dictionary <string, string>(); CalculationOptionsShadowPriest calcOpts = character.CalculationOptions as CalculationOptionsShadowPriest; BossOptions BossOpts = character.BossOptions; Stats baseStats = BaseStats.GetBaseStats(character.Level, character.Class, character.Race); bool Ptr = calcOpts.PTR; dictValues.Add("Health", BasicStats.Health.ToString()); float ResilienceCap = 0.15f, ResilienceFromRating = StatConversion.GetCritReductionFromResilience(1); float Resilience = StatConversion.GetCritReductionFromResilience(BasicStats.Resilience); dictValues.Add("Resilience", string.Format("{0}*-{1}% Damage from DoT and Mana Drains\n\r-{1}% Chance to be crit\r\n-{2}% Damage from Crits.\r\n{3}", BasicStats.Resilience.ToString(), (Resilience * 100f).ToString("0.00"), (Resilience * 100f * 2.2f).ToString("0.00"), (Resilience > ResilienceCap) ? (string.Format("{0} rating above cap", ((float)Math.Floor((Resilience - ResilienceCap) / ResilienceFromRating)).ToString("0"))) : (string.Format("{0} rating below cap", ((float)Math.Ceiling((ResilienceCap - Resilience) / ResilienceFromRating)).ToString("0"))))); dictValues.Add("Stamina", BasicStats.Stamina.ToString()); dictValues.Add("Mana", BasicStats.Mana.ToString()); dictValues.Add("Intellect", BasicStats.Intellect.ToString()); dictValues.Add("Spirit", Math.Floor(BasicStats.Spirit).ToString("0")); dictValues.Add("Spell Power", String.Format("{0}*{1} Bonus Shadow\r\n{2} Bonus Holy\r\n{3} from Inner Fire", Math.Floor(BasicStats.SpellPower), Math.Floor(BasicStats.SpellPower + BasicStats.SpellShadowDamageRating), Math.Floor(BasicStats.SpellPower /*+ BasicStats.SpellHolyDamageRating*/), BasicStats.PriestInnerFire * CalculationsShadowPriest.GetInnerFireSpellPowerBonus(character))); dictValues.Add("Regen", String.Format("{0}*MP5: {1}\r\nOutFSR: {2}", RegenInFSR.ToString("0"), BasicStats.Mp5.ToString(), RegenOutFSR.ToString("0"))); dictValues.Add("Crit", string.Format("{0}%*{1}% from {2} Spell Crit rating\r\n{3}% from Intellect\r\n{4}% from Focused Will\r\n{5}% from Base Crit\r\n{6}% from Buffs\r\n{7}% on Mind Blast, Mind Flay and Mind Sear.\r\n{8}% on VT, SW:P and DP\r\n{9}% on Smite, Holy Fire and Penance.", (BasicStats.SpellCrit * 100f).ToString("0.00"), (StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f).ToString("0.00"), BasicStats.CritRating.ToString("0"), (StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect) * 100f).ToString("0.00"), character.PriestTalents.FocusedWill, (baseStats.SpellCrit * 100f).ToString("0.00"), (BasicStats.SpellCrit * 100f - StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f - StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect) * 100f - baseStats.SpellCrit * 100f - character.PriestTalents.FocusedWill).ToString("0.00"), (BasicStats.SpellCrit * 100f + character.PriestTalents.MindMelt * 2f).ToString("0.00"), (BasicStats.SpellCrit * 100f + character.PriestTalents.MindMelt * 3f).ToString("0.00"), (BasicStats.SpellCrit * 100f + character.PriestTalents.HolySpecialization * 1f).ToString("0.00"))); #if RAWR3 || SILVERLIGHT float Hit = 100 - (StatConversion.GetSpellMiss(character.Level - BossOpts.Level, false) * 100); #else float Hit = 100 - (StatConversion.GetSpellMiss(-calcOpts.TargetLevel, false) * 100); #endif float BonusHit = BasicStats.SpellHit * 100f; float RacialHit = 0; string RacialText = ""; if (character.Race == CharacterRace.Draenei) { RacialHit = 1; RacialText = "1% from Draenei Racial\r\n"; if (!character.ActiveBuffsContains("Heroic Presence")) { BonusHit += 1; } } float DebuffHit = character.PriestTalents.Misery * 1f; if (character.ActiveBuffsConflictingBuffContains("Spell Hit Chance Taken")) { DebuffHit = 3f; } else { BonusHit += DebuffHit; } float RHitRating = 0.01f / StatConversion.GetSpellHitFromRating(1); float ShadowFocusHit = character.PriestTalents.ShadowFocus * 1f; float HitShadow = Hit + BonusHit + ShadowFocusHit; float HitHoly = Hit + BonusHit; dictValues.Add("Hit", string.Format("{0}%*{1}% from {2} Hit Rating\r\n{3}% from Buffs\r\n{4}% from {5} points in Misery\r\n{6}% from {7} points in Shadow Focus\r\n{8}{9}% Hit with Shadow spells, {10}\r\n{11}% Hit with Holy spells, {12}", BonusHit.ToString("0.00"), (StatConversion.GetSpellHitFromRating(BasicStats.HitRating) * 100f).ToString("0.00"), BasicStats.HitRating, (BonusHit - StatConversion.GetSpellHitFromRating(BasicStats.HitRating) * 100f - RacialHit - DebuffHit).ToString("0.00"), DebuffHit, character.PriestTalents.Misery, ShadowFocusHit, character.PriestTalents.ShadowFocus, RacialText, HitShadow.ToString("0.00"), (HitShadow > 100f) ? string.Format("{0} hit rating above cap", Math.Floor((HitShadow - 100f) * RHitRating)) : string.Format("{0} hit rating below cap", Math.Ceiling((100f - HitShadow) * RHitRating)), HitHoly.ToString("0.00"), (HitHoly > 100f) ? string.Format("{0} hit rating above cap", Math.Floor((HitHoly - 100f) * RHitRating)) : string.Format("{0} hit rating below cap", Math.Ceiling((100f - HitHoly) * RHitRating)))); dictValues.Add("Haste", string.Format("{0}%*{1}% from {2} Haste rating\r\n{3}% ({6}) points in Enlightenment\r\n{4}% from Buffs\r\n{5}s Global Cooldown", (BasicStats.SpellHaste * 100f).ToString("0.00"), (StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating) * 100f).ToString("0.00"), BasicStats.HasteRating.ToString(), (character.PriestTalents.Enlightenment * 2).ToString("0"), (((1f + BasicStats.SpellHaste) / (1f + StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating)) / (1f + character.PriestTalents.Enlightenment * 0.02f) - 1f) * 100f).ToString("0.00"), Math.Max(1.0f, 1.5f / (1 + BasicStats.SpellHaste)).ToString("0.00"), character.PriestTalents.Enlightenment)); dictValues.Add("Armor", string.Format("{0}*{1}% Damage Reduction.", (BasicStats.Armor + BasicStats.BonusArmor).ToString("0"), (StatConversion.GetArmorDamageReduction(80, (BasicStats.Armor + BasicStats.BonusArmor), 0f, 0f, 0f) * 100f).ToString("0.00"))); float[] Resistances = { 0, BasicStats.ArcaneResistance + BasicStats.ArcaneResistanceBuff, BasicStats.FireResistance + BasicStats.FireResistanceBuff, BasicStats.FrostResistance + BasicStats.FrostResistanceBuff, BasicStats.NatureResistance + BasicStats.NatureResistanceBuff, BasicStats.ShadowResistance + BasicStats.ShadowResistanceBuff, }; string[] ResistanceNames = { "None", "Arcane", "Fire", "Frost", "Nature", "Shadow", }; string ResistanceString = "*Resistances:"; float MaxResist = Resistances[0]; int MaxResistIndex = 0; float AvgResist = 0f; for (int x = 1; x < Resistances.Length; x++) { AvgResist += Resistances[x]; if (Resistances[x] > MaxResist) { MaxResist = Resistances[x]; MaxResistIndex = x; } ResistanceString += string.Format("\r\n{0} : {1}", ResistanceNames[x], Resistances[x]); } AvgResist /= (Resistances.Length - 1); if (AvgResist == 0) { ResistanceString = "None" + ResistanceString; } else { string ResistanceName = (MaxResist == AvgResist) ? "All" : ResistanceNames[MaxResistIndex]; ResistanceString = string.Format("{0} : {1}", ResistanceName, MaxResist.ToString("0")) + ResistanceString; ResistanceString += string.Format("\r\n\r\nResist ({0}):", ResistanceName); ResistanceString += string.Format("\r\n{0}", StatConversion.GetResistanceTableString(character.Level + 3, character.Level, MaxResist, 0)); } dictValues.Add("Resistance", ResistanceString); SolverBase solver = GetSolver(character, BasicStats); solver.Calculate(this); dictValues.Add("Rotation", string.Format("{0}*{1}", solver.Name, solver.Rotation)); if (solver.SpellSimulation != null) { String s = "Spell Cast List:"; int i = 0; foreach (Spell spell in solver.SpellSimulation) { if (i++ % 10 == 0) { s += "\r\n"; } s += ", " + spell.Name; } s += "\r\n---Repeat---"; dictValues.Add("Castlist", string.Format("{0}*{1}", solver.SpellSimulation.Count, s)); } else { dictValues.Add("Castlist", "Empty"); } dictValues.Add("DPS", string.Format("{0}*Damage Pr Second", solver.DPS.ToString("0"))); //dictValues.Add("SustainDPS", string.Format("{0}*Mana restrained DPS", solver.SustainDPS.ToString("0"))); float baseMana = BaseStats.GetBaseStats(character).Mana; dictValues.Add("SW Pain", new ShadowWordPain(BasicStats, character, baseMana, Ptr).ToString()); DevouringPlague dp = new DevouringPlague(BasicStats, character, baseMana, Ptr); dictValues.Add("Devouring Plague", dp.ToString()); if (dp.ImprovedDP != null) { dictValues.Add("Imp. Devouring Plague", dp.ImprovedDP.ToString()); } else { dictValues.Add("Imp. Devouring Plague", "- *No required talents"); } dictValues.Add("SW Death", new ShadowWordDeath(BasicStats, character, baseMana, Ptr).ToString()); dictValues.Add("Mind Blast", new MindBlast(BasicStats, character, baseMana, Ptr).ToString()); dictValues.Add("PW Shield", new PowerWordShield(BasicStats, character, baseMana, Ptr).ToString()); if (character.PriestTalents.VampiricTouch > 0) { dictValues.Add("Vampiric Touch", new VampiricTouch(BasicStats, character, baseMana, Ptr).ToString()); } else { dictValues.Add("Vampiric Touch", "- *No required talents"); } if (character.PriestTalents.MindFlay > 0) { dictValues.Add("Mind Flay", new MindFlay(BasicStats, character, baseMana, Ptr).ToString()); } else { dictValues.Add("Mind Flay", "- *No required talents"); } dictValues.Add("Shadowfiend", new Shadowfiend(BasicStats, character, baseMana, Ptr).ToString()); dictValues.Add("Smite", new Smite(BasicStats, character, baseMana, Ptr).ToString()); dictValues.Add("Holy Fire", new HolyFire(BasicStats, character, baseMana, Ptr).ToString()); if (character.PriestTalents.Penance > 0) { dictValues.Add("Penance", new Penance(BasicStats, character, baseMana, Ptr).ToString()); } else { dictValues.Add("Penance", "- *No required talents"); } return(dictValues); }