示例#1
0
        public void SetDPS(CharacterCalculationsRetribution calc)
        {
            calc.WhiteSkill           = White;
            calc.SealSkill            = Seal;
            calc.SealDotSkill         = SealDot;
            calc.CommandSkill         = SoC;
            calc.JudgementSkill       = Judge;
            calc.TemplarsVerdictSkill = TV;
            calc.CrusaderStrikeSkill  = CS;
            calc.ConsecrationSkill    = Cons;
            calc.ExorcismSkill        = Exo;
            calc.HolyWrathSkill       = HW;
            calc.HammerOfWrathSkill   = HoW;
            calc.GoakSkill            = GoaK;

            calc.DPSPoints = White.GetDPS() +
                             Seal.GetDPS() +
                             SealDot.GetDPS() +
                             SoC.GetDPS() +
                             Judge.GetDPS() +
                             CS.GetDPS() +
                             TV.GetDPS() +
                             Exo.GetDPS() +
                             HW.GetDPS() +
                             Cons.GetDPS() +
                             HoW.GetDPS() +
                             GoaK.GetDPS() +
                             calc.OtherDPS;
        }
示例#2
0
        public override void SetCharacterCalculations(CharacterCalculationsRetribution calc)
        {
            calc.Solution = new RotationSolution();

            foreach (Skill skill in new[] { Judge, CS, DS, Cons, Exo, HoW })
            {
                float effectiveCooldown;
                if (skill.UsableAfter20PercentHealth)
                {
                    if (skill.UsableBefore20PercentHealth)
                    {
                        effectiveCooldown =
                            Combats.CalcOpts.GetEffectiveAbilityCooldown(skill.RotationAbility.Value) *
                            (1f - Combats.CalcOpts.TimeUnder20) +
                            Combats.CalcOpts.GetEffectiveAbilityCooldownAfter20PercentHealth(
                                skill.RotationAbility.Value) *
                            Combats.CalcOpts.TimeUnder20;
                    }
                    else
                    {
                        effectiveCooldown = Combats.CalcOpts.GetEffectiveAbilityCooldownAfter20PercentHealth(
                            skill.RotationAbility.Value);
                    }
                }
                else
                {
                    if (skill.UsableBefore20PercentHealth)
                    {
                        effectiveCooldown = Combats.CalcOpts.GetEffectiveAbilityCooldown(
                            skill.RotationAbility.Value);
                    }
                    else
                    {
                        effectiveCooldown = 0;
                    }
                }

                calc.Solution.SetAbilityEffectiveCooldown(skill.RotationAbility.Value, effectiveCooldown);
            }

            calc.Rotation = null;
        }
示例#3
0
        public void SetDPS(CharacterCalculationsRetribution calc)
        {
            SetCharacterCalculations(calc);

            calc.AverageSoVStack = AverageSoVStackSize();
            calc.SoVOvertake     = SoVOvertakeTime();

            calc.WhiteDPS           = White.WhiteDPS();
            calc.SealDPS            = SealDPS(Seal, SealDot);
            calc.JudgementDPS       = GetAbilityDps(Judge);
            calc.DivineStormDPS     = GetAbilityDps(DS);
            calc.CrusaderStrikeDPS  = GetAbilityDps(CS);
            calc.ConsecrationDPS    = GetAbilityDps(Cons);
            calc.ExorcismDPS        = GetAbilityDps(Exo);
            calc.HammerOfWrathDPS   = GetAbilityDps(HoW);
            calc.HandOfReckoningDPS = HandOfReckoningDPS(HoR);

            calc.WhiteSkill           = White;
            calc.SealSkill            = Seal;
            calc.JudgementSkill       = Judge;
            calc.DivineStormSkill     = DS;
            calc.CrusaderStrikeSkill  = CS;
            calc.ConsecrationSkill    = Cons;
            calc.ExorcismSkill        = Exo;
            calc.HammerOfWrathSkill   = HoW;
            calc.HandOfReckoningSkill = HoR;

            calc.DPSPoints =
                calc.WhiteDPS +
                calc.SealDPS +
                calc.JudgementDPS +
                calc.CrusaderStrikeDPS +
                calc.DivineStormDPS +
                calc.ExorcismDPS +
                calc.HandOfReckoningDPS +
                calc.ConsecrationDPS +
                calc.HammerOfWrathDPS +
                calc.OtherDPS;
        }
示例#4
0
 public override void SetCharacterCalculations(CharacterCalculationsRetribution calc)
 {
     calc.Solution = Solution;
     calc.Rotation = Rotation;
 }
示例#5
0
 public abstract void SetCharacterCalculations(CharacterCalculationsRetribution calc);
        public CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, RotationCalculation rot)
        {
            // First things first, we need to ensure that we aren't using bad data
            CharacterCalculationsRetribution calc = new CharacterCalculationsRetribution();
            if (character == null) { return calc; }
            if (rot == null) { return calc; }

            calc.CombatStats = rot.Stats;
            calc.Character = rot.Character;
            calc.BasicStats = GetCharacterStats(character, additionalItem, false);
            //Damage procs are modeled as DPS
            calc.OtherDPS = new MagicDamage("", character, rot.Stats, DamageType.Arcane).AverageDamage
                          + new MagicDamage("", character, rot.Stats, DamageType.Fire).AverageDamage
                          + new MagicDamage("", character, rot.Stats, DamageType.Shadow).AverageDamage
                          + new MagicDamage("", character, rot.Stats, DamageType.Frost).AverageDamage
                          + new MagicDamage("", character, rot.Stats, DamageType.Nature).AverageDamage
                          + new MagicDamage("", character, rot.Stats, DamageType.Holy).AverageDamage;
            rot.SetDPS(calc);
            calc.OverallPoints = calc.DPSPoints;

            return calc;
        }
示例#7
0
        /// <summary>
        /// GetCharacterCalculations is the primary method of each model, where a majority of the calculations
        /// and formulae will be used. GetCharacterCalculations should call GetCharacterStats(), and based on
        /// those total stats for the character, and any calculationoptions on the character, perform all the
        /// calculations required to come up with the final calculations defined in
        /// CharacterDisplayCalculationLabels, including an Overall rating, and all Sub ratings defined in
        /// SubPointNameColors.
        /// </summary>
        /// <param name="character">The character to perform calculations for.</param>
        /// <param name="additionalItem">An additional item to treat the character as wearing.
        /// This is used for gems, which don't have a slot on the character to fit in, so are just
        /// added onto the character, in order to get gem calculations.</param>
        /// <returns>A custom CharacterCalculations object which inherits from CharacterCalculationsBase,
        /// containing all of the final calculations defined in CharacterDisplayCalculationLabels. See
        /// CharacterCalculationsBase comments for more details.</returns>
        public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem)
        {
            CalculationOptionsRetribution calcOpts = character.CalculationOptions as CalculationOptionsRetribution;

            GetTalents(character);
            Stats stats = GetCharacterStats(character, additionalItem);

            CharacterCalculationsRetribution calcs = new CharacterCalculationsRetribution();

            calcs.BasicStats  = stats;
            calcs.ActiveBuffs = new List <Buff>(character.ActiveBuffs);

            #region Damage Multipliers
            float twoHandedSpec = 1f + (0.02f * (float)calcOpts.TwoHandedSpec);
            float impSancAura   = 1f + 0.01f * (float)calcOpts.ImprovedSanctityAura;
            float crusade       = 1f + 0.01f * (float)calcOpts.Crusade;
            float vengeance     = 1f + 0.03f * (float)calcOpts.Vengeance;
            float sancAura      = 1f + 0.1f * (float)calcOpts.SanctityAura;
            float spellPower    = 1f + stats.BonusSpellPowerMultiplier;     // Covers all % spell damage increases.  Misery, FI.
            float physPower     = 1f + stats.BonusPhysicalDamageMultiplier; // Covers all % physical damage increases.  Blood Frenzy, FI.
            float partialResist = 0.953f;                                   // Average of 4.7% damage lost to partial resists on spells

            float physCritMult  = 1f + stats.BonusCritMultiplier * 2f;
            float spellCritMult = 1f + stats.BonusCritMultiplier * 3f;
            float jotc          = 219f;

            // Avenging Wrath -- Calculating uptime
            float fightDuration = calcOpts.FightLength * 60;
            int   remainder = 0, noOfFullAW = 0;
            int   div = Math.DivRem(Convert.ToInt32(fightDuration), 180, out remainder);
            if (remainder == 0)
            {
                noOfFullAW = div;
            }
            else
            {
                noOfFullAW = Convert.ToInt32(Math.Ceiling(Convert.ToDouble((fightDuration + 20) / 180)));
            }
            float partialUptime = fightDuration - noOfFullAW * 180;
            if (partialUptime < 0)
            {
                partialUptime = 0;
            }
            float totalUptime = partialUptime + noOfFullAW * 20f;
            float avWrath     = 1f + 0.30f * totalUptime / fightDuration;

            // Combined damage multipliers
            float allDamMult  = avWrath * crusade * impSancAura;
            float holyDamMult = allDamMult * spellPower * sancAura * vengeance;
            float physDamMult = allDamMult * physPower * twoHandedSpec * vengeance;
            #endregion

            float dpsWhite = 0f, dpsSeal = 0f, dpsWindfury = 0f, dpsCrusader = 0f, dpsJudgement = 0f, dpsConsecration = 0f, dpsExorcism = 0f;
            float baseSpeed = 1f, hastedSpeed = 1f, baseDamage = 0f, mitigation = 1f;
            float whiteHit = 0f, physCrits = 0f, totalMiss = 0f, spellCrits = 0f, spellResist = 0f;
            float chanceToGlance = 0.25f, glancingAmount = 0.35f;

            if (character.MainHand != null)
            {
                baseSpeed  = character.MainHand.Speed;
                baseDamage = (character.MainHand.MinDamage + character.MainHand.MaxDamage) / 2f + stats.WeaponDamage;
            }


            #region Attack Speed
            {
                hastedSpeed = baseSpeed / (1f + (stats.HasteRating / 1576f));

                // Mongoose Enchant grants 2% haste
                if (stats.MongooseProc > 0)
                {
                    hastedSpeed /= 1f + (0.02f * 0.4f);  // ASSUMPTION: Mongoose has a 40% uptime
                }
                if (stats.Bloodlust > 0)
                {
                    float bloodlustUptime = (calcOpts.Bloodlust * 40f);

                    if (bloodlustUptime > fightDuration)
                    {
                        bloodlustUptime = 1f;
                    }
                    else
                    {
                        bloodlustUptime /= fightDuration;
                    }

                    hastedSpeed /= 1f + (0.3f * bloodlustUptime);
                }
            }
            #endregion

            #region Mitigation
            {
                float targetArmor = calcOpts.BossArmor, totalArP = stats.ArmorPenetration;

                // Effective armor after ArP
                targetArmor -= totalArP;
                if (targetArmor < 0)
                {
                    targetArmor = 0f;
                }

                // Convert armor to mitigation
                mitigation = 1f - (targetArmor / (targetArmor + 10557.5f));

                // Executioner enchant.  ASSUMPTION: Executioner has a 40% uptime.
                if (stats.ExecutionerProc > 0)
                {
                    float exeArmor = targetArmor, exeMitigation = 1.0f, exeUptime = 0.4f, exeArmorPen = 840f;

                    // Find mitigation while Executioner is up
                    exeArmor = targetArmor - exeArmorPen;
                    if (exeArmor < 0)
                    {
                        exeArmor = 0f;
                    }
                    exeMitigation = 1f - (exeArmor / (exeArmor + 10557.5f));

                    // Weighted average of mitigation with and without Executioner, based on Executioner uptime
                    mitigation = (exeMitigation * exeUptime) + (mitigation * (1 - exeUptime));
                }
            }
            #endregion

            #region Crits, Misses, Resists
            {
                // Crit: Base .65%
                physCrits  = .0065f;
                physCrits += stats.CritRating / 2208f;
                physCrits += stats.Agility / 2500f;
                physCrits += stats.Crit;

                // Dodge: Base 6.5%, Minimum 0%
                float chanceDodged = .065f;
                //chanceDodged -= stats.ExpertiseRating / 1576f;
                chanceDodged -= stats.Expertise * .0025f;
                if (chanceDodged < 0f)
                {
                    chanceDodged = 0f;
                }
                calcs.DodgedAttacks = chanceDodged;

                // Miss: Base 9%, Minimum 0%
                float chanceMiss = .09f;
                chanceMiss -= stats.HitRating / 1576f;
                chanceMiss -= stats.Hit;
                if (chanceMiss < 0f)
                {
                    chanceMiss = 0f;
                }
                calcs.MissedAttacks = chanceMiss;

                // Spell Crit: Base 3.26%  **TODO: include intellect
                spellCrits  = .0326f;
                spellCrits += stats.SpellCritRating / 2208f;
                spellCrits += stats.SpellCrit;

                // Resists: Base 17%, Minimum 1%
                spellResist  = .17f;
                spellResist -= stats.SpellHitRating / 1262f;
                spellResist -= stats.Hit;
                if (spellResist < .01f)
                {
                    spellResist = .01f;
                }

                // Total physical misses
                totalMiss = chanceDodged + chanceMiss;
            }
            #endregion


            #region White Damage
            {
                float whiteAvgDam = 0f, dpsSSO = 0.0f;

                #region SSO Neck Procs
                if (stats.ShatteredSunMightProc > 0)
                {
                    string shattrathFaction = calcOpts.ShattrathFaction;
                    switch (shattrathFaction)
                    {
                    case "Aldor":
                        stats.AttackPower += 39.13f;
                        break;

                    case "Scryer":
                        dpsSSO  = 350f * allDamMult * spellPower;
                        dpsSSO *= 1f + physCrits * physCritMult - totalMiss;
                        dpsSSO /= 50;      // 50 seconds between procs
                        break;
                    }
                }
                #endregion

                // White damage per hit.  Basic white hits are use elsewhere.
                whiteHit    = baseDamage + (stats.AttackPower / 14.0f) * baseSpeed;
                whiteAvgDam = whiteHit * physDamMult;

                // Average white damage per swing
                whiteAvgDam *= (1f + physCrits * physCritMult - totalMiss - chanceToGlance * glancingAmount) * mitigation;

                // Total white DPS.  Scryer SSO neck added as "white"
                dpsWhite  = whiteAvgDam / hastedSpeed;
                dpsWhite += dpsSSO;
            }
            #endregion

            #region Seal
            {
                float sealActualPPM = 0f, sealAvgDam = 0f, windProcRate = .2f;
                if (calcOpts.Seal == 0) // Seal of Command
                {
                    float socPPM = 7f, socCoeff = 0.2f, socHolyCoeff = 0.29f;

                    // Find real PPM.  Procs 7 times per minute before misses
                    sealActualPPM = socPPM * (1f - totalMiss);
                    // Chain Procs: Windfury procs Seal of Command
                    if (stats.WindfuryAPBonus > 0)
                    {
                        // Chance SoC has proc'd on a swing
                        float sealProcChance = socPPM / (60f / hastedSpeed);

                        // Proc chain fails if Windfury misses
                        windProcRate *= (1 - totalMiss);

                        // SoC procs off of Windfury only if SoC has not already proc'd
                        sealActualPPM *= 1 + windProcRate * (1 - sealProcChance);
                    }

                    // Seal Damage per hit
                    sealAvgDam = 0.7f * whiteHit * twoHandedSpec + socCoeff * stats.SpellDamageRating;
                    sealAvgDam = sealAvgDam * holyDamMult + socHolyCoeff * jotc;
                }
                else // Seal of Blood
                {
                    // Find real PPM.  Procs on every hit.
                    sealActualPPM = (60f / hastedSpeed) * (1 - totalMiss);
                    // Chain Procs: Windfury procs Seal of Blood
                    if (stats.WindfuryAPBonus > 0)
                    {
                        sealActualPPM *= 1 + windProcRate * (1 - totalMiss);
                    }

                    // Seal Damage per hit
                    sealAvgDam = 0.35f * whiteHit * holyDamMult * twoHandedSpec;
                }

                // Seal average damage per proc
                sealAvgDam *= (1f + physCrits * physCritMult - totalMiss) * partialResist;

                // Total Seal DPS
                dpsSeal = sealAvgDam * sealActualPPM / 60f;
            }
            #endregion

            #region Windfury
            if (stats.WindfuryAPBonus > 0)
            {
                float windProcRate = .2f, windPerMin = 0f, windAvgDam = 0f, windAPBonus = stats.WindfuryAPBonus;

                // Find real PPM.  Chance to proc on every hit. and damage per hit
                windPerMin = (60f / hastedSpeed) * (1 - totalMiss) * windProcRate;
                // Chain Procs: Seal of Command can procs Windfury
                if (calcOpts.Seal == 0)
                {
                    float socPPM = 7f;

                    // Proc chain fails if either swing or SoC misses
                    socPPM *= (1 - totalMiss) * (1 - totalMiss);

                    // Windfury procs off of SoC only if Windfury has not already proc'd
                    windPerMin += socPPM * windProcRate * (1 - windProcRate);
                }

                // Windfury damage per hit
                windAvgDam  = whiteHit + (windAPBonus / 14) * baseSpeed;
                windAvgDam *= physDamMult;

                // Windfury average damage per proc
                windAvgDam *= (1f + physCrits * physCritMult - totalMiss) * mitigation;

                // Total Windfury DPS
                dpsWindfury = windAvgDam * windPerMin / 60f;
            }
            #endregion

            #region Crusader Strike
            {
                float crusCD = 6f, crusCoeff = 1.1f, crusAvgDam = 0f;

                // Crusader Strike damage per hit
                crusAvgDam  = baseDamage + 3.3f * (stats.AttackPower / 14f);
                crusAvgDam *= crusCoeff * physDamMult * (1f + stats.BonusCrusaderStrikeDamageMultiplier);

                // Crusader Strike average damage per swing
                crusAvgDam *= (1f + physCrits * physCritMult - totalMiss) * mitigation;

                // Total Crusader Strike DPS
                dpsCrusader = crusAvgDam / crusCD;
            }
            #endregion

            #region Judgement
            {
                float judgeCD = 9.0f, judgeCoeff = 0.429f, judgeAvgDam = 0.0f;
                float judgeCrit = physCrits + (0.03f * (float)calcOpts.Fanaticism);

                if (calcOpts.Seal == 0)
                {
                    judgeAvgDam = 240f;
                }
                else
                {
                    judgeAvgDam = 310f;
                }
                judgeAvgDam = (judgeAvgDam + judgeCoeff * stats.SpellDamageRating) * holyDamMult + judgeCoeff * jotc;

                // Judgement average damage per cast.  JoBlood does not get full resisted.
                if (calcOpts.Seal == 0)
                {
                    judgeAvgDam *= (1f + judgeCrit * physCritMult - spellResist);
                }
                else
                {
                    judgeAvgDam *= 1f + judgeCrit * physCritMult;
                }
                judgeAvgDam *= partialResist;

                // Total Judgement DPS
                dpsJudgement = judgeAvgDam / judgeCD;
            }
            #endregion

            #region Consecration
            if (calcOpts.ConsecRank != 0)
            {
                float consCD = 9f, consCoeff = 0.952f, consAvgDam = 0f;
                int   consRank = calcOpts.ConsecRank;

                // Consecration damage pre-resists
                switch (consRank) // Rank damage + coeff * level reduction * spelldamage
                {
                case 1: consAvgDam = 64f + consCoeff * (35f / 70f) * stats.SpellDamageRating; break;

                case 2: consAvgDam = 120f + consCoeff * (45f / 70f) * stats.SpellDamageRating; break;

                case 3: consAvgDam = 184f + consCoeff * (55f / 70f) * stats.SpellDamageRating; break;

                case 4: consAvgDam = 280f + consCoeff * (65f / 70f) * stats.SpellDamageRating; break;

                case 5: consAvgDam = 384f + consCoeff * stats.SpellDamageRating; break;

                case 6: consAvgDam = 512f + consCoeff * stats.SpellDamageRating; break;
                }
                consAvgDam = consAvgDam * holyDamMult + consCoeff * jotc;

                // Consecration average damage post-resists.
                consAvgDam *= partialResist;

                // Total Consecration DPS
                dpsConsecration = consAvgDam / consCD;
            }

            #endregion

            #region Exorcism
            if (calcOpts.Exorcism)
            {
                float exorCD = 18f, exorCoeff = 0.429f, exorAvgDmg = 0f;

                // Exorcism damage per spell hit
                exorAvgDmg = 665f + exorCoeff * stats.SpellDamageRating;
                exorAvgDmg = exorAvgDmg * holyDamMult + exorCoeff * jotc;

                // Exorcism average damage per cast
                exorAvgDmg *= (1f + (spellCrits / 2f) * spellCritMult - spellResist) * partialResist;

                // Total Exorcism DPS
                dpsExorcism = exorAvgDmg / exorCD;
            }
            #endregion


            calcs.WeaponDamage    = whiteHit * physDamMult;
            calcs.AttackSpeed     = hastedSpeed;
            calcs.CritChance      = physCrits;
            calcs.AvoidedAttacks  = totalMiss;
            calcs.EnemyMitigation = 1 - mitigation;

            calcs.WhiteDPS        = dpsWhite;
            calcs.SealDPS         = dpsSeal;
            calcs.WindfuryDPS     = dpsWindfury;
            calcs.CrusaderDPS     = dpsCrusader;
            calcs.JudgementDPS    = dpsJudgement;
            calcs.ConsecrationDPS = dpsConsecration;
            calcs.ExorcismDPS     = dpsExorcism;

            calcs.DPSPoints     = dpsWhite + dpsSeal + dpsWindfury + dpsCrusader + dpsJudgement + dpsConsecration + dpsExorcism;
            calcs.OverallPoints = calcs.DPSPoints;
            return(calcs);
        }
示例#8
0
        public GraphForm(Character character, bool bStats)
        {
            InitializeComponent();

            if (!bStats)
            {
                Text = "Effective Cooldown Graph";
            }

            // Create ARGB bitmap matching the size of the picturebox and associate with picturebox
            bmp = new Bitmap(pictureBoxGraph.Size.Width, pictureBoxGraph.Size.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            pictureBoxGraph.Image = bmp;

            // Greate & initialise GDI+ graphics object via bitmap
            gfx = Graphics.FromImage(bmp);
            gfx.SmoothingMode     = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

            #region Coordinates and scaling.

            /*
             *   +-----------------------------------------------------+-rcBmp  ---
             *   |                                                     |         |EndY
             *   |    ^                                                |        ---
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    |                                                |
             *   |    +--------------------------------------------->  | ---
             *   |                                                     |  |  OrgY
             *   +-----------------------------------------------------+ ---
             *
             *   |----|OrgX                                         |--|EndX
             *
             *
             *
             */

            // Apply a translateion transformation. We'll move the 0,0 to the 0,0 of the chart we want to display
            // This will make most of the rest of the charting easier since all translation are then done by GDI+
            // It also makes working with 'y' a bit easier as it's just flipped in sign rather than translated also.
            int OrgX = 50;  // Leftmost edge of char
            int OrgY = 50;  // Bottom edge of char
            int EndX = 15;  // Margin right of chart
            int EndY = 15;  // Margin top of chart
            gfx.TranslateTransform(OrgX, pictureBoxGraph.Size.Height - OrgY);

            // Bitmap coordinates translated back into transform
            Rectangle rcBmp = new Rectangle(0, 0, pictureBoxGraph.Size.Width, pictureBoxGraph.Size.Height);
            rcBmp.Offset(-OrgX, -(pictureBoxGraph.Size.Height - OrgY));

            // rcChart is image rectangle, watch out, Y has positive value !
            Rectangle rcChart      = new Rectangle(0, 0, pictureBoxGraph.Size.Width - OrgX - EndX, pictureBoxGraph.Size.Height - OrgY - EndY);
            int       YAxisValueX  = -5; // Right alignment of values on the Y axis
            int       XAxisValueY  = +5; // Top margin of values on the X axis
            int       YChartMargin = 5;
            #endregion

            #region Define and calculate charts
            ChartData[] aCharts;
            float       DpsMax;
            float       DpsMin;
            float       DpsScaling;
            if (bStats)
            {
                aCharts = new ChartData[] {
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 0, 0), "1 Strength", new Stats()
                    {
                        Strength = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 192, 96), "1.167 Spell Power", new Stats()
                    {
                        SpellPower = 7 / 6
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 0, 0, 192), "1 Armor Pen.", new Stats()
                    {
                        ArmorPenetrationRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 0, 192), "1 Hit Rating", new Stats()
                    {
                        HitRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 0, 192, 0), "1 Expertise Rating", new Stats()
                    {
                        ExpertiseRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 127, 96), "1 Agility", new Stats()
                    {
                        Agility = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 127, 0), "2 Attack Power", new Stats()
                    {
                        AttackPower = 2
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 96, 127, 192), "1 Crit Rating", new Stats()
                    {
                        CritRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 0, 0, 0), "1 Haste Rating", new Stats()
                    {
                        HasteRating = 1
                    }),
                };

                // Calculate charts
                CalculationsRetribution Calc = new CalculationsRetribution();
                DpsMax = 0;
                DpsMin = 999999999;
                foreach (ChartData cd in aCharts)
                {
                    for (int count = 0; count < rcChart.Width; count++)
                    {
                        Stats chartstats = cd.stats.Clone();
                        chartstats *= count - (rcChart.Width / 2);

                        CharacterCalculationsRetribution chartCalc = Calc.GetCharacterCalculations(character, new Item()
                        {
                            Stats = chartstats
                        }) as CharacterCalculationsRetribution;
                        float Dps = chartCalc.DPSPoints;

                        if (Dps > DpsMax)
                        {
                            DpsMax = Dps;
                        }
                        if (Dps < DpsMin)
                        {
                            DpsMin = Dps;
                        }

                        cd.dps[count] = Dps;
                    }
                }
                DpsScaling = (rcChart.Height - 2 * YChartMargin /* some extra margin*/) / (DpsMax - DpsMin);
            }
            else
            {
                aCharts = new ChartData[] {
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 0, 0), "White", new Stats()
                    {
                        HasteRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 192, 96), "CS", new Stats()
                    {
                        HasteRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 0, 0, 192), "J", new Stats()
                    {
                        HasteRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 0, 192), "DS", new Stats()
                    {
                        HasteRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 0, 192, 0), "Cons", new Stats()
                    {
                        HasteRating = 1
                    }),
                    new ChartData(rcChart.Width, Color.FromArgb(255, 192, 127, 96), "Exo", new Stats()
                    {
                        HasteRating = 1
                    }),
                    //new ChartData(rcChart.Width, Color.FromArgb(255, 192, 127, 0), "HoW", new Stats() { HasteRating = 1 }),
                };

                // Calculate charts
                CalculationsRetribution Calc = new CalculationsRetribution();
                DpsMax = 0;
                DpsMin = 999999999;
                Stats stats = new Stats()
                {
                    HasteRating = 1
                };
                for (int count = 0; count < rcChart.Width; count++)
                {
                    Stats chartstats = stats.Clone();
                    chartstats *= count - (rcChart.Width / 2);

                    CharacterCalculationsRetribution chartCalc = Calc.GetCharacterCalculations(character, new Item()
                    {
                        Stats = chartstats
                    }) as CharacterCalculationsRetribution;
                    float Ecd;

                    Ecd = chartCalc.AttackSpeed;
                    if (Ecd > DpsMax)
                    {
                        DpsMax = Ecd;
                    }
                    if (Ecd < DpsMin)
                    {
                        DpsMin = Ecd;
                    }
                    aCharts[0].dps[count] = Ecd;

                    Ecd = 1 / chartCalc.Solution.GetAbilityUsagePerSecond(Ability.CrusaderStrike);
                    if (Ecd > 50)  // infinity fix
                    {
                        Ecd = 0;
                    }
                    if (Ecd > DpsMax)
                    {
                        DpsMax = Ecd;
                    }
                    if (Ecd < DpsMin)
                    {
                        DpsMin = Ecd;
                    }
                    aCharts[1].dps[count] = Ecd;

                    Ecd = 1 / chartCalc.Solution.GetAbilityUsagePerSecond(Ability.Judgement);
                    if (Ecd > 50)
                    {
                        Ecd = 0;
                    }
                    if (Ecd > DpsMax)
                    {
                        DpsMax = Ecd;
                    }
                    if (Ecd < DpsMin)
                    {
                        DpsMin = Ecd;
                    }
                    aCharts[2].dps[count] = Ecd;

                    Ecd = 1 / chartCalc.Solution.GetAbilityUsagePerSecond(Ability.DivineStorm);
                    if (Ecd > 50)
                    {
                        Ecd = 0;
                    }
                    if (Ecd > DpsMax)
                    {
                        DpsMax = Ecd;
                    }
                    if (Ecd < DpsMin)
                    {
                        DpsMin = Ecd;
                    }
                    aCharts[3].dps[count] = Ecd;
                    Ecd = 1 / chartCalc.Solution.GetAbilityUsagePerSecond(Ability.Consecration);
                    if (Ecd > 50)
                    {
                        Ecd = 0;
                    }
                    if (Ecd > DpsMax)
                    {
                        DpsMax = Ecd;
                    }
                    if (Ecd < DpsMin)
                    {
                        DpsMin = Ecd;
                    }
                    aCharts[4].dps[count] = Ecd;
                    Ecd = 1 / chartCalc.Solution.GetAbilityUsagePerSecond(Ability.Exorcism);
                    if (Ecd > 50)
                    {
                        Ecd = 0;
                    }
                    if (Ecd > DpsMax)
                    {
                        DpsMax = Ecd;
                    }
                    if (Ecd < DpsMin)
                    {
                        DpsMin = Ecd;
                    }
                    aCharts[5].dps[count] = Ecd;

                    /*Ecd = 1 / chartCalc.Solution.GetAbilityUsagePerSecond(Ability.HammerOfWrath);
                     * if (Ecd > 50)
                     *  Ecd = 0;
                     * if (Ecd > DpsMax)
                     *  DpsMax = Ecd;
                     * if (Ecd < DpsMin)
                     *  DpsMin = Ecd;
                     * aCharts[6].dps[count] = Ecd;     */
                }
                DpsScaling = (rcChart.Height - 2 * YChartMargin /* some extra margin*/) / (DpsMax - DpsMin);
            }
            #endregion

            #region Fonts, Pens, Brushes and other helper variables
            Font fntValue = new Font("Arial", 8);
            Font fntTitle = new Font("Arial", 16);

            Pen penBlack  = new Pen(Color.FromArgb(255, 0, 0, 0));
            Pen penGray50 = new Pen(Color.FromArgb(255, 127, 127, 127));
            Pen penGray75 = new Pen(Color.FromArgb(255, 196, 196, 196));
            Pen penGray90 = new Pen(Color.FromArgb(255, 230, 230, 230));
            Pen penRed    = new Pen(Color.FromArgb(255, 255, 0, 0));

            Brush brBlack = new SolidBrush(Color.FromArgb(255, 0, 0, 0));
            Brush brWhite = new SolidBrush(Color.FromArgb(255, 255, 255, 255));
            Brush brRed   = new SolidBrush(Color.FromArgb(255, 255, 0, 0));

            StringFormat fmtRAlignCenter = new StringFormat();
            fmtRAlignCenter.Alignment     = StringAlignment.Far;
            fmtRAlignCenter.LineAlignment = StringAlignment.Center;

            StringFormat fmtRAlignBottom = new StringFormat();
            fmtRAlignBottom.Alignment     = StringAlignment.Far;
            fmtRAlignBottom.LineAlignment = StringAlignment.Far;

            StringFormat fmtLAlignTop = new StringFormat();
            fmtLAlignTop.Alignment     = StringAlignment.Near;
            fmtLAlignTop.LineAlignment = StringAlignment.Near;

            StringFormat fmtCAlignTop = new StringFormat();
            fmtCAlignTop.Alignment     = StringAlignment.Center;
            fmtCAlignTop.LineAlignment = StringAlignment.Near;
            #endregion

            // Clear bitmap to all white.
            gfx.FillRectangle(brWhite, rcBmp);

            #region Draw Axis and titles
            // Stats ticks (draw this first, so other axis information is drawn on top)
            const int Steps = 10; // Chart is per 1 stat, typically, 20 stats go in a iLevel.
            for (int i = 1; i < (rcChart.Width / 2) / Steps; i++)
            {
                Pen pen;
                if (i % 5 == 0)
                {
                    pen = penGray75;
                    gfx.DrawString((-i * 10).ToString(), fntValue, brBlack, rcChart.Width / 2 - i * 10, XAxisValueY, fmtCAlignTop);
                    gfx.DrawString((i * 10).ToString(), fntValue, brBlack, rcChart.Width / 2 + i * 10, XAxisValueY, fmtCAlignTop);
                }
                else
                {
                    pen = penGray90;
                }

                gfx.DrawLine(pen, rcChart.Width / 2 - i * 10, 0, rcChart.Width / 2 - i * 10, -rcChart.Height);
                gfx.DrawLine(pen, rcChart.Width / 2 + i * 10, 0, rcChart.Width / 2 + i * 10, -rcChart.Height);
            }
            // DPS ticks (Draw this first, so other axis information is drawn on top)
            const int MaxDpsLines = 25;
            float     DpsDelta    = DpsMax - DpsMin;
            int[]     Ranges      = { 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, };
            int       Range       = 1;
            foreach (int r in Ranges)
            {
                if (MaxDpsLines * r > DpsDelta)
                {
                    Range = r;
                    break;
                }
            }
            int Y = (int)(Math.Floor(DpsMin / Range) * Range);
            do
            {
                float Y2 = (Y - DpsMin) * DpsScaling;
                if (Y2 > 0)
                {
                    gfx.DrawLine(penGray75, 0, -Y2, rcChart.Width - 5, -Y2);
                    gfx.DrawString(Y.ToString(), fntValue, brBlack, YAxisValueX, -Y2, fmtRAlignCenter);
                }

                Y += Range;
            }while (Y < DpsMax);

            // Y Axis
            gfx.DrawLine(penBlack, 0, 0, 0, -rcChart.Height);
            gfx.DrawString(bStats ? "DPS" : "ECD", fntTitle, brBlack, 5, -(rcChart.Height - 5), fmtLAlignTop);

            // X-axis
            gfx.DrawLine(penBlack, 0, 0, rcChart.Width, 0);
            gfx.DrawString(bStats ? "Stats" : "Haste Rating", fntTitle, brBlack, rcChart.Width - 5, -5, fmtRAlignBottom);

            // Center Y Axis.
            gfx.DrawLine(penBlack, rcChart.Width / 2, 0, rcChart.Width / 2, -rcChart.Height);
            gfx.DrawString("0", fntValue, brBlack, rcChart.Width / 2, XAxisValueY, fmtCAlignTop);

            // "Experimental" text
            gfx.DrawString("Experimental", fntTitle, brRed, rcChart.Width - 5, OrgY, fmtRAlignBottom);
            #endregion

            // Draw charts
            int l = 0;
            foreach (ChartData cd in aCharts)
            {
                for (int count = 0; count < rcChart.Width - 1; count++)
                {
                    gfx.DrawLine(cd.pen, count, -(cd.dps[count] - DpsMin) * DpsScaling - YChartMargin, count + 1, -(cd.dps[count + 1] - DpsMin) * DpsScaling - YChartMargin);
                }
                gfx.DrawString(cd.name, fntValue, cd.brush, 5, -(rcChart.Height - fntTitle.GetHeight() - 5 - l * fntValue.GetHeight()));
                l++;
            }
            Invalidate();
            Update();
        }
        public void SetDPS(CharacterCalculationsRetribution calc)
        {
            calc.WhiteSkill = White;
            calc.SealSkill = Seal;
            calc.SealDotSkill = SealDot;
            calc.CommandSkill = SoC;
            calc.JudgementSkill = Judge;
            calc.TemplarsVerdictSkill = TV;
            calc.CrusaderStrikeSkill = CS;
            calc.ConsecrationSkill = Cons;
            calc.ExorcismSkill = Exo;
            calc.HolyWrathSkill = HW;
            calc.HammerOfWrathSkill = HoW;
            calc.GoakSkill = GoaK;

            calc.DPSPoints = White.GetDPS() +
                Seal.GetDPS() +
                SealDot.GetDPS() +
                SoC.GetDPS() +
                Judge.GetDPS() +
                CS.GetDPS() +
                TV.GetDPS() +
                Exo.GetDPS() +
                HW.GetDPS() +
                Cons.GetDPS() +
                HoW.GetDPS() +
                GoaK.GetDPS() +
                calc.OtherDPS;
        }
示例#10
0
        private void btnGraph_Click(object sender, EventArgs e)
        {
            CalculationsRetribution          retCalc  = new CalculationsRetribution();
            CharacterCalculationsRetribution baseCalc = retCalc.GetCharacterCalculations(Character) as CharacterCalculationsRetribution;
            Bitmap   _prerenderedGraph = global::Rawr.Retribution.Properties.Resources.GraphBase;
            Graphics g = Graphics.FromImage(_prerenderedGraph);

            g.SmoothingMode     = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            float graphHeight = 700f, graphStart = 100f;

            Color[] colors = new Color[] {
                Color.FromArgb(127, 202, 180, 96),  // Strength
                Color.FromArgb(127, 101, 225, 240), // Agility
                Color.FromArgb(127, 0, 4, 3),       // Attack Power
                Color.FromArgb(127, 123, 238, 199), // Crit Rating
                Color.FromArgb(127, 45, 112, 63),   // Hit Rating
                Color.FromArgb(127, 121, 72, 210),  //Expertise Rating
                Color.FromArgb(127, 217, 100, 54),  // Haste Rating
                Color.FromArgb(127, 210, 72, 195),  // Armor Penetration
                Color.FromArgb(127, 206, 189, 191), // Spell Damage
            };
            Stats[] statsList = new Stats[] {
                new Stats()
                {
                    Strength = 10
                },
                new Stats()
                {
                    Agility = 10
                },
                new Stats()
                {
                    AttackPower = 20
                },
                new Stats()
                {
                    CritRating = 10
                },
                new Stats()
                {
                    HitRating = 10
                },
                new Stats()
                {
                    ExpertiseRating = 10
                },
                new Stats()
                {
                    HasteRating = 10
                },
                new Stats()
                {
                    ArmorPenetration = 66.667f
                },
                new Stats()
                {
                    SpellDamageRating = 11.17f
                },
            };

            for (int index = 0; index < statsList.Length; index++)
            {
                Stats   newStats = new Stats();
                Point[] points   = new Point[100];
                for (int count = 0; count < 100; count++)
                {
                    newStats = newStats + statsList[index];

                    CharacterCalculationsRetribution currentCalc = retCalc.GetCharacterCalculations(Character, new Item()
                    {
                        Stats = newStats
                    }) as CharacterCalculationsRetribution;
                    float overallPoints = currentCalc.DPSPoints - baseCalc.DPSPoints;

                    if ((graphHeight - overallPoints) > 16)
                    {
                        points[count] = new Point(Convert.ToInt32(graphStart + count * 5), (Convert.ToInt32(graphHeight - overallPoints)));
                    }
                    else
                    {
                        points[count] = points[count - 1];
                    }
                }
                Brush statBrush = new SolidBrush(colors[index]);
                g.DrawLines(new Pen(statBrush, 3), points);
            }

            #region Graph Ticks
            float graphWidth = 500f;// this.Width - 150f;
            float graphEnd   = graphStart + graphWidth;
            //float graphStartY = 16f;
            float   maxScale = 100f;
            float[] ticks    = new float[] { (float)Math.Round(graphStart + graphWidth * 0.5f),
                                             (float)Math.Round(graphStart + graphWidth * 0.75f),
                                             (float)Math.Round(graphStart + graphWidth * 0.25f),
                                             (float)Math.Round(graphStart + graphWidth * 0.125f),
                                             (float)Math.Round(graphStart + graphWidth * 0.375f),
                                             (float)Math.Round(graphStart + graphWidth * 0.625f),
                                             (float)Math.Round(graphStart + graphWidth * 0.875f) };
            Pen          black200   = new Pen(Color.FromArgb(200, 0, 0, 0));
            Pen          black150   = new Pen(Color.FromArgb(150, 0, 0, 0));
            Pen          black75    = new Pen(Color.FromArgb(75, 0, 0, 0));
            Pen          black50    = new Pen(Color.FromArgb(50, 0, 0, 0));
            Pen          black25    = new Pen(Color.FromArgb(25, 0, 0, 0));
            StringFormat formatTick = new StringFormat();
            formatTick.LineAlignment = StringAlignment.Far;
            formatTick.Alignment     = StringAlignment.Center;
            Brush black200brush = new SolidBrush(Color.FromArgb(200, 0, 0, 0));
            Brush black150brush = new SolidBrush(Color.FromArgb(150, 0, 0, 0));
            Brush black75brush  = new SolidBrush(Color.FromArgb(75, 0, 0, 0));
            Brush black50brush  = new SolidBrush(Color.FromArgb(50, 0, 0, 0));
            Brush black25brush  = new SolidBrush(Color.FromArgb(25, 0, 0, 0));

            g.DrawLine(black200, graphStart - 4, 20, graphEnd + 4, 20);
            g.DrawLine(black200, graphStart, 16, graphStart, _prerenderedGraph.Height - 16);
            g.DrawLine(black200, graphEnd, 16, graphEnd, 19);
            g.DrawLine(black200, ticks[0], 16, ticks[0], 19);
            g.DrawLine(black150, ticks[1], 16, ticks[1], 19);
            g.DrawLine(black150, ticks[2], 16, ticks[2], 19);
            g.DrawLine(black75, ticks[3], 16, ticks[3], 19);
            g.DrawLine(black75, ticks[4], 16, ticks[4], 19);
            g.DrawLine(black75, ticks[5], 16, ticks[5], 19);
            g.DrawLine(black75, ticks[6], 16, ticks[6], 19);
            g.DrawLine(black75, graphEnd, 21, graphEnd, _prerenderedGraph.Height - 4);
            g.DrawLine(black75, ticks[0], 21, ticks[0], _prerenderedGraph.Height - 4);
            g.DrawLine(black50, ticks[1], 21, ticks[1], _prerenderedGraph.Height - 4);
            g.DrawLine(black50, ticks[2], 21, ticks[2], _prerenderedGraph.Height - 4);
            g.DrawLine(black25, ticks[3], 21, ticks[3], _prerenderedGraph.Height - 4);
            g.DrawLine(black25, ticks[4], 21, ticks[4], _prerenderedGraph.Height - 4);
            g.DrawLine(black25, ticks[5], 21, ticks[5], _prerenderedGraph.Height - 4);
            g.DrawLine(black25, ticks[6], 21, ticks[6], _prerenderedGraph.Height - 4);
            g.DrawLine(black200, graphStart - 4, _prerenderedGraph.Height - 20, graphEnd + 4, _prerenderedGraph.Height - 20);

            Font tickFont = new Font("Calibri", 11);
            g.DrawString((0f).ToString(), tickFont, black200brush, graphStart, 16, formatTick);
            g.DrawString((maxScale).ToString(), tickFont, black200brush, graphEnd, 16, formatTick);
            g.DrawString((maxScale * 0.5f).ToString(), tickFont, black200brush, ticks[0], 16, formatTick);
            g.DrawString((maxScale * 0.75f).ToString(), tickFont, black150brush, ticks[1], 16, formatTick);
            g.DrawString((maxScale * 0.25f).ToString(), tickFont, black150brush, ticks[2], 16, formatTick);
            g.DrawString((maxScale * 0.125f).ToString(), tickFont, black75brush, ticks[3], 16, formatTick);
            g.DrawString((maxScale * 0.375f).ToString(), tickFont, black75brush, ticks[4], 16, formatTick);
            g.DrawString((maxScale * 0.625f).ToString(), tickFont, black75brush, ticks[5], 16, formatTick);
            g.DrawString((maxScale * 0.875f).ToString(), tickFont, black75brush, ticks[6], 16, formatTick);

            g.DrawString((0f).ToString(), tickFont, black200brush, graphStart, _prerenderedGraph.Height - 16, formatTick);
            g.DrawString((maxScale).ToString(), tickFont, black200brush, graphEnd, _prerenderedGraph.Height - 16, formatTick);
            g.DrawString((maxScale * 0.5f).ToString(), tickFont, black200brush, ticks[0], _prerenderedGraph.Height - 16, formatTick);
            g.DrawString((maxScale * 0.75f).ToString(), tickFont, black150brush, ticks[1], _prerenderedGraph.Height - 16, formatTick);
            g.DrawString((maxScale * 0.25f).ToString(), tickFont, black150brush, ticks[2], _prerenderedGraph.Height - 16, formatTick);
            g.DrawString((maxScale * 0.125f).ToString(), tickFont, black75brush, ticks[3], _prerenderedGraph.Height - 16, formatTick);
            g.DrawString((maxScale * 0.375f).ToString(), tickFont, black75brush, ticks[4], _prerenderedGraph.Height - 16, formatTick);
            g.DrawString((maxScale * 0.625f).ToString(), tickFont, black75brush, ticks[5], _prerenderedGraph.Height - 16, formatTick);
            g.DrawString((maxScale * 0.875f).ToString(), tickFont, black75brush, ticks[6], _prerenderedGraph.Height - 16, formatTick);
            #endregion

            Graph graph = new Graph(_prerenderedGraph);
            graph.Show();
        }