public Stats getSpecialEffects(SpecialEffect effect)
 {
     Stats statsAverage = new Stats();
     if (effect == mainHandEnchant || effect == offHandEnchant)
     {
         if (mainHandEnchant != null && !mhProcessed)
         {
             statsAverage.Accumulate(mainHandEnchant.Stats, GetMHUptime());
             mhProcessed = true; 
         }
         else if (offHandEnchant != null && !ohProcessed)
         {
             statsAverage.Accumulate(offHandEnchant.Stats, GetOHUptime());
             ohProcessed = true; 
         }
     }
     else if (effect.Trigger == Trigger.Use)
     {
         effect.AccumulateAverageStats(statsAverage);
         foreach (SpecialEffect e in effect.Stats.SpecialEffects())
             statsAverage.Accumulate(this.getSpecialEffects(e) * (effect.Duration / effect.Cooldown));
     }
     else
     {
         SetTriggerChanceAndSpeed(effect);
         foreach (SpecialEffect e in effect.Stats.SpecialEffects())  // deal with secondary effects
         {
             statsAverage.Accumulate(this.getSpecialEffects(e));
         }
         if (effect.MaxStack > 1)
         {
             if (effect.Stats.MoteOfAnger > 0)
             {
                 // When in effect stats, MoteOfAnger is % of melee hits
                 // When in character stats, MoteOfAnger is average procs per second
                 statsAverage.Accumulate(new Stats() { MoteOfAnger = effect.Stats.MoteOfAnger * effect.GetAverageProcsPerSecond(trigger, chance, unhastedAttackSpeed, 0f) / effect.MaxStack });
             }
             else
             {
                 float timeToMax = (float)Math.Min(_cs.FightLength, effect.GetChance(unhastedAttackSpeed) * trigger * effect.MaxStack);
                 float buffDuration = _cs.FightLength;
                 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.MaxStack * (((buffDuration) - .5f * timeToMax) / (buffDuration))));
             }
         }
         else
         {
             effect.AccumulateAverageStats(statsAverage, trigger, chance, unhastedAttackSpeed);
         }
     }
     return statsAverage;
 }
Beispiel #2
0
        public void UpdateStatsGraph(Character character, Stats[] statsList, Color[] colors, int scale, string explanatoryText, string calculation)
        {
            CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(character);
            float baseFigure = GetCalculationValue(baseCalc, calculation);

            if (statsList.Length == 0 || statsList.Length > colors.Length)
            {
                return;                                                            // more than 12 elements for the array would run out of colours
            }
            Point[][] points = new Point[statsList.Length][];
            Chart.Series.Clear();
            for (int index = 0; index < statsList.Length; index++)
            {
                Stats newStats = new Stats();
                points[index] = new Point[2 * scale + 1];
                newStats.Accumulate(statsList[index], -scale - 1);

                for (int count = -scale; count <= scale; count++)
                {
                    newStats.Accumulate(statsList[index]);

                    CharacterCalculationsBase currentCalc = Calculations.GetCharacterCalculations(character, new Item()
                    {
                        Stats = newStats
                    }, false, false, false);
                    float currentFigure = GetCalculationValue(currentCalc, calculation);
                    float dpsChange     = currentFigure - baseFigure;
                    points[index][count + scale] = new Point(count, dpsChange);
                }
                Style dataPointStyle = new Style(typeof(LineDataPoint));
                dataPointStyle.Setters.Add(new Setter(DataPoint.TemplateProperty, Resources["InvisibleDataPointTemplate"]));
                dataPointStyle.Setters.Add(new Setter(DataPoint.BackgroundProperty, new SolidColorBrush(colors[index])));
                Chart.Series.Add(new LineSeries()
                {
                    Title                = statsList[index].ToString(),
                    ItemsSource          = points[index],
                    IndependentValuePath = "X",
                    DependentValuePath   = "Y",
                    DataPointStyle       = dataPointStyle,
                });
            }
            Chart.Axes.Clear();
            Chart.Axes.Add(new LinearAxis()
            {
                Orientation   = AxisOrientation.X,
                Title         = "Stat Change",
                ShowGridLines = true,
            });
            Chart.Axes.Add(new LinearAxis()
            {
                Orientation   = AxisOrientation.Y,
                Title         = calculation,
                ShowGridLines = true,
            });
            orgDataDirty = true;
        }
Beispiel #3
0
        public static void RenderStatsGraph(Graphics g, int graphWidth, int graphHeight, Character character, Stats[] statsList, Color[] colors, int scale, string explanatoryText, string calculation, Style style)
        {
            CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(character);
            float baseFigure = GetCalculationValue(baseCalc, calculation);            
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            float graphOffset = graphWidth / 2.0f, graphStep = (graphWidth - 100) / 2.0f / scale;
            if (statsList.Length == 0 || statsList.Length > colors.Length) return; // more than 12 elements for the array would run out of colours
            float minDpsChange = 0f, maxDpsChange = 0f;
            PointF[][] points = new PointF[statsList.Length][];
            for (int index = 0; index < statsList.Length; index++)
            {
                Stats newStats = new Stats();
                points[index] = new PointF[2 * scale + 1];
                newStats.Accumulate(statsList[index], -scale - 1);
                for (int count = -scale; count <= scale; count++)
                {
                    newStats.Accumulate(statsList[index]);

                    CharacterCalculationsBase currentCalc = Calculations.GetCharacterCalculations(character, new Item() { Stats = newStats }, false, false, false);
                    float currentFigure = GetCalculationValue(currentCalc, calculation);
                    float dpsChange = currentFigure - baseFigure;
                    points[index][count + scale] = new PointF(graphOffset + count * graphStep, dpsChange);
                    if (dpsChange < minDpsChange)
                        minDpsChange = dpsChange;
                    if (dpsChange > maxDpsChange)
                        maxDpsChange = dpsChange;
                }
            }
            float DpsVariance = maxDpsChange - minDpsChange;
            if (DpsVariance == 0)
                DpsVariance = 1;
            for (int index = 0; index < statsList.Length; index++)
            {
                for (int count = -scale; count <= scale; count++)
                {
                    points[index][count + scale].Y = (int)((maxDpsChange - points[index][count + scale].Y) * (graphHeight - 48) / DpsVariance) + 20;
                }
                Brush statBrush = new SolidBrush(colors[index]);
                switch (style)
                {
                    case Style.DpsWarr:
                        g.DrawLines(new Pen(statBrush, 3), points[index]);
                        break;
                    case Style.Mage:
                        g.DrawLines(new Pen(statBrush, 1), points[index]);
                        break;
                }
            }

            RenderGrid(g, graphWidth, graphHeight, character, statsList, colors, scale, 1f, "F1", explanatoryText, calculation, style, minDpsChange, maxDpsChange, DpsVariance, true);
        }
Beispiel #4
0
        /// <summary>
        /// Generate a graph to compare stat progressions.
        /// </summary>
        /// <param name="character"></param>
        /// <param name="statsList">Array of Stats objects that contain what stats to evalutate.</param>
        /// <param name="colors">Color for each stat object.  Should be same or > # of elements as statsList</param>
        /// <param name="scale">How many increments of the stats values should be performed?  Larger values mean much larger calculation times.</param>
        /// <param name="explanatoryText"></param>
        /// <param name="calculation">[In] Generally pass in null or "Overall Points" otherwise pass in the specific subpoint string you are concerned about.</param>
        public void UpdateStatsGraph(Character character, Stats[] statsList, Color[] colors, int scale, string explanatoryText, string calculation)
        {
            CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(character);
            float baseFigure = GetCalculationValue(baseCalc, calculation);
            if (statsList.Length == 0 || statsList.Length > colors.Length) return; // more than 12 elements for the array would run out of colours
            Point[][] points = new Point[statsList.Length][];
            Chart.Series.Clear();
            for (int index = 0; index < statsList.Length; index++)
            {
                Stats newStats = new Stats();
                points[index] = new Point[2 * scale + 1];
                newStats.Accumulate(statsList[index], -scale - 1);

                for (int count = -scale; count <= scale; count++)
                {
                    newStats.Accumulate(statsList[index]);

                    CharacterCalculationsBase currentCalc = Calculations.GetCharacterCalculations(character, new Item() { Stats = newStats }, false, false, false);
                    float currentFigure = GetCalculationValue(currentCalc, calculation);
                    float dpsChange = currentFigure - baseFigure;
                    points[index][count + scale] = new Point(count, dpsChange);
                }
                Style dataPointStyle = new Style(typeof(LineDataPoint));
                dataPointStyle.Setters.Add(new Setter(DataPoint.TemplateProperty, Resources["InvisibleDataPointTemplate"]));
                dataPointStyle.Setters.Add(new Setter(DataPoint.BackgroundProperty, new SolidColorBrush(colors[index])));
                Chart.Series.Add(new LineSeries()
                {
                    Title = statsList[index].ToString(),
                    ItemsSource = points[index],
                    IndependentValuePath = "X",
                    DependentValuePath = "Y",
                    DataPointStyle = dataPointStyle,
                });
            }
            Chart.Axes.Clear();
            Chart.Axes.Add(new LinearAxis()
            {
                Orientation = AxisOrientation.X,
                Title = "Stat Change",
                ShowGridLines = true,
            });
            Chart.Axes.Add(new LinearAxis()
            {
                Orientation = AxisOrientation.Y,
                Title = calculation,
                ShowGridLines = true,
            });
            orgDataDirty = true;
        }
        private Stats CalcCritProcs()
        {
            if (Options.NoProcs)
            {
                return(new Stats());
            }

            Dictionary <int, float> periods = new Dictionary <int, float>();
            Dictionary <int, float> chances = new Dictionary <int, float>();

            PopulateTriggers(periods, chances);

            Stats procStats = new Stats();

            foreach (SpecialEffect effect in Stats.SpecialEffects())
            {
                if (!periods.ContainsKey((int)effect.Trigger))
                {
                    continue;
                }

                Stats proc = CalcNormalProc(effect, periods, chances);
                procStats.Accumulate(proc);
                if (effect.Trigger == Trigger.Use && !IsDoublePot(effect))
                {
                    ExtraCritAtMax
                        += StatUtils.CalcSpellCrit(effect.Stats)
                           - StatUtils.CalcSpellCrit(proc);
                }
            }
            return(procStats);
        }
Beispiel #6
0
 public AbilityHandler(Stats s, CombatTable t, Character c, CalculationOptionsDPSDK opts)
 {
     stats = new Stats();
     stats.Accumulate(s);
     combatTable = t;
     character   = c;
     calcOpts    = opts;
     talents     = character.DeathKnightTalents;
     initialize();
 }
 public Stats getSpecialEffects()
 {
     Stats statsAverage = new Stats();
     foreach (SpecialEffect effect in _stats.SpecialEffects())
     {
         statsAverage.Accumulate(getSpecialEffects(effect));
     }
     AddParagon(statsAverage);
     AddHighestStat(statsAverage);
     AddHighestSecondaryStat(statsAverage);
     return statsAverage;
 }
Beispiel #8
0
        public Stats getSpecialEffects()
        {
            Stats statsAverage = new Stats();

            foreach (SpecialEffect effect in _stats.SpecialEffects())
            {
                statsAverage.Accumulate(getSpecialEffects(effect));
            }
            AddParagon(statsAverage);
            AddHighestStat(statsAverage);
            AddDeathBringerProc(statsAverage);
            return(statsAverage);
        }
Beispiel #9
0
        protected override void Calculate()
        {
            // Hack to not count holy shield when we are trying to calculate crit chance without it
            if (!UseHolyShield && CalcOpts.UseHolyShield)
            {
                Stats.Accumulate(new Stats()
                {
                    Block = -0.3f
                });
            }

            float tableSize = 0.0f;

#if (RAWR3)
            int targetLevel = BossOpts.Level;
#else
            int targetLevel = CalcOpts.TargetLevel;
#endif

            // Miss
            Miss       = Math.Min(1.0f - tableSize, Lookup.AvoidanceChance(Character, Stats, HitResult.Miss, targetLevel));
            tableSize += Miss;
            // Dodge
            Dodge      = Math.Min(1.0f - tableSize, Lookup.AvoidanceChance(Character, Stats, HitResult.Dodge, targetLevel));
            tableSize += Dodge;
            // Parry
            Parry      = Math.Min(1.0f - tableSize, Lookup.AvoidanceChance(Character, Stats, HitResult.Parry, targetLevel));
            tableSize += Parry;
            // Block
            if (Character.OffHand != null && Character.OffHand.Type == ItemType.Shield)
            {
                Block      = Math.Min(1.0f - tableSize, Lookup.AvoidanceChance(Character, Stats, HitResult.Block, targetLevel));
                tableSize += Block;
            }
            // Critical Hit
            Critical   = Math.Min(1.0f - tableSize, Lookup.TargetCritChance(Character, Stats, targetLevel));
            tableSize += Critical;
            // Normal Hit
            Hit = Math.Max(0.0f, 1.0f - tableSize);
            // Partial Resists don't belong in the combat table
            Resist = 1.0f - StatConversion.GetResistanceTable(targetLevel, Character.Level, Stats.FrostResistance, 0.0f)[0];

            // Hack to put back holy shield when we are trying to calculate crit chance without it
            if (!UseHolyShield && CalcOpts.UseHolyShield)
            {
                Stats.Accumulate(new Stats()
                {
                    Block = 0.3f
                });
            }
        }
Beispiel #10
0
 public virtual void AccumulateBuffsStats(Stats stats, IEnumerable <string> buffs)
 {
     foreach (string buffName in buffs)
     {
         if (!string.IsNullOrEmpty(buffName))
         {
             Buff buff = Buff.GetBuffByName(buffName);
             if (buff != null)
             {
                 stats.Accumulate(buff.Stats);
             }
         }
     }
 }
Beispiel #11
0
        /// <summary>
        /// Beware when updating: The spells from an earlier returned Rotation are references to the SpellBox from this Estimation.
        /// </summary>
        /// <param name="baseStats"></param>
        /// <param name="procStats"></param>
        /// <param name="talents"></param>
        /// <param name="calcOpts"></param>
        public void Update(Stats baseStats, Stats procStats, PriestTalents talents, CalculationOptionsShadowPriest calcOpts)
        {
            this.baseStats = baseStats;
            this.procStats = procStats;
            this.talents   = talents;
            this.calcOpts  = calcOpts;

            Stats addedStats = baseStats.Clone();

            addedStats.Accumulate(procStats);
            CombatFactors combatFactors = new CombatFactors(talents, addedStats, Math.Max(calcOpts.NumberOfTargets - 1, 0), calcOpts.LatencyCast, calcOpts.LatencyGcd);

            spellbox.Update(combatFactors);
        }
Beispiel #12
0
        /// <summary>
        /// Beware when updating: The spells from an earlier returned Rotation are references to the SpellBox from this Estimation.
        /// </summary>
        /// <param name="baseStats"></param>
        /// <param name="procStats"></param>
        /// <param name="talents"></param>
        /// <param name="calcOpts"></param>
        public void Update(Stats baseStats, Stats procStats, ShamanTalents talents, CalculationOptionsElemental calcOpts)
        {
            this.baseStats = baseStats;
            this.procStats = procStats;
            this.talents   = talents;
            this.calcOpts  = calcOpts;

            Stats addedStats = baseStats.Clone();

            addedStats.Accumulate(procStats);
            CombatFactors combatFactors = new CombatFactors(talents, addedStats, Math.Max(calcOpts.NumberOfTargets - 1, 0), calcOpts.LatencyCast, calcOpts.LatencyGcd, calcOpts.UseFireNova, calcOpts.UseChainLightning, calcOpts.UseDpsTotem);

            spellbox.Update(combatFactors);
        }
Beispiel #13
0
        public void CalcStats1()
        {
            WarlockTalents talents = Mommy.Talents;

            //Stats.Strength = 453f;
            //Stats.Agility = 883f;
            Stats.SpellPower   = CalcSpellPower();
            Stats.AttackPower  = CalcAttackPower();
            Stats.PhysicalCrit = .0328f;
            Stats.Accumulate(Mommy.PetBuffs);

            FinalizeModifiers();
            SpecialModifiers.Accumulate(TotalModifiers);
            MeleeModifiers.Accumulate(TotalModifiers);

            Stats.SpellHaste    = GetHasteWithProcs(s => s.SpellHaste);
            Stats.PhysicalHaste = GetHasteWithProcs(s => s.PhysicalHaste);
        }
Beispiel #14
0
        public void CalcStats1()
        {
            // Stam & Int have to happen in this stage, so that Demonic
            // Knowledge is in effect when calculating the benefit of a
            // Demonic Pact proc.

            // Crit has to happen in this stage, so that Empowered Imp gives
            // the warlocks the right amount of crit.

            WarlockTalents talents  = Mommy.Talents;
            float          vitality = talents.FelVitality * .05f;
            float          tacticsCrit
                = .02f * talents.DemonicTactics
                  + .1f
                  * talents.ImprovedDemonicTactics
                  * (Mommy.CalcSpellCrit()
                     - Mommy.Stats.SpellCritOnTarget);

            Stats = new Stats()
            {
                Stamina = BaseStamina + StaminaCoef * Mommy.CalcStamina(),
                Intellect
                         = BaseIntellect + IntellectCoef * Mommy.CalcIntellect(),
                Strength = 297f,
                Agility  = 90f,
                BonusStaminaMultiplier   = vitality,
                BonusIntellectMultiplier = vitality,
                SpellCrit
                             = BaseSpellCrit + tacticsCrit + Mommy.Stats.Warlock2T9,
                SpellPower   = BaseSpellPower,
                AttackPower  = BaseAttackPower,
                PhysicalCrit = .0329f + tacticsCrit + Mommy.Stats.Warlock2T9,
            };
            Stats.Accumulate(Mommy.PetBuffs);

            Mommy.Add4pT10(TotalModifiers);
            FinalizeModifiers();
            SpecialModifiers.Accumulate(TotalModifiers);
            MeleeModifiers.Accumulate(TotalModifiers);

            Stats.SpellHaste    = GetHasteWithProcs(s => s.SpellHaste);
            Stats.PhysicalHaste = GetHasteWithProcs(s => s.PhysicalHaste);
        }
Beispiel #15
0
        protected static void AccumulateSpecialEffects(Character character, ref Stats stats, float FightDuration, Dictionary <Trigger, float> triggerIntervals, Dictionary <Trigger, float> triggerChances, List <SpecialEffect> effects, float weight)
        {
            foreach (SpecialEffect effect in effects)
            {
                Stats effectStats = effect.Stats;

                float upTime = 0f;

                if (effect.Trigger == Trigger.Use)
                {
                    if (effect.Stats._rawSpecialEffectDataSize >= 1)
                    {
                        upTime = effect.GetAverageUptime(0f, 1f, 0, FightDuration);
                        List <SpecialEffect> nestedEffect = new List <SpecialEffect>(effect.Stats.SpecialEffects());
                        Stats _stats2 = effectStats.Clone();
                        AccumulateSpecialEffects(character, ref _stats2, effect.Duration, triggerIntervals, triggerChances, nestedEffect, upTime);
                        effectStats = _stats2;
                    }
                    else
                    {
                        upTime = effect.GetAverageStackSize(0f, 1f, 0, FightDuration);
                    }
                }
                else if (effect.Duration == 0f)
                {
                    upTime = effect.GetAverageProcsPerSecond(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger],
                                                             0, FightDuration);
                }
                else if (triggerIntervals.ContainsKey(effect.Trigger))
                {
                    upTime = effect.GetAverageStackSize(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger],
                                                        0, FightDuration);
                }

                if (upTime > 0f)
                {
                    stats.Accumulate(effectStats, upTime * weight);
                }
            }
        }
Beispiel #16
0
        public override Stats GetCharacterStats(Character character, Item additionalItem)
        {
            WarlockTalents            talents  = character.WarlockTalents;
            CalculationOptionsWarlock calcOpts = character.CalculationOptions as CalculationOptionsWarlock;
            BossOptions bossOpts = character.BossOptions;
            Stats       stats    = BaseStats.GetBaseStats(character);

            AccumulateItemStats(stats, character, additionalItem);
            AccumulateBuffsStats(stats, character.ActiveBuffs);
            AccumulateSetBonusStats(stats, character.SetBonusCount);
            ApplyPetsRaidBuff(stats, calcOpts.Pet, talents, character.ActiveBuffs, calcOpts);

            float[] demonicEmbraceValues = { 0f, .04f, .07f, .1f };
            Stats   statsTalents         = new Stats {
                BonusStaminaMultiplier = demonicEmbraceValues[talents.DemonicEmbrace] //Demonic Embrace
            };

            if (talents.Eradication > 0)
            {
                float[] eradicationValues = { 0f, .06f, .12f, .20f };
                statsTalents.AddSpecialEffect(
                    new SpecialEffect(
                        Trigger.CorruptionTick,
                        new Stats()
                {
                    SpellHaste = eradicationValues[talents.Eradication]
                },
                        10f,    //duration
                        0f,     //cooldown
                        .06f)); //chance
            }

            stats.Accumulate(statsTalents);
            stats.ManaRestoreFromMaxManaPerSecond
                = Math.Max(
                      stats.ManaRestoreFromMaxManaPerSecond,
                      .001f * Spell.CalcUprate(talents.SoulLeech > 0 ? 1f : 0f, 15f, bossOpts.BerserkTimer * 1.1f));
            return(stats);
        }
Beispiel #17
0
        private Stats CalcNormalProc(SpecialEffect effect, Dictionary <int, float> periods, Dictionary <int, float> chances)
        {
            Stats effectStats = effect.Stats;
            Stats proc        = effect.GetAverageStats(periods[(int)effect.Trigger], chances[(int)effect.Trigger], CalculationsWarlock.AVG_UNHASTED_CAST_TIME, BossOpts.BerserkTimer);

            // Handle "recursive effects" - i.e. those that *enable* a
            // proc during a short window.
            if (effect.Stats._rawSpecialEffectDataSize == 1 && periods.ContainsKey((int)effect.Stats._rawSpecialEffectData[0].Trigger))
            {
                SpecialEffect inner      = effect.Stats._rawSpecialEffectData[0];
                Stats         innerStats = inner.GetAverageStats(periods[(int)inner.Trigger], chances[(int)inner.Trigger], 1f, effect.Duration);
                float         upTime     = effect.GetAverageUptime(periods[(int)effect.Trigger], chances[(int)effect.Trigger], 1f, BossOpts.BerserkTimer);
                proc.Accumulate(innerStats, upTime);
            }

            // E.g. Sorrowsong
            if (effect.LimitedToExecutePhase)
            {
                proc *= CalcOpts.ThirtyFive;
            }

            return(proc);
        }
Beispiel #18
0
        public static Stats GetBaseStats(int level, CharacterClass characterClass, CharacterRace characterRace, DruidForm characterForm)
        {   // Health, Mana and some other things are same for every race.
            lock (syncLock)
            {
                #region Cache
                if (level == _lastLevel &&
                    characterClass == _lastClass &&
                    characterRace == _lastRace &&
                    characterForm == _lastForm)
                {
                    return(_lastStats.Clone());
                }
                _lastLevel = level;
                _lastClass = characterClass;
                _lastRace  = characterRace;
                _lastForm  = characterForm;
                #endregion

                Stats S = new Stats();
                #region Race, not class benefit
                // Most Level 85 Race and Class Stats come from:
                // http://code.google.com/p/simulationcraft/source/browse/branches/cataclysm/engine/sc_rating.cpp?r=6207
                // When they were still at 80 as of Jan 01st, 2011

                // From SimCraft
                Stats race = new Stats();
                switch (characterRace)
                {
                // Alliance
                case CharacterRace.Human:    race.Strength = 20; race.Agility = 20; race.Stamina = 20; race.Intellect = 20; race.Spirit = 20; break;

                case CharacterRace.Dwarf:    race.Strength = 25; race.Agility = 16; race.Stamina = 21; race.Intellect = 19; race.Spirit = 19; break;

                case CharacterRace.NightElf: race.Strength = 16; race.Agility = 24; race.Stamina = 20; race.Intellect = 20; race.Spirit = 20; break;

                case CharacterRace.Gnome:    race.Strength = 15; race.Agility = 22; race.Stamina = 20; race.Intellect = 24; race.Spirit = 20; break;

                case CharacterRace.Draenei:  race.Strength = 21; race.Agility = 17; race.Stamina = 20; race.Intellect = 20; race.Spirit = 22; break;

                case CharacterRace.Worgen:   race.Strength = 23; race.Agility = 22; race.Stamina = 20; race.Intellect = 16; race.Spirit = 19; break;

                // Horde
                case CharacterRace.Orc:      race.Strength = 23; race.Agility = 17; race.Stamina = 21; race.Intellect = 17; race.Spirit = 22; break;

                case CharacterRace.Undead:   race.Strength = 19; race.Agility = 18; race.Stamina = 20; race.Intellect = 18; race.Spirit = 25; break;

                case CharacterRace.Tauren:   race.Strength = 25; race.Agility = 16; race.Stamina = 21; race.Intellect = 16; race.Spirit = 22; break;

                case CharacterRace.Troll:    race.Strength = 21; race.Agility = 22; race.Stamina = 20; race.Intellect = 16; race.Spirit = 21; break;

                case CharacterRace.BloodElf: race.Strength = 17; race.Agility = 22; race.Stamina = 20; race.Intellect = 23; race.Spirit = 18; break;

                case CharacterRace.Goblin:   race.Strength = 17; race.Agility = 22; race.Stamina = 20; race.Intellect = 23; race.Spirit = 20; break;

                default: { break; }
                }
                ;
                // From Chardev (85)
                //Class           Str Agi Sta Int Spi
                //Druid            76  69  86 136 153
                //Shaman          111  60 128 119 136
                //Death Knight    171 101 154  16  44
                //Hunter           60 178 119  77  88
                //Mage             17  26  43 187 175
                //Paladin         144  77 136  86  97
                //Priest           26  34  51 188 183
                //Rogue           102 186  94  26  53
                //Warlock          43  51  76 161 166
                //Warrior         169 103 153  17  44
                #endregion

                #region Base Stats
                #region All Classes
                S.Miss  = 0.05f;
                S.Block = 0.00f;
                S.Parry = 0.00f;
                #endregion
                switch (characterClass)
                {
                    #region Death Knight
                case CharacterClass.DeathKnight:
                    Stats dk = new Stats()
                    {
                        Strength     = 171, Agility = 101, Stamina = 274, Intellect = 16, Spirit = 44,
                        Health       = 43025f,
                        Dodge        = 0.05f, Parry = 0.05f, Block = 0.00f,
                        PhysicalCrit = 0.0049f, AttackPower = 595f,
                    };
                    S.Accumulate(race);
                    S.Accumulate(dk);
                    break;

                    #endregion
                    #region Druid
                case CharacterClass.Druid:
                    Stats druid = new Stats()
                    {
                        Strength     = 76, Agility = 69, Stamina = 86, Intellect = 136, Spirit = 153,
                        Health       = 39533f, Mana = 18635f,
                        Dodge        = 0.03758f, Parry = 0.05f, Block = 0.05f,
                        PhysicalCrit = 0.03192f, AttackPower = 613f,
                        SpellCrit    = 0.0185f, Mp5 = 931f,
                    };
                    S.Accumulate(race);
                    S.Accumulate(druid);
                    switch (characterForm)
                    {
                    case DruidForm.Moonkin:
                    case DruidForm.Caster:
                        S.AttackPower  = -10;
                        S.PhysicalCrit = 0.0743f;
                        S.Dodge        = 0.0556970f;  //??
                        break;

                    case DruidForm.Bear:
                        S.AttackPower            = 255;
                        S.PhysicalCrit           = 0.074755f;
                        S.Dodge                  = 0.0556970f;
                        S.BonusStaminaMultiplier = 0.2f;
                        break;

                    case DruidForm.Cat:
                        S.AttackPower  = 235;
                        S.PhysicalCrit = 0.074755f;
                        S.Dodge        = 0.0556970f;
                        break;

                    default:
                        break;
                    }
                    break;

                    #endregion
                    #region Hunter
                case CharacterClass.Hunter:
                    Stats hun = new Stats()
                    {
                        // Stats updated 8/19/2011 4.2 w/ Troll Hunter: Tsevon @ US-Dragonmaw w/ no spec.
                        Strength  = 60,
                        Agility   = 178,
                        Stamina   = 119,
                        Intellect = 77,
                        Spirit    = 88,
                        Health    = 39037,
                        Dodge     = 0.03758f, Parry = 0.05f,
                        // This assumes ALL AP from full AP += AGI * 2
                        // So naked Hunter has 31 AP un accounted for.
                        // Naked troll, no gear, no spec, LW & Skinning.
                        PhysicalCrit = 0, AttackPower = 31f, RangedAttackPower = 31f,
                    };
                    S.Accumulate(race);
                    S.Accumulate(hun);
                    break;

                    #endregion
                    #region Mage
                case CharacterClass.Mage:
                    Stats mag = new Stats()
                    {
                        Strength = 17, Agility = 26, Stamina = 43, Intellect = 187, Spirit = 175,
                        Health   = 36853f, Mana = 17138f,
                        Dodge    = 0.03758f, Parry = 0.05f,
                    };
                    S.Accumulate(race);
                    S.Accumulate(mag);
                    break;

                    #endregion
                    #region Paladin
                case CharacterClass.Paladin:
                    Stats pal = new Stats()
                    {
                        Strength     = 144, Agility = 77, Stamina = 136, Intellect = 86, Spirit = 97,
                        Health       = 43285f, Mana = 23422,
                        Dodge        = 0.05f, Parry = 0.05f, Block = 0.05f,
                        PhysicalCrit = 0.00652f, AttackPower = 235f,
                        SpellCrit    = 0.033355f,
                    };
                    S.Accumulate(race);
                    S.Accumulate(pal);
                    break;

                    #endregion
                    #region Priest
                case CharacterClass.Priest:
                    // added adjustments to the base race here because the math using the previous stats
                    // just don't work for the in game calculations on priest tests.
                    // also unsure how these changes would effect other modules if moved up.
                    // adding or subracting from the priest stats don't work and throws all other class
                    // calculations off.
                    switch (characterRace)
                    {
                    case CharacterRace.Human: race.Spirit = 19; break;

                    case CharacterRace.Gnome: race.Intellect = 23; break;

                    case CharacterRace.Goblin: race.Spirit = 18; break;
                    }
                    Stats pri = new Stats()
                    {
                        Strength     = 26, Agility = 34, Stamina = 51, Intellect = 169, Spirit = 178,
                        Health       = 43285f, Mana = 20590f,
                        Dodge        = 0.0337780f,
                        Parry        = 0.05f,
                        PhysicalCrit = 0.027f, SpellCrit = 0.012375f,
                    };
                    pri.Mp5 = pri.Mana * 0.05f;         // Always 5% of base mana in regen.
                    S.Accumulate(race);
                    S.Accumulate(pri);
                    break;

                    #endregion
                    #region Rogue
                case CharacterClass.Rogue:
                    Stats rog = new Stats()
                    {
                        Strength     = 102, Agility = 186, Stamina = 94, Intellect = 26, Spirit = 53,
                        Health       = 40529f,
                        Dodge        = 0.03758f, Parry = 0.05f,
                        PhysicalCrit = 0.03192f, AttackPower = 613f,
                    };
                    S.Accumulate(race);
                    S.Accumulate(rog);
                    break;

                    #endregion
                    #region Shaman
                case CharacterClass.Shaman:
                    Stats sha = new Stats()
                    {
                        Strength     = 111, Agility = 60, Stamina = 128, Intellect = 119, Spirit = 136,
                        Health       = 37037f, Mana = 23430f,
                        Dodge        = 0.0193f, Parry = 0.05f, Block = 0.05f,
                        PhysicalCrit = 0.02910375f, AttackPower = 140f,
                        SpellCrit    = 0.022057f, SpellPower = -10,
                    };
                    S.Accumulate(race);
                    S.Accumulate(sha);
                    break;

                    #endregion
                    #region Warlock
                case CharacterClass.Warlock:
                    Stats warlock = new Stats()
                    {
                        Strength     = 43, Agility = 51, Stamina = 76, Intellect = 153, Spirit = 161,
                        Health       = 38184f, Mana = 20553f,
                        Dodge        = 0.0238110f, Parry = 0.05f, Block = 0.05f,
                        PhysicalCrit = 0.026219999417663f, SpellCrit = 0.017000000923872f,
                    };
                    S.Accumulate(race);
                    S.Accumulate(warlock);
                    break;

                    #endregion
                    #region Warrior
                case CharacterClass.Warrior:
                    Stats war = new Stats()
                    {
                        Strength     = 169, Agility = 103, Stamina = 153, Intellect = 17, Spirit = 44,
                        Health       = 43285f,
                        Dodge        = 0.05f, Parry = 0.05f, Block = 0.05f,
                        PhysicalCrit = 0.03192f, AttackPower = 613f,
                    };
                    S.Accumulate(race);
                    S.Accumulate(war);
                    break;

                    #endregion
                    #region No Class
                default:
                    break;
                    #endregion
                }
                #endregion

                #region Racials
                // Resistance do not stack with other buffs. Until then I'll commenting them out
                if (characterRace == CharacterRace.Gnome)  //CATA: changed from 5% int to 5% mana
                {
                    // S.ArcaneResistance += 85f;
                    S.BonusManaMultiplier = 0.05f;
                    //S.BonusIntellectMultiplier = 0.05f;
                }
                else if (characterRace == CharacterRace.Human)
                {
                    S.BonusSpiritMultiplier = 0.03f;
                    // Patch 4.0.6+ changed from a 3 minute cooldown to 2 minute cooldown
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                    {
                        PVPTrinket = 1
                    }, 0f, 120f));
                }
                else if (characterRace == CharacterRace.NightElf)
                {
                    // S.NatureResistance += 85f;
                    S.Miss += 0.02f;
                }
                else if (characterRace == CharacterRace.Dwarf)
                {
                    // S.FrostResistance += 85f;
                    // Armor +10% for 8 Sec.
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                    {
                        BaseArmorMultiplier = .1f
                    }, 8, 120));
                    // TODO: Add debuff removal.  Doesn't work on all bosses so not sure if we want to.
                }
                else if (characterRace == CharacterRace.Draenei)
                {
                    // S.ArcaneResistance += 85f;
                    S.SpellHit    += 0.01f;
                    S.PhysicalHit += 0.01f;
                    // Patch 4.0.6+ changed from a scaling Health restore to a flat 20% of max health
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                    {
                        HealthRestoreFromMaxHealth = 0.2f / 15f
                    }, 15f, 180f));
                }
                else if (characterRace == CharacterRace.Worgen)
                {
                    // S.NatureResistance = 64f;
                    // S.ShadowResistance = 64f;
                    // Patch 4.0.6+ Darkflight changed from a 3 minute CD to a 2 minute CD
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                    {
                        MovementSpeed = 0.40f
                    }, 10f, 120f));
                    S.PhysicalCrit += 0.01f;
                    S.SpellCrit    += 0.01f;
                }
                else if (characterRace == CharacterRace.Tauren)
                {
                    // S.NatureResistance = 85f;
                    S.Health = (float)Math.Floor(S.Health * 1.05f);
                }
                else if (characterRace == CharacterRace.Troll)
                {
                    S.SnareRootDurReduc = .15f;
                    if (characterClass == CharacterClass.DeathKnight || characterClass == CharacterClass.Warrior || characterClass == CharacterClass.Rogue)
                    {
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                        {
                            PhysicalHaste = 0.2f
                        }, 10f, 180f));
                    }
                    else
                    {
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                        {
                            SpellHaste = 0.2f, PhysicalHaste = 0.2f
                        }, 10f, 180f));
                    }
                }
                else if (characterRace == CharacterRace.Undead)
                {
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                    {
                        FearDurReduc = 1f
                    }, .1f, 120f));
                }
                else if (characterRace == CharacterRace.Orc)
                {
                    S.StunDurReduc = 0.15f;
                    if (characterClass == CharacterClass.Shaman)
                    {
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                        {
                            AttackPower = 65 + (level * 13), SpellPower = 75 + (level * 6)
                        }, 15f, 120f));
                    }
                    else if (characterClass == CharacterClass.Warlock || characterClass == CharacterClass.Mage)
                    {
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                        {
                            SpellPower = 75 + (level * 6)
                        }, 15f, 120f));
                    }
                    else
                    {
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                        {
                            AttackPower = 65 + (level * 13)
                        }, 15f, 120f));
                    }
                }
                else if (characterRace == CharacterRace.BloodElf)
                {
                    // S.ArcaneResistance += 85f;
                    if (characterClass == CharacterClass.DeathKnight || characterClass == CharacterClass.Rogue || characterClass == CharacterClass.Hunter)
                    {
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                        {
                            ManaorEquivRestore = .15f
                        }, 0f, 120f));
                    }
                    else if (characterClass == CharacterClass.Warrior)
                    {
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                        {
                            BonusRageGen = 15f
                        }, 0f, 120f));
                    }
                    else
                    {
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                        {
                            ManaorEquivRestore = .06f
                        }, 0f, 120f));
                    }
                }
                else if (characterRace == CharacterRace.Goblin)
                {
                    S.PhysicalHaste += 0.01f;
                    S.SpellHaste    += 0.01f;
                    // TODO: The damage of the rocket belt proc is dependent on the character's current AP and SP
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats()
                    {
                        FireDamage = 1f + (level * 2)
                    }, 0f, 120f));
                }
                else if (characterRace == CharacterRace.PandarenAlliance || characterRace == CharacterRace.PandarenHorde)
                {
                }
                #endregion

                _lastStats = S.Clone();
                return(S);
            }
        }
Beispiel #19
0
        /// <summary>
        /// Computes average scaled stats given the frequency of triggers; also computers average effect
        /// </summary>
        /// <param name="stats">Stats object into which the average stats will be accumulated.</param>
        /// <param name="triggerInterval">Average time interval between triggers in seconds.</param>
        /// <param name="triggerChance">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)</param>
        /// <param name="attackSpeed">Average unhasted attack speed, used in PPM calculations.</param>
        /// <param name="fightDuration">Duration of fight in seconds.</param>
        /// <param name="scale">Scale factor.</param>
        public float AccumulateAverageStats(Stats stats, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, float attackSpeed = 3.0f, float fightDuration = 0.0f, float scale = 1.0f)
        {
            float factor = scale;
            if (triggerIntervals.ContainsKey(Trigger) && triggerChances.ContainsKey(Trigger))
                factor *= GetAverageFactor(triggerIntervals[Trigger], triggerChances[Trigger], attackSpeed, fightDuration);
            else
                factor *= 0;

            Stats.GenerateSparseData();
            if (Stats.ContainsSpecialEffect())
            {
                if (Stats.sparseIndices.Length != 0)
                {
                    Stats effectStats = Stats.Clone();
                    effectStats.ClearSpecialEffects();
                    effectStats.InvalidateSparseData();
                    foreach (SpecialEffect effect in Stats.SpecialEffects())
                    {
                        // this assumes that the child effect gets consumed at the end of the duration
                        // this assumes that the child effect is off cooldown before the next occurrence of the parent effect
                        effect.AccumulateAverageStats(effectStats, triggerIntervals, triggerChances, attackSpeed, Duration);
                    }
                    stats.Accumulate(effectStats, factor);
                }
                else
                {
                    // this path is an optimization to avoid intermediate dense storage
                    foreach (SpecialEffect effect in Stats.SpecialEffects())
                    {
                        // this assumes that the child effect gets consumed at the end of the duration
                        // this assumes that the child effect is off cooldown before the next occurrence of the parent effect
                        effect.AccumulateAverageStats(stats, triggerIntervals, triggerChances, attackSpeed, Duration, factor);
                    }
                }
            }
            else
                stats.Accumulate(Stats, factor);
            return factor;
        }
        public Stats GetCharacterStats(Character character, Item additionalItem, bool computeAverageStats, CharacterCalculationsHealadin calc)
        {
            CalculationOptionsHealadin calcOpts = character.CalculationOptions as CalculationOptionsHealadin;
            BossOptions bossOpts = character.BossOptions;
            PaladinTalents talents = character.PaladinTalents;
            
            float fightLength = bossOpts.BerserkTimer;

            Stats statsRace = BaseStats.GetBaseStats(character.Level, CharacterClass.Paladin, character.Race);
            Stats statsBaseGear = GetItemStats(character, additionalItem);
            Stats statsBuffs = GetBuffsStats(character, calcOpts);
            Stats stats = statsBaseGear + statsBuffs + statsRace;

            ConvertRatings(stats, talents, calcOpts);
            if (computeAverageStats)
            {
                Stats statsAverage = new Stats();

                foreach (SpecialEffect effect in stats.SpecialEffects())
                {
                    float trigger = 0f;
                    if (calc == null)
                    {
                        trigger = 1.5f / calcOpts.Activity / (1f + stats.SpellHaste);
                        if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                            trigger *= stats.SpellCrit;
                    }
                    else
                    {
                        if (effect.Trigger == Trigger.HealingSpellCast || effect.Trigger == Trigger.HealingSpellHit)
                            trigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                        else if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                            trigger = 1f / Rotation.GetHealingCritsPerSec(calc);
                        else if (effect.Trigger == Trigger.SpellCast || effect.Trigger == Trigger.SpellHit)
                            trigger = 1f / Rotation.GetSpellCastsPerSec(calc);
                        else if (effect.Trigger == Trigger.DamageOrHealingDone)
                            trigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                        else if (effect.Trigger == Trigger.Use)
                        {
                            trigger = 0f;
                            foreach (SpecialEffect childEffect in effect.Stats.SpecialEffects())
                            {
                                float childTrigger = 0f;

                                if (effect.Trigger == Trigger.HealingSpellCast || effect.Trigger == Trigger.HealingSpellHit)
                                    childTrigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                                else if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                                    childTrigger = 1f / Rotation.GetHealingCritsPerSec(calc);
                                else if (effect.Trigger == Trigger.SpellCast || effect.Trigger == Trigger.SpellHit)
                                    childTrigger = 1f / Rotation.GetSpellCastsPerSec(calc);

                                statsAverage.Accumulate(childEffect.Stats,
                                                        effect.GetAverageUptime(0.0f, 1.0f)
                                                            * childEffect.GetAverageStackSize(childTrigger, 1f, 1.5f, effect.Duration));
                            }
                        }
                        else continue;
                    }
                    statsAverage.Accumulate(effect.GetAverageStats(trigger, 1f, 1.5f, fightLength));
                }
                statsAverage.ManaRestore *= fightLength;
                statsAverage.Healed *= fightLength;
                statsAverage.HealedPerSP *= fightLength;

                stats = statsBaseGear + statsBuffs + statsRace + statsAverage;
                ConvertRatings(stats, talents, calcOpts);
            }

            #region Set Bonuses
            int T11Count;
            character.SetBonusCount.TryGetValue("Reinforced Sapphirium Regalia", out T11Count);
            stats.BonusCritChanceDeathCoil = 0; // using this for Holy Light crit bonus, for now
            stats.BonusCritChanceFrostStrike = 0;  // yes, I'm pure evil, using this to track 4T11 
            if (T11Count >= 2)
            {
               
                // T11 Pally 2 piece bonus: add 5% crit to HL
                stats.BonusCritChanceDeathCoil = 0.05f;
            }
            if (T11Count >= 4)
            {
                // T11 Pally 4 piece bonus: 540 spirit buff for 6 secs after HS cast
                stats.BonusCritChanceFrostStrike = 1; 
            }
            #endregion Set Bonuses

            return stats;
        }
Beispiel #21
0
        public static void RenderScalingGraph(Graphics g, int graphWidth, int graphHeight, Character character, Stats[] statsList, Stats baseStat, bool requiresReferenceCalculations, Color[] colors, int scale, string explanatoryText, string calculation, Style style)
        {
            CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(character);

            g.SmoothingMode     = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            float graphOffset = graphWidth / 2.0f, graphStep = (graphWidth - 100) / 2.0f / scale;

            if (statsList.Length == 0 || statsList.Length > colors.Length)
            {
                return;                                                            // more than 12 elements for the array would run out of colours
            }
            float minDpsChange = 0f, maxDpsChange = 0f;

            PointF[][] points = new PointF[statsList.Length][];
            // extract property data for relative stats calculations
            KeyValuePair <PropertyInfo, float>[] properties = new KeyValuePair <PropertyInfo, float> [statsList.Length];
            for (int index = 0; index < statsList.Length; index++)
            {
                var p = statsList[index].Values(x => x > 0);
                foreach (var kvp in p)
                {
                    properties[index] = kvp;
                }
                points[index] = new PointF[2 * scale + 1];
            }
            for (int count = -scale; count <= scale; count++)
            {
                Stats newStats = new Stats();
                newStats.Accumulate(baseStat, count);
                Item item = new Item()
                {
                    Stats = newStats
                };
                if (requiresReferenceCalculations)
                {
                    Calculations.GetCharacterCalculations(character, item, true, false, false);
                }
                for (int index = 0; index < statsList.Length; index++)
                {
                    ComparisonCalculationBase currentCalc = CalculationsBase.GetRelativeStatValue(character, properties[index].Key, item, properties[index].Value);
                    float dpsChange = GetCalculationValue(currentCalc, calculation);
                    points[index][count + scale] = new PointF(graphOffset + count * graphStep, dpsChange);
                    if (dpsChange < minDpsChange)
                    {
                        minDpsChange = dpsChange;
                    }
                    if (dpsChange > maxDpsChange)
                    {
                        maxDpsChange = dpsChange;
                    }
                }
            }
            // restore reference calculation
            if (requiresReferenceCalculations)
            {
                Stats newStats = new Stats();
                Item  item     = new Item()
                {
                    Stats = newStats
                };
                Calculations.GetCharacterCalculations(character, item, true, false, false);
            }
            // increase the spread a bit to so that you can see if something is at the edges and straight
            float DpsVariance = maxDpsChange - minDpsChange;

            minDpsChange -= DpsVariance * 0.05f;
            maxDpsChange += DpsVariance * 0.05f;
            DpsVariance   = maxDpsChange - minDpsChange;
            if (DpsVariance == 0)
            {
                DpsVariance = 1;
            }
            for (int index = 0; index < statsList.Length; index++)
            {
                for (int count = -scale; count <= scale; count++)
                {
                    points[index][count + scale].Y = (int)((maxDpsChange - points[index][count + scale].Y) * (graphHeight - 48) / DpsVariance) + 20;
                }
                Brush statBrush = new SolidBrush(colors[index]);
                switch (style)
                {
                case Style.DpsWarr:
                    g.DrawLines(new Pen(statBrush, 3), points[index]);
                    break;

                case Style.Mage:
                    g.DrawLines(new Pen(statBrush, 1), points[index]);
                    break;
                }
            }
            float unit = 1f;
            var   bp   = baseStat.Values(x => x > 0);

            foreach (var kvp in bp)
            {
                unit = kvp.Value;
            }
            RenderGrid(g, graphWidth, graphHeight, character, statsList, colors, scale, unit, "F", explanatoryText, calculation, style, minDpsChange, maxDpsChange, DpsVariance, false);
        }
Beispiel #22
0
 public static void RenderScalingGraph(Graphics g, int graphWidth, int graphHeight, Character character, Stats[] statsList, Stats baseStat, bool requiresReferenceCalculations, Color[] colors, int scale, string explanatoryText, string calculation, Style style)
 {
     CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(character);
     g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
     g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
     float graphOffset = graphWidth / 2.0f, graphStep = (graphWidth - 100) / 2.0f / scale;
     if (statsList.Length == 0 || statsList.Length > colors.Length) return; // more than 12 elements for the array would run out of colours
     float minDpsChange = 0f, maxDpsChange = 0f;
     PointF[][] points = new PointF[statsList.Length][];
     // extract property data for relative stats calculations
     KeyValuePair<PropertyInfo, float>[] properties = new KeyValuePair<PropertyInfo,float>[statsList.Length];
     for (int index = 0; index < statsList.Length; index++)
     {
         var p = statsList[index].Values(x => x > 0);
         foreach (var kvp in p)
         {
             properties[index] = kvp;
         }
         points[index] = new PointF[2 * scale + 1];
     }
     for (int count = -scale; count <= scale; count++)
     {
         Stats newStats = new Stats();
         newStats.Accumulate(baseStat, count);
         Item item = new Item() { Stats = newStats };
         if (requiresReferenceCalculations)
         {
             Calculations.GetCharacterCalculations(character, item, true, false, false);
         }
         for (int index = 0; index < statsList.Length; index++)
         {
             ComparisonCalculationBase currentCalc = CalculationsBase.GetRelativeStatValue(character, properties[index].Key, item, properties[index].Value);
             float dpsChange = GetCalculationValue(currentCalc, calculation);
             points[index][count + scale] = new PointF(graphOffset + count * graphStep, dpsChange);
             if (dpsChange < minDpsChange)
                 minDpsChange = dpsChange;
             if (dpsChange > maxDpsChange)
                 maxDpsChange = dpsChange;
         }
     }
     // restore reference calculation
     if (requiresReferenceCalculations)
     {
         Stats newStats = new Stats();
         Item item = new Item() { Stats = newStats };
         Calculations.GetCharacterCalculations(character, item, true, false, false);
     }
     // increase the spread a bit to so that you can see if something is at the edges and straight
     float DpsVariance = maxDpsChange - minDpsChange;
     minDpsChange -= DpsVariance * 0.05f;
     maxDpsChange += DpsVariance * 0.05f;
     DpsVariance = maxDpsChange - minDpsChange;
     if (DpsVariance == 0)
         DpsVariance = 1;
     for (int index = 0; index < statsList.Length; index++)
     {
         for (int count = -scale; count <= scale; count++)
         {
             points[index][count + scale].Y = (int)((maxDpsChange - points[index][count + scale].Y) * (graphHeight - 48) / DpsVariance) + 20;
         }
         Brush statBrush = new SolidBrush(colors[index]);
         switch (style)
         {
             case Style.DpsWarr:
                 g.DrawLines(new Pen(statBrush, 3), points[index]);
                 break;
             case Style.Mage:
                 g.DrawLines(new Pen(statBrush, 1), points[index]);
                 break;
         }
     }
     float unit = 1f;
     var bp = baseStat.Values(x => x > 0);
     foreach (var kvp in bp)
     {
         unit = kvp.Value;
     }
     RenderGrid(g, graphWidth, graphHeight, character, statsList, colors, scale, unit, "F", explanatoryText, calculation, style, minDpsChange, maxDpsChange, DpsVariance, false);
 }
Beispiel #23
0
 public void UpdateScalingGraph(Character character, Stats[] statsList, Stats baseStat, bool requiresReferenceCalculations, Color[] colors, int scale, string explanatoryText, string calculation)
 {
     CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(character);
     if (statsList.Length == 0 || statsList.Length > colors.Length) return; // more than 12 elements for the array would run out of colours
     Point[][] points = new Point[statsList.Length][];
     // extract property data for relative stats calculations
     KeyValuePair<PropertyInfo, float>[] properties = new KeyValuePair<PropertyInfo, float>[statsList.Length];
     for (int index = 0; index < statsList.Length; index++)
     {
         var p = statsList[index].Values(x => x > 0);
         foreach (var kvp in p)
         {
             properties[index] = kvp;
         }
         points[index] = new Point[2 * scale + 1];
     }
     float unit = 1f;
     var bp = baseStat.Values(x => x > 0);
     foreach (var kvp in bp)
     {
         unit = kvp.Value;
     }
     Chart.Series.Clear();
     for (int count = -scale; count <= scale; count++)
     {
         Stats newStats = new Stats();
         newStats.Accumulate(baseStat, count);
         Item item = new Item() { Stats = newStats };
         if (requiresReferenceCalculations)
         {
             Calculations.GetCharacterCalculations(character, item, true, false, false);
         }
         for (int index = 0; index < statsList.Length; index++)
         {
             ComparisonCalculationBase currentCalc = CalculationsBase.GetRelativeStatValue(character, properties[index].Key, item, properties[index].Value);
             float dpsChange = GetCalculationValue(currentCalc, calculation);
             points[index][count + scale] = new Point(count * unit, dpsChange);
         }
     }
     for (int index = 0; index < statsList.Length; index++)
     {
         Style dataPointStyle = new Style(typeof(LineDataPoint));
         dataPointStyle.Setters.Add(new Setter(DataPoint.TemplateProperty, Resources["InvisibleDataPointTemplate"]));
         dataPointStyle.Setters.Add(new Setter(DataPoint.BackgroundProperty, new SolidColorBrush(colors[index])));
         Chart.Series.Add(new LineSeries()
         {
             Title = statsList[index].ToString(),
             ItemsSource = points[index],
             IndependentValuePath = "X",
             DependentValuePath = "Y",
             DataPointStyle = dataPointStyle,
         });
     }
     Chart.Axes.Clear();
     Chart.Axes.Add(new LinearAxis()
     {
         Orientation = AxisOrientation.X,
         Title = "Stat Change",
         ShowGridLines = true,
     });
     Chart.Axes.Add(new LinearAxis()
     {
         Orientation = AxisOrientation.Y,
         Title = calculation,
         ShowGridLines = true,
     });
     // restore reference calculation
     if (requiresReferenceCalculations)
     {
         Stats newStats = new Stats();
         Item item = new Item() { Stats = newStats };
         Calculations.GetCharacterCalculations(character, item, true, false, false);
     }
     orgDataDirty = true;
 }
        public StatsPaladin GetCharacterStats(Character character, Item additionalItem, CalculationOptionsProtPaladin calcOpts, BossOptions bossOpts)
        {
            PaladinTalents talents = character.PaladinTalents;

            Stats statsBase = BaseStats.GetBaseStats(character.Level, CharacterClass.Paladin, character.Race);
            statsBase.Expertise += BaseStats.GetRacialExpertise(character, ItemSlot.MainHand);


            Stats statsBuffs = GetBuffsStats(character, calcOpts);
            Stats statsItems = GetItemStats(character, additionalItem, calcOpts);
            Stats statsTalents = new Stats()
            {
                BaseArmorMultiplier = talents.Toughness * 0.1f / 3f,
                BonusStaminaMultiplier = 0.15f // Touched by the Light
            };
            Stats statsGearEnchantsBuffs = new Stats();
            statsGearEnchantsBuffs.Accumulate(statsItems);
            statsGearEnchantsBuffs.Accumulate(statsBuffs);
            StatsPaladin statsTotal = new StatsPaladin();
            statsTotal.Accumulate(statsBase);
            statsTotal.Accumulate(statsItems);
            statsTotal.Accumulate(statsBuffs);
            statsTotal.Accumulate(statsTalents);

            int T11count;
            character.SetBonusCount.TryGetValue("Reinforced Sapphirium Battlearmor", out T11count);
            if (T11count >= 2) { statsTotal.BonusDamageMultiplierCrusaderStrike = 0.05f; }
            if (T11count >= 4) { statsTotal.BonusDurationMultiplierGuardianOfAncientKings = 0.50f; }
            int T12count;
            character.SetBonusCount.TryGetValue("Battlearmor of Immolation", out T12count);
            if (T12count >= 2) { statsTotal.BonusDamageShieldofRighteous = 0.20f; }
            if (T12count >= 4) {
                statsBuffs.AddSpecialEffect(new SpecialEffect(Trigger.DivineProtection,
                    new Stats() { Parry = 0.12f, },
                    10f, 60f));
            }

            statsTotal.Intellect = (float)Math.Floor(statsBase.Intellect * (1.0f + statsTalents.BonusIntellectMultiplier));
            statsTotal.Intellect += (float)Math.Floor((statsItems.Intellect + statsBuffs.Intellect) * (1.0f + statsTalents.BonusIntellectMultiplier));

            statsTotal.BaseAgility = statsBase.Agility + statsTalents.Agility;

            statsTotal.Stamina = (float)Math.Floor(statsBase.Stamina
                                    + statsItems.Stamina
                                    + statsBuffs.Stamina);
            statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina
                                    * (1.0f + statsTotal.BonusStaminaMultiplier)
                                    * (Character.ValidateArmorSpecialization(character, ItemType.Plate) ? 1.05f : 1f)); // Plate specialization

            statsTotal.Strength = (float)Math.Floor((statsBase.Strength + statsTalents.Strength) * (1.0f + statsTotal.BonusStrengthMultiplier));
            statsTotal.Strength += (float)Math.Floor((statsItems.Strength + statsBuffs.Strength) * (1.0f + statsTotal.BonusStrengthMultiplier));

            statsTotal.ParryRating += (float)Math.Floor((statsTotal.Strength - statsBase.Strength) * 0.27f);

            statsTotal.SpellPower = statsTotal.Strength * 0.60f; // Touched by the Light
            statsTotal.SpellPower += statsTotal.Intellect - 10f;

            if (talents.GlyphOfSealOfTruth && calcOpts.SealChoice == "Seal of Truth")
            {
                statsTotal.Expertise += 10.0f;
            }
            statsTotal.Agility = (float)Math.Floor((statsBase.Agility + statsTalents.Agility) * (1.0f + statsTotal.BonusAgilityMultiplier));
            statsTotal.Agility += (float)Math.Floor((statsItems.Agility + statsBuffs.Agility) * (1.0f + statsTotal.BonusAgilityMultiplier));
            statsTotal.Health += StatConversion.GetHealthFromStamina(statsTotal.Stamina, CharacterClass.Paladin);

            statsTotal.Health *= 1f + statsTotal.BonusHealthMultiplier;
            statsTotal.Mana += StatConversion.GetManaFromIntellect(statsTotal.Intellect, CharacterClass.Paladin) * (1f + statsTotal.BonusManaMultiplier);

            // Armor
            statsTotal.Armor  = (float)Math.Floor(statsTotal.Armor * (1f + statsTotal.BaseArmorMultiplier));
            statsTotal.Armor += (float)Math.Floor(statsTotal.BonusArmor * (1f + statsTotal.BonusArmorMultiplier));

            statsTotal.AttackPower += ((statsTotal.Strength - 10f) * 2f);
            statsTotal.AttackPower = (float)Math.Floor(statsTotal.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier));
            statsTotal.NatureResistance += statsTotal.NatureResistanceBuff;
            statsTotal.FireResistance += statsTotal.FireResistanceBuff;
            statsTotal.FrostResistance += statsTotal.FrostResistanceBuff;
            statsTotal.ShadowResistance += statsTotal.ShadowResistanceBuff;
            statsTotal.ArcaneResistance += statsTotal.ArcaneResistanceBuff;
            //statsTotal.BonusCritDamageMultiplier = statsBase.BonusCritDamageMultiplier + statsGearEnchantsBuffs.BonusCritDamageMultiplier;
            statsTotal.CritRating = statsBase.CritRating + statsGearEnchantsBuffs.CritRating;
            statsTotal.ExpertiseRating = statsBase.ExpertiseRating + statsGearEnchantsBuffs.ExpertiseRating;
            statsTotal.HasteRating = statsBase.HasteRating + statsGearEnchantsBuffs.HasteRating;
            statsTotal.HitRating = statsBase.HitRating + statsGearEnchantsBuffs.HitRating;
            statsTotal.MasteryRating = statsBase.MasteryRating + statsGearEnchantsBuffs.MasteryRating;
            statsTotal.BlockRating = statsBase.BlockRating + statsGearEnchantsBuffs.BlockRating;
            statsTotal.WeaponDamage += Lookup.WeaponDamage(character, statsTotal.AttackPower, false);
            //statsTotal.ExposeWeakness = statsBase.ExposeWeakness + statsGearEnchantsBuffs.ExposeWeakness; // Nerfed in 3.1

            // Calculate Procs and Special Effects
            statsTotal.Accumulate(GetSpecialEffectStats(character, statsTotal, calcOpts, bossOpts));

            return statsTotal;
        }
Beispiel #25
0
        public Stats getSpecialEffects(SpecialEffect effect)
        {
            Stats statsAverage = new Stats();

            if (effect == mainHandEnchant || effect == offHandEnchant)
            {
                if (mainHandEnchant != null && !mhProcessed)
                {
                    statsAverage.Accumulate(mainHandEnchant.Stats, GetMHUptime());
                    mhProcessed = true;
                }
                else if (offHandEnchant != null && !ohProcessed)
                {
                    statsAverage.Accumulate(offHandEnchant.Stats, GetOHUptime());
                    ohProcessed = true;
                }
            }
            else if (effect.Trigger == Trigger.Use)
            {
                effect.AccumulateAverageStats(statsAverage);
                foreach (SpecialEffect e in effect.Stats.SpecialEffects())
                {
                    statsAverage.Accumulate(this.getSpecialEffects(e) * (effect.Duration / effect.Cooldown));
                }
            }
            else
            {
                SetTriggerChanceAndSpeed(effect);
                foreach (SpecialEffect e in effect.Stats.SpecialEffects())  // deal with secondary effects
                {
                    statsAverage.Accumulate(this.getSpecialEffects(e));
                }
                if (effect.MaxStack > 1)
                {
                    if (effect.Stats.MoteOfAnger > 0)
                    {
                        // When in effect stats, MoteOfAnger is % of melee hits
                        // When in character stats, MoteOfAnger is average procs per second
                        statsAverage.Accumulate(new Stats()
                        {
                            MoteOfAnger = effect.Stats.MoteOfAnger * effect.GetAverageProcsPerSecond(trigger, chance, unhastedAttackSpeed, 0f) / effect.MaxStack
                        });
                    }
                    else
                    {
                        float timeToMax    = (float)Math.Min(_cs.FightLength, effect.GetChance(unhastedAttackSpeed) * trigger * effect.MaxStack);
                        float buffDuration = _cs.FightLength;
                        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.MaxStack * (((buffDuration) - .5f * timeToMax) / (buffDuration))));
                    }
                }
                else
                {
                    effect.AccumulateAverageStats(statsAverage, trigger, chance, unhastedAttackSpeed);
                }
            }
            return(statsAverage);
        }
Beispiel #26
0
        protected static void AccumulateSpecialEffects(Character character, ref Stats stats, float FightDuration, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, List<SpecialEffect> effects, float weight)
        {
            foreach (SpecialEffect effect in effects)
            {
                Stats effectStats = effect.Stats;

                float upTime = 0f;

                if (effect.Trigger == Trigger.Use)
                {
                    if (effect.Stats._rawSpecialEffectDataSize >= 1)
                    {
                        upTime = effect.GetAverageUptime(0f, 1f, 0, FightDuration);
                        List<SpecialEffect> nestedEffect = new List<SpecialEffect>(effect.Stats.SpecialEffects());
                        Stats _stats2 = effectStats.Clone();
                        AccumulateSpecialEffects(character, ref _stats2, effect.Duration, triggerIntervals, triggerChances, nestedEffect, upTime);
                        effectStats = _stats2;
                    }
                    else
                    {
                        upTime = effect.GetAverageStackSize(0f, 1f, 0, FightDuration);
                    }
                }
                else if (effect.Duration == 0f)
                {
                    upTime = effect.GetAverageProcsPerSecond(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger],
                                                             0, FightDuration);
                }
                else if (triggerIntervals.ContainsKey(effect.Trigger))
                {
                    upTime = effect.GetAverageStackSize(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger],
                                                             0, FightDuration);
                }

                if (upTime > 0f)
                {
                    stats.Accumulate(effectStats, upTime * weight);
                }
            }
        }
        public StatsRetri GetCharacterStats(Character character, Item additionalItem, bool computeAverageStats)
        {
            PaladinTalents talents = character.PaladinTalents;
            CalculationOptionsRetribution calcOpts = character.CalculationOptions as CalculationOptionsRetribution;

            StatsRetri stats = new StatsRetri();
            stats.Accumulate(BaseStats.GetBaseStats(character.Level, CharacterClass.Paladin, character.Race)); //Race
            stats.Accumulate(GetItemStats(character, additionalItem));                                         //Items 
            stats.Accumulate(GetBuffsStats(character, calcOpts));                                              //Buffs 

            // Adjust expertise for racial passive
            stats.Expertise += BaseStats.GetRacialExpertise(character, ItemSlot.MainHand);
            // Judgements of the pure (Flat because it has nearly always a 100% chance.
            stats.PhysicalHaste += PaladinConstants.JUDGEMENTS_OF_THE_PURE * talents.JudgementsOfThePure;
            stats.SpellHaste += PaladinConstants.JUDGEMENTS_OF_THE_PURE * talents.JudgementsOfThePure;
            
            //Sets
            stats.SetSets(character);
                        
            // If wanted, Average out any Proc and OnUse effects into the stats
            if (computeAverageStats)
            {
                StatsRetri statsTmp = stats.Clone();
                ConvertRatings(statsTmp, talents, character);// Convert ratings so we have right value for haste, weaponspeed and talents etc.
                RotationCalculation rot = CreateRotation(character, statsTmp);

                Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>();
                Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>();
                CalculateTriggers(triggerIntervals, triggerChances, rot);

                //Talent special effects
                //GoaK Strength
                stats.AddSpecialEffect(_GoakSE);
                
                // Average out proc effects, and add to global stats.
                Stats statsAverage = new Stats();
                foreach (SpecialEffect effect in stats.SpecialEffects())
                    statsAverage.Accumulate(effect.GetAverageStats(triggerIntervals, triggerChances, AbilityHelper.BaseWeaponSpeed(character), character.BossOptions.BerserkTimer));
                stats.Accumulate(statsAverage);
            }

            // No negative values (from possible charts)
            if (stats.Strength < 0)
                stats.Strength = 0;
            if (stats.Agility < 0)
                stats.Agility = 0;
            if (stats.AttackPower < 0)
                stats.AttackPower = 0;
            if (stats.ExpertiseRating < 0)
                stats.ExpertiseRating = 0;
            if (stats.HitRating < 0)
                stats.HitRating = 0;
            if (stats.CritRating < 0)
                stats.CritRating = 0;
            if (stats.HasteRating < 0)
                stats.HasteRating = 0;
            if (stats.SpellPower < 0)
                stats.SpellPower = 0;
            if (stats.MasteryRating < 0)
                stats.MasteryRating = 0;
        
            // ConvertRatings needs to be done AFTER accounting for the averaged stats, since stat multipliers 
            // should affect the averaged stats also.
            ConvertRatings(stats, talents, character);

            return stats;
        }
Beispiel #28
0
        public override Stats GetCharacterStats(Character character, Item additionalItem)
        {
            PriestTalents talents = character.PriestTalents;

            Stats statsTotal = new Stats();

            Stats baseStats = BaseStats.GetBaseStats(character.Level, character.Class, character.Race);
            Stats itemStats = GetItemStats(character, additionalItem);
            Stats buffStats = GetBuffsStats(character, _calculationOptions);

            // Get the gear/enchants/buffs stats loaded in
            statsTotal.Accumulate(baseStats);
            statsTotal.Accumulate(itemStats);
            statsTotal.Accumulate(buffStats);

            Stats statsTalents = new Stats()
            {
                // we can only wear items that are cloth so we always have our specialization, even naked.
                BonusIntellectMultiplier = 0.05f,

                BonusShadowDamageMultiplier = (1 + 0.02f * talents.TwinDisciplines) *
                                              (1 + 0.02f * talents.TwistedFaith) *
                                              (1 + 0.15f * talents.Shadowform) - 1,

                BonusHolyDamageMultiplier = (1 + 0.02f * talents.TwinDisciplines) - 1,

                // this is the shadow priest model so they must have 'Shadow Power'
                BonusSpellPowerMultiplier = .15f,
            };

            statsTotal.Accumulate(statsTalents);

            statsTotal.Stamina    = (float)Math.Floor(statsTotal.Stamina * (1 + statsTotal.BonusStaminaMultiplier));
            statsTotal.Intellect += (float)Math.Floor(itemStats.Intellect * statsTotal.BonusIntellectMultiplier);
            statsTotal.Spirit     = (float)Math.Round(statsTotal.Spirit * (1 + statsTotal.BonusSpiritMultiplier));

            statsTotal.Health += (float)Math.Floor(StatConversion.GetHealthFromStamina(statsTotal.Stamina) * (1f + statsTotal.BonusHealthMultiplier));

            statsTotal.Mana = (float)Math.Round(statsTotal.Mana + StatConversion.GetManaFromIntellect(statsTotal.Intellect));
            statsTotal.Mana = (float)Math.Round(statsTotal.Mana * (1f + statsTotal.BonusManaMultiplier));

            statsTotal.SpellPower += statsTotal.Intellect - 10;

            float hasteFromRating = StatConversion.GetSpellHasteFromRating(statsTotal.HasteRating);
            float talentedHaste   = (1 + hasteFromRating) * (1 + talents.Darkness * .01f) - 1;

            statsTotal.SpellHaste += character.Race == CharacterRace.Goblin ? talentedHaste * 1.01f : talentedHaste;

            float baseBonus = (float)Math.Floor(baseStats.Spirit * statsTotal.BonusSpiritMultiplier);
            float itemBonus = (float)Math.Floor(itemStats.Spirit * statsTotal.BonusSpiritMultiplier);
            float spiritFromItemsAndEffects = baseBonus + itemBonus + itemStats.Spirit;
            float hitRatingFromSpirit       = (0.5f * talents.TwistedFaith) * Math.Max(0f, spiritFromItemsAndEffects);

            statsTotal.HitRating += hitRatingFromSpirit;
            statsTotal.SpellHit  += StatConversion.GetSpellHitFromRating(statsTotal.HitRating);

            // ignoring the base crit percentage here as the in-game tooltip says that the int -> crit conversion contains the base.
            float critFromInt    = StatConversion.GetSpellCritFromIntellect(statsTotal.Intellect) + 0.012375f;
            float critFromRating = StatConversion.GetSpellCritFromRating(statsTotal.CritRating);

            statsTotal.SpellCrit = character.Race == CharacterRace.Worgen ? (critFromInt + critFromRating) + .01f : (critFromInt + critFromRating);

            // Armor
            statsTotal.Armor      = statsTotal.Armor * (1f + statsTotal.BaseArmorMultiplier);
            statsTotal.BonusArmor = statsTotal.BonusArmor * (1f + statsTotal.BonusArmorMultiplier);
            statsTotal.Armor     += statsTotal.BonusArmor;
            statsTotal.Armor      = (float)Math.Round(statsTotal.Armor);

            return(statsTotal);
        }
        public override Stats GetCharacterStats(Character character, Item additionalItem)
        {
            CalculationOptionsTree opts = character.CalculationOptions as CalculationOptionsTree;
            if (opts == null)
                opts = new CalculationOptionsTree();

            Stats stats = new Stats();
            Stats statsBase = BaseStats.GetBaseStats(character.Level, character.Class, character.Race);
            stats.Accumulate(statsBase);
            stats.BaseAgility = statsBase.Agility;

            // Get the gear/enchants/buffs stats loaded in
            stats.Accumulate(GetItemStats(character, additionalItem));
            stats.Accumulate(GetBuffsStats(character, opts));

            // Talented bonus multipliers
            Stats statsTalents = new Stats()
            {
                BonusIntellectMultiplier = (1 + 0.02f * character.DruidTalents.HeartOfTheWild) * (Character.ValidateArmorSpecialization(character, ItemType.Leather) ? 1.05f : 1f) - 1f,
                BonusManaMultiplier = 0.05f * character.DruidTalents.Furor,
                ManaCostReductionMultiplier = character.DruidTalents.Moonglow * 0.03f,
                SpellCrit = 0.02f * character.DruidTalents.NaturesMajesty
            };

            stats.Accumulate(statsTalents);

            FinalizeStats(stats, stats);

            // Derived stats: Health, mana pool, armor
            stats.Health = (float)Math.Round(stats.Health + StatConversion.GetHealthFromStamina(stats.Stamina));
            stats.Health = (float)Math.Floor(stats.Health * (1f + stats.BonusHealthMultiplier));
            stats.Mana = (float)Math.Round(stats.Mana + StatConversion.GetManaFromIntellect(stats.Intellect));
            stats.Mana = (float)Math.Floor(stats.Mana * (1f + stats.BonusManaMultiplier));

            // Armor
            stats.Armor = stats.Armor * (1f + stats.BaseArmorMultiplier);
            stats.BonusArmor = stats.BonusArmor * (1f + stats.BonusArmorMultiplier);
            stats.Armor += stats.BonusArmor;
            stats.Armor = (float)Math.Round(stats.Armor);

            int T13Count;
            character.SetBonusCount.TryGetValue("Deep Earth Vestments", out T13Count);

            if (character.DruidTalents.NaturesGrace > 0)
                stats.AddSpecialEffect(NaturesGrace[character.DruidTalents.NaturesGrace]);

            if (character.DruidTalents.TreeOfLife > 0)
                stats.AddSpecialEffect(TreeOfLife[character.DruidTalents.NaturalShapeshifter]);

            if (T13Count >= 2)
                stats.AddSpecialEffect(T13InnervateManaCostReduction);

            return stats;
        }
Beispiel #30
0
        void computeStats()
        {
            RandomIntellectEffects = new List<RandomIntellectEffect>();
            RandomIntellectEffects.Add(new RandomIntellectEffect(0, null, 0, 0));
            OnUseIntellectProcsMana = 0.0f;

            calc.MeanMana = calc.BasicStats.Mana;

            List<KeyValuePair<double, SpecialEffect>> hasteProcsList = new List<KeyValuePair<double, SpecialEffect>>();

            // ToL, NG, Heroism, Shard of Woe, etc.
            List<SpecialEffect> dividingEffectsList = new List<SpecialEffect>();
            List<int> dividingEffectBucketsList = new List<int>();

            KeyValuePair<double, SpecialEffect>[] hasteProcs;
            Stats statsDividing = new Stats();
            Stats statsProcs = new Stats();

            Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger,float>();
            Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>();

            triggerIntervals[Trigger.Use] = 0;

            triggerIntervals[Trigger.HealingSpellCast] = triggerIntervals[Trigger.HealingSpellCrit] = triggerIntervals[Trigger.HealingSpellHit] =
                triggerIntervals[Trigger.SpellCast] = triggerIntervals[Trigger.SpellCrit] = triggerIntervals[Trigger.SpellHit] =
                triggerIntervals[Trigger.DamageOrHealingDone] = (float)calc.ProcTriggerInterval;

            triggerIntervals[Trigger.HoTTick] = (float)calc.ProcPeriodicTriggerInterval;

            foreach(Trigger trigger in triggerIntervals.Keys)
                triggerChances[trigger] = 1;

            // NOTE: this ignores crit from procs, but hopefully this shouldn't matter much
            triggerChances[Trigger.HealingSpellCrit] = triggerChances[Trigger.SpellCrit] =
                StatConversion.GetSpellCritFromIntellect(calc.BasicStats.Intellect) + StatConversion.GetSpellCritFromRating(calc.BasicStats.CritRating) + calc.BasicStats.SpellCrit;


            if(opts.TriggerDamageEffects)
            {
                foreach (SpecialEffect effect in calc.BasicStats.SpecialEffects())
                {
                    if (effect.Trigger == Trigger.DamageSpellCast || effect.Trigger == Trigger.DamageSpellHit || effect.Trigger == Trigger.DoTTick || effect.Trigger == Trigger.DamageSpellCrit)
                        InsectSwarm = true;
                }
            }

            if (InsectSwarm)
            {
                triggerIntervals[Trigger.DamageSpellCast] = triggerIntervals[Trigger.DamageSpellHit] = triggerIntervals[Trigger.DoTTick] = triggerIntervals[Trigger.DamageSpellCrit] = (float)calc.DamageProcPeriodicTriggerInterval;

                triggerChances[Trigger.DamageSpellCast] = triggerChances[Trigger.DamageSpellHit] = triggerChances[Trigger.DoTTick] = 1;
                triggerChances[Trigger.DamageSpellCrit] = StatConversion.GetSpellCritFromIntellect(calc.BasicStats.Intellect) + StatConversion.GetSpellCritFromRating(calc.BasicStats.CritRating) + calc.BasicStats.SpellCrit;
            }

            foreach (SpecialEffect effect in calc.BasicStats.SpecialEffects())
            {
                if (triggerIntervals.ContainsKey(effect.Trigger))
                {
                    if (effect.Stats.Intellect > 0 || effect.Stats.HighestStat > 0)
                    {
                        double mana = StatConversion.GetManaFromIntellect((effect.Stats.Intellect + effect.Stats.HighestStat) * (1 + calc.BasicStats.BonusIntellectMultiplier)) * (1 + calc.BasicStats.BonusManaMultiplier);
                        double avgMana = mana * effect.GetAverageFactor(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], 3.0f, character.BossOptions.BerserkTimer);
                        if (effect.Trigger != Trigger.Use)
                            RandomIntellectEffects.Add(new RandomIntellectEffect(mana - avgMana, effect, triggerIntervals[effect.Trigger], triggerChances[effect.Trigger]));

                        if (effect.Trigger == Trigger.Use && effect.Cooldown <= 180.0f)
                            OnUseIntellectProcsMana += mana - avgMana;
                        calc.MeanMana += avgMana;
                    }

                    Stats stats = effect.GetAverageStats(triggerIntervals, triggerChances, 3.0f, character.BossOptions.BerserkTimer);
                    
                    if (effect.Trigger == Trigger.Use
                        && effect.MaxStack <= 1
                        && (effect.Stats is CalculationsTree.TreeOfLifeStats || effect.Stats.HasteRating > 0 || effect.Stats.SpellHaste > 0 || effect.Stats.Intellect > 0 || effect.Stats.SpellPower > 0 || effect.Stats.CritRating > 0 || effect.Stats.SpellCrit > 0 || effect.Stats.MasteryRating > 0 || effect.Stats.NatureSpellsManaCostReduction != 0 || effect.Stats.SpellsManaCostReduction != 0 || effect.Stats.ManaCostReductionMultiplier != 0)
                        )
                    {
                        dividingEffectsList.Add(effect);
                        int bucket = -1;
                        if(opts.SeparateHasteEffects && (effect.Stats.HasteRating > 0 || effect.Stats.SpellHaste > 0))
                            bucket = 0;
                        dividingEffectBucketsList.Add(bucket);
                        statsDividing.Accumulate(stats);
                    }
                    else
                    {
                        if (effect.Stats.HasteRating > 0 || effect.Stats.SpellHaste > 0)
                        {
                            double uptime = effect.GetAverageUptime(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], 3.0f, character.BossOptions.BerserkTimer);
                            hasteProcsList.Add(new KeyValuePair<double, SpecialEffect>(uptime, effect));
                            stats.HasteRating = 0;
                            stats.SpellHaste = 0;
                        }
                        statsProcs.Accumulate(stats);
                    }
                }
            }

            MeanStats = new Stats();
            MeanStats.Accumulate(statsProcs);
            MeanStats.Accumulate(statsDividing);

            // XXX: this fixes Tyrande's Favorite Doll, but seems to be a bug in Rawr
            MeanStats.Mp5 = MeanStats.ManaRestore * 5;
            MeanStats.ManaRestore = 0;

            if (T11Count >= 4)
                MeanStats.Spirit += 540 * opts.HarmonyPeriodicRate;

            CalculationsTree.FinalizeStats(MeanStats, calc.BasicStats);
            MeanStats.Accumulate(calc.BasicStats);

            hasteProcs = hasteProcsList.ToArray();

            calc.Division = FightDivision.ComputeNaive(dividingEffectsList.ToArray(), calc.FightLength, dividingEffectBucketsList.ToArray());

            if (opts.ActivityRate < 1.0f)
            {                                
                SpecialEffect[] newEffects = new SpecialEffect[calc.Division.Effects.Length + 1];
                Array.Copy(calc.Division.Effects, newEffects, calc.Division.Effects.Length);
                newEffects[calc.Division.Effects.Length] = CalculationsTree.InactiveEffect;

                int[] newEffectMasks = new int[calc.Division.Count + 1];
                Array.Copy(calc.Division.EffectMasks, newEffectMasks, calc.Division.Count);
                newEffectMasks[calc.Division.Count] = 1 << calc.Division.Effects.Length;

                double[] newFractions = new double[calc.Division.Count + 1];
                Array.Copy(calc.Division.Fractions, newFractions, calc.Division.Count);

                double curActivityRate = 1;
                for (int i = 0; i < calc.Division.Count; ++i)
                {
                    if (calc.Division.EffectMasks[i] == 0)
                    {
                        double t = Math.Min(1 - opts.ActivityRate, newFractions[i]);
                        curActivityRate -= t;
                        newFractions[i] -= t;
                    }
                }

                if (curActivityRate > opts.ActivityRate)
                {
                    for (int i = 0; i < calc.Division.Count; ++i)
                    {
                        newFractions[i] *= opts.ActivityRate;
                    }
                }

                newFractions[calc.Division.Count] = 1 - opts.ActivityRate;

                calc.Division.Fractions = newFractions;
                calc.Division.Effects = newEffects;
                calc.Division.EffectMasks = newEffectMasks;
                ++calc.Division.Count;
            }

            double baseSpellPower = (float)(MeanStats.SpellPower + Math.Max(0f, calc.BasicStats.Intellect - 10));
            // TODO: does nurturing instinct actually work like this?
            baseSpellPower += Talents.NurturingInstinct * 0.5 * calc.BasicStats.Agility;
            baseSpellPower *= 1 + calc.BasicStats.BonusSpellPowerMultiplier;
            calc.BaseSpellPower = baseSpellPower;

            calc.Stats = new TreeStats[calc.Division.Count];

            for (int div = 0; div < calc.Division.Fractions.Length; ++div)
            {
                Stats statsWithOnUse = new Stats();
                bool treeOfLifeActive = false;
                for (int j = 0; j < calc.Division.Effects.Length; ++j)
                {
                    if ((calc.Division.EffectMasks[div] & (1 << j)) != 0)
                    {
                        if (calc.Division.Effects[j].Stats is CalculationsTree.TreeOfLifeStats)
                            treeOfLifeActive = true;
                        else
                            statsWithOnUse.Accumulate(calc.Division.Effects[j].Stats);
                    }
                }

                statsWithOnUse.Accumulate(statsProcs);

                // XXX: this fixes Tyrande's Favorite Doll, but seems to be a bug in Rawr
                statsWithOnUse.Mp5 = statsWithOnUse.ManaRestore * 5;
                statsWithOnUse.ManaRestore = 0;

                CalculationsTree.FinalizeStats(statsWithOnUse, calc.BasicStats);
                statsWithOnUse.Accumulate(calc.BasicStats);

                calc.Stats[div] = new TreeStats(character, statsWithOnUse, hasteProcs, treeOfLifeActive ? 1.0 : 0.0);
            }

        }
        public Stats GetCharacterStats(Character character, Item additionalItem, bool computeAverageStats, CharacterCalculationsHealadin calc)
        {
            PaladinTalents talents = character.PaladinTalents;

            CalculationOptionsHealadin calcOpts = character.CalculationOptions as CalculationOptionsHealadin;

            if (calcOpts == null)
            {
                calcOpts = new CalculationOptionsHealadin();
            }

#if (RAWR3)
            BossOptions bossOpts = character.BossOptions;
            if (bossOpts == null)
            {
                bossOpts = new BossOptions();
            }
#endif

#if (RAWR3)
            float fightLength = bossOpts.BerserkTimer * 60f;
#else
            float fightLength = calcOpts.Length * 60f;
#endif

            Stats statsRace     = BaseStats.GetBaseStats(character.Level, CharacterClass.Paladin, character.Race);
            Stats statsBaseGear = GetItemStats(character, additionalItem);
            Stats statsBuffs    = GetBuffsStats(character, calcOpts);
            Stats stats         = statsBaseGear + statsBuffs + statsRace;

            ConvertRatings(stats, talents, calcOpts);
            if (computeAverageStats)
            {
                Stats statsAverage = new Stats();

                foreach (SpecialEffect effect in stats.SpecialEffects())
                {
                    float trigger = 0f;
                    if (calc == null)
                    {
                        trigger = 1.5f / calcOpts.Activity / (1f + stats.SpellHaste);
                        if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                        {
                            trigger *= stats.SpellCrit;
                        }
                        else if (effect.Trigger == Trigger.HolyShockCast)
                        {
                            trigger = 6f / calcOpts.HolyShock;
                        }
                    }
                    else
                    {
                        if (effect.Trigger == Trigger.HealingSpellCast || effect.Trigger == Trigger.HealingSpellHit)
                        {
                            trigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                        }
                        else if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                        {
                            trigger = 1f / Rotation.GetHealingCritsPerSec(calc);
                        }
                        else if (effect.Trigger == Trigger.SpellCast || effect.Trigger == Trigger.SpellHit)
                        {
                            trigger = 1f / Rotation.GetSpellCastsPerSec(calc);
                        }
                        else if (effect.Trigger == Trigger.DamageOrHealingDone)
                        {
                            trigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                        }
                        else if (effect.Trigger == Trigger.HolyShockCast)
                        {
                            trigger = 6f / calcOpts.HolyShock;
                        }
                        else if (effect.Trigger == Trigger.Use)
                        {
                            trigger = 0f;
                            foreach (SpecialEffect childEffect in effect.Stats.SpecialEffects())
                            {
                                float childTrigger = 0f;

                                if (effect.Trigger == Trigger.HealingSpellCast || effect.Trigger == Trigger.HealingSpellHit)
                                {
                                    childTrigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                                }
                                else if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                                {
                                    childTrigger = 1f / Rotation.GetHealingCritsPerSec(calc);
                                }
                                else if (effect.Trigger == Trigger.SpellCast || effect.Trigger == Trigger.SpellHit)
                                {
                                    childTrigger = 1f / Rotation.GetSpellCastsPerSec(calc);
                                }

                                statsAverage.Accumulate(childEffect.Stats,
                                                        effect.GetAverageUptime(0.0f, 1.0f)
                                                        * childEffect.GetAverageStackSize(childTrigger, 1f, 1.5f, effect.Duration));
                            }
                        }
                        else if (effect.Trigger == Trigger.HolyLightCast)
                        {
                            trigger = 1f / Rotation.GetHolyLightCastsPerSec(calc);
                        }
                        else
                        {
                            continue;
                        }
                    }
                    statsAverage.Accumulate(effect.GetAverageStats(trigger, 1f, 1.5f, fightLength));
                }
                statsAverage.ManaRestore *= fightLength;
                statsAverage.Healed      *= fightLength;

                stats = statsBaseGear + statsBuffs + statsRace + statsAverage;
                ConvertRatings(stats, talents, calcOpts);
            }

            return(stats);
        }
        public Stats getSpecialEffects(CalculationOptionsTankDK calcOpts, SpecialEffect effect)
        {
            Stats    statsAverage = new Stats();
            Rotation rRotation    = calcOpts.m_Rotation;

            if (effect.Trigger == Trigger.Use)
            {
                if (calcOpts.bUseOnUseAbilities == true)
                {
                    statsAverage.Accumulate(effect.GetAverageStats());
                }
            }
            else
            {
                float trigger             = 0f;
                float chance              = effect.Chance;
                float duration            = effect.Duration;
                float unhastedAttackSpeed = 2f;
                switch (effect.Trigger)
                {
                case Trigger.MeleeCrit:
                case Trigger.PhysicalCrit:
                    trigger             = (1f / rRotation.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f);
                    chance              = combatTable.physCrits * effect.Chance;
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

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

                case Trigger.CurrentHandHit:
                case Trigger.MainHandHit:
                    trigger             = (1f / rRotation.getMeleeSpecialsPerSecond()) + (combatTable.MH.hastedSpeed != 0 ? 1f / combatTable.MH.hastedSpeed : 0.5f);
                    chance              = effect.Chance * (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial));
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

                case Trigger.OffHandHit:
                    trigger             = (1f / rRotation.getMeleeSpecialsPerSecond()) + (combatTable.OH.hastedSpeed != 0 ? 1f / combatTable.OH.hastedSpeed : 0.5f);
                    chance              = effect.Chance * (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial));
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    break;

                case Trigger.DamageDone:
                case Trigger.DamageOrHealingDone:
                    trigger             = (1f / rRotation.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f);
                    unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f);
                    chance = effect.Chance * (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial));
                    break;

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

                case Trigger.DamageSpellCrit:
                case Trigger.SpellCrit:
                    trigger = 1f / rRotation.getSpellSpecialsPerSecond();
                    chance  = combatTable.spellCrits * effect.Chance;
                    break;

                case Trigger.DoTTick:
                    trigger = (rRotation.BloodPlague + rRotation.FrostFever) / 3;
                    break;

                case Trigger.DamageTaken:
                case Trigger.DamageTakenPhysical:
                    trigger             = calcOpts.BossAttackSpeed;
                    chance             *= 1f - (stats.Dodge + stats.Parry + stats.Miss);
                    unhastedAttackSpeed = calcOpts.BossAttackSpeed;
                    break;

                case Trigger.DamageTakenMagical:
                    trigger = calcOpts.IncomingFromMagicFrequency;
                    break;

                //////////////////////////////////
                // DK specific triggers:
                case Trigger.BloodStrikeHit:
                case Trigger.HeartStrikeHit:
                    trigger = rRotation.curRotationDuration / (rRotation.BloodStrike + rRotation.HeartStrike);
                    break;

                case Trigger.PlagueStrikeHit:
                    trigger = rRotation.curRotationDuration / rRotation.PlagueStrike;
                    break;

                case Trigger.RuneStrikeHit:
                    trigger = rRotation.curRotationDuration / rRotation.RuneStrike;
                    break;

                case Trigger.IcyTouchHit:
                    trigger = rRotation.curRotationDuration / rRotation.IcyTouch;
                    break;

                case Trigger.DeathStrikeHit:
                    trigger = rRotation.curRotationDuration / rRotation.DeathStrike;
                    break;

                case Trigger.ObliterateHit:
                    trigger = rRotation.curRotationDuration / rRotation.Obliterate;
                    break;

                case Trigger.ScourgeStrikeHit:
                    trigger = rRotation.curRotationDuration / rRotation.ScourgeStrike;
                    break;

                case Trigger.FrostFeverHit:
                    // Icy Talons triggers off this.
                    trigger = rRotation.curRotationDuration / rRotation.IcyTouch;
                    if (character.DeathKnightTalents.GlyphofHowlingBlast)
                    {
                        trigger += rRotation.curRotationDuration / rRotation.HowlingBlast;
                    }
                    break;
                }
                if (!float.IsInfinity(trigger) && !float.IsNaN(trigger))
                {
                    if (effect.UsesPPM())
                    {
                        // If effect.chance < 0 , then it's using PPM.
                        // Let's get the duration * how many times it procs per min:
                        float UptimePerMin = 0;
                        float fWeight      = 0;
                        if (duration == 0) // Duration of 0 means that it's a 1 time effect that procs every time the proc comes up.
                        {
                            fWeight = Math.Abs(effect.Chance) / 60;
                        }
                        else
                        {
                            UptimePerMin = duration * Math.Abs(effect.Chance);
                            fWeight      = UptimePerMin / 60;
                        }
                        statsAverage.Accumulate(effect.Stats, fWeight);
                    }
                    else
                    {
                        effect.AccumulateAverageStats(statsAverage, trigger, chance, unhastedAttackSpeed, calcOpts.FightLength * 60);
                    }
                }
            }
            return(statsAverage);
        }
        private void CalcHasteAndManaProcs()
        {
            float nonProcHaste = StatUtils.CalcSpellHaste(PreProcStats);

            if (Options.NoProcs)
            {
                WeightedStat staticHaste = new WeightedStat();
                staticHaste.Chance = 1f;
                staticHaste.Value  = nonProcHaste;
                Haste = new List <WeightedStat> {
                    staticHaste
                };
                AvgHaste = nonProcHaste;
                return;
            }

            // the trigger rates are all guestimates at this point, since the
            // real values depend on haste (which obviously has not been
            // finalized yet)
            Dictionary <int, float> periods
                = new Dictionary <int, float>();
            Dictionary <int, float> chances
                = new Dictionary <int, float>();
            float corruptionPeriod = 0f;

            if (Options.GetActiveRotation().Contains("Corruption"))
            {
                corruptionPeriod = 3.1f;
                if (Talents.GlyphQuickDecay)
                {
                    corruptionPeriod /= nonProcHaste;
                }
            }
            PopulateTriggers(
                periods,
                chances,
                CalculationsWarlock.AVG_UNHASTED_CAST_TIME / nonProcHaste
                + Options.Latency,
                1 / 1.5f,
                corruptionPeriod,
                1f);

            // calculate the haste procs
            Haste = new List <WeightedStat>();
            WeightedStat[] percentages
                = GetUptimes(
                      Stats,
                      periods,
                      chances,
                      s => s.SpellHaste,
                      (a, b, c, d, e, f, g, h)
                      => SpecialEffect
                      .GetAverageCombinedUptimeCombinationsMultiplicative(
                          a, b, c, d, e, f, g, h));
            WeightedStat[] ratings
                = GetUptimes(
                      Stats,
                      periods,
                      chances,
                      s => s.HasteRating,
                      (a, b, c, d, e, f, g, h)
                      => SpecialEffect.GetAverageCombinedUptimeCombinations(
                          a, b, c, d, e, f, g, h));
            for (int p = percentages.Length, f = 0; --p >= 0;)
            {
                if (percentages[p].Chance == 0)
                {
                    continue;
                }

                for (int r = ratings.Length; --r >= 0; ++f)
                {
                    if (ratings[r].Chance == 0)
                    {
                        continue;
                    }
                    WeightedStat s = new WeightedStat();
                    s.Chance = percentages[p].Chance * ratings[r].Chance;
                    s.Value
                        = (1 + percentages[p].Value)
                          * (1 + StatConversion.GetSpellHasteFromRating(
                                 ratings[r].Value + Stats.HasteRating))
                          * (1 + Stats.SpellHaste);
                    Haste.Add(s);
                    AvgHaste += s.Chance * s.Value;
                }
            }

            // calculate mana procs
            Stats procStats = new Stats();

            foreach (SpecialEffect effect in Stats.SpecialEffects())
            {
                if (!periods.ContainsKey((int)effect.Trigger))
                {
                    continue;
                }

                Stats proc = effect.GetAverageStats(
                    periods[(int)effect.Trigger],
                    chances[(int)effect.Trigger],
                    CalculationsWarlock.AVG_UNHASTED_CAST_TIME,
                    Options.Duration);
                if (proc.ManaRestore > 0)
                {
                    proc.ManaRestore *= Options.Duration;
                }
                procStats.Accumulate(proc);
            }
            Stats.Mana        += procStats.Mana;
            Stats.ManaRestore += procStats.ManaRestore;
            Stats.ManaRestoreFromBaseManaPPM
                += procStats.ManaRestoreFromBaseManaPPM;
            Stats.ManaRestoreFromMaxManaPerSecond
                      += procStats.ManaRestoreFromMaxManaPerSecond;
            Stats.Mp5 += procStats.Mp5;
        }
        private StatsWarrior GetSpecialEffectStats(Player player)
        {
            StatsWarrior statsSpecialEffects = new StatsWarrior();

            Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>();
            Dictionary<Trigger, float> triggerChances   = new Dictionary<Trigger, float>();

            player.DefendModel = new DefendModel(player);
            player.AttackModel = new AttackModel(player, AttackModelMode.Optimal);

            float effectiveMasteryRating        = Lookup.MaxEffectiveMasteryRating(player);
            float effectiveBuffedMasteryRating  = effectiveMasteryRating * (1.0f - 10.0f / player.CalcOpts.ShieldBlockInterval) +
                                                    Lookup.MaxEffectiveBuffedMasteryRating(player) * (10.0f / player.CalcOpts.ShieldBlockInterval);
            
            triggerIntervals[Trigger.Use]                   = 0.0f;
            triggerIntervals[Trigger.MeleeAttack]           = player.AttackModel.WeaponAttacksPerSecond;
            triggerIntervals[Trigger.MeleeHit]              = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.MeleeCrit]             = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.PhysicalHit]           = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.PhysicalAttack]        = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.PhysicalCrit]          = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.ExecuteHit]            = triggerIntervals[Trigger.MeleeAttack];
            triggerIntervals[Trigger.DoTTick]               = (player.Talents.DeepWounds > 0) ? 2.0f : 0.0f;
            triggerIntervals[Trigger.DamageDone]            = triggerIntervals[Trigger.MeleeAttack] + triggerIntervals[Trigger.DoTTick];
            triggerIntervals[Trigger.DamageOrHealingDone]   = triggerIntervals[Trigger.DamageDone];
            triggerIntervals[Trigger.DamageTaken]           = 1.0f / player.DefendModel.AttackerSwingsPerSecond;
            triggerIntervals[Trigger.DamageAvoided]         = triggerIntervals[Trigger.DamageTaken];
            triggerIntervals[Trigger.DamageParried]         = triggerIntervals[Trigger.DamageTaken];
            triggerIntervals[Trigger.DamageTakenPutsMeBelow35PercHealth] = triggerIntervals[Trigger.DamageTaken];
            triggerIntervals[Trigger.ShieldBlock]           = 60f - (player.Talents.ShieldMastery * 10f);

            triggerChances[Trigger.Use]                     = 1.0f;
            triggerChances[Trigger.MeleeAttack]             = 1.0f;
            triggerChances[Trigger.MeleeHit]                = player.AttackModel.HitsPerSecond / player.AttackModel.WeaponAttacksPerSecond;
            triggerChances[Trigger.MeleeCrit]               = player.AttackModel.CritsPerSecond / player.AttackModel.WeaponAttacksPerSecond;
            triggerChances[Trigger.PhysicalAttack]          = 1.0f;
            triggerChances[Trigger.PhysicalHit]             = triggerChances[Trigger.MeleeHit];
            triggerChances[Trigger.PhysicalCrit]            = triggerChances[Trigger.MeleeCrit];
            triggerChances[Trigger.ExecuteHit]              = triggerChances[Trigger.MeleeHit];
            triggerChances[Trigger.DoTTick]                 = (player.Talents.DeepWounds > 0) ? 1.0f : 0.0f;
            triggerChances[Trigger.DamageDone]              = (player.AttackModel.HitsPerSecond + ((player.Talents.DeepWounds > 0) ? 2.0f : 0.0f))
                                                                / (player.AttackModel.WeaponAttacksPerSecond + ((player.Talents.DeepWounds > 0) ? 1.0f : 0.0f));
            triggerChances[Trigger.DamageOrHealingDone]     = triggerChances[Trigger.DamageDone];
            triggerChances[Trigger.DamageTaken]             = player.DefendModel.AttackerHitsPerSecond / player.DefendModel.AttackerSwingsPerSecond;
            triggerChances[Trigger.DamageAvoided]           = player.DefendModel.DefendTable.AnyAvoid;
            triggerChances[Trigger.DamageParried]           = player.DefendModel.DefendTable.Parry;
            triggerChances[Trigger.DamageTakenPutsMeBelow35PercHealth] = triggerChances[Trigger.DamageTaken] * 0.35f;
            triggerChances[Trigger.ShieldBlock]             = 1.0f;

            foreach (SpecialEffect effect in player.Stats.SpecialEffects())
            {
                if (RelevantTriggers.Contains(effect.Trigger))
                {
                    // Effective Mastery Capping on Large Proc Effects
                    if ((effect.Trigger == Trigger.Use && effect.Stats.MasteryRating > effectiveMasteryRating) || effect.Stats.MasteryRating > effectiveBuffedMasteryRating)
                    {
                        Stats cappedStats = new Stats();
                        cappedStats.Accumulate(effect.Stats);
                        // Assume Use Effects Bypass Shield Block Collision
                        if (effect.Trigger == Trigger.Use)
                            cappedStats.MasteryRating = effectiveMasteryRating;
                        else
                            cappedStats.MasteryRating = effectiveBuffedMasteryRating;
                        // calculate average up-time of this trinket
                        float averageUpTime = 0.0f;
                        if (effect.Trigger == Trigger.ExecuteHit)
                            averageUpTime = effect.GetAverageFactor(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer * (float)player.BossOpts.Under20Perc);
                        else
                            averageUpTime = effect.GetAverageFactor(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer);
                        // accumulate the capped stats from the trinket into our final stats
                        statsSpecialEffects.Accumulate(cappedStats, averageUpTime);
                    }
                    else
                    {
                        if (effect.Trigger == Trigger.ExecuteHit)
                            effect.AccumulateAverageStats(statsSpecialEffects, triggerIntervals, triggerChances, player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer * (float)player.BossOpts.Under20Perc);
                        else
                            effect.AccumulateAverageStats(statsSpecialEffects, triggerIntervals, triggerChances, player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer);
                    }
                }
            }

            // Base Stats
            statsSpecialEffects.Stamina = (float)Math.Floor(statsSpecialEffects.Stamina * (1.0f + player.Stats.BonusStaminaMultiplier));
            statsSpecialEffects.Strength = (float)Math.Floor(statsSpecialEffects.Strength * (1.0f + player.Stats.BonusStrengthMultiplier));
            statsSpecialEffects.Agility = (float)Math.Floor(statsSpecialEffects.Agility * (1.0f + player.Stats.BonusAgilityMultiplier));

            return statsSpecialEffects;
        }
        private float CalcRemainingProcs()
        {
            if (Options.NoProcs)
            {
                return(0f);
            }

            Dictionary <int, float> periods = new Dictionary <int, float>();
            Dictionary <int, float> chances = new Dictionary <int, float>();

            PopulateTriggers(periods, chances);

            float procdDamage = 0f;
            Stats procStats   = new Stats();

            foreach (SpecialEffect effect in Stats.SpecialEffects())
            {
                if (!periods.ContainsKey((int)effect.Trigger))
                {
                    continue;
                }

                Stats effectStats = effect.Stats;
                if (effectStats.ValkyrDamage > 0)
                {
                    SpellModifiers mods = new SpellModifiers();
                    mods.AddCritChance(.05f + Stats.SpellCritOnTarget);
                    mods.AddMultiplicativeMultiplier(
                        Stats.BonusHolyDamageMultiplier);
                    procdDamage
                        += CalcDamageProc(
                               effect,
                               effect.Stats.ValkyrDamage,
                               periods,
                               chances,
                               mods);
                }
                else if (
                    effectStats.ShadowDamage > 0 ||
                    effectStats.FireDamage > 0 ||
                    effectStats.NatureDamage > 0 ||
                    effectStats.HolyDamage > 0 ||
                    effectStats.FrostDamage > 0)
                {
                    SpellModifiers mods = new SpellModifiers();
                    mods.Accumulate(SpellModifiers);
                    if (Options.Imbue.Equals("Grand Firestone"))
                    {
                        mods.AddAdditiveDirectMultiplier(.01f);
                    }
                    if (effectStats.ShadowDamage > 0)
                    {
                        AddShadowModifiers(mods);
                    }
                    else if (effectStats.FireDamage > 0)
                    {
                        AddFireModifiers(mods);
                    }
                    procdDamage
                        += CalcDamageProc(
                               effect,
                               effectStats.ShadowDamage
                               + effectStats.FireDamage
                               + effectStats.NatureDamage
                               + effectStats.HolyDamage
                               + effectStats.FrostDamage,
                               periods,
                               chances,
                               mods);
                }
                else
                {
                    procStats.Accumulate(
                        CalcNormalProc(effect, periods, chances));
                }
            }

            procStats.HasteRating
                                    = procStats.SpellHaste
                                    = procStats.Mana
                                    = procStats.ManaRestore
                                    = procStats.ManaRestoreFromBaseManaPPM
                                    = procStats.ManaRestoreFromMaxManaPerSecond
                                    = procStats.Mp5
                                    = procStats.CritRating
                                    = procStats.SpellCrit
                                    = procStats.SpellCritOnTarget
                                    = procStats.PhysicalCrit
                                    = 0;
            Stats.Accumulate(procStats);

            return(procdDamage);
        }
Beispiel #36
0
        // this is now included in accumulating item stats
        /*public unsafe virtual void AccumulateEnchantsStats(Stats stats, Character character)
        {
            fixed (float* pRawAdditiveData = stats._rawAdditiveData, pRawMultiplicativeData = stats._rawMultiplicativeData, pRawNoStackData = stats._rawNoStackData)
            {
                stats.BeginUnsafe(pRawAdditiveData, pRawMultiplicativeData, pRawNoStackData);
                stats.AccumulateUnsafe(character.BackEnchant.Stats, true);
                stats.AccumulateUnsafe(character.ChestEnchant.Stats, true);
                stats.AccumulateUnsafe(character.FeetEnchant.Stats, true);
                stats.AccumulateUnsafe(character.Finger1Enchant.Stats, true);
                stats.AccumulateUnsafe(character.Finger2Enchant.Stats, true);
                stats.AccumulateUnsafe(character.HandsEnchant.Stats, true);
                stats.AccumulateUnsafe(character.HeadEnchant.Stats, true);
                stats.AccumulateUnsafe(character.LegsEnchant.Stats, true);
                stats.AccumulateUnsafe(character.ShouldersEnchant.Stats, true);
                if (character.MainHand != null &&
                    (character.MainHandEnchant.Slot == ItemSlot.OneHand ||
                    (character.MainHandEnchant.Slot == ItemSlot.TwoHand &&
                    character.MainHand.Slot == ItemSlot.TwoHand)))
                {
                    stats.AccumulateUnsafe(character.MainHandEnchant.Stats, true);
                }
                if (character.OffHand != null && IncludeOffHandInCalculations(character) &&
                    (
                        (
                            character.OffHandEnchant.Slot == ItemSlot.OneHand &&
                            (character.OffHand.Slot == ItemSlot.OneHand ||
                            character.OffHand.Slot == ItemSlot.OffHand) &&
                            character.OffHand.Type != ItemType.None &&
                            character.OffHand.Type != ItemType.Shield
                        )
                        ||
                        (
                            character.OffHandEnchant.Slot == ItemSlot.TwoHand &&
                            character.OffHand.Slot == ItemSlot.TwoHand
                        )
                        ||
                        (
                            character.OffHandEnchant.Slot == ItemSlot.OffHand &&
                            character.OffHand.Slot == ItemSlot.OffHand &&
                            character.OffHand.Type == ItemType.Shield
                        )
                    )
                   )
                {
                    stats.AccumulateUnsafe(character.OffHandEnchant.Stats, true);
                }
                stats.AccumulateUnsafe(character.RangedEnchant.Stats, true);
                stats.AccumulateUnsafe(character.WristEnchant.Stats, true);
                stats.EndUnsafe();
            }
        }*/

        /*public virtual Stats GetEnchantsStats(Character character)
        {
            Stats stats = new Stats();
            AccumulateEnchantsStats(stats, character);
            return stats;
        }*/

        public virtual void AccumulateBuffsStats(Stats stats, List<string> buffs)
        {
            foreach (string buffName in buffs)
                if (!string.IsNullOrEmpty(buffName))
                {
                    Buff buff = Buff.GetBuffByName(buffName);
                    if (buff != null)
                    {
                        stats.Accumulate(buff.Stats);
                    }
                }
        }
Beispiel #37
0
        public Stats GetCharacterStats(Character character, Item additionalItem, bool computeAverageStats, CharacterCalculationsHealadin calc)
        {
            CalculationOptionsHealadin calcOpts = character.CalculationOptions as CalculationOptionsHealadin;
            BossOptions    bossOpts             = character.BossOptions;
            PaladinTalents talents = character.PaladinTalents;

            float fightLength = bossOpts.BerserkTimer;

            Stats statsRace     = BaseStats.GetBaseStats(character.Level, CharacterClass.Paladin, character.Race);
            Stats statsBaseGear = GetItemStats(character, additionalItem);
            Stats statsBuffs    = GetBuffsStats(character, calcOpts);
            Stats stats         = statsBaseGear + statsBuffs + statsRace;

            ConvertRatings(stats, talents, calcOpts);
            if (computeAverageStats)
            {
                Stats statsAverage = new Stats();

                foreach (SpecialEffect effect in stats.SpecialEffects())
                {
                    float trigger = 0f;
                    if (calc == null)
                    {
                        trigger = 1.5f / calcOpts.Activity / (1f + stats.SpellHaste);
                        if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                        {
                            trigger *= stats.SpellCrit;
                        }
                    }
                    else
                    {
                        if (effect.Trigger == Trigger.HealingSpellCast || effect.Trigger == Trigger.HealingSpellHit)
                        {
                            trigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                        }
                        else if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                        {
                            trigger = 1f / Rotation.GetHealingCritsPerSec(calc);
                        }
                        else if (effect.Trigger == Trigger.SpellCast || effect.Trigger == Trigger.SpellHit)
                        {
                            trigger = 1f / Rotation.GetSpellCastsPerSec(calc);
                        }
                        else if (effect.Trigger == Trigger.DamageOrHealingDone)
                        {
                            trigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                        }
                        else if (effect.Trigger == Trigger.Use)
                        {
                            trigger = 0f;
                            foreach (SpecialEffect childEffect in effect.Stats.SpecialEffects())
                            {
                                float childTrigger = 0f;

                                if (effect.Trigger == Trigger.HealingSpellCast || effect.Trigger == Trigger.HealingSpellHit)
                                {
                                    childTrigger = 1f / Rotation.GetHealingCastsPerSec(calc);
                                }
                                else if (effect.Trigger == Trigger.HealingSpellCrit || effect.Trigger == Trigger.SpellCrit)
                                {
                                    childTrigger = 1f / Rotation.GetHealingCritsPerSec(calc);
                                }
                                else if (effect.Trigger == Trigger.SpellCast || effect.Trigger == Trigger.SpellHit)
                                {
                                    childTrigger = 1f / Rotation.GetSpellCastsPerSec(calc);
                                }

                                statsAverage.Accumulate(childEffect.Stats,
                                                        effect.GetAverageUptime(0.0f, 1.0f)
                                                        * childEffect.GetAverageStackSize(childTrigger, 1f, 1.5f, effect.Duration));
                            }
                        }
                        else
                        {
                            continue;
                        }
                    }
                    statsAverage.Accumulate(effect.GetAverageStats(trigger, 1f, 1.5f, fightLength));
                }
                statsAverage.ManaRestore *= fightLength;
                statsAverage.Healed      *= fightLength;
                statsAverage.HealedPerSP *= fightLength;

                stats = statsBaseGear + statsBuffs + statsRace + statsAverage;
                ConvertRatings(stats, talents, calcOpts);
            }

            #region Set Bonuses
            int T11Count;
            character.SetBonusCount.TryGetValue("Reinforced Sapphirium Regalia", out T11Count);
            stats.BonusCritChanceDeathCoil   = 0; // using this for Holy Light crit bonus, for now
            stats.BonusCritChanceFrostStrike = 0; // yes, I'm pure evil, using this to track 4T11
            if (T11Count >= 2)
            {
                // T11 Pally 2 piece bonus: add 5% crit to HL
                stats.BonusCritChanceDeathCoil = 0.05f;
            }
            if (T11Count >= 4)
            {
                // T11 Pally 4 piece bonus: 540 spirit buff for 6 secs after HS cast
                stats.BonusCritChanceFrostStrike = 1;
            }
            #endregion Set Bonuses

            return(stats);
        }
Beispiel #38
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);
        }
        private Stats GetCharacterStatsWithTemporaryEffects(Character character, Item additionalItem, /*out WeightedStat[] armorPenetrationUptimes,*/ out WeightedStat[] critRatingUptimes)
        {
            RogueTalents talents = character.RogueTalents;
            #region Spec determination
            int spec;
            int assCounter = 0, combatCounter = 0, subtCounter = 0;
            for (int i = 0; i <= 18; i++) assCounter += int.Parse(talents.ToString()[i].ToString());
            for (int i = 19; i <= 37; i++) combatCounter += int.Parse(talents.ToString()[i].ToString());
            for (int i = 38; i <= 56; i++) subtCounter += int.Parse(talents.ToString()[i].ToString());
            if (assCounter > combatCounter && assCounter > subtCounter) spec = 0;
            else if (combatCounter > subtCounter) spec = 1;
            else spec = 2;
            #endregion

            CalculationOptionsRogue calcOpts = character.CalculationOptions as CalculationOptionsRogue;
            BossOptions bossOpts = character.BossOptions;
            int targetLevel = bossOpts.Level;
            bool targetPoisonable = calcOpts.TargetPoisonable;

            Stats statsRace = BaseStats.GetBaseStats(character.Level, character.Class, character.Race);

            Stats statsItems = GetItemStats(character, additionalItem);
            Stats statsBuffs = GetBuffsStats(character, calcOpts);

            SpecialEffect LegendarySpecialEffect = new SpecialEffect(Trigger.MeleeHit, new Stats() { Agility = 2f }, 20f, 0f, 1f, 50);
            Stats statsSetBonus = new Stats();
            int LegendaryPartA;
            character.SetBonusCount.TryGetValue("Jaws of Retribution", out LegendaryPartA);
            if (LegendaryPartA >= 2)
            {
                statsSetBonus.AddSpecialEffect(LegendarySpecialEffect);
            }

            LegendarySpecialEffect = new SpecialEffect(Trigger.MeleeHit, new Stats() { Agility = 5f }, 20f, 0f, 1f, 50);
            int LegendaryPartB;
            character.SetBonusCount.TryGetValue("Maw of Oblivion", out LegendaryPartB);
            if (LegendaryPartB >= 2)
            {
                statsSetBonus.AddSpecialEffect(LegendarySpecialEffect);
            }

            // Needs work....
            LegendarySpecialEffect = new SpecialEffect(Trigger.MeleeHit, new Stats() { Agility = 17f }, 20f, 0f, 1f, 50);
            Stats LegendaryStats = new Stats();
            LegendaryStats.AddSpecialEffect(LegendarySpecialEffect);
            // Assume it resets every 45 seconds
            LegendarySpecialEffect = new SpecialEffect(Trigger.MeleeHit, LegendaryStats, 45f, 45f);
            int LegendaryPartC;
            character.SetBonusCount.TryGetValue("Fangs of the Father", out LegendaryPartC);
            if (LegendaryPartC >= 2)
            {
                statsSetBonus.AddSpecialEffect(LegendarySpecialEffect);
                LegendarySpecialEffect = new SpecialEffect(Trigger.MeleeHit, new Stats() { FangsoftheFather = 1f }, 6f, 45f);
                statsSetBonus.AddSpecialEffect(LegendarySpecialEffect);
            }

            Stats statsTalents = new Stats()
            {
                BonusAgilityMultiplier = (1f + (spec == 2 ? RV.Mastery.SinisterCallingMult : 0f)) * (1f + RV.LeatherSpecialization) - 1f,
                BonusAttackPowerMultiplier = (1f + (spec == 1 ? RV.Mastery.VitalityAPMult : 0f)) * (1f + RV.Talents.SavageCombatMult * talents.SavageCombat) - 1f,
                BonusCritChance = character.ActiveBuffs.Contains(Buff.GetBuffByName("Rampage")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Leader of the Pack")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Honor Among Thieves")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Terrifying Roar")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Furious Howl")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Elemental Oath")) ? 0f : RV.Talents.HonorAmongThievesCritBonus * talents.HonorAmongThieves,
                BonusDamageMultiplier = RV.Vendetta.DmgMult * talents.Vendetta * (RV.Vendetta.Duration * (talents.GlyphOfVendetta ? 1f + RV.Glyph.VendettaDurationMult : 1f)) / RV.Vendetta.CD +
                    talents.SanguinaryVein * RV.Talents.SanguinaryVein +
                    RV.Mastery.MasterOfSubtletyDmgMult * RV.Mastery.MasterOfSubtletyDuration / (RV.Vanish.CD - RV.Talents.ElusivenessVanishCDReduc * talents.Elusiveness) +
                    (talents.Preparation > 0 ? RV.Mastery.MasterOfSubtletyDmgMult * RV.Mastery.MasterOfSubtletyDuration / (RV.Talents.PreparationCD * talents.Preparation) : 0f),
                BonusPhysicalDamageMultiplier = character.ActiveBuffs.Contains(Buff.GetBuffByName("Ravage")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Acid Spit")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Brittle Bones")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Blood Frenzy")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Savage Combat")) ? 0f : RV.Talents.SavageCombatMult * talents.SavageCombat,
                BonusBleedDamageMultiplier = character.ActiveBuffs.Contains(Buff.GetBuffByName("Mangle")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Hemorrhage")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Blood Frenzy")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Gore")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Stampede")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Tendon Rip")) ? 0f : RV.Hemo.BleedDmgMult * talents.Hemorrhage,
                PhysicalHit = RV.Talents.PrecisionMult * talents.Precision,
                SpellHit = RV.Talents.PrecisionMult * talents.Precision,
            };

            Stats statsGearEnchantsBuffs = statsItems + statsBuffs + statsSetBonus;
            Stats statsTotal = statsRace + statsItems;
            statsTotal.Accumulate(statsBuffs);
            statsTotal.Accumulate(statsTalents);

            statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina * (1f + statsTotal.BonusStaminaMultiplier));
            statsTotal.Strength = (float)Math.Floor(statsTotal.Strength * (1f + statsTotal.BonusStrengthMultiplier));
            statsTotal.Agility = (float)Math.Floor((statsTotal.Agility - statsRace.Agility) * (1f + statsTotal.BonusAgilityMultiplier)) + statsRace.Agility;
            statsTotal.AttackPower += (statsTotal.Strength - RV.BaseStatCalcReduc / 2) + RV.APperAgi * (statsTotal.Agility - RV.BaseStatCalcReduc) + RV.BaseStatCalcReduc;
            statsTotal.AttackPower = (float)Math.Floor(statsTotal.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier));
            statsTotal.Health += (float)Math.Floor((statsTotal.Stamina - RV.BaseStatCalcReduc) * RV.HPPerStam + RV.BaseStatCalcReduc);
            statsTotal.Armor = (float)Math.Floor(statsTotal.Armor * (1f + statsTotal.BonusArmorMultiplier));
            statsTotal.NatureResistance += statsTotal.NatureResistanceBuff;
            statsTotal.FireResistance += statsTotal.FireResistanceBuff;
            statsTotal.FrostResistance += statsTotal.FrostResistanceBuff;
            statsTotal.ShadowResistance += statsTotal.ShadowResistanceBuff;
            statsTotal.ArcaneResistance += statsTotal.ArcaneResistanceBuff;

            float hasteBonus = (1f + StatConversion.GetPhysicalHasteFromRating(statsTotal.HasteRating, CharacterClass.Rogue)) * (1f + statsTotal.PhysicalHaste) - 1f;
            float speedBonus = (1f + hasteBonus) * (1f + RV.SnD.SpeedBonus) * (1f + (spec == 2 ? RV.Mastery.Executioner + RV.Mastery.ExecutionerPerMast * StatConversion.GetMasteryFromRating(statsTotal.MasteryRating) : 0f)) - 1f;
            float mHSpeed = (character.MainHand == null ? 2 : character.MainHand.Speed);
            float oHSpeed = (character.OffHand == null ? 2 : character.OffHand.Speed);
            float meleeHitInterval = 1f / ((mHSpeed + oHSpeed) / speedBonus);

            //To calculate the Poison hit interval only white attacks are taken into account, IP is assumed on the slowest and DP on the fastest weapon
            float dPPS = bossOpts.BerserkTimer / (Math.Min(mHSpeed, oHSpeed) / speedBonus) * RV.DP.Chance + (spec == 0 ? RV.Mastery.ImprovedPoisonsDPBonus : 0);
            float poisonHitInterval = 1 / (Math.Max(mHSpeed, mHSpeed) * RV.IP.Chance * (1f + RV.Mastery.ImprovedPoisonsIPFreqMult) / RV.IP.NormWeapSpeed + dPPS);
            
            float hitBonus = StatConversion.GetPhysicalHitFromRating(statsTotal.HitRating) + statsTotal.PhysicalHit;
            float spellHitBonus = StatConversion.GetSpellHitFromRating(statsTotal.HitRating) + statsTotal.SpellHit;
            float expertiseBonus = StatConversion.GetDodgeParryReducFromExpertise(StatConversion.GetExpertiseFromRating(statsTotal.ExpertiseRating) + statsTotal.Expertise);
            float chanceDodge = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[targetLevel - character.Level] - expertiseBonus);
            float chanceParry = 0f; //Math.Max(0f, StatConversion.WHITE_PARRY_CHANCE_CAP[targetLevel - character.Level] - expertiseBonus);
            float chanceMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[targetLevel - character.Level] - hitBonus);
            float chanceAvoided = chanceMiss + chanceDodge + chanceParry;
            float chancePoisonAvoided = Math.Max(0f, StatConversion.GetSpellMiss(character.Level - targetLevel, false) - spellHitBonus);

            float rawChanceCrit = StatConversion.GetPhysicalCritFromRating(statsTotal.CritRating)
                                + StatConversion.GetPhysicalCritFromAgility(statsTotal.Agility, CharacterClass.Rogue)
                                + statsTotal.PhysicalCrit
                                + StatConversion.NPC_LEVEL_CRIT_MOD[targetLevel - character.Level];
            float chanceCrit = rawChanceCrit * (1f - chanceAvoided);
            float chanceHit = 1f - chanceAvoided;

            Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>();
            Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>();
            triggerIntervals[Trigger.Use] = 0f;
            triggerIntervals[Trigger.MeleeHit] = meleeHitInterval;
            triggerIntervals[Trigger.PhysicalHit] = meleeHitInterval;
            triggerIntervals[Trigger.MeleeAttack] = meleeHitInterval;
            triggerIntervals[Trigger.PhysicalAttack] = meleeHitInterval;
            triggerIntervals[Trigger.MeleeCrit] = meleeHitInterval;
            triggerIntervals[Trigger.PhysicalCrit] = meleeHitInterval;
            triggerIntervals[Trigger.DoTTick] = 0f;
            triggerIntervals[Trigger.DamageDone] = meleeHitInterval / 2f;
            triggerIntervals[Trigger.DamageOrHealingDone] = meleeHitInterval / 2f; // Need to add Self-heals
            triggerIntervals[Trigger.SpellHit] = poisonHitInterval;
            triggerIntervals[Trigger.EnergyOrFocusDropsBelow20PercentOfMax] = 4f; // Approximating as 80% chance every 4 seconds. TODO: Actually model this
            triggerChances[Trigger.Use] = 1f;
            triggerChances[Trigger.MeleeHit] = Math.Max(0f, chanceHit);
            triggerChances[Trigger.PhysicalHit] = Math.Max(0f, chanceHit);
            triggerChances[Trigger.PhysicalAttack] = triggerChances[Trigger.MeleeAttack] = 1f;
            triggerChances[Trigger.MeleeCrit] = Math.Max(0f, chanceCrit);
            triggerChances[Trigger.PhysicalCrit] = Math.Max(0f, chanceCrit);
            triggerChances[Trigger.DoTTick] = 1f;
            triggerChances[Trigger.DamageDone] = 1f - chanceAvoided / 2f;
            triggerChances[Trigger.DamageOrHealingDone] = 1f - chanceAvoided / 2f; // Need to add Self-heals
            triggerChances[Trigger.SpellHit] = Math.Max(0f, 1f - chancePoisonAvoided);
            triggerChances[Trigger.EnergyOrFocusDropsBelow20PercentOfMax] = 0.80f; // Approximating as 80% chance every 4 seconds. TODO: Actually model this

            // Handle Trinket procs
            Stats statsProcs = new Stats();
            foreach (SpecialEffect effect in statsTotal.SpecialEffects(se => triggerIntervals.ContainsKey(se.Trigger)))
            {
                // JOTHAY's NOTE: The following is an ugly hack to add Recursive Effects to Cat
                // so Victor's Call and similar trinkets can be given more appropriate value
                if (effect.Trigger == Trigger.Use && effect.Stats._rawSpecialEffectDataSize == 1
                    && triggerIntervals.ContainsKey(effect.Stats._rawSpecialEffectData[0].Trigger))
                {
                    float upTime = effect.GetAverageUptime(triggerIntervals[effect.Trigger],
                        triggerChances[effect.Trigger], 1f, bossOpts.BerserkTimer);
                    statsProcs.Accumulate(effect.Stats._rawSpecialEffectData[0].GetAverageStats(
                        triggerIntervals[effect.Stats._rawSpecialEffectData[0].Trigger],
                        triggerChances[effect.Stats._rawSpecialEffectData[0].Trigger], 1f, bossOpts.BerserkTimer),
                        upTime);
                }
                else if (effect.Stats.MoteOfAnger > 0)
                {
                    // When in effect stats, MoteOfAnger is % of melee hits
                    // When in character stats, MoteOfAnger is average procs per second
                    statsProcs.MoteOfAnger = effect.Stats.MoteOfAnger * effect.GetAverageProcsPerSecond(triggerIntervals[effect.Trigger],
                        triggerChances[effect.Trigger], 1f, bossOpts.BerserkTimer) / effect.MaxStack;
                }
                else
                {
                    statsProcs.Accumulate(effect.GetAverageStats(triggerIntervals[effect.Trigger],
                        triggerChances[effect.Trigger], 1f, bossOpts.BerserkTimer),
                        1f);
                }
            }

            statsProcs.Agility += statsProcs.HighestStat + statsProcs.Paragon;
            statsProcs.Stamina = (float)Math.Floor(statsProcs.Stamina * (1f + statsTotal.BonusStaminaMultiplier));
            statsProcs.Strength = (float)Math.Floor(statsProcs.Strength * (1f + statsTotal.BonusStrengthMultiplier));
            statsProcs.Agility = (float)Math.Floor(statsProcs.Agility * (1f + statsTotal.BonusAgilityMultiplier));
            statsProcs.AttackPower += statsProcs.Strength + RV.APperAgi * statsProcs.Agility;
            statsProcs.AttackPower = (float)Math.Floor(statsProcs.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier));
            statsProcs.Health += (float)Math.Floor(statsProcs.Stamina * RV.HPPerStam);
            statsProcs.Armor = (float)Math.Floor(statsProcs.Armor * (1f + statsTotal.BonusArmorMultiplier));

            float HighestSecondaryStatValue = statsProcs.HighestSecondaryStat; // how much HighestSecondaryStat to add
            statsProcs.HighestSecondaryStat = 0f; // remove HighestSecondaryStat stat, since it's not needed
            if (statsTotal.CritRating > statsTotal.HasteRating && statsTotal.CritRating > statsTotal.MasteryRating) {
                statsProcs.CritRating += HighestSecondaryStatValue;
            } else if (statsTotal.HasteRating > statsTotal.CritRating && statsTotal.HasteRating > statsTotal.MasteryRating) {
                statsProcs.HasteRating += HighestSecondaryStatValue;
            } else /*if (statsTotal.MasteryRating > statsTotal.CritRating && statsTotal.MasteryRating > statsTotal.HasteRating)*/ {
                statsProcs.MasteryRating += HighestSecondaryStatValue;
            }

            //Agility is only used for crit from here on out; we'll be converting Agility to CritRating, 
            //and calculating CritRating separately, so don't add any Agility or CritRating from procs here.
            statsProcs.CritRating = statsProcs.Agility = 0;
            statsTotal.Accumulate(statsProcs);

            //Handle Crit procs
            critRatingUptimes = new WeightedStat[0];
            List<SpecialEffect> tempCritEffects = new List<SpecialEffect>();
            List<float> tempCritEffectIntervals = new List<float>();
            List<float> tempCritEffectChances = new List<float>();
            List<float> tempCritEffectScales = new List<float>();

            foreach (SpecialEffect effect in statsTotal.SpecialEffects(se => triggerIntervals.ContainsKey(se.Trigger) && (se.Stats.CritRating + se.Stats.Agility + se.Stats.HighestStat + se.Stats.Paragon) > 0))
            {
                tempCritEffects.Add(effect);
                tempCritEffectIntervals.Add(triggerIntervals[effect.Trigger]);
                tempCritEffectChances.Add(triggerChances[effect.Trigger]);
                tempCritEffectScales.Add(1f);
            }

            if (tempCritEffects.Count == 0)
            {
                critRatingUptimes = new WeightedStat[] { new WeightedStat() { Chance = 1f, Value = 0f } };
            }
            else if (tempCritEffects.Count == 1)
            { //Only one, add it to
                SpecialEffect effect = tempCritEffects[0];
                float uptime = effect.GetAverageUptime(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], 1f, bossOpts.BerserkTimer) * tempCritEffectScales[0];
                float totalAgi = (effect.Stats.Agility + effect.Stats.HighestStat + effect.Stats.Paragon) * (1f + statsTotal.BonusAgilityMultiplier);
                critRatingUptimes = new WeightedStat[] { new WeightedStat() { Chance = uptime, Value = 
                        effect.Stats.CritRating + StatConversion.GetCritFromAgility(totalAgi - 10,
                        CharacterClass.Rogue) * StatConversion.RATING_PER_PHYSICALCRIT },
                    new WeightedStat() { Chance = 1f - uptime, Value = 0f }};
            }
            else if (tempCritEffects.Count > 1)
            {
                List<float> tempCritEffectsValues = new List<float>();
                foreach (SpecialEffect effect in tempCritEffects)
                {
                    float totalAgi = (float)effect.MaxStack * (effect.Stats.Agility + effect.Stats.HighestStat + effect.Stats.Paragon) * (1f + statsTotal.BonusAgilityMultiplier);
                    tempCritEffectsValues.Add(effect.Stats.CritRating +
                        StatConversion.GetCritFromAgility(totalAgi - 10, CharacterClass.Rogue) *
                        StatConversion.RATING_PER_PHYSICALCRIT);
                }

                float[] intervals = new float[tempCritEffects.Count];
                float[] chances = new float[tempCritEffects.Count];
                float[] offset = new float[tempCritEffects.Count];
                for (int i = 0; i < tempCritEffects.Count; i++)
                {
                    intervals[i] = triggerIntervals[tempCritEffects[i].Trigger];
                    chances[i] = triggerChances[tempCritEffects[i].Trigger];
                }
                if (tempCritEffects.Count >= 2)
                {
                    offset[0] = calcOpts.TrinketOffset;
                }
                WeightedStat[] critWeights = SpecialEffect.GetAverageCombinedUptimeCombinations(tempCritEffects.ToArray(), intervals, chances, offset, tempCritEffectScales.ToArray(), 1f, bossOpts.BerserkTimer, tempCritEffectsValues.ToArray());
                critRatingUptimes = critWeights;
            }
            return statsTotal;
        }
        private void CalcHasteAndManaProcs()
        {
            float nonProcHaste = StatUtils.CalcSpellHaste(PreProcStats, CalcOpts.PlayerLevel);
            if (CalcOpts.NoProcs)
            {
                WeightedStat staticHaste = new WeightedStat();
                staticHaste.Chance = 1f;
                staticHaste.Value = nonProcHaste;
                Haste = new List<WeightedStat> { staticHaste };
                AvgHaste = nonProcHaste;
                return;
            }

            // the trigger rates are all guestimates at this point, since the
            // real values depend on haste (which obviously has not been
            // finalized yet)
            Dictionary<int, float> periods = new Dictionary<int, float>();
            Dictionary<int, float> chances = new Dictionary<int, float>();
            float corruptionPeriod = 0f;
            if (CalcOpts.GetActiveRotation().Contains("Corruption"))
            {
                corruptionPeriod = 3.1f / nonProcHaste;
            }
            PopulateTriggers(periods, chances, CalculationsWarlock.AVG_UNHASTED_CAST_TIME / nonProcHaste + CalcOpts.Latency, 1 / 1.5f, corruptionPeriod, 1f);

            // calculate the haste procs
            Haste = new List<WeightedStat>();
            WeightedStat[] percentages = GetUptimes(Stats, periods, chances, s => s.SpellHaste,
                    (a, b, c, d, e, f, g, h) => SpecialEffect.GetAverageCombinedUptimeCombinationsMultiplicative(a, b, c, d, e, f, g, h));
            WeightedStat[] ratings = GetUptimes(Stats, periods, chances, s => s.HasteRating,
                    (a, b, c, d, e, f, g, h) => SpecialEffect.GetAverageCombinedUptimeCombinations(a, b, c, d, e, f, g, h));
            for (int p = percentages.Length, f = 0; --p >= 0; )
            {
                if (percentages[p].Chance == 0)
                {
                    continue;
                }

                for (int r = ratings.Length; --r >= 0; ++f)
                {
                    if (ratings[r].Chance == 0)
                    {
                        continue;
                    }
                    WeightedStat s = new WeightedStat();
                    s.Chance = percentages[p].Chance * ratings[r].Chance;
                    s.Value =   (1 + percentages[p].Value) 
                              * (1 + StatUtils.GetSpellHasteFromRating(ratings[r].Value + Stats.HasteRating, CalcOpts.PlayerLevel))
                              * (1 + Stats.SpellHaste);
                    Haste.Add(s);
                    AvgHaste += s.Chance * s.Value;
                }
            }

            // calculate mana procs
            Stats procStats = new Stats();
            foreach (SpecialEffect effect in Stats.SpecialEffects())
            {
                if (!periods.ContainsKey((int)effect.Trigger))
                {
                    continue;
                }

                Stats proc = effect.GetAverageStats(periods[(int)effect.Trigger], chances[(int)effect.Trigger], CalculationsWarlock.AVG_UNHASTED_CAST_TIME, BossOpts.BerserkTimer);
                if (proc.ManaRestore > 0)
                {
                    proc.ManaRestore *= BossOpts.BerserkTimer;
                }
                procStats.Accumulate(proc);
            }
            Stats.Mana += procStats.Mana;
            Stats.ManaRestore += procStats.ManaRestore;
            Stats.ManaRestoreFromMaxManaPerSecond += procStats.ManaRestoreFromMaxManaPerSecond;
            Stats.Mp5 += procStats.Mp5;
        }
        /// <summary>Gets the total Stats of the Character</summary>
        /// <param name="character">
        /// The Character to get the total Stats of
        /// </param>
        /// <param name="additionalItem">
        /// An additional item to grant the Character the stats of (as if it
        /// were worn)
        /// </param>
        /// <returns>The total stats for the Character</returns>
        public override Stats GetCharacterStats(
            Character character, Item additionalItem)
        {
            WarlockTalents            talents = character.WarlockTalents;
            CalculationOptionsWarlock options
                = character.CalculationOptions as CalculationOptionsWarlock;
            Stats stats = BaseStats.GetBaseStats(character);

            // Items
            AccumulateItemStats(stats, character, additionalItem);

            // Buffs
            AccumulateBuffsStats(stats, character.ActiveBuffs);
            if (options.Imbue.Equals("Grand Spellstone"))
            {
                stats.HasteRating += 60f * (1f + talents.MasterConjuror * 1.5f);
            }
            else
            {
                Debug.Assert(options.Imbue.Equals("Grand Firestone"));
                stats.CritRating += 49f * (1f + talents.MasterConjuror * 1.5f);
            }
            ApplyPetsRaidBuff(
                stats, options.Pet, talents, character.ActiveBuffs);
            float aegis = 1f + talents.DemonicAegis * 0.10f;

            stats.SpellPower += 180f * aegis;                     // fel armor
            stats.SpellDamageFromSpiritPercentage += .3f * aegis; // fel armor

            // Talents
            float[] talentValues = { 0f, .04f, .07f, .1f };
            Stats   statsTalents = new Stats {
                //Demonic Embrace: increases your stamina by 4/7/10%
                BonusStaminaMultiplier = talentValues[talents.DemonicEmbrace],

                //Fel Vitality: increases your maximum Health & Mana by 1/2/3%
                BonusHealthMultiplier = talents.FelVitality * 0.01f,
                BonusManaMultiplier   = talents.FelVitality * 0.01f,

                //Suppression: increases your chance to hit with spells by
                //1/2/3%
                SpellHit = (talents.Suppression * 0.01f),

                //Demonic Tactics: increases your spell crit chance by
                //2/4/6/8/10%
                //Backlash: increases your spell crit chance by 1/2/3%
                BonusCritChance
                    = talents.DemonicTactics * 0.02f + talents.Backlash * 0.01f
            };

            if (talents.Eradication > 0)
            {
                talentValues = new float[] { 0f, .06f, .12f, .20f };
                statsTalents.AddSpecialEffect(
                    new SpecialEffect(
                        Trigger.CorruptionTick,
                        new Stats()
                {
                    SpellHaste = talentValues[talents.Eradication]
                },
                        6f,
                        0f,
                        .06f));
            }
            stats.Accumulate(statsTalents);
            stats.ManaRestoreFromMaxManaPerSecond
                = Math.Max(
                      stats.ManaRestoreFromMaxManaPerSecond,
                      .002f
                      * Spell.CalcUprate(
                          talents.ImprovedSoulLeech * .5f,
                          15f,
                          options.Duration * 1.1f));

            return(stats);
        }
        private Stats CalcCritProcs()
        {
            if (CalcOpts.NoProcs)
            {
                return new Stats();
            }

            Dictionary<int, float> periods = new Dictionary<int, float>();
            Dictionary<int, float> chances = new Dictionary<int, float>();
            PopulateTriggers(periods, chances);

            Stats procStats = new Stats();
            foreach (SpecialEffect effect in Stats.SpecialEffects())
            {
                if (!periods.ContainsKey((int)effect.Trigger))
                {
                    continue;
                }

                Stats proc = CalcNormalProc(effect, periods, chances);
                procStats.Accumulate(proc);
                if (effect.Trigger == Trigger.Use && !IsDoublePot(effect))
                {
                    ExtraCritAtMax += StatUtils.CalcSpellCrit(effect.Stats, BaseIntellect, CalcOpts.PlayerLevel)
                                    - StatUtils.CalcSpellCrit(proc, BaseIntellect, CalcOpts.PlayerLevel);
                }
            }
            return procStats;
        }
Beispiel #43
0
        public static void RenderStatsGraph(Graphics g, int graphWidth, int graphHeight, Character character, Stats[] statsList, Color[] colors, int scale, string explanatoryText, string calculation, Style style)
        {
            CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(character);
            float baseFigure = GetCalculationValue(baseCalc, calculation);

            g.SmoothingMode     = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            float graphOffset = graphWidth / 2.0f, graphStep = (graphWidth - 100) / 2.0f / scale;

            if (statsList.Length == 0 || statsList.Length > colors.Length)
            {
                return;                                                            // more than 12 elements for the array would run out of colours
            }
            float minDpsChange = 0f, maxDpsChange = 0f;

            PointF[][] points = new PointF[statsList.Length][];
            for (int index = 0; index < statsList.Length; index++)
            {
                Stats newStats = new Stats();
                points[index] = new PointF[2 * scale + 1];
                newStats.Accumulate(statsList[index], -scale - 1);
                for (int count = -scale; count <= scale; count++)
                {
                    newStats.Accumulate(statsList[index]);

                    CharacterCalculationsBase currentCalc = Calculations.GetCharacterCalculations(character, new Item()
                    {
                        Stats = newStats
                    }, false, false, false);
                    float currentFigure = GetCalculationValue(currentCalc, calculation);
                    float dpsChange     = currentFigure - baseFigure;
                    points[index][count + scale] = new PointF(graphOffset + count * graphStep, dpsChange);
                    if (dpsChange < minDpsChange)
                    {
                        minDpsChange = dpsChange;
                    }
                    if (dpsChange > maxDpsChange)
                    {
                        maxDpsChange = dpsChange;
                    }
                }
            }
            float DpsVariance = maxDpsChange - minDpsChange;

            if (DpsVariance == 0)
            {
                DpsVariance = 1;
            }
            for (int index = 0; index < statsList.Length; index++)
            {
                for (int count = -scale; count <= scale; count++)
                {
                    points[index][count + scale].Y = (int)((maxDpsChange - points[index][count + scale].Y) * (graphHeight - 48) / DpsVariance) + 20;
                }
                Brush statBrush = new SolidBrush(colors[index]);
                switch (style)
                {
                case Style.DpsWarr:
                    g.DrawLines(new Pen(statBrush, 3), points[index]);
                    break;

                case Style.Mage:
                    g.DrawLines(new Pen(statBrush, 1), points[index]);
                    break;
                }
            }

            RenderGrid(g, graphWidth, graphHeight, character, statsList, colors, scale, 1f, "F1", explanatoryText, calculation, style, minDpsChange, maxDpsChange, DpsVariance, true);
        }
        private float CalcRemainingProcs()
        {
            if (CalcOpts.NoProcs)
            {
                return 0f;
            }

            Dictionary<int, float> periods = new Dictionary<int, float>();
            Dictionary<int, float> chances = new Dictionary<int, float>();
            PopulateTriggers(periods, chances);

            float procdDamage = 0f;
            Stats procStats = new Stats();
            foreach (SpecialEffect effect in Stats.SpecialEffects())
            {
                if (!periods.ContainsKey((int)effect.Trigger))
                {
                    continue;
                }

                Stats effectStats = effect.Stats;
                if (effectStats.HolySummonedDamage > 0)
                {
                    SpellModifiers mods = new SpellModifiers();
                    mods.AddCritChance(.05f + Stats.SpellCritOnTarget);
                    mods.AddMultiplicativeMultiplier(Stats.BonusHolyDamageMultiplier);
                    procdDamage += CalcDamageProc(effect, effect.Stats.HolySummonedDamage, periods, chances, mods);
                }
                else if (
                         effectStats.ShadowDamage > 0
                      || effectStats.FireDamage > 0
                      || effectStats.NatureDamage > 0
                      || effectStats.HolyDamage > 0
                      || effectStats.FrostDamage > 0)
                {
                    SpellModifiers mods = new SpellModifiers();
                    mods.Accumulate(SpellModifiers);

                    if (effectStats.ShadowDamage > 0)
                    {
                        AddShadowModifiers(mods);
                    }
                    else if (effectStats.FireDamage > 0)
                    {
                        AddFireModifiers(mods);
                    }
                    procdDamage += CalcDamageProc(
                            effect,
                              effectStats.ShadowDamage
                            + effectStats.FireDamage
                            + effectStats.NatureDamage
                            + effectStats.HolyDamage
                            + effectStats.FrostDamage,
                            periods,
                            chances,
                            mods);
                }
                else
                {
                    procStats.Accumulate(CalcNormalProc(effect, periods, chances));
                }
            }

            procStats.HasteRating
                = procStats.SpellHaste
                = procStats.Mana
                = procStats.ManaRestore
                = procStats.ManaRestoreFromMaxManaPerSecond
                = procStats.Mp5
                = procStats.CritRating
                = procStats.SpellCrit
                = procStats.SpellCritOnTarget
                = procStats.PhysicalCrit
                = 0;
            Stats.Accumulate(procStats);

            return procdDamage;
        }
Beispiel #45
0
        public void UpdateScalingGraph(Character character, Stats[] statsList, Stats baseStat, bool requiresReferenceCalculations, Color[] colors, int scale, string explanatoryText, string calculation)
        {
            CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(character);

            if (statsList.Length == 0 || statsList.Length > colors.Length)
            {
                return;                                                            // more than 12 elements for the array would run out of colours
            }
            Point[][] points = new Point[statsList.Length][];
            // extract property data for relative stats calculations
            KeyValuePair <PropertyInfo, float>[] properties = new KeyValuePair <PropertyInfo, float> [statsList.Length];
            for (int index = 0; index < statsList.Length; index++)
            {
                var p = statsList[index].Values(x => x > 0);
                foreach (var kvp in p)
                {
                    properties[index] = kvp;
                }
                points[index] = new Point[2 * scale + 1];
            }
            float unit = 1f;
            var   bp   = baseStat.Values(x => x > 0);

            foreach (var kvp in bp)
            {
                unit = kvp.Value;
            }
            Chart.Series.Clear();
            for (int count = -scale; count <= scale; count++)
            {
                Stats newStats = new Stats();
                newStats.Accumulate(baseStat, count);
                Item item = new Item()
                {
                    Stats = newStats
                };
                if (requiresReferenceCalculations)
                {
                    Calculations.GetCharacterCalculations(character, item, true, false, false);
                }
                for (int index = 0; index < statsList.Length; index++)
                {
                    ComparisonCalculationBase currentCalc = CalculationsBase.GetRelativeStatValue(character, properties[index].Key, item, properties[index].Value);
                    float dpsChange = GetCalculationValue(currentCalc, calculation);
                    points[index][count + scale] = new Point(count * unit, dpsChange);
                }
            }
            for (int index = 0; index < statsList.Length; index++)
            {
                Style dataPointStyle = new Style(typeof(LineDataPoint));
                dataPointStyle.Setters.Add(new Setter(DataPoint.TemplateProperty, Resources["InvisibleDataPointTemplate"]));
                dataPointStyle.Setters.Add(new Setter(DataPoint.BackgroundProperty, new SolidColorBrush(colors[index])));
                Chart.Series.Add(new LineSeries()
                {
                    Title                = statsList[index].ToString(),
                    ItemsSource          = points[index],
                    IndependentValuePath = "X",
                    DependentValuePath   = "Y",
                    DataPointStyle       = dataPointStyle,
                });
            }
            Chart.Axes.Clear();
            Chart.Axes.Add(new LinearAxis()
            {
                Orientation   = AxisOrientation.X,
                Title         = "Stat Change",
                ShowGridLines = true,
            });
            Chart.Axes.Add(new LinearAxis()
            {
                Orientation   = AxisOrientation.Y,
                Title         = calculation,
                ShowGridLines = true,
            });
            // restore reference calculation
            if (requiresReferenceCalculations)
            {
                Stats newStats = new Stats();
                Item  item     = new Item()
                {
                    Stats = newStats
                };
                Calculations.GetCharacterCalculations(character, item, true, false, false);
            }
            orgDataDirty = true;
        }
Beispiel #46
0
 public float AccumulateAverageStats(Stats stats, float triggerInterval = 0.0f, float triggerChance = 1.0f, float attackSpeed = 3.0f, float fightDuration = 0.0f)
 {
     Stats.GenerateSparseData();
     float factor = GetAverageFactor(triggerInterval, triggerChance, attackSpeed, fightDuration);
     stats.Accumulate(Stats, factor);
     return factor;
 }
 //NOTE: This is currently unused, because it doesn't account for procs which are both mitigation and survival (ie armor and agility procs)
 private void AccumulateSpecialEffect(Stats statsProcs, Stats statsEffect, float effectUptime, float temporarySurvivalScale)
 {
     if (temporarySurvivalScale != 1f && statsEffect.Armor + statsEffect.Health + statsEffect.Stamina > 0f)
     {
         //Subject to Temporary Survival scaling
         if (temporarySurvivalScale < 1f)
         {
             statsProcs.Accumulate(statsEffect, effectUptime * temporarySurvivalScale);
         }
         else
         {
             statsProcs.Accumulate(statsEffect, 1f - ((2f - temporarySurvivalScale) * (1f - effectUptime)));
         }
     }
     else
     {
         statsProcs.Accumulate(statsEffect, effectUptime);
     }
 }
Beispiel #48
0
        public override Stats GetCharacterStats(Character character, Item additionalItem)
        {
            CalculationOptionsCat calcOpts = character.CalculationOptions as CalculationOptionsCat ?? new CalculationOptionsCat();
            DruidTalents talents = character.DruidTalents;
            BossOptions bossOpts = character.BossOptions;

            bool hasCritBuff = false;
            foreach (Buff buff in character.ActiveBuffs)
            {
                if (buff.Group == "Critical Strike Chance")
                {
                    hasCritBuff = true;
                    break;
                }
            }

            StatsCat statsTotal = new StatsCat()
            {
                BonusAgilityMultiplier = Character.ValidateArmorSpecialization(character, ItemType.Leather) ? 0.05f : 0f,
                BonusAttackPowerMultiplier = (1f + 0.25f) * (1f + talents.HeartOfTheWild * 0.1f / 3f) - 1f,
                //BonusBleedDamageMultiplier = (character.ActiveBuffsConflictingBuffContains("Bleed Damage") ? 0f : 0.3f),

                MovementSpeed = 0.15f * talents.FeralSwiftness,
                RavageCritChanceOnTargetsAbove80Percent = 0.25f * talents.PredatoryStrikes,
                FurySwipesChance = 0.05f * talents.FurySwipes,
                CPOnCrit = 0.5f * talents.PrimalFury,
                FerociousBiteDamageMultiplier = 0.05f* talents.FeralAggression,
                EnergyOnTigersFury = 20f * talents.KingOfTheJungle,
                FreeRavageOnFeralChargeChance = 0.5f * talents.Stampede,
                PhysicalCrit = (hasCritBuff ? 0f : 0.05f * talents.LeaderOfThePack)
                                + (talents.MasterShapeshifter == 1 ? 0.04f : 0f),
                MaxEnergyOnTigersFuryBerserk = 10f * talents.PrimalMadness,
                RipRefreshChanceOnFerociousBiteOnTargetsBelow25Percent = 0.5f * talents.BloodInTheWater,
                ShredDamageMultiplier = talents.RendAndTear * 0.2f / 3f,

                BonusBerserkDuration = (talents.Berserk > 0 ? 15f + (talents.GlyphOfBerserk ? 10f : 0f) : 0f),
                MangleDamageMultiplier = talents.GlyphOfMangle ? 0.1f : 0f,
                SavageRoarDamageMultiplierIncrease = talents.GlyphOfSavageRoar ? 0.05f : 0f,
                FeralChargeCatCooldownReduction = talents.GlyphOfFeralCharge ? 2f : 0f,
//                FerociousBiteMaxExtraEnergyReduction = talents.GlyphOfFerociousBite ? 25f : 0f,
            };

            #region Set Bonuses
            int PvPCount;
            character.SetBonusCount.TryGetValue("Gladiator's Sanctuary", out PvPCount);
            if (PvPCount >= 2)
            {
                statsTotal.Agility += 70f;
                statsTotal.Resilience += 400f;
            }
            if (PvPCount >= 4)
            {
                // the 15% movement speed is only outdoors which most dungeons are not
                statsTotal.Agility += 90f;
            }
            int T11Count;
            character.SetBonusCount.TryGetValue("Stormrider's Battlegarb", out T11Count);
            if (T11Count >= 2) {
                statsTotal.BonusDamageMultiplierRakeTick = (1f + statsTotal.BonusDamageMultiplierRakeTick) * (1f + 0.10f) - 1f;
                statsTotal.BonusDamageMultiplierLacerate = (1f + statsTotal.BonusDamageMultiplierLacerate) * (1f + 0.10f) - 1f;
            }
            if (T11Count >= 4) {
                statsTotal.AddSpecialEffect(new SpecialEffect(Trigger.MangleCatAttack,
                    new Stats() { BonusAttackPowerMultiplier = 0.01f, },
                    30, 0, 1f, 3));
                statsTotal.Tier_11_4pc = true;
                //statsTotal.BonusSurvivalInstinctsDurationMultiplier = (1f + statsTotal.BonusSurvivalInstinctsDurationMultiplier) * (1f + 0.50f) - 1f;
            }
            int T12Count;
            character.SetBonusCount.TryGetValue("Obsidian Arborweave Battlegarb", out T12Count);
            if (T12Count >= 2)
            {
                statsTotal.MangleDamageMultiplier = (1f + statsTotal.MangleDamageMultiplier) * (1f + 0.10f) - 1f;
                statsTotal.ShredDamageMultiplier = (1f + statsTotal.ShredDamageMultiplier) * (1f + 0.10f) - 1f;
            }
            if (T12Count >= 4)
            {
                // Assume that all Finishing Moves are used at 5 combo points 
                // SpecialEffect primary = new SpecialEffect(Trigger.Berserk, new StatsCat(), statsTotal.BonusBerserkDuration, 180f);
                // SpecialEffect secondary = new SpecialEffect(Trigger.FinishingMove,
                //     new StatsCat() { BonusBerserkDuration = 2f, },
                //     0, 5f, 1f);
                // primary.Stats.AddSpecialEffect(secondary);
                // statsTotal.AddSpecialEffect(primary); 
                statsTotal.Tier_12_4pc = true;
            }
            statsTotal.Tier_13_2_piece = false;
            statsTotal.Tier_13_4_piece = false;
            int T13Count;
            character.SetBonusCount.TryGetValue("Deep Earth Battlegarb", out T13Count);
            if (T13Count >= 2)
            {
                // Your Blood in the Water talent now causes Ferocious Bite to refresh the duration of your Rip on targets with 60% or less health.
                statsTotal.Tier_13_2_piece = true;
            }
            if (T13Count >= 4)
            {
                // Your Stampede talent now grants two charges after using Feral Charge (Cat).
                statsTotal.Tier_13_4_piece = true;
            }
            #endregion

            statsTotal.Accumulate(BaseStats.GetBaseStats(character.Level, character.Class, character.Race, BaseStats.DruidForm.Cat));
            statsTotal.Accumulate(GetItemStats(character, additionalItem));
            AccumulateBuffsStats(statsTotal, character.ActiveBuffs);

            statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina * (1f + statsTotal.BonusStaminaMultiplier));
            statsTotal.Strength = (float)Math.Floor(statsTotal.Strength * (1f + statsTotal.BonusStrengthMultiplier));
            statsTotal.Agility = (float)Math.Floor(statsTotal.Agility * (1f + statsTotal.BonusAgilityMultiplier));
            statsTotal.AttackPower += statsTotal.Strength * 1f + (statsTotal.Agility * 2f - 20f); //-20 to account for the first 20 str and first 20 agi only giving 1ap each
            statsTotal.AttackPower = (float)Math.Floor(statsTotal.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier));
            statsTotal.Health += (float)Math.Floor((statsTotal.Stamina - 20f) * 14f + 20f);
            statsTotal.Health = (float)Math.Floor(statsTotal.Health * (1f + statsTotal.BonusHealthMultiplier));
            statsTotal.Armor = (float)Math.Floor(statsTotal.Armor * (1f + statsTotal.BonusArmorMultiplier));
            statsTotal.NatureResistance += statsTotal.NatureResistanceBuff;
            statsTotal.FireResistance += statsTotal.FireResistanceBuff;
            statsTotal.FrostResistance += statsTotal.FrostResistanceBuff;
            statsTotal.ShadowResistance += statsTotal.ShadowResistanceBuff;
            statsTotal.ArcaneResistance += statsTotal.ArcaneResistanceBuff;

            int targetLevel = bossOpts.Level;
            float hasteBonus = StatConversion.GetPhysicalHasteFromRating(statsTotal.HasteRating, CharacterClass.Druid);
            hasteBonus = (1f + hasteBonus) * (1f + statsTotal.PhysicalHaste) - 1f;
            float meleeHitInterval = 1f / ((1f + hasteBonus) + 1f / (3.5f / hasteBonus));
            float hitBonus = StatConversion.GetPhysicalHitFromRating(statsTotal.HitRating) + statsTotal.PhysicalHit;
            float expertiseBonus = StatConversion.GetDodgeParryReducFromExpertise(StatConversion.GetExpertiseFromRating(statsTotal.ExpertiseRating, CharacterClass.Druid) + statsTotal.Expertise, CharacterClass.Druid);
            float chanceDodge = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[targetLevel - 85] - expertiseBonus);
            float chanceParry = 0f;
            float chanceMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[targetLevel - 85] - hitBonus);
            float chanceAvoided = chanceMiss + chanceDodge + chanceParry;

            float rawChanceCrit = StatConversion.GetPhysicalCritFromRating(statsTotal.CritRating)
                                + StatConversion.GetPhysicalCritFromAgility(statsTotal.Agility, CharacterClass.Druid)
                                + statsTotal.PhysicalCrit
                                + StatConversion.NPC_LEVEL_CRIT_MOD[targetLevel - 85];
            float chanceCrit = rawChanceCrit * (1f - chanceAvoided);
            float chanceHit = 1f - chanceAvoided;
            bool usesMangle = (!character.ActiveBuffsContains("Mangle") && !character.ActiveBuffsContains("Trauma"));

            Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>();
            Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>();
            triggerIntervals[Trigger.Use] = 0f;
            triggerIntervals[Trigger.MeleeAttack] = meleeHitInterval;
            triggerIntervals[Trigger.MeleeHit] = meleeHitInterval;
            triggerIntervals[Trigger.PhysicalHit] = meleeHitInterval;
            triggerIntervals[Trigger.PhysicalAttack] = meleeHitInterval;
            triggerIntervals[Trigger.MeleeCrit] = meleeHitInterval;
            triggerIntervals[Trigger.PhysicalCrit] = meleeHitInterval;
            triggerIntervals[Trigger.DoTTick] = 1.5f;
            triggerIntervals[Trigger.DamageDone] = meleeHitInterval / 2f;
            triggerIntervals[Trigger.DamageOrHealingDone] = meleeHitInterval / 2f; // Need to Add Self-Heals
            triggerIntervals[Trigger.RakeTick] = 3f;
            if (usesMangle) {
                triggerIntervals[Trigger.MangleCatHit] = 60f;
                triggerIntervals[Trigger.MangleCatAttack] = 60f;
            }
            triggerIntervals[Trigger.MangleCatOrShredHit] = usesMangle ? 3.76f : 3.87f;
            triggerIntervals[Trigger.MangleCatOrShredOrInfectedWoundsHit] = triggerIntervals[Trigger.MangleCatOrShredHit] / ((talents.InfectedWounds > 0) ? 2f : 1f);
            triggerIntervals[Trigger.EnergyOrFocusDropsBelow20PercentOfMax] = 4f; // doing 80% chance every 4 seconds per Astry
            triggerIntervals[Trigger.FinishingMove] = 9f; // Assume it takes 9 seconds between to perform a finishing move
            triggerIntervals[Trigger.Berserk] = 180f;
            triggerChances[Trigger.Use] = 1f;
            triggerChances[Trigger.MeleeAttack] = 1f;
            triggerChances[Trigger.MeleeHit] = Math.Max(0f, chanceHit);
            triggerChances[Trigger.PhysicalHit] = Math.Max(0f, chanceHit);
            triggerChances[Trigger.PhysicalAttack] = 1f;
            triggerChances[Trigger.MeleeCrit] = Math.Max(0f, chanceCrit);
            triggerChances[Trigger.PhysicalCrit] = Math.Max(0f, chanceCrit);
            triggerChances[Trigger.DoTTick] = 1f;
            triggerChances[Trigger.DamageDone] = 1f - chanceAvoided / 2f;
            triggerChances[Trigger.DamageOrHealingDone] = 1f - chanceAvoided / 2f; // Need to Add Self-Heals
            triggerChances[Trigger.RakeTick] = 1f;
            if (usesMangle) {
                triggerChances[Trigger.MangleCatAttack] = 1f;
                triggerChances[Trigger.MangleCatHit] = chanceHit;
            }
            triggerChances[Trigger.MangleCatOrShredHit] = chanceHit;
            triggerChances[Trigger.MangleCatOrShredOrInfectedWoundsHit] = chanceHit;
            triggerChances[Trigger.EnergyOrFocusDropsBelow20PercentOfMax] = 0.80f; // doing 80% chance every 4 seconds per Astry
            triggerChances[Trigger.FinishingMove] = 1f;
            triggerChances[Trigger.Berserk] = 1f;

            // Handle Trinket procs
            Stats statsProcs = new Stats();
            foreach (SpecialEffect effect in statsTotal.SpecialEffects(se => triggerIntervals.ContainsKey(se.Trigger)))
            {
                // JOTHAY's NOTE: The following is an ugly hack to add Recursive Effects to Cat
                // so Victor's Call and similar trinkets can be given more appropriate value
                if (effect.Trigger == Trigger.Use && effect.Stats._rawSpecialEffectDataSize == 1
                    && triggerIntervals.ContainsKey(effect.Stats._rawSpecialEffectData[0].Trigger))
                {
                    float upTime = effect.GetAverageUptime(triggerIntervals[effect.Trigger],
                        triggerChances[effect.Trigger], 1f, bossOpts.BerserkTimer);
                    statsProcs.Accumulate(effect.Stats._rawSpecialEffectData[0].GetAverageStats(
                        triggerIntervals[effect.Stats._rawSpecialEffectData[0].Trigger],
                        triggerChances[effect.Stats._rawSpecialEffectData[0].Trigger], 1f, bossOpts.BerserkTimer),
                        upTime);
                } else if (effect.Stats.MoteOfAnger > 0) {
                    // When in effect stats, MoteOfAnger is % of melee hits
                    // When in character stats, MoteOfAnger is average procs per second
                    statsProcs.MoteOfAnger = effect.Stats.MoteOfAnger * effect.GetAverageProcsPerSecond(triggerIntervals[effect.Trigger],
                        triggerChances[effect.Trigger], 1f, bossOpts.BerserkTimer) / effect.MaxStack;
                } else {
                    statsProcs.Accumulate(effect.GetAverageStats(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], 1f, bossOpts.BerserkTimer));
                }
            }

            statsProcs.Agility += statsProcs.HighestStat + statsProcs.Paragon;
            statsProcs.Stamina = (float)Math.Floor(statsProcs.Stamina * (1f + statsTotal.BonusStaminaMultiplier));
            statsProcs.Strength = (float)Math.Floor(statsProcs.Strength * (1f + statsTotal.BonusStrengthMultiplier));
            statsProcs.Agility = (float)Math.Floor(statsProcs.Agility * (1f + statsTotal.BonusAgilityMultiplier));
            statsProcs.AttackPower += statsProcs.Strength * 1f + statsProcs.Agility * 2f;
            statsProcs.AttackPower = (float)Math.Floor(statsProcs.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier));
            statsProcs.Health += (float)Math.Floor(statsProcs.Stamina * 10f);
            //statsProcs.Armor += 2f * statsProcs.Agility;
            statsProcs.Armor = (float)Math.Floor(statsProcs.Armor * (1f + statsTotal.BonusArmorMultiplier));
            if (statsProcs.HighestSecondaryStat > 0)
            {
                if (statsTotal.CritRating > statsTotal.HasteRating && statsTotal.CritRating > statsTotal.MasteryRating) {
                    statsProcs.CritRating += statsProcs.HighestSecondaryStat; // this will be invalidated after this, but I'm at least putting it in for now
                } else if (statsTotal.HasteRating > statsTotal.CritRating && statsTotal.HasteRating > statsTotal.MasteryRating) {
                    statsProcs.HasteRating += statsProcs.HighestSecondaryStat;
                } else if (statsTotal.MasteryRating > statsTotal.CritRating && statsTotal.MasteryRating > statsTotal.HasteRating) {
                    statsProcs.MasteryRating += statsProcs.HighestSecondaryStat;
                }
                statsProcs.HighestSecondaryStat = 0;
            }


            //Agility is only used for crit from here on out; we'll be converting Agility to CritRating, 
            //and calculating CritRating separately, so don't add any Agility or CritRating from procs here.
            statsProcs.CritRating = statsProcs.Agility = 0;
            statsTotal.Accumulate(statsProcs);

            //Handle Crit procs
            statsTotal.TemporaryCritRatingUptimes = new WeightedStat[0];
            List<SpecialEffect> tempCritEffects = new List<SpecialEffect>();
            List<float> tempCritEffectIntervals = new List<float>();
            List<float> tempCritEffectChances = new List<float>();
            List<float> tempCritEffectScales = new List<float>();

            foreach (SpecialEffect effect in statsTotal.SpecialEffects(se => triggerIntervals.ContainsKey(se.Trigger) && (se.Stats.CritRating + se.Stats.Agility + se.Stats.HighestStat + se.Stats.Paragon) > 0))
            {
                tempCritEffects.Add(effect);
                tempCritEffectIntervals.Add(triggerIntervals[effect.Trigger]);
                tempCritEffectChances.Add(triggerChances[effect.Trigger]);
                tempCritEffectScales.Add(1f);
            }

            if (tempCritEffects.Count == 0)
            {
                statsTotal.TemporaryCritRatingUptimes = new WeightedStat[] { new WeightedStat() { Chance = 1f, Value = 0f } };
            }
            else if (tempCritEffects.Count == 1)
            { //Only one, add it to
                SpecialEffect effect = tempCritEffects[0];
                float uptime = effect.GetAverageUptime(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], 1f, bossOpts.BerserkTimer) * tempCritEffectScales[0];
                float totalAgi = (float)effect.MaxStack * (effect.Stats.Agility + effect.Stats.HighestStat + effect.Stats.Paragon) * (1f + statsTotal.BonusAgilityMultiplier);
                statsTotal.TemporaryCritRatingUptimes = new WeightedStat[] { new WeightedStat() { Chance = uptime, Value = 
                            effect.Stats.CritRating + StatConversion.GetCritFromAgility(totalAgi,
                            CharacterClass.Druid) * StatConversion.RATING_PER_PHYSICALCRIT },
                        new WeightedStat() { Chance = 1f - uptime, Value = 0f }};
            }
            else if (tempCritEffects.Count > 1)
            {
                List<float> tempCritEffectsValues = new List<float>();
                foreach (SpecialEffect effect in tempCritEffects)
                {
                    float totalAgi = (float)effect.MaxStack * (effect.Stats.Agility + effect.Stats.HighestStat + effect.Stats.Paragon) * (1f + statsTotal.BonusAgilityMultiplier);
                    tempCritEffectsValues.Add(effect.Stats.CritRating +
                        StatConversion.GetCritFromAgility(totalAgi, CharacterClass.Druid) *
                        StatConversion.RATING_PER_PHYSICALCRIT);
                }

                float[] intervals = new float[tempCritEffects.Count];
                float[] chances = new float[tempCritEffects.Count];
                float[] offset = new float[tempCritEffects.Count];
                for (int i = 0; i < tempCritEffects.Count; i++)
                {
                    intervals[i] = triggerIntervals[tempCritEffects[i].Trigger];
                    chances[i] = triggerChances[tempCritEffects[i].Trigger];
                }
                if (tempCritEffects.Count >= 2)
                {
                    offset[0] = calcOpts.TrinketOffset;
                }
                WeightedStat[] critWeights = SpecialEffect.GetAverageCombinedUptimeCombinations(tempCritEffects.ToArray(), intervals, chances, offset,
                    tempCritEffectScales.ToArray(), 1f, bossOpts.BerserkTimer, tempCritEffectsValues.ToArray());
                statsTotal.TemporaryCritRatingUptimes = critWeights;
            }

            return statsTotal;
        }
        public override Stats GetCharacterStats(Character character, Item additionalItem)
        {
            PriestTalents talents = character.PriestTalents;

            Stats statsTotal = new Stats();
            
            Stats baseStats = BaseStats.GetBaseStats(character.Level, character.Class, character.Race);
            Stats itemStats = GetItemStats(character, additionalItem);
            Stats buffStats = GetBuffsStats(character, _calculationOptions);

            // Get the gear/enchants/buffs stats loaded in
            statsTotal.Accumulate(baseStats);
            statsTotal.Accumulate(itemStats);
            statsTotal.Accumulate(buffStats);

            Stats statsTalents = new Stats()
                                     {
                                         // we can only wear items that are cloth so we always have our specialization, even naked.
                                         BonusIntellectMultiplier = 0.05f,

                                         BonusShadowDamageMultiplier = (1 + 0.02f*talents.TwinDisciplines)*
                                                                       (1 + 0.02f*talents.TwistedFaith)*
                                                                       (1 + 0.15f*talents.Shadowform) - 1,

                                         BonusHolyDamageMultiplier = (1 + 0.02f*talents.TwinDisciplines) - 1,

                                         // this is the shadow priest model so they must have 'Shadow Power'
                                         BonusSpellPowerMultiplier = .15f,
                                     };

            statsTotal.Accumulate(statsTalents);
          
            statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina * (1 + statsTotal.BonusStaminaMultiplier));
            statsTotal.Intellect += (float)Math.Floor(itemStats.Intellect * statsTotal.BonusIntellectMultiplier);
            statsTotal.Spirit = (float) Math.Round(statsTotal.Spirit * (1 + statsTotal.BonusSpiritMultiplier));

            statsTotal.Health += (float)Math.Floor(StatConversion.GetHealthFromStamina(statsTotal.Stamina) * (1f + statsTotal.BonusHealthMultiplier));

            statsTotal.Mana = (float) Math.Round(statsTotal.Mana + StatConversion.GetManaFromIntellect(statsTotal.Intellect));
            statsTotal.Mana = (float) Math.Round(statsTotal.Mana*(1f + statsTotal.BonusManaMultiplier));
            
            statsTotal.SpellPower += statsTotal.Intellect - 10;

            float hasteFromRating = StatConversion.GetSpellHasteFromRating(statsTotal.HasteRating);
            float talentedHaste = (1 + hasteFromRating) * (1 + talents.Darkness * .01f) - 1;
            
            statsTotal.SpellHaste += character.Race == CharacterRace.Goblin ? talentedHaste*1.01f : talentedHaste;

            float baseBonus = (float) Math.Floor(baseStats.Spirit*statsTotal.BonusSpiritMultiplier);
            float itemBonus = (float) Math.Floor(itemStats.Spirit*statsTotal.BonusSpiritMultiplier);
            float spiritFromItemsAndEffects = baseBonus + itemBonus + itemStats.Spirit;
            float hitRatingFromSpirit = (0.5f * talents.TwistedFaith) * Math.Max(0f, spiritFromItemsAndEffects);
            
            statsTotal.HitRating += hitRatingFromSpirit;
            statsTotal.SpellHit += StatConversion.GetSpellHitFromRating(statsTotal.HitRating);

            // ignoring the base crit percentage here as the in-game tooltip says that the int -> crit conversion contains the base.
            float critFromInt = StatConversion.GetSpellCritFromIntellect(statsTotal.Intellect) + 0.012375f;
            float critFromRating = StatConversion.GetSpellCritFromRating(statsTotal.CritRating);
            
            statsTotal.SpellCrit = character.Race == CharacterRace.Worgen ? (critFromInt + critFromRating) + .01f : (critFromInt + critFromRating);
            
            // Armor
            statsTotal.Armor = statsTotal.Armor * (1f + statsTotal.BaseArmorMultiplier);
            statsTotal.BonusArmor = statsTotal.BonusArmor * (1f + statsTotal.BonusArmorMultiplier);
            statsTotal.Armor += statsTotal.BonusArmor;
            statsTotal.Armor = (float)Math.Round(statsTotal.Armor);

            return statsTotal;
        }
Beispiel #50
0
        public static Stats GetBaseStats(int level, CharacterClass characterClass, CharacterRace characterRace, DruidForm characterForm)
        {   // Health, Mana and some other things are same for every race.
            lock (syncLock)
            {
                #region Cache
                if (level == _lastLevel
                    && characterClass == _lastClass
                    && characterRace == _lastRace
                    && characterForm == _lastForm)
                    return _lastStats.Clone();
                _lastLevel = level;
                _lastClass = characterClass;
                _lastRace = characterRace;
                _lastForm = characterForm;
                #endregion

                Stats S = new Stats();
                #region Race, not class benefit
                // Most Level 85 Race and Class Stats come from:
                // http://code.google.com/p/simulationcraft/source/browse/branches/cataclysm/engine/sc_rating.cpp?r=6207
                // When they were still at 80 as of Jan 01st, 2011

                // From SimCraft
                Stats race = new Stats();
                switch (characterRace)
                {
                    // Alliance
                    case CharacterRace.Human:    race.Strength = 20; race.Agility = 20; race.Stamina = 20; race.Intellect = 20; race.Spirit = 20; break;
                    case CharacterRace.Dwarf:    race.Strength = 25; race.Agility = 16; race.Stamina = 21; race.Intellect = 19; race.Spirit = 19; break;
                    case CharacterRace.NightElf: race.Strength = 16; race.Agility = 24; race.Stamina = 20; race.Intellect = 20; race.Spirit = 20; break;
                    case CharacterRace.Gnome:    race.Strength = 15; race.Agility = 22; race.Stamina = 20; race.Intellect = 24; race.Spirit = 20; break;
                    case CharacterRace.Draenei:  race.Strength = 21; race.Agility = 17; race.Stamina = 20; race.Intellect = 20; race.Spirit = 22; break;
                    case CharacterRace.Worgen:   race.Strength = 23; race.Agility = 22; race.Stamina = 20; race.Intellect = 16; race.Spirit = 19; break;
                    // Horde
                    case CharacterRace.Orc:      race.Strength = 23; race.Agility = 17; race.Stamina = 21; race.Intellect = 17; race.Spirit = 22; break;
                    case CharacterRace.Undead:   race.Strength = 19; race.Agility = 18; race.Stamina = 20; race.Intellect = 18; race.Spirit = 25; break;
                    case CharacterRace.Tauren:   race.Strength = 25; race.Agility = 16; race.Stamina = 21; race.Intellect = 16; race.Spirit = 22; break;
                    case CharacterRace.Troll:    race.Strength = 21; race.Agility = 22; race.Stamina = 20; race.Intellect = 16; race.Spirit = 21; break;
                    case CharacterRace.BloodElf: race.Strength = 17; race.Agility = 22; race.Stamina = 20; race.Intellect = 23; race.Spirit = 18; break;
                    case CharacterRace.Goblin:   race.Strength = 17; race.Agility = 22; race.Stamina = 20; race.Intellect = 23; race.Spirit = 20; break;
                    default: { break; }
                };
                // From Chardev (85)
                //Class           Str Agi Sta Int Spi
                //Druid            76  69  86 136 153
                //Shaman          111  60 128 119 136
                //Death Knight    171 101 154  16  44
                //Hunter           60 178 119  77  88
                //Mage             17  26  43 187 175
                //Paladin         144  77 136  86  97
                //Priest           26  34  51 188 183
                //Rogue           102 186  94  26  53
                //Warlock          43  51  76 161 166
                //Warrior         169 103 153  17  44
                #endregion

                #region Base Stats
                #region All Classes
                S.Miss  = 0.05f;
                S.Block = 0.00f;
                S.Parry = 0.00f;
                #endregion
                switch (characterClass)
                {
                    #region Death Knight
                    case CharacterClass.DeathKnight:
                        Stats dk = new Stats() {
                            Strength = 171, Agility = 101, Stamina = 274, Intellect = 16, Spirit = 44,
                            Health = 43025f,
                            Dodge = 0.05f, Parry = 0.05f, Block = 0.00f,
                            PhysicalCrit = 0.0049f, AttackPower = 595f,
                        };
                        S.Accumulate(race);
                        S.Accumulate(dk);
                        break;
                    #endregion
                    #region Druid
                    case CharacterClass.Druid:
                        Stats druid = new Stats() {
                            Strength = 76, Agility = 69, Stamina = 86, Intellect = 136, Spirit = 153,
                            Health = 39533f, Mana = 18635f,
                            Dodge = 0.03758f, Parry = 0.05f, Block = 0.05f,
                            PhysicalCrit = 0.03192f, AttackPower = 613f,
                            SpellCrit = 0.0185f, Mp5 = 931f,
                        };
                        S.Accumulate(race);
                        S.Accumulate(druid);
                        switch (characterForm)
                        {
                            case DruidForm.Moonkin:
                            case DruidForm.Caster:
                                S.AttackPower = -10;
                                S.PhysicalCrit = 0.0743f;
                                S.Dodge = 0.0556970f; //??
                                break;
                            case DruidForm.Bear:
                                S.AttackPower = 255;
                                S.PhysicalCrit = 0.074755f;
                                S.Dodge = 0.0556970f;
                                S.BonusStaminaMultiplier = 0.2f;
                                break;
                            case DruidForm.Cat:
                                S.AttackPower = 235;
                                S.PhysicalCrit = 0.074755f;
                                S.Dodge = 0.0556970f;
                                break;
                            default:
                                break;
                        }
                        break;
                    #endregion
                    #region Hunter
                    case CharacterClass.Hunter:
                        Stats hun = new Stats() {
                            // Stats updated 8/19/2011 4.2 w/ Troll Hunter: Tsevon @ US-Dragonmaw w/ no spec.
                            Strength = 60,
                            Agility = 178,
                            Stamina = 119,
                            Intellect = 77,
                            Spirit = 88,
                            Health = 39037,
                            Dodge = 0.03758f, Parry = 0.05f, 
                            // This assumes ALL AP from full AP += AGI * 2
                            // So naked Hunter has 31 AP un accounted for.
                            // Naked troll, no gear, no spec, LW & Skinning.
                            PhysicalCrit = 0, AttackPower = 31f, RangedAttackPower = 31f,
                        };
                        S.Accumulate(race);
                        S.Accumulate(hun);
                        break;
                    #endregion
                    #region Mage
                    case CharacterClass.Mage:
                        Stats mag = new Stats() {
                            Strength = 17, Agility = 26, Stamina = 43, Intellect = 187, Spirit = 175,
                            Health = 36853f, Mana = 17138f,
                            Dodge = 0.03758f, Parry = 0.05f,
                        };
                        S.Accumulate(race);
                        S.Accumulate(mag);
                        break;
                    #endregion
                    #region Paladin
                    case CharacterClass.Paladin:
                        Stats pal = new Stats() {
                            Strength = 144, Agility = 77, Stamina = 136, Intellect = 86, Spirit = 97,
                            Health = 43285f, Mana = 23422,
                            Dodge = 0.05f, Parry = 0.05f, Block = 0.05f,
                            PhysicalCrit = 0.00652f, AttackPower = 235f,
                            SpellCrit = 0.033355f,
                        };
                        S.Accumulate(race);
                        S.Accumulate(pal);
                        break;
                    #endregion
                    #region Priest
                    case CharacterClass.Priest:
                        // added adjustments to the base race here because the math using the previous stats
                        // just don't work for the in game calculations on priest tests. 
                        // also unsure how these changes would effect other modules if moved up.
                        // adding or subracting from the priest stats don't work and throws all other class
                        // calculations off. 
                        switch (characterRace)
                        {
                            case CharacterRace.Human: race.Spirit = 19; break;
                            case CharacterRace.Gnome: race.Intellect = 23; break;
                            case CharacterRace.Goblin: race.Spirit = 18; break;
                        }
                        Stats pri = new Stats() {
                            Strength = 26, Agility = 34, Stamina = 51, Intellect = 169, Spirit = 178,
                            Health = 43285f, Mana = 20590f,
                            Dodge = 0.0337780f,
                            Parry = 0.05f,
                            PhysicalCrit = 0.027f, SpellCrit = 0.012375f,
                        };
                        pri.Mp5 = pri.Mana * 0.05f;     // Always 5% of base mana in regen.
                        S.Accumulate(race);
                        S.Accumulate(pri);
                        break;
                    #endregion
                    #region Rogue
                    case CharacterClass.Rogue:
                        Stats rog = new Stats() {
                            Strength = 102, Agility = 186, Stamina = 94, Intellect = 26, Spirit = 53,
                            Health = 40529f,
                            Dodge = 0.03758f, Parry = 0.05f,
                            PhysicalCrit = 0.03192f, AttackPower = 613f,
                        };
                        S.Accumulate(race);
                        S.Accumulate(rog);
                        break;
                    #endregion
                    #region Shaman
                    case CharacterClass.Shaman:
                        Stats sha = new Stats() {
                            Strength = 111, Agility = 60, Stamina = 128, Intellect = 119, Spirit = 136,
                            Health = 37037f, Mana = 23430f,
                            Dodge = 0.0193f, Parry = 0.05f, Block = 0.05f,
                            PhysicalCrit = 0.02910375f, AttackPower = 140f,
                            SpellCrit = 0.022057f, SpellPower = -10,
                        };
                        S.Accumulate(race);
                        S.Accumulate(sha);
                        break;
                    #endregion
                    #region Warlock
                    case CharacterClass.Warlock:
                        Stats warlock = new Stats() { Strength = 43, Agility = 51, Stamina = 76, Intellect = 153, Spirit = 161,
                            Health = 38184f, Mana = 20553f,
                            Dodge = 0.0238110f, Parry = 0.05f, Block = 0.05f,
                            PhysicalCrit = 0.026219999417663f, SpellCrit = 0.017000000923872f,
                        };
                        S.Accumulate(race);
                        S.Accumulate(warlock);
                        break;
                    #endregion
                    #region Warrior
                    case CharacterClass.Warrior:
                        Stats war = new Stats() {
                            Strength = 169, Agility = 103, Stamina = 153, Intellect = 17, Spirit = 44,
                            Health = 43285f,
                            Dodge = 0.05f, Parry = 0.05f, Block = 0.05f,
                            PhysicalCrit = 0.03192f, AttackPower = 613f,
                        };
                        S.Accumulate(race);
                        S.Accumulate(war);
                        break;
                    #endregion
                    #region No Class
                    default:
                        break;
                    #endregion
                }
                #endregion

                #region Racials
                // Resistance do not stack with other buffs. Until then I'll commenting them out
                if (characterRace == CharacterRace.Gnome)  //CATA: changed from 5% int to 5% mana
                {
                    // S.ArcaneResistance += 85f;
                    S.BonusManaMultiplier = 0.05f;
                    //S.BonusIntellectMultiplier = 0.05f;
                }
                else if (characterRace == CharacterRace.Human)
                {
                    S.BonusSpiritMultiplier = 0.03f;
                    // Patch 4.0.6+ changed from a 3 minute cooldown to 2 minute cooldown
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { PVPTrinket = 1 }, 0f, 120f));
                }
                else if (characterRace == CharacterRace.NightElf)
                {
                    // S.NatureResistance += 85f;
                    S.Miss += 0.02f;
                }
                else if (characterRace == CharacterRace.Dwarf)
                {
                    // S.FrostResistance += 85f;
                    // Armor +10% for 8 Sec.
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { BaseArmorMultiplier = .1f }, 8, 120));
                    // TODO: Add debuff removal.  Doesn't work on all bosses so not sure if we want to.

                }
                else if (characterRace == CharacterRace.Draenei)
                {
                    // S.ArcaneResistance += 85f;
                    S.SpellHit += 0.01f;
                    S.PhysicalHit += 0.01f;
                    // Patch 4.0.6+ changed from a scaling Health restore to a flat 20% of max health
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { HealthRestoreFromMaxHealth = 0.2f / 15f }, 15f, 180f));
                }
                else if (characterRace == CharacterRace.Worgen)
                {
                    // S.NatureResistance = 64f;
                    // S.ShadowResistance = 64f;
                    // Patch 4.0.6+ Darkflight changed from a 3 minute CD to a 2 minute CD
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { MovementSpeed = 0.40f }, 10f, 120f));
                    S.PhysicalCrit += 0.01f;
                    S.SpellCrit += 0.01f;
                }
                else if (characterRace == CharacterRace.Tauren)
                {
                    // S.NatureResistance = 85f;
                    S.Health = (float)Math.Floor(S.Health * 1.05f);
                }
                else if (characterRace == CharacterRace.Troll)
                {
                    S.SnareRootDurReduc = .15f;
                    if (characterClass == CharacterClass.DeathKnight || characterClass == CharacterClass.Warrior || characterClass == CharacterClass.Rogue)
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { PhysicalHaste = 0.2f }, 10f, 180f));
                    else
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { SpellHaste = 0.2f, PhysicalHaste = 0.2f }, 10f, 180f));
                }
                else if (characterRace == CharacterRace.Undead)
                {
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { FearDurReduc = 1f }, .1f, 120f));
                }
                else if (characterRace == CharacterRace.Orc)
                {
                    S.StunDurReduc = 0.15f;
                    if (characterClass == CharacterClass.Shaman)
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { AttackPower = 65 + (level * 13), SpellPower = 75 + (level * 6) }, 15f, 120f));
                    else if (characterClass == CharacterClass.Warlock || characterClass == CharacterClass.Mage)
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { SpellPower = 75 + (level * 6) }, 15f, 120f));
                    else
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { AttackPower = 65 + (level * 13) }, 15f, 120f));
                }
                else if (characterRace == CharacterRace.BloodElf)
                {
                    // S.ArcaneResistance += 85f;
                    if (characterClass == CharacterClass.DeathKnight || characterClass == CharacterClass.Rogue || characterClass == CharacterClass.Hunter)
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { ManaorEquivRestore = .15f }, 0f, 120f));
                    else if (characterClass == CharacterClass.Warrior)
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { BonusRageGen = 15f }, 0f, 120f));
                    else
                        S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { ManaorEquivRestore = .06f }, 0f, 120f));
                }
                else if (characterRace == CharacterRace.Goblin)
                {
                    S.PhysicalHaste += 0.01f;
                    S.SpellHaste += 0.01f;
                    // TODO: The damage of the rocket belt proc is dependent on the character's current AP and SP
                    S.AddSpecialEffect(new SpecialEffect(Trigger.Use, new Stats() { FireDamage = 1f + (level * 2) }, 0f, 120f));
                }
                else if (characterRace == CharacterRace.PandarenAlliance || characterRace == CharacterRace.PandarenHorde)
                {

                }
                #endregion

                _lastStats = S.Clone();
                return S;
            }
        }