protected void Initialize(Character character, StatsHunter stats, CombatFactors cf, CalculationOptionsHunter co, Skills.Ability ability, bool useSpellHit, bool alwaysHit) { Char = character; StatS = stats; calcOpts = co; combatFactors = cf; Abil = ability; if (Abil == null) { isWhite = true; } else { Abil.combatFactors = combatFactors; } PetAbil = PetAttacks.None; this.useSpellHit = useSpellHit; /*// Defaults Miss Dodge Parry Block Glance Critical Hit*/ // Start a calc if (alwaysHit) CalculateAlwaysHit(); else Calculate(); }
public static StatsHunter operator *(StatsHunter a, float b) { StatsHunter c = new StatsHunter(); int i = c._rawAdditiveHunterData.Length; while (--i >= 0) { c._rawAdditiveHunterData[i] = a._rawAdditiveHunterData[i] * b; } i = c._rawMultiplicativeHunterData.Length; while (--i >= 0) { c._rawMultiplicativeHunterData[i] = a._rawMultiplicativeHunterData[i] * b; } i = c._rawInverseMultiplicativeHunterData.Length; while (--i >= 0) { c._rawInverseMultiplicativeHunterData[i] = a._rawInverseMultiplicativeHunterData[i] * b; } i = c._rawNoStackHunterData.Length; while (--i >= 0) { c._rawNoStackHunterData[i] = a._rawNoStackHunterData[i] * b; } return(c); }
/// <summary> /// <b>Bestial Wrath</b>, Instant, 2 min cooldown /// <para>Send your pet into a rage causing 20% additional damage for 10 sec. The beast does not feel pity or remorse or fear and it cannot be stopped unless killed.</para> /// </summary> /// <TalentsAffecting> /// Longevity - Reduces the cooldown of your Bestial Wrath, Intimidation and Pet Special Abilities by 10/20/30%. /// The Beast Within - While your pet is under the effects of Bestial Wrath, you also go into a rage causing 10% additional damage and reducing the focus cost of all shots and abilities by 50% for 10 sec. /// </TalentsAffecting> /// <GlyphsAffecting>Glyph of Bestial Wrath - Decreases the cooldown of Bestial Wrath by 20 sec.</GlyphsAffecting> public BestialWrath(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "Bestial Wrath"; Cd = ((2f * 60f) * (1f - Talents.Longevity)) - (Talents.GlyphOfBestialWrath ? 20f : 0f); // In Seconds Duration = 10f; UseHitTable = false; ReqTalent = true; Talent2ChksValue = Talents.BestialWrath; // TODO: Move these to static SEs. Effect = new SpecialEffect(Trigger.Use, new Stats() { BonusPetDamageMultiplier = 0.20f }, Duration, Cd); if (Talents.TheBeastWithin > 0f) { Effect = new SpecialEffect(Trigger.Use, new Stats() { BonusDamageMultiplier = 0.10f }, Duration, Cd); } Initialize(); }
public CombatFactors(Character character, StatsHunter stats, CalculationOptionsHunter calcOpts, BossOptions bossOpts) { Char = character; if (Char != null) { if (Char.Ranged != null) { RW = Char.Ranged.Item; } else { RW = new Knuckles(); } if (Char.HunterTalents != null) { Talents = Char.HunterTalents; } else { Talents = new HunterTalents(); } } CalcOpts = (calcOpts == null ? new CalculationOptionsHunter() : calcOpts); BossOpts = (bossOpts == null ? new BossOptions() : bossOpts); StatS = stats; InvalidateCache(); // Optimizations //Set_c_values(); }
protected void Initialize(Character character, StatsHunter stats, CalculationOptionsHunter co, float[] avoidChances, PetAttacks ability, bool useSpellHit, bool alwaysHit) { Char = character; StatS = stats; calcOpts = co; combatFactors = null; Abil = null; PetAbil = ability; isWhite = (PetAbil == PetAttacks.None); this.useSpellHit = useSpellHit; /*// Defaults * Miss * Dodge * Parry * Block * Glance * Critical * Hit*/ // Start a calc if (alwaysHit) { CalculateAlwaysHit(); } else { Calculate(avoidChances); } }
public HunterBase(Character charac, StatsHunter stats, HunterTalents talents, Specialization spec, int targetLvL) { character = charac; Stats = stats; Talents = talents; Tree = spec; TargetLevel = targetLvL; }
public HunterBase (Character charac, StatsHunter stats, HunterTalents talents, Specialization spec, int targetLvL) { character = charac; Stats = stats; Talents = talents; Tree = spec; TargetLevel = targetLvL; }
/// <summary> /// <b>Readiness</b>, Instant, 3 min Cd /// <para>When activated, this ability immediately finishes the cooldown on all Hunter abilities.</para> /// </summary> /// <TalentsAffecting></TalentsAffecting> /// <GlyphsAffecting></GlyphsAffecting> public Readiness(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "Readiness"; Cd = 3f * 60f; // In Seconds Duration = 0f; UseHitTable = false; Initialize(); }
/// <summary> /// <b>Rapid Fire</b>, Instant, 5 min Cd /// <para>Increases ranged attack speed by 40% for 15 sec.</para> /// </summary> /// <TalentsAffecting> /// Posthaste - Reduces the cooldown of your Rapid Fire by 1/2 min, and your movement speed is increased by 15/30% for 4 sec after you use Disengage. /// Rapid Recuperation - You gain 6/12 focus every 3 sec while under the effect of Rapid Fire, and you gain 50 focus instantly when you gain Rapid Killing. /// </TalentsAffecting> /// <GlyphsAffecting>Glyph of Rapid Fire [+10% Haste Bonus]</GlyphsAffecting> public RapidFire(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "Rapid Fire"; Cd = (5f - Talents.Posthaste) * 60f; // In Seconds Duration = 15f; UseHitTable = false; Effect = _RAPIDFIRE[Talents.GlyphOfRapidFire ? 1 : 0][Talents.Posthaste]; Initialize(); }
/// <summary>Clones the stats object for duplication with separation</summary> public new StatsHunter Clone() { StatsHunter clone = (StatsHunter)this.MemberwiseClone(); StatsHunter retVal = new StatsHunter(); retVal.Accumulate(base.Clone()); retVal._rawAdditiveHunterData = (float[])clone._rawAdditiveHunterData.Clone(); retVal._rawMultiplicativeHunterData = (float[])clone._rawMultiplicativeHunterData.Clone(); retVal._rawInverseMultiplicativeHunterData = (float[])clone._rawInverseMultiplicativeHunterData.Clone(); retVal._rawNoStackHunterData = (float[])clone._rawNoStackHunterData.Clone(); return(retVal); }
public PetCalculations(Character character, CharacterCalculationsHunter calculatedStats, CalculationOptionsHunter calcopts, BossOptions bossOpts, StatsHunter hunterStats) { this.character = character; this.calculatedStats = calculatedStats; this.CalcOpts = calcopts; this.BossOpts = bossOpts; this.PetTalents = calcopts.PetTalents; this.Talents = character.HunterTalents; this.HunterStats = hunterStats; PetStats = new StatsHunter(); }
/// <summary> /// TODO Zhok: Sniper Training /// <b>Kill Shot</b>, 45 Focus, 45yd, Instant, 10 sec Cd /// <para>You attempt to finish the wounded target off, firing a long range attack /// dealing 150% weapon damage plus RAP*0.30+543. Kill Shot can only be used on /// enemies that have 20% or less health.</para> /// <para>Kill Shot can only be used on enemies that have 20% or less health.</para> /// </summary> /// <TalentsAffecting>Sniper Training - Increases the critical strike chance of your Kill Shot ability by 5/10/15%, and after remaining stationary for 6 sec, your Steady Shot and Cobra Shot deal 2/4/6% more damage for 15 sec.</TalentsAffecting> /// <GlyphsAffecting>Glyph of Kill Shot - If the damage from your Kill Shot fails to kill a target at or below 20% health, your Kill Shot's cooldown is instantly reset. This effect has a 6 sec cooldown.</GlyphsAffecting> public KillShot(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; Name = "Kill Shot"; ReqRangedWeap = true; ReqSkillsRange = true; // In terms of modeling, the Glyph of Kill Shot is basically a 4 second cooldown reduction. Cd = 10 - (Talents.GlyphOfKillShot ? 4f : 0f); FocusCost = 0; // 150% weapon dmg + 45% RAP + 543 DamageBase = (cf.AvgRwWeaponDmgUnhasted * 1.5f) + (StatS.RangedAttackPower * 0.45f) + 543f; Initialize(); }
/// <summary> /// TODO Zhok: Bombardment, Serpent Spread /// <b>Multi-Shot</b>, 40 Focus, 5-35yd /// <para>Fires several missiles, hitting your current target /// and all enemies within 0 yards of that target for 55% of weapon damage.</para> /// </summary> /// <TalentsAffecting>Bombardment - When you critically hit with your Multi-Shot your next Multi-Shot's focus cost will be reduced by 25/50%. /// Concussive Barrage - Your successful Chimera Shot and Multi-Shot attacks have a 50/100% chance to daze the target for 4 sec. /// Serpent Spread - Targets hit by your Multi-Shot are also afflicted by your Serpent Sting equal to 6/9 sec of its total duration. /// </TalentsAffecting> public MultiShot(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "Multi-Shot"; //AbilIterater = (int)CalculationOptionsHunter.Maintenances.MortalStrike_; ReqRangedWeap = true; ReqSkillsRange = true; Targets = 10f; // TODO Zhok: 10? FocusCost = 40f; DamageBase = cf.AvgRwWeaponDmgUnhasted * 1.20f; // Initialize(); }
/// <summary> /// TODO Zhok: Careful Aim, Sniper Training, Termination /// <b>Cobra Shot</b>, 5-40yd, 1.5 sec cast /// <para>Deals weapon damage plus (276 + (RAP * 0.017)) in the form of Nature damage /// and increases the duration of your Serpent Sting on /// the target by 6 sec.</para> /// <para>Generates 9 Focus.</para> /// </summary> /// <TalentsAffecting> /// Careful Aim - Increases the critical strike chance of your Steady Shot, Cobra Shot and Aimed Shot by 30/60% on targets who are above 90% health. /// Rapid Killing - After killing an opponent that yields experience or honor, your next Aimed Shot, Steady Shot or Cobra Shot causes 10/20% additional damage. Lasts 20 sec. /// Sniper Training - Increases the critical strike chance of your Kill Shot ability by 5/10/15%, and after remaining stationary for 6 sec, your Steady Shot and Cobra Shot deal 2/4/6% more damage for 15 sec. /// Termination - Your Steady Shot and Cobra Shot abilities grant an additional 3/6 Focus when dealt on targets at or below 25% health. /// </TalentsAffecting> /// <Note>Cobra Shot is a replacement to [Steady Shot] for beastmaster and survival hunters due to its ability /// (and beastmaster and survival hunters otherwise lack of ability) to increase the duration of [Serpent Sting] without recasting it. </Note> public CobraShot(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "Cobra Shot"; ReqTalent = true; Talent2ChksValue = ((Specialization)Talents.HighestTree == Specialization.Marksmanship ? 0 : 1); ReqRangedWeap = true; ReqSkillsRange = true; CastTime = 1.5f; FocusCost = -9; //Targets += StatS.BonusTargets; DamageBase = combatFactors.AvgRwWeaponDmgUnhasted + (StatS.RangedAttackPower * 0.017f) + 277.21f; Initialize(); }
public MMMAimedShot(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "MMM Aimed Shot"; ReqTalent = true; // Reqiures MM spec. Talent2ChksValue = Talents.MasterMarksman; ReqRangedWeap = true; ReqSkillsRange = true; CastTime = 0; FocusCost = 0; DamageBase = (combatFactors.AvgRwWeaponDmgUnhasted + (StatS.RangedAttackPower * 0.724f) + 776) * 1.60f + 100; Consumes_Tier12_4pc = true; Initialize(); }
/// <summary> /// TODO Zhok: Careful Aim, Dazzled Prey, Sniper Training, Termination /// <b>Steady Shot</b>, 5-40yd, 1.5 sec cast /// <para>A steady shot that causes 100% weapon damage /// plus RAP*0.021+280. Generates 9 Focus.</para> /// </summary> /// <TalentsAffecting> /// Careful Aim - Increases the critical strike chance of your Steady Shot, Cobra Shot and Aimed Shot by 30/60% on targets who are above 80% health. /// Improved Steady Shot - When you Steady Shot twice in a row, your ranged attack speed will be increased by 5/10/15% for 8 sec. /// Master Marksman - You have a 20/40/60% chance when you Steady Shot to gain the Master Marksman effect, lasting 30 sec. After reaching 5 stacks, your next Aimed Shot's cast time and focus cost are reduced by 100% for 10 sec. /// Rapid Killing - After killing an opponent that yields experience or honor, your next Aimed Shot, Steady Shot or Cobra Shot causes 10/20% additional damage. Lasts 20 sec. /// Sniper Training - Increases the critical strike chance of your Kill Shot ability by 5/10/15%, and after remaining stationary for 6 sec, your Steady Shot and Cobra Shot deal 2/4/6% more damage for 15 sec. /// Termination - Your Steady Shot and Cobra Shot abilities grant an additional 3/6 Focus when dealt on targets at or below 25% health. /// </TalentsAffecting> /// <GlyphsAffecting>Glyph of Steady Shot [+10% DMG] /// Glyph of Dazzled Prey - Your Steady Shot generates an additional 2 Focus on targets afflicted by a daze effect.</GlyphsAffecting> public SteadyShot(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "Steady Shot"; ReqRangedWeap = true; ReqSkillsRange = true; UsesGCD = true; CastTime = 1.5f; DamageBase = combatFactors.AvgRwWeaponDmgUnhasted + (StatS.RangedAttackPower * 0.021f) + 280f; DamageBonus = 1f + (Talents.GlyphOfSteadyShot ? 0.10f : 0f); FocusCost = -9; // Initialize(); }
/// <summary> /// TODO Zhok: Thrill of the Hunt, Toxicology, Trap Mastery /// <b>Black Arrow</b>, 35 Focus, 5-40yd, Instant, 30 sec Cd /// <para>Fires a Black Arrow at the target, dealing 2395 Shadow damage over 15 sec. /// Black Arrow shares a cooldown with other Fire Trap spells.</para> /// </summary> /// <TalentsAffecting>Black Arrow (Requires Talent)</TalentsAffecting> /// <GlyphsAffecting></GlyphsAffecting> public BlackArrowBuff(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "Black Arrow"; //AbilIterater = (int)CalculationOptionsHunter.Maintenances.MortalStrike_; ReqTalent = true; Talent2ChksValue = Talents.BlackArrow; ReqRangedWeap = true; ReqSkillsRange = true; //Targets += StatS.BonusTargets; Cd = 30f; //TODO Zhok: Resourcefulness ... - (2f * Talents.Resourcefulness; FocusCost = 35f; // Initialize(); }
// Constructors public WhiteAttacks(Character character, StatsHunter stats, CombatFactors cf, CalculationOptionsHunter calcOpts, BossOptions bossOpts) { Char = character; StatS = stats; Talents = Char.HunterTalents == null ? new HunterTalents() : Char.HunterTalents; combatFactors = cf; CalcOpts = calcOpts; BossOpts = bossOpts; RWAtkTable = new AttackTable(Char, StatS, combatFactors, calcOpts, false, false); FightDuration = BossOpts.BerserkTimer; Targets = BossOpts.Targets.Count; // Should update to better handle Target objects HSOverridesOverDur = 0f; CLOverridesOverDur = 0f; Steady_Freq = 0f; }
/// <summary> /// TODO Zhok: Add Efficiency, Piercing Shots /// <b>Chimera Shot</b>, 50 Focus, 5-40yd, Instant, 10 sec Cd /// <para>An instant shot that causes ranged weapon damage /// plus RAP*0.732+1620, refreshing the duration of your /// Serpent Sting and healing you for 5% of your total health.</para> /// </summary> /// <TalentsAffecting>Chimera Shot (Requires Talent) /// Concussive Barrage - Your successful Chimera Shot and Multi-Shot attacks have a 50/100% chance to daze the target for 4 sec. /// Efficiency - Reduces the focus cost of your Arcane Shot by 1/2/3, and your Explosive Shot and Chimera Shot by 2/4/6. /// Marked for Death - Your Arcane Shot and Chimera Shot have a 50/100% chance to automatically apply the Marked for Death effect. /// Piercing Shots - Your critical Aimed, Steady and Chimera Shots cause the target to bleed for 10/20/30% of the damage dealt over 8 sec. /// </TalentsAffecting> /// <GlyphsAffecting>Glyph of Chimera Shot [-1 sec Cd]</GlyphsAffecting> public ChimeraShot(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; Name = "Chimera Shot"; ReqTalent = true; Talent2ChksValue = Talents.ChimeraShot; ReqRangedWeap = true; ReqSkillsRange = true; //Targets += StatS.BonusTargets; Cd = 10f - (Talents.GlyphOfChimeraShot ? 1f : 0f); // In Seconds FocusCost = 50f - (Talents.Efficiency * 2f); DamageBase = combatFactors.AvgRwWeaponDmgUnhasted + StatS.RangedAttackPower * 0.732f + 1620; RefreshesSS = true; // Initialize(); }
/// <summary> /// TODO Zhok: Add Efficiency, Lock and Load, Thrill of the Hunt /// <b>Explosive Shot</b>, 50 Focus, 5-40yd, Instant, 6 sec Cd /// <para>You fire an explosive charge into the enemy target, dealing /// [RAP * 0.232 + 320] - [RAP * 0.232 + 386] Fire Damage. The charge will /// blast the target every second for an additional 2 sec.</para> /// </summary> /// <TalentsAffecting>Explosive Shot (Requires Spec) /// Efficiency - Reduces the focus cost of your Arcane Shot by 1/2/3, and your Explosive Shot and Chimera Shot by 2/4/6. /// Lock and Load - You have a 50/100% chance when you trap a target with Freezing Trap or Ice Trap to cause your next 2 Arcane Shot or Explosive Shot abilities to cost no focus and trigger no cooldown. /// Sic 'Em! - When you critically hit with your Arcane Shot, Aimed Shot or Explosive Shot the focus cost of your Pet's next basic attack is reduced by 50/100% for 12 sec. /// Thrill of the Hunt - You have a 5/10/15% chance when you use Arcane Shot, Explosive Shot or Black Arrow to instantly regain 40% of the base focus cost of the shot.</TalentsAffecting> /// <GlyphsAffecting>Glyph of Explosive Shot [+6% crit chance]</GlyphsAffecting> public ExplosiveShot(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; Name = "Explosive Shot"; ReqTalent = true; Talent2ChksValue = ((Specialization)Talents.HighestTree == Specialization.Survival ? 1 : 0); ReqRangedWeap = true; ReqSkillsRange = true; Cd = 6f; // In Seconds Duration = 2f; TimeBtwnTicks = 1f; FocusCost = _basefocuscost - (Talents.Efficiency * 2f); // 23.2% RAP + (320 + 386)/2 (Per tick) for 2 seconds DamageBase = ((StatS.RangedAttackPower * 0.232f) + (320 + 386) / 2f); BonusCritChance = Talents.GlyphOfExplosiveShot ? 0.06f : 0f; Initialize(); }
/// <summary> /// TODO Zhok: Cobra Strike, Efficiency, Lock and Load, Sic 'Em, Thrill of the Hunt /// /// <b>Arcane Shot</b>, 25 Focus, 5-40yd, Instant /// <para>An instant shot that causes 100% weapon damage /// plus (RAP * 0.0483)+289 as Arcane damage.</para> /// </summary> /// <TalentsAffecting>Cobra Strikes - You have a 5/10/15% chance when you hit with Arcane Shot to cause your pet's next 2 Basic Attacks to critically hit. /// Efficiency - Reduces the focus cost of your Arcane Shot by 1/2/3, and your Explosive Shot and Chimera Shot by 2/4/6. /// Lock and Load - You have a 50/100% chance when you trap a target with Freezing Trap or Ice Trap to cause your next 2 Arcane Shot or Explosive Shot abilities to cost no focus and trigger no cooldown. /// Marked for Death - Your Arcane Shot and Chimera Shot have a 50/100% chance to automatically apply the Marked for Death effect. /// Sic 'Em! - When you critically hit with your Arcane Shot, Aimed Shot or Explosive Shot the focus cost of your Pet's next basic attack is reduced by 50/100% for 12 sec. /// Thrill of the Hunt - You have a 5/10/15% chance when you use Arcane Shot, Explosive Shot or Black Arrow to instantly regain 40% of the base focus cost of the shot.</TalentsAffecting> /// </TalentsAffecting> /// <GlyphsAffecting>Glyph of Arcane Shot [12% More DMG]</GlyphsAffecting> public ArcaneShot(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; Name = "Arcane Shot"; ReqRangedWeap = true; ReqSkillsRange = true; FocusCost = _basefocuscost - (Talents.Efficiency * 2f); DamageBase = cf.AvgRwWeaponDmgUnhasted + (StatS.RangedAttackPower * 0.0483f) + 289f; DamageBonus = 1 + (Talents.GlyphOfArcaneShot ? 0.12f : 0f); Consumes_Tier12_4pc = true; Initialize(); }
/// <summary> /// <b>Piercing Shots</b> /// <para>Your critical Aimed, Steady and Chimera Shots cause the /// target to bleed for [Pts*10]% of the damage dealt over 8 sec.</para> /// </summary> /// <TalentsAffecting>Piercing Shots (Requires Talent)</TalentsAffecting> /// <GlyphsAffecting></GlyphsAffecting> public PiercingShots(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; // Name = "Piercing Shots"; //AbilIterater = (int)CalculationOptionsHunter.Maintenances.MortalStrike_; ReqTalent = true; Talent2ChksValue = Talents.PiercingShots; ReqRangedWeap = true; ReqSkillsRange = true; //Targets += StatS.BonusTargets; //Cd = 30f; // In Seconds Duration = 8f; TimeBtwnTicks = 1f; // In Seconds DamageBase = StatS.RangedAttackPower * 0.10f + 2765f; // Initialize(); }
/// <summary> /// <b>Serpent Sting</b>, 25 Focus,5-40yd, Instant, No Cd /// <para>Causes (RAP * 0.4 + (460 * 15 sec / 3)) Nature damage over 15 sec.</para> /// </summary> /// <TalentsAffecting> /// Chimera Shot - An instant shot that causes ranged weapon damage plus RAP*0.732+1620, refreshing the duration of your Serpent Sting and healing you for 5% of your total health. /// Noxious Stings - Increases your damage done on targets afflicted by your Serpent Sting by 5/10%. /// Serpent Spread - Targets hit by your Multi-Shot are also afflicted by your Serpent Sting equal to 6/9 sec of its total duration. /// Toxicology - Increases the periodic critical damage of your Serpent Sting and Black Arrow by 50/100%. /// </TalentsAffecting> /// <GlyphsAffecting>Glyph of Serpent Sting - Increases the periodic critical strike chance of your Serpent Sting by 6%.</GlyphsAffecting> public SerpentSting(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; Name = "Serpent Sting"; ReqRangedWeap = true; ReqSkillsRange = true; TimeBtwnTicks = 3f; // In Seconds Duration = 15f; FocusCost = 25f; DamageBase = (StatS.RangedAttackPower * 0.4f + (460f * 15f / 3f)); BonusCritChance = 1f + (Talents.GlyphOfSerpentSting ? 0.06f : 0f) + (Talents.ImprovedSerpentSting * 0.05f); MinRange = 5f; MaxRange = 40f; CanCrit = true; StatS.BonusDamageMultiplier += (.05f * Talents.NoxiousStings); StatS.BonusCritDamageMultiplier += (.5f * Talents.Toxicology); // Improved Serpent Sting // Noxious Stings // Initialize(); }
public CombatFactors(Character character, StatsHunter stats, CalculationOptionsHunter calcOpts, BossOptions bossOpts) { Char = character; if (Char != null) { if (Char.Ranged != null) RW = Char.Ranged.Item; else RW = new Knuckles(); if (Char.HunterTalents != null) Talents = Char.HunterTalents; else Talents = new HunterTalents(); } CalcOpts = (calcOpts == null ? new CalculationOptionsHunter() : calcOpts); BossOpts = (bossOpts == null ? new BossOptions() : bossOpts); StatS = stats; InvalidateCache(); // Optimizations //Set_c_values(); }
/// <summary> /// TODO Zhok: Thrill of the Hunt, Toxicology, Trap Mastery /// <b>Black Arrow</b>, 35 Focus, 5-40yd, Instant, 30 sec Cd /// <para>Fires a Black Arrow at the target, dealing 2035 Shadow damage over 15 sec. /// Black Arrow shares a cooldown with other Fire Trap spells.</para> /// </summary> /// <TalentsAffecting>Black Arrow (Requires Talent) /// Resourcefulness - Reduces the cooldown of all traps and Black Arrow by 2/4/6 sec. /// T.N.T - When you deal periodic damage with your Immolation Trap, Explosive Trap or Black Arrow you have a 6/12% chance to trigger Lock and Load. /// Thrill of the Hunt - You have a 5/10/15% chance when you use Arcane Shot, Explosive Shot or Black Arrow to instantly regain 40% of the base focus cost of the shot. /// Toxicology - Increases the periodic critical damage of your Serpent Sting and Black Arrow by 50/100%. /// Trap Mastery - Immolation Trap, Explosive Trap and Black Arrow - Increases the periodic damage done by 10/20/30%. /// </TalentsAffecting> /// <GlyphsAffecting></GlyphsAffecting> public BlackArrow(Character c, StatsHunter s, CombatFactors cf, WhiteAttacks wa, CalculationOptionsHunter co) { Char = c; StatS = s; combatFactors = cf; Whiteattacks = wa; CalcOpts = co; Name = "Black Arrow"; ReqTalent = true; Talent2ChksValue = Talents.BlackArrow; ReqRangedWeap = true; ReqSkillsRange = true; Cd = 30f - (Talents.Resourcefulness * 2f); Duration = 15f; TimeBtwnTicks = 1f; // TODO Zhok: Haste? FocusCost = _basefocuscost; // 47.35% RAP + 2035 (total damage) // 4.2 Increased the damage by 40% DamageBase = /*(StatS.RangedAttackPower * 0.4735f) +*/ 2850f; DamageBonus = 1f + (Talents.TrapMastery * 0.10f); Initialize(); }
protected void Initialize(Character character, StatsHunter stats, CombatFactors cf, CalculationOptionsHunter co, Skills.Ability ability, bool useSpellHit, bool alwaysHit) { Char = character; StatS = stats; calcOpts = co; combatFactors = cf; Abil = ability; if (Abil == null) { isWhite = true; } else { Abil.combatFactors = combatFactors; } PetAbil = PetAttacks.None; this.useSpellHit = useSpellHit; /*// Defaults * Miss * Dodge * Parry * Block * Glance * Critical * Hit*/ // Start a calc if (alwaysHit) { CalculateAlwaysHit(); } else { Calculate(); } }
private StatsHunter GetTalentStats(HunterTalents talents) { StatsHunter s = new StatsHunter(); #region Specializations : Abilities/Bonuses from picking a tree. switch ((Specialization)talents.HighestTree) { // BM case Specialization.BeastMastery: { // Initimitation // TODO: Implemented in Shots/Abilities // Animal Handler s.BonusAttackPowerMultiplier += 0.25f; // Mastery: Master of Beasts // TODO: s.BonusPetDamageMultiplier += .13 + 0.0167 * Mastery break; } // MM case Specialization.Marksmanship: { // Aimed Shot // TODO: Implemented in Shots/Abilities // Artisan Quiver // TODO: Auto-attack damage + 15% // Mastery: Wild Quiver // TODO: Additional Auto-Shot 16.8% + 2.1% per Mastery break; } // SV case Specialization.Survival: { // Explosive Shot // TODO: Implemented in Shots/Abilities // Intothe Wilderness s.BonusAgilityMultiplier += .1f; // Mastery: Essence of the Viper // Bonus magic damage 8% + 1% per mastery break; } } #endregion #region Beast Master // Talent: Ferocious Inspiration s.BonusDamageMultiplier += talents.FerociousInspiration * 0.03f; #endregion #region Marksman // Talent: Trueshot Aura (Always on and part of the paperdoll numbers) s.BonusAttackPowerMultiplier += talents.TrueshotAura * 0.1f; #endregion #region Survival s.BonusStaminaMultiplier += talents.HunterVsWild * 0.05f; s.BonusHasteMultiplier += talents.Pathing * 0.01f; s.BonusAgilityMultiplier += talents.HuntingParty * 0.02f; // This conflics w/ IcyTalons and other similar buffs. s.PhysicalHaste += talents.HuntingParty * .1f; #endregion return s; }
public PetAttackTable(Character character, StatsHunter stats, CalculationOptionsHunter co, float[] avoidChances, PetAttacks ability, bool useSpellHit, bool alwaysHit) { Initialize(character, stats, co, avoidChances, ability, useSpellHit, alwaysHit); }
private StatsHunter GetCharacterStats(Character character, Item additionalItem, bool bGetMaxStats) { StatsHunter statsTotal = new StatsHunter(); try { #region NullChecks if (null == character) { #if DEBUG throw new NullReferenceException("Character is Null"); #else return statsTotal; #endif } CalculationOptionsHunter calcOpts = character.CalculationOptions as CalculationOptionsHunter; if (null == calcOpts) { calcOpts = new CalculationOptionsHunter(); } HunterTalents talents = character.HunterTalents; if (null == talents) { return statsTotal; } Specialization tree = GetSpecialization(talents); CharacterCalculationsHunter calculatedStats = new CharacterCalculationsHunter(); if (null == calculatedStats) { #if DEBUG throw new NullReferenceException("Character Calculations is Null"); #else return statsTotal; #endif } BossOptions bossOpts = new BossOptions(); bossOpts = character.BossOptions; if (null == bossOpts) { #if DEBUG throw new NullReferenceException("Boss Options is Null"); #else return statsTotal; #endif } #endregion statsTotal.Accumulate(BaseStats.GetBaseStats(character.Level, CharacterClass.Hunter, character.Race)); statsTotal.Accumulate(GetItemStats(character, additionalItem)); AccumulateBuffsStats(statsTotal, character.ActiveBuffs); // DO NOT MOVE GetRelevantStats any lower in this progression of functions. // It will erase stats specific to hunters. statsTotal = GetRelevantStats(statsTotal) as StatsHunter; statsTotal.Accumulate(GetTalentStats(character.HunterTalents)); statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina * (1f + statsTotal.BonusStaminaMultiplier)); statsTotal.Agility = (float)Math.Floor(statsTotal.Agility * (1f + statsTotal.BonusAgilityMultiplier)); calculatedStats.critFromAgi = statsTotal.Agility; // Agi bonus to Survival doesn't affect crit? if (tree == Specialization.Survival) calculatedStats.critFromAgi /= 1.1f; statsTotal.AttackPower += (statsTotal.Agility * 2f); statsTotal.AttackPower = statsTotal.RangedAttackPower = (float)Math.Floor(statsTotal.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier)); statsTotal.Health += (float)Math.Floor((statsTotal.Stamina - 20f) * 14f + 20f); statsTotal.Health = (float)Math.Floor(statsTotal.Health * (1f + statsTotal.BonusHealthMultiplier)); statsTotal.Armor = (float)Math.Floor(statsTotal.Armor * (1f + statsTotal.BonusArmorMultiplier)) + statsTotal.BonusArmor; statsTotal.NatureResistance += statsTotal.NatureResistanceBuff; statsTotal.FireResistance += statsTotal.FireResistanceBuff; statsTotal.FrostResistance += statsTotal.FrostResistanceBuff; statsTotal.ShadowResistance += statsTotal.ShadowResistanceBuff; statsTotal.ArcaneResistance += statsTotal.ArcaneResistanceBuff; int targetLevel = bossOpts.Level; float hasteBonus = StatConversion.GetPhysicalHasteFromRating(statsTotal.HasteRating, CharacterClass.Hunter); statsTotal.RangedHaste = (1f + hasteBonus) * (1f + statsTotal.BonusHasteMultiplier) * (1f + statsTotal.PhysicalHaste) - 1f; float hitBonus = StatConversion.GetPhysicalHitFromRating(statsTotal.HitRating) + statsTotal.PhysicalHit; float chanceMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[targetLevel - character.Level] - hitBonus); float chanceAvoided = chanceMiss; float rawChanceCrit = StatConversion.GetPhysicalCritFromRating(statsTotal.CritRating) + StatConversion.GetCritFromAgility(calculatedStats.critFromAgi, CharacterClass.Hunter) + statsTotal.PhysicalCrit + StatConversion.NPC_LEVEL_CRIT_MOD[targetLevel - character.Level]; calculatedStats.critRateOverall = rawChanceCrit * (1f - chanceAvoided); float chanceHit = 1f - chanceAvoided; if (bGetMaxStats) { #region Special Effects StatsHunter statsProcs = new StatsHunter(); Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>(); Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>(); CalculateTriggers(character, calculatedStats, statsTotal, calcOpts, bossOpts, triggerIntervals, triggerChances); /*if (calcOpts.PetFamily == PETFAMILY.Wolf && calculatedStats.pet.priorityRotation.getSkillFrequency(PetAttacks.FuriousHowl) > 0) { statsTotal.AddSpecialEffect(FuriousHowl); } */ foreach (SpecialEffect effect in statsTotal.SpecialEffects()) { statsProcs.Accumulate(getSpecialEffects(effect, triggerIntervals, triggerChances, character)); } #region Handle Results of Special Effects // Base Stats statsProcs.Stamina = (float)Math.Floor(statsProcs.Stamina * (1f + statsTotal.BonusStaminaMultiplier) * (1f + statsProcs.BonusStaminaMultiplier)); statsProcs.Agility = statsProcs.Agility * (1f + statsTotal.BonusAgilityMultiplier) * (1f + statsProcs.BonusAgilityMultiplier); statsProcs.Agility += statsProcs.HighestStat * (1f + statsTotal.BonusAgilityMultiplier) * (1f + statsProcs.BonusAgilityMultiplier); statsProcs.Agility += statsProcs.Paragon * (1f + statsTotal.BonusAgilityMultiplier) * (1f + statsProcs.BonusAgilityMultiplier); statsProcs.HighestStat = statsProcs.Paragon = 0f; // we've added them into agi so kill it statsProcs.Health += (float)Math.Floor(statsProcs.Stamina * 10f); float HighestSecondaryStatValue = statsProcs.HighestSecondaryStat; // how much HighestSecondaryStat to add statsProcs.HighestSecondaryStat = 0f; // remove HighestSecondaryStat stat, since it's not needed if (statsTotal.CritRating > statsTotal.HasteRating && statsTotal.CritRating > statsTotal.MasteryRating) { statsTotal.CritRating += HighestSecondaryStatValue; } else if (statsTotal.HasteRating > statsTotal.CritRating && statsTotal.HasteRating > statsTotal.MasteryRating) { statsTotal.HasteRating += HighestSecondaryStatValue; } else /*if (statsTotal.MasteryRating > statsTotal.CritRating && statsTotal.MasteryRating > statsTotal.HasteRating)*/ { statsTotal.MasteryRating += HighestSecondaryStatValue; } // Armor statsProcs.Armor = statsProcs.Armor * (1f + statsTotal.BaseArmorMultiplier + statsProcs.BaseArmorMultiplier); //statsProcs.BonusArmor += statsProcs.Agility * 2f; statsProcs.BonusArmor = statsProcs.BonusArmor * (1f + statsTotal.BonusArmorMultiplier + statsProcs.BonusArmorMultiplier); statsProcs.Armor += statsProcs.BonusArmor; statsProcs.BonusArmor = 0; //it's been added to Armor so kill it // Attack Power statsProcs.BonusAttackPowerMultiplier *= (1f + statsProcs.BonusRangedAttackPowerMultiplier); statsProcs.BonusRangedAttackPowerMultiplier = 0; // it's been added to Attack Power so kill it float totalBAPMProcs = (1f + statsTotal.BonusAttackPowerMultiplier) * (1f + statsProcs.BonusAttackPowerMultiplier) - 1f; float apFromAGIProcs = (1f + totalBAPMProcs) * (statsProcs.Agility) * 2f; //float apFromSTRProcs = (1f + totalBAPMProcs) * (statsProcs.Strength); float apBonusOtherProcs = (1f + totalBAPMProcs) * (statsProcs.AttackPower + statsProcs.RangedAttackPower); statsProcs.AttackPower = Math.Max(0f, apFromAGIProcs + /*apFromSTRProcs +*/ apBonusOtherProcs); statsProcs.RangedAttackPower = statsProcs.AttackPower; statsTotal.AttackPower *= (1f + statsProcs.BonusAttackPowerMultiplier); // Make sure the originals get your AP% procs // Crit statsProcs.PhysicalCrit += StatConversion.GetCritFromAgility(statsProcs.Agility, character.Class); statsProcs.PhysicalCrit += StatConversion.GetCritFromRating(statsProcs.CritRating + statsProcs.RangedCritRating, character.Class); // Haste statsProcs.PhysicalHaste = (1f + statsProcs.PhysicalHaste) * (1f + statsProcs.RangedHaste) * (1f + StatConversion.GetPhysicalHasteFromRating(statsProcs.HasteRating, character.Class)) - 1f; #endregion // Add it back into the fold statsTotal.Accumulate(statsProcs); #endregion } return statsTotal; } catch (Exception ex) { new Base.ErrorBox() { Title = "Error in getting Character Stats", Function = "GetCharacterStats()", TheException = ex, }.Show(); } return new StatsHunter(); }
public AttackTable(Character character, StatsHunter stats, CombatFactors cf, CalculationOptionsHunter co, Skills.Ability ability, bool useSpellHit, bool alwaysHit) { Initialize(character, stats, cf, co, ability, useSpellHit, alwaysHit); }
public void Accumulate(StatsHunter data, float weight) { // Check the root data: if (data == null) { return; } #region Base if (data.sparseIndices != null) { int i = 0; for (int a = 0; a < data.sparseAdditiveCount; a++, i++) { int index = data.sparseIndices[i]; rawAdditiveData[index] += weight * data.rawAdditiveData[index]; } for (int a = 0; a < data.sparseMultiplicativeCount; a++, i++) { int index = data.sparseIndices[i]; rawMultiplicativeData[index] = (1 + rawMultiplicativeData[index]) * (1 + weight * data.rawMultiplicativeData[index]) - 1; } for (int a = 0; a < data.sparseInverseMultiplicativeCount; a++, i++) { int index = data.sparseIndices[i]; rawInverseMultiplicativeData[index] = 1 - (1 - rawInverseMultiplicativeData[index]) * (1 - weight * data.rawInverseMultiplicativeData[index]); } for (int a = 0; a < data.sparseNoStackCount; a++, i++) { int index = data.sparseIndices[i]; float value = weight * data.rawNoStackData[index]; if (value > rawNoStackData[index]) rawNoStackData[index] = value; } } else { float[] add = data.rawAdditiveData; for (int i = 0; i < rawAdditiveData.Length; i++) { rawAdditiveData[i] += weight * add[i]; } add = data.rawMultiplicativeData; for (int i = 0; i < rawMultiplicativeData.Length; i++) { rawMultiplicativeData[i] = (1 + rawMultiplicativeData[i]) * (1 + weight * add[i]) - 1; } add = data.rawInverseMultiplicativeData; for (int i = 0; i < rawInverseMultiplicativeData.Length; i++) { rawInverseMultiplicativeData[i] = 1 - (1 - rawInverseMultiplicativeData[i]) * (1 - weight * add[i]); } add = data.rawNoStackData; for (int i = 0; i < rawNoStackData.Length; i++) { if (weight * add[i] > rawNoStackData[i]) rawNoStackData[i] = weight * add[i]; } } #endregion #region Hunter if (data._sparseIndicesHunter != null) { int i = 0; for (int a = 0; a < data._sparseAdditiveHunterCount; a++, i++) { int index = data._sparseIndicesHunter[i]; _rawAdditiveHunterData[index] += weight * data._rawAdditiveHunterData[index]; } for (int a = 0; a < data._sparseMultiplicativeHunterCount; a++, i++) { int index = data._sparseIndicesHunter[i]; _rawMultiplicativeHunterData[index] = (1 + _rawMultiplicativeHunterData[index]) * (1 + weight * data._rawMultiplicativeHunterData[index]) - 1; } for (int a = 0; a < data._sparseInverseMultiplicativeHunterCount; a++, i++) { int index = data._sparseIndicesHunter[i]; _rawInverseMultiplicativeHunterData[index] = 1 - (1 - _rawInverseMultiplicativeHunterData[index]) * (1 - weight * data._rawInverseMultiplicativeHunterData[index]); } for (int a = 0; a < data._sparseNoStackHunterCount; a++, i++) { int index = data._sparseIndicesHunter[i]; float value = weight * data._rawNoStackHunterData[index]; if (value > _rawNoStackHunterData[index]) _rawNoStackHunterData[index] = value; } } else { float[] add = data._rawAdditiveHunterData; for (int i = 0; i < _rawAdditiveHunterData.Length; i++) { _rawAdditiveHunterData[i] += weight * add[i]; } add = data._rawMultiplicativeHunterData; for (int i = 0; i < _rawMultiplicativeHunterData.Length; i++) { _rawMultiplicativeHunterData[i] = (1 + _rawMultiplicativeHunterData[i]) * (1 + weight * add[i]) - 1; } add = data._rawInverseMultiplicativeHunterData; for (int i = 0; i < _rawInverseMultiplicativeHunterData.Length; i++) { _rawInverseMultiplicativeHunterData[i] = 1 - (1 - _rawInverseMultiplicativeHunterData[i]) * (1 - weight * add[i]); } add = data._rawNoStackHunterData; for (int i = 0; i < _rawNoStackHunterData.Length; i++) { if (weight * add[i] > _rawNoStackHunterData[i]) _rawNoStackHunterData[i] = weight * add[i]; } } #endregion if (data._rawSpecialEffectDataSize > 0) { EnsureSpecialEffectCapacity(_rawSpecialEffectDataSize + data._rawSpecialEffectDataSize); Array.Copy(data._rawSpecialEffectData, 0, _rawSpecialEffectData, _rawSpecialEffectDataSize, data._rawSpecialEffectDataSize); _rawSpecialEffectDataSize += data._rawSpecialEffectDataSize; } }
public void Accumulate(StatsHunter data, float weight) { // Check the root data: if (data == null) { return; } #region Base if (data.sparseIndices != null) { int i = 0; for (int a = 0; a < data.sparseAdditiveCount; a++, i++) { int index = data.sparseIndices[i]; rawAdditiveData[index] += weight * data.rawAdditiveData[index]; } for (int a = 0; a < data.sparseMultiplicativeCount; a++, i++) { int index = data.sparseIndices[i]; rawMultiplicativeData[index] = (1 + rawMultiplicativeData[index]) * (1 + weight * data.rawMultiplicativeData[index]) - 1; } for (int a = 0; a < data.sparseInverseMultiplicativeCount; a++, i++) { int index = data.sparseIndices[i]; rawInverseMultiplicativeData[index] = 1 - (1 - rawInverseMultiplicativeData[index]) * (1 - weight * data.rawInverseMultiplicativeData[index]); } for (int a = 0; a < data.sparseNoStackCount; a++, i++) { int index = data.sparseIndices[i]; float value = weight * data.rawNoStackData[index]; if (value > rawNoStackData[index]) { rawNoStackData[index] = value; } } } else { float[] add = data.rawAdditiveData; for (int i = 0; i < rawAdditiveData.Length; i++) { rawAdditiveData[i] += weight * add[i]; } add = data.rawMultiplicativeData; for (int i = 0; i < rawMultiplicativeData.Length; i++) { rawMultiplicativeData[i] = (1 + rawMultiplicativeData[i]) * (1 + weight * add[i]) - 1; } add = data.rawInverseMultiplicativeData; for (int i = 0; i < rawInverseMultiplicativeData.Length; i++) { rawInverseMultiplicativeData[i] = 1 - (1 - rawInverseMultiplicativeData[i]) * (1 - weight * add[i]); } add = data.rawNoStackData; for (int i = 0; i < rawNoStackData.Length; i++) { if (weight * add[i] > rawNoStackData[i]) { rawNoStackData[i] = weight * add[i]; } } } #endregion #region Hunter if (data._sparseIndicesHunter != null) { int i = 0; for (int a = 0; a < data._sparseAdditiveHunterCount; a++, i++) { int index = data._sparseIndicesHunter[i]; _rawAdditiveHunterData[index] += weight * data._rawAdditiveHunterData[index]; } for (int a = 0; a < data._sparseMultiplicativeHunterCount; a++, i++) { int index = data._sparseIndicesHunter[i]; _rawMultiplicativeHunterData[index] = (1 + _rawMultiplicativeHunterData[index]) * (1 + weight * data._rawMultiplicativeHunterData[index]) - 1; } for (int a = 0; a < data._sparseInverseMultiplicativeHunterCount; a++, i++) { int index = data._sparseIndicesHunter[i]; _rawInverseMultiplicativeHunterData[index] = 1 - (1 - _rawInverseMultiplicativeHunterData[index]) * (1 - weight * data._rawInverseMultiplicativeHunterData[index]); } for (int a = 0; a < data._sparseNoStackHunterCount; a++, i++) { int index = data._sparseIndicesHunter[i]; float value = weight * data._rawNoStackHunterData[index]; if (value > _rawNoStackHunterData[index]) { _rawNoStackHunterData[index] = value; } } } else { float[] add = data._rawAdditiveHunterData; for (int i = 0; i < _rawAdditiveHunterData.Length; i++) { _rawAdditiveHunterData[i] += weight * add[i]; } add = data._rawMultiplicativeHunterData; for (int i = 0; i < _rawMultiplicativeHunterData.Length; i++) { _rawMultiplicativeHunterData[i] = (1 + _rawMultiplicativeHunterData[i]) * (1 + weight * add[i]) - 1; } add = data._rawInverseMultiplicativeHunterData; for (int i = 0; i < _rawInverseMultiplicativeHunterData.Length; i++) { _rawInverseMultiplicativeHunterData[i] = 1 - (1 - _rawInverseMultiplicativeHunterData[i]) * (1 - weight * add[i]); } add = data._rawNoStackHunterData; for (int i = 0; i < _rawNoStackHunterData.Length; i++) { if (weight * add[i] > _rawNoStackHunterData[i]) { _rawNoStackHunterData[i] = weight * add[i]; } } } #endregion if (data._rawSpecialEffectDataSize > 0) { EnsureSpecialEffectCapacity(_rawSpecialEffectDataSize + data._rawSpecialEffectDataSize); Array.Copy(data._rawSpecialEffectData, 0, _rawSpecialEffectData, _rawSpecialEffectDataSize, data._rawSpecialEffectDataSize); _rawSpecialEffectDataSize += data._rawSpecialEffectDataSize; } }
public StatsHunter GetSpecialEffectsStats(Character Char, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, float[] attemptedAtkIntervals, float[] hitRates, float[] critRates, float bleedHitInterval, float dmgDoneInterval, StatsHunter statsTotal, StatsHunter statsToProcess) { StatsHunter statsProcs = new StatsHunter(); float fightDuration = BossOpts.BerserkTimer; float atkspeed = attemptedAtkIntervals[1]; StatsHunter _stats, _stats2; foreach (SpecialEffect effect in (statsToProcess != null ? statsToProcess.SpecialEffects() : statsTotal.SpecialEffects())) { switch (effect.Trigger) { case Trigger.Use: _stats = new StatsHunter(); if (effect.Stats._rawSpecialEffectDataSize == 1 && statsToProcess == null) { float uptime = effect.GetAverageUptime(0f, 1f, atkspeed, fightDuration); _stats.AddSpecialEffect(effect.Stats._rawSpecialEffectData[0]); _stats2 = GetSpecialEffectsStats(Char, triggerIntervals, triggerChances, attemptedAtkIntervals, hitRates, critRates, bleedHitInterval, dmgDoneInterval, statsTotal, _stats); _stats = _stats2 * uptime; } else { _stats.Accumulate(effect.GetAverageStats(triggerIntervals, triggerChances, atkspeed, fightDuration, 1f) as StatsHunter); } statsProcs.Accumulate(_stats); break; case Trigger.MeleeHit: case Trigger.PhysicalHit: case Trigger.PhysicalAttack: if (attemptedAtkIntervals[0] > 0f) { statsProcs.Accumulate(effect.GetAverageStats(triggerIntervals, triggerChances, atkspeed, fightDuration, 1f) as StatsHunter); } break; case Trigger.MeleeCrit: case Trigger.PhysicalCrit: if (attemptedAtkIntervals[0] > 0f) { statsProcs.Accumulate(effect.GetAverageStats(triggerIntervals, triggerChances, atkspeed, fightDuration, 1f) as StatsHunter); } break; case Trigger.DoTTick: if (bleedHitInterval > 0f) { statsProcs.Accumulate(effect.GetAverageStats(triggerIntervals, triggerChances, atkspeed, fightDuration, 1f) as StatsHunter); } // 1/sec DeepWounds, 1/3sec Rend break; case Trigger.DamageDone: // physical and dots if (dmgDoneInterval > 0f) { statsProcs.Accumulate(effect.GetAverageStats(triggerIntervals, triggerChances, atkspeed, fightDuration, 1f) as StatsHunter); } break; case Trigger.PetClawBiteSmackCrit: if (attemptedAtkIntervals[3] > 0f) { // Stats add = effect.GetAverageStats(triggerIntervals[3], critRates[1], atkspeed, fightDuration, 1f); // this needs to be fixed to read steady shot frequencies statsProcs.Accumulate(effect.GetAverageStats(triggerIntervals, triggerChances, atkspeed, fightDuration, 1f) as StatsHunter); ; } break; } } return statsProcs; }
public StatsHunter GetBuffsStats(Character character, CalculationOptionsHunter calcOpts) { List<Buff> removedBuffs = new List<Buff>(); List<Buff> addedBuffs = new List<Buff>(); List<Buff> buffGroup = new List<Buff>(); #region Maintenance Auto-Fixing /* Removes the Sunder Armor if you are maintaining it yourself * Also removes Acid Spit and Expose Armor * We are now calculating this internally for better accuracy and to provide value to relevant talents * if (calcOpts.Maintenance[(int)CalculationOptionsDPSWarr.Maintenances.SunderArmor_]) * { * buffGroup.Clear(); * buffGroup.Add(Buff.GetBuffByName("Sunder Armor")); * buffGroup.Add(Buff.GetBuffByName("Acid Spit")); * buffGroup.Add(Buff.GetBuffByName("Expose Armor")); * MaintBuffHelper(buffGroup, character, removedBuffs); * } */ #endregion #region Passive Ability Auto-Fixing // Removes the Trueshot Aura Buff and it's equivalents Unleashed Rage and Abomination's Might if you are // maintaining it yourself. We are now calculating this internally for better accuracy and to provide // value to relevant talents if (character.HunterTalents.TrueshotAura > 0) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffByName("Trueshot Aura")); buffGroup.Add(Buff.GetBuffByName("Unleashed Rage")); buffGroup.Add(Buff.GetBuffByName("Abomination's Might")); MaintBuffHelper(buffGroup, character, removedBuffs); } // Removes the Ferocious Inspiration Buff and it's equivalents Sanctified Retribution and Arcane Tactics if you are // maintaining it yourself. We are now calculating this internally for better accuracy and to provide // value to relevant talents if (character.HunterTalents.FerociousInspiration > 0) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffByName("Ferocious Inspiration")); buffGroup.Add(Buff.GetBuffByName("Sanctified Retribution")); buffGroup.Add(Buff.GetBuffByName("Arcane Tactics")); MaintBuffHelper(buffGroup, character, removedBuffs); } // Removes the Hunting Party Buff and it's equivalents Improved Icy Talons and Windfury Totem if you are // maintaining it yourself. We are now calculating this internally for better accuracy and to provide // value to relevant talents if (character.HunterTalents.HuntingParty > 0) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffByName("Hunting Party")); buffGroup.Add(Buff.GetBuffByName("Improved Icy Talons")); buffGroup.Add(Buff.GetBuffByName("Windfury Totem")); MaintBuffHelper(buffGroup, character, removedBuffs); } // Removes the Hunter's Mark if you are // maintaining it yourself. We are now calculating this internally for better accuracy and to provide // value to relevant talents buffGroup.Clear(); buffGroup.Add(Buff.GetBuffByName("Hunter's Mark")); MaintBuffHelper(buffGroup, character, removedBuffs); #endregion StatsHunter statsBuffs = new StatsHunter(); statsBuffs.Accumulate(GetBuffsStats(character.ActiveBuffs)); AccumulateSetBonusStats(statsBuffs, character.SetBonusCount); #region PvP Set Bonus int PvPcount; character.SetBonusCount.TryGetValue("Gladiator's Pursuit", out PvPcount); if (PvPcount >= 2) { statsBuffs.Resilience += 400f; statsBuffs.Agility += 70f; } if (PvPcount >= 4) { statsBuffs.BonusFocusRegenMultiplier = 0.05f; statsBuffs.Agility += 90f; } #endregion #region Tier 11 Set Bonus int T11count; character.SetBonusCount.TryGetValue("Lightning-Charged Battlegear", out T11count); if (T11count >= 2) { statsBuffs.BonusSerpentStingCritChance = 0.05f; } if (T11count >= 4) { statsBuffs.FourPieceTier11 = 0.2f; } #endregion #region Tier 12 Set Bonus int T12count; character.SetBonusCount.TryGetValue("Flamewaker's Battlegear", out T12count); if (T12count >= 2) { statsBuffs.AddSpecialEffect(_SE_2T12_cs); statsBuffs.AddSpecialEffect(_SE_2T12_ss); } if (T12count >= 4) { statsBuffs.AddSpecialEffect(_SE_4T12); } #endregion foreach (Buff b in removedBuffs) { character.ActiveBuffsAdd(b); } foreach (Buff b in addedBuffs) { character.ActiveBuffs.Remove(b); } return statsBuffs; }
public void GenPetStats() { // Initial Variables int levelDiff = BossOpts.Level - character.Level; StatsHunter petStatsBase = BasePetStats; #region From Hunter StatsHunter petStatsFromHunter = new StatsHunter() { AttackPower = (HunterStats.RangedAttackPower * 0.424f), SpellPower = HunterStats.RangedAttackPower * 0.211807381f, Stamina = HunterStats.Stamina, Agility = HunterStats.Agility, CritRating = HunterStats.CritRating, Strength = HunterStats.Strength, Spirit = HunterStats.PetSpirit, PhysicalHaste = HunterStats.PhysicalHaste, ArcaneResistance = HunterStats.ArcaneResistance * 0.4f, FireResistance = HunterStats.FireResistance * 0.4f, NatureResistance = HunterStats.NatureResistance * 0.4f, ShadowResistance = HunterStats.ShadowResistance * 0.4f, FrostResistance = HunterStats.FrostResistance * 0.4f, Armor = HunterStats.Armor * 0.7f, SpellPenetration = HunterStats.SpellPenetration, Resilience = HunterStats.Resilience, BonusDamageMultiplier = ((HunterStats.BonusDamageMultiplier /* / (1f + Talents.TheBeastWithin * 0.10f)*/)) * ((character.Race == CharacterRace.Orc ? 0.05f : 0f)), BonusPetDamageMultiplier = HunterStats.BonusPetDamageMultiplier, BonusBleedDamageMultiplier = HunterStats.BonusBleedDamageMultiplier, BonusPetAttackPowerMultiplier = HunterStats.BonusPetAttackPowerMultiplier, // PetAttackPower = HunterStats.PetAttackPower, }; #endregion #region From Talents (Pet or Hunter) Stats petStatsTalents = new Stats() { BonusStaminaMultiplier = PetTalents.GreatStamina * 0.04f, MovementSpeed = PetTalents.BoarsSpeed * 0.30f, PhysicalHaste = PetTalents.SerpentSwiftness * 0.05f, PhysicalCrit = PetTalents.SpidersBite * 0.03f , BaseArmorMultiplier = (1f + PetTalents.NaturalArmor * 0.05f) * (1f + PetTalents.PetBarding * 0.05f) * (1.05f) // Base 5% Armor Bonus - 1f, Dodge = PetTalents.PetBarding * 0.01f, CritChanceReduction = PetTalents.GraceOfTheMantis * 0.03f, ArcaneResistance = PetTalents.GreatResistance * 0.05f, FireResistance = PetTalents.GreatResistance * 0.05f, NatureResistance = PetTalents.GreatResistance * 0.05f, ShadowResistance = PetTalents.GreatResistance * 0.05f, FrostResistance = PetTalents.GreatResistance * 0.05f, FearDurReduc = PetTalents.Lionhearted * 0.15f, StunDurReduc = PetTalents.Lionhearted * 0.15f, BonusDamageMultiplier = (1 + (PetTalents.SharkAttack * 0.03f)) * (1.05f) - 1f, // Base 5% Damage //BonusAttackPowerMultiplier = calculatedStats.aspectBonusAPBeast, BonusHealthMultiplier = 0.05f, // Base 5% Health }; float LongevityCdAdjust = 1f - Talents.Longevity * 0.10f; if (PetTalents.Rabid > 0) { float rabidCooldown = 45f * LongevityCdAdjust; SpecialEffect primary = new SpecialEffect(Trigger.Use, new Stats() { }, 20f, rabidCooldown); SpecialEffect secondary = new SpecialEffect(Trigger.MeleeHit, new Stats() { BonusAttackPowerMultiplier = 0.05f }, 20f, 0f, 0.50f, 5); primary.Stats.AddSpecialEffect(secondary); petStatsTalents.AddSpecialEffect(primary); } if (Talents.Frenzy > 0) { if (frenzy == null) { frenzy = new SpecialEffect(Trigger.MeleeCrit, new Stats() { PhysicalHaste = 0.30f, }, 8f, 1f, Talents.Frenzy * 0.20f); } petStatsTalents.AddSpecialEffect(frenzy); } if (PetTalents.LastStand > 0) { SpecialEffect laststand = new SpecialEffect(Trigger.Use, new Stats() { BonusHealthMultiplier = 0.30f, }, 20f, (1f * 60f) * LongevityCdAdjust); petStatsTalents.AddSpecialEffect(laststand); } #endregion #region From Options Stats petStatsOptionsPanel = new Stats() { PhysicalCrit = StatConversion.NPC_LEVEL_CRIT_MOD[levelDiff], //BonusStaminaMultiplier = 0.05f, }; CalculateTimings(); if(priorityRotation.getSkillFrequency(PetAttacks.SerenityDust) > 0){ // TODO: Need to make sure this freq actually works float freq = priorityRotation.getSkillFrequency(PetAttacks.SerenityDust); SpecialEffect serenitydust = new SpecialEffect(Trigger.Use, new Stats() { BonusAttackPowerMultiplier = 0.10f, }, 15f, BossOpts.BerserkTimer / freq); petStatsOptionsPanel.AddSpecialEffect(serenitydust); petStatsOptionsPanel.HealthRestore += (BossOpts.BerserkTimer / freq) * 825f; } #endregion // Totals // Stats petStatsGearEnchantsBuffs = new Stats(); // petStatsGearEnchantsBuffs.Accumulate(petStatsBuffs); StatsHunter petStatsTotal = new StatsHunter(); petStatsTotal.Accumulate(petStatsBase); petStatsTotal.Accumulate(petStatsFromHunter); // petStatsTotal.Accumulate(petStatsBuffs); petStatsTotal.Accumulate(petStatsTalents); petStatsTotal.Accumulate(petStatsOptionsPanel); StatsHunter petStatsProcs = new StatsHunter(); Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>(); Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>(); #region Stamina & Health float totalBSTAM = petStatsTotal.BonusStaminaMultiplier; //calculatedStats.petBaseHealth = BasePetStats.Health; //calculatedStats.petHealthfromStamina = (petStatsFromHunter.Stamina) * 10.4173919f; petStatsTotal.Stamina = (float)Math.Floor((1f + totalBSTAM) * petStatsTotal.Stamina); // Health | all pets have a 5% Bonus Stam petStatsTotal.Health += (petStatsTotal.Stamina) * 10.4173919f; petStatsTotal.Health *= 1f + petStatsTotal.BonusHealthMultiplier; //calculatedStats.petBonusHealth = petStatsTotal.Health - calculatedStats.petBaseHealth - calculatedStats.petHealthfromStamina; if (PetTalents.Bloodthirsty > 0) { SpecialEffect bloodthirsty = new SpecialEffect(Trigger.MeleeHit, new Stats() { HealthRestore = petStatsTotal.Health * 0.05f }, 0f, 0f, PetTalents.Bloodthirsty * 0.10f); petStatsTotal.AddSpecialEffect(bloodthirsty); } #endregion #region Strength float totalBSTRM = petStatsTotal.BonusStrengthMultiplier; petStatsTotal.Strength = /*(float)Math.Floor(*/(1f + totalBSTRM) * petStatsTotal.Strength/*)*/; #endregion #region Agility float totalBAGIM = petStatsTotal.BonusAgilityMultiplier; petStatsTotal.Agility = /*(float)Math.Floor(*/(1f + totalBAGIM) * petStatsTotal.Agility/*)*/; #endregion #region Attack Power petStatsTotal.BonusAttackPowerMultiplier *= (1f + petStatsTotal.BonusPetAttackPowerMultiplier); float totalBAPM = petStatsTotal.BonusAttackPowerMultiplier; float apFromBase = (1f + totalBAPM) * BasePetStats.PetAttackPower; // float apFromBonus = (1f + totalBAPM) * (petStatsTotal.PetAttackPower - BasePetStats.PetAttackPower); float apFromHunter = ((1f + totalBAPM) * petStatsFromHunter.AttackPower); // float apFromSTR = ((1f + totalBAPM) * (petStatsFromHunter.Strength)) * 0.85f;//(petStatsTotal.Strength - 10f) * 2f; // float apFromHvW = 0f; //(1f + totalBAPM) * (HunterStats.Stamina * 0.10f * Talents.HunterVsWild); petStatsTotal.AttackPower = apFromBase /*+ apFromBonus*/ + apFromHunter /*+ apFromSTR + apFromHvW*/; #endregion #region Haste /*if (StatsPetBuffs._rawSpecialEffectData != null && StatsPetBuffs._rawSpecialEffectData.Length > 0) { Stats add = StatsPetBuffs._rawSpecialEffectData[0].GetAverageStats(); StatsPetBuffs.PhysicalHaste *= (1f + add.PhysicalHaste); }*/ #endregion #region Armor petStatsTotal.Armor = (float)Math.Floor((petStatsTotal.Armor - petStatsFromHunter.Armor) * (1f + petStatsTotal.BaseArmorMultiplier) + petStatsFromHunter.Armor); // petStatsTotal.BonusArmor += petStatsTotal.Agility * 2f; petStatsTotal.BonusArmor = (float)Math.Floor(petStatsTotal.BonusArmor * (1f + petStatsTotal.BonusArmorMultiplier)); petStatsTotal.Armor += petStatsTotal.BonusArmor; #endregion #region Hit/Dodge Chances // This is tied directly to the Hunter's chance to miss PetChanceToMiss = Math.Max(0f, StatConversion.YELLOW_MISS_CHANCE_CAP[levelDiff] - HunterStats.PhysicalHit); PetChanceToSpellMiss = -1f * Math.Min(0f, StatConversion.GetSpellMiss(levelDiff, false) - HunterStats.SpellHit); //calculatedStats.petHitTotal = HunterStats.PhysicalHit; //calculatedStats.petHitSpellTotal = HunterStats.PhysicalHit * 17f / 8f; //petStatsTotal.PhysicalHit = calculatedStats.petHitTotal; //petStatsTotal.SpellHit = calculatedStats.petHitSpellTotal; // If the Hunter is Hit Capped, the pet is also Exp Capped // If not, Pet is proportionately lower based on Hunter's Hit // Expertise itself doesn't factor in at all PetChanceToBeDodged = StatConversion.YELLOW_DODGE_CHANCE_CAP[levelDiff] * Math.Min(1f, (PetChanceToMiss / StatConversion.YELLOW_MISS_CHANCE_CAP[levelDiff])); //calculatedStats.petTargetDodge = PetChanceToBeDodged; float[] avoidChances = { PetChanceToMiss, PetChanceToSpellMiss, PetChanceToBeDodged }; #endregion #region Crit Chance petStatsTotal.PhysicalCrit += StatConversion.GetCritFromAgility(petStatsTotal.Agility, CharacterClass.Warrior) + StatConversion.GetCritFromRating(petStatsTotal.CritRating); //calculatedStats.petCritTotalMelee = petStatsTotal.PhysicalCrit; // Cobra Strikes //calculatedStats.petCritFromCobraStrikes = 0; float cobraStrikesProc = Talents.CobraStrikes * 0.2f; if (cobraStrikesProc > 0) { #if FALSE float cobraStrikesCritFreqSteady = calculatedStats.steadyShot.Freq * (1f / calculatedStats.steadyShot.CritChance); float cobraStrikesCritFreqArcane = calculatedStats.arcaneShot.Freq * (1f / calculatedStats.arcaneShot.CritChance); float cobraStrikesCritFreqKill = calculatedStats.killShot.Freq * (1f / calculatedStats.killShot.CritChance); float cobraStrikesCritFreqSteadyInv = cobraStrikesCritFreqSteady > 0 ? 1f / cobraStrikesCritFreqSteady : 0; float cobraStrikesCritFreqArcaneInv = cobraStrikesCritFreqArcane > 0 ? 1f / cobraStrikesCritFreqArcane : 0; float cobraStrikesCritFreqKillInv = cobraStrikesCritFreqKill > 0 ? 1f / cobraStrikesCritFreqKill : 0; float cobraStrikesCritFreqInv = cobraStrikesCritFreqSteadyInv + cobraStrikesCritFreqArcaneInv + cobraStrikesCritFreqKillInv; float cobraStrikesCritFreq = cobraStrikesCritFreqInv > 0 ? 1f / cobraStrikesCritFreqInv : 0; float cobraStrikesProcAdjust = cobraStrikesCritFreq / cobraStrikesProc; float cobraStrikesFreqSteadyInv = calculatedStats.steadyShot.Freq > 0 ? 1f / calculatedStats.steadyShot.Freq : 0; float cobraStrikesFreqArcaneInv = calculatedStats.arcaneShot.Freq > 0 ? 1f / calculatedStats.arcaneShot.Freq : 0; float cobraStrikesFreqKillInv = calculatedStats.killShot.Freq > 0 ? 1f / calculatedStats.killShot.Freq : 0; float cobraStrikesFreqInv = cobraStrikesFreqSteadyInv + cobraStrikesFreqArcaneInv + cobraStrikesFreqKillInv; float cobraStrikesTwoSpecials = 2f * priorityRotation.petSpecialFrequency; float cobraStrikesUptime = 1f - (float)Math.Pow(1f - calculatedStats.steadyShot.CritChance * cobraStrikesProc, cobraStrikesFreqInv * cobraStrikesTwoSpecials); //calculatedStats.petCritFromCobraStrikes = (cobraStrikesUptime + (1f - cobraStrikesUptime) * calculatedStats.petCritTotalMelee) - calculatedStats.petCritTotalMelee; #endif } //calculatedStats.petCritTotalSpecials = petStatsTotal.PhysicalCrit + calculatedStats.petCritFromCobraStrikes; // PetCritChance //critSpecialsAdjust = calculatedStats.petCritTotalSpecials * 1.5f + 1f; #endregion #region Handle Special Effects CalculateTimings(); WhAtkTable = new PetAttackTable(character, petStatsTotal, CalcOpts, avoidChances, false, false); YwAtkTable = new PetAttackTable(character, petStatsTotal, CalcOpts, avoidChances, PetAttacks.Claw, false, false); float AllAttemptedAtksInterval = GenPetFullAttackSpeed(petStatsTotal); float WhtAttemptedAtksInterval = PetAttackSpeed(petStatsTotal); float YlwAttemptedAtksInterval = AllAttemptedAtksInterval - WhtAttemptedAtksInterval; float[] hitRates = { WhAtkTable.AnyLand, // Whites YwAtkTable.AnyLand }; // Yellows float[] critRates = { WhAtkTable.Crit, // Whites YwAtkTable.Crit /*+ calculatedStats.petCritFromCobraStrikes*/ }; // Yellows float bleedHitInterval = 0f; float rakefreq = priorityRotation.getSkillFrequency(PetAttacks.Rake ); if (rakefreq > 0) { bleedHitInterval += rakefreq; } float dmgDoneInterval = 1f; // Need to Fix this float clawbitesmackinterval = 0f; float clawfreq = priorityRotation.getSkillFrequency(PetAttacks.Claw ); if (clawfreq > 0) { clawbitesmackinterval += clawfreq; } float bitefreq = priorityRotation.getSkillFrequency(PetAttacks.Bite ); if (bitefreq > 0) { clawbitesmackinterval += bitefreq; } float smakfreq = priorityRotation.getSkillFrequency(PetAttacks.Smack); if (smakfreq > 0) { clawbitesmackinterval += smakfreq; } PetClawBiteSmackInterval = clawbitesmackinterval; float[] AttemptedAtkIntervals = { AllAttemptedAtksInterval, // All WhtAttemptedAtksInterval, // Whites YlwAttemptedAtksInterval, // Yellows PetClawBiteSmackInterval, // ClawBiteSmack }; petStatsProcs.Accumulate(GetSpecialEffectsStats(character, triggerIntervals, triggerChances, AttemptedAtkIntervals, hitRates, critRates, bleedHitInterval, dmgDoneInterval, petStatsTotal, null) as StatsHunter); #region Stat Results of Special Effects // Base Stats petStatsProcs.Stamina = (float)Math.Floor(petStatsProcs.Stamina * (1f + totalBSTAM) * (1f + petStatsProcs.BonusStaminaMultiplier)); petStatsProcs.Strength = (float)Math.Floor(petStatsProcs.Strength * (1f + totalBSTRM) * (1f + petStatsProcs.BonusStrengthMultiplier)); petStatsProcs.Agility = (float)Math.Floor(petStatsProcs.Agility * (1f + totalBAGIM) * (1f + petStatsProcs.BonusAgilityMultiplier)); petStatsProcs.Health += (float)Math.Floor(petStatsProcs.Stamina * 10f); petStatsProcs.Health += (float)Math.Floor((petStatsProcs.Health + petStatsTotal.Health) * petStatsProcs.BonusHealthMultiplier); // Armor petStatsProcs.Armor = (float)Math.Floor(petStatsProcs.Armor * (1f + petStatsTotal.BaseArmorMultiplier + petStatsProcs.BaseArmorMultiplier)); petStatsProcs.BonusArmor += petStatsProcs.Agility * 2f; petStatsProcs.BonusArmor = (float)Math.Floor(petStatsProcs.BonusArmor * (1f + petStatsTotal.BonusArmorMultiplier + petStatsProcs.BonusArmorMultiplier)); petStatsProcs.Armor += petStatsProcs.BonusArmor; petStatsProcs.BonusArmor = 0; //it's been added to Armor so kill it // Attack Power petStatsProcs.BonusAttackPowerMultiplier *= (1f + petStatsProcs.BonusPetAttackPowerMultiplier); float totalBAPMProcs = (1f + totalBAPM) * (1f + petStatsProcs.BonusAttackPowerMultiplier) - 1f; float apFromSTRProcs = (1f + totalBAPMProcs) * (petStatsProcs.Strength * 2f); float apBonusOtherProcs = (1f + totalBAPMProcs) * (petStatsProcs.AttackPower + petStatsProcs.PetAttackPower); float apBonusFromBasetoNewMulti = (petStatsProcs.BonusAttackPowerMultiplier) * (petStatsTotal.AttackPower); petStatsProcs.AttackPower = (float)Math.Floor(apFromSTRProcs + apBonusOtherProcs + apBonusFromBasetoNewMulti); // Crit petStatsProcs.PhysicalCrit += StatConversion.GetCritFromAgility(petStatsProcs.Agility, CharacterClass.Warrior); //petStatsProcs.PhysicalCrit += StatConversion.GetCritFromRating(petStatsProcs.CritRating, CharacterClass.Warrior); #endregion petStatsTotal.Accumulate(petStatsProcs); GenPetFullAttackSpeed(petStatsTotal); CalculateTimings(); clawbitesmackinterval = 0f; clawfreq = priorityRotation.getSkillFrequency(PetAttacks.Claw); if (clawfreq > 0) { clawbitesmackinterval += clawfreq; } bitefreq = priorityRotation.getSkillFrequency(PetAttacks.Bite); if (bitefreq > 0) { clawbitesmackinterval += bitefreq; } smakfreq = priorityRotation.getSkillFrequency(PetAttacks.Smack); if (smakfreq > 0) { clawbitesmackinterval += smakfreq; } PetClawBiteSmackInterval = clawbitesmackinterval; #endregion PetStats = petStatsTotal; #region Special Abilities Priority Rotation CalculateTimings(); #endregion #region Kill Command MPS //calculatedStats.petKillCommandMPS = 0; killCommandCooldown = 0; if (CalcOpts.useKillCommand) { float killCommandManaCost = 40f /* * calculatedStats.baseMana*/; // float killCommandReadinessFactor = calculatedStats.priorityRotation.containsShot(Shots.Readiness) ? 1.0f / 180f : 0f; float killCommandCooldownBase = 1.0f; // killCommandCooldown = 1.0f / (killCommandCooldownBase + killCommandReadinessFactor); //calculatedStats.petKillCommandMPS = killCommandCooldown > 0 ? killCommandManaCost / killCommandCooldown : 0; } #endregion #region Target Debuffs float armorDebuffSporeCloud = 0; float sporeCloudFrequency = priorityRotation.getSkillFrequency(PetAttacks.SporeCloud); if (sporeCloudFrequency > 0) { float sporeCloudDuration = 9f; float sporeCloudUptime = sporeCloudDuration > sporeCloudFrequency ? 1f : sporeCloudDuration / sporeCloudFrequency; armorDebuffSporeCloud = sporeCloudUptime * 0.03f; } float armorDebuffAcidSpit = 0; float acidSpitFrequency = priorityRotation.getSkillFrequency(PetAttacks.AcidSpit); // AcidSpitEffectiveRate if (acidSpitFrequency > 0) { float acidSpitCalcFreq = priorityRotation.getSkillCooldown(PetAttacks.AcidSpit); float acidSpitDuration = 30; float acidSpitChanceToApply = 1f - PetChanceToMiss - PetChanceToBeDodged; // V45 float acidSpitChancesToMaintain = (float)Math.Floor((acidSpitDuration - 1f) / acidSpitFrequency); // V46 float acidSpitChanceToApplyFirst = acidSpitChancesToMaintain == 0 ? 0 : acidSpitChanceToApply; // V47 float acidSpitChanceToStop = 1f - acidSpitChanceToApplyFirst; // AcidSpitChanceToStop float acidSpitAverageTimeToInc = acidSpitChanceToApplyFirst > 0 ? acidSpitFrequency : 0; // AcidSpitTimeToInc float acidSpitTimeSpentAtMax = acidSpitAverageTimeToInc + (acidSpitDuration * acidSpitChanceToStop); // AcidSpitAverageStackTime PetSkillStack[] stacks = new PetSkillStack[3]; stacks[0] = new PetSkillStack(); stacks[1] = new PetSkillStack(); stacks[2] = new PetSkillStack(); stacks[0].time_to_reach = 0; stacks[1].time_to_reach = acidSpitAverageTimeToInc * 1; stacks[2].time_to_reach = acidSpitAverageTimeToInc * 2; stacks[0].chance_to_max = 0; stacks[1].chance_to_max = acidSpitChanceToStop == 1 ? 0 : acidSpitChanceToStop; stacks[2].chance_to_max = acidSpitChanceToStop == 1 ? 0 : 1 - (stacks[0].chance_to_max + stacks[1].chance_to_max); stacks[0].time_spent = stacks[1].time_to_reach; stacks[1].time_spent = stacks[1].time_to_reach == 0 ? 0 : acidSpitTimeSpentAtMax * (1 - stacks[0].chance_to_max); stacks[2].time_spent = stacks[1].time_to_reach == 0 ? 0 : acidSpitChanceToStop == 0 ? 1 : 1 / acidSpitChanceToStop * acidSpitCalcFreq * (1-(stacks[0].chance_to_max + stacks[1].chance_to_max)); float acidSpitTotalTime = stacks[0].time_spent + stacks[1].time_spent + stacks[2].time_spent; stacks[0].percent_time = acidSpitTotalTime == 0 ? 1 : stacks[0].time_spent / acidSpitTotalTime; stacks[1].percent_time = acidSpitTotalTime == 0 ? 1 : stacks[1].time_spent / acidSpitTotalTime; stacks[2].percent_time = acidSpitTotalTime == 0 ? 1 : stacks[2].time_spent / acidSpitTotalTime; stacks[0].total = stacks[0].percent_time * 0.0f; stacks[1].total = stacks[0].percent_time * 0.1f; stacks[2].total = stacks[0].percent_time * 0.2f; armorDebuffAcidSpit = stacks[0].total + stacks[1].total + stacks[2].total; } float armorDebuffSting = 0; float stingFrequency = priorityRotation.getSkillFrequency(PetAttacks.Sting); if (stingFrequency > 0) { float stingUseFreq = priorityRotation.getSkillFrequency(PetAttacks.Sting); float stingDuration = 20; float stingUptime = stingDuration > stingUseFreq ? 1 : stingDuration / stingUseFreq; armorDebuffSting = stingUptime * 0.05f; } // these local buffs can be overridden if (character.ActiveBuffsConflictingBuffContains("Sting")) { armorDebuffSporeCloud = 0; armorDebuffSting = 0; } if (character.ActiveBuffsConflictingBuffContains("Acid Spit")) { armorDebuffAcidSpit = 0; } //calculatedStats.petArmorDebuffs = 0f - (1f - armorDebuffSporeCloud) * (1f - armorDebuffAcidSpit) * (1f - armorDebuffSting) + 1; #endregion #region Hunter Effects // Ferocious Inspiraion // (Same as above) //calculatedStats.ferociousInspirationDamageAdjust = 1; if (character.HunterTalents.FerociousInspiration > 0) { if (CalcOpts.PetFamily != PETFAMILY.None) { float ferociousInspirationSpecialsEffect = priorityRotation.petSpecialFrequency == 0 ? 0 : 10f / priorityRotation.petSpecialFrequency; //float ferociousInspirationUptime = 1f - (float)Math.Pow(1f - calculatedStats.petCritTotalSpecials, (10f / attackSpeedEffective) + ferociousInspirationSpecialsEffect); float ferociousInspirationEffect = 0.01f * character.HunterTalents.FerociousInspiration; //calculatedStats.ferociousInspirationDamageAdjust = 1.0f + ferociousInspirationUptime * ferociousInspirationEffect; } } // Roar of Recovery //calculatedStats.manaRegenRoarOfRecovery = 0; float roarOfRecoveryFreq = priorityRotation.getSkillFrequency(PetAttacks.RoarOfRecovery); if (roarOfRecoveryFreq > 0) { float roarOfRecoveryUseCount = (float)Math.Ceiling(BossOpts.BerserkTimer / roarOfRecoveryFreq); float roarOfRecoveryManaRestored = HunterStats.Mana * 0.30f * roarOfRecoveryUseCount; // E129 //calculatedStats.manaRegenRoarOfRecovery = roarOfRecoveryUseCount > 0 ? roarOfRecoveryManaRestored / BossOpts.BerserkTimer : 0; } //Invigoration //calculatedStats.manaRegenInvigoration = 0; float invigorationProcChance = Talents.Invigoration * 0.50f; // C32 if (invigorationProcChance > 0) { float invigorationProcFreq = (priorityRotation.petSpecialFrequency /*/ calculatedStats.petCritTotalSpecials*/) / invigorationProcChance; //C35 float invigorationEffect = Talents.Invigoration > 0 ? 0.01f : 0; float invigorationManaGainedPercent = invigorationProcFreq > 0 ? 60f / invigorationProcFreq * invigorationEffect : 0; // C36 float invigorationManaPerMinute = invigorationProcFreq > 0 ? 60f / invigorationProcFreq * invigorationEffect * HunterStats.Mana : 0; // C37 //calculatedStats.manaRegenInvigoration = invigorationManaPerMinute / 60f; } #endregion #region Target Armor Effect //31-10-2009 Drizz: added Armor effect #if RAWR3 || RAWR4 || SILVERLIGHT //double petEffectiveArmor = BossOpts.Armor * (1f - calculatedStats.petArmorDebuffs); //calculatedStats.petTargetArmorReduction = StatConversion.GetArmorDamageReduction(character.Level, BossOpts.Armor, calculatedStats.petArmorDebuffs, 0, 0); #else double petEffectiveArmor = CalcOpts.TargetArmor * (1f - calculatedStats.petArmorDebuffs); calculatedStats.petTargetArmorReduction = StatConversion.GetArmorDamageReduction(character.Level, CalcOpts.TargetArmor, calculatedStats.petArmorDebuffs, 0, 0); #endif //petEffectiveArmor/(petEffectiveArmor - 22167.5f + (467.5f*80f)); #endregion }
/// <summary> /// With Triggers already setup, just pass into the Accumulate function and return the values /// </summary> /// <param name="effect"></param> /// <returns></returns> public StatsHunter getSpecialEffects(SpecialEffect effect, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, Character Char) { StatsHunter statsAverage = new StatsHunter(); triggerIntervals[Trigger.Use] = effect.Cooldown; if (float.IsInfinity(effect.Cooldown)) triggerIntervals[Trigger.Use] = Char.BossOptions.BerserkTimer; ItemInstance RangeWeap = Char.Ranged; float unhastedAttackSpeed = (RangeWeap != null ? RangeWeap.Speed : 2.4f); effect.AccumulateAverageStats(statsAverage, triggerIntervals, triggerChances, unhastedAttackSpeed, Char.BossOptions.BerserkTimer); return statsAverage; }
public override Stats GetRelevantStats(Stats stats) { StatsHunter relevantStats = new StatsHunter() { #region Basic Stats Stamina = stats.Stamina, Health = stats.Health, Agility = stats.Agility, Armor = stats.Armor, BonusArmor = stats.BonusArmor, #endregion #region Ratings AttackPower = stats.AttackPower, RangedAttackPower = stats.RangedAttackPower, PhysicalCrit = stats.PhysicalCrit, CritRating = stats.CritRating, RangedCritRating = stats.RangedCritRating, PhysicalHit = stats.PhysicalHit, HitRating = stats.HitRating, RangedHitRating = stats.RangedHitRating, PhysicalHaste = stats.PhysicalHaste, HasteRating = stats.HasteRating, RangedHasteRating = stats.RangedHasteRating, MasteryRating = stats.MasteryRating, TargetArmorReduction = stats.TargetArmorReduction, Miss = stats.Miss, ScopeDamage = stats.ScopeDamage, #endregion #region Special HighestStat = stats.HighestStat, HighestSecondaryStat = stats.HighestSecondaryStat, Paragon = stats.Paragon, MovementSpeed = stats.MovementSpeed, StunDurReduc = stats.StunDurReduc, SnareRootDurReduc = stats.SnareRootDurReduc, FearDurReduc = stats.FearDurReduc, DarkmoonCardDeathProc = stats.DarkmoonCardDeathProc, #endregion #region Survivability HealthRestore = stats.HealthRestore, HealthRestoreFromMaxHealth = stats.HealthRestoreFromMaxHealth, #endregion // Set Bonuses #region Multipliers BonusStaminaMultiplier = stats.BonusStaminaMultiplier, BonusAgilityMultiplier = stats.BonusAgilityMultiplier, BonusIntellectMultiplier = stats.BonusIntellectMultiplier, BonusAttackPowerMultiplier = stats.BonusAttackPowerMultiplier, DamageTakenReductionMultiplier = stats.DamageTakenReductionMultiplier, BonusDamageMultiplier = stats.BonusDamageMultiplier, BaseArmorMultiplier = stats.BaseArmorMultiplier, BonusArmorMultiplier = stats.BonusArmorMultiplier, BonusCritDamageMultiplier = stats.BonusCritDamageMultiplier, //BonusSpiritMultiplier = stats.BonusSpiritMultiplier, // BonusManaMultiplier = stats.BonusManaMultiplier, BonusBleedDamageMultiplier = stats.BonusBleedDamageMultiplier, BonusPhysicalDamageMultiplier = stats.BonusPhysicalDamageMultiplier, #endregion #region Damage Procs ShadowDamage = stats.ShadowDamage, ArcaneDamage = stats.ArcaneDamage, HolyDamage = stats.HolyDamage, NatureDamage = stats.NatureDamage, FrostDamage = stats.FrostDamage, FireDamage = stats.FireDamage, BonusShadowDamageMultiplier = stats.BonusShadowDamageMultiplier, BonusArcaneDamageMultiplier = stats.BonusArcaneDamageMultiplier, BonusHolyDamageMultiplier = stats.BonusHolyDamageMultiplier, BonusNatureDamageMultiplier = stats.BonusNatureDamageMultiplier, BonusFrostDamageMultiplier = stats.BonusFrostDamageMultiplier, BonusFireDamageMultiplier = stats.BonusFireDamageMultiplier, #endregion }; foreach (SpecialEffect effect in stats.SpecialEffects()) { if (RelevantTriggers.Contains(effect.Trigger) && (HasRelevantStats(effect.Stats) || HasSurvivabilityStats(effect.Stats))) { relevantStats.AddSpecialEffect(effect); } } return relevantStats; }
private StatsHunter GetSpecialEffectsStats(Character Char, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, StatsHunter statsTotal, StatsHunter statsToProcess) { CalculationOptionsHunter calcOpts = Char.CalculationOptions as CalculationOptionsHunter; //BossOptions bossOpts = Char.BossOptions; ItemInstance RangeWeap = Char.MainHand; float speed = (RangeWeap != null ? RangeWeap.Speed : 2.4f); HunterTalents talents = Char.HunterTalents; StatsHunter statsProcs = new StatsHunter(); float fightDuration_M = 450f; //bossOpts.BerserkTimer; StatsHunter _stats, _stats2; try { foreach (SpecialEffect effect in (statsToProcess != null ? statsToProcess.SpecialEffects() : statsTotal.SpecialEffects())) { float fightDuration = fightDuration_M; /*float oldArp = float.Parse(effect.Stats.ArmorPenetrationRating.ToString()); float arpToHardCap = StatConversion.RATING_PER_ARMORPENETRATION; if (effect.Stats.ArmorPenetrationRating > 0) { float arpenBuffs = 0.0f; float currentArp = arpenBuffs + StatConversion.GetArmorPenetrationFromRating(statsTotal.ArmorPenetrationRating + (statsToProcess != null ? statsToProcess.ArmorPenetrationRating : 0f)); arpToHardCap *= (1f - currentArp); }*/ if (triggerIntervals.ContainsKey(effect.Trigger) && (triggerIntervals[effect.Trigger] > 0f || effect.Trigger == Trigger.Use)) { float weight = 1f; switch (effect.Trigger) { case Trigger.Use: _stats = new StatsHunter(); if (effect.Stats._rawSpecialEffectDataSize == 1 && statsToProcess == null) { float uptime = effect.GetAverageUptime(0f, 1f, speed, fightDuration); _stats.AddSpecialEffect(effect.Stats._rawSpecialEffectData[0]); _stats2 = GetSpecialEffectsStats(Char, triggerIntervals, triggerChances, statsTotal, _stats); _stats = _stats2 * uptime; } else { /*if (effect.Stats.ArmorPenetrationRating > 0 && arpToHardCap < effect.Stats.ArmorPenetrationRating) { float uptime = effect.GetAverageUptime(0f, 1f, speed, fightDuration); weight = uptime; _stats.ArmorPenetrationRating = arpToHardCap; } else {*/ // _stats = effect.GetAverageStats(0f, 1f, speed, fightDuration); _stats = effect.GetAverageStats(triggerIntervals, triggerChances, speed, fightDuration, 1f) as StatsHunter; //} } statsProcs.Accumulate(_stats, weight); _stats = null; break; case Trigger.RangedHit: case Trigger.PhysicalHit: case Trigger.PhysicalAttack: _stats = new StatsHunter(); weight = 1.0f; { /*if (effect.Stats.ArmorPenetrationRating > 0 && arpToHardCap < effect.Stats.ArmorPenetrationRating) { float uptime = effect.GetAverageUptime(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], speed, fightDuration); weight = uptime; _stats.ArmorPenetrationRating = arpToHardCap; } else {*/ _stats = effect.GetAverageStats(triggerIntervals, triggerChances, speed, fightDuration, weight) as StatsHunter; //} } statsProcs.Accumulate(_stats, weight); _stats = null; break; case Trigger.MeleeHit: // Pets Only case Trigger.MeleeCrit: // Pets Only case Trigger.RangedCrit: case Trigger.PhysicalCrit: case Trigger.DoTTick: case Trigger.DamageDone: // physical and dots case Trigger.DamageOrHealingDone: // physical and dots case Trigger.HunterAutoShotHit: case Trigger.SteadyShotHit: case Trigger.CobraShotHit: case Trigger.PetClawBiteSmackCrit: _stats = new StatsHunter(); weight = 1.0f; /*if (effect.Stats.ArmorPenetrationRating > 0 && arpToHardCap < effect.Stats.ArmorPenetrationRating) { float uptime = effect.GetAverageUptime(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], speed, fightDuration); weight = uptime; _stats.ArmorPenetrationRating = arpToHardCap; } else {*/ _stats = effect.GetAverageStats(triggerIntervals, triggerChances, speed, fightDuration, weight) as StatsHunter; //} statsProcs.Accumulate(_stats, weight); break; case Trigger.SerpentWyvernStingsDoDamage: _stats = new StatsHunter(); weight = 1.0f; /*if (effect.Stats.ArmorPenetrationRating > 0 && arpToHardCap < effect.Stats.ArmorPenetrationRating) { float uptime = effect.GetAverageUptime(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], speed, fightDuration); weight = uptime; _stats.ArmorPenetrationRating = arpToHardCap; } else {*/ _stats = effect.GetAverageStats(triggerIntervals, triggerChances, speed, fightDuration, weight) as StatsHunter; //} statsProcs.Accumulate(_stats, weight); break; } } //effect.Stats.ArmorPenetrationRating = oldArp; } } catch (Exception ex) { new Base.ErrorBox() { Title = "Error in getting Special Effect information", Function = "GetSpecialEffectsStats()", TheException = ex, }.Show(); } return statsProcs; }
//private static readonly SpecialEffect FuriousHowl = new SpecialEffect(Trigger.Use, new StatsHunter() { AttackPower = 320f, PetAttackPower = 320f, }, 20f, 40f); private static void CalculateTriggers(Character character, CharacterCalculationsHunter calculatedStats, StatsHunter statsTotal, CalculationOptionsHunter calcOpts, BossOptions bossOpts, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances) { const float GCD = 1f; const float GCDpSec = 1f / GCD; int levelDif = bossOpts.Level - character.Level; float critMOD = StatConversion.NPC_LEVEL_CRIT_MOD[levelDif]; HunterTalents talents = character.HunterTalents; float rangedWeaponSpeed = 2.4f; float rangedWeaponDamage = 1; float autoShotSpeed = rangedWeaponSpeed; // Should be the same as rangedWeaponSpeed hasted. float autoShotsPerSecond = 1 / autoShotSpeed; float specialShotsPerSecond = GCDpSec; // I know it doesn't start w/ a special every GCD, but something to start with more than 0. float totalShotsPerSecond = GCDpSec + autoShotsPerSecond; float shotsPerSecondWithoutHawk = 0; // RotationTest rotationTest; //GenRotation(character, statsTotal, calculatedStats, calcOpts, bossOpts, talents, // out rangedWeaponSpeed, out rangedWeaponDamage, out autoShotSpeed, // out autoShotsPerSecond, out specialShotsPerSecond, out totalShotsPerSecond, out shotsPerSecondWithoutHawk, // out rotationTest); float ChanceToMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[levelDif] - statsTotal.PhysicalHit); float ChanceToSpellMiss = Math.Max(0f, StatConversion.GetSpellMiss(levelDif, false) - statsTotal.SpellHit); // TODO: Ensure that we don't have any div by 0 issues here. #region Generic // Use triggerIntervals[Trigger.Use] = 0f; // Should be Effect cooldown. triggerChances[Trigger.Use] = 1f; // Physical Hit triggerIntervals[Trigger.PhysicalAttack] = triggerIntervals[Trigger.RangedHit] = triggerIntervals[Trigger.PhysicalHit] = triggerIntervals[Trigger.RangedCrit] = triggerIntervals[Trigger.PhysicalCrit] = 1f / totalShotsPerSecond; // Note with DOT crits, this may have to be adjusted. triggerChances[Trigger.PhysicalAttack] = triggerChances[Trigger.RangedHit] = triggerChances[Trigger.PhysicalHit] = (1f - ChanceToMiss); triggerChances[Trigger.RangedCrit] = triggerChances[Trigger.PhysicalCrit] = Math.Min(1f + critMOD, Math.Max(0f, statsTotal.PhysicalCrit)); // Dots & damage done. triggerIntervals[Trigger.DoTTick] = talents.PiercingShots > 0 ? 1f : 0f; // Also need to add other DOTs into this value. triggerIntervals[Trigger.DamageDone] = Math.Max(0f, 1f / (totalShotsPerSecond + ((talents.PiercingShots > 0 ? 1f : 0f) > 0 ? 1f / (talents.PiercingShots > 0 ? 1f : 0f) : 0f))); triggerIntervals[Trigger.DamageOrHealingDone] = Math.Max(0f, 1f / (totalShotsPerSecond + ((talents.PiercingShots > 0 ? 1f : 0f) > 0 ? 1f / (talents.PiercingShots > 0 ? 1f : 0f) : 0f))); // Need to add Self/pet-Heals triggerChances[Trigger.DoTTick] = 1f; // This should be up-time on DoTs & PS. triggerChances[Trigger.DamageDone] = 1f; triggerChances[Trigger.DamageOrHealingDone] = 1f; #endregion #region Pets only //triggerIntervals[Trigger.MeleeHit] = //triggerIntervals[Trigger.MeleeCrit] = Math.Max(0f, calculatedStats.PetCalc.PetCompInterval); //triggerIntervals[Trigger.PetClawBiteSmackCrit] = Math.Max(0f, calculatedStats.PetCalc.PetClawBiteSmackInterval); //triggerChances[Trigger.MeleeHit] = calculatedStats.PetCalc.WhAtkTable.AnyLand; //triggerChances[Trigger.MeleeCrit] = Math.Min(1f + critMOD, Math.Max(0f, calculatedStats.PetCalc.WhAtkTable.Crit)); //triggerChances[Trigger.PetClawBiteSmackCrit] = Math.Min(1f + critMOD, Math.Max(0f, calculatedStats.PetCalc.WhAtkTable.Crit)); #endregion #region Hunter Specific triggerIntervals[Trigger.HunterAutoShotHit] = 1f / autoShotsPerSecond; triggerIntervals[Trigger.SteadyShotHit] = 0; // calculatedStats.steadyShot.Cd; triggerIntervals[Trigger.CobraShotHit] = 0; // calculatedStats.cobraShot.Cd; triggerIntervals[Trigger.EnergyOrFocusDropsBelow20PercentOfMax] = 4f; // Approximating as 80% chance every 4 seconds. TODO: Put in some actual method of calculating this triggerChances[Trigger.HunterAutoShotHit] = (1f - ChanceToMiss); triggerChances[Trigger.SteadyShotHit] = (1f - ChanceToMiss); triggerChances[Trigger.CobraShotHit] = (1f - ChanceToMiss); triggerChances[Trigger.EnergyOrFocusDropsBelow20PercentOfMax] = 0.80f; // Approximating as 80% chance every 4 seconds. TODO: Put in some actual method of calculating this #endregion }