--- ============================ HEADER ============================ --- ======= LOCALIZE ======= -- Addon local addonName, addonTable = ... -- HeroDBC local DBC = HeroDBC.DBC -- HeroLib local HL = HeroLib local Cache = HeroCache local Unit = HL.Unit local Player = Unit.Player local Pet = Unit.Pet local Target = Unit.Target local Spell = HL.Spell local MultiSpell = HL.MultiSpell local Item = HL.Item -- HeroRotation local HR = HeroRotation local AoEON = HR.AoEON local CDsON = HR.CDsON local Cast = HR.Cast local CastPooling = HR.CastPooling -- Num/Bool Helper Functions local num = HR.Commons.Everyone.num local bool = HR.Commons.Everyone.bool --- ============================ CONTENT ============================ --- ======= APL LOCALS ======= -- Commons local Everyone = HR.Commons.Everyone -- GUI Settings local Settings = { General = HR.GUISettings.General, Commons = HR.GUISettings.APL.Druid.Commons, Feral = HR.GUISettings.APL.Druid.Feral } -- Spells local S = Spell.Druid.Feral -- Items local I = Item.Druid.Feral local OnUseExcludes = {-- I.TrinketName:ID(), I.AlgetharPuzzleBox:ID(), I.IrideusFragment:ID(), I.ManicGrieftorch:ID(), I.MirrorofFracturedTomorrows:ID(), } -- Rotation Variables local VarNeedBT, VarAlign3Mins, VarLastConvoke, VarLastZerk, VarZerkBiteweave, VarRegrowth, VarLazySwipe local ComboPoints, ComboPointsDeficit local BossFightRemains = 11111 local FightRemains = 11111 -- Enemy Variables local EnemiesMelee, EnemiesCountMelee local Enemies11y, EnemiesCount11y -- Berserk/Incarnation Variables local BsInc = S.Incarnation:IsAvailable() and S.Incarnation or S.Berserk -- Event Registration HL:RegisterForEvent(function() BsInc = S.Incarnation:IsAvailable() and S.Incarnation or S.Berserk end, "SPELLS_CHANGED", "LEARNED_SPELL_IN_TAB") HL:RegisterForEvent(function() BossFightRemains = 11111 FightRemains = 11111 end, "PLAYER_REGEN_ENABLED") HL:RegisterForEvent(function() S.AdaptiveSwarm:RegisterInFlightEffect(391889) S.AdaptiveSwarm:RegisterInFlight() end, "LEARNED_SPELL_IN_TAB") S.AdaptiveSwarm:RegisterInFlightEffect(391889) S.AdaptiveSwarm:RegisterInFlight() -- Interrupt Stuns local InterruptStuns = { { S.MightyBash, "Cast Mighty Bash (Interrupt)", function () return true; end }, } -- PMultiplier and Damage Registrations local function ComputeRakePMultiplier() return Player:StealthUp(true, true) and 1.6 or 1 end S.Rake:RegisterPMultiplier(S.RakeDebuff, ComputeRakePMultiplier) S.Shred:RegisterDamageFormula( function() return -- Attack Power Player:AttackPowerDamageMod() * -- Shred Modifier 0.7762 * -- Stealth Modifier (Player:StealthUp(true) and 1.6 or 1) * -- Versatility Modifier (1 + Player:VersatilityDmgPct() / 100) end ) S.Thrash:RegisterDamageFormula( function() return -- Immediate Damage (Player:AttackPowerDamageMod() * 0.1272) + -- Bleed Damage (Player:AttackPowerDamageMod() * 0.4055) end ) -- Functions for Bloodtalons local BtTriggers = { S.Rake, S.LIMoonfire, S.Thrash, S.BrutalSlash, S.Swipe, S.Shred, S.FeralFrenzy, } local function DebuffRefreshAny(Enemies, Spell) for _, Enemy in pairs(Enemies) do if Enemy:DebuffRefreshable(Spell) then return true end end return false end local function LowRakePMult(Enemies) local Lowest = nil for _, Enemy in pairs(Enemies) do local EnemyPMult = Enemy:PMultiplier(S.Rake) if (not Lowest) or EnemyPMult < Lowest then Lowest = EnemyPMult end end return Lowest end local function BTBuffUp(Trigger) if not S.Bloodtalons:IsAvailable() then return false end return Trigger:TimeSinceLastCast() < math.min(5, S.BloodtalonsBuff:TimeSinceLastAppliedOnPlayer()) end local function BTBuffDown(Trigger) return not BTBuffUp(Trigger) end function CountActiveBtTriggers() local ActiveTriggers = 0 for i = 1, #BtTriggers do if BTBuffUp(BtTriggers[i]) then ActiveTriggers = ActiveTriggers + 1 end end return ActiveTriggers end local function TicksGainedOnRefresh(Spell, Tar) if not Tar then Tar = Target end local AddedDuration = 0 local MaxDuration = 0 -- Added TickTime variable, as Rake and Moonfire don't have tick times in DBC local TickTime = 0 if Spell == S.RipDebuff then AddedDuration = (4 + ComboPoints * 4) MaxDuration = 31.2 TickTime = Spell:TickTime() else AddedDuration = Spell:BaseDuration() MaxDuration = Spell:MaxDuration() TickTime = Spell:TickTime() end local OldTicks = Tar:DebuffTicksRemain(Spell) local OldTime = Tar:DebuffRemains(Spell) local NewTime = AddedDuration + OldTime if NewTime > MaxDuration then NewTime = MaxDuration end local NewTicks = NewTime / TickTime if (not OldTicks) then OldTicks = 0 end local TicksAdded = NewTicks - OldTicks return TicksAdded end local function HighestTTD(enemies) if not enemies then return 0 end local HighTTD = 0 local HighTTDTar = nil for _, enemy in pairs(enemies) do local TTD = enemy:TimeToDie() if TTD > HighTTD then HighTTD = TTD HighTTDTar = enemy end end return HighTTD, HighTTDTar end -- CastCycle/CastTargetIf Functions -- CastTargetIf Filters local function EvaluateTargetIfFilterAdaptiveSwarm(TargetUnit) -- target_if=max:((1+dot.adaptive_swarm_damage.stack)*dot.adaptive_swarm_damage.stack<3*time_to_die) return ((1 + Target:DebuffStack(S.AdaptiveSwarmDebuff)) * Target:DebuffStack(S.AdaptiveSwarmDebuff) < 3 * TargetUnit:TimeToDie()) end local function EvaluateTargetIfFilterLIMoonfire(TargetUnit) -- target_if=max:(3*refreshable)+dot.adaptive_swarm_damage.ticking return ((3 * num(TargetUnit:DebuffRefreshable(S.LIMoonfireDebuff))) + num(TargetUnit:DebuffUp(S.LIMoonfireDebuff))) end local function EvaluateTargetIfFilterRake(TargetUnit) -- target_if=min:(25*(persistent_multiplier1&!(variable.need_bt&active_bt_triggers=2) -- Note: Everything but stack count handled before CastTargetIf call return (TargetUnit:DebuffStack(S.AdaptiveSwarmDebuff) < 3) end local function EvaluateTargetIfBrutalSlashAoeBuilder(TargetUnit) -- if=cooldown.brutal_slash.full_recharge_time<4|target.time_to_die<5 return (S.BrutalSlash:FullRechargeTime() < 4 or TargetUnit:TimeToDie() < 5) end local function EvaluateTargetIfBrutalSlashBT(TargetUnit) -- if=(cooldown.brutal_slash.full_recharge_time<4|target.time_to_die<5)&(buff.bt_brutal_slash.down&(buff.bs_inc.up|variable.need_bt)) return ((S.BrutalSlash:FullRechargeTime() < 4 or TargetUnit:TimeToDie() < 5) and (BTBuffDown(S.BrutalSlash) and (Player:BuffUp(BsInc) or VarNeedBT))) end local function EvaluateTargetIfConvokeCD(TargetUnit) -- if=((target.time_to_die5-talent.ashamanes_guidance.enabled)|target.time_to_die=fight_remains)&(fight_remains<5|(dot.rip.remains>5&buff.tigers_fury.up&(combo_points<2|(buff.bs_inc.up&combo_points<=3))))&(!variable.lastConvoke|buff.potion.up|(time+fight_remains+10)%%300>time%%300)&(talent.dire_fixation.enabled&debuff.dire_fixation.up|!talent.dire_fixation.enabled|spell_targets.swipe_cat>1) local PotionSelected = Everyone.PotionSelected() local CombatTime = HL.CombatTime() return (((TargetUnit:TimeToDie() < FightRemains and TargetUnit:TimeToDie() > 5 - num(S.AshamanesGuidance:IsAvailable())) or TargetUnit:TimeToDie() == FightRemains) and (FightRemains < 5 or (TargetUnit:DebuffRemains(S.RipDebuff) > 5 and Player:BuffUp(S.TigersFury) and (ComboPoints < 2 or (Player:BuffUp(BsInc) and ComboPoints <= 3)))) and ((not VarLastConvoke) or PotionSelected:TimeSinceLastCast() < 30 or (CombatTime + FightRemains + 10) % 300 > CombatTime % 300) and (S.DireFixation:IsAvailable() and TargetUnit:DebuffUp(S.DireFixationDebuff) or (not S.DireFixation:IsAvailable()) or EnemiesCount11y > 1)) end local function EvaluateTargetIfLIMoonfireAoEBuilder(TargetUnit) -- if=spell_targets.swipe_cat<5&dot.moonfire.refreshable -- Note: Target count checked before CastTargetIf call. return (TargetUnit:DebuffRefreshable(S.LIMoonfireDebuff)) end local function EvaluateTargetIfLIMoonfireBT(TargetUnit) -- target_if=max:ticks_gained_on_refresh return (TicksGainedOnRefresh(S.LIMoonfireDebuff, TargetUnit)) end local function EvaluateTargetIfRakeAoeBuilder(TargetUnit) -- if=buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier return (Player:PMultiplier(S.Rake) > TargetUnit:PMultiplier(S.Rake)) end local function EvaluateTargetIfRakeBloodtalons(TargetUnit) -- if=(refreshable|buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier)&buff.bt_rake.down return ((TargetUnit:DebuffRefreshable(S.RakeDebuff) or Player:BuffUp(S.SuddenAmbushBuff) and Player:PMultiplier(S.Rake) > TargetUnit:PMultiplier(S.Rake)) and BTBuffDown(S.Rake)) end local function EvaluateTargetIfFeralFrenzy(TargetUnit) -- if=((talent.dire_fixation.enabled&debuff.dire_fixation.up)|!talent.dire_fixation.enabled|spell_targets.swipe_cat>1)&(combo_points<2|combo_points<3&buff.bs_inc.up|time<10) return ((S.DireFixation:IsAvailable() and TargetUnit:DebuffUp(S.DireFixationDebuff)) or (not S.DireFixation:IsAvailable()) or EnemiesCount11y > 1) end local function EvaluateTargetIfFerociousBiteBerserk(TargetUnit) -- if=combo_points=5&dot.rip.remains>8&variable.zerk_biteweave&spell_targets.swipe_cat>1 return TargetUnit:DebuffRemains(S.RipDebuff) > 5 end -- CastCycle Conditions local function EvaluateCycleAdaptiveSwarm(TargetUnit) -- target_if=((!dot.adaptive_swarm_damage.ticking|dot.adaptive_swarm_damage.remains<2)&(dot.adaptive_swarm_damage.stack<3)&!action.adaptive_swarm_damage.in_flight&!action.adaptive_swarm.in_flight)&target.time_to_die>5 return (((TargetUnit:DebuffDown(S.AdaptiveSwarmDebuff) or TargetUnit:DebuffRemains(S.AdaptiveSwarmDebuff) < 2) and TargetUnit:DebuffStack(S.AdaptiveSwarmDebuff) < 3 and (not S.AdaptiveSwarm:InFlight())) and TargetUnit:TimeToDie() > 5) end local function EvaluateCycleLIMoonfire(TargetUnit) -- target_if=refreshable return (TargetUnit:DebuffRefreshable(S.LIMoonfireDebuff)) end local function EvaluateCycleRakeAoeBuilder(TargetUnit) -- target_if=buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier|refreshable return (Player:BuffUp(S.SuddenAmbushBuff) and Player:PMultiplier(S.Rake) > TargetUnit:PMultiplier(S.Rake) or TargetUnit:DebuffRefreshable(S.RakeDebuff)) end local function EvaluateCycleRake(TargetUnit) -- target_if=buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier&buff.bt_rake.down -- bt_rake check handled before CastCycle return (Player:BuffUp(S.SuddenAmbushBuff) and Player:PMultiplier(S.Rake) > TargetUnit:PMultiplier(S.Rake)) end local function EvaluateCycleRakeMain(TargetUnit) -- target_if=persistent_multiplier>dot.rake.pmultiplier return (Player:PMultiplier(S.Rake) > TargetUnit:PMultiplier(S.Rake)) end local function EvaluateCycleRip(TargetUnit) -- target_if=refreshable&(!talent.primal_wrath.enabled|spell_targets.swipe_cat=1) return (TargetUnit:DebuffRefreshable(S.RipDebuff) and ((not S.PrimalWrath:IsAvailable()) or EnemiesCount11y == 1)) end local function EvaluateCycleThrash(TargetUnit) -- target_if=refreshable return (TargetUnit:DebuffRefreshable(S.ThrashDebuff)) end -- APL Functions local function Precombat() -- Manually added: Group buff check if S.MarkoftheWild:IsCastable() and (Player:BuffDown(S.MarkoftheWildBuff, true) or Everyone.GroupBuffMissing(S.MarkoftheWildBuff)) then if Cast(S.MarkoftheWild, Settings.Commons.GCDasOffGCD.MarkOfTheWild) then return "mark_of_the_wild precombat"; end end -- cat_form,if=!buff.cat_form.up if S.CatForm:IsCastable() then if Cast(S.CatForm) then return "cat_form precombat 2"; end end -- heart_of_the_wild if S.HeartoftheWild:IsCastable() then if Cast(S.HeartoftheWild) then return "heart_of_the_wild precombat 4"; end end -- use_item,name=algethar_puzzle_box if Settings.Commons.Enabled.Trinkets and I.AlgetharPuzzleBox:IsEquippedAndReady() then if Cast(I.AlgetharPuzzleBox, nil, Settings.Commons.DisplayStyle.Trinkets) then return "algethar_puzzle_box precombat 6"; end end -- prowl,if=!buff.prowl.up if S.Prowl:IsCastable() then if Cast(S.Prowl) then return "prowl precombat 4"; end end -- Manually added: wild_charge if S.WildCharge:IsCastable() and (not Target:IsInRange(8)) then if Cast(S.WildCharge, nil, nil, not Target:IsInRange(28)) then return "wild_charge precombat 6"; end end -- Manually added: rake if S.Rake:IsReady() then if Cast(S.Rake, nil, nil, not Target:IsInMeleeRange(8)) then return "rake precombat 8"; end end end local function Variables() -- variable,name=need_bt,value=talent.bloodtalons.enabled&buff.bloodtalons.stack<=1 VarNeedBT = (S.Bloodtalons:IsAvailable() and Player:BuffStack(S.BloodtalonsBuff) <= 1) -- variable,name=align_3minutes,value=spell_targets.swipe_cat=1&!fight_style.dungeonslice local DungeonSlice = Player:IsInParty() and not Player:IsInRaid() VarAlign3Mins = EnemiesCount11y == 1 and not DungeonSlice -- variable,name=lastConvoke,value=fight_remains>cooldown.convoke_the_spirits.remains+3&((talent.ashamanes_guidance.enabled&fight_remains<(cooldown.convoke_the_spirits.remains+60))|(!talent.ashamanes_guidance.enabled&fight_remains<(cooldown.convoke_the_spirits.remains+120))) VarLastConvoke = FightRemains > S.ConvoketheSpirits:CooldownRemains() + 3 and ((S.AshamanesGuidance:IsAvailable() and FightRemains < S.ConvoketheSpirits:CooldownRemains() + 60) or ((not S.AshamanesGuidance:IsAvailable()) and FightRemains < S.ConvoketheSpirits:CooldownRemains() + 12)) -- variable,name=lastZerk,value=fight_remains>(30+(cooldown.bs_inc.remains%1.6))&((talent.berserk_heart_of_the_lion.enabled&fight_remains<(90+(cooldown.bs_inc.remains%1.6)))|(!talent.berserk_heart_of_the_lion.enabled&fight_remains<(180+cooldown.bs_inc.remains))) VarLastZerk = FightRemains > (30 + (BsInc:CooldownRemains() / 1.6)) and ((S.BerserkHeartoftheLion:IsAvailable() and FightRemains < (90 + (BsInc:CooldownRemains() / 1.6))) or ((not S.BerserkHeartoftheLion:IsAvailable()) and FightRemains < (180 + BsInc:CooldownRemains()))) -- variable,name=zerk_biteweave,op=reset VarZerkBiteweave = Settings.Feral.UseZerkBiteweave -- variable,name=regrowth,op=reset VarRegrowth = Settings.Feral.ShowHealSpells -- variable,name=lazy_swipe,op=reset VarLazySwipe = Settings.Feral.UseLazySwipe end local function Clearcasting() -- thrash_cat,if=refreshable&!talent.thrashing_claws.enabled if S.Thrash:IsReady() and (Target:DebuffRefreshable(S.ThrashDebuff) and not S.ThrashingClaws:IsAvailable()) then if Cast(S.Thrash, nil, nil, not Target:IsInMeleeRange(11)) then return "thrash clearcasting 2"; end end -- swipe_cat,if=spell_targets.swipe_cat>1 if S.Swipe:IsReady() and (EnemiesCount11y > 1) then if Cast(S.Swipe, nil, nil, not Target:IsInMeleeRange(11)) then return "swipe clearcasting 4"; end end -- brutal_slash,if=spell_targets.brutal_slash>2 if S.BrutalSlash:IsReady() and (EnemiesCount11y > 2) then if Cast(S.BrutalSlash, nil, nil, not Target:IsInMeleeRange(8)) then return "brutal_slash clearcasting 6"; end end -- shred if S.Shred:IsReady() then if Cast(S.Shred, nil, nil, not Target:IsInMeleeRange(8)) then return "shred clearcasting 8"; end end end local function Builder() -- run_action_list,name=clearcasting,if=buff.clearcasting.react if (Player:BuffUp(S.Clearcasting)) then local ShouldReturn = Clearcasting(); if ShouldReturn then return ShouldReturn; end if HR.CastAnnotated(S.Pool, false, "WAIT") then return "Pool for Clearcasting"; end end -- brutal_slash,if=cooldown.brutal_slash.full_recharge_time<4 if S.BrutalSlash:IsReady() and (S.BrutalSlash:FullRechargeTime() < 4) then if Cast(S.BrutalSlash, nil, nil, not Target:IsInMeleeRange(11)) then return "brutal_slash builder 2"; end end -- pool_resource,if=!action.rake.ready&(dot.rake.refreshable|(buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier&dot.rake.remains>6))&!buff.clearcasting.react if (not S.Rake:IsReady()) and (Target:DebuffRefreshable(S.RakeDebuff) or (Player:BuffUp(S.SuddenAmbushBuff) and Player:PMultiplier(S.Rake) > Target:PMultiplier(S.Rake) and Target:DebuffRemains(S.RakeDebuff) > 6)) and Player:BuffDown(S.Clearcasting) then if HR.CastAnnotated(S.Pool, false, "WAIT") then return "Pool for Rake in Builder()"; end end -- shadowmeld,if=action.rake.ready&!buff.sudden_ambush.up&(dot.rake.refreshable|dot.rake.pmultiplier<1.4)&!buff.prowl.up&!buff.apex_predators_craving.up if S.Shadowmeld:IsCastable() and (S.Rake:IsReady() and Player:BuffDown(S.SuddenAmbushBuff) and (Target:DebuffRefreshable(S.RakeDebuff) or Target:PMultiplier(S.Rake) < 1.4) and Player:BuffDown(S.Prowl) and Player:BuffDown(S.ApexPredatorsCravingBuff)) then if Cast(S.Shadowmeld, Settings.Commons.OffGCDasOffGCD.Racials) then return "shadowmeld builder 4"; end end -- rake,if=refreshable|(buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier) if S.Rake:IsReady() and (Target:DebuffRefreshable(S.RakeDebuff) or (Player:BuffUp(S.SuddenAmbushBuff) and Player:PMultiplier(S.Rake) > Target:PMultiplier(S.Rake))) then if Cast(S.Rake, nil, nil, not Target:IsInMeleeRange(8)) then return "rake builder 6"; end end -- run_action_list,name=clearcasting,if=buff.clearcasting.react -- Note: APL notes this is in here a second time to avoid Moonfire being cast during Clearcasting. -- The profile should work without this, but keeping it here, since it can't hurt anything to have it twice. if (Player:BuffUp(S.Clearcasting)) then local ShouldReturn = Clearcasting(); if ShouldReturn then return ShouldReturn; end if HR.CastAnnotated(S.Pool, false, "WAIT") then return "Pool for Clearcasting"; end end -- moonfire_cat,target_if=refreshable if S.LIMoonfire:IsReady() then if Everyone.CastCycle(S.LIMoonfire, Enemies11y, EvaluateCycleLIMoonfire, not Target:IsSpellInRange(S.LIMoonfire)) then return "moonfire_cat builder 8"; end end -- thrash_cat,target_if=refreshable&!talent.thrashing_claws.enabled if S.Thrash:IsCastable() and (Target:DebuffRefreshable(S.ThrashDebuff) and not S.ThrashingClaws:IsAvailable()) then if Cast(S.Thrash, nil, nil, not Target:IsInMeleeRange(11)) then return "thrash builder 10"; end end -- brutal_slash if S.BrutalSlash:IsReady() then if Cast(S.BrutalSlash, nil, nil, not Target:IsInMeleeRange(11)) then return "brutal_slash builder 12"; end end -- swipe_cat,if=spell_targets.swipe_cat>1|(talent.wild_slashes.enabled&(debuff.dire_fixation.up|!talent.dire_fixation.enabled)) if S.Swipe:IsReady() and (EnemiesCount11y > 1 or (S.WildSlashes:IsAvailable() and (Target:DebuffUp(S.DireFixationDebuff) or not S.DireFixation:IsAvailable()))) then if Cast(S.Swipe, nil, nil, not Target:IsInMeleeRange(11)) then return "swipe builder 14"; end end -- shred if S.Shred:IsReady() then if Cast(S.Shred, nil, nil, not Target:IsInMeleeRange(8)) then return "shred builder 16"; end end end local function AoeBuilder() -- brutal_slash,target_if=min:target.time_to_die,if=cooldown.brutal_slash.full_recharge_time<4|target.time_to_die<5 if S.BrutalSlash:IsReady() then if Everyone.CastTargetIf(S.BrutalSlash, Enemies11y, "min", EvaluateTargetIfFilterTTD, EvaluateTargetIfBrutalSlashAoeBuilder, not Target:IsInMeleeRange(11)) then return "brutal_slash aoe_builder 2"; end end -- thrash_cat,target_if=refreshable,if=buff.clearcasting.react|(spell_targets.thrash_cat>10|(spell_targets.thrash_cat>5&!talent.double_clawed_rake.enabled))&!talent.thrashing_claws if S.Thrash:IsReady() and (Player:BuffUp(S.Clearcasting) or (EnemiesCount11y > 10 or (EnemiesCount11y > 5 and not S.DoubleClawedRake:IsAvailable())) and not S.ThrashingClaws:IsAvailable()) then if Everyone.CastCycle(S.Thrash, Enemies11y, EvaluateCycleThrash, not Target:IsInMeleeRange(11)) then return "thrash aoe_builder 4"; end end -- shadowmeld,target_if=max:druid.rake.ticks_gained_on_refresh,if=action.rake.ready&!buff.sudden_ambush.up&(dot.rake.refreshable|dot.rake.pmultiplier<1.4)&!buff.prowl.up&!buff.apex_predators_craving.up if S.Shadowmeld:IsReady() and (S.Rake:IsReady() and Player:BuffDown(S.SuddenAmbushBuff) and (DebuffRefreshAny(Enemies11y, S.RakeDebuff) or LowRakePMult(Enemies11y) < 1.4) and Player:BuffDown(S.Prowl) and Player:BuffDown(S.ApexPredatorsCravingBuff)) then if Cast(S.Shadowmeld, Settings.Commons.OffGCDasOffGCD.Racials) then return "shadowmeld aoe_builder 6"; end end -- shadowmeld,target_if=druid.rake.ticks_gained_on_refresh,if=action.rake.ready&!buff.sudden_ambush.up&dot.rake.pmultiplier<1.4&!buff.prowl.up&!buff.apex_predators_craving.up if S.Shadowmeld:IsReady() and (S.Rake:IsReady() and Player:BuffDown(S.SuddenAmbushBuff) and LowRakePMult(Enemies11y) < 1.4 and Player:BuffDown(S.Prowl) and Player:BuffDown(S.ApexPredatorsCravingBuff)) then if Cast(S.Shadowmeld, Settings.Commons.OffGCDasOffGCD.Racials) then return "shadowmeld aoe_builder 8"; end end -- rake,target_if=max:druid.rake.ticks_gained_on_refresh,if=buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier if S.Rake:IsReady() and (Player:BuffUp(S.SuddenAmbushBuff)) then if Everyone.CastTargetIf(S.Rake, EnemiesMelee, "max", EvaluateTargetIfFilterRakeTicks, EvaluateTargetIfRakeAoeBuilder, not Target:IsInMeleeRange(8)) then return "rake aoe_builder 10"; end end -- rake,target_if=buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier|refreshable if S.Rake:IsReady() then if Everyone.CastCycle(S.Rake, EnemiesMelee, EvaluateCycleRakeAoeBuilder, not Target:IsInMeleeRange(8)) then return "rake aoe_builder 12"; end end -- thrash_cat,target_if=refreshable if S.Thrash:IsReady() and (Target:DebuffRefreshable(S.ThrashDebuff)) then if Cast(S.Thrash, nil, nil, not Target:IsInMeleeRange(11)) then return "thrash aoe_builder 14"; end end -- brutal_slash if S.BrutalSlash:IsReady() then if Cast(S.BrutalSlash, nil, nil, not Target:IsInMeleeRange(11)) then return "brutal_slash aoe_builder 16"; end end -- moonfire_cat,target_if=max:(3*refreshable)+dot.adaptive_swarm_damage.ticking,if=spell_targets.swipe_cat<5&dot.moonfire.refreshable if S.LIMoonfire:IsReady() and (EnemiesCount11y < 5) then if Everyone.CastTargetIf(S.LIMoonfire, Enemies11y, "max", EvaluateTargetIfFilterLIMoonfire, EvaluateTargetIfLIMoonfireAoEBuilder, not Target:IsSpellInRange(S.LIMoonfire)) then return "moonfire_cat aoe_builders 18"; end end -- swipe_cat if S.Swipe:IsReady() then if Cast(S.Swipe, nil, nil, not Target:IsInMeleeRange(11)) then return "swipe aoe_builder 20"; end end -- moonfire_cat,target_if=max:(3*refreshable)+dot.adaptive_swarm_damage.ticking,if=dot.moonfire.refreshable if S.LIMoonfire:IsReady() then if Everyone.CastTargetIf(S.LIMoonfire, Enemies11y, "max", EvaluateTargetIfFilterLIMoonfire, EvaluateTargetIfLIMoonfireAoEBuilder, not Target:IsSpellInRange(S.LIMoonfire)) then return "moonfire_cat aoe_builders 22"; end end -- shred,target_if=max:target.time_to_die,if=(spell_targets.swipe_cat<4|talent.dire_fixation.enabled)&!buff.sudden_ambush.up&!(variable.lazy_swipe&talent.wild_slashes) if S.Shred:IsReady() and ((EnemiesCount11y < 4 or S.DireFixation:IsAvailable()) and Player:BuffDown(S.SuddenAmbushBuff) and not (VarLazySwipe and S.WildSlashes:IsAvailable())) then if Everyone.CastTargetIf(S.Shred, EnemiesMelee, "max", EvaluateTargetIfFilterTTD, nil, not Target:IsInMeleeRange(8)) then return "shred aoe_builder 24"; end end -- thrash_cat if S.Thrash:IsReady() then if Cast(S.Thrash, nil, nil, not Target:IsInMeleeRange(11)) then return "thrash aoe_builder 26"; end end end local function Bloodtalons() -- brutal_slash,target_if=min:target.time_to_die,if=(cooldown.brutal_slash.full_recharge_time<4|target.time_to_die<5)&(buff.bt_brutal_slash.down&(buff.bs_inc.up|variable.need_bt)) if S.BrutalSlash:IsReady() then if Everyone.CastTargetIf(S.BrutalSlash, Enemies11y, "min", EvaluateTargetIfFilterTTD, EvaluateTargetIfBrutalSlashBT, not Target:IsInMeleeRange(11)) then return "brutal_slash bloodtalons 2"; end end -- prowl,if=action.rake.ready&gcd.remains=0&!buff.sudden_ambush.up&(dot.rake.refreshable|dot.rake.pmultiplier<1.4)&!buff.shadowmeld.up&buff.bt_rake.down&!buff.prowl.up&!buff.apex_predators_craving.up if S.Prowl:IsReady() and (S.Rake:IsReady() and Player:BuffDown(S.SuddenAmbushBuff) and (Target:DebuffRefreshable(S.RakeDebuff) or Target:PMultiplier(S.Rake) < 1.4) and Player:BuffDown(S.Shadowmeld) and BTBuffDown(S.Rake) and Player:BuffDown(S.Prowl) and Player:BuffDown(S.ApexPredatorsCravingBuff)) then if Cast(S.Prowl) then return "prowl bloodtalons 4"; end end -- shadowmeld,if=action.rake.ready&!buff.sudden_ambush.up&(dot.rake.refreshable|dot.rake.pmultiplier<1.4)&!buff.prowl.up&buff.bt_rake.down&cooldown.feral_frenzy.remains<44&!buff.apex_predators_craving.up if S.Shadowmeld:IsReady() and (S.Rake:IsReady() and Player:BuffDown(S.SuddenAmbushBuff) and (Target:DebuffRefreshable(S.RakeDebuff) or Target:PMultiplier(S.Rake) < 1.4) and Player:BuffDown(S.Prowl) and BTBuffDown(S.Rake) and S.FeralFrenzy:CooldownRemains() < 44 and Player:BuffDown(S.ApexPredatorsCravingBuff)) then if Cast(S.Shadowmeld, Settings.Commons.OffGCDasOffGCD.Racials) then return "shadowmeld bloodtalons 6"; end end -- rake,target_if=max:druid.rake.ticks_gained_on_refresh,if=(refreshable|buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier)&buff.bt_rake.down if S.Rake:IsReady() and (BTBuffDown(S.Rake)) then if Everyone.CastTargetIf(S.Rake, EnemiesMelee, "max", EvaluateTargetIfFilterRakeTicks, EvaluateTargetIfRakeBloodtalons, not Target:IsInRange(8)) then return "rake bloodtalons 8"; end end -- rake,target_if=buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier&buff.bt_rake.down if S.Rake:IsReady() and (BTBuffDown(S.Rake)) then if Everyone.CastCycle(S.Rake, EnemiesMelee, EvaluateCycleRake, not Target:IsInMeleeRange(8)) then return "rake bloodtalons 10"; end end -- shred,if=buff.bt_shred.down&buff.clearcasting.react&spell_targets.swipe_cat=1 if S.Shred:IsReady() and (BTBuffDown(S.Shred) and Player:BuffUp(S.Clearcasting) and EnemiesCount11y == 1) then if Cast(S.Shred, nil, nil, not Target:IsInMeleeRange(8)) then return "shred bloodtalons 12"; end end -- thrash_cat,target_if=refreshable,if=buff.bt_thrash.down&buff.clearcasting.react&spell_targets.swipe_cat=1&!talent.thrashing_claws.enabled if S.Thrash:IsReady() and (DebuffRefreshAny(Enemies11y, S.ThrashDebuff) and BTBuffDown(S.Thrash) and Player:BuffUp(S.Clearcasting) and EnemiesCount11y == 1 and not S.ThrashingClaws:IsAvailable()) then if Cast(S.Thrash, nil, nil, not Target:IsInMeleeRange(11)) then return "thrash bloodtalons 14"; end end -- brutal_slash,if=buff.bt_brutal_slash.down if S.BrutalSlash:IsReady() and (BTBuffDown(S.BrutalSlash)) then if Cast(S.BrutalSlash, nil, nil, not Target:IsInMeleeRange(8)) then return "brutal_slash bloodtalons 16"; end end -- moonfire_cat,if=refreshable&buff.bt_moonfire.down&spell_targets.swipe_cat=1 if S.LIMoonfire:IsReady() and (Target:DebuffRefreshable(S.LIMoonfireDebuff) and BTBuffDown(S.LIMoonfire) and EnemiesCount11y == 1) then if Cast(S.LIMoonfire, nil, nil, not Target:IsSpellInRange(S.LIMoonfire)) then return "moonfire_cat bloodtalons 18"; end end -- thrash_cat,target_if=refreshable,if=buff.bt_thrash.down&!talent.thrashing_claws.enabled if S.Thrash:IsReady() and (DebuffRefreshAny(Enemies11y, S.ThrashDebuff) and BTBuffDown(S.Thrash) and not S.ThrashingClaws:IsAvailable()) then if Cast(S.Thrash, nil, nil, not Target:IsInMeleeRange(11)) then return "thrash bloodtalons 20"; end end -- shred,if=buff.bt_shred.down&spell_targets.swipe_cat=1&(!talent.wild_slashes.enabled|(!debuff.dire_fixation.up&talent.dire_fixation.enabled)) if S.Shred:IsReady() and (BTBuffDown(S.Shred) and EnemiesCount11y == 1 and ((not S.WildSlashes:IsAvailable()) or (Target:DebuffDown(S.DireFixationDebuff) and S.DireFixation:IsAvailable()))) then if Cast(S.Shred, nil, nil, not Target:IsInMeleeRange(8)) then return "shred bloodtalons 22"; end end -- swipe_cat,if=buff.bt_swipe.down&talent.wild_slashes.enabled if S.Swipe:IsReady() and (BTBuffDown(S.Swipe) and S.WildSlashes:IsAvailable()) then if Cast(S.Swipe, nil, nil, not Target:IsInMeleeRange(11)) then return "swipe bloodtalons 24"; end end -- moonfire_cat,target_if=max:(3*refreshable)+dot.adaptive_swarm_damage.ticking,if=buff.bt_moonfire.down&spell_targets.swipe_cat<5 if S.LIMoonfire:IsReady() and (BTBuffDown(S.LIMoonfire) and EnemiesCount11y < 5) then if Everyone.CastTargetIf(S.LIMoonfire, Enemies11y, "max", EvaluateTargetIfFilterLIMoonfire, nil, not Target:IsSpellInRange(S.LIMoonfire)) then return "moonfire_cat bloodtalons 18"; end end -- swipe_cat,if=buff.bt_swipe.down if S.Swipe:IsReady() and (BTBuffDown(S.Swipe)) then if Cast(S.Swipe, nil, nil, not Target:IsInMeleeRange(8)) then return "swipe bloodtalons 28"; end end -- moonfire_cat,target_if=max:(3*refreshable)+dot.adaptive_swarm_damage.ticking,if=buff.bt_moonfire.down if S.LIMoonfire:IsReady() and (BTBuffDown(S.LIMoonfire)) then if Everyone.CastTargetIf(S.LIMoonfire, Enemies11y, "max", EvaluateTargetIfFilterLIMoonfire, nil, not Target:IsSpellInRange(S.LIMoonfire)) then return "moonfire_cat bloodtalons 30"; end end -- shred,target_if=max:target.time_to_die,if=(spell_targets>5|talent.dire_fixation.enabled)&buff.bt_shred.down&!buff.sudden_ambush.up&!(variable.lazy_swipe&talent.wild_slashes) if S.Shred:IsReady() and ((EnemiesCountMelee > 5 or S.DireFixation:IsAvailable()) and BTBuffDown(S.Shred) and Player:BuffDown(S.SuddenAmbushBuff) and not (VarLazySwipe and S.WildSlashes:IsAvailable())) then if Everyone.CastTargetIf(S.Shred, EnemiesMelee, "max", EvaluateTargetIfFilterTTD, nil, not Target:IsInMeleeRange(8)) then return "shred bloodtalons 32"; end end -- thrash_cat,if=buff.bt_thrash.down if S.Thrash:IsReady() and (BTBuffDown(S.Thrash)) then if Cast(S.Thrash, nil, nil, not Target:IsInMeleeRange(8)) then return "thrash bloodtalons 34"; end end -- rake,target_if=min:(25*(persistent_multiplier4&!talent.dire_fixation|talent.wild_slashes&variable.lazy_swipe) if S.Rake:IsReady() and (BTBuffDown(S.Rake) and (EnemiesCount11y > 4 and (not S.DireFixation:IsAvailable()) or S.WildSlashes:IsAvailable() and VarLazySwipe)) then if Everyone.CastTargetIf(S.Rake, EnemiesCountMelee, "min", EvaluateTargetIfFilterRake, nil, not Target:IsInMeleeRange(8)) then return "rake bloodtalons 36"; end end end local function Finisher() -- primal_wrath,if=((dot.primal_wrath.refreshable&!talent.circle_of_life_and_death.enabled)|dot.primal_wrath.remains<6|(talent.tear_open_wounds.enabled|(spell_targets.swipe_cat>4&!talent.rampant_ferocity.enabled)))&spell_targets.primal_wrath>1&talent.primal_wrath.enabled if S.PrimalWrath:IsReady() and (((Target:DebuffRefreshable(S.PrimalWrath) and not S.CircleofLifeandDeath:IsAvailable()) or Target:DebuffRemains(S.PrimalWrath) < 6 or (S.TearOpenWounds:IsAvailable() or (EnemiesCount11y > 4 and not S.RampantFerocity:IsAvailable()))) and EnemiesCount11y > 1 and S.PrimalWrath:IsAvailable()) then if Cast(S.PrimalWrath, nil, nil, not Target:IsInMeleeRange(11)) then return "primal_wrath finisher 2"; end end -- rip,target_if=refreshable&(!talent.primal_wrath.enabled|spell_targets.swipe_cat=1) if S.Rip:IsReady() then if Everyone.CastCycle(S.Rip, EnemiesMelee, EvaluateCycleRip, not Target:IsInRange(8)) then return "rip finisher 4"; end end -- pool_resource,for_next=1,if=!action.tigers_fury.ready&buff.apex_predators_craving.down -- ferocious_bite,max_energy=1,target_if=max:target.time_to_die,if=buff.apex_predators_craving.down&(!buff.bs_inc.up|(buff.bs_inc.up&!talent.soul_of_the_forest.enabled)) if S.FerociousBite:IsReady() and (Player:BuffDown(S.ApexPredatorsCravingBuff) and (Player:BuffDown(BsInc) or (Player:BuffUp(BsInc) and not S.SouloftheForest:IsAvailable()))) then if (not S.TigersFury:IsReady()) and Player:BuffDown(S.ApexPredatorsCravingBuff) then if CastPooling(S.FerociousBite, Player:EnergyTimeToX(50)) then return "ferocious_bite finisher 6"; end elseif Player:Energy() >= 50 then if Everyone.CastTargetIf(S.FerociousBite, EnemiesMelee, "max", EvaluateTargetIfFilterTTD, nil, not Target:IsInMeleeRange(8)) then return "ferocious_bite finisher 8"; end end end -- ferocious_bite,target_if=max:target.time_to_die,if=(buff.bs_inc.up&talent.soul_of_the_forest.enabled)|buff.apex_predators_craving.up if S.FerociousBite:IsReady() and ((Player:BuffUp(BsInc) and S.SouloftheForest:IsAvailable()) or Player:BuffUp(S.ApexPredatorsCravingBuff)) then if Everyone.CastTargetIf(S.FerociousBite, EnemiesMelee, "max", EvaluateTargetIfFilterTTD, nil, not Target:IsInMeleeRange(8)) then return "ferocious_bite finisher 10"; end end end local function Berserk() -- ferocious_bite,target_if=max:target.time_to_die,if=combo_points=5&dot.rip.remains>8&variable.zerk_biteweave&spell_targets.swipe_cat>1 if S.FerociousBite:IsReady() and (ComboPoints == 5 and VarZerkBiteweave and EnemiesCount11y > 1) then if Everyone.CastTargetIf(S.FerociousBite, EnemiesMelee, "max", EvaluateTargetIfFilterTTD, EvaluateTargetIfFerociousBiteBerserk, not Target:IsInMeleeRange(8)) then return "ferocious_bite berserk 2"; end end -- call_action_list,name=finisher,if=combo_points=5&!(buff.overflowing_power.stack<=1&active_bt_triggers=2&buff.bloodtalons.stack<=1) if ComboPoints == 5 and not (Player:BuffStack(S.OverflowingPowerBuff) <= 1 and CountActiveBtTriggers() == 2 and Player:BuffStack(S.BloodtalonsBuff) <= 1) then local ShouldReturn = Finisher(); if ShouldReturn then return ShouldReturn; end end -- call_action_list,name=bloodtalons,if=spell_targets.swipe_cat>1 if EnemiesCount11y > 1 then local ShouldReturn = Bloodtalons(); if ShouldReturn then return ShouldReturn; end end -- prowl,if=!(buff.bt_rake.up&active_bt_triggers=2)&(action.rake.ready&gcd.remains=0&!buff.sudden_ambush.up&(dot.rake.refreshable|dot.rake.pmultiplier<1.4)&!buff.shadowmeld.up&cooldown.feral_frenzy.remains<44&!buff.apex_predators_craving.up) if S.Prowl:IsReady() and ((not (BTBuffUp(S.Rake) and CountActiveBtTriggers() == 2)) and (S.Rake:IsReady() and Player:BuffDown(S.SuddenAmbushBuff) and (Target:DebuffRefreshable(S.RakeDebuff) or Target:PMultiplier(S.Rake) < 1.4) and Player:BuffDown(S.Shadowmeld) and S.FeralFrenzy:CooldownRemains() < 44 and Player:BuffDown(S.ApexPredatorsCravingBuff))) then if Cast(S.Prowl) then return "prowl berserk 4"; end end -- shadowmeld,if=!(buff.bt_rake.up&active_bt_triggers=2)&action.rake.ready&!buff.sudden_ambush.up&(dot.rake.refreshable|dot.rake.pmultiplier<1.4)&!buff.prowl.up&!buff.apex_predators_craving.up if S.Shadowmeld:IsCastable() and ((not (BTBuffUp(S.Rake) and CountActiveBtTriggers() == 2)) and S.Rake:IsReady() and Player:BuffDown(S.SuddenAmbushBuff) and (Target:DebuffRefreshable(S.RakeDebuff) or Target:PMultiplier(S.Rake) < 1.4) and Player:BuffDown(S.Prowl) and Player:BuffDown(S.ApexPredatorsCravingBuff)) then if Cast(S.Shadowmeld, Settings.Commons.OffGCDasOffGCD.Racials) then return "shadowmeld berserk 6"; end end -- rake,if=!(buff.bt_rake.up&active_bt_triggers=2)&(refreshable|(buff.sudden_ambush.up&persistent_multiplier>dot.rake.pmultiplier)) if S.Rake:IsReady() and ((not (BTBuffUp(S.Rake) and CountActiveBtTriggers() == 2)) and (Target:DebuffRefreshable(S.RakeDebuff) or (Player:BuffUp(S.SuddenAmbushBuff) and Player:PMultiplier(S.Rake) > Target:PMultiplier(S.Rake)))) then if Cast(S.Rake, nil, nil, not Target:IsInMeleeRange(8)) then return "rake berserk 8"; end end -- shred,if=(active_bt_triggers=2|(talent.dire_fixation.enabled&!debuff.dire_fixation.up))&buff.bt_shred.down if S.Shred:IsReady() and ((CountActiveBtTriggers() == 2 or (S.DireFixation:IsAvailable() and Target:DebuffDown(S.DireFixationDebuff))) and BTBuffDown(S.Shred)) then if Cast(S.Shred, nil, nil, not Target:IsInMeleeRange(8)) then return "shred berserk 10"; end end -- brutal_slash,if=active_bt_triggers=2&buff.bt_brutal_slash.down if S.BrutalSlash:IsReady() and (CountActiveBtTriggers() == 2 and BTBuffDown(S.BrutalSlash)) then if Cast(S.BrutalSlash, nil, nil, not Target:IsInMeleeRange(11)) then return "brutal_slash berserk 12"; end end -- moonfire_cat,if=active_bt_triggers=2&buff.bt_moonfire.down if S.LIMoonfire:IsReady() and (CountActiveBtTriggers() == 2 and BTBuffDown(S.LIMoonfire)) then if Cast(S.LIMoonfire, nil, nil, not Target:IsSpellInRange(S.LIMoonfire)) then return "moonfire_cat berserk 14"; end end -- thrash_cat,if=active_bt_triggers=2&buff.bt_thrash.down&!talent.thrashing_claws&variable.need_bt&(refreshable|talent.brutal_slash.enabled) if S.Thrash:IsReady() and (CountActiveBtTriggers() == 2 and BTBuffDown(S.Thrash) and (not S.ThrashingClaws:IsAvailable()) and VarNeedBT and (Target:DebuffRefreshable(S.ThrashDebuff) or S.BrutalSlash:IsAvailable())) then if Cast(S.Thrash, nil, nil, not Target:IsInMeleeRange(11)) then return "thrash berserk 16"; end end -- moonfire_cat,target_if=refreshable if S.LIMoonfire:IsReady() then if Everyone.CastCycle(S.LIMoonfire, Enemies11y, EvaluateCycleLIMoonfire, not Target:IsSpellInRange(S.LIMoonfire)) then return "moonfire_cat berserk 18"; end end -- brutal_slash,if=cooldown.brutal_slash.charges>1 if S.BrutalSlash:IsReady() and (S.BrutalSlash:Charges() > 1) then if Cast(S.BrutalSlash, nil, nil, not Target:IsInMeleeRange(11)) then return "brutal_slash berserk 20"; end end -- shred if S.Shred:IsReady() then if Cast(S.Shred, nil, nil, not Target:IsInMeleeRange(8)) then return "shred berserk 22"; end end end local function Cooldown() if Settings.Commons.Enabled.Trinkets then -- use_item,name=algethar_puzzle_box,if=fight_remains<35|(!variable.align_3minutes) if I.AlgetharPuzzleBox:IsEquippedAndReady() and (FightRemains < 35 or not VarAlign3Mins) then if Cast(I.AlgetharPuzzleBox, nil, Settings.Commons.DisplayStyle.Trinkets) then return "algethar_puzzle_box cooldown 2"; end end -- use_item,name=algethar_puzzle_box,if=variable.align_3minutes&(cooldown.bs_inc.remains<3&(!variable.lastZerk|!variable.lastConvoke|(variable.lastConvoke&cooldown.convoke_the_spirits.remains<13))) if I.AlgetharPuzzleBox:IsEquippedAndReady() and (VarAlign3Mins and (BsInc:CooldownRemains() < 3 and ((not VarLastZerk) or (not VarLastConvoke) or (VarLastConvoke and S.ConvoketheSpirits:CooldownRemains() < 13)))) then if Cast(I.AlgetharPuzzleBox, nil, Settings.Commons.DisplayStyle.Trinkets) then return "algethar_puzzle_box cooldown 4"; end end end -- incarnation,target_if=max:target.time_to_die,if=(target.time_to_die25)|target.time_to_die=fight_remains local HighTTD, _ = HighestTTD(Enemies11y) if S.Incarnation:IsReady() and ((HighTTD < FightRemains and HighTTD > 25) or HighTTD == FightRemains) then if Cast(S.Incarnation, Settings.Feral.GCDasOffGCD.BsInc) then return "incarnation cooldown 6"; end end -- berserk,target_if=max:target.time_to_die,if=((target.time_to_die18)|target.time_to_die=fight_remains)&((!variable.lastZerk)|(fight_remains<23)|(variable.lastZerk&!variable.lastConvoke)|(variable.lastConvoke&cooldown.convoke_the_spirits.remains<10)) if S.Berserk:IsReady() and (((HighTTD < FightRemains and HighTTD > 18) or HighTTD == FightRemains) and ((not VarLastZerk) or FightRemains < 23 or (VarLastZerk and not VarLastConvoke) or (VarLastConvoke and S.ConvoketheSpirits:CooldownRemains() < 10))) then if Cast(S.Berserk, Settings.Feral.GCDasOffGCD.BsInc) then return "berserk cooldown 8"; end end -- berserking,if=!variable.align_3minutes|buff.bs_inc.up if S.Berserking:IsCastable() and ((not VarAlign3Mins) or Player:BuffUp(BsInc)) then if Cast(S.Berserking, Settings.Commons.OffGCDasOffGCD.Racials) then return "berserking cooldown 12"; end end -- use_item,name=mirror_of_fractured_tomorrows|irideus_fragment,if=!variable.align_3minutes|buff.bs_inc.up|(variable.lastConvoke&!variable.lastZerk&cooldown.convoke_the_spirits.remains=0) if (not VarAlign3Mins) or Player:BuffUp(BsInc) or (VarLastConvoke and (not VarLastZerk) and S.ConvoketheSpirits:CooldownUp()) then if I.MirrorofFracturedTomorrows:IsEquippedAndReady() then if Cast(I.MirrorofFracturedTomorrows, nil, Settings.Commons.DisplayStyle.Trinkets) then return "mirror_of_fractured_tomorrows cooldown "; end end if I.IrideusFragment:IsEquippedAndReady() then if Cast(I.IrideusFragment, nil, Settings.Commons.DisplayStyle.Trinkets) then return "irideus_fragment cooldown "; end end end -- potion,if=buff.bs_inc.up|fight_remains<32|(!variable.lastZerk&variable.lastConvoke&cooldown.convoke_the_spirits.remains<10) if Settings.Commons.Enabled.Potions and (Player:BuffUp(BsInc) or FightRemains < 32 or ((not VarLastZerk) and VarLastConvoke and S.ConvoketheSpirits:CooldownRemains() < 10)) then local PotionSelected = Everyone.PotionSelected() if PotionSelected and PotionSelected:IsReady() then if Cast(PotionSelected, nil, Settings.Commons.DisplayStyle.Potions) then return "potion cooldown 14"; end end end -- convoke_the_spirits,target_if=max:target.time_to_die,if=((target.time_to_die5-talent.ashamanes_guidance.enabled)|target.time_to_die=fight_remains)&(fight_remains<5|(dot.rip.remains>5&buff.tigers_fury.up&(combo_points<2|(buff.bs_inc.up&combo_points<=3))))&(!variable.lastConvoke|buff.potion.up|(time+fight_remains+10)%%300>time%%300)&(talent.dire_fixation.enabled&debuff.dire_fixation.up|!talent.dire_fixation.enabled|spell_targets.swipe_cat>1) if S.ConvoketheSpirits:IsReady() then if Everyone.CastTargetIf(S.ConvoketheSpirits, Enemies11y, "max", EvaluateTargetIfFilterTTD, EvaluateTargetIfConvokeCD, not Target:IsInMeleeRange(8)) then return "convoke_the_spirits cooldown 16"; end end -- use_item,name=manic_grieftorch,target_if=max:target.time_to_die,if=energy.deficit>40 if Settings.Commons.Enabled.Trinkets and I.ManicGrieftorch:IsEquippedAndReady() and (Player:EnergyDeficit() > 40) then if Everyone.CastTargetIf(I.ManicGrieftorch, Enemies11y, "max", EvaluateTargetIfFilterTTD, nil, not Target:IsInRange(40), nil, Settings.Commons.DisplayStyle.Trinkets) then return "manic_grieftorch cooldown 18"; end end -- use_items if Settings.Commons.Enabled.Trinkets or Settings.Commons.Enabled.Items then local ItemToUse, ItemSlot, ItemRange = Player:GetUseableItems(OnUseExcludes) if ItemToUse then local DisplayStyle = Settings.Commons.DisplayStyle.Trinkets if ItemSlot ~= 13 and ItemSlot ~= 14 then DisplayStyle = Settings.Commons.DisplayStyle.Items end if ((ItemSlot == 13 or ItemSlot == 14) and Settings.Commons.Enabled.Trinkets) or (ItemSlot ~= 13 and ItemSlot ~= 14 and Settings.Commons.Enabled.Items) then if Cast(ItemToUse, nil, DisplayStyle, not Target:IsInRange(ItemRange)) then return "Generic use_items for " .. ItemToUse:Name(); end end end end end local function APL() -- Update Enemies if AoEON() then EnemiesMelee = Player:GetEnemiesInMeleeRange(8) Enemies11y = Player:GetEnemiesInMeleeRange(11) EnemiesCountMelee = #EnemiesMelee EnemiesCount11y = #Enemies11y else EnemiesMelee = {} Enemies11y = {} EnemiesCountMelee = 1 EnemiesCount11y = 1 end -- Combo Points ComboPoints = Player:ComboPoints() ComboPointsDeficit = Player:ComboPointsDeficit() if Everyone.TargetIsValid() or Player:AffectingCombat() then -- Calculate fight_remains BossFightRemains = HL.BossFightRemains(nil, true) FightRemains = BossFightRemains if FightRemains == 11111 then FightRemains = HL.FightRemains(Enemies11y, false) end end -- cat_form OOC, if setting is true if S.CatForm:IsCastable() and Settings.Feral.ShowCatFormOOC then if Cast(S.CatForm) then return "cat_form ooc"; end end if Everyone.TargetIsValid() then -- Precombat if not Player:AffectingCombat() then local ShouldReturn = Precombat(); if ShouldReturn then return ShouldReturn; end end -- Interrupts local ShouldReturn = Everyone.Interrupt(13, S.SkullBash, Settings.Feral.OffGCDasOffGCD.SkullBash, InterruptStuns); if ShouldReturn then return ShouldReturn; end -- prowl,if=(buff.bs_inc.down|!in_combat)&!buff.prowl.up if S.Prowl:IsCastable() and (Player:BuffDown(BsInc) or not Player:AffectingCombat()) then if Cast(S.Prowl) then return "prowl main 2"; end end -- cat_form,if=!buff.cat_form.up if S.CatForm:IsCastable() then if Cast(S.CatForm) then return "cat_form main 4"; end end -- invoke_external_buff,name=power_infusion -- Note: We're not handling external buffs -- call_action_list,name=variables Variables() -- tigers_fury,target_if=min:target.time_to_die,if=talent.convoke_the_spirits.enabled|!talent.convoke_the_spirits.enabled&(!buff.tigers_fury.up|energy.deficit>65)|(target.time_to_die<15&talent.predator.enabled) if S.TigersFury:IsCastable() and (S.ConvoketheSpirits:IsAvailable() or (not S.ConvoketheSpirits:IsAvailable()) and (Player:BuffDown(S.TigersFury) or Player:EnergyDeficit() > 65) or (FightRemains < 15 and S.Predator:IsAvailable())) then if Cast(S.TigersFury, Settings.Feral.OffGCDasOffGCD.TigersFury) then return "tigers_fury main 6"; end end -- rake,target_if=persistent_multiplier>dot.rake.pmultiplier,if=buff.prowl.up|buff.shadowmeld.up if S.Rake:IsReady() and (Player:StealthUp(false, true)) then if Everyone.CastCycle(S.Rake, EnemiesMelee, EvaluateCycleRakeMain, not Target:IsInMeleeRange(8)) then return "rake main 8"; end end -- auto_attack,if=!buff.prowl.up&!buff.shadowmeld.up -- natures_vigil,if=spell_targets.swipe_cat>0 if S.NaturesVigil:IsCastable() then if Cast(S.NaturesVigil, Settings.Feral.OffGCDasOffGCD.NaturesVigil) then return "natures_vigil main 10"; end end -- renewal,if=variable.regrowth if S.Renewal:IsCastable() and (VarRegrowth) then if Cast(S.Renewal, Settings.Feral.GCDasOffGCD.Renewal) then return "renewal main 12"; end end -- adaptive_swarm,target=self,if=talent.unbridled_swarm&spell_targets.swipe_cat<=1&dot.adaptive_swarm_heal.stack<4&dot.adaptive_swarm_heal.remains>4 if S.AdaptiveSwarm:IsReady() and (S.UnbridledSwarm:IsAvailable() and EnemiesCount11y <= 1 and Player:BuffStack(S.AdaptiveSwarmHeal) < 4 and Player:BuffRemains(S.AdaptiveSwarmHeal) > 4) then if HR.CastAnnotated(S.AdaptiveSwarm, false, "SELF") then return "adaptive_swarm self main 14"; end end -- adaptive_swarm,target_if=((!dot.adaptive_swarm_damage.ticking|dot.adaptive_swarm_damage.remains<2)&(dot.adaptive_swarm_damage.stack<3)&!action.adaptive_swarm_damage.in_flight&!action.adaptive_swarm.in_flight)&target.time_to_die>5,if=!(variable.need_bt&active_bt_triggers=2)&(!talent.unbridled_swarm.enabled|spell_targets.swipe_cat=1) if S.AdaptiveSwarm:IsReady() and ((not (VarNeedBT and CountActiveBtTriggers() == 2)) and ((not S.UnbridledSwarm:IsAvailable()) or EnemiesCount11y == 1)) then if Everyone.CastCycle(S.AdaptiveSwarm, Enemies11y, EvaluateCycleAdaptiveSwarm, not Target:IsSpellInRange(S.AdaptiveSwarm)) then return "adaptive_swarm main 16"; end end -- adaptive_swarm,target_if=max:((1+dot.adaptive_swarm_damage.stack)*dot.adaptive_swarm_damage.stack<3*time_to_die),if=dot.adaptive_swarm_damage.stack<3&talent.unbridled_swarm.enabled&spell_targets.swipe_cat>1&!(variable.need_bt&active_bt_triggers=2) if S.AdaptiveSwarm:IsReady() and (S.UnbridledSwarm:IsAvailable() and EnemiesCount11y > 1 and not (VarNeedBT and CountActiveBtTriggers() == 2)) then if Everyone.CastTargetIf(S.AdaptiveSwarm, Enemies11y, "max", EvaluateTargetIfFilterAdaptiveSwarm, EvaluateTargetIfAdaptiveSwarm, not Target:IsSpellInRange(S.AdaptiveSwarm)) then return "adaptive_swarm main 18"; end end -- call_action_list,name=cooldown if CDsON() then local ShouldReturn = Cooldown(); if ShouldReturn then return ShouldReturn; end end -- feral_frenzy,target_if=max:target.time_to_die,if=((talent.dire_fixation.enabled&debuff.dire_fixation.up)|!talent.dire_fixation.enabled|spell_targets.swipe_cat>1)&(combo_points<2|combo_points<3&buff.bs_inc.up|time<10) if S.FeralFrenzy:IsReady() and (ComboPoints < 2 or ComboPoints < 3 and Player:BuffUp(BsInc) or HL.CombatTime() < 10) then if Everyone.CastTargetIf(S.FeralFrenzy, EnemiesMelee, "max", EvaluateTargetIfFilterTTD, EvaluateTargetIfFeralFrenzy, not Target:IsInMeleeRange(8)) then return "feral_frenzy main 20"; end end -- ferocious_bite,target_if=max:target.time_to_die,if=buff.apex_predators_craving.up&(spell_targets.swipe_cat=1|!talent.primal_wrath.enabled|!buff.sabertooth.up)&!(variable.need_bt&active_bt_triggers=2) if S.FerociousBite:IsReady() and (Player:BuffUp(S.ApexPredatorsCravingBuff) and (EnemiesCount11y == 1 or (not S.PrimalWrath:IsAvailable()) or Player:BuffDown(S.SabertoothBuff)) and not (VarNeedBT and CountActiveBtTriggers() == 2)) then if Everyone.CastTargetIf(S.FerociousBite, EnemiesMelee, "max", EvaluateTargetIfFilterTTD, nil, not Target:IsInMeleeRange(8)) then return "ferocious_bite main 22"; end end -- call_action_list,name=berserk,if=buff.bs_inc.up if Player:BuffUp(BsInc) then local ShouldReturn = Berserk(); if ShouldReturn then return ShouldReturn; end end -- wait,sec=combo_points=5,if=combo_points=4&buff.predator_revealed.react&energy.deficit>40&spell_targets.swipe_cat=1 if ComboPoints == 4 and Player:BuffUp(S.PredatorRevealedBuff) and Player:EnergyDeficit() > 40 and EnemiesCount11y == 1 then if HR.CastAnnotated(S.Pool, false, "WAIT") then return "Wait for Finisher()"; end end -- call_action_list,name=finisher,if=combo_points>=4&!(combo_points=4&buff.bloodtalons.stack<=1&active_bt_triggers=2&spell_targets.swipe_cat=1) if ComboPoints >= 4 and (not (ComboPoints == 4 and Player:BuffStack(S.BloodtalonsBuff) <= 1 and CountActiveBtTriggers() == 2 and EnemiesCount11y == 1)) then local ShouldReturn = Finisher(); if ShouldReturn then return ShouldReturn; end end -- call_action_list,name=bloodtalons,if=variable.need_bt&!buff.bs_inc.up&combo_points<5 if VarNeedBT and Player:BuffDown(BsInc) and ComboPoints < 5 then local ShouldReturn = Bloodtalons(); if ShouldReturn then return ShouldReturn; end end -- call_action_list,name=aoe_builder,if=spell_targets.swipe_cat>1&talent.primal_wrath.enabled if (EnemiesCount11y > 1 and S.PrimalWrath:IsAvailable()) then local ShouldReturn = AoeBuilder(); if ShouldReturn then return ShouldReturn; end end -- call_action_list,name=builder,if=!buff.bs_inc.up&combo_points<5 if Player:BuffDown(BsInc) and ComboPoints < 5 then local ShouldReturn = Builder(); if ShouldReturn then return ShouldReturn; end end -- regrowth,if=energy<25&buff.predatory_swiftness.up&!buff.clearcasting.up&variable.regrowth if S.Regrowth:IsReady() and VarRegrowth and (Player:Energy() < 25 and Player:BuffUp(S.PredatorySwiftnessBuff) and Player:BuffDown(S.Clearcasting)) then if Cast(S.Regrowth, Settings.Feral.GCDasOffGCD.Regrowth) then return "regrowth main 24"; end end -- Pool if nothing else to do if (true) then if HR.CastAnnotated(S.Pool, false, "WAIT") then return "Pool Energy"; end end end end local function OnInit() S.RipDebuff:RegisterAuraTracking() HR.Print("Feral Druid rotation is currently a work in progress, but has been updated for patch 10.1.5.") end HR.SetAPL(103, APL, OnInit)