Esempio n. 1
0
        private float[] GetMitigation(ref TankDKChar TDK, StatsDK stats, Rotation rot, float fPercentCritMitigation, float ArmorDamageReduction, float[] fCurrentDTPS, float fMagicDR, bool bFactorInAvoidance)
        {
            float[] fTotalMitigation = new float[EnumHelper.GetCount(typeof(MitigationSub))];
            // Ensure the CurrentDTPS structure is the right size.
            if (fCurrentDTPS.Length < EnumHelper.GetCount(typeof(SurvivalSub)))
            {
                fCurrentDTPS = new float[EnumHelper.GetCount(typeof(SurvivalSub))];
            }

            float fSegmentMitigation = 0f;
            float fSegmentDPS = 0f;
            float fPhyDamageDPS = fCurrentDTPS[(int)SurvivalSub.Physical];
            float fMagicDamageDPS = fCurrentDTPS[(int)SurvivalSub.Magic];
            float fBleedDamageDPS = fCurrentDTPS[(int)SurvivalSub.Bleed];
            #region *** Dam Avoided (Crit, Haste, Avoidance) ***
            #region ** Crit Mitigation **
            // Crit mitigation:
            // Crit mitigation work for Physical damage only.
            float fCritMultiplier = .12f;
            // Bleeds can't crit.
            // Neither can spells from bosses.  (As per a Loading screen ToolTip.)
            float fCritDPS = fPhyDamageDPS * fCritMultiplier;
            fSegmentMitigation = (fCritDPS * fPercentCritMitigation);
            // Add in the value of crit mitigation.
            fTotalMitigation[(int)MitigationSub.Crit] = fSegmentMitigation;
            // The max damage at this point needs to include crit.
            fCurrentDTPS[(int)SurvivalSub.Physical] = fCurrentDTPS[(int)SurvivalSub.Physical] + (fCritDPS - fSegmentMitigation);
            #endregion
            #region ** Haste Mitigation **
            // Placeholder for comparing differing DPS values related to haste.
            float fNewIncPhysDPS = 0;
            // Let's just look at Imp Icy Touch 
            #region Improved Icy Touch
            // Get the new slowed AttackSpeed based on ImpIcyTouch
            // Factor in the base slow caused by FF (14% base).
            float fBossAttackSpeedReduction = 0.0f;

            if (rot.Contains(DKability.IcyTouch)
                || rot.Contains(DKability.FrostFever))
            {
                fBossAttackSpeedReduction = 0.2f;
            }
            else if (stats.BossAttackSpeedReductionMultiplier > 0) // FF provided by someone else.
            {
                fBossAttackSpeedReduction = stats.BossAttackSpeedReductionMultiplier;
            }
            // Figure out what the new Physical DPS should be based on that.
            fSegmentDPS = TDK.bo.GetDPSByType(ATTACK_TYPES.AT_MELEE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
            fNewIncPhysDPS = fSegmentDPS / (1 + fBossAttackSpeedReduction);
            // Send the difference to the Mitigation value.
            fSegmentMitigation = fSegmentDPS - fNewIncPhysDPS;
            fTotalMitigation[(int)MitigationSub.Haste] += fSegmentMitigation;
            fCurrentDTPS[(int)SurvivalSub.Physical] -= fSegmentMitigation;
            #endregion

            #endregion
            #region ** Avoidance Mitigation **
            // Let's see how much damage was avoided.
            // Raise the total mitgation by that amount.
            if (bFactorInAvoidance)
            {
                fSegmentDPS = fNewIncPhysDPS;
                fNewIncPhysDPS = TDK.bo.GetDPSByType(ATTACK_TYPES.AT_MELEE, 0, 0, 0, fBossAttackSpeedReduction, stats.Miss, stats.Dodge, stats.EffectiveParry, 0, 0, 0, 0, 0, 0, 0);
                fSegmentMitigation = fSegmentDPS - fNewIncPhysDPS;
                fTotalMitigation[(int)MitigationSub.Avoidance] += fSegmentMitigation;
                fCurrentDTPS[(int)SurvivalSub.Physical] -= fSegmentMitigation;
            }
            #endregion
            #endregion

            #region *** Dam Reduced (AMS, Armor, Magic Resist, DamageTaken Modifiers) ***
            #region ** Anti-Magic Shell **
            // TODO: This is a CD, so would only be in BURST.
            // Anti-Magic Shell. ////////////////////////////////////////////////////////
            // Talent: MagicSuppression increases AMS by 8/16/25% per point.
            // Glyph: GlyphofAntiMagicShell increases AMS by 2 sec.
            // AMS has a 45 sec CD.
            float amsDuration = (5f + (TDK.Char.DeathKnightTalents.GlyphofAntiMagicShell == true ? 2f : 0f)) * (1 + stats.DefensiveCooldownDurationMultiplier);
            float amsUptimePct = amsDuration / 45f;
            // AMS reduces damage taken by 75% up to a max of 50% health.
            float amsReduction = 0.75f * (1f + (TDK.Char.DeathKnightTalents.MagicSuppression * .25f / 3));
            float amsReductionMax = stats.Health * 0.5f * (1 + stats.DefensiveCooldownReductionMultiplier);
            // up to 50% of health means that the amdDRvalue equates to the raw damage points removed.  
            // This means that toon health and INC damage values from the options pane are going to affect this quite a bit.
            float amsDRvalue = (Math.Min(amsReductionMax, (fMagicDamageDPS * amsDuration) * amsReduction) * amsUptimePct);
            // Raise the TotalMitigation by that amount.
            fCurrentDTPS[(int)SurvivalSub.Magic] -= amsDRvalue;
            fTotalMitigation[(int)MitigationSub.AMS] += amsDRvalue;
            #endregion
            #region ** Armor Dam Mitigation **
            // For any physical only damage reductions. 
            // Factor in armor Dam Reduction
            fSegmentMitigation = fPhyDamageDPS * ArmorDamageReduction;
//            calcs.ArmorMitigation = fSegmentMitigation;
            fTotalMitigation[(int)MitigationSub.Armor] += fSegmentMitigation;
            fCurrentDTPS[(int)SurvivalSub.Physical] *= 1f - ArmorDamageReduction;
            #endregion
            #region ** Resistance Dam Mitigation **
            // For any physical only damage reductions. 
            // Factor in armor Dam Reduction
            fSegmentMitigation = fMagicDamageDPS * fMagicDR;
            fTotalMitigation[(int)MitigationSub.Magic] += fSegmentMitigation;
            fCurrentDTPS[(int)SurvivalSub.Magic] -= fCurrentDTPS[(int)SurvivalSub.Magic] * fMagicDR;
            #endregion
            #region ** Dam Taken Mitigation **
            fTotalMitigation[(int)MitigationSub.DamageReduction] += fMagicDamageDPS * (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.SpellDamageTakenReductionMultiplier   );
            fTotalMitigation[(int)MitigationSub.DamageReduction] += fBleedDamageDPS * (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier);
            fTotalMitigation[(int)MitigationSub.DamageReduction] += fPhyDamageDPS   * (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier);
            
            fCurrentDTPS[(int)SurvivalSub.Magic]    *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.SpellDamageTakenReductionMultiplier   );
            fCurrentDTPS[(int)SurvivalSub.Bleed]    *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier);
            fCurrentDTPS[(int)SurvivalSub.Physical] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier);
            #endregion
            #region ** Dam Absorbed ** 
            fTotalMitigation[(int)MitigationSub.DamageReduction] += stats.DamageAbsorbed;

//            fCurrentDTPS[(int)SurvivalSub.Magic] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.SpellDamageTakenReductionMultiplier);
//            fCurrentDTPS[(int)SurvivalSub.Bleed] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier);
//            fCurrentDTPS[(int)SurvivalSub.Physical] *= (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier);

            #endregion
            #endregion

            #region ** Boss Handler Mitigation (Impedences) **
            float TotalDuration = 0;
            float ImprovedDuration = 0;
            float ImpedenceMitigation = 0;
            #region ** Fear Based Mitigation (fear reduction) **
            if (TDK.bo.Fears.Count > 0)
            {
                foreach (Impedance m in TDK.bo.Fears)
                {
                    TotalDuration += m.Duration;
                }
                ImprovedDuration = TotalDuration / (1 + stats.FearDurReduc);
                fSegmentMitigation = (TotalDuration - ImprovedDuration) /* Add some multiplier to this */;
                ImpedenceMitigation += fSegmentMitigation;
            }
            #endregion

            #region ** Movement Based Mitigation (run speed) **
            if (TDK.bo.Moves.Count > 0)
            {
                TotalDuration = 0;
                foreach (Impedance m in TDK.bo.Moves)
                {
                    TotalDuration += m.Duration;
                }
                ImprovedDuration = TotalDuration / (1 + stats.MovementSpeed);
                fSegmentMitigation = (TotalDuration - ImprovedDuration) /* Add some multiplier to this */;
                ImpedenceMitigation += fSegmentMitigation;
            }
            #endregion

            #region ** Disarm Based Mitigation (Disarm reduction) **
            if (TDK.bo.Disarms.Count > 0)
            {
                TotalDuration = 0;
                foreach (Impedance m in TDK.bo.Disarms)
                {
                    TotalDuration += m.Duration;
                }
                ImprovedDuration = TotalDuration / (1 + stats.DisarmDurReduc);
                fSegmentMitigation = (TotalDuration - ImprovedDuration) /* Add some multiplier to this */;
                ImpedenceMitigation += fSegmentMitigation;
            }
            #endregion

            #region ** Stun Based Mitigation (stun reduction) **
            if (TDK.bo.Stuns.Count > 0)
            {
                TotalDuration = 0;
                foreach (Impedance m in TDK.bo.Stuns)
                {
                    TotalDuration += m.Duration;
                }
                ImprovedDuration = TotalDuration / (1 + stats.StunDurReduc);
                fSegmentMitigation = (TotalDuration - ImprovedDuration) /* Add some multiplier to this */;
                ImpedenceMitigation += fSegmentMitigation;
            }
            #endregion
            // calcs.ImpedenceMitigation = ImpedenceMitigation;
            fTotalMitigation[(int)MitigationSub.Impedences] += ImpedenceMitigation;
            #endregion

            #region ** Heals not from DS **
            fSegmentMitigation = StatConversion.ApplyMultiplier(stats.Healed, stats.HealingReceivedMultiplier);
            fSegmentMitigation += (StatConversion.ApplyMultiplier(stats.Hp5, stats.HealingReceivedMultiplier) / 5);
            fSegmentMitigation += StatConversion.ApplyMultiplier(stats.HealthRestore, stats.HealingReceivedMultiplier);
            // Health Returned by other sources than DS:
            if (stats.HealthRestoreFromMaxHealth > 0)
                fSegmentMitigation += (stats.HealthRestoreFromMaxHealth * stats.Health);
            fTotalMitigation[(int)MitigationSub.Heals] = fSegmentMitigation;
            #endregion

            return fTotalMitigation;
        }
Esempio n. 2
0
        private float[] GetSurvival(ref TankDKChar TDK, StatsDK stats, float[] DamagePercentages, float ArmorDamageReduction, float fMagicDR)
        {
            // For right now Survival Rating == Effective Health will be HP + Armor/Resistance mitigation values.
            // Everything else is really mitigating damage based on RNG.

            // The health bonus from Frost presence is now include in the character by default.
            float fPhysicalSurvival = stats.Health;
            float fBleedSurvival = stats.Health;
            float fMagicalSurvival = stats.Health;

            // Physical damage:
            fPhysicalSurvival = GetEffectiveHealth(stats.Health, ArmorDamageReduction, DamagePercentages[(int)SurvivalSub.Physical]);

            // Bleed damage:
            fBleedSurvival    = GetEffectiveHealth(stats.Health, 0, DamagePercentages[(int)SurvivalSub.Bleed]);
            
            // Magic Dam:
            fMagicalSurvival  = GetEffectiveHealth(stats.Health, fMagicDR, DamagePercentages[(int)SurvivalSub.Magic]);

            // Since Armor plays a role in Survival, so shall the other damage taken adjusters.
            // Note, it's expected that (at least for tanks) that DamageTakenMultiplier will be Negative.
            // JOTHAY NOTE: The above is no longer true. DamageTakenReductionMultiplier will now be positive, but must be handled multiplicatively and inversely
            // JOTHAY NOTE: YOUR FORMULAS HAVE BEEN UPDATED TO REFLECT THIS CHANGE
            // So the next line should INCREASE survival because 
            // fPhysicalSurvival * (1 - [some negative value] * (1 - [0 or some negative value])
            // will look like:
            // fPhysicalSurvival * 1.xx * 1.xx
            fPhysicalSurvival       = fPhysicalSurvival / ((1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier));
            fBleedSurvival          = fBleedSurvival    / ((1f - stats.DamageTakenReductionMultiplier) * (1f - stats.PhysicalDamageTakenReductionMultiplier));
            fMagicalSurvival        = fMagicalSurvival  / ((1f - stats.DamageTakenReductionMultiplier) * (1f - stats.SpellDamageTakenReductionMultiplier   ));
            float[] SurvivalResults = new float[EnumHelper.GetCount(typeof(SurvivalSub))];
            SurvivalResults[(int)SurvivalSub.Physical] = fPhysicalSurvival;
            SurvivalResults[(int)SurvivalSub.Bleed] = fBleedSurvival;
            SurvivalResults[(int)SurvivalSub.Magic] = fMagicalSurvival;
            return SurvivalResults;
        }
Esempio n. 3
0
 private float[] GetMitigation(ref TankDKChar TDK, StatsDK stats, Rotation rot, float fPercentCritMitigation, float ArmorDamageReduction, float[] fCurrentDTPS, float fMagicDR)
 {
     return GetMitigation(ref TDK, stats, rot, fPercentCritMitigation, ArmorDamageReduction, fCurrentDTPS, fMagicDR, true);
 }
Esempio n. 4
0
        private float SoftCapSurvival(TankDKChar TDK, float attackValue, float origValue,  bool bIgnoreSoftCap)
        {
            float cappedValue = origValue;
            //
            double survivalCap = ((double)attackValue * (double)TDK.calcOpts.HitsToSurvive) / 1000d;
            if (bIgnoreSoftCap)
                survivalCap = ((double)attackValue * ((double)TDK.calcOpts.HitsToSurvive * 1.077)) / 1000d;
            double survivalRaw = origValue / 1000f;

            //Implement Survival Soft Cap
            if (survivalRaw <= survivalCap) 
            {
                cappedValue = 1000f * (float)survivalRaw;
            }
            else 
            {
                double x = survivalRaw;
                double cap = survivalCap;
                double fourToTheNegativeFourThirds = Math.Pow(4d, -4d / 3d);
                double topLeft = Math.Pow(((x - cap) / cap) + fourToTheNegativeFourThirds, 1d / 4d);
                double topRight = Math.Pow(fourToTheNegativeFourThirds, 1d / 4d);
                double fracTop = topLeft - topRight;
                double fraction = fracTop / 2d;
                double y = (cap * fraction + cap);
                cappedValue = 1000f * (float)y;
            }
            return cappedValue;
        }
Esempio n. 5
0
 private float SoftCapSurvival(TankDKChar TDK, float attackValue, float origValue)
 {
     return SoftCapSurvival(TDK, attackValue, origValue, false);
 }
Esempio n. 6
0
        /// <summary>
        /// Get Character Stats for multiple calls.  Allowing means by which to stack different sets/Special effects.
        /// </summary>
        /// <param name="character"></param>
        /// <param name="additionalItem"></param>
        /// <param name="sType">Enum describing which set of stats we want.</param>
        /// <returns></returns>
        private StatsDK GetCharacterStats(Character character, Item additionalItem, StatType sType, TankDKChar TDK, Rotation rot = null)
        {
            StatsDK statsTotal = new StatsDK();
            if (null == character.CalculationOptions)
            {
                // Possibly put some error text here.
                return statsTotal;
            }
            // Warning TDK can be blank at this point.
            TDK.Char = character;
            TDK.calcOpts = character.CalculationOptions as CalculationOptionsTankDK;
            TDK.bo = character.BossOptions;

            // Start populating data w/ Basic racial & class baseline.
            Stats BStats = BaseStats.GetBaseStats(character);
            statsTotal.Accumulate(BStats);
            statsTotal.BaseAgility = BStats.Agility;

            AccumulateItemStats(statsTotal, character, additionalItem);
            // Stack only the info we care about.
            statsTotal = GetRelevantStatsLocal(statsTotal);

            AccumulateBuffsStats(statsTotal, character.ActiveBuffs);
            AccumulateSetBonusStats(statsTotal, character.SetBonusCount);

            #region Tier Bonuses: Tank
            #region T11
            int tierCount;
            if (character.SetBonusCount.TryGetValue("Magma Plated Battlearmor", out tierCount))
            {
                if (tierCount >= 2) { statsTotal.b2T11_Tank = true; }
                if (tierCount >= 4) { statsTotal.b4T11_Tank = true; }
            }
            if (statsTotal.b4T11_Tank)
                statsTotal.AddSpecialEffect(_SE_IBF[1]);
            else
                statsTotal.AddSpecialEffect(_SE_IBF[0]);
            #endregion
            #region T12
            if (character.SetBonusCount.TryGetValue("Elementium Deathplate Battlearmor", out tierCount))
            {
                if (tierCount >= 2) { statsTotal.b2T12_Tank = true; }
                if (tierCount >= 4) { statsTotal.b4T12_Tank = true; }
            }
            if (statsTotal.b2T12_Tank)
            {
                // Your melee attacks cause Burning Blood on your target, 
                // which deals 800 Fire damage every 2 for 6 sec and 
                // causes your abilities to behave as if you had 2 diseases 
                // present on the target.
                // Implemented in CombatState DiseaseCount

                statsTotal.FireDamage = 800 / 2;
            }
            if (statsTotal.b4T12_Tank)
            {
                // Your when your Dancing Rune Weapon expires, it grants 15% additional parry chance for 12 sec.
                // Implemented in DRW talent Static Special Effect.
            }
            #endregion
            #region T13
            if (character.SetBonusCount.TryGetValue("Necrotic Boneplate Armor", out tierCount))
            {
                if (tierCount >= 2) { statsTotal.b2T13_Tank = true; }
                if (tierCount >= 4) { statsTotal.b4T13_Tank = true; }
            }
            if (statsTotal.b2T13_Tank)
            {
                // When an attack drops your health below 35%, one of your Blood Runes 
                // will immediately activate and convert into a Death Rune for the next 
                // 20 sec. This effect cannot occur more than once every 45 sec.
            }
            if (statsTotal.b4T13_Tank)
            {
                // Your Vampiric Blood ability also affects all party and raid members 
                // for 50% of the effect it has on you.
            }
            #endregion
            #endregion

            Rawr.DPSDK.CalculationsDPSDK.RemoveDuplicateRunes(statsTotal, character, true/*statsTotal.bDW*/);
            Rawr.DPSDK.CalculationsDPSDK.AccumulateTalents(statsTotal, character);
            Rawr.DPSDK.CalculationsDPSDK.AccumulatePresenceStats(statsTotal, Presence.Blood, character.DeathKnightTalents);


            statsTotal.ArcaneResistance += statsTotal.ArcaneResistanceBuff; statsTotal.ArcaneResistanceBuff = 0f;
            statsTotal.FireResistance   += statsTotal.FireResistanceBuff;   statsTotal.FireResistanceBuff   = 0f;
            statsTotal.FrostResistance  += statsTotal.FrostResistanceBuff;  statsTotal.FrostResistanceBuff  = 0f;
            statsTotal.NatureResistance += statsTotal.NatureResistanceBuff; statsTotal.NatureResistanceBuff = 0f;
            statsTotal.ShadowResistance += statsTotal.ShadowResistanceBuff; statsTotal.ShadowResistanceBuff = 0f;

            /* At this point, we're combined all the data from gear and talents and all that happy jazz.
             * However, we haven't applied any special effects nor have we applied any multipliers.
             * Many special effects are now getting dependant upon combat info (rotations).
             */
            StatsDK PreRatingsBase = statsTotal.Clone() as StatsDK;
            // Apply the ratings to actual stats.
            ProcessRatings(statsTotal);
            ProcessAvoidance(statsTotal, TDK.bo.Level, TDK.Char, PreRatingsBase);
            statsTotal.EffectiveParry = 0;
            if (character.MainHand != null)
            {
                statsTotal.EffectiveParry = statsTotal.Parry;
            }
            float fChanceToGetHit = 1f - Math.Min(1f, statsTotal.Miss + statsTotal.Dodge + statsTotal.EffectiveParry);

            // Now comes the special handling for secondary stats passes that are dependant upon Boss & Rotation values.
            if (sType != StatType.Unbuffed
                && (null != TDK.bo && null != rot)) // Make sure we have the rotation and Boss info.
            {
                #region Special Effects
                #region Talent: Bone Shield
                if (character.DeathKnightTalents.BoneShield > 0)
                {
                    int BSStacks = 4;  // The number of bones by default.  
                    if (Rawr.Properties.GeneralSettings.Default.PTRMode)
                        BSStacks = 6;  // The number of bones by default.  
                    float BoneLossRate = Math.Max(2f, TDK.bo.DynamicCompiler_Attacks.AttackSpeed / fChanceToGetHit);  // 2 sec internal cooldown on loosing bones so the DK can't get spammed to death.  
                    float moveVal = character.DeathKnightTalents.GlyphofBoneShield ? 0.15f : 0f;
                    SpecialEffect primary = new SpecialEffect(Trigger.Use,
                        new Stats() { DamageTakenReductionMultiplier = 0.20f, BonusDamageMultiplier = 0.02f, MovementSpeed = moveVal, },
                        BoneLossRate * BSStacks, 60) {BypassCache = true,};
                    statsTotal.AddSpecialEffect(primary);
                }
                #endregion
                #region Vengeance
                // Vengence has the chance to increase AP.
                int iVengenceMax = (int)(statsTotal.Stamina + (BaseStats.GetBaseStats(character).Health) * .1);
                int iAttackPowerMax = (int)statsTotal.AttackPower + iVengenceMax;
                float mitigatedDPS = TDK.bo.GetDPSByType(TDK.role, 0, statsTotal.DamageTakenReductionMultiplier,
                    0, .14f, statsTotal.Miss, statsTotal.Dodge, statsTotal.EffectiveParry, 0, 0,
                    0, 0, 0, 0, 0);
                    //statsTotal.ArcaneResistance, statsTotal.FireResistance, statsTotal.FrostResistance, statsTotal.NatureResistance, statsTotal.ShadowResistance);
                mitigatedDPS = mitigatedDPS * (1 - (float)StatConversion.GetArmorDamageReduction(TDK.bo.Level, statsTotal.Armor, 0f, 0f));
                float APStackSingle = mitigatedDPS * 0.05f * TDK.bo.DynamicCompiler_Attacks.AttackSpeed;
                int APStackCountMax = (int)Math.Floor(iVengenceMax / APStackSingle);
                SpecialEffect seVeng = new SpecialEffect(Trigger.DamageTaken,
                    new Stats() { AttackPower = APStackSingle },
                    2 * 10,
                    0,
                    1,
                    APStackCountMax) { BypassCache = true, };
                Dictionary<Trigger, float> triggerInterval = new Dictionary<Trigger,float>();
                Dictionary<Trigger, float> triggerChance = new Dictionary<Trigger,float>();
                triggerInterval.Add(Trigger.DamageTaken, TDK.bo.DynamicCompiler_Attacks.AttackSpeed);
                triggerChance.Add(Trigger.DamageTaken, 1f); // MitigatedDPS already factors in avoidance.
                statsTotal.VengenceAttackPower = seVeng.GetAverageStats(triggerInterval, triggerChance).AttackPower;
                statsTotal.AttackPower += statsTotal.VengenceAttackPower * TDK.calcOpts.VengeanceWeight;
                #endregion
                statsTotal.AddSpecialEffect(_SE_DeathPact);
                // For now we just factor them in once.
                Rawr.DPSDK.StatsSpecialEffects se = new Rawr.DPSDK.StatsSpecialEffects(rot.m_CT, rot, TDK.bo);
                StatsDK statSE = new StatsDK();

                foreach (SpecialEffect effect in statsTotal.SpecialEffects())
                {
                    if (HasRelevantStats(effect.Stats))
                    {
                        statSE.Accumulate(se.getSpecialEffects(effect));
//                        statsTotal.Accumulate(se.getSpecialEffects(effect)); // This is done further down.
                    }
                }

                // Darkmoon card greatness procs
                if (statSE.HighestStat > 0 || statSE.Paragon > 0)
                {
                    if (statSE.Strength >= statSE.Agility) { statSE.Strength += statSE.HighestStat + statSE.Paragon; }
                    else if (statSE.Agility > statSE.Strength) { statSE.Agility += statSE.HighestStat + statSE.Paragon; }
                    statSE.HighestStat = 0;
                    statSE.Paragon = 0;
                }

                // Any Modifiers from stats need to be applied to statSE
                statSE.Strength = StatConversion.ApplyMultiplier(statSE.Strength, statsTotal.BonusStrengthMultiplier);
                statSE.Agility = StatConversion.ApplyMultiplier(statSE.Agility, statsTotal.BonusAgilityMultiplier);
                statSE.Stamina = StatConversion.ApplyMultiplier(statSE.Stamina, statsTotal.BonusStaminaMultiplier);
                //            statSE.Stamina = (float)Math.Floor(statSE.Stamina);
                statSE.Armor = StatConversion.ApplyMultiplier(statSE.Armor, statsTotal.BaseArmorMultiplier);
                statSE.AttackPower = StatConversion.ApplyMultiplier(statSE.AttackPower, statsTotal.BonusAttackPowerMultiplier);
                statSE.BonusArmor = StatConversion.ApplyMultiplier(statSE.BonusArmor, statsTotal.BonusArmorMultiplier);

                statSE.Armor += statSE.BonusArmor;
                statSE.Health += StatConversion.GetHealthFromStamina(statSE.Stamina) + statSE.BattlemasterHealthProc;
                statSE.Health = statSE.Health * (1 + statSE.BonusHealthMultiplier);
                statsTotal.BonusHealthMultiplier = ((1 + statsTotal.BonusHealthMultiplier) * (1 + statSE.BonusHealthMultiplier)) - 1 ;
                if (character.DeathKnightTalents.BladedArmor > 0)
                {
                    statSE.AttackPower += (statSE.Armor / 180f) * (float)character.DeathKnightTalents.BladedArmor;
                }
                statSE.AttackPower += StatConversion.ApplyMultiplier((statSE.Strength * 2), statsTotal.BonusAttackPowerMultiplier);
                statSE.ParryRating += statSE.Strength * 0.27f;

                // Any Modifiers from statSE need to be applied to stats
                statsTotal.Strength = StatConversion.ApplyMultiplier(statsTotal.Strength, statSE.BonusStrengthMultiplier);
                statsTotal.Agility = StatConversion.ApplyMultiplier(statsTotal.Agility, statSE.BonusAgilityMultiplier);
                statsTotal.Stamina = StatConversion.ApplyMultiplier(statsTotal.Stamina, statSE.BonusStaminaMultiplier);
                //            stats.Stamina = (float)Math.Floor(stats.Stamina);
                statsTotal.Armor = StatConversion.ApplyMultiplier(statsTotal.Armor, statSE.BaseArmorMultiplier);
                statsTotal.AttackPower = StatConversion.ApplyMultiplier(statsTotal.AttackPower, statSE.BonusAttackPowerMultiplier);
                statsTotal.BonusArmor = StatConversion.ApplyMultiplier(statsTotal.BonusArmor, statSE.BonusArmorMultiplier);

                statsTotal.Accumulate(statSE);
                PreRatingsBase.Miss += statSE.Miss;
                PreRatingsBase.Dodge += statSE.Dodge;
                PreRatingsBase.Parry += statSE.Parry;
#if DEBUG
                if (float.IsNaN(statsTotal.Stamina))
                    throw new Exception("Something very wrong in stats.");
#endif
                #endregion // Special effects
            }
            // Apply the Multipliers
            ProcessStatModifiers(statsTotal, character.DeathKnightTalents.BladedArmor, character);
            ProcessAvoidance(statsTotal, TDK.bo.Level, TDK.Char, PreRatingsBase);
            if (character.MainHand != null)
            {
                statsTotal.EffectiveParry = statsTotal.Parry;
            }
            return (statsTotal);
        }
Esempio n. 7
0
        private CharacterCalculationsTankDK GetCharacterCalculations(TankDKChar TDK, StatsDK stats, Rotation rot, bool isBurstCalc, bool needsDisplayCalcs)
        {
            CharacterCalculationsTankDK calcs = new CharacterCalculationsTankDK();

            // Level differences.
            int iLevelDiff = Math.Max(TDK.bo.Level - TDK.Char.Level, 0);

            float fChanceToGetHit = 1f - Math.Min(1f, stats.Miss + stats.Dodge + stats.EffectiveParry);
            float ArmorDamageReduction = (float)StatConversion.GetArmorDamageReduction(TDK.bo.Level, stats.Armor, 0f, 0f);

            #region **** Setup Fight parameters ****

            // Get the values of each type of damage in %.
            // So first we get each type of damage in the same units: DPS.
            // Get the total DPS.

            float[] fCurrentDTPS = new float[3];
            fCurrentDTPS[(int)SurvivalSub.Physical] = 0f;
            fCurrentDTPS[(int)SurvivalSub.Bleed] = 0f;
            fCurrentDTPS[(int)SurvivalSub.Magic] = 0f;
            float[] fCurrentDmgBiggestHit = new float[3];
            fCurrentDmgBiggestHit[(int)SurvivalSub.Physical] = 0f;
            fCurrentDmgBiggestHit[(int)SurvivalSub.Bleed] = 0f;
            fCurrentDmgBiggestHit[(int)SurvivalSub.Magic] = 0f;
            float[] fCurrentDTPSPerc = new float[3];
            fCurrentDTPSPerc[(int)SurvivalSub.Physical] = 1f;
            fCurrentDTPSPerc[(int)SurvivalSub.Bleed] = 0f;
            fCurrentDTPSPerc[(int)SurvivalSub.Magic] = 0f;
            float fTotalDTPS = 0;

            float fAvoidanceTotal = 1f - fChanceToGetHit;

            // We want to start getting the Boss Handler stuff going on.
            // Setup initial Boss data.
            // How much of what kind of damage does this boss deal with?
            #region ** Incoming Boss Dam **
            // Let's make sure this is even valid
            float DPHit = 0;
            float DPTick = 0;
            switch (TDK.calcOpts.PlayerRole)
            {
                case 0:
                    TDK.role = PLAYER_ROLES.MainTank;
                    break;
                case 1:
                    TDK.role = PLAYER_ROLES.OffTank;
                    break;
                case 2:
                    TDK.role = PLAYER_ROLES.TertiaryTank;
                    break;
                default:
                    TDK.role = PLAYER_ROLES.MainTank;
                    break;
            }
            TDK.role = PLAYER_ROLES.MainTank;
            foreach (Attack a in TDK.bo.Attacks)
            {
                // PlayerRole on calcOpts is MT=0, OT=1, TT=2, Any Tank = 3
                // Any Tank means it should be affected by anything that affects a tanking role
                if (a.AffectsRole[PLAYER_ROLES.MainTank] && (TDK.calcOpts.PlayerRole == 0 || TDK.calcOpts.PlayerRole == 3)
                    || a.AffectsRole[PLAYER_ROLES.OffTank] && (TDK.calcOpts.PlayerRole == 1 || TDK.calcOpts.PlayerRole == 3)
                    || a.AffectsRole[PLAYER_ROLES.TertiaryTank] && (TDK.calcOpts.PlayerRole == 2 || TDK.calcOpts.PlayerRole == 3))
                {
                    // TODO: Figure out a way to get the phase changes handled.
                    DPHit = a.DamagePerHit;
                    DPTick = a.DamagePerTick;
                    if (a.DamageIsPerc)
                    {
#if DEBUG
                        if ((a.DamagePerHit >= 1f) || (a.DamagePerTick >= 1f))
                            throw new Exception("Percentage Damage is >= 100%.");
#endif
                        DPHit = a.DamagePerHit * stats.Health;
                        DPTick = a.DamagePerTick * stats.Health;
                    }

                    // Bleeds vs Magic vs Physical
                    if (a.DamageType == ItemDamageType.Physical) 
                    {
                        // Bleed or Physical
                        // Need to figure out how to determine bleed vs. physical hits.
                        // Also need to balance out the physical hits and balance the hit rate.
                        // JOTHAY NOTE: Bleeds are DoTs
                        if (a.IsDoT) {
                            fCurrentDTPS[(int)SurvivalSub.Bleed] += GetDPS(DPHit, a.AttackSpeed) + GetDPS(DPTick, a.TickInterval);
                            if (fCurrentDmgBiggestHit[(int)SurvivalSub.Bleed] < DPHit + (DPTick * a.NumTicks))
                                fCurrentDmgBiggestHit[(int)SurvivalSub.Bleed] = DPHit + (DPTick * a.NumTicks);
                        } 
                        else 
                        {
                            fCurrentDTPS[(int)SurvivalSub.Physical] += GetDPS(DPHit, a.AttackSpeed);
                            if (fCurrentDmgBiggestHit[(int)SurvivalSub.Physical] < DPHit)
                                fCurrentDmgBiggestHit[(int)SurvivalSub.Physical] = DPHit;
                        }
                    } 
                    else 
                    {
                        // Magic now covering magical dots.
                        fCurrentDTPS[(int)SurvivalSub.Magic] += GetDPS(DPHit, a.AttackSpeed) + GetDPS(DPTick, a.TickInterval);
                        if (fCurrentDmgBiggestHit[(int)SurvivalSub.Magic] < DPHit + (DPTick * a.NumTicks))
                            fCurrentDmgBiggestHit[(int)SurvivalSub.Magic] = DPHit + (DPTick * a.NumTicks);
                    }
                }
            }
            fTotalDTPS += fCurrentDTPS[(int)SurvivalSub.Physical];
            fTotalDTPS += fCurrentDTPS[(int)SurvivalSub.Bleed];
            fTotalDTPS += fCurrentDTPS[(int)SurvivalSub.Magic];

            if (fTotalDTPS > 0)
            {
                fCurrentDTPSPerc[(int)SurvivalSub.Physical] = fCurrentDTPS[(int)SurvivalSub.Physical] / fTotalDTPS;
                fCurrentDTPSPerc[(int)SurvivalSub.Bleed]    = fCurrentDTPS[(int)SurvivalSub.Bleed] / fTotalDTPS;
                fCurrentDTPSPerc[(int)SurvivalSub.Magic]    = fCurrentDTPS[(int)SurvivalSub.Magic] / fTotalDTPS;
            }
            #endregion

            // Set the Fight Duration to no larger than the Berserk Timer
            // Question: What is the units for Berserk & Speed Timer? MS/S/M?
            #endregion

            #region ***** Survival Rating *****
            // Magical damage:
            // if there is a max resistance, then it's likely they are stacking for that resistance.  So factor in that Max resistance.
            float fMaxResist = Math.Max(stats.ArcaneResistance, stats.FireResistance);
            fMaxResist       = Math.Max(fMaxResist, stats.FrostResistance);
            fMaxResist       = Math.Max(fMaxResist, stats.NatureResistance);
            fMaxResist       = Math.Max(fMaxResist, stats.ShadowResistance);

            float fMagicDR = StatConversion.GetAverageResistance(TDK.bo.Level, TDK.Char.Level, fMaxResist, 0f);
            calcs.MagicDamageReduction = fMagicDR;

            float[] SurvivalResults = new float [EnumHelper.GetCount(typeof(SurvivalSub))];
            SurvivalResults = GetSurvival(ref TDK, stats, fCurrentDTPSPerc, ArmorDamageReduction, fMagicDR);

            calcs.ArmorDamageReduction   = ArmorDamageReduction;
            calcs.PhysicalSurvival       = SoftCapSurvival(TDK, fCurrentDmgBiggestHit[(int)SurvivalSub.Physical], SurvivalResults[(int)SurvivalSub.Physical], isBurstCalc);
            calcs.BleedSurvival          = SoftCapSurvival(TDK, fCurrentDmgBiggestHit[(int)SurvivalSub.Bleed],    SurvivalResults[(int)SurvivalSub.Bleed], isBurstCalc);
            calcs.MagicSurvival          = SoftCapSurvival(TDK, fCurrentDmgBiggestHit[(int)SurvivalSub.Magic],    SurvivalResults[(int)SurvivalSub.Magic], isBurstCalc);
            calcs.HitsToSurvive          = TDK.calcOpts.HitsToSurvive;
            #endregion

            #region ***** Threat Rating *****
            rot.TotalDamage += (int)(stats.FireDamage * (1 + stats.BonusFireDamageMultiplier) * rot.CurRotationDuration);
            rot.TotalThreat += (int)(stats.FireDamage * (1 + stats.BonusFireDamageMultiplier) * rot.CurRotationDuration) * 2;
            calcs.RotationTime = rot.CurRotationDuration; // Display the rot in secs.
            calcs.Threat = rot.m_TPS;
            calcs.DPS = rot.m_DPS;
            calcs.Blood = rot.m_BloodRunes;
            calcs.Frost = rot.m_FrostRunes;
            calcs.Unholy = rot.m_UnholyRunes;
            calcs.Death = rot.m_DeathRunes;
            calcs.RP = rot.m_RunicPower;
            calcs.TotalThreat = (int)rot.TotalThreat;

            calcs.ThreatWeight = TDK.calcOpts.ThreatWeight;
            if (needsDisplayCalcs)
            {
                TDK.calcOpts.szRotReport = rot.ReportRotation();
            }
            #endregion

            #region ***** Mitigation Rating *****
            float[] fCurrentDTPSNoAvoid = new float[3];
            fCurrentDTPSNoAvoid = fCurrentDTPS.Clone() as float[];
            float[] fCurrentMitigation = GetMitigation(ref TDK, stats, rot, (stats.CritChanceReduction / .06f), ArmorDamageReduction, fCurrentDTPS, fMagicDR);
            calcs.ArmorMitigation        = fCurrentMitigation[(int)MitigationSub.Armor];
            calcs.AvoidanceMitigation    = fCurrentMitigation[(int)MitigationSub.Avoidance];
            calcs.CritMitigation         = fCurrentMitigation[(int)MitigationSub.Crit];
            calcs.DamageTakenMitigation  = fCurrentMitigation[(int)MitigationSub.DamageReduction];
            calcs.DamageTakenMitigation += fCurrentMitigation[(int)MitigationSub.Haste];
            calcs.HealsMitigation        = fCurrentMitigation[(int)MitigationSub.Heals];
            calcs.ImpedenceMitigation    = fCurrentMitigation[(int)MitigationSub.Impedences];
            calcs.MagicDamageReductedByAmount = fCurrentMitigation[(int)MitigationSub.AMS];
            calcs.MagicDamageReductedByAmount += fCurrentMitigation[(int)MitigationSub.Magic];

            calcs.Crit = (.06f - stats.CritChanceReduction);
            calcs.DTPS = 0;
            calcs.DTPSNoAvoidance = 0;
            foreach (float f in fCurrentDTPS)
            {
                // These are sometimes coming back as negative.
                // Assuming we are just 100% absorbing the attack, no damage
                if (f > 0) { calcs.DTPS += f; }
            }
            if (TDK.calcOpts.b_RecoveryInclAvoidance == false)
            {
                GetMitigation(ref TDK, stats, rot, (stats.CritChanceReduction / .06f), ArmorDamageReduction, fCurrentDTPSNoAvoid, fMagicDR, false);
                foreach (float f in fCurrentDTPSNoAvoid)
                {
                    // These are sometimes coming back as negative.
                    // Assuming we are just 100% absorbing the attack, no damage
                    if (f > 0) { calcs.DTPSNoAvoidance += f; }
                }
            }
            // Have to ensure we don't divide by 0
            calcs.Mitigation = StatConversion.MitigationScaler / (Math.Max(1f, calcs.DTPS) / fTotalDTPS);
            #endregion

            return calcs;
        }
Esempio n. 8
0
        public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations)
        {
            #region Setup
            CharacterCalculationsTankDK basecalcs = new CharacterCalculationsTankDK();
            CharacterCalculationsTankDK calcs = new CharacterCalculationsTankDK();
            TankDKChar TDK = new TankDKChar();

            if (character == null) { return calcs; }
            TDK.calcOpts = character.CalculationOptions as CalculationOptionsTankDK;
            if (TDK.calcOpts == null) { return calcs; }
            TDK.Char = character;
            TDK.bo = character.BossOptions;
            // Make sure there is at least one attack in the list.  
            // If there's not, add a Default Melee Attack for processing  
            if (TDK.bo.Attacks.Count < 1)
            {
                TDK.Char.IsLoading = true;
                TDK.bo.DamagingTargs = true;
                TDK.bo.Attacks.Add(BossHandler.ADefaultMeleeAttack);
                TDK.Char.IsLoading = false;
            }
            // Make sure there is a default melee attack  
            // If the above processed, there will be one so this won't have to process  
            // If the above didn't process and there isn't one, add one now  
            if (TDK.bo.DefaultMeleeAttack == null)
            {
                TDK.Char.IsLoading = true;
                TDK.bo.DamagingTargs = true;
                TDK.bo.Attacks.Add(BossHandler.ADefaultMeleeAttack);
                TDK.Char.IsLoading = false;
            }
            // Since the above forced there to be an attack it's safe to do this without a null check  
            // Attack bossAttack = TDK.bo.DefaultMeleeAttack;
            #endregion

            #region Stats
            // Get base stats that will be used for paperdoll:
            StatsDK stats = GetCharacterStats(TDK.Char, additionalItem) as StatsDK;
            // validate that we get a stats object;
            if (null == stats) { return calcs; }

            // This is the point that SHOULD have the right values according to the paper-doll.
            StatsDK sPaperDoll = stats.Clone() as StatsDK;
            #endregion

            #region Evaluation
            Rawr.DPSDK.CharacterCalculationsDPSDK DPSCalcs = new Rawr.DPSDK.CharacterCalculationsDPSDK();
            Rawr.DPSDK.CalculationOptionsDPSDK DPSopts = new Rawr.DPSDK.CalculationOptionsDPSDK();
            DPSopts.presence = Presence.Blood;
            DKCombatTable ct = new DKCombatTable(TDK.Char, stats, DPSCalcs, DPSopts, TDK.bo);
            Rotation rot = new Rotation(ct, true);
            rot.PRE_BloodDiseased();
            // Base calculation values.  This will give us Mitigation, and Survival w/ base stats.
            basecalcs = GetCharacterCalculations(TDK, stats, rot, false, needsDisplayCalculations);
            // Setup max values w/ everything turned on.
            stats = GetCharacterStats(TDK.Char, additionalItem, StatType.Maximum, TDK, rot);
            calcs.SEStats = stats.Clone() as StatsDK;
            ct = new DKCombatTable(TDK.Char, stats, DPSCalcs, DPSopts, TDK.bo);
            rot = new Rotation(ct, true);
            rot.PRE_BloodDiseased();

            calcs = GetCharacterCalculations(TDK, stats, rot, true, needsDisplayCalculations);

            #region Burst
            // Burst as On-Use Abilties.
            calcs.Burst = 0;
            calcs.BurstWeight = TDK.calcOpts.BurstWeight;
            if (calcs.BurstWeight > 0)
            {
                calcs.Burst += calcs.Survivability - basecalcs.Survivability;
                if (calcs.Burst < 0 || float.IsNaN(calcs.Burst)) { calcs.Burst = 0; } // This should never happen but just in case
                calcs.Burst += calcs.Mitigation - basecalcs.Mitigation;
                if (calcs.Burst < 0 || float.IsNaN(calcs.Burst)) { calcs.Burst = 0; } // This should never happen but just in case
                // Survival
                calcs.PhysicalSurvival = basecalcs.PhysicalSurvival;
                calcs.MagicSurvival = basecalcs.MagicSurvival;
                calcs.BleedSurvival = basecalcs.BleedSurvival;
                // Mitigation
                calcs.Mitigation = basecalcs.Mitigation;
            }
            #endregion

            #region **** Recovery: DS & Blood Shield ****
            float minDSHeal = stats.Health * .07f;
            // 4.1: DS Heals for 20% of Dam Taken over the last 5 secs.
            float DTPSFactor = calcs.DTPS * 5f;
            if (TDK.calcOpts.b_RecoveryInclAvoidance == false) 
            {
                DTPSFactor = calcs.DTPSNoAvoidance * 5f;
            }
            float DamDSHeal = (DTPSFactor * .20f) * (1 + .15f * TDK.Char.DeathKnightTalents.ImprovedDeathStrike); // IDS increases heals by .15 * level
            float DSHeal = Math.Max(minDSHeal, DamDSHeal);
            calcs.DSHeal = DSHeal;
            calcs.DSOverHeal = DSHeal * TDK.calcOpts.pOverHealing;
            calcs.DSCount = TDK.bo.BerserkTimer * rot.m_DSperSec;
            float BloodShield = (DSHeal * .5f) * (1 + (stats.Mastery * .0625f));
            calcs.BShield = BloodShield;
            // 4.3 Removing Hitchance for healing
            float DSHealsPSec = (DSHeal * rot.m_DSperSec * (1f - TDK.calcOpts.pOverHealing));
            calcs.TotalDShealed = DSHealsPSec * TDK.bo.BerserkTimer;
            float BShieldPSec = BloodShield * rot.m_DSperSec; // A new shield w/ each DS.
            calcs.TotalBShield = BShieldPSec * TDK.bo.BerserkTimer;
            calcs.Recovery = BloodShield + (DSHeal * (1f - TDK.calcOpts.pOverHealing));
            calcs.HPS += DSHealsPSec;
            calcs.DTPS -= BShieldPSec;
            calcs.RecoveryWeight = TDK.calcOpts.RecoveryWeight;
            #endregion

            #endregion

            #region Key Data Validation
            if (float.IsNaN(calcs.Threat) ||
                float.IsNaN(calcs.Survivability) ||
                float.IsNaN(calcs.Burst) ||
                float.IsNaN(calcs.Recovery) ||
                float.IsNaN(calcs.Mitigation) ||
                float.IsNaN(calcs.OverallPoints))
            {
#if DEBUG
                throw new Exception("One of the Subpoints are Invalid.");
#endif
            }
            #endregion

            #region Display only work
            calcs.Miss = sPaperDoll.Miss;
            calcs.Dodge = sPaperDoll.Dodge;
            calcs.Parry = sPaperDoll.Parry;

            calcs.BasicStats = sPaperDoll;
            calcs.SEStats = stats.Clone() as StatsDK;
            // The full character data.
            calcs.TargetLevel = TDK.bo.Level;

            if (null != rot.m_CT.MH)
            {
                calcs.TargetDodge = rot.m_CT.MH.chanceDodged;
                calcs.TargetMiss = rot.m_CT.MH.chanceMissed;
                calcs.TargetParry = rot.m_CT.MH.chanceParried;
                calcs.Expertise = rot.m_CT.m_Calcs.MHExpertise;
            }
            #endregion
            return calcs;
        }