Пример #1
0
 public SpecialDamageProcs(Character c, Stats charStats, int levelDelta,
                           List <SpecialEffect> effectsList,
                           Dictionary <Trigger, float> triggerIntervals, Dictionary <Trigger, float> triggerChances,
                           float fightDuration, float armorDmgReduc)
 {
     Char             = c;
     StatS            = charStats;
     LevelDelta       = levelDelta;
     EffectsList      = effectsList;
     TriggerIntervals = triggerIntervals;
     TriggerChances   = triggerChances;
     FightDuration    = fightDuration;
     ArmorDmgReduc    = armorDmgReduc;
     CreateDictionaries();
     if (TriggerIntervals.ContainsKey(Trigger.OffHandHit) || TriggerIntervals.ContainsKey(Trigger.MainHandHit))
     {
         if (c.MainHandEnchant != null)
         {
             Stats.SpecialEffectEnumerator e = c.MainHandEnchant.Stats.SpecialEffects();
             while (e.MoveNext())
             {
                 MainHandEffects.Add(e.Current);
             }
         }
         if (c.MainHand != null && c.MainHand.Item != null)
         {
             Stats.SpecialEffectEnumerator e = c.MainHand.Item.Stats.SpecialEffects();
             while (e.MoveNext())
             {
                 MainHandEffects.Add(e.Current);
             }
         }
         if (c.OffHandEnchant != null)
         {
             Stats.SpecialEffectEnumerator e = c.OffHandEnchant.Stats.SpecialEffects();
             while (e.MoveNext())
             {
                 OffHandEffects.Add(e.Current);
             }
         }
         if (c.OffHand != null && c.OffHand.Item != null)
         {
             Stats.SpecialEffectEnumerator e = c.OffHand.Item.Stats.SpecialEffects();
             while (e.MoveNext())
             {
                 OffHandEffects.Add(e.Current);
             }
         }
     }
 }
Пример #2
0
 public StatsSpecialEffects(Character character, Stats stats, CalculationOptionsEnhance calcOpts)
 {
     _character = character;
     _stats     = stats;
     _cs        = new CombatStats(_character, _stats, calcOpts);
     if (character.MainHandEnchant != null)
     {
         Stats.SpecialEffectEnumerator mhEffects = character.MainHandEnchant.Stats.SpecialEffects();
         if (mhEffects.MoveNext())
         {
             mainHandEnchant = mhEffects.Current;
         }
     }
     if (_character.ShamanTalents.DualWield == 1 && character.OffHandEnchant != null)
     {
         Stats.SpecialEffectEnumerator ohEffects = character.OffHandEnchant.Stats.SpecialEffects();
         if (ohEffects.MoveNext())
         {
             offHandEnchant = ohEffects.Current;
         }
     }
 }
Пример #3
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;
        }