public CatAbilityBuilder(StatsCat stats, DruidTalents talents, float weaponDPS, float attackSpeed, float armorDamageMultiplier, 
			float hasteBonus, float critMultiplier, float chanceAvoided, float chanceCritMelee, 
			float chanceCritFurySwipes, float chanceCritMangle, float chanceCritRavage, float chanceCritShred, 
			float chanceCritRake, float chanceCritRip, float chanceCritBite, float chanceGlance)
		{
			Stats = stats;
            Talents = talents;
			WeaponDPS = weaponDPS;
			AttackSpeed = attackSpeed;
			ArmorDamageMultiplier = armorDamageMultiplier;
			HasteBonus = hasteBonus;
			CritMultiplier = critMultiplier;
			ChanceAvoided = chanceAvoided;
			ChanceCritMelee = chanceCritMelee;
			ChanceCritFurySwipes = chanceCritFurySwipes;
			ChanceCritMangle = chanceCritMangle;
			ChanceCritRavage = chanceCritRavage;
			ChanceCritShred = chanceCritShred;
			ChanceCritRake = chanceCritRake;
			ChanceCritRip = chanceCritRip;
			ChanceCritBite = chanceCritBite;
			ChanceGlance = chanceGlance;

			BaseDamage = WeaponDPS + AttackPower / 14f + stats.WeaponDamage;
		}
Exemple #2
0
        private void calculateTalents(DruidTalents druidTalents, CalculationOptionsTree calcOpts)
        {
            manaCostModifier *= 1 - (druidTalents.Moonglow * 0.03f + druidTalents.TranquilSpirit * 0.02f);

            castTimeBeforeHaste -= 0.1f * druidTalents.Naturalist;

            extraCritPercent += 2f * druidTalents.NaturesMajesty;

            //Living Seed, 30% seed, 33% * points spend (1/3)
            extraCritModifier = 0.1f * druidTalents.LivingSeed * calcOpts.Current.LivingSeedEfficiency / 100f;

            // 6% chance to get Omen of Clarity...
            manaCostModifier *= 1 - 0.06f * druidTalents.OmenOfClarity;

            minHeal *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            maxHeal *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            coefDH += (0.2f * druidTalents.EmpoweredTouch);     // ET is additive from http://elitistjerks.com/f73/t37038-restoration_glyphs/p8/#post1240879

            coefDH *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);
        }
Exemple #3
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;
        }
Exemple #4
0
        public CatAbilityBuilder(StatsCat stats, DruidTalents talents, float weaponDPS, float attackSpeed, float armorDamageMultiplier,
                                 float hasteBonus, float critMultiplier, float chanceAvoided, float chanceCritMelee,
                                 float chanceCritFurySwipes, float chanceCritMangle, float chanceCritRavage, float chanceCritShred,
                                 float chanceCritRake, float chanceCritRip, float chanceCritBite, float chanceGlance)
        {
            Stats                 = stats;
            Talents               = talents;
            WeaponDPS             = weaponDPS;
            AttackSpeed           = attackSpeed;
            ArmorDamageMultiplier = armorDamageMultiplier;
            HasteBonus            = hasteBonus;
            CritMultiplier        = critMultiplier;
            ChanceAvoided         = chanceAvoided;
            ChanceCritMelee       = chanceCritMelee;
            ChanceCritFurySwipes  = chanceCritFurySwipes;
            ChanceCritMangle      = chanceCritMangle;
            ChanceCritRavage      = chanceCritRavage;
            ChanceCritShred       = chanceCritShred;
            ChanceCritRake        = chanceCritRake;
            ChanceCritRip         = chanceCritRip;
            ChanceCritBite        = chanceCritBite;
            ChanceGlance          = chanceGlance;

            BaseDamage = WeaponDPS + AttackPower / 14f + stats.WeaponDamage;
        }
Exemple #5
0
        private void calculateTalents(DruidTalents druidTalents, CalculationOptionsTree calcOpts)
        {
            extraTicks += 1 * druidTalents.NaturesSplendor;

            manaCostModifier *= 1 - 0.2f * druidTalents.TreeOfLife - 0.03f * druidTalents.Moonglow;
            // 6% chance to get Omen of Clarity...
            manaCostModifier *= 1 - 0.06f * druidTalents.OmenOfClarity;

            periodicTickModifier *=
                (1 + 0.01f * druidTalents.Genesis +
                 0.05f * druidTalents.ImprovedRejuvenation +
                 0.02f * druidTalents.GiftOfNature) *
                (1 + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1 + 0.06f * druidTalents.TreeOfLife);

            coefHoT *=
                (1 + 0.04f * druidTalents.EmpoweredRejuvenation) *
                (1 + 0.01f * druidTalents.Genesis +
                 0.05f * druidTalents.ImprovedRejuvenation +
                 0.02f * druidTalents.GiftOfNature) *
                (1 + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1 + 0.06f * druidTalents.TreeOfLife);

            #region Glyph of Rapid Rejuvenation
            if (druidTalents.GlyphOfRapidRejuvenation)
            {
                hasteTicks = true;
            }
            #endregion
        }
Exemple #6
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);
     }
 }
Exemple #7
0
        void computeBasics()
        {
            BossOptions bossOpts = character.BossOptions;
            calc.FightLength = bossOpts.BerserkTimer;

            character.SetBonusCount.TryGetValue("Stormrider's Vestments", out T11Count);
            character.SetBonusCount.TryGetValue("Obsidian Arborweave Vestments", out T12Count);
            character.SetBonusCount.TryGetValue("Deep Earth Vestments", out T13Count);
            Talents = character.DruidTalents;
            Restoration = (opts != null) ? opts.Restoration : true;
        }
Exemple #8
0
        public TalentsBase TalentSpec()
        {
            if (Spec == null)
            {
                return(null);
            }
            TalentsBase spec;

            if (Class == CharacterClass.DeathKnight)
            {
                spec = new DeathKnightTalents(Spec);
            }
            else if (Class == CharacterClass.Warrior)
            {
                spec = new WarriorTalents(Spec);
            }
            else if (Class == CharacterClass.Paladin)
            {
                spec = new PaladinTalents(Spec);
            }
            else if (Class == CharacterClass.Shaman)
            {
                spec = new ShamanTalents(Spec);
            }
            else if (Class == CharacterClass.Hunter)
            {
                spec = new HunterTalents(Spec);
            }
            else if (Class == CharacterClass.Rogue)
            {
                spec = new RogueTalents(Spec);
            }
            else if (Class == CharacterClass.Druid)
            {
                spec = new DruidTalents(Spec);
            }
            else if (Class == CharacterClass.Warlock)
            {
                spec = new WarlockTalents(Spec);
            }
            else if (Class == CharacterClass.Priest)
            {
                spec = new PriestTalents(Spec);
            }
            else
            {
                spec = new MageTalents(Spec);
            }
            return(spec);
        }
Exemple #9
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;
        }
Exemple #10
0
        private void calculateTalents(DruidTalents druidTalents, CalculationOptionsTree calcOpts)
        {
            periodicTicks += 2f * druidTalents.NaturesSplendor;

            gcdBeforeHaste -= 1.5f * 0.02f * druidTalents.GiftOfTheEarthmother;

            manaCostModifier *= (1f - 0.2f * druidTalents.TreeOfLife);

            // 6% chance to get Omen of Clarity...
            manaCostModifier *= 1f - 0.06f * druidTalents.OmenOfClarity;

            periodicTick *=
                (1f + 0.01f * druidTalents.Genesis + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            coefHoT *=
                (1f + 0.04f * druidTalents.EmpoweredRejuvenation) *
                (1f + 0.01f * druidTalents.Genesis + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            minHeal *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            maxHeal *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            coefDH *=
                (1f + 0.04f * druidTalents.EmpoweredRejuvenation) *
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            if (druidTalents.GlyphOfLifebloom)
            {
                periodicTicks += 1f;
            }                                                           //(calcOpts.glyphOfLifebloom)
        }
Exemple #11
0
        private void calculateTalents(DruidTalents druidTalents, CalculationOptionsTree calcOpts)
        {
            periodicTicks += 2 * druidTalents.NaturesSplendor;

            manaCostModifier *= (1 - 0.03f * druidTalents.Moonglow - 0.2f * druidTalents.TreeOfLife);
            // 6% chance to get Omen of Clarity...
            manaCostModifier *= 1f - 0.06f * druidTalents.OmenOfClarity;

            extraCritPercent += 5f * druidTalents.NaturesBounty;
            //Living Seed
            extraCritModifier = 0.1f * druidTalents.LivingSeed * calcOpts.Current.LivingSeedEfficiency / 100f;

            minHeal *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            maxHeal *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            coefDH *=
                (1f + 0.04f * druidTalents.EmpoweredRejuvenation) *
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            periodicTick *=
                (1f + 0.01f * druidTalents.Genesis + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            coefHoT *=
                (1f + 0.04f * druidTalents.EmpoweredRejuvenation) *
                (1f + 0.01f * druidTalents.Genesis + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);
        }
Exemple #12
0
        private void calculateTalents(DruidTalents druidTalents, CalculationOptionsTree calcOpts)
        {
            manaCostModifier *= (1f - druidTalents.TranquilSpirit * 0.02f - druidTalents.Moonglow * 0.03f);

            extraCritPercent += 2f * druidTalents.NaturesMajesty;
            extraCritPercent += 5f * druidTalents.NaturesBounty;

            //Living Seed, 30% seed, 33% * points spend (1/3)
            //if (calcOpts.useLivingSeedAsCritMultiplicator)
            extraCritModifier += 0.1f * druidTalents.LivingSeed * calcOpts.Current.LivingSeedEfficiency / 100f;

            // 6% chance to get Omen of Clarity...
            manaCostModifier *= 1f - 0.06f * druidTalents.OmenOfClarity;

            minHeal *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            maxHeal *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            coefDH += (0.1f * druidTalents.EmpoweredTouch);     // From 3.2 Empowered Touch also boosts Nourish
            // Assume also additive, also see http://elitistjerks.com/f73/t37038-restoration_glyphs/p8/#post1240879
            // This is also the value TreeCalcs uses at the moment (8th december 2009)

            coefDH *=
                (1f + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife) *
                (1f + 0.06f * druidTalents.TreeOfLife);

            if (druidTalents.GlyphOfNourish)
            {
                NourishBonusPerHoTGlyphs = 0.06f;
            }
        }
Exemple #13
0
        private void calculateTalents(DruidTalents druidTalents, CalculationOptionsTree calcOpts)
        {
            manaCostModifier *= (1f - 0.2f * druidTalents.TreeOfLife);

            // 6% chance to get Omen of Clarity...
            manaCostModifier *= 1f - 0.06f * druidTalents.OmenOfClarity;

            // Glyph of Wild Growth
            if (druidTalents.GlyphOfWildGrowth)
            {
                maxTargets += 1;
            }

            coefHoT *=
                (1f + 0.01f * druidTalents.Genesis + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.06f * druidTalents.TreeOfLife) *
                (1f + 0.02f * druidTalents.MasterShapeshifter * druidTalents.TreeOfLife);

            periodickTickModifier *=
                (1f + 0.01f * druidTalents.Genesis + 0.02f * druidTalents.GiftOfNature) *
                (1f + 0.06f * druidTalents.TreeOfLife) *
                (1f + 0.02f * druidTalents.MasterShapeshifter);
        }
Exemple #14
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;
        }
Exemple #15
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;
        }
Exemple #16
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);
        }
Exemple #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);
        }
Exemple #18
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;
        }
Exemple #19
0
        TalentsBase parse_talents_wowhead(CharacterClass characterclass, string talent_string)
        {
            // wowhead format: [tree_1]Z[tree_2]Z[tree_3] where the trees are character encodings
            // each character expands to a pair of numbers [0-5][0-5]
            // unused deeper talents are simply left blank instead of filling up the string with zero-zero encodings

            bool hasGlyphs = talent_string.Contains(":");

            int[] talent_trees = new int[] { 0, 0, 0 };
            int[] glyph_trees  = new int[] { 3, 3, 3 };

            switch (characterclass)
            {
            case CharacterClass.Warrior:     { WarriorTalents talents = new WarriorTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.Paladin:     { PaladinTalents talents = new PaladinTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.Hunter:      { HunterTalents talents = new HunterTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.Rogue:       { RogueTalents talents = new RogueTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.Priest:      { PriestTalents talents = new PriestTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.DeathKnight: { DeathKnightTalents talents = new DeathKnightTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.Shaman:      { ShamanTalents talents = new ShamanTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.Mage:        { MageTalents talents = new MageTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.Warlock:     { WarlockTalents talents = new WarlockTalents(); talent_trees = talents.TreeLengths; break; }

            case CharacterClass.Druid:       { DruidTalents talents = new DruidTalents(); talent_trees = talents.TreeLengths; break; }
            }

            int[]   encoding      = new int[talent_trees[0] + talent_trees[1] + talent_trees[2]];
            int[][] glyphEncoding = new int[][] {
                new int[3],
                new int[3],
                new int[3],
            };
            int[] tree_count  = new int[] { 0, 0, 0 };
            int[] glyph_count = new int[] { 0, 0, 0 };

            int tree  = 0;
            int count = 0;

            #region Talents parsing
            for (int i = 1; i < talent_string.Length; i++)
            {
                if (tree >= 3)
                {
                    //sim -> errorf( "Player %s has malformed wowhead talent string. Too many talent trees specified.\n", name() );
                    return(null);
                }

                char c = talent_string[i];

                if (c == ':')
                {
                    break;           // glyph encoding follows
                }
                if (c == 'Z')
                {
                    count = 0;
                    for (int j = 0; j <= tree; j++)
                    {
                        count += talent_trees[tree];
                    }
                    tree++;
                    continue;
                }

                decode_t decode = null;
                for (int j = 0; decoding[j].key != '\0' && decode == null; j++)
                {
                    if (decoding[j].key == c)
                    {
                        decode = decoding[j];
                    }
                }

                if (decode == null)
                {
                    //sim -> errorf( "Player %s has malformed wowhead talent string. Translation for '%c' unknown.\n", name(), c );
                    return(null);
                }

                encoding[count++] += decode.first - '0';
                tree_count[tree]  += 1;

                if (tree_count[tree] < talent_trees[tree])
                {
                    encoding[count++] += decode.second - '0';
                    tree_count[tree]  += 1;
                }

                if (tree_count[tree] >= talent_trees[tree])
                {
                    tree++;
                }
            }
            #endregion

            #region Glyphs Parsing
            #region Notes

            /* This is totally crappy....
             * Glyphs do not follow the same parsing rules. If you apply what was there for talents directly
             * to glyphs you get 1202032213120011050000000000000000. Which should only have 1's and 0's
             *
             *
             * Warriors: As I'm checking glyphs, here's what I get:
             * == PRIMES ==
             *   Link                           decode  id       Name
             * * http://www.wowhead.com/talent#L:0 00 43415 58388 Devastate
             * * http://www.wowhead.com/talent#L:z 01 43416 58367 Bloodthirst
             * * http://www.wowhead.com/talent#L:M 02 43421 58368 Mortal Strike
             * * http://www.wowhead.com/talent#L:c 03 43422 58386 Overpower
             * * http://www.wowhead.com/talent#L:m 04 43423 58385 Slam
             * * http://www.wowhead.com/talent#L:V 05 43424 58364 Revenge
             * * http://www.wowhead.com/talent#L:o 10 43425 58375 Shield Slam
             * * http://www.wowhead.com/talent#L:k 11 43432 58370 Raging Blow
             * * http://www.wowhead.com/talent#L:R 12 45790 63324 Bladestorm
             * == MAJORS ==
             * * http://www.wowhead.com/talent#L:0 00 43397 Long Charge
             * * http://www.wowhead.com/talent#L:z 01 43399 Thunder Clap
             * * http://www.wowhead.com/talent#L:M 02 43413 Rapid Charge
             * * http://www.wowhead.com/talent#L:c 03 43414 Cleaving
             * * http://www.wowhead.com/talent#L:m 04 43417 Piercing Howl
             * * http://www.wowhead.com/talent#L:V 05 43418 Heroic Throw
             * * http://www.wowhead.com/talent#L:o 10 43419 Intervene
             * * http://www.wowhead.com/talent#L:k 11 43427 Sunder Armor
             * * http://www.wowhead.com/talent#L:R 12 43428 Sweeping Strikes
             * * http://www.wowhead.com/talent#L:s 13 43430 Resonating Power
             * * http://www.wowhead.com/talent#L:a 14 43431 Victory Rush
             * * http://www.wowhead.com/talent#L:q 15 45792 Shockwave
             * * http://www.wowhead.com/talent#L:b 20 45795 Spell Reflection
             * * http://www.wowhead.com/talent#L:d 21 45797 Shield Wall
             * * http://www.wowhead.com/talent#L:r 22 63481 Colossus Smash
             * * http://www.wowhead.com/talent#L:f 23 67482 Intercept
             * * http://www.wowhead.com/talent#L:w 24 67483 Death Wish
             * == MINORS ==
             * * http://www.wowhead.com/talent#L:0 00 43395 Battle
             * * http://www.wowhead.com/talent#L:z 01 43396 Berserker Rage
             * * http://www.wowhead.com/talent#L:M 02 43398 Demoralizing Shout
             * * http://www.wowhead.com/talent#L:c 03 43400 Enduring Victory
             * * http://www.wowhead.com/talent#L:m 04 43412 Bloody Healing
             * * http://www.wowhead.com/talent#L:V 05 45793 Furious Sundering
             * * http://www.wowhead.com/talent#L:o 10 45794 Intimidating Shout
             * * http://www.wowhead.com/talent#L:k 11 49084 Command
             *
             * So http://www.wowhead.com/talent#LubcfRMRurkcrZ0b:RMcrsR0kV would mean:
             *   Prime: Bladestorm, Mortal Strike, Overpower
             *   Major: Colossus Smash, Resonating Power, Sweeping Strikes
             *   Minor: Battle, Command, Furious Sundering
             * Which is correct, that's what we come out to
             */
            #endregion

            tree  = 0;
            count = 0;

            if (hasGlyphs)
            {
                for (int i = talent_string.IndexOf(":") + 1; i < talent_string.Length; i++)
                {
                    if (tree >= 3)
                    {
                        //sim -> errorf( "Player %s has malformed wowhead talent string. Too many talent trees specified.\n", name() );
                        return(null);
                    }

                    char c = talent_string[i];

                    if (c == 'Z')
                    {
                        count = 0;

                        /*for (int j = 0; j <= tree; j++) {
                         *  count += glyph_trees[tree];
                         * }*/
                        tree++;
                        continue;
                    }

                    decode_t decode = null;
                    for (int j = 0; decoding[j].key != '\0' && decode == null; j++)
                    {
                        if (decoding[j].key == c)
                        {
                            decode = decoding[j];
                        }
                    }

                    if (decode == null)
                    {
                        //sim -> errorf( "Player %s has malformed wowhead talent string. Translation for '%c' unknown.\n", name(), c );
                        return(null);
                    }

                    glyphEncoding[tree][count++] += (decode.first - '0') * 10 + decode.second - '0';
                    glyph_count[tree]            += 1;

                    if (glyph_count[tree] >= (glyph_trees[tree]))
                    {
                        tree++; count = 0;
                    }
                }
            }
            #endregion

            string newtalentstring = "";
            foreach (int i in encoding)
            {
                newtalentstring += i.ToString();
            }
            if (hasGlyphs)
            {
                //newtalentstring += ".";
                //for (int t = 0; t < 3; t++) {
                //    foreach (int i in glyphEncoding[t]) { newtalentstring += i.ToString(); }
                //}
            }

            switch (characterclass)
            {
            case CharacterClass.Warrior: { return(new WarriorTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.Paladin: { return(new PaladinTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.Hunter: { return(new HunterTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.Rogue: { return(new RogueTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.Priest: { return(new PriestTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.DeathKnight: { return(new DeathKnightTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.Shaman: { return(new ShamanTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.Mage: { return(new MageTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.Warlock: { return(new WarlockTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }

            case CharacterClass.Druid: { return(new DruidTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); }
            }
            return(null);
        }
Exemple #20
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.");
            }
        }
Exemple #21
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);
        }
Exemple #22
0
 public static bool HasTalent(DruidTalents tal)
 {
     return TalentManager.IsSelected((int)tal);
 }
        TalentsBase parse_talents_wowhead(CharacterClass characterclass, string talent_string)
        {
            // wowhead format: [tree_1]Z[tree_2]Z[tree_3] where the trees are character encodings
            // each character expands to a pair of numbers [0-5][0-5]
            // unused deeper talents are simply left blank instead of filling up the string with zero-zero encodings

            bool hasGlyphs = talent_string.Contains(":");

            int[] talent_trees = new int[] { 0, 0, 0 };
            int[] glyph_trees = new int[] { 3, 3, 3 };

            switch (characterclass)
            {
                case CharacterClass.Warrior:     { WarriorTalents talents = new WarriorTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.Paladin:     { PaladinTalents talents = new PaladinTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.Hunter:      { HunterTalents talents = new HunterTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.Rogue:       { RogueTalents talents = new RogueTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.Priest:      { PriestTalents talents = new PriestTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.DeathKnight: { DeathKnightTalents talents = new DeathKnightTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.Shaman:      { ShamanTalents talents = new ShamanTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.Mage:        { MageTalents talents = new MageTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.Warlock:     { WarlockTalents talents = new WarlockTalents(); talent_trees = talents.TreeLengths; break; }
                case CharacterClass.Druid:       { DruidTalents talents = new DruidTalents(); talent_trees = talents.TreeLengths; break; }
            }

            int[] encoding = new int[talent_trees[0] + talent_trees[1] + talent_trees[2]];
            int[][] glyphEncoding = new int[][] {
                new int[3],
                new int[3],
                new int[3],
            };
            int[] tree_count = new int[] { 0, 0, 0 };
            int[] glyph_count = new int[] { 0, 0, 0 };

            int tree = 0;
            int count = 0;

            #region Talents parsing
            for (int i=1; i < talent_string.Length; i++) {
                if (tree >= 3) {
                    //sim -> errorf( "Player %s has malformed wowhead talent string. Too many talent trees specified.\n", name() );
                    return null;
                }

                char c = talent_string[i];

                if (c == ':') break; // glyph encoding follows
 
                if (c == 'Z') {
                    count = 0;
                    for (int j = 0; j <= tree; j++) {
                        count += talent_trees[tree];
                    }
                    tree++;
                    continue;
                }

                decode_t decode = null;
                for (int j=0; decoding[j].key != '\0' && decode==null; j++) {
                    if (decoding[j].key == c) { decode = decoding[j]; }
                }

                if (decode == null) {
                    //sim -> errorf( "Player %s has malformed wowhead talent string. Translation for '%c' unknown.\n", name(), c );
                    return null;
                }

                encoding[count++] += decode.first - '0';
                tree_count[tree] += 1;

                if (tree_count[tree] < talent_trees[tree]) {
                    encoding[count++] += decode.second - '0';
                    tree_count[tree] += 1;
                }

                if (tree_count[tree] >= talent_trees[tree]) {
                    tree++;
                }
            }
            #endregion

            #region Glyphs Parsing
            #region Notes
            /* This is totally crappy....
             * Glyphs do not follow the same parsing rules. If you apply what was there for talents directly
             * to glyphs you get 1202032213120011050000000000000000. Which should only have 1's and 0's
             * 
             * 
             * Warriors: As I'm checking glyphs, here's what I get:
             * == PRIMES ==
             *   Link                           decode  id       Name
            * * http://www.wowhead.com/talent#L:0 00 43415 58388 Devastate
            * * http://www.wowhead.com/talent#L:z 01 43416 58367 Bloodthirst
            * * http://www.wowhead.com/talent#L:M 02 43421 58368 Mortal Strike
            * * http://www.wowhead.com/talent#L:c 03 43422 58386 Overpower
            * * http://www.wowhead.com/talent#L:m 04 43423 58385 Slam
            * * http://www.wowhead.com/talent#L:V 05 43424 58364 Revenge
            * * http://www.wowhead.com/talent#L:o 10 43425 58375 Shield Slam
            * * http://www.wowhead.com/talent#L:k 11 43432 58370 Raging Blow
            * * http://www.wowhead.com/talent#L:R 12 45790 63324 Bladestorm
             * == MAJORS ==
             * * http://www.wowhead.com/talent#L:0 00 43397 Long Charge
             * * http://www.wowhead.com/talent#L:z 01 43399 Thunder Clap
             * * http://www.wowhead.com/talent#L:M 02 43413 Rapid Charge
             * * http://www.wowhead.com/talent#L:c 03 43414 Cleaving
             * * http://www.wowhead.com/talent#L:m 04 43417 Piercing Howl
             * * http://www.wowhead.com/talent#L:V 05 43418 Heroic Throw
             * * http://www.wowhead.com/talent#L:o 10 43419 Intervene
             * * http://www.wowhead.com/talent#L:k 11 43427 Sunder Armor
             * * http://www.wowhead.com/talent#L:R 12 43428 Sweeping Strikes
             * * http://www.wowhead.com/talent#L:s 13 43430 Resonating Power
             * * http://www.wowhead.com/talent#L:a 14 43431 Victory Rush
             * * http://www.wowhead.com/talent#L:q 15 45792 Shockwave
             * * http://www.wowhead.com/talent#L:b 20 45795 Spell Reflection
             * * http://www.wowhead.com/talent#L:d 21 45797 Shield Wall
             * * http://www.wowhead.com/talent#L:r 22 63481 Colossus Smash
             * * http://www.wowhead.com/talent#L:f 23 67482 Intercept
             * * http://www.wowhead.com/talent#L:w 24 67483 Death Wish
             * == MINORS ==
             * * http://www.wowhead.com/talent#L:0 00 43395 Battle
             * * http://www.wowhead.com/talent#L:z 01 43396 Berserker Rage
             * * http://www.wowhead.com/talent#L:M 02 43398 Demoralizing Shout
             * * http://www.wowhead.com/talent#L:c 03 43400 Enduring Victory
             * * http://www.wowhead.com/talent#L:m 04 43412 Bloody Healing
             * * http://www.wowhead.com/talent#L:V 05 45793 Furious Sundering
             * * http://www.wowhead.com/talent#L:o 10 45794 Intimidating Shout
             * * http://www.wowhead.com/talent#L:k 11 49084 Command
             * 
             * So http://www.wowhead.com/talent#LubcfRMRurkcrZ0b:RMcrsR0kV would mean:
             *   Prime: Bladestorm, Mortal Strike, Overpower
             *   Major: Colossus Smash, Resonating Power, Sweeping Strikes
             *   Minor: Battle, Command, Furious Sundering
             * Which is correct, that's what we come out to
             */
            #endregion

            tree = 0;
            count = 0;

            if (hasGlyphs) {
                for (int i=talent_string.IndexOf(":")+1; i < talent_string.Length; i++) {
                    if (tree >= 3) {
                        //sim -> errorf( "Player %s has malformed wowhead talent string. Too many talent trees specified.\n", name() );
                        return null;
                    }

                    char c = talent_string[i];

                    if (c == 'Z') {
                        count = 0;
                        /*for (int j = 0; j <= tree; j++) {
                            count += glyph_trees[tree];
                        }*/
                        tree++;
                        continue;
                    }

                    decode_t decode = null;
                    for (int j=0; decoding[j].key != '\0' && decode==null; j++) {
                        if (decoding[j].key == c) { decode = decoding[j]; }
                    }

                    if (decode == null) {
                        //sim -> errorf( "Player %s has malformed wowhead talent string. Translation for '%c' unknown.\n", name(), c );
                        return null;
                    }

                    glyphEncoding[tree][count++] += (decode.first - '0') * 10 + decode.second - '0';
                    glyph_count[tree] += 1;

                    if (glyph_count[tree] >= (glyph_trees[tree])) { tree++; count = 0; }
                }
            }
            #endregion

            string newtalentstring = "";
            foreach (int i in encoding) { newtalentstring += i.ToString(); }
            if (hasGlyphs) {
                //newtalentstring += ".";
                //for (int t = 0; t < 3; t++) {
                //    foreach (int i in glyphEncoding[t]) { newtalentstring += i.ToString(); }
                //}
            }

            switch (characterclass)
            {
                case CharacterClass.Warrior: { return new WarriorTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.Paladin: { return new PaladinTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.Hunter: { return new HunterTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.Rogue: { return new RogueTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.Priest: { return new PriestTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.DeathKnight: { return new DeathKnightTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.Shaman: { return new ShamanTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.Mage: { return new MageTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.Warlock: { return new WarlockTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
                case CharacterClass.Druid: { return new DruidTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); }
            }
            return null;
        }