Exemplo n.º 1
0
        public override void Initialize(CharacterCalculationsDPSWarr calcs)
        {
            base.Initialize(calcs);

            bool hsok = CalcOpts.Maintenance[(int)Rawr.DPSWarr.CalculationOptionsDPSWarr.Maintenances.HeroicStrike_];
            bool clok =
#if RAWR3 || SILVERLIGHT
                BossOpts.MultiTargs && BossOpts.Targets != null && BossOpts.Targets.Count > 0
#else
                CalcOpts.MultipleTargets
#endif
                && CalcOpts.Maintenance[(int)Rawr.DPSWarr.CalculationOptionsDPSWarr.Maintenances.Cleave_];
            BloodSurge _BS = GetWrapper <BloodSurge>().ability as BloodSurge;

            percHS = (hsok ? 1f : 0f);
            if (clok)
            {
#if RAWR3 || SILVERLIGHT
                //percHS -= (float)BossOpts.MultiTargsPerc;
                {
                    float time = 0;
                    foreach (TargetGroup tg in BossOpts.Targets)
                    {
                        if (tg.Chance <= 0 || tg.Frequency <= 0 || tg.Duration <= 0)
                        {
                            continue;
                        }
                        time += tg.Frequency / BossOpts.BerserkTimer * tg.Duration / 1000f;
                    }
                    float perc = time / BossOpts.BerserkTimer;
                    percHS -= Math.Max(0f, Math.Min(1f, perc));
                }
#else
                percHS -= (float)CalcOpts.MultipleTargetsPerc / 100f;
#endif
            }
            percCL = (clok ? 1f - percHS : 0f);
            if (_BS != null)
            {
                _BS.maintainActs = MaintainCDs;
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// GetCharacterCalculations is the primary method of each model, where a majority of the calculations
        /// and formulae will be used. GetCharacterCalculations should call GetCharacterStats(), and based on
        /// those total stats for the character, and any calculationoptions on the character, perform all the
        /// calculations required to come up with the final calculations defined in
        /// CharacterDisplayCalculationLabels, including an Overall rating, and all Sub ratings defined in
        /// SubPointNameColors.
        /// </summary>
        /// <param name="character">The character to perform calculations for.</param>
        /// <param name="additionalItem">An additional item to treat the character as wearing.
        /// This is used for gems, which don't have a slot on the character to fit in, so are just
        /// added onto the character, in order to get gem calculations.</param>
        /// <returns>A custom CharacterCalculations object which inherits from CharacterCalculationsBase,
        /// containing all of the final calculations defined in CharacterDisplayCalculationLabels. See
        /// CharacterCalculationsBase comments for more details.</returns>
        public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem)
        {
            /*
             * To Do:
             * -Deep wounds
             * -Allow more talent choices
             * -Check for certain talents before calculating
             * -Add in rotation choice
             * -Add in GCD check for haste and slam rotations. (2.5 second min swing timer otherwise gcd ruins it)
             * -Add in control for changing rotation during heroism/bloodlust?
             * -Handle bloodlust/heroism same as mage model instead of average over fight.
             * */
            //character = GetTalents(character);
            Stats stats = GetCharacterStats(character, additionalItem);

            CharacterCalculationsDPSWarr calcs = new CharacterCalculationsDPSWarr();

            calcs.BasicStats = stats;

            CalculationOptionsDPSWarr calcOpts = character.CalculationOptions as CalculationOptionsDPSWarr;

            float avgBaseWeaponHit = 0.0f, hastedSpeed = 2.0f, physicalCritModifier = 0.0f, chanceToBeDodged = 6.5f, chanceToMiss = 9.0f;;
            float chanceToGlance = 0.25f, glancingAmount = 0.35f;
            float slamDPS = 0.0f, msDPS = 0.0f, wfDPS = 0.0f, wwDPS = 0.0f;
            float rotationTime;
            float FlurryHaste;

            #region Mitigation
            //Default Boss Armor
            float bossArmor = calcOpts.BossArmor;

            float totalArP            = stats.ArmorPenetration;
            float modifiedTargetArmor = bossArmor - totalArP;
            float mitigation          = 1 - modifiedTargetArmor / (modifiedTargetArmor + 10557.5f);
            #endregion

            //Flurry *Assumes 4 attakcs per flurry to refresh. This is a loose average of the # of attacks
            //for each flurry including instants & slams.
            //Also allows user to check 100% uptime flurry if desired.
            FlurryHaste = (0.05f * calcOpts.Flurry);

            if (calcOpts.FlurryUptime == 0)
            {
                FlurryHaste = 1.0f + (1 - (float)Math.Pow((1 - (stats.CritRating / 22.08f / 100.0f)), 4.0f)) * FlurryHaste;
            }
            else
            {
                FlurryHaste += 1.0f;
            }

            //Variables for Bloodlust/Deathwish uptime
            int   remainder = 0, noOfFullDW = 0, noOfFullBL;
            int   div;
            float partialUptime, totalUptime;

            //Fight duration
            float fightDuration = calcOpts.FightLength * 60;

            float bloodlust = 1.0f;
            #region Bloodlust
            if (stats.Bloodlust > 0)
            {
                //Bloodlust -- Calculating uptime *Credit to Ret Model
                //Note, not working correctly in the ret model
                //Haste is multiplicative something the ret model isn't handling.
                div = Math.DivRem(Convert.ToInt32(fightDuration), 600, out remainder);
                if (remainder == 0)
                {
                    noOfFullBL = div;
                }
                else
                {
                    noOfFullBL = Convert.ToInt32(Math.Ceiling(Convert.ToDouble((fightDuration + 40) / 600)));
                }

                partialUptime = fightDuration - noOfFullBL * 600;
                if (partialUptime < 0)
                {
                    partialUptime = 0;
                }
                totalUptime = partialUptime + noOfFullBL * 40f;

                bloodlust = (1.0f + (0.30f * (totalUptime / fightDuration)));
            }
            #endregion

            string shattrathFaction = calcOpts.ShattrathFaction;
            if (stats.ShatteredSunMightProc > 0)
            {
                switch (shattrathFaction)
                {
                case "Aldor":
                    stats.AttackPower += 39.13f;
                    break;
                }
            }
            //Check if we have the talent impale.
            float impale = 1.0f + (0.1f * calcOpts.Impale);

            #region White Damage and Multipliers

            //2 Handed Spec
            float twoHandedSpec = 1.0f + (0.01f * calcOpts.TwoHandedSpec);

            if (character.MainHand != null)
            {
                avgBaseWeaponHit = twoHandedSpec * (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage * 2f) / 2.0f;
                hastedSpeed      = (stats.HasteRating == 0) ? character.MainHand.Speed : character.MainHand.Speed / (1 + (stats.HasteRating + stats.DrumsOfBattle) / 1576f);
                hastedSpeed      = hastedSpeed / FlurryHaste;
                hastedSpeed      = hastedSpeed / bloodlust;
            }

            rotationTime  = (hastedSpeed * 4) + (0.5f * 4);
            rotationTime += ((calcOpts.SlamLatency) * 4);

            //Add Attack Power Bonus
            avgBaseWeaponHit += twoHandedSpec * (stats.AttackPower / 14.0f) * ((character.MainHand == null) ? 2.0f : character.MainHand.Speed);


            //Take Non-Stat Buffs into account
            physicalCritModifier = 1.0f + ((stats.CritRating / 22.08f) / 100.0f) * (1f + stats.BonusCritMultiplier * 2f);

            chanceToBeDodged -= (float)(Math.Floor(stats.ExpertiseRating / 3.89f) * 0.25f);
            chanceToBeDodged -= calcOpts.WeaponMastery;
            if (chanceToBeDodged < 0.0f)
            {
                chanceToBeDodged = 0.0f;
            }

            chanceToMiss -= stats.HitRating / 15.76f;
            if (chanceToMiss < 0.0f)
            {
                chanceToMiss = 0.0f;
            }

            float avgBaseWeaponHitPost = (avgBaseWeaponHit * physicalCritModifier - avgBaseWeaponHit * (chanceToMiss + chanceToBeDodged) / 100.0f
                                          - avgBaseWeaponHit * chanceToGlance * glancingAmount);

            //Death Wish -- Calculating uptime *Credit to Ret model AW up time.
            div = Math.DivRem(Convert.ToInt32(fightDuration), 180, out remainder);
            if (remainder == 0)
            {
                noOfFullDW = div;
            }
            else
            {
                noOfFullDW = Convert.ToInt32(Math.Ceiling(Convert.ToDouble((fightDuration + 20) / 180)));
            }
            partialUptime = fightDuration - noOfFullDW * 180;
            if (partialUptime < 0)
            {
                partialUptime = 0;
            }
            totalUptime = partialUptime + noOfFullDW * 20f;
            float deathWish = 1f + 0.20f * totalUptime / fightDuration;

            //Misery
            float misery = 1f + stats.BonusSpellPowerMultiplier;

            //Blood Frenzy : TODO Take from Debuff List
            float damageMod = 1.0f + stats.BonusPhysicalDamageMultiplier;

            float impSancAura = 1.0f;
            //Added Imp Sanc aura to the buff list, if total damage mod is greater then just
            //blood frenzy assume we have imp sanc... (not the best way...)
            if (damageMod > 1.04f)
            {
                impSancAura = 1.02f;
            }
            float ssoNeckProcDPS = 0f;


            //TODO: Add Mitigation
            avgBaseWeaponHitPost *= damageMod * deathWish * mitigation;
            float dpsWhite = (avgBaseWeaponHitPost * 4) / rotationTime;
            calcs.WhiteDPSPoints = dpsWhite;
            calcs.HastedSpeed    = hastedSpeed;
            #endregion

            if (stats.ShatteredSunMightProc > 0)
            {
                switch (shattrathFaction)
                {
                case "Scryer":
                    ssoNeckProcDPS = 350f * deathWish * misery * impSancAura * physicalCritModifier / 50f;
                    break;
                }
            }
            #region Slam
            if (character.MainHand != null)
            {
                avgBaseWeaponHit = twoHandedSpec * (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage * 2f) / 2.0f;
            }
            avgBaseWeaponHit += twoHandedSpec * (stats.AttackPower / 14.0f) * ((character.MainHand == null) ? 1.0f : character.MainHand.Speed);
            //add in slam damage
            avgBaseWeaponHit += twoHandedSpec * 140;

            physicalCritModifier = 1.0f + ((stats.CritRating / 22.08f) / 100.0f) * ((1f + stats.BonusCritMultiplier * 2f) * impale);

            avgBaseWeaponHitPost  = (avgBaseWeaponHit * physicalCritModifier - avgBaseWeaponHit * (chanceToMiss + chanceToBeDodged) / 100.0f);
            avgBaseWeaponHitPost *= damageMod * deathWish * mitigation;
            slamDPS             = (avgBaseWeaponHitPost * 4) / rotationTime;
            calcs.SlamDPSPoints = slamDPS;
            #endregion

            //WW and MS are normalized (2h = 3.3 speed)
            float normalizedAP = twoHandedSpec * (stats.AttackPower / 14.0f) * 3.3f;
            #region Mortal Strike
            if (character.MainHand != null)
            {
                avgBaseWeaponHit = twoHandedSpec * (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage * 2f) / 2.0f;
            }
            avgBaseWeaponHit += normalizedAP;
            //add in MS damage
            avgBaseWeaponHit += twoHandedSpec * 210;

            avgBaseWeaponHitPost  = (avgBaseWeaponHit * physicalCritModifier - avgBaseWeaponHit * (chanceToMiss + chanceToBeDodged) / 100.0f);
            avgBaseWeaponHitPost *= damageMod * deathWish * mitigation;
            msDPS             = (avgBaseWeaponHitPost * 2) / rotationTime;
            calcs.MSDPSPoints = msDPS;
            #endregion

            #region WhirlWind
            if (character.MainHand != null)
            {
                avgBaseWeaponHit = twoHandedSpec * (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage * 2f) / 2.0f;
            }
            avgBaseWeaponHit += normalizedAP;

            avgBaseWeaponHitPost  = (avgBaseWeaponHit * physicalCritModifier - avgBaseWeaponHit * (chanceToMiss + chanceToBeDodged) / 100.0f);
            avgBaseWeaponHitPost *= damageMod * deathWish * mitigation;

            wwDPS             = (avgBaseWeaponHitPost * 1) / rotationTime;
            calcs.WWDPSPoints = wwDPS;
            #endregion

            #region Windfury
            //WF only procs on whites.
            //White time = rotation time/4
            float avgTimeBetnWF = ((rotationTime / 4) / (1.0f - (chanceToBeDodged + chanceToMiss) / 100f)) * 5.0f;
            float wfAPIncrease  = stats.WindfuryAPBonus;
            float wfHitPre      = avgBaseWeaponHit + (wfAPIncrease / 14f) * ((character.MainHand == null) ? 0 : character.MainHand.Speed);
            float wfHitPost     = (wfHitPre * physicalCritModifier) - (wfHitPre * (chanceToMiss + chanceToBeDodged) / 100f) -
                                  (wfHitPre * glancingAmount * chanceToGlance);
            if (wfAPIncrease > 0)
            {
                wfHitPost *= damageMod * deathWish * mitigation;
            }
            else
            {
                wfHitPost = 0f;
            }
            wfDPS             = wfHitPost / avgTimeBetnWF;
            calcs.WFDPSPoints = wfDPS;
            #endregion

            #region SwordSpec
            float swordSpecDps = 0.0f;
            if ((character.MainHand != null) &&
                (character.MainHand.Type == Item.ItemType.TwoHandSword))
            {
                //Assume 11 sword procs every 220 attacks (5%)
                //Each rotation has 11 attacks
                float swordSpecHit = (rotationTime * dpsWhite) / 4;
                swordSpecDps          = (swordSpecHit * 11) / (rotationTime * 20);
                dpsWhite             += swordSpecDps;
                calcs.WhiteDPSPoints += swordSpecDps;
            }
            #endregion

            calcs.DPSPoints               = dpsWhite + (character.MainHand == null ? 0 : slamDPS + msDPS + wwDPS) + wfDPS + ssoNeckProcDPS;
            calcs.SubPoints               = new float[] { calcs.DPSPoints };
            calcs.OverallPoints           = calcs.DPSPoints;
            calcs.BasicStats.WeaponDamage = avgBaseWeaponHit * impSancAura;
            return(calcs);
        }
Exemplo n.º 3
0
        /*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;
        }