local Tinkr, Bastion = ... local AugmentationModule = Bastion.Module:New('AugmentationEvoker') local Player = Bastion.UnitManager:Get('player') local Target = Bastion.UnitManager:Get('target') local Focus = Bastion.UnitManager:Get('focus') local SpellBook = Bastion.SpellBook:New() -- Ensure EventManager is properly initialized local EventManager = Bastion.EventManager:New() -- Constants for Ebon Might logic local EBON_MIGHT_MIN_ENEMIES = 1 local EBON_MIGHT_MIN_TTD = 12 -- seconds local EBON_MIGHT_MIN_ESSENCE = 50 -- percent of max essence -- Spells local Prescience = SpellBook:GetSpell(409311) local EbonMight = SpellBook:GetSpell(395152) local FireBreath = SpellBook:GetSpell(382266) local Upheaval = SpellBook:GetSpell(396286) local Eruption = SpellBook:GetSpell(395160) local LivingFlame = SpellBook:GetSpell(361469) local TipTheScales = SpellBook:GetSpell(370553) local Hover = SpellBook:GetSpell(358267) local AzureStrike = SpellBook:GetSpell(362969) local BlisteringScales = SpellBook:GetSpell(360827) local TimeSkip = SpellBook:GetSpell(404977) -- Buffs and Debuffs local PrescienceBuff = SpellBook:GetSpell(410089) local EbonMightBuff = SpellBook:GetSpell(395296) local EssenceBurstBuff = SpellBook:GetSpell(369299) -- Talents local LeapingFlames = SpellBook:GetSpell(370901) -- Create APLs local CooldownAPL = Bastion.APL:New('cooldown') local BuffAPL = Bastion.APL:New('buff') local DamageAPL = Bastion.APL:New('damage') local MovementAPL = Bastion.APL:New('movement') -- Empowered spell handling local empowering = {} function Player:GetEmpoweredStage() local stage = 0 local _, _, _, startTime, _, _, _, spellID, _, numStages = UnitChannelInfo(self:GetOMToken()) if numStages and numStages > 0 then startTime = startTime / 1000 local currentTime = GetTime() local stageDuration = 0 for i = 1, numStages do stageDuration = stageDuration + GetUnitEmpowerStageDuration((self:GetOMToken()), i - 1) / 1000 if startTime + stageDuration > currentTime then break end stage = i end end return stage end EventManager:RegisterWoWEvent("UNIT_SPELLCAST_EMPOWER_START", function(...) local unit, _, id = ... if not unit then return end local guid = UnitGUID(unit) if not guid then return end empowering[guid] = -1 end) EventManager:RegisterWoWEvent("UNIT_SPELLCAST_EMPOWER_STOP", function(...) local unit, _, id = ... if not unit then return end local guid = UnitGUID(unit) if not guid then return end empowering[guid] = nil end) function Player:GetUnitEmpowerStage() local name, _, _, startTime, endTime, _, _, _, _, numStages = UnitChannelInfo(self:GetOMToken()) if name and empowering[self:GetGUID()] == -1 then empowering[self:GetGUID()] = numStages end if not name then return empowering[self:GetGUID()] or 0 end local getStageDuration = function(stage) if stage == numStages then return GetUnitEmpowerHoldAtMaxTime(self:GetOMToken()) else return GetUnitEmpowerStageDuration(self:GetOMToken(), stage - 1) end end local time = GetTime() - (startTime / 1000) local higheststage = 0 local sumdur = 0 for i = 1, numStages - 1 do local duration = getStageDuration(i) / 1000 sumdur = sumdur + duration if time > sumdur then higheststage = i end end return higheststage end -- Helper Functions local function GetPrescienceTargets() local targets = Bastion.List:New() if Focus:Exists() and not Focus:IsTank() and Player:GetDistance(Focus) <= 40 and Player:CanSee(Focus) then targets:push(Focus) end Bastion.UnitManager:EnumFriends(function(unit) if targets:count() >= 2 then return true end if unit ~= Focus and unit:IsPlayer() and not unit:IsTank() and Player:GetDistance(unit) <= 40 and Player:CanSee(unit) then local aura = unit:GetAuras():FindMy(PrescienceBuff) if not aura:IsUp() or aura:GetRemainingTime() < 10 then targets:push(unit) end end end) return targets end local function IsEbonMightActive() return Player:GetAuras():FindMy(EbonMightBuff):IsUp() end local function IsEssenceCapped() return Player:GetPower() >= Player:GetMaxPower() or Player:GetAuras():FindMy(EssenceBurstBuff):GetCount() == 2 end local function ShouldCastEbonMight() local viableEnemyCount = 0 local playerEssencePercent = (Player:GetPower() / Player:GetMaxPower()) * 100 Bastion.UnitManager:EnumEnemies(function(unit) if unit:IsAffectingCombat() and unit:TimeToDie() >= EBON_MIGHT_MIN_TTD then viableEnemyCount = viableEnemyCount + 1 end end) local isBossFight = Target:Exists() and Target:IsBoss() local hasEnoughEssence = playerEssencePercent >= EBON_MIGHT_MIN_ESSENCE if isBossFight then return hasEnoughEssence and Target:TimeToDie() >= EBON_MIGHT_MIN_TTD else return viableEnemyCount >= EBON_MIGHT_MIN_ENEMIES and hasEnoughEssence end end -- Custom Units local PrescienceTarget = Bastion.UnitManager:CreateCustomUnit('presciencetarget', function() local targets = GetPrescienceTargets() return targets:count() > 0 and targets:peek() or Player end) local EbonMightTarget = Bastion.UnitManager:CreateCustomUnit('ebonmighttarget', function() local target = nil Bastion.UnitManager:EnumFriends(function(unit) if unit:IsPlayer() and Player:GetDistance(unit) <= 40 and Player:CanSee(unit) then target = unit return true end end) return target or Player end) local DamageTarget = Bastion.UnitManager:CreateCustomUnit('damagetarget', function() local target = nil local highestHealth = 0 Bastion.UnitManager:EnumEnemies(function(unit) if unit:IsAffectingCombat() and Player:IsWithinCombatDistance(unit, 40) and Player:CanSee(unit) then local health = unit:GetHealth() if health > highestHealth then target = unit highestHealth = health end end end) return target or Target end) local TankTarget = Bastion.UnitManager:CreateCustomUnit('tanktarget', function() local tank = nil Bastion.UnitManager:EnumFriends(function(unit) if unit:IsTank() and Player:GetDistance(unit) <= 40 and Player:CanSee(unit) then tank = unit return true end end) return tank or Player end) -- Cooldown APL CooldownAPL:AddSpell( EbonMight:CastableIf(function(self) return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and ShouldCastEbonMight() end):SetTarget(EbonMightTarget) ) CooldownAPL:AddSpell( TipTheScales:CastableIf(function(self) return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and IsEbonMightActive() and (FireBreath:IsKnownAndUsable() or Upheaval:IsKnownAndUsable()) end):SetTarget(Player) ) CooldownAPL:AddSpell( TimeSkip:CastableIf(function(self) return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() end):SetTarget(Player) ) -- Buff APL BuffAPL:AddSpell( Prescience:CastableIf(function(self) return GetPrescienceTargets():count() > 0 and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() end):SetTarget(PrescienceTarget):OnCast(function(self) local targets = GetPrescienceTargets() if targets:count() > 1 then self:Cast(targets[2] or Player) end end) ) BuffAPL:AddSpell( BlisteringScales:CastableIf(function(self) return TankTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not TankTarget:GetAuras():FindMy(BlisteringScales):IsUp() end):SetTarget(TankTarget) ) -- Damage APL DamageAPL:AddSpell( FireBreath:CastableIf(function(self) return DamageTarget:IsAffectingCombat() and IsEbonMightActive() end):SetTarget(DamageTarget):OnCast(function(self) local empowerStage = Player:GetUnitEmpowerStage() if empowerStage >= 1 then C_Timer.After(0.1, function() self:ForceCast(DamageTarget) end) end end) ) DamageAPL:AddSpell( Upheaval:CastableIf(function(self) return DamageTarget:IsAffectingCombat() and IsEbonMightActive() end):SetTarget(DamageTarget):OnCast(function(self) local empowerStage = Player:GetUnitEmpowerStage() if empowerStage >= 1 then self:ForceCast(DamageTarget) end end) ) DamageAPL:AddSpell( Eruption:CastableIf(function(self) return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and (IsEbonMightActive() or IsEssenceCapped()) end):SetTarget(DamageTarget) ) DamageAPL:AddSpell( LivingFlame:CastableIf(function(self) return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and (LeapingFlames:IsKnown() or not FireBreath:IsKnownAndUsable()) end):SetTarget(DamageTarget) ) -- Movement APL MovementAPL:AddSpell( Hover:CastableIf(function(self) return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(Hover):IsUp() end):SetTarget(Player) ) MovementAPL:AddSpell( AzureStrike:CastableIf(function(self) return self:IsKnownAndUsable() and Player:IsMoving() and not Player:GetAuras():FindMy(Hover):IsUp() end):SetTarget(DamageTarget) ) -- Module Sync AugmentationModule:Sync(function() -- Check if the player is mounted if Player:IsMounted() then return -- Exit the function if mounted, effectively disabling all APLs end if not Player:IsAffectingCombat() then BuffAPL:Execute() return end CooldownAPL:Execute() BuffAPL:Execute() if DamageAPL:Execute() then return end MovementAPL:Execute() end) Bastion:Register(AugmentationModule)