local Tinkr, Bastion = ... local ProtPaladinModule = Bastion.Module:New('ProtectionPaladin') local Player = Bastion.UnitManager:Get('player') local Target = Bastion.UnitManager:Get('target') local SpellBook = Bastion.SpellBook:New() -- Spells local Consecration = SpellBook:GetSpell(26573) local ShieldOfTheRighteous = SpellBook:GetSpell(53600) local Judgment = SpellBook:GetSpell(275779) local HammerOfWrath = SpellBook:GetSpell(24275) local AvengersShield = SpellBook:GetSpell(31935) local BlessedHammer = SpellBook:GetSpell(204019) local WordOfGlory = SpellBook:GetSpell(85673) local AvengingWrath = SpellBook:GetSpell(31884) local DivineShield = SpellBook:GetSpell(642) local HandOfReckoning = SpellBook:GetSpell(62124) local EyeOfTyr = SpellBook:GetSpell(387174) local DivineSteed = SpellBook:GetSpell(190784) local Rebuke = SpellBook:GetSpell(96231) -- Buffs local ConsecrationBuff = SpellBook:GetSpell(188370) local ShiningLightBuff = SpellBook:GetSpell(327510) -- 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 -- Create APLs local DefaultAPL = Bastion.APL:New('default') local CooldownAPL = Bastion.APL:New('cooldown') local DefensiveAPL = Bastion.APL:New('defensive') local InterruptAPL = Bastion.APL:New('interrupt') local ThreatAPL = Bastion.APL:New('threat') -- Interrupt APL InterruptAPL:AddSpell( Rebuke:CastableIf(function(self) return self:IsKnownAndUsable() and Target:IsCasting() and Target:IsInterruptible() and Player:IsWithinCombatDistance(Target, 5) end):SetTarget(Target) ) InterruptAPL:AddSpell( AvengersShield:CastableIf(function(self) return self:IsKnownAndUsable() and Target:IsCasting() and Target:IsInterruptible() and Rebuke:GetCooldownRemaining() > 0 and Player:IsWithinCombatDistance(Target, 30) end):SetTarget(Target) ) -- Threat Management APL ThreatAPL:AddSpell( HandOfReckoning:CastableIf(function(self) return self:IsKnownAndUsable() and Target:GetDistance(Player) > 10 and Target:GetDistance(Player) <= 30 end):SetTarget(Target) ) ThreatAPL:AddSpell( AvengersShield:CastableIf(function(self) return self:IsKnownAndUsable() and GetEnemiesInRange(30) > 1 and Player:IsWithinCombatDistance(Target, 30) end):SetTarget(Target) ) -- Defensive APL DefensiveAPL:AddSpell( ShieldOfTheRighteous:CastableIf(function(self) return self:IsKnownAndUsable() and Player:GetPower() >= 3 and Player:GetHP() < 80 and Player:IsWithinCombatDistance(Target, 5) end):SetTarget(Player) ) DefensiveAPL:AddSpell( WordOfGlory:CastableIf(function(self) return self:IsKnownAndUsable() and (Player:GetPower() >= 3 or Player:GetAuras():FindMy(ShiningLightBuff):IsUp()) and Player:GetHP() < 50 end):SetTarget(Player) ) DefensiveAPL:AddSpell( EyeOfTyr:CastableIf(function(self) return self:IsKnownAndUsable() and Player:GetHP() < 60 and GetEnemiesInRange(8) >= 3 end):SetTarget(Player) ) -- Cooldown APL CooldownAPL:AddSpell( AvengingWrath:CastableIf(function(self) return self:IsKnownAndUsable() and GetEnemiesInRange(8) >= 3 end):SetTarget(Player) ) -- Default APL DefaultAPL:AddSpell( Consecration:CastableIf(function(self) return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(ConsecrationBuff):IsUp() and Player:IsWithinCombatDistance(Target, 8) end):SetTarget(Player) ) DefaultAPL:AddSpell( ShieldOfTheRighteous:CastableIf(function(self) return self:IsKnownAndUsable() and Player:GetPower() >= 3 and Player:IsWithinCombatDistance(Target, 5) end):SetTarget(Player) ) DefaultAPL:AddSpell( AvengersShield:CastableIf(function(self) return self:IsKnownAndUsable() and Player:IsWithinCombatDistance(Target, 30) end):SetTarget(Target) ) DefaultAPL:AddSpell( Judgment:CastableIf(function(self) return self:IsKnownAndUsable() and Player:IsWithinCombatDistance(Target, 30) end):SetTarget(Target) ) DefaultAPL:AddSpell( HammerOfWrath:CastableIf(function(self) return self:IsKnownAndUsable() and (Target:GetHP() <= 20 or Player:GetAuras():FindMy(AvengingWrath):IsUp()) and Player:IsWithinCombatDistance(Target, 30) end):SetTarget(Target) ) DefaultAPL:AddSpell( BlessedHammer:CastableIf(function(self) return self:IsKnownAndUsable() and Player:IsWithinCombatDistance(Target, 5) end):SetTarget(Target) ) DefaultAPL:AddSpell( Consecration:CastableIf(function(self) return self:IsKnownAndUsable() and Player:IsWithinCombatDistance(Target, 8) end):SetTarget(Player) ) -- Main rotation logic ProtPaladinModule: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 InterruptAPL:Execute() ThreatAPL:Execute() DefensiveAPL:Execute() CooldownAPL:Execute() DefaultAPL:Execute() end) Bastion:Register(ProtPaladinModule)