Beispiel #1
0
        public ArmsRotation(DPSWarrCharacter dpswarrchar) {
            DPSWarrChar = dpswarrchar;

            _cachedLatentGCD = 1.5f + DPSWarrChar.CalcOpts.Latency + DPSWarrChar.CalcOpts.AllowedReact;
            AbilityWrapper.LatentGCD = _cachedLatentGCD;
            _cachedNumGCDsO20 = FightDurationO20 / LatentGCD;
            _cachedNumGCDsU20 = FightDurationU20 / LatentGCD;
#if DEBUG
            //ConstructionCounts["Rotation Arms"]++;
#endif
        }
        public bool Equals(DPSWarrCharacter other)
        {
            if (Char != other.Char)
                return false;
            if (Rot != other.Rot)
                return false;
            if (CombatFactors != other.CombatFactors)
                return false;
            if (CalcOpts != other.CalcOpts)
                return false;
            if (BossOpts != other.BossOpts)
                return false;
            if (Talents != other.Talents)
                return false;

            return true;
        }
        private static void CalculateTriggers(DPSWarrCharacter charStruct, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances)
        {
#if DEBUG
            //ConstructionCounts["CalculateTriggers"]++;
            string addInfo = "No Additional Info";
#endif
            try
            {
                float fightDuration = charStruct.BossOpts.BerserkTimer;
                float fightDurationO20 = charStruct.Rot.FightDurationO20;
                float fightDurationU20 = charStruct.Rot.FightDurationU20;
#if DEBUG
                addInfo = "FightDur Passed";
#endif
                //float fightDuration2Pass = charStruct.calcOpts.SE_UseDur ? fightDuration : 0;

                float attemptedMH = charStruct.Rot.AttemptedAtksOverDurMH;
                float attemptedOH = charStruct.Rot.AttemptedAtksOverDurOH;
                float attempted = attemptedMH + attemptedOH;

                float landMH = charStruct.Rot.LandedAtksOverDurMH;
                float landOH = charStruct.Rot.LandedAtksOverDurOH;
                float land = landMH + landOH;

                float crit = charStruct.Rot.CriticalAtksOverDur;

                float avoidedAttacks = charStruct.CombatFactors.StatS.Dodge + charStruct.CombatFactors.StatS.Parry;

                float dwbleed = 0;
#if DEBUG
                addInfo += "\r\nBig if started";
#endif
                if (charStruct.Char.WarriorTalents.DeepWounds > 0 && crit != 0f)
                {
                    float dwTicks = 1f;
                    foreach (AbilityWrapper aw in charStruct.Rot.DamagingAbilities)
                    {
                        if (aw.AllNumActivates > 0f && aw.Ability.CanCrit)
                        {
                            if (aw.Ability.SwingsOffHand)
                            {

                                dwTicks *= (float)Math.Pow(1f - aw.Ability.MHAtkTable.Crit * (1f - aw.Ability.OHAtkTable.Crit), aw.AllNumActivates / fightDuration);
                                dwTicks *= (float)Math.Pow(1f - aw.Ability.OHAtkTable.Crit, aw.AllNumActivates / fightDuration);
                            }
                            else
                            {
                                // to fix this breaking apart when you're close to yellow crit cap for these abilities, namely OP
                                if (aw.Ability is Skills.Overpower || aw.Ability is Skills.TasteForBlood)
                                    dwTicks *= (1f - aw.AllNumActivates / fightDuration * aw.Ability.MHAtkTable.Crit);
                                else dwTicks *= (float)Math.Pow(1f - aw.Ability.MHAtkTable.Crit, aw.AllNumActivates / fightDuration);
                            }
                        }
                    }
                    dwbleed = fightDuration * dwTicks;
                }
#if DEBUG
                addInfo += "\r\nBuncha Floats started";
#endif
                float bleed = dwbleed + fightDuration * (charStruct.CombatFactors.FuryStance || !charStruct.CalcOpts.MaintenanceTree[(int)Maintenance.Rend] ? 0f : 1f / 3f);

                float bleedHitInterval = fightDuration / bleed;
                float dwbleedHitInterval = fightDuration / dwbleed;
                float attemptedAtkInterval = fightDuration / attempted;
                float attemptedAtksIntervalMH = fightDuration / attemptedMH;
                float attemptedAtksIntervalOH = fightDuration / attemptedOH;
                //float landedAtksInterval = fightDuration / land;
                float dmgDoneInterval = fightDuration / (land + bleed);
                float dmgTakenInterval = 0f;
                {
                    List<Attack> attacks = charStruct.BossOpts.Attacks.FindAll(a => a.AffectsRole[PLAYER_ROLES.MeleeDPS] && a.Validate);
                    float frequency = 0;
                    foreach (Attack a in attacks) {
                        frequency += 1f / a.AttackSpeed;
                    }
                    dmgTakenInterval = 1f / frequency;
                }
                
#if DEBUG
                addInfo += "\r\nAoETargsFreq Passed";
#endif
                float hitRate = 1, hitRateMH = 1, hitRateOH = 1, critRate = 1;
                if (attempted != 0f)
                {
                    hitRate = land / attempted;
                    critRate = crit / attempted;
                }
                if (attemptedMH != 0f)
                    hitRateMH = landMH / attemptedMH;
                if (attemptedOH != 0f)
                    hitRateOH = landOH / attemptedOH;

#if DEBUG
                addInfo += "\r\nTriggers started";
#endif
                triggerIntervals[Trigger.Use] = 0f;
                triggerChances[Trigger.Use] = 1f;

                triggerIntervals[Trigger.MeleeAttack] = attemptedAtkInterval;
                triggerChances[Trigger.MeleeAttack] = 1f;

                float mhWhites = charStruct.Rot.DPSWarrChar.Whiteattacks.MHActivatesAll;
                triggerIntervals[Trigger.WhiteAttack] = fightDuration / (mhWhites != 0 ? mhWhites : 1f);
                triggerChances[Trigger.WhiteAttack] = 1f;

                triggerIntervals[Trigger.WhiteCrit] = fightDuration / (mhWhites != 0 ? mhWhites : 1f);
                triggerChances[Trigger.WhiteCrit] = charStruct.Rot.DPSWarrChar.Whiteattacks.MHAtkTable.Crit;

                triggerIntervals[Trigger.WhiteHit] = fightDuration / (mhWhites != 0 ? mhWhites : 1f);
                triggerChances[Trigger.WhiteHit] = charStruct.Rot.DPSWarrChar.Whiteattacks.MHAtkTable.AnyLand;

                triggerIntervals[Trigger.MeleeHit] = triggerIntervals[Trigger.PhysicalHit] = triggerIntervals[Trigger.PhysicalAttack] = attemptedAtkInterval;
                triggerChances[Trigger.MeleeHit] = triggerChances[Trigger.PhysicalHit] = hitRate;
                triggerChances[Trigger.PhysicalAttack] = 1f;

                triggerIntervals[Trigger.PhysicalCrit] = triggerIntervals[Trigger.MeleeCrit] = attemptedAtkInterval;
                triggerChances[Trigger.PhysicalCrit] = triggerChances[Trigger.MeleeCrit] = critRate;

                triggerIntervals[Trigger.MainHandHit] = attemptedAtksIntervalMH;
                triggerChances[Trigger.MainHandHit] = hitRateMH;
                triggerIntervals[Trigger.OffHandHit] = attemptedAtksIntervalOH;
                triggerChances[Trigger.OffHandHit] = hitRateOH;

                triggerIntervals[Trigger.DoTTick] = bleedHitInterval;
                triggerChances[Trigger.DoTTick] = 1f;

                triggerIntervals[Trigger.DamageDone] = dmgDoneInterval;
                triggerChances[Trigger.DamageDone] = 1f;

                triggerIntervals[Trigger.DamageOrHealingDone] = dmgDoneInterval; // Need to add Self Heals
                triggerChances[Trigger.DamageOrHealingDone] = 1f;

                triggerIntervals[Trigger.DamageTaken] = dmgTakenInterval;
                triggerChances[Trigger.DamageTaken] = 1f;

                triggerIntervals[Trigger.DamageAvoided] = dmgTakenInterval;
                triggerChances[Trigger.DamageAvoided] = avoidedAttacks;

                triggerIntervals[Trigger.HSorSLHit] = fightDurationO20 / charStruct.Rot.CritHSSlamOverDur;
                triggerChances[Trigger.HSorSLHit] = 1f;

                triggerIntervals[Trigger.ExecuteHit] = fightDurationU20 / charStruct.Rot.GetWrapper<Skills.Execute>().NumActivatesU20;
                triggerChances[Trigger.ExecuteHit] = charStruct.Rot.GetWrapper<Skills.Execute>().Ability.MHAtkTable.AnyLand;

                triggerIntervals[Trigger.DeepWoundsTick] = dwbleedHitInterval;
                triggerChances[Trigger.DeepWoundsTick] = 1f;

                triggerIntervals[Trigger.DarkIntentCriticalPeriodicDamageOrHealing] = (float)charStruct.CalcOpts.DarkIntentDotTickInterval;
                triggerChances[Trigger.DarkIntentCriticalPeriodicDamageOrHealing] = (float)charStruct.CalcOpts.DarkIntentDotTickCritChance / 100f;

                triggerIntervals[Trigger.WWorCleaveHit] = fightDuration / (charStruct.Rot.GetWrapper<Skills.Whirlwind>().AllNumActivates + charStruct.Rot.GetWrapper<Skills.Cleave>().AllNumActivates);
                triggerChances[Trigger.WWorCleaveHit] = 1f;

                if (charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20 > 0 && charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesU20 > 0) {
                    triggerIntervals[Trigger.MortalStrikeCrit] =
                        (fightDurationO20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20) * (fightDurationO20 / fightDuration) +
                        (fightDurationU20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesU20) * (fightDurationU20 / fightDuration);
                } else if (charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesU20 > 0) {
                    triggerIntervals[Trigger.MortalStrikeCrit] = fightDurationU20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesU20;
                } else if (charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20 > 0) {
                    triggerIntervals[Trigger.MortalStrikeCrit] = fightDurationO20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20;
                } else {
                    triggerIntervals[Trigger.MortalStrikeCrit] = 0f;
                }
                triggerChances[Trigger.MortalStrikeCrit] = charStruct.Rot.GetWrapper<Skills.MortalStrike>().Ability.MHAtkTable.Crit;

                triggerIntervals[Trigger.MortalStrikeHit] = fightDurationO20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20;
                triggerChances[Trigger.MortalStrikeHit] = charStruct.Rot.GetWrapper<Skills.MortalStrike>().Ability.MHAtkTable.AnyLand;

                triggerIntervals[Trigger.ColossusSmashHit] = fightDuration / charStruct.Rot.GetWrapper<Skills.ColossusSmash>().AllNumActivates;
                triggerChances[Trigger.ColossusSmashHit] = charStruct.Rot.GetWrapper<Skills.ColossusSmash>().Ability.MHAtkTable.AnyLand;

                float opActs = charStruct.Rot.GetWrapper<Skills.Overpower>().AllNumActivates
                             + charStruct.Rot.GetWrapper<Skills.TasteForBlood>().AllNumActivates;
                float rbActs = charStruct.Rot.GetWrapper<Skills.RagingBlow>().AllNumActivates;
                triggerIntervals[Trigger.OPorRBAttack] = (((opActs + rbActs) > 0f) ? (fightDuration / (opActs + rbActs)) : 0f);
                triggerChances[Trigger.OPorRBAttack]   = 1f;

                triggerIntervals[Trigger.BattleorCommandingShout] = 60f - charStruct.Talents.BoomingVoice * 15f;
                triggerChances[Trigger.BattleorCommandingShout] = 1f;

#if DEBUG
                addInfo += "\r\nFinished";
#endif
            } catch (Exception ex) {
                new Base.ErrorBox() {
                    Title = "Error Calculating Triggers",
                    Function = "CalculateTriggers()",
#if DEBUG
                    Info = addInfo,
#endif
                    TheException = ex,
                }.Show();
            }
        }
 public static SpecialEffect GetDeathWishWithMastery(float masteryVal, DPSWarrCharacter dpswarrchar) {
     SpecialEffect retVal = new SpecialEffect(Trigger.Use,
             new Stats() { BonusDamageMultiplier = 0.20f * (1f + masteryVal), DamageTakenReductionMultiplier = (dpswarrchar.Talents.GlyphOfDeathWish ? 0f : -0.05f), },
             30f, 3f * 60f * (1f - 0.10f * dpswarrchar.Talents.IntensifyRage));
     return retVal;
 }
 public static SpecialEffect GetEnragedRegenerationWithMastery(float masteryVal, DPSWarrCharacter dpswarrchar) {
     SpecialEffect retVal = new SpecialEffect(Trigger.MeleeHit,
             new Stats() { BonusDamageMultiplier = 0.10f / 3f * dpswarrchar.Talents.Enrage * (1f + masteryVal), },
             9f, 0f, 0.03f * 3f);
     return retVal;
 }
 public static bool ValidatePlateSpec(DPSWarrCharacter dpswarchar)
 {
     // Null Check
     if (dpswarchar.Char == null) { return false; }
     // Item Type Fails
     if (dpswarchar.Char.Head == null || dpswarchar.Char.Head.Type != ItemType.Plate) { return false; }
     if (dpswarchar.Char.Shoulders == null || dpswarchar.Char.Shoulders.Type != ItemType.Plate) { return false; }
     if (dpswarchar.Char.Chest == null || dpswarchar.Char.Chest.Type != ItemType.Plate) { return false; }
     if (dpswarchar.Char.Wrist == null || dpswarchar.Char.Wrist.Type != ItemType.Plate) { return false; }
     if (dpswarchar.Char.Hands == null || dpswarchar.Char.Hands.Type != ItemType.Plate) { return false; }
     if (dpswarchar.Char.Waist == null || dpswarchar.Char.Waist.Type != ItemType.Plate) { return false; }
     if (dpswarchar.Char.Legs == null || dpswarchar.Char.Legs.Type != ItemType.Plate) { return false; }
     if (dpswarchar.Char.Feet == null || dpswarchar.Char.Feet.Type != ItemType.Plate) { return false; }
     // If it hasn't failed by now, it must be good
     return true;
 }
 public static bool ValidateSMTBonus(DPSWarrCharacter dpswarchar)
 {
     // Null Check
     if (dpswarchar.Char == null) { return false; }
     if (dpswarchar.Char.MainHand == null || dpswarchar.Char.OffHand == null) { return false; }
     // Item Type Fails
     if (dpswarchar.Char.MainHand.Type != ItemType.OneHandAxe
         && dpswarchar.Char.MainHand.Type != ItemType.OneHandSword
         && dpswarchar.Char.MainHand.Type != ItemType.OneHandMace) { return false; }
     if (dpswarchar.Char.OffHand.Type != ItemType.OneHandAxe
         && dpswarchar.Char.OffHand.Type != ItemType.OneHandSword
         && dpswarchar.Char.OffHand.Type != ItemType.OneHandMace) { return false; }
     // If it hasn't failed by now, it must be good
     return true;
 }
        private float ApplySpecialEffect(SpecialEffect effect, DPSWarrCharacter charStruct, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, ref Base.StatsWarrior applyTo)
        {
#if DEBUG
            //ConstructionCounts["ApplySpecialEffect"]++;
#endif
            float fightDuration = charStruct.BossOpts.BerserkTimer;
            float fightDuration2Pass = charStruct.CalcOpts.SE_UseDur ? fightDuration : 0;

            Stats effectStats = effect.Stats;

            float upTime = 0f;
            //float avgStack = 1f;
            /*if (effect.Stats.TargetArmorReduction > 0f || effect.Stats.ArmorPenetrationRating > 0f) {
                //int j = 0;
            }*/
            if (effect.Trigger == Trigger.Use) {
                if (effect.Stats._rawSpecialEffectDataSize == 1) {
                    upTime = effect.GetAverageUptime(0f, 1f, charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass);
                    //float uptime =  (effect.Cooldown / fightDuration);
                    List<SpecialEffect> nestedEffect = new List<SpecialEffect>();
                    nestedEffect.Add(effect.Stats._rawSpecialEffectData[0]);
                    Base.StatsWarrior _stats2 = new Base.StatsWarrior();
                    ApplySpecialEffect(effect.Stats._rawSpecialEffectData[0], charStruct, triggerIntervals, triggerChances, ref _stats2);
                    effectStats = _stats2;
                } else {
                    upTime = effect.GetAverageStackSize(0f, 1f, charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); 
                }
            } else if (effect.Duration == 0f && triggerIntervals.ContainsKey(effect.Trigger) && !float.IsInfinity(triggerIntervals[effect.Trigger])) {
                upTime = effect.GetAverageProcsPerSecond(triggerIntervals[effect.Trigger], 
                                                         triggerChances[effect.Trigger],
                                                         charStruct.CombatFactors.CMHItemSpeed,
                                                         fightDuration2Pass);
            } else if (effect.Trigger == Trigger.ExecuteHit) {
                upTime = effect.GetAverageStackSize(triggerIntervals[effect.Trigger], 
                                                         triggerChances[effect.Trigger],
                                                         charStruct.CombatFactors.CMHItemSpeed,
                                                         fightDuration2Pass * (float)charStruct.BossOpts.Under20Perc);
            } else if (triggerIntervals.ContainsKey(effect.Trigger) && !float.IsInfinity(triggerIntervals[effect.Trigger])) {
                upTime = effect.GetAverageStackSize(triggerIntervals[effect.Trigger], 
                                                         triggerChances[effect.Trigger],
                                                         charStruct.CombatFactors.CMHItemSpeed,
                                                         fightDuration2Pass);
            }

            if (upTime > 0f) {
                if (effect.Duration == 0f) {
                    applyTo.Accumulate(effectStats, upTime * fightDuration);
                } else if (upTime <= effect.MaxStack) {
                    applyTo.Accumulate(effectStats, upTime);
                }
                return upTime;
            }
            return 0f;
        }
        public Base.StatsWarrior GetBuffsStats(DPSWarrCharacter dpswarchar)
        {
            List<Buff> removedBuffs = new List<Buff>();
            List<Buff> addedBuffs = new List<Buff>();

            List<Buff> buffGroup = new List<Buff>();
            #region Maintenance Auto-Fixing
            // Removes the Sunder Armor if you are maintaining it yourself
            // Also removes Acid Spit and Expose Armor
            // We are now calculating this internally for better accuracy and to provide value to relevant talents
            if (dpswarchar.CalcOpts.M_SunderArmor) {
                buffGroup.Clear();
                buffGroup.Add(Buff.GetBuffBySpellId(7386));//Buff.GetBuffByName("Sunder Armor"));
                buffGroup.Add(Buff.GetBuffBySpellId(8647));//Buff.GetBuffByName("Expose Armor"));
                buffGroup.Add(Buff.GetBuffBySpellId(770));//Buff.GetBuffByName("Faerie Fire"));
                buffGroup.Add(Buff.GetBuffBySpellId(35387));//Buff.GetBuffByName("Corrosive Spit"));
                buffGroup.Add(Buff.GetBuffBySpellId(50498));//Buff.GetBuffByName("Tear Armor"));
                MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs);
            }

            // Removes the Shattering Throw Buff if you are maintaining it yourself
            // We are now calculating this internally for better accuracy and to provide value to relevant talents
            if (dpswarchar.CalcOpts.M_ShatteringThrow) {
                buffGroup.Clear();
                buffGroup.Add(Buff.GetBuffBySpellId(64382));//Buff.GetBuffByName("Shattering Throw"));
                MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs);
            }

            // Removes the Thunder Clap & Improved Buffs if you are maintaining it yourself
            // Also removes Judgements of the Just, Infected Wounds, Frost Fever, Improved Icy Touch
            // We are now calculating this internally for better accuracy and to provide value to relevant talents
            if (dpswarchar.CalcOpts.M_ThunderClap) {
                buffGroup.Clear();
                buffGroup.Add(Buff.GetBuffBySpellId(6343));//Buff.GetBuffByName("Thunder Clap"));
                buffGroup.Add(Buff.GetBuffBySpellId(59921));//Buff.GetBuffByName("Frost Fever"));
                buffGroup.Add(Buff.GetBuffBySpellId(53696));//Buff.GetBuffByName("Judgements of the Just"));
                buffGroup.Add(Buff.GetBuffBySpellId(48484));//Buff.GetBuffByName("Infected Wounds"));
                MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs);
            }

            // Removes the Demoralizing Shout & Improved Buffs if you are maintaining it yourself
            // We are now calculating this internally for better accuracy and to provide value to relevant talents
            if (dpswarchar.CalcOpts.M_DemoralizingShout) {
                buffGroup.Clear();
                buffGroup.Add(Buff.GetBuffBySpellId(1160));//Buff.GetBuffByName("Demoralizing Shout"));
                buffGroup.Add(Buff.GetBuffBySpellId(99));//Buff.GetBuffByName("Demoralizing Roar"));
                buffGroup.Add(Buff.GetBuffBySpellId(26016));//Buff.GetBuffByName("Vindication"));
                buffGroup.Add(Buff.GetBuffBySpellId(702));//Buff.GetBuffByName("Curse of Weakness"));
                buffGroup.Add(Buff.GetBuffBySpellId(81132));//Buff.GetBuffByName("Scarlet Fever"));
                MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs);
            }

            // Removes the Battle Shout & Commanding Presence Buffs if you are maintaining it yourself
            // Also removes their equivalent of Blessing of Might (+Improved)
            // We are now calculating this internally for better accuracy and to provide value to relevant talents
            if (dpswarchar.CalcOpts.M_BattleShout) {
                buffGroup.Clear();
                buffGroup.Add(Buff.GetBuffBySpellId(6673));//Buff.GetBuffByName("Battle Shout"));
                buffGroup.Add(Buff.GetBuffBySpellId(8075));//Buff.GetBuffByName("Strength of Earth Totem"));
                buffGroup.Add(Buff.GetBuffBySpellId(57730));//Buff.GetBuffByName("Horn of Winter"));
                buffGroup.Add(Buff.GetBuffBySpellId(93435));//Buff.GetBuffByName("Roar of Courage"));
                MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs);
            }

            // Removes the Commanding Shout & Commanding Presence Buffs if you are maintaining it yourself
            // Also removes their equivalent of Blood Pact (+Improved Imp)
            // We are now calculating this internally for better accuracy and to provide value to relevant talents
            if (dpswarchar.CalcOpts.M_CommandingShout) {
                buffGroup.Clear();
                buffGroup.Add(Buff.GetBuffBySpellId(469));//Buff.GetBuffByName("Commanding Shout"));
                buffGroup.Add(Buff.GetBuffBySpellId(21562));//Buff.GetBuffByName("Power Word: Fortitude"));
                buffGroup.Add(Buff.GetBuffBySpellId(90364));//Buff.GetBuffByName("Quiraji Fortitude"));
                buffGroup.Add(Buff.GetBuffBySpellId(6307));//Buff.GetBuffByName("Blood Pact"));
                MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs);
            }
            #endregion

            #region Passive Ability Auto-Fixing
            // Removes the Blood Frenzy Buff and it's equivalent of Savage Combat if you are maintaining it yourself
            // Cata also has BF giving what Trauma used to
            // We are now calculating this internally for better accuracy and to provide value to relevant talents
            if (dpswarchar.Char.WarriorTalents.BloodFrenzy > 0)
            {
                buffGroup.Clear();
                buffGroup.Add(Buff.GetBuffBySpellId(29859));//Buff.GetBuffByName("Blood Frenzy (Bleed)"));
                buffGroup.Add(Buff.GetBuffBySpellId(33876));//Buff.GetBuffByName("Mangle"));
                buffGroup.Add(Buff.GetBuffBySpellId(16511));//Buff.GetBuffByName("Hemorrhage"));
                buffGroup.Add(Buff.GetBuffBySpellId(50271));//Buff.GetBuffByName("Tendon Rip"));
                buffGroup.Add(Buff.GetBuffBySpellId(35290));//Buff.GetBuffByName("Gore"));
                buffGroup.Add(Buff.GetBuffBySpellId(57386));//Buff.GetBuffByName("Stampede"));
                //
                buffGroup.Add(Buff.GetBuffBySpellId(29859*10));//Buff.GetBuffByName("Blood Frenzy (Vuln)"));
                buffGroup.Add(Buff.GetBuffBySpellId(58413));//Buff.GetBuffByName("Savage Combat"));
                buffGroup.Add(Buff.GetBuffBySpellId(81328));//Buff.GetBuffByName("Brittle Bones"));
                buffGroup.Add(Buff.GetBuffBySpellId(50518));//Buff.GetBuffByName("Ravage"));
                buffGroup.Add(Buff.GetBuffBySpellId(55749));//Buff.GetBuffByName("Acid Spit"));
                MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs);
            }

            // Removes the Rampage Buff and it's equivalent of Leader of the Pack if you are maintaining it yourself
            // We are now calculating this internally for better accuracy and to provide value to relevant talents
            if (dpswarchar.Char.WarriorTalents.Rampage > 0 && dpswarchar.CombatFactors.FuryStance)
            {
                buffGroup.Clear();
                buffGroup.Add(Buff.GetBuffBySpellId(29801));//Buff.GetBuffByName("Rampage"));
                buffGroup.Add(Buff.GetBuffBySpellId(17007));//Buff.GetBuffByName("Leader of the Pack"));
                buffGroup.Add(Buff.GetBuffBySpellId(51701));//Buff.GetBuffByName("Honor Among Thieves"));
                buffGroup.Add(Buff.GetBuffBySpellId(51470));//Buff.GetBuffByName("Elemental Oath"));
                buffGroup.Add(Buff.GetBuffBySpellId(24604));//Buff.GetBuffByName("Furious Howl"));
                buffGroup.Add(Buff.GetBuffBySpellId(90309));//Buff.GetBuffByName("Terrifying Roar"));
                MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs);
            }
            #endregion

            Base.StatsWarrior statsBuffs = new Base.StatsWarrior();
            statsBuffs.Accumulate(GetBuffsStats(dpswarchar.Char.ActiveBuffs));
            AccumulateSetBonusStats(statsBuffs, dpswarchar.Char.SetBonusCount);

            if (dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 22738) != null) {
                statsBuffs.BonusWarrior_PvP_4P_InterceptCDReduc = 5f;
            }
            /*if (dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 70854) != null) {
                statsBuffs.BonusWarrior_PvP_4P_InterceptCDReduc = 5f;
            }
            if (dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 70847) != null) {
                statsBuffs.BonusWarrior_PvP_4P_InterceptCDReduc = 5f;
            }*/
            int T11count;
            dpswarchar.Char.SetBonusCount.TryGetValue("Earthen Warplate", out T11count);
            if (T11count >= 2) {//dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 90293) != null) {
                statsBuffs.BonusMortalStrikeDamageMultiplier = 0.05f;
                statsBuffs.BonusBloodthirstDamageMultiplier = 0.05f;
            }
            if (T11count >= 4) {//dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 90295) != null) {
                statsBuffs.AddSpecialEffect(_SE_4T11);
            }
            int T12count;
            dpswarchar.Char.SetBonusCount.TryGetValue("Molten Giant Warplate", out T12count);
            if (T12count >= 2) {
                statsBuffs.AddSpecialEffect(_SE_2T12[dpswarchar.Talents.BoomingVoice]);
            }
            if (T12count >= 4) {
                statsBuffs.AddSpecialEffect(_SE_4T12);
            }
            
            foreach (Buff b in removedBuffs) { dpswarchar.Char.ActiveBuffsAdd(b); }
            foreach (Buff b in addedBuffs) { dpswarchar.Char.ActiveBuffs.Remove(b); }

            return statsBuffs;
        }
 float getSurv(DPSWarrCharacter dpswarchar, int Iter, bool with)
 {
     bool orig = dpswarchar.CalcOpts.MaintenanceTree[Iter];
     dpswarchar.CalcOpts.MaintenanceTree[Iter] = with;
     CharacterCalculationsDPSWarr calculations = GetCharacterCalculations(dpswarchar.Char.Clone()) as CharacterCalculationsDPSWarr;
     dpswarchar.CalcOpts.MaintenanceTree[Iter] = orig;
     return calculations.TotalHPS;
 }
        private Stats IterativeSpecialEffectsStats(DPSWarrCharacter charStruct, List<SpecialEffect> specialEffects, List<SpecialEffect> critEffects,
            Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, float oldFlurryUptime,
            bool iterate, Base.StatsWarrior iterateOld, Base.StatsWarrior originalStats)
        {
#if DEBUG
            //ConstructionCounts["IterativeSpecialEffectsStats"]++;
#endif
            WarriorTalents talents = charStruct.Char.WarriorTalents;
            float fightDuration = charStruct.BossOpts.BerserkTimer;
            Base.StatsWarrior statsProcs = new Base.StatsWarrior();
            try {
                float dmgTakenInterval = fightDuration / charStruct.BossOpts.AoETargsFreq;

                //float attempted = charStruct.Rot.AttemptedAtksOverDur;
                //float land = charStruct.Rot.LandedAtksOverDur;
                //float crit = charStruct.Rot.CriticalAtksOverDur;

                //int LevelDif = charStruct.bossOpts.Level - charStruct.Char.Level;
                List<Trigger> critTriggers = new List<Trigger>();
                List<float> critWeights = new List<float>();
                bool needsHitTableReset = false;
                foreach (SpecialEffect effect in critEffects) {
                    needsHitTableReset = true;
                    critTriggers.Add(effect.Trigger);
                    critWeights.Add(1f);
                }
                foreach (SpecialEffect effect in specialEffects) {
                    #region old arp code
                    /*if (effect.Stats.ArmorPenetrationRating > 0) {
                        float arpenBuffs =
                            ((combatFactors.CmhItemType == ItemType.TwoHandMace) ? talents.MaceSpecialization * 0.03f : 0.00f) +
                            (!calcOpts.FuryStance ? (0.10f + originalStats.BonusWarrior_T9_2P_ArP) : 0.0f);

                        float OriginalArmorReduction = StatConversion.GetArmorDamageReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif],
                            originalStats.ArmorPenetration, arpenBuffs, originalStats.ArmorPenetrationRating);
                        float ProccedArmorReduction = StatConversion.GetArmorDamageReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif],
                            originalStats.ArmorPenetration + effect.Stats.ArmorPenetration, arpenBuffs, originalStats.ArmorPenetrationRating + effect.Stats.ArmorPenetrationRating);

                        Stats dummyStats = new Stats();
                        float procUptime = ApplySpecialEffect(effect, Char, Rot, combatFactors, calcOpts, originalStats.Dodge + originalStats.Parry, ref dummyStats);

                        float targetReduction = ProccedArmorReduction * procUptime + OriginalArmorReduction * (1f - procUptime);
                        //float arpDiff = OriginalArmorReduction - targetReduction;
                        float procArp = StatConversion.GetRatingFromArmorReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif],
                            originalStats.ArmorPenetration, arpenBuffs, targetReduction);
                        statsProcs.ArmorPenetrationRating += (procArp - originalStats.ArmorPenetrationRating);
                    } 
                    else */
                    #endregion
                    
                    float numProcs = 0;
                    if (effect.Stats.ManaorEquivRestore > 0f && effect.Stats.HealthRestoreFromMaxHealth > 0f) {
                        // effect.Duration = 0, so GetAverageStats won't work
                        float value1 = effect.Stats.ManaorEquivRestore;
                        float value2 = effect.Stats.HealthRestoreFromMaxHealth;
                        SpecialEffect dummy = new SpecialEffect(effect.Trigger, new Stats() { ManaorEquivRestore = value1, HealthRestoreFromMaxHealth = value2 }, effect.Duration, effect.Cooldown, effect.Chance) { BypassCache = true };
                        numProcs = dummy.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration;
                        statsProcs.ManaorEquivRestore += dummy.Stats.ManaorEquivRestore * numProcs;
                        dummy.Stats.ManaorEquivRestore = 0f;
                        //numProcs = effect.GetAverageProcsPerSecond(triggerIntervals[Trigger.PhysicalCrit], triggerChances[Trigger.PhysicalCrit], 0f, 0f) * fightDuration;
                        //statsProcs.HealthRestoreFromMaxHealth += effect.Stats.HealthRestoreFromMaxHealth * numProcs;
                        ApplySpecialEffect(dummy, charStruct, triggerIntervals, triggerChances, ref statsProcs);
                    } else if (effect.Stats.ManaorEquivRestore > 0f) {
                        // effect.Duration = 0, so GetAverageStats won't work
                        numProcs = effect.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration;
                        statsProcs.ManaorEquivRestore += effect.Stats.ManaorEquivRestore * numProcs;
                    } else if (effect.Stats.HealthRestoreFromMaxHealth > 0f) {
                        // effect.Duration = 0, so GetAverageStats won't work
                        numProcs = effect.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration;
                        statsProcs.HealthRestoreFromMaxHealth += effect.Stats.HealthRestoreFromMaxHealth * numProcs;
                    } else {
                        ApplySpecialEffect(effect, charStruct, triggerIntervals, triggerChances, ref statsProcs);
                    }
                }

                WeightedStat[] critProcs;
                if (critEffects.Count == 0) {
                    critProcs = new WeightedStat[] { new WeightedStat() { Value = 0f, Chance = 1f } };
                } else if (critEffects.Count == 1) {
                    float interval = triggerIntervals[critEffects[0].Trigger];
                    float chance = triggerChances[critEffects[0].Trigger];
                    float upTime = critEffects[0].GetAverageStackSize(interval, chance, charStruct.CombatFactors.CMHItemSpeed,
                        (charStruct.CalcOpts.SE_UseDur ? charStruct.BossOpts.BerserkTimer : 0f));
                    upTime *= critWeights[0];
                    critProcs = new WeightedStat[] { new WeightedStat() { Value = critEffects[0].Stats.CritRating, Chance = upTime },
                                                     new WeightedStat() { Value = 0f, Chance = 1f - upTime } };
                } else {
                    float[] intervals = new float[critEffects.Count];
                    float[] chances = new float[critEffects.Count];
                    float[] offset = new float[critEffects.Count];
                    for (int i = 0; i < critEffects.Count; i++) {
                        intervals[i] = triggerIntervals[critEffects[i].Trigger];
                        chances[i] = triggerChances[critEffects[i].Trigger];
                    }

                    critProcs = SpecialEffect.GetAverageCombinedUptimeCombinations(critEffects.ToArray(), intervals, chances, offset, critWeights.ToArray(), charStruct.CombatFactors.CMHItemSpeed,
                        charStruct.BossOpts.BerserkTimer, AdditiveStat.CritRating);
                }
                charStruct.CombatFactors.CritProcs = critProcs;
                float flurryUptime = 0f;
                if (iterate && talents.Flurry > 0f && charStruct.CombatFactors.FuryStance && charStruct.Char.MainHand != null && charStruct.Char.MainHand.Item != null)
                {
                    float numFlurryHits = 3f; // default
                    float mhPerc = 1f; // 100% by default
                    float flurryHaste = 0.25f / 3f * talents.Flurry;
                    bool useOffHand = false;
                    
                    float flurryHitsPerSec = charStruct.CombatFactors.TotalHaste * (1f + flurryHaste) / (1f + flurryHaste * oldFlurryUptime);
                    float temp = 1f / charStruct.Char.MainHand.Item.Speed;
                    if (charStruct.Char.OffHand != null && charStruct.Char.OffHand.Item != null) {
                        useOffHand = true;
                        temp += 1f / charStruct.Char.OffHand.Item.Speed;
                        mhPerc = (charStruct.Char.MainHand.Speed / charStruct.Char.OffHand.Speed) / (1f + charStruct.Char.MainHand.Speed / charStruct.Char.OffHand.Speed);
                        if (charStruct.Char.OffHand.Speed == charStruct.Char.MainHand.Speed) numFlurryHits = 4f;
                    }
                    
                    flurryHitsPerSec *= temp;
                    float flurryDuration = numFlurryHits / flurryHitsPerSec;
                    flurryUptime = 1f;
                    foreach (AbilityWrapper aw in charStruct.Rot.DamagingAbilities) {
                        if (aw.Ability.CanCrit && aw.AllNumActivates > 0f)
                        {
                            float tempFactor = (float) Math.Pow(1f - aw.Ability.MHAtkTable.Crit,
                                                                flurryDuration*
                                                                (aw.AllNumActivates*aw.Ability.SwingsPerActivate*
                                                                 aw.Ability.AvgTargets/fightDuration));
                            flurryUptime *= tempFactor;
                            if (aw.Ability.SwingsOffHand && useOffHand)
                            {
                                flurryUptime *= (float) Math.Pow(1f - aw.Ability.OHAtkTable.Crit,
                                                                 flurryDuration*
                                                                 (aw.AllNumActivates*aw.Ability.SwingsPerActivate*
                                                                  aw.Ability.AvgTargets/fightDuration));
                            }
                        }
                    }
                    flurryUptime *= (float)Math.Pow(1f - charStruct.Rot.DPSWarrChar.Whiteattacks.MHAtkTable.Crit, numFlurryHits * mhPerc);
                    flurryUptime *= (float)Math.Pow(1f - charStruct.Rot.DPSWarrChar.Whiteattacks.OHAtkTable.Crit, numFlurryHits * (1f - mhPerc));
                    flurryUptime = 1f - flurryUptime;
                    statsProcs.PhysicalHaste = (1f + statsProcs.PhysicalHaste) * (1f + flurryHaste * flurryUptime) - 1f;
                }

                charStruct.CombatFactors.StatS = UpdateStatsAndAdd(statsProcs, originalStats, charStruct.Char);
                charStruct.CombatFactors.InvalidateCache();
                //Rot.InvalidateCache();
                if (iterate) {
                    const float precisionWhole = 0.01f;
                    const float precisionDec = 0.0001f;
                    if (statsProcs.Agility - iterateOld.Agility > precisionWhole ||
                        statsProcs.HasteRating - iterateOld.HasteRating > precisionWhole ||
                        statsProcs.HitRating - iterateOld.HitRating > precisionWhole ||
                        statsProcs.CritRating - iterateOld.CritRating > precisionWhole ||
                        statsProcs.PhysicalHaste - iterateOld.PhysicalHaste > precisionDec ||
                        statsProcs.PhysicalCrit - iterateOld.PhysicalCrit > precisionDec ||
                        statsProcs.PhysicalHit - iterateOld.PhysicalHit > precisionDec)
                    {
                        if (needsHitTableReset) charStruct.Rot.ResetHitTables();
                        charStruct.Rot.DoIterations();
                        CalculateTriggers(charStruct, triggerIntervals, triggerChances);
                        return IterativeSpecialEffectsStats(charStruct,
                            specialEffects, critEffects, triggerIntervals, triggerChances, flurryUptime, true, statsProcs, originalStats);
                    } else { /*int j = 0;*/ }
                }

                return statsProcs;
            } catch (Exception ex) {
                new Base.ErrorBox()
                {
                    Title = "Error in creating SpecialEffects Stats",
                    Function = "GetSpecialEffectsStats()",
                    TheException = ex,
                }.Show();
                return new Stats();
            }
        }
        private Base.StatsWarrior GetCharacterStats(Character character, Item additionalItem, StatType statType, CalculationOptionsDPSWarr calcOpts, BossOptions bossOpts,
            out Base.StatsWarrior statsRace, out CombatFactors combatFactors, out Skills.WhiteAttacks whiteAttacks, out Rotation Rot)
        {
#if DEBUG
            //ConstructionCounts["GetCharacterStats_Inner"]++;
#endif
            DPSWarrCharacter dpswarchar = new DPSWarrCharacter { Char = character, CalcOpts = calcOpts, BossOpts = bossOpts, Talents = character.WarriorTalents, CombatFactors = null, Rot = null };
            Base.StatsWarrior statsTotal = GetCharacterStats_Buffed(dpswarchar, additionalItem, statType != StatType.Unbuffed, out statsRace);
            dpswarchar.StatS = statsTotal;
            combatFactors = new CombatFactors(character, statsTotal, calcOpts, bossOpts); // we have to regenerate it here
            dpswarchar.CombatFactors = combatFactors;
            whiteAttacks = new Skills.WhiteAttacks(dpswarchar);
            dpswarchar.Whiteattacks = whiteAttacks;
            if (combatFactors.FuryStance) Rot = new FuryRotation(dpswarchar);
            else Rot = new ArmsRotation(dpswarchar);
            dpswarchar.Rot = Rot;
            
            if (statType == (StatType.Buffed | StatType.Unbuffed))
            {
                return statsTotal;
            }
            // SpecialEffects: Supposed to handle all procs such as Berserking, Mirror of Truth, Grim Toll, etc.
            Rot.Initialize();
            Rot.MakeRotationandDoDPS(false, false);
            Rot.AddValidatedSpecialEffects(statsTotal, character.WarriorTalents);

            DPSWarrCharacter charStruct = new DPSWarrCharacter(){
                CalcOpts = calcOpts,
                BossOpts = bossOpts,
                Char = character,
                CombatFactors = combatFactors,
                Rot = Rot,
                Talents = character.WarriorTalents,
                StatS = statsTotal,
                Whiteattacks = whiteAttacks,
            };

            float fightDuration = bossOpts.BerserkTimer;

            List<SpecialEffect> bersMainHand = new List<SpecialEffect>();
            List<SpecialEffect> bersOffHand = new List<SpecialEffect>();

            if (character.MainHandEnchant != null/* && character.MainHandEnchant.Id == 3789*/) { // 3789 = Berserker Enchant ID, but now supporting other proc effects as well
                Stats.SpecialEffectEnumerator mhEffects = character.MainHandEnchant.Stats.SpecialEffects();
                if (mhEffects.MoveNext()) {
                    bersMainHand.Add(mhEffects.Current); 
                }
            }
            if (character.MainHand != null && character.MainHand.Item.Stats._rawSpecialEffectData != null) {
                Stats.SpecialEffectEnumerator mhEffects = character.MainHand.Item.Stats.SpecialEffects();
                if (mhEffects.MoveNext()) { bersMainHand.Add(mhEffects.Current); }
            }
            if (combatFactors.useOH && character.OffHandEnchant != null /*&& character.OffHandEnchant.Id == 3789*/) {
                Stats.SpecialEffectEnumerator ohEffects = character.OffHandEnchant.Stats.SpecialEffects();
                if (ohEffects.MoveNext()) { bersOffHand.Add(ohEffects.Current); }
            }
            if (character.OffHand != null && character.OffHand.Item.Stats._rawSpecialEffectData != null) {
                Stats.SpecialEffectEnumerator ohEffects = character.OffHand.Item.Stats.SpecialEffects();
                if (ohEffects.MoveNext()) { bersOffHand.Add(ohEffects.Current); }
            }
            if (statType == StatType.Average) {
                DoSpecialEffects(charStruct, bersMainHand, bersOffHand, statsTotal);
            }
            else // if (statType == StatType.Maximum)
            {
                Base.StatsWarrior maxSpecEffects = new Base.StatsWarrior();
                foreach (SpecialEffect effect in statsTotal.SpecialEffects()) maxSpecEffects.Accumulate(effect.Stats);
                return UpdateStatsAndAdd(maxSpecEffects as Base.StatsWarrior, combatFactors.StatS, character);
            }
            //UpdateStatsAndAdd(statsProcs, statsTotal, character); // Already done in GetSpecialEffectStats

            // special case for dual wielding w/ berserker enchant on one/both weapons, as they act independently
            //combatFactors.StatS = statsTotal;
            Base.StatsWarrior bersStats = new Base.StatsWarrior();
            foreach (SpecialEffect e in bersMainHand) {
                if (e.Duration == 0) {
                    bersStats.ShadowDamage = e.GetAverageProcsPerSecond(fightDuration / Rot.AttemptedAtksOverDurMH, Rot.LandedAtksOverDurMH / Rot.AttemptedAtksOverDurMH, combatFactors.CMHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0);
                } else {
                    // berserker enchant id
                    float f = e.GetAverageUptime(fightDuration / Rot.AttemptedAtksOverDurMH, Rot.LandedAtksOverDurMH / Rot.AttemptedAtksOverDurMH, combatFactors.CMHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0);
                    bersStats.Accumulate(e.Stats, f);
                }
            }
            foreach (SpecialEffect e in bersOffHand) {
                if (e.Duration == 0) {
                    bersStats.ShadowDamage += e.GetAverageProcsPerSecond(fightDuration / Rot.AttemptedAtksOverDurOH, Rot.LandedAtksOverDurOH / Rot.AttemptedAtksOverDurOH, combatFactors.COHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0);
                } else {
                    float f = e.GetAverageUptime(fightDuration / Rot.AttemptedAtksOverDurOH, Rot.LandedAtksOverDurOH / Rot.AttemptedAtksOverDurOH, combatFactors.COHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0);
                    bersStats.Accumulate(e.Stats, f);
                }
            }
            combatFactors.StatS = UpdateStatsAndAdd(bersStats, combatFactors.StatS, character);
            combatFactors.InvalidateCache();
            return combatFactors.StatS;
        }
        private void DoSpecialEffects(DPSWarrCharacter charStruct, List<SpecialEffect> bersMainHand, List<SpecialEffect> bersOffHand, Base.StatsWarrior statsTotal)
        {
#if DEBUG
            //ConstructionCounts["DoSpecialEffects"]++;
#endif
            #region Initialize Triggers
            Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>();
            Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>();

            CalculateTriggers(charStruct, triggerIntervals, triggerChances);
            #endregion

            #region ArPen Lists
            /*List<float> tempArPenRatings = new List<float>();
            List<float> tempArPenRatingsCapLimited = new List<float>();
            List<float> tempArPenRatingUptimes = new List<float>();
            List<SpecialEffect> tempArPenEffects = new List<SpecialEffect>();
            List<float> tempArPenEffectIntervals = new List<float>();
            List<float> tempArPenEffectChances = new List<float>();
            List<float> tempArPenEffectScales = new List<float>();*/
            #endregion

            // First Let's add InnerRage in, because that affects other calcs
            if (charStruct.CalcOpts.M_InnerRage) {
                AbilityWrapper ir = charStruct.Rot.GetWrapper<Skills.InnerRage>();
                statsTotal.Accumulate(((ir.Ability as Skills.InnerRage).Effect.Stats as Base.StatsWarrior), (ir.Ability as Skills.InnerRage).Effect.GetAverageUptime(0f, 1.00f));
            }

            List<SpecialEffect> critEffects = new List<SpecialEffect>();

            List<SpecialEffect> firstPass = new List<SpecialEffect>();
            List<SpecialEffect> secondPass = new List<SpecialEffect>();
            List<SpecialEffect> thirdPass = new List<SpecialEffect>();
            //bool doubleExecutioner = false;
            foreach (SpecialEffect effect in statsTotal.SpecialEffects())
            {
                effect.Stats.GenerateSparseData();

                if (!triggerIntervals.ContainsKey(effect.Trigger)) continue;
                else if (effect.Stats.CritRating > 0f)
                {
                    critEffects.Add(effect);
                }
                #region ArP Proc (which don't exist anymore)
                /*else if (effect.Stats.ArmorPenetrationRating > 0f)
                {
                    if (doubleExecutioner) continue;
                    Trigger realTrigger;
                    if (bersMainHand.Contains(effect))
                    {
                        bersMainHand.Remove(effect);
                        doubleExecutioner = true;
                        if (bersOffHand.Contains(effect))
                        {
                            bersOffHand.Remove(effect);
                            realTrigger = Trigger.MeleeHit;
                        }
                        else realTrigger = Trigger.MainHandHit;
                    }
                    else if (bersOffHand.Contains(effect))
                    {
                        bersOffHand.Remove(effect);
                        realTrigger = Trigger.OffHandHit;
                    }
                    else realTrigger = effect.Trigger;
                    tempArPenEffects.Add(effect);
                    tempArPenEffectIntervals.Add(triggerIntervals[realTrigger]);
                    tempArPenEffectChances.Add(triggerChances[realTrigger]);
                    tempArPenEffectScales.Add(1f);
                }*/
                #endregion
                else if (!bersMainHand.Contains(effect) && !bersOffHand.Contains(effect) &&
                   (effect.Stats.Agility > 0f ||
                    effect.Stats.HasteRating > 0f ||
                    effect.Stats.HitRating > 0f ||
                    effect.Stats.CritRating > 0f ||
                    effect.Stats.MasteryRating > 0f ||
                    effect.Stats.PhysicalHaste > 0f ||
                    effect.Stats.PhysicalCrit > 0f ||
                    effect.Stats.PhysicalHit > 0f))
                {
                    // These procs affect rotation
                    firstPass.Add(effect);
                }
                else if (!bersMainHand.Contains(effect) && !bersOffHand.Contains(effect) &&
                   (effect.Stats.FireDamage > 0 ||
                    effect.Stats.FireDamage > 0f ||
                    effect.Stats.NatureDamage > 0f ||
                    effect.Stats.ShadowDamage > 0f ||
                    effect.Stats.HolyDamage > 0f ||
                    effect.Stats.ArcaneDamage > 0f))
                {
                    // It's a Special Damage Proc
                    thirdPass.Add(effect);
                }
                else if (!bersMainHand.Contains(effect) && !bersOffHand.Contains(effect))
                {
                    // Any other stat proc
                    secondPass.Add(effect);
                }
            }

            #region ArP Proc Cap handling
            /*if (tempArPenEffects.Count == 0)
            {
                //tempArPenRatings.Add(0.0f);
                //tempArPenRatingUptimes.Add(1.0f);
            }
            else if (tempArPenEffects.Count == 1)
            { //Only one, add it to
                SpecialEffect effect = tempArPenEffects[0];
                float uptime = effect.GetAverageStackSize(tempArPenEffectIntervals[0], tempArPenEffectChances[0], charStruct.combatFactors.CmhItemSpeed,
                    (charStruct.calcOpts.SE_UseDur ? charStruct.bossOpts.BerserkTimer : 0f)) * tempArPenEffectScales[0];
                tempArPenRatings.Add(effect.Stats.ArmorPenetrationRating);
                tempArPenRatingUptimes.Add(uptime);
                tempArPenRatings.Add(0.0f);
                tempArPenRatingUptimes.Add(1.0f - uptime);
            }
            else if (tempArPenEffects.Count > 1)
            {
                //if (tempArPenEffects.Count >= 2)
                //{
                //    offset[0] = calcOpts.TrinketOffset;
                //}
                WeightedStat[] arPenWeights = SpecialEffect.GetAverageCombinedUptimeCombinations(tempArPenEffects.ToArray(), tempArPenEffectIntervals.ToArray(), tempArPenEffectChances.ToArray(), new float[tempArPenEffectChances.Count], tempArPenEffectScales.ToArray(), charStruct.combatFactors.CmhItemSpeed,
                    charStruct.bossOpts.BerserkTimer,
                    AdditiveStat.ArmorPenetrationRating);
                for (int i = 0; i < arPenWeights.Length; i++)
                {
                    tempArPenRatings.Add(arPenWeights[i].Value);
                    tempArPenRatingUptimes.Add(arPenWeights[i].Chance);
                }
            }*/
            // Get the average Armor Pen Rating across all procs
            /*if (tempArPenRatings.Count > 0f)
            {
                Stats originalStats = charStruct.combatFactors.StatS;
                int LevelDif = charStruct.bossOpts.Level- charStruct.Char.Level;

                float arpenBuffs = 0.0f;

                //float OriginalArmorReduction = StatConversion.GetArmorDamageReduction(charStruct.Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.TargetArmorReduction, arpenBuffs, Math.Max(0f, originalStats.ArmorPenetrationRating));
                float AverageArmorPenRatingsFromProcs = 0f;
                float tempCap = StatConversion.RATING_PER_ARMORPENETRATION - StatConversion.GetRatingFromArmorPenetration(arpenBuffs);

                for (int i = 0; i < tempArPenRatings.Count; i++)
                {
                    tempArPenRatingsCapLimited.Add(Math.Max(0f, Math.Min(tempCap - originalStats.ArmorPenetrationRating, tempArPenRatings[i])));
                    //float bah = StatConversion.GetArmorDamageReduction(charStruct.Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.TargetArmorReduction, arpenBuffs, Math.Max(0f, originalStats.ArmorPenetrationRating + tempArPenRatingsCapLimited[i]));
                    AverageArmorPenRatingsFromProcs += tempArPenRatingUptimes[i] * tempArPenRatingsCapLimited[i];
                }
                Stats dummyStats = new Stats();
                
                //float procArp = StatConversion.GetRatingFromArmorReduction(charStruct.Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.TargetArmorReduction, arpenBuffs, ProccedArmorReduction);
                originalStats.ArmorPenetrationRating += AverageArmorPenRatingsFromProcs;//(procArp - originalStats.ArmorPenetrationRating);                
            }*/
            #endregion

            IterativeSpecialEffectsStats(charStruct, firstPass,  critEffects, triggerIntervals, triggerChances, 0f, true, new Base.StatsWarrior(), charStruct.CombatFactors.StatS);
            IterativeSpecialEffectsStats(charStruct, secondPass, critEffects, triggerIntervals, triggerChances, 0f, false, null, charStruct.CombatFactors.StatS);
            IterativeSpecialEffectsStats(charStruct, thirdPass,  critEffects, triggerIntervals, triggerChances, 0f, false, null, charStruct.CombatFactors.StatS);
        }
        private Base.StatsWarrior GetCharacterStats_Buffed(DPSWarrCharacter dpswarchar, Item additionalItem, bool isBuffed, out Base.StatsWarrior statsRace)
        {
#if DEBUG
            //ConstructionCounts["GetCharacterStats_Buffed"]++;
#endif
            if (dpswarchar.CalcOpts == null) { dpswarchar.CalcOpts = dpswarchar.Char.CalculationOptions as CalculationOptionsDPSWarr; }
            if (dpswarchar.BossOpts == null) { dpswarchar.BossOpts = dpswarchar.Char.BossOptions; }
            if (dpswarchar.CombatFactors == null) { dpswarchar.CombatFactors = new CombatFactors(dpswarchar.Char, new Base.StatsWarrior(), dpswarchar.CalcOpts, dpswarchar.BossOpts); }
            WarriorTalents talents = dpswarchar.Char.WarriorTalents;

            #region From Race
            statsRace = new Base.StatsWarrior();
            statsRace.Accumulate(BaseStats.GetBaseStats(dpswarchar.Char.Level, CharacterClass.Warrior, dpswarchar.Char.Race));
            #endregion
            #region From Gear/Buffs
            Base.StatsWarrior statsBuffs = (isBuffed ? GetBuffsStats(dpswarchar) : new Base.StatsWarrior());
            Base.StatsWarrior statsItems = new Base.StatsWarrior();
            statsItems.Accumulate(GetItemStats(dpswarchar.Char, additionalItem));
            #endregion
            #region From Options
            Base.StatsWarrior statsOptionsPanel = new Base.StatsWarrior()
            {
                //BonusStrengthMultiplier = (dpswarchar.combatFactors.FuryStance ? talents.ImprovedBerserkerStance * 0.04f : 0f),
                //PhysicalCrit = (dpswarchar.combatFactors.FuryStance ? 0.03f + statsBuffs.BonusWarrior_T9_2P_Crit : 0f),
                // Stance Related Damage Given/Taken mods
                /*
                  Battle Stance
                        A balanced combat stance.  Increases damage done by 5%.  Decreases damage taken by 5%.
                  Berserker Stance
                        An aggressive combat stance.  Increases damage done by 10%.
                */
                DamageTakenReductionMultiplier = (!dpswarchar.CombatFactors.FuryStance ? -0.05f : 0f),
                BonusDamageMultiplier = (!dpswarchar.CombatFactors.FuryStance ? 0.05f : 0.10f),

                // Battle Shout
                Strength = (dpswarchar.CalcOpts.M_BattleShout ? 549f : 0f),
                Agility = (dpswarchar.CalcOpts.M_BattleShout ? 549f : 0f),
                // Commanding Shout
                Stamina = (dpswarchar.CalcOpts.M_CommandingShout ? 585f : 0f),
                // Demo Shout
                BossPhysicalDamageDealtReductionMultiplier = (dpswarchar.CalcOpts.M_DemoralizingShout ? 0.10f : 0f),
                // Sunder Armor
                ArmorPenetration = (dpswarchar.CalcOpts.M_SunderArmor ? 0.04f * 3f : 0f),
                // Thunder Clap
                BossAttackSpeedReductionMultiplier = (dpswarchar.CalcOpts.M_ThunderClap ? 0.20f : 0f),
            };
            if (dpswarchar.CalcOpts.M_ColossusSmash) { statsOptionsPanel.AddSpecialEffect(TalentsAsSpecialEffects.ColossusSmash); }
            #endregion
            #region From Talents
            Base.StatsWarrior statsTalents = new Base.StatsWarrior()
            {
                // Offensive
                BonusDamageMultiplier = ((!dpswarchar.CombatFactors.FuryStance
                                           && dpswarchar.Char.MainHand != null
                                           && dpswarchar.Char.MainHand.Slot == ItemSlot.TwoHand
                                         ? 1.12f : 1.00f)
                                         * (dpswarchar.CombatFactors.FuryStance
                                            && talents.SingleMindedFury > 0
                                            && HelperFunctions.ValidateSMTBonus(dpswarchar)
                                         ? 1.20f : 1.00f))
                                         - 1f,
                BonusPhysicalDamageMultiplier = ((dpswarchar.CalcOpts.M_Rend // Have Rend up
                                                 || talents.DeepWounds > 0 // Have Deep Wounds
                                                ? (1.0f + talents.BloodFrenzy * 0.02f) : 1.0f)
                                                * (dpswarchar.CombatFactors.FuryStance
                                                ? 1.05f : 1.0f))
                                                - 1f,
                BonusBleedDamageMultiplier = (dpswarchar.CalcOpts.M_Rend // Have Rend up
                                                 || talents.DeepWounds > 0 // Have Deep Wounds
                                                ? talents.BloodFrenzy * 0.15f : 0f),
                PhysicalCrit = (talents.Rampage > 0 && dpswarchar.CombatFactors.FuryStance && isBuffed ? 0.05f + 0.02f : 0f), // Cata has a new +2% on self (group gets 5%, self gets total 7%)
                PhysicalHit = (dpswarchar.CombatFactors.FuryStance ? 0.03f : 0f), // Fury Spec has passive 3% Hit
                BonusWhiteDamageMultiplier = (dpswarchar.CombatFactors.FuryStance ? 0.40f : 0f), // Fury Spec has passive 40% Bonus White Damage Mult
                // Defensive
                BaseArmorMultiplier = talents.Toughness * 0.10f / 3f,
                BonusHealingReceived = talents.FieldDressing * 0.03f,
                BonusStrengthMultiplier = HelperFunctions.ValidatePlateSpec(dpswarchar) ? 0.05f : 0f,
                // Specific Abilities
                BonusMortalStrikeDamageMultiplier = (1f + (dpswarchar.Talents.GlyphOfMortalStrike ? 0.10f : 0f))
                                                  * (1f + dpswarchar.Talents.WarAcademy * 0.05f)
                                                  - 1f,
                BonusRagingBlowDamageMultiplier = dpswarchar.Talents.WarAcademy * 0.05f,
                BonusOverpowerDamageMultiplier = (dpswarchar.Talents.GlyphOfOverpower ? 0.10f : 0f),
                BonusSlamDamageMultiplier = (1f + dpswarchar.Talents.ImprovedSlam * 0.20f)
                                          * (1f + dpswarchar.Talents.WarAcademy * 0.05f)
                                          - 1f,
                BonusVictoryRushDamageMultiplier = dpswarchar.Talents.WarAcademy * 0.05f,
                BonusBloodthirstDamageMultiplier = (dpswarchar.Talents.GlyphOfBloodthirst ? 0.10f : 0f),
            };
            // Add Talents that give SpecialEffects
            if (talents.WreckingCrew > 0 && dpswarchar.Char.MainHand != null) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.WreckingCrew[talents.WreckingCrew]); }
            if (talents.LambsToTheSlaughter > 0 && dpswarchar.CalcOpts.M_MortalStrike)
            {
                statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.LambsToTheSlaughter[talents.LambsToTheSlaughter]);
                // TODO: This should also refresh rend in 4.1.0
            }
            if (talents.BloodCraze > 0) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.BloodCraze[talents.BloodCraze]); }
            if (talents.Executioner > 0 && dpswarchar.CalcOpts.M_ExecuteSpam) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.Executioner[talents.Executioner]); }
            if (talents.BloodFrenzy > 0) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.BloodFrenzy[talents.BloodFrenzy]); }
            if (talents.MeatCleaver > 0 && (dpswarchar.CalcOpts.M_Whirlwind || dpswarchar.CalcOpts.M_Cleave)) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.MeatCleaver[talents.MeatCleaver]); }
            #endregion

            Base.StatsWarrior statsTotal = new Base.StatsWarrior();
            statsTotal.Accumulate(statsRace);
            statsTotal.Accumulate(statsItems);
            statsTotal.Accumulate(statsBuffs);
            statsTotal.Accumulate(statsTalents);
            statsTotal.Accumulate(statsOptionsPanel);
            statsTotal = UpdateStatsAndAdd(statsTotal, null, dpswarchar.Char);
            float multiplier = 0.0560f;
            float masteryBonusVal = (2f*0.056f + multiplier * StatConversion.GetMasteryFromRating(statsTotal.MasteryRating, CharacterClass.Warrior));
            if (talents.DeathWish > 0 && dpswarchar.CalcOpts.M_DeathWish && dpswarchar.CombatFactors.FuryStance) {
                statsTotal.AddSpecialEffect(TalentsAsSpecialEffects.GetDeathWishWithMastery(masteryBonusVal, dpswarchar));
            }
            if (talents.Enrage > 0 && dpswarchar.CombatFactors.FuryStance) {
                statsTotal.AddSpecialEffect(TalentsAsSpecialEffects.GetEnragedRegenerationWithMastery(masteryBonusVal, dpswarchar));
            }
            //Stats statsProcs = new Stats();

            // Dodge (your dodging incoming attacks)
            // Warriors no longer gain dodge from Agility since patch 4.2
            // statsTotal.Dodge += StatConversion.GetDodgeFromAgility(statsTotal.Agility, dpswarchar.Char.Class);
            statsTotal.Dodge += StatConversion.GetDodgeFromRating(statsTotal.DodgeRating, dpswarchar.Char.Class);

            // Parry (your parrying incoming attacks)
            statsTotal.Parry += StatConversion.GetParryFromRating(statsTotal.ParryRating, dpswarchar.Char.Class);

            return statsTotal;
        }
 public Dictionary<ItemDamageType, float> SoftCapSurvivals(DPSWarrCharacter dpswarchar, Dictionary<ItemDamageType, float> highestDmg, Dictionary<ItemDamageType, float> survivalRaws) {
     Dictionary<ItemDamageType, float> cappedValues = new Dictionary<ItemDamageType, float>() { };
     //
     foreach (ItemDamageType t in highestDmg.Keys) {
         cappedValues[t] = SoftCapSurv(survivalRaws[t], highestDmg[t]);
     }
     //
     return cappedValues;
 }
        /*public static string GetConstructionCounts() {
            string retVal = "";
            //
            foreach (string c in ConstructionCounts.Keys) {
                retVal += string.Format("{0:000000}/{1:00000.00}: {2}\n",
                    ConstructionCounts[c] < 5 ? 0 : ConstructionCounts[c] - 5,
                    (float)(ConstructionCounts[c] < 5 ? 0 : ConstructionCounts[c] - 5) / (float)(ConstructionCounts["GetCharacterCalculations"] < 5 ? 1 : ConstructionCounts["GetCharacterCalculations"] - 5),
                    c);
            }
            //
            return retVal + "\n";
        }
        public static Dictionary<string, int> ConstructionCounts = new Dictionary<string,int>() {
            { "GetCharacterCalculations", 0 },
            { "GetCharacterStats", 0 },
            { "GetCharacterStats_Inner", 0 },
            { "GetCharacterStats_Buffed", 0 },
            { "GetCharacterStats_Override", 0 },
            { "CalculateTriggers", 0 },
            { "IterativeSpecialEffectsStats", 0 },
            { "ApplySpecialEffect", 0 },
            { "UpdateStatsAndAdd", 0 },
            { "DoSpecialEffects", 0 },
            { "Rotation Base", 0 },
            { "Rotation Arms", 0 },
            { "Rotation Fury", 0 },
            { "CombatFactors", 0 },
        };*/
#endif

        public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations)
        {
#if DEBUG
            //ConstructionCounts["GetCharacterCalculations"]++;
#endif
            CharacterCalculationsDPSWarr calc = new CharacterCalculationsDPSWarr();
            try {
                #region Object Creation
                // First things first, we need to ensure that we aren't using bad data
                if (character == null) { return calc; }
                CalculationOptionsDPSWarr calcOpts = character.CalculationOptions as CalculationOptionsDPSWarr;
                if (calcOpts == null) { return calc; }
                //
                BossOptions bossOpts = character.BossOptions;
                
                CombatFactors combatFactors;
                Skills.WhiteAttacks whiteAttacks;
                Rotation Rot;

                Base.StatsWarrior statsRace = null;
                Base.StatsWarrior stats = GetCharacterStats(character, additionalItem, StatType.Average, calcOpts, bossOpts, out statsRace, out combatFactors, out whiteAttacks, out Rot);

                DPSWarrCharacter charStruct = new DPSWarrCharacter() {
                    CalcOpts = calcOpts,
                    BossOpts = bossOpts,
                    Rot = Rot,
                    CombatFactors = combatFactors,
                    Char = character,
                    Talents = character.WarriorTalents
                };

                /*if (calcOpts.UseMarkov) {
                    if (combatFactors.FuryStance) {
                        //Markov.StateSpaceGeneratorFuryTest b = new Markov.StateSpaceGeneratorFuryTest();
                        //Markov.StateSpaceGeneratorFuryTest.StateSpaceGeneratorFuryTest1(character, stats, combatFactors, whiteAttacks, calcOpts, bossOpts, needsDisplayCalculations);
                    } else {
                        //Markov.StateSpaceGeneratorArmsTest b = new Markov.StateSpaceGeneratorArmsTest();
                        //Markov.StateSpaceGeneratorArmsTest.StateSpaceGeneratorArmsTest1(character, stats, combatFactors, whiteAttacks, calcOpts, bossOpts, needsDisplayCalculations);
                    }
                }*/
                #endregion

                calc.Duration = bossOpts.BerserkTimer;
                calc.PTRMode = calcOpts.PtrMode;

                calc.AverageStats = stats;
                if (needsDisplayCalculations) {
                    calc.UnbuffedStats = GetCharacterStats(character, additionalItem, StatType.Unbuffed, calcOpts, bossOpts, out statsRace);
                    calc.BuffedStats = GetCharacterStats(character, additionalItem, StatType.Buffed, calcOpts, bossOpts, out statsRace);
                    calc.BuffsStats = GetBuffsStats(charStruct);
                    calc.MaximumStats = GetCharacterStats(character, additionalItem, StatType.Maximum, calcOpts, bossOpts, out statsRace);
                }
                
                calc.CombatFactors = combatFactors;
                calc.Rot = Rot;
                calc.TargetLevel = bossOpts.Level;
                calc.BaseHealth = statsRace.Health; 
                {// == Attack Table ==
                    // Miss
                    calc.Miss = stats.Miss;
                    calc.HitRating = stats.HitRating;
                    calc.ExpertiseRating = stats.ExpertiseRating;
                    calc.Expertise = StatConversion.GetExpertiseFromRating(stats.ExpertiseRating, CharacterClass.Warrior) + stats.Expertise;
                    calc.MHExpertise = combatFactors.CMHexpertise;
                    calc.OHExpertise = combatFactors.COhexpertise;
                    calc.AgilityCritBonus = StatConversion.GetCritFromAgility(stats.Agility, CharacterClass.Warrior);
                    calc.CritRating = stats.CritRating;
                    calc.CritPercent = stats.PhysicalCrit;
                    calc.MHCrit = combatFactors.CMHycrit;
                    calc.OHCrit = combatFactors.COhycrit;
                } 
                // Offensive
                //calc.ArmorPenetrationRating = stats.ArmorPenetrationRating;
                //calc.ArmorPenetrationRating2Perc = StatConversion.GetArmorPenetrationFromRating(stats.ArmorPenetrationRating);
                //calc.ArmorPenetration = Math.Min(1f, calc.ArmorPenetrationRating2Perc);
                calc.HasteRating = stats.HasteRating;
                calc.HastePercent = stats.PhysicalHaste;
                calc.MasteryVal = StatConversion.GetMasteryFromRating(stats.MasteryRating, CharacterClass.Warrior);
                
                // DPS
                Rot.Initialize(calc);

                calc.PlateSpecValid = HelperFunctions.ValidatePlateSpec(charStruct);
                
                // Neutral
                // Defensive
                calc.Armor = stats.Armor; 

                Rot.MakeRotationandDoDPS(true, needsDisplayCalculations);

                #region Special Damage Procs, like Bandit's Insignia or Hand-mounted Pyro Rockets
                calc.SpecProcDPS = calc.SpecProcDmgPerHit = calc.SpecProcActs = 0f;
                if (stats._rawSpecialEffectData != null && character.MainHand != null) {
                    bool runIt = false;
                    foreach (SpecialEffect s in stats.SpecialEffects()) {
                        if (s.Stats == null) { continue; }
                        if (s.Stats.ShadowDamage != 0) { runIt = true; break; }
                        if (s.Stats.FireDamage   != 0) { runIt = true; break; }
                        if (s.Stats.HolyDamage   != 0) { runIt = true; break; }
                        if (s.Stats.FrostDamage  != 0) { runIt = true; break; }
                        if (s.Stats.NatureDamage != 0) { runIt = true; break; }
                        if (s.Stats.ArcaneDamage != 0) { runIt = true; break; }
                    }
                    if (runIt) {
                        DamageProcs.SpecialDamageProcs SDP;
                        Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>();
                        Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>();
                        CalculateTriggers(charStruct, triggerIntervals, triggerChances);
                        if (character.Race == CharacterRace.Goblin && statsRace._rawSpecialEffectData.Length > 0) {
                            // Fix the damage for Goblin Rockets
                            foreach (SpecialEffect s in stats.SpecialEffects()) {
                                if (s.Stats != null && s.Stats.FireDamage == (1f + character.Level * 2)) {
                                    s.Stats.FireDamage += stats.AttackPower * 0.25f   // AP Bonus
                                                        + stats.Intellect * 0.50193f; // Int Bonus
                                }
                            }
                        }
                        SDP = new Rawr.DamageProcs.SpecialDamageProcs(character, stats, calc.TargetLevel - character.Level,
                            new List<SpecialEffect>(stats.SpecialEffects()),
                            triggerIntervals, triggerChances,
                            bossOpts.BerserkTimer,
                            combatFactors.DamageReduction);

                        calc.SpecProcDPS = SDP.CalculateAll();
                        calc.SpecProcDmgPerHit = SDP.GetDamagePerHit;
                        calc.SpecProcActs = SDP.GetTotalNumProcs;
                    }
                }
                calc.TotalDPS += calc.SpecProcDPS;
                #endregion

                #region Survivability
                List<Attack> Attacks;
                if (calcOpts.SurvScale != 0f && (Attacks = bossOpts.Attacks.FindAll(a => a.AffectsRole[PLAYER_ROLES.MeleeDPS])).Count > 0) {
                    Dictionary<ItemDamageType, float> countsDmg = new Dictionary<ItemDamageType, float>() {
                        { ItemDamageType.Physical, 0f },
                        { ItemDamageType.Arcane, 0f },
                        { ItemDamageType.Fire, 0f },
                        { ItemDamageType.Frost, 0f },
                        { ItemDamageType.Holy, 0f },
                        { ItemDamageType.Nature, 0f },
                        { ItemDamageType.Shadow, 0f },
                    };
                    Dictionary<ItemDamageType, float> percDmg = new Dictionary<ItemDamageType, float>() {
                        { ItemDamageType.Physical, 0f },
                        { ItemDamageType.Arcane, 0f },
                        { ItemDamageType.Fire, 0f },
                        { ItemDamageType.Frost, 0f },
                        { ItemDamageType.Holy, 0f },
                        { ItemDamageType.Nature, 0f },
                        { ItemDamageType.Shadow, 0f },
                    };
                    Dictionary<ItemDamageType, float> highestDmg = new Dictionary<ItemDamageType, float>() {
                        { ItemDamageType.Physical, 0f },
                        { ItemDamageType.Arcane, 0f },
                        { ItemDamageType.Fire, 0f },
                        { ItemDamageType.Frost, 0f },
                        { ItemDamageType.Holy, 0f },
                        { ItemDamageType.Nature, 0f },
                        { ItemDamageType.Shadow, 0f },
                    };
                    int totalCount = 0;
                    foreach (Attack a in Attacks) {
                        countsDmg[a.DamageType] += 1; totalCount++;
                        if ((a.DamagePerHit + a.DamagePerTick) > highestDmg[a.DamageType]) {
                            highestDmg[a.DamageType] = (a.DamagePerHit + a.DamagePerTick);
                        }
                    }
                    foreach (ItemDamageType t in countsDmg.Keys) { percDmg[t] = countsDmg[t] / (float)totalCount; }
                    float TotalConstantDamageReduction = 1f - (1f - (stats.Armor > 0 ? StatConversion.GetArmorDamageReduction(bossOpts.Level, character.Level, stats.Armor, 0f, 0f) : 0))
                                                            * (1f - stats.DamageTakenReductionMultiplier)
                                                            * (1f - stats.BossPhysicalDamageDealtReductionMultiplier);
                    Dictionary<ItemDamageType, float> SurvivabilityPointsRaw = new Dictionary<ItemDamageType, float>() {
                        { ItemDamageType.Physical, stats.Health / (1f - TotalConstantDamageReduction) },
                        { ItemDamageType.Arcane,   stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.ArcaneResistance, 0)) * (1f - stats.DamageTakenReductionMultiplier)) },
                        { ItemDamageType.Fire,     stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.FireResistance,   0)) * (1f - stats.DamageTakenReductionMultiplier)) },
                        { ItemDamageType.Frost,    stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.FrostResistance,  0)) * (1f - stats.DamageTakenReductionMultiplier)) },
                        { ItemDamageType.Holy,     stats.Health / ((1f)                                                                                                   * (1f - stats.DamageTakenReductionMultiplier)) },
                        { ItemDamageType.Nature,   stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.NatureResistance, 0)) * (1f - stats.DamageTakenReductionMultiplier)) },
                        { ItemDamageType.Shadow,   stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.ShadowResistance, 0)) * (1f - stats.DamageTakenReductionMultiplier)) },
                    };
                    Dictionary<ItemDamageType, float> SurvivabilityPoints = SoftCapSurvivals(charStruct, highestDmg, SurvivabilityPointsRaw);

                    if (stats.HealthRestoreFromMaxHealth > 0) {
                        stats.HealthRestore += stats.HealthRestoreFromMaxHealth * stats.Health * bossOpts.BerserkTimer;
                    }

                    float survs = 0f;
                    foreach (ItemDamageType t in SurvivabilityPoints.Keys) { survs += SurvivabilityPoints[t] * percDmg[t]; }
                    float HealthRest2Surv = survs / 100f + Rot._HPS_TTL + stats.HealthRestore / 100f;
                    calc.TotalHPS = Rot._HPS_TTL;
                    calc.Survivability = calcOpts.SurvScale * HealthRest2Surv;
                } else {
                    // No point in running all those calcs just to zero them out after
                    calc.TotalHPS = Rot._HPS_TTL;
                    calc.Survivability = 0f;
                }
                #endregion

                calc.OverallPoints = calc.TotalDPS + calc.Survivability;

                #region Old ArP cap calc, keeping in case another cap has to be implemented
                //calculatedStats.UnbuffedStats = GetCharacterStats(character, additionalItem, StatType.Unbuffed, calcOpts, bossOpts);
                /*if (needsDisplayCalculations)
                {
                    calc.BuffedStats = GetCharacterStats(character, additionalItem, StatType.Buffed, calcOpts, bossOpts, out statsRace);
                    //calculatedStats.MaximumStats = GetCharacterStats(character, additionalItem, StatType.Maximum, calcOpts, bossOpts);

                    float maxArp = calc.BuffedStats.ArmorPenetrationRating;
                    foreach (SpecialEffect effect in calc.BuffedStats.SpecialEffects(s => s.Stats.ArmorPenetrationRating > 0f))
                    {
                        maxArp += effect.Stats.ArmorPenetrationRating;
                    }
                    calc.MaxArmorPenetration = StatConversion.GetArmorPenetrationFromRating(maxArp);
                }*/
                #endregion

#if DEBUG
                /*foreach (string c in Rotation.ConstructionCounts.Keys) {
                    if (!ConstructionCounts.ContainsKey(c)) { ConstructionCounts.Add(c, 0); }
                    ConstructionCounts[c] = Rotation.ConstructionCounts[c];
                }
                foreach (string c in CombatFactors.ConstructionCounts.Keys) {
                    if (!ConstructionCounts.ContainsKey(c)) { ConstructionCounts.Add(c, 0); }
                    ConstructionCounts[c] = CombatFactors.ConstructionCounts[c];
                }
                string counts = GetConstructionCounts();
                Clipboard.SetText(counts);
                //System.Windows.MessageBox.Show(counts, "DPSWarr Counts", System.Windows.MessageBoxButton.OK);*/
#endif
            } catch (Exception ex) {
                new Base.ErrorBox() {
                    Title = "Error in creating Stat Pane Calculations",
                    Function = "GetCharacterCalculations()",
                    TheException = ex,
                }.Show();
            }
            return calc;
        }
        public override ComparisonCalculationBase[] GetCustomChartData(Character character, string chartName) {
            if (character == null) { return null;}
            Character zeOriginal = character.Clone();
            Character zeClone = character.Clone();
            CharacterCalculationsDPSWarr calculations = GetCharacterCalculations(zeOriginal) as CharacterCalculationsDPSWarr;
            CalculationOptionsDPSWarr calcOpts = zeOriginal.CalculationOptions as CalculationOptionsDPSWarr;
            ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = true;
            bool[] origMaints = (bool[])calcOpts.MaintenanceTree.Clone();
            DPSWarrCharacter dpswarchar = new DPSWarrCharacter() {
                Char = zeOriginal,
                CalcOpts = (CalculationOptionsDPSWarr)zeOriginal.CalculationOptions,
                BossOpts = zeOriginal.BossOptions,
                CombatFactors = null,
                Rot = null,
            };

            switch (chartName) {
                #region Ability DPS
                case "Ability DPS": {
                    List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>();
                    foreach (AbilityWrapper aw in calculations.Rot.TheAbilityList)
                    {
                        if (aw.Ability.DamageOnUse == 0) { continue; }
                        ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
                        comparison.Name = aw.Ability.Name;
                        comparison.Description = aw.Ability.Desc;
                        if (aw.Ability is Skills.Rend) {
                            comparison.DPSPoints = (aw.Ability as Skills.Rend).GetDPS(aw.NumActivatesO20, calculations.Rot.GetWrapper<Skills.ThunderClap>().NumActivatesO20, calculations.Rot.TimeOver20Perc) * calculations.Rot.TimeOver20Perc
                                                 + (aw.Ability as Skills.Rend).GetDPS(aw.NumActivatesU20, calculations.Rot.GetWrapper<Skills.ThunderClap>().NumActivatesU20, calculations.Rot.TimeUndr20Perc) * calculations.Rot.TimeUndr20Perc;
                        } else {
                            comparison.DPSPoints = aw.AllDPS;
                        }
                        comparison.SurvPoints = aw.AllHPS;
                        comparison.ImageSource = aw.Ability.Icon;
                        comparisons.Add(comparison);
                    }
                    foreach (ComparisonCalculationDPSWarr comp in comparisons) {
                        comp.OverallPoints = comp.DPSPoints + comp.SurvPoints;
                    }
                    calcOpts.MaintenanceTree = origMaints;
                    ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false;
                    return comparisons.ToArray();
                }
                #endregion
                #region Ability Damage per GCD
                case "Ability Damage per GCD":
                    {
                        List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>();
                        foreach (AbilityWrapper aw in calculations.Rot.TheAbilityList)
                        {
                            if (aw.Ability.DamageOnUse == 0) { continue; }
                            ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
                            comparison.Name = aw.Ability.Name;
                            comparison.Description = aw.Ability.Desc;
                            comparison.DPSPoints = (
                                ((aw.Ability is Skills.Rend) ? ((aw.Ability as Skills.Rend).TickSize * (aw.Ability as Skills.Rend).NumTicks)
                                : aw.Ability.DamageOnUse)) / (aw.Ability.GCDTime == 0 ? 1f : (aw.Ability.GCDTime / calculations.Rot.LatentGCD));
                            comparison.SurvPoints = aw.Ability.GetAvgHealingOnUse(aw.AllNumActivates);
                            comparison.ImageSource = aw.Ability.Icon;
                            comparisons.Add(comparison);
                        }
                        foreach (ComparisonCalculationDPSWarr comp in comparisons)
                        {
                            comp.OverallPoints = comp.DPSPoints + comp.SurvPoints;
                        }
                        calcOpts.MaintenanceTree = origMaints;
                        ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false;
                        return comparisons.ToArray();
                    }
                #endregion
                #region Ability Maintenance Changes
                case "Ability Maintenance Changes": {
                    List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>();
                    #region Rage Generators
                    comparisons.Add(getComp(dpswarchar, "Berserker Rage", (int)Maintenance.BerserkerRage));
                    comparisons.Add(getComp(dpswarchar, "Deadly Calm", (int)Maintenance.DeadlyCalm));
                    #endregion
                    #region Maintenance
                    comparisons.Add(getComp(dpswarchar, "Battle Shout", (int)Maintenance.BattleShout));
                    comparisons.Add(getComp(dpswarchar, "Commanding Shout", (int)Maintenance.CommandingShout));
                    comparisons.Add(getComp(dpswarchar, "Demoralizing Shout", (int)Maintenance.DemoralizingShout));
                    comparisons.Add(getComp(dpswarchar, "Sunder Armor", (int)Maintenance.SunderArmor));
                    comparisons.Add(getComp(dpswarchar, "Thunder Clap", (int)Maintenance.ThunderClap));
                    comparisons.Add(getComp(dpswarchar, "Hamstring", (int)Maintenance.Hamstring));
                    #endregion
                    #region Periodics
                    comparisons.Add(getComp(dpswarchar, "Shattering Throw", (int)Maintenance.ShatteringThrow));
                    comparisons.Add(getComp(dpswarchar, "Sweeping Strikes", (int)Maintenance.SweepingStrikes));
                    comparisons.Add(getComp(dpswarchar, "Death Wish", (int)Maintenance.DeathWish));
                    comparisons.Add(getComp(dpswarchar, "Recklessness", (int)Maintenance.Recklessness));
                    comparisons.Add(getComp(dpswarchar, "Enraged Regeneration", (int)Maintenance.EnragedRegeneration));
                    #endregion
                    #region Damage Dealers
                    if (calculations.Rot.GetType() == typeof(FuryRotation)) {
                        #region Fury
                        comparisons.Add(getComp(dpswarchar, "Bloodsurge", (int)Maintenance.Bloodsurge));
                        comparisons.Add(getComp(dpswarchar, "Bloodthirst", (int)Maintenance.Bloodthirst));
//                        comparisons.Add(getComp(dpswarchar, "Whirlwind", (int)Maintenance.Whirlwind));
                        comparisons.Add(getComp(dpswarchar, "Raging Blow", (int)Maintenance.RagingBlow));
                        #endregion
                    } else if (calculations.Rot.GetType() == typeof(ArmsRotation)) {
                        #region Arms
                        comparisons.Add(getComp(dpswarchar, "Bladestorm", (int)Maintenance.Bladestorm));
                        comparisons.Add(getComp(dpswarchar, "Mortal Strike", (int)Maintenance.MortalStrike));
                        comparisons.Add(getComp(dpswarchar, "Rend", (int)Maintenance.Rend));
                        comparisons.Add(getComp(dpswarchar, "Overpower", (int)Maintenance.Overpower));
                        comparisons.Add(getComp(dpswarchar, "Taste for Blood", (int)Maintenance.TasteForBlood));
                        comparisons.Add(getComp(dpswarchar, "Colossus Smash", (int)Maintenance.ColossusSmash));
                        comparisons.Add(getComp(dpswarchar, "Slam", (int)Maintenance.Slam));
                        #endregion
                    }
                    comparisons.Add(getComp(dpswarchar, "<20% Execute Spamming", (int)Maintenance.ExecuteSpam));
                    #endregion
                    #region Rage Dumps
                    comparisons.Add(getComp(dpswarchar, "Heroic Strike", (int)Maintenance.HeroicStrike));
                    comparisons.Add(getComp(dpswarchar, "Cleave", (int)Maintenance.Cleave));
                    comparisons.Add(getComp(dpswarchar, "Inner Rage", (int)Maintenance.InnerRage));
                    #endregion
                    foreach (ComparisonCalculationDPSWarr comp in comparisons) {
                        comp.OverallPoints = comp.DPSPoints + comp.SurvPoints;
                    }
                    calcOpts.MaintenanceTree = origMaints;
                    ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false;
                    return comparisons.ToArray();
                }
                #endregion
                #region Rage Cost per Damage
                case "Rage Cost per Damage": {
                    List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>();
                    float DeepWoundsDamage = calculations.Rot.DW.TickSize * 6f;

                    foreach (AbilityWrapper aw in calculations.Rot.TheAbilityList)
                    {
                        if (aw.Ability.DamageOnUse == 0 || aw.Ability.RageCost == -1f) { continue; }
                        ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
                        comparison.Name = aw.Ability.Name;
                        comparison.Description = string.Format("Costs {0} Rage\r\n{1}", aw.Ability.RageCost, aw.Ability.Desc);
                        float extraRage = (aw.Ability is Skills.Execute) ? (aw.Ability as Skills.Execute).UsedExtraRage : 0f;
                        float val = (((aw.Ability.RageCost + extraRage) != 0) ? (aw.Ability.RageCost < -1f ? aw.Ability.RageCost * -1f : aw.Ability.RageCost) + extraRage : 1f);
                        if (aw.Ability is Skills.Rend) {
                            comparison.SubPoints[0] = ((aw.Ability as Skills.Rend).TickSize * (aw.Ability as Skills.Rend).NumTicks) / val;
                        } else {
                            comparison.SubPoints[0] = aw.Ability.DamageOnUse / val;
                        }
                        comparison.SubPoints[1] = (aw.Ability.MHAtkTable.Crit * DeepWoundsDamage) / val;
                        comparison.ImageSource = aw.Ability.Icon;
                        comparisons.Add(comparison);
                    }
                    foreach (ComparisonCalculationDPSWarr comp in comparisons) {
                        comp.OverallPoints = comp.SubPoints[0] + comp.SubPoints[1];
                    }
                    calcOpts.MaintenanceTree = origMaints;
                    ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false;
                    return comparisons.ToArray();
                }
                #endregion
                #region Execute Spam
                case "Execute Spam": {
                    List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>();
                    {
                        bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam;
                        bool orig2 = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = false;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = false;
                        //CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr;
                        ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
                        comparison.Name = "Without Execute Spam";
                        comparison.Description = "Turning <20% Execute Spam off on the options pane will change your DPS to this";
                        comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0];
                        comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1];
                        comparison.Equipped = orig == false;
                        comparison.ImageSource = Skills.MortalStrike.SIcon;
                        comparisons.Add(comparison);
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = orig;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = orig2;
                    }
                    {
                        bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam;
                        bool orig2 = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = true;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = false;
                        //CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr;
                        ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
                        comparison.Name = "With Execute Spam";
                        comparison.Description = "Turning <20% Execute Spam on on the options pane will change your DPS to this";
                        comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0];
                        comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1];
                        comparison.Equipped = orig == true && orig2 == false;
                        comparison.ImageSource = Skills.Execute.SIcon;
                        comparisons.Add(comparison);
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = orig;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = orig2;
                    }
                    {
                        bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam;
                        bool orig2 = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = true;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = true;
                        CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr;
                        ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
                        comparison.Name = "With Execute Spam Stage 2";
                        comparison.Description = "Turning <20% Execute Spam on on the options pane AND Enforcing Taste for Blood will change your DPS to this";
                        comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0];
                        comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1];
                        comparison.Equipped = orig == true && orig2 == true;
                        comparison.ImageSource = Skills.TasteForBlood.SIcon;
                        comparisons.Add(comparison);
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = orig;
                        ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = orig2;
                    }
                    foreach (ComparisonCalculationDPSWarr comp in comparisons)
                    {
                        comp.OverallPoints = comp.SubPoints[0] + comp.SubPoints[1];
                    }
                    calcOpts.MaintenanceTree = origMaints;
                    ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false;
                    return comparisons.ToArray();
                }
                #endregion
                #region PTR Testing
                case "PTR Testing":
                    {
                        List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>();
                        {
                            bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode;
                            ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode = false;
                            CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr;
                            ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
                            comparison.Name = "Live Mode";
                            comparison.Description = "This makes Thunderclap apply Rend's Initial Damage along with its own.";
                            comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0];
                            comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1];
                            comparison.Equipped = orig == false;
                            comparison.ImageSource = "spell_nature_callstorm";
                            comparisons.Add(comparison);
                            ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode = orig;
                        }
                        {
                            bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode;
                            ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode = true;
                            CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr;
                            ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
                            comparison.Name = "PTR Mode";
                            comparison.Description = "This makes Thunderclap NOT apply Rend's Initial Damage along with its own.";
                            comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0];
                            comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1];
                            comparison.Equipped = orig == true;
                            comparison.ImageSource = Skills.Rend.SIcon;
                            comparisons.Add(comparison);
                            ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode = orig;
                        }
                        foreach (ComparisonCalculationDPSWarr comp in comparisons)
                        {
                            comp.OverallPoints = comp.SubPoints[0] + comp.SubPoints[1];
                        }
                        calcOpts.MaintenanceTree = origMaints;
                        ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false;
                        return comparisons.ToArray();
                    }
                #endregion
                default: { calcOpts.MaintenanceTree = origMaints; return new ComparisonCalculationBase[0]; }
            }
        }
 ComparisonCalculationDPSWarr getComp(DPSWarrCharacter dpswarchar, string Name, int Iter) {
     ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr();
     comparison.Name = Name;
     comparison.Equipped = dpswarchar.CalcOpts.MaintenanceTree[Iter] == true;
     float with = getDPS(dpswarchar, Iter, true);
     float without = getDPS(dpswarchar, Iter, false);
     comparison.DPSPoints = with - without;
     with = getSurv(dpswarchar, Iter, true) * dpswarchar.CalcOpts.SurvScale;
     without = getSurv(dpswarchar, Iter, false) * dpswarchar.CalcOpts.SurvScale;
     comparison.SurvPoints = with - without;
     //comparison.ImageSource = aw.ability.Icon; // TODO
     //comparison.Description = aw.ability.Desc; // TODO
     return comparison;
 }