Пример #1
0
        public Stats getSpecialEffects(CalculationOptionsDPSDK calcOpts, SpecialEffect effect)
        {
            Stats    statsAverage = new Stats();
            Rotation rotation     = calcOpts.rotation;

            if (effect.Trigger == Trigger.Use)
            {
                effect.AccumulateAverageStats(statsAverage);
                foreach (SpecialEffect e in effect.Stats.SpecialEffects())
                {
                    statsAverage.Accumulate(this.getSpecialEffects(calcOpts, e), (effect.Duration / effect.Cooldown));
                }
            }
            else
            {
                double trigger             = 0f;
                float  chance              = 0f;
                float  unhastedAttackSpeed = 2f;
                switch (effect.Trigger)
                {
                case Trigger.MeleeCrit:
                case Trigger.PhysicalCrit:
                    trigger             = (1f / ((rotation.getMeleeSpecialsPerSecond() * (combatTable.DW ? 2f : 1f)) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f)));
                    chance              = combatTable.physCrits;
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

                case Trigger.MeleeHit:
                case Trigger.PhysicalHit:
                    trigger             = (1f / ((rotation.getMeleeSpecialsPerSecond() * (combatTable.DW ? 2f : 1f)) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f)));
                    chance              = 1f - (combatTable.missedSpecial + combatTable.dodgedSpecial) * (1f - combatTable.totalMHMiss);
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

                case Trigger.CurrentHandHit:
                    trigger = (1f / ((rotation.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f)));
                    chance  = 1f - (combatTable.missedSpecial + combatTable.dodgedSpecial) * (1f - combatTable.totalMHMiss);
                    // TODO: need to know if this is MH or OH.
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

                case Trigger.MainHandHit:
                    trigger             = (1f / ((rotation.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f)));
                    chance              = 1f - (combatTable.missedSpecial + combatTable.dodgedSpecial) * (1f - combatTable.totalMHMiss);
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

                case Trigger.OffHandHit:
                    trigger             = (1f / ((rotation.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f)));
                    chance              = 1f - (combatTable.missedSpecial + combatTable.dodgedSpecial) * (1f - combatTable.totalMHMiss);
                    unhastedAttackSpeed = (combatTable.OH != null ? combatTable.OH.baseSpeed : 2.0f);
                    break;

                case Trigger.DamageDone:
                    trigger             = 1f / (((rotation.getMeleeSpecialsPerSecond() * (combatTable.DW ? 2f : 1f)) + rotation.getSpellSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime: 0.5f));
                    chance              = (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial)) * (1f - combatTable.totalMHMiss);
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

                case Trigger.DamageOrHealingDone:
                    // Need to add Self Healing
                    trigger             = 1f / (((rotation.getMeleeSpecialsPerSecond() * (combatTable.DW ? 2f : 1f)) + rotation.getSpellSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f));
                    chance              = (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial)) * (1f - combatTable.totalMHMiss);
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

                case Trigger.DamageSpellCast:
                case Trigger.SpellCast:
                case Trigger.DamageSpellHit:
                case Trigger.SpellHit:
                    trigger = 1f / rotation.getSpellSpecialsPerSecond();
                    chance  = 1f - combatTable.spellResist;
                    break;

                case Trigger.DamageSpellCrit:
                case Trigger.SpellCrit:
                    trigger = 1f / rotation.getSpellSpecialsPerSecond();
                    chance  = combatTable.spellCrits;
                    break;

                case Trigger.BloodStrikeHit:
                    trigger = rotation.CurRotationDuration / (rotation.BloodStrike * (combatTable.DW ? 2f : 1f));
                    chance  = 1f;
                    break;

                case Trigger.HeartStrikeHit:
                    trigger = rotation.CurRotationDuration / rotation.HeartStrike;
                    chance  = 1f;
                    break;

                case Trigger.BloodStrikeOrHeartStrikeHit:
                    trigger = rotation.CurRotationDuration / ((rotation.BloodStrike + rotation.HeartStrike) * (combatTable.DW ? 2f : 1f));
                    chance  = 1f;
                    break;

                case Trigger.ObliterateHit:
                    trigger = rotation.CurRotationDuration / (rotation.Obliterate * (combatTable.DW ? 2f : 1f));
                    chance  = 1f;
                    break;

                case Trigger.ScourgeStrikeHit:
                    trigger = rotation.CurRotationDuration / rotation.ScourgeStrike;
                    chance  = 1f;
                    break;

                case Trigger.DeathStrikeHit:
                    trigger = rotation.CurRotationDuration / rotation.DeathStrike;
                    chance  = 1f;
                    break;

                case Trigger.PlagueStrikeHit:
                    trigger = rotation.CurRotationDuration / (rotation.PlagueStrike * (combatTable.DW ? 2f : 1f));
                    chance  = 1f;
                    break;

                case Trigger.DoTTick:
                    trigger = (rotation.BloodPlague + rotation.FrostFever) / 3;
                    chance  = 1f;
                    break;
                }
#if false // Pull out the embedded handling in this situation.
                foreach (SpecialEffect e in effect.Stats.SpecialEffects())
                {
                    statsAverage.Accumulate(this.getSpecialEffects(calcOpts, e));
                }
 #endif
                if (effect.MaxStack > 1)
                {
                    float timeToMax    = (float)Math.Min(calcOpts.FightLength * 60, effect.GetChance(unhastedAttackSpeed) * trigger * effect.MaxStack);
                    float buffDuration = calcOpts.FightLength * 60f;
                    if (effect.Stats.AttackPower == 250f || effect.Stats.AttackPower == 215f || effect.Stats.HasteRating == 57f || effect.Stats.HasteRating == 64f)
                    {
                        buffDuration = 20f;
                    }
                    if (timeToMax * .5f > buffDuration)
                    {
                        timeToMax = 2 * buffDuration;
                    }
                    statsAverage.Accumulate(effect.Stats, effect.GetAverageStackSize((float)trigger, chance, unhastedAttackSpeed, buffDuration));
                }
                else
                {
                    effect.AccumulateAverageStats(statsAverage, (float)trigger, chance, unhastedAttackSpeed, calcOpts.FightLength * 60);
                }
            }

            return(statsAverage);
        }
Пример #2
0
        public StatsSpecialEffects(DKCombatTable t, Rotation rot, BossOptions bo)
        {
            if (rot.ml_Rot == null || rot.ml_Rot.Count == 0)
            {
#if DEBUG
                throw new Exception("Invalid or Incomplete rotation.");
#endif
            }
            combatTable = t;
            m_Rot       = rot;
            m_bo        = bo;

            triggerIntervals = new Dictionary <Trigger, float>();
            // Chance that trigger of correct type is produced (for example for
            /// SpellCrit trigger you would set triggerInterval to average time between hits and set
            /// triggerChance to crit chance)
            triggerChances      = new Dictionary <Trigger, float>();
            unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
            float unhastedAttackSpeedOH     = (combatTable.OH != null ? combatTable.OH.baseSpeed : 2.0f);
            float unhastedAttackSpeedSpells = (combatTable.OH != null ? combatTable.OH.baseSpeed : 2.0f);

            #region Use
            triggerIntervals.Add(Trigger.Use, 0);
            triggerChances.Add(Trigger.Use, 1);
            #endregion
            #region Basic Hit
            float fMeleeHitTriggerInterval      = (1f / ((m_Rot.getMeleeSpecialsPerSecond() * (combatTable.DW ? 2f : 1f)) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f)));
            float fMeleeHitTriggerInterval1ofDW = (1f / ((m_Rot.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f)));
            float fPhysicalHitChance            = 1f - (combatTable.missedSpecial + combatTable.dodgedSpecial) * (1f - combatTable.totalMHMiss);
            triggerIntervals.Add(Trigger.MeleeHit, fMeleeHitTriggerInterval);
            triggerIntervals.Add(Trigger.PhysicalHit, fMeleeHitTriggerInterval);
            triggerIntervals.Add(Trigger.MeleeAttack, fMeleeHitTriggerInterval);
            triggerIntervals.Add(Trigger.PhysicalAttack, fMeleeHitTriggerInterval);
            triggerChances.Add(Trigger.MeleeHit, fPhysicalHitChance);
            triggerChances.Add(Trigger.PhysicalHit, fPhysicalHitChance);
            triggerChances.Add(Trigger.MeleeAttack, fPhysicalHitChance);
            triggerChances.Add(Trigger.PhysicalAttack, fPhysicalHitChance);
            // TODO: interval would be quicker since it should include DOTTick interval.
            triggerIntervals.Add(Trigger.DamageDone, fMeleeHitTriggerInterval);
            triggerChances.Add(Trigger.DamageDone, fPhysicalHitChance);
            triggerIntervals.Add(Trigger.DamageOrHealingDone, fMeleeHitTriggerInterval);
            triggerChances.Add(Trigger.DamageOrHealingDone, fPhysicalHitChance);
            #endregion
            #region Special Hit
            triggerIntervals.Add(Trigger.CurrentHandHit, fMeleeHitTriggerInterval1ofDW);
            triggerChances.Add(Trigger.CurrentHandHit, fPhysicalHitChance);
            triggerIntervals.Add(Trigger.MainHandHit, fMeleeHitTriggerInterval1ofDW);
            triggerChances.Add(Trigger.MainHandHit, fPhysicalHitChance);
            float fMeleeHitTriggerIntervalOH = (combatTable.OH == null ? 0 : fMeleeHitTriggerInterval1ofDW);
            triggerIntervals.Add(Trigger.OffHandHit, fMeleeHitTriggerIntervalOH);
            triggerChances.Add(Trigger.OffHandHit, fPhysicalHitChance);
            #endregion
            #region Basic Crit
            triggerIntervals.Add(Trigger.MeleeCrit, fMeleeHitTriggerInterval);
            triggerChances.Add(Trigger.MeleeCrit, combatTable.physCrits);
            triggerIntervals.Add(Trigger.PhysicalCrit, fMeleeHitTriggerInterval);
            triggerChances.Add(Trigger.PhysicalCrit, combatTable.physCrits);
            #endregion

            #region Spell Hit
            float fSpellHitInterval = 0;
            fSpellHitInterval = 1f / m_Rot.getSpellSpecialsPerSecond();
            float fSpellHitChance  = 0;
            float fSpellCritChance = 0;
            switch (m_Rot.curRotationType)
            {
            case Rotation.Type.Unholy:
            {
                // Unholy
                fSpellHitChance  = m_Rot.GetAbilityOfType(DKability.DeathCoil).HitChance;
                fSpellCritChance = m_Rot.GetAbilityOfType(DKability.DeathCoil).CritChance;
                break;
            }

            case Rotation.Type.Frost:
            {
                if (m_Rot.Contains(DKability.HowlingBlast))
                {
                    fSpellHitChance  = m_Rot.GetAbilityOfType(DKability.HowlingBlast).HitChance;
                    fSpellCritChance = m_Rot.GetAbilityOfType(DKability.HowlingBlast).CritChance;
                }
                else
                {
                    fSpellHitChance  = m_Rot.GetAbilityOfType(DKability.IcyTouch).HitChance;
                    fSpellCritChance = m_Rot.GetAbilityOfType(DKability.IcyTouch).CritChance;
                }
                break;
            }

            case Rotation.Type.Blood:
            {
                fSpellHitChance  = m_Rot.GetAbilityOfType(DKability.IcyTouch).HitChance;
                fSpellCritChance = m_Rot.GetAbilityOfType(DKability.IcyTouch).CritChance;
                break;
            }
            }
            triggerIntervals.Add(Trigger.DamageSpellCast, fSpellHitInterval);
            triggerChances.Add(Trigger.DamageSpellCast, fSpellHitChance);
            triggerIntervals.Add(Trigger.SpellCast, fSpellHitInterval);
            triggerChances.Add(Trigger.SpellCast, fSpellHitChance);
            triggerIntervals.Add(Trigger.DamageSpellHit, fSpellHitInterval);
            triggerChances.Add(Trigger.DamageSpellHit, fSpellHitChance);
            triggerIntervals.Add(Trigger.SpellHit, fSpellHitInterval);
            triggerChances.Add(Trigger.SpellHit, fSpellHitChance);
            #endregion
            #region Spell Crit
            triggerIntervals.Add(Trigger.SpellCrit, fSpellHitInterval);
            triggerChances.Add(Trigger.SpellCrit, fSpellCritChance);
            triggerIntervals.Add(Trigger.DamageSpellCrit, fSpellHitInterval);
            triggerChances.Add(Trigger.DamageSpellCrit, fSpellCritChance);
            #endregion

            #region Specific Strikes
//            triggerIntervals.Add(Trigger.BloodStrikeHit, 0);
//            triggerChances.Add(Trigger.BloodStrikeHit, 0);
            if (m_Rot.HasTrigger(Trigger.BloodStrikeHit))
            {
                triggerIntervals.Add(Trigger.BloodStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.BloodStrikeHit) * (combatTable.DW ? 2f : 1f)));
                triggerChances.Add(Trigger.BloodStrikeHit, m_Rot.GetAbilityOfType(DKability.BloodStrike).HitChance);
            }
//            triggerIntervals.Add(Trigger.HeartStrikeHit, 0);
//            triggerChances.Add(Trigger.HeartStrikeHit, 0);
            if (m_Rot.HasTrigger(Trigger.HeartStrikeHit))
            {
                triggerIntervals.Add(Trigger.HeartStrikeHit, m_Rot.CurRotationDuration / m_Rot.CountTrigger(Trigger.HeartStrikeHit));
                triggerChances.Add(Trigger.HeartStrikeHit, m_Rot.GetAbilityOfType(DKability.HeartStrike).HitChance);
            }
//            triggerIntervals.Add(Trigger.ObliterateHit, 0);
//            triggerChances.Add(Trigger.ObliterateHit, 0);
            if (m_Rot.HasTrigger(Trigger.ObliterateHit))
            {
                triggerIntervals.Add(Trigger.ObliterateHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.ObliterateHit) * (combatTable.DW ? 2f : 1f)));
                triggerChances.Add(Trigger.ObliterateHit, m_Rot.GetAbilityOfType(DKability.Obliterate).HitChance);
            }
//            triggerIntervals.Add(Trigger.ScourgeStrikeHit, 0);
//            triggerChances.Add(Trigger.ScourgeStrikeHit, 0);
            if (m_Rot.HasTrigger(Trigger.ScourgeStrikeHit))
            {
                triggerIntervals.Add(Trigger.ScourgeStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.ScourgeStrikeHit) * (combatTable.DW ? 2f : 1f)));
                triggerChances.Add(Trigger.ScourgeStrikeHit, m_Rot.GetAbilityOfType(DKability.ScourgeStrike).HitChance);
            }
//            triggerIntervals.Add(Trigger.DeathStrikeHit, 0);
//            triggerChances.Add(Trigger.DeathStrikeHit, 0);
            if (m_Rot.HasTrigger(Trigger.DeathStrikeHit))
            {
                triggerIntervals.Add(Trigger.DeathStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.DeathStrikeHit) * (combatTable.DW ? 2f : 1f)));
                triggerChances.Add(Trigger.DeathStrikeHit, m_Rot.GetAbilityOfType(DKability.DeathStrike).HitChance);
            }
//            triggerIntervals.Add(Trigger.PlagueStrikeHit, 0);
//            triggerChances.Add(Trigger.PlagueStrikeHit, 0);
            if (m_Rot.HasTrigger(Trigger.PlagueStrikeHit))
            {
                triggerIntervals.Add(Trigger.PlagueStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.PlagueStrikeHit) * (combatTable.DW ? 2f : 1f)));
                triggerChances.Add(Trigger.PlagueStrikeHit, m_Rot.GetAbilityOfType(DKability.PlagueStrike).HitChance);
            }
//            triggerIntervals.Add(Trigger.IcyTouchHit, 0);
//            triggerChances.Add(Trigger.IcyTouchHit, 0);
            if (m_Rot.HasTrigger(Trigger.IcyTouchHit))
            {
                triggerIntervals.Add(Trigger.IcyTouchHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.IcyTouchHit) * (combatTable.DW ? 2f : 1f)));
                triggerChances.Add(Trigger.IcyTouchHit, m_Rot.GetAbilityOfType(DKability.IcyTouch).HitChance);
            }
//            triggerIntervals.Add(Trigger.RuneStrikeHit, 0);
//            triggerChances.Add(Trigger.RuneStrikeHit, 0);
            if (m_Rot.HasTrigger(Trigger.RuneStrikeHit))
            {
                triggerIntervals.Add(Trigger.RuneStrikeHit, m_Rot.CurRotationDuration / (m_Rot.CountTrigger(Trigger.RuneStrikeHit) * (combatTable.DW ? 2f : 1f)));
                triggerChances.Add(Trigger.RuneStrikeHit, m_Rot.GetAbilityOfType(DKability.RuneStrike).HitChance);
            }
            #endregion

            #region Misc Offensive
            triggerIntervals.Add(Trigger.DeathRuneGained, (m_Rot.m_DeathRunes > 0) ? m_Rot.CurRotationDuration / (m_Rot.m_DeathRunes) : 0);
            triggerChances.Add(Trigger.DeathRuneGained, 1);
            triggerIntervals.Add(Trigger.KillingMachine, (combatTable.m_CState.m_Talents.KillingMachine > 0) ? (60 / (5 * combatTable.m_CState.m_Talents.KillingMachine / 3)) : 0); // KM is a PPM
            triggerChances.Add(Trigger.KillingMachine, 1);
            triggerIntervals.Add(Trigger.DoTTick, 1);                                                                                                                               // TODO: assumes 2 diseases.  but w/ Blood & unholy's chance for a 3rd plus UB could also tick.... should be dynamic.
            triggerChances.Add(Trigger.DoTTick, 1);
            #endregion

            #region Defensive
            triggerChances.Add(Trigger.DamageParried, Math.Min(1f, m_Rot.m_CT.m_CState.m_Stats.EffectiveParry));
            triggerIntervals.Add(Trigger.DamageParried, m_bo.DynamicCompiler_FilteredAttacks(m_bo.GetFilteredAttackList(AVOIDANCE_TYPES.Parry)).AttackSpeed);
            float fAvoidance = (m_Rot.m_CT.m_CState.m_Stats.EffectiveParry
                                + m_Rot.m_CT.m_CState.m_Stats.Dodge
                                + m_Rot.m_CT.m_CState.m_Stats.Miss);
            triggerChances.Add(Trigger.DamageAvoided, Math.Min(1f, fAvoidance));
            triggerIntervals.Add(Trigger.DamageAvoided, m_bo.DynamicCompiler_FilteredAttacks(m_bo.GetFilteredAttackList(
                                                                                                 AVOIDANCE_TYPES.Parry | AVOIDANCE_TYPES.Block | AVOIDANCE_TYPES.Dodge | AVOIDANCE_TYPES.Miss)).AttackSpeed);
            triggerChances.Add(Trigger.DamageTaken, Math.Min(1f, 1f - fAvoidance));
            triggerIntervals.Add(Trigger.DamageTaken, m_bo.DynamicCompiler_Attacks.AttackSpeed);
            triggerChances.Add(Trigger.DamageTakenPhysical, Math.Min(1f, 1f - fAvoidance));
            triggerIntervals.Add(Trigger.DamageTakenPhysical, m_bo.DynamicCompiler_FilteredAttacks(m_bo.GetFilteredAttackList(ItemDamageType.Physical)).AttackSpeed);
            triggerChances.Add(Trigger.DamageTakenPutsMeBelow35PercHealth, 0.35f);
            triggerIntervals.Add(Trigger.DamageTakenPutsMeBelow35PercHealth, m_bo.DynamicCompiler_Attacks.AttackSpeed);
            triggerChances.Add(Trigger.DamageTakenMagical, 1);
            List <Attack> attacks = new List <Attack>();
            foreach (ItemDamageType i in EnumHelper.GetValues(typeof(ItemDamageType)))
            {
                if (i != ItemDamageType.Physical)
                {
                    foreach (Attack a in m_bo.GetFilteredAttackList(i))
                    {
                        attacks.Add(a);
                    }
                }
            }
            triggerIntervals.Add(Trigger.DamageTakenMagical, m_bo.DynamicCompiler_FilteredAttacks(attacks).AttackSpeed);


            #endregion
        }