Exemple #1
0
        public CombatFactors(Character character, StatsHunter stats, CalculationOptionsHunter calcOpts, BossOptions bossOpts)
        {
            Char = character;
            if (Char != null)
            {
                if (Char.Ranged != null)
                {
                    RW = Char.Ranged.Item;
                }
                else
                {
                    RW = new Knuckles();
                }
                if (Char.HunterTalents != null)
                {
                    Talents = Char.HunterTalents;
                }
                else
                {
                    Talents = new HunterTalents();
                }
            }
            CalcOpts = (calcOpts == null ? new CalculationOptionsHunter() : calcOpts);
            BossOpts = (bossOpts == null ? new BossOptions() : bossOpts);
            StatS    = stats;
            InvalidateCache();
            // Optimizations

            //Set_c_values();
        }
Exemple #2
0
 public HunterBase (Character charac, StatsHunter stats, HunterTalents talents, Specialization spec, int targetLvL)
 {
     character = charac;
     Stats = stats;
     Talents = talents;
     Tree = spec;
     TargetLevel = targetLvL;
 }
Exemple #3
0
 public HunterBase(Character charac, StatsHunter stats, HunterTalents talents, Specialization spec, int targetLvL)
 {
     character   = charac;
     Stats       = stats;
     Talents     = talents;
     Tree        = spec;
     TargetLevel = targetLvL;
 }
Exemple #4
0
        public Rotation(CombatFactors CF, HunterTalents t)
        {
            Char          = null;
            StatS         = null;
            Talents       = t;
            CombatFactors = CF;
            CalcOpts      = null;
            BossOpts      = null;

            AbilityList = new Dictionary <Type, AbilWrapper>();
            InvalidateCache();
        }
        public PetCalculations(Character character, CharacterCalculationsHunter calculatedStats, CalculationOptionsHunter calcopts, BossOptions bossOpts,
            StatsHunter hunterStats)
        {
            this.character = character;
            this.calculatedStats = calculatedStats;
            this.CalcOpts = calcopts;
            this.BossOpts = bossOpts;
            this.PetTalents = calcopts.PetTalents;
            this.Talents = character.HunterTalents;
            this.HunterStats = hunterStats;

            PetStats = new StatsHunter();
        }
Exemple #6
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);
        }
 public CombatFactors(Character character, StatsHunter stats, CalculationOptionsHunter calcOpts, BossOptions bossOpts)
 {
     Char = character;
     if (Char != null)
     {
         if (Char.Ranged != null)
             RW = Char.Ranged.Item;
         else
             RW = new Knuckles();
         if (Char.HunterTalents != null)
             Talents = Char.HunterTalents;
         else
             Talents = new HunterTalents();
     }
     CalcOpts = (calcOpts == null ? new CalculationOptionsHunter() : calcOpts);
     BossOpts = (bossOpts == null ? new BossOptions() : bossOpts);
     StatS = stats;
     InvalidateCache();
     // Optimizations
     
     //Set_c_values();
 }
Exemple #8
0
        public RotationType GetRotationType(HunterTalents t)
        {
            RotationType curRotationType = RotationType.Custom;

            if (t != null)
            {
                if (t.HighestTree == (int)Specialization.BeastMastery)
                {
                    // Beast Mastery
                    curRotationType = Rotation.RotationType.BeastMastery;
                }
                else if (t.HighestTree == (int)Specialization.Marksmanship)
                {
                    // Marksmanship
                    curRotationType = Rotation.RotationType.Marksmanship;
                }
                if (t.HighestTree == (int)Specialization.Survival)
                {
                    // Survival
                    curRotationType = Rotation.RotationType.Survival;
                }
            }
            return(curRotationType);
        }
Exemple #9
0
        public void validateShots(HunterTalents Talents)
        {
            // check each shot in the priority is allowed to be there.
            // this means removing dupes and anything after steady shot.
            // it also means removing shots which have already had their cooldowns used

            bool removeAll             = false;
            bool used_arcane_explosive = false;
            bool used_aimed_multi      = false;
            bool used_black_immo       = false;

            for (int i = 0; i < priorities.Length; i++)
            {
                // we've already seen steadyShot - remove everything else
                if (priorities[i] != null && removeAll)
                {
                    priorities[i].FailReason_SteadyBefore = true; priorities[i] = null;
                }

                // this shot has been used aleady
                if (priorities[i] != null && priorities[i].FailReason_AlreadyUsed)
                {
                    priorities[i] = null;
                }

                // we've already used a shot which shares cooldown with this one
                if (priorities[i] != null && (priorities[i].Type == Shots.ArcaneShot && used_arcane_explosive))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                if (priorities[i] != null && (priorities[i].Type == Shots.ExplosiveShot && used_arcane_explosive))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                if (priorities[i] != null && (priorities[i].Type == Shots.AimedShot && used_aimed_multi))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                if (priorities[i] != null && (priorities[i].Type == Shots.MultiShot && used_aimed_multi))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                if (priorities[i] != null && (priorities[i].Type == Shots.BlackArrow && used_black_immo))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                if (priorities[i] != null && (priorities[i].Type == Shots.ImmolationTrap && used_black_immo))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                if (priorities[i] != null && (priorities[i].Type == Shots.ExplosiveTrap && used_black_immo))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                if (priorities[i] != null && (priorities[i].Type == Shots.FreezingTrap && used_black_immo))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                if (priorities[i] != null && (priorities[i].Type == Shots.FrostTrap && used_black_immo))
                {
                    priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;
                }
                //if (priorities[i] != null && (priorities[i].Type == Shots.Volley && used_black_immo)) { priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null; }

                // Requires Multiple Targets
#if RAWR3 || SILVERLIGHT
                if (priorities[i] != null && (priorities[i].Type == Shots.Volley && (!BossOpts.MultiTargs || (BossOpts.MultiTargs && BossOpts.MultiTargsTime == 0))))
                {
                    priorities[i].FailReason_RequiresMultiTargs = true; priorities[i] = null;
                }
#else
                if (priorities[i] != null && (priorities[i].Type == Shots.Volley && (!CalcOpts.MultipleTargets || (CalcOpts.MultipleTargets && CalcOpts.MultipleTargetsPerc == 0))))
                {
                    priorities[i].FailReason_RequiresMultiTargs = true; priorities[i] = null;
                }
#endif

                // shots which require talents
                if (priorities[i] != null && priorities[i].Type == Shots.BlackArrow && Talents.BlackArrow == 0)
                {
                    priorities[i].FailReason_LackTalent = true; priorities[i] = null;
                }
                if (priorities[i] != null && priorities[i].Type == Shots.ChimearaShot && Talents.ChimeraShot == 0)
                {
                    priorities[i].FailReason_LackTalent = true; priorities[i] = null;
                }
                if (priorities[i] != null && priorities[i].Type == Shots.AimedShot && Talents.AimedShot == 0)
                {
                    priorities[i].FailReason_LackTalent = true; priorities[i] = null;
                }
                if (priorities[i] != null && priorities[i].Type == Shots.ExplosiveShot && Talents.ExplosiveShot == 0)
                {
                    priorities[i].FailReason_LackTalent = true; priorities[i] = null;
                }
                if (priorities[i] != null && priorities[i].Type == Shots.BestialWrath && Talents.BestialWrath == 0)
                {
                    priorities[i].FailReason_LackTalent = true; priorities[i] = null;
                }
                if (priorities[i] != null && priorities[i].Type == Shots.SilencingShot && Talents.SilencingShot == 0)
                {
                    priorities[i].FailReason_LackTalent = true; priorities[i] = null;
                }

                if (priorities[i] != null)
                {
                    priorities[i].FailReason_AlreadyUsed = true;
                    if (priorities[i].Type == Shots.SteadyShot)
                    {
                        removeAll = true;
                    }

                    if (priorities[i].Type == Shots.ArcaneShot)
                    {
                        used_arcane_explosive = true;
                    }
                    if (priorities[i].Type == Shots.ExplosiveShot)
                    {
                        used_arcane_explosive = true;
                    }
                    if (priorities[i].Type == Shots.AimedShot)
                    {
                        used_aimed_multi = true;
                    }
                    if (priorities[i].Type == Shots.MultiShot)
                    {
                        used_aimed_multi = true;
                    }
                    if (priorities[i].Type == Shots.BlackArrow)
                    {
                        used_black_immo = true;
                    }
                    if (priorities[i].Type == Shots.ImmolationTrap)
                    {
                        used_black_immo = true;
                    }
                    if (priorities[i].Type == Shots.ExplosiveTrap)
                    {
                        used_black_immo = true;
                    }
                    if (priorities[i].Type == Shots.FreezingTrap)
                    {
                        used_black_immo = true;
                    }
                    if (priorities[i].Type == Shots.FrostTrap)
                    {
                        used_black_immo = true;
                    }
                    //if (priorities[i].Type == Shots.Volley) used_black_immo = true;
                }
            }

            // store some derived facts about the rotation for later use
            chimeraRefreshesSerpent = false;
            chimeraRefreshesViper   = false;
            if (containsShot(Shots.ChimearaShot))
            {
                if (containsShot(Shots.SerpentSting))
                {
                    chimeraRefreshesSerpent = true;
                }
                if (containsShot(Shots.ViperSting))
                {
                    chimeraRefreshesViper = true;
                }
            }
        }
Exemple #10
0
 private static bool HasTalent(HunterTalents tal)
 {
     return TalentManager.IsSelected((int)tal);
 }
Exemple #11
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);
        }
 private Specialization GetSpecialization(HunterTalents t)
 {
     Specialization curSpecialization = Specialization.BeastMastery;
     if (t.HighestTree < EnumHelper.GetCount(typeof(Specialization)))
         curSpecialization = (Specialization)t.HighestTree;
     return curSpecialization;
 }
        private StatsHunter GetTalentStats(HunterTalents talents)
        {
            StatsHunter s = new StatsHunter();
            #region Specializations : Abilities/Bonuses from picking a tree.
            switch ((Specialization)talents.HighestTree)
            {
                // BM
                case Specialization.BeastMastery:
                    {
                        // Initimitation 
                        // TODO: Implemented in Shots/Abilities
                        // Animal Handler
                        s.BonusAttackPowerMultiplier += 0.25f;
                        // Mastery: Master of Beasts
                        // TODO: s.BonusPetDamageMultiplier += .13 + 0.0167 * Mastery
                        break;
                    }
                // MM
                case Specialization.Marksmanship:
                    {
                        // Aimed Shot
                        // TODO: Implemented in Shots/Abilities
                        // Artisan Quiver
                        // TODO: Auto-attack damage + 15%
                        // Mastery: Wild Quiver
                        // TODO: Additional Auto-Shot 16.8% + 2.1% per Mastery
                        break;
                    }
                // SV
                case Specialization.Survival:
                    {
                        // Explosive Shot
                        // TODO: Implemented in Shots/Abilities
                        // Intothe Wilderness
                        s.BonusAgilityMultiplier += .1f;
                        // Mastery: Essence of the Viper
                        // Bonus magic damage 8% + 1% per mastery
                        break;
                    }

            }
            #endregion

            #region Beast Master
            // Talent: Ferocious Inspiration
            s.BonusDamageMultiplier += talents.FerociousInspiration * 0.03f;
            #endregion

            #region Marksman
            // Talent: Trueshot Aura (Always on and part of the paperdoll numbers)
            s.BonusAttackPowerMultiplier += talents.TrueshotAura * 0.1f;
            #endregion
            #region Survival
            s.BonusStaminaMultiplier += talents.HunterVsWild * 0.05f;
            s.BonusHasteMultiplier += talents.Pathing * 0.01f;
            s.BonusAgilityMultiplier += talents.HuntingParty * 0.02f;
            // This conflics w/ IcyTalons and other similar buffs.
            s.PhysicalHaste += talents.HuntingParty * .1f;
            #endregion
            
            return s;
        }
Exemple #14
0
        public void validateShots(HunterTalents Talents) { 
            // check each shot in the priority is allowed to be there.
            // this means removing dupes and anything after steady shot.
            // it also means removing shots which have already had their cooldowns used

            bool removeAll = false;
            bool used_arcane_explosive = false;
            bool used_aimed_multi = false;
            bool used_black_immo = false;

            for (int i = 0; i < priorities.Length; i++) {
                // we've already seen steadyShot - remove everything else
                if (priorities[i] != null && removeAll) { priorities[i].FailReason_SteadyBefore = true; priorities[i] = null; }

                // this shot has been used aleady
                if (priorities[i] != null && priorities[i].FailReason_AlreadyUsed) priorities[i] = null;                

                // we've already used a shot which shares cooldown with this one
                if (priorities[i] != null && (priorities[i].Type == Shots.ArcaneShot && used_arcane_explosive)) { priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null; }
                if (priorities[i] != null && (priorities[i].Type == Shots.ExplosiveShot && used_arcane_explosive)){ priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;}
                if (priorities[i] != null && (priorities[i].Type == Shots.AimedShot && used_aimed_multi)){ priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;}
                if (priorities[i] != null && (priorities[i].Type == Shots.MultiShot && used_aimed_multi)){ priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;}
                if (priorities[i] != null && (priorities[i].Type == Shots.BlackArrow && used_black_immo)){ priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;}
                if (priorities[i] != null && (priorities[i].Type == Shots.ImmolationTrap && used_black_immo)){ priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null;}
                if (priorities[i] != null && (priorities[i].Type == Shots.ExplosiveTrap && used_black_immo)) { priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null; }
                if (priorities[i] != null && (priorities[i].Type == Shots.FreezingTrap && used_black_immo)) { priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null; }
                if (priorities[i] != null && (priorities[i].Type == Shots.IceTrap && used_black_immo)) { priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null; }
                //if (priorities[i] != null && (priorities[i].Type == Shots.Volley && used_black_immo)) { priorities[i].FailReason_SharedCooldownUsed = true; priorities[i] = null; }

                // Requires Multiple Targets ... TODO Zhok: Rly use multi here?
#if RAWR3 || RAWR4 || SILVERLIGHT
                if (priorities[i] != null && (priorities[i].Type == Shots.MultiShot && (!BossOpts.MultiTargs || (BossOpts.MultiTargs && BossOpts.MultiTargsTime == 0)))) { priorities[i].FailReason_RequiresMultiTargs = true; priorities[i] = null; }
#else
                if (priorities[i] != null && (priorities[i].Type == Shots.Volley && (!CalcOpts.MultipleTargets || (CalcOpts.MultipleTargets && CalcOpts.MultipleTargetsPerc == 0)))) { priorities[i].FailReason_RequiresMultiTargs = true; priorities[i] = null; }
#endif

                // shots which require talents
                if (priorities[i] != null && priorities[i].Type == Shots.BlackArrow && Talents.BlackArrow == 0){ priorities[i].FailReason_LackTalent = true; priorities[i] = null; }
                if (priorities[i] != null && priorities[i].Type == Shots.ChimeraShot && Talents.ChimeraShot == 0) { priorities[i].FailReason_LackTalent = true; priorities[i] = null; }
#if !RAWR4
                if (priorities[i] != null && priorities[i].Type == Shots.AimedShot && Talents.AimedShot == 0) { priorities[i].FailReason_LackTalent = true; priorities[i] = null; }
                if (priorities[i] != null && priorities[i].Type == Shots.ExplosiveShot && Talents.ExplosiveShot == 0){priorities[i].FailReason_LackTalent = true; priorities[i] = null;}
#endif
                if (priorities[i] != null && priorities[i].Type == Shots.BestialWrath && Talents.BestialWrath == 0) { priorities[i].FailReason_LackTalent = true; priorities[i] = null; }
                if (priorities[i] != null && priorities[i].Type == Shots.SilencingShot && Talents.SilencingShot == 0) { priorities[i].FailReason_LackTalent = true; priorities[i] = null; }

                if (priorities[i] != null)
                {
                    priorities[i].FailReason_AlreadyUsed = true;
                    if (priorities[i].Type == Shots.SteadyShot) removeAll = true;

                    if (priorities[i].Type == Shots.ArcaneShot) used_arcane_explosive = true;
                    if (priorities[i].Type == Shots.ExplosiveShot) used_arcane_explosive = true;
                    if (priorities[i].Type == Shots.AimedShot) used_aimed_multi = true;
                    if (priorities[i].Type == Shots.MultiShot) used_aimed_multi = true;
                    if (priorities[i].Type == Shots.BlackArrow) used_black_immo = true;
                    if (priorities[i].Type == Shots.ImmolationTrap) used_black_immo = true;
                    if (priorities[i].Type == Shots.ExplosiveTrap) used_black_immo = true;
                    if (priorities[i].Type == Shots.FreezingTrap) used_black_immo = true;
                    if (priorities[i].Type == Shots.IceTrap) used_black_immo = true;
                    //if (priorities[i].Type == Shots.Volley) used_black_immo = true;
                }
            }
   
            // store some derived facts about the rotation for later use
            chimeraRefreshesSerpent = false;
            chimeraRefreshesViper = false;
            if (containsShot(Shots.ChimeraShot))
            {
                if (containsShot(Shots.SerpentSting)) chimeraRefreshesSerpent = true;
            }
        }
        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;
        }