Exemplo n.º 1
0
        public TreeStats(Character character, Stats stats, KeyValuePair <double, SpecialEffect>[] hasteProcs, double treeOfLifeUptime)
        {
            CalculationOptionsTree opts = character.CalculationOptions as CalculationOptionsTree;

            bool Restoration = (opts != null) ? opts.Restoration : true;

            Haste = new HasteStats(stats, hasteProcs);

            SpellCrit = StatConversion.GetSpellCritFromIntellect(stats.Intellect) + StatConversion.GetSpellCritFromRating(stats.CritRating) + stats.SpellCrit;

            SpellPower = (float)(stats.SpellPower + Math.Max(0f, stats.Intellect - 10));
            // TODO: does nurturing instinct actually work like this?
            SpellPower += character.DruidTalents.NurturingInstinct * 0.5 * stats.Agility;
            SpellPower *= (1 + stats.BonusSpellPowerMultiplier);

            TreeOfLifeUptime = treeOfLifeUptime;
            double mastery = 8.0f + StatConversion.GetMasteryFromRating(stats.MasteryRating);

            if (Restoration)
            {
                Harmony = mastery * 0.0125f;
            }

            SpellsManaCostReduction = stats.SpellsManaCostReduction + stats.NatureSpellsManaCostReduction;
            BonusCritHealMultiplier = stats.BonusCritHealMultiplier;

            PassiveDirectHealBonus   = (Restoration ? 1.25f : 1.0f) + Harmony;
            PassivePeriodicHealBonus = (Restoration ? 1.25f : 1.0f) + opts.HarmonyPeriodicRate * Harmony + 0.02f * character.DruidTalents.Genesis;
            DirectHealMultiplier     = (1 + stats.BonusHealingDoneMultiplier) * (1.0f + character.DruidTalents.MasterShapeshifter * 0.04f) * (1 + TreeOfLifeUptime * 0.15f);
            PeriodicHealMultiplier   = DirectHealMultiplier * (1 + stats.BonusPeriodicHealingMultiplier);
            SpellsManaCostMultiplier = 1 - stats.ManaCostReductionMultiplier;

            Healed = stats.Healed + stats.HealedPerSP * SpellPower;
        }
Exemplo n.º 2
0
 public static float CalcSpellCrit(Stats stats)
 {
     return(stats.SpellCrit
            + StatConversion.GetSpellCritFromIntellect(CalcIntellect(stats))
            + StatConversion.GetSpellCritFromRating(stats.CritRating)
            + stats.BonusCritChance
            + stats.SpellCritOnTarget);
 }
Exemplo n.º 3
0
        public override Stats GetCharacterStats(Character character, Item additionalItem)
        {
            CalculationOptionsHealPriest calcOpts = character.CalculationOptions as CalculationOptionsHealPriest;

            StatsPriest statsTotal = new StatsPriest()
            {
                SpellHaste = PriestInformation.GetDarkness(character.PriestTalents.Darkness),
                InnerFire  = true,
                BonusIntellectMultiplier = 0.05f,   // Cloth bonus.
                PriestSpec = PriestSpec.GetPriestSpec(character.PriestTalents),
            };

            if (statsTotal.PriestSpec == ePriestSpec.Spec_Disc)
            {
                statsTotal.SpellCombatManaRegeneration = 0.5f;
                statsTotal.BonusIntellectMultiplier    = 0.15f;
            }
            else if (statsTotal.PriestSpec == ePriestSpec.Spec_Holy)
            {
                statsTotal.SpellCombatManaRegeneration = 0.5f + PriestInformation.GetHolyConcentration(character.PriestTalents.HolyConcentration);
                statsTotal.BonusHealingDoneMultiplier  = 0.15f;
            }
            else if (statsTotal.PriestSpec == ePriestSpec.Spec_ERROR)
            {
                throw new Exception("Unpossible Talent Spec!");
            }

            statsTotal.Accumulate(BaseStats.GetBaseStats(character));
            statsTotal.Accumulate(GetItemStats(character, additionalItem));
            statsTotal.Accumulate(GetBuffsStats(character, calcOpts));


            statsTotal.Stamina     = (float)Math.Floor((statsTotal.Stamina) * (1 + statsTotal.BonusStaminaMultiplier));
            statsTotal.Intellect   = (float)Math.Floor((statsTotal.Intellect) * (1 + statsTotal.BonusIntellectMultiplier));
            statsTotal.Spirit      = (float)Math.Floor((statsTotal.Spirit) * (1 + statsTotal.BonusSpiritMultiplier));
            statsTotal.SpellPower += (statsTotal.InnerFire ? PriestInformation.GetInnerFireSpellPowerBonus(character) : 0) + (statsTotal.Intellect - 10);
            statsTotal.SpellPower *= (1f + statsTotal.BonusSpellPowerMultiplier);
            statsTotal.Mana       += StatConversion.GetManaFromIntellect(statsTotal.Intellect);
            statsTotal.Mana       *= (1f + statsTotal.BonusManaMultiplier);
            statsTotal.Health     += StatConversion.GetHealthFromStamina(statsTotal.Stamina);
            statsTotal.Health      = (float)Math.Floor(statsTotal.Health * (1f + statsTotal.BonusHealthMultiplier));
            statsTotal.SpellCrit  += StatConversion.GetSpellCritFromIntellect(statsTotal.Intellect)
                                     + StatConversion.GetSpellCritFromRating(statsTotal.CritRating);
            statsTotal.SpellHaste = (1f + statsTotal.SpellHaste) * (1f + StatConversion.GetSpellHasteFromRating(statsTotal.HasteRating)) - 1f;
            statsTotal.Armor     *= (1 + (statsTotal.InnerFire ? PriestInformation.GetInnerFireArmorBonus(character) : 0));

            if (statsTotal.PriestSpec == ePriestSpec.Spec_Disc)
            {
                statsTotal.ShieldDiscipline = (PriestInformation.DisciplineMasteryBase + StatConversion.GetMasteryFromRating(statsTotal.MasteryRating)) * PriestInformation.DisciplineMasteryEffect;
            }
            else if (statsTotal.PriestSpec == ePriestSpec.Spec_Holy)
            {
                statsTotal.EchoofLight = (PriestInformation.HolyMasteryBase + StatConversion.GetMasteryFromRating(statsTotal.MasteryRating)) * PriestInformation.HolyMasteryEffect;
            }

            return(statsTotal);
        }
Exemplo n.º 4
0
 public static float GetSpellCritFromRating(float rating, int playerLevel)
 {
     if (playerLevel == 85)
     {
         return(StatConversion.GetSpellCritFromRating(rating));
     }
     else
     {
         float[] scaling = { 45.905986785888672f, 60.278423309326172f, 79.155647277832031f, 103.985641479492188f, 136.538131713867188f };
         return(rating / scaling[playerLevel - 80] * 0.01f);
     }
 }
Exemplo n.º 5
0
        private void ConvertRatings(Stats stats, PaladinTalents talents, CalculationOptionsHealadin calcOpts)
        {
            stats.Stamina *= 1f + stats.BonusStaminaMultiplier;

            // Intellect is used to calculate initial mana pool.
            // To avoid temporary intellect from highest stat procs changing initial mana pool
            // we track temporary intellect separatly in HighestStat property and combine it with intellect
            // when needed.
            // TODO: However this doesn't help to deal with pure temporary intellect procs (if there are any).
            // NOTE: If we add highest stat to intellect after we calculate mana, the only visible change
            // will be the displayed intellect for the character.
            stats.Intellect   *= (1f + stats.BonusIntellectMultiplier);
            stats.HighestStat *= (1f + stats.BonusIntellectMultiplier);

            stats.SpellCrit = stats.SpellCrit +
                              StatConversion.GetSpellCritFromIntellect(
                stats.Intellect + stats.HighestStat,
                CharacterClass.Paladin) +
                              StatConversion.GetSpellCritFromRating(stats.CritRating, CharacterClass.Paladin);

            // I want to track haste before talent seperately, going to use RangedHaste for that.
            // I plan to use this on the "Stats" page so I can report sources of haste seperatly
            stats.RangedHaste = (1f + stats.SpellHaste) *
                                (1f + StatConversion.GetSpellHasteFromRating(stats.HasteRating, CharacterClass.Paladin))
                                - 1f;

            // calculating physical haste for use in melee attacks, which will generate mana
            // can also divide spellhaste / physicalhaste to get orignal value of spellhaste, which is from buffs as far as I can tell
            stats.PhysicalHaste = (1f + talents.JudgementsOfThePure * 0.03f) *
                                  (1f + talents.SpeedOfLight * 0.01f) *
                                  (1f + StatConversion.GetSpellHasteFromRating(stats.HasteRating, CharacterClass.Paladin))
                                  - 1f;

            stats.SpellHaste = (1f + talents.JudgementsOfThePure * 0.03f) *
                               (1f + talents.SpeedOfLight * 0.01f) *
                               (1f + stats.SpellHaste) *
                               (1f + StatConversion.GetSpellHasteFromRating(stats.HasteRating, CharacterClass.Paladin))
                               - 1f;

            // GetManaFromIntellect/GetHealthFromStamina account for the fact
            // that the first 20 Int/Sta only give 1 Mana/Health each.
            stats.Mana += StatConversion.GetManaFromIntellect(stats.Intellect, CharacterClass.Paladin) *
                          (1f + stats.BonusManaMultiplier);
            stats.Health += StatConversion.GetHealthFromStamina(stats.Stamina, CharacterClass.Paladin);

            stats.PhysicalHit += StatConversion.GetPhysicalHitFromRating(
                stats.HitRating,
                CharacterClass.Paladin);
        }
Exemplo n.º 6
0
 public static float CalcSpellCrit(Stats stats, float baseIntellect, int playerLevel)
 {
     if (playerLevel == 85)
     {
         return(stats.SpellCrit
                + StatConversion.GetSpellCritFromIntellect(CalcIntellect(stats, baseIntellect, playerLevel))
                + StatConversion.GetSpellCritFromRating(stats.CritRating)
                + stats.BonusCritChance
                + stats.SpellCritOnTarget);
     }
     else
     {
         return(stats.SpellCrit
                + GetSpellCritFromIntellect(CalcIntellect(stats, baseIntellect, playerLevel), playerLevel)
                + GetSpellCritFromRating(stats.CritRating, playerLevel)
                + stats.BonusCritChance
                + stats.SpellCritOnTarget);
     }
 }
Exemplo n.º 7
0
        private void ConvertRatings(Stats stats, PaladinTalents talents, CalculationOptionsHealadin calcOpts)
        {
            stats.Stamina *= 1f + stats.BonusStaminaMultiplier;

            // Intellect is used to calculate initial mana pool.
            // To avoid temporary intellect from highest stat procs changing initial mana pool
            // we track temporary intellect separatly in HighestStat property and combine it with intellect
            // when needed.
            // TODO: However this doesn't help to deal with pure temporary intellect procs (if there are any).
            // NOTE: If we add highest stat to intellect after we calculate mana, the only visible change
            // will be the displayed intellect for the character.
            stats.Intellect   *= (1f + stats.BonusIntellectMultiplier) * (1f + talents.DivineIntellect * .02f);
            stats.HighestStat *= (1f + stats.BonusIntellectMultiplier) * (1f + talents.DivineIntellect * .02f);

            stats.SpellPower += 0.04f * (stats.Intellect + stats.HighestStat) * talents.HolyGuidance;
            stats.SpellCrit   = stats.SpellCrit +
                                StatConversion.GetSpellCritFromIntellect(
                stats.Intellect + stats.HighestStat,
                CharacterClass.Paladin) +
                                StatConversion.GetSpellCritFromRating(stats.CritRating, CharacterClass.Paladin) +
                                talents.SanctityOfBattle * .01f +
                                talents.Conviction * .01f;

            stats.SpellHaste = (1f + talents.JudgementsOfThePure * (calcOpts.JotP ? .03f : 0f)) *
                               (1f + stats.SpellHaste) *
                               (1f + StatConversion.GetSpellHasteFromRating(stats.HasteRating, CharacterClass.Paladin))
                               - 1f;

            // GetManaFromIntellect/GetHealthFromStamina account for the fact
            // that the first 20 Int/Sta only give 1 Mana/Health each.
            stats.Mana += StatConversion.GetManaFromIntellect(stats.Intellect, CharacterClass.Paladin) *
                          (1f + stats.BonusManaMultiplier);
            stats.Health += StatConversion.GetHealthFromStamina(stats.Stamina, CharacterClass.Paladin);

            stats.PhysicalHit += StatConversion.GetPhysicalHitFromRating(
                stats.HitRating,
                CharacterClass.Paladin);
        }
Exemplo n.º 8
0
        public void CritsAndResists()
        {
            #region Crits, Resists
            {
                // Attack Rolltable (DW):
                // 27.0% miss     (8.0% with 2H)
                //  6.5% dodge
                // 24.0% glancing (75% hit-dmg)
                // xx.x% crit
                // remaining = hit

                float targetArmor = calcOpts.BossArmor, totalArP = stats.ArmorPenetration;

                float arpBuffs = talents.BloodGorged * 2f / 100;

                physicalMitigation = 1f - StatConversion.GetArmorDamageReduction(character.Level, targetArmor,
                                                                                 stats.ArmorPenetration, arpBuffs, stats.ArmorPenetrationRating);

                calcs.EnemyMitigation = 1f - physicalMitigation;
                calcs.EffectiveArmor  = physicalMitigation;

                // Crit: Base .65%
                physCrits        = .0065f;
                physCrits       += StatConversion.GetPhysicalCritFromRating(stats.CritRating);
                physCrits       += StatConversion.GetPhysicalCritFromAgility(stats.Agility, CharacterClass.DeathKnight);
                physCrits       += .01f * (float)(talents.DarkConviction + talents.EbonPlaguebringer + talents.Annihilation);
                physCrits       += stats.PhysicalCrit;
                calcs.CritChance = physCrits;

                float chanceAvoided = 0.335f;

                float chanceDodged = StatConversion.WHITE_DODGE_CHANCE_CAP[calcOpts.TargetLevel - 80];

                calcs.DodgedMHAttacks = MH.chanceDodged;
                calcs.DodgedOHAttacks = OH.chanceDodged;

                if (character.MainHand != null)
                {
                    chanceDodged = MH.chanceDodged;
                }

                if (character.OffHand != null)
                {
                    if (DW)
                    {
                        chanceDodged += OH.chanceDodged;
                        chanceDodged /= 2;
                    }
                    else if (character.MainHand == null)
                    {
                        chanceDodged = OH.chanceDodged;
                    }
                }

                calcs.DodgedAttacks = chanceDodged;
                // Process White hits:
                float chanceMiss = DW ? StatConversion.WHITE_MISS_CHANCE_CAP_DW[calcOpts.TargetLevel - 80] : StatConversion.WHITE_MISS_CHANCE_CAP[calcOpts.TargetLevel - 80];
                chanceMiss -= StatConversion.GetPhysicalHitFromRating(stats.HitRating);
                chanceMiss -= hitBonus;
                chanceMiss -= stats.PhysicalHit;
                // Cap the Miss rate at 0%
                chanceMiss           = Math.Max(chanceMiss, 0f);
                calcs.MissedAttacks  = chanceMiss;
                whiteMiss            = chanceMiss;
                chanceAvoided        = chanceDodged + chanceMiss;
                calcs.AvoidedAttacks = chanceDodged + chanceMiss;

                // Process Yellow hits
                chanceMiss    = StatConversion.YELLOW_MISS_CHANCE_CAP[calcOpts.TargetLevel - 80];
                chanceMiss   -= StatConversion.GetPhysicalHitFromRating(stats.HitRating);
                chanceMiss   -= hitBonus;
                chanceMiss   -= stats.PhysicalHit;
                chanceMiss    = Math.Max(chanceMiss, 0f);
                chanceDodged  = MH.chanceDodged;
                missedSpecial = chanceMiss;
                dodgedSpecial = chanceDodged;
                // calcs.MissedAttacks = chanceMiss

                spellCrits            = 0f;
                spellCrits           += StatConversion.GetSpellCritFromRating(stats.CritRating);
                spellCrits           += stats.SpellCrit + stats.SpellCritOnTarget;
                spellCrits           += .01f * (float)(talents.DarkConviction + talents.EbonPlaguebringer);
                calcs.SpellCritChance = spellCrits;

                // Resists: Base 17%
                spellResist  = .17f;
                spellResist -= StatConversion.GetSpellHitFromRating(stats.HitRating);
                spellResist -= .01f * talents.Virulence;
                spellResist -= stats.SpellHit;
                if (spellResist < 0f)
                {
                    spellResist = 0f;
                }

                // Total physical misses
                totalMHMiss = calcs.DodgedMHAttacks + whiteMiss;
                totalOHMiss = calcs.DodgedOHAttacks + whiteMiss;
                double spellGCD    = (calcOpts.rotation.presence == CalculationOptionsDPSDK.Presence.Blood ? 1.5d / ((1 + (StatConversion.GetHasteFromRating(stats.HasteRating, CharacterClass.DeathKnight))) * (1d + stats.SpellHaste)) < 1d ? 1d : 1.5d / ((1 + (StatConversion.GetHasteFromRating(stats.HasteRating, CharacterClass.DeathKnight))) * (1d + stats.SpellHaste)): 1d);
                double physicalGCD = (calcOpts.rotation.presence == CalculationOptionsDPSDK.Presence.Blood ? 1.5d : 1d);
                float  minDuration = totalMeleeAbilities * (float)physicalGCD +
                                     totalSpellAbilities * (float)spellGCD;

                realDuration = (float)Math.Max(minDuration, calcOpts.rotation.CurRotationDuration);

                float dodgeMissPerRotation = (float)(totalMeleeAbilities - calcOpts.rotation.FrostStrike);
                chanceAvoided = chanceDodged + chanceMiss;
                double GChanceAvoided             = (1 / (1 - chanceAvoided)) - 1;
                double GSpellResist               = (1 / (1 - spellResist)) - 1;
                double ProbableGCDLossPerRotation = dodgeMissPerRotation * physicalGCD * GChanceAvoided +
                                                    (calcOpts.rotation.IcyTouch + calcOpts.rotation.Pestilence) * spellGCD * GSpellResist;

                realDuration += (float)(((minDuration + ProbableGCDLossPerRotation) / realDuration < 1 ? (minDuration + ProbableGCDLossPerRotation) / realDuration : 1) * ProbableGCDLossPerRotation);
                // This last line is a bit hackish, but basically the extra GCD is more inconvenient the closer we are to having a GCD-capped rotation; once we're GCD-capped, they cost the full value.
            }
            #endregion
        }
Exemplo n.º 9
0
        public override Stats GetCharacterStats(Character character, Item additionalItem)
        {
            CalculationOptionsElemental calcOpts = character.CalculationOptions as CalculationOptionsElemental;

            Stats statsRace  = BaseStats.GetBaseStats(character);
            Stats statsItems = GetItemStats(character, additionalItem);
            Stats statsBuffs = GetBuffsStats(character, calcOpts);

            Stats statsTotal = statsRace + statsItems + statsBuffs;

            if (statsTotal.HighestStat > 0)
            {
                if (statsTotal.Spirit > statsTotal.Intellect)
                {
                    statsTotal.Spirit += (statsTotal.HighestStat * 15f / 50f);
                }
                else
                {
                    statsTotal.Intellect += (statsTotal.HighestStat * 15f / 50f);
                }
            }

            statsTotal.Strength  *= 1 + statsTotal.BonusStrengthMultiplier;
            statsTotal.Agility   *= 1 + statsTotal.BonusAgilityMultiplier;
            statsTotal.Stamina   *= 1 + statsTotal.BonusStaminaMultiplier;
            statsTotal.Intellect *= 1 + statsTotal.BonusIntellectMultiplier;
            statsTotal.Spirit    *= 1 + statsTotal.BonusSpiritMultiplier;

            statsTotal.Strength  = (float)Math.Floor(statsTotal.Strength);
            statsTotal.Agility   = (float)Math.Floor(statsTotal.Agility);
            statsTotal.Stamina   = (float)Math.Floor(statsTotal.Stamina);
            statsTotal.Intellect = (float)Math.Floor(statsTotal.Intellect);
            statsTotal.Spirit    = (float)Math.Floor(statsTotal.Spirit);

            if (Character.ValidateArmorSpecialization(character, ItemType.Mail))
            {
                statsTotal.Intellect *= 1.05f;
            }

            statsTotal.AttackPower += statsTotal.Strength + statsTotal.Agility;

            statsTotal.Mana += StatConversion.GetManaFromIntellect(statsTotal.Intellect);
            statsTotal.Mana *= (float)Math.Round(1f + statsTotal.BonusManaMultiplier);

            statsTotal.Health += StatConversion.GetHealthFromStamina(statsTotal.Stamina);
            statsTotal.Health *= (float)Math.Round(1f + statsTotal.BonusHealthMultiplier);

            statsTotal.SpellCrit += StatConversion.GetSpellCritFromRating(statsTotal.CritRating);
            statsTotal.SpellCrit += StatConversion.GetSpellCritFromIntellect(statsTotal.Intellect);
            statsTotal.SpellCrit += statsTotal.SpellCritOnTarget;
            statsTotal.SpellHit  += StatConversion.GetSpellHitFromRating(statsTotal.HitRating +
                                                                         ((.33f * character.ShamanTalents.ElementalPrecision) * statsTotal.Spirit));

            // Flametongue weapon assumed
            statsTotal.SpellPower += (float)Math.Floor(747.78 * (1f + character.ShamanTalents.ElementalWeapons * .2f));
            if (character.ShamanTalents.GlyphofFlametongueWeapon)
            {
                statsTotal.SpellCrit += .02f;
            }

            // Water shield assumed
            statsTotal.Mp5 += 100;
            if (character.ShamanTalents.GlyphofWaterShield)
            {
                statsTotal.Mp5 += 30;
            }

            return(statsTotal);
        }
Exemplo n.º 10
0
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> dictValues = new Dictionary <string, string>();
            Stats baseStats = BaseStats.GetBaseStats(character);

            dictValues.Add("Health", BasicStats.Health.ToString());
            dictValues.Add("Stamina", BasicStats.Stamina.ToString());
            float ResilienceCap = 0.15f, ResilienceFromRating = StatConversion.GetCritReductionFromResilience(1);
            float Resilience = StatConversion.GetCritReductionFromResilience(BasicStats.Resilience);

            dictValues.Add("Resilience", string.Format("{0}*-{1}% Damage from DoT and Mana Drains\n\r-{1}% Chance to be crit\r\n-{2}% Damage from Crits.\r\n{3}",
                                                       BasicStats.Resilience.ToString(),
                                                       (Resilience * 100f).ToString("0.00"),
                                                       (Resilience * 100f * 2.2f).ToString("0.00"),
                                                       (Resilience > ResilienceCap)?(string.Format("{0} rating above cap", ((float)Math.Floor((Resilience - ResilienceCap) / ResilienceFromRating)).ToString("0"))):(string.Format("{0} rating below cap", ((float)Math.Ceiling((ResilienceCap - Resilience) / ResilienceFromRating)).ToString("0")))));
            dictValues.Add("Mana", BasicStats.Mana.ToString());
            dictValues.Add("Intellect", BasicStats.Intellect.ToString());
            dictValues.Add("Spirit", Math.Floor(BasicStats.Spirit).ToString("0"));
            dictValues.Add("Spell Power", string.Format("{0}*{1} from Inner Fire",
                                                        Math.Floor(BasicStats.SpellPower).ToString("0"),
                                                        BasicStats.PriestInnerFire * CalculationsHealPriest.GetInnerFireSpellPowerBonus(character)));
            //dictValues.Add("Healing", Math.Floor(BasicStats.SpellPower * 1.88f).ToString("0"));
            dictValues.Add("In FSR MP5", string.Format("{0}*{1} from MP5\r\n{2} from Meditation\r\n{3} Outside FSR\r\n{4} OFSR w/MP5",
                                                       (BasicStats.Mp5 + RegenInFSR).ToString("0"),
                                                       BasicStats.Mp5.ToString("0"),
                                                       RegenInFSR.ToString("0"),
                                                       RegenOutFSR.ToString("0"),
                                                       (BasicStats.Mp5 + RegenOutFSR).ToString("0")));
            dictValues.Add("Spell Crit", string.Format("{0}%*{1}% from Intellect\r\n{2}% from {6} Crit rating\r\n{3}% from Focused Will\r\n{4}% Class Base\r\n{5}% from Buffs",
                                                       (BasicStats.SpellCrit * 100f).ToString("0.00"), (StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect) * 100f).ToString("0.00"), (StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f).ToString("0.00"), character.PriestTalents.FocusedWill.ToString("0"), (baseStats.SpellCrit * 100f).ToString("0.00"), (BasicStats.SpellCrit * 100f - baseStats.SpellCrit * 100f - StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f - StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect) * 100f - character.PriestTalents.FocusedWill * 1f).ToString("0.00"), BasicStats.CritRating));
            dictValues.Add("Healing Crit", string.Format("{0}%*{1} ({1}%) points in Holy Specialization\r\n{2} ({3}%) points in Renewed Hope",
                                                         ((BasicStats.SpellCrit * 100f) + character.PriestTalents.HolySpecialization * 1f + character.PriestTalents.RenewedHope * 2f).ToString("0.00"),
                                                         character.PriestTalents.HolySpecialization, character.PriestTalents.RenewedHope, character.PriestTalents.RenewedHope * 2));
            dictValues.Add("Spell Haste", string.Format("{0}%*{1}% from {2} Haste rating\r\n{3}% ({6}) points in Enlightenment\r\n{4}% from Buffs\r\n{5}s Global Cooldown",
                                                        (BasicStats.SpellHaste * 100f).ToString("0.00"), (StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating) * 100f).ToString("0.00"), BasicStats.HasteRating.ToString(), (character.PriestTalents.Enlightenment * 2).ToString("0"), (((1 + BasicStats.SpellHaste) / (1 + StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating)) / (1 + character.PriestTalents.Enlightenment * 0.02f) - 1) * 100f).ToString("0.00"), Math.Max(1.0f, 1.5f / (1 + BasicStats.SpellHaste)).ToString("0.00"), character.PriestTalents.Enlightenment));
            dictValues.Add("Armor", string.Format("{0}*{1}% Damage Reduction.",
                                                  (BasicStats.Armor + BasicStats.BonusArmor).ToString("0"),
                                                  (StatConversion.GetArmorDamageReduction(80, (BasicStats.Armor + BasicStats.BonusArmor), 0f, 0f, 0f) * 100f).ToString("0.00")));


            float[] Resistances =
            {
                0,
                BasicStats.ArcaneResistance + BasicStats.ArcaneResistanceBuff,
                BasicStats.FireResistance + BasicStats.FireResistanceBuff,
                BasicStats.FrostResistance + BasicStats.FrostResistanceBuff,
                BasicStats.NatureResistance + BasicStats.NatureResistanceBuff,
                BasicStats.ShadowResistance + BasicStats.ShadowResistanceBuff,
            };

            string[] ResistanceNames =
            {
                "None",
                "Arcane",
                "Fire",
                "Frost",
                "Nature",
                "Shadow",
            };

            string ResistanceString = "*Resistances:";

            float MaxResist      = Resistances[0];
            int   MaxResistIndex = 0;
            float AvgResist      = 0f;

            for (int x = 1; x < Resistances.Length; x++)
            {
                AvgResist += Resistances[x];
                if (Resistances[x] > MaxResist)
                {
                    MaxResist      = Resistances[x];
                    MaxResistIndex = x;
                }
                ResistanceString += string.Format("\r\n{0} : {1}", ResistanceNames[x], Resistances[x]);
            }
            AvgResist /= (Resistances.Length - 1);

            if (AvgResist == 0)
            {
                ResistanceString = "None" + ResistanceString;
            }
            else
            {
                string ResistanceName = (MaxResist == AvgResist) ? "All" : ResistanceNames[MaxResistIndex];
                ResistanceString  = string.Format("{0} : {1}", ResistanceName, MaxResist.ToString("0")) + ResistanceString;
                ResistanceString += string.Format("\r\n\r\nResist ({0}):", ResistanceName);
                ResistanceString += string.Format("\r\n{0}", StatConversion.GetResistanceTableString(character.Level + 3, character.Level, MaxResist, 0));
            }

            dictValues.Add("Resistance", ResistanceString);

            BaseSolver solver;

            if ((character.CalculationOptions as CalculationOptionsHealPriest).Role == eRole.CUSTOM)
            {
                solver = new AdvancedSolver(BasicStats, character);
            }
            else
            {
                solver = new Solver(BasicStats, character);
            }
            solver.Calculate(this);

            dictValues.Add("Role", string.Format("{0}*{1}", solver.Role, solver.ActionList));
            dictValues.Add("Burst", string.Format("{0}", HPSBurstPoints.ToString("0")));
            dictValues.Add("Sustained", string.Format("{0}", HPSSustainPoints.ToString("0")));

            dictValues.Add("Renew", new Renew(BasicStats, character).ToString());
            dictValues.Add("Flash Heal", new FlashHeal(BasicStats, character).ToString());
            dictValues.Add("Greater Heal", new Heal(BasicStats, character).ToString());
            dictValues.Add("PoH", new PrayerOfHealing(BasicStats, character).ToString());
            dictValues.Add("Binding Heal", new BindingHeal(BasicStats, character).ToString());
            dictValues.Add("Prayer of Mending", new PrayerOfMending(BasicStats, character).ToString());
            dictValues.Add("Power Word Shield", new PowerWordShield(BasicStats, character).ToString());
            dictValues.Add("Holy Nova", new HolyNova(BasicStats, character).ToString());

            if (character.PriestTalents.CircleOfHealing > 0)
            {
                dictValues.Add("CoH", new CircleOfHealing(BasicStats, character).ToString());
            }
            else
            {
                dictValues.Add("CoH", "- *No required talents");
            }

            if (character.PriestTalents.Lightwell > 0)
            {
                dictValues.Add("Lightwell", new Lightwell(BasicStats, character).ToString());
            }
            else
            {
                dictValues.Add("Lightwell", "- *No required talents");
            }

            if (character.PriestTalents.Penance > 0)
            {
                dictValues.Add("Penance", new Penance(BasicStats, character).ToString());
            }
            else
            {
                dictValues.Add("Penance", "- *No required talents");
            }

            if (Race == CharacterRace.Draenei)
            {
                dictValues.Add("Gift of the Naaru", new GiftOfTheNaaru(BasicStats, character).ToString());
            }
            else
            {
                dictValues.Add("Gift of the Naaru", "-");
            }
            dictValues.Add("Divine Hymn", new DivineHymn(BasicStats, character).ToString());
            dictValues.Add("Resurrection", new Resurrection(BasicStats, character).ToString());

            return(dictValues);
        }
Exemplo n.º 11
0
        public void UpdateCalcs(bool firstPass)
        {
            // talents
            if (_calcOpts.PriorityInUse(EnhanceAbility.StormStrike))
            {
                stormstrikeBonusCrit = .25f * _talents.Stormstrike + (_talents.GlyphofStormstrike ? .1f : 0f);
            }
            else
            {
                stormstrikeBonusCrit = 0f;
            }
            //set bonus
            enhance4T11 = 0f;
            _character.SetBonusCount.TryGetValue("Battlegear of the Raging Elements", out setCount);
            if (setCount >= 4)
            {
                enhance4T11 = 0.1f;
            }

            critMultiplierSpell = 1.5f * (1 + _stats.BonusSpellCritDamageMultiplier);
            critMultiplierMelee = 2f * (1 + _stats.BonusCritDamageMultiplier);

            // Melee
            float hitBonus = _stats.PhysicalHit + StatConversion.GetHitFromRating(_stats.HitRating) + 0.06f;  //DualWieldSpecialization

            expertiseBonusMH = GetDPRfromExp(_stats.Expertise + StatConversion.GetExpertiseFromRating(_stats.ExpertiseRating) + BaseStats.GetRacialExpertise(_character, ItemSlot.MainHand));
            expertiseBonusOH = GetDPRfromExp(_stats.Expertise + StatConversion.GetExpertiseFromRating(_stats.ExpertiseRating) + BaseStats.GetRacialExpertise(_character, ItemSlot.OffHand));

            float meleeCritModifier = _stats.PhysicalCrit;
            float baseMeleeCrit     = StatConversion.GetCritFromRating(_stats.CritRating) +
                                      StatConversion.GetCritFromAgility(_stats.Agility, _character.Class) + .01f * _talents.Acuity;

            chanceDodgeMH = Math.Max(0f, DodgeChanceCap - expertiseBonusMH);
            chanceDodgeOH = Math.Max(0f, DodgeChanceCap - expertiseBonusOH);
            float ParryChance = ParryChanceCap - expertiseBonusMH;

            //chanceParryMH = (float)Math.Max(0f, _bossOpts.InBack ? ParryChance * PecentageInfrontBoss : ParryChance);
            ParryChance = ParryChanceCap - expertiseBonusOH;
            //chanceParryOH = (float)Math.Max(0f, _bossOpts.InBack ? ParryChance * PecentageInfrontBoss : ParryChance);
            chanceWhiteMissMH  = Math.Max(0f, WhiteHitCap - hitBonus) + chanceDodgeMH + chanceParryMH;
            chanceWhiteMissOH  = Math.Max(0f, WhiteHitCap - hitBonus) + chanceDodgeOH + chanceParryOH;
            chanceYellowMissMH = Math.Max(0f, YellowHitCap - hitBonus) + chanceDodgeMH + chanceParryMH; // base miss 8% now
            chanceYellowMissOH = Math.Max(0f, YellowHitCap - hitBonus) + chanceDodgeOH + chanceParryOH; // base miss 8% now

            // SetCritValues((1 + _stats.BonusCritChance) * (baseMeleeCrit + meleeCritModifier) + .00005f); //fudge factor for rounding
            SetCritValues(baseMeleeCrit + meleeCritModifier + .00005f); //fudge factor for rounding
            // set two export values so that ED crit isn't included
            exportMeleeCritMH = chanceWhiteCritMH + whiteCritDepression;
            exportMeleeCritOH = chanceWhiteCritOH + whiteCritDepression;

            // Spells
            ftBonusCrit = 0f;
            if (_calcOpts.MainhandImbue == "Flametongue")
            {
                ftBonusCrit += _talents.GlyphofFlametongueWeapon ? .02f : 0f;
            }
            if (_calcOpts.OffhandImbue == "Flametongue")
            {
                ftBonusCrit += _talents.GlyphofFlametongueWeapon ? .02f : 0f;
            }

            if (baseStats == null)
            {
                baseStats = BaseStats.GetBaseStats(_character);
            }

            elemPrecMod = _talents.ElementalPrecision > 0 ? (_stats.Spirit - baseStats.Spirit) * (_talents.ElementalPrecision / 3f) : 0f;
            float hitBonusSpell = _stats.SpellHit + StatConversion.GetSpellHitFromRating(_stats.HitRating + elemPrecMod);

            chanceSpellMiss = Math.Max(0f, SpellMissRate - hitBonusSpell);
            overSpellHitCap = Math.Max(0f, hitBonusSpell - SpellMissRate);
            float spellCritModifier = _stats.SpellCrit + _stats.SpellCritOnTarget + ftBonusCrit;
            float baseSpellCrit     = StatConversion.GetSpellCritFromRating(_stats.CritRating) +
                                      StatConversion.GetSpellCritFromIntellect(_stats.Intellect) + .01f * _talents.Acuity;

            //chanceSpellCrit = Math.Min(0.75f, (1 + _stats.BonusCritChance) * (baseSpellCrit + spellCritModifier) + .00005f); //fudge factor for rounding
            chanceSpellCrit = Math.Min(0.75f, baseSpellCrit + spellCritModifier + .00005f); //fudge factor for rounding

            float hasteBonus = StatConversion.GetHasteFromRating(_stats.HasteRating, _character.Class);

            unhastedMHSpeed = _character.MainHand == null ? 3.0f : _character.MainHand.Item.Speed;
            unhastedOHSpeed = _character.OffHand == null ? 3.0f : _character.OffHand.Item.Speed;
            float baseHastedMHSpeed    = unhastedMHSpeed / (1f + hasteBonus) / (1f + _stats.PhysicalHaste);
            float baseHastedOHSpeed    = unhastedOHSpeed / (1f + hasteBonus) / (1f + _stats.PhysicalHaste);
            float chanceToProcWFPerHit = .2f + (_character.ShamanTalents.GlyphofWindfuryWeapon ? .02f : 0f);

            if (_bossOpts.MultiTargs && _bossOpts.Targets != null && _bossOpts.Targets.Count > 0)
            {
                foreach (TargetGroup tg in _bossOpts.Targets)
                {
                    if (tg.Frequency <= 0 || tg.Chance <= 0)
                    {
                        continue; //bad one, skip it
                    }
                    float upTime = (tg.Frequency / fightLength * (tg.Duration / 1000f) * tg.Chance) / fightLength;
                    multiTargetMultiplier += (Math.Max(10, tg.NumTargs - (tg.NearBoss ? 0 : 1))) * upTime;
                }
            }

            //The Swing Loop
            //This is where we figure out feedback systems -- WF, MW, ED, Flurry, etc.
            //--------------
            flurryUptime = 1f;
            uWUptime     = 0f;
            //uFUptime = 0f;
            edUptime = 0f;
            float stormstrikeSpeed = firstPass ? (_talents.Stormstrike == 1 ? 8f : 0f) : AbilityCooldown(EnhanceAbility.StormStrike);
            float shockSpeed       = firstPass ? BaseShockSpeed : AbilityCooldown(EnhanceAbility.EarthShock);
            float lavaLashSpeed    = firstPass ? 10f : AbilityCooldown(EnhanceAbility.LavaLash);
            float fireNovaSpeed    = firstPass ? BaseFireNovaSpeed : AbilityCooldown(EnhanceAbility.FireNova);

            if (_calcOpts.PriorityInUse(EnhanceAbility.MagmaTotem))
            {
                fireTotemUptime = firstPass ? 1.0f : 60f / AbilityCooldown(EnhanceAbility.MagmaTotem);
            }
            else if (_calcOpts.PriorityInUse(EnhanceAbility.SearingTotem))
            {
                fireTotemUptime = firstPass ? 1.0f : 60f / AbilityCooldown(EnhanceAbility.SearingTotem);
            }
            else if (_calcOpts.PriorityInUse(EnhanceAbility.RefreshTotems)) // if no Searing or Magma totem use refresh of Flametongue totem.
            {
                fireTotemUptime = firstPass ? 1.0f : 300f / AbilityCooldown(EnhanceAbility.RefreshTotems);
            }

            float mwPPM             = (10f / 3f) * _talents.MaelstromWeapon;
            float flurryHasteBonus  = .10f * _talents.Flurry;
            float uWHasteBonus      = .4f + .1f * _talents.ElementalWeapons;
            float edCritBonus       = .03f * _talents.ElementalDevastation;
            float staticShockChance = .15f * _character.ShamanTalents.StaticShock;

            hitsPerSMHSS = 0f;
            hitsPerSOHSS = 0f;
            hitsPerSOH   = 0f;
            hitsPerSMH   = 0f;
            hitsPerSWF   = 0f;
            if (_talents.Stormstrike == 1)
            {
                hitsPerSMHSS = (1f - chanceYellowMissMH) / stormstrikeSpeed;
                hitsPerSOHSS = (1f - 2 * chanceYellowMissOH) / stormstrikeSpeed; //OH only swings if MH connect
            }
            hitsPerSLL = lavaLashSpeed == 0 ? 0f : (1f - chanceYellowMissOH) / lavaLashSpeed;
            float swingsPerSMHMelee = 0f;
            float swingsPerSOHMelee = 0f;
            float wfProcsPerSecond  = 0f;
            float mwProcsPerSecond  = 0f;

            secondsToFiveStack = 10f;
            float averageMeleeCritChance   = (chanceWhiteCritMH + chanceWhiteCritOH + chanceYellowCritMH + chanceYellowCritOH) / 4f;
            float averageMeleeHitChance    = ((1f - chanceWhiteMissMH - chanceDodgeMH - chanceParryMH) + (1f - chanceWhiteMissOH - chanceDodgeOH - chanceParryOH)) / 2f;
            float averageMeleeMissChance   = (chanceWhiteMissMH + chanceWhiteMissOH) / 2f;
            float whiteHitsPerSMH          = 0f;
            float whiteHitsPerSOH          = 0f;
            float moteHitsPerS             = 0f;
            float yellowHitsPerSMH         = 0f;
            float yellowHitsPerSOH         = 0f;
            float flameTongueHitsPerSecond = 0f;

            for (int i = 0; i < 5; i++)
            {
                // float bonusHaste = (1f + (flurryUptime * flurryHasteBonus));
                float bonusHaste = 1 / (1 - flurryUptime + flurryUptime / (1 + flurryHasteBonus)) / (1 - uWUptime + uWUptime / (1 + uWHasteBonus)); // use time based not proc based flurryUptime
                hastedMHSpeed     = baseHastedMHSpeed / bonusHaste;
                hastedOHSpeed     = baseHastedOHSpeed / bonusHaste;
                swingsPerSMHMelee = 1f / hastedMHSpeed;
                swingsPerSOHMelee = (hastedOHSpeed == 0f) ? 0f : 1f / hastedOHSpeed;
                whiteHitsPerSMH   = ChanceWhiteHitMH * swingsPerSMHMelee;
                whiteHitsPerSOH   = ChanceWhiteHitOH * swingsPerSOHMelee;
                moteHitsPerS      = _stats.MoteOfAnger * 2 * AverageWhiteHitChance;
                // Windfury model
                if (_calcOpts.MainhandImbue == "Windfury")
                {
                    float hitsThatProcWFPerS = whiteHitsPerSMH + hitsPerSMHSS;
                    if (unhastedOHSpeed != 0f)
                    {
                        hitsThatProcWFPerS += moteHitsPerS / 2; // half the hits will be OH and thus won't proc WF
                    }
                    else
                    {
                        hitsThatProcWFPerS += moteHitsPerS; // if no offhand then all motes will be MH weapon by definition
                    }
                    float maxExpectedWFPerFight = hitsThatProcWFPerS * chanceToProcWFPerHit * fightLength;
                    float ineligibleSeconds     = maxExpectedWFPerFight * (3.25f - hastedMHSpeed);
                    float expectedWFPerFight    = hitsThatProcWFPerS * chanceToProcWFPerHit * (fightLength - ineligibleSeconds);
                    wfProcsPerSecond = expectedWFPerFight / fightLength;
                    hitsPerSWF       = 3f * wfProcsPerSecond * (1f - chanceYellowMissMH);
                }
                yellowHitsPerSMH = hitsPerSWF + hitsPerSMHSS;
                yellowHitsPerSOH = hitsPerSOHSS + hitsPerSLL;

                //Due to attack table, a white swing has the same chance to crit as a yellow hit
// Old Flurry calc changed 10 Nov 2009
//                couldCritSwingsPerSecond = whiteHitsPerSMH + whiteHitsPerSOH + yellowHitsPerSMH + yellowHitsPerSOH;
//                float swingsThatConsumeFlurryPerSecond = swingsPerSMHMelee + swingsPerSOHMelee;
//                flurryUptime = 1f - (float)Math.Pow(1 - averageMeleeCritChance, (3 / swingsThatConsumeFlurryPerSecond) * couldCritSwingsPerSecond);  // old formulae
                flurryUptime = CalculateFlurryUptime(averageMeleeCritChance, averageMeleeHitChance, averageMeleeMissChance);
                //uWUptime = (float)Math.Max(12f / 15f, ??);  //FIXME!!!!
                uWUptime = 7.5f / 15f;  //Temp Uptime until above line is fixed

                // Maelstrom Weapon time to 5 stacks calc
                if (unhastedOHSpeed != 0f)
                {
                    hitsPerSMH       = whiteHitsPerSMH + yellowHitsPerSMH + moteHitsPerS / 2;
                    hitsPerSOH       = whiteHitsPerSOH + yellowHitsPerSOH + moteHitsPerS / 2;
                    mwProcsPerSecond = (mwPPM / (60f / unhastedMHSpeed)) * hitsPerSMH + (mwPPM / (60f / unhastedOHSpeed)) * hitsPerSOH;
                }
                else
                {
                    hitsPerSMH       = whiteHitsPerSMH + yellowHitsPerSMH + moteHitsPerS;
                    hitsPerSOH       = 0f;
                    mwProcsPerSecond = (mwPPM / (60f / unhastedMHSpeed)) * hitsPerSMH;
                }
                secondsToFiveStack = 5f / mwProcsPerSecond;

                // Elemental Devastation Uptime calc
                staticShocksPerSecond    = (hitsPerSLL + hitsPerSMHSS + hitsPerSOHSS) * staticShockChance;
                flameTongueHitsPerSecond = (_calcOpts.MainhandImbue == "Flametongue" ? HitsPerSMH : 0f) +
                                           ((_calcOpts.OffhandImbue == "Flametongue") ? HitsPerSOH : 0f);
                spellAttacksPerSec = (1f / secondsToFiveStack + 1f / shockSpeed + 1f / fireNovaSpeed + staticShocksPerSecond) // + flameTongueHitsPerSecond)
                                     * (1f - chanceSpellMiss);
                float couldCritSpellsPerS = spellAttacksPerSec;
                edUptime = 1f - (float)Math.Pow(1 - chanceSpellCrit, 10 * couldCritSpellsPerS);
                averageMeleeCritChance = (chanceWhiteCritMH + chanceWhiteCritOH + chanceYellowCritMH + chanceYellowCritOH) / 4f + edUptime * edCritBonus;
            }
            float yellowAttacksPerSecond = hitsPerSWF + hitsPerSMHSS;

            if (unhastedMHSpeed != 0)
            {
                yellowAttacksPerSecond += hitsPerSOHSS;
            }

            // set output variables
            edBonusCrit = edUptime * edCritBonus;
            //SetCritValues((1 + _stats.BonusCritChance) * (baseMeleeCrit + meleeCritModifier) + edBonusCrit + .00005f); //fudge factor for rounding
            SetCritValues(baseMeleeCrit + meleeCritModifier + edBonusCrit + .00005f); //fudge factor for rounding
            meleeAttacksPerSec = hitsPerSMH + hitsPerSOH;
            meleeCritsPerSec   = (whiteHitsPerSMH * chanceWhiteCritMH) + (whiteHitsPerSOH * chanceWhiteCritOH) +
                                 (yellowHitsPerSMH * chanceYellowCritMH) + (yellowHitsPerSOH * chanceYellowCritOH) +
                                 (_stats.MoteOfAnger * 2 * AverageWhiteCritChance);
            spellCritsPerSec  = spellAttacksPerSec * ChanceSpellCrit;
            spellCastsPerSec  = spellAttacksPerSec;
            spellMissesPerSec = spellAttacksPerSec * chanceSpellMiss;
            chanceMeleeHit    = meleeAttacksPerSec / (swingsPerSMHMelee + swingsPerSOHMelee + 2f * wfProcsPerSecond + .25f + 1f / 6f);
            maxMana           = _stats.Mana;
            float spellhaste = _stats.SpellHaste + StatConversion.GetSpellHasteFromRating(_stats.HasteRating);

            averageFSDotTime  = _talents.GlyphofFlameShock ? 27f : 18f;
            averageFSTickTime = 3f / (1f + spellhaste);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Unlike the melee combat system, spell crit makes absolutely no difference to hit chance.
        /// All spells, regardless of whether they are treated as binary or not, roll hit and crit separately.
        /// Conceptually, the game rolls for your hit chance first, and if the spell hits you have a separate roll for whether it crits.
        /// Overall chance to crit over all spells cast is thus affected by hit rate.
        /// To calculate overall crit rate, multiplying the two chances together:
        /// Crit rate over all spell casts = crit/// hit
        ///
        /// For example, a caster with no spell hit rating gear or talents,
        /// against a mob 3 levels higher (83% hit chance), and 30% crit rating from gear and talents:
        /// crit rate over all spell casts = 30%/// 83% = 24.9%
        ///
        /// A level 80 player against a level 83 boss needs +26.232*k hit rating, to achieve +k% chance to hit with spells.
        /// In addition, direct damage spells suffer from partial resistance, but again, that has no effect on whether a spell hits or not.
        /// </summary>
        /// <param name="character"></param>
        /// <param name="stats"></param>
        /// <returns></returns>
        public static float SpellCritChance(Character character, Stats stats, int targetLevel)
        {
            float spellCrit = Math.Min(1f, StatConversion.GetSpellCritFromRating(stats.CritRating, CharacterClass.Paladin) + StatConversion.GetSpellCritFromIntellect(stats.Intellect, CharacterClass.Paladin) + stats.SpellCrit + stats.SpellCritOnTarget);

            return(spellCrit * SpellHitChance(character, stats, targetLevel));
        }
Exemplo n.º 13
0
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> dictValues = new Dictionary <string, string>();
            Buff   activeBuff;
            string s;
            Stats  baseStats = BaseStats.GetBaseStats(Character);

            #region General
            dictValues["Health"]     = BasicStats.Health.ToString("0");
            dictValues["Mana"]       = BasicStats.Mana.ToString("0");
            dictValues["Item Level"] = String.Format("{0}*Lowest: {1}\nHighest: {2}", Character.AvgWornItemLevel.ToString("0"), Character.MinWornItemLevel.ToString("0"), Character.MaxWornItemLevel.ToString("0"));
            dictValues["Speed"]      = String.Format("{0}%*{0}% Run speed",
                                                     ((1f + BasicStats.MovementSpeed) * 100f).ToString("0"));
            #endregion
            #region Attributes
            dictValues["Strength"]  = BasicStats.Strength.ToString();
            dictValues["Agility"]   = BasicStats.Agility.ToString();
            dictValues["Stamina"]   = BasicStats.Stamina.ToString();
            dictValues["Intellect"] = BasicStats.Intellect.ToString();
            dictValues["Spirit"]    = BasicStats.Spirit.ToString();
            #endregion
            #region Spell
            #region Spell Power
            s = String.Empty;
            float intPower = BasicStats.Intellect - 10;
            if (BasicStats.InnerFire)
            {
                s += String.Format("\n{0} from Inner Fire", PriestInformation.GetInnerFireSpellPowerBonus(Character));
            }
            activeBuff = GetActiveBuffsByGroup("Spell Power");
            if (activeBuff != null)
            {
                s += makeActiveBuffTextPercent(activeBuff, activeBuff.Stats.BonusSpellPowerMultiplier);
            }
            dictValues["Spell Power"] = String.Format("{0}*{1} from {2} Intellect{3}",
                                                      BasicStats.SpellPower.ToString("0"),
                                                      intPower.ToString("0"),
                                                      BasicStats.Intellect.ToString("0"),
                                                      s
                                                      );
            #endregion
            #region Haste
            s = String.Empty;
            if (Character.PriestTalents.Darkness > 0)
            {
                s += String.Format("\n{0}% from {1} points in Darkness", Character.PriestTalents.Darkness, Character.PriestTalents.Darkness);
            }
            activeBuff = GetActiveBuffsByGroup("Spell Haste");
            if (activeBuff != null)
            {
                s += makeActiveBuffTextPercent(activeBuff, activeBuff.Stats.SpellHaste);
            }
            activeBuff = GetActiveBuffsByGroup("Dark Intent");
            if (activeBuff != null)
            {
                s += makeActiveBuffTextPercent(activeBuff, activeBuff.Stats.SpellHaste);
            }
            dictValues["Haste"] = String.Format("{0}%*{1}% from {2} Haste Rating{3}",
                                                (BasicStats.SpellHaste * 100f).ToString("0.00"),
                                                (StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating) * 100f).ToString("0.00"), BasicStats.HasteRating.ToString("0"),
                                                s
                                                );
            #endregion
            dictValues["Hit"]         = (BasicStats.SpellHit * 100f).ToString("0.00");
            dictValues["Penetration"] = BasicStats.SpellPenetration.ToString("0");
            #region Mana Regen
            float manaRegen = StatConversion.GetSpiritRegenSec(BasicStats.Spirit, BasicStats.Intellect) * 5f;
            s          = String.Format("\n{0} Mana per 5 sec from Base Mana Regeneration", (baseStats.Mana * 0.05f).ToString("0"));
            activeBuff = GetActiveBuffsByGroup("Mana Regeneration");
            if (activeBuff != null)
            {
                s += makeActiveBuffText(activeBuff, activeBuff.Stats.Mp5);
            }
            dictValues["Mana Regen"] = String.Format("{0}*{1} from Spirit based regen{2}",
                                                     (manaRegen + BasicStats.Mp5).ToString("0"),
                                                     manaRegen.ToString("0"),
                                                     s
                                                     );
            dictValues["Combat Regen"] = String.Format("{0}*{1} from Spirit based regen{2}",
                                                       (manaRegen * BasicStats.SpellCombatManaRegeneration + BasicStats.Mp5).ToString("0"),
                                                       (manaRegen * BasicStats.SpellCombatManaRegeneration).ToString("0"),
                                                       s
                                                       );
            #endregion
            #region Crit
            s          = String.Empty;
            activeBuff = GetActiveBuffsByGroup("Critical Strike Chance");
            if (activeBuff != null)
            {
                s += makeActiveBuffTextPercent(activeBuff, activeBuff.Stats.SpellCrit);
            }
            activeBuff = GetActiveBuffsByGroup("Focus Magic, Spell Critical Strike Chance");
            if (activeBuff != null)
            {
                s += makeActiveBuffTextPercent(activeBuff, activeBuff.Stats.SpellCrit);
            }
            dictValues["Crit Chance"] = String.Format("{0}%*{1}% from {2} Crit Rating\n{3}% from {4} Intellect\n{5}% from Priest base{6}",
                                                      (BasicStats.SpellCrit * 100f).ToString("0.00"),
                                                      (StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f).ToString("0.00"), BasicStats.CritRating.ToString("0"),
                                                      (StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect) * 100f).ToString("0.00"), BasicStats.Intellect.ToString("0"),
                                                      (baseStats.SpellCrit * 100f).ToString("0.00"),
                                                      s
                                                      );
            #endregion
            #region Mastery
            s = string.Empty;
            float masteryBase = 0, masteryBonus = 0;
            if (BasicStats.PriestSpec == ePriestSpec.Spec_Disc)
            {
                masteryBase  = PriestInformation.DisciplineMasteryBase;
                masteryBonus = PriestInformation.DisciplineMasteryEffect * 100f;
                s           += String.Format("\n\nEach point of mastery increases the potency of Absorbs by an additional {0}%.", masteryBonus.ToString("0.00"));
            }
            else if (BasicStats.PriestSpec == ePriestSpec.Spec_Holy)
            {
                masteryBase  = PriestInformation.HolyMasteryBase;
                masteryBonus = PriestInformation.HolyMasteryEffect * 100f;
                s           += String.Format("\n\nEach point of mastery provides an additional {0}% healing over 6 sec.", masteryBonus.ToString("0.00"));
            }
            dictValues["Mastery"] = String.Format("{0}%*{1}% from {2} Mastery Rating\n{3}% from {4} Base Mastery{5}",
                                                  ((StatConversion.GetMasteryFromRating(BasicStats.MasteryRating) + masteryBase) * masteryBonus).ToString("0.00"),
                                                  (StatConversion.GetMasteryFromRating(BasicStats.MasteryRating) * masteryBonus).ToString("0.00"), BasicStats.MasteryRating.ToString("0"),
                                                  (masteryBase * masteryBonus).ToString("0.00"), masteryBase.ToString("0"),
                                                  s
                                                  );
            #endregion
            #endregion
            #region Defense
            dictValues["Armor"] = String.Format("{0}*{1}% physical damage reduction from same level target",
                                                BasicStats.Armor.ToString("0"),
                                                (StatConversion.GetDamageReductionFromArmor(Character.Level, BasicStats.Armor) * 100f).ToString("0.00"));
            dictValues["Dodge"]      = String.Format("{0}%", (BasicStats.Dodge * 100f).ToString("0.00"));
            dictValues["Resilience"] = String.Format("{0}*{1}% damage reduction on attacks from other players\n{2}% damage reduction from spells",
                                                     BasicStats.Resilience.ToString("0"),
                                                     (StatConversion.GetDamageReductionFromResilience(BasicStats.Resilience) * 100f).ToString("0.00"),
                                                     (Character.PriestTalents.InnerSanctum * 2f).ToString("0"));
            #endregion
            #region Resistance
            string   resistTxt   = "{0}*PvP\n{1}\n\nBoss\n{2}";
            string[] resistList  = { "Arcane", "Fire", "Frost", "Nature", "Shadow" };
            float[]  resistances = { BasicStats.ArcaneResistance + BasicStats.ArcaneResistanceBuff,
                                     BasicStats.FireResistance + BasicStats.FireResistanceBuff,
                                     BasicStats.FrostResistance + BasicStats.FrostResistanceBuff,
                                     BasicStats.NatureResistance + BasicStats.NatureResistanceBuff,
                                     BasicStats.ShadowResistance + BasicStats.ShadowResistanceBuff };

            for (int x = 0; x < resistList.Length; x++)
            {
                dictValues[resistList[x]] = String.Format(resistTxt,
                                                          resistances[x].ToString("0"),
                                                          StatConversion.GetResistanceTableString(Character.Level, Character.Level, resistances[x], 0f),
                                                          StatConversion.GetResistanceTableString(Character.Level + 3, Character.Level, resistances[x], 0f)
                                                          );
            }
            #endregion
            #region Model
            CalculationOptionsHealPriest calcOpts = Character.CalculationOptions as CalculationOptionsHealPriest;
            if (calcOpts != null)
            {
                PriestSolver solver = PriestModels.GetModel(this, calcOpts, true);
                solver.Solve();
                List <string> reqs = solver.MeetsRequirements();
                string        disp;
                if (reqs.Count > 0)
                {
                    disp = String.Format("{0}\n\n{1}", solver.Name, String.Join("\n", reqs));
                }
                else
                {
                    disp = solver.Name;
                }
                dictValues["Role"]       = disp;
                dictValues["Burst Goal"] = this.BurstGoal.ToString("0");
                dictValues["Sust. Goal"] = this.SustainGoal.ToString("0");
                dictValues["Mana Goal"]  = this.ManaGoal.ToString("0");
                dictValues["Burst"]      = this.BurstPoints.ToString("0");
                dictValues["Sustained"]  = this.SustainPoints.ToString("0");
                dictValues["Mana "]      = this.ManaPoints.ToString("0");
            }
            #endregion
            #region Holy Spells
            SpellHeal spellHeal = new SpellHeal(Character, BasicStats);
            dictValues["Heal"] = String.Format("{0}*{1}", spellHeal.HPS().ToString("0"), spellHeal.ToString());
            SpellGreaterHeal spellGreaterHeal = new SpellGreaterHeal(Character, BasicStats);
            dictValues["Greater Heal"] = String.Format("{0}*{1}", spellGreaterHeal.HPS().ToString("0"), spellGreaterHeal.ToString());
            SpellFlashHeal spellFlashHeal = new SpellFlashHeal(Character, BasicStats);
            dictValues["Flash Heal"] = String.Format("{0}*{1}", spellFlashHeal.HPS().ToString("0"), spellFlashHeal.ToString());
            SpellBindingHeal spellBindingHeal = new SpellBindingHeal(Character, BasicStats);
            dictValues["Binding Heal"] = String.Format("{0}*{1}", spellBindingHeal.HPS().ToString("0"), spellBindingHeal.ToString());
            SpellRenew spellRenew = new SpellRenew(Character, BasicStats);
            dictValues["Renew"] = String.Format("{0}*{1}", spellRenew.HPS().ToString("0"), spellRenew.ToString());
            if (Character.PriestTalents.Lightwell > 0)
            {
                SpellLightwell spellLW = new SpellLightwell(Character, BasicStats);
                dictValues["Lightwell"] = String.Format("{0}*{1}", spellLW.HPS().ToString("0"), spellLW.ToString());
            }
            else
            {
                dictValues["Lightwell"] = "N/A*You do not have the talent required.";
            }
            SpellPrayerOfHealing spellProH = new SpellPrayerOfHealing(Character, BasicStats);
            dictValues["ProH"] = String.Format("{0}*{1}", spellProH.HPS().ToString("0"), spellProH.ToString());
            SpellHolyNova spellHolyNova = new SpellHolyNova(Character, BasicStats);
            dictValues["Holy Nova"] = String.Format("NYI*{1}", spellHolyNova.HPS().ToString("0"), spellHolyNova.ToString());
            if (Character.PriestTalents.CircleOfHealing > 0)
            {
                SpellCircleOfHealing spellCoH = new SpellCircleOfHealing(Character, BasicStats);
                dictValues["CoH"] = String.Format("{0}*{1}", spellCoH.HPS().ToString("0"), spellCoH.ToString());
            }
            else
            {
                dictValues["CoH"] = "N/A*You do not have the talent required.";
            }
            if (BasicStats.PriestSpec == ePriestSpec.Spec_Disc)
            {
                SpellPenance spellPenance = new SpellPenance(Character, BasicStats);
                dictValues["Penance"] = String.Format("{0}*{1}", spellPenance.HPS().ToString("0"), spellPenance.ToString());
            }
            else
            {
                dictValues["Penance"] = "N/A*You do not have the correct Talent specialization.";
            }
            if (Character.PriestTalents.Revelations > 0)
            {
                SpellSerenity spellSerenity = new SpellSerenity(Character, BasicStats);
                dictValues["HW Serenity"] = String.Format("{0}*{1}", spellSerenity.HPS().ToString("0"), spellSerenity.ToString());
                SpellSanctuary spellSanctuary = new SpellSanctuary(Character, BasicStats);
                dictValues["HW Sanctuary"] = String.Format("{0}*{1}", spellSanctuary.HPS().ToString("0"), spellSanctuary.ToString());
            }
            else
            {
                dictValues["HW Serenity"]  = "N/A*You do not have the talent required.";
                dictValues["HW Sanctuary"] = "N/A*You do not have the talent required.";
            }
            SpellPrayerOfMending spellProM = new SpellPrayerOfMending(Character, BasicStats, 1);
            dictValues["ProM"]        = String.Format("{0}*{1}", spellProM.HPS().ToString("0"), spellProM.ToString());
            spellProM                 = new SpellPrayerOfMending(Character, BasicStats);
            dictValues["ProM 5 Hits"] = String.Format("{0}*{1}", spellProM.HPS().ToString("0"), spellProM.ToString());
            SpellPowerWordShield spellPWS = new SpellPowerWordShield(Character, BasicStats);
            dictValues["PWS"] = String.Format("{0}*{1}", spellPWS.HPS().ToString("0"), spellPWS.ToString());
            SpellDivineHymn spellDivineHymn = new SpellDivineHymn(Character, BasicStats);
            dictValues["Divine Hymn"] = String.Format("{0}*{1}", spellDivineHymn.HPS().ToString("0"), spellDivineHymn.ToString());
            if (Character.Race == CharacterRace.Draenei)
            {
                SpellGiftOfTheNaaru spellGoat = new SpellGiftOfTheNaaru(Character, BasicStats);
                dictValues["Gift of the Naaru"] = String.Format("{0}*{1}", spellGoat.HPS().ToString("0"), spellGoat.ToString());
            }
            else
            {
                dictValues["Gift of the Naaru"] = "N/A*You are not a spacegoat!";
            }
            SpellResurrection spellResurrection = new SpellResurrection(Character, BasicStats);
            dictValues["Resurrection"] = String.Format("{0}*{1}", spellResurrection.CastTime.ToString("0.00"), spellResurrection.ToString());
            #endregion
            #region Shadow Spells
            #endregion
            return(dictValues);
        }
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> retVal = new Dictionary <string, string>();

            retVal.Add("Health", baseStats.Health.ToString());
            retVal.Add("Mana", baseStats.Mana.ToString());
            retVal.Add("Armor", baseStats.Armor.ToString());
            retVal.Add("Agility", baseStats.Agility.ToString());
            retVal.Add("Stamina", baseStats.Stamina.ToString());
            retVal.Add("Intellect", baseStats.Intellect.ToString());
            retVal.Add("Spirit", baseStats.Spirit.ToString());
            retVal.Add("Spell Power", SpellPower.ToString());
            retVal.Add("Spell Hit", String.Format("{0:F}%*{1} Hit Rating, {2:F}% Hit From Gear, {3} Rating To Cap",
                                                  100 * SpellHit,
                                                  baseStats.HitRating,
                                                  100 * StatConversion.GetSpellHitFromRating(baseStats.HitRating),
                                                  StatConversion.GetRatingFromHit(Math.Max(0, 0.17f - SpellHit))));
            retVal.Add("Spell Crit", String.Format("{0:F}%*{1} Crit Rating, {2:F}% Crit From Gear, {3:F}% Crit From Intellect",
                                                   100 * SpellCrit,
                                                   baseStats.CritRating,
                                                   100 * StatConversion.GetSpellCritFromRating(baseStats.CritRating),
                                                   100 * StatConversion.GetSpellCritFromIntellect(baseStats.Intellect)));
            retVal.Add("Spell Haste", String.Format("{0:F}%*{1} Haste Rating, {2:F}% Haste From Gear",
                                                    100 * SpellHaste,
                                                    baseStats.HasteRating,
                                                    100 * StatConversion.GetSpellHasteFromRating(baseStats.HasteRating)));
            retVal.Add("MP5 Not Casting", String.Format("{0:F0}", ManaRegen * 5.0f));
            retVal.Add("MP5 While Casting", String.Format("{0:F0}", ManaRegen5SR * 5.0f));
            retVal.Add("Total Score", String.Format("{0:F2}", SubPoints[0] + SubPoints[1]));
            retVal.Add("Selected Rotation", SelectedRotation.Name);
            retVal.Add("Selected DPS", String.Format("{0:F2}", SelectedRotation.RotationData.DPS));
            retVal.Add("Selected Time To OOM", String.Format(SelectedRotation.RotationData.TimeToOOM > new TimeSpan(0, 0, 0) ? "{0} m {1} s" : "Not during fight", SelectedRotation.RotationData.TimeToOOM.Minutes, SelectedRotation.RotationData.TimeToOOM.Seconds));
            retVal.Add("Selected Cycle Length", String.Format("{0:F1} s", SelectedRotation.Duration));

            StringBuilder sb             = new StringBuilder("*");
            float         rotationDamage = SelectedRotation.RotationData.DPS * SelectedRotation.Duration;

            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Starfire", 100 * SelectedRotation.StarfireAvgHit * SelectedRotation.StarfireCount / rotationDamage,
                                        SelectedRotation.StarfireAvgHit * SelectedRotation.StarfireCount,
                                        SelectedRotation.StarfireCount));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Moonfire", 100 * (SelectedRotation.MoonfireAvgHit) * SelectedRotation.MoonfireCasts / rotationDamage,
                                        (SelectedRotation.MoonfireAvgHit) * SelectedRotation.MoonfireCasts,
                                        SelectedRotation.MoonfireCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Insect Swarm", 100 * SelectedRotation.InsectSwarmAvgHit * SelectedRotation.InsectSwarmCasts / rotationDamage,
                                        SelectedRotation.InsectSwarmAvgHit * (SelectedRotation.InsectSwarmCasts),
                                        SelectedRotation.InsectSwarmCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Wrath", 100 * SelectedRotation.WrathAvgHit * SelectedRotation.WrathCount / rotationDamage,
                                        SelectedRotation.WrathAvgHit * SelectedRotation.WrathCount,
                                        SelectedRotation.WrathCount));

            retVal.Add("Selected Spell Breakdown", sb.ToString());
            retVal.Add("Burst Rotation", BurstDPSRotation.Name);
            retVal.Add("Burst DPS", String.Format("{0:F2}", BurstDPSRotation.RotationData.BurstDPS));
            retVal.Add("Burst Time To OOM", String.Format(BurstDPSRotation.RotationData.TimeToOOM > new TimeSpan(0, 0, 0) ? "{0} m {1} s" : "Not during fight", BurstDPSRotation.RotationData.TimeToOOM.Minutes, BurstDPSRotation.RotationData.TimeToOOM.Seconds));
            retVal.Add("Burst Cycle Length", String.Format("{0:F1} s", BurstDPSRotation.Duration));

            sb             = new StringBuilder("*");
            rotationDamage = BurstDPSRotation.RotationData.DPS * BurstDPSRotation.Duration;
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Starfire", 100 * BurstDPSRotation.StarfireAvgHit * BurstDPSRotation.StarfireCount / rotationDamage,
                                        BurstDPSRotation.StarfireAvgHit * BurstDPSRotation.StarfireCount,
                                        BurstDPSRotation.StarfireCount));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Moonfire", 100 * BurstDPSRotation.MoonfireAvgHit * SelectedRotation.MoonfireCasts / rotationDamage,
                                        (BurstDPSRotation.MoonfireAvgHit) * BurstDPSRotation.MoonfireCasts,
                                        BurstDPSRotation.MoonfireCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Insect Swarm", 100 * BurstDPSRotation.InsectSwarmAvgHit * BurstDPSRotation.InsectSwarmCasts / rotationDamage,
                                        BurstDPSRotation.InsectSwarmAvgHit * BurstDPSRotation.InsectSwarmCasts,
                                        BurstDPSRotation.InsectSwarmCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Wrath", 100 * BurstDPSRotation.WrathAvgHit * BurstDPSRotation.WrathCount / rotationDamage,
                                        BurstDPSRotation.WrathAvgHit * BurstDPSRotation.WrathCount,
                                        BurstDPSRotation.WrathCount));

            retVal.Add("Burst Spell Breakdown", sb.ToString());
            retVal.Add("Starfire", String.Format("{0:F2} dps*{1:F2} s avg\n{2:F2} s w/NG\n{3:F2}% max non-Eclipse\n{4:F2}% max Eclipse\n{5:F2} avg hit\n{6:F0} avg mana",
                                                 SelectedRotation.StarfireAvgHit / (SelectedRotation.StarfireAvgCast > 0 ? SelectedRotation.StarfireAvgCast : 1f),
                                                 SelectedRotation.StarfireAvgCast,
                                                 SelectedRotation.StarfireNGCastTime,
                                                 100 * SelectedRotation.StarfireNonEclipseCrit,
                                                 100 * SelectedRotation.StarfireEclipseCrit,
                                                 SelectedRotation.StarfireAvgHit,
                                                 SelectedRotation.StarfireManaCost));
            retVal.Add("Wrath", String.Format("{0:F2} dps*{1:F2} s avg\n{2:F2} s w/NG\n{3:F2} avg hit\n{4:F0} avg mana",
                                              SelectedRotation.WrathAvgHit / (SelectedRotation.WrathAvgCast > 0 ? SelectedRotation.WrathAvgCast : 1f),
                                              SelectedRotation.WrathAvgCast,
                                              SelectedRotation.WrathNGCastTime,
                                              SelectedRotation.WrathAvgHit,
                                              SelectedRotation.WrathManaCost));
            retVal.Add("Moonfire", String.Format("{0:F2} dps*{1:F2} s avg\n{2:F2} avg hit\n{3:F0} avg mana",
                                                 SelectedRotation.MoonfireAvgHit / (SelectedRotation.MoonfireDuration > 0 ? SelectedRotation.MoonfireDuration : 1f),
                                                 SelectedRotation.MoonfireCastTime,
                                                 (SelectedRotation.MoonfireAvgHit),
                                                 SelectedRotation.MoonfireManaCost));
            retVal.Add("Insect Swarm", String.Format("{0:F2} dps*{1:F2} s avg\n{2:F2} avg hit\n{3:F0} avg mana",
                                                     SelectedRotation.InsectSwarmAvgHit / (SelectedRotation.InsectSwarmDuration > 0 ? SelectedRotation.InsectSwarmDuration : 1f),
                                                     SelectedRotation.InsectSwarmCastTime,
                                                     SelectedRotation.InsectSwarmAvgHit,
                                                     SelectedRotation.InsectSwarmManaCost));
            retVal.Add("Starfall", String.Format("{0:F2} dps*{1:F2} avg per cast\n{2:F2} avg per star\n{3:F0} avg mana",
                                                 SelectedRotation.StarfallDamage / 10.0f,
                                                 SelectedRotation.StarfallDamage,
                                                 SelectedRotation.StarfallDamage / (SelectedRotation.StarfallStars > 0 ? SelectedRotation.StarfallStars : 1f),
                                                 StarfallMana));
            retVal.Add("Treants", String.Format("{0:F2} dps*{1:F2} avg per cast\n{2:F2} avg per tree",
                                                TreantDamage / 30.0f, TreantDamage, TreantDamage / 3.0f));

            return(retVal);
        }
Exemplo n.º 15
0
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> retVal = new Dictionary <string, string>();

            //
            if (BasicStats == null)
            {
                BasicStats = new Stats();
            }

            retVal.Add("Health", BasicStats.Health.ToString());
            retVal.Add("Mana", BasicStats.Mana.ToString());
            retVal.Add("Armor", BasicStats.Armor.ToString());
            retVal.Add("Agility", Math.Floor(BasicStats.Agility).ToString());
            retVal.Add("Stamina", Math.Floor(BasicStats.Stamina).ToString());
            retVal.Add("Intellect", Math.Floor(BasicStats.Intellect).ToString());
            retVal.Add("Spirit", Math.Floor(BasicStats.Spirit).ToString());

            retVal.Add("Fight Length", String.Format("{0:F0}s", FightLength));
            retVal.Add("Divisions", Division.Fractions.Length.ToString() + "*" + Division.GetDivisionDetailTooltip(i => String.Format("{0:F0}s ({1:F2}%)", Division.Fractions[i] * FightLength, 100 * Division.Fractions[i])));

            retVal.Add("Innervates", String.Format("{0:F2}", Innervates) + ((Innervates != Math.Floor(Innervates)) ? "*Fractional values denote the reduced value of innervates near the fight beginning or end" : ""));
            retVal.Add("Innervate Effect Delay", String.Format("{0:F2}", InnervateEffectDelay) + ((InnervateEffect != null) ? "*Innervate when this effect is up, and it wouldn't cap mana:\n" + InnervateEffect.ToString() : ""));
            retVal.Add("Mean Mana", String.Format("{0:F0}", MeanMana));
            retVal.Add("Innervate Mana", String.Format("{0:F0}", InnervateMana));
            retVal.Add("Innervate Size", String.Format("{0:F0}", InnervateSize));

            addSpellStatValues(retVal, "Spell Power", "{0:F0}", x => x.SpellPower, String.Format("{0:F0} Base Spell Power", Math.Floor(BaseSpellPower)));
            addSpellStatValues(retVal, "Spell Crit", "{0:F2}%", x => x.SpellCrit * 100, String.Format("{0} Crit Rating from Gear, {1:F}% Crit from Gear, {2:F}% Crit from Gear Intellect",
                                                                                                      BasicStats.CritRating,
                                                                                                      100 * StatConversion.GetSpellCritFromRating(BasicStats.CritRating),
                                                                                                      100 * StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect)));
            addSpellStatValues(retVal, "Spell Haste", "{0:F2}%", x => 100 * (1 / x.Haste.HastedSecond - 1), String.Format("{0} Haste Rating from Gear, {1:F}% Haste from Gear, {2:F}% Haste from Gear and Buffs",
                                                                                                                          (float)BasicStats.HasteRating,
                                                                                                                          100 * StatConversion.GetSpellHasteFromRating((float)BasicStats.HasteRating),
                                                                                                                          100 * ((1 + StatConversion.GetSpellHasteFromRating((float)BasicStats.HasteRating)) * (1.0f + BasicStats.SpellHaste) - 1)
                                                                                                                          ));
            addSpellStatValues(retVal, "Spell Mana Cost Reduction", "{0:F0}", x => x.SpellsManaCostReduction);
            addSpellStatValues(retVal, "Spell Crit Extra Bonus", "{0:F0}%", x => 100 * x.BonusCritHealMultiplier);
            string masteryInfo = String.Format("{0:F0} Mastery Rating from Gear, {1:F2} Mastery from Gear",
                                               BasicStats.MasteryRating, 8 + StatConversion.GetMasteryFromRating(BasicStats.MasteryRating));

            addSpellStatValues(retVal, "Harmony", "{0:F}%", x => 100 * x.Harmony, masteryInfo);

            retVal.Add("Mana Regen", String.Format("{0:F0}", ManaRegen));
            retVal.Add("Base Mana Regen", String.Format("{0:F0}", BaseRegen));
            retVal.Add("Initial Mana Pool Regen", String.Format("{0:F0}", ManaPoolRegen));
            retVal.Add("Spirit Mana Regen", String.Format("{0:F0}", SpiritRegen));
            retVal.Add("Innervate Mana Regen", String.Format("{0:F0}", InnervateRegen));
            retVal.Add("Replenishment Mana Regen", String.Format("{0:F0}", ReplenishmentRegen));
            retVal.Add("Revitalize Mana Regen", String.Format("{0:F0}", RevitalizeRegen));
            retVal.Add("Potion Mana Regen", String.Format("{0:F0}", PotionRegen));

            retVal.Add("Total Score", String.Format("{0:F2}", OverallPoints));
            string[] longNames = { "Raid Sustained", "Raid Burst", "Tank Sustained", "Tank Burst" };
            for (int i = 0; i < longNames.Length; ++i)
            {
                retVal.Add(longNames[i] + " HPS", String.Format("{0:F2}", Solutions[i].Distribution.TotalEPS()));
                retVal.Add(longNames[i] + " Directs/s", String.Format("{0:F2}", Solutions[i].Distribution.TotalCPS()));
                retVal.Add(longNames[i] + " Ticks/s", String.Format("{0:F2}", Solutions[i].Distribution.TotalTPS()));
            }

            retVal.Add("Proc trigger interval", String.Format("{0:F2}", ProcTriggerInterval));
            retVal.Add("Proc periodic trigger interval", String.Format("{0:F2}", ProcPeriodicTriggerInterval));

            ContinuousAction[] actions = ContinuousAction.AverageActionSets(Actions, Division.Fractions);

            for (int i = 0; i < actions.Length; ++i)
            {
                retVal.Add(CalculationsTree.ActionNames[i] + " HPCT", actions[i].EPSText + "*" + Division.GetDivisionDetailTooltip(div => Actions[div][i].EPSText));
                retVal.Add(CalculationsTree.ActionNames[i] + " MPCT", actions[i].MPSText + "*" + Division.GetDivisionDetailTooltip(div => Actions[div][i].MPSText));
                retVal.Add(CalculationsTree.ActionNames[i] + " HPM", actions[i].EPMText + "*" + Division.GetDivisionDetailTooltip(div => Actions[div][i].EPMText));
            }

            string[] names = { "Raid S.", "Raid B.", "Tank S.", "Tank B." };
            for (int i = 0; i < names.Length; ++i)
            {
                Solutions[i].GetProperties(retVal, names[i] + ' ', CalculationsTree.ActionNames, CalculationsTree.PassiveNames);
            }

            addSpellCalculationValues(retVal, "Time", s => s.Action.Time);
            addSpellCalculationValues(retVal, "Duration", s => s.Duration);
            addSpellCalculationValues(retVal, "Mana", s => s.Action.Mana);
            addSpellCalculationValues(retVal, "Direct", s => s.Action.Direct);
            addSpellCalculationValues(retVal, "Tick", s => s.Tick);
            addSpellCalculationValues(retVal, "Ticks", s => s.Ticks);
            addSpellCalculationValues(retVal, "Periodic", s => s.Action.Periodic);
            addSpellCalculationValues(retVal, "Raid Direct", s => s.RaidAction.Direct);
            addSpellCalculationValues(retVal, "Raid Periodic", s => s.RaidAction.Periodic);
            addSpellCalculationValues(retVal, "Tank Direct", s => s.TankAction.Direct);
            addSpellCalculationValues(retVal, "Tank Periodic", s => s.TankAction.Periodic);
            return(retVal);
        }
Exemplo n.º 16
0
        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);
                };
            }
        }
        /// <summary>
        /// Builds a dictionary containing the values to display for each of the calculations defined in
        /// CharacterDisplayCalculationLabels. The key should be the Label of each display calculation,
        /// and the value should be the value to display, optionally appended with '*' followed by any
        /// string you'd like displayed as a tooltip on the value.
        /// </summary>
        /// <returns>A Dictionary<string, string> containing the values to display for each of the
        /// calculations defined in CharacterDisplayCalculationLabels.</returns>
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> dictValues = new Dictionary <string, string>();

            #region Simulation stats
            dictValues.Add("Rotation", String.Format("{0}*{1}", Name, Abilities));
            dictValues.Add("Warlock DPS", String.Format("{0:0}", DpsPoints));
            dictValues.Add("Pet DPS", String.Format("{0:0}", PetDPSPoints));
            dictValues.Add("Total DPS", String.Format("{0:0}", OverallPoints));
            dictValues.Add("Damage Done", String.Format("{0:0}", OverallDamage));
            dictValues.Add("Mana Used", String.Format("{0:0}", TotalManaCost));
            #endregion

            #region HP/Mana stats
            dictValues.Add("Health", String.Format("{0:0}", TotalStats.Health));
            dictValues.Add("Mana", String.Format("{0:0}", TotalStats.Mana));
            #endregion

            #region Base stats
            dictValues.Add("Strength", String.Format("{0}", TotalStats.Strength));
            dictValues.Add("Agility", String.Format("{0}", TotalStats.Agility));
            dictValues.Add("Stamina", String.Format("{0}", TotalStats.Stamina));
            dictValues.Add("Intellect", String.Format("{0}", TotalStats.Intellect));
            dictValues.Add("Spirit", String.Format("{0}", TotalStats.Spirit));
            dictValues.Add("Armor", String.Format("{0}", TotalStats.Armor));
            #endregion

            #region Pet stats
            Minion minion = MinionFactory.CreateMinion(Options.Pet, Character, TotalStats);
            if (minion != null)
            {
                dictValues.Add("Pet Strength", String.Format("{0}", minion.Strength()));
                dictValues.Add("Pet Agility", String.Format("{0}", minion.Agility()));
                dictValues.Add("Pet Stamina", String.Format("{0:0}", minion.Stamina()));
                dictValues.Add("Pet Intellect", String.Format("{0:0}", minion.Intellect()));
                dictValues.Add("Pet Spirit", String.Format("{0}", minion.Spirit()));
                dictValues.Add("Pet Armor", String.Format("{0}", minion.Armor()));
            }
            #endregion

            #region Spell stats
            //pet scaling consts: http://www.wowwiki.com/Warlock_minions
            const float petInheritedAttackPowerPercentage = 0.57f;
            const float petInheritedSpellPowerPercentage  = 0.15f;

            dictValues.Add("Bonus Damage", String.Format("{0}*Shadow Damage\t{1}\r\nFire Damage\t{2}\r\n\r\nYour Fire Damage increases your pet's Attack Power by {3} and Spell Damage by {4}.",
                                                         TotalStats.SpellPower,
                                                         TotalStats.SpellPower + TotalStats.SpellShadowDamageRating,
                                                         TotalStats.SpellPower + TotalStats.SpellFireDamageRating,
                                                         Math.Round((TotalStats.SpellPower + TotalStats.SpellFireDamageRating) * petInheritedAttackPowerPercentage, 0),
                                                         Math.Round((TotalStats.SpellPower + TotalStats.SpellFireDamageRating) * petInheritedSpellPowerPercentage, 0)
                                                         ));

            #region Hit / Miss chance
            //float bonusHit = TotalStats.SpellHit;
            float onePercentOfHitRating = (1 / StatConversion.GetSpellHitFromRating(1));
            float hitFromRating         = StatConversion.GetSpellHitFromRating(TotalStats.HitRating);
            float hitFromTalents        = (Character.WarlockTalents.Suppression * 0.01f);
            float hitFromBuffs          = (TotalStats.SpellHit - hitFromRating - hitFromTalents);
            float targetHit             = (Options.TargetHit / 100f);
            float totalHit   = (targetHit + TotalStats.SpellHit);
            float missChance = (totalHit > 1 ? 0 : (1 - totalHit));

            //calculate the amount of hit rating that is over or under the cap
            float hitDelta = (totalHit > 1)
                                 ? (float)Math.Floor((totalHit - 1) * onePercentOfHitRating)
                                 : (float)Math.Ceiling((1 - totalHit) * onePercentOfHitRating);
            //now we can calculate the hitcap value
            float hitCap = (totalHit > 1)
                               ? TotalStats.HitRating - hitDelta
                               : TotalStats.HitRating + hitDelta;

            dictValues.Add("Hit Rating", String.Format("{0}*{1:0.00%} Hit Chance (max 100%) | {2:0.00%} Miss Chance \r\n\r\n"
                                                       + "{3:0.00%}\t Base Hit Chance on a Level {4:0} target\r\n"
                                                       + "{5:0.00%}\t from {6:0} Hit Rating [gear, food and/or flasks]\r\n"
                                                       + "{7:0.00%}\t from Talent: Suppression\r\n"
                                                       + "{8:0.00%}\t from Buffs: Racial and/or Spell Hit Chance Taken\r\n\r\n"
                                                       + "{9}\r\n\r\n"
                                                       + "Hit Rating caps:\r\n"
                                                       + "446 - hard cap (no hit from talents, gear or buffs)\r\n"
                                                       + "420 - Heroic Presence\r\n"
                                                       + "368 - Suppression\r\n"
                                                       + "342 - Suppression and Heroic Presence\r\n"
                                                       + "289 - Suppression, Improved Faerie Fire / Misery\r\n"
                                                       + "263 - Suppression, Improved Faerie Fire / Misery and Heroic Presence",
                                                       TotalStats.HitRating, totalHit, missChance,
                                                       targetHit, Options.TargetLevel,
                                                       hitFromRating, TotalStats.HitRating,
                                                       hitFromTalents,
                                                       hitFromBuffs,
                                                       String.Format("You are {0} hit rating {1} the {2} cap.", hitDelta, ((totalHit > 1) ? "above" : "below"), hitCap)
                                                       ));

            dictValues.Add("Miss chance", String.Format("{0:0.00%}", missChance));
            #endregion

            #region Crit %
            Stats statsBase         = BaseStats.GetBaseStats(Character);
            float critFromRating    = StatConversion.GetSpellCritFromRating(TotalStats.CritRating);
            float critFromIntellect = StatConversion.GetSpellCritFromIntellect(TotalStats.Intellect);
            float critFromBuffs     = TotalStats.SpellCrit - statsBase.SpellCrit - critFromRating - critFromIntellect
                                      - (Character.WarlockTalents.DemonicTactics * 0.02f)
                                      - (Character.WarlockTalents.Backlash * 0.01f);

            dictValues.Add("Crit Chance", String.Format("{0:0.00%}*"
                                                        + "{1:0.00%}\tfrom {2:0} Spell Crit rating\r\n"
                                                        + "{3:0.00%}\tfrom {4:0} Intellect\r\n"
                                                        + "{5:0.000%}\tfrom Warlock Class Bonus\r\n"
                                                        + "{6:0%}\tfrom Talent: Demonic Tactics\r\n"
                                                        + "{7:0%}\tfrom Talent: Backlash\r\n"
                                                        + "{8:0%}\tfrom Buffs",
                                                        TotalStats.SpellCrit,
                                                        critFromRating, TotalStats.CritRating,
                                                        critFromIntellect, TotalStats.Intellect,
                                                        statsBase.SpellCrit,
                                                        (Character.WarlockTalents.DemonicTactics * 0.02f),
                                                        (Character.WarlockTalents.Backlash * 0.01f),
                                                        critFromBuffs
                                                        ));
            #endregion

            dictValues.Add("Haste Rating", String.Format("{0}%*{1}%\tfrom {2} Haste rating\r\n{3}%\tfrom Buffs\r\n{4}s\tGlobal Cooldown",
                                                         (TotalStats.SpellHaste * 100f).ToString("0.00"),
                                                         (StatConversion.GetSpellHasteFromRating(TotalStats.HasteRating) * 100f).ToString("0.00"),
                                                         TotalStats.HasteRating,
                                                         (TotalStats.SpellHaste * 100f - StatConversion.GetSpellHasteFromRating(TotalStats.HasteRating) * 100f).ToString("0.00"),
                                                         Math.Max(1.0f, 1.5f / (1 + TotalStats.SpellHaste)).ToString("0.00")));

            dictValues.Add("Mana Regen", String.Format("{0}*{0} mana regenerated every 5 seconds while not casting\r\n{1} mana regenerated every 5 seconds while casting",
                                                       GetManaRegenOutOfCombat(),
                                                       GetManaRegenInCombat()));
            #endregion

            #region Shadow school
            dictValues.Add("Shadow Bolt", new ShadowBolt(TotalStats, Character, Options).ToString());
            if (Character.WarlockTalents.Haunt > 0)
            {
                dictValues.Add("Haunt", new Haunt(TotalStats, Character, Options).ToString());
            }
            else
            {
                dictValues.Add("Haunt", "- *Required talent not available");
            }
            dictValues.Add("Corruption", new Corruption(TotalStats, Character, Options).ToString());
            dictValues.Add("Curse of Agony", new CurseOfAgony(TotalStats, Character, Options).ToString());
            dictValues.Add("Curse of Doom", new CurseOfDoom(TotalStats, Character, Options).ToString());
            if (Character.WarlockTalents.UnstableAffliction > 0)
            {
                dictValues.Add("Unstable Affliction", new UnstableAffliction(TotalStats, Character, Options).ToString());
            }
            else
            {
                dictValues.Add("Unstable Affliction", "- *Required talent not available");
            }
            dictValues.Add("Death Coil", new DeathCoil(TotalStats, Character, Options).ToString());
            dictValues.Add("Drain Life", new DrainLife(TotalStats, Character, Options).ToString());
            dictValues.Add("Drain Soul", new DrainSoul(TotalStats, Character, Options).ToString());
            dictValues.Add("Seed of Corruption", new SeedOfCorruption(TotalStats, Character, Options).ToString());
            dictValues.Add("Shadowflame", new Shadowflame(TotalStats, Character, Options).ToString());
            if (Character.WarlockTalents.Shadowburn > 0)
            {
                dictValues.Add("Shadowburn", new Shadowburn(TotalStats, Character, Options).ToString());
            }
            else
            {
                dictValues.Add("Shadowburn", "- *Required talent not available");
            }
            if (Character.WarlockTalents.Shadowfury > 0)
            {
                dictValues.Add("Shadowfury", new Shadowfury(TotalStats, Character, Options).ToString());
            }
            else
            {
                dictValues.Add("Shadowfury", "- *Required talent not available");
            }
            dictValues.Add("Life Tap", new LifeTap(TotalStats, Character, Options).ToString());
            if (Character.WarlockTalents.DarkPact > 0)
            {
                dictValues.Add("Dark Pact", new DarkPact(TotalStats, Character, Options).ToString());
            }
            else
            {
                dictValues.Add("Dark Pact", "- *Required talent not available");
            }

            #endregion

            #region Fire school
            dictValues.Add("Incinerate", new Incinerate(TotalStats, Character, Options).ToString());
            dictValues.Add("Immolate", new Immolate(TotalStats, Character, Options).ToString());
            if (Character.WarlockTalents.Conflagrate > 0)
            {
                dictValues.Add("Conflagrate", new Conflagrate(TotalStats, Character, Options).ToString());
            }
            else
            {
                dictValues.Add("Conflagrate", "- *Required talent not available");
            }
            if (Character.WarlockTalents.ChaosBolt > 0)
            {
                dictValues.Add("Chaos Bolt", new ChaosBolt(TotalStats, Character, Options).ToString());
            }
            else
            {
                dictValues.Add("Chaos Bolt", "- *Required talent not available");
            }
            dictValues.Add("Rain of Fire", new RainOfFire(TotalStats, Character, Options).ToString());
            dictValues.Add("Hellfire", new Hellfire(TotalStats, Character, Options).ToString());

            if (Character.WarlockTalents.Metamorphosis > 0)
            {
                dictValues.Add("Immolation Aura", new ImmolationAura(TotalStats, Character, Options).ToString());
            }
            else
            {
                dictValues.Add("Immolation Aura", "- *Required talent not available");
            }

            dictValues.Add("Searing Pain", new SearingPain(TotalStats, Character, Options).ToString());
            dictValues.Add("Soul Fire", new SoulFire(TotalStats, Character, Options).ToString());
            #endregion

            return(dictValues);
        }
Exemplo n.º 18
0
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> retVal = new Dictionary <string, string>();

            //
            if (baseStats == null)
            {
                baseStats = new StatsMoonkin();
            }
            if (SelectedRotation == null)
            {
                SelectedRotation = new RotationData();
            }
            if (BurstRotation == null)
            {
                BurstRotation = new RotationData();
            }
            //
            retVal.Add("Health", baseStats.Health.ToString());
            retVal.Add("Mana", baseStats.Mana.ToString());
            retVal.Add("Armor", baseStats.Armor.ToString());
            retVal.Add("Agility", baseStats.Agility.ToString());
            retVal.Add("Stamina", baseStats.Stamina.ToString());
            retVal.Add("Intellect", baseStats.Intellect.ToString());
            retVal.Add("Spirit", baseStats.Spirit.ToString());
            retVal.Add("Spell Power", SpellPower.ToString());

            float totalHitDelta = SpellHitCap - SpellHit;

            retVal.Add("Spell Hit", String.Format("{0:F}%*{1} Hit Rating, {2:F}% Hit From Gear" + (totalHitDelta != 0 ? ", {3} Rating {4}" : ""),
                                                  100 * SpellHit,
                                                  baseStats.HitRating,
                                                  100 * StatConversion.GetSpellHitFromRating(baseStats.HitRating),
                                                  StatConversion.GetRatingFromHit(Math.Abs(totalHitDelta)),
                                                  totalHitDelta > 0 ? "To Cap" : "Over Cap"));
            retVal.Add("Spell Crit", String.Format("{0:F}%*{1} Crit Rating, {2:F}% Crit From Gear, {3:F}% Crit From Intellect",
                                                   100 * SpellCrit,
                                                   baseStats.CritRating,
                                                   100 * StatConversion.GetSpellCritFromRating(baseStats.CritRating),
                                                   100 * StatConversion.GetSpellCritFromIntellect(baseStats.Intellect)));
            retVal.Add("Spell Haste", String.Format("{0:F}%*{1} Haste Rating, {2:F}% Haste From Gear",
                                                    100 * SpellHaste,
                                                    baseStats.HasteRating,
                                                    100 * StatConversion.GetSpellHasteFromRating(baseStats.HasteRating)));
            retVal.Add("Mastery", String.Format("{0:F}*{1:F} Eclipse %, {2} Rating",
                                                Mastery,
                                                Mastery * 2.0f,
                                                baseStats.MasteryRating));
            retVal.Add("Mana Regen", String.Format("{0:F0}", ManaRegen * 5.0f));
            retVal.Add("Total Score", String.Format("{0:F2}", OverallPoints));
            retVal.Add("Selected Rotation", String.Format("*{0}", SelectedRotation.Name));
            retVal.Add("Selected DPS", String.Format("{0:F2}", SelectedRotation.SustainedDPS));
            retVal.Add("Selected Time To OOM", String.Format(SelectedRotation.TimeToOOM > new TimeSpan(0, 0, 0) ? "{0} m {1} s" : "Not during fight", SelectedRotation.TimeToOOM.Minutes, SelectedRotation.TimeToOOM.Seconds));
            retVal.Add("Selected Cycle Length", String.Format("{0:F1} s", SelectedRotation.Duration));

            StringBuilder sb             = new StringBuilder("*");
            float         rotationDamage = SelectedRotation.SustainedDPS * SelectedRotation.Duration;

            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Starfire", 100 * SelectedRotation.StarfireAvgHit * SelectedRotation.StarfireCount / rotationDamage,
                                        SelectedRotation.StarfireAvgHit * SelectedRotation.StarfireCount,
                                        SelectedRotation.StarfireCount));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Moonfire", 100 * (SelectedRotation.MoonfireAvgHit) * SelectedRotation.MoonfireCasts / rotationDamage,
                                        (SelectedRotation.MoonfireAvgHit) * SelectedRotation.MoonfireCasts,
                                        SelectedRotation.MoonfireCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Insect Swarm", 100 * SelectedRotation.InsectSwarmAvgHit * SelectedRotation.InsectSwarmCasts / rotationDamage,
                                        SelectedRotation.InsectSwarmAvgHit * (SelectedRotation.InsectSwarmCasts),
                                        SelectedRotation.InsectSwarmCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Wrath", 100 * SelectedRotation.WrathAvgHit * SelectedRotation.WrathCount / rotationDamage,
                                        SelectedRotation.WrathAvgHit * SelectedRotation.WrathCount,
                                        SelectedRotation.WrathCount));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Starsurge", 100 * SelectedRotation.StarSurgeAvgHit * SelectedRotation.StarSurgeCount / rotationDamage,
                                        SelectedRotation.StarSurgeAvgHit * SelectedRotation.StarSurgeCount,
                                        SelectedRotation.StarSurgeCount));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Starfall", 100 * SelectedRotation.StarfallDamage * SelectedRotation.StarfallCasts / rotationDamage,
                                        SelectedRotation.StarfallDamage * SelectedRotation.StarfallCasts,
                                        SelectedRotation.StarfallCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Wild Mushroom", 100 * SelectedRotation.MushroomDamage * SelectedRotation.MushroomCasts / rotationDamage,
                                        SelectedRotation.MushroomDamage * SelectedRotation.MushroomCasts,
                                        SelectedRotation.MushroomCasts));

            retVal.Add("Selected Spell Breakdown", sb.ToString());

            retVal.Add("Burst Rotation", String.Format("*{0}", BurstRotation.Name));
            retVal.Add("Burst DPS", String.Format("{0:F2}", BurstRotation.BurstDPS));
            retVal.Add("Burst Time To OOM", String.Format(BurstRotation.TimeToOOM > new TimeSpan(0, 0, 0) ? "{0} m {1} s" : "Not during fight", BurstRotation.TimeToOOM.Minutes, BurstRotation.TimeToOOM.Seconds));
            retVal.Add("Burst Cycle Length", String.Format("{0:F1} s", BurstRotation.Duration));

            sb             = new StringBuilder("*");
            rotationDamage = BurstRotation.BurstDPS * BurstRotation.Duration;
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Starfire", 100 * BurstRotation.StarfireAvgHit * BurstRotation.StarfireCount / rotationDamage,
                                        BurstRotation.StarfireAvgHit * BurstRotation.StarfireCount,
                                        BurstRotation.StarfireCount));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Moonfire", 100 * (BurstRotation.MoonfireAvgHit) * BurstRotation.MoonfireCasts / rotationDamage,
                                        (BurstRotation.MoonfireAvgHit) * BurstRotation.MoonfireCasts,
                                        BurstRotation.MoonfireCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Insect Swarm", 100 * BurstRotation.InsectSwarmAvgHit * BurstRotation.InsectSwarmCasts / rotationDamage,
                                        BurstRotation.InsectSwarmAvgHit * (BurstRotation.InsectSwarmCasts),
                                        BurstRotation.InsectSwarmCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Wrath", 100 * BurstRotation.WrathAvgHit * BurstRotation.WrathCount / rotationDamage,
                                        BurstRotation.WrathAvgHit * BurstRotation.WrathCount,
                                        BurstRotation.WrathCount));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Starsurge", 100 * BurstRotation.StarSurgeAvgHit * BurstRotation.StarSurgeCount / rotationDamage,
                                        BurstRotation.StarSurgeAvgHit * BurstRotation.StarSurgeCount,
                                        BurstRotation.StarSurgeCount));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Starfall", 100 * BurstRotation.StarfallDamage * BurstRotation.StarfallCasts / rotationDamage,
                                        BurstRotation.StarfallDamage * BurstRotation.StarfallCasts,
                                        BurstRotation.StarfallCasts));
            sb.AppendLine(String.Format("{0}: {1:F2}%, {2:F2} damage, {3:F0} count", "Wild Mushroom", 100 * BurstRotation.MushroomDamage * BurstRotation.MushroomCasts / rotationDamage,
                                        BurstRotation.MushroomDamage * BurstRotation.MushroomCasts,
                                        BurstRotation.MushroomCasts));

            retVal.Add("Burst Spell Breakdown", sb.ToString());

            retVal.Add("Nature's Grace Uptime", String.Format("{0:F2}%", SelectedRotation.NaturesGraceUptime * 100));
            retVal.Add("Solar Eclipse Uptime", String.Format("{0:F2}%", SelectedRotation.SolarUptime * 100));
            retVal.Add("Lunar Eclipse Uptime", String.Format("{0:F2}%", SelectedRotation.LunarUptime * 100));

            retVal.Add("Starfire", String.Format("{0:F2} dps*{1:F2} s avg\n {2:F2} avg hit\n{3:F0} avg energy",
                                                 SelectedRotation.StarfireAvgHit / (SelectedRotation.StarfireAvgCast > 0 ? SelectedRotation.StarfireAvgCast : 1f),
                                                 SelectedRotation.StarfireAvgCast,
                                                 SelectedRotation.StarfireAvgHit,
                                                 SelectedRotation.StarfireAvgEnergy));
            retVal.Add("Wrath", String.Format("{0:F2} dps*{1:F2} s avg\n {2:F2} avg hit\n{3:F0} avg energy",
                                              SelectedRotation.WrathAvgHit / (SelectedRotation.WrathAvgCast > 0 ? SelectedRotation.WrathAvgCast : 1f),
                                              SelectedRotation.WrathAvgCast,
                                              SelectedRotation.WrathAvgHit,
                                              SelectedRotation.WrathAvgEnergy));
            retVal.Add("Starsurge", String.Format("{0:F2} dps*{1:F2} s avg\n {2:F2} avg hit\n{3:F0} avg energy",
                                                  SelectedRotation.StarSurgeAvgHit / (SelectedRotation.StarSurgeAvgCast > 0 ? SelectedRotation.StarSurgeAvgCast : 1f),
                                                  SelectedRotation.StarSurgeAvgCast,
                                                  SelectedRotation.StarSurgeAvgHit,
                                                  SelectedRotation.StarSurgeAvgEnergy));
            retVal.Add("Moonfire", String.Format("{0:F2} dps*{1:F2} s avg\n{2:F2} avg hit",
                                                 SelectedRotation.MoonfireAvgHit / (SelectedRotation.MoonfireDuration > 0 ? SelectedRotation.MoonfireDuration : 1f),
                                                 SelectedRotation.MoonfireAvgCast,
                                                 SelectedRotation.MoonfireAvgHit));
            retVal.Add("Insect Swarm", String.Format("{0:F2} dps*{1:F2} s avg\n{2:F2} avg hit",
                                                     SelectedRotation.InsectSwarmAvgHit / (SelectedRotation.InsectSwarmDuration > 0 ? SelectedRotation.InsectSwarmDuration : 1f),
                                                     SelectedRotation.InsectSwarmAvgCast,
                                                     SelectedRotation.InsectSwarmAvgHit));
            retVal.Add("Starfall", String.Format("{0:F2} dps*{1:F2} avg per cast\n{2:F2} avg per star",
                                                 SelectedRotation.StarfallDamage / 10.0f,
                                                 SelectedRotation.StarfallDamage,
                                                 SelectedRotation.StarfallDamage / (SelectedRotation.StarfallStars > 0 ? SelectedRotation.StarfallStars : 1f)));
            retVal.Add("Treants", String.Format("{0:F2} dps*{1:F2} avg per cast\n{2:F2} avg per tree",
                                                SelectedRotation.TreantDamage / 30.0f, SelectedRotation.TreantDamage, SelectedRotation.TreantDamage / 3.0f));
            retVal.Add("Wild Mushroom", String.Format("{0:F2} dps*{1:F2} avg per cast\n{2:F2} avg per mushroom",
                                                      SelectedRotation.MushroomDamage / 10.0f,
                                                      SelectedRotation.MushroomDamage,
                                                      SelectedRotation.MushroomDamage / 3f));

            return(retVal);
        }
Exemplo n.º 19
0
        public void UpdateCalcs(bool firstPass)
        {
            // talents
            callOfThunder       = .05f * _talents.CallOfThunder;
            critMultiplierMelee = 2f * (1 + _stats.BonusCritMultiplier);
            critMultiplierSpell = (1.5f + .1f * _character.ShamanTalents.ElementalFury) * (1 + _stats.BonusSpellCritMultiplier);

            // Melee
            float hitBonus = _stats.PhysicalHit + StatConversion.GetHitFromRating(_stats.HitRating) + 0.02f * _talents.DualWieldSpecialization;

            expertiseBonusMH = GetDPRfromExp(_stats.Expertise + StatConversion.GetExpertiseFromRating(_stats.ExpertiseRating));
            expertiseBonusOH = GetDPRfromExp(_stats.Expertise + StatConversion.GetExpertiseFromRating(_stats.ExpertiseRating));

            // Need to modify expertiseBonusMH & OH if Orc and have racial bonus weapons
            if (_character.Race == CharacterRace.Orc)
            {
                ItemType mhType = _character.MainHand == null ? ItemType.None : _character.MainHand.Type;
                ItemType ohType = _character.OffHand == null ? ItemType.None : _character.OffHand.Type;
                if (mhType == ItemType.OneHandAxe || mhType == ItemType.FistWeapon) // patch 3.2 includes fists
                {
                    expertiseBonusMH += 0.0125f;
                }
                if (ohType == ItemType.OneHandAxe || ohType == ItemType.FistWeapon) // patch 3.2 includes fists
                {
                    expertiseBonusOH += 0.0125f;
                }
            }

            float meleeCritModifier = _stats.PhysicalCrit;
            float baseMeleeCrit     = StatConversion.GetCritFromRating(_stats.CritRating) +
                                      StatConversion.GetCritFromAgility(_stats.Agility, _character.Class) + .01f * _talents.ThunderingStrikes;

            chanceDodgeMH = Math.Max(0f, DodgeChanceCap - expertiseBonusMH);
            chanceDodgeOH = Math.Max(0f, DodgeChanceCap - expertiseBonusOH);
            float ParryChance = ParryChanceCap - expertiseBonusMH;

            chanceParryMH      = (float)Math.Max(0f, _calcOpts.InBack ? ParryChance * (1f - _calcOpts.InBackPerc / 100f) : ParryChance);
            ParryChance        = ParryChanceCap - expertiseBonusOH;
            chanceParryOH      = (float)Math.Max(0f, _calcOpts.InBack ? ParryChance * (1f - _calcOpts.InBackPerc / 100f) : ParryChance);
            chanceWhiteMissMH  = Math.Max(0f, WhiteHitCap - hitBonus) + chanceDodgeMH + chanceParryMH;
            chanceWhiteMissOH  = Math.Max(0f, WhiteHitCap - hitBonus) + chanceDodgeOH + chanceParryOH;
            chanceYellowMissMH = Math.Max(0f, YellowHitCap - hitBonus) + chanceDodgeMH + chanceParryMH; // base miss 8% now
            chanceYellowMissOH = Math.Max(0f, YellowHitCap - hitBonus) + chanceDodgeOH + chanceParryOH; // base miss 8% now

            // SetCritValues((1 + _stats.BonusCritChance) * (baseMeleeCrit + meleeCritModifier) + .00005f); //fudge factor for rounding
            SetCritValues(baseMeleeCrit + meleeCritModifier + .00005f); //fudge factor for rounding
            // set two export values so that ED crit isn't included
            exportMeleeCritMH = chanceWhiteCritMH + whiteCritDepression;
            exportMeleeCritOH = chanceWhiteCritOH + whiteCritDepression;

            // Spells
            ftBonusCrit = 0f;
            if (_calcOpts.MainhandImbue == "Flametongue")
            {
                ftBonusCrit += _talents.GlyphofFlametongueWeapon ? .02f : 0f;
            }
            if (_calcOpts.OffhandImbue == "Flametongue" && _talents.DualWield == 1)
            {
                ftBonusCrit += _talents.GlyphofFlametongueWeapon ? .02f : 0f;
            }

            float spellCritModifier = _stats.SpellCrit + _stats.SpellCritOnTarget + ftBonusCrit;
            float hitBonusSpell     = _stats.SpellHit + StatConversion.GetSpellHitFromRating(_stats.HitRating);

            chanceSpellMiss = Math.Max(0f, SpellMissRate - hitBonusSpell);
            overSpellHitCap = Math.Max(0f, hitBonusSpell - SpellMissRate);
            float baseSpellCrit = StatConversion.GetSpellCritFromRating(_stats.CritRating) +
                                  StatConversion.GetSpellCritFromIntellect(_stats.Intellect) + .01f * _talents.ThunderingStrikes;

            //chanceSpellCrit = Math.Min(0.75f, (1 + _stats.BonusCritChance) * (baseSpellCrit + spellCritModifier) + .00005f); //fudge factor for rounding
            chanceSpellCrit = Math.Min(0.75f, baseSpellCrit + spellCritModifier + .00005f); //fudge factor for rounding

            float hasteBonus = StatConversion.GetHasteFromRating(_stats.HasteRating, _character.Class);

            unhastedMHSpeed = _character.MainHand == null ? 3.0f : _character.MainHand.Item.Speed;
            unhastedOHSpeed = _character.OffHand == null ? 3.0f : _character.OffHand.Item.Speed;
            float baseHastedMHSpeed    = unhastedMHSpeed / (1f + hasteBonus) / (1f + _stats.PhysicalHaste);
            float baseHastedOHSpeed    = unhastedOHSpeed / (1f + hasteBonus) / (1f + _stats.PhysicalHaste);
            float chanceToProcWFPerHit = .2f + (_character.ShamanTalents.GlyphofWindfuryWeapon ? .02f : 0f);

            //The Swing Loop
            //This is where we figure out feedback systems -- WF, MW, ED, Flurry, etc.
            //--------------
            flurryUptime = 1f;
            edUptime     = 0f;
            float stormstrikeSpeed = firstPass ? (_talents.Stormstrike == 1 ? 8f : 0f) : AbilityCooldown(EnhanceAbility.StormStrike);
            float shockSpeed       = firstPass ? BaseShockSpeed : AbilityCooldown(EnhanceAbility.EarthShock);
            float lavaLashSpeed    = firstPass ? (_talents.LavaLash == 1 ? 6f : 0f) : AbilityCooldown(EnhanceAbility.LavaLash);
            float fireNovaSpeed    = firstPass ? BaseFireNovaSpeed : AbilityCooldown(EnhanceAbility.FireNova);

            if (_calcOpts.PriorityInUse(EnhanceAbility.MagmaTotem))
            {
                fireTotemUptime = firstPass ? 1.0f : 20f / AbilityCooldown(EnhanceAbility.MagmaTotem);
            }
            else if (_calcOpts.PriorityInUse(EnhanceAbility.SearingTotem))
            {
                fireTotemUptime = firstPass ? 1.0f : 60f / AbilityCooldown(EnhanceAbility.SearingTotem);
            }
            else if (_calcOpts.PriorityInUse(EnhanceAbility.RefreshTotems)) // if no Searing or Magma totem use refresh of Flametongue totem.
            {
                fireTotemUptime = firstPass ? 1.0f : 300f / AbilityCooldown(EnhanceAbility.RefreshTotems);
            }

            float mwPPM             = 2 * _talents.MaelstromWeapon * (1 + _stats.Enhance4T8 * 0.2f);
            float flurryHasteBonus  = .06f * _talents.Flurry + _stats.Enhance4T7;
            float edCritBonus       = .03f * _talents.ElementalDevastation;
            float staticShockChance = (.02f * _character.ShamanTalents.StaticShock + (_stats.Enhance2T9 == 1f ? 0.03f : 0f));

            hitsPerSMHSS = 0f;
            hitsPerSOHSS = 0f;
            hitsPerSOH   = 0f;
            hitsPerSMH   = 0f;
            hitsPerSWF   = 0f;
            if (_talents.Stormstrike == 1)
            {
                hitsPerSMHSS = (1f - chanceYellowMissMH) / stormstrikeSpeed;
                hitsPerSOHSS = _character.ShamanTalents.DualWield == 1 ? ((1f - 2 * chanceYellowMissOH) / stormstrikeSpeed) : 0f; //OH only swings if MH connects
            }
            hitsPerSLL = lavaLashSpeed == 0 ? 0f : (1f - chanceYellowMissOH) / lavaLashSpeed;
            float swingsPerSMHMelee = 0f;
            float swingsPerSOHMelee = 0f;
            float wfProcsPerSecond  = 0f;
            float mwProcsPerSecond  = 0f;

            secondsToFiveStack = 10f;
            float averageMeleeCritChance   = (chanceWhiteCritMH + chanceWhiteCritOH + chanceYellowCritMH + chanceYellowCritOH) / 4f;
            float averageMeleeHitChance    = ((1f - chanceWhiteMissMH - chanceDodgeMH - chanceParryMH) + (1f - chanceWhiteMissOH - chanceDodgeOH - chanceParryOH)) / 2f;
            float averageMeleeMissChance   = (chanceWhiteMissMH + chanceWhiteMissOH) / 2f;
            float whiteHitsPerSMH          = 0f;
            float whiteHitsPerSOH          = 0f;
            float moteHitsPerS             = 0f;
            float yellowHitsPerSMH         = 0f;
            float yellowHitsPerSOH         = 0f;
            float flameTongueHitsPerSecond = 0f;

            for (int i = 0; i < 5; i++)
            {
                // float bonusHaste = (1f + (flurryUptime * flurryHasteBonus));
                float bonusHaste = 1 / (1 - flurryUptime + flurryUptime / (1 + flurryHasteBonus)); // use time based not proc based flurryUptime
                hastedMHSpeed     = baseHastedMHSpeed / bonusHaste;
                hastedOHSpeed     = baseHastedOHSpeed / bonusHaste;
                swingsPerSMHMelee = 1f / hastedMHSpeed;
                swingsPerSOHMelee = (hastedOHSpeed == 0f) ? 0f : 1f / hastedOHSpeed;
                whiteHitsPerSMH   = ChanceWhiteHitMH * swingsPerSMHMelee;
                whiteHitsPerSOH   = ChanceWhiteHitOH * swingsPerSOHMelee;
                moteHitsPerS      = _stats.MoteOfAnger * 2 * AverageWhiteHitChance;
                // Windfury model
                if (_calcOpts.MainhandImbue == "Windfury")
                {
                    float hitsThatProcWFPerS = whiteHitsPerSMH + hitsPerSMHSS;
                    if (_character.ShamanTalents.DualWield == 1)
                    {
                        hitsThatProcWFPerS += moteHitsPerS / 2; // half the hits will be OH and thus won't proc WF
                    }
                    else
                    {
                        hitsThatProcWFPerS += moteHitsPerS; // if no offhand then all motes will be MH weapon by definition
                    }
                    float maxExpectedWFPerFight = hitsThatProcWFPerS * chanceToProcWFPerHit * fightLength;
                    float ineligibleSeconds     = maxExpectedWFPerFight * (3.25f - hastedMHSpeed);
                    float expectedWFPerFight    = hitsThatProcWFPerS * chanceToProcWFPerHit * (fightLength - ineligibleSeconds);
                    wfProcsPerSecond = expectedWFPerFight / fightLength;
                    hitsPerSWF       = 2f * wfProcsPerSecond * (1f - chanceYellowMissMH);
                }
                yellowHitsPerSMH = hitsPerSWF + hitsPerSMHSS;
                yellowHitsPerSOH = hitsPerSOHSS + hitsPerSLL;

                //Due to attack table, a white swing has the same chance to crit as a yellow hit
// Old Flurry calc changed 10 Nov 2009
//                couldCritSwingsPerSecond = whiteHitsPerSMH + whiteHitsPerSOH + yellowHitsPerSMH + yellowHitsPerSOH;
//                float swingsThatConsumeFlurryPerSecond = swingsPerSMHMelee + swingsPerSOHMelee;
//                flurryUptime = 1f - (float)Math.Pow(1 - averageMeleeCritChance, (3 / swingsThatConsumeFlurryPerSecond) * couldCritSwingsPerSecond);  // old formulae
                flurryUptime = CalculateFlurryUptime(averageMeleeCritChance, averageMeleeHitChance, averageMeleeMissChance);

                // Maelstrom Weapon time to 5 stacks calc
                if (_character.ShamanTalents.DualWield == 1 && unhastedOHSpeed != 0f)
                {
                    hitsPerSMH       = whiteHitsPerSMH + yellowHitsPerSMH + moteHitsPerS / 2;
                    hitsPerSOH       = whiteHitsPerSOH + yellowHitsPerSOH + moteHitsPerS / 2;
                    mwProcsPerSecond = (mwPPM / (60f / unhastedMHSpeed)) * hitsPerSMH + (mwPPM / (60f / unhastedOHSpeed)) * hitsPerSOH;
                }
                else
                {
                    hitsPerSMH       = whiteHitsPerSMH + yellowHitsPerSMH + moteHitsPerS;
                    hitsPerSOH       = 0f;
                    mwProcsPerSecond = (mwPPM / (60f / unhastedMHSpeed)) * hitsPerSMH;
                }
                secondsToFiveStack = 5f / mwProcsPerSecond;

                // Elemental Devastation Uptime calc
                staticShocksPerSecond    = (hitsPerSMH + hitsPerSOH) * staticShockChance;
                flameTongueHitsPerSecond = (_calcOpts.MainhandImbue == "Flametongue" ? HitsPerSMH : 0f) +
                                           ((_calcOpts.OffhandImbue == "Flametongue" && _talents.DualWield == 1) ? HitsPerSOH : 0f);
                spellAttacksPerSec = (1f / secondsToFiveStack + 1f / shockSpeed + 1f / fireNovaSpeed + staticShocksPerSecond) // + flameTongueHitsPerSecond)
                                     * (1f - chanceSpellMiss);
                float couldCritSpellsPerS = spellAttacksPerSec - staticShocksPerSecond;                                       // LS procs from Static Shock cannot crit
                edUptime = 1f - (float)Math.Pow(1 - chanceSpellCrit, 10 * couldCritSpellsPerS);
                averageMeleeCritChance = (chanceWhiteCritMH + chanceWhiteCritOH + chanceYellowCritMH + chanceYellowCritOH) / 4f + edUptime * edCritBonus;
            }
            float yellowAttacksPerSecond = hitsPerSWF + hitsPerSMHSS;

            if (_character.ShamanTalents.DualWield == 1 && unhastedMHSpeed != 0)
            {
                yellowAttacksPerSecond += hitsPerSOHSS;
            }

            // set output variables
            edBonusCrit = edUptime * edCritBonus;
            //SetCritValues((1 + _stats.BonusCritChance) * (baseMeleeCrit + meleeCritModifier) + edBonusCrit + .00005f); //fudge factor for rounding
            SetCritValues(baseMeleeCrit + meleeCritModifier + edBonusCrit + .00005f); //fudge factor for rounding
            meleeAttacksPerSec = hitsPerSMH + hitsPerSOH;
            meleeCritsPerSec   = (whiteHitsPerSMH * chanceWhiteCritMH) + (whiteHitsPerSOH * chanceWhiteCritOH) +
                                 (yellowHitsPerSMH * chanceYellowCritMH) + (yellowHitsPerSOH * chanceYellowCritOH) +
                                 (_stats.MoteOfAnger * 2 * AverageWhiteCritChance);
            spellCritsPerSec  = spellAttacksPerSec * ChanceSpellCrit;
            spellCastsPerSec  = spellAttacksPerSec;
            spellMissesPerSec = spellAttacksPerSec * chanceSpellMiss;
            chanceMeleeHit    = meleeAttacksPerSec / (swingsPerSMHMelee + swingsPerSOHMelee + 2f * wfProcsPerSecond + .25f + 1f / 6f);
            maxMana           = _stats.Mana;
            float spellhaste = _stats.SpellHaste + StatConversion.GetSpellHasteFromRating(_stats.HasteRating);

            averageFSDotTime = 18f / (1f + spellhaste);
        }
Exemplo n.º 20
0
        public void Solve(Character character, ref CharacterCalculationsMoonkin calcs)
        {
            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;
            DruidTalents talents = character.DruidTalents;

            procEffects = new List <ProcEffect>();
            RecreateSpells(talents, ref calcs);
            cachedResults = new Dictionary <string, RotationData>();

            float trinketDPS     = 0.0f;
            float baseSpellPower = calcs.SpellPower;
            float baseHit        = GetSpellHit(calcs);
            float baseCrit       = calcs.SpellCrit;
            float baseHaste      = calcs.SpellHaste;

            BuildProcList(calcs);

            float         maxDamageDone      = 0.0f;
            float         maxBurstDamageDone = 0.0f;
            SpellRotation maxBurstRotation   = rotations[0];
            SpellRotation maxRotation        = rotations[0];

            float manaPool = GetEffectiveManaPool(character, calcOpts, calcs);

            // Do tree calculations: Calculate damage per cast.
            float treeDamage = (talents.ForceOfNature == 1) ? DoTreeCalcs(baseSpellPower, calcs.BasicStats.PhysicalHaste, calcs.BasicStats.ArmorPenetration, calcs.BasicStats.PhysicalCrit, calcOpts.TreantLifespan, character.DruidTalents.Brambles) : 0.0f;
            // Extend that to number of casts per fight.
            float treeCasts = (float)Math.Floor(calcs.FightLength / 3) + 1.0f;

            // Partial cast: If the fight lasts 3.x minutes and x is less than 0.5 (30 sec tree duration), calculate a partial cast
            if ((int)calcs.FightLength % 3 == 0 && calcs.FightLength - (int)calcs.FightLength < 0.5)
            {
                treeCasts += (calcs.FightLength - (int)calcs.FightLength) / 0.5f - 1.0f;
            }
            treeDamage *= treeCasts;
            // Multiply by raid-wide damage increases.
            treeDamage *= (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusPhysicalDamageMultiplier);
            // Calculate the DPS averaged over the fight length.
            float treeDPS = treeDamage / (calcs.FightLength * 60.0f);
            // Calculate mana usage for trees.
            float treeManaUsage = (float)Math.Ceiling(treeCasts) * CalculationsMoonkin.BaseMana * 0.12f;

            manaPool -= talents.ForceOfNature == 1 ? treeManaUsage : 0.0f;

            // Do Starfall calculations.
            bool  starfallGlyph    = talents.GlyphOfStarfall;
            Buff  tier102PieceBuff = character.ActiveBuffs.Find(theBuff => theBuff.Name == "Lasherweave Regalia (T10) 2 Piece Bonus");
            float numberOfStarHits = 10.0f;
            float starfallDamage   = (talents.Starfall == 1) ? DoStarfallCalcs(baseSpellPower, baseHit, baseCrit,
                                                                               (1 + calcs.BasicStats.BonusDamageMultiplier) *
                                                                               (1 + calcs.BasicStats.BonusSpellPowerMultiplier) *
                                                                               (1 + calcs.BasicStats.BonusArcaneDamageMultiplier) *
                                                                               (1 + (talents.GlyphOfFocus ? 0.1f : 0.0f)), Wrath.CriticalDamageModifier, out numberOfStarHits) : 0.0f;
            float starfallCD       = 1.5f - (starfallGlyph ? 0.5f : 0.0f);
            float numStarfallCasts = (float)Math.Floor(calcs.FightLength / starfallCD) + 1.0f;
            // Partial cast: If the difference between fight length and total starfall CD time is less than 10 seconds (duration),
            // calculate a partial cast
            float starfallDiff = calcs.FightLength * 60.0f - (numStarfallCasts - 1) * starfallCD * 60.0f;

            if (starfallDiff > 0 && starfallDiff < 10)
            {
                numStarfallCasts += starfallDiff / 60.0f / (1.0f / 6.0f) - 1.0f;
            }
            starfallDamage *= numStarfallCasts;
            float starfallManaUsage = (float)Math.Ceiling(numStarfallCasts) * CalculationsMoonkin.BaseMana * 0.39f;

            manaPool -= talents.Starfall == 1 ? starfallManaUsage : 0.0f;

            // Simple faerie fire mana calc
            float faerieFireCasts = (float)Math.Floor(calcs.FightLength / 5) + (calcs.FightLength % 5 != 0 ? 1.0f : 0.0f);
            float faerieFireMana  = faerieFireCasts * CalculationsMoonkin.BaseMana * 0.08f;

            if (talents.ImprovedFaerieFire > 0)
            {
                manaPool -= faerieFireMana;
            }

            // Calculate effect of casting Starfall/Treants/ImpFF (regular FF is assumed to be provided by a feral)
            float globalCooldown = 1.5f / (1 + baseHaste) + calcs.Latency;
            float treantTime     = (talents.ForceOfNature == 1) ? globalCooldown * (float)Math.Ceiling(treeCasts) : 0.0f;
            float starfallTime   = (talents.Starfall == 1) ? globalCooldown * (float)Math.Ceiling(numStarfallCasts) : 0.0f;
            float faerieFireTime = (talents.ImprovedFaerieFire > 0) ? globalCooldown * faerieFireCasts : 0.0f;

            float totalTimeInRotation   = calcs.FightLength * 60.0f - (treantTime + starfallTime + faerieFireTime);
            float percentTimeInRotation = totalTimeInRotation / (calcs.FightLength * 60.0f);

#if RAWR3
            BossOptions bossOpts = character.BossOptions;
            if (bossOpts == null)
            {
                bossOpts = new BossOptions();
            }

            float fearShare         = (bossOpts.FearingTargsDur / 1000) / bossOpts.FearingTargsFreq;
            float stunShare         = (bossOpts.StunningTargsDur / 1000) / bossOpts.StunningTargsFreq;
            float invulnerableShare = bossOpts.TimeBossIsInvuln / bossOpts.BerserkTimer;

            List <Attack> attacks = bossOpts.GetFilteredAttackList(ATTACK_TYPES.AT_AOE);
            attacks.AddRange(bossOpts.GetFilteredAttackList(ATTACK_TYPES.AT_RANGED));
            int   movementCount           = attacks.Count;
            float assumedMovementDuration = 2.0f;   // Assume 2 seconds per move
            float accumulatedDurations    = 0.0f;
            foreach (Attack a in attacks)
            {
                accumulatedDurations += a.AttackSpeed;
            }

            float movementShare = (movementCount == 0 ? 0 : assumedMovementDuration / (accumulatedDurations / movementCount) / (1 + calcs.BasicStats.MovementSpeed));

            percentTimeInRotation -= movementShare + fearShare + stunShare + invulnerableShare;
#endif

            float manaGained = manaPool - calcs.BasicStats.Mana;

            float oldArcaneMultiplier = calcs.BasicStats.BonusArcaneDamageMultiplier;
            float oldNatureMultiplier = calcs.BasicStats.BonusNatureDamageMultiplier;

            foreach (SpellRotation rot in rotations)
            {
                rot.Solver = this;
                float             accumulatedDamage = 0.0f;
                float             totalUpTime       = 0.0f;
                float[]           spellDetails      = new float[NUM_SPELL_DETAILS];
                List <ProcEffect> activatedEffects  = new List <ProcEffect>();
                List <ProcEffect> alwaysUpEffects   = new List <ProcEffect>();

                // Pre-calculate rotational variables with base stats
                rot.DamageDone(talents, calcs, baseSpellPower, baseHit, baseCrit, baseHaste);

                // Reset variables modified in the pre-loop to base values
                float currentSpellPower = baseSpellPower;
                float currentCrit       = baseCrit;
                float currentHaste      = baseHaste;
                calcs.BasicStats.BonusArcaneDamageMultiplier = oldArcaneMultiplier;
                calcs.BasicStats.BonusNatureDamageMultiplier = oldNatureMultiplier;
                // Calculate spell power/spell damage modifying trinkets in a separate pre-loop
                foreach (ProcEffect proc in procEffects)
                {
                    if (proc.Effect.Stats.SpellPower > 0 || proc.Effect.Stats.Spirit > 0)
                    {
                        float procSpellPower = proc.Effect.Stats.SpellPower;
                        if (proc.Effect.Stats.Spirit > 0)
                        {
                            procSpellPower += proc.Effect.Stats.Spirit * (0.1f * talents.ImprovedMoonkinForm);
                        }

                        float triggerInterval = 0.0f, triggerChance = 1.0f;
                        switch (proc.Effect.Trigger)
                        {
                        case Trigger.DamageDone:
                        case Trigger.DamageOrHealingDone:
                            triggerInterval = ((rot.Duration / rot.CastCount) + (rot.Duration / (rot.MoonfireTicks + rot.InsectSwarmTicks))) / 2.0f;
                            break;

                        case Trigger.Use:
                            break;

                        case Trigger.SpellHit:
                        case Trigger.DamageSpellHit:
                            triggerInterval = rot.Duration / rot.CastCount;
                            triggerChance   = GetSpellHit(calcs);
                            break;

                        case Trigger.SpellCrit:
                        case Trigger.DamageSpellCrit:
                            triggerInterval = rot.Duration / (rot.CastCount - rot.InsectSwarmCasts);
                            triggerChance   = baseCrit;
                            break;

                        case Trigger.SpellCast:
                        case Trigger.DamageSpellCast:
                            triggerInterval = rot.Duration / rot.CastCount;
                            break;

                        case Trigger.MoonfireCast:
                            triggerInterval = rot.Duration / rot.MoonfireCasts;
                            break;

                        case Trigger.DoTTick:
                        case Trigger.InsectSwarmOrMoonfireTick:
                            triggerInterval = rot.Duration / (rot.InsectSwarmTicks + rot.MoonfireTicks);
                            break;

                        case Trigger.MoonfireTick:
                            triggerInterval = rot.Duration / rot.MoonfireTicks;
                            break;

                        case Trigger.InsectSwarmTick:
                            triggerInterval = rot.Duration / rot.InsectSwarmTicks;
                            break;

                        default:
                            triggerChance = 0.0f;
                            break;
                        }
                        if (triggerChance > 0)
                        {
                            currentSpellPower += (proc.Effect.MaxStack > 1 ? proc.Effect.GetAverageStackSize(triggerInterval, triggerChance, 3.0f, calcs.FightLength * 60.0f) : 1) *
                                                 proc.Effect.GetAverageUptime(triggerInterval, triggerChance) * procSpellPower;
                        }
                    }
                    // 2T10 (both if statements, which is why it isn't else-if)
                    if (proc.Effect.Stats.BonusArcaneDamageMultiplier > 0)
                    {
                        calcs.BasicStats.BonusArcaneDamageMultiplier += proc.Effect.GetAverageUptime(rot.Duration / rot.CastCount, 1f) * proc.Effect.Stats.BonusArcaneDamageMultiplier;
                    }
                    if (proc.Effect.Stats.BonusNatureDamageMultiplier > 0)
                    {
                        calcs.BasicStats.BonusNatureDamageMultiplier += proc.Effect.GetAverageUptime(rot.Duration / rot.CastCount, 1f) * proc.Effect.Stats.BonusNatureDamageMultiplier;
                    }
                    if (proc.Effect.Stats._rawSpecialEffectDataSize > 0)
                    {
                        SpecialEffect childEffect = proc.Effect.Stats._rawSpecialEffectData[0];
                        // Nevermelting Ice Crystal
                        if (childEffect.Stats.CritRating != 0)
                        {
                            float maxStack             = proc.Effect.Stats.CritRating;
                            float numNegativeStacks    = childEffect.GetAverageStackSize(rot.Duration / (rot.CastCount - rot.InsectSwarmCasts), baseCrit, 3.0f, proc.Effect.Duration);
                            float averageNegativeValue = childEffect.Stats.CritRating * numNegativeStacks;
                            float averageCritRating    = maxStack + averageNegativeValue;
                            currentCrit += StatConversion.GetSpellCritFromRating(averageCritRating * proc.Effect.GetAverageUptime(0f, 1f));
                        }
                        // Fetish of Volatile Power
                        else if (childEffect.Stats.HasteRating != 0)
                        {
                            currentHaste += StatConversion.GetSpellHasteFromRating(childEffect.Stats.HasteRating * childEffect.GetAverageStackSize(rot.Duration / rot.CastCount, 1f, 3.0f, proc.Effect.Duration) * proc.Effect.GetAverageUptime(0f, 1f));
                        }
                    }
                }
                // Calculate damage and mana contributions for non-stat-boosting trinkets
                // Separate stat-boosting proc trinkets into their own list
                foreach (ProcEffect proc in procEffects)
                {
                    if (proc.CalculateDPS != null)
                    {
                        accumulatedDamage += proc.CalculateDPS(rot, calcs, currentSpellPower, baseHit, currentCrit, currentHaste) * rot.Duration;
                    }
                    else if (proc.Activate != null)
                    {
                        float upTime = proc.UpTime(rot, calcs);
                        // Procs with 100% uptime should be activated and not put into the combination loop
                        if (upTime == 1)
                        {
                            alwaysUpEffects.Add(proc);
                            proc.Activate(character, calcs, ref currentSpellPower, ref baseHit, ref currentCrit, ref currentHaste);
                        }
                        // Procs with uptime 0 < x < 100 should be activated
                        else if (upTime > 0)
                        {
                            activatedEffects.Add(proc);
                        }
                    }
                    else if (proc.CalculateMP5 != null)
                    {
                        manaGained += proc.CalculateMP5(rot, calcs, currentSpellPower, baseHit, currentCrit, currentHaste) / 5.0f * calcs.FightLength * 60.0f;
                    }
                }
                // Calculate stat-boosting trinkets, taking into effect interactions with other stat-boosting procs
                int sign = 1;
                Dictionary <int, float>   cachedDamages = new Dictionary <int, float>();
                Dictionary <int, float>   cachedUptimes = new Dictionary <int, float>();
                Dictionary <int, float[]> cachedDetails = new Dictionary <int, float[]>();
                // Iterate over the entire set of trinket combinations (each trinket by itself, 2 at a time, ...)
                for (int i = 1; i <= activatedEffects.Count; ++i)
                {
                    // Create a new combination generator for this "level" of trinket interaction
                    CombinationGenerator gen = new CombinationGenerator(activatedEffects.Count, i);
                    // Iterate over all combinations
                    while (gen.HasNext())
                    {
                        float tempUpTime    = 1.0f;
                        int[] vals          = gen.GetNext();
                        int   pairs         = 0;
                        int   lengthCounter = 0;
                        // Activate the trinkets, calculate the damage and uptime, then deactivate them
                        foreach (int idx in vals)
                        {
                            pairs |= 1 << idx;
                            ++lengthCounter;
                            activatedEffects[idx].Activate(character, calcs, ref currentSpellPower, ref baseHit, ref currentCrit, ref currentHaste);
                        }
                        float tempDPS = rot.DamageDone(talents, calcs, currentSpellPower, baseHit, currentCrit, currentHaste) / rot.Duration;
                        spellDetails[0]  = Starfire.DamagePerHit;
                        spellDetails[1]  = Wrath.DamagePerHit;
                        spellDetails[2]  = Moonfire.DamagePerHit + Moonfire.DotEffect.DamagePerHit;
                        spellDetails[3]  = InsectSwarm.DotEffect.DamagePerHit;
                        spellDetails[4]  = Starfire.CastTime;
                        spellDetails[5]  = Wrath.CastTime;
                        spellDetails[6]  = Moonfire.CastTime;
                        spellDetails[7]  = InsectSwarm.CastTime;
                        spellDetails[8]  = Starfire.NGCastTime;
                        spellDetails[9]  = Wrath.NGCastTime;
                        spellDetails[10] = Starfire.ManaCost;
                        spellDetails[11] = Wrath.ManaCost;
                        spellDetails[12] = Moonfire.ManaCost;
                        spellDetails[13] = InsectSwarm.ManaCost;
                        foreach (int idx in vals)
                        {
                            tempUpTime *= activatedEffects[idx].UpTime(rot, calcs);
                            activatedEffects[idx].Deactivate(character, calcs, ref currentSpellPower, ref baseHit, ref currentCrit, ref currentHaste);
                        }
                        if (tempUpTime == 0)
                        {
                            continue;
                        }
                        // Adjust previous probability tables by the current factor
                        // At the end of the algorithm, this ensures that the probability table will contain the individual
                        // probabilities of each effect or set of effects.
                        // These adjustments only need to be made for higher levels of the table, and if the current probability is > 0.
                        if (lengthCounter > 1)
                        {
                            List <int> keys = new List <int>(cachedUptimes.Keys);
                            foreach (int subset in keys)
                            {
                                // Truly a subset?
                                if ((pairs & subset) != subset)
                                {
                                    continue;
                                }

                                // Calculate the "layer" of the current subset by getting the set bit count.
                                int subsetLength = 0;
                                for (int j = subset; j > 0; ++subsetLength)
                                {
                                    j &= --j;
                                }

                                // Set the sign of the operation: Evenly separated layers are added, oddly separated layers are subtracted
                                int newSign = ((lengthCounter - subsetLength) % 2 == 0) ? 1 : -1;

                                // Adjust by current uptime * sign of operation.
                                cachedUptimes[subset] += newSign * tempUpTime;
                            }
                        }
                        // Cache the results to be calculated later
                        cachedUptimes[pairs] = tempUpTime;
                        cachedDamages[pairs] = tempDPS;
                        cachedDetails[pairs] = new float[NUM_SPELL_DETAILS];
                        Array.Copy(spellDetails, cachedDetails[pairs], NUM_SPELL_DETAILS);
                        totalUpTime += sign * tempUpTime;
                    }
                    sign = -sign;
                }
                float accumulatedDPS = 0.0f;
                Array.Clear(spellDetails, 0, spellDetails.Length);
                // Apply the above-calculated probabilities to the previously stored damage calculations and add to the result.
                foreach (KeyValuePair <int, float> kvp in cachedUptimes)
                {
                    if (kvp.Value == 0)
                    {
                        continue;
                    }
                    accumulatedDPS += kvp.Value * cachedDamages[kvp.Key];
                    for (int i = 0; i < NUM_SPELL_DETAILS; ++i)
                    {
                        spellDetails[i] += kvp.Value * cachedDetails[kvp.Key][i];
                    }
                }
                float damageDone = rot.DamageDone(talents, calcs, currentSpellPower, baseHit, currentCrit, currentHaste);
                accumulatedDPS   += (1 - totalUpTime) * damageDone / rot.Duration;
                spellDetails[0]  += (1 - totalUpTime) * Starfire.DamagePerHit;
                spellDetails[1]  += (1 - totalUpTime) * Wrath.DamagePerHit;
                spellDetails[2]  += (1 - totalUpTime) * Moonfire.DamagePerHit + Moonfire.DotEffect.DamagePerHit;
                spellDetails[3]  += (1 - totalUpTime) * InsectSwarm.DotEffect.DamagePerHit;
                spellDetails[4]  += (1 - totalUpTime) * Starfire.CastTime;
                spellDetails[5]  += (1 - totalUpTime) * Wrath.CastTime;
                spellDetails[6]  += (1 - totalUpTime) * Moonfire.CastTime;
                spellDetails[7]  += (1 - totalUpTime) * InsectSwarm.CastTime;
                spellDetails[8]  += (1 - totalUpTime) * Starfire.NGCastTime;
                spellDetails[9]  += (1 - totalUpTime) * Wrath.NGCastTime;
                spellDetails[10] += (1 - totalUpTime) * Starfire.ManaCost;
                spellDetails[11] += (1 - totalUpTime) * Wrath.ManaCost;
                spellDetails[12] += (1 - totalUpTime) * Moonfire.ManaCost;
                spellDetails[13] += (1 - totalUpTime) * InsectSwarm.ManaCost;

                accumulatedDamage += accumulatedDPS * rot.Duration;

                float burstDPS     = accumulatedDamage / rot.Duration * percentTimeInRotation;
                float sustainedDPS = burstDPS;
                float timeToOOM    = (manaPool / rot.RotationData.ManaUsed) * rot.Duration;
                if (timeToOOM <= 0)
                {
                    timeToOOM = calcs.FightLength * 60.0f;                   // Happens when ManaUsed is less than 0
                }
                if (timeToOOM < calcs.FightLength * 60.0f)
                {
                    rot.RotationData.TimeToOOM = new TimeSpan(0, (int)(timeToOOM / 60), (int)(timeToOOM % 60));
                    sustainedDPS = burstDPS * timeToOOM / (calcs.FightLength * 60.0f);
                }
                float t10StarfallDamage = starfallDamage;
                // Approximate the effect of the 2T10 set bonus
                if (tier102PieceBuff != null && character.DruidTalents.OmenOfClarity == 1)
                {
                    Stats.SpecialEffectEnumerator enumerator = tier102PieceBuff.Stats.SpecialEffects();
                    enumerator.MoveNext();
                    SpecialEffect effect = enumerator.Current;
                    float         upTime = effect.GetAverageUptime(rot.Duration / rot.CastCount, 1f);
                    t10StarfallDamage = upTime * (starfallDamage * (1 + effect.Stats.BonusArcaneDamageMultiplier)) + (1 - upTime) * starfallDamage;
                }
                float starfallDPS = t10StarfallDamage / (calcs.FightLength * 60.0f);
                burstDPS     += trinketDPS + treeDPS + starfallDPS;
                sustainedDPS += trinketDPS + treeDPS + starfallDPS;

                rot.StarfallDamage        = t10StarfallDamage / numStarfallCasts;
                rot.StarfallStars         = numberOfStarHits;
                rot.RotationData.BurstDPS = burstDPS;
                rot.RotationData.DPS      = sustainedDPS;
                rot.StarfireAvgHit        = spellDetails[0];
                rot.WrathAvgHit           = spellDetails[1];
                rot.MoonfireAvgHit        = spellDetails[2];
                rot.InsectSwarmAvgHit     = spellDetails[3];
                rot.StarfireAvgCast       = spellDetails[4];
                rot.WrathAvgCast          = spellDetails[5];
                rot.MoonfireCastTime      = spellDetails[6];
                rot.InsectSwarmCastTime   = spellDetails[7];
                rot.StarfireNGCastTime    = spellDetails[8];
                rot.WrathNGCastTime       = spellDetails[9];
                rot.StarfireManaCost      = spellDetails[10];
                rot.WrathManaCost         = spellDetails[11];
                rot.MoonfireManaCost      = spellDetails[12];
                rot.InsectSwarmManaCost   = spellDetails[13];

                // Update the sustained DPS rotation if any one of the following three cases is true:
                // 1) No user rotation is selected and sustained DPS is maximum
                // 2) A user rotation is selected, Eclipse is not present, and the user rotation matches the current rotation
                // 3) A user rotation is selected, Eclipse is present, and the user rotation's dot spells matches this rotation's
                if ((calcOpts.UserRotation == "None" && sustainedDPS > maxDamageDone) ||
                    (character.DruidTalents.Eclipse == 0 && calcOpts.UserRotation == rot.Name) ||
                    (character.DruidTalents.Eclipse > 0 && (calcOpts.UserRotation == rot.Name.Replace("Filler", "SF") ||
                                                            calcOpts.UserRotation == rot.Name.Replace("Filler", "W"))))
                {
                    maxDamageDone = sustainedDPS;
                    maxRotation   = rot;
                }
                if (burstDPS > maxBurstDamageDone)
                {
                    maxBurstDamageDone = burstDPS;
                    maxBurstRotation   = rot;
                }
                rot.ManaGained += manaGained / (calcs.FightLength * 60.0f) * rot.Duration;
                rot.RotationData.ManaGained += manaGained / (calcs.FightLength * 60.0f) * rot.Duration;
                if (rot.Name.Contains("Filler"))
                {
                    cachedResults[rot.Name.Replace("Filler", "SF")] = rot.RotationData;
                    cachedResults[rot.Name.Replace("Filler", "W")]  = rot.RotationData;
                }
                else
                {
                    cachedResults[rot.Name] = rot.RotationData;
                }

                // Deactivate always-up procs
                foreach (ProcEffect proc in alwaysUpEffects)
                {
                    proc.Deactivate(character, calcs, ref currentSpellPower, ref baseHit, ref currentCrit, ref currentHaste);
                }
            }
            // Present the findings to the user.
            calcs.TreantDamage     = treeDamage / treeCasts;
            calcs.StarfallMana     = starfallManaUsage / numStarfallCasts;
            calcs.SelectedRotation = maxRotation;
            calcs.BurstDPSRotation = maxBurstRotation;
            calcs.SubPoints        = new float[] { maxDamageDone, maxBurstDamageDone };
            calcs.OverallPoints    = calcs.SubPoints[0] + calcs.SubPoints[1];
            calcs.Rotations        = cachedResults;
        }
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> dictValues = new Dictionary <string, string>();

            dictValues.Add("Health", BasicStats.Health.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Mana", BasicStats.Mana.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Attack Power", BasicStats.AttackPower.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Agility", BasicStats.Agility.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Strength", BasicStats.Strength.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Intellect", BasicStats.Intellect.ToString("F0", CultureInfo.InvariantCulture));

            dictValues.Add("White Hit", WhiteHit.ToString("F2", CultureInfo.InvariantCulture) + "%");
            if (YellowHit < 100f && TotalExpertiseMH < 26)
            {
                float ratingRequired = (float)Math.Ceiling(4f * StatConversion.GetRatingFromExpertise(100f - YellowHit));
                dictValues.Add("Yellow Hit", String.Format("{0}% (Under Cap)*You need {1} more expertise to cap specials (WF,SS)",
                                                           YellowHit.ToString("F2", CultureInfo.InvariantCulture),
                                                           ratingRequired.ToString("F0", CultureInfo.InvariantCulture)));
            }
            else
            {
                if (ParriedAttacks > 0)
                {
                    float ratingRequired = (float)Math.Ceiling(4f * StatConversion.GetRatingFromExpertise(100f - YellowHit));
                    dictValues.Add("Yellow Hit", String.Format("{0}%*Being in front of boss allows your attacks to be parried\r\nYou would need {1} more expertise to cap specials (WF,SS)",
                                                               YellowHit.ToString("F2", CultureInfo.InvariantCulture),
                                                               ratingRequired.ToString("F0", CultureInfo.InvariantCulture)));
                }
                else
                {
                    dictValues.Add("Yellow Hit", YellowHit.ToString("F2", CultureInfo.InvariantCulture) + "%");
                }
            }
            if (OverSpellHitCap > 0.38f) // only warn if more than .38% over cap (equivalent to 10 hit rating)
            {
                dictValues.Add("Spell Hit", String.Format("{0}% (Over Cap)*Over Spell Hit Cap by {1}%",
                                                          SpellHit.ToString("F2", CultureInfo.InvariantCulture),
                                                          OverSpellHitCap.ToString("F2", CultureInfo.InvariantCulture)));
            }
            else
            {
                if (SpellHit < 100f)
                {
                    float ratingRequired = (float)Math.Ceiling(StatConversion.GetRatingFromHit(1f - SpellHit / 100f));
                    dictValues.Add("Spell Hit", String.Format("{0}% (Under Cap)*You need {1} more hit rating to cap spells (ES, LB etc)",
                                                              SpellHit.ToString("F2", CultureInfo.InvariantCulture),
                                                              ratingRequired.ToString("F0", CultureInfo.InvariantCulture)));
                }
                else
                {
                    dictValues.Add("Spell Hit", SpellHit.ToString("F2", CultureInfo.InvariantCulture) + "%");
                }
            }
            if (OverMeleeCritCap > 0.21f) // only warn if more than .21% over cap (equivalent to 10 crit rating)
            {
                dictValues.Add("Melee Crit", String.Format("{0} (Over Cap)*Crit Rating {1} (+{2}% crit chance)\r\nOver Soft Cap by {3}%",
                                                           MeleeCrit.ToString("F2", CultureInfo.InvariantCulture) + "%",
                                                           BasicStats.CritRating.ToString("F0", CultureInfo.InvariantCulture),
                                                           (StatConversion.GetCritFromRating(BasicStats.CritRating) * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                           OverMeleeCritCap.ToString("F2", CultureInfo.InvariantCulture)));
            }
            else
            {
                dictValues.Add("Melee Crit", String.Format("{0}*Crit Rating {1} (+{2}% crit chance)",
                                                           MeleeCrit.ToString("F2", CultureInfo.InvariantCulture) + "%",
                                                           BasicStats.CritRating.ToString("F0", CultureInfo.InvariantCulture),
                                                           (StatConversion.GetCritFromRating(BasicStats.CritRating) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            }

            dictValues.Add("Spell Crit", String.Format("{0}*Crit Rating {1} (+{2}% crit chance)",
                                                       SpellCrit.ToString("F2", CultureInfo.InvariantCulture) + "%",
                                                       BasicStats.CritRating.ToString("F0", CultureInfo.InvariantCulture),
                                                       (StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f).ToString("F2", CultureInfo.InvariantCulture)));

            dictValues.Add("Spellpower", BasicStats.SpellPower.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Total Expertise", getExpertiseString());
            dictValues.Add("Haste Rating", String.Format("{0}*{1}% Melee Haste\r\n{2}% Spell Haste",
                                                         BasicStats.HasteRating.ToString("F0", CultureInfo.InvariantCulture),
                                                         (StatConversion.GetHasteFromRating(BasicStats.HasteRating, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                         (StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Hit Rating", String.Format("{0}*{1}% Melee Hit\r\n{2}% Spell Hit",
                                                       BasicStats.HitRating.ToString("F0", CultureInfo.InvariantCulture),
                                                       (StatConversion.GetHitFromRating(BasicStats.HitRating) * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                       (StatConversion.GetSpellHitFromRating(BasicStats.HitRating) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Armour Pen Rating", String.Format("{0}*{1}% Armour Penetration",
                                                              BasicStats.ArmorPenetrationRating.ToString("F0", CultureInfo.InvariantCulture),
                                                              (StatConversion.GetArmorPenetrationFromRating(BasicStats.ArmorPenetrationRating) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            float spellMiss = 100 - SpellHit;

            dictValues.Add("Avoided Attacks", String.Format("{0}%*{1}% Boss Dodged\r\n{2}% Boss Parried\r\n{3}% Spell Misses\r\n{4}% White Misses",
                                                            AvoidedAttacks.ToString("F2", CultureInfo.InvariantCulture),
                                                            DodgedAttacks.ToString("F2", CultureInfo.InvariantCulture),
                                                            ParriedAttacks.ToString("F2", CultureInfo.InvariantCulture),
                                                            spellMiss.ToString("F2", CultureInfo.InvariantCulture),
                                                            MissedAttacks.ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Avg MH Speed", AvMHSpeed.ToString("F2", CultureInfo.InvariantCulture));
            dictValues.Add("Avg OH Speed", AvOHSpeed.ToString("F2", CultureInfo.InvariantCulture));
            dictValues.Add("Armor Mitigation", ArmorMitigation.ToString("F2", CultureInfo.InvariantCulture) + "%*Amount of physical damage lost due to boss armor");

            dictValues.Add("ED Uptime", String.Format("{0}%*{1}% ED Bonus Crit",
                                                      EDUptime.ToString("F2", CultureInfo.InvariantCulture),
                                                      EDBonusCrit.ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Flurry Uptime", FlurryUptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Avg Time to 5 Stack", String.Format("{0} sec*{1} PPM",
                                                                SecondsTo5Stack.ToString("F2", CultureInfo.InvariantCulture),
                                                                _MWPPM.ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("MH Enchant Uptime", MHEnchantUptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("OH Enchant Uptime", OHEnchantUptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Trinket 1 Uptime", Trinket1Uptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Trinket 2 Uptime", Trinket2Uptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Fire Totem Uptime", FireTotemUptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Tier 10 2 pc Uptime", T10_2Uptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Tier 10 4 pc Uptime", T10_4Uptime.ToString("F2", CultureInfo.InvariantCulture) + "%");

            dictValues.Add("DPS Points", DPSPoints.ToString("F2", CultureInfo.InvariantCulture));
            dictValues.Add("Survivability Points", SurvivabilityPoints.ToString("F2", CultureInfo.InvariantCulture));
            dictValues.Add("Overall Points", OverallPoints.ToString("F2", CultureInfo.InvariantCulture));

            dictValues.Add("White Damage", dpsOutputFormat(SwingDamage, DPSPoints, true));
            dictValues.Add("Windfury Attack", dpsOutputFormat(WindfuryAttack, DPSPoints, true));
            dictValues.Add("Flametongue Attack", dpsOutputFormat(FlameTongueAttack, DPSPoints, false));
            dictValues.Add("Lightning Bolt", dpsOutputFormat(LightningBolt, DPSPoints, false));
            dictValues.Add("Earth Shock", dpsOutputFormat(EarthShock, DPSPoints, false));
            dictValues.Add("Flame Shock", dpsOutputFormat(FlameShock, DPSPoints, false));
            dictValues.Add("Searing/Magma Totem", dpsOutputFormat(SearingMagma, DPSPoints, false));
            dictValues.Add("Stormstrike", dpsOutputFormat(Stormstrike, DPSPoints, true));
            dictValues.Add("Spirit Wolf", dpsOutputFormat(SpiritWolf, DPSPoints, true));
            dictValues.Add("Fire Nova", dpsOutputFormat(FireNova, DPSPoints, false));
            dictValues.Add("Fire Elemental", FireElemental.getDPSOutput());
            dictValues.Add("Lightning Shield", dpsOutputFormat(LightningShield, DPSPoints, false));
            dictValues.Add("Lava Lash", dpsOutputFormat(LavaLash, DPSPoints, true));
            dictValues.Add("Total DPS", DPSPoints.ToString("F2", CultureInfo.InvariantCulture));

            dictValues.Add("Enhance Version", _version);

            dictValues.Add("Status", String.Format("Enhance Model : DPS Points {0}, Survivability Points {1}, Overall Points {2}",
                                                   DPSPoints.ToString("F2", CultureInfo.InvariantCulture),
                                                   SurvivabilityPoints.ToString("F2", CultureInfo.InvariantCulture),
                                                   OverallPoints.ToString("F2", CultureInfo.InvariantCulture)));

            return(dictValues);
        }
Exemplo n.º 22
0
        public override Stats GetCharacterStats(Character character, Item additionalItem)
        {
            PriestTalents talents = character.PriestTalents;

            Stats statsTotal = new Stats();

            Stats baseStats = BaseStats.GetBaseStats(character.Level, character.Class, character.Race);
            Stats itemStats = GetItemStats(character, additionalItem);
            Stats buffStats = GetBuffsStats(character, _calculationOptions);

            // Get the gear/enchants/buffs stats loaded in
            statsTotal.Accumulate(baseStats);
            statsTotal.Accumulate(itemStats);
            statsTotal.Accumulate(buffStats);

            Stats statsTalents = new Stats()
            {
                // we can only wear items that are cloth so we always have our specialization, even naked.
                BonusIntellectMultiplier = 0.05f,

                BonusShadowDamageMultiplier = (1 + 0.02f * talents.TwinDisciplines) *
                                              (1 + 0.02f * talents.TwistedFaith) *
                                              (1 + 0.15f * talents.Shadowform) - 1,

                BonusHolyDamageMultiplier = (1 + 0.02f * talents.TwinDisciplines) - 1,

                // this is the shadow priest model so they must have 'Shadow Power'
                BonusSpellPowerMultiplier = .15f,
            };

            statsTotal.Accumulate(statsTalents);

            statsTotal.Stamina    = (float)Math.Floor(statsTotal.Stamina * (1 + statsTotal.BonusStaminaMultiplier));
            statsTotal.Intellect += (float)Math.Floor(itemStats.Intellect * statsTotal.BonusIntellectMultiplier);
            statsTotal.Spirit     = (float)Math.Round(statsTotal.Spirit * (1 + statsTotal.BonusSpiritMultiplier));

            statsTotal.Health += (float)Math.Floor(StatConversion.GetHealthFromStamina(statsTotal.Stamina) * (1f + statsTotal.BonusHealthMultiplier));

            statsTotal.Mana = (float)Math.Round(statsTotal.Mana + StatConversion.GetManaFromIntellect(statsTotal.Intellect));
            statsTotal.Mana = (float)Math.Round(statsTotal.Mana * (1f + statsTotal.BonusManaMultiplier));

            statsTotal.SpellPower += statsTotal.Intellect - 10;

            float hasteFromRating = StatConversion.GetSpellHasteFromRating(statsTotal.HasteRating);
            float talentedHaste   = (1 + hasteFromRating) * (1 + talents.Darkness * .01f) - 1;

            statsTotal.SpellHaste += character.Race == CharacterRace.Goblin ? talentedHaste * 1.01f : talentedHaste;

            float baseBonus = (float)Math.Floor(baseStats.Spirit * statsTotal.BonusSpiritMultiplier);
            float itemBonus = (float)Math.Floor(itemStats.Spirit * statsTotal.BonusSpiritMultiplier);
            float spiritFromItemsAndEffects = baseBonus + itemBonus + itemStats.Spirit;
            float hitRatingFromSpirit       = (0.5f * talents.TwistedFaith) * Math.Max(0f, spiritFromItemsAndEffects);

            statsTotal.HitRating += hitRatingFromSpirit;
            statsTotal.SpellHit  += StatConversion.GetSpellHitFromRating(statsTotal.HitRating);

            // ignoring the base crit percentage here as the in-game tooltip says that the int -> crit conversion contains the base.
            float critFromInt    = StatConversion.GetSpellCritFromIntellect(statsTotal.Intellect) + 0.012375f;
            float critFromRating = StatConversion.GetSpellCritFromRating(statsTotal.CritRating);

            statsTotal.SpellCrit = character.Race == CharacterRace.Worgen ? (critFromInt + critFromRating) + .01f : (critFromInt + critFromRating);

            // Armor
            statsTotal.Armor      = statsTotal.Armor * (1f + statsTotal.BaseArmorMultiplier);
            statsTotal.BonusArmor = statsTotal.BonusArmor * (1f + statsTotal.BonusArmorMultiplier);
            statsTotal.Armor     += statsTotal.BonusArmor;
            statsTotal.Armor      = (float)Math.Round(statsTotal.Armor);

            return(statsTotal);
        }
Exemplo n.º 23
0
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> dictValues = new Dictionary <string, string>();

            dictValues.Add("DPS Points", DPS.ToString("F2", CultureInfo.InvariantCulture));
            dictValues.Add("Survivability Points", Survivability.ToString("F2", CultureInfo.InvariantCulture));
            dictValues.Add("Overall Points", OverallPoints.ToString("F2", CultureInfo.InvariantCulture));

            dictValues.Add("Health", BasicStats.Health.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Mana", BasicStats.Mana.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Strength", String.Format("{0}*Increases Attack Power by {1}",
                                                     BasicStats.Strength.ToString("F0", CultureInfo.InvariantCulture),
                                                     (BasicStats.Strength - 10f).ToString("F0", CultureInfo.InvariantCulture)));
            dictValues.Add("Agility", String.Format("{0}*Increases Attack Power by {1}\r\nIncreases Critical Hit chance by {2}%",
                                                    BasicStats.Agility.ToString("F0", CultureInfo.InvariantCulture),
                                                    ((BasicStats.Agility * 2f) - 20f).ToString("F0", CultureInfo.InvariantCulture),
                                                    (StatConversion.GetCritFromAgility(BasicStats.Agility, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Stamina", String.Format("{0}*Increase Health by {1}",
                                                    BasicStats.Stamina.ToString("F0", CultureInfo.InvariantCulture),
                                                    (StatConversion.GetHealthFromStamina(BasicStats.Stamina)).ToString("F0", CultureInfo.InvariantCulture)));
            dictValues.Add("Intellect", String.Format("{0}*Increases Mana by {1}\r\nIncreases Spell Power by {2}\r\nIncreases Spell Critical Hit chance by {3}%",
                                                      BasicStats.Intellect.ToString("F0", CultureInfo.InvariantCulture),
                                                      (StatConversion.GetManaFromIntellect(BasicStats.Intellect)).ToString("F0", CultureInfo.InvariantCulture),
                                                      (BasicStats.Intellect - 10f).ToString("F0", CultureInfo.InvariantCulture),
                                                      (StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Spirit", String.Format("{0}*Increases mana regeneration by {1} every 5 seconds while not casting",
                                                   BasicStats.Spirit.ToString("F0", CultureInfo.InvariantCulture),
                                                   (StatConversion.GetSpiritRegenSec(BasicStats.Spirit, BasicStats.Intellect) * 5f).ToString("F0", CultureInfo.InvariantCulture)));
            dictValues.Add("Mastery", String.Format("{0}*Mastery rating of {1} adds {2} Mastery\r\nIncreases all Fire, Frost, and Nature Damage by {3}%.",
                                                    (8f + StatConversion.GetMasteryFromRating(BasicStats.MasteryRating)).ToString("F2", CultureInfo.InvariantCulture),
                                                    BasicStats.MasteryRating.ToString("F0", CultureInfo.InvariantCulture),
                                                    (StatConversion.GetMasteryFromRating(BasicStats.MasteryRating)).ToString("F2", CultureInfo.InvariantCulture),
                                                    ((8f + StatConversion.GetMasteryFromRating(BasicStats.MasteryRating)) * 2.5f).ToString("F2", CultureInfo.InvariantCulture)));

            //dictValues.Add("Damage"
            //dictValues.Add("DPS"
            dictValues.Add("Attack Power", BasicStats.AttackPower.ToString("F0", CultureInfo.InvariantCulture));
            //dictValues.Add("Speed"
            dictValues.Add("Melee Haste", String.Format("{0}%*Haste Rating of {1} adds {2}% Haste",
                                                        (StatConversion.GetHasteFromRating(BasicStats.HasteRating, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                        BasicStats.HasteRating.ToString("F0", CultureInfo.InvariantCulture),
                                                        (StatConversion.GetHasteFromRating(BasicStats.HasteRating, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Melee Hit", String.Format("{0}%*Hit Rating of {1} adds {2}% Hit chance\r\n{3}% Draenei Hit Bonus",
                                                      (StatConversion.GetHitFromRating(BasicStats.HitRating) * 100f + DraeneiHitBonus * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                      BasicStats.HitRating.ToString("F0", CultureInfo.InvariantCulture),
                                                      (StatConversion.GetHitFromRating(BasicStats.HitRating) * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                      DraeneiHitBonus * 100f));
            dictValues.Add("Melee Crit", String.Format("{0}%*Crit Rating of {1} adds {2}% Crit chance",
                                                       ((StatConversion.GetCritFromRating(BasicStats.CritRating, CharacterClass.Shaman) * 100f) + (StatConversion.GetCritFromAgility(BasicStats.Agility, CharacterClass.Shaman) * 100f)).ToString("F2", CultureInfo.InvariantCulture),
                                                       BasicStats.CritRating.ToString("F0", CultureInfo.InvariantCulture),
                                                       (StatConversion.GetCritFromRating(BasicStats.CritRating, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Expertise", getExpertiseString());// String.Format("{0} / {1}*Reduces chance to be dodged or parried by {2}% / {3}%\r\nExpertise Rating of {4} adds {5} Expertise",

            dictValues.Add("Spell Power", BasicStats.SpellPower.ToString("F0", CultureInfo.InvariantCulture));
            dictValues.Add("Spell Haste", String.Format("{0}%*Haste Rating of {1} adds {2}% Haste",
                                                        (StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                        BasicStats.HasteRating.ToString("F0", CultureInfo.InvariantCulture),
                                                        (StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Spell Hit", String.Format("{0}%*Hit Rating of {1} adds {2}% Hit chance\r\n{3}% Draenei Hit Bonus",
                                                      (StatConversion.GetSpellHitFromRating(BasicStats.HitRating + ElemPrecMod) * 100f + DraeneiHitBonus * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                      (BasicStats.HitRating + ElemPrecMod).ToString("F0", CultureInfo.InvariantCulture),
                                                      (StatConversion.GetSpellHitFromRating(BasicStats.HitRating + ElemPrecMod) * 100f).ToString("F2", CultureInfo.InvariantCulture),
                                                      DraeneiHitBonus * 100f));
            dictValues.Add("Spell Crit", String.Format("{0}%*Crit Rating of {1} adds {2}% Crit chance",
                                                       ((StatConversion.GetSpellCritFromRating(BasicStats.CritRating, CharacterClass.Shaman) * 100f) + (StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect, CharacterClass.Shaman) * 100f)).ToString("F2", CultureInfo.InvariantCulture),
                                                       BasicStats.CritRating.ToString("F0", CultureInfo.InvariantCulture),
                                                       (StatConversion.GetSpellCritFromRating(BasicStats.CritRating, CharacterClass.Shaman) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Combat Regen", String.Format("{0}*{0} mana regenerated every 5 seconds while in combat",
                                                         BaseRegen.ToString("F0", CultureInfo.InvariantCulture)));

            //dictValues.Add("Avg Agility", _attackPower.ToString("F0", CultureInfo.InvariantCulture));
            //dictValues.Add("Avg Intellect"
            //dictValues.Add("Avg Mastery"
            //dictValues.Add("Avg Attack Power"
            dictValues.Add("Avg Speed", String.Format("{0} / {1}", AvMHSpeed.ToString("F2", CultureInfo.InvariantCulture), AvOHSpeed.ToString("F2", CultureInfo.InvariantCulture)));
            //dictValues.Add("Avg Melee Haste"
            //dictValues.Add("Avg Melee Hit"
            //dictValues.Add("Avg Melee Crit"
            //dictValues.Add("Avg Expertise"
            //dictValues.Add("Avg Spell Power"
            //dictValues.Add("Avg Spell Haste"
            //dictValues.Add("Avg Spell Hit"
            //dictValues.Add("Avg Spell Crit"
            dictValues.Add("Avg Combat Regen", ManaRegen.ToString("F0", CultureInfo.InvariantCulture));

            /*dictValues.Add("White Hit", WhiteHit.ToString("F2", CultureInfo.InvariantCulture) + "%");
             * if (YellowHit < 100f && TotalExpertiseMH < 26)
             * {
             *  float ratingRequired = (float)Math.Ceiling(4f * StatConversion.GetRatingFromExpertise(100f - YellowHit));
             *  dictValues.Add("Yellow Hit", String.Format("{0}% (Under Cap)*You need {1} more expertise to cap specials (WF,SS)",
             *      YellowHit.ToString("F2", CultureInfo.InvariantCulture),
             *      ratingRequired.ToString("F0", CultureInfo.InvariantCulture)));
             * }
             * else
             * {
             *  if (ParriedAttacks > 0)
             *  {
             *      float ratingRequired = (float)Math.Ceiling(4f * StatConversion.GetRatingFromExpertise(100f - YellowHit));
             *      dictValues.Add("Yellow Hit", String.Format("{0}%*Being in front of boss allows your attacks to be parried\r\nYou would need {1} more expertise to cap specials (WF,SS)",
             *         YellowHit.ToString("F2", CultureInfo.InvariantCulture),
             *         ratingRequired.ToString("F0", CultureInfo.InvariantCulture)));
             *  }
             *  else
             *      dictValues.Add("Yellow Hit", YellowHit.ToString("F2", CultureInfo.InvariantCulture) + "%");
             * }
             * if (OverSpellHitCap > 0.38f) // only warn if more than .38% over cap (equivalent to 10 hit rating)
             *  dictValues.Add("Spell Hit", String.Format("{0}% (Over Cap)*Over Spell Hit Cap by {1}%",
             *      SpellHit.ToString("F2", CultureInfo.InvariantCulture),
             *      OverSpellHitCap.ToString("F2", CultureInfo.InvariantCulture)));
             * else
             * {
             *  if (SpellHit < 100f)
             *  {
             *      float ratingRequired = (float)Math.Ceiling(StatConversion.GetRatingFromSpellHit(1f - SpellHit/100f));
             *      dictValues.Add("Spell Hit", String.Format("{0}% (Under Cap)*You need {1} more hit rating to cap spells (ES, LB etc)",
             *          SpellHit.ToString("F2", CultureInfo.InvariantCulture),
             *          ratingRequired.ToString("F0", CultureInfo.InvariantCulture)));
             *  }
             *  else
             *      dictValues.Add("Spell Hit", SpellHit.ToString("F2", CultureInfo.InvariantCulture) + "%");
             * }*/
            /*if (OverMeleeCritCap > 0.21f) // only warn if more than .21% over cap (equivalent to 10 crit rating)
             *  dictValues.Add("Melee Crit", String.Format("{0} (Over Cap)*Crit Rating {1} (+{2}% crit chance)\r\nOver Soft Cap by {3}%",
             *      MeleeCrit.ToString("F2", CultureInfo.InvariantCulture) + "%",
             *      BasicStats.CritRating.ToString("F0", CultureInfo.InvariantCulture),
             *      (StatConversion.GetCritFromRating(BasicStats.CritRating) * 100f).ToString("F2", CultureInfo.InvariantCulture),
             *      OverMeleeCritCap.ToString("F2", CultureInfo.InvariantCulture)));
             * else
             *  dictValues.Add("Melee Crit", String.Format("{0}*Crit Rating {1} (+{2}% crit chance)",
             *      MeleeCrit.ToString("F2", CultureInfo.InvariantCulture) + "%",
             *      BasicStats.CritRating.ToString("F0", CultureInfo.InvariantCulture),
             *      (StatConversion.GetCritFromRating(BasicStats.CritRating) * 100f).ToString("F2", CultureInfo.InvariantCulture)));
             *
             * dictValues.Add("Spell Crit", String.Format("{0}*Crit Rating {1} (+{2}% crit chance)",
             *  SpellCrit.ToString("F2", CultureInfo.InvariantCulture) + "%",
             *  BasicStats.CritRating.ToString("F0", CultureInfo.InvariantCulture),
             *  (StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f).ToString("F2", CultureInfo.InvariantCulture)));*/

            float spellMiss = 100 - SpellHit;

            dictValues.Add("Avoided Attacks", String.Format("{0}%*{1}% Boss Dodged\r\n{2}% Boss Parried\r\n{3}% Spell Misses\r\n{4}% White Misses",
                                                            AvoidedAttacks.ToString("F2", CultureInfo.InvariantCulture),
                                                            DodgedAttacks.ToString("F2", CultureInfo.InvariantCulture),
                                                            ParriedAttacks.ToString("F2", CultureInfo.InvariantCulture),
                                                            spellMiss.ToString("F2", CultureInfo.InvariantCulture),
                                                            MissedAttacks.ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Armor Mitigation", ArmorMitigation.ToString("F2", CultureInfo.InvariantCulture) + "%*Amount of physical damage lost due to boss armor");

            dictValues.Add("ED Uptime", String.Format("{0}%*{1}% ED Bonus Crit",
                                                      EDUptime.ToString("F2", CultureInfo.InvariantCulture),
                                                      EDBonusCrit.ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("Flurry Uptime", FlurryUptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Avg Time to 5 Stack", String.Format("{0} sec*{1} PPM",
                                                                SecondsTo5Stack.ToString("F2", CultureInfo.InvariantCulture),
                                                                _MWPPM.ToString("F2", CultureInfo.InvariantCulture)));
            dictValues.Add("MH Enchant Uptime", MHEnchantUptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("OH Enchant Uptime", OHEnchantUptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Trinket 1 Uptime", Trinket1Uptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Trinket 2 Uptime", Trinket2Uptime.ToString("F2", CultureInfo.InvariantCulture) + "%");
            dictValues.Add("Fire Totem Uptime", FireTotemUptime.ToString("F2", CultureInfo.InvariantCulture) + "%");

            dictValues.Add("White Damage", dpsOutputFormat(SwingDamage, DPS, true));
            dictValues.Add("Windfury Attack", dpsOutputFormat(WindfuryAttack, DPS, true));
            dictValues.Add("Flametongue Attack", dpsOutputFormat(FlameTongueAttack, DPS, true));
            dictValues.Add("Stormstrike", dpsOutputFormat(Stormstrike, DPS, true));
            dictValues.Add("Lava Lash", dpsOutputFormat(LavaLash, DPS, true));
            dictValues.Add("Searing/Magma Totem", dpsOutputFormat(SearingMagma, DPS, false));
            dictValues.Add("Earth Shock", dpsOutputFormat(EarthShock, DPS, false));
            dictValues.Add("Flame Shock", dpsOutputFormat(FlameShock, DPS, false));
            dictValues.Add("Lightning Bolt", dpsOutputFormat(LightningBolt, DPS, false));
            dictValues.Add("Unleash Wind", dpsOutputFormat(UnleashWind, DPS, true));
            dictValues.Add("Unleash Flame", dpsOutputFormat(UnleashFlame, DPS, false));
            dictValues.Add("Lightning Shield", dpsOutputFormat(LightningShield, DPS, false));
            dictValues.Add("Chain Lightning", dpsOutputFormat(ChainLightning, DPS, false));
            dictValues.Add("Fire Nova", dpsOutputFormat(FireNova, DPS, false));
            dictValues.Add("Fire Elemental", FireElemental.getDPSOutput());
            dictValues.Add("Spirit Wolf", dpsOutputFormat(SpiritWolf, DPS, true));
            dictValues.Add("Other", dpsOutputFormat(Other, DPS, false));
            dictValues.Add("Total DPS", DPS.ToString("F2", CultureInfo.InvariantCulture));

            /*dictValues.Add("Status", String.Format("Enhance Model : DPS Points {0}, Survivability Points {1}, Overall Points {2}",
             *  DPS.ToString("F2", CultureInfo.InvariantCulture),
             *  Survivability.ToString("F2", CultureInfo.InvariantCulture),
             *  OverallPoints.ToString("F2", CultureInfo.InvariantCulture)));*/

            return(dictValues);
        }
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string>    dictValues = new Dictionary <string, string>();
            CalculationOptionsShadowPriest calcOpts   = character.CalculationOptions as CalculationOptionsShadowPriest;
            BossOptions BossOpts  = character.BossOptions;
            Stats       baseStats = BaseStats.GetBaseStats(character.Level, character.Class, character.Race);
            bool        Ptr       = calcOpts.PTR;

            dictValues.Add("Health", BasicStats.Health.ToString());
            float ResilienceCap = 0.15f, ResilienceFromRating = StatConversion.GetCritReductionFromResilience(1);
            float Resilience = StatConversion.GetCritReductionFromResilience(BasicStats.Resilience);

            dictValues.Add("Resilience", string.Format("{0}*-{1}% Damage from DoT and Mana Drains\n\r-{1}% Chance to be crit\r\n-{2}% Damage from Crits.\r\n{3}",
                                                       BasicStats.Resilience.ToString(),
                                                       (Resilience * 100f).ToString("0.00"),
                                                       (Resilience * 100f * 2.2f).ToString("0.00"),
                                                       (Resilience > ResilienceCap) ? (string.Format("{0} rating above cap", ((float)Math.Floor((Resilience - ResilienceCap) / ResilienceFromRating)).ToString("0"))) : (string.Format("{0} rating below cap", ((float)Math.Ceiling((ResilienceCap - Resilience) / ResilienceFromRating)).ToString("0")))));
            dictValues.Add("Stamina", BasicStats.Stamina.ToString());
            dictValues.Add("Mana", BasicStats.Mana.ToString());
            dictValues.Add("Intellect", BasicStats.Intellect.ToString());
            dictValues.Add("Spirit", Math.Floor(BasicStats.Spirit).ToString("0"));
            dictValues.Add("Spell Power", String.Format("{0}*{1} Bonus Shadow\r\n{2} Bonus Holy\r\n{3} from Inner Fire",
                                                        Math.Floor(BasicStats.SpellPower),
                                                        Math.Floor(BasicStats.SpellPower + BasicStats.SpellShadowDamageRating),
                                                        Math.Floor(BasicStats.SpellPower /*+ BasicStats.SpellHolyDamageRating*/),
                                                        BasicStats.PriestInnerFire * CalculationsShadowPriest.GetInnerFireSpellPowerBonus(character)));
            dictValues.Add("Regen", String.Format("{0}*MP5: {1}\r\nOutFSR: {2}", RegenInFSR.ToString("0"), BasicStats.Mp5.ToString(), RegenOutFSR.ToString("0")));

            dictValues.Add("Crit", string.Format("{0}%*{1}% from {2} Spell Crit rating\r\n{3}% from Intellect\r\n{4}% from Focused Will\r\n{5}% from Base Crit\r\n{6}% from Buffs\r\n{7}% on Mind Blast, Mind Flay and Mind Sear.\r\n{8}% on VT, SW:P and DP\r\n{9}% on Smite, Holy Fire and Penance.",
                                                 (BasicStats.SpellCrit * 100f).ToString("0.00"),
                                                 (StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f).ToString("0.00"),
                                                 BasicStats.CritRating.ToString("0"),
                                                 (StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect) * 100f).ToString("0.00"),
                                                 character.PriestTalents.FocusedWill,
                                                 (baseStats.SpellCrit * 100f).ToString("0.00"),
                                                 (BasicStats.SpellCrit * 100f - StatConversion.GetSpellCritFromRating(BasicStats.CritRating) * 100f - StatConversion.GetSpellCritFromIntellect(BasicStats.Intellect) * 100f - baseStats.SpellCrit * 100f - character.PriestTalents.FocusedWill).ToString("0.00"),
                                                 (BasicStats.SpellCrit * 100f + character.PriestTalents.MindMelt * 2f).ToString("0.00"),
                                                 (BasicStats.SpellCrit * 100f + character.PriestTalents.MindMelt * 3f).ToString("0.00"),
                                                 (BasicStats.SpellCrit * 100f + character.PriestTalents.HolySpecialization * 1f).ToString("0.00")));

#if RAWR3 || SILVERLIGHT
            float Hit = 100 - (StatConversion.GetSpellMiss(character.Level - BossOpts.Level, false) * 100);
#else
            float Hit = 100 - (StatConversion.GetSpellMiss(-calcOpts.TargetLevel, false) * 100);
#endif
            float  BonusHit   = BasicStats.SpellHit * 100f;
            float  RacialHit  = 0;
            string RacialText = "";
            if (character.Race == CharacterRace.Draenei)
            {
                RacialHit  = 1;
                RacialText = "1% from Draenei Racial\r\n";
                if (!character.ActiveBuffsContains("Heroic Presence"))
                {
                    BonusHit += 1;
                }
            }
            float DebuffHit = character.PriestTalents.Misery * 1f;
            if (character.ActiveBuffsConflictingBuffContains("Spell Hit Chance Taken"))
            {
                DebuffHit = 3f;
            }
            else
            {
                BonusHit += DebuffHit;
            }

            float RHitRating     = 0.01f / StatConversion.GetSpellHitFromRating(1);
            float ShadowFocusHit = character.PriestTalents.ShadowFocus * 1f;
            float HitShadow      = Hit + BonusHit + ShadowFocusHit;
            float HitHoly        = Hit + BonusHit;
            dictValues.Add("Hit", string.Format("{0}%*{1}% from {2} Hit Rating\r\n{3}% from Buffs\r\n{4}% from {5} points in Misery\r\n{6}% from {7} points in Shadow Focus\r\n{8}{9}% Hit with Shadow spells, {10}\r\n{11}% Hit with Holy spells, {12}",
                                                BonusHit.ToString("0.00"),
                                                (StatConversion.GetSpellHitFromRating(BasicStats.HitRating) * 100f).ToString("0.00"), BasicStats.HitRating,
                                                (BonusHit - StatConversion.GetSpellHitFromRating(BasicStats.HitRating) * 100f - RacialHit - DebuffHit).ToString("0.00"),
                                                DebuffHit, character.PriestTalents.Misery,
                                                ShadowFocusHit, character.PriestTalents.ShadowFocus,
                                                RacialText,
                                                HitShadow.ToString("0.00"), (HitShadow > 100f) ? string.Format("{0} hit rating above cap", Math.Floor((HitShadow - 100f) * RHitRating)) : string.Format("{0} hit rating below cap", Math.Ceiling((100f - HitShadow) * RHitRating)),
                                                HitHoly.ToString("0.00"), (HitHoly > 100f) ? string.Format("{0} hit rating above cap", Math.Floor((HitHoly - 100f) * RHitRating)) : string.Format("{0} hit rating below cap", Math.Ceiling((100f - HitHoly) * RHitRating))));

            dictValues.Add("Haste", string.Format("{0}%*{1}% from {2} Haste rating\r\n{3}% ({6}) points in Enlightenment\r\n{4}% from Buffs\r\n{5}s Global Cooldown",
                                                  (BasicStats.SpellHaste * 100f).ToString("0.00"), (StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating) * 100f).ToString("0.00"), BasicStats.HasteRating.ToString(), (character.PriestTalents.Enlightenment * 2).ToString("0"), (((1f + BasicStats.SpellHaste) / (1f + StatConversion.GetSpellHasteFromRating(BasicStats.HasteRating)) / (1f + character.PriestTalents.Enlightenment * 0.02f) - 1f) * 100f).ToString("0.00"), Math.Max(1.0f, 1.5f / (1 + BasicStats.SpellHaste)).ToString("0.00"), character.PriestTalents.Enlightenment));
            dictValues.Add("Armor", string.Format("{0}*{1}% Damage Reduction.",
                                                  (BasicStats.Armor + BasicStats.BonusArmor).ToString("0"),
                                                  (StatConversion.GetArmorDamageReduction(80, (BasicStats.Armor + BasicStats.BonusArmor), 0f, 0f, 0f) * 100f).ToString("0.00")));

            float[] Resistances =
            {
                0,
                BasicStats.ArcaneResistance + BasicStats.ArcaneResistanceBuff,
                BasicStats.FireResistance + BasicStats.FireResistanceBuff,
                BasicStats.FrostResistance + BasicStats.FrostResistanceBuff,
                BasicStats.NatureResistance + BasicStats.NatureResistanceBuff,
                BasicStats.ShadowResistance + BasicStats.ShadowResistanceBuff,
            };

            string[] ResistanceNames =
            {
                "None",
                "Arcane",
                "Fire",
                "Frost",
                "Nature",
                "Shadow",
            };

            string ResistanceString = "*Resistances:";

            float MaxResist      = Resistances[0];
            int   MaxResistIndex = 0;
            float AvgResist      = 0f;
            for (int x = 1; x < Resistances.Length; x++)
            {
                AvgResist += Resistances[x];
                if (Resistances[x] > MaxResist)
                {
                    MaxResist      = Resistances[x];
                    MaxResistIndex = x;
                }
                ResistanceString += string.Format("\r\n{0} : {1}", ResistanceNames[x], Resistances[x]);
            }
            AvgResist /= (Resistances.Length - 1);

            if (AvgResist == 0)
            {
                ResistanceString = "None" + ResistanceString;
            }
            else
            {
                string ResistanceName = (MaxResist == AvgResist) ? "All" : ResistanceNames[MaxResistIndex];
                ResistanceString  = string.Format("{0} : {1}", ResistanceName, MaxResist.ToString("0")) + ResistanceString;
                ResistanceString += string.Format("\r\n\r\nResist ({0}):", ResistanceName);
                ResistanceString += string.Format("\r\n{0}", StatConversion.GetResistanceTableString(character.Level + 3, character.Level, MaxResist, 0));
            }

            dictValues.Add("Resistance", ResistanceString);

            SolverBase solver = GetSolver(character, BasicStats);
            solver.Calculate(this);

            dictValues.Add("Rotation", string.Format("{0}*{1}", solver.Name, solver.Rotation));
            if (solver.SpellSimulation != null)
            {
                String s = "Spell Cast List:";
                int    i = 0;
                foreach (Spell spell in solver.SpellSimulation)
                {
                    if (i++ % 10 == 0)
                    {
                        s += "\r\n";
                    }
                    s += ", " + spell.Name;
                }
                s += "\r\n---Repeat---";
                dictValues.Add("Castlist", string.Format("{0}*{1}", solver.SpellSimulation.Count, s));
            }
            else
            {
                dictValues.Add("Castlist", "Empty");
            }
            dictValues.Add("DPS", string.Format("{0}*Damage Pr Second", solver.DPS.ToString("0")));
            //dictValues.Add("SustainDPS", string.Format("{0}*Mana restrained DPS", solver.SustainDPS.ToString("0")));

            float baseMana = BaseStats.GetBaseStats(character).Mana;

            dictValues.Add("SW Pain", new ShadowWordPain(BasicStats, character, baseMana, Ptr).ToString());
            DevouringPlague dp = new DevouringPlague(BasicStats, character, baseMana, Ptr);
            dictValues.Add("Devouring Plague", dp.ToString());
            if (dp.ImprovedDP != null)
            {
                dictValues.Add("Imp. Devouring Plague", dp.ImprovedDP.ToString());
            }
            else
            {
                dictValues.Add("Imp. Devouring Plague", "- *No required talents");
            }
            dictValues.Add("SW Death", new ShadowWordDeath(BasicStats, character, baseMana, Ptr).ToString());
            dictValues.Add("Mind Blast", new MindBlast(BasicStats, character, baseMana, Ptr).ToString());
            dictValues.Add("PW Shield", new PowerWordShield(BasicStats, character, baseMana, Ptr).ToString());

            if (character.PriestTalents.VampiricTouch > 0)
            {
                dictValues.Add("Vampiric Touch", new VampiricTouch(BasicStats, character, baseMana, Ptr).ToString());
            }
            else
            {
                dictValues.Add("Vampiric Touch", "- *No required talents");
            }

            if (character.PriestTalents.MindFlay > 0)
            {
                dictValues.Add("Mind Flay", new MindFlay(BasicStats, character, baseMana, Ptr).ToString());
            }
            else
            {
                dictValues.Add("Mind Flay", "- *No required talents");
            }

            dictValues.Add("Shadowfiend", new Shadowfiend(BasicStats, character, baseMana, Ptr).ToString());

            dictValues.Add("Smite", new Smite(BasicStats, character, baseMana, Ptr).ToString());
            dictValues.Add("Holy Fire", new HolyFire(BasicStats, character, baseMana, Ptr).ToString());
            if (character.PriestTalents.Penance > 0)
            {
                dictValues.Add("Penance", new Penance(BasicStats, character, baseMana, Ptr).ToString());
            }
            else
            {
                dictValues.Add("Penance", "- *No required talents");
            }

            return(dictValues);
        }