public void AddShadowModifiers(SpellModifiers modifiers)
 {
     modifiers.AddMultiplicativeMultiplier(
         Stats.BonusShadowDamageMultiplier);
     modifiers.AddAdditiveMultiplier(
         Talents.ShadowMastery * .03f);
     if (Options.GetActiveRotation().Contains("Shadow Bolt") ||
         (Options.GetActiveRotation().Contains("Haunt") &&
          Talents.Haunt > 0))
     {
         modifiers.AddMultiplicativeTickMultiplier(
             Talents.ShadowEmbrace * .01f * 3f);
     }
     if (CastSpells.ContainsKey("Haunt"))
     {
         modifiers.AddMultiplicativeTickMultiplier(
             ((Haunt)CastSpells["Haunt"]).GetAvgTickBonus());
     }
     if (Pet is Succubus)
     {
         float bonus = Talents.MasterDemonologist * .01f;
         modifiers.AddMultiplicativeMultiplier(bonus);
         modifiers.AddCritChance(bonus);
     }
 }
 public void AddFireModifiers(SpellModifiers modifiers)
 {
     modifiers.AddMultiplicativeMultiplier(
         Stats.BonusFireDamageMultiplier);
     modifiers.AddAdditiveMultiplier(Talents.Emberstorm * .03f);
     if (Pet is Imp)
     {
         float bonus = Talents.MasterDemonologist * .01f;
         modifiers.AddMultiplicativeMultiplier(bonus);
         modifiers.AddCritChance(bonus);
     }
 }
Exemple #3
0
 // Kitchen Sink Constructor
 public Spell(
     CharacterCalculationsWarlock mommy,
     MagicSchool magicSchool,
     SpellTree spellTree,
     float percentBaseMana,
     float baseCastTime,
     float cooldown,
     float recastPeriod,
     bool canMiss,
     float avgDirectDamage,
     float directCoefficient,
     float addedDirectMultiplier,
     float baseTickDamage,
     float numTicks,
     float tickCoefficient,
     float addedTickMultiplier,
     bool canTickCrit,
     float bonusCritChance,
     float bonusCritMultiplier)
 {
     Mommy = mommy;
     MagicSchool = magicSchool;
     MySpellTree = spellTree;
     ManaCost = mommy.BaseMana * percentBaseMana;
     BaseCastTime = baseCastTime;
     BaseDamage = avgDirectDamage;
     Cooldown = cooldown;
     RecastPeriod = recastPeriod;
     CanMiss = canMiss;
     DirectCoefficient = directCoefficient;
     BaseTickDamage = baseTickDamage;
     NumTicks = numTicks;
     TickCoefficient = tickCoefficient;
     CanTickCrit = canTickCrit;
     SimulatedStats = new Dictionary<string, SimulatedStat>();
     WarlockTalents talents = mommy.Talents;
     SpellModifiers = new SpellModifiers();
     SpellModifiers.AddMultiplicativeDirectMultiplier(addedDirectMultiplier);
     SpellModifiers.AddMultiplicativeTickMultiplier(addedTickMultiplier);
     SpellModifiers.AddCritChance(bonusCritChance);
     SpellModifiers.AddCritBonusMultiplier(bonusCritMultiplier);
 }
        private float CalcRemainingProcs()
        {
            if (Options.NoProcs)
            {
                return(0f);
            }

            Dictionary <int, float> periods = new Dictionary <int, float>();
            Dictionary <int, float> chances = new Dictionary <int, float>();

            PopulateTriggers(periods, chances);

            float procdDamage = 0f;
            Stats procStats   = new Stats();

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

                Stats effectStats = effect.Stats;
                if (effectStats.ValkyrDamage > 0)
                {
                    SpellModifiers mods = new SpellModifiers();
                    mods.AddCritChance(.05f + Stats.SpellCritOnTarget);
                    mods.AddMultiplicativeMultiplier(
                        Stats.BonusHolyDamageMultiplier);
                    procdDamage
                        += CalcDamageProc(
                               effect,
                               effect.Stats.ValkyrDamage,
                               periods,
                               chances,
                               mods);
                }
                else if (
                    effectStats.ShadowDamage > 0 ||
                    effectStats.FireDamage > 0 ||
                    effectStats.NatureDamage > 0 ||
                    effectStats.HolyDamage > 0 ||
                    effectStats.FrostDamage > 0)
                {
                    SpellModifiers mods = new SpellModifiers();
                    mods.Accumulate(SpellModifiers);
                    if (Options.Imbue.Equals("Grand Firestone"))
                    {
                        mods.AddAdditiveDirectMultiplier(.01f);
                    }
                    if (effectStats.ShadowDamage > 0)
                    {
                        AddShadowModifiers(mods);
                    }
                    else if (effectStats.FireDamage > 0)
                    {
                        AddFireModifiers(mods);
                    }
                    procdDamage
                        += CalcDamageProc(
                               effect,
                               effectStats.ShadowDamage
                               + effectStats.FireDamage
                               + effectStats.NatureDamage
                               + effectStats.HolyDamage
                               + effectStats.FrostDamage,
                               periods,
                               chances,
                               mods);
                }
                else
                {
                    procStats.Accumulate(
                        CalcNormalProc(effect, periods, chances));
                }
            }

            procStats.HasteRating
                                    = procStats.SpellHaste
                                    = procStats.Mana
                                    = procStats.ManaRestore
                                    = procStats.ManaRestoreFromBaseManaPPM
                                    = procStats.ManaRestoreFromMaxManaPerSecond
                                    = procStats.Mp5
                                    = procStats.CritRating
                                    = procStats.SpellCrit
                                    = procStats.SpellCritOnTarget
                                    = procStats.PhysicalCrit
                                    = 0;
            Stats.Accumulate(procStats);

            return(procdDamage);
        }
        private float CalcPersonalDps()
        {
            // SP & Crit: lock before pet (both affected by procs)
            // Procs after crit

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

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

            float timeRemaining   = Options.Duration;
            float totalMana       = CalcUsableMana(timeRemaining);
            float maxMana         = StatUtils.CalcMana(PreProcStats);
            float manaFromEffects = totalMana - maxMana;
            float manaUsed        = 0f;

            #region Calculate NumCasts for each spell

            // execute stage collision delays
            Spell  execute        = null;
            float  executePercent = GetExecutePercentage();
            string executeName    = Options.GetActiveRotation().Execute;
            if (executePercent > 0)
            {
                execute = GetSpell(executeName);
                SetupSpells(true);
                RecordCollisionDelays(
                    new CastingState(this, execute, executePercent));
            }

            // normal collision delays
            Spell filler = GetSpell(Options.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(Options.GetActiveRotation().Execute, execute);
            }
            timeRemaining
                -= lifeTap.GetAvgTimeUsed()
                   * lifeTap.AddCastsForRegen(
                       timeRemaining, totalMana - manaUsed, filler);
            filler.Spam(timeRemaining);
            CastSpells.Add(Options.GetActiveRotation().Filler, filler);

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

            #endregion

            #region Calculate spell modifiers, Part 1

            // 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
                                      = .13f;
            }
            float critBuff = CalcAddedCritBuff();
            Stats.SpellCritOnTarget    += critBuff;
            PetBuffs.SpellCritOnTarget += critBuff;
            Stats.SpellPower           += lifeTap.GetAvgBonusSpellPower();

            // create the SpellModifiers object
            SpellModifiers = new SpellModifiers();
            SpellModifiers.AddMultiplicativeMultiplier(
                Stats.BonusDamageMultiplier);
            SpellModifiers.AddMultiplicativeMultiplier(
                Talents.Malediction * .01f);
            SpellModifiers.AddMultiplicativeMultiplier(
                Talents.DemonicPact * .02f);
            SpellModifiers.AddCritOverallMultiplier(
                Stats.BonusCritMultiplier);
            if (Talents.Metamorphosis > 0)
            {
                SpellModifiers.AddMultiplicativeMultiplier(
                    GetMetamorphosisBonus());
            }
            if (Pet is Felguard)
            {
                SpellModifiers.AddMultiplicativeMultiplier(
                    Talents.MasterDemonologist * .01f);
            }
            Add4pT10(SpellModifiers);

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

            if (Pet != null)
            {
                Pet.CalcStats1();
                Stats.SpellPower
                    += Talents.DemonicKnowledge
                       * .04f
                       * (Pet.CalcStamina() + Pet.CalcIntellect());
            }

            #endregion

            float damageDone = CalcRemainingProcs();

            #region Calculate Spell Modifiers, Part 2

            if (Pet != null)
            {
                Pet.CalcStats2();
                Stats.SpellPower += Pet.ApplyPactProcBenefit();
            }

            // 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();
                }
            }

            #endregion

            #region Calculate damage done for each spell
            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;
            }
            #endregion

            return(damageDone / Options.Duration);
        }
        private float CalcRemainingProcs()
        {
            if (CalcOpts.NoProcs)
            {
                return 0f;
            }

            Dictionary<int, float> periods = new Dictionary<int, float>();
            Dictionary<int, float> chances = new Dictionary<int, float>();
            PopulateTriggers(periods, chances);

            float procdDamage = 0f;
            Stats procStats = new Stats();
            foreach (SpecialEffect effect in Stats.SpecialEffects())
            {
                if (!periods.ContainsKey((int)effect.Trigger))
                {
                    continue;
                }

                Stats effectStats = effect.Stats;
                if (effectStats.HolySummonedDamage > 0)
                {
                    SpellModifiers mods = new SpellModifiers();
                    mods.AddCritChance(.05f + Stats.SpellCritOnTarget);
                    mods.AddMultiplicativeMultiplier(Stats.BonusHolyDamageMultiplier);
                    procdDamage += CalcDamageProc(effect, effect.Stats.HolySummonedDamage, periods, chances, mods);
                }
                else if (
                         effectStats.ShadowDamage > 0
                      || effectStats.FireDamage > 0
                      || effectStats.NatureDamage > 0
                      || effectStats.HolyDamage > 0
                      || effectStats.FrostDamage > 0)
                {
                    SpellModifiers mods = new SpellModifiers();
                    mods.Accumulate(SpellModifiers);

                    if (effectStats.ShadowDamage > 0)
                    {
                        AddShadowModifiers(mods);
                    }
                    else if (effectStats.FireDamage > 0)
                    {
                        AddFireModifiers(mods);
                    }
                    procdDamage += CalcDamageProc(
                            effect,
                              effectStats.ShadowDamage
                            + effectStats.FireDamage
                            + effectStats.NatureDamage
                            + effectStats.HolyDamage
                            + effectStats.FrostDamage,
                            periods,
                            chances,
                            mods);
                }
                else
                {
                    procStats.Accumulate(CalcNormalProc(effect, periods, chances));
                }
            }

            procStats.HasteRating
                = procStats.SpellHaste
                = procStats.Mana
                = procStats.ManaRestore
                = procStats.ManaRestoreFromMaxManaPerSecond
                = procStats.Mp5
                = procStats.CritRating
                = procStats.SpellCrit
                = procStats.SpellCritOnTarget
                = procStats.PhysicalCrit
                = 0;
            Stats.Accumulate(procStats);

            return procdDamage;
        }
        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;
        }