Ejemplo n.º 1
0
        public override Dictionary <string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary <string, string> dictValues = new Dictionary <string, string>();

            try {
                string format   = "";
                int    LevelDif = CombatFactors.LevelDif;

                // Base Stats
                #region Health & Stamina
                dictValues.Add("Health and Stamina", string.Format("{0:##,##0} : {1:##,##0}*" +
                                                                   "{2:00,000} : Base Health" +
                                                                   "\r\n{3:00,000} : Stam Bonus",
                                                                   AverageStats.Health, AverageStats.Stamina,
                                                                   BaseHealth,
                                                                   StatConversion.GetHealthFromStamina(AverageStats.Stamina)));
                #endregion
                dictValues.Add("Armor", string.Format("{0}", Armor));
                #region Strength
                {
                    int    formIter  = 1;
                    string theFormat = "";

                    float[] passiveContrsVals = new float[] {
                        BuffsStats.Strength,
                        BuffsStats.BonusStrengthMultiplier,
                        PlateSpecValid ? 0.05f : 0.00f,
                    };

                    string[] passiveContrs = new string[] {
                        "Buffs : Simple",
                        "Buffs : Multi",
                        "Plate Specialization",
                    };

                    theFormat += "{0:0.#}*"; // Averaged % and Averaged Rating
                    theFormat += "The Pane shows Averaged Values*";
                    //theFormat += "\r\n";
                    theFormat += "\r\n= Your Passive Contributions =";
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} : " + passiveContrs[0]; formIter++;
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":00.#%} : " + passiveContrs[1]; formIter++;
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":00.#%} : " + passiveContrs[2]; formIter++;
                    theFormat += "\r\n";
                    theFormat += "\r\n= UnProc'd =";
                    theFormat += "\r\nValue: {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;
                    theFormat += "\r\nIncreases Attack Power by {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;
                    theFormat += "\r\n";
                    theFormat += "\r\n= Proc'd =";
                    theFormat += "\r\nValue: {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;
                    theFormat += "\r\nIncreases Attack Power by {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;

                    dictValues.Add("Strength", string.Format(theFormat,
                                                             // Averaged Stats
                                                             AverageStats.Strength,
                                                             // Passive Contributions
                                                             passiveContrsVals[0], passiveContrsVals[1], passiveContrsVals[2],
                                                             // UnProc'd Stats
                                                             BuffedStats.Strength,
                                                             BuffedStats.Strength * 2f,
                                                             // Proc'd Stats
                                                             MaximumStats.Strength,
                                                             MaximumStats.Strength * 2f
                                                             ));
                }
                #endregion
                #region Attack Power
                dictValues.Add("Attack Power", string.Format("{0}*" +
                                                             "Increases White DPS by {1:0.0}\r\n" +
                                                             "\r\n" +
                                                             "Buffed: {2:0}\r\n" +
                                                             "Proc'd: {3:0}", (int)AverageStats.AttackPower, AverageStats.AttackPower / 14f,
                                                             BuffedStats.AttackPower, MaximumStats.AttackPower));
                #endregion
                #region Agility
                {
                    int    formIter  = 1;
                    string theFormat = "";

                    float[] passiveContrsVals = new float[] {
                        BuffsStats.Agility,
                        BuffsStats.BonusAgilityMultiplier,
                    };

                    string[] passiveContrs = new string[] { "Buffs : Simple", "Buffs : Multi" };

                    theFormat += "{0:0.#}*"; // Averaged % and Averaged Rating
                    theFormat += "The Pane shows Averaged Values*";
                    //theFormat += "\r\n";
                    theFormat += "\r\n= Your Passive Contributions =";
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} : " + passiveContrs[0]; formIter++;
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":00.#%} : " + passiveContrs[1]; formIter++;
                    theFormat += "\r\n";
                    theFormat += "\r\n= UnProc'd =";
                    theFormat += "\r\nIncreases Crit by {" + string.Format("{0}", formIter) + ":0.#%}"; formIter++;
                    //theFormat += "\r\nIncreases Armor by {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;
                    theFormat += "\r\n";
                    theFormat += "\r\n= Proc'd =";
                    theFormat += "\r\nIncreases Crit by {" + string.Format("{0}", formIter) + ":0.#%}"; formIter++;
                    //theFormat += "\r\nIncreases Armor by {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;

                    dictValues.Add("Agility", string.Format(theFormat,
                                                                                                                                            // Averaged Stats
                                                            AverageStats.Agility,
                                                                                                                                            // Passive Contributions
                                                            passiveContrsVals[0], passiveContrsVals[1],
                                                                                                                                            // UnProc'd Stats
                                                            StatConversion.GetCritFromAgility(BuffedStats.Agility, CharacterClass.Warrior),
                                                                                                                                            //StatConversion.GetArmorFromAgility(BuffedStats.Agility),
                                                                                                                                            // Proc'd Stats
                                                            StatConversion.GetCritFromAgility(MaximumStats.Agility, CharacterClass.Warrior) //,
                                                                                                                                            //StatConversion.GetArmorFromAgility(MaximumStats.Agility)
                                                            ));
                }
                #endregion
                #region Crit
                {
                    // sub to add neg number as pos, for overcapping to compensate
                    // for boss level on yellows (or whites, I dont remember which)
                    // Whites clip crit cap with glances, dodges, parries, misses
                    float WhCritCap = 1f;// +StatConversion.NPC_LEVEL_CRIT_MOD[LevelDif];
                    //float YwCritCap = 1f + StatConversion.NPC_LEVEL_CRIT_MOD[LevelDif];
                    if (CombatFactors.useOH)
                    {
                        WhCritCap -= (Whites.OHAtkTable.Glance + Whites.OHAtkTable.AnyNotLand);
                        //    YwCritCap -= (new AttackTable(combatFactors.Char, BuffedStats, combatFactors, combatFactors.CalcOpts, FW, false, false, false)).AnyNotLand;
                    }
                    else
                    {
                        WhCritCap -= (Whites.MHAtkTable.Glance + Whites.MHAtkTable.AnyNotLand);
                        // Yellows clip crit cap with dodges, parries, misses
                        //    YwCritCap -= (new AttackTable(combatFactors.Char, BuffedStats, combatFactors, combatFactors.CalcOpts, FW, true, false, false)).AnyNotLand;
                    }
                    float useRamp = 0f;
                    if (CombatFactors.Char.WarriorTalents.Rampage > 0f)
                    {
                        useRamp = 0.05f + 0.02f;
                    }
                    float[] passiveContrsVals = new float[] {
                        0.03192f,
                        AgilityCritBonus,
                        StatConversion.GetCritFromRating(BuffedStats.CritRating),
                        BuffsStats.PhysicalCrit + useRamp,
                    };
                    float passiveContrsTtlVal = passiveContrsVals[0] + passiveContrsVals[1]
                                                + passiveContrsVals[2] + passiveContrsVals[3];
                    string[] passiveContrs = new string[] { "Base Crit", "From Agility", "From Crit Rating", "Buffs" };

                    //float WhUnProcdCrit = StatConversion.GetCritFromRating(BuffedStats.CritRating + BuffedStats.DeathbringerProc);
                    float WhProcdCrit            = StatConversion.GetCritFromRating(MaximumStats.CritRating - BuffedStats.CritRating);
                    bool  isWhUnProcdOverCap     = passiveContrsTtlVal > WhCritCap;
                    bool  isWhProcdOverCap       = passiveContrsTtlVal + WhProcdCrit > WhCritCap;
                    float amountWhUnProcdOverCap = Math.Abs(StatConversion.GetRatingFromCrit(WhCritCap - passiveContrsTtlVal));
                    float amountWhProcdOverCap   = Math.Abs(StatConversion.GetRatingFromCrit(WhCritCap - (passiveContrsTtlVal + WhProcdCrit)));

                    //float YwUnProcdCrit = StatConversion.GetCritFromRating(BuffedStats.CritRating + BuffedStats.DeathbringerProc);
                    //float YwProcdCrit = StatConversion.GetCritFromRating(MaximumStats.CritRating + MaximumStats.DeathbringerProc);
                    //bool isYwUnProcdOverCap = passiveContrsTtlVal + YwUnProcdCrit > YwCritCap;
                    //bool isYwProcdOverCap = passiveContrsTtlVal + YwProcdCrit > YwCritCap;
                    //float amountYwUnProcdOverCap = Math.Abs(StatConversion.GetRatingFromCrit(YwCritCap - (passiveContrsTtlVal + YwUnProcdCrit)));
                    //float amountYwProcdOverCap = Math.Abs(StatConversion.GetRatingFromCrit(YwCritCap - (passiveContrsTtlVal + YwProcdCrit)));

                    string theFormat = GenFormattedString(passiveContrs, true,
                                                          isWhUnProcdOverCap, isWhProcdOverCap
                                                          //isYwUnProcdOverCap, isYwProcdOverCap
                                                          );

                    dictValues.Add("Crit", string.Format(theFormat,
                                                         // Averaged Stats
                                                         CritPercent, AverageStats.CritRating,
                                                         // Passive Contributions
                                                         passiveContrsVals[0], passiveContrsVals[1],
                                                         passiveContrsVals[2], passiveContrsVals[3],
                                                         // UnProc'd Stats
                                                         BuffedStats.CritRating,
                                                         Math.Min(WhCritCap, passiveContrsTtlVal), amountWhUnProcdOverCap,
                                                         //Math.Min(YwCritCap, passiveContrsTtlVal + YwUnProcdCrit), amountYwUnProcdOverCap,
                                                         // Proc'd Stats
                                                         MaximumStats.CritRating,
                                                         Math.Min(WhCritCap, passiveContrsTtlVal + WhProcdCrit), amountWhProcdOverCap
                                                         //Math.Min(YwCritCap, passiveContrsTtlVal + YwProcdCrit), amountYwProcdOverCap
                                                         ));
                }
                #endregion
                #region Armor Penetration
                {
                    //float ArPCap = 1.00f;
                    //float[] passiveContrsVals = new float[] { 1.00f };
                    //float passiveContrsTtlVal = passiveContrsVals[0];
                    //string[] passiveContrs = new string[] { "Colossus Smash (6 sec/20 sec). Sudden Death resets cooldown sooner." };
                    //float UnProcdArP = StatConversion.GetArmorPenetrationFromRating(BuffedStats.ArmorPenetrationRating);
                    //float ProcdArP = StatConversion.GetArmorPenetrationFromRating(MaximumStats.ArmorPenetrationRating);
                    //bool isUnProcdOverCap = passiveContrsTtlVal + UnProcdArP > ArPCap;
                    //bool isProcdOverCap = passiveContrsTtlVal + ProcdArP > ArPCap;
                    //float amountUnProcdOverCap = Math.Abs(StatConversion.GetRatingFromArmorPenetration(ArPCap - (passiveContrsTtlVal + UnProcdArP)));
                    //float amountProcdOverCap = Math.Abs(StatConversion.GetRatingFromArmorPenetration(ArPCap - (passiveContrsTtlVal + ProcdArP)));
                    //string theFormat = GenFormattedString(passiveContrs, true, isUnProcdOverCap, isProcdOverCap);
                    dictValues.Add("Armor Penetration", string.Format("{0:0%}",                                        //theFormat,
                                                                                                                       // Averaged Stats
                                                                      ArmorPenetration + AverageStats.ArmorPenetration //, AverageStats.ArmorPenetrationRating,
                                                                                                                       // Passive Contributions
                                                                                                                       //passiveContrsVals[0],
                                                                                                                       // UnProc'd Stats
                                                                                                                       //BuffedStats.ArmorPenetrationRating,
                                                                                                                       //Math.Min(ArPCap, passiveContrsTtlVal + UnProcdArP),
                                                                                                                       //amountUnProcdOverCap,
                                                                                                                       // Proc'd Stats
                                                                                                                       //MaximumStats.ArmorPenetrationRating,
                                                                                                                       //Math.Min(ArPCap, passiveContrsTtlVal + ProcdArP),
                                                                                                                       //amountProcdOverCap
                                                                      ));
                }
                #endregion
                #region Haste
                {
                    // Haste has no cap? Shouldn't there be a 100% cap or something?
                    // We should also state the before/after effects of haste on white swings
                    // Maybe a good point to show how much swing time is lost to Slams too?
                    float heroism = 0f;
                    if (BuffsStats._rawSpecialEffectData != null)
                    {
                        foreach (SpecialEffect effect in BuffsStats._rawSpecialEffectData)
                        {
                            if (effect != null && effect.Stats.PhysicalHaste > 0)
                            {
                                heroism = effect.GetAverageStats().PhysicalHaste;
                            }
                        }
                    }
                    float[] passiveContrsVals = new float[] {
                        CombatFactors.Char.WarriorTalents.BloodFrenzy * 0.025f,
                            BuffsStats.PhysicalHaste,
                            heroism,
                    };
                    float passiveContrsTtlVal = (1f + passiveContrsVals[0])
                                                * (1f + passiveContrsVals[1])
                                                * (1f + passiveContrsVals[2])
                                                - 1f;
                    string[] passiveContrs = new string[] { "Blood Frenzy", "Buffs", "Heroism (Averaged)" };
                    float    UnProcdHaste  = StatConversion.GetHasteFromRating(BuffedStats.HasteRating, CharacterClass.Warrior);
                    float    ProcdHaste    = StatConversion.GetHasteFromRating(MaximumStats.HasteRating, CharacterClass.Warrior);
                    string   theFormat     = GenFormattedString(passiveContrs);

                    dictValues.Add("Haste", string.Format(theFormat,
                                                          // Averaged Stats
                                                          HastePercent, AverageStats.HasteRating,
                                                          // Passive Contributions
                                                          passiveContrsVals[0], passiveContrsVals[1], passiveContrsVals[2],
                                                          // UnProc'd Stats
                                                          BuffedStats.HasteRating,
                                                          (1f + passiveContrsTtlVal) * (1f + UnProcdHaste) - 1f,
                                                          // Proc'd Stats
                                                          MaximumStats.HasteRating,
                                                          (1f + passiveContrsTtlVal) * (1f + ProcdHaste) - 1f
                                                          ));
                }
                #endregion
                #region Hit
                {
                    // old
                    float HitPercent   = StatConversion.GetHitFromRating(HitRating);
                    float HitPercBonus = AverageStats.PhysicalHit;
                    // Hit Soft Cap ratings check, how far from it
                    float capA1         = StatConversion.WHITE_MISS_CHANCE_CAP[LevelDif];
                    float convcapA1     = (float)Math.Ceiling(StatConversion.GetRatingFromHit(capA1));
                    float sec2lastNumA1 = (convcapA1 - StatConversion.GetRatingFromHit(HitPercent) - StatConversion.GetRatingFromHit(HitPercBonus)) * -1;
                    //float lastNumA1    = StatConversion.GetRatingFromExpertise((convcapA1 - Math.Min(MhExpertise, (OhExpertise != 0 ? OhExpertise : MhExpertise))) * -1);
                    // Hit Hard Cap ratings check, how far from it
                    float capA2         = StatConversion.WHITE_MISS_CHANCE_CAP_DW[LevelDif];
                    float convcapA2     = (float)Math.Ceiling(StatConversion.GetRatingFromHit(capA2));
                    float sec2lastNumA2 = (convcapA2 - StatConversion.GetRatingFromHit(HitPercent) - StatConversion.GetRatingFromHit(HitPercBonus)) * -1;
                    //float lastNumA2   = StatConversion.GetRatingFromExpertise((sec2lastNumA2 - Math.Min(MhExpertise, (OhExpertise != 0 ? OhExpertise : MhExpertise))) * -1);
                    dictValues.Add("Hit",
                                   string.Format("{0:000.00%} : {1}*" + "{2:0.00%} : From Other Bonuses" +
                                                 "\r\n{3:0.00%} : Total Hit % Bonus" +
                                                 "\r\n\r\nWhite Two-Hander Cap: " +
                                                 (sec2lastNumA1 > 0 ? "You can free {4:0} Rating"
                                                           : "You need {4:0} more Rating") +
                                                 "\r\nWhite Dual Wield Cap: " +
                                                 (sec2lastNumA2 > 0 ? "You can free {5:0} Rating"
                                                           : "You need {5:0} more Rating"),
                                                 StatConversion.GetHitFromRating(AverageStats.HitRating),
                                                 AverageStats.HitRating,
                                                 HitPercBonus,
                                                 HitPercent + HitPercBonus,
                                                 (sec2lastNumA1 > 0 ? sec2lastNumA1 : sec2lastNumA1 * -1),
                                                 (sec2lastNumA2 > 0 ? sec2lastNumA2 : sec2lastNumA2 * -1)
                                                 ));
                }
                #endregion
                #region Expertise
                {
                    // Dodge Cap ratings check, how far from it, uses lesser of MH and OH
                    // Also factors in Weapon Mastery
                    float capB1         = StatConversion.YELLOW_DODGE_CHANCE_CAP[LevelDif];
                    float convcapB1     = (float)Math.Ceiling(StatConversion.GetExpertiseFromDodgeParryReduc(capB1));
                    float sec2lastNumB1 = (convcapB1 - Math.Min(MHExpertise, (OHExpertise != 0 ? OHExpertise : MHExpertise))) * -1;
                    float lastNumB1     = StatConversion.GetRatingFromExpertise((convcapB1 - Math.Min(MHExpertise, (OHExpertise != 0 ? OHExpertise : MHExpertise))) * -1);
                    // Parry Cap ratings check, how far from it, uses lesser of MH and OH
                    float capB2         = StatConversion.YELLOW_PARRY_CHANCE_CAP[LevelDif];
                    float convcapB2     = (float)Math.Ceiling(StatConversion.GetExpertiseFromDodgeParryReduc(capB2));
                    float sec2lastNumB2 = (convcapB2 - Math.Min(MHExpertise, (OHExpertise != 0 ? OHExpertise : MHExpertise))) * -1;
                    float lastNumB2     = StatConversion.GetRatingFromExpertise((convcapB2 - Math.Min(MHExpertise, (OHExpertise != 0 ? OHExpertise : MHExpertise))) * -1);
                    dictValues.Add("Expertise",
                                   string.Format("{0:000.00%} : {1:00.00} : {2}*" +
                                                 "Following includes Racial bonus" +
                                                 "\r\n{3:00.00%} : {4:00.00} : MH" +
                                                 "\r\n{5:00.00%} : {6:00.00} : OH" +
                                                 "\r\n\r\n" + "Dodge Cap: " +
                                                 (lastNumB1 > 0 ? "You can free {7:0} Expertise ({8:0} Rating)"
                                     : "You need {7:0} more Expertise ({8:0} Rating)") +
                                                 "\r\n" + "Parry Cap: " +
                                                 (lastNumB2 > 0 ? "You can free {9:0} Expertise ({10:0} Rating)"
                                     : "You need {9:0} more Expertise ({10:0} Rating)"),
                                                 StatConversion.GetDodgeParryReducFromExpertise(Expertise),
                                                 Expertise,
                                                 AverageStats.ExpertiseRating,
                                                 StatConversion.GetDodgeParryReducFromExpertise(MHExpertise), MHExpertise,
                                                 StatConversion.GetDodgeParryReducFromExpertise(OHExpertise), OHExpertise,
                                                 (sec2lastNumB1 > 0 ? sec2lastNumB1 : sec2lastNumB1 * -1), (lastNumB1 > 0 ? lastNumB1 : lastNumB1 * -1),
                                                 (sec2lastNumB2 > 0 ? sec2lastNumB2 : sec2lastNumB2 * -1), (lastNumB2 > 0 ? lastNumB2 : lastNumB2 * -1)
                                                 ));
                }
                #endregion
                #region Mastery
                {
                    dictValues.Add("Mastery",
                                   string.Format("{0:000.00} : {1:00.00}*" + "The Pane shows Averaged Values*", MasteryVal, AverageStats.MasteryRating)
                                   + (CombatFactors.FuryStance ?
                                      string.Format(
                                          @"As a Fury Warrior, you are being granted the Unshackled Fury ability.
Increases the benefit of abilities that cause or require you to be enraged by (44.8%+{0:0.0%}={1:0.0%}).
Each point of Mastery increases enrage effects by an additional 5.60%.

Also, it is changing the following:
Increases the Weapon Damage of Raging Blow
Increases the damage bonus of Death Wish and Enrage
Increases the healing of Enraged Regeneration
Increases the Rage generated by Berserker Rage through Glyph of Berserker Rage
(Might increase rage generation through damage taken with Berserker Rage active,
I'm not sure, and it's a bit hard for me to test, any input on this would be appreciated.)",
                                          MasteryVal * 0.056f, 8f * 0.056f + MasteryVal * 0.056f)
                         :
                                      string.Format(
                                          @"As an Arms Warrior, you are being granted the Strikes of Opportunity ability.
Grants a (16%+{0:0.0%}={1:0.0%}) chance for your melee attacks to instantly
trigger an additional melee attack for {2:0%} normal damage. Each point of
Mastery increases this chance by 2%.",
                                          MasteryVal * Skills.StrikesOfOpportunity.BonusChance,
                                          Skills.StrikesOfOpportunity.BaseChance + MasteryVal * Skills.StrikesOfOpportunity.BonusChance,
                                          Skills.StrikesOfOpportunity.DamageModifier)
                                      )

                                   /*// Averaged Stats
                                    * AverageStats.Mastery,
                                    * // Passive Contributions
                                    * passiveContrsVals[0], passiveContrsVals[1], passiveContrsVals[2],
                                    * // UnProc'd Stats
                                    * BuffedStats.Mastery,
                                    * BuffedStats.Mastery * 2f,
                                    * // Proc'd Stats
                                    * MaximumStats.Mastery,
                                    * StatConversion.GetMasteryFromRating MaximumStats.Mastery * 2f*/
                                   );
                }
                #endregion

                dictValues.Add("Description 1", string.Format("DPS  : PerHit  : #ActsD"));
                dictValues.Add("Description 2", string.Format("DPS  : PerHit  : #ActsD"));
                dictValues.Add("Description 3", string.Format("DPS  : PerHit  : #ActsD"));
                // DPS Abilities
                format = "{0:0000} : {1:00000} : {2:000.00}";
                if (TotalDPS < 0f)
                {
                    TotalDPS = 0f;
                }
                foreach (AbilityWrapper aw in Rot.TheAbilityList)
                {
                    if (aw.Ability is Skills.Rend)
                    {
                        AbilityWrapper TH     = Rot.GetWrapper <Skills.ThunderClap>();
                        Skills.Rend    rend   = (aw.Ability as Skills.Rend);
                        float          DPSO20 = rend.GetDPS(aw.NumActivatesO20, TH.NumActivatesO20, rend.TimeOver20Perc);
                        float          DPSU20 = rend.GetDPS(aw.NumActivatesU20, TH.NumActivatesU20, rend.TimeUndr20Perc);
                        float          allDPS = DPSU20 > 0 ? DPSO20 * rend.TimeOver20Perc + DPSU20 * rend.TimeUndr20Perc : DPSO20; // Average above and below
                        dictValues.Add(aw.Ability.Name, string.Format(format, allDPS, rend.TickSize * rend.NumTicks, aw.AllNumActivates)
                                       + rend.GenTooltip(aw.AllNumActivates, DPSO20, DPSU20, allDPS / TotalDPS));
                    }
                    else
                    {
                        dictValues.Add(aw.Ability.Name, string.Format(format, aw.AllDPS, aw.Ability.DamageOnUse, aw.AllNumActivates)
                                       + aw.Ability.GenTooltip(aw.AllNumActivates, aw.DPS_O20, aw.DPS_U20, aw.AllDPS / TotalDPS));
                    }
                }
                // DPS General
                dictValues.Add("Deep Wounds", string.Format("{0:0000}*{1:00.0%} of DPS", Rot.DW.TickSize, Rot.DW.TickSize <= 0f || TotalDPS <= 0f ? 0f : Rot.DW.TickSize / TotalDPS));
                dictValues.Add("Special DMG Procs", string.Format("{0:0000} : {1:00000} : {2:000.00}*{3:00.0%} of DPS",
                                                                  SpecProcDPS, SpecProcDmgPerHit, SpecProcActs,
                                                                  SpecProcDPS <= 0f || TotalDPS <= 0f ? 0f : SpecProcDPS / TotalDPS));
                dictValues.Add("White DPS", string.Format("{0:0000} : {1:00000} : {2:000.00}", WhiteDPS, WhiteDmg, Whites.MHActivatesAll + Whites.OHActivatesAll)
                               + Whites.GenTooltip(WhiteDPSMH, WhiteDPSOH, TotalDPS, WhiteDPSMH_U20, WhiteDPSOH_U20));
                dictValues.Add("Total DPS", string.Format("{0:#,##0} : {1:#,###,##0}*" + ((!string.IsNullOrEmpty(Rot.GCDUsage) ? Rot.GCDUsage : "No GCD Usage*") + (CombatFactors.FuryStance ? "Fury isn't Cata Ready" : "")), TotalDPS, TotalDPS * Duration));
                // Rage
                format = "{0:0000}";
                dictValues.Add("Description 4", string.Format("Gen'd: Need : Avail"));
                dictValues.Add("Rage Above 20%", string.Format("{0:0000} : {1:0000} : {2:0000}", WhiteRageO20 + OtherRageO20, NeedyRageO20, FreeRageO20));
                dictValues.Add("Rage Below 20%", string.Format("{0:0000} : {1:0000} : {2:0000}", WhiteRageU20 + OtherRageU20, NeedyRageU20, FreeRageU20));
            } catch (Exception ex) {
                new Base.ErrorBox()
                {
                    Title        = "Error in creating Stat Pane Dictionaries",
                    Function     = "GetCharacterDisplayCalculationValues()",
                    TheException = ex,
                }.Show();
            }
            return(dictValues);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Copied this function from Arms, going to try and rewrite it so it works for Fury
        /// </summary>
        public float SettleAll(float totalPercTimeLost, float rageUsedByMaintenance, float percTimeU20, float availRageO20, out float percFailRageO20)
        {
            float percTimeO20         = (1f - percTimeU20);
            float percTimeInDPS       = (1f - totalPercTimeLost);
            float percTimeInDPSAndO20 = percTimeInDPS * percTimeO20;

            availRageO20 -= rageUsedByMaintenance * percTimeO20;

            /* The following are dependant on other attacks as they are proccing abilities or are the fallback item
             * We need to loop these until the activates are relatively unchanged
             * Heroic Strike, when there is rage to do so, handled by the Heroic Strike class
             * Alternate to Cleave is MultiTargs is active, but only to the perc of time where Targs is active
             * After iterating how many Overrides can be done and still do other abilities, then do the white dps
             *
             * Starting Assumptions:
             * No ability ever procs so Slam sucks up all the cooldowns (except under <20% with that active, where Exec sucks all of them)
             * Heroic Strike and Cleave won't be used at all
             *
             * Hoped Ending Results:
             * Since we shouldn't actually do normal Slam as Fury, Slams should end up zero (Bloodsurge DPS will be named as such instead of Slam)
             * All abilities will have proc'd and abilities that can proc from other ones will have their activates settled
             * Heroic Strikes and Cleave will activate when there's enough rage to support them AND Executes
             * Execute will get extra rage leftovers if there are any (since you won't use HS/CL <20%)
             */

            float preloopAvailGCDsO20 = GCDsAvailableO20, preloopAvailRageO20 = availRageO20;

            float oldGCDs_CS = 0f, oldGCDs_WW = 0f, oldGCDs_BT = 0f, oldGCDs_BS = 0f, oldGCDs_RB = 0f, oldGCDs_HS = 0f, oldGCDs_CL = 0f, oldGCDs_VR = 0f, oldGCDs_SL = 0f, oldActs_IR = 0f;


            AbilityWrapper CS = GetWrapper <ColossusSmash>();
            AbilityWrapper BT = GetWrapper <Bloodthirst>();
            AbilityWrapper BS = GetWrapper <BloodSurge>();
            AbilityWrapper RB = GetWrapper <RagingBlow>();
            AbilityWrapper HS = GetWrapper <HeroicStrike>();
            AbilityWrapper CL = GetWrapper <Cleave>();
            AbilityWrapper IR = GetWrapper <InnerRage>();

            AbilityWrapper WW = GetWrapper <Whirlwind>();
            AbilityWrapper SL = GetWrapper <Slam>();
            AbilityWrapper VR = GetWrapper <VictoryRush>();

            BT.NumActivatesO20 = preloopAvailGCDsO20 / 2f;
            DPSWarrChar.Whiteattacks.SlamActsOverDurO20 = 0;
            float origAvailRageO20   = preloopAvailRageO20;
            float repassAvailRageO20 = 0f;

            percFailRageO20 = 1f;

            int Iterator = 0;

            #region >20%
            while (Iterator < 50 && (
                       Math.Abs(CS.NumActivatesO20 - oldGCDs_CS) > 0.1f ||
                       Math.Abs(WW.NumActivatesO20 - oldGCDs_WW) > 0.1f ||
                       Math.Abs(BT.NumActivatesO20 - oldGCDs_BT) > 0.1f ||
                       Math.Abs(BS.NumActivatesO20 - oldGCDs_BS) > 0.1f ||
                       Math.Abs(RB.NumActivatesO20 - oldGCDs_RB) > 0.1f ||
                       Math.Abs(HS.NumActivatesO20 - oldGCDs_HS) > 0.1f ||
                       Math.Abs(CL.NumActivatesO20 - oldGCDs_CL) > 0.1f ||
                       Math.Abs(VR.NumActivatesO20 - oldGCDs_VR) > 0.1f ||
                       Math.Abs(SL.NumActivatesO20 - oldGCDs_SL) > 0.1f ||
                       Math.Abs(IR.NumActivatesO20 - oldActs_IR) > 0.1f))
            {
                // Reset items so we can keep iterating
                oldGCDs_CS         = CS.NumActivatesO20; oldGCDs_BT = BT.NumActivatesO20; oldGCDs_BS = BS.NumActivatesO20; oldGCDs_RB = RB.NumActivatesO20;
                oldGCDs_HS         = HS.NumActivatesO20; oldGCDs_CL = CL.NumActivatesO20;
                CS.NumActivatesO20 = BT.NumActivatesO20 = BS.NumActivatesO20 = RB.NumActivatesO20 = HS.NumActivatesO20 = CL.NumActivatesO20 = 0;
                availRageO20       = origAvailRageO20;
                availRageO20      += (float)((DPSWarrChar.Whiteattacks.MHActivatesO20 * DPSWarrChar.Whiteattacks.MHAtkTable.AnyLand) *
                                             6.5 * DPSWarrChar.CombatFactors.MH.Speed);
                if (DPSWarrChar.CombatFactors.useOH)
                {
                    availRageO20 += (float)((DPSWarrChar.Whiteattacks.OHActivatesO20 * DPSWarrChar.Whiteattacks.OHAtkTable.AnyLand) *
                                            6.5 * DPSWarrChar.CombatFactors.OH.Speed) / 2f;
                }

                float acts;

                // GCDsAvailableO20 is an expensive operation, trying to
                // see if I can speed it up by not calling it so much
                float gcdsAvailableO20 = GCDsAvailableO20;

                // ==== Primary Ability Priorities ====

                // Reduc abilities due to lack of Rage for maintaining the rotation
                if (repassAvailRageO20 < 0f || percFailRageO20 != 1f)
                {
                    // total the amount of rage you really need and turn it into a percentage that we failed
                    // if repassAvailRage was -100 and availRage was 900, then this becomes 1 + (-100 / 900 - (-100)) = 1 - 100/1000 = 90%
                    percFailRageO20 *= 1f + repassAvailRageO20 / (availRageO20 - repassAvailRageO20);
                    if (percFailRageO20 > 1f)
                    {
                        percFailRageO20 = 1f;
                    }
                }
                else
                {
                    percFailRageO20 = 1f;
                }

                // Inner Rage, Gives a 15% Damage Buff but 50% Rage Cost Debuff, should only be used when Rage is too high
                if (IR.Ability.Validated && percFailRageO20 == 1f)
                {
                    acts = IR.Ability.Activates * percTimeInDPSAndO20;
                    IR.NumActivatesO20 = acts;
                }

                // Colossus Smash, Highest Ability Prio because it gives 100% ArP when used
                if (CS.Ability.Validated)
                {
                    acts = Math.Min(gcdsAvailableO20, CS.Ability.Activates * percTimeInDPSAndO20 * percFailRageO20);
                    CS.NumActivatesO20 = acts;
                    availRageO20      -= CS.RageO20 * RageModTotal * RageModBattleTrance;
                    gcdsAvailableO20  -= CS.GCDUsageO20;
                }

                // Raging Blow
                if (RB.Ability.Validated)
                {
                    acts = Math.Min(gcdsAvailableO20, RB.Ability.Activates * percTimeInDPSAndO20 * percFailRageO20);
                    RB.NumActivatesO20 = acts;
                    availRageO20      -= RB.RageO20 * RageModTotal * RageModBattleTrance;
                    gcdsAvailableO20  -= RB.GCDUsageO20;
                }

                // Bloodthirst
                if (BT.Ability.Validated)
                {
                    acts = Math.Min(gcdsAvailableO20, BT.Ability.Activates * percTimeInDPSAndO20 * percFailRageO20);
                    BT.NumActivatesO20 = acts;
                    availRageO20      -= BT.RageO20 * RageModTotal;
                    gcdsAvailableO20  -= BT.GCDUsageO20;
                }

//                DoIterations(); // JOTHAY NOTE: Need to determine exactly what this is doing, may be able to push it to a GetActivates Function
                // Bloodsurge
                if (BS.Ability.Validated)
                {
                    acts = Math.Min(gcdsAvailableO20, (BS.Ability as BloodSurge).GetActivates(BT.NumActivatesO20, percTimeO20) * percTimeInDPS * percFailRageO20);
                    BS.NumActivatesO20 = acts;// *(1f - WWspace - CSspace - BTspace);
                    availRageO20      -= BS.RageO20 * RageModTotal;
                    gcdsAvailableO20  -= BS.GCDUsageO20;
                }


                // Slam
                if (SL.Ability.Validated)
                {
                    acts = Math.Min(gcdsAvailableO20, SL.Ability.Activates * percTimeInDPSAndO20);
                    SL.NumActivatesO20 = acts;
                    availRageO20      -= SL.RageO20 * RageModTotal * RageModBattleTrance;
                    gcdsAvailableO20  -= SL.GCDUsageO20;
                }
                else
                {
                    SL.NumActivatesO20 = 0f;
                }

                // Whirlwind
                if (WW.Ability.Validated)
                {
                    acts = Math.Min(gcdsAvailableO20, WW.Ability.Activates * percTimeInDPSAndO20 * percFailRageO20);
                    WW.NumActivatesO20 = acts;// *(1f - CSspace);
                    availRageO20      -= WW.RageO20 * RageModTotal * RageModBattleTrance;
                    gcdsAvailableO20  -= WW.GCDUsageO20;
                }


                // Victory Rush
                if (VR.Ability.Validated)
                {
                    // If Slam does more damage and we aren't failing at rage, then we ignore Victory Rush
                    if ((SL.Ability.Validated && VR.Ability.DamageOnUse > SL.Ability.DamageOnUse)
                        // if we are rage failing, VR is free so we might use that instead
                        || (percFailRageO20 != 1f)
                        // NOTE: Relearn this if
                        || (HS.Ability.Validated && percFailRageO20 == 1f && VR.Ability.DamageOnUse > SL.Ability.DamageOnUse))
                    {
                        acts = Math.Min(gcdsAvailableO20, VR.Ability.Activates * percTimeInDPSAndO20); // Since VR is Free, we don't reduc for Rage Fails
                        VR.NumActivatesO20 = acts;                                                     // *(1f - WWspace - CSspace - BTspace - BSspace - RBspace);
                        //availRage -= VR.Rage; // it's free
                        gcdsAvailableO20 -= VR.GCDUsageO20;
                    }
                }

                // Cleaves before Heroic Strikes
                if (percFailRageO20 >= 1f && CL.Ability.Validated && availRageO20 > 0 /*&& gcdsAvailableO20 > 0*/)
                {
                    // We are trying to limit this cause to whatever rage is remaining and
                    // not go overboard to make this thing think we are PercFailRaging
                    float multTargsPerc = DPSWarrChar.BossOpts.MultiTargsTime / FightDuration;
                    acts = CL.Ability.Activates * multTargsPerc * percTimeInDPSAndO20
                           * (1f / RageModTotal)
                           * (1f / RageModBattleTrance);
                    float clRageLimitedActs = (availRageO20 / CL.Ability.RageCost) * multTargsPerc * percTimeInDPSAndO20
                                              * (1f / RageModTotal)
                                              * (1f / RageModBattleTrance);
                    CL.NumActivatesO20 = Math.Min(clRageLimitedActs, acts);
                    availRageO20      -= CL.RageO20 * RageModTotal * RageModBattleTrance;
                }

                // Heroic Strikes, limited by rage and Cleaves
                if (percFailRageO20 >= 1f && HS.Ability.Validated && availRageO20 > 0 /*&& gcdsAvailableO20 > 0*/)
                {
                    // We are trying to limit this cause to whatever rage is remaining and
                    // not go overboard to make this thing think we are PercFailRaging
                    acts = HS.Ability.Activates * percTimeInDPSAndO20 - CL.NumActivatesO20;
                    float hsRageLimitedActs = (availRageO20 / (HS.Ability.RageCost * RageModTotal * RageModBattleTrance)) * percTimeInDPSAndO20
                                              - CL.NumActivatesO20;
                    HS.NumActivatesO20 = Math.Min(hsRageLimitedActs, acts);
                    availRageO20      -= HS.RageO20 * RageModTotal * RageModBattleTrance;
                }


                (HS.Ability as HeroicStrike).InciteBonusCrits(HS.NumActivatesO20);
                repassAvailRageO20 = availRageO20;
                // check for not enough rage to maintain rotation
                InvalidateCache();
                Iterator++;
            }
            #endregion

            float DPS_TTL = 0f;
            float rageNeededO20 = 0f, rageGenOtherO20 = 0f;
            foreach (AbilityWrapper aw in TheAbilityList)
            {
                DPS_TTL  += aw.DPS_O20;
                _HPS_TTL += aw.HPS_O20;
                if (aw.RageO20 > 0)
                {
                    rageNeededO20 += aw.RageO20;
                }
                else
                {
                    rageGenOtherO20 -= aw.RageO20;
                }
            }

            DPS_TTL += DPSWarrChar.Whiteattacks.GetMHdps(DPSWarrChar.Whiteattacks.MHActivatesO20, TimeOver20Perc) * percTimeInDPS;
            DPS_TTL += DPSWarrChar.Whiteattacks.GetOHdps(DPSWarrChar.Whiteattacks.OHActivatesO20, TimeOver20Perc) * percTimeInDPS;

            return(DPS_TTL);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Copied this function from Arms, going to try and rewrite it so it works for Fury
        /// </summary>
        public void MakeRotationAndDoDPS(bool setCalcs, float percTimeUnder20)
        {
            if (DPSWarrChar.Char.MainHand == null)
            {
                return;
            }
            _HPS_TTL = 0f;
            if (_needDisplayCalcs)
            {
                GCDUsage += string.Format("All=Over20%+Under20%. Only applicable if using Exec Spam\n{0:000.000}={1:000.000}+{2:000.000} : Total GCDs\n\n", NumGCDsAll, NumGCDsO20, NumGCDsU20);
            }

            // ==== Impedences ========================
            if (_needDisplayCalcs)
            {
                GCDUsage += "Impedences: Things that prevent you from DPS'g\n";
            }
            float TotalPercTimeLost = CalculateTimeLost();

            if (_needDisplayCalcs && TotalPercTimeLost <= 0f)
            {
                GCDUsage += "None\n\n";
            }
            else if (_needDisplayCalcs)
            {
                GCDUsage += (TotalPercTimeLost != 0f ? "\n" : "");
            }

            // ==== Rage Generation Priorities ========
            float availRage = 0f;
            float PercFailRageUnder20 = 1f, PercFailRageOver20 = 1f;

            availRage += RageGenOverDurOther + RageGainedWhileMoving;

            // ==== Maintenance and Anti-Impedence Priorities ====
            if (_needDisplayCalcs)
            {
                GCDUsage += "Maintenance: Things that you do periodically to Buff yourself or the raid\n";
            }
            float rageUsedByMaintenance = DoMaintenanceActivates(TotalPercTimeLost);

            // ==== Standard Priorities ===============
            if (_needDisplayCalcs)
            {
                GCDUsage += "Abilities: Things that you do to damage the Target. These are not in order of priority.\n";
            }
            _DPS_TTL_O20 = SettleAll(TotalPercTimeLost, rageUsedByMaintenance, percTimeUnder20, availRage, out PercFailRageOver20);
            if (percTimeUnder20 != 0f)
            {
                _DPS_TTL_U20 = SettleAllU20(TotalPercTimeLost, rageUsedByMaintenance, percTimeUnder20, availRage, out PercFailRageUnder20);
            }
            if (_needDisplayCalcs)
            {
                // We need to add Inner Rage now that we know how many there are
                AbilityWrapper aw = GetWrapper <InnerRage>();
                GCDUsage = aw.AllNumActivates > 0 ? GCDUsage.Insert(GCDUsage.IndexOf("Abilities") - 2,
                                                                    string.Format("{0:000.000}={1:000.000}+{2:000.000} : {3}{4}\n",
                                                                                  aw.AllNumActivates, aw.NumActivatesO20, aw.NumActivatesU20,
                                                                                  aw.Ability.Name, (!aw.Ability.UsesGCD ? " (Doesn't use GCDs)" : "")
                                                                                  ))
                    : GCDUsage;
            }

            CalcDeepWounds();
            _DPS_TTL_O20 += DW.TickSize;
            _DPS_TTL_U20 += DW.TickSize;

            // Add each of the abilities' DPS and HPS values and other aesthetics
            if (_needDisplayCalcs)
            {
                if (PercFailRageOver20 != 1.0f || PercFailRageUnder20 != 1.0f)
                {
                    GCDUsage += (PercFailRageOver20 < 1.0f ? string.Format("WARNING! You are losing {0:0.0%} of your abilities due\nto Rage Starvation before Execute Spam.\n", (1f - PercFailRageOver20)) : "");
                    GCDUsage += (PercFailRageUnder20 < 1.0f ? string.Format("WARNING! You are losing {0:0.0%} of your abilities due\nto Rage Starvation during Execute Spam.\n", (1f - PercFailRageUnder20)) : "");
                    GCDUsage += "\n";
                }
                List <AbilityWrapper> dmgAbils = DamagingAbilities;
                foreach (AbilityWrapper aw in dmgAbils)
                {
                    if (aw.AllNumActivates > 0 && !aw.Ability.IsMaint && !(aw.Ability is HeroicLeap))
                    {
                        GCDUsage += string.Format("{0:000.000}={1:000.000}+{2:000.000} : {3}{4}\n",
                                                  aw.AllNumActivates, aw.NumActivatesO20, aw.NumActivatesU20,
                                                  aw.Ability.Name,
                                                  aw.Ability.UsesGCD ? "" : " (Doesn't use GCDs)");
                    }
                }
                GCDUsage += string.Format("\n{0:000.000}={1:000.000}+{2:000.000} : Available GCDs (should be at or near zero)",
                                          GCDsAvailableO20 + (percTimeUnder20 != 0f ? GCDsAvailableU20 : 0f),
                                          GCDsAvailableO20,
                                          (percTimeUnder20 != 0f ? GCDsAvailableU20 : 0f));
            }

            // Return result
            if (setCalcs)
            {
                this.calcs.WhiteDPSMH     = DPSWarrChar.Whiteattacks.GetMHdps(DPSWarrChar.Whiteattacks.MHActivatesO20, TimeOver20Perc);
                this.calcs.WhiteDPSMH_U20 = DPSWarrChar.Whiteattacks.GetMHdps(DPSWarrChar.Whiteattacks.MHActivatesU20, TimeUndr20Perc);
                this.calcs.WhiteDPSOH     = DPSWarrChar.Whiteattacks.GetOHdps(DPSWarrChar.Whiteattacks.OHActivatesO20, TimeOver20Perc);
                this.calcs.WhiteDPSOH_U20 = DPSWarrChar.Whiteattacks.GetOHdps(DPSWarrChar.Whiteattacks.OHActivatesU20, TimeUndr20Perc);
                {
                    if (this.calcs.WhiteDPSMH > 0 && this.calcs.WhiteDPSMH_U20 > 0 && this.calcs.WhiteDPSOH > 0 && this.calcs.WhiteDPSOH_U20 > 0)
                    {
                        this.calcs.WhiteDPS = (this.calcs.WhiteDPSMH + this.calcs.WhiteDPSOH) * (1f - percTimeUnder20) + (this.calcs.WhiteDPSMH_U20 + this.calcs.WhiteDPSOH_U20) * (percTimeUnder20);
                    }
                    else if (this.calcs.WhiteDPSMH_U20 > 0 && this.calcs.WhiteDPSOH_U20 > 0)
                    {
                        this.calcs.WhiteDPS = this.calcs.WhiteDPSMH_U20 + this.calcs.WhiteDPSOH_U20;
                    }
                    else if (this.calcs.WhiteDPSMH > 0 && this.calcs.WhiteDPSOH > 0)
                    {
                        this.calcs.WhiteDPS = this.calcs.WhiteDPSMH + this.calcs.WhiteDPSOH;
                    }
                    else
                    {
                        this.calcs.WhiteDPS = 0f;
                    }
                }
                this.calcs.WhiteDmg = (this.DPSWarrChar.Whiteattacks.MHDamageOnUse + this.DPSWarrChar.Whiteattacks.OHDamageOnUse);

                {
                    if (_DPS_TTL_O20 > 0 && _DPS_TTL_U20 > 0)
                    {
                        this.calcs.TotalDPS = _DPS_TTL_O20 * (1f - percTimeUnder20) + _DPS_TTL_U20 * percTimeUnder20;
                    }
                    else if (_DPS_TTL_U20 > 0)
                    {
                        this.calcs.TotalDPS = _DPS_TTL_U20;
                    }
                    else if (_DPS_TTL_O20 > 0)
                    {
                        this.calcs.TotalDPS = _DPS_TTL_O20;
                    }
                    else
                    {
                        this.calcs.TotalDPS = 0f;
                    }
                }

                this.calcs.WhiteRageO20 = DPSWarrChar.Whiteattacks.MHRageGenOverDurO20 + DPSWarrChar.Whiteattacks.OHRageGenOverDurO20;
                this.calcs.OtherRageO20 = this.RageGenOverDurOtherO20;
                this.calcs.NeedyRageO20 = this.RageNeededOverDurO20;
                this.calcs.FreeRageO20  = calcs.WhiteRageO20 + calcs.OtherRageO20 - calcs.NeedyRageO20;

                this.calcs.WhiteRageU20 = DPSWarrChar.Whiteattacks.MHRageGenOverDurU20 + DPSWarrChar.Whiteattacks.OHRageGenOverDurU20;
                this.calcs.OtherRageU20 = this.RageGenOverDurOtherU20;
                this.calcs.NeedyRageU20 = this.RageNeededOverDurU20;
                this.calcs.FreeRageU20  = calcs.WhiteRageU20 + calcs.OtherRageU20 - calcs.NeedyRageU20;
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Copied this function from Arms, going to try and rewrite it so it works for Fury
        /// </summary>
        public float SettleAllU20(float totalPercTimeLost, float rageUsedByMaintenance, float percTimeU20, float availRageU20, out float percFailRageU20)
        {
            float percTimeInDPS       = (1f - totalPercTimeLost);
            float percTimeInDPSAndU20 = TimeUndr20Perc * percTimeInDPS;

            availRageU20 -= rageUsedByMaintenance * TimeUndr20Perc;

            /* The following are dependant on other attacks as they are proccing abilities or are the fallback item
             * We need to loop these until the activates are relatively unchanged
             * Heroic Strike, when there is rage to do so, handled by the Heroic Strike class
             * After iterating how many Overrides can be done and still do other abilities, then do the white dps
             *
             * Starting Assumptions:
             * No ability ever procs so Execute sucks up all the cooldowns
             * Heroic Strike and Cleave won't be used at all
             * Execute FreeRage is 0, no extra rage available
             *
             * Hoped Ending Results:
             * All abilities will have proc'd and abilities that can proc from other ones will have their activates settled
             * Execute will get extra rage leftovers if there are any (since you won't use HS/CL <20%)
             */

            float preloopAvailGCDsU20 = GCDsAvailableU20, preloopAvailRageU20 = availRageU20;

            float          origavailGCDsU20 = preloopAvailGCDsU20;
            AbilityWrapper CS         = GetWrapper <ColossusSmash>();
            AbilityWrapper EX         = GetWrapper <Execute>();
            AbilityWrapper RB         = GetWrapper <RagingBlow>();
            AbilityWrapper BT         = GetWrapper <Bloodthirst>();
            AbilityWrapper BS         = GetWrapper <BloodSurge>();
            Execute        EX_ability = EX.Ability as Execute;

            EX.NumActivatesU20 = origavailGCDsU20 * percTimeInDPS;
            DPSWarrChar.Whiteattacks.SlamActsOverDurU20 = 0f;
            float origAvailRageU20 = preloopAvailRageU20;

            availRageU20            += DPSWarrChar.Whiteattacks.WhiteRageGenOverDurU20 * percTimeInDPS;
            EX_ability.DumbActivates = EX.NumActivatesU20;
            EX_ability.FreeRage      = availRageU20;
            float repassAvailRageU20 = 0f;

            percFailRageU20 = 1f;

            bool rbOverExec = RB.Ability.Validated &&
                              EX.Ability.Validated &&
                              (EX.Ability.DamageOnUseOverride / (EX.Ability.GCDTime / LatentGCD))
                              < (RB.Ability.DamageOnUseOverride / (RB.Ability.GCDTime / LatentGCD)) &&
                              DPSWarrChar.CalcOpts.M_ExecuteSpamStage2;

            int Iterator = 0;

            #region <20%

            float oldBTGCDs = 0f;
            float oldRBGCDs = 0f;
            float oldEXGCDs = 0f;
            float oldCSGCDs = 0f;
            while (Iterator < 50 && (false ||
                                     Math.Abs(CS.NumActivatesU20 - oldCSGCDs) > 0.1f ||
                                     Math.Abs(EX.NumActivatesU20 - oldEXGCDs) > 0.1f ||
                                     Math.Abs(RB.NumActivatesU20 - oldRBGCDs) > 0.1f ||
                                     Math.Abs(BT.NumActivatesU20 - oldBTGCDs) > 0.1f
                                     ))
            {
                //oldBTGCDs = BT.NumActivatesU20;
                oldRBGCDs = RB.NumActivatesU20;
                oldCSGCDs = CS.NumActivatesU20;
                oldEXGCDs = EX.NumActivatesU20;
                // Set these all back to 0 so we can start fresh but factor the previous values where needed
                CS.NumActivatesU20 = EX.NumActivatesU20 = BT.NumActivatesU20 = RB.NumActivatesU20 = 0f;
                // Reset the Rage
                availRageU20 = origAvailRageU20;
                // TODO: I'd like to cache whiteRageGenOverDur but it changes with slams. Research a better solution
                availRageU20 += (float)((DPSWarrChar.Whiteattacks.MHActivatesU20 * DPSWarrChar.Whiteattacks.MHAtkTable.AnyLand) *
                                        6.5 * DPSWarrChar.CombatFactors.MH.Speed);
                if (DPSWarrChar.CombatFactors.useOH)
                {
                    availRageU20 += (float)((DPSWarrChar.Whiteattacks.OHActivatesU20 * DPSWarrChar.Whiteattacks.OHAtkTable.AnyLand) *
                                            6.5 * DPSWarrChar.CombatFactors.OH.Speed) / 2f;
                }
                //availRageU20 += DPSWarrChar.Whiteattacks.WhiteRageGenOverDurU20 * percTimeInDPS;

                float acts;
                // GCDsAvailableU20 is an expensive operation, trying to
                // see if I can speed it up by not calling it so much
                float gcdsAvailableU20 = GCDsAvailableU20;

                // ==== Primary Ability Priorities ====

                // Reduc abilities due to lack of Rage for maintaining the rotation
                if (repassAvailRageU20 < 0f || percFailRageU20 != 1f)
                {
                    // total the amount of rage you really need and turn it into a percentage that we failed
                    percFailRageU20 *= 1f + repassAvailRageU20 / (availRageU20 - repassAvailRageU20); // if repassAvailRage was -100 and availRage was 900, then this becomes 1 + (-100 / 900 - (-100)) = 1 - 100/1000 = 90%
                    if (percFailRageU20 > 1f)
                    {
                        percFailRageU20 = 1f;
                    }
                }
                else
                {
                    percFailRageU20 = 1f;
                }

                // Colossus Smash, Highest Ability Prio because it gives 100% ArP when used
                if (CS.Ability.Validated)
                {
                    acts = Math.Min(gcdsAvailableU20, CS.Ability.Activates * percTimeInDPSAndU20 * percFailRageU20);
                    CS.NumActivatesU20 = acts;
                    availRageU20      -= CS.RageU20 * RageModTotal;
                    gcdsAvailableU20  -= CS.GCDUsageU20;
                }

                if (RB.Ability.Validated && rbOverExec && gcdsAvailableU20 > 0)
                {
                    acts = Math.Min(gcdsAvailableU20, RB.Ability.Activates) * percTimeInDPSAndU20 * percFailRageU20;
                    RB.NumActivatesU20 = acts;// *(1f - WWspace - CSspace - BTspace - BSspace);
                    availRageU20      -= RB.RageU20 * RageModTotal;
                    gcdsAvailableU20  -= RB.GCDUsageU20;
                }
                // Execute for remainder of GCDs
                if (EX.Ability.Validated && gcdsAvailableU20 > 0)
                {
                    acts = gcdsAvailableU20 * percTimeInDPS;
                    EX.NumActivatesU20 = (EX.Ability as Execute).DumbActivates = acts;
                    availRageU20      -= EX.RageU20 * RageModTotal;
                }
                else
                {
                    EX.NumActivatesU20 = 0f;
                }

                (EX.Ability as Execute).FreeRage = repassAvailRageU20 = availRageU20; // check for not enough rage to maintain rotation and set Execute's FreeRage to this value
                InvalidateCache();
                Iterator++;
            }
            #endregion

            float DPS_TTL = 0f;
            float rageNeededU20 = 0f, rageGenOtherU20 = 0f;
            foreach (AbilityWrapper aw in TheAbilityList)
            {
                DPS_TTL  += aw.DPS_U20;
                _HPS_TTL += aw.HPS_U20;
                if (aw.RageU20 > 0)
                {
                    rageNeededU20 += aw.RageU20;
                }
                else
                {
                    rageGenOtherU20 -= aw.RageU20;
                }
            }

            DPS_TTL += DPSWarrChar.Whiteattacks.GetMHdps(DPSWarrChar.Whiteattacks.MHActivatesU20, TimeUndr20Perc) * percTimeInDPS;
            DPS_TTL += DPSWarrChar.Whiteattacks.GetOHdps(DPSWarrChar.Whiteattacks.OHActivatesU20, TimeUndr20Perc) * percTimeInDPS;

            return(DPS_TTL);
        }