Exemple #1
0
        public float GetExecutePercentage()
        {
            string executeName = CalcOpts.GetActiveRotation().Execute;

            if (executeName == null || executeName == "")
            {
                return(0f);
            }

            Spell execute = GetSpell(executeName);

            if (!execute.IsCastable())
            {
                return(0f);
            }

            if (execute is SoulFire)
            {
                return(CalcOpts.ThirtyFive);
            }
            else
            {
                return(CalcOpts.TwentyFive);
            }
        }
Exemple #2
0
        /// <param name="stats">
        /// This should already have buffStats factored in.
        /// </param>
        public CharacterCalculationsWarlock(Character character, Stats stats, Stats petBuffs)
        {
            Character     = character;
            CalcOpts      = character.CalculationOptions as CalculationOptionsWarlock;
            BossOpts      = character.BossOptions;
            Talents       = character.WarlockTalents;
            Stats         = stats;
            PreProcStats  = Stats.Clone();
            PetBuffs      = petBuffs;
            BaseMana      = BaseStats.GetBaseStats(character).Mana;
            BaseIntellect = BaseStats.GetBaseStats(character).Intellect;
            Spells        = new Dictionary <string, Spell>();
            CastSpells    = new Dictionary <string, Spell>();
            HitChance     = Math.Min(1f, CalcOpts.GetBaseHitRate() / 100f + CalcSpellHit());

            int temp;

            if (character.SetBonusCount.TryGetValue("Shadowflame Regalia", out temp))
            {
                Warlock_T11_2P = (temp >= 2);
                Warlock_T11_4P = (temp >= 4);
            }

            if (!CalcOpts.Pet.Equals("None") && (Demonology || !CalcOpts.Pet.Equals("Felguard")))
            {
                Type type = Type.GetType("Rawr.Warlock." + CalcOpts.Pet);
                Pet = (Pet)Activator.CreateInstance(type, new object[] { this });
            }

            float personalDps = CalcPersonalDps();
            float petDps      = CalcPetDps();

            SubPoints     = new float[] { personalDps, petDps };
            OverallPoints = personalDps + petDps;
        }
Exemple #3
0
 private void SetupSpells(bool execute)
 {
     Priorities = new List <Spell>();
     foreach (string spellName in CalcOpts.GetActiveRotation().GetPrioritiesForCalcs(Talents, execute))
     {
         Spell spell = GetSpell(spellName);
         if (spell.IsCastable() && (!execute || spell.IsCastDuringExecute()))
         {
             Priorities.Add(spell);
             if (!CastSpells.ContainsKey(spellName))
             {
                 CastSpells.Add(spellName, spell);
             }
         }
     }
 }
Exemple #4
0
 public void AddShadowModifiers(SpellModifiers modifiers)
 {
     modifiers.AddMultiplicativeMultiplier(Stats.BonusShadowDamageMultiplier);
     modifiers.AddMultiplicativeMultiplier(Affliction ? .3f : 0f); // Shadow Mastery
     modifiers.AddMultiplicativeMultiplier(Demonology ? .15f : 0f);
     if (Affliction)
     {
         modifiers.AddAdditiveTickMultiplier(.1304f + (CalcMastery() - 8f) * .0163f);
     }
     if (CalcOpts.GetActiveRotation().Contains("Shadow Bolt") || (CalcOpts.GetActiveRotation().Contains("Haunt") && Talents.Haunt > 0))
     {
         // we assume a steady state of 3 stacks
         float[] talentEffects = { 0f, .03f, .04f, .05f };
         modifiers.AddMultiplicativeTickMultiplier(talentEffects[Talents.ShadowEmbrace] * 3f);
     }
     if (CastSpells.ContainsKey("Haunt"))
     {
         modifiers.AddMultiplicativeTickMultiplier(((Haunt)CastSpells["Haunt"]).GetAvgTickBonus());
     }
 }
Exemple #5
0
        private void CalcHasteAndManaProcs()
        {
            float nonProcHaste = StatUtils.CalcSpellHaste(PreProcStats, CalcOpts.PlayerLevel);

            if (CalcOpts.NoProcs)
            {
                WeightedStat staticHaste = new WeightedStat();
                staticHaste.Chance = 1f;
                staticHaste.Value  = nonProcHaste;
                Haste = new List <WeightedStat> {
                    staticHaste
                };
                AvgHaste = nonProcHaste;
                return;
            }

            // the trigger rates are all guestimates at this point, since the
            // real values depend on haste (which obviously has not been
            // finalized yet)
            Dictionary <int, float> periods = new Dictionary <int, float>();
            Dictionary <int, float> chances = new Dictionary <int, float>();
            float corruptionPeriod          = 0f;

            if (CalcOpts.GetActiveRotation().Contains("Corruption"))
            {
                corruptionPeriod = 3.1f / nonProcHaste;
            }
            PopulateTriggers(periods, chances, CalculationsWarlock.AVG_UNHASTED_CAST_TIME / nonProcHaste + CalcOpts.Latency, 1 / 1.5f, corruptionPeriod, 1f);

            // calculate the haste procs
            Haste = new List <WeightedStat>();
            WeightedStat[] percentages = GetUptimes(Stats, periods, chances, s => s.SpellHaste,
                                                    (a, b, c, d, e, f, g, h) => SpecialEffect.GetAverageCombinedUptimeCombinationsMultiplicative(a, b, c, d, e, f, g, h));
            WeightedStat[] ratings = GetUptimes(Stats, periods, chances, s => s.HasteRating,
                                                (a, b, c, d, e, f, g, h) => SpecialEffect.GetAverageCombinedUptimeCombinations(a, b, c, d, e, f, g, h));
            for (int p = percentages.Length, f = 0; --p >= 0;)
            {
                if (percentages[p].Chance == 0)
                {
                    continue;
                }

                for (int r = ratings.Length; --r >= 0; ++f)
                {
                    if (ratings[r].Chance == 0)
                    {
                        continue;
                    }
                    WeightedStat s = new WeightedStat();
                    s.Chance = percentages[p].Chance * ratings[r].Chance;
                    s.Value  = (1 + percentages[p].Value)
                               * (1 + StatUtils.GetSpellHasteFromRating(ratings[r].Value + Stats.HasteRating, CalcOpts.PlayerLevel))
                               * (1 + Stats.SpellHaste);
                    Haste.Add(s);
                    AvgHaste += s.Chance * s.Value;
                }
            }

            // calculate mana procs
            Stats procStats = new Stats();

            foreach (SpecialEffect effect in Stats.SpecialEffects())
            {
                if (!periods.ContainsKey((int)effect.Trigger))
                {
                    continue;
                }

                Stats proc = effect.GetAverageStats(periods[(int)effect.Trigger], chances[(int)effect.Trigger], CalculationsWarlock.AVG_UNHASTED_CAST_TIME, BossOpts.BerserkTimer);
                if (proc.ManaRestore > 0)
                {
                    proc.ManaRestore *= BossOpts.BerserkTimer;
                }
                procStats.Accumulate(proc);
            }
            Stats.Mana        += procStats.Mana;
            Stats.ManaRestore += procStats.ManaRestore;
            Stats.ManaRestoreFromMaxManaPerSecond += procStats.ManaRestoreFromMaxManaPerSecond;
            Stats.Mp5 += procStats.Mp5;
        }
Exemple #6
0
        private float CalcPersonalDps()
        {
            // SP & Crit: lock before pet (both affected by procs)
            // Procs after crit

            if (CalcOpts.GetActiveRotation().GetError() != null)
            {
                return(0f);
            }

            CalcHasteAndManaProcs();
            AvgTimeUsed = Spell.GetTimeUsed(CalculationsWarlock.AVG_UNHASTED_CAST_TIME, 0f, Haste, CalcOpts.Latency);

            float timeRemaining   = BossOpts.BerserkTimer;
            float totalMana       = CalcUsableMana(timeRemaining);
            float maxMana         = StatUtils.CalcMana(PreProcStats, BaseIntellect, CalcOpts.PlayerLevel);
            float manaFromEffects = totalMana - maxMana;
            float manaUsed        = 0f;

            // Calculate NumCasts for each spell

            // execute stage collision delays
            Spell  execute        = null;
            float  executePercent = GetExecutePercentage();
            string executeName    = CalcOpts.GetActiveRotation().Execute;

            if (executePercent > 0)
            {
                execute = GetSpell(executeName);
                SetupSpells(true);
                RecordCollisionDelays(new CastingState(this, execute, executePercent));
            }

            // normal collision delays
            Spell filler = GetSpell(CalcOpts.GetActiveRotation().Filler);

            SetupSpells(false);
            RecordCollisionDelays(new CastingState(this, filler, 1f - executePercent));

            // calc numcasts
            foreach (Spell spell in Priorities)
            {
                float numCasts = spell.GetNumCasts();
                timeRemaining -= numCasts * spell.GetAvgTimeUsed();
                manaUsed      += numCasts * spell.ManaCost;
            }
            LifeTap lifeTap = (LifeTap)GetSpell("Life Tap");

            if (executePercent > 0)
            {
                float executeTime = executePercent * timeRemaining;
                float taps        = lifeTap.AddCastsForRegen(timeRemaining * executePercent, maxMana + (manaFromEffects - manaUsed) * executePercent, execute);
                executeTime -= taps * lifeTap.GetAvgTimeUsed();
                manaUsed    += taps * lifeTap.ManaCost;
                execute.Spam(executeTime);
                timeRemaining -= executeTime;
                manaUsed      += execute.ManaCost * execute.GetNumCasts();
                CastSpells.Add(CalcOpts.GetActiveRotation().Execute, execute);
            }
            timeRemaining -= lifeTap.GetAvgTimeUsed() * lifeTap.AddCastsForRegen(timeRemaining, totalMana - manaUsed, filler);
            filler.Spam(timeRemaining);
            if (!CastSpells.ContainsKey(CalcOpts.GetActiveRotation().Filler))
            {
                CastSpells.Add(CalcOpts.GetActiveRotation().Filler, filler);
            }

            foreach (Spell spell in CastSpells.Values)
            {
                spell.AdjustAfterCastingIsSet();
            }

            // add procs to RawStats
            if (CastSpells.ContainsKey("Curse Of The Elements"))
            {
                // If the raid is already providing this debuff, the curse will
                // not actually end up casting, so this will not double-count
                // the debuff.
                Stats.BonusFireDamageMultiplier
                                      = Stats.BonusShadowDamageMultiplier
                                      = Stats.BonusHolyDamageMultiplier
                                      = Stats.BonusFrostDamageMultiplier
                                      = Stats.BonusNatureDamageMultiplier
                                      = PetBuffs.BonusFireDamageMultiplier
                                      = PetBuffs.BonusShadowDamageMultiplier
                                      = PetBuffs.BonusHolyDamageMultiplier
                                      = PetBuffs.BonusFrostDamageMultiplier
                                      = PetBuffs.BonusNatureDamageMultiplier
                                      = .08f;
            }
            float critBuff = CalcAddedCritBuff();

            Stats.SpellCritOnTarget    += critBuff;
            PetBuffs.SpellCritOnTarget += critBuff;

            // create the SpellModifiers object
            SpellModifiers = new SpellModifiers();
            SpellModifiers.AddMultiplicativeMultiplier(Stats.BonusDamageMultiplier);
            SpellModifiers.AddMultiplicativeMultiplier(Talents.DemonicPact * .02f);
            SpellModifiers.AddCritOverallMultiplier(Stats.BonusCritDamageMultiplier);
            if (Talents.Metamorphosis > 0)
            {
                SpellModifiers.AddMultiplicativeMultiplier(
                    GetMetamorphosisBonus());
            }

            Stats critProcs = CalcCritProcs();

            Stats.CritRating        += critProcs.CritRating;
            Stats.SpellCrit         += critProcs.SpellCrit;
            Stats.SpellCritOnTarget += critProcs.SpellCritOnTarget;
            SpellModifiers.AddCritChance(CalcSpellCrit());

            if (Pet != null)
            {
                Pet.CalcStats1();
            }

            float damageDone = CalcRemainingProcs();

            if (Pet != null)
            {
                Pet.CalcStats2();
            }

            // TODO: Mana Feed talent: You gain 4% total mana whenever your demon critically hits with its Basic Attack.
            // (PTR: 16% if your pet is a Felguard or Felhunter)

            // finilize each spell's modifiers.
            // Start with Conflagrate, since pyroclasm depends on its results.
            if (CastSpells.ContainsKey("Conflagrate"))
            {
                CastSpells["Conflagrate"].FinalizeSpellModifiers();
            }
            foreach (Spell spell in CastSpells.Values)
            {
                if (!(spell is Conflagrate))
                {
                    spell.FinalizeSpellModifiers();
                }
            }

            Spell conflagrate = null;
            float spellPower  = CalcSpellPower();

            foreach (KeyValuePair <string, Spell> pair in CastSpells)
            {
                Spell spell = pair.Value;
                if (pair.Key.Equals("Conflagrate"))
                {
                    conflagrate = spell;
                    continue; // save until we're sure immolate is done
                }
                spell.SetDamageStats(spellPower);
                damageDone += spell.GetNumCasts() * spell.AvgDamagePerCast;
            }
            if (conflagrate != null)
            {
                conflagrate.SetDamageStats(spellPower);
                damageDone += conflagrate.GetNumCasts() * conflagrate.AvgDamagePerCast;
            }

            return(damageDone / BossOpts.BerserkTimer);
        }
Exemple #7
0
        /// <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>();

            dictValues.Add("Personal DPS", string.Format("{0:0}", PersonalDps));
            dictValues.Add("Pet DPS", string.Format("{0:0}", PetDps));
            dictValues.Add("Total DPS", string.Format("{0:0}", OverallPoints));

            dictValues.Add("Health", string.Format("{0:0}*{1:0} stamina", CalcHealth(), CalcStamina()));
            dictValues.Add("Mana", string.Format("{0:0}*{1:0} intellect", CalcMana(), CalcIntellect()));

            dictValues.Add("Spell Power", string.Format("{0:0.0}*{1:0.0}\tBefore Procs", CalcSpellPower(), StatUtils.CalcSpellPower(PreProcStats, BaseIntellect, CalcOpts.PlayerLevel)));

            #region Hit Rating
            float onePercentOfHitRating = (1 / StatUtils.GetSpellHitFromRating(1, CalcOpts.PlayerLevel));
            float hitFromRating         = StatUtils.GetSpellHitFromRating(Stats.HitRating, CalcOpts.PlayerLevel);
            float hitFromBuffs          = (CalcSpellHit() - hitFromRating);
            float targetHit             = CalcOpts.GetBaseHitRate() / 100f;
            float totalHit   = targetHit + CalcSpellHit();
            float missChance = totalHit > 1 ? 0 : (1 - totalHit);
            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 Buffs: Racial and/or Spell Hit Chance Taken\r\n\r\n"
                    + "You are {8} hit rating {9} the hard cap [no hit from gear, talents or buffs]\r\n\r\n",
                    Stats.HitRating,
                    totalHit,
                    missChance,
                    targetHit,
                    CalcOpts.TargetLevel,
                    hitFromRating,
                    Stats.HitRating,
                    hitFromBuffs,
                    Math.Ceiling(Math.Abs((totalHit - 1) * onePercentOfHitRating)),
                    (totalHit > 1) ? "above" : "below"));
            #endregion

            dictValues.Add("Crit Chance", string.Format("{0:0.00%}*{1:0.00%}\tBefore Procs", CalcSpellCrit(), StatUtils.CalcSpellCrit(PreProcStats, BaseIntellect, CalcOpts.PlayerLevel)));
            dictValues.Add("Average Haste",
                           string.Format(
                               "{0:0.00}%*"
                               + "{1:0.00}%\tfrom {2:0.0} Haste rating\r\n"
                               + "{3:0.00}%\tfrom Buffs\r\n"
                               + "{4:0.0}ish%\tfrom Procs\r\n"
                               + "\r\n"
                               + "{5:0.00}s\tGlobal Cooldown\r\n",
                               (AvgHaste - 1f) * 100f,
                               StatUtils.GetSpellHasteFromRating(Stats.HasteRating, CalcOpts.PlayerLevel) * 100f,
                               Stats.HasteRating,
                               Stats.SpellHaste * 100f,
                               (AvgHaste - StatUtils.CalcSpellHaste(PreProcStats, CalcOpts.PlayerLevel)) * 100f,
                               Math.Max(1.0f, 1.5f / AvgHaste)));
            dictValues.Add("Mastery", string.Format("{0:0.0}*from {1:0.0} Mastery Rating", CalcMastery(), Stats.MasteryRating));

            if (Pet == null)
            {
                dictValues.Add("Pet Mana", "-");
                dictValues.Add("Basic Melee Damage", "-");
                dictValues.Add("Basic Melee DPS", "-");
                dictValues.Add("Attack Power", "-");
                dictValues.Add("Basic Melee Speed", "-");
                dictValues.Add("Spell Damage", "-");
            }
            else
            {
                dictValues.Add("Pet Mana", string.Format("{0:0.0}", Pet.CalcMana()));
                dictValues.Add("Basic Melee Damage", string.Format("{0:0.0}", Pet.CalcMeleeDamage()));
                dictValues.Add("Basic Melee DPS", string.Format("{0:0.0}", Pet.CalcMeleeDps()));
                dictValues.Add("Attack Power", string.Format("{0:0.0}", Pet.CalcAttackPower()));
                dictValues.Add("Basic Melee Speed", string.Format("{0:0.0}", Pet.CalcMeleeSpeed()));
                dictValues.Add("Spell Damage", string.Format("{0:0.0}", Pet.CalcSpellPower()));
            }

            if (Pet is Felhunter)
            {
                dictValues.Add("Shadow Bite (Fel Hunter)", string.Format("{0:0.0}", Pet.GetSpecialDamage()));
            }
            else
            {
                dictValues.Add("Shadow Bite (Fel Hunter)", "-");
            }

            if (Pet is Imp)
            {
                dictValues.Add("Fire Bolt (Imp)", string.Format("{0:0.0}", Pet.GetSpecialDamage()));
            }
            else
            {
                dictValues.Add("Fire Bolt (Imp)", "-");
            }

            if (Pet is Succubus)
            {
                dictValues.Add("Lash of Pain (Succubus)", string.Format("{0:0.0}", Pet.GetSpecialDamage()));
            }
            else
            {
                dictValues.Add("Lash of Pain (Succubus)", "-");
            }

            if (Pet is Felguard)
            {
                dictValues.Add("Legion Strike (Felguard)", string.Format("{0:0.0}", Pet.GetSpecialDamage()));
            }
            else
            {
                dictValues.Add("Legion Strike (Felguard)", "-");
            }

            // Spell Stats
            foreach (string spellName in Spell.ALL_SPELLS)
            {
                if (CastSpells.ContainsKey(spellName))
                {
                    dictValues.Add(spellName, CastSpells[spellName].GetToolTip());
                }
                else
                {
                    dictValues.Add(spellName, "-");
                }
            }

            return(dictValues);
        }