Basic rotations that work
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OkRotations/BMHunter.lua

445 lines
14 KiB

-- Talents: C0PAAAAAAAAAAAAAAAAAAAAAAAMmxGDsALjGaYDAAAAAADAAAAAAgZsNjxMjZYmxMMmZMzYMjZyMMmxMzMmZMDjhZGmlBjZG2A
local Tinkr, Bastion = ...
local BMHunterModule = Bastion.Module:New('BMHunter')
local Player = Bastion.UnitManager:Get('player')
local Pet = Bastion.UnitManager:Get('pet')
-- Initialize SpellBook
local SpellBook = Bastion.SpellBook:New()
-- Spells
local HuntersMark = SpellBook:GetSpell(257284)
local BestialWrath = SpellBook:GetSpell(19574)
local BarbedShot = SpellBook:GetSpell(217200)
local DireBeast = SpellBook:GetSpell(120679)
local KillCommand = SpellBook:GetSpell(34026)
local BlackArrow = SpellBook:GetSpell(194599)
local KillShot = SpellBook:GetSpell(53351)
local CallOfTheWild = SpellBook:GetSpell(359844)
local Bloodshed = SpellBook:GetSpell(321530)
local CobraShot = SpellBook:GetSpell(193455)
local ExplosiveShot = SpellBook:GetSpell(212431)
local MultiShot = SpellBook:GetSpell(2643)
local AutoShot = SpellBook:GetSpell(75)
local CallPet = SpellBook:GetSpell(883)
local FeedPet = SpellBook:GetSpell(6991)
local CounterShot = SpellBook:GetSpell(147362)
local MendPet = SpellBook:GetSpell(136)
local Exhilaration = SpellBook:GetSpell(109304)
local SurvivalOfTheFittest = SpellBook:GetSpell(264735)
local TranquilizingShot = SpellBook:GetSpell(19801)
local Fetch = SpellBook:GetSpell(125050)
-- Buffs and Debuffs
local FrenzyBuff = SpellBook:GetSpell(272790)
local BeastCleaveBuff = SpellBook:GetSpell(268877)
local BestialWrathBuff = SpellBook:GetSpell(19574)
-- Custom Units
local BestTarget = Bastion.UnitManager:CreateCustomUnit('besttarget', function()
local bestTarget = nil
local highestHealth = 0
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsAffectingCombat() and unit:GetDistance(Player) <= 40 and Player:CanSee(unit) then
local health = unit:GetHealth()
if health > highestHealth then
highestHealth = health
bestTarget = unit
end
end
end)
return bestTarget or Bastion.UnitManager:Get('target')
end)
local InterruptTarget = Bastion.UnitManager:CreateCustomUnit('interrupttarget', function()
local target = nil
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsAffectingCombat() and unit:GetDistance(Player) <= 40 and Player:CanSee(unit)
and unit:IsCasting() and unit:IsInterruptible() then
local castPercentage = GetCastPercentage(unit)
if castPercentage >= 30 and castPercentage <= 80 then
target = unit
return true
end
end
end)
return target or BestTarget
end)
local ExecuteTarget = Bastion.UnitManager:CreateCustomUnit('executetarget', function()
local target = nil
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsAffectingCombat() and unit:GetDistance(Player) <= 40 and Player:CanSee(unit)
and unit:GetHP() < 20 then
target = unit
return true
end
end)
return target or Bastion.UnitManager:Get('none')
end)
-- APLs
local DefaultAPL = Bastion.APL:New('default')
local CooldownAPL = Bastion.APL:New('cooldown')
local AoEAPL = Bastion.APL:New('aoe')
local PetAPL = Bastion.APL:New('pet')
local InterruptAPL = Bastion.APL:New('interrupt')
local DefensiveAPL = Bastion.APL:New('defensive')
local BossAPL = Bastion.APL:New('boss')
local TranqAPL = Bastion.APL:New('tranq')
local ExecuteAPL = Bastion.APL:New('execute')
-- Helper Functions
local function GetBarbedShotCharges()
return BarbedShot:GetCharges()
end
local function GetFrenzyStacks()
return Pet:GetAuras():FindMy(FrenzyBuff):GetCount()
end
local function IsKillerCobraTalented()
-- Implement logic to check if Killer Cobra is talented
-- This is a placeholder and should be replaced with actual talent checking logic
return true
end
local function IsVenomsBiteTalented()
-- Implement logic to check if Venom's Bite is talented
-- This is a placeholder and should be replaced with actual talent checking logic
return true
end
local function ShouldUseMultiShot()
local beastCleaveBuff = Pet:GetAuras():FindMy(BeastCleaveBuff)
return Player:GetEnemies(8) >= 2 and (not beastCleaveBuff:IsUp() or beastCleaveBuff:GetRemainingTime() < 2)
end
local lastFeedTime = 0
local function ShouldFeedPet()
return GetTime() - lastFeedTime > 1800 -- 30 minutes
end
local lastAutoAttackTime = 0
local function IsAutoAttacking()
return GetTime() - lastAutoAttackTime < 2
end
local function GetCastPercentage(unit)
local spell, _, _, startTimeMS, endTimeMS = UnitCastingInfo(unit:GetOMToken())
if not spell then
return 0
end
local castTime = (endTimeMS - startTimeMS) / 1000
local elapsed = (GetTime() * 1000 - startTimeMS) / 1000
return elapsed / castTime * 100
end
local function ShouldInterrupt(unit)
local castPercentage = GetCastPercentage(unit)
local randomThreshold = math.random(30, 80)
return castPercentage >= randomThreshold
end
-- Variables to manage the random delay for Tranquilizing Shot
local tranqTargetTime = 0 -- Tracks when a valid target was first identified
local tranqReactionDelay = 0 -- Stores the random reaction delay
-- Function to generate a random reaction delay between 400ms and 2 seconds
local function GetRandomReactionDelay()
return math.random(400, 2000) / 1000 -- Convert milliseconds to seconds
end
-- Function to check if the reaction delay has passed
local function ShouldUseTranquilizingShot()
local currentTime = GetTime()
if tranqTargetTime == 0 then return false end
return currentTime - tranqTargetTime >= tranqReactionDelay
end
local TranqTarget = Bastion.UnitManager:CreateCustomUnit('tranqtarget', function()
local target = nil
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsAffectingCombat() and unit:GetDistance(Player) <= 40 and Player:CanSee(unit)
and unit:GetAuras():HasAnyDispelableAura(TranquilizingShot) then
target = unit
-- Set the target identification time if it hasn't been set
if tranqTargetTime == 0 then
tranqTargetTime = GetTime()
tranqReactionDelay = GetRandomReactionDelay()
end
return true
end
end)
-- Reset the target time if no valid target is found
if not target then
tranqTargetTime = 0
end
return target or Bastion.UnitManager:Get('none')
end)
-- Default APL
DefaultAPL:AddSpell(
AutoShot:CastableIf(function(self)
return BestTarget:Exists() and not IsAutoAttacking() and Player:CanSee(BestTarget)
end):SetTarget(BestTarget):OnCast(function()
lastAutoAttackTime = GetTime()
end)
)
DefaultAPL:AddSpell(
BarbedShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and (GetFrenzyStacks() < 3 or GetBarbedShotCharges() == 2)
end):SetTarget(BestTarget)
)
DefaultAPL:AddSpell(
KillCommand:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
DefaultAPL:AddSpell(
DireBeast:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
DefaultAPL:AddSpell(
CobraShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and (Player:GetAuras():FindMy(BestialWrathBuff):IsUp() or Player:GetPower() > 70)
end):SetTarget(BestTarget)
)
DefaultAPL:AddSpell(
ExplosiveShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and not Player:GetAuras():FindMy(BestialWrathBuff):IsUp()
end):SetTarget(BestTarget)
)
-- Cooldown APL
CooldownAPL:AddSpell(
HuntersMark:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and not BestTarget:GetAuras():FindMy(HuntersMark):IsUp()
end):SetTarget(BestTarget)
)
CooldownAPL:AddSpell(
BestialWrath:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Player)
)
CooldownAPL:AddSpell(
BlackArrow:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
CooldownAPL:AddSpell(
CallOfTheWild:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Player)
)
CooldownAPL:AddSpell(
Bloodshed:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
-- AoE APL
AoEAPL:AddSpell(
BarbedShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and (GetFrenzyStacks() < 3 or GetBarbedShotCharges() == 2)
end):SetTarget(BestTarget)
)
AoEAPL:AddSpell(
BlackArrow:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
AoEAPL:AddSpell(
MultiShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and ShouldUseMultiShot()
end):SetTarget(BestTarget)
)
AoEAPL:AddSpell(
DireBeast:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
AoEAPL:AddSpell(
CallOfTheWild:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Player)
)
AoEAPL:AddSpell(
BestialWrath:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Player)
)
AoEAPL:AddSpell(
KillCommand:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
AoEAPL:AddSpell(
BarbedShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and GetBarbedShotCharges() > 0
end):SetTarget(BestTarget)
)
AoEAPL:AddSpell(
CobraShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetAuras():FindMy(BestialWrathBuff):IsUp() and IsKillerCobraTalented()
end):SetTarget(BestTarget)
)
AoEAPL:AddSpell(
ExplosiveShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
AoEAPL:AddSpell(
CobraShot:CastableIf(function(self)
return BestTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(BestTarget)
)
-- Pet APL
PetAPL:AddSpell(
CallPet:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and not Pet:Exists()
end):SetTarget(Player)
)
PetAPL:AddSpell(
FeedPet:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Pet:Exists() and ShouldFeedPet()
end):SetTarget(Pet):OnCast(function()
lastFeedTime = GetTime()
end)
)
PetAPL:AddSpell(
MendPet:CastableIf(function(self)
return Pet:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Pet:GetHP() < 90 and not Pet:GetAuras():FindMy(MendPet):IsUp()
end):SetTarget(Pet)
)
-- Interrupt APL
InterruptAPL:AddSpell(
CounterShot:CastableIf(function(self)
if not BestTarget:Exists() or not self:IsKnownAndUsable() or Player:IsCastingOrChanneling() then
return false
end
if BestTarget:IsCasting() and BestTarget:IsInterruptible() then
return ShouldInterrupt(BestTarget)
end
return false
end):SetTarget(BestTarget)
)
-- Defensive APL
DefensiveAPL:AddSpell(
Exhilaration:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetHP() < 80
end):SetTarget(Player)
)
DefensiveAPL:AddSpell(
SurvivalOfTheFittest:CastableIf(function(self)
return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetHP() < 80
end):SetTarget(Player)
)
-- Tranq APL
TranqAPL:AddSpell(
TranquilizingShot:CastableIf(function(self)
local target = TranqTarget
return target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and ShouldUseTranquilizingShot()
end):SetTarget(TranqTarget):OnCast(function()
-- Reset the tranq target time after casting
tranqTargetTime = 0
end)
)
-- Boss APL
BossAPL:AddSpell(
CallOfTheWild:CastableIf(function(self)
return BestTarget:Exists() and BestTarget:IsBoss() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Player)
)
ExecuteAPL:AddSpell(
KillShot:CastableIf(function(self)
return ExecuteTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(ExecuteTarget)
)
BMHunterModule:Sync(function()
if Player:IsMounted() then
return
end
DefensiveAPL:Execute()
PetAPL:Execute()
if Player:IsAffectingCombat() then
ExecuteAPL:Execute()
InterruptAPL:Execute()
TranqAPL:Execute() -- Add this line to execute the Tranq APL
if BestTarget:Exists() and BestTarget:IsBoss() then
BossAPL:Execute()
end
local enemyCount = Player:GetEnemies(8)
if enemyCount >= 2 then
AoEAPL:Execute()
else
CooldownAPL:Execute()
DefaultAPL:Execute()
end
end
end)
Bastion:Register(BMHunterModule)
-- 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)