Example #1
0
        public CombatTable2(Character c, Stats stats, CharacterCalculationsTankDK calcs, CalculationOptionsTankDK calcOpts)
        {
            if (calcOpts.bExperimental)
            {
                this.m_CState = new CombatState();
                if (c != null)
                {
                    if (c.DeathKnightTalents == null)
                    {
                        c.DeathKnightTalents = new DeathKnightTalents();
                    }
                    this.m_CState.m_Talents = (DeathKnightTalents)c.DeathKnightTalents.Clone();
                }
                this.m_CState.m_Stats = stats.Clone();
                m_Calcs = calcs;
                m_Opts  = calcOpts;
                this.m_CState.m_NumberOfTargets = (float)m_Opts.uNumberTargets;
                m_Rotation = calcOpts.m_Rotation;

                //TODO: Handle Expertise
                if (c.MainHand != null && c.MainHand.Item.Type != ItemType.None)
                {
                    m_CState.MH = new Weapon(c.MainHand.Item, m_CState.m_Stats, m_Opts, 0);
                    m_CState.OH = null;
                    if (c.MainHand.Slot != ItemSlot.TwoHand)
                    {
                        if (c.OffHand != null && c.OffHand.Item.Type != ItemType.None)
                        {
                            m_CState.OH = new Weapon(c.OffHand.Item, this.m_CState.m_Stats, m_Opts, 0);
                        }
                    }
                }
                else
                {
                    m_CState.MH = null;
                    m_CState.OH = null;
                }

                // Checking the rotation:
                if (m_Rotation.IcyTouch == 0 &&
                    m_Rotation.PlagueStrike == 0 &&
                    m_Rotation.BloodStrike == 0)
                {
                    // Then this is probably a null rotation, and
                    // so let's build one?
                    m_Rotation = new Rotation(this.m_CState.m_Talents);
                }

                BuildRotation();

                // TODO: move this out of the constructor
                CompileRotation(m_Rotation);
            }
        }
Example #2
0
        public void TankDK_Rotation()
        {
            Rawr.TankDK.CharacterCalculationsTankDK CalcTankDK = new Rawr.TankDK.CharacterCalculationsTankDK();
            CalculationOptionsTankDK calcOpts = new CalculationOptionsTankDK();

            Rawr.DK.StatsDK TotalStats = new Rawr.DK.StatsDK();

            Rawr.DPSDK.CharacterCalculationsDPSDK DPSCalcs = new Rawr.DPSDK.CharacterCalculationsDPSDK();
            Rawr.DPSDK.CalculationOptionsDPSDK    DPSopts  = new Rawr.DPSDK.CalculationOptionsDPSDK();

            Rawr.DK.DKCombatTable ct  = new Rawr.DK.DKCombatTable(m_char, TotalStats, DPSCalcs, DPSopts, m_char.BossOptions);
            Rawr.DK.Rotation      rot = new Rawr.DK.Rotation(ct, false);
            rot.PRE_BloodDiseased();
            Assert.IsTrue(rot.m_TPS > 0, "rotation BloodDiseased produces 0 DPS");
        }
        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;
        }
        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;
        }
Example #5
0
        public void TankDK_Rotation()
        {
            Rawr.TankDK.CharacterCalculationsTankDK CalcTankDK = new Rawr.TankDK.CharacterCalculationsTankDK();
            CalculationOptionsTankDK calcOpts = new CalculationOptionsTankDK();
            Rawr.DK.StatsDK TotalStats = new Rawr.DK.StatsDK();

            Rawr.DPSDK.CharacterCalculationsDPSDK DPSCalcs = new Rawr.DPSDK.CharacterCalculationsDPSDK();
            Rawr.DPSDK.CalculationOptionsDPSDK DPSopts = new Rawr.DPSDK.CalculationOptionsDPSDK();

            Rawr.DK.DKCombatTable ct = new Rawr.DK.DKCombatTable(m_char, TotalStats, DPSCalcs, DPSopts, m_char.BossOptions);
            Rawr.DK.Rotation rot = new Rawr.DK.Rotation(ct, false);
            rot.PRE_BloodDiseased();
            Assert.IsTrue(rot.m_TPS > 0, "rotation BloodDiseased produces 0 DPS");
        }