Exemple #1
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 #2
0
 public static bool HasTalent( MageTalents tal)
 {
     return TalentManager.IsSelected((int)tal);
 }
Exemple #3
0
 public static bool HasTalent(MageTalents tal)
 {
     return(TalentManager.IsSelected((int)tal));
 }
Exemple #4
0
        public virtual void CalculateDerivedStats(CastingState castingState, bool outOfFiveSecondRule, bool pom, bool spammedDot, bool round, bool forceHit, bool forceMiss, bool dotUptime)
        {
            MageTalents            mageTalents        = castingState.MageTalents;
            Stats                  baseStats          = castingState.BaseStats;
            CalculationOptionsMage calculationOptions = castingState.CalculationOptions;

            if (AreaEffect)
            {
                // do not count debuffs for aoe effects, can't assume it will be up on all
                // do not include molten fury (molten fury relates to boss), instead amplify all by average
                if (castingState.MoltenFury)
                {
                    SpellModifier /= (1 + 0.04f * castingState.MageTalents.MoltenFury);
                }
                if (castingState.MageTalents.MoltenFury > 0)
                {
                    SpellModifier *= (1 + 0.04f * castingState.MageTalents.MoltenFury * castingState.CalculationOptions.MoltenFuryPercentage);
                }
            }

            SpellModifier *= AdditiveSpellModifier;

            if (CritRate < 0.0f)
            {
                CritRate = 0.0f;
            }
            if (CritRate > 1.0f)
            {
                CritRate = 1.0f;
            }

            Ticks     = template.Ticks;
            CastProcs = template.CastProcs;
            HitProcs  = Ticks * HitRate;
            if (AreaEffect)
            {
                TargetProcs = HitProcs * castingState.CalculationOptions.AoeTargets;
            }
            else
            {
                TargetProcs = HitProcs;
            }

            Pom = pom;
            if (Instant)
            {
                InterruptProtection = 1;
            }

            CastTime = template.CalculateCastTime(castingState, InterruptProtection, CritRate, pom, BaseCastTime, out ChannelReduction);

            // add crit rate for on use stacking crit effects (would be better if it was computed
            // on cycle level, but right now the architecture doesn't allow that too well)
            // we'd actually need some iterations of this as cast time can depend on crit etc, just ignore that for now
            for (int i = 0; i < castingState.Solver.StackingNonHasteEffectCooldownsCount; i++)
            {
                EffectCooldown effectCooldown = castingState.Solver.StackingNonHasteEffectCooldowns[i];
                if (castingState.EffectsActive(effectCooldown.Mask))
                {
                    Stats stats = effectCooldown.SpecialEffect.Stats;
                    for (int j = 0; j < stats._rawSpecialEffectDataSize; j++)
                    {
                        SpecialEffect effect = stats._rawSpecialEffectData[j];
                        if (effect.Chance == 1f && effect.Cooldown == 0f && (effect.Trigger == Trigger.DamageSpellCrit || effect.Trigger == Trigger.SpellCrit))
                        {
                            if (effect.Stats.CritRating < 0 && effectCooldown.SpecialEffect.Stats.CritRating > 0)
                            {
                                float critScale = castingState.CalculationOptions.LevelScalingFactor / 1400f;
                                CritRate += SpecialEffect.GetAverageStackingCritRate(CastTime, effectCooldown.SpecialEffect.Duration, HitRate, CritRate, effectCooldown.SpecialEffect.Stats.CritRating * critScale, effect.Stats.CritRating * critScale, effect.MaxStack);
                                if (CritRate > 1.0f)
                                {
                                    CritRate = 1.0f;
                                }
                            }
                        }
                    }
                }
            }

            if (castingState.Frozen)
            {
                CritRate *= (1 + castingState.MageTalents.Shatter);
                if (CritRate > 1.0f)
                {
                    CritRate = 1.0f;
                }
            }

            CritProcs = HitProcs * CritRate;
            if ((MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire) && mageTalents.Ignite > 0)
            {
                IgniteProcs = CritProcs;
            }
            else
            {
                IgniteProcs = 0;
            }

            if (DotTickInterval > 0)
            {
                // non-spammed we have to take into account haste on dot duration and increase in number of ticks
                // probably don't want to take into account haste procs as that might skew optimization thresholds as we're averaging cast time over procs
                // reevaluate this if needed
                float x = DotTickInterval / DotDuration;
                DotExtraTicks   = (float)Math.Floor((castingState.CastingSpeed - 1) / x + 0.5);
                DotFullDuration = (DotDuration + DotTickInterval * DotExtraTicks) / castingState.CastingSpeed;
                if (spammedDot)
                {
                    // spammed dots no longer clip on reapplication
                    DotProcs = Math.Min(CastTime, DotDuration) / DotTickInterval;
                }
                else
                {
                    DotProcs = DotDuration / DotTickInterval + DotExtraTicks;
                }
            }
            else
            {
                DotProcs        = 0;
                DotFullDuration = 0;
                DotExtraTicks   = 0;
            }

            SpammedDot = spammedDot;
            if ((BaseMinDamage > 0 || BasePeriodicDamage > 0) && !forceMiss)
            {
                if (dotUptime)
                {
                    CalculateDirectAverageDamage(castingState.Solver, RawSpellDamage, forceHit);
                    AverageThreat = AverageDamage * ThreatMultiplier;

                    CalculateDotAverageDamage(castingState.Solver, RawSpellDamage, forceHit);
                    DotAverageThreat = DotAverageDamage * ThreatMultiplier;
                }
                else
                {
                    CalculateAverageDamage(castingState.Solver, RawSpellDamage, spammedDot, forceHit);
                    AverageThreat = AverageDamage * ThreatMultiplier;
                }
            }
            else
            {
                AverageDamage             = 0;
                AverageThreat             = 0;
                DamagePerSpellPower       = 0;
                DamagePerMastery          = 0;
                DamagePerCrit             = 0;
                IgniteDamage              = 0;
                IgniteDamagePerSpellPower = 0;
                IgniteDamagePerMastery    = 0;
                IgniteDamagePerCrit       = 0;
                if (dotUptime)
                {
                    DotAverageDamage       = 0;
                    DotAverageThreat       = 0;
                    DotDamagePerSpellPower = 0;
                    DotDamagePerMastery    = 0;
                    DotDamagePerCrit       = 0;
                }
            }
            if (ChannelReduction != 0)
            {
                Ticks               *= (1 - ChannelReduction);
                HitProcs            *= (1 - ChannelReduction);
                CritProcs           *= (1 - ChannelReduction);
                TargetProcs         *= (1 - ChannelReduction);
                CastProcs            = CastProcs2 + (CastProcs - CastProcs2) * (1 - ChannelReduction);
                AverageDamage       *= (1 - ChannelReduction);
                AverageThreat       *= (1 - ChannelReduction);
                DamagePerSpellPower *= (1 - ChannelReduction);
                DamagePerMastery    *= (1 - ChannelReduction);
                DamagePerCrit       *= (1 - ChannelReduction);
            }
            AverageCost = CalculateCost(castingState.Solver, round);

            Absorb      = 0;
            TotalAbsorb = 0;

            if (outOfFiveSecondRule)
            {
                OO5SR = 1;
            }
            else
            {
                OO5SR = 0;
            }
        }
Exemple #5
0
        public virtual void CalculateDerivedStats(CastingState castingState, bool outOfFiveSecondRule, bool pom, bool spammedDot, bool round, bool forceHit, bool forceMiss, bool dotUptime)
        {
            MageTalents            mageTalents        = castingState.MageTalents;
            Stats                  baseStats          = castingState.BaseStats;
            CalculationOptionsMage calculationOptions = castingState.CalculationOptions;

            SpellModifier *= AdditiveSpellModifier;

            if (CritRate < 0.0f)
            {
                CritRate = 0.0f;
            }
            if (CritRate > 1.0f)
            {
                CritRate = 1.0f;
            }

            Ticks     = template.Ticks;
            CastProcs = template.CastProcs;
            HitProcs  = Ticks * HitRate;
            CritProcs = HitProcs * CritRate;
            if ((MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire) && mageTalents.Ignite > 0)
            {
                IgniteProcs = CritProcs;
            }
            else
            {
                IgniteProcs = 0;
            }
            TargetProcs = HitProcs;

            Pom = pom;
            if (Instant)
            {
                InterruptProtection = 1;
            }

            CastTime = template.CalculateCastTime(castingState, InterruptProtection, CritRate, pom, BaseCastTime, out ChannelReduction);

            // add crit rate for on use stacking crit effects (would be better if it was computed
            // on cycle level, but right now the architecture doesn't allow that too well)
            // we'd actually need some iterations of this as cast time can depend on crit etc, just ignore that for now
            for (int i = 0; i < castingState.Solver.StackingNonHasteEffectCooldownsCount; i++)
            {
                EffectCooldown effectCooldown = castingState.Solver.StackingNonHasteEffectCooldowns[i];
                if (castingState.EffectsActive(effectCooldown.Mask))
                {
                    Stats stats = effectCooldown.SpecialEffect.Stats;
                    for (int j = 0; j < stats._rawSpecialEffectDataSize; j++)
                    {
                        SpecialEffect effect = stats._rawSpecialEffectData[j];
                        if (effect.Chance == 1f && effect.Cooldown == 0f && (effect.Trigger == Trigger.DamageSpellCrit || effect.Trigger == Trigger.SpellCrit))
                        {
                            if (effect.Stats.CritRating < 0 && effectCooldown.SpecialEffect.Stats.CritRating > 0)
                            {
                                float critScale = castingState.CalculationOptions.LevelScalingFactor / 1400f;
                                CritRate += SpecialEffect.GetAverageStackingCritRate(CastTime, effectCooldown.SpecialEffect.Duration, HitRate, CritRate, effectCooldown.SpecialEffect.Stats.CritRating * critScale, effect.Stats.CritRating * critScale, effect.MaxStack);
                                if (CritRate > 1.0f)
                                {
                                    CritRate = 1.0f;
                                }
                            }
                        }
                    }
                }
            }

            if (DotTickInterval > 0)
            {
                if (spammedDot)
                {
                    DotProcs = (float)Math.Floor(Math.Min(CastTime, DotDuration) / DotTickInterval);
                }
                else
                {
                    DotProcs = DotDuration / DotTickInterval;
                }
            }
            else
            {
                DotProcs = 0;
            }

            SpammedDot = spammedDot;
            if (Ticks > 0 && !forceMiss)
            {
                if (dotUptime)
                {
                    AverageDamage = CalculateDirectAverageDamage(castingState.Solver, RawSpellDamage, forceHit, out DamagePerSpellPower, out IgniteDamage, out IgniteDamagePerSpellPower);
                    AverageThreat = AverageDamage * ThreatMultiplier;

                    DotAverageDamage = CalculateDotAverageDamage(baseStats, calculationOptions, RawSpellDamage, forceHit, out DotDamagePerSpellPower);
                    DotAverageThreat = DotAverageDamage * ThreatMultiplier;
                }
                else
                {
                    AverageDamage = CalculateAverageDamage(castingState.Solver, RawSpellDamage, spammedDot, forceHit, out DamagePerSpellPower, out IgniteDamage, out IgniteDamagePerSpellPower);
                    AverageThreat = AverageDamage * ThreatMultiplier;
                }
            }
            else
            {
                AverageDamage             = 0;
                AverageThreat             = 0;
                DamagePerSpellPower       = 0;
                IgniteDamage              = 0;
                IgniteDamagePerSpellPower = 0;
                if (dotUptime)
                {
                    DotAverageDamage       = 0;
                    DotAverageThreat       = 0;
                    DotDamagePerSpellPower = 0;
                }
            }
            if (ChannelReduction != 0)
            {
                Ticks               *= (1 - ChannelReduction);
                HitProcs            *= (1 - ChannelReduction);
                CritProcs           *= (1 - ChannelReduction);
                TargetProcs         *= (1 - ChannelReduction);
                CastProcs            = CastProcs2 + (CastProcs - CastProcs2) * (1 - ChannelReduction);
                AverageDamage       *= (1 - ChannelReduction);
                AverageThreat       *= (1 - ChannelReduction);
                DamagePerSpellPower *= (1 - ChannelReduction);
            }
            AverageCost = CalculateCost(castingState.Solver, round);

            Absorb      = 0;
            TotalAbsorb = 0;

            if (outOfFiveSecondRule)
            {
                OO5SR = 1;
            }
            else
            {
                OO5SR = 0;
            }
        }
Exemple #6
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 #7
0
        public void InitializeEffectDamage(Solver solver, MagicSchool magicSchool, float minDamage, float maxDamage)
        {
            Stats                  baseStats          = solver.BaseStats;
            MageTalents            mageTalents        = solver.MageTalents;
            CalculationOptionsMage calculationOptions = solver.CalculationOptions;

            //AreaEffect = areaEffect;
            //BaseCost = cost - (int)baseStats.SpellsManaReduction;
            MagicSchool   = magicSchool;
            BaseMinDamage = minDamage;
            BaseMaxDamage = maxDamage;
            //BasePeriodicDamage = periodicDamage;
            //SpellDamageCoefficient = spellDamageCoefficient;
            //Ticks = 1;
            //CastProcs = 0;
            //CastProcs2 = 0;
            //DotDamageCoefficient = dotDamageCoefficient;
            //DotDuration = dotDuration;

            BaseDirectDamageModifier = 1.0f;
            BaseDotDamageModifier    = 1.0f;
            BaseCostModifier         = 1.0f;

            //Range = range;

            /*float baseCostAmplifier = calculationOptions.EffectCostMultiplier;
             * baseCostAmplifier *= (1.0f - 0.01f * mageTalents.Precision);
             * if (mageTalents.FrostChanneling > 0) baseCostAmplifier *= (1.0f - 0.01f - 0.03f * mageTalents.FrostChanneling);
             * if (MagicSchool == MagicSchool.Arcane) baseCostAmplifier *= (1.0f - 0.01f * mageTalents.ArcaneFocus);
             * BaseCostAmplifier = baseCostAmplifier;*/

            /*float baseInterruptProtection = baseStats.InterruptProtection;
             * if (MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire)
             * {
             *  baseInterruptProtection += 0.35f * mageTalents.BurningSoul;
             *  AffectedByFlameCap = true;
             * }
             * BaseInterruptProtection = baseInterruptProtection;*/

            float realResistance;

            switch (MagicSchool)
            {
            case MagicSchool.Arcane:
                BaseSpellModifier         = solver.BaseArcaneSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseArcaneAdditiveSpellModifier;
                BaseCritRate     = solver.BaseArcaneCritRate;
                CritBonus        = solver.BaseArcaneCritBonus;
                HitRate          = solver.BaseArcaneHitRate;
                ThreatMultiplier = solver.ArcaneThreatMultiplier;
                realResistance   = calculationOptions.ArcaneResist;
                break;

            case MagicSchool.Fire:
                BaseSpellModifier         = solver.BaseFireSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFireAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFireCritRate;
                CritBonus        = solver.BaseFireCritBonus;
                HitRate          = solver.BaseFireHitRate;
                ThreatMultiplier = solver.FireThreatMultiplier;
                realResistance   = calculationOptions.FireResist;
                break;

            case MagicSchool.FrostFire:
                BaseSpellModifier         = solver.BaseFrostFireSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFrostFireAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFrostFireCritRate;
                CritBonus        = solver.BaseFrostFireCritBonus;
                HitRate          = solver.BaseFrostFireHitRate;
                ThreatMultiplier = solver.FrostFireThreatMultiplier;
                if (calculationOptions.FireResist == -1)
                {
                    realResistance = calculationOptions.FrostResist;
                }
                else if (calculationOptions.FrostResist == -1)
                {
                    realResistance = calculationOptions.FireResist;
                }
                else
                {
                    realResistance = Math.Min(calculationOptions.FireResist, calculationOptions.FrostResist);
                }
                break;

            case MagicSchool.Frost:
                BaseSpellModifier         = solver.BaseFrostSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFrostAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFrostCritRate;
                CritBonus        = solver.BaseFrostCritBonus;
                HitRate          = solver.BaseFrostHitRate;
                ThreatMultiplier = solver.FrostThreatMultiplier;
                realResistance   = calculationOptions.FrostResist;
                break;

            case MagicSchool.Nature:
                BaseSpellModifier         = solver.BaseNatureSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseNatureAdditiveSpellModifier;
                BaseCritRate     = solver.BaseNatureCritRate;
                CritBonus        = solver.BaseNatureCritBonus;
                HitRate          = solver.BaseNatureHitRate;
                ThreatMultiplier = solver.NatureThreatMultiplier;
                realResistance   = calculationOptions.NatureResist;
                break;

            case MagicSchool.Shadow:
                BaseSpellModifier         = solver.BaseShadowSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseShadowAdditiveSpellModifier;
                BaseCritRate     = solver.BaseShadowCritRate;
                CritBonus        = solver.BaseShadowCritBonus;
                HitRate          = solver.BaseShadowHitRate;
                ThreatMultiplier = solver.ShadowThreatMultiplier;
                realResistance   = calculationOptions.ShadowResist;
                break;

            case MagicSchool.Holy:
            default:
                BaseSpellModifier         = solver.BaseHolySpellModifier;
                BaseAdditiveSpellModifier = solver.BaseHolyAdditiveSpellModifier;
                BaseCritRate     = solver.BaseHolyCritRate;
                CritBonus        = solver.BaseHolyCritBonus;
                HitRate          = solver.BaseHolyHitRate;
                ThreatMultiplier = solver.HolyThreatMultiplier;
                realResistance   = calculationOptions.HolyResist;
                break;
            }

            int playerLevel = calculationOptions.PlayerLevel;
            int targetLevel = calculationOptions.TargetLevel;

            /*if (areaEffect)
             * {
             *  targetLevel = calculationOptions.AoeTargetLevel;
             *  float hitRate = ((targetLevel <= playerLevel + 2) ? (0.96f - (targetLevel - playerLevel) * 0.01f) : (0.94f - (targetLevel - playerLevel - 2) * 0.11f)) + calculations.BaseSpellHit;
             *  if (MagicSchool == MagicSchool.Arcane) hitRate += 0.01f * mageTalents.ArcaneFocus;
             *  if (hitRate > Spell.MaxHitRate) hitRate = Spell.MaxHitRate;
             *  HitRate = hitRate;
             * }
             * else
             * {
             *  targetLevel = calculationOptions.TargetLevel;
             * }*/

            RealResistance      = realResistance;
            PartialResistFactor = (realResistance == -1) ? 0 : (1 - StatConversion.GetAverageResistance(playerLevel, targetLevel, realResistance, baseStats.SpellPenetration));
        }
Exemple #8
0
        public void InitializeDamage(Solver solver, bool areaEffect, int range, MagicSchool magicSchool, SpellData spellData, float hitProcs, float castProcs, float dotDuration)
        {
            Stats                  baseStats          = solver.BaseStats;
            MageTalents            mageTalents        = solver.MageTalents;
            CalculationOptionsMage calculationOptions = solver.CalculationOptions;

            AreaEffect             = areaEffect;
            BaseCost               = spellData.Cost - (int)baseStats.SpellsManaReduction;
            MagicSchool            = magicSchool;
            Ticks                  = hitProcs;
            CastProcs              = castProcs;
            CastProcs2             = castProcs;
            BaseMinDamage          = spellData.MinDamage;
            BaseMaxDamage          = spellData.MaxDamage;
            SpellDamageCoefficient = spellData.SpellDamageCoefficient;
            BasePeriodicDamage     = spellData.PeriodicDamage;
            DotDamageCoefficient   = spellData.DotDamageCoefficient;
            DotDuration            = dotDuration;

            BaseDirectDamageModifier = 1.0f;
            BaseDotDamageModifier    = 1.0f;
            BaseCostModifier         = 1.0f;

            float baseCostAmplifier = calculationOptions.EffectCostMultiplier;

            baseCostAmplifier *= (1.0f - 0.01f * mageTalents.Precision);
            if (mageTalents.FrostChanneling > 0)
            {
                baseCostAmplifier *= (1.0f - 0.01f - 0.03f * mageTalents.FrostChanneling);
            }
            if (MagicSchool == MagicSchool.Arcane)
            {
                baseCostAmplifier *= (1.0f - 0.01f * mageTalents.ArcaneFocus);
            }
            BaseCostAmplifier = baseCostAmplifier;

            float baseInterruptProtection = baseStats.InterruptProtection;

            if (MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire)
            {
                baseInterruptProtection += 0.35f * mageTalents.BurningSoul;
            }
            BaseInterruptProtection = baseInterruptProtection;

            float realResistance;

            switch (MagicSchool)
            {
            case MagicSchool.Arcane:
                BaseSpellModifier         = solver.BaseArcaneSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseArcaneAdditiveSpellModifier;
                BaseCritRate     = solver.BaseArcaneCritRate;
                CritBonus        = solver.BaseArcaneCritBonus;
                HitRate          = solver.BaseArcaneHitRate;
                ThreatMultiplier = solver.ArcaneThreatMultiplier;
                realResistance   = calculationOptions.ArcaneResist;
                if (range != 0)
                {
                    Range = range + mageTalents.MagicAttunement * 3;
                }
                break;

            case MagicSchool.Fire:
                BaseSpellModifier         = solver.BaseFireSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFireAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFireCritRate;
                CritBonus        = solver.BaseFireCritBonus;
                HitRate          = solver.BaseFireHitRate;
                ThreatMultiplier = solver.FireThreatMultiplier;
                realResistance   = calculationOptions.FireResist;
                if (range != 0)
                {
                    Range = range + mageTalents.FlameThrowing * 3;
                }
                break;

            case MagicSchool.FrostFire:
                BaseSpellModifier         = solver.BaseFrostFireSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFrostFireAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFrostFireCritRate;
                CritBonus        = solver.BaseFrostFireCritBonus;
                HitRate          = solver.BaseFrostFireHitRate;
                ThreatMultiplier = solver.FrostFireThreatMultiplier;
                if (calculationOptions.FireResist == -1)
                {
                    realResistance = calculationOptions.FrostResist;
                }
                else if (calculationOptions.FrostResist == -1)
                {
                    realResistance = calculationOptions.FireResist;
                }
                else
                {
                    realResistance = Math.Min(calculationOptions.FireResist, calculationOptions.FrostResist);
                }
                Range = range;
                break;

            case MagicSchool.Frost:
                BaseSpellModifier         = solver.BaseFrostSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFrostAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFrostCritRate;
                CritBonus        = solver.BaseFrostCritBonus;
                HitRate          = solver.BaseFrostHitRate;
                ThreatMultiplier = solver.FrostThreatMultiplier;
                realResistance   = calculationOptions.FrostResist;
                Range            = range * (1 + mageTalents.ArcticReach * 0.1f);
                break;

            case MagicSchool.Nature:
                BaseSpellModifier         = solver.BaseNatureSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseNatureAdditiveSpellModifier;
                BaseCritRate     = solver.BaseNatureCritRate;
                CritBonus        = solver.BaseNatureCritBonus;
                HitRate          = solver.BaseNatureHitRate;
                ThreatMultiplier = solver.NatureThreatMultiplier;
                realResistance   = calculationOptions.NatureResist;
                Range            = range;
                break;

            case MagicSchool.Shadow:
                BaseSpellModifier         = solver.BaseShadowSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseShadowAdditiveSpellModifier;
                BaseCritRate     = solver.BaseShadowCritRate;
                CritBonus        = solver.BaseShadowCritBonus;
                HitRate          = solver.BaseShadowHitRate;
                ThreatMultiplier = solver.ShadowThreatMultiplier;
                realResistance   = calculationOptions.ShadowResist;
                Range            = range;
                break;

            case MagicSchool.Holy:
            default:
                BaseSpellModifier         = solver.BaseHolySpellModifier;
                BaseAdditiveSpellModifier = solver.BaseHolyAdditiveSpellModifier;
                BaseCritRate     = solver.BaseHolyCritRate;
                CritBonus        = solver.BaseHolyCritBonus;
                HitRate          = solver.BaseHolyHitRate;
                ThreatMultiplier = solver.HolyThreatMultiplier;
                realResistance   = calculationOptions.HolyResist;
                Range            = range;
                break;
            }

            int playerLevel = calculationOptions.PlayerLevel;
            int targetLevel;

            if (areaEffect)
            {
                targetLevel = calculationOptions.AoeTargetLevel;
                float hitRate = ((targetLevel <= playerLevel + 2) ? (0.96f - (targetLevel - playerLevel) * 0.01f) : (0.94f - (targetLevel - playerLevel - 2) * 0.11f)) + solver.BaseSpellHit;
                if (MagicSchool == MagicSchool.Arcane)
                {
                    hitRate += 0.01f * mageTalents.ArcaneFocus;
                }
                if (hitRate > Spell.MaxHitRate)
                {
                    hitRate = Spell.MaxHitRate;
                }
                HitRate = hitRate;
            }
            else
            {
                targetLevel = calculationOptions.TargetLevel;
            }

            RealResistance      = realResistance;
            PartialResistFactor = (realResistance == -1) ? 0 : (1 - StatConversion.GetAverageResistance(playerLevel, targetLevel, realResistance, baseStats.SpellPenetration));
        }
Exemple #9
0
        public void InitializeDamage(Solver solver, bool areaEffect, int range, MagicSchool magicSchool, int cost, float minDamage, float maxDamage, float spellDamageCoefficient, float periodicDamage, float dotDamageCoefficient, float hitProcs, float castProcs, float dotDuration)
        {
            Stats                  baseStats          = solver.BaseStats;
            MageTalents            mageTalents        = solver.MageTalents;
            CalculationOptionsMage calculationOptions = solver.CalculationOptions;

            AreaEffect        = areaEffect;
            AreaEffectDot     = areaEffect;
            MaximumAOETargets = 10;
            int manaReduction = (int)baseStats.SpellsManaCostReduction;

            if (manaReduction == 405)
            {
                // Shard of Woe hax
                manaReduction = 205;
            }
            BaseCost               = Math.Max(cost - manaReduction, 0);
            MagicSchool            = magicSchool;
            Ticks                  = hitProcs;
            CastProcs              = castProcs;
            CastProcs2             = castProcs;
            BaseMinDamage          = minDamage;
            BaseMaxDamage          = maxDamage;
            SpellDamageCoefficient = spellDamageCoefficient;
            BasePeriodicDamage     = periodicDamage;
            DotDamageCoefficient   = dotDamageCoefficient;
            DotDuration            = dotDuration;

            BaseDirectDamageModifier = 1.0f;
            BaseDotDamageModifier    = 1.0f;
            BaseCostModifier         = 1.0f;

            float baseCostAmplifier = calculationOptions.EffectCostMultiplier;

            if (mageTalents.EnduringWinter > 0)
            {
                BaseCostModifier -= 0.03f * mageTalents.EnduringWinter + (mageTalents.EnduringWinter == 3 ? 0.01f : 0.00f);
            }
            BaseCostAmplifier = baseCostAmplifier;

            float baseInterruptProtection = baseStats.InterruptProtection;

            baseInterruptProtection += 0.23f * mageTalents.BurningSoul + (mageTalents.BurningSoul == 3 ? 0.01f : 0.0f);
            BaseInterruptProtection  = baseInterruptProtection;

            float realResistance;

            switch (MagicSchool)
            {
            case MagicSchool.Arcane:
                BaseSpellModifier         = solver.BaseArcaneSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseArcaneAdditiveSpellModifier;
                BaseCritRate     = solver.BaseArcaneCritRate;
                CritBonus        = solver.BaseArcaneCritBonus;
                HitRate          = solver.BaseArcaneHitRate;
                ThreatMultiplier = solver.ArcaneThreatMultiplier;
                realResistance   = calculationOptions.ArcaneResist;
                IgniteFactor     = 0;
                break;

            case MagicSchool.Fire:
                BaseSpellModifier         = solver.BaseFireSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFireAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFireCritRate;
                CritBonus        = solver.BaseFireCritBonus;
                HitRate          = solver.BaseFireHitRate;
                ThreatMultiplier = solver.FireThreatMultiplier;
                realResistance   = calculationOptions.FireResist;
                IgniteFactor     = solver.IgniteFactor;
                break;

            case MagicSchool.FrostFire:
                BaseSpellModifier         = solver.BaseFrostFireSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFrostFireAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFrostFireCritRate;
                CritBonus        = solver.BaseFrostFireCritBonus;
                HitRate          = solver.BaseFrostFireHitRate;
                ThreatMultiplier = solver.FrostFireThreatMultiplier;
                if (calculationOptions.FireResist == -1)
                {
                    realResistance = calculationOptions.FrostResist;
                }
                else if (calculationOptions.FrostResist == -1)
                {
                    realResistance = calculationOptions.FireResist;
                }
                else
                {
                    realResistance = Math.Min(calculationOptions.FireResist, calculationOptions.FrostResist);
                }
                Range        = range;
                IgniteFactor = solver.IgniteFactor;
                break;

            case MagicSchool.Frost:
                BaseSpellModifier         = solver.BaseFrostSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseFrostAdditiveSpellModifier;
                BaseCritRate     = solver.BaseFrostCritRate;
                CritBonus        = solver.BaseFrostCritBonus;
                HitRate          = solver.BaseFrostHitRate;
                ThreatMultiplier = solver.FrostThreatMultiplier;
                realResistance   = calculationOptions.FrostResist;
                IgniteFactor     = 0;
                break;

            case MagicSchool.Nature:
                BaseSpellModifier         = solver.BaseNatureSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseNatureAdditiveSpellModifier;
                BaseCritRate     = solver.BaseNatureCritRate;
                CritBonus        = solver.BaseNatureCritBonus;
                HitRate          = solver.BaseNatureHitRate;
                ThreatMultiplier = solver.NatureThreatMultiplier;
                realResistance   = calculationOptions.NatureResist;
                Range            = range;
                IgniteFactor     = 0;
                break;

            case MagicSchool.Shadow:
                BaseSpellModifier         = solver.BaseShadowSpellModifier;
                BaseAdditiveSpellModifier = solver.BaseShadowAdditiveSpellModifier;
                BaseCritRate     = solver.BaseShadowCritRate;
                CritBonus        = solver.BaseShadowCritBonus;
                HitRate          = solver.BaseShadowHitRate;
                ThreatMultiplier = solver.ShadowThreatMultiplier;
                realResistance   = calculationOptions.ShadowResist;
                Range            = range;
                IgniteFactor     = 0;
                break;

            case MagicSchool.Holy:
            default:
                BaseSpellModifier         = solver.BaseHolySpellModifier;
                BaseAdditiveSpellModifier = solver.BaseHolyAdditiveSpellModifier;
                BaseCritRate     = solver.BaseHolyCritRate;
                CritBonus        = solver.BaseHolyCritBonus;
                HitRate          = solver.BaseHolyHitRate;
                ThreatMultiplier = solver.HolyThreatMultiplier;
                realResistance   = calculationOptions.HolyResist;
                Range            = range;
                IgniteFactor     = 0;
                break;
            }

            NonHSCritRate = baseStats.SpellCritOnTarget;

            int playerLevel = calculationOptions.PlayerLevel;
            int targetLevel;

            if (areaEffect)
            {
                targetLevel = calculationOptions.AoeTargetLevel;
                float hitRate = ((targetLevel <= playerLevel + 2) ? (0.96f - (targetLevel - playerLevel) * 0.01f) : (0.94f - (targetLevel - playerLevel - 2) * 0.11f)) + solver.BaseSpellHit;
                if (hitRate > Spell.MaxHitRate)
                {
                    hitRate = Spell.MaxHitRate;
                }
                HitRate = hitRate;
            }
            else
            {
                targetLevel = calculationOptions.TargetLevel;
            }

            RealResistance      = realResistance;
            PartialResistFactor = (realResistance == -1) ? 0 : (1 - StatConversion.GetAverageResistance(playerLevel, targetLevel, realResistance, baseStats.SpellPenetration));
        }
        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;
        }