-- Covenants.lua -- November 2020 local addon, ns = ... local Hekili = _G[ addon ] local state = Hekili.State local class = Hekili.Class local all = Hekili.Class.specs[ 0 ] local GetItemCooldown = C_Item.GetItemCooldown local GetItemCount = C_Item.GetItemCount local IsUsableItem = C_Item.IsUsableItem local GetItemInfo = C_Item.GetItemInfo local IsUsableSpell = C_Spell.IsSpellUsable local AreCovenantsDisabled = ns.AreCovenantsDisabled -- Covenants do local CovenantSignatures = { kyrian = { 324739 }, necrolord = { 324631 }, night_fae = { 310143, 324701 }, venthyr = { 300728 }, } CovenantSignatures[1] = CovenantSignatures.kyrian CovenantSignatures[2] = CovenantSignatures.venthyr CovenantSignatures[3] = CovenantSignatures.night_fae CovenantSignatures[4] = CovenantSignatures.necrolord local CovenantKeys = { "kyrian", "venthyr", "night_fae", "necrolord" } local GetActiveCovenantID = C_Covenants.GetActiveCovenantID -- v1, no caching. state.covenant = setmetatable( {}, { __index = function( t, k ) if AreCovenantsDisabled() then if type( k ) == "string" and k == "none" then return true end return false end if type( k ) == "number" then if disabled then return false end if GetActiveCovenantID() == k then return true end if CovenantSignatures[ k ] then for _, spell in ipairs( CovenantSignatures[ k ] ) do if IsSpellKnownOrOverridesKnown( spell ) then return true end end end return false end -- Strings. local myCovenant = GetActiveCovenantID() if k == "none" then if disabled then return true end -- thanks glue if myCovenant > 0 then return false end -- We have to rule out Threads of Fate as well as real Covenants. for i, cov in ipairs( CovenantSignatures ) do for _, spell in ipairs( cov ) do if IsSpellKnownOrOverridesKnown( spell ) then return false end end end return true end if disabled then return false end if myCovenant > 0 then if k == CovenantKeys[ myCovenant ] then return true end end if CovenantSignatures[ k ] then for _, spell in ipairs( CovenantSignatures[ k ] ) do if IsSpellKnownOrOverridesKnown( spell ) then return true end end end -- Support covenant.fae_guardians and similar syntax. if class.abilities[ k ] then if state:IsKnown( k ) then return true end end return false end, } ) end -- 9.0 Covenant Shared Abilities and Effects do all:RegisterGear( "relic_of_the_first_ones", 184807 ) all:RegisterAbilities( { door_of_shadows = { id = 300728, cast = 1.5, cooldown = function () return equipped.relic_of_the_first_ones and 48 or 60 end, gcd = "spell", toggle = "cooldowns", startsCombat = true, texture = 3586270, handler = function () end, }, phial_of_serenity = { name = function () return ( GetItemInfo( 177278 ) ) or "Phial of Serenity" end, listName = function () local _, link, _, _, _, _, _, _, _, tex = GetItemInfo( 177278 ) if link and tex then return "|T" .. tex .. ":0|t " .. link end return "|cff00ccff[Phial of Serenity]|r" end, cast = 0, cooldown = function () return time > 0 and 3600 or 60 end, gcd = "off", item = 177278, bagItem = true, startsCombat = false, texture = 463534, toggle = function () if not toggle.interrupts then return "interrupts" end if not toggle.essences then return "essences" end return "essences" end, usable = function () if GetItemCount( 177278 ) == 0 then return false, "requires phial in bags" elseif not IsUsableItem( 177278 ) then return false, "phial on combat cooldown" elseif health.current == health.max then return false, "requires a health deficit" end return true end, readyTime = function () local start, duration = GetItemCooldown( 177278 ) return max( 0, start + duration - query_time ) end, handler = function () gain( 0.15 * health.max, "health" ) removeBuff( "dispellable_disease" ) removeBuff( "dispellable_poison" ) removeBuff( "dispellable_curse" ) removeBuff( "dispellable_bleed" ) -- TODO: Bleeds? end, }, fleshcraft = { id = 324631, cast = function () return 3 * haste end, channeled = true, cooldown = function () return equipped.relic_of_the_first_ones and 96 or 120 end, gcd = "spell", startsCombat = false, texture = 3586267, start = function () applyBuff( "fleshcraft" ) if conduit.volatile_solvent.enabled then applyBuff( "volatile_solvent" ) end end, auras = { fleshcraft = { id = 324867, duration = 120, max_stack = 1 }, volatile_solvent_beast = { id = 323498, duration = 120, max_stack = 1, }, volatile_solvent_elemental = { id = 323504, duration = 120, max_stack = 1, }, volatile_solvent_aberration = { id = 323497, duration = 120, max_stack = 1, }, volatile_solvent_mechanical = { id = 323507, duration = 120, max_stack = 1, }, volatile_solvent_undead = { id = 323509, duration = 120, max_stack = 1, }, volatile_solvent_humanoid = { id = 323491, duration = 120, max_stack = 1, }, volatile_solvent_demon = { id = 323500, duration = 120, max_stack = 1, }, volatile_solvent_dragonkin = { id = 323502, duration = 120, max_stack = 1, }, volatile_solvent_giant = { id = 323506, duration = 120, max_stack = 1, }, volatile_solvent = { alias = { "volatile_solvent_beast", "volatile_solvent_elemental", "volatile_solvent_aberration", "volatile_solvent_mechanical", "volatile_solvent_undead", "volatile_solvent_humanoid", "volatile_solvent_demon", "volatile_solvent_dragonkin", "volatile_solvent_giant" }, aliasMode = "longest", -- use duration info from the buff with the longest remaining time. aliasType = "buff", duration = 120, }, } }, } ) all:RegisterAuras( { echo_of_eonar = { id = 338489, duration = 10, max_stack = 1, }, maw_rattle = { id = 341617, duration = 10, max_stack = 1 }, sephuzs_proclamation = { id = 339463, duration = 15, max_stack = 1 }, sephuz_proclamation_icd = { duration = 30, max_stack = 1, -- TODO: Track last application of Sephuz's buff via event and create a generator to manufacture this buff. }, third_eye_of_the_jailer = { id = 339970, duration = 60, max_stack = 5, }, vitality_sacrifice_buff = { id = 338746, duration = 20, max_stack = 1, }, --[[ vitality_sacrifice_debuff = { id = 339131, duration = 60, max_stack = 1 } ]] } ) end local baseClass = UnitClassBase( "player" ) if baseClass == "DEATHKNIGHT" then all:RegisterAbilities( { shackle_the_unworthy = { id = 312202, cast = 0, cooldown = 60, gcd = "spell", startsCombat = true, texture = 3565442, toggle = "essences", handler = function () applyDebuff( "target", "shackle_the_unworthy" ) end, auras = { final_sentence = { id = 353823, duration = 10, max_stack = 5, }, } }, abomination_limb = { id = 315443, cast = 0, cooldown = 120, gcd = "spell", startsCombat = true, texture = 3578196, toggle = "essences", handler = function () applyBuff( "abomination_limb" ) if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { abominations_frenzy = { id = 353546, duration = 12, max_stack = 1, } } }, deaths_due = { id = 324128, cast = 0, charges = function () if not talent.deaths_echo.enabled then return end return 2 end, cooldown = 15, recharge = function () if not talent.deaths_echo.enabled then return end return 15 end, gcd = "spell", spend = function () return buff.crimson_scourge.up and 0 or 1 end, spendType = "runes", startsCombat = true, texture = 3636837, notalent = "defile", handler = function () removeBuff( "crimson_scourge" ) if legendary.phearomones.enabled and buff.death_and_decay.down then stat.haste = stat.haste + ( state.spec.blood and 0.1 or 0.15 ) end applyBuff( "death_and_decay" ) setCooldown( "death_and_decay", 15 ) applyBuff( "deaths_due_buff" ) -- TODO: Model increase RP income within Death's Due. applyDebuff( "target", "deaths_due_debuff" ) -- Note: Debuff is actually a buff on the target... end, bind = { "defile", "any_dnd" }, auras = { deaths_due_buff = { id = 324165, duration = function () return legendary.rampant_transference.enabled and 12 or 10 end, max_stack = 15, copy = "deaths_due" }, deaths_due_debuff = { id = 324164, duration = 15, max_stack = 15, generate = function( t, auraType ) local name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = FindUnitDebuffByID( "target", 324164, "PLAYER" ) if name and expirationTime > query_time then t.name = name t.count = count > 0 and count or 1 t.expires = expirationTime t.applied = expirationTime - duration t.caster = "player" return end t.count = 0 t.expires = 0 t.applied = 0 t.caster = "nobody" end }, } }, swarming_mist = { id = 311648, cast = 0, cooldown = 60, gcd = "spell", spend = 1, spendType = "runes", toggle = "essences", startsCombat = true, texture = 3565716, handler = function () applyBuff( "swarming_mist" ) if conduit.impenetrable_gloom.enabled then applyBuff( "impenetrable_gloom" ) end if legendary.insatiable_hunger.enabled then applyBuff( "insatiable_hunger" ) end end, auras = { swarming_mist = { -- Venthyr id = 311648, duration = 8, tick_time = 1, max_stack = 1, }, -- Conduit impenetrable_gloom = { id = 338629, duration = 4, max_stack = 1 }, insatiable_hunger = { id = 353729, duration = 8, max_stack = 1, }, } }, } ) elseif baseClass == "DEMONHUNTER" then all:RegisterAbilities( { elysian_decree = { id = 306830, cast = 0, cooldown = 60, gcd = "spell", startsCombat = true, texture = 3565443, handler = function () create_sigil( "elysian_decree" ) if legendary.blind_faith.enabled then applyBuff( "blind_faith" ) end end, auras = { blind_faith = { id = 355894, duration = 20, max_stack = 1, }, } }, --[[ fodder_to_the_flame = { id = 329554, cast = 0, cooldown = 120, gcd = "spell", toggle = "essences", startsCombat = true, texture = 3591588, handler = function () applyDebuff( "player", "fodder_to_the_flame_chase" ) applyDebuff( "player", "fodder_to_the_flame_cooldown" ) end, auras = { -- The buff from standing in the pool. fodder_to_the_flame = { id = 330910, duration = function () return 30 + ( conduit.brooding_pool.mod * 0.001 ) end, max_stack = 1, }, -- The demon is linked to you. fodder_to_the_flame_chase = { id = 328605, duration = 3600, max_stack = 1, }, -- This is essentially the countdown before the demon despawns (you can Imprison it for a long time). fodder_to_the_flame_cooldown = { id = 342357, duration = 120, max_stack = 1, }, } }, ]] the_hunt = { id = 323639, cast = 1, cooldown = 180, gcd = "spell", toggle = "essences", startsCombat = true, texture = 3636838, handler = function () applyDebuff( "target", "the_hunt" ) applyDebuff( "target", "the_hunt_dot" ) applyDebuff( "target", "the_hunt_root" ) setDistance( 5 ) if legendary.blazing_slaughter.enabled then applyBuff( "immolation_aura" ) applyBuff( "blazing_slaughter" ) end end, auras = { the_hunt_root = { id = 323996, duration = 1.5, max_stack = 1, }, the_hunt_dot = { id = 345335, duration = 6, max_stack = 1, }, the_hunt = { id = 323802, duration = 30, max_stack = 1, }, blazing_slaughter = { id = 355892, duration = 12, max_stack = 20, } } }, sinful_brand = { id = 317009, cast = 0, cooldown = function () return 60 + ( conduit.sinful_brand.mod * 0.001 ) end, gcd = "spell", toggle = "essences", startsCombat = true, texture = 3565717, handler = function () applyDebuff( "target", "sinful_brand" ) end, auras = { sinful_brand = { id = 317009, duration = 8, max_stack = 1, } } } } ) elseif baseClass == "DRUID" then local SinfulHysteriaHandler = setfenv( function () applyBuff( "ravenous_frenzy_sinful_hysteria" ) end, state ) all:RegisterAbilities( { kindred_spirits = { id = 326434, cast = 2.5, cooldown = 0, gcd = "spell", startsCombat = false, texture = 3565444, essential = true, usable = function () return buff.lone_spirit.down and buff.kindred_spirits.down, "lone_spirit/kindred_spirits already applied" end, bind = "empower_bond", handler = function () if not ( buff.moonkin_form.up or buff.treant_form.up ) then unshift() end -- Let's just assume. applyBuff( "lone_spirit" ) end, auras = { -- Damager kindred_empowerment = { id = 327139, duration = 10, max_stack = 1, shared = "player", }, -- From Damager kindred_empowerment_partner = { id = 327022, duration = 10, max_stack = 1, dot = "buff", shared = "player", }, kindred_focus = { id = 327148, duration = 10, max_stack = 1, shared = "player", }, kindred_focus_partner = { id = 327071, duration = 10, max_stack = 1, dot = "buff", shared = "player", }, -- Tank kindred_protection = { id = 327037, duration = 10, max_stack = 1, shared = "player", }, kindred_protection_partner = { id = 327148, duration = 10, max_stack = 1, dot = "buff", shared = "player", }, kindred_spirits = { id = 326967, duration = 3600, max_stack = 1, shared = "player", }, lone_spirit = { id = 338041, duration = 3600, max_stack = 1, }, lone_empowerment = { id = 338142, duration = 10, max_stack = 1, }, kindred_empowerment_energize = { alias = { "kindred_empowerment", "kindred_focus", "kindred_protection", "lone_empowerment" }, aliasMode = "first", aliasType = "buff", duration = 10, }, kindred_affinity = { id = 357564, duration = 3600, max_stack = 1, } } }, empower_bond = { id = 326647, known = function () return covenant.kyrian and ( buff.lone_spirit.up or buff.kindred_spirits.up ) end, cast = 0, cooldown = function () return 60 * ( 1 - ( conduit.deep_allegiance.mod * 0.01 ) ) end, gcd = "spell", startsCombat = false, usable = function () return buff.lone_spirit.up or buff.kindred_spirits.up, "requires kindred_spirits/lone_spirit" end, toggle = function () return role.tank and "defensives" or "essences" end, bind = "kindred_spirits", handler = function () if buff.lone_spirit.up then if role.tank then applyBuff( "lone_protection" ) elseif role.healer then applyBuff( "lone_meditation" ) else applyBuff( "lone_empowerment" ) end else if role.tank then applyBuff( "kindred_protection" ) applyBuff( "kindred_protection_partner" ) elseif role.healer then applyBuff( "kindred_meditation" ) applyBuff( "kindred_meditation_partner" ) else applyBuff( "kindred_empowerment" ) applyBuff( "kindred_empowerment_partner" ) end end end, copy = { "lone_empowerment", "lone_meditation", "lone_protection", 326462, 326446, 338142, 338018 } }, ravenous_frenzy = { id = 323546, cast = 0, cooldown = 180, gcd = "off", startsCombat = true, texture = 3565718, toggle = "essences", handler = function () applyBuff( "ravenous_frenzy" ) if legendary.sinful_hysteria.enabled then state:QueueAuraExpiration( "ravenous_frenzy", SinfulHysteriaHandler, buff.ravenous_frenzy.expires ) end end, auras = { ravenous_frenzy = { id = 323546, duration = 20, max_stack = 20, }, ravenous_frenzy_sinful_hysteria = { id = 355315, duration = 5, max_stack = 20, }, ravenous_frenzy_stun = { id = 323557, duration = 1, max_stack = 1, }, } } } ) elseif baseClass == "HUNTER" then all:RegisterAbilities( { resonating_arrow = { id = 308491, cast = 0, cooldown = 60, gcd = "spell", startsCombat = true, texture = 3565445, handler = function () applyDebuff( "target", "resonating_arrow" ) active_dot.resonating_arrow = active_enemies applyBuff( "resonating_arrow" ) if legendary.pact_of_the_soulstalkers.enabled then applyBuff( "pact_of_the_soulstalkers" ) end end, toggle = "essences", auras = { resonating_arrow = { id = 308498, duration = 10, max_stack = 1, }, pact_of_the_soulstalkers = { id = 356263, duration = 10, max_stack = 1, } } }, death_chakram = { id = 325028, cast = 0, cooldown = 45, gcd = "spell", startsCombat = true, texture = 3578207, toggle = "essences", handler = function () applyBuff( "death_chakram" ) if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { death_chakram = { duration = 3.5, tick_time = 0.5, max_stack = 1, generate = function( t, auraType ) local cast = action.death_chakram.lastCast or 0 if cast + class.auras.death_chakram.duration >= query_time then t.name = class.abilities.death_chakram.name t.count = 1 t.applied = cast t.expires = cast + duration t.caster = "player" return end t.count = 0 t.applied = 0 t.expires = 0 t.caster = "nobody" end } } }, wild_spirits = { id = 328231, cast = 0, cooldown = 120, gcd = "spell", startsCombat = true, texture = 3636840, spend = 0, spendType = "focus", toggle = "essences", handler = function () applyDebuff( "target", "wild_mark" ) applyBuff( "wild_spirits" ) end, disabled = function () return covenant.night_fae and not IsSpellKnownOrOverridesKnown( 328231 ), "you have not finished your night_fae covenant intro" end, auras = { wild_mark = { id = 328275, duration = function () return conduit.spirit_attunement.enabled and 18 or 15 end, max_stack = 1 }, wild_spirits = { duration = function () return conduit.spirit_attunement.enabled and 18 or 15 end, max_stack = 1, generate = function( t ) local cast = action.wild_spirits.lastCast or 0 local up = cast + t.duration > state.query_time t.name = t.name or class.abilities.wild_spirits.name t.count = up and 1 or 0 t.expires = up and cast + t.duration or 0 t.applied = up and cast or 0 t.caster = "player" end, } } }, flayed_shot = { id = 324149, cast = 0, cooldown = 30, gcd = "spell", startsCombat = true, texture = 3565719, toggle = "essences", handler = function () applyDebuff( "target", "flayed_shot" ) end, auras = { flayed_shot = { id = 324149, duration = 20, max_stack = 1, }, flayers_mark = { id = 324156, duration = 12, max_stack = 1 }, pouch_of_razor_fragments = { id = 356620, duration = 6, max_stack = 1, } } }, -- Not really a Covenant ability but putting it here to keep it bundled into SL stuff. wailing_arrow = { id = 355589, cast = 1.9, cooldown = 60, gcd = "spell", spend = 15, spendType = "focus", toggle = "cooldowns", startsCombat = true, handler = function () interrupt() applyDebuff( "target", "wailing_arrow" ) end, auras = { wailing_arrow = { id = 355589, duration = 5, max_stack = 1, } } }, } ) elseif baseClass == "MAGE" then all:RegisterAbilities( { radiant_spark = { id = 307443, cast = 1.5, cooldown = 30, gcd = "spell", spend = 0.02, spendType = "mana", startsCombat = true, texture = 3565446, toggle = "essences", handler = function () applyBuff( "radiant_spark" ) applyDebuff( "target", "radiant_spark" ) -- applyDebuff( "target", "radiant_spark_vulnerability" ) -- RSV doesn't apply until the next hit. end, auras = { radiant_spark = { id = 307443, duration = 10, max_stack = 1 }, radiant_spark_vulnerability = { id = 307454, duration = 8, max_stack = 5 }, radiant_spark_consumed = { id = 307747, duration = 10, max_stack = 1 }, } }, deathborne = { id = 324220, cast = 1.5, cooldown = 180, gcd = "spell", spend = 0.05, spendType = "mana", startsCombat = false, texture = 3578226, toggle = "essences", -- maybe should be cooldowns. handler = function () applyBuff( "deathborne" ) if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { deathborne = { id = 324220, duration = function () return 20 + ( conduit.gift_of_the_lich.mod * 0.001 ) end, max_stack = 1, }, } }, shifting_power = { id = 314791, cast = function () return 4 * haste end, channeled = true, cooldown = 45, gcd = "spell", spend = 0.05, spendType = "mana", startsCombat = true, texture = 3636841, toggle = "essences", -- -action.shifting_power.execute_time%action.shifting_power.new_tick_time*(dbc.effect.815503.base_value%1000+conduit.discipline_of_the_grove.time_value) cdr = function () return - action.shifting_power.execute_time / action.shifting_power.tick_time * ( -3 + conduit.discipline_of_the_grove.time_value ) end, full_reduction = function () return - action.shifting_power.execute_time / action.shifting_power.tick_time * ( -3 + conduit.discipline_of_the_grove.time_value ) end, start = function () applyBuff( "shifting_power" ) end, tick = function () -- TODO: Identify which abilities have their CDs reduced. end, finish = function () removeBuff( "shifting_power" ) end, auras = { shifting_power = { id = 314791, duration = function () return 4 * haste end, tick_time = function () return haste end, max_stack = 1, }, heart_of_the_fae = { id = 356881, duration = 15, max_stack = 1, } } }, mirrors_of_torment = { id = 314793, cast = 1.5, cooldown = 90, gcd = "spell", spend = 0.04, spendType = "mana", startsCombat = true, texture = 3565720, toggle = "essences", handler = function () applyDebuff( "target", "mirrors_of_torment", nil, 3 ) end, auras = { mirrors_of_torment = { id = 314793, duration = 20, max_stack = 3, -- ??? }, -- Conduit siphoned_malice = { id = 337090, duration = 10, max_stack = 3 } }, }, } ) elseif baseClass == "MONK" then all:RegisterAbilities( { weapons_of_order = { id = 310454, cast = 0, cooldown = 120, gcd = "spell", toggle = "essences", startsCombat = false, texture = 3565447, handler = function () applyBuff( "weapons_of_order" ) if state.spec.mistweaver then setCooldown( "essence_font", 0 ) if legendary.call_to_arms.enabled then summonPet( "yulon", 12 ) end elseif state.spec.brewmaster then setCooldown( "keg_smash", 0 ) if legendary.call_to_arms.enabled then summonPet( "niuzao", 12 ) end else if legendary.call_to_arms.enabled then summonPet( "xuen", 12 ) end end end, auras = { weapons_of_order = { id = 310454, duration = function () return conduit.strike_with_clarity.enabled and 35 or 30 end, max_stack = 1, }, weapons_of_order_debuff = { id = 312106, duration = 8, max_stack = 5 }, weapons_of_order_ww = { id = 311054, duration = 5, max_stack = 1, copy = "weapons_of_order_buff" } } }, bonedust_brew = { id = 325216, cast = 0, cooldown = 60, gcd = "spell", toggle = "essences", startsCombat = true, texture = 3578227, handler = function () applyDebuff( "target", "bonedust_brew" ) if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { bonedust_brew = { id = 325216, duration = 10, max_stack = 1, copy = "bonedust_brew_debuff" } } }, faeline_stomp = { id = 327104, cast = 0, cooldown = 30, gcd = "spell", startsCombat = true, texture = 3636842, toggle = "essences", handler = function () applyBuff( "faeline_stomp" ) if spec.brewmaster then applyDebuff( "target", "breath_of_fire" ) active_dot.breath_of_fire = active_enemies end if legendary.fae_exposure.enabled then applyDebuff( "target", "fae_exposure" ) end end, auras = { faeline_stomp = { id = 327104, duration = 30, max_stack = 1, }, fae_exposure = { id = 356773, duration = 10, max_stack = 1, } } }, fallen_order = { id = 326860, cast = 0, cooldown = 180, gcd = "spell", startsCombat = false, texture = 3565721, toggle = "essences", handler = function () applyBuff( "fallen_order" ) end, auras = { fallen_order = { id = 326860, duration = 24, max_stack = 1 } } }, } ) elseif baseClass == "PALADIN" then all:RegisterAbilities( { divine_toll = { id = 304971, cast = 0, cooldown = 60, gcd = "spell", startsCombat = true, texture = 3565448, toggle = "essences", handler = function () local spellToCast if state.spec.protection then spellToCast = class.abilities.avengers_shield.handler elseif state.spec.retribution then spellToCast = class.abilities.judgment.handler else spellToCast = class.abilities.holy_shock.handler end for i = 1, min( 5, true_active_enemies ) do spellToCast() end if legendary.divine_resonance.enabled then applyBuff( "divine_resonance" ) state:QueueAuraEvent( "divine_toll", spellToCast, buff.divine_resonance.expires, "AURA_PERIODIC" ) state:QueueAuraEvent( "divine_toll", spellToCast, buff.divine_resonance.expires - 5, "AURA_PERIODIC" ) state:QueueAuraEvent( "divine_toll", spellToCast, buff.divine_resonance.expires - 10, "AURA_PERIODIC" ) end end, auras = { divine_resonance = { id = 355455, duration = 15, max_stack = 1, }, } }, vanquishers_hammer = { id = 328204, cast = 0, cooldown = 30, gcd = "spell", spend = function () if buff.divine_purpose.up then return 0 end return 1 - ( buff.the_magistrates_judgment.up and 1 or 0 ) end, spendType = "holy_power", startsCombat = true, texture = 3578228, toggle = "essences", handler = function () removeBuff( "divine_purpose" ) removeBuff( "the_magistrates_judgment" ) applyBuff( "vanquishers_hammer", nil, legendary.dutybound_gavel.enabled and 2 or nil ) if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { vanquishers_hammer = { id = 328204, duration = 15, max_stack = function () return legendary.dutybound_gavel.enabled and 2 or 1 end, } } }, blessing_of_summer = { id = 328620, cast = 0, cooldown = 45, gcd = "spell", spend = 0.05, spendType = "mana", startsCombat = false, texture = 3636845, toggle = "essences", buff = "blessing_of_summer_active", handler = function () applyBuff( "blessing_of_summer" ) -- We'll just apply to self because we don't care. removeBuff( "blessing_of_summer_active" ) applyBuff( "blessing_of_autumn_active" ) setCooldown( "blessing_of_autumn", 45 ) end, auras = { blessing_of_summer = { id = 328620, duration = function () return 30 * ( 1 - ( conduit.the_long_summer.mod * 0.01 ) ) end, max_stack = 1, }, blessing_of_summer_active = { duration = 3600, max_stack = 1, generate = function( t ) if IsActiveSpell( 328620 ) then t.name = class.auras.blessing_of_summer.name .. " Active" t.count = 1 t.applied = now t.expires = now + 3600 t.caster = "player" return end t.count = 0 t.applied = 0 t.expires = 0 t.caster = "nobody" end, }, -- Leaving reactive for now, will see if we need to do anything differently. equinox = { id = 355567, duration = 10, max_stack = 1, }, }, bind = { "blessing_of_spring", "blessing_of_autumn", "blessing_of_winter" } }, blessing_of_autumn = { id = 328622, cast = 0, cooldown = 45, gcd = "spell", spend = 0.05, spendType = "mana", startsCombat = false, texture = 3636843, toggle = "essences", buff = "blessing_of_autumn_active", handler = function () applyBuff( "blessing_of_autumn" ) removeBuff( "blessing_of_autumn_active" ) applyBuff( "blessing_of_winter_active" ) setCooldown( "blessing_of_winter", 45 ) end, auras = { blessing_of_autumn = { id = 328622, duration = 30, max_stack = 1, }, blessing_of_autumn_active = { duration = 3600, max_stack = 1, generate = function( t ) if IsActiveSpell( 328622 ) then t.name = class.auras.blessing_of_autumn.name .. " Active" t.count = 1 t.applied = now t.expires = now + 3600 t.caster = "player" return end t.count = 0 t.applied = 0 t.expires = 0 t.caster = "nobody" end, } }, bind = { "blessing_of_summer", "blessing_of_spring", "blessing_of_winter" } }, blessing_of_winter = { id = 328281, cast = 0, cooldown = 45, gcd = "spell", spend = 0.05, spendType = "mana", startsCombat = false, texture = 3636846, toggle = "essences", buff = "blessing_of_winter_active", handler = function () applyBuff( "blessing_of_winter" ) removeBuff( "blessing_of_winter_active" ) applyBuff( "blessing_of_spring_active" ) setCooldown( "blessing_of_spring", 45 ) end, auras = { blessing_of_winter = { id = 328281, duration = 30, max_stack = 1, }, blessing_of_winter_debuff = { id = 328506, duration = 6, max_stack = 10 }, blessing_of_winter_active = { duration = 3600, max_stack = 1, generate = function( t ) if IsActiveSpell( 328281 ) then t.name = class.auras.blessing_of_winter.name .. " Active" t.count = 1 t.applied = now t.expires = now + 3600 t.caster = "player" return end t.count = 0 t.applied = 0 t.expires = 0 t.caster = "nobody" end, } }, bind = { "blessing_of_summer", "blessing_of_autumn", "blessing_of_spring" } }, blessing_of_spring = { id = 328282, cast = 0, cooldown = 45, gcd = "spell", spend = 0.05, spendType = "mana", startsCombat = false, texture = 3636844, toggle = "essences", buff = "blessing_of_spring_active", handler = function () applyBuff( "blessing_of_spring" ) removeBuff( "blessing_of_spring_active" ) applyBuff( "blessing_of_summer_active" ) setCooldown( "blessing_of_summer", 45 ) end, auras = { blessing_of_spring = { id = 328281, duration = 30, max_stack = 1, friendly = true, }, blessing_of_spring_active = { duration = 3600, max_stack = 1, generate = function( t ) if IsActiveSpell( 328282 ) then t.name = class.auras.blessing_of_winter.name .. " Active" t.count = 1 t.applied = now t.expires = now + 3600 t.caster = "player" return end t.count = 0 t.applied = 0 t.expires = 0 t.caster = "nobody" end, } }, bind = { "blessing_of_summer", "blessing_of_autumn", "blessing_of_winter" } }, ashen_hallow = { id = 316958, cast = function () return 1.5 * haste end, cooldown = 120, gcd = "spell", startsCombat = false, texture = 3565722, toggle = "essences", auras = { hammer_of_wrath_hallow = { duration = function () return legendary.radiant_embers.enabled and 45 or 30 end, max_stack = 1, generate = function( t ) if IsUsableSpell( 24275 ) and not ( target.health_pct < 20 or ( level > 57 and ( buff.avenging_wrath.up or buff.crusade.up ) ) and not buff.final_verdict.up ) then t.name = class.abilities.hammer_of_wrath.name .. " " .. class.abilities.ashen_hallow.name t.count = 1 t.applied = action.ashen_hallow.lastCast t.expires = action.ashen_hallow.lastCast + 30 t.caster = "player" return end t.count = 0 t.applied = 0 t.expires = 0 t.caster = "nobody" end, }, } }, } ) elseif baseClass == "PRIEST" then all:RegisterAbilities( { boon_of_the_ascended = { id = 325013, cast = 1.5, cooldown = 180, gcd = "spell", startsCombat = false, texture = 3565449, toggle = "essences", handler = function () applyBuff( "boon_of_the_ascended" ) end, auras = { boon_of_the_ascended = { id = 325013, duration = 10, max_stack = 20 -- ??? } } }, ascended_nova = { id = 325020, known = 325013, cast = 0, cooldown = 0, gcd = "totem", -- actually 1s and not 1.5s... startsCombat = true, texture = 3528287, buff = "boon_of_the_ascended", bind = "boon_of_the_ascended", handler = function () addStack( "boon_of_the_ascended", nil, active_enemies ) end }, ascended_blast = { id = 325283, known = 15407, cast = 0, cooldown = 3, hasteCD = true, gcd = "totem", startsCombat = true, texture = 3528286, buff = "boon_of_the_ascended", bind = "mind_flay", handler = function () addStack( "boon_of_the_ascended", nil, 5 ) if state.spec.shadow then gain( 6, "insanity" ) end end, }, unholy_nova = { id = 324724, cast = 0, cooldown = 60, gcd = "spell", spend = 0.05, spendType = "mana", startsCombat = true, texture = 3578229, toggle = "essences", handler = function () applyDebuff( "target", "unholy_transfusion" ) active_dot.unholy_transfusion = active_enemies if legendary.pallid_command.enabled then applyBuff( "pallid_command" ) end if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, range = 15, auras = { unholy_transfusion = { id = 324724, duration = function () return conduit.festering_transfusion.enabled and 17 or 15 end, max_stack = 1, }, pallid_command = { id = 356418, duration = 20, max_stack = 1 } } }, fae_guardians = { id = 327661, cast = 0, cooldown = 90, gcd = "spell", spend = 0.02, spendType = "mana", toggle = "essences", nobuff = "direct_mask", handler = function () applyBuff( "fae_guardians" ) summonPet( "wrathful_faerie" ) applyDebuff( "target", "wrathful_faerie" ) summonPet( "guardian_faerie" ) applyBuff( "guardian_faerie" ) summonPet( "benevolent_faerie" ) applyBuff( "benevolent_faerie" ) if legendary.bwonsamdis_pact.enabled then applyBuff( "direct_mask" ) applyDebuff( "target", "haunted_mask" ) end -- TODO: Check totem/guardian API re: faeries. end, bind = "direct_mask", auras = { fae_guardians = { id = 327661, duration = 20, max_stack = 1, }, wrathful_faerie = { id = 342132, duration = 20, max_stack = 1, }, wrathful_faerie_fermata = { id = 345452, duration = function () return conduit.fae_fermata.enabled and ( conduit.fae_fermata.mod * 0.001 ) or 3 end, max_stack = 1 }, guardian_faerie = { id = 327694, duration = 20, max_stack = 1, }, guardian_faerie_fermata = { id = 345451, duration = function () return conduit.fae_fermata.enabled and ( conduit.fae_fermata.mod * 0.001 ) or 3 end, max_stack = 1 }, benevolent_faerie = { id = 327710, duration = 20, max_stack = 1, }, benevolent_faerie_fermata = { id = 345453, duration = function () return conduit.fae_fermata.enabled and ( conduit.fae_fermata.mod * 0.001 ) or 3 end, max_stack = 1 }, haunted_mask = { id = 356968, duration = 20, max_stack = 1, }, direct_mask = { duration = 20, max_stack = 1, } } }, direct_mask = { id = 356532, cast = 0, cooldown = 0, gcd = "off", buff = "direct_mask", bind = "fae_guardians", handler = function () applyDebuff( "target", "haunted_mask" ) end, }, mindgames = { id = 323673, cast = 1.5, cooldown = 45, gcd = "spell", spend = 0.002, spendType = "mana", toggle = "essences", handler = function () applyDebuff( "target", "mindgames" ) end, auras = { mindgames = { id = 323673, duration = function () return ( conduit.shattered_perceptions.enabled and 7 or 5 ) + ( legendary.shadow_word_manipulation.enabled and 3 or 0 ) end, max_stack = 1, }, shadow_word_manipulation = { id = 357028, duration = 10, max_stack = 1, }, }, }, } ) elseif baseClass == "ROGUE" then all:RegisterAbilities( { echoing_reprimand = { id = 323547, cast = 0, cooldown = 45, gcd = "spell", spend = function () return 10 * ( ( talent.shadow_focus.enabled and ( buff.shadow_dance.up or buff.stealth.up ) ) and 0.8 or 1 ) end, spendType = "energy", startsCombat = true, texture = 3565450, toggle = "essences", cp_gain = function () return ( buff.shadow_blades.up and 1 or 0 ) + ( buff.broadside.up and 1 or 0 ) + 2 end, handler = function () -- Can't predict the Animacharge, unless you have the legendary. if legendary.resounding_clarity.enabled then applyBuff( "echoing_reprimand_2", nil, 2 ) applyBuff( "echoing_reprimand_3", nil, 3 ) applyBuff( "echoing_reprimand_4", nil, 4 ) applyBuff( "echoing_reprimand_5", nil, 5 ) end gain( action.echoing_reprimand.cp_gain, "combo_points" ) end, disabled = function () return covenant.kyrian and not IsSpellKnownOrOverridesKnown( 323547 ), "you have not finished your kyrian covenant intro" end, auras = { echoing_reprimand_2 = { id = 323558, duration = 45, max_stack = 6, }, echoing_reprimand_3 = { id = 323559, duration = 45, max_stack = 6, }, echoing_reprimand_4 = { id = 323560, duration = 45, max_stack = 6, copy = 354835, }, echoing_reprimand_5 = { id = 354838, duration = 45, max_stack = 6, }, echoing_reprimand = { alias = { "echoing_reprimand_2", "echoing_reprimand_3", "echoing_reprimand_4", "echoing_reprimand_5" }, aliasMode = "first", aliasType = "buff", meta = { stack = function () if combo_points.current > 1 and combo_points.current < 6 and buff[ "echoing_reprimand_" .. combo_points.current ].up then return combo_points.current end if buff.echoing_reprimand_2.up then return 2 end if buff.echoing_reprimand_3.up then return 3 end if buff.echoing_reprimand_4.up then return 4 end if buff.echoing_reprimand_5.up then return 5 end return 0 end } } } }, serrated_bone_spike = { id = 328547, cast = 0, charges = function () return legendary.deathspike.equipped and 5 or 3 end, cooldown = 30, recharge = 30, gcd = "spell", startsCombat = true, texture = 3578230, toggle = "essences", cycle = "serrated_bone_spike", cp_gain = function () return ( buff.broadside.up and 1 or 0 ) + active_dot.serrated_bone_spike end, handler = function () applyDebuff( "target", "serrated_bone_spike" ) debuff.serrated_bone_spike.exsanguinated_rate = 1 gain( action.serrated_bone_spike.cp_gain, "combo_points" ) if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { serrated_bone_spike = { id = 324073, duration = 3600, max_stack = 1, exsanguinated = false, meta = { exsanguinated_rate = function( t ) return t.up and tracked_bleeds.serrated_bone_spike.rate[ target.unit ] or 1 end, last_tick = function( t ) return t.up and ( tracked_bleeds.serrated_bone_spike.last_tick[ target.unit ] or t.applied ) or 0 end, tick_time = function( t ) return t.up and ( haste * 2 / t.exsanguinated_rate ) or ( haste * 2 ) end, }, copy = "serrated_bone_spike_dot", }, } }, sepsis = { id = 328305, cast = 0, cooldown = 90, gcd = "spell", startsCombat = true, texture = 3636848, toggle = "essences", handler = function () applyDebuff( "target", "sepsis" ) debuff.sepsis.exsanguinated_rate = 1 end, }, } ) elseif baseClass == "SHAMAN" then all:RegisterAbilities( { vesper_totem = { id = 324386, cast = 0, cooldown = 60, gcd = "totem", spend = 0.1, spendType = "mana", startsCombat = true, texture = 3565451, toggle = "essences", handler = function () summonPet( "vesper_totem", 30 ) applyBuff( "vesper_totem" ) vesper_totem_heal_charges = 3 vesper_totem_dmg_charges = 3 vesper_totem_used_charges = 0 end, auras = { vesper_totem = { duration = 30, max_stack = 1, } } }, primordial_wave = { id = 326059, cast = 0, cooldown = 45, recharge = 45, charges = 1, gcd = "spell", spend = 0.1, spendType = "mana", startsCombat = true, texture = 3578231, toggle = "essences", cycle = "flame_shock", velocity = 45, impact = function () applyDebuff( "target", "flame_shock" ) applyBuff( "primordial_wave" ) if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { primordial_wave = { id = 327164, duration = 15, max_stack = 1, }, splintered_elements = { id = 354648, duration = 10, max_stack = 10, }, } }, fae_transfusion = { id = 328923, cast = function () return haste * 3 * ( 1 + ( conduit.essential_extraction.mod * 0.01 ) ) end, channeled = true, cooldown = 120, gcd = "spell", spend = 0.075, spendType = "mana", startsCombat = true, texture = 3636849, toggle = "essences", nobuff = "fae_transfusion", start = function () applyBuff( "fae_transfusion" ) end, tick = function () if legendary.seeds_of_rampant_growth.enabled then if state.spec.enhancement then reduceCooldown( "feral_spirit", 9 ) elseif state.spec.elemental then reduceCooldown( talent.storm_elemental.enabled and "storm_elemental" or "fire_elemental", 6 ) else reduceCooldown( "healing_tide_totem", 5 ) end addStack( "seeds_of_rampant_growth" ) end end, finish = function () if state.spec.enhancement then addStack( "maelstrom_weapon", nil, 3 ) end end, auras = { fae_transfusion = { id = 328933, duration = 20, max_stack = 1 }, seeds_of_rampant_growth = { id = 358945, duration = 15, max_stack = 5 } }, }, fae_transfusion_heal = { id = 328930, cast = 0, channeled = true, cooldown = 0, gcd = "spell", suffix = "(Heal)", startsCombat = false, texture = 3636849, buff = "fae_transfusion", handler = function () removeBuff( "fae_transfusion" ) end, }, chain_harvest = { id = 320674, cast = 2.5, cooldown = 90, gcd = "spell", spend = 0.1, spendType = "mana", startsCombat = true, texture = 3565725, toggle = "essences", handler = function () if legendary.elemental_conduit.enabled then applyDebuff( "target", "flame_shock" ) active_dot.flame_shock = min( active_enemies, active_dot.flame_shock + min( 5, active_enemies ) ) end end, } } ) elseif baseClass == "WARLOCK" then all:RegisterAbilities( { scouring_tithe = { id = 312321, cast = 2, cooldown = 40, gcd = "spell", spend = 0.02, spendType = "mana", startsCombat = true, texture = 3565452, toggle = "essences", handler = function () applyDebuff( "target", "scouring_tithe" ) end, auras = { scouring_tithe = { id = 312321, duration = 18, max_stack = 1, }, -- Conduit soul_tithe = { id = 340238, duration = 10, max_stack = 1 }, -- Legendary languishing_soul_detritus = { id = 356255, duration = 8, max_stack = 1, }, }, }, decimating_bolt = { id = 325289, cast = 2.5, cooldown = 45, gcd = "spell", spend = 0.04, spendType = "mana", startsCombat = true, texture = 3578232, toggle = "essences", indicator = function() if active_enemies > 1 and settings.cycle and target.time_to_die > shortest_ttd then return "cycle" end end, handler = function () applyBuff( "decimating_bolt", nil, 3 ) if legendary.shard_of_annihilation.enabled then applyBuff( "shard_of_annihilation" ) end if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { decimating_bolt = { id = 325299, duration = 3600, max_stack = 3, }, shard_of_annihilation = { id = 356342, duration = 44, max_stack = 1, } } }, soul_rot = { id = 325640, cast = 1.5, cooldown = 60, gcd = "spell", spend = 0.005, spendType = "mana", startsCombat = true, texture = 3636850, toggle = "essences", handler = function () applyDebuff( "target", "soul_rot" ) active_dot.soul_rot = min( 4, active_enemies ) if legendary.decaying_soul_satchel.enabled then applyBuff( "decaying_soul_satchel", nil, active_dot.soul_rot ) end end, auras = { soul_rot = { id = 325640, duration = 8, max_stack = 1 }, decaying_soul_satchel = { id = 356369, duration = 8, max_stack = 4, } } }, impending_catastrophe = { id = 321792, cast = 2, cooldown = 60, gcd = "spell", spend = 0.04, spendType = "mana", startsCombat = true, texture = 3565726, toggle = "essences", velocity = 30, impact = function () applyDebuff( "target", "impending_catastrophe" ) end, auras = { impending_catastrophe = { id = 322170, duration = function () return 12 * ( 1 + conduit.catastrophic_origin.mod * 0.01 ) end, max_stack = 1, copy = "impending_catastrophe_dot" }, } }, } ) elseif baseClass == "WARRIOR" then all:RegisterAbilities( { -- Warrior - Kyrian - 307865 - spear_of_bastion (Spear of Bastion) spear_of_bastion = { id = 307865, cast = 0, cooldown = 60, gcd = "spell", spend = function () return -25 * ( 1 + conduit.piercing_verdict.mod * 0.01 ) end, spendType = "rage", startsCombat = true, texture = 3565453, toggle = "essences", velocity = 30, handler = function () applyDebuff( "target", "spear_of_bastion" ) if legendary.elysian_might.enabled then applyBuff( "elysian_might" ) end end, auras = { spear_of_bastion = { id = 307871, duration = function () return legendary.elysian_might.enabled and 8 or 4 end, max_stack = 1 }, elysian_might = { id = 311193, duration = 8, max_stack = 1, }, } }, -- Warrior - Necrolord - 324143 - conquerors_banner (Conqueror's Banner) conquerors_banner = { id = 324143, cast = 0, cooldown = 180, gcd = "spell", startsCombat = false, texture = 3578234, toggle = "essences", handler = function () applyBuff( "conquerors_banner" ) if conduit.veterans_repute.enabled then applyBuff( "veterans_repute" ) end if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { conquerors_banner = { id = 324143, duration = 20, max_stack = 1 }, -- Conduit veterans_repute = { id = 339267, duration = 30, max_stack = 1 } } }, -- Warrior - Night Fae - 325886 - ancient_aftershock (Ancient Aftershock) ancient_aftershock = { id = 325886, cast = 0, cooldown = function () return 90 - conduit.destructive_reverberations.mod * 0.001 end, gcd = "spell", startsCombat = true, texture = 3636851, toggle = "essences", handler = function () applyDebuff( "target", "ancient_aftershock" ) -- Rage gain will be reactive, can't tell what is going to get hit. end, auras = { ancient_aftershock = { id = 325886, duration = 1, max_stack = 1, }, } }, -- Warrior - Venthyr - 317320 - condemn (Condemn) condemn = { id = function () return talent.massacre.enabled and 330325 or 317485 end, known = 317349, cast = 0, cooldown = function () return state.spec.fury and ( 4.5 * haste ) or 0 end, hasteCD = true, gcd = "spell", rangeSpell = function () return class.abilities.execute and class.abilities.execute.id end, spend = function () if state.spec.fury then return -20 end return buff.sudden_death.up and 0 or 20 end, spendType = "rage", startsCombat = true, texture = 3565727, -- toggle = "essences", -- no need to toggle. usable = function () if buff.sudden_death.up then return true end if cycle_for_condemn then return true end return target.health_pct < ( talent.massacre.enabled and 35 or 20 ) or target.health_pct > 80, "requires > 80% or < " .. ( talent.massacre.enabled and 35 or 20 ) .. "% health" end, cycle = "condemn_ineligible", indicator = function () if cycle_for_condemn then return "cycle" end end, handler = function () applyDebuff( "target", "condemned" ) if not state.spec.fury and buff.sudden_death.down then local extra = min( 20, rage.current ) if extra > 0 then spend( extra, "rage" ) end if state.spec.arms and state.talent.improved_execute.enabled then gain( 4 + floor( 0.1 * extra ), "rage" ) end end if legendary.sinful_surge.enabled then if state.spec.protection and buff.last_stand.up then buff.last_stand.expires = buff.last_stand.expires + 3 elseif state.spec.arms and debuff.colossus_smash.up then debuff.colossus_smash.expires = debuff.colossus_smash.expires + 1.5 elseif state.spec.fury and buff.recklessness.up then buff.recklessness.expires = buff.recklessness.expires + 1.5 end end if legendary.exploiter.enabled then applyDebuff( "target", "exploiter", nil, min( 2, debuff.exploiter.stack + 1 ) ) end removeBuff( "sudden_death" ) if conduit.ashen_juggernaut.enabled then addStack( "ashen_juggernaut" ) end end, auras = { condemned = { id = 317491, duration = 10, max_stack = 1, }, -- Target Swapping condemn_ineligible = { duration = 3600, max_stack = 1, generate = function( t, auraType ) if buff.sudden_death.up or not covenant.venthyr or ( target.health_pct > ( talent.massacre.enabled and 35 or 20 ) and target.health_pct < 80 ) then t.count = 1 t.expires = query_time + 3600 t.applied = query_time t.duration = 3600 t.caster = "player" return end t.count = 0 t.expires = 0 t.applied = 0 t.caster = "nobody" end } }, copy = { 317485, 330325, 317349, 330334 } } } ) end