Ejemplo n.º 1
0
        // Calculate damage and casting time for a single, direct-damage spell.
        private void DoMainNuke(DruidTalents talents, CharacterCalculationsMoonkin calcs, ref Spell mainNuke, float spellPower, float spellHit, float spellCrit, float spellHaste)
        {
            float latency        = calcs.Latency;
            int   naturesGrace   = talents.NaturesGrace;
            int   starlightWrath = talents.StarlightWrath;

            float overallDamageModifier = mainNuke.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier);

            overallDamageModifier *= mainNuke.School == SpellSchool.Arcane ? (1 + calcs.BasicStats.BonusArcaneDamageMultiplier) : (1 + calcs.BasicStats.BonusNatureDamageMultiplier);
            overallDamageModifier *= 1 - 0.02f * (calcs.TargetLevel - 80);

            float gcd           = 1.5f / (1.0f + spellHaste);
            float instantCast   = (float)Math.Max(gcd, 1.0f) + latency;
            float ngGCD         = (float)Math.Max(gcd / 1.2f, 1.0f);
            float instantCastNG = ngGCD + latency;

            mainNuke.CastTime = mainNuke.BaseCastTime - 0.1f * starlightWrath;
            float totalCritChance = spellCrit + mainNuke.CriticalChanceModifier;
            float normalCastTime  = (float)Math.Max(mainNuke.CastTime / (1 + spellHaste), instantCast);

            mainNuke.NGCastTime = (float)Math.Max(mainNuke.CastTime / (1 + spellHaste) / (1 + 0.2f * naturesGrace / 3.0f), instantCastNG);
            float NGProcChance = totalCritChance * naturesGrace / 3.0f;
            float NGUptime     = 1.0f - (float)Math.Pow(1.0f - NGProcChance, Math.Floor(3.0f / mainNuke.NGCastTime) + 1.0f);

            mainNuke.CastTime = (1 - NGUptime) * normalCastTime + NGUptime * mainNuke.NGCastTime;
            // Damage calculations
            float damagePerNormalHit = (mainNuke.BaseDamage + mainNuke.SpellDamageModifier * (spellPower + mainNuke.IdolExtraSpellPower)) * overallDamageModifier;
            float damagePerCrit      = damagePerNormalHit * mainNuke.CriticalDamageModifier * (1 + calcs.BasicStats.MoonkinT10CritDot);

            mainNuke.DamagePerHit = (totalCritChance * damagePerCrit + (1 - totalCritChance) * damagePerNormalHit) * spellHit;
        }
Ejemplo n.º 2
0
        private static float DoManaRestoreCalcs(CharacterCalculationsMoonkin calcs, SpellRotation rotation, float hitRate)
        {
            float manaFromOther   = calcs.BasicStats.ManaRestorePerCast * rotation.CastCount;
            float manaFromJoW     = calcs.BasicStats.ManaRestorePerHit * (hitRate * rotation.CastCount);
            float manaFromTrinket = 0.0f;

            // Pendant of the Violet Eye - stacking mp5 buff for 20 sec
            if (calcs.BasicStats.Mp5OnCastFor20SecOnUse2Min > 0)
            {
                float currentTime       = 0.0f;
                float currentMp5        = 21.0f;
                float timeSinceLastCast = 0.0f;
                while (currentTime < 20.0f)
                {
                    manaFromTrinket   += currentMp5 / 5.0f * 2.0f;
                    currentTime       += 2.0f;
                    timeSinceLastCast += 2.0f;
                    if (timeSinceLastCast >= rotation.Duration / rotation.CastCount)
                    {
                        timeSinceLastCast -= rotation.Duration / rotation.CastCount;
                        currentMp5        += 21.0f;
                    }
                }
                manaFromTrinket /= 120.0f;
                manaFromTrinket *= rotation.Duration;
            }
            return(manaFromJoW + manaFromOther + manaFromTrinket);
        }
Ejemplo n.º 3
0
        public float GetSpellHit(CharacterCalculationsMoonkin calcs)
        {
            float baseHit = 1.0f;

            switch (calcs.TargetLevel)
            {
            case 80:
                baseHit -= 0.04f;
                break;

            case 81:
                baseHit -= 0.05f;
                break;

            case 82:
                baseHit -= 0.06f;
                break;

            case 83:
                baseHit -= 0.17f;
                break;

            default:
                baseHit -= 0.17f;
                break;
            }
            baseHit = (float)Math.Min(1.0f, baseHit + calcs.SpellHit);
            return(baseHit);
        }
Ejemplo n.º 4
0
        // Calculate damage and casting time for a single, direct-damage spell.
        public void DoMainNuke(CharacterCalculationsMoonkin calcs, ref Spell mainNuke, float spellPower, float spellHit, float spellCrit, float spellHaste, float naturesGraceUptime, float latency)
        {
            float naturesGraceBonusHaste = 0.15f;

            float overallDamageModifier = mainNuke.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier);

            // Add a check for the higher of the two spell schools, as Starsurge always chooses the higher one
            overallDamageModifier *= mainNuke.School == SpellSchool.Arcane ? (1 + calcs.BasicStats.BonusArcaneDamageMultiplier) :
                                     (mainNuke.School == SpellSchool.Nature ? (1 + calcs.BasicStats.BonusNatureDamageMultiplier) :
                                      (1 + (calcs.BasicStats.BonusArcaneDamageMultiplier > calcs.BasicStats.BonusNatureDamageMultiplier ? calcs.BasicStats.BonusArcaneDamageMultiplier : calcs.BasicStats.BonusNatureDamageMultiplier)));

            float gcd           = 1.5f / (1.0f + spellHaste);
            float ngGcd         = gcd / (1 + naturesGraceBonusHaste);
            float instantCast   = (float)Math.Max(gcd, 1.0f) + latency;
            float instantCastNG = (float)Math.Max(ngGcd, 1.0f) + latency;

            float totalCritChance = spellCrit + mainNuke.CriticalChanceModifier;
            float baseCastTime    = (float)Math.Max(mainNuke.BaseCastTime / (1 + spellHaste), instantCast);
            float ngCastTime      = (float)Math.Max(mainNuke.BaseCastTime / (1 + spellHaste) / (1 + naturesGraceBonusHaste), instantCastNG);

            mainNuke.CastTime = (1 - naturesGraceUptime) * baseCastTime + naturesGraceUptime * ngCastTime;
            // Damage calculations
            float damagePerNormalHit = (mainNuke.BaseDamage + mainNuke.SpellDamageModifier * spellPower) * overallDamageModifier;
            float damagePerCrit      = damagePerNormalHit * mainNuke.CriticalDamageModifier;

            mainNuke.DamagePerHit  = (totalCritChance * damagePerCrit + (1 - totalCritChance) * damagePerNormalHit) * spellHit;
            mainNuke.AverageEnergy = mainNuke.BaseEnergy;
        }
Ejemplo n.º 5
0
        // Calculate damage and casting time for a single, direct-damage spell.
        public void DoMainNuke(CharacterCalculationsMoonkin calcs, ref Spell mainNuke, float spellPower, float spellHit, float spellCrit, float spellHaste, float naturesGraceUptime, float latency)
        {
            float naturesGraceBonusHaste = 0.15f;

            float overallDamageModifier = mainNuke.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier);
            // Add a check for the higher of the two spell schools, as Starsurge always chooses the higher one
            overallDamageModifier *= mainNuke.School == SpellSchool.Arcane ? (1 + calcs.BasicStats.BonusArcaneDamageMultiplier) :
                (mainNuke.School == SpellSchool.Nature ? (1 + calcs.BasicStats.BonusNatureDamageMultiplier) :
                (1 + (calcs.BasicStats.BonusArcaneDamageMultiplier > calcs.BasicStats.BonusNatureDamageMultiplier ? calcs.BasicStats.BonusArcaneDamageMultiplier : calcs.BasicStats.BonusNatureDamageMultiplier)));

            float gcd = 1.5f / (1.0f + spellHaste);
            float ngGcd = gcd / (1 + naturesGraceBonusHaste);
            float instantCast = (float)Math.Max(gcd, 1.0f) + latency;
            float instantCastNG = (float)Math.Max(ngGcd, 1.0f) + latency;

            float totalCritChance = spellCrit + mainNuke.CriticalChanceModifier;
            float baseCastTime = (float)Math.Max(mainNuke.BaseCastTime / (1 + spellHaste), instantCast);
            float ngCastTime = (float)Math.Max(mainNuke.BaseCastTime / (1 + spellHaste) / (1 + naturesGraceBonusHaste), instantCastNG);
            mainNuke.CastTime = (1 - naturesGraceUptime) * baseCastTime + naturesGraceUptime * ngCastTime;
            // Damage calculations
            float damagePerNormalHit = (mainNuke.BaseDamage + mainNuke.SpellDamageModifier * spellPower) * overallDamageModifier;
            float damagePerCrit = damagePerNormalHit * mainNuke.CriticalDamageModifier;
            mainNuke.DamagePerHit = (totalCritChance * damagePerCrit + (1 - totalCritChance) * damagePerNormalHit) * spellHit;
            mainNuke.AverageEnergy = mainNuke.BaseEnergy;
        }
Ejemplo n.º 6
0
 // Create proc effect calculations for proc-based trinkets.
 private void BuildProcList(CharacterCalculationsMoonkin calcs)
 {
     // Implement a new handler for each special effect in the calculations stats
     foreach (SpecialEffect effect in calcs.BasicStats.SpecialEffects())
     {
         procEffects.Add(new ProcEffect(effect));
     }
 }
Ejemplo n.º 7
0
 // Calculate damage and casting time for a damage-over-time effect.
 private void DoDotSpell(DruidTalents talents, CharacterCalculationsMoonkin calcs, ref Spell dotSpell, float spellPower, float spellHit, float spellCrit, float spellHaste)
 {
     if (dotSpell.Name == "MF")
     {
         DoMoonfire(talents, calcs, ref dotSpell, spellPower, spellHit, spellCrit, spellHaste);
     }
     else
     {
         DoInsectSwarm(talents, calcs, ref dotSpell, spellPower, spellHit, spellCrit, spellHaste);
     }
 }
Ejemplo n.º 8
0
        public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem)
        {
            CharacterCalculationsMoonkin calcs = new CharacterCalculationsMoonkin();
            Stats stats = GetCharacterStats(character, additionalItem);

            calcs.BasicStats = stats;

            float hitRatingMultiplier  = 1.0f / CalculationsMoonkin.hitRatingConversionFactor;
            float critRatingMultiplier = 1.0f / CalculationsMoonkin.critRatingConversionFactor;

            calcs.SpellCrit = stats.SpellCritRating * critRatingMultiplier;
            calcs.SpellHit  = stats.SpellHitRating * hitRatingMultiplier;

            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;

            // All spells: Damage +((0.08/0.16/0.25) * Int)
            switch (calcOpts.LunarGuidance)
            {
            case 1:
                stats.SpellDamageFromIntellectPercentage += 0.08f;
                break;

            case 2:
                stats.SpellDamageFromIntellectPercentage += 0.16f;
                break;

            case 3:
                stats.SpellDamageFromIntellectPercentage += 0.25f;
                break;

            default:
                stats.SpellDamageFromIntellectPercentage += 0.0f;
                break;
            }
            calcs.ArcaneDamage = stats.SpellDamageRating + stats.SpellArcaneDamageRating + stats.SpellDamageFromIntellectPercentage * stats.Intellect + stats.SpellDamageFromSpiritPercentage * stats.Spirit;
            calcs.NatureDamage = stats.SpellDamageRating + stats.SpellNatureDamageRating + stats.SpellDamageFromIntellectPercentage * stats.Intellect + stats.SpellDamageFromSpiritPercentage * stats.Spirit;

            calcs.Latency     = calcOpts.Latency;
            calcs.FightLength = calcOpts.FightLength;
            calcs.TargetLevel = calcOpts.TargetLevel;
            calcs.Scryer      = calcOpts.AldorScryer == "Scryer";

            // 2.4 spirit regen
            float baseRegenConstant = 0.00932715221261f;
            float spiritRegen       = 0.001f + baseRegenConstant * (float)Math.Sqrt(calcs.BasicStats.Intellect) * calcs.BasicStats.Spirit;

            calcs.ManaRegen    = spiritRegen + stats.Mp5 / 5f;
            calcs.ManaRegen5SR = spiritRegen * stats.SpellCombatManaRegeneration + stats.Mp5 / 5f;

            // Run the solver to do final calculations
            MoonkinSolver.Solve(character, ref calcs);

            return(calcs);
        }
Ejemplo n.º 9
0
        private float DoMushroomCalcs(CharacterCalculationsMoonkin calcs, float effectiveNatureDamage, float spellHit, float spellCrit)
        {
            float hitDamageModifier  = (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusNatureDamageMultiplier);
            float critDamageModifier = 1.5f * (1 + calcs.BasicStats.BonusCritDamageMultiplier);
            // 845-1022 damage
            float baseDamage    = (845 + 1022) / 2;
            float damagePerHit  = (baseDamage + effectiveNatureDamage * 0.6032f) * hitDamageModifier;
            float damagePerCrit = damagePerHit * critDamageModifier;

            return(spellHit * (damagePerHit * (1 - spellCrit) + damagePerCrit * spellCrit));
        }
Ejemplo n.º 10
0
        // Calculate damage and casting time for a damage-over-time effect.
        public void DoDotSpell(CharacterCalculationsMoonkin calcs, ref Spell dotSpell, float spellPower, float spellHit, float spellCrit, float spellHaste, float naturesGraceUptime, float latency)
        {
            float naturesGraceBonusHaste = 0.15f;

            float schoolMultiplier = dotSpell.School == SpellSchool.Arcane ? calcs.BasicStats.BonusArcaneDamageMultiplier : calcs.BasicStats.BonusNatureDamageMultiplier;

            float overallDamageModifier = dotSpell.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + schoolMultiplier);

            float dotEffectDamageModifier = dotSpell.DotEffect.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + schoolMultiplier);

            float gcd           = 1.5f / (1.0f + spellHaste);
            float ngGcd         = gcd / (1 + naturesGraceBonusHaste);
            float instantCast   = (float)Math.Max(gcd, 1.0f) + latency;
            float instantCastNG = (float)Math.Max(ngGcd, 1.0f) + latency;

            dotSpell.CastTime = naturesGraceUptime * instantCastNG + (1 - naturesGraceUptime) * instantCast;

            // Flatly calculated tick rate
            float baseTickRate = dotSpell.DotEffect.BaseTickLength / (1 + spellHaste);
            float ngTickRate   = baseTickRate / (1 + naturesGraceBonusHaste);

            // Round the tick rate to the nearest millisecond
            baseTickRate = ((int)Math.Floor(baseTickRate * 1000 + 0.5f)) / 1000f;
            ngTickRate   = ((int)Math.Floor(ngTickRate * 1000 + 0.5f)) / 1000f;

            // Round the number of ticks up if > .5, down if <= .5
            int baseTicks = (int)Math.Ceiling((dotSpell.DotEffect.BaseDuration / baseTickRate) - 0.5f);
            int ngTicks   = (int)Math.Ceiling((dotSpell.DotEffect.BaseDuration / ngTickRate) - 0.5f);

            dotSpell.DotEffect.NumberOfTicks = naturesGraceUptime * ngTicks + (1 - naturesGraceUptime) * baseTicks;

            float baseDuration = baseTickRate * baseTicks;
            float ngDuration   = ngTickRate * ngTicks;

            dotSpell.DotEffect.Duration = naturesGraceUptime * ngDuration + (1 - naturesGraceUptime) * baseDuration;

            dotSpell.DotEffect.TickLength = (1 - naturesGraceUptime) * baseTickRate + naturesGraceUptime * ngTickRate;

            float mfDirectDamage  = (dotSpell.BaseDamage + dotSpell.SpellDamageModifier * spellPower) * overallDamageModifier;
            float mfCritDamage    = mfDirectDamage * dotSpell.CriticalDamageModifier;
            float totalCritChance = spellCrit + dotSpell.CriticalChanceModifier;

            dotSpell.DamagePerHit = (totalCritChance * mfCritDamage + (1 - totalCritChance) * mfDirectDamage) * spellHit;
            float normalDamagePerTick = dotSpell.DotEffect.TickDamage + dotSpell.DotEffect.SpellDamageModifierPerTick * spellPower;
            float critDamagePerTick   = normalDamagePerTick * dotSpell.CriticalDamageModifier;
            float damagePerTick       = (totalCritChance * critDamagePerTick + (1 - totalCritChance) * normalDamagePerTick) * dotEffectDamageModifier;

            dotSpell.DotEffect.DamagePerHit = dotSpell.DotEffect.NumberOfTicks * damagePerTick * spellHit;
            dotSpell.AverageEnergy          = dotSpell.BaseEnergy;
        }
Ejemplo n.º 11
0
        // Calculate damage and casting time for the Moonfire effect.
        private void DoMoonfire(DruidTalents talents, CharacterCalculationsMoonkin calcs, ref Spell dotSpell, float spellPower, float spellHit, float spellCrit, float spellHaste)
        {
            float latency = calcs.Latency;

            float naturesGrace = talents.NaturesGrace;

            float overallDamageModifier = dotSpell.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusArcaneDamageMultiplier);

            overallDamageModifier *= 1 - 0.02f * (calcs.TargetLevel - 80);

            float dotEffectDamageModifier = dotSpell.DotEffect.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusArcaneDamageMultiplier);

            dotEffectDamageModifier *= 1 - 0.02f * (calcs.TargetLevel - 80);

            float gcd            = 1.5f / (1.0f + spellHaste);
            float instantCast    = (float)Math.Max(gcd, 1.0f) + latency;
            float ngGCD          = (float)Math.Max(gcd / 1.2f, 1.0f);
            float instantCastNG  = ngGCD + latency;
            float normalCastTime = (float)Math.Max(dotSpell.CastTime / (1 + spellHaste), instantCast);
            float NGCastTime     = (float)Math.Max(dotSpell.CastTime / (1 + spellHaste) / (1 + 0.2f * naturesGrace / 3.0f), instantCastNG);
            float NGProcChance   = spellCrit * naturesGrace / 3.0f;
            float NGUptime       = 1.0f - (float)Math.Pow(1.0f - NGProcChance, Math.Floor(3.0f / normalCastTime) + 1.0f);

            dotSpell.CastTime = (1 - NGUptime) * normalCastTime + NGUptime * NGCastTime;

            float mfDirectDamage  = (dotSpell.BaseDamage + dotSpell.SpellDamageModifier * (spellPower + dotSpell.IdolExtraSpellPower)) * overallDamageModifier;
            float mfCritDamage    = mfDirectDamage * dotSpell.CriticalDamageModifier;
            float totalCritChance = spellCrit + dotSpell.CriticalChanceModifier;

            dotSpell.DamagePerHit = (totalCritChance * mfCritDamage + (1 - totalCritChance) * mfDirectDamage) * spellHit;
            float normalDamagePerTick = dotSpell.DotEffect.TickDamage + dotSpell.DotEffect.SpellDamageModifierPerTick * spellPower;
            float damagePerTick       = 0.0f;

            if (dotSpell.DotEffect.CanCrit)
            {
                float critDamagePerTick = normalDamagePerTick * dotSpell.CriticalDamageModifier;
                damagePerTick = (totalCritChance * critDamagePerTick + (1 - totalCritChance) * normalDamagePerTick) * dotEffectDamageModifier;
            }
            else
            {
                damagePerTick = normalDamagePerTick * dotEffectDamageModifier;
            }
            dotSpell.DotEffect.DamagePerHit = dotSpell.DotEffect.NumberOfTicks * damagePerTick * spellHit;
        }
Ejemplo n.º 12
0
        // Starfall
        private float DoStarfallCalcs(CharacterCalculationsMoonkin calcs, float effectiveArcaneDamage, float spellHit, float spellCrit)
        {
            float hitDamageModifier = (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusArcaneDamageMultiplier);
            // Starfall is affected by Moonfury
            float critDamageModifier  = 1.5f * (1 + calcs.BasicStats.BonusCritDamageMultiplier) + (1.5f * (1 + calcs.BasicStats.BonusCritDamageMultiplier) - 1);
            float baseDamagePerStar   = (370.0f + 428.0f) / 2.0f;
            float mainStarCoefficient = 0.247f;

            float damagePerBigStarHit = (baseDamagePerStar + effectiveArcaneDamage * mainStarCoefficient) * hitDamageModifier;

            float critDamagePerBigStarHit = damagePerBigStarHit * critDamageModifier;

            float averageDamagePerBigStar = spellCrit * critDamagePerBigStarHit + (1 - spellCrit) * damagePerBigStarHit;

            float numberOfStarHits = 10f;

            float avgNumBigStarsHit = spellHit * numberOfStarHits;

            return(avgNumBigStarsHit * averageDamagePerBigStar);
        }
Ejemplo n.º 13
0
        // Modified version of above function specifically for use in calculating Moonkin 4T8 proc.
        public void DoSpecialStarfire(CharacterCalculationsMoonkin calcs, ref Spell mainNuke, float spellPower, float spellHit, float spellCrit, float spellHaste)
        {
            float latency = calcs.Latency;
            float overallDamageModifier = mainNuke.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusArcaneDamageMultiplier);

            overallDamageModifier *= 1 - 0.02f * (calcs.TargetLevel - 80);

            mainNuke.CastTime = mainNuke.BaseCastTime;
            float totalCritChance = spellCrit + mainNuke.CriticalChanceModifier;
            float normalCastTime  = (float)Math.Max(1.0f, mainNuke.CastTime / (1 + spellHaste)) + latency;

            mainNuke.NGCastTime = (float)Math.Max(1.0f, mainNuke.CastTime / (1 + spellHaste) / (1 + 0.2f * Solver.NaturesGrace / 3.0f)) + latency;
            float NGProcChance = totalCritChance * Solver.NaturesGrace / 3.0f;
            float NGUptime     = 1.0f - (float)Math.Pow(1.0f - NGProcChance, Math.Floor(3.0f / mainNuke.NGCastTime) + 1.0f);

            mainNuke.CastTime = (1 - NGUptime) * normalCastTime + NGUptime * mainNuke.NGCastTime;
            // Damage calculations
            float damagePerNormalHit = (mainNuke.BaseDamage + mainNuke.SpellDamageModifier * (spellPower + mainNuke.IdolExtraSpellPower)) * overallDamageModifier;
            float damagePerCrit      = damagePerNormalHit * mainNuke.CriticalDamageModifier * (1 + calcs.BasicStats.MoonkinT10CritDot);

            mainNuke.DamagePerHit = (totalCritChance * damagePerCrit + (1 - totalCritChance) * damagePerNormalHit) * spellHit;
        }
Ejemplo n.º 14
0
        // Now returns damage per cast to allow adjustments for fight length
        private float DoTreeCalcs(CharacterCalculationsMoonkin calcs, int playerLevel, int bossLevel, float effectiveNatureDamage, float treantLifespan)
        {
            float sunderPercent = calcs.BasicStats.TargetArmorReduction;
            float meleeHit      = calcs.SpellHit * (StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel] / StatConversion.GetSpellMiss(playerLevel - bossLevel, false));
            float physicalDamageMultiplierBonus = (1f + calcs.BasicStats.BonusDamageMultiplier) * (1f + calcs.BasicStats.BonusPhysicalDamageMultiplier);
            float physicalDamageMultiplierReduc = (1f - calcs.BasicStats.DamageTakenReductionMultiplier) * (1f - calcs.BasicStats.PhysicalDamageTakenReductionMultiplier);
            // 932 = base AP, 57% spell power scaling
            float attackPower = 932.0f + (float)Math.Floor(0.57f * effectiveNatureDamage);
            // 1.65 s base swing speed
            float baseAttackSpeed = 1.65f;
            float attackSpeed     = baseAttackSpeed / (1 + calcs.BasicStats.PhysicalHaste);
            // 580 = base DPS
            float damagePerHit = (580f + attackPower / 14.0f) * baseAttackSpeed;
            // 5% base crit rate, inherit crit debuffs
            // Remove crit depression, as it doesn't appear to have an effect (unless it's base ~10% crit rate)
            float critRate = 0.05f;
            // White hit glancing rate
            float glancingRate = StatConversion.WHITE_GLANCE_CHANCE_CAP[bossLevel - playerLevel];
            // Hit rate determined by the amount of melee hit, not by spell hit
            float missRate = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel] - meleeHit);
            // Since the trees inherit expertise from their hit, scale their hit rate such that when they are hit capped, they are expertise capped
            float dodgeRate = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[bossLevel - playerLevel] * (missRate / StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel]));
            // Armor damage reduction, including Sunder
            float damageReduction = StatConversion.GetArmorDamageReduction(playerLevel, StatConversion.NPC_ARMOR[bossLevel - playerLevel] * (1f - sunderPercent), 0, 0);

            // Final normal damage per swing
            damagePerHit *= 1.0f - damageReduction;
            damagePerHit *= physicalDamageMultiplierReduc;
            damagePerHit *= physicalDamageMultiplierBonus;
            // Damage per swing, including crits/glances/misses
            // This is a cheesy approximation of a true combat table, but because crit/miss/dodge rates will all be fairly low, I don't need to do the whole thing
            damagePerHit = (critRate * damagePerHit * 2.0f) + (glancingRate * damagePerHit * 0.75f) + ((1 - critRate - glancingRate - missRate - dodgeRate) * damagePerHit);
            // Total damage done in their estimated lifespan
            float damagePerTree = (treantLifespan * 30.0f / attackSpeed) * damagePerHit;

            return(3 * damagePerTree);
        }
Ejemplo n.º 15
0
        // Calculate damage and casting time for the Insect Swarm effect.
        private void DoInsectSwarm(DruidTalents talents, CharacterCalculationsMoonkin calcs, ref Spell dotSpell, float spellPower, float spellHit, float spellCrit, float spellHaste)
        {
            float latency = calcs.Latency;

            float naturesGrace = talents.NaturesGrace;

            float dotEffectDamageModifier = dotSpell.DotEffect.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusNatureDamageMultiplier);

            dotEffectDamageModifier *= 1 - 0.02f * (calcs.TargetLevel - 80);

            float gcd            = 1.5f / (1.0f + spellHaste);
            float instantCast    = (float)Math.Max(gcd, 1.0f) + latency;
            float ngGCD          = (float)Math.Max(gcd / 1.2f, 1.0f);
            float instantCastNG  = ngGCD + latency;
            float normalCastTime = (float)Math.Max(dotSpell.CastTime / (1 + spellHaste), instantCast);
            float NGCastTime     = (float)Math.Max(dotSpell.CastTime / (1 + spellHaste) / (1 + 0.2f * naturesGrace / 3.0f), instantCastNG);
            float NGProcChance   = spellCrit * naturesGrace / 3.0f;
            float NGUptime       = 1.0f - (float)Math.Pow(1.0f - NGProcChance, Math.Floor(3.0f / normalCastTime) + 1.0f);

            dotSpell.CastTime = (1 - NGUptime) * normalCastTime + NGUptime * NGCastTime;
            float damagePerTick = (dotSpell.DotEffect.TickDamage + dotSpell.DotEffect.SpellDamageModifierPerTick * (spellPower + dotSpell.IdolExtraSpellPower)) * dotEffectDamageModifier;

            dotSpell.DotEffect.DamagePerHit = dotSpell.DotEffect.NumberOfTicks * damagePerTick * spellHit;
        }
Ejemplo n.º 16
0
        // Non-rotation-specific mana calculations
        private float GetEffectiveManaPool(Character character, CalculationOptionsMoonkin calcOpts, CharacterCalculationsMoonkin calcs)
        {
            float fightLength = character.BossOptions.BerserkTimer * 60.0f;

            float innervateCooldown = 180;

            // Mana/5 calculations
            float totalManaRegen = calcs.ManaRegen * fightLength;

            // Mana pot calculations
            float manaRestoredByPots = 0.0f;
            foreach (Buff b in character.ActiveBuffs)
            {
                if (b.Stats.ManaRestore > 0)
                {
                    manaRestoredByPots = b.Stats.ManaRestore;
                    break;
                }
            }

            // Innervate calculations
            float innervateDelay = calcOpts.InnervateDelay * 60.0f;
            int numInnervates = (calcOpts.Innervate && fightLength - innervateDelay > 0) ? ((int)(fightLength - innervateDelay) / (int)innervateCooldown + 1) : 0;
            float totalInnervateMana = numInnervates * 0.2f * calcs.BasicStats.Mana;
            totalInnervateMana *= 1 + 0.15f * character.DruidTalents.Dreamstate;

            // Replenishment calculations
            float replenishmentPerTick = calcs.BasicStats.Mana * calcs.BasicStats.ManaRestoreFromMaxManaPerSecond;
            float replenishmentMana = calcOpts.ReplenishmentUptime * replenishmentPerTick * character.BossOptions.BerserkTimer * 60;

            return calcs.BasicStats.Mana + totalInnervateMana + totalManaRegen + manaRestoredByPots + replenishmentMana;
        }
Ejemplo n.º 17
0
        // Perform damage and mana calculations for all spells in the given rotation.  Returns damage done over the total duration.
        public float DamageDone(Character character, CharacterCalculationsMoonkin calcs, float treantLifespan, float spellPower, float spellHit, float spellCrit, float spellHaste, float masteryPoints, float latency)
        {
            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;
            DruidTalents talents = character.DruidTalents;
            Spell        sf      = Solver.Starfire;
            Spell        ss      = Solver.Starsurge;
            Spell        w       = Solver.Wrath;
            Spell        mf      = Solver.Moonfire;
            Spell        iSw     = Solver.InsectSwarm;

            // 4.1: The bug causing the Eclipse buff to be rounded down to the nearest percent has been fixed
            float eclipseBonus = 1 + MoonkinSolver.ECLIPSE_BASE + masteryPoints * 0.02f;

            RotationData.NaturesGraceUptime = (float)GetInterpolatedNGUpime(spellHaste, calcs.BasicStats.BonusWrathEnergy > 0, calcs.BasicStats.T13FourPieceActive, talents.GlyphOfStarfire);

            RotationData.Duration = (float)GetInterpolatedCastTime(calcs.SpellHaste, calcs.BasicStats.BonusWrathEnergy > 0, calcs.BasicStats.T13FourPieceActive, talents.GlyphOfStarfire);

            double[] castDistribution = GetInterpolatedCastTable(calcs.SpellHaste, calcs.BasicStats.BonusWrathEnergy > 0, calcs.BasicStats.T13FourPieceActive, talents.GlyphOfStarfire);

            double percentOfMoonfiresExtended = talents.GlyphOfStarfire ? GetPercentOfMoonfiresExtended(calcs.SpellHaste, calcs.BasicStats.BonusWrathEnergy > 0, calcs.BasicStats.T13FourPieceActive) : 0;

            DoMainNuke(calcs, ref sf, spellPower, spellHit, spellCrit, spellHaste, RotationData.NaturesGraceUptime, latency);
            DoMainNuke(calcs, ref ss, spellPower, spellHit, spellCrit, spellHaste, RotationData.NaturesGraceUptime, latency);
            DoMainNuke(calcs, ref w, spellPower, spellHit, spellCrit, spellHaste, RotationData.NaturesGraceUptime, latency);
            double gcd   = Math.Max(1, 1.5 / (1 + spellHaste)) + latency;
            double ngGcd = Math.Max(1, 1.5 / (1 + spellHaste) / (1 + 0.05 * talents.NaturesGrace)) + latency;

            // Moonfire related local variables
            float mfBaseDur, mfMeanDur, mfMaxDur, mfMeanMaxDur, mfTicks, mfMaxTicks;

            mfBaseDur = mf.DotEffect.BaseDuration;
            mfMaxDur  = mfBaseDur + (talents.GlyphOfStarfire ? 9f : 0f);

            // Determine Nature's Grace uptime against Moonfire
            float mfNGUptime = (float)Math.Min(2 * mfMaxDur / RotationData.Duration, 1);

            DoDotSpell(calcs, ref mf, spellPower, spellHit, spellCrit, spellHaste, mfNGUptime, latency);
            // Insect Swarm never benefits from Nature's Grace
            DoDotSpell(calcs, ref iSw, spellPower, spellHit, spellCrit, spellHaste, 0, latency);

            mfTicks      = mf.DotEffect.NumberOfTicks;
            mfMaxTicks   = mfTicks + (talents.GlyphOfStarfire ? 6 : 0);
            mfMeanDur    = mf.DotEffect.Duration;
            mfMeanMaxDur = mf.DotEffect.Duration + (talents.GlyphOfStarfire ? 6 * mf.DotEffect.TickLength : 0f);

            RotationData.MoonfireAvgCast    = mf.CastTime;
            RotationData.InsectSwarmAvgCast = iSw.CastTime;

            // Break the cast distribution down into its component cast counts
            double wrathCasts                 = castDistribution[1] * RotationData.Duration / w.CastTime;
            double eclipseWrathCasts          = castDistribution[5] * RotationData.Duration / w.CastTime;
            double nonEclipsedWrathPercentage = castDistribution[1] / (castDistribution[1] + castDistribution[5]);
            double eclipsedWrathPercentage    = castDistribution[5] / (castDistribution[1] + castDistribution[5]);

            RotationData.WrathAvgHit    = (float)(nonEclipsedWrathPercentage * w.DamagePerHit + eclipsedWrathPercentage * w.DamagePerHit * eclipseBonus);
            RotationData.WrathAvgEnergy = w.AverageEnergy;
            RotationData.WrathCount     = (float)(wrathCasts + eclipseWrathCasts);
            double starfireCasts                 = castDistribution[0] * RotationData.Duration / sf.CastTime;
            double eclipseStarfireCasts          = castDistribution[4] * RotationData.Duration / sf.CastTime;
            double nonEclipsedStarfirePercentage = castDistribution[0] / (castDistribution[0] + castDistribution[4]);
            double eclipsedStarfirePercentage    = castDistribution[4] / (castDistribution[0] + castDistribution[4]);

            RotationData.StarfireAvgHit    = (float)(nonEclipsedStarfirePercentage * sf.DamagePerHit + eclipsedStarfirePercentage * sf.DamagePerHit * eclipseBonus);
            RotationData.StarfireAvgEnergy = sf.AverageEnergy;
            RotationData.StarfireCount     = (float)(starfireCasts + eclipseStarfireCasts);
            double starsurgeCasts                 = castDistribution[2] * RotationData.Duration / ss.CastTime;
            double eclipseStarsurgeCasts          = castDistribution[6] * RotationData.Duration / ss.CastTime;
            double shootingStarsProcs             = castDistribution[3] * RotationData.Duration / gcd;
            double eclipseShootingStarsProcs      = castDistribution[7] * RotationData.Duration / gcd;
            double allStarsurgePercentage         = castDistribution[2] + castDistribution[6] + castDistribution[3] + castDistribution[7];
            double nonEclipsedStarsurgePercentage = (castDistribution[2] + castDistribution[3]) / allStarsurgePercentage;
            double eclipsedStarsurgePercentage    = (castDistribution[6] + castDistribution[7]) / allStarsurgePercentage;
            double starsurgePercentage            = (castDistribution[2] + castDistribution[6]) / allStarsurgePercentage;
            double shootingStarsPercentage        = (castDistribution[3] + castDistribution[7]) / allStarsurgePercentage;

            RotationData.StarSurgeAvgHit    = (float)(nonEclipsedStarsurgePercentage * ss.DamagePerHit + eclipsedStarsurgePercentage * ss.DamagePerHit * eclipseBonus);
            RotationData.StarSurgeAvgEnergy = ss.AverageEnergy;
            RotationData.StarSurgeCount     = (float)(starsurgeCasts + eclipseStarsurgeCasts + shootingStarsProcs + eclipseShootingStarsProcs);
            double moonfireCasts                 = castDistribution[8] * RotationData.Duration / mf.CastTime;
            double eclipsedMoonfireCasts         = castDistribution[10] * RotationData.Duration / mf.CastTime;
            double nonEclipsedMoonfirePercentage = castDistribution[8] / (castDistribution[8] + castDistribution[10]);
            double eclipsedMoonfirePercentage    = castDistribution[10] / (castDistribution[8] + castDistribution[10]);

            RotationData.MoonfireCasts = (float)(moonfireCasts + eclipsedMoonfireCasts);
            double insectSwarmCasts                 = castDistribution[9] * RotationData.Duration / iSw.CastTime;
            double eclipsedInsectSwarmCasts         = castDistribution[11] * RotationData.Duration / iSw.CastTime;
            double nonEclipsedInsectSwarmPercentage = castDistribution[9] / (castDistribution[9] + castDistribution[11]);
            double eclipsedInsectSwarmPercentage    = castDistribution[11] / (castDistribution[9] + castDistribution[11]);

            RotationData.InsectSwarmCasts = (float)(insectSwarmCasts + eclipsedInsectSwarmCasts);

            double unextendedMoonfireAverage = nonEclipsedMoonfirePercentage * (mf.DamagePerHit + mf.DotEffect.DamagePerHit) +
                                               eclipsedMoonfirePercentage * (mf.DamagePerHit + mf.DotEffect.DamagePerHit) * eclipseBonus;
            double mfExtendedDotDamage     = mfMaxTicks * (mf.DotEffect.DamagePerHit / mf.DotEffect.NumberOfTicks);
            double extendedMoonfireAverage = nonEclipsedMoonfirePercentage * (mf.DamagePerHit + mfExtendedDotDamage) +
                                             eclipsedMoonfirePercentage * (mf.DamagePerHit + mfExtendedDotDamage) * eclipseBonus;

            RotationData.MoonfireTicks    = (float)(percentOfMoonfiresExtended * mfMaxTicks + (1 - percentOfMoonfiresExtended) * mfTicks);
            RotationData.MoonfireDuration = (float)(percentOfMoonfiresExtended * mfMeanDur + (1 - percentOfMoonfiresExtended) * mfMeanMaxDur);
            RotationData.MoonfireAvgHit   = (float)(percentOfMoonfiresExtended * extendedMoonfireAverage + (1 - percentOfMoonfiresExtended) * unextendedMoonfireAverage);

            RotationData.InsectSwarmTicks    = RotationData.InsectSwarmCasts * iSw.DotEffect.NumberOfTicks;
            RotationData.InsectSwarmDuration = iSw.DotEffect.Duration;
            RotationData.InsectSwarmAvgHit   = (float)(nonEclipsedInsectSwarmPercentage * iSw.DotEffect.DamagePerHit +
                                                       eclipsedInsectSwarmPercentage * iSw.DotEffect.DamagePerHit * eclipseBonus);

            RotationData.StarfireAvgCast = sf.CastTime;
            RotationData.WrathAvgCast    = w.CastTime;

            RotationData.AverageInstantCast = (float)(gcd * (1 - RotationData.NaturesGraceUptime) + ngGcd * RotationData.NaturesGraceUptime);

            RotationData.StarSurgeAvgCast = (float)(starsurgePercentage * ss.CastTime + shootingStarsPercentage * RotationData.AverageInstantCast);

            // Modify the rotation duration to simulate the energy bonus from Dragonwrath procs
            if (calcs.BasicStats.DragonwrathProc > 0)
            {
                float baselineNukeDuration = RotationData.StarfireCount * RotationData.StarfireAvgCast +
                                             RotationData.WrathCount * RotationData.WrathAvgCast +
                                             RotationData.StarSurgeCount * RotationData.StarSurgeAvgCast;
                float dragonwrathNukeDuration = baselineNukeDuration / (1 + MoonkinSolver.DRAGONWRATH_PROC_RATE);
                RotationData.Duration -= (baselineNukeDuration - dragonwrathNukeDuration);
            }

            RotationData.LunarUptime = (float)(castDistribution[4] + 0.5 * castDistribution[6] + 0.5 * castDistribution[7] + 0.5 * castDistribution[10]);
            RotationData.SolarUptime = (float)(castDistribution[5] + 0.5 * castDistribution[6] + 0.5 * castDistribution[7] + 0.5 * castDistribution[10] + castDistribution[11]);

            float starfallReduction = (float)(starsurgeCasts + shootingStarsProcs + eclipseStarsurgeCasts + eclipseShootingStarsProcs) * 5f;
            float starfallCooldown  = (90f - (talents.GlyphOfStarfall ? 30f : 0f)) - (talents.GlyphOfStarsurge ? starfallReduction : 0);
            float starfallRatio     = talents.Starfall == 1 ?
                                      (RotationData.StarfallCastMode == StarfallMode.OnCooldown ? RotationData.AverageInstantCast / (starfallCooldown + RotationData.AverageInstantCast) : 0f)
                : 0f;
            float starfallTime = RotationData.StarfallCastMode == StarfallMode.LunarOnly ? RotationData.AverageInstantCast : 0f;
            float treantRatio  = talents.ForceOfNature == 1 ? RotationData.AverageInstantCast / (180f + RotationData.AverageInstantCast) : 0;

            float starfallBaseDamage = (talents.Starfall > 0 && RotationData.StarfallCastMode == StarfallMode.Unused) ? 0 : DoStarfallCalcs(calcs, spellPower, spellHit, spellCrit);

            starfallBaseDamage *= 1 + (talents.GlyphOfFocus ? 0.1f : 0f);
            // Dragonwrath
            starfallBaseDamage *= 1 + (calcs.BasicStats.DragonwrathProc > 0 ? MoonkinSolver.DRAGONWRATH_PROC_RATE : 0f);
            float starfallEclipseDamage = starfallBaseDamage * eclipseBonus;

            RotationData.TreantDamage = talents.ForceOfNature == 0 ? 0 : DoTreeCalcs(calcs, character.Level, character.BossOptions.Level, spellPower, treantLifespan);
            // T12 2-piece: 2-sec cast, 5192-6035 damage, affected by hit, 15-sec duration
            float T122PieceHitDamage = (5192 + 6035) / 2f * spellHit * (1 + calcs.BasicStats.BonusFireDamageMultiplier);
            // I'm going to assume a 150% crit modifier on the 2T12 proc until I'm told otherwise
            float T122PieceCritDamage = T122PieceHitDamage * 1.5f;
            // Use 2.5% crit rate based on EJ testing
            // Hard-code 4.5 casts/proc based on EJ testing
            float T122PieceBaseDamage = (0.975f * T122PieceHitDamage + 0.025f * T122PieceCritDamage) * 4.5f;

            // Without glyph of Starsurge, you cannot fit a Starfall in every Lunar eclipse.
            // The actual result will be better than 1/2, because you will be able to cast SFall later in each Eclipse as the fight goes on,
            // but you will miss a Lunar proc entirely eventually.
            float starfallCooldownOverlap = starfallCooldown - RotationData.Duration;
            float rotationsToMiss         = starfallCooldownOverlap > 0 ? RotationData.Duration * RotationData.LunarUptime / starfallCooldownOverlap : 0f;
            float starfallFraction        = rotationsToMiss > 0 ? (float)(Math.Ceiling(rotationsToMiss) / (Math.Ceiling(rotationsToMiss) + 1)) : 1f;

            RotationData.StarfallCasts = RotationData.StarfallCastMode == StarfallMode.OnCooldown ? starfallRatio * RotationData.Duration / RotationData.AverageInstantCast
                : (RotationData.StarfallCastMode == StarfallMode.LunarOnly ? starfallFraction : 0f);
            RotationData.TreantCasts   = treantRatio * RotationData.Duration / RotationData.AverageInstantCast;
            RotationData.StarfallStars = 10f;
            if (RotationData.StarfallCastMode == StarfallMode.LunarOnly)
            {
                RotationData.LunarUptime += starfallFraction * RotationData.AverageInstantCast / RotationData.Duration;
            }
            else if (RotationData.StarfallCastMode == StarfallMode.OnCooldown)
            {
                RotationData.SolarUptime *= 1 + starfallRatio;
                RotationData.LunarUptime *= 1 + starfallRatio;
            }

            RotationData.Duration += RotationData.StarfallCasts * RotationData.AverageInstantCast + RotationData.TreantCasts * RotationData.AverageInstantCast;

            RotationData.StarfallDamage = RotationData.StarfallCastMode == StarfallMode.OnCooldown ?
                                          RotationData.LunarUptime * starfallEclipseDamage + (1 - RotationData.LunarUptime) * starfallBaseDamage :
                                          starfallEclipseDamage;

            float moonfireDamage    = RotationData.MoonfireAvgHit * RotationData.MoonfireCasts;
            float insectSwarmDamage = RotationData.InsectSwarmAvgHit * RotationData.InsectSwarmCasts;

            // Calculate total damage done for external cooldowns per rotation
            float starfallDamage  = RotationData.StarfallDamage * RotationData.StarfallCasts;
            float treantDamage    = RotationData.TreantDamage * RotationData.TreantCasts;
            float T122PieceDamage = 0f;

            if (calcs.BasicStats.ContainsSpecialEffect(se => se.Trigger == Trigger.MageNukeCast))
            {
                foreach (SpecialEffect effect in calcs.BasicStats.SpecialEffects(se => se.Trigger == Trigger.MageNukeCast))
                {
                    T122PieceDamage = T122PieceBaseDamage * effect.GetAverageUptime(RotationData.Duration / (RotationData.WrathCount + RotationData.StarfireCount), 1f);
                }
            }

            // Calculate mana cost per cast.
            // Starfall - 35% of base mana
            float starfallManaCost = (int)(0.35f * MoonkinSolver.BaseMana) - calcs.BasicStats.SpellsManaCostReduction - calcs.BasicStats.NatureSpellsManaCostReduction;
            // Force of Nature - 12% of base mana
            float treantManaCost = (int)(0.12f * MoonkinSolver.BaseMana) - calcs.BasicStats.SpellsManaCostReduction - calcs.BasicStats.NatureSpellsManaCostReduction;

            RotationData.CastCount = RotationData.WrathCount + RotationData.StarfireCount + RotationData.StarSurgeCount +
                                     RotationData.MoonfireCasts + RotationData.InsectSwarmCasts + RotationData.StarfallCasts + RotationData.TreantCasts;
            RotationData.DotTicks = RotationData.InsectSwarmTicks + RotationData.MoonfireTicks;
            RotationData.ManaUsed = RotationData.WrathCount * w.BaseManaCost +
                                    RotationData.StarfireCount * sf.BaseManaCost +
                                    RotationData.StarSurgeCount * ss.BaseManaCost +
                                    RotationData.MoonfireCasts * mf.BaseManaCost +
                                    RotationData.InsectSwarmCasts * iSw.BaseManaCost +
                                    RotationData.StarfallCasts * starfallManaCost +
                                    RotationData.TreantCasts * treantManaCost;

            float manaSavingsFromOOC = MoonkinSolver.OOC_PROC_CHANCE * (RotationData.MoonfireCasts / RotationData.CastCount * mf.BaseManaCost) +
                                       MoonkinSolver.OOC_PROC_CHANCE * (RotationData.InsectSwarmCasts / RotationData.CastCount * iSw.BaseManaCost) +
                                       MoonkinSolver.OOC_PROC_CHANCE * (RotationData.StarfireCount / RotationData.CastCount * sf.BaseManaCost) +
                                       MoonkinSolver.OOC_PROC_CHANCE * (RotationData.WrathCount / RotationData.CastCount * w.BaseManaCost) +
                                       MoonkinSolver.OOC_PROC_CHANCE * (RotationData.StarSurgeCount / RotationData.CastCount * ss.BaseManaCost) +
                                       MoonkinSolver.OOC_PROC_CHANCE * (RotationData.StarfallCasts / RotationData.CastCount * starfallManaCost);

            RotationData.ManaUsed -= manaSavingsFromOOC;

            RotationData.ManaGained = 2 * MoonkinSolver.EUPHORIA_PERCENT * talents.Euphoria * calcs.BasicStats.Mana;

            return(RotationData.WrathAvgHit * RotationData.WrathCount +
                   RotationData.StarfireAvgHit * RotationData.StarfireCount +
                   RotationData.StarSurgeAvgHit * RotationData.StarSurgeCount +
                   moonfireDamage + insectSwarmDamage + treantDamage + starfallDamage + T122PieceDamage);
        }
Ejemplo n.º 18
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;
        }
Ejemplo n.º 19
0
        /*private void RecreateRotations()
        {
            rotations[0] = new SpellRotation() { RotationData = new RotationData() { Name = "None" } };
            for (int mfMode = 0; mfMode < 2; ++mfMode)
            {
                for (int isMode = 0; isMode < 2; ++isMode)
                {
                    for (int sfMode = 0; sfMode < 3; ++sfMode)
                    {
                        for (int wmMode = 0; wmMode < 3; ++wmMode)
                        {
                            int index = 1 + (wmMode + 3 * sfMode + 9 * isMode + 18 * mfMode);
                            DotMode mfModeEnum = (DotMode)mfMode;
                            DotMode isModeEnum = (DotMode)isMode;
                            StarfallMode sfModeEnum = (StarfallMode)sfMode;
                            MushroomMode wmModeEnum = (MushroomMode)wmMode;
                            string name = String.Format("MF {0} IS {1} SF {2} WM {3}",
                                mfModeEnum.ToString(),
                                isModeEnum.ToString(),
                                sfModeEnum.ToString(),
                                wmModeEnum.ToString());
                            rotations[index] = new SpellRotation()
                            {
                                RotationData = new RotationData()
                                {
                                    Name = name,
                                    MoonfireRefreshMode = mfModeEnum,
                                    InsectSwarmRefreshMode = isModeEnum,
                                    StarfallCastMode = sfModeEnum,
                                    WildMushroomCastMode = wmModeEnum
                                }
                            };
                        }
                    }
                }
            }
        }*/

        // Add talented effects to the spells
        private void UpdateSpells(Character character, ref CharacterCalculationsMoonkin calcs)
        {
            DruidTalents talents = character.DruidTalents;
            StatsMoonkin stats = calcs.BasicStats;

            switch (talents.StarlightWrath)
            {
                case 1:
                    Starfire.BaseCastTime -= 0.15f;
                    Wrath.BaseCastTime -= 0.15f;
                    break;
                case 2:
                    Starfire.BaseCastTime -= 0.25f;
                    Wrath.BaseCastTime -= 0.25f;
                    break;
                case 3:
                    Starfire.BaseCastTime -= 0.5f;
                    Wrath.BaseCastTime -= 0.5f;
                    break;
                default:
                    break;
            }

            float moonfireDotGlyph = talents.GlyphOfMoonfire ? 0.2f : 0.0f;
            float insectSwarmGlyph = talents.GlyphOfInsectSwarm ? 0.3f : 0.0f;
            // Add spell-specific damage
            // Moonfire, Insect Swarm: glyphs
            Moonfire.DotEffect.AllDamageModifier += moonfireDotGlyph;
            InsectSwarm.DotEffect.AllDamageModifier += insectSwarmGlyph;
            // Moonfire: Direct damage +(0.03 * Blessing of the Grove)
            Moonfire.AllDamageModifier += 0.03f * talents.BlessingOfTheGrove;
            // Moonfire, Insect Swarm: +2/4/6 seconds for Genesis
            Moonfire.DotEffect.BaseDuration += 2f * talents.Genesis;
            InsectSwarm.DotEffect.BaseDuration += 2f * talents.Genesis;
            // Wrath: 10% for glyph
            Wrath.AllDamageModifier += (talents.GlyphOfWrath ? 0.1f : 0f);

            // Add spell-specific critical strike damage
            // Burning Shadowspirit Diamond
            float baseCritMultiplier = 1.5f * (1 + stats.BonusCritDamageMultiplier);
            float moonfuryMultiplier = baseCritMultiplier + (baseCritMultiplier - 1);
            Starfire.CriticalDamageModifier = Wrath.CriticalDamageModifier = Moonfire.CriticalDamageModifier = InsectSwarm.CriticalDamageModifier = moonfuryMultiplier;
            Starsurge.CriticalDamageModifier = moonfuryMultiplier;

            // Reduce spell-specific mana costs
            // Shard of Woe (Mana cost -405)
            Starfire.BaseManaCost -= calcs.BasicStats.SpellsManaCostReduction;
            Moonfire.BaseManaCost -= calcs.BasicStats.SpellsManaCostReduction;
            Wrath.BaseManaCost -= calcs.BasicStats.SpellsManaCostReduction + calcs.BasicStats.NatureSpellsManaCostReduction;
            InsectSwarm.BaseManaCost -= calcs.BasicStats.SpellsManaCostReduction + calcs.BasicStats.NatureSpellsManaCostReduction;
            Starsurge.BaseManaCost -= calcs.BasicStats.SpellsManaCostReduction;
            // All spells: Mana cost -(0.03 * Moonglow)
            Starfire.BaseManaCost *= 1.0f - (0.03f * talents.Moonglow);
            Moonfire.BaseManaCost *= 1.0f - (0.03f * talents.Moonglow);
            Wrath.BaseManaCost *= 1.0f - (0.03f * talents.Moonglow);
            InsectSwarm.BaseManaCost *= 1.0f - (0.03f * talents.Moonglow);
            Starsurge.BaseManaCost *= 1.0f - (0.03f * talents.Moonglow);

            // Add set bonuses
            Moonfire.CriticalChanceModifier += stats.BonusCritChanceMoonfire;
            InsectSwarm.CriticalChanceModifier += stats.BonusCritChanceInsectSwarm;
            Starfire.AllDamageModifier *= 1 + stats.BonusNukeDamageModifier;
            Wrath.AllDamageModifier *= 1 + stats.BonusNukeDamageModifier;
            Starsurge.AllDamageModifier *= 1 + stats.BonusNukeDamageModifier;

            // PTR changes go here
            /*if (((CalculationOptionsMoonkin)character.CalculationOptions).PTRMode)
            {
                MoonkinSolver.DRAGONWRATH_PROC_RATE = 0.0675f;
            }
            else
            {
                MoonkinSolver.DRAGONWRATH_PROC_RATE = 0.11f;
            }*/

            // Dragonwrath, Tarecgosa's Rest: X% chance on damaging spell cast to proc a duplicate version of the spell.
            // If it duplicates a DoT tick, it fires Wrath of Tarecgosa for an equivalent amount of damage.
            // Wrath, Starfire, and Starsurge will duplicate the Eclipse energy gained.
            if (calcs.BasicStats.DragonwrathProc > 0)
            {
                Starfire.AllDamageModifier += MoonkinSolver.DRAGONWRATH_PROC_RATE;
                Wrath.AllDamageModifier += MoonkinSolver.DRAGONWRATH_PROC_RATE;
                Starsurge.AllDamageModifier += MoonkinSolver.DRAGONWRATH_PROC_RATE;
                Moonfire.AllDamageModifier += MoonkinSolver.DRAGONWRATH_PROC_RATE;
                Moonfire.DotEffect.AllDamageModifier += MoonkinSolver.DRAGONWRATH_PROC_RATE;
                InsectSwarm.DotEffect.AllDamageModifier += MoonkinSolver.DRAGONWRATH_PROC_RATE;
            }
        }
Ejemplo n.º 20
0
        // Redo the spell calculations
        private void RecreateSpells(DruidTalents talents, ref CharacterCalculationsMoonkin calcs)
        {
            ResetSpellList();
            if (talents.Eclipse == 0)
            {
                rotations = new List <SpellRotation>(new SpellRotation[]
                {
                    new SpellRotation()
                    {
                        Name       = "MF/SF",
                        SpellsUsed = new List <string>(new string[] { "MF", "SF" })
                    },
                    new SpellRotation()
                    {
                        Name       = "MF/W",
                        SpellsUsed = new List <string>(new string[] { "MF", "W" })
                    },
                    new SpellRotation()
                    {
                        Name       = "IS/SF",
                        SpellsUsed = new List <string>(new string[] { "IS", "SF" })
                    },
                    new SpellRotation()
                    {
                        Name       = "IS/W",
                        SpellsUsed = new List <string>(new string[] { "IS", "W" })
                    },
                    new SpellRotation()
                    {
                        Name       = "IS/MF/SF",
                        SpellsUsed = new List <string>(new string[] { "IS", "MF", "SF" })
                    },
                    new SpellRotation()
                    {
                        Name       = "IS/MF/W",
                        SpellsUsed = new List <string>(new string[] { "IS", "MF", "W" })
                    },
                    new SpellRotation()
                    {
                        Name       = "SF Spam",
                        SpellsUsed = new List <string>(new string[] { "SF" })
                    },
                    new SpellRotation()
                    {
                        Name       = "W Spam",
                        SpellsUsed = new List <string>(new string[] { "W" })
                    }
                });
            }
            else
            {
                rotations = new List <SpellRotation>(new SpellRotation[] {
                    new SpellRotation()
                    {
                        Name       = "W Spam",
                        SpellsUsed = new List <string>(new string[] { "W" })
                    },
                    new SpellRotation()
                    {
                        Name       = "SF Spam",
                        SpellsUsed = new List <string>(new string[] { "SF" })
                    },
                    new SpellRotation()
                    {
                        Name       = "MF/Filler",
                        SpellsUsed = new List <string>(new String[] { "MF", "SF" })
                    },
                    new SpellRotation()
                    {
                        Name       = "IS/Filler",
                        SpellsUsed = new List <string>(new string[] { "IS", "W" })
                    },
                    new SpellRotation()
                    {
                        Name       = "IS/MF/Filler",
                        SpellsUsed = new List <string>(new string[] { "IS", "MF", "SF" })
                    }
                });
            }

            UpdateSpells(talents, ref calcs);
        }
Ejemplo n.º 21
0
        // Non-rotation-specific mana calculations
        private float GetEffectiveManaPool(Character character, CalculationOptionsMoonkin calcOpts, CharacterCalculationsMoonkin calcs)
        {
            float fightLength = calcs.FightLength * 60.0f;

            float innervateCooldown = 360 - calcs.BasicStats.InnervateCooldownReduction;

            // Mana/5 calculations
            float totalManaRegen = calcs.ManaRegen5SR * fightLength;

            // Mana pot calculations
            float manaRestoredByPots = 0.0f;

            foreach (Buff b in character.ActiveBuffs)
            {
                if (b.Stats.ManaRestore > 0)
                {
                    manaRestoredByPots = b.Stats.ManaRestore;
                    break;
                }
            }

            // Innervate calculations
            float innervateDelay     = calcOpts.InnervateDelay * 60.0f;
            int   numInnervates      = (calcOpts.Innervate && fightLength - innervateDelay > 0) ? ((int)(fightLength - innervateDelay) / (int)innervateCooldown + 1) : 0;
            float totalInnervateMana = numInnervates * CalculationsMoonkin.BaseMana * (4.5f + (character.DruidTalents.GlyphOfInnervate ? 0.9f : 0.0f));

            // Replenishment calculations
            float replenishmentPerTick = calcs.BasicStats.Mana * calcs.BasicStats.ManaRestoreFromMaxManaPerSecond;
            float replenishmentMana    = calcOpts.ReplenishmentUptime * replenishmentPerTick * calcs.FightLength * 60;

            return(calcs.BasicStats.Mana + totalInnervateMana + totalManaRegen + manaRestoredByPots + replenishmentMana);
        }
Ejemplo n.º 22
0
        // Add talented effects to the spells
        private void UpdateSpells(DruidTalents talents, ref CharacterCalculationsMoonkin calcs)
        {
            Stats stats = calcs.BasicStats;

            // Add (possibly talented) +spelldmg
            // Starfire: Damage +(0.04 * Wrath of Cenarius)
            // Wrath: Damage +(0.02 * Wrath of Cenarius)
            Wrath.SpellDamageModifier    += 0.02f * talents.WrathOfCenarius;
            Starfire.SpellDamageModifier += 0.04f * talents.WrathOfCenarius;

            // Add spell damage from idols
            //Starfire.IdolExtraSpellPower += stats.StarfireDmg;
            Starfire.BaseDamage += stats.StarfireDmg;
            //Moonfire.IdolExtraSpellPower += stats.MoonfireDmg;
            Moonfire.BaseDamage += stats.MoonfireDmg;
            Wrath.BaseDamage    += stats.WrathDmg;
            //InsectSwarm.IdolExtraSpellPower += stats.InsectSwarmDmg;
            InsectSwarm.DotEffect.TickDamage += stats.InsectSwarmDmg / InsectSwarm.DotEffect.NumberOfTicks;

            float moonfireDDGlyph  = talents.GlyphOfMoonfire ? -0.9f : 0.0f;
            float moonfireDotGlyph = talents.GlyphOfMoonfire ? 0.75f : 0.0f;
            float insectSwarmGlyph = talents.GlyphOfInsectSwarm ? 0.3f : 0.0f;

            // Add spell-specific damage
            // Starfire, Moonfire, Wrath: Damage +(0.03 * Moonfury) (Additive with 4T9?)
            // Moonfire: Damage +(0.05 * Imp Moonfire) (Additive with Moonfury/Genesis/Glyph)
            // Moonfire, Insect Swarm: Dot Damage +(0.01 * Genesis) (Additive with Moonfury/Imp. Moonfire/Glyph/Set bonus)
            Wrath.AllDamageModifier    *= 1 + (float)Math.Floor(talents.Moonfury * 10 / 3.0f) / 100.0f + stats.BonusMoonkinNukeDamage;
            Moonfire.AllDamageModifier *= 1 + (float)Math.Floor(talents.Moonfury * 10 / 3.0f) / 100.0f
                                          + 0.05f * talents.ImprovedMoonfire
                                          + moonfireDDGlyph;
            Moonfire.DotEffect.AllDamageModifier *= 1 + (float)Math.Floor(talents.Moonfury * 10 / 3.0f) / 100.0f
                                                    + 0.01f * talents.Genesis
                                                    + 0.05f * talents.ImprovedMoonfire
                                                    + moonfireDotGlyph;
            Starfire.AllDamageModifier *= 1 + (float)Math.Floor(talents.Moonfury * 10 / 3.0f) / 100.0f + stats.BonusMoonkinNukeDamage;
            InsectSwarm.DotEffect.AllDamageModifier *= 1 + 0.01f * talents.Genesis
                                                       + insectSwarmGlyph
                                                       + stats.BonusInsectSwarmDamage;

            // Moonfire, Insect Swarm: One extra tick (Nature's Splendor)
            Moonfire.DotEffect.Duration    += 3.0f * talents.NaturesSplendor;
            InsectSwarm.DotEffect.Duration += 2.0f * talents.NaturesSplendor;
            // Moonfire: Crit chance +(0.05 * Imp Moonfire)
            Moonfire.CriticalChanceModifier += 0.05f * talents.ImprovedMoonfire;

            // Add spell-specific crit chance
            // Wrath, Starfire: Crit chance +(0.02 * Nature's Majesty)
            Wrath.CriticalChanceModifier    += 0.02f * talents.NaturesMajesty;
            Starfire.CriticalChanceModifier += 0.02f * talents.NaturesMajesty;

            // Add spell-specific critical strike damage
            // Chaotic Skyfire Diamond
            Starfire.CriticalDamageModifier = stats.BonusCritMultiplier > 0 ? 1.5f * (1 + stats.BonusCritMultiplier) : 1.5f;
            Wrath.CriticalDamageModifier    = stats.BonusCritMultiplier > 0 ? 1.5f * (1 + stats.BonusCritMultiplier) : 1.5f;
            Moonfire.CriticalDamageModifier = stats.BonusCritMultiplier > 0 ? 1.5f * (1 + stats.BonusCritMultiplier) : 1.5f;
            // Starfire, Moonfire, Wrath: Crit damage +(0.2 * Vengeance)
            Starfire.CriticalDamageModifier = (Starfire.CriticalDamageModifier - 1.0f) * (1 + 0.2f * talents.Vengeance) + 1.0f;
            Wrath.CriticalDamageModifier    = (Wrath.CriticalDamageModifier - 1.0f) * (1 + 0.2f * talents.Vengeance) + 1.0f;
            Moonfire.CriticalDamageModifier = (Moonfire.CriticalDamageModifier - 1.0f) * (1 + 0.2f * talents.Vengeance) + 1.0f;

            // Reduce spell-specific mana costs
            // Starfire, Moonfire, Wrath: Mana cost -(0.03 * Moonglow)
            Starfire.BaseManaCost *= 1.0f - (0.03f * talents.Moonglow);
            Moonfire.BaseManaCost *= 1.0f - (0.03f * talents.Moonglow);
            Wrath.BaseManaCost    *= 1.0f - (0.03f * talents.Moonglow);

            // Add set bonuses
            // 2T6
            Moonfire.DotEffect.Duration += stats.MoonfireExtension;
            // 4T6
            Starfire.CriticalChanceModifier += stats.StarfireCritChance;
            // 4T7
            Starfire.CriticalChanceModifier += stats.BonusNukeCritChance;
            Wrath.CriticalChanceModifier    += stats.BonusNukeCritChance;
            // 2T9
            Moonfire.DotEffect.CanCrit = stats.MoonfireDotCrit == 1;

            // Nature's Grace
            NaturesGrace = talents.NaturesGrace;
        }
Ejemplo n.º 23
0
 private static void DoTrinketCalcs(CharacterCalculationsMoonkin calcs, SpellRotation rotation, float hitRate, ref float effectiveArcaneDamage, ref float effectiveNatureDamage, ref float effectiveSpellCrit, ref float effectiveSpellHaste)
 {
     // Unseen Moon proc
     if (rotation.HasMoonfire && calcs.BasicStats.UnseenMoonDamageBonus > 0)
     {
         float numberOfProcs    = 0.5f; // 50% proc chance
         float timeBetweenProcs = rotation.Duration / numberOfProcs;
         effectiveArcaneDamage += calcs.BasicStats.UnseenMoonDamageBonus * 10.0f / timeBetweenProcs;
         effectiveNatureDamage += calcs.BasicStats.UnseenMoonDamageBonus * 10.0f / timeBetweenProcs;
     }
     // Ashtongue Talisman of Equilibrium (Moonkin version)
     if (rotation.StarfireCount > 0 && calcs.BasicStats.DruidAshtongueTrinket > 0)
     {
         double starfireCastsIn8Sec = 8.0 / starfire.CastTime;
         double uptime = 1 - Math.Pow((1 - 0.25), starfireCastsIn8Sec);
         effectiveArcaneDamage += calcs.BasicStats.DruidAshtongueTrinket * (float)uptime;
         effectiveNatureDamage += calcs.BasicStats.DruidAshtongueTrinket * (float)uptime;
     }
     // The Lightning Capacitor
     if (calcs.BasicStats.LightningCapacitorProc > 0)
     {
         float specialDamageModifier = (1 + calcs.BasicStats.BonusSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusNatureSpellPowerMultiplier);
         float baseDamage            = (694 + 806) / 2.0f;
         float averageDamage         = hitRate * baseDamage * (1 + 0.5f * calcs.SpellCrit) * specialDamageModifier;
         float timeBetweenProcs      = rotation.Duration / (hitRate * (calcs.SpellCrit + rotation.AverageCritChance) * rotation.CastCount);
         if (timeBetweenProcs < 2.5f)
         {
             timeBetweenProcs = timeBetweenProcs * 3.0f + 2.5f;
         }
         else
         {
             timeBetweenProcs *= 3.0f;
         }
         trinketExtraDPS += averageDamage / timeBetweenProcs;
     }
     // Shatterered Sun Pendant (45s internal CD)
     if (calcs.BasicStats.ShatteredSunAcumenProc > 0)
     {
         if (calcs.Scryer)
         {
             float specialDamageModifier = (1 + calcs.BasicStats.BonusSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusArcaneSpellPowerMultiplier);
             float baseDamage            = (333 + 367) / 2.0f;
             float averageDamage         = hitRate * baseDamage * (1 + 0.5f * calcs.SpellCrit) * specialDamageModifier;
             trinketExtraDPS += averageDamage / 45.0f;
         }
         else
         {
             effectiveArcaneDamage += 120.0f * 10.0f / 45.0f;
             effectiveNatureDamage += 120.0f * 10.0f / 45.0f;
         }
     }
     // Timbal's Focusing Crystal (10% proc on a DoT tick, 15s internal cooldown)
     if (calcs.BasicStats.TimbalsProc > 0 && rotation.TotalDotTicks > 0)
     {
         float specialDamageModifier = (1 + calcs.BasicStats.BonusShadowSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusSpellPowerMultiplier);
         float baseDamage            = (285 + 475) / 2.0f;
         float averageDamage         = hitRate * baseDamage * (1 + 0.5f * calcs.SpellCrit) * specialDamageModifier;
         float timeBetweenProcs      = 1 / (rotation.TotalDotTicks / rotation.Duration * 0.1f) + 15.0f;
         trinketExtraDPS += averageDamage / timeBetweenProcs;
     }
     // Spell damage for 10 seconds on resist
     if (calcs.BasicStats.SpellDamageFor10SecOnResist > 0)
     {
         float procsPerRotation = (1 - hitRate) * rotation.CastCount;
         float timeBetweenProcs = rotation.Duration / procsPerRotation;
         effectiveArcaneDamage += calcs.BasicStats.SpellDamageFor10SecOnResist * 10.0f / timeBetweenProcs;
         effectiveNatureDamage += calcs.BasicStats.SpellDamageFor10SecOnResist * 10.0f / timeBetweenProcs;
     }
     // 5% chance of spell damage on hit, no cooldown.
     if (calcs.BasicStats.SpellDamageFor10SecOnHit_5 > 0)
     {
         float procsPerRotation = 0.05f * hitRate * rotation.CastCount;
         float timeBetweenProcs = rotation.Duration / procsPerRotation;
         effectiveArcaneDamage += calcs.BasicStats.SpellDamageFor10SecOnHit_5 * 10.0f / timeBetweenProcs;
         effectiveNatureDamage += calcs.BasicStats.SpellDamageFor10SecOnHit_5 * 10.0f / timeBetweenProcs;
     }
     // 10% chance of spell damage on hit, 45 second cooldown.
     if (calcs.BasicStats.SpellDamageFor10SecOnHit_10_45 > 0)
     {
         float procsPerRotation = 0.1f * hitRate * rotation.CastCount;
         float timeBetweenProcs = rotation.Duration / procsPerRotation;
         effectiveArcaneDamage += calcs.BasicStats.SpellDamageFor10SecOnHit_10_45 * 10.0f / (45.0f + timeBetweenProcs);
         effectiveNatureDamage += calcs.BasicStats.SpellDamageFor10SecOnHit_10_45 * 10.0f / (45.0f + timeBetweenProcs);
     }
     // 20% chance of spell damage on crit, 45 second cooldown.
     if (calcs.BasicStats.SpellDamageFor10SecOnCrit_20_45 > 0)
     {
         float procsPerRotation = 0.2f * hitRate * (effectiveSpellCrit / CalculationsMoonkin.critRatingConversionFactor) * rotation.CastCount;
         float timeBetweenProcs = rotation.Duration / procsPerRotation;
         effectiveArcaneDamage += calcs.BasicStats.SpellDamageFor10SecOnHit_5 * 10.0f / (45.0f + timeBetweenProcs);
         effectiveNatureDamage += calcs.BasicStats.SpellDamageFor10SecOnHit_5 * 10.0f / (45.0f + timeBetweenProcs);
     }
     // 15% chance of spell haste on cast, 45-second cooldown (Mystical Skyfire Diamond)
     if (calcs.BasicStats.SpellHasteFor6SecOnCast_15_45 > 0)
     {
         float procsPerRotation = 0.15f * rotation.CastCount;
         float timeBetweenProcs = rotation.Duration / procsPerRotation;
         effectiveSpellHaste += calcs.BasicStats.SpellHasteFor6SecOnCast_15_45 * 6.0f / (45.0f + timeBetweenProcs);
     }
     // 10% chance of spell haste on hit, 45-second cooldown (Quagmirran's Eye)
     if (calcs.BasicStats.SpellHasteFor6SecOnHit_10_45 > 0)
     {
         float procsPerRotation = 0.1f * hitRate * rotation.CastCount;
         float timeBetweenProcs = rotation.Duration / procsPerRotation;
         effectiveSpellHaste += calcs.BasicStats.SpellHasteFor6SecOnHit_10_45 * 6.0f / (45.0f + timeBetweenProcs);
     }
     // Haste trinkets
     if (calcs.BasicStats.SpellHasteFor20SecOnUse2Min > 0)
     {
         effectiveSpellHaste += calcs.BasicStats.SpellHasteFor20SecOnUse2Min * 20.0f / 120.0f;
     }
     // Spell damage trinkets
     if (calcs.BasicStats.SpellDamageFor15SecOnUse90Sec > 0)
     {
         effectiveArcaneDamage += calcs.BasicStats.SpellDamageFor15SecOnUse90Sec * 15.0f / 90.0f;
         effectiveNatureDamage += calcs.BasicStats.SpellDamageFor15SecOnUse90Sec * 15.0f / 90.0f;
     }
     if (calcs.BasicStats.SpellDamageFor20SecOnUse2Min > 0)
     {
         effectiveArcaneDamage += calcs.BasicStats.SpellDamageFor20SecOnUse2Min * 20.0f / 120.0f;
         effectiveNatureDamage += calcs.BasicStats.SpellDamageFor20SecOnUse2Min * 20.0f / 120.0f;
     }
 }
Ejemplo n.º 24
0
        private static void UpdateSpells(Character character, ref CharacterCalculationsMoonkin calcs)
        {
            Stats stats = calcs.BasicStats;
            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;

            // Add (possibly talented) +spelldmg
            // Starfire: Damage +(0.04 * Wrath of Cenarius)
            // Wrath: Damage +(0.02 * Wrath of Cenarius)
            wrath.SpellDamageModifier    += 0.02f * calcOpts.WrathofCenarius;
            starfire.SpellDamageModifier += 0.04f * calcOpts.WrathofCenarius;

            // Add spell damage from idols
            starfire.DamagePerHit += stats.StarfireDmg;
            moonfire.DamagePerHit += stats.MoonfireDmg;
            wrath.DamagePerHit    += stats.WrathDmg;

            // Add spell-specific damage
            // Starfire, Moonfire, Wrath: Damage +(0.02 * Moonfury)
            wrath.SpecialDamageModifier    *= 1.0f + (0.02f * calcOpts.Moonfury);
            moonfire.SpecialDamageModifier *= 1.0f + (0.02f * calcOpts.Moonfury);
            starfire.SpecialDamageModifier *= 1.0f + (0.02f * calcOpts.Moonfury);

            // Wrath, Insect Swarm: Nature spell damage multipliers
            wrath.SpecialDamageModifier       *= ((1 + calcs.BasicStats.BonusNatureSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusSpellPowerMultiplier));
            insectSwarm.SpecialDamageModifier *= ((1 + calcs.BasicStats.BonusNatureSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusSpellPowerMultiplier));
            // Starfire, Moonfire: Arcane damage multipliers
            starfire.SpecialDamageModifier *= ((1 + calcs.BasicStats.BonusArcaneSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusSpellPowerMultiplier));
            moonfire.SpecialDamageModifier *= ((1 + calcs.BasicStats.BonusArcaneSpellPowerMultiplier) * (1 + calcs.BasicStats.BonusSpellPowerMultiplier));

            // Level-based partial resistances
            wrath.SpecialDamageModifier    *= 1 - 0.02f * (calcs.TargetLevel - 70);
            starfire.SpecialDamageModifier *= 1 - 0.02f * (calcs.TargetLevel - 70);
            moonfire.SpecialDamageModifier *= 1 - 0.02f * (calcs.TargetLevel - 70);
            // Insect Swarm is a binary spell

            // Add spell-specific crit chance
            // Wrath, Starfire: Crit chance +(0.02 * Focused Starlight)
            wrath.SpecialCriticalModifier    += 0.02f * calcOpts.FocusedStarlight;
            starfire.SpecialCriticalModifier += 0.02f * calcOpts.FocusedStarlight;
            // Moonfire: Damage, Crit chance +(0.05 * Imp Moonfire)
            moonfire.SpecialDamageModifier   *= 1.0f + (0.05f * calcOpts.ImpMoonfire);
            moonfire.SpecialCriticalModifier += 0.05f * calcOpts.ImpMoonfire;

            // Add spell-specific critical strike damage
            // Starfire, Moonfire, Wrath: Crit damage +(0.2 * Vengeance)
            starfire.CriticalHitMultiplier *= 1 + 0.2f * calcOpts.Vengeance;
            moonfire.CriticalHitMultiplier *= 1 + 0.2f * calcOpts.Vengeance;
            wrath.CriticalHitMultiplier    *= 1 + 0.2f * calcOpts.Vengeance;
            // Chaotic Skyfire Diamond
            starfire.CriticalHitMultiplier *= 1.0f + 1.5f / 0.5f * stats.BonusSpellCritMultiplier;
            moonfire.CriticalHitMultiplier *= 1.0f + 1.5f / 0.5f * stats.BonusSpellCritMultiplier;
            wrath.CriticalHitMultiplier    *= 1.0f + 1.5f / 0.5f * stats.BonusSpellCritMultiplier;

            // Reduce spell-specific mana costs
            // Starfire, Moonfire, Wrath: Mana cost -(0.03 * Moonglow)
            starfire.ManaCost *= 1.0f - (0.03f * calcOpts.Moonglow);
            moonfire.ManaCost *= 1.0f - (0.03f * calcOpts.Moonglow);
            wrath.ManaCost    *= 1.0f - (0.03f * calcOpts.Moonglow);

            // Reduce spell-specific cast times
            // Wrath, Starfire: Cast time -(0.1 * Starlight Wrath)
            wrath.CastTime    -= 0.1f * calcOpts.StarlightWrath;
            starfire.CastTime -= 0.1f * calcOpts.StarlightWrath;

            // Add set bonuses
            moonfire.DoT.Duration            += stats.MoonfireExtension;
            starfire.SpecialCriticalModifier += stats.StarfireCritChance;
        }
Ejemplo n.º 25
0
        public static void Solve(Character character, ref CharacterCalculationsMoonkin calcs)
        {
            // Try to reset the cached results dictionary on each call
            cachedResults = new Dictionary <string, RotationData>();
            float effectiveSpellHit            = calcs.BasicStats.SpellHitRating;
            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;
            bool  naturesGrace = calcOpts.NaturesGrace > 0 ? true : false;
            float fightLength  = calcs.FightLength * 60.0f;

            float baseHitRate = 0.83f;

            switch (calcs.TargetLevel)
            {
            case 70:
                baseHitRate = 0.96f;
                break;

            case 71:
                baseHitRate = 0.95f;
                break;

            case 72:
                baseHitRate = 0.94f;
                break;

            case 73:
                baseHitRate = 0.83f;
                break;

            default:
                baseHitRate = 0.83f;
                break;
            }

            if (baseHitRate + effectiveSpellHit / CalculationsMoonkin.hitRatingConversionFactor > 0.99f)
            {
                effectiveSpellHit = CalculationsMoonkin.hitRatingConversionFactor * (0.99f - baseHitRate);
            }

            RecreateSpells(character, ref calcs);

            float maxDPS    = 0.0f;
            float maxRawDPS = 0.0f;

            foreach (SpellRotation rotation in SpellRotations)
            {
                // Reset all parameters to defaults
                Spell.GlobalCooldown = 1.5f;
                float effectiveArcaneDamage = calcs.ArcaneDamage;
                float effectiveNatureDamage = calcs.NatureDamage;
                float effectiveSpellCrit    = calcs.BasicStats.SpellCritRating;
                float effectiveSpellHaste   = calcs.BasicStats.SpellHasteRating;
                float effectiveMana         = GetEffectiveManaPool(character, calcs);

                // Trinkets
                trinketExtraDPS = 0.0f;
                // Do a pre-emptive call to rotation.DPS to get corrected cast times for spells
                rotation.DPS(effectiveArcaneDamage, effectiveNatureDamage, baseHitRate + effectiveSpellHit / CalculationsMoonkin.hitRatingConversionFactor, effectiveSpellCrit / CalculationsMoonkin.critRatingConversionFactor, effectiveSpellHaste / CalculationsMoonkin.hasteRatingConversionFactor, effectiveMana, fightLength, naturesGrace, calcs.BasicStats.StarfireBonusWithDot, calcs.Latency);
                rotation.ResetRotationalVariables();
                DoTrinketCalcs(calcs, rotation, baseHitRate + effectiveSpellHit / CalculationsMoonkin.hitRatingConversionFactor, ref effectiveArcaneDamage, ref effectiveNatureDamage, ref effectiveSpellCrit, ref effectiveSpellHaste);

                // JoW/mana restore procs
                effectiveMana += DoManaRestoreCalcs(calcs, rotation, baseHitRate + effectiveSpellHit / CalculationsMoonkin.hitRatingConversionFactor) * (fightLength / rotation.Duration);

                // Calculate average global cooldown based on effective haste rating (includes trinkets)
                Spell.GlobalCooldown /= 1 + effectiveSpellHaste * (1 / CalculationsMoonkin.hasteRatingConversionFactor);
                // Reset the cast time on Insect Swarm and Moonfire, since this is affected by haste
                insectSwarm.CastTime = Spell.GlobalCooldown;
                moonfire.CastTime    = Spell.GlobalCooldown;
                // Incorporate Nature's Grace with Moonfire into the rotational calculations
                if (naturesGrace && rotation.HasMoonfire && rotation.StarfireCount > 0)
                {
                    float critFromGear = effectiveSpellCrit * (1 / CalculationsMoonkin.critRatingConversionFactor);
                    starfire.CastTime -= ((1 - (rotation.AverageCritChance + critFromGear)) * (moonfire.SpecialCriticalModifier + critFromGear) * 0.5f) / rotation.StarfireCount;
                }

                float currentDPS = rotation.DPS(effectiveArcaneDamage, effectiveNatureDamage, baseHitRate + effectiveSpellHit / CalculationsMoonkin.hitRatingConversionFactor, effectiveSpellCrit / CalculationsMoonkin.critRatingConversionFactor, effectiveSpellHaste / CalculationsMoonkin.hasteRatingConversionFactor, effectiveMana, fightLength, naturesGrace, calcs.BasicStats.StarfireBonusWithDot, calcs.Latency) + trinketExtraDPS;
                // Restore Starfire's cast time because the object is reused
                if (naturesGrace && rotation.HasMoonfire && rotation.StarfireCount > 0)
                {
                    float critFromGear = effectiveSpellCrit * (1 / CalculationsMoonkin.critRatingConversionFactor);
                    starfire.CastTime += ((1 - (rotation.AverageCritChance + critFromGear)) * (moonfire.SpecialCriticalModifier + critFromGear) * 0.5f) / rotation.StarfireCount;
                }
                float currentRawDPS = rotation.RawDPS + trinketExtraDPS;
                if (currentDPS > maxDPS)
                {
                    calcs.SelectedRotation = rotation;
                    maxDPS = currentDPS;
                }
                if (currentRawDPS > maxRawDPS)
                {
                    calcs.MaxDPSRotation = rotation;
                    maxRawDPS            = currentRawDPS;
                }
                cachedResults[rotation.Name] = new RotationData()
                {
                    RawDPS    = currentRawDPS,
                    DPS       = currentDPS,
                    DPM       = rotation.DPM,
                    TimeToOOM = rotation.TimeToOOM
                };
            }
            calcs.SubPoints     = new float[] { maxDPS, maxRawDPS };
            calcs.OverallPoints = calcs.SubPoints[0] + calcs.SubPoints[1];
            calcs.Rotations     = cachedResults;
        }
Ejemplo n.º 26
0
 // Create proc effect calculations for proc-based trinkets.
 private void BuildProcList(CharacterCalculationsMoonkin calcs)
 {
     // Implement a new handler for each special effect in the calculations stats
     foreach (SpecialEffect effect in calcs.BasicStats.SpecialEffects())
     {
         procEffects.Add(new ProcEffect(effect));
     }
 }
Ejemplo n.º 27
0
        // Calculate damage and casting time for a damage-over-time effect.
        public void DoDotSpell(CharacterCalculationsMoonkin calcs, ref Spell dotSpell, float spellPower, float spellHit, float spellCrit, float spellHaste, float naturesGraceUptime, float latency)
        {
            float naturesGraceBonusHaste = 0.15f;

            float schoolMultiplier = dotSpell.School == SpellSchool.Arcane ? calcs.BasicStats.BonusArcaneDamageMultiplier : calcs.BasicStats.BonusNatureDamageMultiplier;

            float overallDamageModifier = dotSpell.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + schoolMultiplier);

            float dotEffectDamageModifier = dotSpell.DotEffect.AllDamageModifier * (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + schoolMultiplier);

            float gcd = 1.5f / (1.0f + spellHaste);
            float ngGcd = gcd / (1 + naturesGraceBonusHaste);
            float instantCast = (float)Math.Max(gcd, 1.0f) + latency;
            float instantCastNG = (float)Math.Max(ngGcd, 1.0f) + latency;
            dotSpell.CastTime = naturesGraceUptime * instantCastNG + (1 - naturesGraceUptime) * instantCast;

            // Flatly calculated tick rate
            float baseTickRate = dotSpell.DotEffect.BaseTickLength / (1 + spellHaste);
            float ngTickRate = baseTickRate / (1 + naturesGraceBonusHaste);

            // Round the tick rate to the nearest millisecond
            baseTickRate = ((int)Math.Floor(baseTickRate * 1000 + 0.5f)) / 1000f;
            ngTickRate = ((int)Math.Floor(ngTickRate * 1000 + 0.5f)) / 1000f;

            // Round the number of ticks up if > .5, down if <= .5
            int baseTicks = (int)Math.Ceiling((dotSpell.DotEffect.BaseDuration / baseTickRate) - 0.5f);
            int ngTicks = (int)Math.Ceiling((dotSpell.DotEffect.BaseDuration / ngTickRate) - 0.5f);

            dotSpell.DotEffect.NumberOfTicks = naturesGraceUptime * ngTicks + (1 - naturesGraceUptime) * baseTicks;

            float baseDuration = baseTickRate * baseTicks;
            float ngDuration = ngTickRate * ngTicks;

            dotSpell.DotEffect.Duration = naturesGraceUptime * ngDuration + (1 - naturesGraceUptime) * baseDuration;

            dotSpell.DotEffect.TickLength = (1 - naturesGraceUptime) * baseTickRate + naturesGraceUptime * ngTickRate;

            float mfDirectDamage = (dotSpell.BaseDamage + dotSpell.SpellDamageModifier * spellPower) * overallDamageModifier;
            float mfCritDamage = mfDirectDamage * dotSpell.CriticalDamageModifier;
            float totalCritChance = spellCrit + dotSpell.CriticalChanceModifier;
            dotSpell.DamagePerHit = (totalCritChance * mfCritDamage + (1 - totalCritChance) * mfDirectDamage) * spellHit;
            float normalDamagePerTick = dotSpell.DotEffect.TickDamage + dotSpell.DotEffect.SpellDamageModifierPerTick * spellPower;
            float critDamagePerTick = normalDamagePerTick * dotSpell.CriticalDamageModifier;
            float damagePerTick = (totalCritChance * critDamagePerTick + (1 - totalCritChance) * normalDamagePerTick) * dotEffectDamageModifier;
            dotSpell.DotEffect.DamagePerHit = dotSpell.DotEffect.NumberOfTicks * damagePerTick * spellHit;
            dotSpell.AverageEnergy = dotSpell.BaseEnergy;
        }
Ejemplo n.º 28
0
        // Perform damage and mana calculations for all spells in the given rotation.  Returns damage done over the total duration.
        public float DamageDone(Character character, CharacterCalculationsMoonkin calcs, float treantLifespan, float spellPower, float spellHit, float spellCrit, float spellHaste, float masteryPoints, float latency)
        {
            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;
            DruidTalents talents = character.DruidTalents;
            Spell sf = Solver.Starfire;
            Spell ss = Solver.Starsurge;
            Spell w = Solver.Wrath;
            Spell mf = Solver.Moonfire;
            Spell iSw = Solver.InsectSwarm;

            // 4.1: The bug causing the Eclipse buff to be rounded down to the nearest percent has been fixed
            float eclipseBonus = 1 + MoonkinSolver.ECLIPSE_BASE + masteryPoints * 0.02f;

            RotationData.NaturesGraceUptime = (float)GetInterpolatedNGUpime(spellHaste, calcs.BasicStats.BonusWrathEnergy > 0, calcs.BasicStats.T13FourPieceActive, talents.GlyphOfStarfire);

            RotationData.Duration = (float)GetInterpolatedCastTime(calcs.SpellHaste, calcs.BasicStats.BonusWrathEnergy > 0, calcs.BasicStats.T13FourPieceActive, talents.GlyphOfStarfire);

            double[] castDistribution = GetInterpolatedCastTable(calcs.SpellHaste, calcs.BasicStats.BonusWrathEnergy > 0, calcs.BasicStats.T13FourPieceActive, talents.GlyphOfStarfire);

            double percentOfMoonfiresExtended = talents.GlyphOfStarfire ? GetPercentOfMoonfiresExtended(calcs.SpellHaste, calcs.BasicStats.BonusWrathEnergy > 0, calcs.BasicStats.T13FourPieceActive) : 0;

            DoMainNuke(calcs, ref sf, spellPower, spellHit, spellCrit, spellHaste, RotationData.NaturesGraceUptime, latency);
            DoMainNuke(calcs, ref ss, spellPower, spellHit, spellCrit, spellHaste, RotationData.NaturesGraceUptime, latency);
            DoMainNuke(calcs, ref w, spellPower, spellHit, spellCrit, spellHaste, RotationData.NaturesGraceUptime, latency);
            double gcd = Math.Max(1, 1.5 / (1 + spellHaste)) + latency;
            double ngGcd = Math.Max(1, 1.5 / (1 + spellHaste) / (1 + 0.05 * talents.NaturesGrace)) + latency;

            // Moonfire related local variables
            float mfBaseDur, mfMeanDur, mfMaxDur, mfMeanMaxDur, mfTicks, mfMaxTicks;
            mfBaseDur = mf.DotEffect.BaseDuration;
            mfMaxDur = mfBaseDur + (talents.GlyphOfStarfire ? 9f : 0f);

            // Determine Nature's Grace uptime against Moonfire
            float mfNGUptime = (float)Math.Min(2 * mfMaxDur / RotationData.Duration, 1);
            DoDotSpell(calcs, ref mf, spellPower, spellHit, spellCrit, spellHaste, mfNGUptime, latency);
            // Insect Swarm never benefits from Nature's Grace
            DoDotSpell(calcs, ref iSw, spellPower, spellHit, spellCrit, spellHaste, 0, latency);

            mfTicks = mf.DotEffect.NumberOfTicks;
            mfMaxTicks = mfTicks + (talents.GlyphOfStarfire ? 6 : 0);
            mfMeanDur = mf.DotEffect.Duration;
            mfMeanMaxDur = mf.DotEffect.Duration + (talents.GlyphOfStarfire ? 6 * mf.DotEffect.TickLength : 0f);

            RotationData.MoonfireAvgCast = mf.CastTime;
            RotationData.InsectSwarmAvgCast = iSw.CastTime;

            // Break the cast distribution down into its component cast counts
            double wrathCasts = castDistribution[1] * RotationData.Duration / w.CastTime;
            double eclipseWrathCasts = castDistribution[5] * RotationData.Duration / w.CastTime;
            double nonEclipsedWrathPercentage = castDistribution[1] / (castDistribution[1] + castDistribution[5]);
            double eclipsedWrathPercentage = castDistribution[5] / (castDistribution[1] + castDistribution[5]);
            RotationData.WrathAvgHit = (float)(nonEclipsedWrathPercentage * w.DamagePerHit + eclipsedWrathPercentage * w.DamagePerHit * eclipseBonus);
            RotationData.WrathAvgEnergy = w.AverageEnergy;
            RotationData.WrathCount = (float)(wrathCasts + eclipseWrathCasts);
            double starfireCasts = castDistribution[0] * RotationData.Duration / sf.CastTime;
            double eclipseStarfireCasts = castDistribution[4] * RotationData.Duration / sf.CastTime;
            double nonEclipsedStarfirePercentage = castDistribution[0] / (castDistribution[0] + castDistribution[4]);
            double eclipsedStarfirePercentage = castDistribution[4] / (castDistribution[0] + castDistribution[4]);
            RotationData.StarfireAvgHit = (float)(nonEclipsedStarfirePercentage * sf.DamagePerHit + eclipsedStarfirePercentage * sf.DamagePerHit * eclipseBonus);
            RotationData.StarfireAvgEnergy = sf.AverageEnergy;
            RotationData.StarfireCount = (float)(starfireCasts + eclipseStarfireCasts);
            double starsurgeCasts = castDistribution[2] * RotationData.Duration / ss.CastTime;
            double eclipseStarsurgeCasts = castDistribution[6] * RotationData.Duration / ss.CastTime;
            double shootingStarsProcs = castDistribution[3] * RotationData.Duration / gcd;
            double eclipseShootingStarsProcs = castDistribution[7] * RotationData.Duration / gcd;
            double allStarsurgePercentage = castDistribution[2] + castDistribution[6] + castDistribution[3] + castDistribution[7];
            double nonEclipsedStarsurgePercentage = (castDistribution[2] + castDistribution[3]) / allStarsurgePercentage;
            double eclipsedStarsurgePercentage = (castDistribution[6] + castDistribution[7]) / allStarsurgePercentage;
            double starsurgePercentage = (castDistribution[2] + castDistribution[6]) / allStarsurgePercentage;
            double shootingStarsPercentage = (castDistribution[3] + castDistribution[7]) / allStarsurgePercentage;
            RotationData.StarSurgeAvgHit = (float)(nonEclipsedStarsurgePercentage * ss.DamagePerHit + eclipsedStarsurgePercentage * ss.DamagePerHit * eclipseBonus);
            RotationData.StarSurgeAvgEnergy = ss.AverageEnergy;
            RotationData.StarSurgeCount = (float)(starsurgeCasts + eclipseStarsurgeCasts + shootingStarsProcs + eclipseShootingStarsProcs);
            double moonfireCasts = castDistribution[8] * RotationData.Duration / mf.CastTime;
            double eclipsedMoonfireCasts = castDistribution[10] * RotationData.Duration / mf.CastTime;
            double nonEclipsedMoonfirePercentage = castDistribution[8] / (castDistribution[8] + castDistribution[10]);
            double eclipsedMoonfirePercentage = castDistribution[10] / (castDistribution[8] + castDistribution[10]);
            RotationData.MoonfireCasts = (float)(moonfireCasts + eclipsedMoonfireCasts);
            double insectSwarmCasts = castDistribution[9] * RotationData.Duration / iSw.CastTime;
            double eclipsedInsectSwarmCasts = castDistribution[11] * RotationData.Duration / iSw.CastTime;
            double nonEclipsedInsectSwarmPercentage = castDistribution[9] / (castDistribution[9] + castDistribution[11]);
            double eclipsedInsectSwarmPercentage = castDistribution[11] / (castDistribution[9] + castDistribution[11]);
            RotationData.InsectSwarmCasts = (float)(insectSwarmCasts + eclipsedInsectSwarmCasts);

            double unextendedMoonfireAverage = nonEclipsedMoonfirePercentage * (mf.DamagePerHit + mf.DotEffect.DamagePerHit) +
                eclipsedMoonfirePercentage * (mf.DamagePerHit + mf.DotEffect.DamagePerHit) * eclipseBonus;
            double mfExtendedDotDamage = mfMaxTicks * (mf.DotEffect.DamagePerHit / mf.DotEffect.NumberOfTicks);
            double extendedMoonfireAverage = nonEclipsedMoonfirePercentage * (mf.DamagePerHit + mfExtendedDotDamage) +
                eclipsedMoonfirePercentage * (mf.DamagePerHit + mfExtendedDotDamage) * eclipseBonus;

            RotationData.MoonfireTicks = (float)(percentOfMoonfiresExtended * mfMaxTicks + (1 - percentOfMoonfiresExtended) * mfTicks);
            RotationData.MoonfireDuration = (float)(percentOfMoonfiresExtended * mfMeanDur + (1 - percentOfMoonfiresExtended) * mfMeanMaxDur);
            RotationData.MoonfireAvgHit = (float)(percentOfMoonfiresExtended * extendedMoonfireAverage + (1 - percentOfMoonfiresExtended) * unextendedMoonfireAverage);

            RotationData.InsectSwarmTicks = RotationData.InsectSwarmCasts * iSw.DotEffect.NumberOfTicks;
            RotationData.InsectSwarmDuration = iSw.DotEffect.Duration;
            RotationData.InsectSwarmAvgHit = (float)(nonEclipsedInsectSwarmPercentage * iSw.DotEffect.DamagePerHit +
                eclipsedInsectSwarmPercentage * iSw.DotEffect.DamagePerHit * eclipseBonus);

            RotationData.StarfireAvgCast = sf.CastTime;
            RotationData.WrathAvgCast = w.CastTime;

            RotationData.AverageInstantCast = (float)(gcd * (1 - RotationData.NaturesGraceUptime) + ngGcd * RotationData.NaturesGraceUptime);

            RotationData.StarSurgeAvgCast = (float)(starsurgePercentage * ss.CastTime + shootingStarsPercentage * RotationData.AverageInstantCast);

            // Modify the rotation duration to simulate the energy bonus from Dragonwrath procs
            if (calcs.BasicStats.DragonwrathProc > 0)
            {
                float baselineNukeDuration = RotationData.StarfireCount * RotationData.StarfireAvgCast +
                    RotationData.WrathCount * RotationData.WrathAvgCast +
                    RotationData.StarSurgeCount * RotationData.StarSurgeAvgCast;
                float dragonwrathNukeDuration = baselineNukeDuration / (1 + MoonkinSolver.DRAGONWRATH_PROC_RATE);
                RotationData.Duration -= (baselineNukeDuration - dragonwrathNukeDuration);
            }

            RotationData.LunarUptime = (float)(castDistribution[4] + 0.5 * castDistribution[6] + 0.5 * castDistribution[7] + 0.5 * castDistribution[10]);
            RotationData.SolarUptime = (float)(castDistribution[5] + 0.5 * castDistribution[6] + 0.5 * castDistribution[7] + 0.5 * castDistribution[10] + castDistribution[11]);

            float starfallReduction = (float)(starsurgeCasts + shootingStarsProcs + eclipseStarsurgeCasts + eclipseShootingStarsProcs) * 5f;
            float starfallCooldown = (90f - (talents.GlyphOfStarfall ? 30f : 0f)) - (talents.GlyphOfStarsurge ? starfallReduction : 0);
            float starfallRatio = talents.Starfall == 1 ?
                (RotationData.StarfallCastMode == StarfallMode.OnCooldown ? RotationData.AverageInstantCast / (starfallCooldown + RotationData.AverageInstantCast) : 0f)
                : 0f;
            float starfallTime = RotationData.StarfallCastMode == StarfallMode.LunarOnly ? RotationData.AverageInstantCast : 0f;
            float treantRatio = talents.ForceOfNature == 1 ? RotationData.AverageInstantCast / (180f + RotationData.AverageInstantCast) : 0;

            float starfallBaseDamage = (talents.Starfall > 0 && RotationData.StarfallCastMode == StarfallMode.Unused) ? 0 : DoStarfallCalcs(calcs, spellPower, spellHit, spellCrit);
            starfallBaseDamage *= 1 + (talents.GlyphOfFocus ? 0.1f : 0f);
            // Dragonwrath
            starfallBaseDamage *= 1 + (calcs.BasicStats.DragonwrathProc > 0 ? MoonkinSolver.DRAGONWRATH_PROC_RATE : 0f);
            float starfallEclipseDamage = starfallBaseDamage * eclipseBonus;
            RotationData.TreantDamage = talents.ForceOfNature == 0 ? 0 : DoTreeCalcs(calcs, character.Level, character.BossOptions.Level, spellPower, treantLifespan);
            // T12 2-piece: 2-sec cast, 5192-6035 damage, affected by hit, 15-sec duration
            float T122PieceHitDamage = (5192 + 6035) / 2f * spellHit * (1 + calcs.BasicStats.BonusFireDamageMultiplier);
            // I'm going to assume a 150% crit modifier on the 2T12 proc until I'm told otherwise
            float T122PieceCritDamage = T122PieceHitDamage * 1.5f;
            // Use 2.5% crit rate based on EJ testing
            // Hard-code 4.5 casts/proc based on EJ testing
            float T122PieceBaseDamage = (0.975f * T122PieceHitDamage + 0.025f * T122PieceCritDamage) * 4.5f;

            // Without glyph of Starsurge, you cannot fit a Starfall in every Lunar eclipse.
            // The actual result will be better than 1/2, because you will be able to cast SFall later in each Eclipse as the fight goes on,
            // but you will miss a Lunar proc entirely eventually.
            float starfallCooldownOverlap = starfallCooldown - RotationData.Duration;
            float rotationsToMiss = starfallCooldownOverlap > 0 ? RotationData.Duration * RotationData.LunarUptime / starfallCooldownOverlap : 0f;
            float starfallFraction = rotationsToMiss > 0 ? (float)(Math.Ceiling(rotationsToMiss) / (Math.Ceiling(rotationsToMiss) + 1)) : 1f;
            RotationData.StarfallCasts = RotationData.StarfallCastMode == StarfallMode.OnCooldown ? starfallRatio * RotationData.Duration / RotationData.AverageInstantCast
                : (RotationData.StarfallCastMode == StarfallMode.LunarOnly ? starfallFraction : 0f);
            RotationData.TreantCasts = treantRatio * RotationData.Duration / RotationData.AverageInstantCast;
            RotationData.StarfallStars = 10f;
            if (RotationData.StarfallCastMode == StarfallMode.LunarOnly)
                RotationData.LunarUptime += starfallFraction * RotationData.AverageInstantCast / RotationData.Duration;
            else if (RotationData.StarfallCastMode == StarfallMode.OnCooldown)
            {
                RotationData.SolarUptime *= 1 + starfallRatio;
                RotationData.LunarUptime *= 1 + starfallRatio;
            }

            RotationData.Duration += RotationData.StarfallCasts * RotationData.AverageInstantCast + RotationData.TreantCasts * RotationData.AverageInstantCast;

            RotationData.StarfallDamage = RotationData.StarfallCastMode == StarfallMode.OnCooldown ?
                RotationData.LunarUptime * starfallEclipseDamage + (1 - RotationData.LunarUptime) * starfallBaseDamage :
                starfallEclipseDamage;

            float moonfireDamage = RotationData.MoonfireAvgHit * RotationData.MoonfireCasts;
            float insectSwarmDamage = RotationData.InsectSwarmAvgHit * RotationData.InsectSwarmCasts;

            // Calculate total damage done for external cooldowns per rotation
            float starfallDamage = RotationData.StarfallDamage * RotationData.StarfallCasts;
            float treantDamage = RotationData.TreantDamage * RotationData.TreantCasts;
            float T122PieceDamage = 0f;
            if (calcs.BasicStats.ContainsSpecialEffect(se => se.Trigger == Trigger.MageNukeCast))
            {
                foreach (SpecialEffect effect in calcs.BasicStats.SpecialEffects(se => se.Trigger == Trigger.MageNukeCast))
                {
                    T122PieceDamage = T122PieceBaseDamage * effect.GetAverageUptime(RotationData.Duration / (RotationData.WrathCount + RotationData.StarfireCount), 1f);
                }
            }

            // Calculate mana cost per cast.
            // Starfall - 35% of base mana
            float starfallManaCost = (int)(0.35f * MoonkinSolver.BaseMana) - calcs.BasicStats.SpellsManaCostReduction - calcs.BasicStats.NatureSpellsManaCostReduction;
            // Force of Nature - 12% of base mana
            float treantManaCost = (int)(0.12f * MoonkinSolver.BaseMana) - calcs.BasicStats.SpellsManaCostReduction - calcs.BasicStats.NatureSpellsManaCostReduction;

            RotationData.CastCount = RotationData.WrathCount + RotationData.StarfireCount + RotationData.StarSurgeCount +
                RotationData.MoonfireCasts + RotationData.InsectSwarmCasts + RotationData.StarfallCasts + RotationData.TreantCasts;
            RotationData.DotTicks = RotationData.InsectSwarmTicks + RotationData.MoonfireTicks;
            RotationData.ManaUsed = RotationData.WrathCount * w.BaseManaCost +
                RotationData.StarfireCount * sf.BaseManaCost +
                RotationData.StarSurgeCount * ss.BaseManaCost +
                RotationData.MoonfireCasts * mf.BaseManaCost +
                RotationData.InsectSwarmCasts * iSw.BaseManaCost +
                RotationData.StarfallCasts * starfallManaCost +
                RotationData.TreantCasts * treantManaCost;

            float manaSavingsFromOOC = MoonkinSolver.OOC_PROC_CHANCE * (RotationData.MoonfireCasts / RotationData.CastCount * mf.BaseManaCost) +
                MoonkinSolver.OOC_PROC_CHANCE * (RotationData.InsectSwarmCasts / RotationData.CastCount * iSw.BaseManaCost) +
                MoonkinSolver.OOC_PROC_CHANCE * (RotationData.StarfireCount / RotationData.CastCount * sf.BaseManaCost) +
                MoonkinSolver.OOC_PROC_CHANCE * (RotationData.WrathCount / RotationData.CastCount * w.BaseManaCost) +
                MoonkinSolver.OOC_PROC_CHANCE * (RotationData.StarSurgeCount / RotationData.CastCount * ss.BaseManaCost) +
                MoonkinSolver.OOC_PROC_CHANCE * (RotationData.StarfallCasts / RotationData.CastCount * starfallManaCost);

            RotationData.ManaUsed -= manaSavingsFromOOC;

            RotationData.ManaGained = 2 * MoonkinSolver.EUPHORIA_PERCENT * talents.Euphoria * calcs.BasicStats.Mana;

            return RotationData.WrathAvgHit * RotationData.WrathCount +
                RotationData.StarfireAvgHit * RotationData.StarfireCount +
                RotationData.StarSurgeAvgHit * RotationData.StarSurgeCount +
                moonfireDamage + insectSwarmDamage + treantDamage + starfallDamage + T122PieceDamage;
        }
Ejemplo n.º 29
0
 // Now returns damage per cast to allow adjustments for fight length
 private float DoTreeCalcs(CharacterCalculationsMoonkin calcs, int playerLevel, int bossLevel, float effectiveNatureDamage, float treantLifespan)
 {
     float sunderPercent = calcs.BasicStats.TargetArmorReduction;
     float meleeHit = calcs.SpellHit * (StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel] / StatConversion.GetSpellMiss(playerLevel - bossLevel, false));
     float physicalDamageMultiplierBonus = (1f + calcs.BasicStats.BonusDamageMultiplier) * (1f + calcs.BasicStats.BonusPhysicalDamageMultiplier);
     float physicalDamageMultiplierReduc = (1f - calcs.BasicStats.DamageTakenReductionMultiplier) * (1f - calcs.BasicStats.PhysicalDamageTakenReductionMultiplier);
     // 932 = base AP, 57% spell power scaling
     float attackPower = 932.0f + (float)Math.Floor(0.57f * effectiveNatureDamage);
     // 1.65 s base swing speed
     float baseAttackSpeed = 1.65f;
     float attackSpeed = baseAttackSpeed / (1 + calcs.BasicStats.PhysicalHaste);
     // 580 = base DPS
     float damagePerHit = (580f + attackPower / 14.0f) * baseAttackSpeed;
     // 5% base crit rate, inherit crit debuffs
     // Remove crit depression, as it doesn't appear to have an effect (unless it's base ~10% crit rate)
     float critRate = 0.05f;
     // White hit glancing rate
     float glancingRate = StatConversion.WHITE_GLANCE_CHANCE_CAP[bossLevel - playerLevel];
     // Hit rate determined by the amount of melee hit, not by spell hit
     float missRate = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel] - meleeHit);
     // Since the trees inherit expertise from their hit, scale their hit rate such that when they are hit capped, they are expertise capped
     float dodgeRate = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[bossLevel - playerLevel] * (missRate / StatConversion.WHITE_MISS_CHANCE_CAP[bossLevel - playerLevel]));
     // Armor damage reduction, including Sunder
     float damageReduction = StatConversion.GetArmorDamageReduction(playerLevel, StatConversion.NPC_ARMOR[bossLevel - playerLevel] * (1f - sunderPercent), 0, 0);
     // Final normal damage per swing
     damagePerHit *= 1.0f - damageReduction;
     damagePerHit *= physicalDamageMultiplierReduc;
     damagePerHit *= physicalDamageMultiplierBonus;
     // Damage per swing, including crits/glances/misses
     // This is a cheesy approximation of a true combat table, but because crit/miss/dodge rates will all be fairly low, I don't need to do the whole thing
     damagePerHit = (critRate * damagePerHit * 2.0f) + (glancingRate * damagePerHit * 0.75f) + ((1 - critRate - glancingRate - missRate - dodgeRate) * damagePerHit);
     // Total damage done in their estimated lifespan
     float damagePerTree = (treantLifespan * 30.0f / attackSpeed) * damagePerHit;
     return 3 * damagePerTree;
 }
Ejemplo n.º 30
0
        public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations)
        {
            // First things first, we need to ensure that we aren't using bad data
            CharacterCalculationsMoonkin calc = new CharacterCalculationsMoonkin();
            if (character == null) { return calc; }
            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;
            if (calcOpts == null) { return calc; }
            //
            _reforgePriority = calcOpts.ReforgePriority;
            _enableSpiritToHit = calcOpts.AllowReforgingSpiritToHit;
            StatsMoonkin stats = (StatsMoonkin)GetCharacterStats(character, additionalItem);
            calc.BasicStats = stats;

            calc.SpellPower = (float)Math.Floor((1 + stats.BonusSpellPowerMultiplier) * (stats.SpellPower + stats.Intellect - 10));
            calc.SpellCrit = StatConversion.GetSpellCritFromIntellect(stats.Intellect) + StatConversion.GetSpellCritFromRating(stats.CritRating) + stats.SpellCrit + stats.SpellCritOnTarget;
            calc.SpellHit = StatConversion.GetSpellHitFromRating(stats.HitRating) + stats.SpellHit;
            calc.SpellHitCap = StatConversion.GetSpellMiss(character.Level - character.BossOptions.Level, false);
            calc.SpellHaste = (1 + StatConversion.GetSpellHasteFromRating(stats.HasteRating)) * (1 + stats.SpellHaste) - 1;
            calc.Mastery = 8.0f + StatConversion.GetMasteryFromRating(stats.MasteryRating);
            calc.ManaRegen = stats.Mp5 / 5f;

            // Generate the cycles
            /*if (referenceCalculation)
            {
                using (System.IO.StreamWriter writer = System.IO.File.CreateText("C:\\users\\Noah\\Desktop\\CastDistribution.txt"))
                {
                    //MoonkinCycleGenerator generator = new MoonkinCycleGenerator
                    //{
                    //EuphoriaChance = 0.24,
                    //Has4T12 = false,
                    //HasteLevel = 0,
                    //ShootingStarsChance = 0.04,
                    //StarlightWrathLevel = 3
                    //};

                    MoonkinSimulator generator = new MoonkinSimulator() { HasGlyphOfStarfire = false, Has4T13 = true };

                    writer.WriteLine("public static double[,] T13CastDistribution = new double[21, 12] {");

                    double[] baseRotationLengths = new double[21];
                    double[] baseNGUptimes = new double[21];
                    double[] baseMFExtended = new double[21];

                    for (int haste = 0; haste <= 100; haste += 5)
                    {
                        generator.HasteLevel = haste / 100.0;
                        double[] values = generator.GenerateCycle();
                        writer.Write("{");
                        for (int i = 0; i < values.Length; ++i)
                        {
                            writer.Write(String.Format(" {0},", values[i]));
                        }
                        writer.WriteLine(" },");
                        baseRotationLengths[haste / 5] = generator.GetRotationLength();
                        baseNGUptimes[haste / 5] = generator.GetNGUptime();
                        //baseMFExtended[haste / 5] = generator.GetPercentMoonfiresExtended();
                    }

                    writer.WriteLine("};");

                    generator.HasGlyphOfStarfire = true;

                    writer.WriteLine("public static double[,] T13CastDistributionGoSF = new double[21, 12] {");

                    double[] T12RotationLengths = new double[21];
                    double[] T12NGUptimes = new double[21];
                    double[] T12MFExtended = new double[21];

                    for (int haste = 0; haste <= 100; haste += 5)
                    {
                        generator.HasteLevel = haste / 100.0;
                        double[] values = generator.GenerateCycle();
                        writer.Write("{");
                        for (int i = 0; i < values.Length; ++i)
                        {
                            writer.Write(String.Format(" {0},", values[i]));
                        }
                        writer.WriteLine(" },");
                        T12RotationLengths[haste / 5] = generator.GetRotationLength();
                        T12NGUptimes[haste / 5] = generator.GetNGUptime();
                        T12MFExtended[haste / 5] = generator.GetPercentMoonfiresExtended();
                    }

                    writer.WriteLine("};");

                    writer.Write("public static double[] T13RotationDurations = new double[21] {");

                    for (int i = 0; i < baseRotationLengths.Length; ++i)
                    {
                        writer.Write(String.Format(" {0},", baseRotationLengths[i]));
                    }

                    writer.WriteLine(" };");

                    writer.Write("public static double[] T13RotationDurationsGoSF = new double[21] {");

                    for (int i = 0; i < T12RotationLengths.Length; ++i)
                    {
                        writer.Write(String.Format(" {0},", T12RotationLengths[i]));
                    }

                    writer.WriteLine(" };");

                    writer.Write("public static double[] T13NGUptimes = new double[21] {");

                    for (int i = 0; i < baseNGUptimes.Length; ++i)
                    {
                        writer.Write(String.Format(" {0},", baseNGUptimes[i]));
                    }

                    writer.WriteLine(" };");

                    writer.Write("public static double[] T13NGUptimesGoSF = new double[21] {");

                    for (int i = 0; i < T12NGUptimes.Length; ++i)
                    {
                        writer.Write(String.Format(" {0},", T12NGUptimes[i]));
                    }

                    writer.WriteLine(" };");

                    //writer.Write("public static double[] BasePercentMoonfiresExtended = new double[21] {");

                    //for (int i = 0; i < baseMFExtended.Length; ++i)
                    //{
                        //writer.Write(String.Format(" {0},", baseMFExtended[i]));
                    //}

                    //writer.WriteLine(" };");

                    writer.Write("public static double[] T13PercentMoonfiresExtended = new double[21] {");

                    for (int i = 0; i < T12MFExtended.Length; ++i)
                    {
                        writer.Write(String.Format(" {0},", T12MFExtended[i]));
                    }

                    writer.WriteLine(" };");
                }
                System.Windows.Application.Current.Shutdown();
            }*/

            // Run the solver against the generated cycle
            new MoonkinSolver().Solve(character, ref calc);

            return calc;
        }
Ejemplo n.º 31
0
 private float DoMushroomCalcs(CharacterCalculationsMoonkin calcs, float effectiveNatureDamage, float spellHit, float spellCrit)
 {
     float hitDamageModifier = (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusNatureDamageMultiplier);
     float critDamageModifier = 1.5f * (1 + calcs.BasicStats.BonusCritDamageMultiplier);
     // 845-1022 damage
     float baseDamage = (845 + 1022) / 2;
     float damagePerHit = (baseDamage + effectiveNatureDamage * 0.6032f) * hitDamageModifier;
     float damagePerCrit = damagePerHit * critDamageModifier;
     return spellHit * (damagePerHit * (1 - spellCrit) + damagePerCrit * spellCrit);
 }
Ejemplo n.º 32
0
        // Perform damage and mana calculations for all spells in the given rotation.  Returns damage done over the total duration.
        public float DamageDone(DruidTalents talents, CharacterCalculationsMoonkin calcs, float spellPower, float spellHit, float spellCrit, float spellHaste)
        {
            if (talents.Eclipse > 0)
            {
                return(DoEclipseCalcs(talents, calcs, Solver, spellPower, spellHit, spellCrit, spellHaste));
            }
            float latency = calcs.Latency;

            float moonkinFormProc = (talents.MoonkinForm == 1) ? 0.02f * calcs.BasicStats.Mana : 0.0f;
            bool  starfireGlyph   = talents.GlyphOfStarfire;
            int   impInsectSwarm  = talents.ImprovedInsectSwarm;

            switch (SpellsUsed.Count)
            {
            // Nuke only
            case 1:
                Spell mainNuke = Solver.FindSpell(SpellsUsed[0]);
                DoMainNuke(talents, calcs, ref mainNuke, spellPower, spellHit, spellCrit, spellHaste);

                float omenProcChance = talents.OmenOfClarity == 1 ? 0.06f : 0;
                mainNuke.ManaCost       = mainNuke.BaseManaCost - mainNuke.BaseCastTime / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana * spellHit - (spellCrit + mainNuke.CriticalChanceModifier) * moonkinFormProc - mainNuke.BaseManaCost * omenProcChance * spellHit;
                Duration                = mainNuke.CastTime;
                RotationData.ManaUsed   = ManaUsed = mainNuke.ManaCost;
                RotationData.ManaGained = ManaGained = mainNuke.BaseManaCost - mainNuke.ManaCost;
                RotationData.DPM        = mainNuke.DamagePerHit / mainNuke.ManaCost;
                CastCount               = 1.0f;
                DotTicks                = 0.0f;
                WrathCount              = mainNuke.Name == "W" ? 1.0f : 0.0f;
                StarfireCount           = mainNuke.Name == "SF" ? 1.0f : 0.0f;

                return(mainNuke.DamagePerHit);

            // Nuke + 1 DotEffect
            case 2:
                // Find the spells
                Spell DotEffectSpell = Solver.FindSpell(SpellsUsed[0]);
                mainNuke = Solver.FindSpell(SpellsUsed[1]);
                // Do Starfire glyph calculations, if applicable; then do DoT spell calculations
                if (starfireGlyph && mainNuke.Name == "SF" && DotEffectSpell.Name == "MF")
                {
                    DotEffectSpell.DotEffect.Duration += 9.0f;
                }
                DoDotSpell(talents, calcs, ref DotEffectSpell, spellPower, spellHit, spellCrit, spellHaste);

                // Do iIS calculations, if applicable
                if (impInsectSwarm > 0)
                {
                    if (mainNuke.Name == "SF" && DotEffectSpell.Name == "MF")
                    {
                        mainNuke.CriticalChanceModifier += 0.01f * impInsectSwarm;
                    }
                    else if (mainNuke.Name == "W" && DotEffectSpell.Name == "IS")
                    {
                        mainNuke.AllDamageModifier *= 1 + 0.01f * impInsectSwarm;
                    }
                }
                // Calculate main nuke damage
                DoMainNuke(talents, calcs, ref mainNuke, spellPower, spellHit, spellCrit, spellHaste);

                // Set rotation duration
                Duration = DotEffectSpell.DotEffect.Duration;

                // Calculate mana usage and damage done for this rotation
                float timeSpentCastingNuke = Duration - DotEffectSpell.CastTime;
                float nukeDamageDone       = mainNuke.DamagePerHit / mainNuke.CastTime * timeSpentCastingNuke;

                float numNukeCasts   = timeSpentCastingNuke / mainNuke.CastTime;
                float nukeManaSpent  = mainNuke.BaseManaCost * numNukeCasts;
                float totalManaSpent = nukeManaSpent + DotEffectSpell.BaseManaCost;
                CastCount     = numNukeCasts + 1.0f;
                WrathCount    = mainNuke.Name == "W" ? numNukeCasts : 0.0f;
                StarfireCount = mainNuke.Name == "SF" ? numNukeCasts : 0.0f;
                DotTicks      = DotEffectSpell.DotEffect.NumberOfTicks;
                if (DotEffectSpell.Name == "IS")
                {
                    InsectSwarmTicks = DotTicks;
                }
                else if (DotEffectSpell.Name == "MF")
                {
                    MoonfireTicks = DotTicks;
                    MoonfireCasts = 1f;
                }

                float manaFromJoW = (mainNuke.ManaCost - mainNuke.BaseCastTime / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana) * numNukeCasts;
                manaFromJoW += DotEffectSpell.ManaCost - 1.5f / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana;
                float manaFromOoC = ((0.06f) * mainNuke.BaseManaCost
                                     + (numNukeCasts - 1) * (0.06f) * mainNuke.BaseManaCost
                                     + (0.06f) * DotEffectSpell.BaseManaCost) * spellHit;
                float manaFromMoonkin = moonkinFormProc * spellHit * ((spellCrit + mainNuke.CriticalChanceModifier) * numNukeCasts + DotEffectSpell.CriticalChanceModifier);

                float actualManaSpent = totalManaSpent - manaFromJoW - manaFromMoonkin - (talents.OmenOfClarity == 1 ? manaFromOoC : 0.0f);

                RotationData.ManaUsed   = ManaUsed = actualManaSpent;
                RotationData.ManaGained = ManaGained = totalManaSpent - actualManaSpent;
                RotationData.DPM        = (nukeDamageDone + DotEffectSpell.DamagePerHit + DotEffectSpell.DotEffect.DamagePerHit) / totalManaSpent;

                // Undo iIS, if applicable
                if (impInsectSwarm > 0)
                {
                    if (mainNuke.Name == "SF" && DotEffectSpell.Name == "MF")
                    {
                        mainNuke.CriticalChanceModifier -= 0.01f * impInsectSwarm;
                    }
                    else if (mainNuke.Name == "W" && DotEffectSpell.Name == "IS")
                    {
                        mainNuke.AllDamageModifier /= 1 + 0.01f * impInsectSwarm;
                    }
                }
                // Undo SF glyph, if applicable
                if (starfireGlyph && mainNuke.Name == "SF" && DotEffectSpell.Name == "MF")
                {
                    DotEffectSpell.DotEffect.Duration -= 9.0f;
                }
                // Return the damage done per rotation
                return(nukeDamageDone + DotEffectSpell.DamagePerHit + DotEffectSpell.DotEffect.DamagePerHit);

            // Nuke + both DotEffects
            case 3:
                // Find the spells
                Spell moonFire    = Solver.FindSpell(SpellsUsed[0]);
                Spell insectSwarm = Solver.FindSpell(SpellsUsed[1]);
                mainNuke = Solver.FindSpell(SpellsUsed[2]);
                // Do Starfire glyph calculations, if applicable; then do DoT spell calculations
                if (starfireGlyph && mainNuke.Name == "SF")
                {
                    moonFire.DotEffect.Duration += 9.0f;
                }
                DoDotSpell(talents, calcs, ref moonFire, spellPower, spellHit, spellCrit, spellHaste);
                DoDotSpell(talents, calcs, ref insectSwarm, spellPower, spellHit, spellCrit, spellHaste);

                // Do iIS calculations, if applicable
                if (impInsectSwarm > 0)
                {
                    if (mainNuke.Name == "SF")
                    {
                        mainNuke.CriticalChanceModifier += 0.01f * impInsectSwarm;
                    }
                    else if (mainNuke.Name == "W")
                    {
                        mainNuke.AllDamageModifier *= 1 + 0.01f * impInsectSwarm;
                    }
                }
                // Calculate main nuke damage
                DoMainNuke(talents, calcs, ref mainNuke, spellPower, spellHit, spellCrit, spellHaste);

                // Set rotation duration
                Duration = moonFire.DotEffect.Duration;

                // Calculate mana usage and damage done for this rotation
                float timeSpentCastingIS = insectSwarm.CastTime * moonFire.DotEffect.Duration / insectSwarm.DotEffect.Duration;
                float insectSwarmDamage  = insectSwarm.DotEffect.DamagePerHit * moonFire.DotEffect.Duration / insectSwarm.DotEffect.Duration;
                timeSpentCastingNuke = Duration - timeSpentCastingIS - moonFire.CastTime;
                nukeDamageDone       = mainNuke.DamagePerHit / mainNuke.CastTime * timeSpentCastingNuke;

                numNukeCasts = timeSpentCastingNuke / mainNuke.CastTime;
                float numISCasts = timeSpentCastingIS / insectSwarm.CastTime;
                nukeManaSpent    = mainNuke.BaseManaCost * numNukeCasts;
                totalManaSpent   = nukeManaSpent + moonFire.BaseManaCost + numISCasts * insectSwarm.BaseManaCost;
                CastCount        = numNukeCasts + numISCasts + 1.0f;
                WrathCount       = mainNuke.Name == "W" ? numNukeCasts : 0.0f;
                StarfireCount    = mainNuke.Name == "SF" ? numNukeCasts : 0.0f;
                DotTicks         = moonFire.DotEffect.NumberOfTicks + numISCasts * insectSwarm.DotEffect.NumberOfTicks;
                InsectSwarmTicks = numISCasts * insectSwarm.DotEffect.NumberOfTicks;
                MoonfireTicks    = moonFire.DotEffect.NumberOfTicks;
                MoonfireCasts    = 1.0f;

                manaFromJoW = numNukeCasts * (mainNuke.ManaCost - mainNuke.BaseCastTime / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana) +
                              (moonFire.ManaCost - 1.5f / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana) +
                              numISCasts * (insectSwarm.ManaCost - 1.5f / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana);
                manaFromOoC = ((0.06f) * mainNuke.BaseManaCost
                               + (numNukeCasts - 1 - numISCasts) * (0.06f) * mainNuke.BaseManaCost
                               + (0.06f) * moonFire.BaseManaCost
                               + (0.06f) * numISCasts * insectSwarm.BaseManaCost
                               + (0.06f) * numISCasts * mainNuke.BaseManaCost) * spellHit;
                manaFromMoonkin = moonkinFormProc * spellHit * ((spellCrit + mainNuke.CriticalChanceModifier) * numNukeCasts + moonFire.CriticalChanceModifier);

                actualManaSpent = totalManaSpent - manaFromJoW - manaFromMoonkin - (talents.OmenOfClarity == 1 ? manaFromOoC : 0.0f);

                RotationData.ManaUsed   = ManaUsed = actualManaSpent;
                RotationData.ManaGained = ManaGained = totalManaSpent - actualManaSpent;
                RotationData.DPM        = (nukeDamageDone + moonFire.DamagePerHit + moonFire.DotEffect.DamagePerHit + insectSwarm.DotEffect.DamagePerHit) / totalManaSpent;

                // Undo iIS, if applicable
                if (impInsectSwarm > 0)
                {
                    if (mainNuke.Name == "SF")
                    {
                        mainNuke.CriticalChanceModifier -= 0.01f * impInsectSwarm;
                    }
                    else if (mainNuke.Name == "W")
                    {
                        mainNuke.AllDamageModifier /= 1 + 0.01f * impInsectSwarm;
                    }
                }
                // Undo SF glyph
                if (starfireGlyph && mainNuke.Name == "SF")
                {
                    moonFire.DotEffect.Duration -= 9.0f;
                }
                // Return the damage done per rotation
                return(nukeDamageDone + moonFire.DamagePerHit + moonFire.DotEffect.DamagePerHit + insectSwarmDamage);

            default:
                throw new Exception("Invalid rotation specified in rotation solver.");
            }
        }
Ejemplo n.º 33
0
        // Starfall
        private float DoStarfallCalcs(CharacterCalculationsMoonkin calcs, float effectiveArcaneDamage, float spellHit, float spellCrit)
        {
            float hitDamageModifier = (1 + calcs.BasicStats.BonusSpellDamageMultiplier) * (1 + calcs.BasicStats.BonusDamageMultiplier) * (1 + calcs.BasicStats.BonusArcaneDamageMultiplier);
            // Starfall is affected by Moonfury
            float critDamageModifier = 1.5f * (1 + calcs.BasicStats.BonusCritDamageMultiplier) + (1.5f * (1 + calcs.BasicStats.BonusCritDamageMultiplier) - 1);
            float baseDamagePerStar = (370.0f + 428.0f) / 2.0f;
            float mainStarCoefficient = 0.247f;

            float damagePerBigStarHit = (baseDamagePerStar + effectiveArcaneDamage * mainStarCoefficient) * hitDamageModifier;

            float critDamagePerBigStarHit = damagePerBigStarHit * critDamageModifier;

            float averageDamagePerBigStar = spellCrit * critDamagePerBigStarHit + (1 - spellCrit) * damagePerBigStarHit;

            float numberOfStarHits = 10f;

            float avgNumBigStarsHit = spellHit * numberOfStarHits;

            return avgNumBigStarsHit * averageDamagePerBigStar;
        }
Ejemplo n.º 34
0
        private float DoEclipseCalcs(DruidTalents talents, CharacterCalculationsMoonkin calcs, MoonkinSolver solver, float spellPower, float spellHit, float spellCrit, float spellHaste)
        {
            float latency = calcs.Latency;

            float omenOfClarityProcChance = talents.OmenOfClarity * 0.06f;
            float moonkinFormProc         = (talents.MoonkinForm == 1) ? 0.02f * calcs.BasicStats.Mana : 0.0f;
            bool  starfireGlyph           = talents.GlyphOfStarfire;
            int   impInsectSwarm          = talents.ImprovedInsectSwarm;

            float moonfireCasts    = SpellsUsed.Contains("MF") ? 1.0f : 0.0f;
            float insectSwarmCasts = SpellsUsed.Contains("IS") ? 1.0f : 0.0f;

            Spell moonfire    = moonfireCasts > 0 ? solver.FindSpell("MF") : null;
            Spell insectSwarm = insectSwarmCasts > 0 ? solver.FindSpell("IS") : null;

            // Do SF glyph
            if (starfireGlyph && moonfire != null)
            {
                moonfire.DotEffect.Duration += 9.0f;
            }

            float eclipseMultiplier = 0.4f + calcs.BasicStats.EclipseBonus;

            float eclipseDuration = 15.0f;
            //float eclipseCooldown = 30.0f;

            Spell preLunarCast = solver.FindSpell("W");

            // Do improved Insect Swarm
            if (insectSwarm != null)
            {
                preLunarCast.AllDamageModifier *= 1 + 0.01f * impInsectSwarm;
            }

            Spell solarEclipseCast = new Spell(preLunarCast);

            // Eclipse bonus and improved Insect Swarm
            // NOTE: Eclipse bonus additive with Moonfury and 4T9; multiplicative with everything else
            solarEclipseCast.AllDamageModifier = 1 + (float)Math.Floor(talents.Moonfury * 10 / 3.0f) / 100.0f + calcs.BasicStats.BonusMoonkinNukeDamage + eclipseMultiplier;
            if (insectSwarm != null)
            {
                solarEclipseCast.AllDamageModifier *= 1 + 0.01f * impInsectSwarm;
            }

            Spell preSolarCast = solver.FindSpell("SF");

            if (moonfire != null)
            {
                preSolarCast.CriticalChanceModifier += 0.01f * impInsectSwarm;
            }

            Spell lunarEclipseCast = new Spell(preSolarCast);

            lunarEclipseCast.CriticalChanceModifier = (float)Math.Min(1.0f - spellCrit, lunarEclipseCast.CriticalChanceModifier + eclipseMultiplier);

            DoMainNuke(talents, calcs, ref preSolarCast, spellPower, spellHit, spellCrit, spellHaste);
            DoMainNuke(talents, calcs, ref solarEclipseCast, spellPower, spellHit, spellCrit, spellHaste);
            DoMainNuke(talents, calcs, ref preLunarCast, spellPower, spellHit, spellCrit, spellHaste);
            DoMainNuke(talents, calcs, ref lunarEclipseCast, spellPower, spellHit, spellCrit, spellHaste);

            float moonfireRatio    = 0.0f;
            float insectSwarmRatio = 0.0f;

            if (moonfire != null)
            {
                DoDotSpell(talents, calcs, ref moonfire, spellPower, spellHit, spellCrit, spellHaste);
                moonfireRatio = moonfire.CastTime / moonfire.DotEffect.Duration;
            }
            if (insectSwarm != null)
            {
                DoDotSpell(talents, calcs, ref insectSwarm, spellPower, spellHit, spellCrit, spellHaste);
                insectSwarmRatio = insectSwarm.CastTime / insectSwarm.DotEffect.Duration;
            }

            float castRatio = moonfireRatio + (1 - moonfireRatio) * insectSwarmRatio;

            float lunarProcChance  = (spellCrit + preLunarCast.CriticalChanceModifier) * spellHit * talents.Eclipse / 3.0f * 0.6f;
            float castsToProcLunar = 1.0f / lunarProcChance;
            float timeToProcLunar  = preLunarCast.CastTime * (castsToProcLunar - 0.5f);

            float solarProcChance  = (spellCrit + preSolarCast.CriticalChanceModifier) * spellHit * talents.Eclipse / 3.0f;
            float castsToProcSolar = 1.0f / solarProcChance;
            float timeToProcSolar  = preSolarCast.CastTime * (castsToProcSolar - 0.5f);

            float preLunarTime       = timeToProcLunar + (preLunarCast.CastTime * 0.5f) + preLunarCast.NGCastTime * 1.5f;
            float preLunarDPS        = preLunarCast.DamagePerHit / preLunarCast.CastTime;
            float preLunarManaUsed   = preLunarCast.BaseManaCost / preLunarCast.CastTime * preLunarTime;
            float preLunarManaGained = (preLunarCast.BaseManaCost * omenOfClarityProcChance) +
                                       ((spellCrit + preLunarCast.CriticalChanceModifier) * spellHit * moonkinFormProc) +
                                       (preLunarCast.BaseCastTime / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana);

            float lunarTime       = (eclipseDuration - (preLunarCast.NGCastTime * 1.5f) - lunarEclipseCast.CastTime * 0.5f) * (1.0f - castRatio);
            float lunarDPS        = lunarEclipseCast.DamagePerHit / lunarEclipseCast.CastTime;
            float lunarManaUsed   = lunarEclipseCast.BaseManaCost / lunarEclipseCast.CastTime * lunarTime;
            float lunarManaGained = (lunarEclipseCast.BaseManaCost * omenOfClarityProcChance) +
                                    ((spellCrit + lunarEclipseCast.CriticalChanceModifier) * spellHit * moonkinFormProc) +
                                    (lunarEclipseCast.BaseCastTime / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana);

            float preSolarTime       = timeToProcSolar + (lunarEclipseCast.CastTime * 0.5f) + preSolarCast.NGCastTime;
            float preSolarDPS        = preSolarCast.DamagePerHit / preSolarCast.CastTime;
            float preSolarManaUsed   = preSolarCast.BaseManaCost / preSolarCast.CastTime * preSolarTime;
            float preSolarManaGained = (preSolarCast.BaseManaCost * omenOfClarityProcChance) +
                                       ((spellCrit + preSolarCast.CriticalChanceModifier) * spellHit * moonkinFormProc) +
                                       (preSolarCast.BaseCastTime / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana);

            float solarTime       = (eclipseDuration - (preSolarCast.NGCastTime) - (preLunarCast.CastTime * 0.5f)) * (1.0f - castRatio);
            float solarDPS        = solarEclipseCast.DamagePerHit / solarEclipseCast.CastTime;
            float solarManaUsed   = solarEclipseCast.BaseManaCost / solarEclipseCast.CastTime * solarTime;
            float solarManaGained = (solarEclipseCast.BaseManaCost * omenOfClarityProcChance) +
                                    ((spellCrit + solarEclipseCast.CriticalChanceModifier) * spellHit * moonkinFormProc) +
                                    (solarEclipseCast.BaseCastTime / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana);

            float rotationLength = (solarTime + lunarTime + preSolarTime + preLunarTime) / (1 - castRatio);

            float etSpentOnMFMFPriority = rotationLength * moonfireRatio;
            float etSpentOnISMFPriority = rotationLength * castRatio - etSpentOnMFMFPriority;
            float etSpentOnISISPriority = rotationLength * insectSwarmRatio;
            float etSpentOnMFISPriority = rotationLength * castRatio - etSpentOnISISPriority;

            float moonfireDamagePerCastTime    = moonfireRatio > 0 ? (moonfire.DamagePerHit + moonfire.DotEffect.DamagePerHit) / moonfire.CastTime : 0.0f;
            float insectSwarmDamagePerCastTime = insectSwarmRatio > 0 ? insectSwarm.DotEffect.DamagePerHit / insectSwarm.CastTime : 0.0f;

            float dotDamageMoonfirePriority    = etSpentOnMFMFPriority * moonfireDamagePerCastTime + etSpentOnISMFPriority * insectSwarmDamagePerCastTime;
            float dotDamageInsectSwarmPriority = etSpentOnISISPriority * insectSwarmDamagePerCastTime + etSpentOnMFISPriority * moonfireDamagePerCastTime;

            float totalDotDamage = 0.0f;

            if (dotDamageMoonfirePriority > dotDamageInsectSwarmPriority)
            {
                moonfireCasts    = moonfireRatio > 0 ? etSpentOnMFMFPriority / moonfire.CastTime : 0.0f;
                insectSwarmCasts = insectSwarmRatio > 0 ? etSpentOnISMFPriority / insectSwarm.CastTime : 0.0f;
                totalDotDamage   = dotDamageMoonfirePriority;
            }
            else
            {
                moonfireCasts    = moonfireRatio > 0 ? etSpentOnMFISPriority / moonfire.CastTime : 0.0f;
                insectSwarmCasts = insectSwarmRatio > 0 ? etSpentOnISISPriority / insectSwarm.CastTime : 0.0f;
                totalDotDamage   = dotDamageInsectSwarmPriority;
            }

            float moonfireTicks    = moonfire != null ? moonfireCasts * moonfire.DotEffect.NumberOfTicks : 0.0f;
            float insectSwarmTicks = insectSwarm != null ? insectSwarmCasts * insectSwarm.DotEffect.NumberOfTicks : 0.0f;

            float moonfireManaUsed    = moonfire != null ? moonfireCasts * moonfire.BaseManaCost : 0.0f;
            float insectSwarmManaUsed = insectSwarm != null ? insectSwarmCasts * insectSwarm.BaseManaCost : 0.0f;

            float damageDone = preSolarTime * preSolarDPS + solarTime * solarDPS + preLunarTime * preLunarDPS + lunarTime * lunarDPS +
                               totalDotDamage;

            Duration         = rotationLength;
            DotTicks         = moonfireTicks + insectSwarmTicks;
            InsectSwarmTicks = insectSwarmTicks;
            MoonfireTicks    = moonfireTicks;
            MoonfireCasts    = moonfireCasts;
            InsectSwarmCasts = insectSwarmCasts;
            CastCount        = castsToProcLunar + (lunarTime / lunarEclipseCast.CastTime) + castsToProcSolar + (solarTime / solarEclipseCast.CastTime) + moonfireCasts + insectSwarmCasts;

            WrathCount    = castsToProcLunar + (solarTime / solarEclipseCast.CastTime);
            StarfireCount = castsToProcSolar + (lunarTime / lunarEclipseCast.CastTime);

            ManaUsed   = preSolarManaUsed + solarManaUsed + preLunarManaUsed + lunarManaUsed + moonfireManaUsed + insectSwarmManaUsed;
            ManaGained = castsToProcSolar * preSolarManaGained + (solarTime / solarEclipseCast.CastTime) * solarManaGained + castsToProcLunar * preLunarManaGained + (lunarTime / lunarEclipseCast.CastTime) * lunarManaGained;

            if (spellCrit + lunarEclipseCast.CriticalChanceModifier > StarfireEclipseCrit)
            {
                StarfireEclipseCrit = spellCrit + lunarEclipseCast.CriticalChanceModifier;
            }
            if (spellCrit + preSolarCast.CriticalChanceModifier > StarfireNonEclipseCrit)
            {
                StarfireNonEclipseCrit = spellCrit + preSolarCast.CriticalChanceModifier;
            }

            InsectSwarmDuration = insectSwarm != null ? insectSwarm.DotEffect.Duration : 0.0f;
            MoonfireDuration    = moonfire != null ? moonfire.DotEffect.Duration : 0.0f;

            float mfSavingsFromOoC = moonfire != null ? (moonfire.BaseManaCost - (moonfire.BaseManaCost *
                                                                                  (1 - StarfireCount / WrathCount * 0.06f - (1 - StarfireCount / WrathCount) * 0.06f))) : 0.0f;
            float isSavingsFromOoC = insectSwarm != null ? (insectSwarm.BaseManaCost - (insectSwarm.BaseManaCost *
                                                                                        (1 - StarfireCount / WrathCount * 0.06f - (1 - StarfireCount / WrathCount) * 0.06f))) : 0.0f;

            ManaGained += moonfire != null ? (moonfireCasts * (mfSavingsFromOoC +
                                                               ((spellCrit + moonfire.CriticalChanceModifier) * moonkinFormProc * spellHit)
                                                               + 1.5f / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana)) : 0.0f;
            ManaGained += insectSwarm != null ? (insectSwarmCasts * (isSavingsFromOoC + 1.5f / 60.0f * calcs.BasicStats.ManaRestoreFromBaseManaPPM * CalculationsMoonkin.BaseMana)) : 0.0f;

            RotationData.ManaGained = ManaGained;
            RotationData.DPM        = damageDone / ManaUsed;
            ManaUsed -= ManaGained;
            RotationData.ManaUsed = ManaUsed;

            // Undo SF glyph
            if (starfireGlyph && moonfire != null)
            {
                moonfire.DotEffect.Duration -= 9.0f;
            }

            // Undo improved Insect Swarm
            if (insectSwarm != null)
            {
                preLunarCast.AllDamageModifier /= 1 + 0.01f * impInsectSwarm;
            }
            if (moonfire != null)
            {
                preSolarCast.CriticalChanceModifier -= 0.01f * impInsectSwarm;
            }

            return(damageDone);
        }
Ejemplo n.º 35
0
        private static void RecreateSpells(Character character, ref CharacterCalculationsMoonkin calcs)
        {
            starfire       = new Starfire();
            wrath          = new Wrath();
            insectSwarm    = new InsectSwarm();
            moonfire       = new Moonfire();
            SpellRotations = new List <SpellRotation>(new SpellRotation[]
            {
                new SpellRotation()
                {
                    Name   = "MF/SFx4",
                    Spells = new List <Spell>(new Spell[]
                    {
                        moonfire,
                        starfire
                    })
                },
                new SpellRotation()
                {
                    Name   = "MF/Wx8",
                    Spells = new List <Spell>(new Spell[]
                    {
                        moonfire,
                        wrath
                    })
                },
                new SpellRotation()
                {
                    Name   = "IS/SFx4",
                    Spells = new List <Spell>(new Spell[]
                    {
                        insectSwarm,
                        starfire
                    })
                },
                new SpellRotation()
                {
                    Name   = "IS/Wx8",
                    Spells = new List <Spell>(new Spell[]
                    {
                        insectSwarm,
                        wrath
                    })
                },
                new SpellRotation()
                {
                    Name   = "MF/SFx3/W",
                    Spells = new List <Spell>(new Spell[]
                    {
                        moonfire,
                        starfire,
                        wrath
                    })
                },
                new SpellRotation()
                {
                    Name   = "IS/SFx3/W",
                    Spells = new List <Spell>(new Spell[]
                    {
                        insectSwarm,
                        starfire,
                        wrath
                    })
                },
                new SpellRotation()
                {
                    Name   = "IS/MF/SFx3",
                    Spells = new List <Spell>(new Spell[]
                    {
                        insectSwarm,
                        moonfire,
                        starfire
                    })
                },
                new SpellRotation()
                {
                    Name   = "IS/MF/Wx7",
                    Spells = new List <Spell>(new Spell[]
                    {
                        insectSwarm,
                        moonfire,
                        wrath
                    })
                },
                new SpellRotation()
                {
                    Name   = "SF Spam",
                    Spells = new List <Spell>(new Spell[]
                    {
                        starfire
                    })
                },
                new SpellRotation()
                {
                    Name   = "W Spam",
                    Spells = new List <Spell>(new Spell[]
                    {
                        wrath
                    })
                }
            });

            UpdateSpells(character, ref calcs);
        }
Ejemplo n.º 36
0
        private static float GetEffectiveManaPool(Character character, CharacterCalculationsMoonkin calcs)
        {
            float fightLength = calcs.FightLength * 60.0f;

            float innervateCooldown = 360 - calcs.BasicStats.InnervateCooldownReduction;

            // Mana/5 calculations
            float totalManaRegen = calcs.ManaRegen5SR * fightLength;

            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;
            // Mana pot calculations
            float manaPotDelay       = calcOpts.ManaPotDelay * 60.0f;
            int   numPots            = calcOpts.ManaPots && fightLength - manaPotDelay > 0 ? ((int)(fightLength - manaPotDelay) / 120 + 1) : 0;
            float manaRestoredByPots = 0.0f;

            if (numPots > 0)
            {
                float manaPerPot = 0.0f;
                if (calcOpts.ManaPotType == "Super Mana Potion")
                {
                    manaPerPot = 2400.0f;
                }
                if (calcOpts.ManaPotType == "Fel Mana Potion")
                {
                    manaPerPot = 3200.0f;
                }
                // Bonus from Alchemist's Stone
                if (calcs.BasicStats.BonusManaPotion > 0)
                {
                    manaPerPot *= 1 + calcs.BasicStats.BonusManaPotion;
                }

                manaRestoredByPots = numPots * manaPerPot;
            }

            // Innervate calculations
            float innervateDelay     = calcOpts.InnervateDelay * 60.0f;
            int   numInnervates      = calcOpts.Innervate && fightLength - innervateDelay > 0 ? ((int)(fightLength - innervateDelay) / (int)innervateCooldown + 1) : 0;
            float totalInnervateMana = 0.0f;

            if (numInnervates > 0)
            {
                // Innervate mana rate increases only spirit-based regen
                float spiritRegen = (calcs.ManaRegen - calcs.BasicStats.Mp5 / 5f);
                // Add in calculations for an innervate weapon
                if (calcOpts.InnervateWeapon)
                {
                    float baseRegenConstant = 0.00932715221261f;
                    // Calculate the intellect from a weapon swap
                    float userIntellect = calcs.BasicStats.Intellect - (character.MainHand == null ? 0 : character.MainHand.Stats.Intellect) - (character.OffHand == null ? 0 : character.OffHand.Stats.Intellect)
                                          + calcOpts.InnervateWeaponInt;
                    // Do the same with spirit
                    float userSpirit = calcs.BasicStats.Spirit - (character.MainHand == null ? 0 : character.MainHand.Stats.Spirit) - (character.OffHand == null ? 0 : character.OffHand.Stats.Spirit)
                                       + calcOpts.InnervateWeaponSpi;
                    // The new spirit regen for innervate periods uses the new weapon stats
                    spiritRegen = baseRegenConstant * (float)Math.Sqrt(userIntellect) * userSpirit;
                }
                float innervateManaRate = spiritRegen * 4 + calcs.BasicStats.Mp5 / 5f;
                float innervateTime     = numInnervates * 20.0f;
                totalInnervateMana = innervateManaRate * innervateTime - (numInnervates * calcs.BasicStats.Mana * 0.04f);
            }
            // Shadow priest calculations
            float sPriestMp5  = calcOpts.ShadowPriest;
            float sPriestMana = sPriestMp5 / 5 * fightLength;

            return(calcs.BasicStats.Mana + totalInnervateMana + totalManaRegen + manaRestoredByPots + sPriestMana);
        }
Ejemplo n.º 37
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);
                };
            }
        }
Ejemplo n.º 38
0
        public void Solve(Character character, ref CharacterCalculationsMoonkin calcs)
        {
            CalculationOptionsMoonkin calcOpts = character.CalculationOptions as CalculationOptionsMoonkin;
            DruidTalents talents = character.DruidTalents;
            procEffects = new List<ProcEffect>();
            UpdateSpells(character, ref calcs);

            float trinketDPS = 0.0f;
            float baseSpellPower = calcs.SpellPower;
            float baseHit = 1 - Math.Max(0, calcs.SpellHitCap - calcs.SpellHit);
            float baseCrit = calcs.SpellCrit;
            float baseHaste = calcs.SpellHaste;
            float baseMastery = calcs.Mastery;
            float sub35PercentTime = (float)(character.BossOptions.Under20Perc + character.BossOptions.Under35Perc);

            BuildProcList(calcs);

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

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

            float manaGained = manaPool - calcs.BasicStats.Mana;

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

            int rotationIndex = 1;
            foreach (SpellRotation rot in Rotations)
            {
                if (rot.RotationData.Name == "None") continue;
                rot.Solver = this;

                // Reset variables modified in the pre-loop to base values
                float currentSpellPower = baseSpellPower;
                float currentCrit = baseCrit;
                float currentHaste = baseHaste;
                float currentMastery = baseMastery;
                float currentTrinketDPS = trinketDPS;
                calcs.BasicStats.BonusArcaneDamageMultiplier = oldArcaneMultiplier;
                calcs.BasicStats.BonusNatureDamageMultiplier = oldNatureMultiplier;
                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>();

                float baselineDPS = rot.DamageDone(character, calcs, calcOpts.TreantLifespan, currentSpellPower, baseHit, currentCrit, currentHaste, currentMastery, calcOpts.Latency);

                // Calculate spell power/spell damage modifying trinkets in a separate pre-loop
                // Add spell crit effects here as well, since they no longer affect timing
                // Add Intellect procs here, since they are a combination of spell power and spell crit
                foreach (ProcEffect proc in procEffects)
                {
                    bool handled = false;
                    if (proc.Effect.Stats.SpellPower > 0 || proc.Effect.Stats.CritRating > 0 || proc.Effect.Stats.MasteryRating > 0 || proc.Effect.Stats.Intellect > 0 || proc.Effect.Stats.HighestStat > 0)
                    {
                        handled = true;
                        float procIntellect = (float)Math.Floor((1 + calcs.BasicStats.BonusIntellectMultiplier) * (proc.Effect.Stats.Intellect + proc.Effect.Stats.HighestStat));
                        float procSpellPower = (float)Math.Floor((1 + calcs.BasicStats.BonusSpellPowerMultiplier) * (proc.Effect.Stats.SpellPower + procIntellect));
                        float procSpellCrit = StatConversion.GetSpellCritFromRating(proc.Effect.Stats.CritRating) + StatConversion.GetSpellCritFromIntellect(procIntellect);
                        float procMastery = StatConversion.GetMasteryFromRating(proc.Effect.Stats.MasteryRating);

                        float triggerInterval = 0.0f, triggerChance = 1.0f;
                        switch (proc.Effect.Trigger)
                        {
                            case Trigger.DamageDone:
                            case Trigger.DamageOrHealingDone:
                                triggerInterval = ((rot.RotationData.Duration / rot.RotationData.CastCount) + (rot.RotationData.Duration / (rot.RotationData.MoonfireTicks + rot.RotationData.InsectSwarmTicks))) / 2.0f;
                                break;
                            case Trigger.Use:
                                break;
                            case Trigger.SpellHit:
                            case Trigger.DamageSpellHit:
                                triggerInterval = rot.RotationData.Duration / rot.RotationData.CastCount;
                                triggerChance = baseHit;
                                break;
                            case Trigger.SpellCrit:
                            case Trigger.DamageSpellCrit:
                                triggerInterval = rot.RotationData.Duration / (rot.RotationData.CastCount - rot.RotationData.InsectSwarmCasts);
                                triggerChance = baseCrit;
                                break;
                            case Trigger.SpellCast:
                            case Trigger.DamageSpellCast:
                                triggerInterval = rot.RotationData.Duration / rot.RotationData.CastCount;
                                break;
                            case Trigger.MoonfireCast:
                                triggerInterval = rot.RotationData.Duration / rot.RotationData.MoonfireCasts;
                                break;
                            case Trigger.DoTTick:
                                triggerInterval = rot.RotationData.Duration / (rot.RotationData.InsectSwarmTicks + rot.RotationData.MoonfireTicks);
                                break;
                            case Trigger.MoonfireTick:
                                triggerInterval = rot.RotationData.Duration / rot.RotationData.MoonfireTicks;
                                break;
                            case Trigger.InsectSwarmTick:
                                triggerInterval = rot.RotationData.Duration / rot.RotationData.InsectSwarmTicks;
                                break;
                            default:
                                triggerChance = 0.0f;
                                break;
                        }
                        if (triggerChance > 0)
                        {
                            float durationMultiplier = proc.Effect.LimitedToExecutePhase ? sub35PercentTime : 1f;
                            currentSpellPower += (proc.Effect.MaxStack > 1 ? proc.Effect.GetAverageStackSize(triggerInterval, triggerChance, 3.0f, character.BossOptions.BerserkTimer * 60.0f * durationMultiplier) : 1) *
                            proc.Effect.GetAverageUptime(triggerInterval, triggerChance, 3.0f, character.BossOptions.BerserkTimer * 60.0f) * procSpellPower * durationMultiplier;
                            currentCrit += (proc.Effect.MaxStack > 1 ? proc.Effect.GetAverageStackSize(triggerInterval, triggerChance, 3.0f, character.BossOptions.BerserkTimer * 60.0f * durationMultiplier) : 1) *
                                proc.Effect.GetAverageUptime(triggerInterval, triggerChance, 3.0f, character.BossOptions.BerserkTimer * 60.0f) * procSpellCrit * durationMultiplier;
                            currentMastery += (proc.Effect.MaxStack > 1 ? proc.Effect.GetAverageStackSize(triggerInterval, triggerChance, 3.0f, character.BossOptions.BerserkTimer * 60.0f * durationMultiplier) : 1) *
                                proc.Effect.GetAverageUptime(triggerInterval, triggerChance, 3.0f, character.BossOptions.BerserkTimer * 60.0f) * procMastery * durationMultiplier;
                        }
                    }
                    // 2T10 (both if statements, which is why it isn't else-if)
                    if (proc.Effect.Stats.BonusArcaneDamageMultiplier > 0)
                    {
                        handled = true;
                        calcs.BasicStats.BonusArcaneDamageMultiplier += proc.Effect.GetAverageUptime(rot.RotationData.Duration / rot.RotationData.CastCount, 1f, 3.0f, character.BossOptions.BerserkTimer * 60.0f) * proc.Effect.Stats.BonusArcaneDamageMultiplier;
                    }
                    if (proc.Effect.Stats.BonusNatureDamageMultiplier > 0)
                    {
                        handled = true;
                        calcs.BasicStats.BonusNatureDamageMultiplier += proc.Effect.GetAverageUptime(rot.RotationData.Duration / rot.RotationData.CastCount, 1f, 3.0f, character.BossOptions.BerserkTimer * 60.0f) * proc.Effect.Stats.BonusNatureDamageMultiplier;
                    }
                    // Variable Pulse Lightning Capacitor
                    // This might catch some other effects, I probably need a better way to differentiate
                    if (proc.Effect.Trigger == Trigger.DamageSpellCrit && proc.Effect.Stats.NatureDamage > 0)
                    {
                        float procInterval = rot.RotationData.Duration / (rot.RotationData.CastCount - rot.RotationData.InsectSwarmCasts + rot.RotationData.DotTicks);
                        currentTrinketDPS += proc.Effect.GetAverageProcsPerSecond(procInterval, currentCrit, 3.0f, character.BossOptions.BerserkTimer * 60.0f) * proc.Effect.Stats.NatureDamage;
                    }
                    // Nested special effects
                    if (proc.Effect.Stats._rawSpecialEffectDataSize > 0)
                    {
                        handled = true;
                        SpecialEffect childEffect = proc.Effect.Stats._rawSpecialEffectData[0];
                        // Heart of Ignacious
                        if (childEffect.Stats.SpellPower > 0)
                        {
                            float averageStack = childEffect.GetAverageStackSize(rot.RotationData.Duration / rot.RotationData.CastCount, baseHit, 3.0f, proc.Effect.Duration);
                            currentSpellPower += (float)Math.Floor((1 + calcs.BasicStats.BonusSpellPowerMultiplier) * childEffect.Stats.SpellPower) * averageStack * proc.Effect.GetAverageUptime(rot.RotationData.Duration / rot.RotationData.CastCount, baseHit);
                        }
                        // 4T11
                        if (childEffect.Stats.SpellCrit != 0)
                        {
                            float maxStack = proc.Effect.Stats.SpellCrit;
                            float numNegativeStacks = childEffect.GetAverageStackSize(rot.RotationData.Duration / (rot.RotationData.CastCount - rot.RotationData.InsectSwarmCasts), Math.Min(1.0f, baseCrit + maxStack), 3.0f, proc.Effect.Duration);
                            float averageNegativeValue = childEffect.Stats.SpellCrit * numNegativeStacks;
                            float averageCrit = maxStack + averageNegativeValue;
                            currentCrit += averageCrit * proc.Effect.GetAverageUptime(rot.RotationData.Duration / 2f, 1f, 3.0f, character.BossOptions.BerserkTimer * 60.0f);
                        }
                    }
                    if (!handled)
                    {
                        if (proc.CalculateDPS != null)
                        {
                            accumulatedDamage += proc.CalculateDPS(rot, calcs, character.BossOptions.BerserkTimer, currentSpellPower, baseHit, currentCrit, currentHaste) * rot.RotationData.Duration;
                        }
                        if (proc.Activate != null)
                        {
                            float upTime = proc.UpTime(rot, calcs, character.BossOptions.BerserkTimer, (float)(character.BossOptions.Under35Perc + character.BossOptions.Under20Perc));
                            // 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, ref currentMastery);
                            }
                            // Procs with uptime 0 < x < 100 should be activated
                            else if (upTime > 0)
                                activatedEffects.Add(proc);
                        }
                        if (proc.CalculateMP5 != null)
                        {
                            manaGained += proc.CalculateMP5(rot, calcs, character.BossOptions.BerserkTimer, currentSpellPower, baseHit, currentCrit, currentHaste) / 5.0f * character.BossOptions.BerserkTimer * 60.0f;
                        }
                    }
                }
                // Calculate stat-boosting trinkets, taking into effect interactions with other stat-boosting procs
                int sign = 1;
                float[] cachedDamages = new float[1 << activatedEffects.Count];
                float[] cachedUptimes = new float[1 << activatedEffects.Count];
                float[,] cachedDetails = new float[1 << activatedEffects.Count, NUM_SPELL_DETAILS];
                List<int> calculatedPairs = new List<int>();
                // 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, ref currentMastery);
                        }
                        currentCrit = (float)Math.Min(1.0f, currentCrit);
                        float tempDPS = rot.DamageDone(character, calcs, calcOpts.TreantLifespan, currentSpellPower, baseHit, currentCrit, currentHaste, currentMastery, calcOpts.Latency) / rot.RotationData.Duration;
                        spellDetails[0] = rot.RotationData.StarfireAvgHit;
                        spellDetails[1] = rot.RotationData.WrathAvgHit;
                        spellDetails[2] = rot.RotationData.MoonfireAvgHit;
                        spellDetails[3] = rot.RotationData.InsectSwarmAvgHit;
                        spellDetails[4] = rot.RotationData.StarSurgeAvgHit;
                        spellDetails[5] = rot.RotationData.StarfireAvgCast;
                        spellDetails[6] = rot.RotationData.WrathAvgCast;
                        spellDetails[7] = rot.RotationData.MoonfireAvgCast;
                        spellDetails[8] = rot.RotationData.InsectSwarmAvgCast;
                        spellDetails[9] = rot.RotationData.StarSurgeAvgCast;
                        spellDetails[10] = rot.RotationData.AverageInstantCast;
                        spellDetails[11] = rot.RotationData.StarfireAvgEnergy;
                        spellDetails[12] = rot.RotationData.WrathAvgEnergy;
                        spellDetails[13] = rot.RotationData.StarSurgeAvgEnergy;
                        spellDetails[14] = rot.RotationData.TreantDamage;
                        spellDetails[15] = rot.RotationData.StarfallDamage;
                        spellDetails[16] = rot.RotationData.MushroomDamage;
                        foreach (int idx in vals)
                        {
                            tempUpTime *= activatedEffects[idx].UpTime(rot, calcs, character.BossOptions.BerserkTimer, (float)(character.BossOptions.Under35Perc + character.BossOptions.Under20Perc));
                            activatedEffects[idx].Deactivate(character, calcs, ref currentSpellPower, ref baseHit, ref currentCrit, ref currentHaste, ref currentMastery);
                        }
                        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)
                        {
                            foreach (int subset in calculatedPairs)
                            {
                                // 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;
                        for (int idx = 0; idx < NUM_SPELL_DETAILS; ++idx)
                        {
                            cachedDetails[pairs, idx] = spellDetails[idx];
                        }
                        calculatedPairs.Add(pairs);
                        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.
                for (int idx = 0; idx < cachedUptimes.Length; ++idx)
                {
                    if (cachedUptimes[idx] == 0) continue;
                    accumulatedDPS += cachedUptimes[idx] * cachedDamages[idx];
                    for (int i = 0; i < NUM_SPELL_DETAILS; ++i)
                    {
                        spellDetails[i] += cachedUptimes[idx] * cachedDetails[idx,i];
                    }
                }
                float damageDone = rot.DamageDone(character, calcs, calcOpts.TreantLifespan, currentSpellPower, baseHit, currentCrit, currentHaste, currentMastery, calcOpts.Latency);
                accumulatedDPS += (1 - totalUpTime) * damageDone / rot.RotationData.Duration;
                spellDetails[0] += (1 - totalUpTime) * rot.RotationData.StarfireAvgHit;
                spellDetails[1] += (1 - totalUpTime) * rot.RotationData.WrathAvgHit;
                spellDetails[2] += (1 - totalUpTime) * rot.RotationData.MoonfireAvgHit;
                spellDetails[3] += (1 - totalUpTime) * rot.RotationData.InsectSwarmAvgHit;
                spellDetails[4] += (1 - totalUpTime) * rot.RotationData.StarSurgeAvgHit;
                spellDetails[5] += (1 - totalUpTime) * rot.RotationData.StarfireAvgCast;
                spellDetails[6] += (1 - totalUpTime) * rot.RotationData.WrathAvgCast;
                spellDetails[7] += (1 - totalUpTime) * rot.RotationData.MoonfireAvgCast;
                spellDetails[8] += (1 - totalUpTime) * rot.RotationData.InsectSwarmAvgCast;
                spellDetails[9] += (1 - totalUpTime) * rot.RotationData.StarSurgeAvgCast;
                spellDetails[10] += (1 - totalUpTime) * rot.RotationData.AverageInstantCast;
                spellDetails[11] += (1 - totalUpTime) * rot.RotationData.StarfireAvgEnergy;
                spellDetails[12] += (1 - totalUpTime) * rot.RotationData.WrathAvgEnergy;
                spellDetails[13] += (1 - totalUpTime) * rot.RotationData.StarSurgeAvgEnergy;
                spellDetails[14] += (1 - totalUpTime) * rot.RotationData.TreantDamage;
                spellDetails[15] += (1 - totalUpTime) * rot.RotationData.StarfallDamage;
                spellDetails[16] += (1 - totalUpTime) * rot.RotationData.MushroomDamage;

                float burstDPS = accumulatedDPS + accumulatedDamage / rot.RotationData.Duration;
                float sustainedDPS = burstDPS;

                // Mana calcs:
                // Main rotation - all spells
                // Movement rotation - Lunar Shower MF, IS, Shooting Stars procs, and Starfall only
                rot.RotationData.ManaGained += manaGained / (character.BossOptions.BerserkTimer * 60.0f) * rot.RotationData.Duration;
                float timeToOOM = manaPool / ((rot.RotationData.ManaUsed - rot.RotationData.ManaGained) / rot.RotationData.Duration);
                if (timeToOOM <= 0) timeToOOM = character.BossOptions.BerserkTimer * 60.0f;   // Happens when ManaUsed is less than 0
                if (timeToOOM < character.BossOptions.BerserkTimer * 60.0f)
                {
                    rot.RotationData.TimeToOOM = new TimeSpan(0, (int)(timeToOOM / 60), (int)(timeToOOM % 60));
                    sustainedDPS = burstDPS * timeToOOM / (character.BossOptions.BerserkTimer * 60.0f);
                }
                
                burstDPS += currentTrinketDPS;
                sustainedDPS += currentTrinketDPS;

                rot.RotationData.SustainedDPS = sustainedDPS;
                rot.RotationData.BurstDPS = burstDPS;
                rot.RotationData.StarfireAvgHit = spellDetails[0];
                rot.RotationData.WrathAvgHit = spellDetails[1];
                rot.RotationData.MoonfireAvgHit = spellDetails[2];
                rot.RotationData.InsectSwarmAvgHit = spellDetails[3];
                rot.RotationData.StarSurgeAvgHit = spellDetails[4];
                rot.RotationData.StarfireAvgCast = spellDetails[5];
                rot.RotationData.WrathAvgCast = spellDetails[6];
                rot.RotationData.MoonfireAvgCast = spellDetails[7];
                rot.RotationData.InsectSwarmAvgCast = spellDetails[8];
                rot.RotationData.StarSurgeAvgCast = spellDetails[9];
                rot.RotationData.AverageInstantCast = spellDetails[10];
                rot.RotationData.StarfireAvgEnergy = spellDetails[11];
                rot.RotationData.WrathAvgEnergy = spellDetails[12];
                rot.RotationData.StarSurgeAvgEnergy = spellDetails[13];
                rot.RotationData.TreantDamage = spellDetails[14];
                rot.RotationData.StarfallDamage = spellDetails[15];
                rot.RotationData.MushroomDamage = spellDetails[16];

                // 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) || rot.RotationData.Name == calcOpts.UserRotation)
                {
                    maxDamageDone = sustainedDPS;
                    maxRotation = rot;
                }
                if (burstDPS > maxBurstDamageDone)
                {
                    maxBurstDamageDone = burstDPS;
                    maxBurstRotation = rot;
                }
                cachedResults[rotationIndex - 1] = rot.RotationData;

                // Deactivate always-up procs
                foreach (ProcEffect proc in alwaysUpEffects)
                {
                    proc.Deactivate(character, calcs, ref currentSpellPower, ref baseHit, ref currentCrit, ref currentHaste, ref currentMastery);
                }

                ++rotationIndex;
            }
            // Present the findings to the user.
            calcs.SelectedRotation = maxRotation.RotationData;
            calcs.BurstRotation = maxBurstRotation.RotationData;
            calcs.SubPoints = new float[] { maxBurstDamageDone, maxDamageDone };
            calcs.OverallPoints = calcs.SubPoints[0] + calcs.SubPoints[1];
            calcs.Rotations = cachedResults;
        }