public Player(Character character)
 {
     this.Character = character;
     this.Stats = new Base.StatsWarrior();
     this.Talents = this.Character.WarriorTalents;
     this.CalcOpts = this.Character.CalculationOptions as CalculationOptionsProtWarr;
     this.BossOpts = this.Character.BossOptions;
     this.DefendModel = null;
     this.AttackModel = null;
 }
Example #2
0
 protected void Initialize(Character character, Base.StatsWarrior stats, CombatFactors cf, BossOptions bo,
     Skills.Ability ability, bool ismh, bool usespellhit, bool userangedhit, bool alwaysHit)
 {
     Char = character;
     StatS = stats;
     bossOpts = bo;
     combatFactors = cf;
     Abil = ability;
     isWhite = (Abil == null);
     isMH = ismh;
     useSpellHit = usespellhit;
     useRangedHit = userangedhit;
     // Start a calc
     Reset(alwaysHit);
 }
Example #3
0
 protected void Initialize(Character character, Base.StatsWarrior stats, CombatFactors cf, BossOptions bo,
                           Skills.Ability ability, bool ismh, bool usespellhit, bool userangedhit, bool alwaysHit)
 {
     Char          = character;
     StatS         = stats;
     bossOpts      = bo;
     combatFactors = cf;
     Abil          = ability;
     isWhite       = (Abil == null);
     isMH          = ismh;
     useSpellHit   = usespellhit;
     useRangedHit  = userangedhit;
     // Start a calc
     Reset(alwaysHit);
 }
Example #4
0
        public CombatFactors(Character character, Base.StatsWarrior stats, CalculationOptionsDPSWarr calcOpts, BossOptions bossOpts)
        {
            Char      = character;
            MH        = Char == null || Char.MainHand == null ? new Knuckles() : Char.MainHand.Item;
            OH        = Char == null || Char.OffHand == null || (Char.WarriorTalents.TitansGrip == 0 && Char.WarriorTalents.SingleMindedFury == 0) ? null : Char.OffHand.Item;
            Talents   = Char == null || Char.WarriorTalents == null ? new WarriorTalents() : Char.WarriorTalents;
            CalcOpts  = (calcOpts == null ? new CalculationOptionsDPSWarr() : calcOpts);
            BossOpts  = (bossOpts == null ? new BossOptions() : bossOpts);
            StatS     = stats;
            CritProcs = new WeightedStat[] { new WeightedStat()
                                             {
                                                 Chance = 1f, Value = 0f
                                             } };
            InvalidateCache();
            // Optimizations

            //SetCvalues();
#if DEBUG
            //ConstructionCounts["CombatFactors"]++;
#endif
        }
        public StatsWarrior GetBuffsStats(Player player)
        {
            Base.StatsWarrior statsBuffs = new Base.StatsWarrior();
            statsBuffs.Accumulate(GetBuffsStats(player.Character.ActiveBuffs, player.Character.SetBonusCount));

            if (player.Character.ActiveBuffs.Find<Buff>(x => x.SpellId == 22738) != null)
            {
                statsBuffs.BonusWarrior_PvP_4P_InterceptCDReduc = 5f;
            }
            if (player.Character.ActiveBuffs.Find<Buff>(x => x.SpellId == 70843) != null)
            {
                statsBuffs.BonusShieldSlamDamageMultiplier = 0.20f;
                statsBuffs.BonusShockwaveDamageMultiplier = 0.20f;
            }
            if (player.Character.ActiveBuffs.Find<Buff>(x => x.SpellId == 70844) != null)
            {
                //Your Battle Shout and Commanding Shout abilities now cause you to absorb damage equal to 20% of your maximum health. Lasts 10 sec.
                //statsBuffs.BonusWarrior_PvP_4P_InterceptCDReduc = 5f;
            }
            if (player.Character.ActiveBuffs.Find<Buff>(x => x.SpellId == 90296) != null)
            {
                statsBuffs.BonusShieldSlamDamageMultiplier = 0.05f;
            }
            if (player.Character.ActiveBuffs.Find<Buff>(x => x.SpellId == 90296) != null)
            {
                statsBuffs.BonusShieldWallDurMultiplier = 0.50f;
            }

            int T12count;
            player.Character.SetBonusCount.TryGetValue("Molten Giant Battleplate", out T12count);
            if (T12count >= 2)
            {
                statsBuffs.BonusShieldSlamDamageMultiplier = 0.20f;
            }
            if (T12count >= 4)
            {
                statsBuffs.AddSpecialEffect(_SE_4T12[player.Talents.ShieldMastery]);
            }

            return statsBuffs;
        }
Example #6
0
 public AttackTable(Character character, Base.StatsWarrior stats, CombatFactors cf, BossOptions bo, Skills.Ability ability, bool ismh, bool useSpellHit, bool useRangedHit, bool alwaysHit)
 {
     Initialize(character, stats, cf, bo, ability, ismh, useSpellHit, useRangedHit, alwaysHit);
 }
        public Base.StatsWarrior GetBuffsStats(DPSWarrCharacter dpswarchar)
        {
            List<Buff> removedBuffs = new List<Buff>();
            List<Buff> addedBuffs = new List<Buff>();

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

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

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

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

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

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

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

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

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

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

            return statsBuffs;
        }
        private float ApplySpecialEffect(SpecialEffect effect, DPSWarrCharacter charStruct, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, ref Base.StatsWarrior applyTo)
        {
#if DEBUG
            //ConstructionCounts["ApplySpecialEffect"]++;
#endif
            float fightDuration = charStruct.BossOpts.BerserkTimer;
            float fightDuration2Pass = charStruct.CalcOpts.SE_UseDur ? fightDuration : 0;

            Stats effectStats = effect.Stats;

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

            if (upTime > 0f) {
                if (effect.Duration == 0f) {
                    applyTo.Accumulate(effectStats, upTime * fightDuration);
                } else if (upTime <= effect.MaxStack) {
                    applyTo.Accumulate(effectStats, upTime);
                }
                return upTime;
            }
            return 0f;
        }
        private Stats IterativeSpecialEffectsStats(DPSWarrCharacter charStruct, List<SpecialEffect> specialEffects, List<SpecialEffect> critEffects,
            Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, float oldFlurryUptime,
            bool iterate, Base.StatsWarrior iterateOld, Base.StatsWarrior originalStats)
        {
#if DEBUG
            //ConstructionCounts["IterativeSpecialEffectsStats"]++;
#endif
            WarriorTalents talents = charStruct.Char.WarriorTalents;
            float fightDuration = charStruct.BossOpts.BerserkTimer;
            Base.StatsWarrior statsProcs = new Base.StatsWarrior();
            try {
                float dmgTakenInterval = fightDuration / charStruct.BossOpts.AoETargsFreq;

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

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

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

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

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

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

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

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

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

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

            float fightDuration = bossOpts.BerserkTimer;

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

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

            // special case for dual wielding w/ berserker enchant on one/both weapons, as they act independently
            //combatFactors.StatS = statsTotal;
            Base.StatsWarrior bersStats = new Base.StatsWarrior();
            foreach (SpecialEffect e in bersMainHand) {
                if (e.Duration == 0) {
                    bersStats.ShadowDamage = e.GetAverageProcsPerSecond(fightDuration / Rot.AttemptedAtksOverDurMH, Rot.LandedAtksOverDurMH / Rot.AttemptedAtksOverDurMH, combatFactors.CMHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0);
                } else {
                    // berserker enchant id
                    float f = e.GetAverageUptime(fightDuration / Rot.AttemptedAtksOverDurMH, Rot.LandedAtksOverDurMH / Rot.AttemptedAtksOverDurMH, combatFactors.CMHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0);
                    bersStats.Accumulate(e.Stats, f);
                }
            }
            foreach (SpecialEffect e in bersOffHand) {
                if (e.Duration == 0) {
                    bersStats.ShadowDamage += e.GetAverageProcsPerSecond(fightDuration / Rot.AttemptedAtksOverDurOH, Rot.LandedAtksOverDurOH / Rot.AttemptedAtksOverDurOH, combatFactors.COHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0);
                } else {
                    float f = e.GetAverageUptime(fightDuration / Rot.AttemptedAtksOverDurOH, Rot.LandedAtksOverDurOH / Rot.AttemptedAtksOverDurOH, combatFactors.COHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0);
                    bersStats.Accumulate(e.Stats, f);
                }
            }
            combatFactors.StatS = UpdateStatsAndAdd(bersStats, combatFactors.StatS, character);
            combatFactors.InvalidateCache();
            return combatFactors.StatS;
        }
        private Base.StatsWarrior GetCharacterStats_Buffed(DPSWarrCharacter dpswarchar, Item additionalItem, bool isBuffed, out Base.StatsWarrior statsRace)
        {
#if DEBUG
            //ConstructionCounts["GetCharacterStats_Buffed"]++;
#endif
            if (dpswarchar.CalcOpts == null) { dpswarchar.CalcOpts = dpswarchar.Char.CalculationOptions as CalculationOptionsDPSWarr; }
            if (dpswarchar.BossOpts == null) { dpswarchar.BossOpts = dpswarchar.Char.BossOptions; }
            if (dpswarchar.CombatFactors == null) { dpswarchar.CombatFactors = new CombatFactors(dpswarchar.Char, new Base.StatsWarrior(), dpswarchar.CalcOpts, dpswarchar.BossOpts); }
            WarriorTalents talents = dpswarchar.Char.WarriorTalents;

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

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

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

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

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

            return statsTotal;
        }