From 5cb836fcca3340eeed061c9dc8b3a7cbef6d3510 Mon Sep 17 00:00:00 2001 From: cyzin Date: Mon, 13 Nov 2023 14:11:04 -0800 Subject: [PATCH] Configure Blood DK Normal and Threat Openers to conditionally skip using Blood Tap and the subsequent Icy Touch enabled by it. If the DK has the T10 4pc bonus enabled and does not schedule the first Blood Tap usage outside the time the opener uses Blood Tap + the Blood Tap Cooldown, the noted spells will not be used in either opener scenario. This change comes with a caveat of potentially delaying scheduled Blood Tap casts if the opener Blood Tap is delayed due to ability misses. --- sim/deathknight/tank/openers.go | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/sim/deathknight/tank/openers.go b/sim/deathknight/tank/openers.go index 541916c760..9496f9940b 100644 --- a/sim/deathknight/tank/openers.go +++ b/sim/deathknight/tank/openers.go @@ -1,12 +1,17 @@ package tank import ( + "github.com/wowsims/wotlk/sim/core/proto" + "slices" "time" "github.com/wowsims/wotlk/sim/core" "github.com/wowsims/wotlk/sim/deathknight" ) +const threatOpenerCastsBeforeBloodTap = 2 +const normalOpenerCastsBeforeBloodTap = 3 + func (dk *TankDeathknight) TankRA_BloodSpell(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration { casted := dk.BloodSpell.Cast(sim, target) advance := dk.LastOutcome.Matches(core.OutcomeLanded) @@ -33,7 +38,38 @@ func (dk *TankDeathknight) TankRA_IT(sim *core.Simulation, target *core.Unit, s return -1 } +func shouldUseBloodTapInOpener(dk *TankDeathknight) bool { + bloodTapDefensiveCd := dk.GetMajorCooldown(dk.BloodTap.ActionID) + if bloodTapDefensiveCd != nil { + timings := bloodTapDefensiveCd.GetTimings() + slices.Sort(timings) + if len(timings) == 0 || + (len(timings) > 0 && timings[0] < + dk.BloodTap.CD.Duration+getPlannedOpenerBloodTapUsageTime(dk)) { + return false + } else { + return true + } + } + return true +} + +func getPlannedOpenerBloodTapUsageTime(dk *TankDeathknight) time.Duration { + if dk.Rotation.Opener == proto.TankDeathknight_Rotation_Threat { + return threatOpenerCastsBeforeBloodTap * core.GCDDefault + } + return normalOpenerCastsBeforeBloodTap * core.GCDDefault +} + func (dk *TankDeathknight) setupTankRegularERWOpener() { + if shouldUseBloodTapInOpener(dk) { + dk.setupTankRegularERWOpenerWithBloodTap() + } else { + dk.setupTankRegularERWOpenerWithoutBloodTap() + } +} + +func (dk *TankDeathknight) setupTankRegularERWOpenerWithBloodTap() { dk.RotationSequence. NewAction(dk.TankRA_IT). NewAction(dk.RotationActionCallback_PS). @@ -50,7 +86,30 @@ func (dk *TankDeathknight) setupTankRegularERWOpener() { NewAction(dk.TankRA_FuSpell) } +func (dk *TankDeathknight) setupTankRegularERWOpenerWithoutBloodTap() { + dk.RotationSequence. + NewAction(dk.TankRA_IT). + NewAction(dk.RotationActionCallback_PS). + NewAction(dk.TankRA_FuSpell). + NewAction(dk.TankRA_BloodSpell). + NewAction(dk.RotationActionCallback_ERW). + NewAction(dk.RotationActionCallback_Pesti). + NewAction(dk.TankRA_IT). + NewAction(dk.TankRA_IT). + NewAction(dk.TankRA_IT). + NewAction(dk.RotationActionCallback_RD). + NewAction(dk.TankRA_FuSpell) +} + func (dk *TankDeathknight) setupTankThreatERWOpener() { + if shouldUseBloodTapInOpener(dk) { + dk.setupTankThreatERWOpenerWithBloodTap() + } else { + dk.setupTankThreatERWOpenerWithoutBloodTap() + } +} + +func (dk *TankDeathknight) setupTankThreatERWOpenerWithBloodTap() { dk.RotationSequence. NewAction(dk.TankRA_IT). NewAction(dk.TankRA_IT). @@ -63,3 +122,9 @@ func (dk *TankDeathknight) setupTankThreatERWOpener() { NewAction(dk.RotationActionCallback_PS). NewAction(dk.TankRA_BloodSpell) } + +// If maintaining GCD parity is desired, the threat opener without BT should cast DS with +// the GCD previously allocated to the Blood Tapped IT before using ERW +func (dk *TankDeathknight) setupTankThreatERWOpenerWithoutBloodTap() { + dk.setupTankRegularERWOpenerWithoutBloodTap() +}