|
|
|
local Tinkr, Bastion = ...
|
|
|
|
local BloodDKModule = Bastion.Module:New('BloodDeathKnight')
|
|
|
|
local Player = Bastion.UnitManager:Get('player')
|
|
|
|
local Target = Bastion.UnitManager:Get('target')
|
|
|
|
local SpellBook = Bastion.SpellBook:New()
|
|
|
|
|
|
|
|
-- Spells
|
|
|
|
local DeathStrike = SpellBook:GetSpell(49998)
|
|
|
|
local Marrowrend = SpellBook:GetSpell(195182)
|
|
|
|
local HeartStrike = SpellBook:GetSpell(206930)
|
|
|
|
local BloodBoil = SpellBook:GetSpell(50842)
|
|
|
|
local DeathAndDecay = SpellBook:GetSpell(43265)
|
|
|
|
local DancingRuneWeapon = SpellBook:GetSpell(49028)
|
|
|
|
local Tombstone = SpellBook:GetSpell(219809)
|
|
|
|
local Bonestorm = SpellBook:GetSpell(194844)
|
|
|
|
local AbominationLimb = SpellBook:GetSpell(315443)
|
|
|
|
local DeathsCaress = SpellBook:GetSpell(195292)
|
|
|
|
local RaiseDead = SpellBook:GetSpell(46585)
|
|
|
|
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 MindFreeze = SpellBook:GetSpell(47528)
|
|
|
|
local Asphyxiate = SpellBook:GetSpell(221562)
|
|
|
|
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 and unit:IsAffectingCombat() then
|
|
|
|
count = count + 1
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
return count
|
|
|
|
end
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
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')
|
|
|
|
|
|
|
|
-- Threat Management APL
|
|
|
|
ThreatAPL:AddSpell(
|
|
|
|
DeathsCaress:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable() and Target:GetDistance(Player) > 10
|
|
|
|
end):SetTarget(Target)
|
|
|
|
)
|
|
|
|
|
|
|
|
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()
|
|
|
|
end):SetTarget(Target)
|
|
|
|
)
|
|
|
|
|
|
|
|
UtilityAPL:AddSpell(
|
|
|
|
Asphyxiate:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable() and not Target:IsStunned()
|
|
|
|
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)
|
|
|
|
return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(DancingRuneWeaponBuff):IsUp()
|
|
|
|
end):SetTarget(Player)
|
|
|
|
)
|
|
|
|
|
|
|
|
CooldownAPL:AddSpell(
|
|
|
|
AbominationLimb:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable()
|
|
|
|
end):SetTarget(Player)
|
|
|
|
)
|
|
|
|
|
|
|
|
CooldownAPL:AddSpell(
|
|
|
|
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(
|
|
|
|
VampiricBlood:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable() and Player:GetHP() <= 60
|
|
|
|
end):SetTarget(Player)
|
|
|
|
)
|
|
|
|
|
|
|
|
DefensiveAPL:AddSpell(
|
|
|
|
IceboundFortitude:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable() and Player:GetHP() <= 40
|
|
|
|
end):SetTarget(Player)
|
|
|
|
)
|
|
|
|
|
|
|
|
DefensiveAPL:AddSpell(
|
|
|
|
AntiMagicShell:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable() and Player:GetHP() <= 70
|
|
|
|
end):SetTarget(Player)
|
|
|
|
)
|
|
|
|
|
|
|
|
-- Default APL
|
|
|
|
DefaultAPL:AddSpell(
|
|
|
|
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 = GetBoneShieldStacks()
|
|
|
|
return self:IsKnownAndUsable() and
|
|
|
|
(boneShieldCount <= 6 or
|
|
|
|
(boneShieldCount <= 7 and Player:GetAuras():FindMy(BoneShield):GetRemainingTime() < 3))
|
|
|
|
end):SetTarget(Target)
|
|
|
|
)
|
|
|
|
|
|
|
|
DefaultAPL:AddSpell(
|
|
|
|
Tombstone:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable() and GetBoneShieldStacks() >= 6
|
|
|
|
and not Player:GetAuras():FindMy(DancingRuneWeaponBuff):IsUp()
|
|
|
|
and Player:GetAuras():FindMy(DeathAndDecayBuff):IsUp()
|
|
|
|
and DancingRuneWeapon:GetCooldownRemaining() > 25
|
|
|
|
end):SetTarget(Player)
|
|
|
|
)
|
|
|
|
|
|
|
|
DefaultAPL:AddSpell(
|
|
|
|
BloodBoil:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable() and self:GetCharges() > 1
|
|
|
|
end):SetTarget(Target)
|
|
|
|
)
|
|
|
|
|
|
|
|
DefaultAPL:AddSpell(
|
|
|
|
DeathStrike:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable() and ShouldUseDeathStrike()
|
|
|
|
end):SetTarget(Target)
|
|
|
|
)
|
|
|
|
|
|
|
|
DefaultAPL:AddSpell(
|
|
|
|
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)
|
|
|
|
)
|
|
|
|
|
|
|
|
DefaultAPL:AddSpell(
|
|
|
|
HeartStrike:CastableIf(function(self)
|
|
|
|
return self:IsKnownAndUsable()
|
|
|
|
end):SetTarget(Target)
|
|
|
|
)
|
|
|
|
|
|
|
|
-- 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)
|
|
|
|
end
|
|
|
|
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)
|
|
|
|
|
|
|
|
Bastion:Register(BloodDKModule)
|