local Tinkr, Bastion = ... local HolyPaladinModule = Bastion.Module:New('HolyPaladin') local Player = Bastion.UnitManager:Get('player') local Target = Bastion.UnitManager:Get('target') local MELEE_RANGE = 5 -- Adjust this value based on your class's melee range -- Initialize SpellBook local SpellBook = Bastion.SpellBook:New() -- Power type constant local HOLY_POWER = Enum.PowerType.HolyPower -- Healing threshold variable local healingThreshold = 90 -- Spells local AutoAttack = SpellBook:GetSpell(6603) local BeaconOfVirtue = SpellBook:GetSpell(200025) local BlessingOfSummer = SpellBook:GetSpell(388007) local SacredWeapon = SpellBook:GetSpell(383395) local Consecration = SpellBook:GetSpell(26573) local Judgment = SpellBook:GetSpell(275773) local HolyPrism = SpellBook:GetSpell(114165) local WordOfGlory = SpellBook:GetSpell(85673) local ShieldOfTheRighteous = SpellBook:GetSpell(53600) local HolyShock = SpellBook:GetSpell(20473) local HolyLight = SpellBook:GetSpell(82326) local CrusaderStrike = SpellBook:GetSpell(35395) local HammerOfWrath = SpellBook:GetSpell(24275) local FlashOfLight = SpellBook:GetSpell(19750) local LightOfDawn = SpellBook:GetSpell(85222) local DivineShield = SpellBook:GetSpell(642) local LayOnHands = SpellBook:GetSpell(633) local BlessingOfProtection = SpellBook:GetSpell(1022) local BlessingOfSacrifice = SpellBook:GetSpell(6940) local AvengingWrath = SpellBook:GetSpell(31884) -- Buffs local DivineFavorBuff = SpellBook:GetSpell(388039) local InfusionOfLightBuff = SpellBook:GetSpell(54149) local ConsecrationBuff = SpellBook:GetSpell(188370) -- Custom Units local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit) local lowest = nil local lowestHP = math.huge Bastion.UnitManager:EnumFriends(function(unit) if unit:IsDead() or Player:GetDistance(unit) > 40 or not Player:CanSee(unit) then return false end local hp = unit:GetHP() if hp < lowestHP then lowest = unit lowestHP = hp end end) return lowest or Player end) local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) local tank = nil Bastion.UnitManager:EnumFriends(function(unit) if Player:GetDistance(unit) > 40 or not Player:CanSee(unit) or unit:IsDead() then return false end if unit:IsTank() then tank = unit return true end end) return tank or Player end) local BestTarget = Bastion.UnitManager:CreateCustomUnit('besttarget', function() local bestMeleeTarget = nil local bestRangedTarget = nil local highestMeleeHealth = 0 local highestRangedHealth = 0 Bastion.UnitManager:EnumEnemies(function(unit) if unit:IsAffectingCombat() and unit:GetDistance(Player) <= 40 and Player:CanSee(unit) then local health = unit:GetHealth() if unit:GetDistance(Player) <= MELEE_RANGE then -- Melee target if health > highestMeleeHealth then highestMeleeHealth = health bestMeleeTarget = unit end else -- Ranged target if health > highestRangedHealth then highestRangedHealth = health bestRangedTarget = unit end end end end) -- Return the best melee target if it exists, otherwise return the best ranged target return bestMeleeTarget or bestRangedTarget or Target end) local ExecuteTarget = Bastion.UnitManager:CreateCustomUnit('executetarget', function() local target = nil Bastion.UnitManager:EnumEnemies(function(unit) if unit:IsAffectingCombat() and unit:GetDistance(Player) <= 30 and Player:CanSee(unit) and unit:GetHP() < 20 then target = unit return true end end) return target or Bastion.UnitManager:Get('none') end) -- Custom Functions local function IsInMeleeRange(unit) return Player:GetDistance(unit) <= MELEE_RANGE end -- Create APLs local DefaultAPL = Bastion.APL:New('default') local HealingAPL = Bastion.APL:New('healing') local DamageAPL = Bastion.APL:New('damage') local CooldownAPL = Bastion.APL:New('cooldown') local DefensiveAPL = Bastion.APL:New('defensive') local ExecuteAPL = Bastion.APL:New('execute') local DamagePriorityAPL = Bastion.APL:New('damagepriority') -- Utility function to check if healing is needed local function HealingNeeded() return Lowest:GetHP() < healingThreshold end -- Utility function to check if emergency healing is needed local function EmergencyHealingNeeded() return Lowest:GetHP() < 50 end -- Utility function to check if Beacon of Virtue should be cast local function ShouldCastBeaconOfVirtue() local lowHealthCount = 0 local totalPartyMembers = 0 Bastion.UnitManager:EnumFriends(function(unit) if unit:GetDistance(Player) <= 40 and Player:CanSee(unit) then totalPartyMembers = totalPartyMembers + 1 if unit:GetHP() < 80 then lowHealthCount = lowHealthCount + 1 end end end) return lowHealthCount >= math.min(3, math.ceil(totalPartyMembers * 0.6)) end -- Utility function to check if anyone in the party needs healing local function AnyoneNeedsHealing() local needsHealing = false Bastion.UnitManager:EnumFriends(function(unit) if unit:GetHP() < healingThreshold then needsHealing = true return true end end) return needsHealing end -- Variables for auto-attack tracking local lastAutoAttackTime = 0 -- Event handler for auto-attack tracking Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function() local _, subEvent, _, sourceGUID, _, _, _, _, _, _, _, spellID = CombatLogGetCurrentEventInfo() if sourceGUID == Player:GetGUID() and (subEvent == "SWING_DAMAGE" or subEvent == "SWING_MISSED") then lastAutoAttackTime = GetTime() end end) -- Function to check if auto-attacking local function IsAutoAttacking() return GetTime() - lastAutoAttackTime < 2 end -- Function to check if we're in an aggressive damage phase local function AggressiveDamagePhase() return Player:GetAuras():FindMy(AvengingWrath):IsUp() or Player:GetPower(HOLY_POWER) >= 4 or (Player:GetPartyHPAround(40, 90) == 0 and Player:GetHP() > 80) end -- Healing APL HealingAPL:AddSpell( BeaconOfVirtue:CastableIf(function(self) return self:IsKnownAndUsable() and ShouldCastBeaconOfVirtue() and Player:GetPower(HOLY_POWER) >= 3 and not Player:IsCastingOrChanneling() end):SetTarget(Lowest):OnCast(function() WordOfGlory:ForceCast(Lowest) end) ) HealingAPL:AddSpell( HolyPrism:CastableIf(function(self) return self:IsKnownAndUsable() and HealingNeeded() end):SetTarget(Lowest) ) HealingAPL:AddSpell( WordOfGlory:CastableIf(function(self) return self:IsKnownAndUsable() and Player:GetPower(HOLY_POWER) >= 3 and HealingNeeded() end):SetTarget(Lowest) ) HealingAPL:AddSpell( HolyShock:CastableIf(function(self) return self:IsKnownAndUsable() and HealingNeeded() end):SetTarget(Lowest) ) HealingAPL:AddSpell( LightOfDawn:CastableIf(function(self) return self:IsKnownAndUsable() and Player:GetPower(HOLY_POWER) >= 3 and Player:GetPartyHPAround(15, healingThreshold) >= 3 end):SetTarget(Player) ) HealingAPL:AddSpell( FlashOfLight:CastableIf(function(self) return self:IsKnownAndUsable() and (Player:GetAuras():FindMy(InfusionOfLightBuff):IsUp() or EmergencyHealingNeeded()) end):SetTarget(Lowest) ) HealingAPL:AddSpell( HolyLight:CastableIf(function(self) return self:IsKnownAndUsable() and Player:GetAuras():FindMy(DivineFavorBuff):IsUp() and HealingNeeded() end):SetTarget(Lowest) ) -- Damage APL DamageAPL:AddSpell( AutoAttack:CastableIf(function(self) return BestTarget:Exists() and not IsAutoAttacking() and Player:CanSee(BestTarget) and Player:IsWithinCombatDistance(BestTarget, 5) end):SetTarget(BestTarget):OnCast(function() lastAutoAttackTime = GetTime() end) ) DamageAPL:AddSpell( Judgment:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and Player:IsWithinCombatDistance(BestTarget, 30) and not Player:IsCastingOrChanneling() end):SetTarget(BestTarget) ) DamageAPL:AddSpell( HolyPrism:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and Player:IsWithinCombatDistance(BestTarget, 30) and not Player:IsCastingOrChanneling() end):SetTarget(BestTarget) ) DamageAPL:AddSpell( CrusaderStrike:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and IsInMeleeRange(BestTarget) and not Player:IsCastingOrChanneling() end):SetTarget(BestTarget) ) -- Execute APL ExecuteAPL:AddSpell( HammerOfWrath:CastableIf(function(self) return ExecuteTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() end):SetTarget(ExecuteTarget) ) -- Cooldown APL CooldownAPL:AddSpell( AvengingWrath:CastableIf(function(self) return self:IsKnownAndUsable() and Player:GetPartyHPAround(40, 75) >= 3 end):SetTarget(Player) ) CooldownAPL:AddSpell( BlessingOfSummer:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and Player:IsWithinCombatDistance(BestTarget, 30) and not Player:IsCastingOrChanneling() end):SetTarget(Player) ) CooldownAPL:AddSpell( SacredWeapon:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and Player:IsWithinCombatDistance(BestTarget, 30) and not Player:IsCastingOrChanneling() end):SetTarget(Player) ) CooldownAPL:AddSpell( Consecration:CastableIf(function(self) return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(ConsecrationBuff):IsUp() and BestTarget:Exists() and Player:IsWithinCombatDistance(BestTarget, 10) and not Player:IsMoving() and not Player:IsCastingOrChanneling() end):SetTarget(Player) ) -- Defensive APL DefensiveAPL:AddSpell( DivineShield:CastableIf(function(self) return self:IsKnownAndUsable() and Player:GetHP() < 20 end):SetTarget(Player) ) DefensiveAPL:AddSpell( LayOnHands:CastableIf(function(self) return self:IsKnownAndUsable() and Lowest:GetHP() < 10 end):SetTarget(Lowest) ) DefensiveAPL:AddSpell( BlessingOfProtection:CastableIf(function(self) return self:IsKnownAndUsable() and Lowest:GetHP() < 30 and not Lowest:IsTank() end):SetTarget(Lowest) ) DefensiveAPL:AddSpell( BlessingOfSacrifice:CastableIf(function(self) return self:IsKnownAndUsable() and Tank:GetHP() < 50 and Player:GetHP() > 50 end):SetTarget(Tank) ) -- Damage Priority APL DamagePriorityAPL:AddSpell( Judgment:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and Player:IsWithinCombatDistance(BestTarget, 30) and not Player:IsCastingOrChanneling() end):SetTarget(BestTarget) ) DamagePriorityAPL:AddSpell( HolyShock:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and Player:IsWithinCombatDistance(BestTarget, 40) and not Player:IsCastingOrChanneling() end):SetTarget(BestTarget) ) DamagePriorityAPL:AddSpell( CrusaderStrike:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and IsInMeleeRange(BestTarget) and not Player:IsCastingOrChanneling() end):SetTarget(BestTarget) ) DamagePriorityAPL:AddSpell( ShieldOfTheRighteous:CastableIf(function(self) return self:IsKnownAndUsable() and BestTarget:Exists() and IsInMeleeRange(BestTarget) and not Player:IsCastingOrChanneling() and Player:GetPower(HOLY_POWER) >= 3 and not HealingNeeded() end):SetTarget(BestTarget) ) -- Default APL DefaultAPL:AddAPL(DefensiveAPL, function() return true end) DefaultAPL:AddAPL(CooldownAPL, function() return true end) DefaultAPL:AddAPL(HealingAPL, function() return HealingNeeded() end) DefaultAPL:AddAPL(ExecuteAPL, function() return true end) DefaultAPL:AddAPL(DamagePriorityAPL, function() return not HealingNeeded() and AggressiveDamagePhase() end) DefaultAPL:AddAPL(DamageAPL, function() return not HealingNeeded() end) HolyPaladinModule:Sync(function() if Player:IsAffectingCombat() then healingThreshold = AggressiveDamagePhase() and 85 or 90 DefaultAPL:Execute() else healingThreshold = 100 if AnyoneNeedsHealing() then HealingAPL:Execute() end end end) Bastion:Register(HolyPaladinModule)