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;
                }
            }
        }
Example #3
0
 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;
     }
 }