public static float GetSpellCastsPerSec(CharacterCalculationsHealadin calc) { return(GetHealingCastsPerSec(calc) + (calc.RotationBoL / calc.BoL.CastTime() + calc.RotationJotP / calc.JotP.CastTime() + calc.RotationSS / calc.SS.CastTime()) / calc.FightLength); }
public float ManaPool(CharacterCalculationsHealadin calc) { DivinePleas = (float)Math.Ceiling((FightLength - 120f) / (120f * CalcOpts.DivinePlea)); calc.ManaBase = Stats.Mana; calc.ManaLayOnHands = Stats.Mana * ((Talents.GlyphOfDivinity ? 0.1f : 0) * loh.Casts()); calc.ManaArcaneTorrent = (Character.Race == CharacterRace.BloodElf ? Stats.Mana * .06f * (float)Math.Ceiling(FightLength / 120f - .25f) : 0); calc.ManaDivinePlea = Stats.Mana * (Talents.GlyphOfDivinePlea ? 0.18f : 0.12f) * DivinePleas; calc.ManaMp5 = FightLength * Stats.Mp5 / 5f; // this Stats.ManaRestoreFromMaxManaPerSecond is 0 is is messing up the replenishment calculation! //calc.ManaReplenishment = Stats.ManaRestoreFromMaxManaPerSecond * Stats.Mana * FightLength * CalcOpts.Replenishment; calc.ManaReplenishment = 0.001f * Stats.Mana * FightLength * CalcOpts.Replenishment; calc.ManaOther += Stats.ManaRestore; // add calc.ManaJudgements calc.ManaJudgements = HealadinConstants.basemana * 0.15f * jotp.Casts(); if (Stats.HighestStat > 0) { float greatnessMana = Stats.HighestStat * StatConversion.RATING_PER_MANA; calc.ManaReplenishment += Stats.ManaRestoreFromMaxManaPerSecond * FightLength * greatnessMana * CalcOpts.Replenishment; // Replenishment calc.ManaDivinePlea += DivinePleas * greatnessMana * .1f; // Divine Plea } // check if this is correct regen per 5 seconds.. // combat regen = 50% of spirit regen (from Meditation), plus MP5 from gear, plus 5% base mana per 5 secs. Base mana = 23422 at 85 float effective_spirit = Stats.Spirit + Stats.BonusCritChanceFrostStrike * 540 * 6 / CalcOpts.HolyShock; // add in bonus spirit from 4T11 procs float spirit_regen = StatConversion.GetSpiritRegenSec(effective_spirit, Stats.Intellect) * 5f; calc.CombatRegenRate = spirit_regen * 0.5f + Stats.Mp5 + HealadinConstants.basemana * 0.05f; calc.ManaRegenRate = spirit_regen + Stats.Mp5 + HealadinConstants.basemana * 0.05f; calc.CombatRegenTotal = calc.CombatRegenRate * FightLength / 5f; return(calc.ManaBase + calc.ManaDivinePlea + calc.CombatRegenTotal + calc.ManaOther + calc.ManaReplenishment + calc.ManaLayOnHands + calc.ManaJudgements); }
public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations) { // First things first, we need to ensure that we aren't using bad data CharacterCalculationsHealadin calc = new CharacterCalculationsHealadin(); if (character == null) { return(calc); } CalculationOptionsHealadin calcOpts = character.CalculationOptions as CalculationOptionsHealadin; if (calcOpts == null) { return(calc); } // Stats stats; calc = null; PaladinTalents talents = character.PaladinTalents; for (int i = 0; i < 5; i++) { float oldBurst = 0; if (i > 0) { oldBurst = calc.BurstPoints; } stats = GetCharacterStats(character, additionalItem, true, calc); calc = new CharacterCalculationsHealadin(); Rotation rot = new Rotation(character, stats); if (i > 0) { calc.BurstPoints = oldBurst; } else { calc.BurstPoints = rot.CalculateBurstHealing(calc); } calc.FightPoints = rot.CalculateFightHealing(calc); calc.OverallPoints = calc.BurstPoints + calc.FightPoints; } calc.BasicStats = GetCharacterStats(character, additionalItem, false, null); return(calc); }
public float ManaPool(CharacterCalculationsHealadin calc) { DivinePleas = (float)Math.Ceiling((FightLength - 60f) / (60f * CalcOpts.DivinePlea)); calc.ManaBase = Stats.Mana; calc.ManaLayOnHands = 1950 * ((Talents.GlyphOfDivinity ? 1 : 0) + (CalcOpts.LoHSelf ? 1 : 0)) * (Talents.GlyphOfDivinity ? 2f : 1f); calc.ManaArcaneTorrent = (Character.Race == CharacterRace.BloodElf ? Stats.Mana * .06f * (float)Math.Ceiling(FightLength / 120f - .25f) : 0); calc.ManaDivinePlea = Stats.Mana * .25f * DivinePleas; calc.ManaMp5 = FightLength * Stats.Mp5 / 5f; calc.ManaReplenishment = Stats.ManaRestoreFromMaxManaPerSecond * Stats.Mana * FightLength * CalcOpts.Replenishment; calc.ManaOther += Stats.ManaRestore; if (Stats.HighestStat > 0) { float greatnessMana = Stats.HighestStat * StatConversion.RATING_PER_MANA; calc.ManaReplenishment += Stats.ManaRestoreFromMaxManaPerSecond * FightLength * greatnessMana * CalcOpts.Replenishment; // Replenishment calc.ManaDivinePlea += DivinePleas * greatnessMana * .25f; // Divine Plea } return(calc.ManaBase + calc.ManaDivinePlea + calc.ManaMp5 + calc.ManaOther + calc.ManaReplenishment + calc.ManaLayOnHands); }
public float CalculateBurstHealing(CharacterCalculationsHealadin calc) { return(fol.HPS() * CalcOpts.BurstScale); }
public float CalculateFightHealing(CharacterCalculationsHealadin calc) { #region Copying Stuff to Calc calc.FightLength = FightLength; calc.HL = hl; calc.FoL = fol; calc.HS = hs; calc.DL = dl; calc.WoG = wog; calc.LoD = lod; calc.HR = hr; calc.JotP = jotp; calc.BoL = bol; calc.LoH = loh; calc.PotI = poti; calc.EJ = ej; calc.JudgeCasts = jotp.Casts(); calc.RotationJudge = jotp.Time() + CalcOpts.Userdelay * calc.JudgeCasts; calc.UsageJudge = jotp.Usage(); if (Talents.BeaconOfLight > 0) { calc.RotationBoL = bol.Time(); calc.UsageBoL = bol.Usage(); } calc.RotationHS = hs.Time() + hs.Casts() + CalcOpts.Userdelay; calc.HealedHS = hs.Healed(); calc.UsageHS = hs.Usage(); calc.RotationLoH = loh.Casts() * (loh.CastTime() + CalcOpts.Userdelay); calc.HealedLoH = loh.Casts() * loh.AverageHealed(); calc.UsageLoH = 0f; // always 0, costs no mana, code was: loh.Casts() * loh.BaseMana; calc.UsageCleanse = CalcOpts.Cleanse * cleanse.BaseMana; calc.RotationCleanse = CalcOpts.Cleanse * (cleanse.CastTime() + CalcOpts.Userdelay); calc.CleanseCasts = CalcOpts.Cleanse; #endregion Stats.BonusCritChanceObliterate = CalcOpts.CritOverheals; // I really need to put some variables in Stats that are for holy pally calc.HasteJotP = Talents.JudgementsOfThePure * 3f; calc.HasteSoL = Talents.SpeedOfLight * 1f; calc.SpellPowerTotal = Stats.Intellect + Stats.SpellPower; #region Divine Favor - old code, commented out for now /* if (Talents.DivineFavor > 0) * { * DivineFavor df = new DivineFavor(this); * calc.RotationHL += df.Time(); * calc.UsageHL += df.Usage(); * calc.HealedHL += df.Healed(); * } */ #endregion #region active time, remaining mana / time float remainingMana = calc.TotalMana = ManaPool(calc); remainingMana -= calc.UsageJudge + calc.UsageBoL + calc.UsageHS + calc.UsageHR + calc.UsageCleanse; // start with amount of time you are active in a fight calc.ActiveTime = FightLength * CalcOpts.Activity; float remainingTime = calc.ActiveTime; // now subtract time for lots of stuff we need to cast remainingTime -= calc.RotationJudge + calc.RotationBoL + calc.RotationHS + calc.RotationLoH + calc.RotationHR + calc.RotationCleanse; float RotationDP = DivinePleas * (calc.HS.CastTime() + CalcOpts.Userdelay); remainingTime -= RotationDP; // subtract divine plea cast times. HS is also a GCD, so I just used that to calculate it #endregion #region HS and holy power calc.HolyPowerCasts = hs.Casts() / 3f; if (Talents.LightOfDawn != 0) { calc.LoDCasts = (float)Math.Floor(calc.HolyPowerCasts * CalcOpts.HolyPoints); calc.WoGCasts = (float)Math.Floor(calc.HolyPowerCasts * (1f - CalcOpts.HolyPoints)); } else { calc.WoGCasts = (float)Math.Floor(calc.HolyPowerCasts); calc.LoDCasts = 0; } calc.RotationWoG = calc.WoGCasts * (wog.CastTime() + CalcOpts.Userdelay); calc.UsageWoG = calc.WoGCasts * wog.BaseMana; calc.HealedWoG = calc.WoGCasts * wog.AverageHealed(); calc.RotationLoD = calc.LoDCasts * (lod.CastTime() + CalcOpts.Userdelay); calc.UsageLoD = calc.LoDCasts * lod.BaseMana; calc.HealedLoD = calc.LoDCasts * lod.AverageHealed() * CalcOpts.LoDTargets; remainingTime -= calc.RotationWoG + calc.RotationLoD; #endregion #region Holy Radiance calc.HRCasts = (float)Math.Floor(FightLength / CalcOpts.HRCasts); calc.RotationHR = calc.HRCasts * (hr.CastTime() + CalcOpts.Userdelay); calc.UsageHR = calc.HRCasts * hr.BaseMana; calc.HealedHR = calc.HRCasts * hr.AverageHealed() * CalcOpts.HREff; remainingTime -= calc.RotationHR; #endregion #region Melee mana regen // Melee hits generate 4% base mana. From what I read, this proc has a 4 sec CD. 15ppm max. float InstantCastRotationTotal = calc.RotationLoD + calc.RotationWoG + calc.RotationHS + calc.RotationHR + calc.RotationJudge + calc.RotationBoL + calc.RotationLoH + calc.RotationCleanse; float MeleeTime = InstantCastRotationTotal * CalcOpts.Melee; // Ideally the SwingTime would be the weapon speed, then logic would be added to estimate the # of procs. As the # of swings increases, // the number of procs would have dimishing returns. (because more frequent swings means more swings happen during the 4 sec CD, and don't proc) // For now I am going to fudge it and estimate a proc every 5 secs. Every 4 is unrealistic since 4 is not divisable by weapon speed, // so some time is always wasted. Also consider when one alternates between swings and casts, then each swing would proc. Whatever, 5 is good enough for now. float SwingTime = 5f; float MeleeManaPerProc = HealadinConstants.basemana * 0.04f; // calc.MeleeSwings = MeleeTime / SwingTime; calc.MeleeProcs = MeleeTime / SwingTime; // find a way to model this better, as swings approaches MaxMeleeProcs, it should have diminishing returns somehow calc.ManaMelee = calc.MeleeProcs * MeleeManaPerProc; remainingMana += calc.ManaMelee; calc.TotalMana += calc.ManaMelee; #endregion Melee mana regen #region Filler casts // now that we did everything from the options panel, fill in the rest of the available cast time // we will use Holy Light to fill in the time. If we still have mana left, we will replace Holy Light casts with Divine Light, until we run out of mana. // Note, when I kept the filler casts to whole numbers, Haste was not valued. Trying the other way to see how haste is valued then. // DL and HL have the same casttime, so lets just figure max number of casts with time remaining float IoLprocs = hs.Casts() * Talents.InfusionOfLight * hs.ChanceToCrit(); // Infusion of Light procs - -0.75 sec to next HL or DL casttime per point remainingTime += IoLprocs * 0.75f; // assuming we will be casting at least a few HL or DL, and can cast them when IoL procs, before next HS cast float fill_casts = /*(float)Math.Floor*/ (remainingTime / (hl.CastTime() + CalcOpts.Userdelay)); calc.HLCasts = 0; // Holy Light calc.DLCasts = 0; // Divine Light calc.FoLCasts = 0; float mana_fill_hl = fill_casts * hl.BaseMana; float mana_fill_dl = fill_casts * dl.BaseMana; if (remainingMana < mana_fill_hl) // if you would run out of mana just casting Holy Light { // then figure out how many Holy Lights you can cast // calc.HLCasts = /*(float)Math.Floor*/(remainingMana / hl.BaseMana); float timeleft = calc.HLCasts * (hl.CastTime() + +CalcOpts.Userdelay); float MeleeMPS = calc.MeleeProcs / MeleeManaPerProc; calc.HLCasts = ((remainingMana / (hl.MPS() - MeleeMPS) / hl.CastTime())); // use time to spare to melee for more mana, for more HL casts remainingTime -= calc.HLCasts * (hl.CastTime() + +CalcOpts.Userdelay); float moremeleemana = remainingTime / SwingTime * MeleeManaPerProc; calc.RotationMelee = remainingTime; calc.MeleeProcs += remainingTime / SwingTime; calc.ManaMelee += moremeleemana; calc.TotalMana += moremeleemana; } else if (remainingMana < mana_fill_dl) // else if you would run out of mana just casting Divine Light { remainingMana -= fill_casts * hl.BaseMana; // how much mana do we have to spare if we casts all Holy Lights calc.DLCasts = /*(float)Math.Floor*/ (remainingMana / (dl.BaseMana - hl.BaseMana)); calc.HLCasts = fill_casts - calc.DLCasts; } else // my God, you have a crapton of mana, start casting Flash of Light { calc.DLCasts = (IoLprocs * 0.75f) / dl.CastTime(); remainingTime -= IoLprocs * 0.75f; remainingMana -= calc.DLCasts * dl.BaseMana; remainingMana -= fill_casts * dl.BaseMana; // how much mana do we have to spare if we casts all Divine Lights calc.FoLCasts = /*(float)Math.Floor*/ ((remainingMana / (fol.MPS() - dl.MPS()) / fol.CastTime())); if (calc.FoLCasts > (remainingTime / (fol.CastTime() + CalcOpts.Userdelay))) { calc.FoLCasts = remainingTime / (fol.CastTime() + CalcOpts.Userdelay); } remainingTime -= calc.FoLCasts * (fol.CastTime() + CalcOpts.Userdelay); calc.DLCasts += /*(float)Math.Floor*/ (remainingTime / (dl.CastTime() + CalcOpts.Userdelay)); } calc.RotationHL = calc.HLCasts * (hl.CastTime() + CalcOpts.Userdelay); calc.UsageHL = calc.HLCasts * hl.BaseMana; calc.HealedHL = calc.HLCasts * hl.AverageHealed(); calc.RotationDL = calc.DLCasts * (dl.CastTime() + CalcOpts.Userdelay); calc.UsageDL = calc.DLCasts * dl.BaseMana; calc.HealedDL = calc.DLCasts * dl.AverageHealed(); calc.RotationFoL = calc.FoLCasts * (fol.CastTime() + CalcOpts.Userdelay); calc.UsageFoL = calc.FoLCasts * fol.BaseMana; calc.HealedFoL = calc.FoLCasts * fol.AverageHealed(); if (calc.RotationHL > (IoLprocs * 0.75f)) { calc.RotationHL -= IoLprocs * 0.75f; } else if (calc.RotationDL > (IoLprocs * 0.75f)) { calc.RotationDL -= IoLprocs * 0.75f; } #endregion Filler Casts #region Conviction // does enlightened judgements crit seperately from judgement? does it count towards conviction proc? // does Protector of the Innocent crit seperately? does it count towards conviction proc? // I'm assuming yes for all these questions for now. // Frankly this was my first pass at modeling Conviction. It's far from perfect, but the results are // in the same ballpark as combatlogs, and should be somewhat accurate in this talent's effect on // the value of crit. float ConvictionUptime; // time having 3 stacks up float ConvictionCasts = 2f * (calc.HLCasts + calc.DLCasts + calc.FoLCasts + calc.WoGCasts + calc.JudgeCasts + calc.HSCasts) + calc.LoHCasts + calc.LoDCasts * CalcOpts.LoDTargets * (5f + (Talents.GlyphOfLightOfDawn ? 1f : 0f)) + calc.MeleeSwings; float CastsPerSec = ConvictionCasts / ActiveTime; // Average crit chance is for all spells. Start with spellcrit, then add extra for each spell. float AverageCritChance = Stats.SpellCrit + (hs.Casts() / ConvictionCasts * (hs.ChanceToCrit() - Stats.SpellCrit)) + (calc.HLCasts / ConvictionCasts * (hl.ChanceToCrit() - Stats.SpellCrit)); float ChancePerCastToDrop = (float)Math.Pow((1 - AverageCritChance), (CastsPerSec * 15)); float FullStackTime = // average time we keep 3 stacks once we have it // might not be the best way, but for now using the time to get to 60% chance the stack has dropped // considering the tail 40% can take very long to drop potentially, pulling the median out past 50% // combat logs show this is the correct ballpark 1f / CastsPerSec * (float)Math.Log(0.4f, (1f - ChancePerCastToDrop)); float AverageStackBuildTime = // average time to build from 0 to 3 stacks 1f / CastsPerSec * (3 / AverageCritChance); // time to get 3 stacks, assuming you dont drop the stack along the way // now should add time for if stacks drop along the way float StackBuilds = // number of times we will build the stack in the fight 1 + (ActiveTime - AverageStackBuildTime) / (AverageStackBuildTime + FullStackTime); ConvictionUptime = StackBuilds * AverageStackBuildTime / 3f + // lets count stack builds as having 1 stack on average for now (StackBuilds - 1f) * FullStackTime; float ConvictionBuff = 1f + ConvictionUptime / ActiveTime * 0.03f * Talents.Conviction; calc.HealedHL *= ConvictionBuff; calc.HealedDL *= ConvictionBuff; calc.HealedFoL *= ConvictionBuff; calc.HealedHS *= ConvictionBuff; calc.HealedWoG *= ConvictionBuff; calc.HealedLoD *= ConvictionBuff; calc.HealedLoH *= ConvictionBuff; #endregion Conviction #region Talent heals: Enlightened Judgement, Protector of the Innocent, Illuminated healing, Beacon // Enlightened Judgement talent heals calc.HealedJudge = calc.JudgeCasts * ej.AverageHealed() * ConvictionBuff; // Protector of the Innocent calc.PotICasts = fill_casts + calc.WoGCasts + calc.LoDCasts + calc.LoHCasts + calc.HSCasts; calc.HealedPotI = calc.PotICasts * poti.AverageHealed() * ConvictionBuff; calc.UsageTotal = calc.UsageFoL + calc.UsageDL + calc.UsageHL + calc.UsageLoD + calc.UsageWoG + calc.UsageHS + calc.UsageHR + calc.UsageJudge + calc.UsageBoL + calc.UsageCleanse; calc.RotationTotal = calc.RotationFoL + calc.RotationDL + calc.RotationHL + calc.RotationLoD + calc.RotationWoG + calc.RotationHS + calc.RotationHR + calc.RotationJudge + calc.RotationBoL + calc.RotationLoH + calc.RotationCleanse + calc.RotationMelee + RotationDP; // Illumnated Healing calc.HealedIH = calc.HealedFoL + calc.HealedHL + calc.HealedHS + calc.HealedWoG + calc.HealedLoD + calc.HealedDL; calc.TotalHealed = calc.HealedIH + calc.HealedPotI + calc.HealedJudge; // BoL doesn't use LoH, but Illuminated healing does - so add LoH to IH and do final IH calcs calc.HealedIH += calc.HealedLoH; calc.HealedIH *= CalcOpts.IHEff * (0.12f + ((8 + Stats.MasteryRating / 179.28f) * 0.015f)); // Beacon of Light calc.HealedBoL = bol.HealingDone(calc.TotalHealed + calc.HealedHL); // added HL heals again to reflect 4.2 change, where HL transferes 100% to beacon target, rather than 50% for everything else #endregion // adding Holy Radiance and LoH after BoL calculation, as they do not count towards BoL heals calc.TotalHealed += calc.HealedHR + calc.HealedLoH + calc.HealedIH + calc.HealedBoL; calc.HealedOther = Stats.Healed + Stats.HealedPerSP * calc.SpellPowerTotal; calc.HealedOther += calc.TotalHealed * Stats.ShieldFromHealedProc; calc.TotalHealed += calc.HealedOther; calc.AvgHPS = calc.TotalHealed / FightLength; calc.AvgHPM = calc.TotalHealed / calc.TotalMana; return(calc.AvgHPS * (1f - CalcOpts.BurstScale)); }
public override ComparisonCalculationBase[] GetCustomChartData(Character character, string chartName) { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) { calc = new CharacterCalculationsHealadin(); } ComparisonCalculationHealadin FoL = new ComparisonCalculationHealadin("Flash of Light"); ComparisonCalculationHealadin HL11 = new ComparisonCalculationHealadin("Holy Light 11"); ComparisonCalculationHealadin HL10 = new ComparisonCalculationHealadin("Holy Light 10"); ComparisonCalculationHealadin HL9 = new ComparisonCalculationHealadin("Holy Light 9"); ComparisonCalculationHealadin HL8 = new ComparisonCalculationHealadin("Holy Light 8"); ComparisonCalculationHealadin HL7 = new ComparisonCalculationHealadin("Holy Light 7"); ComparisonCalculationHealadin HL6 = new ComparisonCalculationHealadin("Holy Light 6"); ComparisonCalculationHealadin HL5 = new ComparisonCalculationHealadin("Holy Light 5"); ComparisonCalculationHealadin HL4 = new ComparisonCalculationHealadin("Holy Light 4"); CalculationOptionsHealadin calcOpts = character.CalculationOptions as CalculationOptionsHealadin; if (calcOpts == null) { calcOpts = new CalculationOptionsHealadin(); } calc[0] = new Spell("Flash of Light", 7, calcOpts.BoL); calc[1] = new Spell("Holy Light", 11, calcOpts.BoL); calc[2] = new Spell("Holy Light", 10, calcOpts.BoL); calc[3] = new Spell("Holy Light", 9, calcOpts.BoL); calc[4] = new Spell("Holy Light", 8, calcOpts.BoL); calc[5] = new Spell("Holy Light", 7, calcOpts.BoL); calc[6] = new Spell("Holy Light", 6, calcOpts.BoL); calc[7] = new Spell("Holy Light", 5, calcOpts.BoL); calc[8] = new Spell("Holy Light", 4, calcOpts.BoL); switch (chartName) { case "Healing per second": FoL.OverallPoints = FoL.ThroughputPoints = calc[0].Hps; HL11.OverallPoints = HL11.ThroughputPoints = calc[1].Hps; HL10.OverallPoints = HL10.ThroughputPoints = calc[2].Hps; HL9.OverallPoints = HL9.ThroughputPoints = calc[3].Hps; HL8.OverallPoints = HL8.ThroughputPoints = calc[4].Hps; HL7.OverallPoints = HL7.ThroughputPoints = calc[5].Hps; HL6.OverallPoints = HL6.ThroughputPoints = calc[6].Hps; HL5.OverallPoints = HL5.ThroughputPoints = calc[7].Hps; HL4.OverallPoints = HL4.ThroughputPoints = calc[8].Hps; break; case "Average heal": FoL.OverallPoints = FoL.ThroughputPoints = calc[0].AverageHeal; HL11.OverallPoints = HL11.ThroughputPoints = calc[1].AverageHeal; HL10.OverallPoints = HL10.ThroughputPoints = calc[2].AverageHeal; HL9.OverallPoints = HL9.ThroughputPoints = calc[3].AverageHeal; HL8.OverallPoints = HL8.ThroughputPoints = calc[4].AverageHeal; HL7.OverallPoints = HL7.ThroughputPoints = calc[5].AverageHeal; HL6.OverallPoints = HL6.ThroughputPoints = calc[6].AverageHeal; HL5.OverallPoints = HL5.ThroughputPoints = calc[7].AverageHeal; HL4.OverallPoints = HL4.ThroughputPoints = calc[8].AverageHeal; break; case "Healing per mana": FoL.OverallPoints = FoL.LongevityPoints = calc[0].Hpm; HL11.OverallPoints = HL11.LongevityPoints = calc[1].Hpm; HL10.OverallPoints = HL10.LongevityPoints = calc[2].Hpm; HL9.OverallPoints = HL9.LongevityPoints = calc[3].Hpm; HL8.OverallPoints = HL8.LongevityPoints = calc[4].Hpm; HL7.OverallPoints = HL7.LongevityPoints = calc[5].Hpm; HL6.OverallPoints = HL6.LongevityPoints = calc[6].Hpm; HL5.OverallPoints = HL5.LongevityPoints = calc[7].Hpm; HL4.OverallPoints = HL4.LongevityPoints = calc[8].Hpm; break; case "Mana per second": FoL.OverallPoints = FoL.LongevityPoints = calc[0].Mps; HL11.OverallPoints = HL11.LongevityPoints = calc[1].Mps; HL10.OverallPoints = HL10.LongevityPoints = calc[2].Mps; HL9.OverallPoints = HL9.LongevityPoints = calc[3].Mps; HL8.OverallPoints = HL8.LongevityPoints = calc[4].Mps; HL7.OverallPoints = HL7.LongevityPoints = calc[5].Mps; HL6.OverallPoints = HL6.LongevityPoints = calc[6].Mps; HL5.OverallPoints = HL5.LongevityPoints = calc[7].Mps; HL4.OverallPoints = HL4.LongevityPoints = calc[8].Mps; break; } return(new ComparisonCalculationBase[] { FoL, HL11, HL10, HL9, HL8, HL7, HL6, HL5, HL4 }); }
public float CalculateBurstHealing(CharacterCalculationsHealadin calc) { return fol.HPS() * CalcOpts.BurstScale; }
public static float GetHealingCritsPerSec(CharacterCalculationsHealadin calc) { return (calc.RotationHL / calc.HL.CastTime() * calc.HL.ChanceToCrit() + calc.RotationFoL / calc.FoL.CastTime() * calc.FoL.ChanceToCrit() + calc.RotationHS / calc.HS.CastTime() * calc.HS.ChanceToCrit()) / calc.FightLength; }
public static float GetSpellCastsPerSec(CharacterCalculationsHealadin calc) { return GetHealingCastsPerSec(calc) + (calc.RotationBoL / calc.BoL.CastTime() + calc.RotationJudge / calc.JotP.CastTime()) / calc.FightLength; }
public override ComparisonCalculationBase[] GetCustomChartData(Character character, string chartName) { if (chartName == "Mana Pool Breakdown (needs updating)") { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) calc = new CharacterCalculationsHealadin(); ComparisonCalculationHealadin Base = new ComparisonCalculationHealadin("Base"); ComparisonCalculationHealadin Mp5 = new ComparisonCalculationHealadin("Mp5"); ComparisonCalculationHealadin Replenishment = new ComparisonCalculationHealadin("Replenishment"); ComparisonCalculationHealadin ArcaneTorrent = new ComparisonCalculationHealadin("Arcane Torrent"); ComparisonCalculationHealadin DivinePlea = new ComparisonCalculationHealadin("Divine Plea"); ComparisonCalculationHealadin LoH = new ComparisonCalculationHealadin("Lay on Hands"); ComparisonCalculationHealadin Other = new ComparisonCalculationHealadin("Potion & Other"); Base.OverallPoints = Base.ThroughputPoints = calc.ManaBase; Mp5.OverallPoints = Mp5.ThroughputPoints = calc.ManaMp5; LoH.OverallPoints = LoH.ThroughputPoints = calc.ManaLayOnHands; Replenishment.OverallPoints = Replenishment.ThroughputPoints = calc.ManaReplenishment; ArcaneTorrent.OverallPoints = ArcaneTorrent.ThroughputPoints = calc.ManaArcaneTorrent; DivinePlea.OverallPoints = DivinePlea.ThroughputPoints = calc.ManaDivinePlea; Other.OverallPoints = Other.ThroughputPoints = calc.ManaOther; return new ComparisonCalculationBase[] { Base, Mp5, Replenishment, LoH, ArcaneTorrent, DivinePlea, Other }; } else if (chartName == "Mana Usage Breakdown (needs updating)") { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) calc = new CharacterCalculationsHealadin(); ComparisonCalculationHealadin FoL = new ComparisonCalculationHealadin("Flash of Light"); ComparisonCalculationHealadin HL = new ComparisonCalculationHealadin("Holy Light"); ComparisonCalculationHealadin HS = new ComparisonCalculationHealadin("Holy Shock"); ComparisonCalculationHealadin JotP = new ComparisonCalculationHealadin("Judgements and Seals"); ComparisonCalculationHealadin BoL = new ComparisonCalculationHealadin("Beacon of Light"); FoL.OverallPoints = FoL.ThroughputPoints = calc.UsageFoL; HL.OverallPoints = HL.ThroughputPoints = calc.UsageHL; HS.OverallPoints = HS.ThroughputPoints = calc.UsageHS; JotP.OverallPoints = JotP.ThroughputPoints = calc.UsageJudge; BoL.OverallPoints = BoL.ThroughputPoints = calc.UsageBoL; return new ComparisonCalculationBase[] { FoL, HL, HS, JotP, BoL }; } else if (chartName == "Healing Breakdown (needs updating)") { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) calc = new CharacterCalculationsHealadin(); ComparisonCalculationHealadin FoL = new ComparisonCalculationHealadin("Flash of Light"); ComparisonCalculationHealadin HL = new ComparisonCalculationHealadin("Holy Light"); ComparisonCalculationHealadin HS = new ComparisonCalculationHealadin("Holy Shock"); ComparisonCalculationHealadin GHL = new ComparisonCalculationHealadin("Glyph of Holy Light"); ComparisonCalculationHealadin BoL = new ComparisonCalculationHealadin("Beacon of Light"); FoL.OverallPoints = FoL.ThroughputPoints = calc.HealedFoL / calc.FightLength; HL.OverallPoints = HL.ThroughputPoints = calc.HealedHL / calc.FightLength; HS.OverallPoints = HS.ThroughputPoints = calc.HealedHS / calc.FightLength; GHL.OverallPoints = GHL.ThroughputPoints = calc.HealedGHL / calc.FightLength; BoL.OverallPoints = BoL.ThroughputPoints = calc.HealedBoL / calc.FightLength; return new ComparisonCalculationBase[] { FoL, HL, HS, GHL, BoL }; } else if (chartName == "Rotation Breakdown (needs updating)") { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) calc = new CharacterCalculationsHealadin(); ComparisonCalculationHealadin FoL = new ComparisonCalculationHealadin("Flash of Light"); ComparisonCalculationHealadin HL = new ComparisonCalculationHealadin("Holy Light"); ComparisonCalculationHealadin HS = new ComparisonCalculationHealadin("Holy Shock"); ComparisonCalculationHealadin JotP = new ComparisonCalculationHealadin("Judgements and Seals"); ComparisonCalculationHealadin BoL = new ComparisonCalculationHealadin("Beacon of Light"); FoL.OverallPoints = FoL.ThroughputPoints = calc.RotationFoL; HL.OverallPoints = HL.ThroughputPoints = calc.RotationHL; HS.OverallPoints = HS.ThroughputPoints = calc.RotationHS; JotP.OverallPoints = JotP.ThroughputPoints = calc.RotationJudge; BoL.OverallPoints = BoL.ThroughputPoints = calc.RotationBoL; return new ComparisonCalculationBase[] { FoL, HL, HS, JotP, BoL }; } return new ComparisonCalculationBase[] {}; }
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 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); }
public float CalculateFightHealing(CharacterCalculationsHealadin calc) { float fol_BaseHealed = 0f; #region Copying Stuff to Calc calc.FightLength = FightLength; calc.HL = hl; calc.FoL = fol; calc.HS = hs; calc.SS = ss; calc.JotP = jotp; calc.BoL = bol; calc.RotationJotP = jotp.Time(); calc.UsageJotP = jotp.Usage(); calc.RotationSS = ss.Time(); calc.UsageSS = ss.Usage(); calc.HealedSS = ss.TotalAborb(); calc.RotationBoL = bol.Time(); calc.UsageBoL = bol.Usage(); calc.RotationHS = hs.Time(); calc.HealedHS = hs.Healed(); calc.UsageHS = hs.Usage(); #endregion #region Divine Illumination if (Talents.DivineIllumination > 0) { DivineIllumination di = new DivineIllumination(this); calc.RotationHL += di.Time(); calc.UsageHL += di.Usage(); calc.HealedHL += di.Healed(); } #endregion #region Divine Favor if (Talents.DivineFavor > 0) { DivineFavor df = new DivineFavor(this); calc.RotationHL += df.Time(); calc.UsageHL += df.Usage(); calc.HealedHL += df.Healed(); } #endregion #region Infusion of Light float iol_hlcasts = 0; float iol_folcasts = 0; if (CalcOpts.InfusionOfLight) { float iol_count = hs.Casts() * hs.ChanceToCrit(); HolyLight hl_iol = new HolyLight(this) { ExtraCritChance = .1f * Talents.InfusionOfLight }; if (Stats.HolyLightCastTimeReductionFromHolyShock > 0) { hl_iol.CastTimeReductionFromHolyShock = true; } iol_hlcasts = hs.Casts() * CalcOpts.IoLHolyLight * hs.ChanceToCrit(); calc.UsageHL += iol_hlcasts * hl_iol.AverageCost(); calc.RotationHL += iol_hlcasts * hl_iol.CastTime(); calc.HealedHL += iol_hlcasts * hl_iol.AverageHealed(); iol_folcasts = hs.Casts() * (1f - CalcOpts.IoLHolyLight) * hs.ChanceToCrit(); } #endregion float remainingMana = calc.TotalMana = ManaPool(calc); remainingMana -= calc.UsageJotP + calc.UsageBoL + calc.UsageHS + calc.UsageHL + calc.UsageFoL + calc.UsageSS; float remainingTime = FightLength * CalcOpts.Activity; remainingTime -= calc.RotationJotP + calc.RotationBoL + calc.RotationSS + calc.RotationHS + calc.RotationFoL + calc.RotationHL; FoLCasts = 0f; if (remainingMana > 0) { if (Stats.HolyLightCastTimeReductionFromHolyShock > 0) // Calculations for Holy Light with cost reduction from Holy Shock { // Get the Holy Light model for cast time reduction from Holy Shock HolyLight hl_hs = new HolyLight(this) { CastTimeReductionFromHolyShock = true }; // Calculate how much time we have available to cast Holy Lights after Holy Shock float hl_hs_time_available = Math.Min(remainingTime, Math.Max(0, (remainingMana - (remainingTime * fol.MPS())) / (hl_hs.MPS() - fol.MPS()))); // Calculate the maximum number of casts available with the cast time reduction float hs_hlcasts = hs.Casts() - iol_hlcasts; // Calculate the amount of time needed for all of the casts float hl_hs_time_needed = hs_hlcasts * hl_hs.CastTime(); float manaCost = 0f; float timeCost = 0f; if (hl_hs_time_available > 0 && hl_hs_time_available < hl_hs_time_needed) // We have enough time to cast the Holy Lights needed, cast them all { // Calculate mana and time cost manaCost = hs_hlcasts * hl_hs.AverageCost(); timeCost = hs_hlcasts * hl_hs.CastTime(); // Update Holy Light stats calc.UsageHL += manaCost; calc.RotationHL += timeCost; calc.HealedHL += hs_hlcasts * hl_hs.AverageHealed(); } else if (hl_hs_time_available >= remainingTime) // There's not enough time to cast the Holy Lights needed, so we'll only cast what we can { // Calculate mana and time cost float remainingCasts = remainingTime / hl_hs.CastTime(); manaCost = remainingCasts * hl_hs.AverageCost(); timeCost = remainingCasts * hl_hs.CastTime(); // Update Holy Light stats calc.UsageHL += manaCost; calc.RotationHL += timeCost; calc.HealedHL += remainingCasts * hl_hs.AverageHealed(); } // Update remaining mana and remaining time remainingMana -= manaCost; remainingTime -= timeCost; } // Calculate how much time we have available to cast regular Holy Lights float hl_time_available = Math.Min(remainingTime, Math.Max(0, (remainingMana - (remainingTime * fol.MPS())) / (hl.MPS() - fol.MPS()))); // The rest of the time will be for Flash of Light float fol_time = remainingTime - hl_time_available; if (hl_time_available == 0) // If we didn't have any time available for Holy Lights, check to see when we run out of mana while casting Flash of Light { fol_time = Math.Min(remainingTime, remainingMana / fol.MPS()); } else // If we do have time available for Holy Lights, update the Holy Light stats { calc.HealedHL += hl.HPS() * hl_time_available; calc.UsageHL += hl.MPS() * hl_time_available; calc.RotationHL += hl_time_available; } // Calculate Flash of Light data if (iol_folcasts > 0) // We are using Infusion of Light, so we must calculate it differently than normal { // Get the Flash of Light model for Infusion of Light FlashOfLight fol_iol = new FlashOfLight(this) { InfusionOfLight = true }; // If there is less time remaining than it would take to cast all of the // Infusion of Light procs, we are forced to drop some of them. Note // that this should *never* happen, but is here for a sanity check. if (fol_time <= iol_folcasts * fol_iol.CastTime()) { // Determine how many total casts we are going to be able to do FoLCasts = fol_time / fol_iol.CastTime(); // Adding Flash of Light with Infusion of Light stats calc.UsageFoL += FoLCasts * fol_iol.AverageCost(); calc.RotationFoL += FoLCasts * fol_iol.CastTime(); calc.HealedFoL += FoLCasts * fol_iol.AverageHealed(); fol_BaseHealed += FoLCasts * fol_iol.BaseAverageHealed(); } else { // Adding Flash of Light with Infusion of Light calc.UsageFoL += iol_folcasts * fol_iol.AverageCost(); calc.RotationFoL += iol_folcasts * fol_iol.CastTime(); calc.HealedFoL += iol_folcasts * fol_iol.AverageHealed(); fol_BaseHealed += iol_folcasts * fol_iol.BaseAverageHealed(); // Determine how much time we have left to cast Flash of Light without the procs fol_time = fol_time - iol_folcasts * fol.CastTime(); // Determine how many total casts, with and without Infusion of Light float remaining_folcasts = fol_time / fol.CastTime(); FoLCasts = iol_folcasts + remaining_folcasts; // Adding Flash of Light stats calc.RotationFoL += fol_time; calc.UsageFoL += fol.MPS() * fol_time; calc.HealedFoL += fol.HPS() * fol_time; fol_BaseHealed += remaining_folcasts * fol.BaseAverageHealed(); } } else // We don't use Infusion of Light, so just calculate normal data for Flash of Light { // Determine how many total casts, with and without Infusion of Light FoLCasts = fol_time / fol.CastTime(); // Adding Flash of Light stats calc.RotationFoL += fol_time; calc.UsageFoL += fol.MPS() * fol_time; calc.HealedFoL += fol.HPS() * fol_time; fol_BaseHealed += FoLCasts * fol.BaseAverageHealed(); } } calc.TotalHealed = calc.HealedFoL + calc.HealedHL + calc.HealedHS; if (Talents.BeaconOfLight > 0) { calc.TotalHealed += calc.HealedBoL = bol.HealingDone(fol_BaseHealed + calc.HealedHL + calc.HealedHS); } calc.TotalHealed += calc.HealedGHL = hl.GlyphOfHolyLight(calc.HealedHL); calc.HealedOther = Stats.Healed; calc.HealedOther += calc.TotalHealed * Stats.ShieldFromHealed; calc.TotalHealed += calc.HealedOther; calc.TotalHealed += calc.HealedSS; calc.AvgHPS = calc.TotalHealed / FightLength; calc.AvgHPM = calc.TotalHealed / calc.TotalMana; return(calc.AvgHPS * (1f - CalcOpts.BurstScale)); }
public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations) { // First things first, we need to ensure that we aren't using bad data CharacterCalculationsHealadin calc = new CharacterCalculationsHealadin(); if (character == null) { return calc; } CalculationOptionsHealadin calcOpts = character.CalculationOptions as CalculationOptionsHealadin; if (calcOpts == null) { return calc; } // Stats stats; calc = null; PaladinTalents talents = character.PaladinTalents; for (int i = 0; i < 5; i++) { float oldBurst = 0; if (i > 0) oldBurst = calc.BurstPoints; stats = GetCharacterStats(character, additionalItem, true, calc); calc = new CharacterCalculationsHealadin(); Rotation rot = new Rotation(character, stats); if (i > 0) calc.BurstPoints = oldBurst; else calc.BurstPoints = rot.CalculateBurstHealing(calc); calc.FightPoints = rot.CalculateFightHealing(calc); calc.OverallPoints = calc.BurstPoints + calc.FightPoints; } calc.BasicStats = GetCharacterStats(character, additionalItem, false, null); return calc; }
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; }
public override ComparisonCalculationBase[] GetCustomChartData(Character character, string chartName) { if (chartName == "Mana Pool Breakdown") { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) { calc = new CharacterCalculationsHealadin(); } ComparisonCalculationHealadin Base = new ComparisonCalculationHealadin("Base"); ComparisonCalculationHealadin Mp5 = new ComparisonCalculationHealadin("Mp5"); ComparisonCalculationHealadin Replenishment = new ComparisonCalculationHealadin("Replenishment"); ComparisonCalculationHealadin ArcaneTorrent = new ComparisonCalculationHealadin("Arcane Torrent"); ComparisonCalculationHealadin DivinePlea = new ComparisonCalculationHealadin("Divine Plea"); ComparisonCalculationHealadin LoH = new ComparisonCalculationHealadin("Lay on Hands"); ComparisonCalculationHealadin Other = new ComparisonCalculationHealadin("Potion & Other"); Base.OverallPoints = Base.ThroughputPoints = calc.ManaBase; Mp5.OverallPoints = Mp5.ThroughputPoints = calc.ManaMp5; LoH.OverallPoints = LoH.ThroughputPoints = calc.ManaLayOnHands; Replenishment.OverallPoints = Replenishment.ThroughputPoints = calc.ManaReplenishment; ArcaneTorrent.OverallPoints = ArcaneTorrent.ThroughputPoints = calc.ManaArcaneTorrent; DivinePlea.OverallPoints = DivinePlea.ThroughputPoints = calc.ManaDivinePlea; Other.OverallPoints = Other.ThroughputPoints = calc.ManaOther; return(new ComparisonCalculationBase[] { Base, Mp5, Replenishment, LoH, ArcaneTorrent, DivinePlea, Other }); } else if (chartName == "Mana Usage Breakdown") { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) { calc = new CharacterCalculationsHealadin(); } ComparisonCalculationHealadin FoL = new ComparisonCalculationHealadin("Flash of Light"); ComparisonCalculationHealadin HL = new ComparisonCalculationHealadin("Holy Light"); ComparisonCalculationHealadin HS = new ComparisonCalculationHealadin("Holy Shock"); ComparisonCalculationHealadin JotP = new ComparisonCalculationHealadin("Judgements and Seals"); ComparisonCalculationHealadin BoL = new ComparisonCalculationHealadin("Beacon of Light"); ComparisonCalculationHealadin SS = new ComparisonCalculationHealadin("Sacred Shield"); FoL.OverallPoints = FoL.ThroughputPoints = calc.UsageFoL; HL.OverallPoints = HL.ThroughputPoints = calc.UsageHL; HS.OverallPoints = HS.ThroughputPoints = calc.UsageHS; JotP.OverallPoints = JotP.ThroughputPoints = calc.UsageJotP; BoL.OverallPoints = BoL.ThroughputPoints = calc.UsageBoL; SS.OverallPoints = SS.ThroughputPoints = calc.UsageSS; return(new ComparisonCalculationBase[] { FoL, HL, HS, JotP, BoL, SS }); } else if (chartName == "Healing Breakdown") { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) { calc = new CharacterCalculationsHealadin(); } ComparisonCalculationHealadin FoL = new ComparisonCalculationHealadin("Flash of Light"); ComparisonCalculationHealadin HL = new ComparisonCalculationHealadin("Holy Light"); ComparisonCalculationHealadin HS = new ComparisonCalculationHealadin("Holy Shock"); ComparisonCalculationHealadin GHL = new ComparisonCalculationHealadin("Glyph of Holy Light"); ComparisonCalculationHealadin BoL = new ComparisonCalculationHealadin("Beacon of Light"); ComparisonCalculationHealadin SS = new ComparisonCalculationHealadin("Sacred Shield"); FoL.OverallPoints = FoL.ThroughputPoints = calc.HealedFoL / calc.FightLength; HL.OverallPoints = HL.ThroughputPoints = calc.HealedHL / calc.FightLength; HS.OverallPoints = HS.ThroughputPoints = calc.HealedHS / calc.FightLength; GHL.OverallPoints = GHL.ThroughputPoints = calc.HealedGHL / calc.FightLength; BoL.OverallPoints = BoL.ThroughputPoints = calc.HealedBoL / calc.FightLength; SS.OverallPoints = SS.ThroughputPoints = calc.HealedSS / calc.FightLength; return(new ComparisonCalculationBase[] { FoL, HL, HS, GHL, BoL, SS }); } else if (chartName == "Rotation Breakdown") { CharacterCalculationsHealadin calc = GetCharacterCalculations(character) as CharacterCalculationsHealadin; if (calc == null) { calc = new CharacterCalculationsHealadin(); } ComparisonCalculationHealadin FoL = new ComparisonCalculationHealadin("Flash of Light"); ComparisonCalculationHealadin HL = new ComparisonCalculationHealadin("Holy Light"); ComparisonCalculationHealadin HS = new ComparisonCalculationHealadin("Holy Shock"); ComparisonCalculationHealadin JotP = new ComparisonCalculationHealadin("Judgements and Seals"); ComparisonCalculationHealadin BoL = new ComparisonCalculationHealadin("Beacon of Light"); ComparisonCalculationHealadin SS = new ComparisonCalculationHealadin("Sacred Shield"); FoL.OverallPoints = FoL.ThroughputPoints = calc.RotationFoL; HL.OverallPoints = HL.ThroughputPoints = calc.RotationHL; HS.OverallPoints = HS.ThroughputPoints = calc.RotationHS; JotP.OverallPoints = JotP.ThroughputPoints = calc.RotationJotP; BoL.OverallPoints = BoL.ThroughputPoints = calc.RotationBoL; SS.OverallPoints = SS.ThroughputPoints = calc.RotationSS; return(new ComparisonCalculationBase[] { FoL, HL, HS, JotP, BoL, SS }); } return(new ComparisonCalculationBase[] {}); }
public static float GetHolyLightCastsPerSec(CharacterCalculationsHealadin calc) { return (calc.RotationHL / calc.HL.CastTime()) / calc.FightLength; }
public static float GetHolyLightCastsPerSec(CharacterCalculationsHealadin calc) { return((calc.RotationHL / calc.HL.CastTime()) / calc.FightLength); }
public static float GetSpellCritsPerSec(CharacterCalculationsHealadin calc) { return GetHealingCritsPerSec(calc); }
public static float GetSpellCritsPerSec(CharacterCalculationsHealadin calc) { return(GetHealingCritsPerSec(calc)); }
public float CalculateFightHealing(CharacterCalculationsHealadin calc) { #region Copying Stuff to Calc calc.FightLength = FightLength; calc.HL = hl; calc.FoL = fol; calc.HS = hs; calc.DL = dl; calc.WoG = wog; calc.LoD = lod; calc.HR = hr; calc.JotP = jotp; calc.BoL = bol; calc.LoH = loh; calc.PotI = poti; calc.EJ = ej; calc.JudgeCasts = jotp.Casts(); calc.RotationJudge = jotp.Time() + CalcOpts.Userdelay * calc.JudgeCasts; calc.UsageJudge = jotp.Usage(); if (Talents.BeaconOfLight > 0) { calc.RotationBoL = bol.Time(); calc.UsageBoL = bol.Usage(); } calc.RotationHS = hs.Time() + hs.Casts() + CalcOpts.Userdelay; calc.HealedHS = hs.Healed(); calc.UsageHS = hs.Usage(); calc.RotationLoH = loh.Casts() * (loh.CastTime() + CalcOpts.Userdelay); calc.HealedLoH = loh.Casts() * loh.AverageHealed(); calc.UsageLoH = 0f; // always 0, costs no mana, code was: loh.Casts() * loh.BaseMana; calc.UsageCleanse = CalcOpts.Cleanse * cleanse.BaseMana; calc.RotationCleanse = CalcOpts.Cleanse * ( cleanse.CastTime() + CalcOpts.Userdelay); calc.CleanseCasts = CalcOpts.Cleanse; #endregion Stats.BonusCritChanceObliterate = CalcOpts.CritOverheals; // I really need to put some variables in Stats that are for holy pally calc.HasteJotP = Talents.JudgementsOfThePure * 3f; calc.HasteSoL = Talents.SpeedOfLight * 1f; calc.SpellPowerTotal = Stats.Intellect + Stats.SpellPower; #region Divine Favor - old code, commented out for now /* if (Talents.DivineFavor > 0) { DivineFavor df = new DivineFavor(this); calc.RotationHL += df.Time(); calc.UsageHL += df.Usage(); calc.HealedHL += df.Healed(); } */ #endregion #region active time, remaining mana / time float remainingMana = calc.TotalMana = ManaPool(calc); remainingMana -= calc.UsageJudge + calc.UsageBoL + calc.UsageHS + calc.UsageHR + calc.UsageCleanse; // start with amount of time you are active in a fight calc.ActiveTime = FightLength * CalcOpts.Activity; float remainingTime = calc.ActiveTime; // now subtract time for lots of stuff we need to cast remainingTime -= calc.RotationJudge + calc.RotationBoL + calc.RotationHS + calc.RotationLoH + calc.RotationHR + calc.RotationCleanse; float RotationDP = DivinePleas * (calc.HS.CastTime() + CalcOpts.Userdelay); remainingTime -= RotationDP; // subtract divine plea cast times. HS is also a GCD, so I just used that to calculate it #endregion #region HS and holy power calc.HolyPowerCasts = hs.Casts() / 3f; if (Talents.LightOfDawn != 0) { calc.LoDCasts = (float)Math.Floor(calc.HolyPowerCasts * CalcOpts.HolyPoints); calc.WoGCasts = (float)Math.Floor(calc.HolyPowerCasts * (1f - CalcOpts.HolyPoints)); } else { calc.WoGCasts = (float)Math.Floor(calc.HolyPowerCasts); calc.LoDCasts = 0; } calc.RotationWoG = calc.WoGCasts * (wog.CastTime() + CalcOpts.Userdelay); calc.UsageWoG = calc.WoGCasts * wog.BaseMana; calc.HealedWoG = calc.WoGCasts * wog.AverageHealed(); calc.RotationLoD = calc.LoDCasts * (lod.CastTime() + CalcOpts.Userdelay); calc.UsageLoD = calc.LoDCasts * lod.BaseMana; calc.HealedLoD = calc.LoDCasts * lod.AverageHealed() * CalcOpts.LoDTargets; remainingTime -= calc.RotationWoG + calc.RotationLoD; #endregion #region Holy Radiance calc.HRCasts = (float)Math.Floor(FightLength / CalcOpts.HRCasts); calc.RotationHR = calc.HRCasts * (hr.CastTime() + CalcOpts.Userdelay); calc.UsageHR = calc.HRCasts * hr.BaseMana; calc.HealedHR = calc.HRCasts * hr.AverageHealed() * CalcOpts.HREff; remainingTime -= calc.RotationHR; #endregion #region Melee mana regen // Melee hits generate 4% base mana. From what I read, this proc has a 4 sec CD. 15ppm max. float InstantCastRotationTotal = calc.RotationLoD + calc.RotationWoG + calc.RotationHS + calc.RotationHR + calc.RotationJudge + calc.RotationBoL + calc.RotationLoH + calc.RotationCleanse; float MeleeTime = InstantCastRotationTotal * CalcOpts.Melee; // Ideally the SwingTime would be the weapon speed, then logic would be added to estimate the # of procs. As the # of swings increases, // the number of procs would have dimishing returns. (because more frequent swings means more swings happen during the 4 sec CD, and don't proc) // For now I am going to fudge it and estimate a proc every 5 secs. Every 4 is unrealistic since 4 is not divisable by weapon speed, // so some time is always wasted. Also consider when one alternates between swings and casts, then each swing would proc. Whatever, 5 is good enough for now. float SwingTime = 5f; float MeleeManaPerProc = HealadinConstants.basemana * 0.04f; // calc.MeleeSwings = MeleeTime / SwingTime; calc.MeleeProcs = MeleeTime / SwingTime; // find a way to model this better, as swings approaches MaxMeleeProcs, it should have diminishing returns somehow calc.ManaMelee = calc.MeleeProcs * MeleeManaPerProc; remainingMana += calc.ManaMelee; calc.TotalMana += calc.ManaMelee; #endregion Melee mana regen #region Filler casts // now that we did everything from the options panel, fill in the rest of the available cast time // we will use Holy Light to fill in the time. If we still have mana left, we will replace Holy Light casts with Divine Light, until we run out of mana. // Note, when I kept the filler casts to whole numbers, Haste was not valued. Trying the other way to see how haste is valued then. // DL and HL have the same casttime, so lets just figure max number of casts with time remaining float IoLprocs = hs.Casts() * Talents.InfusionOfLight * hs.ChanceToCrit(); // Infusion of Light procs - -0.75 sec to next HL or DL casttime per point remainingTime += IoLprocs * 0.75f; // assuming we will be casting at least a few HL or DL, and can cast them when IoL procs, before next HS cast float fill_casts = /*(float)Math.Floor*/(remainingTime / (hl.CastTime() + CalcOpts.Userdelay)); calc.HLCasts = 0; // Holy Light calc.DLCasts = 0; // Divine Light calc.FoLCasts = 0; float mana_fill_hl = fill_casts * hl.BaseMana; float mana_fill_dl = fill_casts * dl.BaseMana; if (remainingMana < mana_fill_hl) // if you would run out of mana just casting Holy Light { // then figure out how many Holy Lights you can cast // calc.HLCasts = /*(float)Math.Floor*/(remainingMana / hl.BaseMana); float timeleft = calc.HLCasts * (hl.CastTime() + + CalcOpts.Userdelay); float MeleeMPS = calc.MeleeProcs / MeleeManaPerProc; calc.HLCasts = ((remainingMana / (hl.MPS() - MeleeMPS) / hl.CastTime())); // use time to spare to melee for more mana, for more HL casts remainingTime -= calc.HLCasts * (hl.CastTime() + + CalcOpts.Userdelay); float moremeleemana = remainingTime / SwingTime * MeleeManaPerProc; calc.RotationMelee = remainingTime; calc.MeleeProcs += remainingTime / SwingTime; calc.ManaMelee += moremeleemana; calc.TotalMana += moremeleemana; } else if (remainingMana < mana_fill_dl) // else if you would run out of mana just casting Divine Light { remainingMana -= fill_casts * hl.BaseMana; // how much mana do we have to spare if we casts all Holy Lights calc.DLCasts = /*(float)Math.Floor*/(remainingMana / (dl.BaseMana - hl.BaseMana)); calc.HLCasts = fill_casts - calc.DLCasts; } else // my God, you have a crapton of mana, start casting Flash of Light { calc.DLCasts = (IoLprocs * 0.75f) / dl.CastTime(); remainingTime -= IoLprocs * 0.75f; remainingMana -= calc.DLCasts * dl.BaseMana; remainingMana -= fill_casts * dl.BaseMana; // how much mana do we have to spare if we casts all Divine Lights calc.FoLCasts = /*(float)Math.Floor*/((remainingMana / (fol.MPS() - dl.MPS()) / fol.CastTime())); if (calc.FoLCasts > (remainingTime / (fol.CastTime() + CalcOpts.Userdelay))) calc.FoLCasts = remainingTime / (fol.CastTime() + CalcOpts.Userdelay); remainingTime -= calc.FoLCasts * (fol.CastTime() + CalcOpts.Userdelay); calc.DLCasts += /*(float)Math.Floor*/(remainingTime / (dl.CastTime() + CalcOpts.Userdelay)); } calc.RotationHL = calc.HLCasts * (hl.CastTime() + CalcOpts.Userdelay); calc.UsageHL = calc.HLCasts * hl.BaseMana; calc.HealedHL = calc.HLCasts * hl.AverageHealed(); calc.RotationDL = calc.DLCasts * (dl.CastTime() + CalcOpts.Userdelay); calc.UsageDL = calc.DLCasts * dl.BaseMana; calc.HealedDL = calc.DLCasts * dl.AverageHealed(); calc.RotationFoL = calc.FoLCasts * (fol.CastTime() + CalcOpts.Userdelay); calc.UsageFoL = calc.FoLCasts * fol.BaseMana; calc.HealedFoL = calc.FoLCasts * fol.AverageHealed(); if (calc.RotationHL > (IoLprocs * 0.75f)) calc.RotationHL -= IoLprocs * 0.75f; else if (calc.RotationDL > (IoLprocs * 0.75f)) calc.RotationDL -= IoLprocs * 0.75f; #endregion Filler Casts #region Conviction // does enlightened judgements crit seperately from judgement? does it count towards conviction proc? // does Protector of the Innocent crit seperately? does it count towards conviction proc? // I'm assuming yes for all these questions for now. // Frankly this was my first pass at modeling Conviction. It's far from perfect, but the results are // in the same ballpark as combatlogs, and should be somewhat accurate in this talent's effect on // the value of crit. float ConvictionUptime; // time having 3 stacks up float ConvictionCasts = 2f * (calc.HLCasts + calc.DLCasts + calc.FoLCasts + calc.WoGCasts + calc.JudgeCasts + calc.HSCasts) + calc.LoHCasts + calc.LoDCasts * CalcOpts.LoDTargets * (5f + (Talents.GlyphOfLightOfDawn ? 1f : 0f)) + calc.MeleeSwings; float CastsPerSec = ConvictionCasts / ActiveTime; // Average crit chance is for all spells. Start with spellcrit, then add extra for each spell. float AverageCritChance = Stats.SpellCrit + (hs.Casts() / ConvictionCasts * (hs.ChanceToCrit() - Stats.SpellCrit)) + (calc.HLCasts / ConvictionCasts * (hl.ChanceToCrit() - Stats.SpellCrit)); float ChancePerCastToDrop = (float)Math.Pow((1 - AverageCritChance), (CastsPerSec * 15)); float FullStackTime = // average time we keep 3 stacks once we have it // might not be the best way, but for now using the time to get to 60% chance the stack has dropped // considering the tail 40% can take very long to drop potentially, pulling the median out past 50% // combat logs show this is the correct ballpark 1f / CastsPerSec * (float)Math.Log(0.4f, (1f - ChancePerCastToDrop)); float AverageStackBuildTime = // average time to build from 0 to 3 stacks 1f / CastsPerSec * (3 / AverageCritChance); // time to get 3 stacks, assuming you dont drop the stack along the way // now should add time for if stacks drop along the way float StackBuilds = // number of times we will build the stack in the fight 1 + (ActiveTime - AverageStackBuildTime) / (AverageStackBuildTime + FullStackTime); ConvictionUptime = StackBuilds * AverageStackBuildTime / 3f + // lets count stack builds as having 1 stack on average for now (StackBuilds - 1f) * FullStackTime; float ConvictionBuff = 1f + ConvictionUptime / ActiveTime * 0.03f * Talents.Conviction; calc.HealedHL *= ConvictionBuff; calc.HealedDL *= ConvictionBuff; calc.HealedFoL *= ConvictionBuff; calc.HealedHS *= ConvictionBuff; calc.HealedWoG *= ConvictionBuff; calc.HealedLoD *= ConvictionBuff; calc.HealedLoH *= ConvictionBuff; #endregion Conviction #region Talent heals: Enlightened Judgement, Protector of the Innocent, Illuminated healing, Beacon // Enlightened Judgement talent heals calc.HealedJudge = calc.JudgeCasts * ej.AverageHealed() * ConvictionBuff; // Protector of the Innocent calc.PotICasts = fill_casts + calc.WoGCasts + calc.LoDCasts + calc.LoHCasts + calc.HSCasts; calc.HealedPotI = calc.PotICasts * poti.AverageHealed() * ConvictionBuff; calc.UsageTotal = calc.UsageFoL + calc.UsageDL + calc.UsageHL + calc.UsageLoD + calc.UsageWoG + calc.UsageHS + calc.UsageHR + calc.UsageJudge + calc.UsageBoL + calc.UsageCleanse; calc.RotationTotal = calc.RotationFoL + calc.RotationDL + calc.RotationHL + calc.RotationLoD + calc.RotationWoG + calc.RotationHS + calc.RotationHR + calc.RotationJudge + calc.RotationBoL + calc.RotationLoH + calc.RotationCleanse + calc.RotationMelee + RotationDP; // Illumnated Healing calc.HealedIH = calc.HealedFoL + calc.HealedHL + calc.HealedHS + calc.HealedWoG + calc.HealedLoD + calc.HealedDL; calc.TotalHealed = calc.HealedIH + calc.HealedPotI + calc.HealedJudge; // BoL doesn't use LoH, but Illuminated healing does - so add LoH to IH and do final IH calcs calc.HealedIH += calc.HealedLoH; calc.HealedIH *= CalcOpts.IHEff * (0.12f + ((8 + Stats.MasteryRating / 179.28f) * 0.015f)); // Beacon of Light calc.HealedBoL = bol.HealingDone(calc.TotalHealed + calc.HealedHL); // added HL heals again to reflect 4.2 change, where HL transferes 100% to beacon target, rather than 50% for everything else #endregion // adding Holy Radiance and LoH after BoL calculation, as they do not count towards BoL heals calc.TotalHealed += calc.HealedHR + calc.HealedLoH + calc.HealedIH + calc.HealedBoL; calc.HealedOther = Stats.Healed + Stats.HealedPerSP * calc.SpellPowerTotal; calc.HealedOther += calc.TotalHealed * Stats.ShieldFromHealedProc; calc.TotalHealed += calc.HealedOther; calc.AvgHPS = calc.TotalHealed / FightLength; calc.AvgHPM = calc.TotalHealed / calc.TotalMana; return calc.AvgHPS * (1f - CalcOpts.BurstScale); }
public static float GetHealingCritsPerSec(CharacterCalculationsHealadin calc) { return((calc.RotationHL / calc.HL.CastTime() * calc.HL.ChanceToCrit() + calc.RotationFoL / calc.FoL.CastTime() * calc.FoL.ChanceToCrit() + calc.RotationHS / calc.HS.CastTime() * calc.HS.ChanceToCrit()) / calc.FightLength); }
public float ManaPool(CharacterCalculationsHealadin calc) { DivinePleas = (float)Math.Ceiling((FightLength - 120f) / (120f * CalcOpts.DivinePlea)); calc.ManaBase = Stats.Mana; calc.ManaLayOnHands = Stats.Mana * ((Talents.GlyphOfDivinity ? 0.1f : 0) * loh.Casts()); calc.ManaArcaneTorrent = (Character.Race == CharacterRace.BloodElf ? Stats.Mana * .06f * (float)Math.Ceiling(FightLength / 120f - .25f) : 0); calc.ManaDivinePlea = Stats.Mana * (Talents.GlyphOfDivinePlea ? 0.18f : 0.12f) * DivinePleas; calc.ManaMp5 = FightLength * Stats.Mp5 / 5f; // this Stats.ManaRestoreFromMaxManaPerSecond is 0 is is messing up the replenishment calculation! //calc.ManaReplenishment = Stats.ManaRestoreFromMaxManaPerSecond * Stats.Mana * FightLength * CalcOpts.Replenishment; calc.ManaReplenishment = 0.001f * Stats.Mana * FightLength * CalcOpts.Replenishment; calc.ManaOther += Stats.ManaRestore; // add calc.ManaJudgements calc.ManaJudgements = HealadinConstants.basemana * 0.15f * jotp.Casts(); if (Stats.HighestStat > 0) { float greatnessMana = Stats.HighestStat * StatConversion.RATING_PER_MANA; calc.ManaReplenishment += Stats.ManaRestoreFromMaxManaPerSecond * FightLength * greatnessMana * CalcOpts.Replenishment; // Replenishment calc.ManaDivinePlea += DivinePleas * greatnessMana * .1f; // Divine Plea } // check if this is correct regen per 5 seconds.. // combat regen = 50% of spirit regen (from Meditation), plus MP5 from gear, plus 5% base mana per 5 secs. Base mana = 23422 at 85 float effective_spirit = Stats.Spirit + Stats.BonusCritChanceFrostStrike * 540 * 6 / CalcOpts.HolyShock; // add in bonus spirit from 4T11 procs float spirit_regen = StatConversion.GetSpiritRegenSec(effective_spirit, Stats.Intellect) * 5f; calc.CombatRegenRate = spirit_regen * 0.5f + Stats.Mp5 + HealadinConstants.basemana * 0.05f; calc.ManaRegenRate = spirit_regen + Stats.Mp5 + HealadinConstants.basemana * 0.05f; calc.CombatRegenTotal = calc.CombatRegenRate * FightLength / 5f; return calc.ManaBase + calc.ManaDivinePlea + calc.CombatRegenTotal + calc.ManaOther + calc.ManaReplenishment + calc.ManaLayOnHands + calc.ManaJudgements; }
public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem) { //_cachedCharacter = character; Stats stats = GetCharacterStats(character, additionalItem); CharacterCalculationsHealadin calculatedStats = new CharacterCalculationsHealadin(); //CharacterCalculationsHealadin oldStats = _cachedCharacterStatsWithSlotEmpty as CharacterCalculationsHealadin; calculatedStats.BasicStats = stats; CalculationOptionsHealadin calcOpts = character.CalculationOptions as CalculationOptionsHealadin; if (calcOpts == null) { calcOpts = new CalculationOptionsHealadin(); } float activity = calcOpts.Activity / 100f; float time = calcOpts.Length * 60; float length = time * activity; float totalMana = stats.Mana + (time * stats.Mp5 / 5) + (calcOpts.Spriest * time / 5) + ((1 + stats.BonusManaPotion) * calcOpts.ManaAmt * (float)Math.Ceiling((time / 60 - 1) / calcOpts.ManaTime)) + calcOpts.Spiritual; if (stats.MementoProc > 0) { totalMana += (float)Math.Ceiling(time / 60 - .25) * stats.MementoProc * 3; } calculatedStats[0] = new Spell("Flash of Light", 7, calcOpts.BoL); calculatedStats[1] = new Spell("Holy Light", 11, calcOpts.BoL); calculatedStats[2] = new Spell("Holy Light", 10, calcOpts.BoL); calculatedStats[3] = new Spell("Holy Light", 9, calcOpts.BoL); calculatedStats[4] = new Spell("Holy Light", 8, calcOpts.BoL); calculatedStats[5] = new Spell("Holy Light", 7, calcOpts.BoL); calculatedStats[6] = new Spell("Holy Light", 6, calcOpts.BoL); calculatedStats[7] = new Spell("Holy Light", 5, calcOpts.BoL); calculatedStats[8] = new Spell("Holy Light", 4, calcOpts.BoL); #region Divine Illumination Spell hl1_di = new Spell("Holy Light", 11, calcOpts.BoL); hl1_di.Calculate(stats, true); Spell hl2_di = new Spell("Holy Light", 9, calcOpts.BoL); hl2_di.Calculate(stats, true); Spell fol_di = new Spell("Flash of Light", 7, calcOpts.BoL); fol_di.Calculate(stats, true); float hl1_dimana = calculatedStats[1].Mps - hl1_di.Mps; float hl2_dimana = calculatedStats[3].Mps - hl2_di.Mps; float fol_dimana = calculatedStats[0].Mps - fol_di.Mps; float di_mana = (hl1_dimana * 6 + hl2_dimana * 3 + fol_dimana * 6) * activity * (float)Math.Ceiling((time - 1) / 180); totalMana += di_mana; #endregion #region Divine Favor totalMana += (float)Math.Ceiling((time - 30) / 120f) * calculatedStats[1].DFMana(); calculatedStats.Healed = (float)Math.Ceiling((time - 30) / 120f) * calculatedStats[1].DFHeal(); #endregion Spell FoL = calculatedStats[0]; int rank1 = 12 - calcOpts.Rank1; int rank2 = 12 - calcOpts.Rank2; float HL_Mps = calculatedStats[rank1].Mps * (1f - calcOpts.Ratio) + calculatedStats[rank2].Mps * calcOpts.Ratio; float HL_Hps = calculatedStats[rank1].Hps * (1f - calcOpts.Ratio) + calculatedStats[rank2].Hps * calcOpts.Ratio; float time_hl = Math.Min(length, Math.Max(0, (totalMana - (length * FoL.Mps)) / (HL_Mps - FoL.Mps))); float time_fol = length - time_hl; if (time_hl == 0) { time_fol = Math.Min(length, totalMana / FoL.Mps); } calculatedStats.TimeHL = time_hl / length; float healing_fol = time_fol * FoL.Hps; float healing_hl = time_hl * HL_Hps; calculatedStats.Healed += healing_fol + healing_hl; calculatedStats.HLHPS = HL_Hps; calculatedStats.FoLHPS = FoL.Hps; calculatedStats.ThroughputPoints = calculatedStats.Healed / time;// FoL.Hps* activity; //calculatedStats.LongevityPoints = calculatedStats.Healed / time - FoL.Hps; /*if (oldStats == null) * { * calculatedStats.ThroughputPoints = FoL.Hps * length; * calculatedStats.LongevityPoints = calculatedStats.Healed - calculatedStats.ThroughputPoints; * } * else * { * float otime = Math.Max(oldStats.TimeHL * length, time_hl); * calculatedStats.LongevityPoints = (length-otime) * oldStats.FoLHPS + otime * oldStats.HLHPS; * calculatedStats.ThroughputPoints = calculatedStats.Healed - calculatedStats.LongevityPoints; * }*/ calculatedStats.OverallPoints = calculatedStats.ThroughputPoints;// +calculatedStats.LongevityPoints; calculatedStats.HealHL = healing_hl / calculatedStats.Healed; calculatedStats.AvgHPS = calculatedStats.Healed / length * activity; calculatedStats.AvgHPM = calculatedStats.Healed / totalMana; return(calculatedStats); }