From 2f49dbd3657d762e47bc32c96b25da064247bb9b Mon Sep 17 00:00:00 2001 From: rosenrusinov Date: Thu, 11 May 2023 13:55:43 +0200 Subject: [PATCH 1/5] first pass --- sim/core/pet.go | 14 ++-- sim/deathknight/deathknight.go | 1 + sim/deathknight/dps/dps_deathknight.go | 2 +- sim/deathknight/dps/rotation_unholy.go | 53 ++++++++----- sim/deathknight/dps/rotation_unholy_helper.go | 74 ++++++++----------- sim/deathknight/summon_gargoyle.go | 4 +- 6 files changed, 77 insertions(+), 71 deletions(-) diff --git a/sim/core/pet.go b/sim/core/pet.go index 5021e082d8..4a20f78546 100644 --- a/sim/core/pet.go +++ b/sim/core/pet.go @@ -162,6 +162,14 @@ func (pet *Pet) Enable(sim *Simulation, petAgent PetAgent) { //reset current mana after applying stats pet.manaBar.reset() + // Call onEnable callbacks before enabling auto swing + // to not have to reorder PAs multiple times + pet.enabled = true + + if pet.OnPetEnable != nil { + pet.OnPetEnable(sim) + } + pet.SetGCDTimer(sim, MaxDuration(0, sim.CurrentTime)) if sim.CurrentTime >= 0 { pet.AutoAttacks.EnableAutoSwing(sim) @@ -172,12 +180,6 @@ func (pet *Pet) Enable(sim *Simulation, petAgent PetAgent) { }) } - pet.enabled = true - - if pet.OnPetEnable != nil { - pet.OnPetEnable(sim) - } - if sim.Log != nil { pet.Log(sim, "Pet stats: %s", pet.GetStats()) pet.Log(sim, "Pet inherited stats: %s", pet.ApplyStatDependencies(pet.inheritedStats)) diff --git a/sim/deathknight/deathknight.go b/sim/deathknight/deathknight.go index 7274d07ec8..b74ea699b1 100644 --- a/sim/deathknight/deathknight.go +++ b/sim/deathknight/deathknight.go @@ -74,6 +74,7 @@ type Deathknight struct { Gargoyle *GargoylePet SummonGargoyle *core.Spell + SummonGargoyleAura *core.Aura GargoyleSummonDelay time.Duration OnGargoyleStartFirstCast func() diff --git a/sim/deathknight/dps/dps_deathknight.go b/sim/deathknight/dps/dps_deathknight.go index 8320e3627f..74b5fc20f9 100644 --- a/sim/deathknight/dps/dps_deathknight.go +++ b/sim/deathknight/dps/dps_deathknight.go @@ -341,7 +341,7 @@ func (dk *DpsDeathknight) gargoyleHasteCooldownSync(actionID core.ActionID, isPo majorCd.ShouldActivate = func(sim *core.Simulation, character *core.Character) bool { if !dk.Rotation.PreNerfedGargoyle { - aura := dk.GetAura("Summon Gargoyle") + aura := dk.SummonGargoyleAura if aura != nil && aura.IsActive() { return true diff --git a/sim/deathknight/dps/rotation_unholy.go b/sim/deathknight/dps/rotation_unholy.go index 1f0926574d..ea30f2fcb3 100644 --- a/sim/deathknight/dps/rotation_unholy.go +++ b/sim/deathknight/dps/rotation_unholy.go @@ -131,11 +131,15 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim } cast := false - prioSs, _ := dk.bonusProcRotationChecks(sim) + prioSs := dk.virulenceRotationChecks(sim) - if dk.uhDiseaseCheck(sim, target, dk.DeathAndDecay, true, 1) { - if prioSs { - if dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) { + gargCheckSpellGCD := dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) + gargCheck2GCD := dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) + gargCheckDiseaseRecast := dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) + + if prioSs { + if dk.uhDiseaseCheck(sim, target, dk.ScourgeStrike, true, 2) { + if gargCheck2GCD { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -146,26 +150,35 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim NewAction(dk.RotationActionCallback_UnholyDndRotation) } } else { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheckDiseaseRecast { dk.uhAfterGargoyleSequence(sim) - return sim.CurrentTime + } else { + dk.uhRecastDiseasesSequence(sim) } - cast = dk.DeathAndDecay.Cast(sim, target) + return sim.CurrentTime } } else { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault+50*time.Millisecond) { - dk.uhAfterGargoyleSequence(sim) + if dk.uhDiseaseCheck(sim, target, dk.DeathAndDecay, true, 1) { + if gargCheckSpellGCD { + dk.uhAfterGargoyleSequence(sim) + return sim.CurrentTime + } + cast = dk.DeathAndDecay.Cast(sim, target) + } else { + if gargCheckDiseaseRecast { + dk.uhAfterGargoyleSequence(sim) + } else { + dk.uhRecastDiseasesSequence(sim) + } return sim.CurrentTime } - dk.uhRecastDiseasesSequence(sim) - return sim.CurrentTime } if !cast { - if dk.uhDiseaseCheck(sim, target, dk.ScourgeStrike, true, 1) { + if dk.uhDiseaseCheck(sim, target, dk.DeathAndDecay, true, 1) { if !dk.uhShouldWaitForDnD(sim, true, true, true) { if dk.Talents.ScourgeStrike && dk.ScourgeStrike.IsReady(sim) { - if dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) { + if gargCheck2GCD { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -176,7 +189,7 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim NewAction(dk.RotationActionCallback_UnholyDndRotation) } } else if dk.IcyTouch.CanCast(sim, nil) && dk.PlagueStrike.CanCast(sim, nil) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) { + if gargCheckDiseaseRecast { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -191,7 +204,7 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim if !cast { if dk.shShouldSpreadDisease(sim) { if !dk.uhShouldWaitForDnD(sim, true, false, false) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheckSpellGCD { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -200,14 +213,14 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim } if !cast { if dk.uhDeathCoilCheck(sim) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheckSpellGCD { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } cast = dk.DeathCoil.Cast(sim, target) } if !cast { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheckSpellGCD { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -467,12 +480,12 @@ func (dk *DpsDeathknight) RotationActionUH_BS(sim *core.Simulation, target *core bloodPresenceSwitch := false // Go back to Blood Presence after Gargoyle - if !dk.Rotation.PreNerfedGargoyle && !dk.SummonGargoyle.IsReady(sim) && dk.Rotation.Presence == proto.Deathknight_Rotation_Blood && dk.Rotation.GargoylePresence == proto.Deathknight_Rotation_Unholy && dk.PresenceMatches(deathknight.UnholyPresence) && !dk.HasActiveAura("Summon Gargoyle") { + if !dk.Rotation.PreNerfedGargoyle && !dk.SummonGargoyle.IsReady(sim) && dk.Rotation.Presence == proto.Deathknight_Rotation_Blood && dk.Rotation.GargoylePresence == proto.Deathknight_Rotation_Unholy && dk.PresenceMatches(deathknight.UnholyPresence) && !dk.SummonGargoyleAura.IsActive() { bloodPresenceSwitch = true } // Do not switch presences if gargoyle is still up if it's nerfed gargoyle - if !dk.Rotation.PreNerfedGargoyle && !dk.HasActiveAura("Summon Gargoyle") { + if !dk.Rotation.PreNerfedGargoyle && !dk.SummonGargoyleAura.IsActive() { // Go back to Blood Presence after Bloodlust if dk.Rotation.Presence == proto.Deathknight_Rotation_Blood && dk.Rotation.BlPresence == proto.Deathknight_Rotation_Unholy && dk.PresenceMatches(deathknight.UnholyPresence) && !dk.HasActiveAuraWithTag("Bloodlust") { bloodPresenceSwitch = true @@ -499,7 +512,7 @@ func (dk *DpsDeathknight) RotationActionUH_BS(sim *core.Simulation, target *core s.ConditionalAdvance(casted && advance) } else { bloodSpell := dk.BloodBoil - _, prioBs := dk.bonusProcRotationChecks(sim) + prioBs := dk.unholyMightRotationChecks(sim) if dk.desolationAuraCheck(sim) || prioBs { bloodSpell = dk.BloodStrike } diff --git a/sim/deathknight/dps/rotation_unholy_helper.go b/sim/deathknight/dps/rotation_unholy_helper.go index d98c799ffa..e231b935d1 100644 --- a/sim/deathknight/dps/rotation_unholy_helper.go +++ b/sim/deathknight/dps/rotation_unholy_helper.go @@ -45,6 +45,13 @@ type UnholyRotation struct { sigil SigilType unholyMight bool + + virulenceAura *core.Aura + unholyMightAura *core.Aura + blackMagicProc *core.Aura + fallenCrusaderProc *core.Aura + berserkingMh *core.Aura + berserkingOh *core.Aura } func (ur *UnholyRotation) Reset(sim *core.Simulation) { @@ -75,16 +82,22 @@ func (ur *UnholyRotation) Initialize(dk *DpsDeathknight) { if dk.Talents.SummonGargoyle && dk.Rotation.UseGargoyle { dk.setupWeaponSwap() + ur.blackMagicProc = dk.GetAura("Black Magic Proc") + ur.fallenCrusaderProc = dk.GetAura("Rune Of The Fallen Crusader Proc") + ur.berserkingMh = dk.GetAura("Berserking MH Proc") + ur.berserkingOh = dk.GetAura("Berserking OH Proc") } // Init Sigil of Virulence Rotation if dk.Talents.ScourgeStrike && dk.Equip[core.ItemSlotRanged].ID == 47673 { ur.sigil = Sigil_Virulence + ur.virulenceAura = dk.GetAura("Sigil of Virulence Proc") } // Init T9 2P Proc if dk.HasSetBonus(deathknight.ItemSetThassariansBattlegear, 2) { ur.unholyMight = true + ur.unholyMightAura = dk.GetAura("Unholy Might Proc") } } @@ -114,22 +127,23 @@ func (dk *DpsDeathknight) uhBloodRuneAction(isFirst bool) deathknight.RotationAc } } -func (dk *DpsDeathknight) bonusProcRotationChecks(sim *core.Simulation) (bool, bool) { +func (dk *DpsDeathknight) virulenceRotationChecks(sim *core.Simulation) bool { // If we have sigil of virulence // Higher prio SS then Dnd when gargoyle is ready prioSs := false if dk.Talents.ScourgeStrike && dk.ur.sigil == Sigil_Virulence && (dk.SummonGargoyle.IsReady(sim) || dk.SummonGargoyle.CD.TimeToReady(sim) < 10*time.Second) { - virulenceAura := dk.GetAura("Sigil of Virulence Proc") - prioSs = !virulenceAura.IsActive() || virulenceAura.RemainingDuration(sim) < 10*time.Second + prioSs = !dk.ur.virulenceAura.IsActive() || dk.ur.virulenceAura.RemainingDuration(sim) < 10*time.Second } + return prioSs +} +func (dk *DpsDeathknight) unholyMightRotationChecks(sim *core.Simulation) bool { // If we have T9 2P we prio BS over BB for refreshing the buff when out of ICD prioBs := false if dk.ur.unholyMight { - unholyMightAura := dk.GetAura("Unholy Might Proc") - prioBs = unholyMightAura.StartedAt() == 0 || unholyMightAura.StartedAt() < sim.CurrentTime-45*time.Second + prioBs = dk.ur.unholyMightAura.StartedAt() == 0 || dk.ur.unholyMightAura.StartedAt() < sim.CurrentTime-45*time.Second } - return prioSs, prioBs + return prioBs } func (dk *DpsDeathknight) weaponSwapCheck(sim *core.Simulation) bool { @@ -138,23 +152,17 @@ func (dk *DpsDeathknight) weaponSwapCheck(sim *core.Simulation) bool { } // Swap if gargoyle will still be on CD for full ICD or if gargoyle is already active - shouldSwapBm := dk.ur.bmIcd < sim.CurrentTime && (dk.SummonGargoyle.CD.TimeToReady(sim) > 45*time.Second || dk.HasActiveAura("Summon Gargoyle")) - shouldSwapBackFromBm := dk.HasActiveAura("Black Magic Proc") // || dk.GetAura("Rune Of The Fallen Crusader Proc").RemainingDuration(sim) < 5*time.Second + shouldSwapBm := dk.ur.bmIcd < sim.CurrentTime && (dk.SummonGargoyle.CD.TimeToReady(sim) > 45*time.Second || dk.SummonGargoyleAura.IsActive()) + shouldSwapBackFromBm := dk.ur.blackMagicProc.IsActive() // || dk.GetAura("Rune Of The Fallen Crusader Proc").RemainingDuration(sim) < 5*time.Second if dk.ur.mhSwap == WeaponSwap_BlackMagic { if !dk.ur.mhSwapped && shouldSwapBm { // Swap to BM - if sim.Log != nil { - sim.Log("Swapping MH to BM") - } dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}, true) dk.ur.mhSwapped = true } else if dk.ur.mhSwapped && shouldSwapBackFromBm { // Swap to Normal set and set BM Icd tracker - if sim.Log != nil { - sim.Log("Swapping MH to Normal") - } - dk.ur.bmIcd = dk.GetAura("Black Magic Proc").ExpiresAt() + 35*time.Second + dk.ur.bmIcd = dk.ur.blackMagicProc.ExpiresAt() + 35*time.Second dk.ur.mhSwapped = false dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}, true) } @@ -163,58 +171,40 @@ func (dk *DpsDeathknight) weaponSwapCheck(sim *core.Simulation) bool { if dk.ur.ohSwap == WeaponSwap_BlackMagic { if !dk.ur.ohSwapped && shouldSwapBm { // Swap to BM - if sim.Log != nil { - sim.Log("Swapping OH to BM") - } dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}, true) dk.ur.ohSwapped = true } else if dk.ur.ohSwapped && shouldSwapBackFromBm { // Swap to Normal set and set BM Icd tracker - if sim.Log != nil { - sim.Log("Swapping OH to Normal") - } - dk.ur.bmIcd = dk.GetAura("Black Magic Proc").ExpiresAt() + 35*time.Second + dk.ur.bmIcd = dk.ur.blackMagicProc.ExpiresAt() + 35*time.Second dk.ur.ohSwapped = false dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}, true) } } - shouldSwapBerserking := dk.HasActiveAura("Rune Of The Fallen Crusader Proc") && - dk.GetAura("Rune Of The Fallen Crusader Proc").RemainingDuration(sim) > time.Second*10 + shouldSwapBerserking := dk.ur.fallenCrusaderProc.IsActive() && + dk.ur.fallenCrusaderProc.RemainingDuration(sim) > time.Second*10 shouldSwapBackfromBerserking := false //dk.GetAura("Rune Of The Fallen Crusader Proc").RemainingDuration(sim) < 5*time.Second if dk.ur.mhSwap == WeaponSwap_Berserking { - if !dk.ur.mhSwapped && !dk.HasActiveAura("Berserking MH Proc") && shouldSwapBerserking { + if !dk.ur.mhSwapped && !dk.ur.berserkingMh.IsActive() && shouldSwapBerserking { // Swap to Berserking - if sim.Log != nil { - sim.Log("Swapping MH to Berserking") - } dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}, true) dk.ur.mhSwapped = true - } else if dk.ur.mhSwapped && (dk.HasActiveAura("Berserking MH Proc") || shouldSwapBackfromBerserking) { + } else if dk.ur.mhSwapped && (dk.ur.berserkingMh.IsActive() || shouldSwapBackfromBerserking) { // Swap to Normal set - if sim.Log != nil { - sim.Log("Swapping MH to Normal") - } dk.ur.mhSwapped = false dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}, true) } } if dk.ur.ohSwap == WeaponSwap_Berserking { - if !dk.ur.ohSwapped && !dk.HasActiveAura("Berserking OH Proc") && shouldSwapBerserking { + if !dk.ur.ohSwapped && !dk.ur.berserkingOh.IsActive() && shouldSwapBerserking { // Swap to Berserking - if sim.Log != nil { - sim.Log("Swapping OH to Berserking") - } dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}, true) dk.ur.ohSwapped = true - } else if dk.ur.ohSwapped && (dk.HasActiveAura("Berserking OH Proc") || shouldSwapBackfromBerserking) { + } else if dk.ur.ohSwapped && (dk.ur.berserkingOh.IsActive() || shouldSwapBackfromBerserking) { // Swap to Normal set - if sim.Log != nil { - sim.Log("Swapping OH to Normal") - } dk.ur.ohSwapped = false dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}, true) } @@ -394,7 +384,7 @@ func (dk *DpsDeathknight) uhGargoyleCheck(sim *core.Simulation, target *core.Uni } // Go back to Unholy Presence after Gargoyle - if !dk.Rotation.PreNerfedGargoyle && !dk.SummonGargoyle.IsReady(sim) && dk.Rotation.Presence == proto.Deathknight_Rotation_Unholy && dk.Rotation.GargoylePresence == proto.Deathknight_Rotation_Blood && dk.PresenceMatches(deathknight.BloodPresence) && !dk.HasActiveAura("Summon Gargoyle") { + if !dk.Rotation.PreNerfedGargoyle && !dk.SummonGargoyle.IsReady(sim) && dk.Rotation.Presence == proto.Deathknight_Rotation_Unholy && dk.Rotation.GargoylePresence == proto.Deathknight_Rotation_Blood && dk.PresenceMatches(deathknight.BloodPresence) && !dk.SummonGargoyleAura.IsActive() { if dk.BloodTapAura.IsActive() { dk.BloodTapAura.Deactivate(sim) } @@ -402,7 +392,7 @@ func (dk *DpsDeathknight) uhGargoyleCheck(sim *core.Simulation, target *core.Uni } // Do not switch presences if gargoyle is still up if it's nerfed gargoyle - if !dk.Rotation.PreNerfedGargoyle && dk.HasActiveAura("Summon Gargoyle") { + if !dk.Rotation.PreNerfedGargoyle && dk.SummonGargoyleAura.IsActive() { return false } diff --git a/sim/deathknight/summon_gargoyle.go b/sim/deathknight/summon_gargoyle.go index ad49ff3824..c0ebb63158 100644 --- a/sim/deathknight/summon_gargoyle.go +++ b/sim/deathknight/summon_gargoyle.go @@ -12,7 +12,7 @@ func (dk *Deathknight) registerSummonGargoyleCD() { return } - summonGargoyleAura := dk.RegisterAura(core.Aura{ + dk.SummonGargoyleAura = dk.RegisterAura(core.Aura{ Label: "Summon Gargoyle", ActionID: core.ActionID{SpellID: 49206}, Duration: time.Second * 30, @@ -48,7 +48,7 @@ func (dk *Deathknight) registerSummonGargoyleCD() { dk.Gargoyle.updateCastSpeed() // Add a dummy aura to show in metrics - summonGargoyleAura.Activate(sim) + dk.SummonGargoyleAura.Activate(sim) // Start casting after a 2.5s delay to simulate the summon animation pa := core.PendingAction{ From 1b82aceade0c39d068f63c1c3e5b2eeb6effddbc Mon Sep 17 00:00:00 2001 From: rosenrusinov Date: Thu, 11 May 2023 14:36:14 +0200 Subject: [PATCH 2/5] fix gcd error --- sim/deathknight/dps/rotation_unholy.go | 47 +++++++++----------------- sim/deathknight/scourge_strike.go | 7 ++-- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/sim/deathknight/dps/rotation_unholy.go b/sim/deathknight/dps/rotation_unholy.go index ea30f2fcb3..221d2ca8b4 100644 --- a/sim/deathknight/dps/rotation_unholy.go +++ b/sim/deathknight/dps/rotation_unholy.go @@ -131,15 +131,9 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim } cast := false - prioSs := dk.virulenceRotationChecks(sim) - - gargCheckSpellGCD := dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) - gargCheck2GCD := dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) - gargCheckDiseaseRecast := dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) - - if prioSs { - if dk.uhDiseaseCheck(sim, target, dk.ScourgeStrike, true, 2) { - if gargCheck2GCD { + if dk.uhDiseaseCheck(sim, target, dk.DeathAndDecay, true, 1) { + if dk.virulenceRotationChecks(sim) { + if dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -150,35 +144,26 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim NewAction(dk.RotationActionCallback_UnholyDndRotation) } } else { - if gargCheckDiseaseRecast { - dk.uhAfterGargoyleSequence(sim) - } else { - dk.uhRecastDiseasesSequence(sim) - } - return sim.CurrentTime - } - } else { - if dk.uhDiseaseCheck(sim, target, dk.DeathAndDecay, true, 1) { - if gargCheckSpellGCD { + if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } cast = dk.DeathAndDecay.Cast(sim, target) - } else { - if gargCheckDiseaseRecast { - dk.uhAfterGargoyleSequence(sim) - } else { - dk.uhRecastDiseasesSequence(sim) - } + } + } else { + if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault+50*time.Millisecond) { + dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } + dk.uhRecastDiseasesSequence(sim) + return sim.CurrentTime } if !cast { - if dk.uhDiseaseCheck(sim, target, dk.DeathAndDecay, true, 1) { + if dk.uhDiseaseCheck(sim, target, dk.ScourgeStrike, true, 1) { if !dk.uhShouldWaitForDnD(sim, true, true, true) { if dk.Talents.ScourgeStrike && dk.ScourgeStrike.IsReady(sim) { - if gargCheck2GCD { + if dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -189,7 +174,7 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim NewAction(dk.RotationActionCallback_UnholyDndRotation) } } else if dk.IcyTouch.CanCast(sim, nil) && dk.PlagueStrike.CanCast(sim, nil) { - if gargCheckDiseaseRecast { + if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -204,7 +189,7 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim if !cast { if dk.shShouldSpreadDisease(sim) { if !dk.uhShouldWaitForDnD(sim, true, false, false) { - if gargCheckSpellGCD { + if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -213,14 +198,14 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim } if !cast { if dk.uhDeathCoilCheck(sim) { - if gargCheckSpellGCD { + if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } cast = dk.DeathCoil.Cast(sim, target) } if !cast { - if gargCheckSpellGCD { + if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } diff --git a/sim/deathknight/scourge_strike.go b/sim/deathknight/scourge_strike.go index daba30162e..0594f57e74 100644 --- a/sim/deathknight/scourge_strike.go +++ b/sim/deathknight/scourge_strike.go @@ -35,6 +35,10 @@ func (dk *Deathknight) registerScourgeStrikeShadowDamageSpell() *core.Spell { } func (dk *Deathknight) registerScourgeStrikeSpell() { + if !dk.Talents.ScourgeStrike { + return + } + shadowDamageSpell := dk.registerScourgeStrikeShadowDamageSpell() bonusBaseDamage := dk.sigilOfAwarenessBonus() + dk.sigilOfArthriticBindingBonus() hasGlyph := dk.HasMajorGlyph(proto.DeathknightMajorGlyph_GlyphOfScourgeStrike) @@ -57,9 +61,6 @@ func (dk *Deathknight) registerScourgeStrikeSpell() { }, IgnoreHaste: true, }, - ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return dk.Talents.ScourgeStrike - }, BonusCritRating: (dk.subversionCritBonus() + dk.viciousStrikesCritChanceBonus() + dk.scourgeborneBattlegearCritBonus()) * core.CritRatingPerCritChance, From 58961c6335c35c6fd64277c9a4d40030118be027 Mon Sep 17 00:00:00 2001 From: rosenrusinov Date: Thu, 11 May 2023 14:47:46 +0200 Subject: [PATCH 3/5] cache gargoyle checks --- sim/deathknight/dps/rotation_unholy.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sim/deathknight/dps/rotation_unholy.go b/sim/deathknight/dps/rotation_unholy.go index 221d2ca8b4..bf07e704d6 100644 --- a/sim/deathknight/dps/rotation_unholy.go +++ b/sim/deathknight/dps/rotation_unholy.go @@ -187,9 +187,10 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim return sim.CurrentTime } if !cast { + gargCheck := dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) if dk.shShouldSpreadDisease(sim) { if !dk.uhShouldWaitForDnD(sim, true, false, false) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheck { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -198,14 +199,14 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim } if !cast { if dk.uhDeathCoilCheck(sim) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheck { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } cast = dk.DeathCoil.Cast(sim, target) } if !cast { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheck { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -256,13 +257,13 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholySsRotation(sim *core.Simu fuStrike = dk.Obliterate } if dk.uhDiseaseCheck(sim, target, fuStrike, true, 1) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if dk.uhGargoyleCheck(sim, target, core.GCDDefault+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } casted = fuStrike.Cast(sim, target) } else { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()*2+50*time.Millisecond) { + if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -270,15 +271,16 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholySsRotation(sim *core.Simu return sim.CurrentTime } if !casted { + gargCheck := dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) if dk.shShouldSpreadDisease(sim) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheck { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } casted = dk.uhSpreadDiseases(sim, target, s) } else { if dk.uhDiseaseCheck(sim, target, dk.BloodStrike, true, 1) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if dk.uhGargoyleCheck(sim, target, core.GCDDefault+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -288,7 +290,7 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholySsRotation(sim *core.Simu casted = dk.BloodBoil.Cast(sim, target) } } else { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()*2+50*time.Millisecond) { + if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } @@ -298,14 +300,14 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholySsRotation(sim *core.Simu } if !casted { if dk.uhDeathCoilCheck(sim) { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheck { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } casted = dk.DeathCoil.Cast(sim, target) } if !casted { - if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) { + if gargCheck { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } From aa8f54c10a76145afb549883879e56e1f41aa1b4 Mon Sep 17 00:00:00 2001 From: rosenrusinov Date: Mon, 22 May 2023 10:57:56 +0200 Subject: [PATCH 4/5] update rotations --- sim/deathknight/dps/rotation_unholy.go | 49 ++++++++++--------- sim/deathknight/dps/rotation_unholy_helper.go | 18 +++++-- sim/deathknight/rune_strike.go | 4 ++ sim/deathknight/talents_unholy.go | 13 ++--- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/sim/deathknight/dps/rotation_unholy.go b/sim/deathknight/dps/rotation_unholy.go index 6dea9adaa7..3d24a2820a 100644 --- a/sim/deathknight/dps/rotation_unholy.go +++ b/sim/deathknight/dps/rotation_unholy.go @@ -28,26 +28,29 @@ func (dk *DpsDeathknight) setupUnholyRotations() { NewAction(dk.getSecondDiseaseAction()). NewAction(dk.uhBloodRuneAction(true)) + fuStrikeAction := dk.RotationActionCallback_DS if dk.Talents.ScourgeStrike { - if dk.ur.sigil == Sigil_Virulence { - dk.RotationSequence. - NewAction(dk.RotationActionCallback_SS). - NewAction(dk.RotationActionUH_BS). - NewAction(dk.RotationActionUH_SS_Sigil). - NewAction(dk.RotationActionUH_BS). - NewAction(dk.RotationActionUH_SS_Sigil). - NewAction(dk.RotationActionUH_BS) - } else if dk.ur.sigil == Sigil_HangedMan { - dk.RotationSequence. - NewAction(dk.RotationActionCallback_SS). - NewAction(dk.RotationActionUH_BS). - NewAction(dk.RotationActionCallback_DC). - NewAction(dk.RotationActionCallback_ERW). - NewAction(dk.RotationActionCallback_SS). - NewAction(dk.RotationActionUH_BS). - NewAction(dk.RotationActionCallback_SS). - NewAction(dk.RotationActionUH_BS) - } + fuStrikeAction = dk.RotationActionCallback_SS + } + + if dk.ur.sigil == Sigil_Virulence { + dk.RotationSequence. + NewAction(fuStrikeAction). + NewAction(dk.RotationActionUH_BS). + NewAction(dk.RotationActionUH_SS_Sigil). + NewAction(dk.RotationActionUH_BS). + NewAction(dk.RotationActionUH_SS_Sigil). + NewAction(dk.RotationActionUH_BS) + } else if dk.ur.sigil == Sigil_HangedMan { + dk.RotationSequence. + NewAction(fuStrikeAction). + NewAction(dk.RotationActionUH_BS). + NewAction(dk.RotationActionCallback_DC). + NewAction(dk.RotationActionCallback_ERW). + NewAction(fuStrikeAction). + NewAction(dk.RotationActionUH_BS). + NewAction(fuStrikeAction). + NewAction(dk.RotationActionUH_BS) } if dk.Rotation.UseDeathAndDecay || (!dk.Talents.ScourgeStrike && dk.Talents.Annihilation == 0) { @@ -139,7 +142,7 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } - cast = dk.ScourgeStrike.Cast(sim, target) + cast = dk.uhCastVirulenceStrike(sim, target) if cast { dk.RotationSequence.Clear(). NewAction(dk.RotationActionUH_BS). @@ -164,12 +167,12 @@ func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Sim if !cast { if dk.uhDiseaseCheck(sim, target, dk.ScourgeStrike, true, 1) { if !dk.uhShouldWaitForDnD(sim, true, true, true) { - if dk.Talents.ScourgeStrike && dk.ScourgeStrike.IsReady(sim) { + if dk.ur.sigil == Sigil_Virulence && dk.DeathStrike.IsReady(sim) { if dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) { dk.uhAfterGargoyleSequence(sim) return sim.CurrentTime } - cast = dk.ScourgeStrike.Cast(sim, target) + cast = dk.uhCastVirulenceStrike(sim, target) if cast { dk.RotationSequence.Clear(). NewAction(dk.RotationActionUH_BS). @@ -528,7 +531,7 @@ func (dk *DpsDeathknight) RotationActionUH_SS_Sigil(sim *core.Simulation, target dk.EmpowerRuneWeapon.Cast(sim, target) } - casted := dk.ScourgeStrike.Cast(sim, target) + casted := dk.uhCastVirulenceStrike(sim, target) advance := dk.LastOutcome.Matches(core.OutcomeLanded) s.ConditionalAdvance(casted && advance) diff --git a/sim/deathknight/dps/rotation_unholy_helper.go b/sim/deathknight/dps/rotation_unholy_helper.go index c834b639f9..53f916ee55 100644 --- a/sim/deathknight/dps/rotation_unholy_helper.go +++ b/sim/deathknight/dps/rotation_unholy_helper.go @@ -78,7 +78,7 @@ func (ur *UnholyRotation) Initialize(dk *DpsDeathknight) { } // Init Sigil of Virulence Rotation - if dk.Talents.ScourgeStrike && dk.Equip[core.ItemSlotRanged].ID == 47673 { + if dk.Equip[core.ItemSlotRanged].ID == 47673 { ur.sigil = Sigil_Virulence } @@ -114,13 +114,21 @@ func (dk *DpsDeathknight) uhBloodRuneAction(isFirst bool) deathknight.RotationAc } } +func (dk *DpsDeathknight) uhCastVirulenceStrike(sim *core.Simulation, target *core.Unit) bool { + if dk.Talents.ScourgeStrike { + return dk.ScourgeStrike.Cast(sim, target) + } else { + return dk.DeathStrike.Cast(sim, target) + } +} + func (dk *DpsDeathknight) bonusProcRotationChecks(sim *core.Simulation) (bool, bool) { // If we have sigil of virulence // Higher prio SS then Dnd when gargoyle is ready - prioSs := false - if dk.Talents.ScourgeStrike && dk.ur.sigil == Sigil_Virulence && (dk.SummonGargoyle.IsReady(sim) || dk.SummonGargoyle.CD.TimeToReady(sim) < 10*time.Second) { + prioVirulenceStrike := false + if dk.ur.sigil == Sigil_Virulence && (dk.SummonGargoyle.IsReady(sim) || dk.SummonGargoyle.CD.TimeToReady(sim) < 10*time.Second) { virulenceAura := dk.GetAura("Sigil of Virulence Proc") - prioSs = !virulenceAura.IsActive() || virulenceAura.RemainingDuration(sim) < 10*time.Second + prioVirulenceStrike = !virulenceAura.IsActive() || virulenceAura.RemainingDuration(sim) < 10*time.Second } // If we have T9 2P we prio BS over BB for refreshing the buff when out of ICD @@ -129,7 +137,7 @@ func (dk *DpsDeathknight) bonusProcRotationChecks(sim *core.Simulation) (bool, b unholyMightAura := dk.GetAura("Unholy Might Proc") prioBs = unholyMightAura.StartedAt() == 0 || unholyMightAura.StartedAt() < sim.CurrentTime-45*time.Second } - return prioSs, prioBs + return prioVirulenceStrike, prioBs } func (dk *DpsDeathknight) weaponSwapCheck(sim *core.Simulation) bool { diff --git a/sim/deathknight/rune_strike.go b/sim/deathknight/rune_strike.go index 51593e950b..9024b196d7 100644 --- a/sim/deathknight/rune_strike.go +++ b/sim/deathknight/rune_strike.go @@ -65,6 +65,10 @@ func (dk *Deathknight) newRuneStrikeSpell(isMH bool) *core.Spell { baseDamage *= dk.RoRTSBonus(target) result := spell.CalcAndDealDamage(sim, target, baseDamage, outcomeApplier) + if result.Damage > 0 && dk.Talents.Necrosis > 0 { + dk.necrosisDamage(result.Damage, sim, target, spell) + } + if isMH { dk.threatOfThassarianProc(sim, result, dk.RuneStrikeOh) dk.RuneStrikeAura.Deactivate(sim) diff --git a/sim/deathknight/talents_unholy.go b/sim/deathknight/talents_unholy.go index 54ed0a421d..4dad22db53 100644 --- a/sim/deathknight/talents_unholy.go +++ b/sim/deathknight/talents_unholy.go @@ -107,14 +107,17 @@ func (dk *Deathknight) applyNecrosis() { dk.AutoAttacks.OHConfig.ApplyEffects = dk.necrosisOHAuto } +func (dk *Deathknight) necrosisDamage(damage float64, sim *core.Simulation, target *core.Unit, spell *core.Spell) { + dk.Necrosis.SpellMetrics[target.UnitIndex].Hits++ + dk.Necrosis.SpellMetrics[target.UnitIndex].Casts++ + dk.Necrosis.CalcAndDealDamage(sim, target, damage*dk.NecrosisCoeff, spell.OutcomeAlwaysHit) +} func (dk *Deathknight) necrosisOHAuto(sim *core.Simulation, target *core.Unit, spell *core.Spell) { baseDamage := spell.Unit.OHWeaponDamage(sim, spell.MeleeAttackPower()) + spell.BonusWeaponDamage() if result := spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWhite); result.Damage > 0 { - dk.Necrosis.SpellMetrics[target.UnitIndex].Hits++ - dk.Necrosis.SpellMetrics[target.UnitIndex].Casts++ - dk.Necrosis.CalcAndDealDamage(sim, target, result.Damage*dk.NecrosisCoeff, spell.OutcomeAlwaysHit) + dk.necrosisDamage(result.Damage, sim, target, spell) } } func (dk *Deathknight) necrosisMHAuto(sim *core.Simulation, target *core.Unit, spell *core.Spell) { @@ -122,9 +125,7 @@ func (dk *Deathknight) necrosisMHAuto(sim *core.Simulation, target *core.Unit, s spell.BonusWeaponDamage() if result := spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWhite); result.Damage > 0 { - dk.Necrosis.SpellMetrics[target.UnitIndex].Hits++ - dk.Necrosis.SpellMetrics[target.UnitIndex].Casts++ - dk.Necrosis.CalcAndDealDamage(sim, target, result.Damage*dk.NecrosisCoeff, spell.OutcomeAlwaysHit) + dk.necrosisDamage(result.Damage, sim, target, spell) } } From a96563386ef6ff76af0cfa19c4ea78eef73d2a3d Mon Sep 17 00:00:00 2001 From: rosenrusinov Date: Mon, 22 May 2023 11:38:02 +0200 Subject: [PATCH 5/5] update tests --- sim/deathknight/dps/TestUnholy.results | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim/deathknight/dps/TestUnholy.results b/sim/deathknight/dps/TestUnholy.results index fa35246995..62506e45e5 100644 --- a/sim/deathknight/dps/TestUnholy.results +++ b/sim/deathknight/dps/TestUnholy.results @@ -734,9 +734,9 @@ dps_results: { dps_results: { key: "TestUnholy-AllItems-SigilofVirulence-47673" value: { - dps: 8046.96581 - tps: 5128.16517 - hps: 266.55313 + dps: 8245.23172 + tps: 5205.95757 + hps: 238.66172 } } dps_results: {