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.

2826 lines
171 KiB

-- DruidBalance.lua
-- June 2018
local addon, ns = ...
local Hekili = _G[ addon ]
local class = Hekili.Class
local state = Hekili.State
local PTR = ns.PTR
-- Conduits
-- [-] fury_of_the_skies
-- [x] precise_alignment
-- [-] stellar_inspiration
-- [-] umbral_intensity
-- Covenants
-- [x] deep_allegiance
-- [-] endless_thirst
-- [-] evolved_swarm
-- [-] conflux_of_elements
-- Endurance
-- [x] tough_as_bark
-- [x] ursine_vigor
-- [-] innate_resolve
-- Finesse
-- [x] born_anew
-- [-] front_of_the_pack
-- [x] born_of_the_wilds
-- [x] tireless_pursuit
if UnitClassBase( "player" ) == "DRUID" then
local spec = Hekili:NewSpecialization( 102, true )
spec:RegisterResource( Enum.PowerType.LunarPower, {
fury_of_elune = {
aura = "fury_of_elune_ap",
4 years ago
debuff = true,
last = function ()
4 years ago
local app = state.debuff.fury_of_elune_ap.applied
local t = state.query_time
return app + floor( ( t - app ) * 2 ) * 0.5
end,
interval = 0.5,
value = 2.5
},
celestial_infusion = {
aura = "celestial_infusion",
last = function ()
local app = state.buff.celestial_infusion.applied
local t = state.query_time
return app + floor( ( t - app ) * 2 ) * 0.5
end,
interval = 0.5,
value = 2.5
},
natures_balance = {
talent = "natures_balance",
last = function ()
local app = state.combat
local t = state.query_time
return app + floor( ( t - app ) / 2 ) * 2
end,
interval = 2,
value = 1,
}
} )
spec:RegisterResource( Enum.PowerType.Mana )
spec:RegisterResource( Enum.PowerType.Energy )
spec:RegisterResource( Enum.PowerType.ComboPoints )
spec:RegisterResource( Enum.PowerType.Rage )
-- Talents
spec:RegisterTalents( {
natures_balance = 22385, -- 202430
warrior_of_elune = 22386, -- 202425
force_of_nature = 22387, -- 205636
tiger_dash = 19283, -- 252216
renewal = 18570, -- 108238
wild_charge = 18571, -- 102401
feral_affinity = 22155, -- 202157
guardian_affinity = 22157, -- 197491
restoration_affinity = 22159, -- 197492
mighty_bash = 21778, -- 5211
mass_entanglement = 18576, -- 102359
heart_of_the_wild = 18577, -- 319454
soul_of_the_forest = 18580, -- 114107
starlord = 21706, -- 202345
incarnation = 21702, -- 102560
stellar_drift = 22389, -- 202354
twin_moons = 21712, -- 279620
stellar_flare = 22165, -- 202347
solstice = 21648, -- 343647
fury_of_elune = 21193, -- 202770
new_moon = 21655, -- 274281
} )
-- PvP Talents
spec:RegisterPvpTalents( {
celestial_guardian = 180, -- 233754
crescent_burn = 182, -- 200567
deep_roots = 834, -- 233755
dying_stars = 822, -- 232546
faerie_swarm = 836, -- 209749
high_winds = 5383, -- 200931
moon_and_stars = 184, -- 233750
moonkin_aura = 185, -- 209740
owlkin_adept = 5407, -- 354541
protector_of_the_grove = 3728, -- 209730
star_burst = 3058, -- 356517
thorns = 3731, -- 305497
} )
spec:RegisterPower( "lively_spirit", 279642, {
id = 279648,
duration = 20,
max_stack = 1,
} )
local mod_circle_hot = setfenv( function( x )
return legendary.circle_of_life_and_death.enabled and ( 0.85 * x ) or x
end, state )
local mod_circle_dot = setfenv( function( x )
return legendary.circle_of_life_and_death.enabled and ( 0.75 * x ) or x
end, state )
-- Auras
spec:RegisterAuras( {
aquatic_form = {
id = 276012,
},
astral_influence = {
id = 197524,
},
barkskin = {
id = 22812,
duration = 12,
max_stack = 1,
},
bear_form = {
id = 5487,
duration = 3600,
max_stack = 1,
},
cat_form = {
id = 768,
duration = 3600,
max_stack = 1,
},
celestial_alignment = {
id = 194223,
duration = function () return 20 + ( conduit.precise_alignment.mod * 0.001 ) end,
max_stack = 1,
},
dash = {
id = 1850,
duration = 10,
max_stack = 1,
},
eclipse_lunar = {
id = 48518,
duration = 15,
max_stack = 1,
meta = {
empowered = function( t ) return t.up and t.empowerTime >= t.applied end,
}
},
eclipse_solar = {
id = 48517,
duration = 15,
max_stack = 1,
meta = {
empowered = function( t ) return t.up and t.empowerTime >= t.applied end,
}
},
elunes_wrath = {
id = 64823,
duration = 10,
max_stack = 1
},
entangling_roots = {
id = 339,
duration = 30,
type = "Magic",
max_stack = 1,
},
feline_swiftness = {
id = 131768,
},
flight_form = {
id = 276029,
},
force_of_nature = {
id = 205644,
duration = 15,
max_stack = 1,
},
frenzied_regeneration = {
id = 22842,
duration = 3,
max_stack = 1,
},
fury_of_elune_ap = {
id = 202770,
duration = 8,
tick_time = 0.5,
max_stack = 1,
generate = function ( t )
local applied = action.fury_of_elune.lastCast
if applied and now - applied < 8 then
t.count = 1
t.expires = applied + 8
t.applied = applied
t.caster = "player"
return
end
t.count = 0
t.expires = 0
t.applied = 0
t.caster = "nobody"
end,
copy = "fury_of_elune"
},
growl = {
id = 6795,
duration = 3,
max_stack = 1,
},
heart_of_the_wild = {
id = 108291,
duration = 45,
max_stack = 1,
copy = { 108292, 108293, 108294 }
},
incarnation = {
id = 102560,
duration = function () return 30 + ( conduit.precise_alignment.mod * 0.001 ) end,
max_stack = 1,
copy = "incarnation_chosen_of_elune"
},
ironfur = {
id = 192081,
duration = 7,
max_stack = 1,
},
mass_entanglement = {
id = 102359,
duration = 30,
type = "Magic",
max_stack = 1,
},
mighty_bash = {
id = 5211,
duration = 5,
max_stack = 1,
},
moonfire = {
id = 164812,
duration = function () return mod_circle_dot( 22 ) end,
tick_time = function () return mod_circle_dot( 2 ) * haste end,
type = "Magic",
max_stack = 1,
},
moonkin_form = {
id = 24858,
duration = 3600,
max_stack = 1,
},
owlkin_frenzy = {
id = 157228,
duration = 10,
max_stack = function () return pvptalent.owlkin_adept.enabled and 2 or 1 end,
},
prowl = {
id = 5215,
duration = 3600,
max_stack = 1,
},
regrowth = {
id = 8936,
duration = function () return mod_circle_hot( 12 ) end,
type = "Magic",
max_stack = 1,
},
shadowmeld = {
id = 58984,
duration = 3600,
max_stack = 1,
},
solar_beam = {
id = 81261,
duration = 3600,
max_stack = 1,
},
solstice = {
id = 343648,
duration = 6,
max_stack = 1,
},
stag_form = {
id = 210053,
duration = 3600,
max_stack = 1,
generate = function ()
local form = GetShapeshiftForm()
local stag = form and form > 0 and select( 4, GetShapeshiftFormInfo( form ) )
local sf = buff.stag_form
if stag == 210053 then
sf.count = 1
sf.applied = now
sf.expires = now + 3600
sf.caster = "player"
return
end
sf.count = 0
sf.applied = 0
sf.expires = 0
sf.caster = "nobody"
end,
},
starfall = {
id = 191034,
duration = 8,
max_stack = 1,
},
starlord = {
id = 279709,
duration = 20,
max_stack = 3,
},
stellar_flare = {
id = 202347,
duration = function () return mod_circle_dot( 24 ) end,
tick_time = function () return mod_circle_dot( 2 ) * haste end,
type = "Magic",
max_stack = 1,
},
sunfire = {
id = 164815,
duration = function () return mod_circle_dot( 18 ) end,
tick_time = function () return mod_circle_dot( 2 ) * haste end,
type = "Magic",
max_stack = 1,
},
thick_hide = {
id = 16931,
},
thrash_bear = {
id = 192090,
duration = function () return mod_circle_dot( 15 ) end,
tick_time = function () return mod_circle_dot( 3 ) * haste end,
max_stack = 3,
},
tiger_dash = {
id = 252216,
duration = 5,
max_stack = 1,
},
travel_form = {
id = 783,
duration = 3600,
max_stack = 1,
},
treant_form = {
id = 114282,
duration = 3600,
max_stack = 1,
},
typhoon = {
id = 61391,
duration = 6,
type = "Magic",
max_stack = 1,
},
warrior_of_elune = {
id = 202425,
duration = 3600,
type = "Magic",
max_stack = 3,
},
wild_charge = {
id = 102401,
duration = 0.5,
max_stack = 1,
},
yseras_gift = {
id = 145108,
},
-- Alias for Celestial Alignment vs. Incarnation
4 years ago
ca_inc = {},
--[[
alias = { "incarnation", "celestial_alignment" },
aliasMode = "first", -- use duration info from the first buff that's up, as they should all be equal.
aliasType = "buff",
4 years ago
-- duration = function () return talent.incarnation.enabled and 30 or 20 end,
}, ]]
any_form = {
alias = { "bear_form", "cat_form", "moonkin_form" },
duration = 3600,
aliasMode = "first",
aliasType = "buff",
},
-- PvP Talents
celestial_guardian = {
id = 234081,
duration = 3600,
max_stack = 1,
},
cyclone = {
4 years ago
id = 33786,
duration = 6,
max_stack = 1,
},
faerie_swarm = {
id = 209749,
duration = 5,
type = "Magic",
max_stack = 1,
},
high_winds = {
id = 200931,
duration = 4,
max_stack = 1,
},
moon_and_stars = {
id = 234084,
duration = 10,
max_stack = 1,
},
moonkin_aura = {
id = 209746,
duration = 18,
type = "Magic",
max_stack = 3,
},
thorns = {
4 years ago
id = 305497,
duration = 12,
type = "Magic",
max_stack = 1,
},
-- Azerite Powers
arcanic_pulsar = {
id = 287790,
duration = 3600,
max_stack = 9,
},
dawning_sun = {
id = 276153,
duration = 8,
max_stack = 1,
},
sunblaze = {
id = 274399,
duration = 20,
max_stack = 1
},
-- Legendaries
balance_of_all_things_arcane = {
id = 339946,
duration = 8,
max_stack = 8
},
balance_of_all_things_nature = {
id = 339943,
duration = 8,
max_stack = 8,
},
celestial_infusion = {
id = 367907,
duration = 8,
max_stack = 1
},
oath_of_the_elder_druid = {
id = 338643,
duration = 60,
max_stack = 1
},
oneths_perception = {
id = 339800,
duration = 30,
max_stack = 1,
},
oneths_clear_vision = {
id = 339797,
duration = 30,
max_stack = 1,
},
primordial_arcanic_pulsar = {
id = 338825,
duration = 3600,
max_stack = 10,
},
timeworn_dreambinder = {
id = 340049,
duration = 6,
max_stack = 2,
},
} )
-- Adaptive Swarm Stuff
do
local applications = {
SPELL_AURA_APPLIED = true,
SPELL_AURA_REFRESH = true,
SPELL_AURA_APPLIED_DOSE = true
}
local casts = { SPELL_CAST_SUCCESS = true }
local removals = {
SPELL_AURA_REMOVED = true,
SPELL_AURA_BROKEN = true,
SPELL_AURA_BROKEN_SPELL = true,
SPELL_AURA_REMOVED_DOSE = true,
SPELL_DISPEL = true
}
local deaths = {
UNIT_DIED = true,
UNIT_DESTROYED = true,
UNIT_DISSIPATES = true,
PARTY_KILL = true,
SPELL_INSTAKILL = true,
}
local spellIDs = {
[325733] = true,
[325748] = true,
[325727] = true
}
local flights = {}
local pending = {}
local swarms = {}
-- Flow: Cast -> In Flight -> Application -> Ticks -> Removal -> In Flight -> Application -> Ticks -> Removal -> ...
-- If the swarm target dies, it will jump again.
local insert, remove = table.insert, table.remove
function Hekili:EmbedAdaptiveSwarm( s )
s:RegisterCombatLogEvent( function( _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName )
if not state.covenant.necrolord then return end
if sourceGUID == state.GUID and spellIDs[ spellID ] then
-- On cast, we need to show we have a cast-in-flight.
if casts[ subtype ] then
local dot
if bit.band( destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY ) == 0 then
dot = "adaptive_swarm_damage"
else
dot = "adaptive_swarm_heal"
end
insert( flights, { destGUID, 3, GetTime() + 5, dot } )
-- On application, we need to store the GUID of the unit so we can get the stacks and expiration time.
elseif applications[ subtype ] and #flights > 0 then
local n, flight
for i, v in ipairs( flights ) do
if v[1] == destGUID then
n = i
flight = v
break
end
if not flight and v[1] == "unknown" then
n = i
flight = v
end
end
if flight then
local swarm = swarms[ destGUID ]
local now = GetTime()
if swarm and swarm.expiration > now then
swarm.stacks = swarm.stacks + flight[2]
swarm.dot = bit.band( destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY ) == 0 and "adaptive_swarm_damage" or "adaptive_swarm_heal"
swarm.expiration = now + class.auras[ swarm.dot ].duration
else
swarms[ destGUID ] = {}
swarms[ destGUID ].stacks = flight[2]
swarms[ destGUID ].dot = bit.band( destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY ) == 0 and "adaptive_swarm_damage" or "adaptive_swarm_heal"
swarms[ destGUID ].expiration = now + class.auras[ swarms[ destGUID ].dot ].duration
end
remove( flights, n )
else
swarms[ destGUID ] = {}
swarms[ destGUID ].stacks = 3 -- We'll assume it's fresh.
swarms[ destGUID ].dot = bit.band( destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY ) == 0 and "adaptive_swarm_damage" or "adaptive_swarm_heal"
swarms[ destGUID ].expiration = GetTime() + class.auras[ swarms[ destGUID ].dot ].duration
end
elseif removals[ subtype ] then
-- If we have a swarm for this, remove it.
local swarm = swarms[ destGUID ]
if swarm then
swarms[ destGUID ] = nil
if swarm.stacks > 1 then
local dot
if bit.band( destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY ) == 0 then
dot = "adaptive_swarm_heal"
else
dot = "adaptive_swarm_damage"
end
insert( flights, { "unknown", swarm.stacks - 1, GetTime() + 5, dot } )
end
end
end
elseif swarms[ destGUID ] and deaths[ subtype ] then
-- If we have a swarm for this, remove it.
local swarm = swarms[ destGUID ]
if swarm then
swarms[ destGUID ] = nil
if swarm.stacks > 1 then
if bit.band( destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY ) == 0 then
dot = "adaptive_swarm_heal"
else
dot = "adaptive_swarm_damage"
end
insert( flights, { "unknown", swarm.stacks - 1, GetTime() + 5, dot } )
end
end
end
end )
--[[ s:RegisterEvent( "UNIT_AURA", function( _, unit )
if not state.covenant.necrolord then return end
local guid = UnitGUID( unit )
if pending[ guid ] then
if UnitIsFriend( unit, "player" ) then
local name, _, count, _, _, expirationTime = FindUnitBuffByID( unit, 325748, "PLAYER" )
print( "Buff", name, count, guid, pending[ guid ] )
if name then
swarms[ guid ] = {
stacks = count,
expiration = expirationTime,
}
pending[ guid ] = nil
return
end
else
local name, _, count, _, _, expirationTime = FindUnitDebuffByID( unit, 325733, "PLAYER" )
print( "Debuff", name, count, guid, pending[ guid ] )
if name then
swarms[ guid ] = {
stacks = count,
expiration = expirationTime,
}
pending[ guid ] = nil
return
end
end
pending[ guid ] = pending[ guid ] + 1
if pending[ guid ] > 2 then
pending[ guid ] = nil
end
end
end ) ]]
function s.GetActiveSwarms()
return swarms
end
function s.GetPendingSwarms()
return pending
end
function s.GetInFlightSwarms()
return flights
end
local flySwarm, landSwarm
landSwarm = setfenv( function( aura )
if aura.key == "adaptive_swarm_heal_in_flight" then
applyBuff( "adaptive_swarm_heal", 12, min( 5, buff.adaptive_swarm_heal.stack + aura.count ) )
buff.adaptive_swarm_heal.expires = query_time + 12
state:QueueAuraEvent( "adaptive_swarm", flySwarm, buff.adaptive_swarm_heal.expires, "AURA_EXPIRATION", buff.adaptive_swarm_heal )
else
applyDebuff( "target", "adaptive_swarm_damage", 12, min( 5, debuff.adaptive_swarm_damage.stack + aura.count ) )
debuff.adaptive_swarm_damage.expires = query_time + 12
state:QueueAuraEvent( "adaptive_swarm", flySwarm, debuff.adaptive_swarm_damage.expires, "AURA_EXPIRATION", debuff.adaptive_swarm_damage )
end
end, state )
flySwarm = setfenv( function( aura )
if aura.key == "adaptive_swarm_heal" then
applyBuff( "adaptive_swarm_heal_in_flight", 5, aura.count - 1 )
state:QueueAuraEvent( "adaptive_swarm", landSwarm, query_time + 5, "AURA_EXPIRATION", buff.adaptive_swarm_heal_in_flight )
else
applyBuff( "adaptive_swarm_damage_in_flight", 5, aura.count - 1 )
state:QueueAuraEvent( "adaptive_swarm", landSwarm, query_time + 5, "AURA_EXPIRATION", buff.adaptive_swarm_damage_in_flight )
end
end, state )
s.SwarmOnReset = setfenv( function()
for k, v in pairs( swarms ) do
if v.expiration + 0.1 <= now then swarms[ k ] = nil end
end
for i = #flights, 1, -1 do
if flights[i][3] + 0.1 <= now then remove( flights, i ) end
end
local target = UnitGUID( "target" )
local tSwarm = swarms[ target ]
if not UnitIsFriend( "target", "player" ) and tSwarm and tSwarm.expiration > now then
applyDebuff( "target", "adaptive_swarm_damage", tSwarm.expiration - now, tSwarm.stacks )
debuff.adaptive_swarm_damage.expires = tSwarm.expiration
if tSwarm.stacks > 1 then
state:QueueAuraEvent( "adaptive_swarm", flySwarm, tSwarm.expiration, "AURA_EXPIRATION", debuff.adaptive_swarm_damage )
end
end
if buff.adaptive_swarm_heal.up and buff.adaptive_swarm_heal.stack > 1 then
state:QueueAuraEvent( "adaptive_swarm", flySwarm, buff.adaptive_swarm_heal.expires, "AURA_EXPIRATION", buff.adaptive_swarm_heal )
else
for k, v in pairs( swarms ) do
if k ~= target and v.dot == "adaptive_swarm_heal" then
applyBuff( "adaptive_swarm_heal", v.expiration - now, v.stacks )
buff.adaptive_swarm_heal.expires = v.expiration
if v.stacks > 1 then
state:QueueAuraEvent( "adaptive_swarm", flySwarm, buff.adaptive_swarm_heal.expires, "AURA_EXPIRATION", buff.adaptive_swarm_heal )
end
end
end
end
local flight
for i, v in ipairs( flights ) do
if not flight or v[3] > now and v[3] > flight then flight = v end
end
if flight then
local dot = flight[4] .. "_in_flight"
applyBuff( dot, flight[3] - now, flight[2] )
state:QueueAuraEvent( dot, landSwarm, flight[3], "AURA_EXPIRATION", buff[ dot ] )
end
Hekili:Debug( "Swarm Info:\n Damage - %.2f remains, %d stacks.\n Dmg In Flight - %.2f remains, %d stacks.\n Heal - %.2f remains, %d stacks.\n Heal In Flight - %.2f remains, %d stacks.\n Count Dmg: %d, Count Heal: %d.", dot.adaptive_swarm_damage.remains, dot.adaptive_swarm_damage.stack, buff.adaptive_swarm_damage_in_flight.remains, buff.adaptive_swarm_damage_in_flight.stack, buff.adaptive_swarm_heal.remains, buff.adaptive_swarm_heal.stack, buff.adaptive_swarm_heal_in_flight.remains, buff.adaptive_swarm_heal_in_flight.stack, active_dot.adaptive_swarm_damage, active_dot.adaptive_swarm_heal )
end, state )
function Hekili:DumpSwarmInfo()
local line = "Flights:"
for k, v in pairs( flights ) do
line = line .. " " .. k .. ":" .. table.concat( v, ":" )
end
print( line )
line = "Pending:"
for k, v in pairs( pending ) do
line = line .. " " .. k .. ":" .. v
end
print( line )
line = "Swarms:"
for k, v in pairs( swarms ) do
line = line .. " " .. k .. ":" .. v.stacks .. ":" .. v.expiration
end
print( line )
end
-- Druid - Necrolord - 325727 - adaptive_swarm (Adaptive Swarm)
spec:RegisterAbility( "adaptive_swarm", {
id = 325727,
cast = 0,
cooldown = 25,
gcd = "spell",
spend = 0.05,
spendType = "mana",
startsCombat = true,
texture = 3578197,
-- For Feral, we want to put Adaptive Swarm on the highest health enemy.
indicator = function ()
if state.spec.feral and active_enemies > 1 and target.time_to_die < longest_ttd then return "cycle" end
end,
handler = function ()
applyDebuff( "target", "adaptive_swarm_dot", nil, 3 )
if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end
end,
copy = { "adaptive_swarm_damage", "adaptive_swarm_heal", 325733, 325748 },
auras = {
adaptive_swarm_dot = {
id = 325733,
duration = function () return mod_circle_dot( 12 ) end,
tick_time = function () return mod_circle_dot( 2 ) * haste end,
max_stack = 5,
--[[ meta = {
stack = function( t ) return t.down and dot.adaptive_swarm_hot.up and max( 0, dot.adaptive_swarm_hot.count - 1 ) or t.count end,
}, ]]
copy = "adaptive_swarm_damage"
},
adaptive_swarm_hot = {
id = 325748,
duration = function () return mod_circle_hot( 12 ) end,
tick_time = function () return mod_circle_hot( 2 ) * haste end,
max_stack = 5,
--[[ meta = {
stack = function( t ) return t.down and dot.adaptive_swarm_dot.up and max( 0, dot.adaptive_swarm_dot.count - 1 ) or t.count end,
}, ]]
dot = "buff",
copy = "adaptive_swarm_heal"
},
adaptive_swarm_damage_in_flight = {
duration = 5,
max_stack = 5
},
adaptive_swarm_heal_in_flight = {
duration = 5,
max_stack = 5,
},
adaptive_swarm = {
alias = { "adaptive_swarm_damage", "adaptive_swarm_heal" },
aliasMode = "first", -- use duration info from the first buff that's up, as they should all be equal.
aliasType = "any",
},
adaptive_swarm_in_flight = {
alias = { "adaptive_swarm_damage", "adaptive_swarm_heal" },
aliasMode = "shortest", -- use duration info from the first buff that's up, as they should all be equal.
aliasType = "any",
},
}
} )
end
end
Hekili:EmbedAdaptiveSwarm( spec )
spec:RegisterStateFunction( "break_stealth", function ()
removeBuff( "shadowmeld" )
if buff.prowl.up then
setCooldown( "prowl", 6 )
removeBuff( "prowl" )
end
end )
-- Function to remove any form currently active.
spec:RegisterStateFunction( "unshift", function()
if conduit.tireless_pursuit.enabled and ( buff.cat_form.up or buff.travel_form.up ) then applyBuff( "tireless_pursuit" ) end
removeBuff( "cat_form" )
removeBuff( "bear_form" )
removeBuff( "travel_form" )
removeBuff( "moonkin_form" )
removeBuff( "travel_form" )
removeBuff( "aquatic_form" )
removeBuff( "stag_form" )
removeBuff( "celestial_guardian" )
if legendary.oath_of_the_elder_druid.enabled and debuff.oath_of_the_elder_druid_icd.down and talent.restoration_affinity.enabled then
applyBuff( "heart_of_the_wild" )
applyDebuff( "player", "oath_of_the_elder_druid_icd" )
end
end )
local affinities = {
bear_form = "guardian_affinity",
cat_form = "feral_affinity",
moonkin_form = "balance_affinity",
}
-- Function to apply form that is passed into it via string.
spec:RegisterStateFunction( "shift", function( form )
if conduit.tireless_pursuit.enabled and ( buff.cat_form.up or buff.travel_form.up ) then applyBuff( "tireless_pursuit" ) end
removeBuff( "cat_form" )
removeBuff( "bear_form" )
removeBuff( "travel_form" )
removeBuff( "moonkin_form" )
removeBuff( "travel_form" )
removeBuff( "aquatic_form" )
removeBuff( "stag_form" )
applyBuff( form )
if affinities[ form ] and legendary.oath_of_the_elder_druid.enabled and debuff.oath_of_the_elder_druid_icd.down and talent[ affinities[ form ] ].enabled then
applyBuff( "heart_of_the_wild" )
applyDebuff( "player", "oath_of_the_elder_druid_icd" )
end
if form == "bear_form" and pvptalent.celestial_guardian.enabled then
applyBuff( "celestial_guardian" )
end
end )
spec:RegisterStateExpr( "lunar_eclipse", function ()
return 0
end )
spec:RegisterStateExpr( "solar_eclipse", function ()
return 0
end )
spec:RegisterHook( "runHandler", function( ability )
local a = class.abilities[ ability ]
if not a or a.startsCombat then
break_stealth()
end
end )
--[[ This is intended to cause an AP reset on entering an encounter, but it's not working.
4 years ago
spec:RegisterHook( "start_combat", function( action )
4 years ago
if boss and astral_power.current > 50 then
spend( astral_power.current - 50, "astral_power" )
end
4 years ago
end ) ]]
4 years ago
spec:RegisterHook( "pregain", function( amt, resource, overcap, clean )
if buff.memory_of_lucid_dreams.up then
if amt > 0 and resource == "astral_power" then
return amt * 2, resource, overcap, true
end
end
end )
spec:RegisterHook( "prespend", function( amt, resource, clean )
if buff.memory_of_lucid_dreams.up then
if amt < 0 and resource == "astral_power" then
return amt * 2, resource, overcap, true
end
end
end )
local check_for_ap_overcap = setfenv( function( ability )
local a = ability or this_action
if not a then return true end
a = action[ a ]
if not a then return true end
local cost = 0
if a.spendType == "astral_power" then cost = a.cost end
return astral_power.current - cost + ( talent.shooting_stars.enabled and 4 or 0 ) + ( talent.natures_balance.enabled and ceil( execute_time / 2 ) or 0 ) < astral_power.max
end, state )
spec:RegisterStateExpr( "ap_check", function() return check_for_ap_overcap() end )
-- Simplify lookups for AP abilities consistent with SimC.
local ap_checks = {
"force_of_nature", "full_moon", "half_moon", "incarnation", "moonfire", "new_moon", "starfall", "starfire", "starsurge", "sunfire", "wrath"
}
for i, lookup in ipairs( ap_checks ) do
spec:RegisterStateExpr( lookup, function ()
return action[ lookup ]
end )
end
spec:RegisterStateExpr( "active_moon", function ()
return "new_moon"
end )
local function IsActiveSpell( id )
local slot = FindSpellBookSlotBySpellID( id )
if not slot then return false end
local _, _, spellID = GetSpellBookItemName( slot, "spell" )
return id == spellID
end
state.IsActiveSpell = IsActiveSpell
local ExpireCelestialAlignment = setfenv( function()
eclipse.state = "ANY_NEXT"
eclipse.reset_stacks()
if buff.eclipse_lunar.down then removeBuff( "starsurge_empowerment_lunar" ) end
if buff.eclipse_solar.down then removeBuff( "starsurge_empowerment_solar" ) end
if Hekili.ActiveDebug then Hekili:Debug( "Expire CA_Inc: %s - Starfire(%d), Wrath(%d), Solar(%.2f), Lunar(%.2f)", eclipse.state, eclipse.starfire_counter, eclipse.wrath_counter, buff.eclipse_solar.remains, buff.eclipse_lunar.remains ) end
end, state )
local ExpireEclipseLunar = setfenv( function()
eclipse.state = "SOLAR_NEXT"
eclipse.reset_stacks()
4 years ago
eclipse.wrath_counter = 0
removeBuff( "starsurge_empowerment_lunar" )
if Hekili.ActiveDebug then Hekili:Debug( "Expire Lunar: %s - Starfire(%d), Wrath(%d), Solar(%.2f), Lunar(%.2f)", eclipse.state, eclipse.starfire_counter, eclipse.wrath_counter, buff.eclipse_solar.remains, buff.eclipse_lunar.remains ) end
end, state )
local ExpireEclipseSolar = setfenv( function()
eclipse.state = "LUNAR_NEXT"
eclipse.reset_stacks()
4 years ago
eclipse.starfire_counter = 0
removeBuff( "starsurge_empowerment_solar" )
if Hekili.ActiveDebug then Hekili:Debug( "Expire Solar: %s - Starfire(%d), Wrath(%d), Solar(%.2f), Lunar(%.2f)", eclipse.state, eclipse.starfire_counter, eclipse.wrath_counter, buff.eclipse_solar.remains, buff.eclipse_lunar.remains ) end
end, state )
spec:RegisterStateTable( "eclipse", setmetatable( {
-- ANY_NEXT, IN_SOLAR, IN_LUNAR, IN_BOTH, SOLAR_NEXT, LUNAR_NEXT
state = "ANY_NEXT",
wrath_counter = 2,
starfire_counter = 2,
reset = setfenv( function()
eclipse.starfire_counter = GetSpellCount( 197628 ) or 0
eclipse.wrath_counter = GetSpellCount( 5176 ) or 0
if buff.eclipse_solar.up and buff.eclipse_lunar.up then
eclipse.state = "IN_BOTH"
4 years ago
-- eclipse.reset_stacks()
elseif buff.eclipse_solar.up then
eclipse.state = "IN_SOLAR"
4 years ago
-- eclipse.reset_stacks()
elseif buff.eclipse_lunar.up then
eclipse.state = "IN_LUNAR"
4 years ago
-- eclipse.reset_stacks()
elseif eclipse.starfire_counter > 0 and eclipse.wrath_counter > 0 then
eclipse.state = "ANY_NEXT"
elseif eclipse.starfire_counter == 0 and eclipse.wrath_counter > 0 then
eclipse.state = "LUNAR_NEXT"
elseif eclipse.starfire_counter > 0 and eclipse.wrath_counter == 0 then
eclipse.state = "SOLAR_NEXT"
elseif eclipse.starfire_count == 0 and eclipse.wrath_counter == 0 and buff.eclipse_lunar.down and buff.eclipse_solar.down then
eclipse.state = "ANY_NEXT"
eclipse.reset_stacks()
end
if buff.ca_inc.up then
state:QueueAuraExpiration( "ca_inc", ExpireCelestialAlignment, buff.ca_inc.expires )
elseif buff.eclipse_solar.up then
state:QueueAuraExpiration( "eclipse_solar", ExpireEclipseSolar, buff.eclipse_solar.expires )
elseif buff.eclipse_lunar.up then
state:QueueAuraExpiration( "eclipse_lunar", ExpireEclipseLunar, buff.eclipse_lunar.expires )
end
buff.eclipse_solar.empowerTime = 0
buff.eclipse_lunar.empowerTime = 0
if buff.eclipse_solar.up and action.starsurge.lastCast > buff.eclipse_solar.applied then buff.eclipse_solar.empowerTime = action.starsurge.lastCast end
if buff.eclipse_lunar.up and action.starsurge.lastCast > buff.eclipse_lunar.applied then buff.eclipse_lunar.empowerTime = action.starsurge.lastCast end
end, state ),
reset_stacks = setfenv( function()
eclipse.wrath_counter = 2
eclipse.starfire_counter = 2
end, state ),
trigger_both = setfenv( function( duration )
eclipse.state = "IN_BOTH"
eclipse.reset_stacks()
if legendary.balance_of_all_things.enabled then
applyBuff( "balance_of_all_things_arcane", nil, 8, 8 )
applyBuff( "balance_of_all_things_nature", nil, 8, 8 )
end
if talent.solstice.enabled then applyBuff( "solstice" ) end
removeBuff( "starsurge_empowerment_lunar" )
removeBuff( "starsurge_empowerment_solar" )
applyBuff( "eclipse_lunar", ( duration or class.auras.eclipse_lunar.duration ) + buff.eclipse_lunar.remains )
if set_bonus.tier28_2pc > 0 then applyBuff( "celestial_infusion" ) end
applyBuff( "eclipse_solar", ( duration or class.auras.eclipse_solar.duration ) + buff.eclipse_solar.remains )
state:QueueAuraExpiration( "ca_inc", ExpireCelestialAlignment, buff.ca_inc.expires )
state:RemoveAuraExpiration( "eclipse_solar" )
state:QueueAuraExpiration( "eclipse_solar", ExpireEclipseSolar, buff.eclipse_solar.expires )
state:RemoveAuraExpiration( "eclipse_lunar" )
state:QueueAuraExpiration( "eclipse_lunar", ExpireEclipseLunar, buff.eclipse_lunar.expires )
end, state ),
advance = setfenv( function()
if Hekili.ActiveDebug then Hekili:Debug( "Eclipse Advance (Pre): %s - Starfire(%d), Wrath(%d), Solar(%.2f), Lunar(%.2f)", eclipse.state, eclipse.starfire_counter, eclipse.wrath_counter, buff.eclipse_solar.remains, buff.eclipse_lunar.remains ) end
if not ( eclipse.state == "IN_SOLAR" or eclipse.state == "IN_LUNAR" or eclipse.state == "IN_BOTH" ) then
4 years ago
if eclipse.starfire_counter == 0 and ( eclipse.state == "SOLAR_NEXT" or eclipse.state == "ANY_NEXT" ) then
applyBuff( "eclipse_solar", class.auras.eclipse_solar.duration + buff.eclipse_solar.remains )
4 years ago
state:RemoveAuraExpiration( "eclipse_solar" )
state:QueueAuraExpiration( "eclipse_solar", ExpireEclipseSolar, buff.eclipse_solar.expires )
if talent.solstice.enabled then applyBuff( "solstice" ) end
if legendary.balance_of_all_things.enabled then applyBuff( "balance_of_all_things_nature", nil, 5, 8 ) end
eclipse.state = "IN_SOLAR"
eclipse.starfire_counter = 0
eclipse.wrath_counter = 2
if Hekili.ActiveDebug then Hekili:Debug( "Eclipse Advance (Post): %s - Starfire(%d), Wrath(%d), Solar(%.2f), Lunar(%.2f)", eclipse.state, eclipse.starfire_counter, eclipse.wrath_counter, buff.eclipse_solar.remains, buff.eclipse_lunar.remains ) end
return
end
4 years ago
if eclipse.wrath_counter == 0 and ( eclipse.state == "LUNAR_NEXT" or eclipse.state == "ANY_NEXT" ) then
4 years ago
applyBuff( "eclipse_lunar", class.auras.eclipse_lunar.duration + buff.eclipse_lunar.remains )
if set_bonus.tier28_2pc > 0 then applyDebuff( "target", "fury_of_elune_ap" ) end
4 years ago
state:RemoveAuraExpiration( "eclipse_lunar" )
state:QueueAuraExpiration( "eclipse_lunar", ExpireEclipseLunar, buff.eclipse_lunar.expires )
if talent.solstice.enabled then applyBuff( "solstice" ) end
if legendary.balance_of_all_things.enabled then applyBuff( "balance_of_all_things_nature", nil, 5, 8 ) end
eclipse.state = "IN_LUNAR"
eclipse.wrath_counter = 0
eclipse.starfire_counter = 2
if Hekili.ActiveDebug then Hekili:Debug( "Eclipse Advance (Post): %s - Starfire(%d), Wrath(%d), Solar(%.2f), Lunar(%.2f)", eclipse.state, eclipse.starfire_counter, eclipse.wrath_counter, buff.eclipse_solar.remains, buff.eclipse_lunar.remains ) end
return
end
end
if eclipse.state == "IN_SOLAR" then eclipse.state = "LUNAR_NEXT" end
if eclipse.state == "IN_LUNAR" then eclipse.state = "SOLAR_NEXT" end
if eclipse.state == "IN_BOTH" then eclipse.state = "ANY_NEXT" end
if Hekili.ActiveDebug then Hekili:Debug( "Eclipse Advance (Post): %s - Starfire(%d), Wrath(%d), Solar(%.2f), Lunar(%.2f)", eclipse.state, eclipse.starfire_counter, eclipse.wrath_counter, buff.eclipse_solar.remains, buff.eclipse_lunar.remains ) end
end, state )
}, {
__index = function( t, k )
-- any_next
if k == "any_next" then
return eclipse.state == "ANY_NEXT"
-- in_any
elseif k == "in_any" then
return eclipse.state == "IN_SOLAR" or eclipse.state == "IN_LUNAR" or eclipse.state == "IN_BOTH"
-- in_solar
elseif k == "in_solar" then
return eclipse.state == "IN_SOLAR"
-- in_lunar
elseif k == "in_lunar" then
return eclipse.state == "IN_LUNAR"
-- in_both
elseif k == "in_both" then
return eclipse.state == "IN_BOTH"
-- solar_next
elseif k == "solar_next" then
return eclipse.state == "SOLAR_NEXT"
-- solar_in
elseif k == "solar_in" then
return eclipse.starfire_counter
-- solar_in_2
elseif k == "solar_in_2" then
return eclipse.starfire_counter == 2
-- solar_in_1
elseif k == "solar_in_1" then
return eclipse.starfire_counter == 1
-- lunar_next
elseif k == "lunar_next" then
return eclipse.state == "LUNAR_NEXT"
-- lunar_in
elseif k == "lunar_in" then
return eclipse.wrath_counter
-- lunar_in_2
elseif k == "lunar_in_2" then
return eclipse.wrath_counter == 2
-- lunar_in_1
elseif k == "lunar_in_1" then
return eclipse.wrath_counter == 1
end
end
} ) )
spec:RegisterStateTable( "druid", setmetatable( {},{
__index = function( t, k )
if k == "catweave_bear" then return false
elseif k == "owlweave_bear" then return false
elseif k == "primal_wrath" then return debuff.rip
elseif k == "lunar_inspiration" then return debuff.moonfire_cat
elseif k == "no_cds" then return not toggle.cooldowns
elseif k == "delay_berserking" then return settings.delay_berserking
4 years ago
elseif rawget( debuff, k ) ~= nil then return debuff[ k ] end
return false
end
} ) )
local LycarasHandler = setfenv( function ()
if buff.travel_form.up then state:RunHandler( "stampeding_roar" )
elseif buff.moonkin_form.up then state:RunHandler( "starfall" )
elseif buff.bear_form.up then state:RunHandler( "barkskin" )
elseif buff.cat_form.up then state:RunHandler( "primal_wrath" )
else state:RunHandler( "wild_growth" ) end
end, state )
local SinfulHysteriaHandler = setfenv( function ()
applyBuff( "ravenous_frenzy_sinful_hysteria" )
end, state )
spec:RegisterHook( "reset_precast", function ()
if IsActiveSpell( class.abilities.new_moon.id ) then active_moon = "new_moon"
elseif IsActiveSpell( class.abilities.half_moon.id ) then active_moon = "half_moon"
elseif IsActiveSpell( class.abilities.full_moon.id ) then active_moon = "full_moon"
else active_moon = nil end
-- UGLY
if talent.incarnation.enabled then
rawset( cooldown, "ca_inc", cooldown.incarnation )
4 years ago
rawset( buff, "ca_inc", buff.incarnation )
else
rawset( cooldown, "ca_inc", cooldown.celestial_alignment )
4 years ago
rawset( buff, "ca_inc", buff.celestial_alignment )
end
if buff.warrior_of_elune.up then
setCooldown( "warrior_of_elune", 3600 )
end
eclipse.reset()
if buff.lycaras_fleeting_glimpse.up then
state:QueueAuraExpiration( "lycaras_fleeting_glimpse", LycarasHandler, buff.lycaras_fleeting_glimpse.expires )
end
if legendary.sinful_hysteria.enabled and buff.ravenous_frenzy.up then
state:QueueAuraExpiration( "ravenous_frenzy", SinfulHysteriaHandler, buff.ravenous_frenzy.expires )
end
end )
spec:RegisterHook( "step", function()
if Hekili.ActiveDebug then Hekili:Debug( "Eclipse State: %s, Wrath: %d, Starfire: %d; Lunar: %.2f, Solar: %.2f\n", eclipse.state or "NOT SET", eclipse.wrath_counter, eclipse.starfire_counter, buff.eclipse_lunar.remains, buff.eclipse_solar.remains ) end
end )
spec:RegisterHook( "spend", function( amt, resource )
if legendary.primordial_arcanic_pulsar.enabled and resource == "astral_power" and amt > 0 then
local v1 = ( buff.primordial_arcanic_pulsar.v1 or 0 ) + amt
if v1 >= 300 then
applyBuff( talent.incarnation.enabled and "incarnation" or "celestial_alignment", 9 )
v1 = v1 - 300
end
if v1 > 0 then
applyBuff( "primordial_arcanic_pulsar", nil, max( 1, floor( amt / 30 ) ) )
buff.primordial_arcanic_pulsar.v1 = v1
else
removeBuff( "primordial_arcanic_pulsar" )
end
end
end )
4 years ago
-- Tier 28
spec:RegisterGear( "tier28", 188853, 188851, 188849, 188848, 188847 )
spec:RegisterSetBonuses( "tier28_2pc", 364423, "tier28_4pc", 363497 )
-- 2-Set - Celestial Pillar - Entering Lunar Eclipse creates a Fury of Elune at 25% effectiveness that follows your current target for 8 sec.
-- 4-Set - Umbral Infusion - While in an Eclipse, the cost of Starsurge and Starfall is reduced by 20%.
-- Legion Sets (for now).
spec:RegisterGear( "tier21", 152127, 152129, 152125, 152124, 152126, 152128 )
spec:RegisterAura( "solar_solstice", {
id = 252767,
duration = 6,
max_stack = 1,
} )
spec:RegisterGear( "tier20", 147136, 147138, 147134, 147133, 147135, 147137 )
spec:RegisterGear( "tier19", 138330, 138336, 138366, 138324, 138327, 138333 )
spec:RegisterGear( "class", 139726, 139728, 139723, 139730, 139725, 139729, 139727, 139724 )
spec:RegisterGear( "impeccable_fel_essence", 137039 )
spec:RegisterGear( "oneths_intuition", 137092 )
spec:RegisterAuras( {
oneths_intuition = {
id = 209406,
duration = 3600,
max_stacks = 1,
},
oneths_overconfidence = {
id = 209407,
duration = 3600,
max_stacks = 1,
},
} )
spec:RegisterGear( "radiant_moonlight", 151800 )
spec:RegisterGear( "the_emerald_dreamcatcher", 137062 )
spec:RegisterAura( "the_emerald_dreamcatcher", {
id = 224706,
duration = 5,
max_stack = 2,
} )
-- Abilities
spec:RegisterAbilities( {
barkskin = {
id = 22812,
cast = 0,
cooldown = function () return 60 * ( 1 + ( conduit.tough_as_bark.mod * 0.01 ) ) end,
gcd = "off",
toggle = "defensives",
defensive = true,
startsCombat = false,
texture = 136097,
handler = function ()
applyBuff( "barkskin" )
end,
},
bear_form = {
id = 5487,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = -25,
spendType = "rage",
startsCombat = false,
texture = 132276,
noform = "bear_form",
handler = function ()
shift( "bear_form" )
if conduit.ursine_vigor.enabled then applyBuff( "ursine_vigor" ) end
end,
auras = {
-- Conduit
ursine_vigor = {
id = 340541,
duration = 4,
max_stack = 1
}
}
},
cat_form = {
id = 768,
cast = 0,
cooldown = 0,
gcd = "spell",
startsCombat = false,
texture = 132115,
noform = "cat_form",
handler = function ()
shift( "cat_form" )
end,
auras = {
-- Conduit
tireless_pursuit = {
id = 340546,
duration = function () return conduit.tireless_pursuit.enabled and conduit.tireless_pursuit.mod or 3 end,
max_stack = 1,
}
}
},
celestial_alignment = {
id = 194223,
cast = 0,
cooldown = function () return ( essence.vision_of_perfection.enabled and 0.85 or 1 ) * 180 end,
gcd = "off",
toggle = "cooldowns",
startsCombat = true,
texture = 136060,
notalent = "incarnation",
handler = function ()
applyBuff( "celestial_alignment" )
stat.haste = stat.haste + 0.1
eclipse.trigger_both( 20 )
if pvptalent.moon_and_stars.enabled then applyBuff( "moon_and_stars" ) end
end,
copy = "ca_inc"
},
cyclone = {
id = 33786,
cast = function () return pvptalent.owlkin_adept.enabled and buff.owlkin_frenzy.up and 0.85 or 1.7 end,
cooldown = 0,
gcd = "spell",
spend = 0.1,
spendType = "mana",
startsCombat = true,
texture = 136022,
handler = function ()
applyDebuff( "target", "cyclone" )
end,
},
dash = {
id = 1850,
cast = 0,
cooldown = 120,
gcd = "off",
startsCombat = false,
texture = 132120,
notalent = "tiger_dash",
handler = function ()
if not buff.cat_form.up then
shift( "cat_form" )
end
applyBuff( "dash" )
end,
},
entangling_roots = {
id = 339,
cast = function () return pvptalent.owlkin_adept.enabled and buff.owlkin_frenzy.up and 0.85 or 1.7 end,
cooldown = 0,
gcd = "spell",
spend = 0.06,
spendType = "mana",
startsCombat = false,
texture = 136100,
handler = function ()
applyDebuff( "target", "entangling_roots" )
end,
},
faerie_swarm = {
id = 209749,
cast = 0,
cooldown = 30,
gcd = "spell",
pvptalent = "faerie_swarm",
startsCombat = true,
texture = 538516,
handler = function ()
applyDebuff( "target", "faerie_swarm" )
end,
},
ferocious_bite = {
id = 22568,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = 50,
spendType = "energy",
startsCombat = true,
texture = 132127,
form = "cat_form",
usable = function () return combo_points.current > 0 end,
handler = function ()
--[[ if target.health.pct < 25 and debuff.rip.up then
applyDebuff( "target", "rip", min( debuff.rip.duration * 1.3, debuff.rip.remains + debuff.rip.duration ) )
end ]]
spend( combo_points.current, "combo_points" )
end,
},
--[[ flap = {
id = 164862,
cast = 0,
cooldown = 0,
gcd = "spell",
startsCombat = true,
texture = 132925,
handler = function ()
end,
}, ]]
force_of_nature = {
id = 205636,
cast = 0,
cooldown = 60,
gcd = "spell",
spend = -20,
spendType = "astral_power",
toggle = "cooldowns",
startsCombat = true,
texture = 132129,
talent = "force_of_nature",
ap_check = function() return check_for_ap_overcap( "force_of_nature" ) end,
handler = function ()
summonPet( "treants", 10 )
end,
},
frenzied_regeneration = {
id = 22842,
cast = 0,
charges = function () return ( talent.guardian_affinity.enabled and buff.heart_of_the_wild.up ) and 2 or nil end,
cooldown = 36,
recharge = 36,
gcd = "spell",
spend = 10,
spendType = "rage",
startsCombat = false,
texture = 132091,
form = "bear_form",
talent = "guardian_affinity",
handler = function ()
applyBuff( "frenzied_regeneration" )
gain( 0.08 * health.max, "health" )
end,
},
full_moon = {
id = 274283,
known = 274281,
cast = 3,
charges = 3,
cooldown = 20,
recharge = 20,
gcd = "spell",
spend = -40,
spendType = "astral_power",
texture = 1392542,
startsCombat = true,
talent = "new_moon",
bind = "half_moon",
ap_check = function() return check_for_ap_overcap( "full_moon" ) end,
usable = function () return active_moon == "full_moon" end,
handler = function ()
spendCharges( "new_moon", 1 )
spendCharges( "half_moon", 1 )
-- Radiant Moonlight, NYI.
active_moon = "new_moon"
end,
},
fury_of_elune = {
id = 202770,
cast = 0,
cooldown = 60,
gcd = "spell",
-- toggle = "cooldowns",
startsCombat = true,
texture = 132123,
handler = function ()
if not buff.moonkin_form.up then unshift() end
applyDebuff( "target", "fury_of_elune_ap" )
end,
},
growl = {
id = 6795,
cast = 0,
cooldown = 8,
gcd = "off",
startsCombat = true,
texture = 132270,
form = "bear_form",
handler = function ()
applyDebuff( "target", "growl" )
end,
},
half_moon = {
id = 274282,
known = 274281,
cast = 2,
charges = 3,
cooldown = 20,
recharge = 20,
gcd = "spell",
spend = -20,
spendType = "astral_power",
texture = 1392543,
startsCombat = true,
talent = "new_moon",
bind = "new_moon",
ap_check = function() return check_for_ap_overcap( "half_moon" ) end,
usable = function () return active_moon == "half_moon" end,
handler = function ()
spendCharges( "new_moon", 1 )
spendCharges( "full_moon", 1 )
active_moon = "full_moon"
end,
},
heart_of_the_wild = {
id = 319454,
cast = 0,
cooldown = function () return 300 * ( 1 - ( conduit.born_of_the_wilds.mod * 0.01 ) ) end,
gcd = "spell",
toggle = "cooldowns",
talent = "heart_of_the_wild",
startsCombat = true,
texture = 135879,
handler = function ()
applyBuff( "heart_of_the_wild" )
if talent.feral_affinity.enabled then
shift( "cat_form" )
elseif talent.guardian_affinity.enabled then
shift( "bear_form" )
elseif talent.restoration_affinity.enabled then
unshift()
end
end,
},
hibernate = {
id = 2637,
cast = 1.5,
cooldown = 0,
gcd = "spell",
spend = 0.15,
spendType = "mana",
startsCombat = false,
texture = 136090,
handler = function ()
applyDebuff( "target", "hibernate" )
end,
},
incarnation = {
id = 102560,
cast = 0,
cooldown = function () return ( essence.vision_of_perfection.enabled and 0.85 or 1 ) * 180 end,
gcd = "off",
spend = -40,
spendType = "astral_power",
toggle = "cooldowns",
startsCombat = false,
texture = 571586,
talent = "incarnation",
handler = function ()
shift( "moonkin_form" )
applyBuff( "incarnation" )
stat.crit = stat.crit + 0.10
stat.haste = stat.haste + 0.10
eclipse.trigger_both( 20 )
if pvptalent.moon_and_stars.enabled then applyBuff( "moon_and_stars" ) end
end,
copy = { "incarnation_chosen_of_elune", "Incarnation" },
},
innervate = {
id = 29166,
cast = 0,
cooldown = 180,
gcd = "off",
toggle = "cooldowns",
startsCombat = false,
texture = 136048,
usable = function () return group end,
handler = function ()
active_dot.innervate = 1
end,
auras = {
innervate = {
id = 29166,
duration = 10,
max_stack = 1
}
}
},
ironfur = {
id = 192081,
cast = 0,
cooldown = 0.5,
gcd = "spell",
spend = 45,
spendType = "rage",
startsCombat = true,
texture = 1378702,
handler = function ()
applyBuff( "ironfur" )
end,
},
maim = {
id = 22570,
cast = 0,
cooldown = 20,
gcd = "spell",
talent = "feral_affinity",
spend = 30,
spendType = "energy",
startsCombat = true,
texture = 132134,
usable = function () return combo_points.current > 0, "requires combo points" end,
handler = function ()
applyDebuff( "target", "maim" )
spend( combo_points.current, "combo_points" )
end,
},
mangle = {
id = 33917,
cast = 0,
cooldown = 6,
gcd = "spell",
spend = -10,
spendType = "rage",
startsCombat = true,
texture = 132135,
form = "bear_form",
handler = function ()
end,
},
mass_entanglement = {
id = 102359,
cast = 0,
cooldown = function () return 30 * ( 1 - ( conduit.born_of_the_wilds.mod * 0.01 ) ) end,
gcd = "spell",
startsCombat = false,
texture = 538515,
talent = "mass_entanglement",
handler = function ()
applyDebuff( "target", "mass_entanglement" )
active_dot.mass_entanglement = max( active_dot.mass_entanglement, active_enemies )
end,
},
mighty_bash = {
id = 5211,
cast = 0,
cooldown = function () return 50 * ( 1 - ( conduit.born_of_the_wilds.mod * 0.01 ) ) end,
gcd = "spell",
startsCombat = true,
texture = 132114,
talent = "mighty_bash",
handler = function ()
applyDebuff( "target", "mighty_bash" )
end,
},
moonfire = {
id = 8921,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = -2,
spendType = "astral_power",
startsCombat = true,
texture = 136096,
cycle = "moonfire",
ap_check = function() return check_for_ap_overcap( "moonfire" ) end,
handler = function ()
if not buff.moonkin_form.up and not buff.bear_form.up then unshift() end
applyDebuff( "target", "moonfire" )
if talent.twin_moons.enabled and active_enemies > 1 then
active_dot.moonfire = min( active_enemies, active_dot.moonfire + 1 )
end
end,
},
moonkin_form = {
id = 24858,
cast = 0,
cooldown = 0,
gcd = "spell",
startsCombat = false,
texture = 136036,
noform = "moonkin_form",
essential = true,
handler = function ()
shift( "moonkin_form" )
end,
},
new_moon = {
id = 274281,
cast = 1,
charges = 3,
cooldown = 20,
recharge = 20,
gcd = "spell",
spend = -10,
spendType = "astral_power",
texture = 1392545,
startsCombat = true,
talent = "new_moon",
bind = "full_moon",
ap_check = function() return check_for_ap_overcap( "new_moon" ) end,
usable = function () return active_moon == "new_moon" end,
handler = function ()
spendCharges( "half_moon", 1 )
spendCharges( "full_moon", 1 )
active_moon = "half_moon"
end,
},
prowl = {
id = 5215,
cast = 0,
cooldown = 6,
gcd = "spell",
startsCombat = false,
texture = 514640,
usable = function () return time == 0 end,
handler = function ()
shift( "cat_form" )
applyBuff( "prowl" )
removeBuff( "shadowmeld" )
end,
},
rake = {
id = 1822,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = 35,
spendType = "energy",
startsCombat = true,
texture = 132122,
talent = "feral_affinity",
form = "cat_form",
handler = function ()
applyDebuff( "target", "rake" )
end,
},
regrowth = {
id = 8936,
cast = 1.5,
cooldown = 0,
gcd = "spell",
spend = 0.17,
spendType = "mana",
startsCombat = false,
texture = 136085,
handler = function ()
if buff.moonkin_form.down then unshift() end
applyBuff( "regrowth" )
end,
},
rejuvenation = {
id = 774,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = 0.11,
spendType = "mana",
startsCombat = false,
texture = 136081,
talent = "restoration_affinity",
handler = function ()
if buff.moonkin_form.down then unshift() end
applyBuff( "rejuvenation" )
end,
},
remove_corruption = {
id = 2782,
cast = 0,
cooldown = 8,
gcd = "spell",
spend = 0.06,
spendType = "mana",
startsCombat = true,
texture = 135952,
handler = function ()
end,
},
renewal = {
id = 108238,
cast = 0,
cooldown = 90,
gcd = "spell",
startsCombat = true,
texture = 136059,
talent = "renewal",
handler = function ()
gain( 0.3 * health.max, "health" )
end,
},
--[[ revive = {
id = 50769,
cast = 10,
cooldown = 0,
gcd = "spell",
spend = 0.04,
spendType = "mana",
startsCombat = true,
texture = 132132,
handler = function ()
end,
}, ]]
rip = {
id = 1079,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = 30,
spendType = "energy",
startsCombat = true,
texture = 132152,
talent = "feral_affinity",
form = "cat_form",
usable = function () return combo_points.current > 0 end,
handler = function ()
spend( combo_points.current, "combo_points" )
applyDebuff( "target", "rip" )
end,
},
shred = {
id = 5221,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = 40,
spendType = "energy",
startsCombat = true,
texture = 136231,
form = "cat_form",
handler = function ()
gain( 1, "combo_points" )
end,
},
solar_beam = {
id = 78675,
cast = 0,
cooldown = 60,
gcd = "off",
spend = 0.17,
spendType = "mana",
toggle = "interrupts",
startsCombat = true,
texture = 252188,
debuff = "casting",
readyTime = state.timeToInterrupt,
handler = function ()
if buff.moonkin_form.down then unshift() end
interrupt()
end,
},
soothe = {
id = 2908,
cast = 0,
cooldown = 10,
gcd = "spell",
spend = 0.06,
spendType = "mana",
startsCombat = true,
texture = 132163,
usable = function () return buff.dispellable_enrage.up end,
handler = function ()
if buff.moonkin_form.down then unshift() end
removeBuff( "dispellable_enrage" )
end,
},
stag_form = {
id = 210053,
cast = 0,
cooldown = 0,
gcd = "spell",
startsCombat = false,
texture = 1394966,
noform = "travel_form",
handler = function ()
shift( "stag_form" )
end,
},
stampeding_roar = {
id = 106898,
cast = 0,
cooldown = 120,
gcd = "spell",
startsCombat = false,
texture = 464343,
handler = function ()
if buff.bear_form.down and buff.cat_form.down then
shift( "bear_form" )
end
applyBuff( "stampeding_roar" )
end,
},
starfall = {
id = 191034,
cast = 0,
cooldown = function () return talent.stellar_drift.enabled and 12 or 0 end,
gcd = "spell",
spend = function () return ( buff.oneths_perception.up and 0 or 50 ) * ( 1 - ( buff.timeworn_dreambinder.stack * 0.1 ) ) * ( set_bonus.tier28_4pc > 0 and ( buff.eclipse_solar.up or buff.eclipse_lunar.up ) and 0.85 or 1 ) end,
spendType = "astral_power",
startsCombat = true,
texture = 236168,
ap_check = function() return check_for_ap_overcap( "starfall" ) end,
handler = function ()
if talent.starlord.enabled then
if buff.starlord.stack < 3 then stat.haste = stat.haste + 0.04 end
addStack( "starlord", buff.starlord.remains > 0 and buff.starlord.remains or nil, 1 )
end
applyBuff( "starfall" )
if level > 53 then
if debuff.moonfire.up then debuff.moonfire.expires = debuff.moonfire.expires + 4 end
if debuff.sunfire.up then debuff.sunfire.expires = debuff.sunfire.expires + 4 end
end
removeBuff( "oneths_perception" )
if legendary.timeworn_dreambinder.enabled then
addStack( "timeworn_dreambinder", nil, 1 )
end
end,
},
starfire = {
id = function () return state.spec.balance and 194153 or 197628 end,
known = function () return state.spec.balance and IsPlayerSpell( 194153 ) or IsPlayerSpell( 197628 ) end,
cast = function ()
if buff.warrior_of_elune.up or buff.elunes_wrath.up or buff.owlkin_frenzy.up then return 0 end
return haste * ( buff.eclipse_lunar and ( level > 46 and 0.8 or 0.92 ) or 1 ) * 2.25
end,
cooldown = 0,
gcd = "spell",
spend = function () return ( buff.warrior_of_elune.up and 1.4 or 1 ) * -8 end,
spendType = "astral_power",
startsCombat = true,
texture = 135753,
ap_check = function() return check_for_ap_overcap( "starfire" ) end,
talent = function () return ( not state.spec.balance and "balance_affinity" or nil ) end,
handler = function ()
if not buff.moonkin_form.up then unshift() end
if eclipse.state == "ANY_NEXT" or eclipse.state == "SOLAR_NEXT" then
eclipse.starfire_counter = eclipse.starfire_counter - 1
eclipse.advance()
end
if level > 53 then
if debuff.moonfire.up then debuff.moonfire.expires = debuff.moonfire.expires + 4 end
if debuff.sunfire.up then debuff.sunfire.expires = debuff.sunfire.expires + 4 end
end
if buff.elunes_wrath.up then
removeBuff( "elunes_wrath" )
elseif buff.warrior_of_elune.up then
removeStack( "warrior_of_elune" )
if buff.warrior_of_elune.down then
setCooldown( "warrior_of_elune", 45 )
end
elseif buff.owlkin_frenzy.up then
removeStack( "owlkin_frenzy" )
end
if azerite.dawning_sun.enabled then applyBuff( "dawning_sun" ) end
end,
copy = { 194153, 197628 }
},
starsurge = {
id = 78674,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = function () return ( buff.oneths_clear_vision.up and 0 or 30 ) * ( 1 - ( buff.timeworn_dreambinder.stack * 0.1 ) ) * ( set_bonus.tier28_4pc > 0 and ( buff.eclipse_solar.up or buff.eclipse_lunar.up ) and 0.85 or 1 ) end,
spendType = "astral_power",
startsCombat = true,
texture = 135730,
ap_check = function() return check_for_ap_overcap( "starsurge" ) end,
handler = function ()
if talent.starlord.enabled then
if buff.starlord.stack < 3 then stat.haste = stat.haste + 0.04 end
addStack( "starlord", buff.starlord.remains > 0 and buff.starlord.remains or nil, 1 )
end
removeBuff( "oneths_clear_vision" )
removeBuff( "sunblaze" )
if buff.eclipse_solar.up then buff.eclipse_solar.empowerTime = query_time; applyBuff( "starsurge_empowerment_solar" ) end
if buff.eclipse_lunar.up then buff.eclipse_lunar.empowerTime = query_time; applyBuff( "starsurge_empowerment_lunar" ) end
if pvptalent.moonkin_aura.enabled then
addStack( "moonkin_aura", nil, 1 )
end
if azerite.arcanic_pulsar.enabled then
addStack( "arcanic_pulsar" )
if buff.arcanic_pulsar.stack == 9 then
removeBuff( "arcanic_pulsar" )
applyBuff( "ca_inc", 6 )
eclipse.trigger_both( 6 )
end
end
if legendary.timeworn_dreambinder.enabled then
addStack( "timeworn_dreambinder", nil, 1 )
end
end,
auras = {
starsurge_empowerment_lunar = {
duration = 3600,
max_stack = 30,
generate = function( t )
local last = action.starsurge.lastCast
t.name = "Starsurge Empowerment (Lunar)"
if eclipse.in_any then
t.applied = last
t.duration = buff.eclipse_lunar.expires - last
t.expires = t.applied + t.duration
t.count = 1
t.caster = "player"
return
end
t.applied = 0
t.duration = 0
t.expires = 0
t.count = 0
t.caster = "nobody"
end,
copy = "starsurge_lunar"
},
starsurge_empowerment_solar = {
duration = 3600,
max_stack = 30,
generate = function( t )
local last = action.starsurge.lastCast
t.name = "Starsurge Empowerment (Solar)"
if eclipse.in_any then
t.applied = last
t.duration = buff.eclipse_solar.expires - last
t.expires = t.applied + t.duration
t.count = 1
t.caster = "player"
return
end
t.applied = 0
t.duration = 0
t.expires = 0
t.count = 0
t.caster = "nobody"
end,
copy = "starsurge_solar"
}
}
},
stellar_flare = {
id = 202347,
cast = 1.5,
cooldown = 0,
gcd = "spell",
spend = -8,
spendType = "astral_power",
startsCombat = true,
texture = 1052602,
cycle = "stellar_flare",
talent = "stellar_flare",
ap_check = function() return check_for_ap_overcap( "stellar_flare" ) end,
handler = function ()
applyDebuff( "target", "stellar_flare" )
end,
},
sunfire = {
id = 93402,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = -2,
spendType = "astral_power",
startsCombat = true,
texture = 236216,
cycle = "sunfire",
ap_check = function()
return astral_power.current - action.sunfire.cost + ( talent.shooting_stars.enabled and 4 or 0 ) + ( talent.natures_balance.enabled and ceil( execute_time / 1.5 ) or 0 ) < astral_power.max
end,
readyTime = function()
return mana[ "time_to_" .. ( 0.12 * mana.max ) ]
end,
handler = function ()
spend( 0.12 * mana.max, "mana" ) -- I want to see AP in mouseovers.
applyDebuff( "target", "sunfire" )
active_dot.sunfire = active_enemies
end,
},
swiftmend = {
id = 18562,
cast = 0,
charges = 1,
cooldown = 25,
recharge = 25,
gcd = "spell",
spend = 0.14,
spendType = "mana",
startsCombat = false,
texture = 134914,
talent = "restoration_affinity",
handler = function ()
if buff.moonkin_form.down then unshift() end
gain( health.max * 0.1, "health" )
end,
},
--[[ May want to revisit this and split out swipe_cat from swipe_bear.
swipe_bear = {
id = 213764,
cast = 0,
cooldown = function () return haste * ( buff.cat_form.up and 0 or 6 ) end,
gcd = "spell",
spend = function () return buff.cat_form.up and 40 or nil end,
spendType = function () return buff.cat_form.up and "energy" or nil end,
startsCombat = true,
texture = 134296,
talent = "feral_affinity",
usable = function () return buff.cat_form.up or buff.bear_form.up end,
handler = function ()
if buff.cat_form.up then
gain( 1, "combo_points" )
end
end,
copy = { "swipe", 106785, 213771 },
bind = { "swipe", "swipe_bear", "swipe_cat" }
}, ]]
thrash_bear = {
id = 106832,
cast = 0,
cooldown = 0,
gcd = "spell",
spend = -5,
spendType = "rage",
cycle = "thrash_bear",
startsCombat = true,
texture = 451161,
talent = "guardian_affinity",
form = "bear_form",
handler = function ()
applyDebuff( "target", "thrash_bear", nil, debuff.thrash.stack + 1 )
end,
copy = { "thrash", 106832 },
bind = { "thrash", "thrash_bear", "thrash_cat" }
},
tiger_dash = {
id = 252216,
cast = 0,
cooldown = 45,
gcd = "off",
startsCombat = false,
texture = 1817485,
talent = "tiger_dash",
handler = function ()
shift( "cat_form" )
applyBuff( "tiger_dash" )
end,
},
thorns = {
4 years ago
id = 305497,
cast = 0,
cooldown = 45,
gcd = "spell",
pvptalent = function ()
if essence.conflict_and_strife.enabled then return end
return "thorns"
end,
spend = 0.12,
spendType = "mana",
startsCombat = false,
texture = 136104,
handler = function ()
applyBuff( "thorns" )
end,
},
travel_form = {
id = 783,
cast = 0,
cooldown = 0,
gcd = "spell",
startsCombat = false,
texture = 132144,
noform = "travel_form",
handler = function ()
shift( "travel_form" )
end,
},
treant_form = {
id = 114282,
cast = 0,
cooldown = 0,
gcd = "spell",
startsCombat = false,
texture = 132145,
handler = function ()
shift( "treant_form" )
end,
},
typhoon = {
id = 132469,
cast = 0,
cooldown = 30,
gcd = "spell",
startsCombat = true,
texture = 236170,
talent = "typhoon",
handler = function ()
applyDebuff( "target", "typhoon" )
if target.distance < 15 then setDistance( target.distance + 5 ) end
end,
},
ursols_vortex = {
id = 102793,
cast = 0,
cooldown = 60,
gcd = "spell",
talent = "restoration_affinity",
startsCombat = true,
texture = 571588,
handler = function ()
end,
},
warrior_of_elune = {
id = 202425,
cast = 0,
cooldown = 45,
gcd = "spell",
startsCombat = true,
texture = 135900,
talent = "warrior_of_elune",
usable = function () return buff.warrior_of_elune.down end,
handler = function ()
applyBuff( "warrior_of_elune", nil, 3 )
end,
},
--[[ wartime_ability = {
id = 264739,
cast = 0,
cooldown = 0,
gcd = "spell",
startsCombat = true,
texture = 1518639,
handler = function ()
end,
}, ]]
wild_charge = {
id = function () return buff.moonkin_form.up and 102383 or 102401 end,
known = 102401,
cast = 0,
cooldown = 15,
gcd = "spell",
startsCombat = false,
-- texture = 538771,
talent = "wild_charge",
handler = function ()
if buff.moonkin_form.up then setDistance( target.distance + 10 ) end
end,
copy = { 102401, 102383 }
},
wild_growth = {
id = 48438,
cast = 1.5,
cooldown = 10,
gcd = "spell",
spend = 0.3,
spendType = "mana",
startsCombat = false,
texture = 236153,
talent = "wild_growth",
handler = function ()
unshift()
applyBuff( "wild_growth" )
end,
},
wrath = {
id = 190984,
known = function () return state.spec.balance and IsPlayerSpell( 190984 ) or IsPlayerSpell( 5176 ) end,
cast = function () return haste * ( buff.eclipse_solar.up and ( level > 46 and 0.8 or 0.92 ) or 1 ) * 1.5 end,
cooldown = 0,
gcd = "spell",
spend = function () return ( talent.soul_of_the_forest.enabled and buff.eclipse_solar.up ) and -9 or -6 end,
spendType = "astral_power",
startsCombat = true,
texture = 535045,
ap_check = function () return check_for_ap_overcap( "solar_wrath" ) end,
velocity = 20,
4 years ago
impact = function ()
if not state.spec.balance and ( eclipse.state == "ANY_NEXT" or eclipse.state == "LUNAR_NEXT" ) then
eclipse.wrath_counter = eclipse.wrath_counter - 1
eclipse.advance()
end
end,
handler = function ()
if not buff.moonkin_form.up then unshift() end
4 years ago
if state.spec.balance and ( eclipse.state == "ANY_NEXT" or eclipse.state == "LUNAR_NEXT" ) then
eclipse.wrath_counter = eclipse.wrath_counter - 1
eclipse.advance()
end
removeBuff( "dawning_sun" )
if azerite.sunblaze.enabled then applyBuff( "sunblaze" ) end
end,
copy = { "solar_wrath", 5176 }
},
} )
spec:RegisterOptions( {
enabled = true,
aoe = 3,
nameplates = false,
nameplateRange = 8,
damage = true,
damageDots = true,
damageExpiration = 6,
4 years ago
enhancedRecheck = true,
potion = "spectral_intellect",
package = "Balance",
} )
spec:RegisterSetting( "starlord_cancel", false, {
name = "Cancel |T462651:0|t Starlord",
desc = "If checked, the addon will recommend canceling your Starlord buff before starting to build stacks with Starsurge again.\n\n" ..
"You will likely want a |cFFFFD100/cancelaura Starlord|r macro to manage this during combat.",
icon = 462651,
iconCoords = { 0.1, 0.9, 0.1, 0.9 },
type = "toggle",
width = "full"
} )
spec:RegisterSetting( "delay_berserking", false, {
name = "Delay |T135727:0|t Berserking",
desc = "If checked, the default priority will attempt to adjust the timing of |T135727:0|t Berserking to be consistent with simmed Power Infusion usage.",
type = "toggle",
width = "full",
} )
-- Starlord Cancel Override
class.specs[0].abilities.cancel_buff.funcs.usable = setfenv( function ()
if not settings.starlord_cancel and args.buff_name == "starlord" then return false, "starlord cancel option disabled" end
return args.buff_name ~= nil, "no buff name detected"
end, state )
spec:RegisterPack( "Balance", 20220501, [[di1qcgqiHOhHOsxIuuSjsPpPuQrjrDkfLvjrYRqaZIuKBrkWUO4xiQAykfDmeQLrPQNjryAKc6Aik2gPO03qqQXrPsoNerSojIAEiq3tiSpfv9psrvrhuPKwOIkEiIstKuu5IKc1grqYhjfvLgjPOQYjvujRuPWlLisAMKc5MiiQDQuIFskQYqrq4OKIQclvIi1tbPPkrQRQOsTvjIeFfbrglIk2Re(RKgmXHPAXKQhlyYG6YqBwHpJiJwP60QSAsrvvVgHmBsUTqTBr)gy4uYXPuPwUuphPPRQRRKTdIVtPmEeuNxiTEkvmFfz)OUG4Isxaf2FSyl2VP92Vjz2KyJ9eVzjSxdlG(rTWcOwEGiNewan9ySa6CCLNbSaQLhvbC4IsxaLcwDalGU)VfTKjp51DLNbudOxCWq6(9LU5aKFoUYZaQbqVyYs(yyZ(hR0854uye6UYZaAEc)fq1xN6NRSqVakS)yXwSFt7TFtYSjXg7jEZsqCjPaQV(Dqxaf6ft2cO7hmmMf6fqHrAOa6CCLNbKfnxVoyEJTA1NIf7TlnXI9BAV98g8gKD3tsiTK5n0aw2kmmcZcuGYBwMd6XgEdnGfYU7jjeML3Bs4xVblbNIuwEalHObfwFVjHp1WBObSusJXaiimlRmXasPEhLfiEFUUcPSu(mOrtSy1iKk99MUAsilAW8Sy1ied99MUAs4mdVHgWYwHaoywSAm40)ssSqi1(VZYny5(TPS87il2AqsIfnoOolkA4n0awiKDIqwiliHaicz53rwGAD99uwCwu3)kKLyqJSmuiHpDfYs5BWsuWILDho3(zz)EwUNf6fVuVNiyrvrzX297SmhnVTwAwialKfvi9pxXYwvhPmgZxtSC)2WSqj6SMz4n0awiKDIqwIb0NLThhP9V2ySFjDBwObm9(auwCllvuwEal6akLLXrA)PSasvudVHgWsPB0FwknigzbmyzokFNL5O8DwMJY3zXPS4SqTWW5kw((sIW3WBObSO5zHj2Su(mOrtSqi1(VRjwiKA)31elqFVhxJZyj2HrwIbnYsJ0tDy(S8awqVvh2SeaX6(Rb0373WBObSqOocZsj1lHBeMfno2cyd7ymFwc7yGiwgGMfYQ5yzrDsOH3qdyPKgJbqqwG71bBsqnatzjSJbIOgEdEJTMj49hHzzoUYZaYYwjeAelbpzrhzzawjml(ZY()w0sM8Kx3vEgqnGEXbdP73x6Mdq(54kpdOga9Ijl5JHn7FSsZNJtHrO7kpdO5j8xavD0Nwu6cOalmXUO0fBH4IsxaftxxHWfZPaQh(dKfqT1(VxafgPH(S(dKfqjengC6ZI9Sqi1(VZINWS4Sa99MUAsilGKfOLMfB3VZYwos7plekhzXtywMdyRLMfqZc037X1ilGFhBBhflGg67X(8cOLzbdQZIIg1k9UMiHFwMMybdQZIIMlRuGYBwMMybdQZIIMlR6GFNLPjwWG6SOOXZO1ej8ZYmw0YIvJqmeBS1(VZIwwIKfRgHyS3yR9FV4l2I9fLUakMUUcHlMtbup8hilGsFVhxJfqd99yFEb0YSuMLizPxjoanj0O7kpdyfmQUsv)9ljrny66keMLPjwIKLaacME(M8iT)1HJSmnXsKSqTqLQ(EtcFQH(EpCLILiyHywMMyjswExH5Bs)xnsR6UYZaAW01vimlZyzAILYSGb1zrrdfO8UMiHFwMMybdQZIIMlRQv6nlttSGb1zrrZLvDWVZY0elyqDwu04z0AIe(zzglZyrllrYcf)QoixuZFyBVDvT3kuavDjwdWfqjtXxSLsuu6cOy66keUyofq9WFGSak99MUAsyb0qFp2NxaTml9kXbOjHgDx5zaRGr1vQ6VFjjQbtxxHWSOLLaacME(M8iT)1HJSOLfQfQu13Bs4tn037HRuSebleZYmw0YsKSqXVQdYf18h22Bxv7TcfqvxI1aCbuYu8fFbuyC4l1xu6ITqCrPlG6H)azbukq5Dvh94cOy66keUyofFXwSVO0fqX01viCXCkGg67X(8cO)fJSqqwkZI9Sukw8WFG0yR9F3eC6x)lgzHaS4H)aPH(EpUgnbN(1)IrwMvaL(9f(ITqCbup8hilGgCLQ6H)azvD0VaQ6OFn9ySakWctSl(ITuIIsxaftxxHWfZPakWQakf)cOE4pqwafI3NRRWcOqC1clGsTqLQ(EtcFQH(EpCLIL5zHyw0YszwIKL3vy(g67Tc0WgmDDfcZY0elVRW8n0hvkVRW9nEdMUUcHzzglttSqTqLQ(EtcFQH(EpCLIL5zX(cOWin0N1FGSaku8PSSvGgZcizPeeGfB3VdwplW9nEw8eMfB3VZc03BfOHzXtywSNaSa(DSTDuSakeVRPhJfqpA1byXxSfnSO0fqX01viCXCkGcSkGsXVaQh(dKfqH4956kSakexTWcOuluPQV3KWNAOV3JRrwMNfIlGcJ0qFw)bYcOqXNYsqHoeKfB7yYc037X1ilbpzz)EwSNaS8EtcFkl22VWolhLLgviepFwgGMLFhzrJdQZIIS8aw0rwSACGDJWS4jml22VWolJtPWMLhWsWPFbuiExtpglGE0AqHoeS4l2czkkDbumDDfcxmNcOaRcOu8lG6H)azbuiEFUUclGcXvlSaQvJqQKcWgInXaqoUgzzAIfRgHujfGneBORCCnYY0elwncPskaBi2qFVPRMeYY0elwncPskaBi2qFVhUsXY0elwncPskaBi2mwD0kyur1krwMMyXQriM2HGjyrRJgt7eLLPjw0xJHj41ldMgJ9lPSebl6RXWe86Lbd8Q9)ajlttSaX7Z1vO5OvhGfqHrAOpR)azb0skEFUUcz539NLWogiIYYnyjkyXI3ilxYIZcPamlpGfhc4Gz53rwO3V8)ajl22XgzXz57ljcFwWpWYrzzrrywUKfD8THyYsWPpTakeVRPhJfqVSskax8fBrZwu6cOy66keUyofq9WFGSaQo2uSj6ssfqHrAOpR)azb05MISmhSPyt0LKyXFw(DKfmHzbmyHq1yANOSyBhtw2D6JSCuwCDaeKfn7MAgnXIpESzHSGecGiKfB3VZYCaEPzXtywa)o22okYIT73zHSBL8ZvgkGg67X(8cOLzPmlrYsaabtpFtEK2)6WrwMMyjswcaGcgylnbqcbqew)DSsTU(EQzzXY0elrYsVsCaAsOr3vEgWkyuDLQ(7xsIAW01vimlZyrll6RXWe86LbtJX(LuwMNfIjdlAzjswcaiy65BGG5VhTzzAILaacME(giy(7rBw0YI(AmmbVEzWSSyrll6RXW0oemblAD0yANOMLflAzPml6RXW0oemblAD0yANOMgJ9lPSqWiyHy7zrdyrdzPuS0RehGMeAOxowQ6Eu6J95gmDDfcZY0el6RXWe86LbtJX(LuwiiletmlttSqmlKNfQfQu1DN(ileKLYSqSPKWIgWY7kmFd9rLY76q5nAW01vimlLILnneZIgWcCVoydmQ8OvDSPyt0LKyPuSSPPeSmJLzSmJfTSaX7Z1vO5YkPaCXxSfcDrPlGIPRRq4I5uan03J95fqlZI(AmmbVEzW0ySFjLL5zHyYWIwwkZsKS0RehGMeAOxowQ6Eu6J95gmDDfcZY0el6RXW0oemblAD0yANOMgJ9lPSqqwiUKWIww0xJHPDiycw06OX0ornllwMXY0el6akLfTSmos7FTXy)skleKf7jdlZyrllq8(CDfAUSskaxafgPH(S(dKfqjeGNfB3VZIZcz3k5NRmWYV7plhn3(zXzHqSuuVzXQbbwanl22XKLFhzzCK2FwoklUoy9S8awWeUaQh(dKfqTa)bYIVyl2vrPlGIPRRq4I5uafyvaLIFbup8hilGcX7Z1vybuiUAHfqd4PyPmlLzzCK2)AJX(Luw0awiMmSObSeaafmWwAcE9YGPXy)sklZyH8SqSDTjlZyzEwc4PyPmlLzzCK2)AJX(Luw0awiMmSObSeaafmWwAcGecGiS(7yLAD99utJX(LuwMXc5zHy7AtwMXIwwIKL2p4kcbZ34WWuds4J(uw0
5 years ago
end