Blood Death Knight Rotation Improvements

Changes:
- Improved dynamic target selection for better threat management
- Added AoE threat management logic
- Implemented smarter use of defensive cooldowns
- Optimized ability usage based on current combat situation

New Features:
- Added GetAvailableRunes() function for more accurate rune management
- Implemented GetLowestThreatEnemy() function for improved tanking
- Added ShouldUseBonestorm() function for optimal Bonestorm usage

Optimizations:
- Improved Bone Shield management logic
- Enhanced Death Strike usage based on health and Runic Power
- Optimized Death and Decay placement using GetBestDeathAndDecayLocation()

Bug Fixes:
- Fixed issues with target selection and spell casting
- Resolved errors related to unit indexing in spell casts

Miscellaneous:
- Added mount detection to pause rotation while mounted
- Improved code structure and readability
- Enhanced comments for better code understanding

This update significantly improves the Blood Death Knight rotation,
focusing on better threat management, defensive play, and overall
performance in dungeon environments.
main
Emlembow 10 months ago
parent dd09c31dfb
commit deb58bc7a7
  1. 309
      BloodDK.lua

@ -14,43 +14,87 @@ local DancingRuneWeapon = SpellBook:GetSpell(49028)
local Tombstone = SpellBook:GetSpell(219809) local Tombstone = SpellBook:GetSpell(219809)
local Bonestorm = SpellBook:GetSpell(194844) local Bonestorm = SpellBook:GetSpell(194844)
local AbominationLimb = SpellBook:GetSpell(315443) local AbominationLimb = SpellBook:GetSpell(315443)
local EmpowerRuneWeapon = SpellBook:GetSpell(47568)
local DeathsCaress = SpellBook:GetSpell(195292) local DeathsCaress = SpellBook:GetSpell(195292)
local RaiseDead = SpellBook:GetSpell(46585) local RaiseDead = SpellBook:GetSpell(46585)
local BlindingSleet = SpellBook:GetSpell(207167) local Consumption = SpellBook:GetSpell(274156)
local SoulReaper = SpellBook:GetSpell(343294)
local VampiricBlood = SpellBook:GetSpell(55233)
local IceboundFortitude = SpellBook:GetSpell(48792)
local AntiMagicShell = SpellBook:GetSpell(48707) local AntiMagicShell = SpellBook:GetSpell(48707)
local GorefiendGrasp = SpellBook:GetSpell(108199)
local MindFreeze = SpellBook:GetSpell(47528) local MindFreeze = SpellBook:GetSpell(47528)
local Asphyxiate = SpellBook:GetSpell(221562) local Asphyxiate = SpellBook:GetSpell(221562)
local RaiseAlly = SpellBook:GetSpell(61999) local DeathGrip = SpellBook:GetSpell(49576)
local AntiMagicZone = SpellBook:GetSpell(51052) local GorefiendGrasp = SpellBook:GetSpell(108199)
local VampiricBlood = SpellBook:GetSpell(55233)
-- Buffs -- Buffs
local BoneShield = SpellBook:GetSpell(195181) local BoneShield = SpellBook:GetSpell(195181)
local DancingRuneWeaponBuff = SpellBook:GetSpell(81256) local DancingRuneWeaponBuff = SpellBook:GetSpell(81256)
local DeathAndDecayBuff = SpellBook:GetSpell(188290) local DeathAndDecayBuff = SpellBook:GetSpell(188290)
local Coagulopathy = SpellBook:GetSpell(391481)
local IcyTalons = SpellBook:GetSpell(194879)
-- Debuffs
local BloodPlague = SpellBook:GetSpell(55078)
-- Helper Functions -- Helper Functions
local function GetEnemiesInRange(range) local function GetEnemiesInRange(range)
local count = 0 local count = 0
Bastion.UnitManager:EnumEnemies(function(unit) Bastion.UnitManager:EnumEnemies(function(unit)
if unit:GetDistance(Player) <= range then if unit:GetDistance(Player) <= range and unit:IsAffectingCombat() then
count = count + 1 count = count + 1
end end
end) end)
return count return count
end end
local function HasEnoughRunicPower(cost) local function GetLowestThreatEnemy()
return Player:GetPower() >= cost local lowestThreatUnit = nil
local lowestThreat = math.huge
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsAffectingCombat() and unit:GetDistance(Player) <= 30 then
local _, _, scaledPercent = UnitDetailedThreatSituation(Player:GetOMToken(), unit:GetOMToken())
if scaledPercent and scaledPercent < lowestThreat then
lowestThreatUnit = unit
lowestThreat = scaledPercent
end
end
end)
return lowestThreatUnit
end
local function GetBoneShieldStacks()
return Player:GetAuras():FindMy(BoneShield):GetCount()
end end
local function ShouldUseDeathStrike() local function ShouldUseDeathStrike()
local hp = Player:GetHP() local hp = Player:GetHP()
local runicPower = Player:GetPower() local runicPower = Player:GetPower()
return hp <= 70 or runicPower > 110 or (hp <= 85 and runicPower >= 45)
return hp <= 70 or runicPower > 110 or (hp <= 85 and HasEnoughRunicPower(45)) end
local function GetBestDeathAndDecayLocation()
return Bastion.UnitManager:FindEnemiesCentroid(10, 30)
end
local function ShouldUseBonestorm()
return GetBoneShieldStacks() > 10 and
not Player:GetAuras():FindMy(DancingRuneWeaponBuff):IsUp() and
Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp() and
Player:GetPower() >= 100 and
DancingRuneWeapon:GetCooldownRemaining() > 0
end
local function GetAvailableRunes()
local count = 0
for i = 1, 6 do
local start, duration, runeReady = GetRuneCooldown(i)
if runeReady then
count = count + 1
end
end
return count
end end
-- Create APLs -- Create APLs
@ -58,20 +102,28 @@ local DefaultAPL = Bastion.APL:New('default')
local CooldownAPL = Bastion.APL:New('cooldown') local CooldownAPL = Bastion.APL:New('cooldown')
local DefensiveAPL = Bastion.APL:New('defensive') local DefensiveAPL = Bastion.APL:New('defensive')
local UtilityAPL = Bastion.APL:New('utility') local UtilityAPL = Bastion.APL:New('utility')
local ThreatAPL = Bastion.APL:New('threat')
-- Utility APL -- Threat Management APL
UtilityAPL:AddSpell( ThreatAPL:AddSpell(
BlindingSleet:CastableIf(function(self) DeathsCaress:CastableIf(function(self)
return self:IsKnownAndUsable() and GetEnemiesInRange(12) >= 3 return self:IsKnownAndUsable() and Target:GetDistance(Player) > 10
end):SetTarget(Player) end):SetTarget(Target)
) )
UtilityAPL:AddSpell( ThreatAPL:AddSpell(
GorefiendGrasp:CastableIf(function(self) DeathGrip:CastableIf(function(self)
return self:IsKnownAndUsable() and GetEnemiesInRange(15) >= 3 return self:IsKnownAndUsable() and Target:GetDistance(Player) > 10
end):SetTarget(Target) end):SetTarget(Target)
) )
ThreatAPL:AddSpell(
BloodBoil:CastableIf(function(self)
return self:IsKnownAndUsable() and self:GetCharges() > 0 and GetEnemiesInRange(10) > 1
end):SetTarget(Player)
)
-- Utility APL
UtilityAPL:AddSpell( UtilityAPL:AddSpell(
MindFreeze:CastableIf(function(self) MindFreeze:CastableIf(function(self)
return self:IsKnownAndUsable() and Target:IsCasting() and Target:IsInterruptible() return self:IsKnownAndUsable() and Target:IsCasting() and Target:IsInterruptible()
@ -84,6 +136,12 @@ UtilityAPL:AddSpell(
end):SetTarget(Target) end):SetTarget(Target)
) )
UtilityAPL:AddSpell(
GorefiendGrasp:CastableIf(function(self)
return self:IsKnownAndUsable() and GetEnemiesInRange(15) >= 3
end):SetTarget(Target)
)
-- Cooldown APL -- Cooldown APL
CooldownAPL:AddSpell( CooldownAPL:AddSpell(
DancingRuneWeapon:CastableIf(function(self) DancingRuneWeapon:CastableIf(function(self)
@ -98,40 +156,51 @@ CooldownAPL:AddSpell(
) )
CooldownAPL:AddSpell( CooldownAPL:AddSpell(
EmpowerRuneWeapon:CastableIf(function(self) Bonestorm:CastableIf(function(self)
return self:IsKnownAndUsable() and ShouldUseBonestorm()
end):SetTarget(Player)
)
CooldownAPL:AddSpell(
RaiseDead:CastableIf(function(self)
return self:IsKnownAndUsable() return self:IsKnownAndUsable()
end):SetTarget(Player) end):SetTarget(Player)
) )
-- Defensive APL -- Defensive APL
DefensiveAPL:AddSpell( DefensiveAPL:AddSpell(
AntiMagicShell:CastableIf(function(self) VampiricBlood:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 70 return self:IsKnownAndUsable() and Player:GetHP() <= 60
end):SetTarget(Player) end):SetTarget(Player)
) )
DefensiveAPL:AddSpell( DefensiveAPL:AddSpell(
AntiMagicZone:CastableIf(function(self) IceboundFortitude:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 50 return self:IsKnownAndUsable() and Player:GetHP() <= 40
end):SetTarget(Player) end):SetTarget(Player)
) )
DefensiveAPL:AddSpell( DefensiveAPL:AddSpell(
VampiricBlood:CastableIf(function(self) AntiMagicShell:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 60 return self:IsKnownAndUsable() and Player:GetHP() <= 70
end):SetTarget(Player) end):SetTarget(Player)
) )
-- Default APL -- Default APL
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
DeathStrike:CastableIf(function(self) DeathAndDecay:CastableIf(function(self)
return self:IsKnownAndUsable() and ShouldUseDeathStrike() return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp() and GetEnemiesInRange(10) > 0
end):SetTarget(Target) end):SetTarget(Player):OnCast(function(self)
local loc = GetBestDeathAndDecayLocation()
if loc then
self:Click(loc)
end
end)
) )
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
Marrowrend:CastableIf(function(self) Marrowrend:CastableIf(function(self)
local boneShieldCount = Player:GetAuras():FindMy(BoneShield):GetCount() local boneShieldCount = GetBoneShieldStacks()
return self:IsKnownAndUsable() and return self:IsKnownAndUsable() and
(boneShieldCount <= 6 or (boneShieldCount <= 6 or
(boneShieldCount <= 7 and Player:GetAuras():FindMy(BoneShield):GetRemainingTime() < 3)) (boneShieldCount <= 7 and Player:GetAuras():FindMy(BoneShield):GetRemainingTime() < 3))
@ -140,37 +209,34 @@ DefaultAPL:AddSpell(
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
Tombstone:CastableIf(function(self) Tombstone:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetAuras():FindMy(BoneShield):GetCount() > 6 return self:IsKnownAndUsable() and GetBoneShieldStacks() >= 6
and not Player:GetAuras():FindMy(DancingRuneWeaponBuff):IsUp() and not Player:GetAuras():FindMy(DancingRuneWeaponBuff):IsUp()
and Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp() and Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp()
and DancingRuneWeapon:GetCooldownRemaining() > 0 and DancingRuneWeapon:GetCooldownRemaining() > 25
end):SetTarget(Player) end):SetTarget(Player)
) )
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
Bonestorm:CastableIf(function(self) BloodBoil:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetAuras():FindMy(BoneShield):GetCount() > 11 return self:IsKnownAndUsable() and self:GetCharges() > 1
and not Player:GetAuras():FindMy(DancingRuneWeaponBuff):IsUp() end):SetTarget(Target)
and Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp()
and HasEnoughRunicPower(100)
and DancingRuneWeapon:GetCooldownRemaining() > 0
end):SetTarget(Player)
) )
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
DeathAndDecay:CastableIf(function(self) DeathStrike:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp() return self:IsKnownAndUsable() and ShouldUseDeathStrike()
end):SetTarget(Player):OnCast(function(self) end):SetTarget(Target)
local loc = Bastion.UnitManager:FindEnemiesCentroid(10, 30)
if loc then
self:Click(loc)
end
end)
) )
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
BloodBoil:CastableIf(function(self) Consumption:CastableIf(function(self)
return self:IsKnownAndUsable() and self:GetCharges() > 1 return self:IsKnownAndUsable() and GetAvailableRunes() >= 2
end):SetTarget(Target)
)
DefaultAPL:AddSpell(
SoulReaper:CastableIf(function(self)
return self:IsKnownAndUsable() and Target:GetHP() <= 35
end):SetTarget(Target) end):SetTarget(Target)
) )
@ -180,113 +246,62 @@ DefaultAPL:AddSpell(
end):SetTarget(Target) end):SetTarget(Target)
) )
-- Opener sequence -- Main rotation logic
local OpenerSequence = Bastion.Sequencer:New({ BloodDKModule:Sync(function()
function(self) -- Check if the player is mounted
if DeathAndDecay:IsKnownAndUsable() then if IsMounted() or Player:IsMounted() then
DeathAndDecay:Cast(Player):OnCast(function(self) return -- Exit the function if mounted, pausing the rotation
local loc = Bastion.UnitManager:FindEnemiesCentroid(10, 30) end
if loc then
self:Click(loc) if not Player:IsAffectingCombat() then
if not Target:Exists() or Target:IsDead() then
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsAlive() and unit:GetDistance(Player) <= 30 then
Target = unit
return true
end end
end) end)
return true
end
return false
end,
function(self)
if DeathsCaress:IsKnownAndUsable() then
DeathsCaress:Cast(Target)
return true
end end
return false
end,
function(self)
if RaiseDead:IsKnownAndUsable() then
RaiseDead:Cast(Player)
return true
end
return false
end,
function(self)
if DancingRuneWeapon:IsKnownAndUsable() then
DancingRuneWeapon:Cast(Player)
return true
end
return false
end,
function(self)
if BloodBoil:IsKnownAndUsable() then
BloodBoil:Cast(Target)
return true
end
return false
end,
function(self)
if Tombstone:IsKnownAndUsable() then
Tombstone:Cast(Player)
return true
end
return false
end,
function(self)
if HeartStrike:IsKnownAndUsable() then
HeartStrike:Cast(Target)
return true
end
return false
end,
function(self)
if DeathStrike:IsKnownAndUsable() and ShouldUseDeathStrike() then
DeathStrike:Cast(Target)
return true
end
return false
end,
function(self)
if HeartStrike:IsKnownAndUsable() then
HeartStrike:Cast(Target)
return true
end
return false
end,
function(self)
if BloodBoil:IsKnownAndUsable() then
BloodBoil:Cast(Target)
return true
end
return false
end,
function(self)
if DeathStrike:IsKnownAndUsable() and ShouldUseDeathStrike() then
DeathStrike:Cast(Target)
return true
end
return false
end,
function(self)
if Marrowrend:IsKnownAndUsable() then
Marrowrend:Cast(Target)
return true
end
return false
end,
}, function()
return not Player:IsAffectingCombat()
end)
DefaultAPL:AddSequence(OpenerSequence, function()
return not Player:IsAffectingCombat()
end)
BloodDKModule:Sync(function()
if not Player:IsAffectingCombat() then
return return
end end
-- Dynamic target selection and threat management
local lowestThreatEnemy = GetLowestThreatEnemy()
if lowestThreatEnemy then
Target = lowestThreatEnemy
elseif not Target:Exists() or Target:IsDead() or Target:GetDistance(Player) > 30 then
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsAlive() and unit:GetDistance(Player) <= 30 and unit:IsAffectingCombat() then
Target = unit
return true
end
end)
end
ThreatAPL:Execute()
DefensiveAPL:Execute() DefensiveAPL:Execute()
UtilityAPL:Execute() UtilityAPL:Execute()
CooldownAPL:Execute() CooldownAPL:Execute()
-- AoE threat management
if GetEnemiesInRange(10) > 1 and BloodBoil:IsKnownAndUsable() and BloodBoil:GetCharges() > 0 then
BloodBoil:Cast(Player)
end
-- Single target threat management
if Target:Exists() and not Target:GetAuras():FindMy(BloodPlague):IsUp() then
if Target:GetDistance(Player) <= 10 then
BloodBoil:Cast(Target)
elseif DeathsCaress:IsKnownAndUsable() then
DeathsCaress:Cast(Target)
end
end
-- Maintain Coagulopathy and Icy Talons
if Player:GetAuras():FindMy(Coagulopathy):GetRemainingTime() < 2 or Player:GetAuras():FindMy(IcyTalons):GetRemainingTime() < 2 then
DeathStrike:Cast(Target)
end
DefaultAPL:Execute() DefaultAPL:Execute()
end) end)

Loading…
Cancel
Save