local Tinkr, Bastion = ... local FeralModule = Bastion.Module:New('feral') local Evaluator = Tinkr.Util.Evaluator local Player = Bastion.UnitManager:Get('player') local None = Bastion.UnitManager:Get('none') local Target = Bastion.UnitManager:Get('target') local myconf = Tinkr.Util.Config:New('feral_druid') -- for saving variables -- Spellbook spells local Shadowmeld = Bastion.SpellBook:GetSpell(58984) local Barkskin = Bastion.SpellBook:GetSpell(22812) local BearForm = Bastion.SpellBook:GetSpell(5487) --local Berserk = Bastion.SpellBook:GetSpell(50334) local CatForm = Bastion.SpellBook:GetSpell(768) local Dash = Bastion.SpellBook:GetSpell(1850) local EntanglingRoots = Bastion.SpellBook:GetSpell(339) local FeralFrenzy = Bastion.SpellBook:GetSpell(274837) local FerociousBite = Bastion.SpellBook:GetSpell(22568) --local FrenziedRegeneration = Bastion.SpellBook:GetSpell(22842) local Growl = Bastion.SpellBook:GetSpell(6795) --local Ironfur = Bastion.SpellBook:GetSpell(192081) local Mangle = Bastion.SpellBook:GetSpell(33917) local MarkoftheWild = Bastion.SpellBook:GetSpell(1126) local Moonfire = Bastion.SpellBook:GetSpell(8921) local MoonfireDebuff = Bastion.SpellBook:GetSpell(164812) local PredatorySwiftness = Bastion.SpellBook:GetSpell(69369) local PrimalWrath = Bastion.SpellBook:GetSpell(285381) local Prowl = Bastion.SpellBook:GetSpell(5215) local Rake = Bastion.SpellBook:GetSpell(1822) local RakeDebuff = Bastion.SpellBook:GetSpell(155722) local Regrowth = Bastion.SpellBook:GetSpell(8936) local Revive = Bastion.SpellBook:GetSpell(50769) local Rip = Bastion.SpellBook:GetSpell(1079) local Shred = Bastion.SpellBook:GetSpell(5221) local SkullBash = Bastion.SpellBook:GetSpell(106839) --local Thrash = Bastion.SpellBook:GetSpell(106830) local Thrash = Bastion.SpellBook:GetSpell(106832) local ThrashDebuff = Bastion.SpellBook:GetSpell(106830) local TigersFury = Bastion.SpellBook:GetSpell(5217) local TravelForm = Bastion.SpellBook:GetSpell(783) local Wrath = Bastion.SpellBook:GetSpell(5176) --local Maul = Bastion.SpellBook:GetSpell(6807) --local SurvivalInstincts = Bastion.SpellBook:GetSpell(61336) --local Swipe = Bastion.SpellBook:GetSpell(106785) local Swipe = Bastion.SpellBook:GetSpell(213764) local BrutalSlash = Bastion.SpellBook:GetSpell(202028) local ApexPredator = Bastion.SpellBook:GetSpell(391882) local Incarnation = Bastion.SpellBook:GetSpell(102543) -- Define Units local InterruptTarget = Bastion.UnitManager:CreateCustomUnit('skullbash', function(unit) local interrupt = nil Bastion.UnitManager:EnumEnemies(function(unit) if unit:IsDead() then return false end if not Player:CanSee(unit) then return false end if Player:GetDistance(unit) > 40 then return false end if Player:InMelee(unit) and unit:IsInterruptible(5) and Player:IsFacing(unit) then interrupt = unit return true end end) if interrupt == nil then interrupt = None end return interrupt end) local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) local tank = nil Bastion.UnitManager:EnumFriends(function(unit) if Player:GetDistance(unit) > 40 then return false end if not Player:CanSee(unit) then return false end if unit:IsDead() then return false end if unit:IsTank() then tank = unit return true end return false end) if tank == nil then tank = None end return tank end) local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) local explosive = nil Bastion.UnitManager:EnumEnemies(function(unit) if unit:IsDead() then return false end if not Player:CanSee(unit) then return false end if Player:GetDistance(unit) > 40 then return false end if Player:InMelee(unit) and unit:GetID() == 120651 and Player:IsFacing(unit) then explosive = unit return true end end) if explosive == nil then explosive = None end return explosive end) -- Rotation code here local function Buffs() -- Mark of the Wild local motw = myconf:Read('motw') MarkoftheWild:Condition('Mark of the Wild', function() return motw and MarkoftheWild:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(MarkoftheWild):IsUp() and not Player:IsMounted() end) MarkoftheWild:Cast(Player, 'Mark of the Wild') end local function DruidProwl() -- Prowl Prowl:Condition('Prowl', function() return Prowl:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(Prowl):IsUp() and Player:GetDistance(Target) <= 25 and not Player:IsMounted() and Target:IsHostile() and not Target:IsDead() end) Prowl:Cast(Player, 'Prowl') end local function Opener() -- Rake Rake:Condition('Rake', function() return Rake:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:InMelee(Target) and not Target:GetAuras():FindMy(RakeDebuff):IsUp() and Target:Exists() and Target:IsHostile() and not Target:IsDead() end) Rake:Cast(Target, 'Rake') end local function Interrupt() -- Skull Bash SkullBash:Condition('SkullBash', function() return SkullBash:IsKnownAndUsable() and InterruptTarget:Exists() and not Player:IsCastingOrChanneling() end) C_Timer.After(1.5, function() SkullBash:Cast(Target, 'SkullBash') end) end local function Damage() -- Cat Form CatForm:Condition('Cat Form', function() return CatForm:IsKnownAndUsable() and not Player:GetAuras():FindMy(CatForm):IsUp() end) CatForm:Cast(Player, 'Cat Form') -- Healing with Predatory Swiftness Regrowth:Condition('Regrowth', function() return Regrowth:IsKnownAndUsable() and Player:GetAuras():FindMy(PredatorySwiftness):IsUp() end) Regrowth:Cast(Player, 'Regrowth') -- Cooldowns -- Tiger's Fury TigersFury:Condition('Tigers Fury', function() return TigersFury:IsKnownAndUsable() and not Player:GetAuras():FindMy(TigersFury):IsUp() end) TigersFury:Cast(Player, 'Tigers Fury') -- Feral Frenzy on cooldown and when combo points are zero FeralFrenzy:Condition('Feral Frenzy', function() return FeralFrenzy:IsKnownAndUsable() and Player:GetComboPoints(Target) == 0 end) FeralFrenzy:Cast(Player, 'Feral Frenzy') -- Incarnation: Avatar of Ashamane Incarnation:Condition('Incarnation', function() return Incarnation:IsKnownAndUsable() end) Incarnation:Cast(Player, 'Incarnation') -- Multi Target Rotation if Player:GetEnemies(8) >= 2 then -- Primal Wrath at 5 combo points PrimalWrath:Condition('Primal Wrath', function() return PrimalWrath:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetComboPoints(Target) >= 5 and (not Target:GetAuras():FindMy(Rip):IsUp() or Target:GetAuras():FindMy(Rip):GetRemainingTime() <= 7) end) PrimalWrath:Cast(Target, 'Primal Wrath') -- Spend Apex Predator Procs on Ferocious Bite FerociousBite:Condition('Ferocious Bite Apex Predator', function() return FerociousBite:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetAuras():FindMy(ApexPredator):IsUp() end) FerociousBite:Cast(Target, 'Ferocious Bite Apex Predator') -- Maintain Rake Rake:Condition('Maintain Rake', function() return Rake:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:InMelee(Target) and Target:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 4 end) Rake:Cast(Target, 'Maintain Rake') -- Cast Thrash if its not up Thrash:Condition('Thrash', function() return Thrash:IsKnownAndUsable() and not Target:GetAuras():FindMy(ThrashDebuff):IsUp() and Player:InMelee(Target) end) Thrash:Cast(Target, 'Thrash') -- Avoid capping Brutal Slash charges BrutalSlash:Condition('Brutal Slash', function() return BrutalSlash:IsKnownAndUsable() and Player:InMelee(Target) end) BrutalSlash:Cast(Target, 'Brutal Slash') -- Shred to generate combo points Shred:Condition('Shred', function() return Shred:IsKnownAndUsable() and Player:InMelee(Target) end) Shred:Cast(Target, 'Shred') end -- Single Target Rotation if Player:GetEnemies(8) == 1 then -- Maintain Rip - you'll want to cast this at 5 combo points Rip:Condition('Rip', function() return Rip:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetComboPoints(Target) >= 5 and (not Target:GetAuras():FindMy(Rip):IsUp() or Target:GetAuras():FindMy(Rip):GetRemainingTime() <= 7) end) Rip:Cast(Target, 'Rip') -- Spend Apex Predator Procs on Ferocious Bite FerociousBite:Condition('Ferocious Bite Apex Predator', function() return FerociousBite:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetAuras():FindMy(ApexPredator):IsUp() end) FerociousBite:Cast(Target, 'Ferocious Bite Apex Predator') -- If you have 5 combo points and Rip is not in Pandemic then cast Ferocious Bite FerociousBite:Condition('Ferocious Bite', function() return FerociousBite:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetComboPoints(Target) >= 5 end) FerociousBite:Cast(Target, 'Ferocious Bite') -- Maintain Rake Rake:Condition('Maintain Rake', function() return Rake:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:InMelee(Target) and Target:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 4 end) Rake:Cast(Target, 'Maintain Rake') -- Avoid capping Brutal Slash charges BrutalSlash:Condition('Brutal Slash', function() return BrutalSlash:IsKnownAndUsable() and Player:InMelee(Target) end) BrutalSlash:Cast(Target, 'Brutal Slash') -- Cast Thrash if its not up Thrash:Condition('Thrash', function() return Thrash:IsKnownAndUsable() and not Target:GetAuras():FindMy(ThrashDebuff):IsUp() and Player:InMelee(Target) end) Thrash:Cast(Target, 'Thrash') -- Shred to generate combo points Shred:Condition('Shred', function() return Shred:IsKnownAndUsable() and Player:InMelee(Target) end) Shred:Cast(Target, 'Shred') end end -- Module that dictates workflow FeralModule:Sync(function() if not Player:IsAffectingCombat() then if Buffs() then return end if DruidProwl() then return end if Opener() then return end end if Player:IsAffectingCombat() and Target:Exists() and Target:IsHostile() and not Target:IsDead() then if Interrupt() then return end if Damage() then return end end end) Bastion:Register(FeralModule) local tab1 = { layoutConfig = { padding = { top = 40 } }, rows = { [1] = { shadpr = { type = 'label', label = 'Open Combat' } }, [2] = { opener = { type = 'dropdown', label = 'Opener', column = 6, order = 1, options = { { text = 'Moonfire', value = 'moonfire'}, { text = 'None', value = 'none'}, }, initialValue = myconf:Read('opener', 'moonfire'), onValueChanged = function(_, value) myconf:Write('opener', value) end }, }, [3] = { shadpr = { type = 'label', label = 'Buffs' } }, [4] = { motw = { type = 'checkbox', label = 'Mark of the Wild', column = 12, order = 1, initialValue = myconf:Read('motw', false), onValueChanged = function(_, flag) myconf:Write('motw', flag) end }, }, [5] = { shadpr = { type = 'label', label = 'Defensives' } }, [6] = { shield1 = { type = 'checkbox', label = 'Ironfur', column = 6, order = 1, initialValue = myconf:Read('ironfur', false), onValueChanged = function(_, flag) myconf:Write('ironfur', flag) end }, shield2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, min = 1, max = 100, precision = 0, initialValue = myconf:Read('ironfurpercent', 35), onValueChanged = function(_, value) myconf:Write('ironfurpercent', value) end }, }, [7] = { dispersion1 = { type = 'checkbox', label = 'Frenzied Regeneration', column = 6, order = 1, initialValue = myconf:Read('fregen', false), onValueChanged = function(_, flag) myconf:Write('fregen', flag) end }, dispersion2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, min = 1, max = 100, precision = 0, initialValue = myconf:Read('fregenpercent', 35), onValueChanged = function(_, value) myconf:Write('fregenpercent', value) end }, }, [8] = { shadpr = { type = 'label', label = 'Interrupts' } }, [9] = { silence = { type = 'checkbox', label = 'Skull Bash on CD', column = 12, order = 1, initialValue = myconf:Read('skullbash', false), onValueChanged = function(_, flag) myconf:Write('skullbash', flag) end }, }, [10] = { shadpr = { type = 'header', label = 'Purify Disease' } }, [11] = { incombat = { type = 'checkbox', label = 'In Combat', column = 6, order = 1, initialValue = myconf:Read('purifyic', false), onValueChanged = function(_, flag) myconf:Write('purifyic', flag) end }, outcombat = { type = 'checkbox', label = 'Out of Combat', column = 6, order = 2, initialValue = myconf:Read('purifyooc', false), onValueChanged = function(_, flag) myconf:Write('purifyooc', flag) end }, }, [12] = { shadpr = { type = 'header', label = 'Dispel Magic' } }, [13] = { incombat = { type = 'checkbox', label = 'In Combat', column = 12, order = 1, initialValue = myconf:Read('dispelic', false), onValueChanged = function(_, flag) myconf:Write('dispelic', flag) end }, }, [14] = { shadpr = { type = 'header', label = 'Dispel Delay Time' } }, [15] = { delay = { type = 'slider', label = 'In seconds', column = 6, order = 1, min = 0.5, max = 2, precision = 1, initialValue = myconf:Read('dispeldelay', 1), onValueChanged = function(_, value) myconf:Write('dispeldelay', value) end }, }, }, } local tab2 = { layoutConfig = { padding = { top = 40 } }, rows = { [1] = { shadpr = { type = 'header', label = 'Player Healing' } }, [2] = { incombat = { type = 'checkbox', label = 'In Combat Healing', column = 6, order = 1, initialValue = myconf:Read('playericheal', false), onValueChanged = function(_, flag) myconf:Write('playericheal', flag) end }, outcombat = { type = 'checkbox', label = 'Out of Combat Healing', column = 6, order = 2, initialValue = myconf:Read('playeroocheal', false), onValueChanged = function(_, flag) myconf:Write('playeroocheal', flag) end }, }, [3] = { shadowmend1 = { type = 'checkbox', label = 'Shadow Mend', column = 6, order = 1, initialValue = myconf:Read('shadowmend', false), onValueChanged = function(_, flag) myconf:Write('shadowmend', flag) end }, shadowmend2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, min = 1, max = 100, precision = 0, initialValue = myconf:Read('shadowmendpercent', 35), onValueChanged = function(_, value) myconf:Write('shadowmendpercent', value) end }, }, }, } local feralconfig = { layoutConfig = { padding = { top = 30 } }, rows = { [1] = { container = { type = 'tab', fullSize = true, tabs = { { name = 'player', title = 'Player Settings', layout = tab1 }, { name = 'group', title = 'Placeholder', layout = tab2 } }, } }, }, } Bastion.settingstemplate(feralconfig, 'Feral Druid', 400, 600, 1.00, 0.49, 0.04) -- config, Title of Settings Frame, Width, Height, ClassRGB -- Class colours -- Death Knight 0.77 0.12 0.23 Red -- Demon Hunter 0.64 0.19 0.79 Dark Magenta -- Druid 1.00 0.49 0.04 Orange -- Evoker 0.20 0.58 0.50 Dark Emerald -- Hunter 0.67 0.83 0.45 Green -- Mage 0.25 0.78 0.92 Light Blue -- Monk 0.00 1.00 0.60 Spring Green -- Paladin 0.96 0.55 0.73 Pink -- Priest 1.00 1.00 1.00 White -- Rogue 1.00 0.96 0.41 Yellow -- Shaman 0.00 0.44 0.87 Blue -- Warlock 0.53 0.53 0.93 Purple -- Warrior 0.78 0.61 0.43 Tan