/// <summary> /// Adds a maintenance ability to the rotation if it's been validated /// </summary> /// <param name="totalPercTimeLost">Time lost due to stun/fear/movement</param> /// <param name="abil">The ability to add</param> /// <returns>The final result from Abil.GetManaUseOverDur</returns> private float AddMaintenanceAbility(float totalPercTimeLost, AbilWrapper aw) { if (!aw.ability.Validated) { return(0f); } float Abil_GCDs = Math.Min(GCDsAvailable, aw.ability.Activates * (1f - totalPercTimeLost)); aw.numActivates = Abil_GCDs; //availGCDs -= Abil_GCDs; if (_needDisplayCalcs && Abil_GCDs > 0) { GCDUsage += Abil_GCDs.ToString("000.00") + " : " + aw.ability.Name + (aw.ability.UsesGCD ? "\n" : "(Doesn't use GCDs)\n"); } _HPS_TTL += aw.HPS; _DPS_TTL += aw.DPS; return(aw.ability.GetManaUseOverDur(Abil_GCDs)); }
public override void doIterations() { try { base.doIterations(); // Fury Iteration BloodSurge BS = GetWrapper <BloodSurge>().ability as BloodSurge; AbilWrapper HS = GetWrapper <HeroicStrike>(); AbilWrapper CL = GetWrapper <Cleave>(); OnAttack _HS = HS.ability as OnAttack; OnAttack _CL = CL.ability as OnAttack; float ovdRage, hsRageUsed, clRageUsed; float oldBS, oldHS, oldCL; do { oldBS = BS.Activates; oldHS = HS.allNumActivates; oldCL = CL.allNumActivates; ovdRage = FreeRageOverDur; hsRageUsed = ovdRage * percHS; clRageUsed = ovdRage * percCL; WhiteAtks.HSOverridesOverDur = HS.numActivates = Math.Min(hsRageUsed / _HS.FullRageCost, WhiteAtks.MhActivatesNoHS); WhiteAtks.CLOverridesOverDur = CL.numActivates = Math.Min(clRageUsed / _CL.FullRageCost, WhiteAtks.MhActivatesNoHS - WhiteAtks.HSOverridesOverDur); BS.hsActivates = HS.allNumActivates; } while (Math.Abs(1f - (BS.Activates != 0 ? oldBS / BS.Activates : 1f)) > 0.005f || Math.Abs(1f - (HS.allNumActivates <= 0 ? oldHS / HS.allNumActivates : 1f)) > 0.005f || Math.Abs(1f - (CL.allNumActivates <= 0 ? oldCL / CL.allNumActivates : 1f)) > 0.005f); } catch (Exception ex) { ErrorBox eb = new ErrorBox("Error in performing Fury Iterations", ex.Message, "doIterations()", "No Additional Info", ex.StackTrace); eb.Show(); } }
private void AddAbility(AbilWrapper abilWrapper) { AbilityList.Add(abilWrapper.ability.GetType(), abilWrapper); }
public override void MakeRotationandDoDPS(bool setCalcs, bool needsDisplayCalculations) { #region Cata Calcs try { base.MakeRotationandDoDPS(setCalcs, needsDisplayCalculations); MakeRotationAndDoDPS(setCalcs, TimeUndr20Perc); } catch (Exception ex) { new Base.ErrorBox() { Title = "Error in creating Fury Rotation Details", Function = "MakeRotationandDoDPS()", TheException = ex, }.Show(); } #endregion #region WotLK based Calcs #if FALSE base.MakeRotationandDoDPS(setCalcs, needsDisplayCalcs); //new_MakeRotationandDoDPS(setCalcs); // Starting Numbers float DPS_TTL = 0f, HPS_TTL = 0f; float LatentGCD = 1.5f + CalcOpts.Latency; if (_needDisplayCalcs) { GCDUsage += "NumGCDs: " + NumGCDsO20.ToString() + "\n\n"; } float GCDsused = 0f; float availGCDs = Math.Max(0f, NumGCDsO20 - GCDsused); float availRage = 0f; float rageadd = 0f; //float timelostwhilestunned = 0f; //float percTimeInStun = 0f; if (Char.MainHand == null) { return; } //doIterations(); // ==== Rage Generation Priorities ======== availRage += RageGenOverDur_Other; /*Bloodrage */ //AddAnItem(ref availRage, percTimeInStun, ref _Blood_GCDs, ref HPS_TTL, ref _Blood_HPS, BR); /*Berserker Rage */ /*float Reck_GCDs = Math.Min(availGCDs, RK.Activates); * _Reck_GCDs = Reck_GCDs; * GCDsused += Math.Min(NumGCDs, Reck_GCDs); * GCDUsage += RK.Name + ": " + Reck_GCDs.ToString() + "\n"; * availGCDs = Math.Max(0f, NumGCDs - GCDsused); * rageadd = RK.GetRageUsePerSecond(Reck_GCDs); * availRage -= rageadd; * RageNeeded += rageadd;*/ //doIterations(); #region CalcOpts GCD Losses #region Stuns /*if (CalcOpts.StunningTargets && CalcOpts.Stuns.Count > 0f) * { * float numStuns = FightDuration / CalcOpts.StunningTargetsFreq; * if (!CalcOpts.SE_UseDur) numStuns = (float)Math.Floor(numStuns); * // ways to counter, no GCD, just use reaction time * if (Talents.HeroicFury > 0f) * { * float numUses = Math.Min(numStuns, GetWrapper<HeroicFury>().ability.Activates); * numStuns -= numUses; * timeLost += numUses * (CalcOpts.React / 1000f + CalcOpts.Latency); // not using GetReact because it's not on the GCD * GetWrapper<HeroicFury>().numActivates += numUses; * } * if (Char.Race == CharacterRace.Human) * { * float numUses = Math.Min(numStuns, GetWrapper<EveryManForHimself>().ability.Activates); * numStuns -= numUses; * timeLost += numUses * CalcOpts.React / 1000f + CalcOpts.Latency; // not using GetReact because it's not on the GCD * GetWrapper<EveryManForHimself>().numActivates += numUses; * } * * if (numStuns > 0f) * { * float stunDur = CalcOpts.StunningTargetsDur / 1000f * (1f - Talents.IronWill * 2f / 30f); * timeLost += stunDur * numStuns; * } * }*/ #endregion float otherTimeLost = CalculateTimeLost(); DoMaintenanceActivates(otherTimeLost); float timeLostPerc = otherTimeLost; #endregion AbilWrapper WW = GetWrapper <WhirlWind>(); AbilWrapper BT = GetWrapper <BloodThirst>(); AbilWrapper BS = GetWrapper <BloodSurge>(); AbilWrapper RB = GetWrapper <RagingBlow>(); AbilWrapper HS = GetWrapper <HeroicStrike>(); AbilWrapper CL = GetWrapper <Cleave>(); // Priority 1 : Whirlwind on every CD float WW_GCDs = Math.Min(availGCDs, WW.ability.Activates) * (1f - timeLostPerc); WW.numActivatesO20 = WW_GCDs; GCDsused += Math.Min(NumGCDsO20, WW_GCDs); if (_needDisplayCalcs) { GCDUsage += WW.ability.Name + ": " + WW_GCDs.ToString() + "\n"; } availGCDs = Math.Max(0f, NumGCDsO20 - GCDsused); DPS_TTL += WW.DPSO20; HPS_TTL += WW.HPSO20; rageadd = WW.allRage; availRage -= rageadd; // Priority 2 : Bloodthirst on every CD float BT_GCDs = Math.Min(availGCDs, BT.ability.Activates) * (1f - timeLostPerc); BT.numActivatesO20 = BT_GCDs; GCDsused += Math.Min(NumGCDsO20, BT_GCDs); if (_needDisplayCalcs) { GCDUsage += BT.ability.Name + ": " + BT_GCDs.ToString() + "\n"; } availGCDs = Math.Max(0f, NumGCDsO20 - GCDsused); DPS_TTL += BT.DPSO20; HPS_TTL += BT.HPSO20; rageadd = BT.allRage; availRage -= rageadd; doIterations(); // Priority 3 : Bloodsurge Blood Proc (Do an Instant Slam) if available float BS_GCDs = Math.Min(availGCDs, BS.ability.Activates) * (1f - timeLostPerc); BS.numActivatesO20 = BS_GCDs; GCDsused += Math.Min(NumGCDsO20, BS_GCDs); if (_needDisplayCalcs) { GCDUsage += BS.ability.Name + ": " + BS_GCDs.ToString() + "\n"; } availGCDs = Math.Max(0f, NumGCDsO20 - GCDsused); DPS_TTL += BS.DPSO20; HPS_TTL += BS.HPSO20; rageadd = BS.allRage; availRage -= rageadd; // Priority 4 : Raging Blow if available float RB_GCDs = Math.Min(availGCDs, RB.ability.Activates) * (1f - timeLostPerc); RB.numActivatesO20 = RB_GCDs; GCDsused += Math.Min(NumGCDsO20, RB_GCDs); if (_needDisplayCalcs) { GCDUsage += RB.ability.Name + ": " + RB_GCDs.ToString() + "\n"; } availGCDs = Math.Max(0f, NumGCDsO20 - GCDsused); DPS_TTL += RB.DPSO20; HPS_TTL += RB.HPSO20; rageadd = RB.allRage; availRage -= rageadd; InvalidateCache(); // Heroic Strike/Cleave now that they are on GCDs. These should be rage dumps // Computing them together as you use HS for single, CL for Multiple if (/*PercFailRage == 1f &&*/ (HS.ability.Validated || CL.ability.Validated)) { float acts = Math.Min(GCDsAvailableO20, HS.ability.Activates /** percTimeInDPSAndOver20*/); float Abil_GCDs = acts; CL.numActivatesO20 = Abil_GCDs * (BossOpts.MultiTargsTime / FightDuration); HS.numActivatesO20 = Abil_GCDs - CL.numActivatesO20; (HS.ability as HeroicStrike).InciteBonusCrits(HS.numActivatesO20); //availGCDs = Math.Max(0f, origNumGCDs - GCDsused); availRage -= HS.RageO20 + CL.RageO20; } //HSspace = HS.numActivates / NumGCDs * HS.ability.UseTime / LatentGCD; //CLspace = CL.numActivates / NumGCDs * CL.ability.UseTime / LatentGCD; // Priority 4 : Heroic Strike, when there is rage to do so, handled by the Heroic Strike class // Alternate to Cleave is MultiTargs is active // After iterating how many Overrides can be done and still do other abilities, then do the white dps availRage += Whiteattacks.MHRageGenOverDur + Whiteattacks.OHRageGenOverDur; /*bool HSok = CalcOpts.Maintenance[(int)CalculationOptionsDPSWarr.Maintenances.HeroicStrike_]; * bool CLok = * BossOpts.MultiTargs && BossOpts.Targets != null && BossOpts.Targets.Count > 0 * && CalcOpts.Maintenance[(int)CalculationOptionsDPSWarr.Maintenances.Cleave_];*/ Whiteattacks.Slam_ActsOverDurO20 = Whiteattacks.Slam_ActsOverDurU20 = 0f;// _SL_GCDs; DPS_TTL += Whiteattacks.MhDPS * (1f - timeLostPerc) + Whiteattacks.OhDPS * (1f - timeLostPerc); DPS_TTL += GetWrapper <HeroicStrike>().DPSO20; DPS_TTL += GetWrapper <Cleave>().DPSO20; calcDeepWounds(); DPS_TTL += DW.DPS; if (_needDisplayCalcs) { GCDUsage += "\nAvail: " + availGCDs.ToString(); } // Return result _HPS_TTL = HPS_TTL; if (setCalcs) { this.calcs.TotalDPS = DPS_TTL; this.calcs.WhiteDPS = (Whiteattacks.MhDPS + Whiteattacks.OhDPS) * (1f - timeLostPerc); this.calcs.WhiteRageO20 = Whiteattacks.MHRageGenOverDur + Whiteattacks.OHRageGenOverDur; this.calcs.OtherRageO20 = RageGenOverDur_Other; this.calcs.NeedyRageO20 = RageNeededOverDur; this.calcs.FreeRageO20 = calcs.WhiteRageO20 + calcs.OtherRageO20 - calcs.NeedyRageO20; this.calcs.WhiteDPSMH = this.Whiteattacks.MhDPS; this.calcs.WhiteDPSMHU20 = -1f; // Fury doesn't use this this.calcs.WhiteDPSOH = this.Whiteattacks.OhDPS; this.calcs.WhiteDmg = this.Whiteattacks.MhDamageOnUse + this.Whiteattacks.OhDamageOnUse; } #endif #endregion }
/*private float bloodsurge_percUsed; // Since we can only bloodsurge once every * // 8secs, this keeps track of how many times we * // can actually slam vs refresh an ability * private float rotationSlipTime = 0f;// for when maint abilities fail * * private float gcdCounter; // where we are in the fight * * // maintenance * private float numSunderGCDs = 0f, numThunderGCDs = 0f, numDemoShoutGCDs = 0f, * numBattleShoutGCDs = 0f, numCommandingShoutGCDs = 0f; * * // cooldowns on the GCD * private float numDeathwishGCDs = 0f, numBerserkerRageGCDs = 0f; * * // dps * private float numBloodthirstGCDs = 0f, numWhirlwindGCDs = 0f, numBloodsurgeGCDs = 0f; * * * private void Preprocess(Skills.Ability ability) { Preprocess(ability, 1f); } * /// <summary> * /// Preprocesses the rotation variables for each maintenance ability * /// </summary> * /// <param name="ability">An ability that may be maintained</param> * /// <param name="stacks">The number of stacks applied at the START of the fight (ex: Battle Shout would be 0, Sunder Armor would be 5, Demo Shout would be 1)</param> * private void Preprocess(Skills.Ability ability, float stacks) * { * if (ability.Validated) * { * float slip = 1f / DS.MHAtkTable.AnyLand; // initial application * float reapplyPeriod = (int)(DS.Duration / ROTATION_LENGTH); * * bloodsurge_percUsed -= 1f / reapplyPeriod; // keep it up * rotationSlipTime += (1f - slip);// if I on average have to do * // 1.08 demos to apply it, then * // 8% of my rotations have a 1.5sec slip * numDemoShoutGCDs += stacks * slip; * gcdCounter += stacks * slip; * } * } * * public void new_MakeRotationandDoDPS(bool setCalcs) * { * float LatentGCD = 1.5f + CalcOpts.GetLatency(); * float NumGCDs = CalcOpts.Duration / LatentGCD; * GCDUsage += "NumGCDs: " + NumGCDs.ToString() + "\n\n"; * * // Maintenance abilities * * // First, apply initial debuffs * bloodsurge_percUsed = 1f; // Since we can only bloodsurge once every 8secs, * // this keeps track of how many times we can actually slam vs refresh an ability * * // maintenance * numSunderGCDs = 0f; numThunderGCDs = 0f; numDemoShoutGCDs = 0f; * numBattleShoutGCDs = 0f; numCommandingShoutGCDs = 0f; * // cooldowns on the GCD * numDeathwishGCDs = 0f; numBerserkerRageGCDs = 0f; * // dps * numBloodthirstGCDs = 0f; numWhirlwindGCDs = 0f; numBloodsurgeGCDs = 0f; * * gcdCounter = 0f; // where we are in the fight * * ////////////////////////////////////////////////////////////////////////// * // The following code block does two things in one pass: // * // 1) Determines how long it takes to apply initial debuffs (stored in // * // gcdCounter) // * // 2) Determines how frequently we actually get to bloodsurge based on // * // free GCDs (stored in bloodsurge_percUsed) // * ////////////////////////////////////////////////////////////////////////// * Preprocess(SN, 5f); * Preprocess(TH); * Preprocess(DS); * // Assuming these are already applied at the start of the fight * Preprocess(BTS, 0f); * Preprocess(CS, 0f); * * if (bloodsurge_percUsed < 0f) * bloodsurge_percUsed = Math.Max(bloodsurge_percUsed, 0f); * * } */ #endregion public override void MakeRotationandDoDPS(bool setCalcs, bool needsDisplayCalcs) { base.MakeRotationandDoDPS(setCalcs, needsDisplayCalcs); //new_MakeRotationandDoDPS(setCalcs); // Starting Numbers float DPS_TTL = 0f, HPS_TTL = 0f; float LatentGCD = 1.5f + CalcOpts.Latency; if (_needDisplayCalcs) { GCDUsage += "NumGCDs: " + NumGCDs.ToString() + "\n\n"; } float GCDsused = 0f; float availGCDs = Math.Max(0f, NumGCDs - GCDsused); float availRage = 0f; float rageadd = 0f; //float timelostwhilestunned = 0f; //float percTimeInStun = 0f; if (Char.MainHand == null) { return; } //doIterations(); // ==== Rage Generation Priorities ======== availRage += RageGenOverDur_Other; /*Bloodrage */ //AddAnItem(ref availRage, percTimeInStun, ref _Blood_GCDs, ref HPS_TTL, ref _Blood_HPS, BR); /*Berserker Rage */ /*float Reck_GCDs = Math.Min(availGCDs, RK.Activates); * _Reck_GCDs = Reck_GCDs; * GCDsused += Math.Min(NumGCDs, Reck_GCDs); * GCDUsage += RK.Name + ": " + Reck_GCDs.ToString() + "\n"; * availGCDs = Math.Max(0f, NumGCDs - GCDsused); * rageadd = RK.GetRageUsePerSecond(Reck_GCDs); * availRage -= rageadd; * RageNeeded += rageadd;*/ //doIterations(); #region CalcOpts GCD Losses #region Stuns /*if (CalcOpts.StunningTargets && CalcOpts.Stuns.Count > 0f) * { * float numStuns = FightDuration / CalcOpts.StunningTargetsFreq; * if (!CalcOpts.SE_UseDur) numStuns = (float)Math.Floor(numStuns); * // ways to counter, no GCD, just use reaction time * if (Talents.HeroicFury > 0f) * { * float numUses = Math.Min(numStuns, GetWrapper<HeroicFury>().ability.Activates); * numStuns -= numUses; * timeLost += numUses * (CalcOpts.React / 1000f + CalcOpts.Latency); // not using GetReact because it's not on the GCD * GetWrapper<HeroicFury>().numActivates += numUses; * } * if (Char.Race == CharacterRace.Human) * { * float numUses = Math.Min(numStuns, GetWrapper<EveryManForHimself>().ability.Activates); * numStuns -= numUses; * timeLost += numUses * CalcOpts.React / 1000f + CalcOpts.Latency; // not using GetReact because it's not on the GCD * GetWrapper<EveryManForHimself>().numActivates += numUses; * } * * if (numStuns > 0f) * { * float stunDur = CalcOpts.StunningTargetsDur / 1000f * (1f - Talents.IronWill * 2f / 30f); * timeLost += stunDur * numStuns; * } * }*/ #endregion float otherTimeLost = CalculateTimeLost(null); DoMaintenanceActivates(otherTimeLost); float timeLostPerc = otherTimeLost; #endregion AbilWrapper WW = GetWrapper <WhirlWind>(); AbilWrapper BT = GetWrapper <BloodThirst>(); AbilWrapper BS = GetWrapper <BloodSurge>(); // Priority 1 : Whirlwind on every CD float WW_GCDs = Math.Min(availGCDs, WW.ability.Activates) * (1f - timeLostPerc); WW.numActivates = WW_GCDs; GCDsused += Math.Min(NumGCDs, WW_GCDs); if (_needDisplayCalcs) { GCDUsage += WW.ability.Name + ": " + WW_GCDs.ToString() + "\n"; } availGCDs = Math.Max(0f, NumGCDs - GCDsused); DPS_TTL += WW.DPS; HPS_TTL += WW.HPS; rageadd = WW.allRage; availRage -= rageadd; // Priority 2 : Bloodthirst on every CD float BT_GCDs = Math.Min(availGCDs, BT.ability.Activates) * (1f - timeLostPerc); BT.numActivates = BT_GCDs; GCDsused += Math.Min(NumGCDs, BT_GCDs); if (_needDisplayCalcs) { GCDUsage += BT.ability.Name + ": " + BT_GCDs.ToString() + "\n"; } availGCDs = Math.Max(0f, NumGCDs - GCDsused); DPS_TTL += BT.DPS; HPS_TTL += BT.HPS; rageadd = BT.allRage; availRage -= rageadd; doIterations(); // Priority 3 : Bloodsurge Blood Proc (Do an Instant Slam) if available float BS_GCDs = Math.Min(availGCDs, BS.ability.Activates) * (1f - timeLostPerc); BS.numActivates = BS_GCDs; GCDsused += Math.Min(NumGCDs, BS_GCDs); if (_needDisplayCalcs) { GCDUsage += BS.ability.Name + ": " + BS_GCDs.ToString() + "\n"; } availGCDs = Math.Max(0f, NumGCDs - GCDsused); DPS_TTL += BS.DPS; HPS_TTL += BS.HPS; rageadd = BS.allRage; availRage -= rageadd; InvalidateCache(); //Sword Spec, Doesn't eat GCDs /*float SS_Acts = SS.GetActivates(GetLandedYellowsOverDur()); * _SS_Acts = SS_Acts; * _SS_DPS = SS.GetDPS(SS_Acts); * DPS_TTL += _SS_DPS;*/ // TODO: Add Rage since it's a white hit //doIterations(); // Priority 4 : Heroic Strike, when there is rage to do so, handled by the Heroic Strike class // Alternate to Cleave is MultiTargs is active // After iterating how many Overrides can be done and still do other abilities, then do the white dps availRage += WhiteAtks.MHRageGenOverDur + WhiteAtks.OHRageGenOverDur; bool HSok = CalcOpts.Maintenance[(int)Rawr.DPSWarr.CalculationOptionsDPSWarr.Maintenances.HeroicStrike_]; bool CLok = #if RAWR3 || SILVERLIGHT BossOpts.MultiTargs && BossOpts.Targets != null && BossOpts.Targets.Count > 0 #else CalcOpts.MultipleTargets #endif && CalcOpts.Maintenance[(int)Rawr.DPSWarr.CalculationOptionsDPSWarr.Maintenances.Cleave_]; WhiteAtks.Slam_ActsOverDur = 0f;// _SL_GCDs; DPS_TTL += WhiteAtks.MhDPS * (1f - timeLostPerc) + WhiteAtks.OhDPS * (1f - timeLostPerc); DPS_TTL += GetWrapper <HeroicStrike>().DPS; DPS_TTL += GetWrapper <Cleave>().DPS; calcDeepWounds(); DPS_TTL += DW.DPS; if (_needDisplayCalcs) { GCDUsage += "\nAvail: " + availGCDs.ToString(); } // Return result _HPS_TTL = HPS_TTL; if (setCalcs) { this.calcs.TotalDPS = DPS_TTL; this.calcs.WhiteDPS = (WhiteAtks.MhDPS + WhiteAtks.OhDPS) * (1f - timeLostPerc); this.calcs.WhiteRage = WhiteAtks.MHRageGenOverDur + WhiteAtks.OHRageGenOverDur; this.calcs.OtherRage = RageGenOverDur_Other; this.calcs.NeedyRage = RageNeededOverDur; this.calcs.FreeRage = calcs.WhiteRage + calcs.OtherRage - calcs.NeedyRage; this.calcs.WhiteDPSMH = this.WhiteAtks.MhDPS; this.calcs.WhiteDPSOH = this.WhiteAtks.OhDPS; this.calcs.WhiteDmg = this.WhiteAtks.MhDamageOnUse + this.WhiteAtks.OhDamageOnUse; } }