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. 307
      BloodDK.lua

@ -14,43 +14,87 @@ local DancingRuneWeapon = SpellBook:GetSpell(49028)
local Tombstone = SpellBook:GetSpell(219809)
local Bonestorm = SpellBook:GetSpell(194844)
local AbominationLimb = SpellBook:GetSpell(315443)
local EmpowerRuneWeapon = SpellBook:GetSpell(47568)
local DeathsCaress = SpellBook:GetSpell(195292)
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 GorefiendGrasp = SpellBook:GetSpell(108199)
local MindFreeze = SpellBook:GetSpell(47528)
local Asphyxiate = SpellBook:GetSpell(221562)
local RaiseAlly = SpellBook:GetSpell(61999)
local AntiMagicZone = SpellBook:GetSpell(51052)
local VampiricBlood = SpellBook:GetSpell(55233)
local DeathGrip = SpellBook:GetSpell(49576)
local GorefiendGrasp = SpellBook:GetSpell(108199)
-- Buffs
local BoneShield = SpellBook:GetSpell(195181)
local DancingRuneWeaponBuff = SpellBook:GetSpell(81256)
local DeathAndDecayBuff = SpellBook:GetSpell(188290)
local Coagulopathy = SpellBook:GetSpell(391481)
local IcyTalons = SpellBook:GetSpell(194879)
-- Debuffs
local BloodPlague = SpellBook:GetSpell(55078)
-- Helper Functions
local function GetEnemiesInRange(range)
local count = 0
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:GetDistance(Player) <= range then
if unit:GetDistance(Player) <= range and unit:IsAffectingCombat() then
count = count + 1
end
end)
return count
end
local function HasEnoughRunicPower(cost)
return Player:GetPower() >= cost
local function GetLowestThreatEnemy()
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
local function ShouldUseDeathStrike()
local hp = Player:GetHP()
local runicPower = Player:GetPower()
return hp <= 70 or runicPower > 110 or (hp <= 85 and runicPower >= 45)
end
local function GetBestDeathAndDecayLocation()
return Bastion.UnitManager:FindEnemiesCentroid(10, 30)
end
return hp <= 70 or runicPower > 110 or (hp <= 85 and HasEnoughRunicPower(45))
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
-- Create APLs
@ -58,20 +102,28 @@ local DefaultAPL = Bastion.APL:New('default')
local CooldownAPL = Bastion.APL:New('cooldown')
local DefensiveAPL = Bastion.APL:New('defensive')
local UtilityAPL = Bastion.APL:New('utility')
local ThreatAPL = Bastion.APL:New('threat')
-- Utility APL
UtilityAPL:AddSpell(
BlindingSleet:CastableIf(function(self)
return self:IsKnownAndUsable() and GetEnemiesInRange(12) >= 3
end):SetTarget(Player)
-- Threat Management APL
ThreatAPL:AddSpell(
DeathsCaress:CastableIf(function(self)
return self:IsKnownAndUsable() and Target:GetDistance(Player) > 10
end):SetTarget(Target)
)
UtilityAPL:AddSpell(
GorefiendGrasp:CastableIf(function(self)
return self:IsKnownAndUsable() and GetEnemiesInRange(15) >= 3
ThreatAPL:AddSpell(
DeathGrip:CastableIf(function(self)
return self:IsKnownAndUsable() and Target:GetDistance(Player) > 10
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(
MindFreeze:CastableIf(function(self)
return self:IsKnownAndUsable() and Target:IsCasting() and Target:IsInterruptible()
@ -84,6 +136,12 @@ UtilityAPL:AddSpell(
end):SetTarget(Target)
)
UtilityAPL:AddSpell(
GorefiendGrasp:CastableIf(function(self)
return self:IsKnownAndUsable() and GetEnemiesInRange(15) >= 3
end):SetTarget(Target)
)
-- Cooldown APL
CooldownAPL:AddSpell(
DancingRuneWeapon:CastableIf(function(self)
@ -98,40 +156,51 @@ 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()
end):SetTarget(Player)
)
-- Defensive APL
DefensiveAPL:AddSpell(
AntiMagicShell:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 70
VampiricBlood:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 60
end):SetTarget(Player)
)
DefensiveAPL:AddSpell(
AntiMagicZone:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 50
IceboundFortitude:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 40
end):SetTarget(Player)
)
DefensiveAPL:AddSpell(
VampiricBlood:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 60
AntiMagicShell:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetHP() <= 70
end):SetTarget(Player)
)
-- Default APL
DefaultAPL:AddSpell(
DeathStrike:CastableIf(function(self)
return self:IsKnownAndUsable() and ShouldUseDeathStrike()
end):SetTarget(Target)
DeathAndDecay:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp() and GetEnemiesInRange(10) > 0
end):SetTarget(Player):OnCast(function(self)
local loc = GetBestDeathAndDecayLocation()
if loc then
self:Click(loc)
end
end)
)
DefaultAPL:AddSpell(
Marrowrend:CastableIf(function(self)
local boneShieldCount = Player:GetAuras():FindMy(BoneShield):GetCount()
local boneShieldCount = GetBoneShieldStacks()
return self:IsKnownAndUsable() and
(boneShieldCount <= 6 or
(boneShieldCount <= 7 and Player:GetAuras():FindMy(BoneShield):GetRemainingTime() < 3))
@ -140,37 +209,34 @@ DefaultAPL:AddSpell(
DefaultAPL:AddSpell(
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 Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp()
and DancingRuneWeapon:GetCooldownRemaining() > 0
and DancingRuneWeapon:GetCooldownRemaining() > 25
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
Bonestorm:CastableIf(function(self)
return self:IsKnownAndUsable() and Player:GetAuras():FindMy(BoneShield):GetCount() > 11
and not Player:GetAuras():FindMy(DancingRuneWeaponBuff):IsUp()
and Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp()
and HasEnoughRunicPower(100)
and DancingRuneWeapon:GetCooldownRemaining() > 0
end):SetTarget(Player)
BloodBoil:CastableIf(function(self)
return self:IsKnownAndUsable() and self:GetCharges() > 1
end):SetTarget(Target)
)
DefaultAPL:AddSpell(
DeathAndDecay:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp()
end):SetTarget(Player):OnCast(function(self)
local loc = Bastion.UnitManager:FindEnemiesCentroid(10, 30)
if loc then
self:Click(loc)
end
end)
DeathStrike:CastableIf(function(self)
return self:IsKnownAndUsable() and ShouldUseDeathStrike()
end):SetTarget(Target)
)
DefaultAPL:AddSpell(
BloodBoil:CastableIf(function(self)
return self:IsKnownAndUsable() and self:GetCharges() > 1
Consumption:CastableIf(function(self)
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)
)
@ -180,113 +246,62 @@ DefaultAPL:AddSpell(
end):SetTarget(Target)
)
-- Opener sequence
local OpenerSequence = Bastion.Sequencer:New({
function(self)
if DeathAndDecay:IsKnownAndUsable() then
DeathAndDecay:Cast(Player):OnCast(function(self)
local loc = Bastion.UnitManager:FindEnemiesCentroid(10, 30)
if loc then
self:Click(loc)
-- Main rotation logic
BloodDKModule:Sync(function()
-- Check if the player is mounted
if IsMounted() or Player:IsMounted() then
return -- Exit the function if mounted, pausing the rotation
end
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)
return true
end
return false
end,
function(self)
if DeathsCaress:IsKnownAndUsable() then
DeathsCaress:Cast(Target)
return true
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
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()
UtilityAPL: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()
end)

Loading…
Cancel
Save