public Dictionary<string, string> GetCharacterDisplayCalculationValues(bool computeReconstruction) { Dictionary<string, string> dictValues = DisplayCalculationValues = new Dictionary<string, string>(); dictValues.Add("Stamina", BaseStats.Stamina.ToString()); dictValues.Add("Intellect", BaseStats.Intellect.ToString()); dictValues.Add("Armor", BaseStats.Armor.ToString()); dictValues.Add("Health", BaseStats.Health.ToString()); dictValues.Add("Mana", BaseStats.Mana.ToString()); float levelScalingFactor = CalculationOptions.LevelScalingFactor; dictValues.Add("Crit Chance", String.Format("{0:F}%*Crit rating {1} (+{2:F}% crit chance)", 100 * Math.Max(0, BaseState.CritRate), BaseStats.CritRating, BaseStats.CritRating / 14f * levelScalingFactor)); if (ManaAdeptBonus > 0) { dictValues.Add("Mastery", String.Format("{0:F}*Mastery rating {1} (+{2:F} mastery)\r\nMana Adept {3:F}%", BaseState.Mastery, BaseStats.MasteryRating, BaseStats.MasteryRating / 14f * levelScalingFactor, ManaAdeptBonus * 100)); } else if (FlashburnBonus > 0) { dictValues.Add("Mastery", String.Format("{0:F}*Mastery rating {1} (+{2:F} mastery)\r\nFlashburn {3:F}%", BaseState.Mastery, BaseStats.MasteryRating, BaseStats.MasteryRating / 14f * levelScalingFactor, FlashburnBonus * 100)); } else if (FrostburnBonus > 0) { dictValues.Add("Mastery", String.Format("{0:F}*Mastery rating {1} (+{2:F} mastery)\r\nFrostburn {3:F}%", BaseState.Mastery, BaseStats.MasteryRating, BaseStats.MasteryRating / 14f * levelScalingFactor, FrostburnBonus * 100)); } else { dictValues.Add("Mastery", String.Format("{0:F}*Mastery rating {1} (+{2:F} mastery)", BaseState.Mastery, BaseStats.MasteryRating, BaseStats.MasteryRating / 14f * levelScalingFactor)); } // hit rating = hitrate * 800 / levelScalingFactor dictValues.Add("Hit Chance", String.Format("+{0:F}%*Hit rating {1} (+{2:F}% hit chance)\r\n\r\n+0\t{3:F}% miss chance{4}\r\n+1\t{5:F}% miss chance{6}\r\n+2\t{7:F}% miss chance{8}\r\n+3\t{9:F}% miss chance{10}", 100 * BaseState.SpellHit, BaseStats.HitRating, BaseStats.HitRating * levelScalingFactor / 8f, 100 * Math.Max(0, 0.04 - BaseState.SpellHit), GetHitRatingDescription(0.96f + BaseState.SpellHit), 100 * Math.Max(0, 0.05 - BaseState.SpellHit), GetHitRatingDescription(0.95f + BaseState.SpellHit), 100 * Math.Max(0, 0.06 - BaseState.SpellHit), GetHitRatingDescription(0.94f + BaseState.SpellHit), 100 * Math.Max(0, 0.17 - BaseState.SpellHit), GetHitRatingDescription(0.83f + BaseState.SpellHit))); dictValues.Add("Penetration", BaseStats.SpellPenetration.ToString()); dictValues.Add("Haste", String.Format("{0:F}%*Haste rating {1} (+{2:F}% haste)", 100 * (BaseState.CastingSpeed - 1f), BaseState.SpellHasteRating, BaseState.SpellHasteRating / 10f * levelScalingFactor)); float arcaneSpellPower = BaseState.ArcaneSpellPower * (1 + BaseStats.BonusSpellPowerMultiplier); float fireSpellPower = BaseState.FireSpellPower * (1 + BaseStats.BonusSpellPowerMultiplier); float frostSpellPower = BaseState.FrostSpellPower * (1 + BaseStats.BonusSpellPowerMultiplier); float spellPower = Math.Min(arcaneSpellPower, Math.Min(fireSpellPower, frostSpellPower)); dictValues.Add("Spell Power", string.Format("{0:F}*Arcane {1:F}\r\nFire {2:F}\r\nFrost {3:F}", spellPower, arcaneSpellPower, fireSpellPower, frostSpellPower)); dictValues.Add("Mana Regen", Math.Floor(BaseState.ManaRegen * 5).ToString()); dictValues.Add("Combat Regen", Math.Floor(BaseState.ManaRegen5SR * 5).ToString()); dictValues.Add("Arcane Resist", (BaseStats.ArcaneResistance).ToString()); dictValues.Add("Fire Resist", (BaseStats.FireResistance).ToString()); dictValues.Add("Nature Resist", (BaseStats.NatureResistance).ToString()); dictValues.Add("Frost Resist", (BaseStats.FrostResistance).ToString()); dictValues.Add("Shadow Resist", (BaseStats.ShadowResistance).ToString()); dictValues.Add("Physical Mitigation", String.Format("{0:F}%", 100 * BaseState.MeleeMitigation)); dictValues.Add("Resilience", string.Format("{0:F}*{1:F}% Damage Taken Reduction", BaseStats.Resilience, 100 * DamageTakenReduction)); //dictValues.Add("Defense", BaseState.Defense.ToString()); dictValues.Add("Crit Reduction", String.Format("{0:F}%*Spell Crit Reduction: {0:F}%\r\nPhysical Crit Reduction: {1:F}%\r\nCrit Damage Reduction: {2:F}%", BaseState.SpellCritReduction * 100, BaseState.PhysicalCritReduction * 100, BaseState.CritDamageReduction * 100)); dictValues.Add("Dodge", String.Format("{0:F}%", 100 * BaseState.Dodge)); dictValues.Add("Chance to Die", String.Format("{0:F}%", 100 * ChanceToDie)); dictValues.Add("Mean Incoming Dps", String.Format("{0:F}", MeanIncomingDps)); List<CycleId> cycleList = new List<CycleId>() { CycleId.FBLBPyro, CycleId.ScLBPyro, CycleId.FFBLBPyro, CycleId.ABSpam234AM, CycleId.AB3ABar023AM, CycleId.AB23ABar023AM, CycleId.AB2ABar02AMABABar, CycleId.AB2ABar12AMABABar, CycleId.FrBDFFFBIL, CycleId.ArcaneManaNeutral, CycleId.ABSpam0234AMABar, CycleId.ABSpam0234AMABABar, CycleId.AB2ABar2AMABar0AMABABar, CycleId.ABABar1AM, CycleId.FBPyro, CycleId.AB3ABar123AM, CycleId.AB4ABar1234AM, CycleId.AE4AB, CycleId.AERampAB, CycleId.FBLB3Pyro, CycleId.FFBLB3Pyro, CycleId.ABSpam34AM, CycleId.ABSpam4AM, CycleId.AB4ABar34AM, CycleId.AB4ABar4AM }; List<SpellId> spellList = new List<SpellId>() { SpellId.ArcaneMissiles, SpellId.ArcaneBarrage, SpellId.Scorch, SpellId.Fireball, SpellId.PyroblastPOM, SpellId.Frostbolt, SpellId.FireBlast, SpellId.ArcaneExplosion0, SpellId.FlamestrikeSingle, SpellId.Blizzard, SpellId.BlastWave, SpellId.DragonsBreath, SpellId.ConeOfCold, SpellId.FrostfireBolt, SpellId.LivingBomb, SpellId.IceLance, SpellId.FlameOrb, SpellId.Combustion, SpellId.PyroblastSpammed }; foreach (CycleId cycle in cycleList) { Cycle s = BaseState.GetCycle(cycle); if (s != null) { string name = s.Name; if (cycle == CycleId.ArcaneManaNeutral) { name = "ArcaneManaNeutral"; } else if (cycle == CycleId.FBLB3Pyro) { name = "FBLB3Pyro"; } else if (cycle == CycleId.FFBLB3Pyro) { name = "FFBLB3Pyro"; } if (s.Note != null) { dictValues.Add(name, string.Format("{0:F} Dps*{11}\r\n{1:F} Mps\r\n{2:F} Tps\r\n{8:F} Dps per Spell Power\r\n{9:F} Dps per Mastery\r\n{10:F} Dps per Crit\r\n{3:F} Cast Procs / Sec\r\n{4:F} Hit Procs / Sec\r\n{7:F} Crit Procs / Sec\r\n{5:F} Damage Procs / Sec\r\n{6:F} Dot Procs / Sec\r\n{12:F} Nuke Procs / Sec\r\n{13:F} Nuke Procs 2 / Sec", s.DamagePerSecond, s.ManaPerSecond, s.ThreatPerSecond, s.CastProcs / s.CastTime, s.HitProcs / s.CastTime, s.DamageProcs / s.CastTime, s.DotProcs / s.CastTime, s.CritProcs / s.CastTime, s.DpsPerSpellPower, s.DpsPerMastery, s.DpsPerCrit / 100, s.Note, s.NukeProcs / s.CastTime, s.NukeProcs2 / s.CastTime)); } else { dictValues.Add(name, string.Format("{0:F} Dps*{1:F} Mps\r\n{2:F} Tps\r\n{8:F} Dps per Spell Power\r\n{9:F} Dps per Mastery\r\n{10:F} Dps per Crit\r\n{3:F} Cast Procs / Sec\r\n{4:F} Hit Procs / Sec\r\n{7:F} Crit Procs / Sec\r\n{5:F} Damage Procs / Sec\r\n{6:F} Dot Procs / Sec\r\n{11:F} Nuke Procs / Sec\r\n{12:F} Nuke Procs 2 / Sec", s.DamagePerSecond, s.ManaPerSecond, s.ThreatPerSecond, s.CastProcs / s.CastTime, s.HitProcs / s.CastTime, s.DamageProcs / s.CastTime, s.DotProcs / s.CastTime, s.CritProcs / s.CastTime, s.DpsPerSpellPower, s.DpsPerMastery, s.DpsPerCrit / 100, s.NukeProcs / s.CastTime, s.NukeProcs2 / s.CastTime)); } } } Spell bs; foreach (SpellId spell in spellList) { bs = BaseState.GetSpell(spell); if (bs != null) { dictValues.Add(bs.Name, GetSpellTooltip(bs)); } } if (Wand != null) { dictValues.Add(Wand.Name, GetSpellTooltip(Wand)); } bs = BaseState.GetSpell(SpellId.ArcaneBlast0); dictValues.Add("Arcane Blast(0)", GetSpellTooltip(bs, true)); bs = BaseState.GetSpell(SpellId.ArcaneBlast4); dictValues.Add("Arcane Blast(4)", GetSpellTooltip(bs, true)); bs = BaseState.FrozenState.GetSpell(SpellId.DeepFreeze); dictValues.Add("Deep Freeze", GetSpellTooltip(bs)); bs = BaseState.GetSpell(SpellId.LivingBombAOE); dictValues.Add("Living Bomb AOE", GetSpellTooltip(bs)); bs = BaseState.GetSpell(SpellId.FlamestrikeSpammed); dictValues.Add("FlamestrikeSpam", GetSpellTooltip(bs)); Spell abss = BaseState.GetSpell(SpellId.MageWard); dictValues.Add("Mage Ward", string.Format("{0:F} Absorb*{1:F} Mps\r\nAverage Cast Time: {2:F}\r\n{3:F} Mana", abss.Absorb, ((Cycle)abss).ManaPerSecond, abss.CastTime - abss.Latency, abss.ABCost)); float totalDamage = (CalculationOptions.TargetDamage > 0.0f) ? CalculationOptions.TargetDamage : BaseCalculations.DpsRating * CalculationOptions.FightDuration; dictValues.Add("Total Damage", String.Format("{0:F}*Upper Bound: {1:F}\r\nLower Bound: {2:F}", totalDamage, UpperBound, LowerBound)); dictValues.Add("Score", String.Format("{0:F}", BaseCalculations.OverallPoints)); dictValues.Add("Dps", String.Format("{0:F}", BaseCalculations.DpsRating)); dictValues.Add("Tps", String.Format("{0:F}", Tps)); dictValues.Add("Status", String.Format("Score: {0:F}, Dps: {1:F}, Survivability: {2:F}", BaseCalculations.OverallPoints, BaseCalculations.DpsRating, BaseCalculations.SurvivabilityRating)); dictValues.Add("Sequence", computeReconstruction ? ReconstructSequence() : "..."); StringBuilder sb = new StringBuilder("*"); if (MageArmor != null) sb.AppendLine(MageArmor); Dictionary<string, double> combinedSolution = new Dictionary<string, double>(); Dictionary<string, int> combinedSolutionData = new Dictionary<string, int>(); double idleRegen = 0; double evocation = 0; double evocationIV = 0; double evocationHero = 0; double evocationIVHero = 0; double manaPotion = 0; double manaGem = 0; double drums = 0; double we = 0; double mi = 0; double cmg = 0; double ward = 0; bool segmentedOutput = CharacterCalculationsMage.DebugCooldownSegmentation; DamageSources = new Dictionary<string, SpellContribution>(); ManaSources = new Dictionary<string, float>(); ManaUsage = new Dictionary<string, float>(); ManaSources["Initial Mana"] = StartingMana; ManaSources["Replenishment"] = 0.0f; ManaSources["Mana Gem"] = 0.0f; ManaSources["Mana Potion"] = 0.0f; ManaSources["MP5"] = 0.0f; ManaSources["Intellect/Spirit"] = 0.0f; ManaSources["Evocation"] = 0.0f; ManaSources["Judgement of Wisdom"] = 0.0f; ManaSources["Innervate"] = 0.0f; ManaSources["Mana Tide"] = 0.0f; ManaSources["Drinking"] = 0.0f; ManaSources["Water Elemental"] = 0.0f; ManaSources["Other"] = 0.0f; ManaUsage["Overflow"] = 0.0f; ManaUsage["Summon Water Elemental"] = 0.0f; ManaUsage["Summon Mirror Image"] = 0.0f; float spiritFactor = 0.003345f; CastingState evoBaseState = BaseState; /*if (CalculationOptions.Enable2T10Evocation && BaseStats.Mage2T10 > 0) { evoBaseState = BaseState.Tier10TwoPieceState; }*/ for (int i = 0; i < SolutionVariable.Count; i++) { if (Solution[i] > 0.01) { switch (SolutionVariable[i].Type) { case VariableType.IdleRegen: idleRegen += Solution[i]; // manaRegen = -(calculationResult.BaseState.ManaRegen * (1 - calculationOptions.Fragmentation) + calculationResult.BaseState.ManaRegen5SR * calculationOptions.Fragmentation); // ManaRegen = SpiritRegen + characterStats.Mp5 / 5f + SpiritRegen * 4 * 20 * calculationOptions.Innervate / calculationOptions.FightDuration + calculationOptions.ManaTide * 0.24f * characterStats.Mana / calculationOptions.FightDuration + characterStats.ManaRestoreFromMaxManaPerSecond * characterStats.Mana; // ManaRegen5SR = SpiritRegen * characterStats.SpellCombatManaRegeneration + characterStats.Mp5 / 5f + SpiritRegen * (5 - characterStats.SpellCombatManaRegeneration) * 20 * calculationOptions.Innervate / calculationOptions.FightDuration + calculationOptions.ManaTide * 0.24f * characterStats.Mana / calculationOptions.FightDuration + characterStats.ManaRestoreFromMaxManaPerSecond * characterStats.Mana; if (!CalculationOptions.EffectDisableManaSources) { ManaSources["Intellect/Spirit"] += (float)Solution[i] * (BaseState.SpiritRegen * (1 - CalculationOptions.Fragmentation) + BaseState.SpiritRegen * BaseStats.SpellCombatManaRegeneration * CalculationOptions.Fragmentation); ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * ((15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration) * (1 - CalculationOptions.Fragmentation) + (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration) * CalculationOptions.Fragmentation); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; } if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F} sec", "Idle Regen", Solution[i], SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); break; case VariableType.Evocation: evocation += Solution[i]; //double evoManaRegen5SR = ((0.001f + BaseStats.Spirit * spiritFactor * (float)Math.Sqrt(BaseStats.Intellect)) * BaseStats.SpellCombatManaRegeneration + BaseStats.Mp5 / 5f + calculationResult.BaseState.SpiritRegen * (5 - characterStats.SpellCombatManaRegeneration) * 20 * calculationOptions.Innervate / calculationOptions.FightDuration + calculationOptions.ManaTide * 0.24f * characterStats.Mana / calculationOptions.FightDuration); //double evocationRegen = evoManaRegen5SR + 0.15f * BaseStats.Mana / 2f * calculationResult.BaseState.CastingSpeed; //calculationResult.EvocationRegenIV = evoManaRegen5SR + 0.15f * evocationMana / 2f * calculationResult.BaseState.CastingSpeed * 1.2; //calculationResult.EvocationRegenHero = evoManaRegen5SR + 0.15f * evocationMana / 2f * calculationResult.BaseState.CastingSpeed * 1.3; //calculationResult.EvocationRegenIVHero = evoManaRegen5SR + 0.15f * evocationMana / 2f * calculationResult.BaseState.CastingSpeed * 1.2 * 1.3; ManaSources["Intellect/Spirit"] += (float)Solution[i] * (0.001f + BaseStats.Spirit * spiritFactor * (float)Math.Sqrt(BaseStats.Intellect)) * BaseStats.SpellCombatManaRegeneration; ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; ManaSources["Evocation"] += (float)Solution[i] * 0.15f * BaseStats.Mana / 2f * evoBaseState.CastingSpeed; if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Evocation", Solution[i] / EvocationDuration, SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); break; case VariableType.EvocationIV: evocationIV += Solution[i]; ManaSources["Intellect/Spirit"] += (float)Solution[i] * (0.001f + BaseStats.Spirit * spiritFactor * (float)Math.Sqrt(BaseStats.Intellect)) * BaseStats.SpellCombatManaRegeneration; ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; ManaSources["Evocation"] += (float)Solution[i] * 0.15f * BaseStats.Mana / 2f * evoBaseState.CastingSpeed * 1.2f; if (segmentedOutput) { if (SolutionVariable[i].State != null && SolutionVariable[i].State.EffectsActive((int)StandardEffect.IcyVeins)) { sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}", "Icy Veins+Evocation", Solution[i], SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); } else { sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Evocation (Icy Veins)", Solution[i] / EvocationDurationIV, SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); } } break; case VariableType.EvocationHero: evocationHero += Solution[i]; ManaSources["Intellect/Spirit"] += (float)Solution[i] * (0.001f + BaseStats.Spirit * spiritFactor * (float)Math.Sqrt(BaseStats.Intellect)) * BaseStats.SpellCombatManaRegeneration; ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; ManaSources["Evocation"] += (float)Solution[i] * 0.15f * BaseStats.Mana / 2f * evoBaseState.CastingSpeed * 1.3f; if (segmentedOutput) { if (SolutionVariable[i].State != null && SolutionVariable[i].State.EffectsActive((int)StandardEffect.Heroism)) { sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}", "Heroism+Evocation", Solution[i], SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); } else { sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Evocation (Heroism)", Solution[i] / EvocationDurationHero, SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); } } break; case VariableType.EvocationIVHero: evocationIVHero += Solution[i]; ManaSources["Intellect/Spirit"] += (float)Solution[i] * (0.001f + BaseStats.Spirit * spiritFactor * (float)Math.Sqrt(BaseStats.Intellect)) * BaseStats.SpellCombatManaRegeneration; ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; ManaSources["Evocation"] += (float)Solution[i] * 0.15f * BaseStats.Mana / 2f * evoBaseState.CastingSpeed * 1.2f * 1.3f; if (segmentedOutput) { if (SolutionVariable[i].State != null && SolutionVariable[i].State.EffectsActive((int)StandardEffect.IcyVeins | (int)StandardEffect.Heroism)) { sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}", "Icy Veins+Heroism+Evocation", Solution[i], SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); } else { sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Evocation (Icy Veins+Heroism)", Solution[i] / EvocationDurationIVHero, SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); } } break; case VariableType.ManaPotion: manaPotion += Solution[i]; // (1 + characterStats.BonusManaPotion) * calculationResult.ManaPotionValue ManaSources["Mana Potion"] += (float)(Solution[i] * (1 + BaseStats.BonusManaPotionEffectMultiplier) * ManaPotionValue); if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Mana Potion", Solution[i], SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); break; case VariableType.ManaGem: manaGem += Solution[i]; ManaSources["Mana Gem"] += (float)(Solution[i] * (1 + BaseStats.BonusManaGem) * ManaGemValue); if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Mana Gem", Solution[i], SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); break; case VariableType.Drinking: ManaSources["Intellect/Spirit"] += (float)Solution[i] * (BaseState.SpiritRegen); ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; if (CalculationOptions.PlayerLevel < 75) { ManaSources["Drinking"] += (float)Solution[i] * 240f; } else if (CalculationOptions.PlayerLevel < 80) { ManaSources["Drinking"] += (float)Solution[i] * 306f; } else { ManaSources["Drinking"] += (float)Solution[i] * 432f; } sb.AppendLine(String.Format("{0}: {1:F} sec", "Drinking", Solution[i])); break; case VariableType.TimeExtension: break; case VariableType.ManaOverflow: ManaUsage["Overflow"] += (float)Solution[i]; if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Mana Overflow", Solution[i], SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); break; case VariableType.AfterFightRegen: ManaSources["Intellect/Spirit"] += (float)Solution[i] * (BaseState.SpiritRegen); ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; if (CalculationOptions.PlayerLevel < 75) { ManaSources["Drinking"] += (float)Solution[i] * 240f; } else if (CalculationOptions.PlayerLevel < 80) { ManaSources["Drinking"] += (float)Solution[i] * 306f; } else { ManaSources["Drinking"] += (float)Solution[i] * 432f; } sb.AppendLine(String.Format("{0}: {1:F} sec", "Drinking Regen", Solution[i])); break; case VariableType.SummonWaterElemental: { we += Solution[i]; ManaSources["Intellect/Spirit"] += (float)Solution[i] * (BaseState.SpiritRegen * BaseStats.SpellCombatManaRegeneration); ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; ManaUsage["Summon Water Elemental"] += (float)Solution[i] * (int)(0.16 * SpellTemplate.BaseMana[CalculationOptions.PlayerLevel]) / BaseGlobalCooldown; if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Summon Water Elemental", Solution[i] / BaseGlobalCooldown, SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); Spell waterbolt = SolutionVariable[i].State.GetSpell(SpellId.Waterbolt); SpellContribution contrib; if (!DamageSources.TryGetValue(waterbolt.Name, out contrib)) { contrib = new SpellContribution() { Name = waterbolt.Name }; DamageSources[waterbolt.Name] = contrib; } contrib.Hits += (float)Solution[i] / waterbolt.CastTime; contrib.Damage += waterbolt.DamagePerSecond * (float)Solution[i]; } break; case VariableType.SummonMirrorImage: { mi += Solution[i]; ManaSources["Intellect/Spirit"] += (float)Solution[i] * (BaseState.SpiritRegen * BaseStats.SpellCombatManaRegeneration); ManaSources["MP5"] += (float)Solution[i] * BaseStats.Mp5 / 5f; ManaSources["Innervate"] += (float)Solution[i] * (15732 * CalculationOptions.Innervate / CalculationOptions.FightDuration); ManaSources["Replenishment"] += (float)Solution[i] * BaseStats.ManaRestoreFromMaxManaPerSecond * BaseStats.Mana; ManaUsage["Summon Mirror Image"] += (float)Solution[i] * (int)(0.10 * SpellTemplate.BaseMana[CalculationOptions.PlayerLevel]) / BaseGlobalCooldown; if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Summon Mirror Image", Solution[i] / BaseGlobalCooldown, SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); Spell mirrorImage = SolutionVariable[i].State.GetSpell(SpellId.MirrorImage); SpellContribution contrib; if (!DamageSources.TryGetValue("Mirror Image", out contrib)) { contrib = new SpellContribution() { Name = "Mirror Image" }; DamageSources["Mirror Image"] = contrib; } contrib.Hits += 3 * (MageTalents.GlyphOfMirrorImage ? 4 : 3) * (float)Solution[i] / mirrorImage.CastTime; contrib.Damage += mirrorImage.DamagePerSecond * (float)Solution[i]; } break; case VariableType.ConjureManaGem: cmg += Solution[i]; Cycle smg = SolutionVariable[i].Cycle; smg.AddManaUsageContribution(ManaUsage, (float)Solution[i]); smg.AddManaSourcesContribution(ManaSources, (float)Solution[i]); if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F}x", "Conjure Mana Gem", Solution[i] / ConjureManaGem.CastTime, SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); break; /*case VariableType.Ward: ward += Solution[i]; Cycle sward = SolutionVariable[i].Cycle; sward.AddManaUsageContribution(ManaUsage, (float)Solution[i]); sward.AddManaSourcesContribution(ManaSources, (float)Solution[i]); if (segmentedOutput) sb.AppendLine(String.Format("{2} {0}: {1:F}x", Ward.Name, Solution[i] / Ward.CastTime, SegmentList[SolutionVariable[i].Segment])); break;*/ case VariableType.Wand: case VariableType.Spell: double value; Cycle s = SolutionVariable[i].Cycle; s.AddDamageContribution(DamageSources, (float)Solution[i], 0); s.AddManaUsageContribution(ManaUsage, (float)Solution[i]); s.AddManaSourcesContribution(ManaSources, (float)Solution[i]); string label = ((SolutionVariable[i].State.BuffLabel.Length > 0) ? (SolutionVariable[i].State.BuffLabel + "+") : "") + s.Name; combinedSolution.TryGetValue(label, out value); combinedSolution[label] = value + Solution[i]; combinedSolutionData[label] = i; if (segmentedOutput) sb.AppendLine(String.Format("{2}.{3} {0}: {1:F} sec", label, Solution[i], SegmentList[SolutionVariable[i].Segment], SolutionVariable[i].ManaSegment)); break; } } } if (Specialization == Specialization.Arcane) { // we need to special case damage sources because of mana adept DamageSources.Clear(); CalculateArcaneDamageSources(); } if (!segmentedOutput) { if (idleRegen > 0) { sb.AppendLine(String.Format("{0}: {1:F} sec", "Idle Regen", idleRegen)); } if (evocation > 0) { sb.AppendLine(String.Format("{0}: {1:F}x ({2:F} mps)", "Evocation", evocation / EvocationDuration, EvocationRegen)); } if (evocationIV > 0) { sb.AppendLine(String.Format("{0}: {1:F}x ({2:F} mps)", "Evocation (Icy Veins)", evocationIV / EvocationDurationIV, EvocationRegenIV)); } if (evocationHero > 0) { sb.AppendLine(String.Format("{0}: {1:F}x ({2:F} mps)", "Evocation (Heroism)", evocationHero / EvocationDurationHero, EvocationRegenHero)); } if (evocationIVHero > 0) { sb.AppendLine(String.Format("{0}: {1:F}x ({2:F} mps)", "Evocation (Icy Veins+Heroism)", evocationIVHero / EvocationDurationIVHero, EvocationRegenIVHero)); } if (manaPotion > 0) { sb.AppendLine(String.Format("{0}: {1:F}x", "Mana Potion", manaPotion)); } if (manaGem > 0) { sb.AppendLine(String.Format("{0}: {1:F}x", "Mana Gem", manaGem)); } if (drums > 0) { sb.AppendLine(String.Format("{0}: {1:F}x", "Drums of Battle", drums / BaseGlobalCooldown)); } if (we > 0) { sb.AppendLine(String.Format("{0}: {1:F}x", "Summon Water Elemental", we / BaseGlobalCooldown)); } if (mi > 0) { sb.AppendLine(String.Format("{0}: {1:F}x", "Summon Mirror Image", mi / BaseGlobalCooldown)); } if (cmg > 0) { sb.AppendLine(String.Format("{0}: {1:F}x", "Conjure Mana Gem", cmg / ConjureManaGem.CastTime)); } if (ward > 0) { sb.AppendLine(String.Format("{0}: {1:F}x", Ward.Name, ward / Ward.CastTime)); } foreach (KeyValuePair<string, double> kvp in combinedSolution) { Cycle s = SolutionVariable[combinedSolutionData[kvp.Key]].Cycle; if (s != null) { sb.AppendLine(String.Format("{0}: {1:F} sec ({2:F} dps, {3:F} mps, {4:F} tps)", kvp.Key, kvp.Value, s.DamagePerSecond, s.ManaPerSecond, s.ThreatPerSecond)); } else { sb.AppendLine(String.Format("{0}: {1:F} sec", kvp.Key, kvp.Value)); } } } //if (WaterElemental) sb.AppendLine(String.Format("Water Elemental: {0:F}x", WaterElementalDuration / 45f)); dictValues.Add("Spell Cycles", sb.ToString()); sb = new StringBuilder("*"); List<SpellContribution> contribList = new List<SpellContribution>(DamageSources.Values); contribList.Sort(); foreach (SpellContribution contrib in contribList) { if (contrib.HitDamage > 0) { sb.AppendFormat("{0}: {1:F}%, {2:F} Damage\r\n", contrib.Name, 100.0 * contrib.Damage / totalDamage, contrib.Damage); sb.AppendFormat("\tHits (#: {0:F}, Avg: {1:F}, Total: {2:F})\r\n", contrib.Hits, contrib.HitDamage / contrib.Hits, contrib.HitDamage); } else { sb.AppendFormat("{0}: {1:F}%, {2:F} Damage, {3:F} Hits\r\n", contrib.Name, 100.0 * contrib.Damage / totalDamage, contrib.Damage, contrib.Hits); } if (contrib.CritDamage > 0) { sb.AppendFormat("\tCrits (#: {0:F}, Avg: {1:F}, Total: {2:F})\r\n", contrib.Crits, contrib.CritDamage / contrib.Crits, contrib.CritDamage); } if (contrib.TickDamage > 0) { sb.AppendFormat("\tTicks (#: {0:F}, Avg: {1:F}, Total: {2:F})\r\n", contrib.Ticks, contrib.TickDamage / contrib.Ticks, contrib.TickDamage); } } dictValues.Add("By Spell", sb.ToString()); dictValues.Add("Minimum Range", String.Format("{0:F}", MinimumRange)); dictValues.Add("Threat Reduction", String.Format("{0:F}%", ThreatReduction * 100)); CalculationOptions.Calculations = this; return dictValues; }
private void CalculateArcaneDamageSources() { // for now requires solution variables to work int[] sort = new int[SolutionVariable.Count]; for (int j = 0; j < SolutionVariable.Count; j++) { sort[j] = j; } Array.Sort(sort, (x, y) => { SolutionVariable vx = SolutionVariable[x]; SolutionVariable vy = SolutionVariable[y]; int comp = vx.Segment.CompareTo(vy.Segment); if (comp != 0) return comp; comp = vx.ManaSegment.CompareTo(vy.ManaSegment); if (comp != 0) return comp; // first instant mana gain, then negative mps // then mana overflow, then positive mps comp = Solver.GetQuadraticIndex(vx).CompareTo(Solver.GetQuadraticIndex(vy)); if (comp != 0) return comp; return vx.Mps.CompareTo(vy.Mps); }); float mana = StartingMana; int gemCount = 0; float time = 0; float maxMana = BaseStats.Mana; for (int j = 0; j < SolutionVariable.Count; j++) { int i = sort[j]; if (Solution[i] > 0.01) { double mps = SolutionVariable[i].Mps; Cycle cycle = SolutionVariable[i].Cycle; var variableType = SolutionVariable[i].Type; if (variableType == VariableType.Wand) { cycle = Wand; mps = cycle.ManaPerSecond; } else if (variableType == VariableType.ManaGem) { mps = 0.0; } else if (variableType == VariableType.ManaPotion) { mps = 0.0; } else if (variableType == VariableType.Drinking) { mps = -BaseState.ManaRegenDrinking; } else if (variableType == VariableType.AfterFightRegen) { mps = -BaseState.ManaRegenDrinking; } float duration = (float)Solution[i]; CastingState state = SolutionVariable[i].State; if (variableType == VariableType.ManaPotion || variableType == VariableType.ManaGem) { float value = duration; duration = 0; if (variableType == VariableType.ManaGem) { mana += (float)((1 + BaseStats.BonusManaGem) * ManaGemValue * value); gemCount++; } else if (variableType == VariableType.ManaPotion) { mana += (float)((1 + BaseStats.BonusManaPotionEffectMultiplier) * ManaPotionValue * value); } if (mana < 0) mana = 0; if (mana > maxMana) { mana = maxMana; } } else { float partTime = duration; if (mana - mps * duration < 0) partTime = (float)(mana / mps); else if (mana - mps * duration > maxMana) partTime = (float)((mana - maxMana) / mps); float startMana = mana; mana -= (float)(mps * duration); if (mana < 0) mana = 0; if (mana > maxMana) { mana = maxMana; } float averageMana = (partTime * (startMana + mana) / 2.0f + (duration - partTime) * mana) / duration; switch (variableType) { case VariableType.SummonWaterElemental: { Spell waterbolt = SolutionVariable[i].State.GetSpell(SpellId.Waterbolt); SpellContribution contrib; if (!DamageSources.TryGetValue(waterbolt.Name, out contrib)) { contrib = new SpellContribution() { Name = waterbolt.Name }; DamageSources[waterbolt.Name] = contrib; } contrib.Hits += (float)Solution[i] / waterbolt.CastTime; contrib.Damage += waterbolt.DamagePerSecond * (float)Solution[i]; } break; case VariableType.SummonMirrorImage: { Spell mirrorImage = SolutionVariable[i].State.GetSpell(SpellId.MirrorImage); SpellContribution contrib; if (!DamageSources.TryGetValue("Mirror Image", out contrib)) { contrib = new SpellContribution() { Name = "Mirror Image" }; DamageSources["Mirror Image"] = contrib; } contrib.Hits += 3 * (MageTalents.GlyphOfMirrorImage ? 4 : 3) * (float)Solution[i] / mirrorImage.CastTime; contrib.Damage += mirrorImage.DamagePerSecond * (float)Solution[i]; } break; case VariableType.Wand: case VariableType.Spell: cycle.AddDamageContribution(DamageSources, (float)Solution[i], averageMana); break; } } time += duration; } } }
public void AddEffectContribution(Dictionary<string, SpellContribution> dict, float duration, float averageMana) { SpellContribution contrib; if (CastingState.WaterElemental) { Spell waterbolt = CastingState.GetSpell(SpellId.Waterbolt); if (!dict.TryGetValue(waterbolt.Name, out contrib)) { contrib = new SpellContribution() { Name = waterbolt.Name }; dict[waterbolt.Name] = contrib; } contrib.Hits += duration / waterbolt.CastTime; contrib.Damage += (float)(waterbolt.AverageDamage + effectSpellPower * waterbolt.DamagePerSpellPower) / waterbolt.CastTime * duration; } if (CastingState.MirrorImage) { Spell mirrorImage = CastingState.GetSpell(SpellId.MirrorImage); if (!dict.TryGetValue("Mirror Image", out contrib)) { contrib = new SpellContribution() { Name = "Mirror Image" }; dict["Mirror Image"] = contrib; } contrib.Hits += 3 * (CastingState.MageTalents.GlyphOfMirrorImage ? 4 : 3) * duration / mirrorImage.CastTime; contrib.Damage += (float)(mirrorImage.AverageDamage + effectSpellPower * mirrorImage.DamagePerSpellPower) / mirrorImage.CastTime * duration; } if (CastingState.CalculationOptions.ProcCombustion && CastingState.MageTalents.Combustion == 1) { Spell combustion = CastingState.GetSpell(SpellId.Combustion); float factor; if (CastingState.Solver.Mage4T13) { factor = GetAverageFactor(Solver.SpecialEffectCombustion4T13); } else { factor = GetAverageFactor(Solver.SpecialEffectCombustion); } combustion.AddSpellContribution(dict, factor * duration / 10f, 0, (float)effectSpellPower, (float)effectMastery, (float)effectCrit, (float)effectManaAdeptMultiplier, averageMana); } if (Ticks > 0) { for (int i = 0; i < CastingState.Solver.DamageProcEffectsCount; i++) { SpecialEffect effect = CastingState.Solver.DamageProcEffects[i]; string name = null; float effectsPerSecond = GetAverageFactor(effect); float boltDps = 0f; float spellMultiplier = 1f; if (CastingState.Solver.Specialization == Specialization.Arcane) { double manaAdeptBonus = CastingState.ManaAdeptBonus + 0.015f * effectMasteryRating / 14 * CastingState.CalculationOptions.LevelScalingFactor; spellMultiplier = (float)(1 + averageMana / CastingState.BaseStats.Mana * manaAdeptBonus * effectManaAdeptMultiplier); } if (effect.Stats.ArcaneDamage > 0) { boltDps = CastingState.ArcaneAverageDamage * effect.Stats.ArcaneDamage * effectsPerSecond * spellMultiplier; name = "Arcane Damage Proc"; } if (effect.Stats.FireDamage > 0) { boltDps = CastingState.FireAverageDamage * effect.Stats.FireDamage * effectsPerSecond * spellMultiplier; name = "Fire Damage Proc"; } if (effect.Stats.FrostDamage > 0) { boltDps = CastingState.FrostAverageDamage * effect.Stats.FrostDamage * effectsPerSecond * spellMultiplier; name = "Frost Damage Proc"; } if (effect.Stats.ShadowDamage > 0) { boltDps = CastingState.ShadowAverageDamage * effect.Stats.ShadowDamage * effectsPerSecond * spellMultiplier; name = "Shadow Damage Proc"; } if (effect.Stats.NatureDamage > 0) { boltDps = CastingState.NatureAverageDamage * effect.Stats.NatureDamage * effectsPerSecond * spellMultiplier; name = "Nature Damage Proc"; } if (effect.Stats.HolyDamage > 0) { boltDps = CastingState.HolyAverageDamage * effect.Stats.HolyDamage * effectsPerSecond * spellMultiplier; name = "Holy Damage Proc"; } if (effect.Stats.HolySummonedDamage > 0) { boltDps = CastingState.HolySummonedAverageDamage * effect.Stats.HolySummonedDamage * effectsPerSecond; name = "Holy Damage"; } if (effect.Stats.FireSummonedDamage > 0) { boltDps = CastingState.FireSummonedAverageDamage * effect.Stats.FireSummonedDamage * effectsPerSecond; name = "Fire Damage"; } if (!dict.TryGetValue(name, out contrib)) { contrib = new SpellContribution() { Name = name }; dict[name] = contrib; } if (effect.Duration == 0) { contrib.Hits += effectsPerSecond * duration; } contrib.Damage += boltDps * duration; } } if (IgniteProcs > 0 && dict.TryGetValue("Ignite", out contrib)) { double rate = IgniteProcs / CastTime; double k = Math.Exp(-2 * rate); double ticks = k * (1 + k); double ticksPerSecond = rate * ticks; contrib.Hits += duration * (float)ticksPerSecond; } }