private void SetCvalues() { CMHItemType = MH.Type; CMHItemSpeed = MH.Speed; if (OH != null) { COHItemType = OH.Type; COHItemSpeed = OH.Speed; } useMH = _useMH; // public variable gets set once useOH = _useOH; CMHRacialExpertise = BaseStats.GetRacialExpertise(Char, ItemSlot.MainHand); //GetRacialExpertiseFromWeaponType(CmhItemType); CMHexpertise = StatS.Expertise + StatConversion.GetExpertiseFromRating(Math.Max(0, StatS.ExpertiseRating)) + CMHRacialExpertise; CYmiss = YwMissChance; CWmiss = WhMissChance; CMHdodge = MhDodgeChance; CMHparry = MhParryChance; CMHblock = MhBlockChance; CMHwcrit = MhWhCritChance; CMHycrit = MhYwCritChance; CGlance = GlanceChance; if (useOH) { COHRacialExpertise = BaseStats.GetRacialExpertise(Char, ItemSlot.OffHand);// GetRacialExpertiseFromWeaponType(CohItemType); COhexpertise = StatS.Expertise + StatConversion.GetExpertiseFromRating(Math.Max(0, StatS.ExpertiseRating)) + COHRacialExpertise; COhdodge = OhDodgeChance; COhparry = OhParryChance; COhblock = OhBlockChance; COhwcrit = OhWhCritChance; COhycrit = OhYwCritChance; } else { COHItemType = ItemType.None; COHItemSpeed = 0f; COHRacialExpertise = 0f; COhexpertise = 0f; COhdodge = StatConversion.WHITE_DODGE_CHANCE_CAP[LevelDif]; COhparry = StatConversion.WHITE_PARRY_CHANCE_CAP[LevelDif]; COhblock = 0.0f; COhwcrit = 0.0f; COhycrit = 0.0f; } }
public void UpdateCalcs(bool firstPass) { // talents if (_calcOpts.PriorityInUse(EnhanceAbility.StormStrike)) { stormstrikeBonusCrit = .25f * _talents.Stormstrike + (_talents.GlyphofStormstrike ? .1f : 0f); } else { stormstrikeBonusCrit = 0f; } //set bonus enhance4T11 = 0f; _character.SetBonusCount.TryGetValue("Battlegear of the Raging Elements", out setCount); if (setCount >= 4) { enhance4T11 = 0.1f; } critMultiplierSpell = 1.5f * (1 + _stats.BonusSpellCritDamageMultiplier); critMultiplierMelee = 2f * (1 + _stats.BonusCritDamageMultiplier); // Melee float hitBonus = _stats.PhysicalHit + StatConversion.GetHitFromRating(_stats.HitRating) + 0.06f; //DualWieldSpecialization expertiseBonusMH = GetDPRfromExp(_stats.Expertise + StatConversion.GetExpertiseFromRating(_stats.ExpertiseRating) + BaseStats.GetRacialExpertise(_character, ItemSlot.MainHand)); expertiseBonusOH = GetDPRfromExp(_stats.Expertise + StatConversion.GetExpertiseFromRating(_stats.ExpertiseRating) + BaseStats.GetRacialExpertise(_character, ItemSlot.OffHand)); float meleeCritModifier = _stats.PhysicalCrit; float baseMeleeCrit = StatConversion.GetCritFromRating(_stats.CritRating) + StatConversion.GetCritFromAgility(_stats.Agility, _character.Class) + .01f * _talents.Acuity; chanceDodgeMH = Math.Max(0f, DodgeChanceCap - expertiseBonusMH); chanceDodgeOH = Math.Max(0f, DodgeChanceCap - expertiseBonusOH); float ParryChance = ParryChanceCap - expertiseBonusMH; //chanceParryMH = (float)Math.Max(0f, _bossOpts.InBack ? ParryChance * PecentageInfrontBoss : ParryChance); ParryChance = ParryChanceCap - expertiseBonusOH; //chanceParryOH = (float)Math.Max(0f, _bossOpts.InBack ? ParryChance * PecentageInfrontBoss : ParryChance); chanceWhiteMissMH = Math.Max(0f, WhiteHitCap - hitBonus) + chanceDodgeMH + chanceParryMH; chanceWhiteMissOH = Math.Max(0f, WhiteHitCap - hitBonus) + chanceDodgeOH + chanceParryOH; chanceYellowMissMH = Math.Max(0f, YellowHitCap - hitBonus) + chanceDodgeMH + chanceParryMH; // base miss 8% now chanceYellowMissOH = Math.Max(0f, YellowHitCap - hitBonus) + chanceDodgeOH + chanceParryOH; // base miss 8% now // SetCritValues((1 + _stats.BonusCritChance) * (baseMeleeCrit + meleeCritModifier) + .00005f); //fudge factor for rounding SetCritValues(baseMeleeCrit + meleeCritModifier + .00005f); //fudge factor for rounding // set two export values so that ED crit isn't included exportMeleeCritMH = chanceWhiteCritMH + whiteCritDepression; exportMeleeCritOH = chanceWhiteCritOH + whiteCritDepression; // Spells ftBonusCrit = 0f; if (_calcOpts.MainhandImbue == "Flametongue") { ftBonusCrit += _talents.GlyphofFlametongueWeapon ? .02f : 0f; } if (_calcOpts.OffhandImbue == "Flametongue") { ftBonusCrit += _talents.GlyphofFlametongueWeapon ? .02f : 0f; } if (baseStats == null) { baseStats = BaseStats.GetBaseStats(_character); } elemPrecMod = _talents.ElementalPrecision > 0 ? (_stats.Spirit - baseStats.Spirit) * (_talents.ElementalPrecision / 3f) : 0f; float hitBonusSpell = _stats.SpellHit + StatConversion.GetSpellHitFromRating(_stats.HitRating + elemPrecMod); chanceSpellMiss = Math.Max(0f, SpellMissRate - hitBonusSpell); overSpellHitCap = Math.Max(0f, hitBonusSpell - SpellMissRate); float spellCritModifier = _stats.SpellCrit + _stats.SpellCritOnTarget + ftBonusCrit; float baseSpellCrit = StatConversion.GetSpellCritFromRating(_stats.CritRating) + StatConversion.GetSpellCritFromIntellect(_stats.Intellect) + .01f * _talents.Acuity; //chanceSpellCrit = Math.Min(0.75f, (1 + _stats.BonusCritChance) * (baseSpellCrit + spellCritModifier) + .00005f); //fudge factor for rounding chanceSpellCrit = Math.Min(0.75f, baseSpellCrit + spellCritModifier + .00005f); //fudge factor for rounding float hasteBonus = StatConversion.GetHasteFromRating(_stats.HasteRating, _character.Class); unhastedMHSpeed = _character.MainHand == null ? 3.0f : _character.MainHand.Item.Speed; unhastedOHSpeed = _character.OffHand == null ? 3.0f : _character.OffHand.Item.Speed; float baseHastedMHSpeed = unhastedMHSpeed / (1f + hasteBonus) / (1f + _stats.PhysicalHaste); float baseHastedOHSpeed = unhastedOHSpeed / (1f + hasteBonus) / (1f + _stats.PhysicalHaste); float chanceToProcWFPerHit = .2f + (_character.ShamanTalents.GlyphofWindfuryWeapon ? .02f : 0f); if (_bossOpts.MultiTargs && _bossOpts.Targets != null && _bossOpts.Targets.Count > 0) { foreach (TargetGroup tg in _bossOpts.Targets) { if (tg.Frequency <= 0 || tg.Chance <= 0) { continue; //bad one, skip it } float upTime = (tg.Frequency / fightLength * (tg.Duration / 1000f) * tg.Chance) / fightLength; multiTargetMultiplier += (Math.Max(10, tg.NumTargs - (tg.NearBoss ? 0 : 1))) * upTime; } } //The Swing Loop //This is where we figure out feedback systems -- WF, MW, ED, Flurry, etc. //-------------- flurryUptime = 1f; uWUptime = 0f; //uFUptime = 0f; edUptime = 0f; float stormstrikeSpeed = firstPass ? (_talents.Stormstrike == 1 ? 8f : 0f) : AbilityCooldown(EnhanceAbility.StormStrike); float shockSpeed = firstPass ? BaseShockSpeed : AbilityCooldown(EnhanceAbility.EarthShock); float lavaLashSpeed = firstPass ? 10f : AbilityCooldown(EnhanceAbility.LavaLash); float fireNovaSpeed = firstPass ? BaseFireNovaSpeed : AbilityCooldown(EnhanceAbility.FireNova); if (_calcOpts.PriorityInUse(EnhanceAbility.MagmaTotem)) { fireTotemUptime = firstPass ? 1.0f : 60f / AbilityCooldown(EnhanceAbility.MagmaTotem); } else if (_calcOpts.PriorityInUse(EnhanceAbility.SearingTotem)) { fireTotemUptime = firstPass ? 1.0f : 60f / AbilityCooldown(EnhanceAbility.SearingTotem); } else if (_calcOpts.PriorityInUse(EnhanceAbility.RefreshTotems)) // if no Searing or Magma totem use refresh of Flametongue totem. { fireTotemUptime = firstPass ? 1.0f : 300f / AbilityCooldown(EnhanceAbility.RefreshTotems); } float mwPPM = (10f / 3f) * _talents.MaelstromWeapon; float flurryHasteBonus = .10f * _talents.Flurry; float uWHasteBonus = .4f + .1f * _talents.ElementalWeapons; float edCritBonus = .03f * _talents.ElementalDevastation; float staticShockChance = .15f * _character.ShamanTalents.StaticShock; hitsPerSMHSS = 0f; hitsPerSOHSS = 0f; hitsPerSOH = 0f; hitsPerSMH = 0f; hitsPerSWF = 0f; if (_talents.Stormstrike == 1) { hitsPerSMHSS = (1f - chanceYellowMissMH) / stormstrikeSpeed; hitsPerSOHSS = (1f - 2 * chanceYellowMissOH) / stormstrikeSpeed; //OH only swings if MH connect } hitsPerSLL = lavaLashSpeed == 0 ? 0f : (1f - chanceYellowMissOH) / lavaLashSpeed; float swingsPerSMHMelee = 0f; float swingsPerSOHMelee = 0f; float wfProcsPerSecond = 0f; float mwProcsPerSecond = 0f; secondsToFiveStack = 10f; float averageMeleeCritChance = (chanceWhiteCritMH + chanceWhiteCritOH + chanceYellowCritMH + chanceYellowCritOH) / 4f; float averageMeleeHitChance = ((1f - chanceWhiteMissMH - chanceDodgeMH - chanceParryMH) + (1f - chanceWhiteMissOH - chanceDodgeOH - chanceParryOH)) / 2f; float averageMeleeMissChance = (chanceWhiteMissMH + chanceWhiteMissOH) / 2f; float whiteHitsPerSMH = 0f; float whiteHitsPerSOH = 0f; float moteHitsPerS = 0f; float yellowHitsPerSMH = 0f; float yellowHitsPerSOH = 0f; float flameTongueHitsPerSecond = 0f; for (int i = 0; i < 5; i++) { // float bonusHaste = (1f + (flurryUptime * flurryHasteBonus)); float bonusHaste = 1 / (1 - flurryUptime + flurryUptime / (1 + flurryHasteBonus)) / (1 - uWUptime + uWUptime / (1 + uWHasteBonus)); // use time based not proc based flurryUptime hastedMHSpeed = baseHastedMHSpeed / bonusHaste; hastedOHSpeed = baseHastedOHSpeed / bonusHaste; swingsPerSMHMelee = 1f / hastedMHSpeed; swingsPerSOHMelee = (hastedOHSpeed == 0f) ? 0f : 1f / hastedOHSpeed; whiteHitsPerSMH = ChanceWhiteHitMH * swingsPerSMHMelee; whiteHitsPerSOH = ChanceWhiteHitOH * swingsPerSOHMelee; moteHitsPerS = _stats.MoteOfAnger * 2 * AverageWhiteHitChance; // Windfury model if (_calcOpts.MainhandImbue == "Windfury") { float hitsThatProcWFPerS = whiteHitsPerSMH + hitsPerSMHSS; if (unhastedOHSpeed != 0f) { hitsThatProcWFPerS += moteHitsPerS / 2; // half the hits will be OH and thus won't proc WF } else { hitsThatProcWFPerS += moteHitsPerS; // if no offhand then all motes will be MH weapon by definition } float maxExpectedWFPerFight = hitsThatProcWFPerS * chanceToProcWFPerHit * fightLength; float ineligibleSeconds = maxExpectedWFPerFight * (3.25f - hastedMHSpeed); float expectedWFPerFight = hitsThatProcWFPerS * chanceToProcWFPerHit * (fightLength - ineligibleSeconds); wfProcsPerSecond = expectedWFPerFight / fightLength; hitsPerSWF = 3f * wfProcsPerSecond * (1f - chanceYellowMissMH); } yellowHitsPerSMH = hitsPerSWF + hitsPerSMHSS; yellowHitsPerSOH = hitsPerSOHSS + hitsPerSLL; //Due to attack table, a white swing has the same chance to crit as a yellow hit // Old Flurry calc changed 10 Nov 2009 // couldCritSwingsPerSecond = whiteHitsPerSMH + whiteHitsPerSOH + yellowHitsPerSMH + yellowHitsPerSOH; // float swingsThatConsumeFlurryPerSecond = swingsPerSMHMelee + swingsPerSOHMelee; // flurryUptime = 1f - (float)Math.Pow(1 - averageMeleeCritChance, (3 / swingsThatConsumeFlurryPerSecond) * couldCritSwingsPerSecond); // old formulae flurryUptime = CalculateFlurryUptime(averageMeleeCritChance, averageMeleeHitChance, averageMeleeMissChance); //uWUptime = (float)Math.Max(12f / 15f, ??); //FIXME!!!! uWUptime = 7.5f / 15f; //Temp Uptime until above line is fixed // Maelstrom Weapon time to 5 stacks calc if (unhastedOHSpeed != 0f) { hitsPerSMH = whiteHitsPerSMH + yellowHitsPerSMH + moteHitsPerS / 2; hitsPerSOH = whiteHitsPerSOH + yellowHitsPerSOH + moteHitsPerS / 2; mwProcsPerSecond = (mwPPM / (60f / unhastedMHSpeed)) * hitsPerSMH + (mwPPM / (60f / unhastedOHSpeed)) * hitsPerSOH; } else { hitsPerSMH = whiteHitsPerSMH + yellowHitsPerSMH + moteHitsPerS; hitsPerSOH = 0f; mwProcsPerSecond = (mwPPM / (60f / unhastedMHSpeed)) * hitsPerSMH; } secondsToFiveStack = 5f / mwProcsPerSecond; // Elemental Devastation Uptime calc staticShocksPerSecond = (hitsPerSLL + hitsPerSMHSS + hitsPerSOHSS) * staticShockChance; flameTongueHitsPerSecond = (_calcOpts.MainhandImbue == "Flametongue" ? HitsPerSMH : 0f) + ((_calcOpts.OffhandImbue == "Flametongue") ? HitsPerSOH : 0f); spellAttacksPerSec = (1f / secondsToFiveStack + 1f / shockSpeed + 1f / fireNovaSpeed + staticShocksPerSecond) // + flameTongueHitsPerSecond) * (1f - chanceSpellMiss); float couldCritSpellsPerS = spellAttacksPerSec; edUptime = 1f - (float)Math.Pow(1 - chanceSpellCrit, 10 * couldCritSpellsPerS); averageMeleeCritChance = (chanceWhiteCritMH + chanceWhiteCritOH + chanceYellowCritMH + chanceYellowCritOH) / 4f + edUptime * edCritBonus; } float yellowAttacksPerSecond = hitsPerSWF + hitsPerSMHSS; if (unhastedMHSpeed != 0) { yellowAttacksPerSecond += hitsPerSOHSS; } // set output variables edBonusCrit = edUptime * edCritBonus; //SetCritValues((1 + _stats.BonusCritChance) * (baseMeleeCrit + meleeCritModifier) + edBonusCrit + .00005f); //fudge factor for rounding SetCritValues(baseMeleeCrit + meleeCritModifier + edBonusCrit + .00005f); //fudge factor for rounding meleeAttacksPerSec = hitsPerSMH + hitsPerSOH; meleeCritsPerSec = (whiteHitsPerSMH * chanceWhiteCritMH) + (whiteHitsPerSOH * chanceWhiteCritOH) + (yellowHitsPerSMH * chanceYellowCritMH) + (yellowHitsPerSOH * chanceYellowCritOH) + (_stats.MoteOfAnger * 2 * AverageWhiteCritChance); spellCritsPerSec = spellAttacksPerSec * ChanceSpellCrit; spellCastsPerSec = spellAttacksPerSec; spellMissesPerSec = spellAttacksPerSec * chanceSpellMiss; chanceMeleeHit = meleeAttacksPerSec / (swingsPerSMHMelee + swingsPerSOHMelee + 2f * wfProcsPerSecond + .25f + 1f / 6f); maxMana = _stats.Mana; float spellhaste = _stats.SpellHaste + StatConversion.GetSpellHasteFromRating(_stats.HasteRating); averageFSDotTime = _talents.GlyphofFlameShock ? 27f : 18f; averageFSTickTime = 3f / (1f + spellhaste); }