private float CalcDamageProc( SpecialEffect effect, float damagePerProc, Dictionary <int, float> periods, Dictionary <int, float> chances, SpellModifiers modifiers) { damagePerProc *= modifiers.GetFinalDirectMultiplier() * (1 + (modifiers.GetFinalCritMultiplier() - 1) * modifiers.CritChance) * (1 - StatConversion.GetAverageResistance( 80, Options.TargetLevel, 0f, 0f)); float numProcs = Options.Duration * effect.GetAverageProcsPerSecond( periods[(int)effect.Trigger], chances[(int)effect.Trigger], CalculationsWarlock.AVG_UNHASTED_CAST_TIME, Options.Duration); return(numProcs * damagePerProc); }
public Stats getSpecialEffects(SpecialEffect effect) { Stats statsAverage = new Stats(); if (effect == mainHandEnchant || effect == offHandEnchant) { if (mainHandEnchant != null && !mhProcessed) { statsAverage.Accumulate(mainHandEnchant.Stats, GetMHUptime()); mhProcessed = true; } else if (offHandEnchant != null && !ohProcessed) { statsAverage.Accumulate(offHandEnchant.Stats, GetOHUptime()); ohProcessed = true; } } else if (effect.Trigger == Trigger.Use) { effect.AccumulateAverageStats(statsAverage); foreach (SpecialEffect e in effect.Stats.SpecialEffects()) statsAverage.Accumulate(this.getSpecialEffects(e) * (effect.Duration / effect.Cooldown)); } else { SetTriggerChanceAndSpeed(effect); foreach (SpecialEffect e in effect.Stats.SpecialEffects()) // deal with secondary effects { statsAverage.Accumulate(this.getSpecialEffects(e)); } if (effect.MaxStack > 1) { if (effect.Stats.MoteOfAnger > 0) { // When in effect stats, MoteOfAnger is % of melee hits // When in character stats, MoteOfAnger is average procs per second statsAverage.Accumulate(new Stats() { MoteOfAnger = effect.Stats.MoteOfAnger * effect.GetAverageProcsPerSecond(trigger, chance, unhastedAttackSpeed, 0f) / effect.MaxStack }); } else { float timeToMax = (float)Math.Min(_cs.FightLength, effect.GetChance(unhastedAttackSpeed) * trigger * effect.MaxStack); float buffDuration = _cs.FightLength; if (effect.Stats.AttackPower == 250f || effect.Stats.AttackPower == 215f || effect.Stats.HasteRating == 57f || effect.Stats.HasteRating == 64f) { buffDuration = 20f; } if (timeToMax * .5f > buffDuration) { timeToMax = 2 * buffDuration; } statsAverage.Accumulate(effect.Stats * (effect.MaxStack * (((buffDuration) - .5f * timeToMax) / (buffDuration)))); } } else { effect.AccumulateAverageStats(statsAverage, trigger, chance, unhastedAttackSpeed); } } return statsAverage; }
// Functions public float GetActivates(float landedatksoverdur) { if (AbilIterater != -1 && !CalcOpts.Maintenance[AbilIterater]) { return(0f); } float acts = Buff.GetAverageProcsPerSecond(landedatksoverdur / FightDuration, 1f /*MHAtkTable.AnyLand*/, combatFactors._c_mhItemSpeed, FightDuration); acts *= FightDuration; if (StatS.BonusWarrior_T10_4P_BSSDProcChange > 0) { acts *= 1.20f; } return(acts * (1f - Whiteattacks.RageSlip(FightDuration / acts, RageCost + UsedExtraRage))); }
private float CalculateTotalDamagePerSecond(SpecialEffect effect, float baseDamage, ItemDamageType type) { float totalDamage = 0f; float totalDamagePerSec = 0f; float totalNumProcs = 0f; float totalNumProcsPerSec = 0f; try { float triggerInterval = TriggerIntervals[effect.Trigger]; float triggerChance = TriggerChances[effect.Trigger]; if (TriggerChances.ContainsKey(Trigger.MainHandHit) && MainHandEffects.Contains(effect) && (!OffHandEffects.Contains(effect) || !dualWieldProcs)) { triggerInterval = TriggerIntervals[Trigger.MainHandHit]; triggerChance = TriggerChances[Trigger.MainHandHit]; if (OffHandEffects.Contains(effect)) { dualWieldProcs = true; } } else if (TriggerChances.ContainsKey(Trigger.OffHandHit) && OffHandEffects.Contains(effect)) { triggerInterval = TriggerIntervals[Trigger.OffHandHit]; triggerChance = TriggerChances[Trigger.OffHandHit]; } // Process the Effects totalNumProcsPerSec = effect.GetAverageProcsPerSecond(triggerInterval, triggerChance, Char.MainHand.Speed, FightDuration); totalNumProcs = totalNumProcsPerSec * FightDuration; totalDamage = totalNumProcs * CalculateThisDamage(type, baseDamage); totalDamagePerSec = totalDamage / FightDuration; // Set our Results into the Dictionaries TotalDamage[type] += totalDamage; TotalDamagePerSec[type] += totalDamagePerSec; TotalNumProcs[type] += totalNumProcs; TotalNumProcsPerSec[type] += totalNumProcsPerSec; } catch (Exception ex) { new Base.ErrorBox() { Title = "Error calculating special proc DPS", Function = "CalculateTotalDamagePerSecond(...)", TheException = ex, }.Show(); } return(totalDamagePerSec); }
//private static Dictionary<float, SpecialEffect> _SE_StrikesOfOpportunity = new Dictionary<float,SpecialEffect>(); public float GetActivates(float meleeAttemptsOverDur, float perc) { // This attack doesn't consume GCDs and doesn't affect the swing timer //float effectActs = _SE_StrikesOfOpportunity[DPSWarrChar.StatS.MasteryRating].GetAverageProcsPerSecond( var se = new SpecialEffect(Trigger.MeleeAttack, null, 0f, 0.5f, (float)Math.Min(Skills.StrikesOfOpportunity.BaseChance + (float)Math.Max(0f, Skills.StrikesOfOpportunity.BonusChance * StatConversion.GetMasteryFromRating(DPSWarrChar.StatS.MasteryRating, CharacterClass.Warrior)), 1f) ) { BypassCache = true }; float effectActs = se.GetAverageProcsPerSecond((FightDuration * perc) / meleeAttemptsOverDur, 1f, DPSWarrChar.CombatFactors.CMHItemSpeed, FightDuration * perc); effectActs *= FightDuration * perc; return(effectActs); }
public ProcEffect(SpecialEffect effect) { this.Effect = effect; // Shadow damage procs - most widely varied at the current moment if (effect.Stats.ShadowDamage > 0) { CalculateDPS = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float sp, float sHi, float sc, float sHa) { SpecialEffect e = Effect; float specialDamageModifier = (1 + c.BasicStats.BonusSpellPowerMultiplier) * (1 + c.BasicStats.BonusShadowDamageMultiplier) * (1 + c.BasicStats.BonusDamageMultiplier); float triggerInterval = 0.0f; switch (e.Trigger) { case Trigger.DoTTick: // Extract triggerInterval = r.Duration / r.DotTicks; break; case Trigger.SpellHit: // Pendulum triggerInterval = r.Duration / r.CastCount; break; case Trigger.DamageDone: // DMC: Death triggerInterval = r.Duration / (r.CastCount + r.DotTicks); break; case Trigger.DamageOrHealingDone: // DMC: Greatness // Need to add Self-Heals triggerInterval = r.Duration / (r.CastCount + r.DotTicks); break; default: return(0.0f); } float procsPerSecond = e.GetAverageProcsPerSecond(triggerInterval, 1.0f, 3.0f, c.FightLength * 60.0f); return(e.Stats.ShadowDamage * specialDamageModifier * procsPerSecond); }; } // Lightning Capacitor, Thunder Capacitor, Reign of the Unliving/Undead, Nibelung else if (effect.Stats.NatureDamage > 0 || effect.Stats.FireDamage > 0 || effect.Stats.ValkyrDamage > 0) { if (effect.Stats.NatureDamage > 0) { CalculateDPS = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float sp, float sHi, float sc, float sHa) { float specialDamageModifier = (1 + c.BasicStats.BonusSpellPowerMultiplier) * (1 + c.BasicStats.BonusNatureDamageMultiplier) * (1 + c.BasicStats.BonusDamageMultiplier); float procsPerSecond = Effect.GetAverageProcsPerSecond(r.Duration / (r.CastCount * sc), 1.0f, 3.0f, c.FightLength * 60.0f); return(Effect.Stats.NatureDamage * specialDamageModifier * procsPerSecond); }; } else if (effect.Stats.FireDamage > 0) { CalculateDPS = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float sp, float sHi, float sc, float sHa) { float specialDamageModifier = (1 + c.BasicStats.BonusSpellPowerMultiplier) * (1 + c.BasicStats.BonusFireDamageMultiplier) * (1 + c.BasicStats.BonusDamageMultiplier); float procsPerSecond = Effect.GetAverageProcsPerSecond(r.Duration / (r.CastCount * sc), 1.0f, 3.0f, c.FightLength * 60.0f); return(Effect.Stats.FireDamage * specialDamageModifier * procsPerSecond); }; } else { CalculateDPS = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float sp, float sHi, float sc, float sHa) { float specialDamageModifier = (1 + c.BasicStats.BonusSpellPowerMultiplier) * (1 + c.BasicStats.BonusDamageMultiplier); float procsPerSecond = Effect.GetAverageProcsPerSecond(r.Duration / (r.CastCount * sc), 1.0f, 3.0f, c.FightLength * 60.0f); return(Effect.Stats.ValkyrDamage * specialDamageModifier * procsPerSecond); }; } } else if (effect.Stats.Mp5 > 0) { CalculateMP5 = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float sp, float sHi, float sc, float sHa) { SpecialEffect e = Effect; float procsPerSecond = e.GetAverageProcsPerSecond(r.Duration / r.CastCount, 1.0f, 3.0f, c.FightLength * 60f); return((e.Stats.Mp5 / 5.0f * e.Duration) * procsPerSecond * 5.0f); }; } // Moonkin 4T8 set bonus (15% chance on IS tick to proc an instant-cast Starfire) else if (effect.Stats.StarfireProc == 1) { CalculateDPS = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float sp, float sHi, float sc, float sHa) { if (r.InsectSwarmTicks == 0) { return(0.0f); } Spell newSF = new Spell() { AllDamageModifier = r.Solver.Starfire.AllDamageModifier, BaseCastTime = 1.5f, BaseDamage = r.Solver.Starfire.BaseDamage, BaseManaCost = r.Solver.Starfire.BaseManaCost, CriticalChanceModifier = r.Solver.Starfire.CriticalChanceModifier, CriticalDamageModifier = r.Solver.Starfire.CriticalDamageModifier, DotEffect = null, IdolExtraSpellPower = r.Solver.Starfire.IdolExtraSpellPower, Name = r.Solver.Starfire.Name, School = r.Solver.Starfire.School, SpellDamageModifier = r.Solver.Starfire.SpellDamageModifier }; r.DoSpecialStarfire(c, ref newSF, sp, sHi, sc, sHa); float timeBetweenProcs = r.Solver.InsectSwarm.DotEffect.TickLength / Effect.Chance; float replaceWrathWithSFDPS = (newSF.DamagePerHit / newSF.CastTime) - (r.Solver.Wrath.DamagePerHit / r.Solver.Wrath.CastTime); float replaceSFWithSFDPS = (newSF.DamagePerHit / newSF.CastTime) - (r.Solver.Starfire.DamagePerHit / r.Solver.Starfire.CastTime); return((replaceWrathWithSFDPS * (r.WrathCount / (r.WrathCount + r.StarfireCount)) + replaceSFWithSFDPS * (r.StarfireCount / (r.WrathCount + r.StarfireCount))) / timeBetweenProcs); }; } else if (Effect.Stats._rawSpecialEffectDataSize == 0 && (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.SpellMiss || Effect.Trigger == Trigger.Use || Effect.Trigger == Trigger.MoonfireCast || Effect.Trigger == Trigger.InsectSwarmOrMoonfireTick || Effect.Trigger == Trigger.MoonfireTick || Effect.Trigger == Trigger.InsectSwarmTick || Effect.Trigger == Trigger.DoTTick) && (Effect.Stats.HasteRating > 0 || Effect.Stats.SpellHaste > 0 || Effect.Stats.CritRating > 0 || Effect.Stats.HighestStat > 0)) { Activate = delegate(Character ch, CharacterCalculationsMoonkin c, ref float sp, ref float sHi, ref float sc, ref float sHa) { SpecialEffect e = Effect; int maxStack = e.MaxStack; Stats st = e.Stats; float critRating = st.CritRating; float spellCrit = StatConversion.GetSpellCritFromRating(critRating * maxStack); float hasteRating = st.HasteRating; float spellHaste = StatConversion.GetSpellHasteFromRating(hasteRating * maxStack); spellHaste += st.SpellHaste; float highestStat = st.HighestStat; if (critRating > 0) { sc += spellCrit; } if (spellHaste > 0) { sHa += spellHaste; } if (highestStat > 0) { if (c.BasicStats.Spirit > c.BasicStats.Intellect) { Stats s = c.BasicStats.Clone(); s.Spirit += highestStat; CharacterCalculationsMoonkin cNew = CalculationsMoonkin.GetInnerCharacterCalculations(ch, s, null); storedStats.SpellPower = cNew.SpellPower - c.SpellPower; sp += storedStats.SpellPower; } else { Stats s = c.BasicStats.Clone(); s.Intellect += highestStat; CharacterCalculationsMoonkin cNew = CalculationsMoonkin.GetInnerCharacterCalculations(ch, s, null); storedStats.SpellPower = cNew.SpellPower - c.SpellPower; storedStats.SpellCrit = cNew.SpellCrit - c.SpellCrit; sp += storedStats.SpellPower; sc += storedStats.SpellCrit; } } }; Deactivate = delegate(Character ch, CharacterCalculationsMoonkin c, ref float sp, ref float sHi, ref float sc, ref float sHa) { SpecialEffect e = Effect; int maxStack = e.MaxStack; Stats st = e.Stats; float critRating = st.CritRating; float spellCrit = StatConversion.GetSpellCritFromRating(critRating * maxStack); float hasteRating = st.HasteRating; float spellHaste = StatConversion.GetSpellHasteFromRating(hasteRating * maxStack); spellHaste += st.SpellHaste; float highestStat = st.HighestStat; if (critRating > 0) { sc -= spellCrit; } if (spellHaste > 0) { sHa -= spellHaste; } if (highestStat > 0) { sp -= storedStats.SpellPower; if (c.BasicStats.Intellect >= c.BasicStats.Spirit) { sc -= storedStats.SpellCrit; } } }; UpTime = delegate(SpellRotation r, CharacterCalculationsMoonkin c) { float upTime = 0.0f; switch (Effect.Trigger) { case Trigger.Use: upTime = Effect.GetAverageUptime(0f, 1f); break; case Trigger.SpellHit: case Trigger.DamageSpellHit: upTime = Effect.GetAverageUptime(r.Duration / r.CastCount, r.Solver.GetSpellHit(c)); break; case Trigger.DamageSpellCrit: case Trigger.SpellCrit: upTime = Effect.GetAverageUptime(r.Duration / (r.CastCount - (r.InsectSwarmTicks / r.Solver.InsectSwarm.DotEffect.NumberOfTicks)), c.SpellCrit); break; case Trigger.SpellCast: case Trigger.DamageSpellCast: upTime = Effect.GetAverageUptime(r.Duration / r.CastCount, 1f); break; case Trigger.MoonfireCast: upTime = Effect.GetAverageUptime(r.Duration / r.MoonfireCasts, 1f); break; case Trigger.InsectSwarmOrMoonfireTick: upTime = Effect.GetAverageUptime(r.Duration / (r.InsectSwarmTicks + r.MoonfireTicks), 1f); break; case Trigger.MoonfireTick: upTime = Effect.GetAverageUptime(r.Duration / r.MoonfireTicks, 1f); break; case Trigger.InsectSwarmTick: upTime = Effect.GetAverageUptime(r.Duration / r.InsectSwarmTicks, 1f); break; case Trigger.DoTTick: upTime = Effect.GetAverageUptime(r.Duration / (r.MoonfireTicks + r.InsectSwarmTicks), 1f); break; case Trigger.DamageDone: case Trigger.DamageOrHealingDone: upTime = Effect.GetAverageUptime(((r.Duration / r.CastCount) + (r.Duration / (r.MoonfireTicks + r.InsectSwarmTicks))) / 2.0f, 1f); break; default: break; } return(upTime); }; } }
private float CalculateTotalDamagePerSecond(SpecialEffect effect, float baseDamage, ItemDamageType type) { float totalDamage = 0f; float totalDamagePerSec = 0f; float totalNumProcs = 0f; float totalNumProcsPerSec = 0f; try { float triggerInterval = TriggerIntervals[effect.Trigger]; float triggerChance = TriggerChances[effect.Trigger]; if (TriggerChances.ContainsKey(Trigger.MainHandHit) && MainHandEffects.Contains(effect) && (!OffHandEffects.Contains(effect) || !dualWieldProcs)) { triggerInterval = TriggerIntervals[Trigger.MainHandHit]; triggerChance = TriggerChances[Trigger.MainHandHit]; if (OffHandEffects.Contains(effect)) dualWieldProcs = true; } else if (TriggerChances.ContainsKey(Trigger.OffHandHit) && OffHandEffects.Contains(effect)) { triggerInterval = TriggerIntervals[Trigger.OffHandHit]; triggerChance = TriggerChances[Trigger.OffHandHit]; } // Process the Effects totalNumProcsPerSec = effect.GetAverageProcsPerSecond(triggerInterval, triggerChance, Char.MainHand.Speed, FightDuration); totalNumProcs = totalNumProcsPerSec * FightDuration; totalDamage = totalNumProcs * CalculateThisDamage(type, baseDamage); totalDamagePerSec = totalDamage / FightDuration; // Set our Results into the Dictionaries TotalDamage[type] += totalDamage; TotalDamagePerSec[type] += totalDamagePerSec; TotalNumProcs[type] += totalNumProcs; TotalNumProcsPerSec[type] += totalNumProcsPerSec; } catch (Exception ex) { new Base.ErrorBox() { Title = "Error calculating special proc DPS", Function = "CalculateTotalDamagePerSecond(...)", TheException = ex, }.Show(); } return totalDamagePerSec; }
private float CalcDamageProc(SpecialEffect effect, float damagePerProc, Dictionary<int, float> periods, Dictionary<int, float> chances, SpellModifiers modifiers) { damagePerProc *= modifiers.GetFinalDirectMultiplier() * (1 + (modifiers.GetFinalCritMultiplier() - 1) * modifiers.CritChance) * (1 - StatConversion.GetAverageResistance(CalcOpts.PlayerLevel, CalcOpts.TargetLevel, 0f, 0f)); float numProcs = BossOpts.BerserkTimer * effect.GetAverageProcsPerSecond( periods[(int)effect.Trigger], chances[(int)effect.Trigger], CalculationsWarlock.AVG_UNHASTED_CAST_TIME, BossOpts.BerserkTimer); return numProcs * damagePerProc; }
public Stats getSpecialEffects(SpecialEffect effect) { Stats statsAverage = new Stats(); if (effect == mainHandEnchant || effect == offHandEnchant) { if (mainHandEnchant != null && !mhProcessed) { statsAverage.Accumulate(mainHandEnchant.Stats, GetMHUptime()); mhProcessed = true; } else if (offHandEnchant != null && !ohProcessed) { statsAverage.Accumulate(offHandEnchant.Stats, GetOHUptime()); ohProcessed = true; } } else if (effect.Trigger == Trigger.Use) { effect.AccumulateAverageStats(statsAverage); foreach (SpecialEffect e in effect.Stats.SpecialEffects()) { statsAverage.Accumulate(this.getSpecialEffects(e) * (effect.Duration / effect.Cooldown)); } } else { SetTriggerChanceAndSpeed(effect); foreach (SpecialEffect e in effect.Stats.SpecialEffects()) // deal with secondary effects { statsAverage.Accumulate(this.getSpecialEffects(e)); } if (effect.MaxStack > 1) { if (effect.Stats.MoteOfAnger > 0) { // When in effect stats, MoteOfAnger is % of melee hits // When in character stats, MoteOfAnger is average procs per second statsAverage.Accumulate(new Stats() { MoteOfAnger = effect.Stats.MoteOfAnger * effect.GetAverageProcsPerSecond(trigger, chance, unhastedAttackSpeed, 0f) / effect.MaxStack }); } else { float timeToMax = (float)Math.Min(_cs.FightLength, effect.GetChance(unhastedAttackSpeed) * trigger * effect.MaxStack); float buffDuration = _cs.FightLength; if (effect.Stats.AttackPower == 250f || effect.Stats.AttackPower == 215f || effect.Stats.HasteRating == 57f || effect.Stats.HasteRating == 64f) { buffDuration = 20f; } if (timeToMax * .5f > buffDuration) { timeToMax = 2 * buffDuration; } statsAverage.Accumulate(effect.Stats * (effect.MaxStack * (((buffDuration) - .5f * timeToMax) / (buffDuration)))); } } else { effect.AccumulateAverageStats(statsAverage, trigger, chance, unhastedAttackSpeed); } } return(statsAverage); }
private float ApplySpecialEffect(SpecialEffect effect, DPSWarrCharacter charStruct, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, ref Base.StatsWarrior applyTo) { #if DEBUG //ConstructionCounts["ApplySpecialEffect"]++; #endif float fightDuration = charStruct.BossOpts.BerserkTimer; float fightDuration2Pass = charStruct.CalcOpts.SE_UseDur ? fightDuration : 0; Stats effectStats = effect.Stats; float upTime = 0f; //float avgStack = 1f; /*if (effect.Stats.TargetArmorReduction > 0f || effect.Stats.ArmorPenetrationRating > 0f) { //int j = 0; }*/ if (effect.Trigger == Trigger.Use) { if (effect.Stats._rawSpecialEffectDataSize == 1) { upTime = effect.GetAverageUptime(0f, 1f, charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); //float uptime = (effect.Cooldown / fightDuration); List<SpecialEffect> nestedEffect = new List<SpecialEffect>(); nestedEffect.Add(effect.Stats._rawSpecialEffectData[0]); Base.StatsWarrior _stats2 = new Base.StatsWarrior(); ApplySpecialEffect(effect.Stats._rawSpecialEffectData[0], charStruct, triggerIntervals, triggerChances, ref _stats2); effectStats = _stats2; } else { upTime = effect.GetAverageStackSize(0f, 1f, charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); } } else if (effect.Duration == 0f && triggerIntervals.ContainsKey(effect.Trigger) && !float.IsInfinity(triggerIntervals[effect.Trigger])) { upTime = effect.GetAverageProcsPerSecond(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); } else if (effect.Trigger == Trigger.ExecuteHit) { upTime = effect.GetAverageStackSize(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass * (float)charStruct.BossOpts.Under20Perc); } else if (triggerIntervals.ContainsKey(effect.Trigger) && !float.IsInfinity(triggerIntervals[effect.Trigger])) { upTime = effect.GetAverageStackSize(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); } if (upTime > 0f) { if (effect.Duration == 0f) { applyTo.Accumulate(effectStats, upTime * fightDuration); } else if (upTime <= effect.MaxStack) { applyTo.Accumulate(effectStats, upTime); } return upTime; } return 0f; }
private Stats IterativeSpecialEffectsStats(DPSWarrCharacter charStruct, List<SpecialEffect> specialEffects, List<SpecialEffect> critEffects, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, float oldFlurryUptime, bool iterate, Base.StatsWarrior iterateOld, Base.StatsWarrior originalStats) { #if DEBUG //ConstructionCounts["IterativeSpecialEffectsStats"]++; #endif WarriorTalents talents = charStruct.Char.WarriorTalents; float fightDuration = charStruct.BossOpts.BerserkTimer; Base.StatsWarrior statsProcs = new Base.StatsWarrior(); try { float dmgTakenInterval = fightDuration / charStruct.BossOpts.AoETargsFreq; //float attempted = charStruct.Rot.AttemptedAtksOverDur; //float land = charStruct.Rot.LandedAtksOverDur; //float crit = charStruct.Rot.CriticalAtksOverDur; //int LevelDif = charStruct.bossOpts.Level - charStruct.Char.Level; List<Trigger> critTriggers = new List<Trigger>(); List<float> critWeights = new List<float>(); bool needsHitTableReset = false; foreach (SpecialEffect effect in critEffects) { needsHitTableReset = true; critTriggers.Add(effect.Trigger); critWeights.Add(1f); } foreach (SpecialEffect effect in specialEffects) { #region old arp code /*if (effect.Stats.ArmorPenetrationRating > 0) { float arpenBuffs = ((combatFactors.CmhItemType == ItemType.TwoHandMace) ? talents.MaceSpecialization * 0.03f : 0.00f) + (!calcOpts.FuryStance ? (0.10f + originalStats.BonusWarrior_T9_2P_ArP) : 0.0f); float OriginalArmorReduction = StatConversion.GetArmorDamageReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.ArmorPenetration, arpenBuffs, originalStats.ArmorPenetrationRating); float ProccedArmorReduction = StatConversion.GetArmorDamageReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.ArmorPenetration + effect.Stats.ArmorPenetration, arpenBuffs, originalStats.ArmorPenetrationRating + effect.Stats.ArmorPenetrationRating); Stats dummyStats = new Stats(); float procUptime = ApplySpecialEffect(effect, Char, Rot, combatFactors, calcOpts, originalStats.Dodge + originalStats.Parry, ref dummyStats); float targetReduction = ProccedArmorReduction * procUptime + OriginalArmorReduction * (1f - procUptime); //float arpDiff = OriginalArmorReduction - targetReduction; float procArp = StatConversion.GetRatingFromArmorReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.ArmorPenetration, arpenBuffs, targetReduction); statsProcs.ArmorPenetrationRating += (procArp - originalStats.ArmorPenetrationRating); } else */ #endregion float numProcs = 0; if (effect.Stats.ManaorEquivRestore > 0f && effect.Stats.HealthRestoreFromMaxHealth > 0f) { // effect.Duration = 0, so GetAverageStats won't work float value1 = effect.Stats.ManaorEquivRestore; float value2 = effect.Stats.HealthRestoreFromMaxHealth; SpecialEffect dummy = new SpecialEffect(effect.Trigger, new Stats() { ManaorEquivRestore = value1, HealthRestoreFromMaxHealth = value2 }, effect.Duration, effect.Cooldown, effect.Chance) { BypassCache = true }; numProcs = dummy.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration; statsProcs.ManaorEquivRestore += dummy.Stats.ManaorEquivRestore * numProcs; dummy.Stats.ManaorEquivRestore = 0f; //numProcs = effect.GetAverageProcsPerSecond(triggerIntervals[Trigger.PhysicalCrit], triggerChances[Trigger.PhysicalCrit], 0f, 0f) * fightDuration; //statsProcs.HealthRestoreFromMaxHealth += effect.Stats.HealthRestoreFromMaxHealth * numProcs; ApplySpecialEffect(dummy, charStruct, triggerIntervals, triggerChances, ref statsProcs); } else if (effect.Stats.ManaorEquivRestore > 0f) { // effect.Duration = 0, so GetAverageStats won't work numProcs = effect.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration; statsProcs.ManaorEquivRestore += effect.Stats.ManaorEquivRestore * numProcs; } else if (effect.Stats.HealthRestoreFromMaxHealth > 0f) { // effect.Duration = 0, so GetAverageStats won't work numProcs = effect.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration; statsProcs.HealthRestoreFromMaxHealth += effect.Stats.HealthRestoreFromMaxHealth * numProcs; } else { ApplySpecialEffect(effect, charStruct, triggerIntervals, triggerChances, ref statsProcs); } } WeightedStat[] critProcs; if (critEffects.Count == 0) { critProcs = new WeightedStat[] { new WeightedStat() { Value = 0f, Chance = 1f } }; } else if (critEffects.Count == 1) { float interval = triggerIntervals[critEffects[0].Trigger]; float chance = triggerChances[critEffects[0].Trigger]; float upTime = critEffects[0].GetAverageStackSize(interval, chance, charStruct.CombatFactors.CMHItemSpeed, (charStruct.CalcOpts.SE_UseDur ? charStruct.BossOpts.BerserkTimer : 0f)); upTime *= critWeights[0]; critProcs = new WeightedStat[] { new WeightedStat() { Value = critEffects[0].Stats.CritRating, Chance = upTime }, new WeightedStat() { Value = 0f, Chance = 1f - upTime } }; } else { float[] intervals = new float[critEffects.Count]; float[] chances = new float[critEffects.Count]; float[] offset = new float[critEffects.Count]; for (int i = 0; i < critEffects.Count; i++) { intervals[i] = triggerIntervals[critEffects[i].Trigger]; chances[i] = triggerChances[critEffects[i].Trigger]; } critProcs = SpecialEffect.GetAverageCombinedUptimeCombinations(critEffects.ToArray(), intervals, chances, offset, critWeights.ToArray(), charStruct.CombatFactors.CMHItemSpeed, charStruct.BossOpts.BerserkTimer, AdditiveStat.CritRating); } charStruct.CombatFactors.CritProcs = critProcs; float flurryUptime = 0f; if (iterate && talents.Flurry > 0f && charStruct.CombatFactors.FuryStance && charStruct.Char.MainHand != null && charStruct.Char.MainHand.Item != null) { float numFlurryHits = 3f; // default float mhPerc = 1f; // 100% by default float flurryHaste = 0.25f / 3f * talents.Flurry; bool useOffHand = false; float flurryHitsPerSec = charStruct.CombatFactors.TotalHaste * (1f + flurryHaste) / (1f + flurryHaste * oldFlurryUptime); float temp = 1f / charStruct.Char.MainHand.Item.Speed; if (charStruct.Char.OffHand != null && charStruct.Char.OffHand.Item != null) { useOffHand = true; temp += 1f / charStruct.Char.OffHand.Item.Speed; mhPerc = (charStruct.Char.MainHand.Speed / charStruct.Char.OffHand.Speed) / (1f + charStruct.Char.MainHand.Speed / charStruct.Char.OffHand.Speed); if (charStruct.Char.OffHand.Speed == charStruct.Char.MainHand.Speed) numFlurryHits = 4f; } flurryHitsPerSec *= temp; float flurryDuration = numFlurryHits / flurryHitsPerSec; flurryUptime = 1f; foreach (AbilityWrapper aw in charStruct.Rot.DamagingAbilities) { if (aw.Ability.CanCrit && aw.AllNumActivates > 0f) { float tempFactor = (float) Math.Pow(1f - aw.Ability.MHAtkTable.Crit, flurryDuration* (aw.AllNumActivates*aw.Ability.SwingsPerActivate* aw.Ability.AvgTargets/fightDuration)); flurryUptime *= tempFactor; if (aw.Ability.SwingsOffHand && useOffHand) { flurryUptime *= (float) Math.Pow(1f - aw.Ability.OHAtkTable.Crit, flurryDuration* (aw.AllNumActivates*aw.Ability.SwingsPerActivate* aw.Ability.AvgTargets/fightDuration)); } } } flurryUptime *= (float)Math.Pow(1f - charStruct.Rot.DPSWarrChar.Whiteattacks.MHAtkTable.Crit, numFlurryHits * mhPerc); flurryUptime *= (float)Math.Pow(1f - charStruct.Rot.DPSWarrChar.Whiteattacks.OHAtkTable.Crit, numFlurryHits * (1f - mhPerc)); flurryUptime = 1f - flurryUptime; statsProcs.PhysicalHaste = (1f + statsProcs.PhysicalHaste) * (1f + flurryHaste * flurryUptime) - 1f; } charStruct.CombatFactors.StatS = UpdateStatsAndAdd(statsProcs, originalStats, charStruct.Char); charStruct.CombatFactors.InvalidateCache(); //Rot.InvalidateCache(); if (iterate) { const float precisionWhole = 0.01f; const float precisionDec = 0.0001f; if (statsProcs.Agility - iterateOld.Agility > precisionWhole || statsProcs.HasteRating - iterateOld.HasteRating > precisionWhole || statsProcs.HitRating - iterateOld.HitRating > precisionWhole || statsProcs.CritRating - iterateOld.CritRating > precisionWhole || statsProcs.PhysicalHaste - iterateOld.PhysicalHaste > precisionDec || statsProcs.PhysicalCrit - iterateOld.PhysicalCrit > precisionDec || statsProcs.PhysicalHit - iterateOld.PhysicalHit > precisionDec) { if (needsHitTableReset) charStruct.Rot.ResetHitTables(); charStruct.Rot.DoIterations(); CalculateTriggers(charStruct, triggerIntervals, triggerChances); return IterativeSpecialEffectsStats(charStruct, specialEffects, critEffects, triggerIntervals, triggerChances, flurryUptime, true, statsProcs, originalStats); } else { /*int j = 0;*/ } } return statsProcs; } catch (Exception ex) { new Base.ErrorBox() { Title = "Error in creating SpecialEffects Stats", Function = "GetSpecialEffectsStats()", TheException = ex, }.Show(); return new Stats(); } }
public ProcEffect(SpecialEffect effect) { this.Effect = effect; // Damage procs - unified code handler if (effect.Stats.ShadowDamage > 0 || effect.Stats.FireDamage > 0 || effect.Stats.FrostDamage > 0 || effect.Stats.NatureDamage > 0 || effect.Stats.HolyDamage > 0 || effect.Stats.ArcaneDamage > 0 || effect.Stats.HolySummonedDamage > 0) { CalculateDPS = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float fightLength, float sp, float sHi, float sc, float sHa) { SpecialEffect e = Effect; float schoolModifier = 1 + (effect.Stats.ShadowDamage > 0 ? c.BasicStats.BonusShadowDamageMultiplier : 0) + (effect.Stats.FireDamage > 0 ? c.BasicStats.BonusFireDamageMultiplier : 0) + (effect.Stats.FrostDamage > 0 ? c.BasicStats.BonusFrostDamageMultiplier : 0) + (effect.Stats.NatureDamage > 0 ? c.BasicStats.BonusNatureDamageMultiplier : 0) + (effect.Stats.HolyDamage > 0 ? c.BasicStats.BonusHolyDamageMultiplier : 0) + (effect.Stats.ArcaneDamage > 0 ? c.BasicStats.BonusArcaneDamageMultiplier : 0); float specialDamageModifier = schoolModifier; float baseValue = e.Stats.ShadowDamage + e.Stats.FireDamage + e.Stats.FrostDamage + e.Stats.NatureDamage + e.Stats.HolyDamage + e.Stats.ArcaneDamage + e.Stats.HolySummonedDamage; float triggerInterval = 0.0f, triggerChance = 1.0f; switch (e.Trigger) { case Trigger.DoTTick: triggerInterval = r.RotationData.Duration / r.RotationData.DotTicks; break; case Trigger.DamageSpellHit: case Trigger.SpellHit: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; triggerChance = sHi; break; case Trigger.SpellCast: case Trigger.DamageSpellCast: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; break; case Trigger.SpellCrit: case Trigger.DamageSpellCrit: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; triggerChance = sc * sHi; break; case Trigger.DamageDone: case Trigger.DamageOrHealingDone: triggerInterval = r.RotationData.Duration / (r.RotationData.CastCount + r.RotationData.DotTicks); break; case Trigger.Use: break; default: return(0.0f); } float procsPerSecond = e.GetAverageProcsPerSecond(triggerInterval, triggerChance, 3.0f, fightLength * 60.0f); return(baseValue * (e.Duration == 0 ? 1 : e.Duration) * specialDamageModifier * procsPerSecond); }; } if (effect.Stats.Mp5 > 0) { CalculateMP5 = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float fightLength, float sp, float sHi, float sc, float sHa) { SpecialEffect e = Effect; float triggerInterval = 0.0f, triggerChance = 1.0f; switch (e.Trigger) { case Trigger.DoTTick: triggerInterval = r.RotationData.Duration / r.RotationData.DotTicks; break; case Trigger.DamageSpellHit: case Trigger.SpellHit: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; triggerChance = sHi; break; case Trigger.SpellCast: case Trigger.DamageSpellCast: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; break; case Trigger.SpellCrit: case Trigger.DamageSpellCrit: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; triggerChance = sc * sHi; break; case Trigger.DamageDone: case Trigger.DamageOrHealingDone: triggerInterval = r.RotationData.Duration / (r.RotationData.CastCount + r.RotationData.DotTicks); break; case Trigger.Use: break; default: return(0.0f); } float procsPerSecond = e.GetAverageProcsPerSecond(triggerInterval, triggerChance, 3.0f, fightLength * 60f); return((e.Stats.Mp5 / 5.0f * e.Duration) * procsPerSecond * 5.0f); }; } if (effect.Stats.ManaRestoreFromMaxManaPerSecond > 0) { CalculateMP5 = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float fightLength, float sp, float sHi, float sc, float sHa) { SpecialEffect e = Effect; float triggerInterval = 0.0f, triggerChance = 1.0f; switch (e.Trigger) { case Trigger.DoTTick: triggerInterval = r.RotationData.Duration / r.RotationData.DotTicks; break; case Trigger.DamageSpellHit: case Trigger.SpellHit: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; triggerChance = sHi; break; case Trigger.SpellCast: case Trigger.DamageSpellCast: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; break; case Trigger.SpellCrit: case Trigger.DamageSpellCrit: triggerInterval = r.RotationData.Duration / r.RotationData.CastCount; triggerChance = sc * sHi; break; case Trigger.DamageDone: case Trigger.DamageOrHealingDone: triggerInterval = r.RotationData.Duration / (r.RotationData.CastCount + r.RotationData.DotTicks); break; case Trigger.Use: break; default: return(0.0f); } float procsPerSecond = e.GetAverageProcsPerSecond(triggerInterval, triggerChance, 3.0f, fightLength * 60f); return(e.Stats.ManaRestoreFromMaxManaPerSecond * c.BasicStats.Mana * e.Duration * procsPerSecond * 5.0f); }; } if (Effect.Stats._rawSpecialEffectDataSize == 0 && (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.SpellMiss || Effect.Trigger == Trigger.Use || Effect.Trigger == Trigger.MoonfireCast || Effect.Trigger == Trigger.InsectSwarmCast || Effect.Trigger == Trigger.InsectSwarmOrMoonfireCast || Effect.Trigger == Trigger.MoonfireTick || Effect.Trigger == Trigger.InsectSwarmTick || Effect.Trigger == Trigger.DoTTick) && (Effect.Stats.HasteRating > 0 || Effect.Stats.SpellHaste > 0)) { Activate = delegate(Character ch, CharacterCalculationsMoonkin c, ref float sp, ref float sHi, ref float sc, ref float sHa, ref float m) { SpecialEffect e = Effect; int maxStack = e.MaxStack; Stats st = e.Stats; float hasteRating = st.HasteRating; float spellHaste = StatConversion.GetSpellHasteFromRating(hasteRating * maxStack); spellHaste += st.SpellHaste; //float highestStat = st.HighestStat; if (spellHaste > 0) { sHa += spellHaste; } /*if (st.Intellect > 0 || highestStat > 0) * { * float procIntellect = (float)Math.Floor((1 + c.BasicStats.BonusIntellectMultiplier) * (st.Intellect + st.HighestStat)); * storedStats.SpellPower = (float)Math.Floor((1 + c.BasicStats.BonusSpellPowerMultiplier) * procIntellect); * storedStats.SpellCrit = StatConversion.GetSpellCritFromIntellect(procIntellect); * sp += storedStats.SpellPower; * sc += storedStats.SpellCrit; * }*/ }; Deactivate = delegate(Character ch, CharacterCalculationsMoonkin c, ref float sp, ref float sHi, ref float sc, ref float sHa, ref float m) { SpecialEffect e = Effect; int maxStack = e.MaxStack; Stats st = e.Stats; float hasteRating = st.HasteRating; float spellHaste = StatConversion.GetSpellHasteFromRating(hasteRating * maxStack); spellHaste += st.SpellHaste; //float highestStat = st.HighestStat; if (spellHaste > 0) { sHa -= spellHaste; } /*if (st.Intellect > 0 || highestStat > 0) * { * sp -= storedStats.SpellPower; * sc -= storedStats.SpellCrit; * }*/ }; UpTime = delegate(SpellRotation r, CharacterCalculationsMoonkin c, float fightLength, float sub35Percent) { float upTime = 0.0f; float procTime = fightLength * 60.0f * (Effect.LimitedToExecutePhase ? sub35Percent : 1f); switch (Effect.Trigger) { case Trigger.Use: upTime = Effect.GetAverageUptime(0f, 1f, 3.0f, procTime); break; case Trigger.SpellHit: case Trigger.DamageSpellHit: upTime = Effect.GetAverageUptime(r.RotationData.Duration / r.RotationData.CastCount, 1 - Math.Max(0, c.SpellHitCap - c.SpellHit), 3.0f, procTime); break; case Trigger.DamageSpellCrit: case Trigger.SpellCrit: upTime = Effect.GetAverageUptime(r.RotationData.Duration / (r.RotationData.CastCount - r.RotationData.InsectSwarmCasts), c.SpellCrit, 3.0f, procTime); break; case Trigger.SpellCast: case Trigger.DamageSpellCast: upTime = Effect.GetAverageUptime(r.RotationData.Duration / r.RotationData.CastCount, 1f, 3.0f, procTime); break; case Trigger.MoonfireCast: upTime = Effect.GetAverageUptime(r.RotationData.Duration / r.RotationData.MoonfireCasts, 1f, 3.0f, procTime); break; case Trigger.InsectSwarmCast: upTime = Effect.GetAverageUptime(r.RotationData.Duration / r.RotationData.InsectSwarmCasts, 1f, 3.0f, procTime); break; case Trigger.InsectSwarmOrMoonfireCast: upTime = Effect.GetAverageUptime(r.RotationData.Duration / (r.RotationData.MoonfireCasts + r.RotationData.InsectSwarmCasts), 1f, 3.0f, procTime); break; case Trigger.EclipseProc: upTime = Effect.GetAverageUptime(r.RotationData.Duration / 2f, 1f, 3.0f, procTime); break; case Trigger.MoonfireTick: upTime = Effect.GetAverageUptime(r.RotationData.Duration / r.RotationData.MoonfireTicks, 1f, 3.0f, procTime); break; case Trigger.InsectSwarmTick: upTime = Effect.GetAverageUptime(r.RotationData.Duration / r.RotationData.InsectSwarmTicks, 1f, 3.0f, procTime); break; case Trigger.DoTTick: upTime = Effect.GetAverageUptime(r.RotationData.Duration / (r.RotationData.MoonfireTicks + r.RotationData.InsectSwarmTicks), 1f, 3.0f, procTime); break; case Trigger.DamageDone: case Trigger.DamageOrHealingDone: upTime = Effect.GetAverageUptime(((r.RotationData.Duration / r.RotationData.CastCount) + (r.RotationData.Duration / (r.RotationData.MoonfireTicks + r.RotationData.InsectSwarmTicks))) / 2.0f, 1f, 3.0f, procTime); break; default: break; } return(upTime * (Effect.LimitedToExecutePhase ? sub35Percent : 1f)); }; } }
private float GetAverageFactor(SpecialEffect effect) { float triggerInterval; float triggerChance; if (GetTriggerData(effect, out triggerInterval, out triggerChance)) { float durationMultiplier = 1; if (effect.LimitedToExecutePhase) { durationMultiplier = CastingState.CalculationOptions.MoltenFuryPercentage; } if (effect.MaxStack > 1) { return durationMultiplier * effect.GetAverageStackSize(triggerInterval, triggerChance, 3f, durationMultiplier * CastingState.CalculationOptions.FightDuration); } else if (effect.Duration > 0) { return durationMultiplier * effect.GetAverageUptime(triggerInterval, triggerChance, 3f, durationMultiplier * CastingState.CalculationOptions.FightDuration); } else { return durationMultiplier * effect.GetAverageProcsPerSecond(triggerInterval, triggerChance, 3f, durationMultiplier * CastingState.CalculationOptions.FightDuration); } } return 0; }
//private static Dictionary<float, SpecialEffect> _SE_StrikesOfOpportunity = new Dictionary<float,SpecialEffect>(); public float GetActivates(float meleeAttemptsOverDur, float perc) { // This attack doesn't consume GCDs and doesn't affect the swing timer //float effectActs = _SE_StrikesOfOpportunity[DPSWarrChar.StatS.MasteryRating].GetAverageProcsPerSecond( var se = new SpecialEffect(Trigger.MeleeAttack, null, 0f, 0.5f, (float)Math.Min(Skills.StrikesOfOpportunity.BaseChance + (float)Math.Max(0f, Skills.StrikesOfOpportunity.BonusChance * StatConversion.GetMasteryFromRating(DPSWarrChar.StatS.MasteryRating, CharacterClass.Warrior)), 1f) ) { BypassCache = true }; float effectActs = se.GetAverageProcsPerSecond((FightDuration * perc) / meleeAttemptsOverDur, 1f, DPSWarrChar.CombatFactors.CMHItemSpeed, FightDuration * perc); effectActs *= FightDuration * perc; return effectActs; }