public void calculateFrequencySums() { specialShotsPerSecond = 0f; critSpecialShotsPerSecond = 0f; critsRatioSum = 0f; // 31-10-2009 Drizz: Updated to follow 92b // I still don't understand the method of how critSpecialShotsPerSecond is calculated. for (int i = 0; i < priorities.Length; i++) { if (priorities[i] == null) { continue; } ShotData s = priorities[i]; if (s.final_freq > 0) { specialShotsPerSecond += 1f / s.final_freq; if (s.Type == Shots.SerpentSting) { critSpecialShotsPerSecond += 1f / (s.final_freq / s.rotation_cooldown * 3f); } else { critSpecialShotsPerSecond += 1f / (s.final_freq); } } critsRatioSum += s.CritsRatio; } }
public RotationShotInfo(ShotData shot) { this.type = shot.Type; this.cooldown = shot.Cd; this.castTime = shot.Cd; this.check_gcd = shot.TriggersGCD; this.no_gcd = !shot.TriggersGCD && shot.Type != Shots.Readiness; this.damage = shot.Damage; if (shot.Type == Shots.SerpentSting) { this.cooldown = shot.Duration; } }
public void calculateRotationMPS() { MPS = 0; for (int i = 0; i < priorities.Length; i++) { if (priorities[i] != null) { ShotData s = priorities[i]; s.calculateMPS(this); MPS += s.MPS; } } }
public void calculateCrits() { // called after we find out shot crit rates. // we calculated frequencies and ratios in earlier calls. critsCompositeSum = 0f; for (int i = 0; i < priorities.Length; i++) { if (priorities[i] == null) { continue; } ShotData s = priorities[i]; s.calculateComposites(this); critsCompositeSum += s.CritsComposite; } }
public void calculateFrequencies() { // This function calculates the frequencies for each shot. // We have already calculated all shot cooldowns and validated the rotation by this point. // THIS FUNCTION IS CALLED THREE TIMES - TRY TO REMEMBER THIS! // 1) When we have mana and basic timings // 2) When we have LAL proc info // 3) When we have steady shot speed ShotData PrevShot = null; for (int i = 0; i < priorities.Length; i++) { if (priorities[i] == null) { continue; } ShotData s = priorities[i]; s.calculateTimings(this, PrevShot); PrevShot = s; } }
public void calculateRotationDPS() { bool debug_shot_rotation = false; DPS = 0; for (int i = 0; i < priorities.Length; i++) { if (priorities[i] != null) { ShotData s = priorities[i]; s.calculateDPS(this); #region Spreadsheet Calculations // the hidden shot calculations! // G (RotationCooldown) // H (Mana) // I (Damage) // J (TimeUsed) // K (Ratio) =IF(OR(AND(ChimeraRefreshesViper,E30="Viper Sting"),AND(ChimeraRefreshesSerpent,E30="Serpent Sting")),"Refreshed",AB30) // N (MPS) =IF(AND(NOT(UseRotationTest),OR(E30="Explosive Shot",E30="Arcane Shot)),V30,IF(L30="Refreshed","Refreshed",AD30)) // O (StartFreq) =IF(AJ30=TRUE,0,IF(G30>J30,ROUNDUP((G30-J30)/(GCD+CastLag),0)*(GCD+CastLag)+J30,GCD+CastLag))*IF(VLOOKUP(E30,ShotsTable,8,FALSE)<>FALSE,1,0) // P (GCDLeft) // Q (GCDNeeded) =IF(AND(NOT(UseKillShot,E30="Kill Shot"),0,IF(OR(AND(ChimeraRefreshesViper,E30="Viper Sting"),AND(ChimeraRefreshesSerpent,E30="Serpent Sting")),0,IF(O30>0,IF(J30>O30,1,J30/O30),0))) // R (GCDUsed) // S (InBetFreq) =IF(AND(O30>0,R30>0),J30/R30,0) // T (InBetStings) // W (LALFreq) =IF(OR(O30=0,T30=2),0,IF(E30="Explosive Shot",IF(LALExplosiveFrequency>0,LALExplosiveFrequency,S30),IF(E30="Arcane Shot",IF(LALArcaneFrequency>0,LALArcaneFrequency,S30),S30))) // X (LALGCDLeft) // Y (LALGCDNeeded) =IF(AND(NOT(UseKillShot),E30="Kill Shot"),0,IF(AND(ChimeraRefreshesSerpent,E30="Serpent Sting"),0,IF(W30>0,IF(J30>W30,1,J30/W30),0))) // Z (LALGCDUsed) // AA (ActualFreq) =IF(UseRotationTest,'Rotation Test'!D11,IF(AND(W30>0,Z30>0),J3/Z30,0)) // AB (ActualRatio) =IF(AND(NOT(UseKillShot),E30="Kill Shot"),0,IF(AND(ChimeraRefereshesSerpent,E30="Serpent Sting"),0,IF(AA30>0,IF(J30>AA30,1,J30/AA30),0))) // AC (ActualDPS) =IF(OR(AND(ChimeraRefreshesViper,E30="Viper Sting"),AND(ChimeraRefreshesSerpent,E30="Serpent Sting")),I30/G30,IF(AA30>0,I30/AA30,0)) // AD (ActualMPS) =IF(OR(AND(ChimeraRefreshesViper,E30="Viper Sting"),AND(ChimeraRefreshesSerpent,E30="Serpent Sting")),0,IF(AA30>0,H30/AA30,0)) // AJ (InvalidShotPriority) =IF(VLOOKUP(E30,ShotPriorityList,2,FALSE)<>F30,TRUE,OR(IF(AND(E30="Arcane Shot",ExplosiveInRotation),IF(VLOOKUP("Explosive Shot",ShotPriorityList,2,FALSE)<F30,TRUE,FALSE),FALSE,IF(AND( #endregion DPS += s.DPS; if (debug_shot_rotation) { string col1 = String.Format("{0,6:0.00}", s.rotation_cooldown); string col2 = String.Format("{0,3:0}", s.ManaCost); string col3 = String.Format("{0,4:0}", s.Damage); string col4 = String.Format("{0:0.00}", s.time_used); string col5 = String.Format("{0,6:0.00}%", 100 * s.ratio); string col6 = String.Format("{0,6:0.00}", s.Freq); string col7 = String.Format("{0,6:0.00}", s.DPS); string col8 = String.Format("{0,6:0.00}", s.MPS); Debug.WriteLine("Shot: |" + col1 + "|" + col2 + "|" + col3 + "|" + col4 + "|" + col5 + "|" + col6 + "|" + col7 + "|" + col8 + "|"); } } else { if (debug_shot_rotation) { Debug.WriteLine("None: |------|---|----|----|-------|------| 0.00| 0.00|"); } } } if (debug_shot_rotation) { string debug_dps = String.Format("{0,6:0.00}", DPS); string debug_mps = String.Format("{0,6:0.00}", MPS); Debug.WriteLine("TOTAL: |" + debug_dps + "|" + debug_mps + "|"); } }
public void calculateLALProcs(Character character) { float lal_trigger_freq = 0f; float lal_trigger_duration = 0f; ShotData proc_shot = null; if (proc_shot == null) { proc_shot = getShotInRotation(Shots.BlackArrow); } if (proc_shot == null) { proc_shot = getShotInRotation(Shots.ImmolationTrap); } if (proc_shot == null) { proc_shot = getShotInRotation(Shots.ExplosiveTrap); } if (proc_shot == null) { proc_shot = getShotInRotation(Shots.FreezingTrap); } if (proc_shot == null) { proc_shot = getShotInRotation(Shots.FrostTrap); } if (proc_shot == null) { proc_shot = getShotInRotation(Shots.Volley); } if (proc_shot != null) { lal_trigger_freq = proc_shot.lal_freq; lal_trigger_duration = proc_shot.Duration; } float lal_proc_chance = 0.02f * character.HunterTalents.LockAndLoad; //lal_proc_chance = 0.06; //isolation testing for LAL float lal_proc_freq = 0f; if (CalcOpts.UseRotationTest) { // the spreadsheet gets the LAL proc frequency from the rotation test, // but it doesn't really matter because it's only used to calculate some // stats we'll throw away (final_freq via lal_freq). } else { if (lal_trigger_freq > 0 && lal_proc_chance > 0) { lal_proc_freq = (lal_trigger_duration > lal_trigger_freq ? 1f : lal_trigger_freq / lal_trigger_duration) * 3f / lal_proc_chance; } } ShotData lalExplosive = getShotInRotation(Shots.ExplosiveShot); ShotData lalArcane = getShotInRotation(Shots.ArcaneShot); float pre_lal_explosive_freq = (lalExplosive != null && CalcOpts.LALShotToUse == Shots.ExplosiveShot) ? lalExplosive.inbet_freq : 0; float pre_lal_arcane_freq = (lalArcane != null && CalcOpts.LALShotToUse == Shots.ArcaneShot) ? lalArcane.inbet_freq : 0; LALExplosiveFrequency = 0; if (lal_proc_freq > 0 && pre_lal_explosive_freq > 0) { LALExplosiveFrequency = 1f / (1f / (lal_proc_freq / (pre_lal_explosive_freq / (pre_lal_arcane_freq + pre_lal_explosive_freq) * CalcOpts.LALShotsReplaced)) + 1f / pre_lal_explosive_freq); } LALArcaneFrequency = 0; if (lal_proc_freq > 0 && pre_lal_arcane_freq > 0) { LALArcaneFrequency = 1f / (1f / (lal_proc_freq / (pre_lal_arcane_freq / (pre_lal_explosive_freq + pre_lal_arcane_freq) * CalcOpts.LALShotsReplaced)) + 1f / pre_lal_arcane_freq); } }
public void calculateTimings(ShotPriority Priority, ShotData PrevShot) { float CastLag = Priority.CalcOpts.Latency; #region Starting Calculations start_freq = (rotation_cooldown > time_used) ? (float)Math.Ceiling((rotation_cooldown - time_used) / (GCD + CastLag)) * (GCD + CastLag) + time_used : GCD + CastLag; start_gcd_needed = (start_freq > 0) ? (time_used > start_freq ? 1f : time_used / start_freq) : 0f; if (Priority.chimeraRefreshesSerpent && Type == Shots.SerpentSting) { start_gcd_needed = 0f; } if (!Priority.useKillShot && Type == Shots.KillShot) { start_gcd_needed = 0f; } start_gcd_left = 1f; if (PrevShot != null) { start_gcd_left = PrevShot.start_gcd_left - PrevShot.start_gcd_used; } start_gcd_used = 1f - (start_gcd_left - start_gcd_needed); if (PrevShot != null) { start_gcd_used = start_gcd_left > start_gcd_needed ? start_gcd_needed : start_gcd_left; } //Debug.WriteLine("Start Freq is " + start_freq); //Debug.WriteLine("GCD Left is " + start_gcd_left); //Debug.WriteLine("GCD Needed is " + start_gcd_needed); //Debug.WriteLine("GCD Used is " + start_gcd_used); #endregion #region In-Between Calculations inbet_freq = (start_freq > 0 && start_gcd_used > 0) ? time_used / start_gcd_used : 0; if (PrevShot != null) { if (Type == Shots.SerpentSting) { sting_count = PrevShot.sting_count < 2 ? PrevShot.sting_count + 1 : PrevShot.sting_count; } else { sting_count = PrevShot.sting_count == 2 ? 3 : PrevShot.sting_count; } } else { sting_count = (Type == Shots.SerpentSting) ? 1 : 0; } //Debug.WriteLine("Pre-LAL freq = "+inbet_freq); //Debug.WriteLine("Pre-LAL sting count = " + sting_count); #endregion #region Lock-and-Load Calculations lal_freq = inbet_freq; if (Type == Shots.ExplosiveShot && Priority.LALExplosiveFrequency > 0f) { lal_freq = Priority.LALExplosiveFrequency; } if (Type == Shots.ArcaneShot && Priority.LALArcaneFrequency > 0f) { lal_freq = Priority.LALArcaneFrequency; } if (start_freq == 0f || sting_count == 2f) { lal_freq = 0f; } lal_gcd_left = 1; if (PrevShot != null) { lal_gcd_left = PrevShot.lal_gcd_left - PrevShot.lal_gcd_used; } lal_gcd_needed = lal_freq > 0f ? (time_used > lal_freq ? 1f : time_used / lal_freq) : 0f; if (!Priority.useKillShot && Type == Shots.KillShot) { lal_gcd_needed = 0f; } if (Priority.chimeraRefreshesSerpent && Type == Shots.SerpentSting) { lal_gcd_needed = 0f; } lal_gcd_used = 1f - (lal_gcd_left - lal_gcd_needed); if (PrevShot != null) { lal_gcd_used = lal_gcd_left > lal_gcd_needed ? lal_gcd_needed : lal_gcd_left; } #endregion // RotationTest may come in later and override this value, // hence the split-function that we call here final_freq = (lal_freq > 0 && lal_gcd_used > 0) ? time_used / lal_gcd_used : 0; finishCalculateTimings(Priority); }
public void calculateTimings(ShotPriority Priority, ShotData PrevShot) { float CastLag = Priority.CalcOpts.Latency; #region Starting Calculations start_freq = (rotation_cooldown > time_used) ? (float)Math.Ceiling((rotation_cooldown - time_used) / (GCD + CastLag)) * (GCD + CastLag) + time_used : GCD + CastLag; start_gcd_needed = (start_freq > 0) ? (time_used > start_freq ? 1f : time_used / start_freq) : 0f; if (Priority.chimeraRefreshesSerpent && Type == Shots.SerpentSting) start_gcd_needed = 0f; if (!Priority.useKillShot && Type == Shots.KillShot) start_gcd_needed = 0f; start_gcd_left = 1f; if (PrevShot != null) { start_gcd_left = PrevShot.start_gcd_left - PrevShot.start_gcd_used; } start_gcd_used = 1f - (start_gcd_left - start_gcd_needed); if (PrevShot != null) { start_gcd_used = start_gcd_left > start_gcd_needed ? start_gcd_needed : start_gcd_left; } //Debug.WriteLine("Start Freq is " + start_freq); //Debug.WriteLine("GCD Left is " + start_gcd_left); //Debug.WriteLine("GCD Needed is " + start_gcd_needed); //Debug.WriteLine("GCD Used is " + start_gcd_used); #endregion #region In-Between Calculations inbet_freq = (start_freq > 0 && start_gcd_used > 0) ? time_used / start_gcd_used : 0; if (PrevShot != null) { if (Type == Shots.SerpentSting) { sting_count = PrevShot.sting_count < 2 ? PrevShot.sting_count + 1 : PrevShot.sting_count; } else { sting_count = PrevShot.sting_count == 2 ? 3 : PrevShot.sting_count; } } else { sting_count = (Type == Shots.SerpentSting) ? 1 : 0; } //Debug.WriteLine("Pre-LAL freq = "+inbet_freq); //Debug.WriteLine("Pre-LAL sting count = " + sting_count); #endregion #region Lock-and-Load Calculations lal_freq = inbet_freq; if (Type == Shots.ExplosiveShot && Priority.LALExplosiveFrequency > 0f) lal_freq = Priority.LALExplosiveFrequency; if (Type == Shots.ArcaneShot && Priority.LALArcaneFrequency > 0f) lal_freq = Priority.LALArcaneFrequency; if (start_freq == 0f || sting_count == 2f) lal_freq = 0f; lal_gcd_left = 1; if (PrevShot != null) { lal_gcd_left = PrevShot.lal_gcd_left - PrevShot.lal_gcd_used; } lal_gcd_needed = lal_freq > 0f ? (time_used > lal_freq ? 1f : time_used / lal_freq) : 0f; if (!Priority.useKillShot && Type == Shots.KillShot) lal_gcd_needed = 0f; if (Priority.chimeraRefreshesSerpent && Type == Shots.SerpentSting) lal_gcd_needed = 0f; lal_gcd_used = 1f - (lal_gcd_left - lal_gcd_needed); if (PrevShot != null) { lal_gcd_used = lal_gcd_left > lal_gcd_needed ? lal_gcd_needed : lal_gcd_left; } #endregion // RotationTest may come in later and override this value, // hence the split-function that we call here final_freq = (lal_freq > 0 && lal_gcd_used > 0) ? time_used / lal_gcd_used : 0; finishCalculateTimings(Priority); }
public void RunTest() { float FightDuration = BossOpts.BerserkTimer; float Latency = CalcOpts.Latency; float CDCutoff = CalcOpts.CDCutoff; float Longevity = character.HunterTalents.Longevity; float GCD = 1.5f; float BA = calculatedStats.blackArrow.Duration; float it = calculatedStats.immolationTrap.Duration; float et = calculatedStats.explosiveTrap.Duration; float ft = calculatedStats.freezingTrap.Duration; float frt = calculatedStats.frostTrap.Duration; //float vly = calculatedStats.volley.Duration; float LALChance = character.HunterTalents.BlackArrow == 1 ? character.HunterTalents.LockAndLoad * CalcOpts.LALProcChance : -1; bool RandomProcs = CalcOpts.RandomizeProcs; int ISSfix = 0; int IAotHfix = 0; int LALfix = 0; float AutoShotSpeed = 2; // Speed at which we shoot auto-shots //float IAotHEffect = 1 + calculatedStats.quickShotsEffect; // haste increase during IAoTH float IAotHChance = -1; float WaitForESCS = CalcOpts.waitForCooldown; bool InterleaveLAL = CalcOpts.interleaveLAL; bool ArcAimedPrio = CalcOpts.prioritiseArcAimedOverSteady; float ISSTalent = character.HunterTalents.ImprovedSteadyShot * 5; float ISSDuration = -1; int ISSprocsAimed = 0; int ISSProcsArcane = 0; int ISSProcsChimera = 0; float BossHPPercentage = CalcOpts.BossHPPerc * 100; float Sub20Time = (BossHPPercentage > 20) ? FightDuration - (float)BossOpts.Under20Perc * FightDuration : 0; // time *until* we hit sub-20 bool UseKillShot = calculatedStats.priorityRotation.containsShot(Shots.KillShot); bool BMSpec = character.HunterTalents.BestialWrath + character.HunterTalents.TheBeastWithin > 0; if (CalcOpts.PetFamily == PETFAMILY.None) { BMSpec = false; } int currentShot; float currentTime; //int selectedShot; float ElapsedTime; float BWTime; float RFTime; //float VolleyTime; float BWCD; float RFCD; //float VLYCD; float LALTimer; // timer till current serpent sting expires float LALShots; // amount of free shots left float LALDuration; // time since last Explosive Shot while having L&L up float LastLALCheck; // time since last check for proc float LastLALProc; // time since last proc int LALProcs; // Amount of procs Shots LALType; // Type of L&L proc, i.e. Immolation trap or Black Arrow float IAotHDuration; // Time until buff expires float LastAutoShotCheck; // Last time we checked for IAotH procs int IAotHProcs; bool SerpentUsed; // Have we used Serpent Sting yet? #region Initialize shotData & shotTable shotData = new Dictionary <Shots, RotationShotInfo>(); // the shot info (focus, cooldown, etc) shotTable = new RotationShotInfo[10]; // the rotation info for (int i = 0; i < calculatedStats.priorityRotation.priorities.Length; i++) { ShotData shot = calculatedStats.priorityRotation.priorities[i]; if (shot != null && shot.Type != Shots.None) { shotData[shot.Type] = new RotationShotInfo(shot); shotTable[i] = shotData[shot.Type]; } else { shotTable[i] = null; } } #endregion #region Shot Timing Adjustments // we need to adjust rapid fire cooldown, since we accounted for readiness initially if (shotData.ContainsKey(Shots.RapidFire)) { shotData[Shots.RapidFire].cooldown = (5 - character.HunterTalents.RapidKilling) * 60f; } // set Steady Shot cast time so it's only static haste if (shotData.ContainsKey(Shots.SteadyShot)) { //shotData[Shots.SteadyShot].castTime = 2f / (1f + calculatedStats.hasteStaticTotal); } // Set Auto Shot Speed to statically hasted value AutoShotSpeed = 0f;//calculatedStats.autoShotStaticSpeed; #endregion #region Variable setup // these are used by both the frequency rotation and here in the rotation test bool chimeraRefreshesSerpent = containsShot(Shots.ChimeraShot) && containsShot(Shots.SerpentSting); currentShot = 1; currentTime = 0; ElapsedTime = 0; //Lock And Load variables LALTimer = -1; LALShots = -1; LALDuration = -1; LastLALCheck = -1; LastLALProc = -50; LALType = Shots.None; //IAotH variables // 021109 Drizz: Updating init variables to align with v92b spreadsheet LastAutoShotCheck = 0; // -50; IAotHDuration = 0; // -1; IAotHProcs = 0; SerpentUsed = false; // some things not initialized in the spreadsheet BWTime = 0; //VolleyTime = 0; RFTime = 0; BWCD = 0; RFCD = 0; //VLYCD = 0; currentTime = 0; LALProcs = 0; float IAotHUptime = 0; #endregion #region The Main Loop do { bool haveShot = false; Shots thisShot = Shots.None; #region Find shot - Non-GCD // Check shots that are off the GCD for (int i = 0; i < shotTable.Length && !haveShot; i++) { RotationShotInfo s = shotTable[i]; if (s != null) { if (s.type == Shots.BestialWrath && BWTime > currentTime) { // do nothing if Rapid Fire is still active } else if (s.type == Shots.RapidFire && RFTime > currentTime) { // Check if TBW/BW CD is nearly up if we want to use Rapid Fire } else if (BMSpec && s.type == Shots.Readiness && BWCD < (currentTime + CDCutoff)) { // Wait for Rapid Fire if it's close too } else if (s.type == Shots.Readiness && RFCD < (currentTime + CDCutoff)) { // wait for Rapid Fire } else if (s.type == Shots.BestialWrath && !BMSpec) { // do nothing //} else if (s.type == Shots.Volley && VLYCD < (currentTime + CDCutoff)) { // do nothing, we're channelling } else { // check if the shot is off the GCD if (s.no_gcd && s.time_until_off_cd <= currentTime) { thisShot = s.type; haveShot = true; } } } } #endregion #region Find Shot - Any // Check all shots whether they are off CD for (int i = 0; i < shotTable.Length && !haveShot; i++) { RotationShotInfo s = shotTable[i]; // known as thisShot if (s != null) { // During L&L proc Explosive Shot gets priority over everything else // Do nothing if L&L just proc'ed and ES is still on CD if (s.time_until_off_cd > currentTime && LALShots == 3 && s.type == Shots.ExplosiveShot) { } // First L&L proc shot else if (s.time_until_off_cd < currentTime && LALShots == 3 && s.type == Shots.ExplosiveShot) { s.time_until_off_cd = 0; LALShots--; haveShot = true; thisShot = s.type; // Cells(row + 1, col + 5).value = Cells(row + 1, col + 5).value + "L&L #1" } // If We're interleaving, make sure the last ES was at least 2 seconds before this else if (InterleaveLAL && LALShots < 3 && LALShots > 0 && LALDuration - currentTime > -2 && LALDuration - currentTime <= 0 && s.type == Shots.ExplosiveShot) { currentTime += 0.5f; s.time_until_off_cd = 0; haveShot = true; thisShot = s.type; if (LALShots == 2) { // Cells(row + 1, col + 5).value = Cells(row + 1, col + 5).value + "L&L #2" } else if (LALShots == 1) { // Cells(row + 1, col + 5).value = Cells(row + 1, col + 5).value + "L&L #3" } LALShots--; } // If we're not interleaving other shots with L&L procs reset ES cooldown and advance timer by 0.5 seconds (to miss the last tick) else if (!InterleaveLAL && LALShots < 3 && LALShots > 0 && s.type == Shots.ExplosiveShot) { // ****************************************************************************** // 29-10-2009 - Drizz: Commenting out this line to align with Spreadsheet (v92b) // currentTime += 0.5; // ****************************************************************************** s.time_until_off_cd = 0; haveShot = true; thisShot = s.type; if (LALShots == 2) { // Cells(row + 1, col + 5).value = Cells(row + 1, col + 5).value + "L&L #2" } else if (LALShots == 1) { // Cells(row + 1, col + 5).value = Cells(row + 1, col + 5).value + "L&L #3" } LALShots--; } float waitTime = (float)Math.Round(s.time_until_off_cd - currentTime, 2); // Check if shots with CDs come off CD soon enough to wait for them, excluding stings if (!haveShot && waitTime > 0 && s.time_until_off_cd > currentTime && s.time_until_off_cd <= currentTime + WaitForESCS && s.type != Shots.SerpentSting && s.type != Shots.BlackArrow) { if (!ArcAimedPrio && (thisShot == Shots.AimedShot || thisShot == Shots.ArcaneShot || thisShot == Shots.MultiShot)) { // do nothing if we don't want to prioritise Aimed/Arcane or Multi-Shot } // ************************************************************************ //29-10-2009 Drizz : Added one else if check to align with Spreadsheet v92b else if (s.type == Shots.Readiness && RFCD < (currentTime + CDCutoff + waitTime)) { // Do nothing if we Rapid Fire isn't soon enough of CD } // ************************************************************************ else if (s.check_gcd) { bool result = true; // Check if other shots that are about to come off CD do more damage than this one for (int j = i; j < shotTable.Length && result; j++) { RotationShotInfo thatShot = shotTable[j]; if (thatShot != null) { // 021109 Drizz: Removed the last && on this line (align with Worksheet 92b. if (thatShot.time_until_off_cd > currentTime && thatShot.time_until_off_cd <= currentTime + WaitForESCS) // && checkGCD(thisShot)) { if (compareDamage(thisShot, thatShot.type, waitTime) != thisShot) { result = false; // Cells(row + 1, col + 5).value = Cells(row + 1, col + 5).value + "(Skipped " & thisShot & ") " //exit for loop } } } } if (result) { currentTime = s.time_until_off_cd; thisShot = s.type; haveShot = true; // Cells(row + 1, col + 5).value = Cells(row + 1, col + 5).value + "(Waited " & waitTime & "s) " } } } // this is a horrible round hack, i know. the issue probably comes // somewhere from a float->float cast. // the issue is that we have currentTime at (e.g.) 65.35 and steady shot CD is at 65.3500000001 // so we skip forward by 0.1 seconds when we didn;t really need to. if (!haveShot && s.time_until_off_cd <= currentTime + 0.00001) { if (s.type == Shots.SerpentSting && chimeraRefreshesSerpent && SerpentUsed) { // do nothing for refreshed Serpent Sting except the first time } else if (s.type == Shots.KillShot && (!UseKillShot || currentTime < Sub20Time)) { // Do not use Kill Shot if Boss HP is above 20% } else if (s.type == Shots.BestialWrath && BWTime > currentTime) { // do nothing if TBW or BW is still active } else if (s.type == Shots.RapidFire && RFTime > currentTime) { // do nothing if Rapid Fire is still active } else if (BMSpec && s.type == Shots.Readiness && BWCD < (currentTime + CDCutoff)) { // Check if TBW/BW CD is nearly up if we want to use Rapid Fire } else if (s.type == Shots.Readiness && RFCD < (currentTime + CDCutoff)) { // Wait for Rapid Fire if it's close too } else if (s.type == Shots.SerpentSting && FightDuration - currentTime < s.cooldown) { // do nothing if Rapid Fire is about to come off CD // do nothing if we don't get all the ticks of Serpent Sting anyway } else if (s.type == Shots.BlackArrow && FightDuration - currentTime < BA) { // ditto for Black Arrow } else if (s.type == Shots.BestialWrath && !BMSpec) { // do nothing } else { thisShot = s.type; haveShot = true; } } } } #endregion #region Execute shot if (!haveShot) { // If no shot, advance 100 ms // This case should almost never happen unless Steady Shot isn't in the rotation currentTime += 0.1f; //Debug.WriteLine("no shot found - advancing time to "+currentTime); } else { float currentAutoShotSpeed; currentAutoShotSpeed = AutoShotSpeed; RotationShotInfo thisShotInfo = shotData[thisShot]; if (thisShotInfo.type == Shots.SerpentSting && !SerpentUsed) { SerpentUsed = true; } // Adjust AutoShot Speed if Rapid Fire is active if (RFTime > currentTime) { currentAutoShotSpeed *= 0.6f; } // Adjust AutoShotSpeed if IAotH is active /*if (IAotHDuration - currentTime > 0) * { * currentAutoShotSpeed *= (1f / IAotHEffect); * }*/ // Check if we had an auto shot since the last check if (currentTime - LastAutoShotCheck > currentAutoShotSpeed) { // ******************************************************* // 021109 Drizz: Added if around LastAutoShotCheck, Updated from v92b (spreadsheet) if (currentTime == 0) { LastAutoShotCheck = currentTime; } else { LastAutoShotCheck += currentAutoShotSpeed; } // ******************************************************* // 021109 Drizz: Updating to use "LastAutoShotCheck" instead of currentTime (Align with v92b) if (RandomProcs) { // 10% proc chance if (randomProc(IAotHChance)) { if (IAotHDuration - LastAutoShotCheck < 0) { // No IAotH proc active IAotHUptime = IAotHUptime + 12; } else { IAotHUptime = IAotHUptime - (IAotHDuration - LastAutoShotCheck) + 12; } IAotHDuration = LastAutoShotCheck + 12; IAotHProcs = IAotHProcs + 1; } } else if (IAotHChance > 0) { IAotHfix = IAotHfix + 1; if (IAotHfix * IAotHChance > 100) { IAotHfix = 0; // reset counter if (IAotHDuration - LastAutoShotCheck < 0) { // No IAotH proc active IAotHUptime = IAotHUptime + 12; } else { IAotHUptime = IAotHUptime - (IAotHDuration - LastAutoShotCheck) + 12; } IAotHDuration = LastAutoShotCheck + 12; IAotHProcs = IAotHProcs + 1; } } } // count shots currentShot++; // advance row on the test page //row++ thisShotInfo.countUsed++; // Determine Steady Shot haste float SShaste = 1f; if (RFTime > currentTime) { SShaste *= 1.4f; } //if (IAotHDuration > currentTime) SShaste *= IAotHEffect; // Increment cooldown on the shot just fired if (thisShot == Shots.SteadyShot && thisShotInfo.castTime * (1f / SShaste) < GCD + Latency) { thisShotInfo.time_until_off_cd = currentTime + GCD + Latency; } else if (thisShot == Shots.SteadyShot) { thisShotInfo.time_until_off_cd = currentTime + thisShotInfo.castTime * (1f / SShaste) + Latency; } else if (checkGCD(thisShot)) { thisShotInfo.time_until_off_cd = currentTime + thisShotInfo.cooldown + Latency; } else { // Shots that are off the GCD do not incur latency thisShotInfo.time_until_off_cd = currentTime + thisShotInfo.cooldown; } // Note down shot current time and shot used // 091109 Drizz: If we now can get the debug info within Rawr, no need to go by debug option. /*if (calculatedStats.collectSequence) * { * float timeUsed = 0; * float castEnd = currentTime; * float onCDUntil = currentTime + thisShotInfo.cooldown; * * // Note down cast time used, cast end time and time till CD * if (!checkGCD(thisShot)) * { * // These do not cost time * // (we set these values just above) * } * else * { * // Steady Shot can fire faster than GCD so check * if (thisShot == Shots.SteadyShot) * { * timeUsed = thisShotInfo.castTime * (1 / SShaste) + Latency; * castEnd = currentTime + thisShotInfo.castTime * (1 / SShaste) + Latency; * } * //else if (thisShot == Shots.Volley) * //{ * // timeUsed = thisShotInfo.castTime + Latency; * // castEnd = currentTime + thisShotInfo.castTime + Latency; * //} * else * { * // Other shots fire at GCD + Latency * timeUsed = GCD + Latency; * castEnd = currentTime + GCD + Latency; * } * onCDUntil = thisShotInfo.time_until_off_cd; * } * // 091109 Drizz: Removing the Debug.Writeline * //Debug.WriteLine(" "+ currentTime + ": " + thisShot + " (" +timeUsed+"/"+castEnd+"/"+onCDUntil + ")"); * calculatedStats.sequence += String.Format("{0,6:0.00}:{1,-13}" + " : {2,7:0.000}:{3,7:0.000}:{4,7:0.000}", currentTime, thisShot, timeUsed, castEnd, onCDUntil) + Environment.NewLine; #if !RAWR3 && !RAWR4 && !SILVERLIGHT * Debug.Flush(); #endif * } */ // Set L&L Timer after Black Arrow/Immolation Trap was used if (thisShot == Shots.BlackArrow || thisShot == Shots.ImmolationTrap || thisShot == Shots.ExplosiveTrap || thisShot == Shots.FreezingTrap || thisShot == Shots.IceTrap) { switch (thisShot) { case Shots.BlackArrow: { LALTimer = currentTime + BA; break; } case Shots.ImmolationTrap: { LALTimer = currentTime + it; break; } case Shots.ExplosiveTrap: { LALTimer = currentTime + et; break; } case Shots.FreezingTrap: { LALTimer = currentTime + ft; break; } default: { LALTimer = currentTime + frt; break; } // Shots.IceTrap } LALType = thisShot; LastLALCheck = currentTime - 3; } if (currentTime + thisShotInfo.cooldown > ElapsedTime) { if (thisShotInfo.cooldown < GCD + Latency) { ElapsedTime = currentTime + GCD + Latency; } else { ElapsedTime = currentTime + thisShotInfo.cooldown; } } // If we have Improved Steady Shot and have just cast Steady Shot if (ISSTalent > 0 && thisShot == Shots.SteadyShot) { if (RandomProcs) { if (randomProc(ISSTalent)) { ISSDuration = currentTime + 12; } } else if (ISSTalent > 0) { ISSfix++; if (ISSfix * ISSTalent > 100) { ISSfix = 0; ISSDuration = currentTime + 12; } } } if (ISSDuration > currentTime) { if (thisShot == Shots.AimedShot) { ISSprocsAimed++; ISSDuration = -1; //Cells(row, col + 5).value = Cells(row, col + 5).value + "(ISS proc)" } else if (thisShot == Shots.ArcaneShot) { ISSProcsArcane++; ISSDuration = -1; //Cells(row, col + 5).value = Cells(row, col + 5).value + "(ISS proc)" } else if (thisShot == Shots.ChimeraShot) { ISSProcsChimera++; ISSDuration = -1; //Cells(row, col + 5).value = Cells(row, col + 5).value + "(ISS proc)" } } // If we used Explosive Shot, set the duration of the debuff if (thisShot == Shots.ExplosiveShot && LALShots > 0) { LALDuration = currentTime + 2; } // If we used Explosive Shot, set Arcane Shot on CD as well and likewise if (thisShot == Shots.ArcaneShot && shotData.ContainsKey(Shots.ExplosiveShot)) { shotData[Shots.ExplosiveShot].time_until_off_cd = currentTime + shotData[Shots.ExplosiveShot].cooldown + Latency; } if (thisShot == Shots.ExplosiveShot && shotData.ContainsKey(Shots.ArcaneShot)) { shotData[Shots.ArcaneShot].time_until_off_cd = currentTime + shotData[Shots.ArcaneShot].cooldown + Latency; } // If we used Multi-Shot set Aimed Shot cooldown as well and vice-versa if (thisShot == Shots.MultiShot && shotData.ContainsKey(Shots.AimedShot)) { shotData[Shots.AimedShot].time_until_off_cd = currentTime + shotData[Shots.AimedShot].cooldown + Latency; } if (thisShot == Shots.AimedShot && shotData.ContainsKey(Shots.MultiShot)) { shotData[Shots.MultiShot].time_until_off_cd = currentTime + shotData[Shots.MultiShot].cooldown + Latency; } // If Black Arrow is active (LALTimer) do check a proc every 3 seconds and if no LAL proc has occured within the last 22 seconds if (LALTimer > currentTime && currentTime - LastLALCheck > 3 && LALDuration < currentTime && currentTime - LastLALProc > 22) { if (RandomProcs) { // And currentTime - LastLALProc > 30 Then --- currently no ICD if (randomProc(LALChance)) { //L&L procs LALShots = 3; LastLALProc = currentTime; LALProcs++; //Cells(row, col + 5).value = Cells(row, col + 5).value + "(Proc L&L after this shot)" } } else if (LALChance > 0) { LALfix++; if (LALfix * LALChance > 100) { //L&L procs LALShots = 3; LastLALProc = currentTime; LALProcs++; //Cells(row, col + 5).value = /Cells(row, col + 5).value + "(Proc L&L after this shot)" LALfix = 0; } } LastLALCheck = currentTime; } // If we used Beast Within, set duration to 18 seconds if (thisShot == Shots.BestialWrath) { BWTime = currentTime + 10; // also record the time when it comes off CD BWCD = thisShotInfo.time_until_off_cd; } // If we used Volley, set duration to 6 seconds //if (thisShot == Shots.Volley) //{ // VolleyTime = currentTime + thisShotInfo.castTime; // // also record the time when it comes off CD // VLYCD = thisShotInfo.time_until_off_cd; //} if (thisShot == Shots.RapidFire) { RFTime = currentTime + 15; // also record the time when it comes off CD RFCD = thisShotInfo.time_until_off_cd; } // If Readiness is used, reset CD on other shots if (thisShot == Shots.Readiness) { // Reset everything but Readiness and Serpent Sting and TBW for (int i = 0; i < shotTable.Length; i++) { RotationShotInfo s = shotTable[i]; if (s != null) { if (s.type != Shots.Readiness && s.type != Shots.SerpentSting && s.type != Shots.BestialWrath) { s.time_until_off_cd = 0; } } } } if (LALTimer > currentTime) { //Cells(row, col + 5).value = "(" & LALType & ") " + Cells(row, col + 5).value } // Advance time by 0 if the shot is off the GCD, otherwise by GCD+Latency if (!checkGCD(thisShot)) { // do nothing } else if (thisShot == Shots.SteadyShot && thisShotInfo.castTime * (1f / SShaste) < GCD + Latency) { currentTime += GCD + Latency; } else if (thisShot == Shots.SteadyShot) { currentTime += thisShotInfo.castTime * (1f / SShaste) + Latency; //} else if (thisShot == Shots.Volley) { // currentTime += thisShotInfo.castTime + Latency; } else { currentTime += GCD + Latency; } // more here... } #endregion }while (currentTime < FightDuration - 1); #endregion #region Save Results foreach (var pair in shotData) { pair.Value.frequency = pair.Value.countUsed > 0 ? currentTime / pair.Value.countUsed : 0; } for (int i = 0; i < calculatedStats.priorityRotation.priorities.Length; i++) { ShotData s = calculatedStats.priorityRotation.priorities[i]; if (s != null) { s.setFrequency(calculatedStats.priorityRotation, shotData.ContainsKey(s.Type) ? shotData[s.Type].frequency : 0); } } this.TestTimeElapsed = currentTime; this.ISSProcsAimed = ISSprocsAimed; this.ISSProcsArcane = ISSProcsArcane; this.ISSProcsChimera = ISSProcsChimera; this.IAotHProcs = IAotHProcs; this.IAotHTime = IAotHUptime; this.LALProcCount = LALProcs; this.IAotHUptime = this.TestTimeElapsed > 0 ? 1.0f * this.IAotHTime / this.TestTimeElapsed : 0; this.ISSAimedUptime = this.ISSProcsAimed > 0 ? 1.0f * this.ISSProcsAimed / shotData[Shots.AimedShot].countUsed : 0; this.ISSArcaneUptime = this.ISSProcsArcane > 0 ? 1.0f * this.ISSProcsArcane / shotData[Shots.ArcaneShot].countUsed : 0; this.ISSChimeraUptime = this.ISSProcsChimera > 0 ? 1.0f * this.ISSProcsChimera / shotData[Shots.ChimeraShot].countUsed : 0; #endregion /* * ActiveWorkbook.Names.Add name:="RotationTestResultNames", RefersToR1C1:= _ * "='Rotation Test'!R5C7:R" & row & "C7" * ActiveWorkbook.Names.Add name:="RotationTestTable", RefersToR1C1:= _ * "='Rotation Test'!R5C6:R" & row & "C" & col + 5 */ }
private ComparisonCalculationHunter comparisonFromShotDPF(ShotData shot) { ComparisonCalculationHunter comp = new ComparisonCalculationHunter(); float dpm = shot.FocusCost > 0 ? (float)(shot.Damage / shot.FocusCost) : 0; comp.Name = Enum.GetName(typeof(Shots), shot.Type); comp.SubPoints = new float[] { dpm }; comp.OverallPoints = dpm; return comp; }
private ComparisonCalculationHunter comparisonFromShotRotationFocusPS(ShotData shot) { ComparisonCalculationHunter comp = new ComparisonCalculationHunter(); comp.Name = Enum.GetName(typeof(Shots), shot.Type); comp.SubPoints = new float[] { (float)shot.FocusPS }; comp.OverallPoints = (float)shot.FocusPS; return comp; }
private ComparisonCalculationHunter comparisonFromShotSpammedMPS(ShotData shot) { ComparisonCalculationHunter comp = new ComparisonCalculationHunter(); float shotWait = shot.Duration > shot.Cd ? shot.Duration : shot.Cd; float mps = shotWait > 0 ? (float)(shot.FocusCost / shotWait) : 0; comp.Name = Enum.GetName(typeof(Shots), shot.Type); comp.SubPoints = new float[] { mps }; comp.OverallPoints = mps; return comp; }
/*private ComparisonCalculationHunter[] GetPetTalentSpecsChart(Character character, CharacterCalculationsHunter calcs) { List<ComparisonCalculationBase> talentCalculations = new List<ComparisonCalculationBase>(); Character newChar = character.Clone(); /*PetTalentsBase nothing = character.CurrentTalents.Clone(); for (int i = 0; i < nothing.Data.Length; i++) nothing.Data[i] = 0; for (int i = 0; i < nothing.GlyphData.Length; i++) nothing.GlyphData[i] = false; newChar.CurrentTalents = nothing; CharacterCalculationsBase baseCalc = Calculations.GetCharacterCalculations(newChar, null, false, true, true); CharacterCalculationsBase newCalc; ComparisonCalculationBase compare; bool same, found = false; foreach (SavedTalentSpec sts in SavedTalentSpec.SpecsFor(character.Class)) { same = false; if (sts.Equals(character.CurrentTalents)) same = true; newChar.CurrentTalents = sts.TalentSpec(); newCalc = Calculations.GetCharacterCalculations(newChar, null, false, true, true); compare = Calculations.GetCharacterComparisonCalculations(baseCalc, newCalc, sts.Name, same); compare.Item = null; compare.Name = sts.ToString(); compare.Description = sts.Spec; talentCalculations.Add(compare); found = found || same; } if (!found) { newCalc = Calculations.GetCharacterCalculations(character, null, false, true, true); compare = Calculations.GetCharacterComparisonCalculations(baseCalc, newCalc, "Custom", true); talentCalculations.Add(compare); } //CGL_Legend.LegendItems = Calculations.SubPointNameColors; //ComparisonGraph.LegendItems = Calculations.SubPointNameColors; //ComparisonGraph.Mode = ComparisonGraph.DisplayMode.Subpoints; //ComparisonGraph.DisplayCalcs(talentCalculations.ToArray()); return talentCalculations.ToArray(); /*List<ComparisonCalculationHunter> talentCalculations = new List<ComparisonCalculationHunter>(); Character baseChar = character.Clone(); CalculationOptionsHunter baseCalcOpts = baseChar.CalculationOptions as CalculationOptionsHunter; Character newChar = character.Clone(); CalculationOptionsHunter newCalcOpts = newChar.CalculationOptions as CalculationOptionsHunter; CharacterCalculationsHunter currentCalc; CharacterCalculationsHunter newCalc; ComparisonCalculationHunter compare; currentCalc = (CharacterCalculationsHunter)Calculations.GetCharacterCalculations(baseChar, null, false, true, false); foreach (PropertyInfo pi in baseCalcOpts.PetTalents.GetType().GetProperties()) { PetTalentDataAttribute[] petTalentDatas = pi.GetCustomAttributes(typeof(PetTalentDataAttribute), true) as PetTalentDataAttribute[]; int orig; if (petTalentDatas.Length > 0) { PetTalentDataAttribute petTalentData = petTalentDatas[0]; orig = baseCalcOpts.PetTalents.Data[petTalentData.Index]; if (petTalentData.MaxPoints == (int)pi.GetValue(baseCalcOpts.PetTalents, null)) { newCalcOpts.PetTalents.Data[petTalentData.Index]--; newCalc = (CharacterCalculationsHunter)GetCharacterCalculations(newChar, null, false, true, false); compare = (ComparisonCalculationHunter)GetCharacterComparisonCalculations(newCalc, currentCalc, petTalentData.Name, petTalentData.MaxPoints == orig); } else { newCalcOpts.PetTalents.Data[petTalentData.Index]++; newCalc = (CharacterCalculationsHunter)GetCharacterCalculations(newChar, null, false, true, false); compare = (ComparisonCalculationHunter)GetCharacterComparisonCalculations(currentCalc, newCalc, petTalentData.Name, petTalentData.MaxPoints == orig); } string text = string.Format("Current Rank {0}/{1}\r\n\r\n", orig, petTalentData.MaxPoints); if (orig == 0) { // We originally didn't have it, so first rank is next rank text += "Next Rank:\r\n"; text += petTalentData.Description[0]; } else if (orig >= petTalentData.MaxPoints) { // We originally were at max, so there isn't a next rank, just show the capped one text += petTalentData.Description[petTalentData.MaxPoints - 1]; } else { // We aren't at 0 or MaxPoints originally, so it's just a point in between text += petTalentData.Description[orig - 1]; text += "\r\n\r\nNext Rank:\r\n"; text += petTalentData.Description[orig]; } compare.Description = text; compare.Item = null; talentCalculations.Add(compare); newCalcOpts.PetTalents.Data[petTalentData.Index] = orig; } } return talentCalculations.ToArray(); }*/ #if FALSE private ComparisonCalculationHunter comparisonFromShotSpammedDPS(ShotData shot) { ComparisonCalculationHunter comp = new ComparisonCalculationHunter(); float shotWait = shot.Duration > shot.Cd ? shot.Duration : shot.Cd; float dps = shotWait > 0 ? (float)(shot.Damage / shotWait) : 0; comp.Name = Enum.GetName(typeof(Shots), shot.Type); comp.HunterDPSPoints = dps; comp.OverallPoints = dps; return comp; }
public RotationShotInfo(ShotData shot) { this.type = shot.Type; this.cooldown = shot.Cd; this.castTime = shot.Cd; this.check_gcd = shot.TriggersGCD; this.no_gcd = !shot.TriggersGCD && shot.Type != Shots.Readiness; this.damage = shot.Damage; if (shot.Type == Shots.SerpentSting) this.cooldown = shot.Duration; }