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.
6565 lines
171 KiB
6565 lines
171 KiB
-- Classes.lua
|
|
-- July 2024
|
|
|
|
local addon, ns = ...
|
|
local Hekili = _G[ addon ]
|
|
|
|
local class = Hekili.Class
|
|
local state = Hekili.State
|
|
|
|
local CommitKey = ns.commitKey
|
|
local FindUnitBuffByID, FindUnitDebuffByID = ns.FindUnitBuffByID, ns.FindUnitDebuffByID
|
|
local GetItemInfo = ns.CachedGetItemInfo
|
|
local GetResourceInfo, GetResourceKey = ns.GetResourceInfo, ns.GetResourceKey
|
|
local ResetDisabledGearAndSpells = ns.ResetDisabledGearAndSpells
|
|
local RegisterEvent = ns.RegisterEvent
|
|
local RegisterUnitEvent = ns.RegisterUnitEvent
|
|
|
|
local formatKey = ns.formatKey
|
|
local getSpecializationKey = ns.getSpecializationKey
|
|
local tableCopy = ns.tableCopy
|
|
|
|
local LSR = LibStub( "SpellRange-1.0" )
|
|
|
|
local insert, wipe = table.insert, table.wipe
|
|
|
|
local mt_resource = ns.metatables.mt_resource
|
|
|
|
local GetActiveLossOfControlData, GetActiveLossOfControlDataCount = C_LossOfControl.GetActiveLossOfControlData, C_LossOfControl.GetActiveLossOfControlDataCount
|
|
local GetItemCooldown = C_Item.GetItemCooldown
|
|
local GetSpellDescription, GetSpellTexture = C_Spell.GetSpellDescription, C_Spell.GetSpellTexture
|
|
local GetSpecialization, GetSpecializationInfo = _G.GetSpecialization, _G.GetSpecializationInfo
|
|
local GetItemSpell, GetItemCount, IsUsableItem = C_Item.GetItemSpell, C_Item.GetItemCount, C_Item.IsUsableItem
|
|
local GetSpellInfo = C_Spell.GetSpellInfo
|
|
local GetSpellLink = C_Spell.GetSpellLink
|
|
|
|
local UnitBuff, UnitDebuff = ns.UnitBuff, ns.UnitDebuff
|
|
|
|
local specTemplate = {
|
|
enabled = true,
|
|
|
|
aoe = 2,
|
|
cycle = false,
|
|
cycle_min = 6,
|
|
gcdSync = true,
|
|
|
|
nameplates = true,
|
|
petbased = false,
|
|
|
|
damage = true,
|
|
damageExpiration = 8,
|
|
damageDots = false,
|
|
damageOnScreen = true,
|
|
damageRange = 0,
|
|
damagePets = false,
|
|
|
|
throttleRefresh = false,
|
|
regularRefresh = 0.5,
|
|
combatRefresh = 0.25,
|
|
|
|
throttleTime = false,
|
|
maxTime = 20,
|
|
|
|
-- Toggles
|
|
custom1Name = "Custom 1",
|
|
custom2Name = "Custom 2",
|
|
noFeignedCooldown = false,
|
|
|
|
abilities = {
|
|
['**'] = {
|
|
disabled = false,
|
|
toggle = "default",
|
|
clash = 0,
|
|
targetMin = 0,
|
|
targetMax = 0,
|
|
boss = false
|
|
}
|
|
},
|
|
items = {
|
|
['**'] = {
|
|
disabled = false,
|
|
toggle = "default",
|
|
clash = 0,
|
|
targetMin = 0,
|
|
targetMax = 0,
|
|
boss = false,
|
|
criteria = nil
|
|
}
|
|
},
|
|
|
|
ranges = {},
|
|
settings = {},
|
|
phases = {},
|
|
cooldowns = {},
|
|
utility = {},
|
|
defensives = {},
|
|
custom1 = {},
|
|
custom2 = {},
|
|
}
|
|
ns.specTemplate = specTemplate -- for options.
|
|
|
|
|
|
local function Aura_DetectSharedAura( t, type )
|
|
if not t then return end
|
|
local finder = type == "debuff" and FindUnitDebuffByID or FindUnitBuffByID
|
|
local aura = class.auras[ t.key ]
|
|
|
|
local name, _, count, _, duration, expirationTime, caster = finder( aura.shared, aura.id )
|
|
|
|
if name then
|
|
t.count = count > 0 and count or 1
|
|
|
|
if expirationTime > 0 then
|
|
t.applied = expirationTime - duration
|
|
t.expires = expirationTime
|
|
else
|
|
t.applied = state.query_time
|
|
t.expires = state.query_time + t.duration
|
|
end
|
|
t.caster = caster
|
|
return
|
|
end
|
|
|
|
t.count = 0
|
|
t.applied = 0
|
|
t.expires = 0
|
|
t.caster = "nobody"
|
|
end
|
|
|
|
|
|
local protectedFunctions = {
|
|
-- Channels.
|
|
start = true,
|
|
tick = true,
|
|
finish = true,
|
|
|
|
-- Casts
|
|
handler = true, -- Cast finish.
|
|
impact = true, -- Projectile impact.
|
|
}
|
|
|
|
|
|
local HekiliSpecMixin = {
|
|
RegisterResource = function( self, resourceID, regen, model, meta )
|
|
local resource = GetResourceKey( resourceID )
|
|
|
|
if not resource then
|
|
Hekili:Error( "Unable to identify resource with PowerType " .. resourceID .. "." )
|
|
return
|
|
end
|
|
|
|
local r = self.resources[ resource ] or {}
|
|
|
|
r.resource = resource
|
|
r.type = resourceID
|
|
r.state = model or setmetatable( {
|
|
resource = resource,
|
|
type = resourceID,
|
|
|
|
forecast = {},
|
|
fcount = 0,
|
|
times = {},
|
|
values = {},
|
|
|
|
actual = 0,
|
|
max = 1,
|
|
|
|
active_regen = 0.001,
|
|
inactive_regen = 0.001,
|
|
last_tick = 0,
|
|
|
|
swingGen = false,
|
|
|
|
add = function( amt, overcap )
|
|
-- Bypasses forecast, useful in hooks.
|
|
if overcap then r.state.amount = r.state.amount + amt
|
|
else r.state.amount = max( 0, min( r.state.amount + amt, r.state.max ) ) end
|
|
end,
|
|
|
|
timeTo = function( x )
|
|
return state:TimeToResource( r.state, x )
|
|
end,
|
|
|
|
--[[ reset = function()
|
|
wipe( r.state.times )
|
|
wipe( r.state.values )
|
|
end ]]
|
|
}, mt_resource )
|
|
r.state.regenModel = regen
|
|
r.state.meta = meta or {}
|
|
|
|
for _, func in pairs( r.state.meta ) do
|
|
setfenv( func, state )
|
|
end
|
|
|
|
if r.state.regenModel then
|
|
for _, v in pairs( r.state.regenModel ) do
|
|
v.resource = v.resource or resource
|
|
self.resourceAuras[ v.resource ] = self.resourceAuras[ v.resource ] or {}
|
|
|
|
if v.aura then
|
|
self.resourceAuras[ v.resource ][ v.aura ] = true
|
|
end
|
|
|
|
if v.channel then
|
|
self.resourceAuras[ v.resource ].casting = true
|
|
end
|
|
|
|
if v.swing then
|
|
r.state.swingGen = true
|
|
end
|
|
end
|
|
end
|
|
|
|
self.primaryResource = self.primaryResource or resource
|
|
self.resources[ resource ] = r
|
|
|
|
CommitKey( resource )
|
|
end,
|
|
|
|
RegisterTalents = function( self, talents )
|
|
for talent, id in pairs( talents ) do
|
|
self.talents[ talent ] = id
|
|
CommitKey( talent )
|
|
|
|
local hero = id[ 4 ]
|
|
|
|
if hero then
|
|
self.talents[ hero ] = id
|
|
CommitKey( hero )
|
|
id[ 4 ] = nil
|
|
end
|
|
end
|
|
end,
|
|
|
|
RegisterPvpTalents = function( self, pvp )
|
|
for talent, spell in pairs( pvp ) do
|
|
self.pvptalents[ talent ] = spell
|
|
CommitKey( talent )
|
|
end
|
|
end,
|
|
|
|
RegisterAura = function( self, aura, data )
|
|
CommitKey( aura )
|
|
|
|
local a = setmetatable( {
|
|
funcs = {}
|
|
}, {
|
|
__index = function( t, k )
|
|
if t.funcs[ k ] then return t.funcs[ k ]() end
|
|
|
|
local setup = rawget( t, "onLoad" )
|
|
if setup then
|
|
t.onLoad = nil
|
|
setup( t )
|
|
|
|
return t[ k ]
|
|
end
|
|
end
|
|
} )
|
|
|
|
a.key = aura
|
|
|
|
if not data.id then
|
|
self.pseudoAuras = self.pseudoAuras + 1
|
|
data.id = ( -1000 * self.id ) - self.pseudoAuras
|
|
end
|
|
|
|
-- default values.
|
|
data.duration = data.duration or 30
|
|
data.max_stack = data.max_stack or 1
|
|
|
|
-- This is a shared buff that can come from anyone, give it a special generator.
|
|
--[[ if data.shared then
|
|
a.generate = Aura_DetectSharedAura
|
|
end ]]
|
|
|
|
for element, value in pairs( data ) do
|
|
if type( value ) == "function" then
|
|
setfenv( value, state )
|
|
if element ~= "generate" then a.funcs[ element ] = value
|
|
else a[ element ] = value end
|
|
else
|
|
a[ element ] = value
|
|
end
|
|
|
|
class.knownAuraAttributes[ element ] = true
|
|
end
|
|
|
|
self.auras[ aura ] = a
|
|
|
|
if a.id then
|
|
if a.id > 0 then
|
|
-- Hekili:ContinueOnSpellLoad( a.id, function( success )
|
|
a.onLoad = function( a )
|
|
local d = GetSpellInfo( a.id )
|
|
a.name = d and d.name
|
|
|
|
if not a.name then
|
|
for k, v in pairs( class.auraList ) do
|
|
if v == a then class.auraList[ k ] = nil end
|
|
end
|
|
|
|
Hekili.InvalidSpellIDs = Hekili.InvalidSpellIDs or {}
|
|
Hekili.InvalidSpellIDs[ a.id ] = a.name or a.key
|
|
|
|
a.id = a.key
|
|
a.name = a.name or a.key
|
|
|
|
return
|
|
end
|
|
|
|
a.desc = GetSpellDescription( a.id )
|
|
|
|
local texture = a.texture or GetSpellTexture( a.id )
|
|
|
|
if self.id > 0 then
|
|
class.auraList[ a.key ] = "|T" .. texture .. ":0|t " .. a.name
|
|
end
|
|
|
|
self.auras[ a.name ] = a
|
|
if GetSpecializationInfo( GetSpecialization() or 0 ) == self.id then
|
|
-- Copy to class table as well.
|
|
class.auras[ a.name ] = a
|
|
end
|
|
|
|
if self.pendingItemSpells[ a.name ] then
|
|
local items = self.pendingItemSpells[ a.name ]
|
|
|
|
if type( items ) == "table" then
|
|
for i, item in ipairs( items ) do
|
|
local ability = self.abilities[ item ]
|
|
ability.itemSpellKey = a.key .. "_" .. ability.itemSpellID
|
|
|
|
self.abilities[ ability.itemSpellKey ] = a
|
|
class.abilities[ ability.itemSpellKey ] = a
|
|
end
|
|
else
|
|
local ability = self.abilities[ items ]
|
|
ability.itemSpellKey = a.key .. "_" .. ability.itemSpellID
|
|
|
|
self.abilities[ ability.itemSpellKey ] = a
|
|
class.abilities[ ability.itemSpellKey ] = a
|
|
end
|
|
|
|
self.pendingItemSpells[ a.name ] = nil
|
|
self.itemPended = nil
|
|
end
|
|
end
|
|
end
|
|
self.auras[ a.id ] = a
|
|
end
|
|
|
|
if data.meta then
|
|
for k, v in pairs( data.meta ) do
|
|
if type( v ) == "function" then data.meta[ k ] = setfenv( v, state ) end
|
|
class.knownAuraAttributes[ k ] = true
|
|
end
|
|
end
|
|
|
|
if data.copy then
|
|
if type( data.copy ) ~= "table" then
|
|
self.auras[ data.copy ] = a
|
|
else
|
|
for _, key in ipairs( data.copy ) do
|
|
self.auras[ key ] = a
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
|
|
RegisterAuras = function( self, auras )
|
|
for aura, data in pairs( auras ) do
|
|
self:RegisterAura( aura, data )
|
|
end
|
|
end,
|
|
|
|
|
|
RegisterPower = function( self, power, id, aura )
|
|
self.powers[ power ] = id
|
|
CommitKey( power )
|
|
|
|
if aura and type( aura ) == "table" then
|
|
self:RegisterAura( power, aura )
|
|
end
|
|
end,
|
|
|
|
RegisterPowers = function( self, powers )
|
|
for k, v in pairs( powers ) do
|
|
self.powers[ k ] = v.id
|
|
self.powers[ v.id ] = k
|
|
|
|
for token, ids in pairs( v.triggers ) do
|
|
if not self.auras[ token ] then
|
|
self:RegisterAura( token, {
|
|
id = v.id,
|
|
copy = ids
|
|
} )
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
|
|
RegisterStateExpr = function( self, key, func )
|
|
setfenv( func, state )
|
|
self.stateExprs[ key ] = func
|
|
class.stateExprs[ key ] = func
|
|
CommitKey( key )
|
|
end,
|
|
|
|
RegisterStateFunction = function( self, key, func )
|
|
setfenv( func, state )
|
|
self.stateFuncs[ key ] = func
|
|
class.stateFuncs[ key ] = func
|
|
CommitKey( key )
|
|
end,
|
|
|
|
RegisterStateTable = function( self, key, data )
|
|
for _, f in pairs( data ) do
|
|
if type( f ) == "function" then
|
|
setfenv( f, state )
|
|
end
|
|
end
|
|
|
|
local meta = getmetatable( data )
|
|
|
|
if meta and meta.__index then
|
|
setfenv( meta.__index, state )
|
|
end
|
|
|
|
self.stateTables[ key ] = data
|
|
class.stateTables[ key ] = data
|
|
CommitKey( key )
|
|
end,
|
|
|
|
-- Phases are for more durable variables that should be recalculated over the course of recommendations.
|
|
-- The start/finish conditions are calculated on reset and that state is persistent between sets of recommendations.
|
|
-- Within a set of recommendations, the phase conditions are recalculated when the clock advances and/or when ability handlers are fired.
|
|
-- Notably, finish is only fired if we are currently in the phase.
|
|
RegisterPhase = function( self, key, start, finish, ... )
|
|
if start then start = setfenv( start, state ) end
|
|
if finish then finish = setfenv( finish, state ) end
|
|
|
|
self.phases[ key ] = {
|
|
activate = start,
|
|
deactivate = finish,
|
|
virtual = {},
|
|
real = {}
|
|
}
|
|
|
|
local phase = self.phases[ key ]
|
|
local n = select( "#", ... )
|
|
|
|
for i = 1, n do
|
|
local hook = select( i, ... )
|
|
|
|
if hook == "reset_precast" then
|
|
self:RegisterHook( hook, function()
|
|
local d = display or "Primary"
|
|
|
|
if phase.real[ d ] == nil then
|
|
phase.real[ d ] = false
|
|
end
|
|
|
|
local original = phase.real[ d ]
|
|
|
|
if state.time == 0 and not InCombatLockdown() then
|
|
phase.real[ d ] = false
|
|
-- Hekili:Print( format( "[ %s ] Phase '%s' set to '%s' (%s) - out of combat.", self.name or "Unspecified", key, tostring( phase.real[ d ] ), hook ) )
|
|
-- if Hekili.ActiveDebug then Hekili:Debug( "[ %s ] Phase '%s' set to '%s' (%s) - out of combat.", self.name or "Unspecified", key, tostring( phase.virtual[ display or "Primary" ] ), hook ) end
|
|
end
|
|
|
|
if not phase.real[ d ] and phase.activate() then
|
|
phase.real[ d ] = true
|
|
end
|
|
|
|
if phase.real[ d ] and phase.deactivate() then
|
|
phase.real[ d ] = false
|
|
end
|
|
|
|
--[[ if phase.real[ d ] ~= original then
|
|
if d == "Primary" then Hekili:Print( format( "Phase change for %s [ %s ] (from %s to %s).", key, d, tostring( original ), tostring( phase.real[ d ] ) ) ) end
|
|
end ]]
|
|
|
|
phase.virtual[ d ] = phase.real[ d ]
|
|
|
|
if Hekili.ActiveDebug then Hekili:Debug( "[ %s ] Phase '%s' set to '%s' (%s).", self.name or "Unspecified", key, tostring( phase.virtual[ d ] ), hook ) end
|
|
end )
|
|
else
|
|
self:RegisterHook( hook, function()
|
|
local d = display or "Primary"
|
|
local previous = phase.virtual[ d ]
|
|
|
|
if phase.virtual[ d ] ~= true and phase.activate() then
|
|
phase.virtual[ d ] = true
|
|
end
|
|
|
|
if phase.virtual[ d ] == true and phase.deactivate() then
|
|
phase.virtual[ d ] = false
|
|
end
|
|
|
|
if Hekili.ActiveDebug and phase.virtual[ d ] ~= previous then Hekili:Debug( "[ %s ] Phase '%s' set to '%s' (%s) - virtual.", self.name or "Unspecified", key, tostring( phase.virtual[ d ] ), hook ) end
|
|
end )
|
|
end
|
|
end
|
|
|
|
self:RegisterVariable( key, function()
|
|
return self.phases[ key ].virtual[ display or "Primary" ]
|
|
end )
|
|
end,
|
|
|
|
RegisterPhasedVariable = function( self, key, default, value, ... )
|
|
value = setfenv( value, state )
|
|
|
|
self.phases[ key ] = {
|
|
update = value,
|
|
virtual = {},
|
|
real = {}
|
|
}
|
|
|
|
local phase = self.phases[ key ]
|
|
local n = select( "#", ... )
|
|
|
|
if type( default ) == "function" then
|
|
phase.default = setfenv( default, state )
|
|
else
|
|
phase.default = setfenv( function() return default end, state )
|
|
end
|
|
|
|
for i = 1, n do
|
|
local hook = select( i, ... )
|
|
|
|
if hook == "reset_precast" then
|
|
self:RegisterHook( hook, function()
|
|
local d = display or "Primary"
|
|
|
|
if phase.real[ d ] == nil or ( state.time == 0 and not InCombatLockdown() ) then
|
|
phase.real[ d ] = phase.default()
|
|
end
|
|
|
|
local original = phase.real[ d ] or "nil"
|
|
|
|
phase.real[ d ] = phase.update( phase.real[ d ], phase.default() )
|
|
phase.virtual[ d ] = phase.real[ d ]
|
|
|
|
if Hekili.ActiveDebug then
|
|
Hekili:Debug( "[ %s ] Phased variable '%s' set to '%s' (%s) - was '%s'.", self.name or "Unspecified", key, tostring( phase.virtual[ display or "Primary" ] ), hook, tostring( original ) )
|
|
end
|
|
end )
|
|
else
|
|
self:RegisterHook( hook, function()
|
|
local d = display or "Primary"
|
|
local previous = phase.virtual[ d ]
|
|
|
|
phase.virtual[ d ] = phase.update( phase.virtual[ d ], phase.default() )
|
|
|
|
if Hekili.ActiveDebug and phase.virtual[ d ] ~= previous then Hekili:Debug( "[ %s ] Phased variable '%s' set to '%s' (%s) - virtual.", self.name or "Unspecified", key, tostring( phase.virtual[ display or "Primary" ] ), hook ) end
|
|
end )
|
|
end
|
|
end
|
|
|
|
self:RegisterVariable( key, function()
|
|
return self.phases[ key ].virtual[ display or "Primary" ]
|
|
end )
|
|
end,
|
|
|
|
RegisterGear = function( self, key, ... )
|
|
local n = select( "#", ... )
|
|
|
|
local gear = self.gear[ key ] or {}
|
|
|
|
for i = 1, n do
|
|
local item = select( i, ... )
|
|
table.insert( gear, item )
|
|
gear[ item ] = true
|
|
end
|
|
|
|
self.gear[ key ] = gear
|
|
CommitKey( key )
|
|
end,
|
|
|
|
-- Check for the set bonus based on hidden aura instead of counting the number of equipped items.
|
|
-- This may be useful for tier set items that are crafted so their item ID doesn't match.
|
|
-- The alternative is *probably* to treat sets based on bonusIDs.
|
|
RegisterSetBonus = function( self, key, spellID )
|
|
self.setBonuses[ key ] = spellID
|
|
CommitKey( key )
|
|
end,
|
|
|
|
RegisterSetBonuses = function( self, ... )
|
|
local n = select( "#", ... )
|
|
|
|
for i = 1, n, 2 do
|
|
self:RegisterSetBonus( select( i, ... ) )
|
|
end
|
|
end,
|
|
|
|
RegisterPotion = function( self, potion, data )
|
|
self.potions[ potion ] = data
|
|
|
|
data.key = potion
|
|
|
|
if data.copy then
|
|
if type( data.copy ) == "table" then
|
|
for _, key in ipairs( data.copy ) do
|
|
self.potions[ key ] = data
|
|
CommitKey( key )
|
|
end
|
|
else
|
|
self.potions[ data.copy ] = data
|
|
CommitKey( data.copy )
|
|
end
|
|
end
|
|
|
|
local potionItem = Item:CreateFromItemID( data.item )
|
|
|
|
if not potionItem:IsItemEmpty() then
|
|
potionItem:ContinueOnItemLoad( function()
|
|
local name = potionItem:GetItemName() or data.name
|
|
local link = potionItem:GetItemLink() or data.link
|
|
|
|
data.name = name
|
|
data.link = link
|
|
|
|
class.potionList[ potion ] = link
|
|
return true
|
|
end )
|
|
end
|
|
|
|
CommitKey( potion )
|
|
end,
|
|
|
|
RegisterPotions = function( self, potions )
|
|
for k, v in pairs( potions ) do
|
|
self:RegisterPotion( k, v )
|
|
end
|
|
end,
|
|
|
|
RegisterRecheck = function( self, func )
|
|
self.recheck = func
|
|
end,
|
|
|
|
RegisterHook = function( self, hook, func, noState )
|
|
if not ( noState == true or hook == "COMBAT_LOG_EVENT_UNFILTERED" and noState == nil ) then
|
|
func = setfenv( func, state )
|
|
end
|
|
self.hooks[ hook ] = self.hooks[ hook ] or {}
|
|
insert( self.hooks[ hook ], func )
|
|
end,
|
|
|
|
RegisterAbility = function( self, ability, data )
|
|
CommitKey( ability )
|
|
|
|
local a = setmetatable( {
|
|
funcs = {},
|
|
}, {
|
|
__index = function( t, k )
|
|
local setup = rawget( t, "onLoad" )
|
|
if setup then
|
|
t.onLoad = nil
|
|
setup( t )
|
|
return t[ k ]
|
|
end
|
|
|
|
if t.funcs[ k ] then return t.funcs[ k ]() end
|
|
if k == "lastCast" then return state.history.casts[ t.key ] or t.realCast end
|
|
if k == "lastUnit" then return state.history.units[ t.key ] or t.realUnit end
|
|
end,
|
|
} )
|
|
|
|
a.key = ability
|
|
a.from = self.id
|
|
|
|
if not data.id then
|
|
if data.item then
|
|
class.specs[ 0 ].itemAbilities = class.specs[ 0 ].itemAbilities + 1
|
|
data.id = -100 - class.specs[ 0 ].itemAbilities
|
|
else
|
|
self.pseudoAbilities = self.pseudoAbilities + 1
|
|
data.id = -1000 * self.id - self.pseudoAbilities
|
|
end
|
|
end
|
|
|
|
if data.id and type( data.id ) == "function" then
|
|
if not data.copy or type( data.copy ) == "table" and #data.copy == 0 then
|
|
Hekili:Error( "RegisterAbility for %s (Specialization %d) will fail; ability has an ID function but needs to have 'copy' entries for the abilities table.", ability, self.id )
|
|
end
|
|
end
|
|
|
|
|
|
local item = data.item
|
|
if item and type( item ) == "function" then
|
|
setfenv( item, state )
|
|
item = item()
|
|
end
|
|
|
|
if data.meta then
|
|
for k, v in pairs( data.meta ) do
|
|
if type( v ) == "function" then data.meta[ k ] = setfenv( v, state ) end
|
|
end
|
|
end
|
|
|
|
-- default values.
|
|
if not data.cast then data.cast = 0 end
|
|
if not data.cooldown then data.cooldown = 0 end
|
|
if not data.recharge then data.recharge = data.cooldown end
|
|
if not data.charges then data.charges = 1 end
|
|
|
|
if data.hasteCD then
|
|
if type( data.cooldown ) == "number" and data.cooldown > 0 then data.cooldown = Hekili:Loadstring( "return " .. data.cooldown .. " * haste" ) end
|
|
if type( data.recharge ) == "number" and data.recharge > 0 then data.recharge = Hekili:Loadstring( "return " .. data.recharge .. " * haste" ) end
|
|
end
|
|
|
|
if not data.fixedCast and type( data.cast ) == "number" then
|
|
data.cast = Hekili:Loadstring( "return " .. data.cast .. " * haste" )
|
|
end
|
|
|
|
if data.toggle == "interrupts" and data.gcd == "off" and data.readyTime == state.timeToInterrupt and data.interrupt == nil then
|
|
data.interrupt = true
|
|
end
|
|
|
|
for key, value in pairs( data ) do
|
|
if type( value ) == "function" then
|
|
setfenv( value, state )
|
|
|
|
if not protectedFunctions[ key ] then a.funcs[ key ] = value
|
|
else a[ key ] = value end
|
|
data[ key ] = nil
|
|
else
|
|
a[ key ] = value
|
|
end
|
|
end
|
|
|
|
if ( a.velocity or a.flightTime ) and a.impact then
|
|
a.isProjectile = true
|
|
end
|
|
|
|
a.realCast = 0
|
|
|
|
if item then
|
|
--[[ local name, link, _, _, _, _, _, _, _, texture = GetItemInfo( item )
|
|
|
|
a.name = name or ability
|
|
a.link = link or ability ]]
|
|
|
|
class.itemMap[ item ] = ability
|
|
|
|
-- Register the item if it doesn't already exist.
|
|
class.specs[0]:RegisterGear( ability, item )
|
|
|
|
local actionItem = Item:CreateFromItemID( item )
|
|
if not actionItem:IsItemEmpty() then
|
|
actionItem:ContinueOnItemLoad( function( success )
|
|
local name = actionItem:GetItemName()
|
|
local link = actionItem:GetItemLink()
|
|
local texture = actionItem:GetItemIcon()
|
|
|
|
|
|
if name then
|
|
if not a.name or a.name == a.key then a.name = name end
|
|
if not a.link or a.link == a.key then a.link = link end
|
|
a.texture = a.texture or texture
|
|
|
|
if a.suffix then
|
|
a.actualName = name
|
|
a.name = a.name .. " " .. a.suffix
|
|
end
|
|
|
|
self.abilities[ ability ] = self.abilities[ ability ] or a
|
|
self.abilities[ a.name ] = self.abilities[ a.name ] or a
|
|
self.abilities[ a.link ] = self.abilities[ a.link ] or a
|
|
self.abilities[ data.id ] = self.abilities[ a.link ] or a
|
|
|
|
a.itemLoaded = GetTime()
|
|
|
|
if a.item and a.item ~= 158075 then
|
|
a.itemSpellName, a.itemSpellID = GetItemSpell( a.item )
|
|
|
|
if a.itemSpellID then
|
|
a.itemSpellKey = a.key .. "_" .. a.itemSpellID
|
|
self.abilities[ a.itemSpellKey ] = a
|
|
class.abilities[ a.itemSpellKey ] = a
|
|
end
|
|
|
|
if a.itemSpellName then
|
|
local itemAura = self.auras[ a.itemSpellName ]
|
|
|
|
if itemAura then
|
|
a.itemSpellKey = itemAura.key .. "_" .. a.itemSpellID
|
|
self.abilities[ a.itemSpellKey ] = a
|
|
class.abilities[ a.itemSpellKey ] = a
|
|
|
|
else
|
|
if self.pendingItemSpells[ a.itemSpellName ] then
|
|
if type( self.pendingItemSpells[ a.itemSpellName ] ) == "table" then
|
|
table.insert( self.pendingItemSpells[ a.itemSpellName ], ability )
|
|
else
|
|
local first = self.pendingItemSpells[ a.itemSpellName ]
|
|
self.pendingItemSpells[ a.itemSpellName ] = {
|
|
first,
|
|
ability
|
|
}
|
|
end
|
|
else
|
|
self.pendingItemSpells[ a.itemSpellName ] = ability
|
|
a.itemPended = GetTime()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if not a.unlisted then
|
|
class.abilityList[ ability ] = a.listName or ( "|T" .. ( a.texture or texture ) .. ":0|t " .. link )
|
|
class.itemList[ item ] = a.listName or ( "|T" .. a.texture .. ":0|t " .. link )
|
|
class.abilityByName[ a.name ] = a
|
|
end
|
|
|
|
if data.copy then
|
|
if type( data.copy ) == "string" or type( data.copy ) == "number" then
|
|
self.abilities[ data.copy ] = a
|
|
elseif type( data.copy ) == "table" then
|
|
for _, key in ipairs( data.copy ) do
|
|
self.abilities[ key ] = a
|
|
end
|
|
end
|
|
end
|
|
|
|
if data.items then
|
|
local addedToItemList = false
|
|
|
|
for _, id in ipairs( data.items ) do
|
|
local copyItem = Item:CreateFromItemID( id )
|
|
|
|
if not copyItem:IsItemEmpty() then
|
|
copyItem:ContinueOnItemLoad( function()
|
|
local name = copyItem:GetItemName()
|
|
local link = copyItem:GetItemLink()
|
|
local texture = copyItem:GetItemIcon()
|
|
|
|
if name then
|
|
class.abilities[ name ] = a
|
|
self.abilities[ name ] = a
|
|
|
|
if not class.itemList[ id ] then
|
|
class.itemList[ id ] = a.listName or ( "|T" .. ( a.texture or texture ) .. ":0|t " .. link )
|
|
addedToItemList = true
|
|
end
|
|
end
|
|
end )
|
|
end
|
|
end
|
|
|
|
if addedToItemList then
|
|
if ns.ReadKeybindings then ns.ReadKeybindings() end
|
|
end
|
|
end
|
|
|
|
if ability then class.abilities[ ability ] = a end
|
|
if a.name then class.abilities[ a.name ] = a end
|
|
if a.link then class.abilities[ a.link ] = a end
|
|
if a.id then class.abilities[ a.id ] = a end
|
|
|
|
Hekili.OptionsReady = false
|
|
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end )
|
|
end
|
|
end
|
|
|
|
if a.id and a.id > 0 then
|
|
-- Hekili:ContinueOnSpellLoad( a.id, function( success )
|
|
a.onLoad = function()
|
|
local spellInfo = GetSpellInfo( a.id )
|
|
|
|
if spellInfo == nil then
|
|
spellInfo = GetItemInfo( a.id )
|
|
end
|
|
if spellInfo then
|
|
a.name = spellInfo.name
|
|
else
|
|
a.name = nil
|
|
end
|
|
|
|
if not a.name then
|
|
for k, v in pairs( class.abilityList ) do
|
|
if v == a then class.abilityList[ k ] = nil end
|
|
end
|
|
Hekili.InvalidSpellIDs = Hekili.InvalidSpellIDs or {}
|
|
table.insert( Hekili.InvalidSpellIDs, a.id )
|
|
Hekili:Error( "Name info not available for " .. a.id .. "." )
|
|
return
|
|
end
|
|
|
|
if not a.name then Hekili:Error( "Name info not available for " .. a.id .. "." ); return false end
|
|
|
|
a.desc = GetSpellDescription( a.id ) -- was returning raw tooltip data.
|
|
|
|
if a.suffix then
|
|
a.actualName = a.name
|
|
a.name = a.name .. " " .. a.suffix
|
|
end
|
|
|
|
local texture = a.texture or GetSpellTexture( a.id )
|
|
|
|
self.abilities[ a.name ] = self.abilities[ a.name ] or a
|
|
class.abilities[ a.name ] = class.abilities[ a.name ] or a
|
|
|
|
if not a.unlisted then
|
|
class.abilityList[ ability ] = a.listName or ( "|T" .. texture .. ":0|t " .. a.name )
|
|
class.abilityByName[ a.name ] = class.abilities[ a.name ] or a
|
|
end
|
|
|
|
if a.rangeSpell and type( a.rangeSpell ) == "number" then
|
|
Hekili:ContinueOnSpellLoad( a.rangeSpell, function( success )
|
|
if success then
|
|
local info = GetSpellInfo( a.rangeSpell )
|
|
if info then
|
|
a.rangeSpell = info.name
|
|
else
|
|
a.rangeSpell = nil
|
|
end
|
|
else
|
|
a.rangeSpell = nil
|
|
end
|
|
end )
|
|
end
|
|
|
|
Hekili.OptionsReady = false
|
|
end
|
|
end
|
|
|
|
self.abilities[ ability ] = a
|
|
self.abilities[ a.id ] = a
|
|
|
|
if not a.unlisted then class.abilityList[ ability ] = class.abilityList[ ability ] or a.listName or a.name end
|
|
|
|
if data.copy then
|
|
if type( data.copy ) == "string" or type( data.copy ) == "number" then
|
|
self.abilities[ data.copy ] = a
|
|
elseif type( data.copy ) == "table" then
|
|
for _, key in ipairs( data.copy ) do
|
|
self.abilities[ key ] = a
|
|
end
|
|
end
|
|
end
|
|
|
|
if data.items then
|
|
for _, itemID in ipairs( data.items ) do
|
|
class.itemMap[ itemID ] = ability
|
|
end
|
|
end
|
|
|
|
if a.dual_cast or a.funcs.dual_cast then
|
|
self.can_dual_cast = true
|
|
self.dual_cast[ a.key ] = true
|
|
end
|
|
|
|
if a.empowered or a.funcs.empowered then
|
|
self.can_empower = true
|
|
end
|
|
|
|
if a.auras then
|
|
self:RegisterAuras( a.auras )
|
|
end
|
|
end,
|
|
|
|
RegisterAbilities = function( self, abilities )
|
|
for ability, data in pairs( abilities ) do
|
|
self:RegisterAbility( ability, data )
|
|
end
|
|
end,
|
|
|
|
RegisterPack = function( self, name, version, import )
|
|
self.packs[ name ] = {
|
|
version = tonumber( version ),
|
|
import = import:gsub("([^|])|([^|])", "%1||%2")
|
|
}
|
|
end,
|
|
|
|
RegisterPriority = function( self, name, version, notes, priority )
|
|
end,
|
|
|
|
RegisterRanges = function( self, ... )
|
|
if type( ... ) == "table" then
|
|
self.ranges = ...
|
|
return
|
|
end
|
|
|
|
for i = 1, select( "#", ... ) do
|
|
insert( self.ranges, ( select( i, ... ) ) )
|
|
end
|
|
end,
|
|
|
|
RegisterRangeFilter = function( self, name, func )
|
|
self.filterName = name
|
|
self.filter = func
|
|
end,
|
|
|
|
RegisterOptions = function( self, options )
|
|
self.options = options
|
|
end,
|
|
|
|
RegisterEvent = function( self, event, func )
|
|
RegisterEvent( event, function( ... )
|
|
if state.spec.id == self.id then func( ... ) end
|
|
end )
|
|
end,
|
|
|
|
RegisterUnitEvent = function( self, event, unit1, unit2, func )
|
|
RegisterUnitEvent( event, unit1, unit2, function( ... )
|
|
if state.spec.id == self.id then func( ... ) end
|
|
end )
|
|
end,
|
|
|
|
RegisterCombatLogEvent = function( self, func )
|
|
self:RegisterHook( "COMBAT_LOG_EVENT_UNFILTERED", func )
|
|
end,
|
|
|
|
RegisterCycle = function( self, func )
|
|
self.cycle = setfenv( func, state )
|
|
end,
|
|
|
|
RegisterPet = function( self, token, id, spell, duration, ... )
|
|
CommitKey( token )
|
|
|
|
self.pets[ token ] = {
|
|
id = type( id ) == "function" and setfenv( id, state ) or id,
|
|
token = token,
|
|
spell = spell,
|
|
duration = type( duration ) == "function" and setfenv( duration, state ) or duration
|
|
}
|
|
|
|
local n = select( "#", ... )
|
|
|
|
if n and n > 0 then
|
|
for i = 1, n do
|
|
local copy = select( i, ... )
|
|
self.pets[ copy ] = self.pets[ token ]
|
|
end
|
|
end
|
|
end,
|
|
|
|
RegisterTotem = function( self, token, id )
|
|
self.totems[ token ] = id
|
|
self.totems[ id ] = token
|
|
|
|
CommitKey( token )
|
|
end,
|
|
|
|
|
|
GetSetting = function( self, info )
|
|
local setting = info[ #info ]
|
|
return Hekili.DB.profile.specs[ self.id ].settings[ setting ]
|
|
end,
|
|
|
|
SetSetting = function( self, info, val )
|
|
local setting = info[ #info ]
|
|
Hekili.DB.profile.specs[ self.id ].settings[ setting ] = val
|
|
end,
|
|
|
|
-- option should be an AceOption table.
|
|
RegisterSetting = function( self, key, value, option )
|
|
CommitKey( key )
|
|
|
|
table.insert( self.settings, {
|
|
name = key,
|
|
default = value,
|
|
info = option
|
|
} )
|
|
|
|
option.order = 100 + #self.settings
|
|
|
|
option.get = option.get or function( info )
|
|
local setting = info[ #info ]
|
|
local val = Hekili.DB.profile.specs[ self.id ].settings[ setting ]
|
|
|
|
if val ~= nil then return val end
|
|
return value
|
|
end
|
|
|
|
option.set = option.set or function( info, val )
|
|
local setting = info[ #info ]
|
|
Hekili.DB.profile.specs[ self.id ].settings[ setting ] = val
|
|
end
|
|
end,
|
|
|
|
-- For faster variables.
|
|
RegisterVariable = function( self, key, func )
|
|
CommitKey( key )
|
|
self.variables[ key ] = setfenv( func, state )
|
|
end,
|
|
}
|
|
|
|
|
|
function Hekili:RestoreDefaults()
|
|
local p = self.DB.profile
|
|
local reverted = {}
|
|
local changed = {}
|
|
|
|
for k, v in pairs( class.packs ) do
|
|
local existing = rawget( p.packs, k )
|
|
|
|
if not existing or not existing.version or existing.version ~= v.version then
|
|
local data = self.DeserializeActionPack( v.import )
|
|
|
|
if data and type( data ) == "table" then
|
|
p.packs[ k ] = data.payload
|
|
data.payload.version = v.version
|
|
data.payload.date = v.version
|
|
data.payload.builtIn = true
|
|
|
|
if not existing or not existing.version or existing.version < v.version then
|
|
insert( changed, k )
|
|
else
|
|
insert( reverted, k )
|
|
end
|
|
|
|
local specID = data.payload.spec
|
|
|
|
if specID then
|
|
local spec = rawget( p.specs, specID )
|
|
if spec then
|
|
if spec.package then
|
|
local currPack = p.packs[ spec.package ]
|
|
if not currPack or currPack.spec ~= specID then
|
|
spec.package = k
|
|
end
|
|
else
|
|
spec.package = k
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if #changed > 0 or #reverted > 0 then
|
|
self:LoadScripts()
|
|
end
|
|
|
|
if #changed > 0 then
|
|
local msg
|
|
|
|
if #changed == 1 then
|
|
msg = "The |cFFFFD100" .. changed[1] .. "|r priority was updated."
|
|
elseif #changed == 2 then
|
|
msg = "The |cFFFFD100" .. changed[1] .. "|r and |cFFFFD100" .. changed[2] .. "|r priorities were updated."
|
|
else
|
|
msg = "|cFFFFD100" .. changed[1] .. "|r"
|
|
|
|
for i = 2, #changed - 1 do
|
|
msg = msg .. ", |cFFFFD100" .. changed[i] .. "|r"
|
|
end
|
|
|
|
msg = "The " .. msg .. ", and |cFFFFD100" .. changed[ #changed ] .. "|r priorities were updated."
|
|
end
|
|
|
|
if msg then
|
|
C_Timer.After( 5, function()
|
|
if Hekili.DB.profile.notifications.enabled then Hekili:Notify( msg, 6 ) end
|
|
Hekili:Print( msg )
|
|
end )
|
|
end
|
|
end
|
|
|
|
if #reverted > 0 then
|
|
local msg
|
|
|
|
if #reverted == 1 then
|
|
msg = "The |cFFFFD100" .. reverted[1] .. "|r priority was reverted."
|
|
elseif #reverted == 2 then
|
|
msg = "The |cFFFFD100" .. reverted[1] .. "|r and |cFFFFD100" .. reverted[2] .. "|r priorities were reverted."
|
|
else
|
|
msg = "|cFFFFD100" .. reverted[1] .. "|r"
|
|
|
|
for i = 2, #reverted - 1 do
|
|
msg = msg .. ", |cFFFFD100" .. reverted[i] .. "|r"
|
|
end
|
|
|
|
msg = "The " .. msg .. ", and |cFFFFD100" .. reverted[ #reverted ] .. "|r priorities were reverted."
|
|
end
|
|
|
|
if msg then
|
|
C_Timer.After( 6, function()
|
|
if Hekili.DB.profile.notifications.enabled then Hekili:Notify( msg, 6 ) end
|
|
Hekili:Print( msg )
|
|
end )
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function Hekili:RestoreDefault( name )
|
|
local p = self.DB.profile
|
|
|
|
local default = class.packs[ name ]
|
|
|
|
if default then
|
|
local data = self.DeserializeActionPack( default.import )
|
|
|
|
if data and type( data ) == "table" then
|
|
p.packs[ name ] = data.payload
|
|
data.payload.version = default.version
|
|
data.payload.date = default.version
|
|
data.payload.builtIn = true
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
ns.restoreDefaults = function( category, purge )
|
|
end
|
|
|
|
|
|
ns.isDefault = function( name, category )
|
|
if not name or not category then
|
|
return false
|
|
end
|
|
|
|
for i, default in ipairs( class.defaults ) do
|
|
if default.type == category and default.name == name then
|
|
return true, i
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
|
|
function Hekili:NewSpecialization( specID, isRanged, icon )
|
|
|
|
if not specID or specID < 0 then return end
|
|
|
|
local id, name, _, texture, role, pClass
|
|
|
|
if Hekili.IsRetail() and specID > 0 then id, name, _, texture, role, pClass = GetSpecializationInfoByID( specID )
|
|
else
|
|
id = specID
|
|
texture = icon
|
|
end
|
|
|
|
if not id then
|
|
Hekili:Error( "Unable to generate specialization DB for spec ID #" .. specID .. "." )
|
|
return nil
|
|
end
|
|
|
|
if specID ~= 0 then
|
|
class.initialized = true
|
|
end
|
|
|
|
local token = getSpecializationKey( id )
|
|
|
|
local spec = class.specs[ id ] or {
|
|
id = id,
|
|
key = token,
|
|
name = name,
|
|
texture = texture,
|
|
role = role,
|
|
class = pClass,
|
|
melee = not isRanged,
|
|
|
|
resources = {},
|
|
resourceAuras = {},
|
|
primaryResource = nil,
|
|
|
|
talents = {},
|
|
pvptalents = {},
|
|
powers = {},
|
|
|
|
auras = {},
|
|
pseudoAuras = 0,
|
|
|
|
abilities = {},
|
|
pseudoAbilities = 0,
|
|
itemAbilities = 0,
|
|
pendingItemSpells = {},
|
|
|
|
pets = {},
|
|
totems = {},
|
|
|
|
potions = {},
|
|
|
|
ranges = {},
|
|
settings = {},
|
|
|
|
stateExprs = {}, -- expressions are returned as values and take no args.
|
|
stateFuncs = {}, -- functions can take arguments and can be used as helper functions in handlers.
|
|
stateTables = {}, -- tables are... tables.
|
|
|
|
gear = {},
|
|
setBonuses = {},
|
|
|
|
hooks = {},
|
|
funcHooks = {},
|
|
phases = {},
|
|
interrupts = {},
|
|
|
|
dual_cast = {},
|
|
|
|
packs = {},
|
|
options = {},
|
|
|
|
variables = {}
|
|
}
|
|
|
|
class.num = class.num + 1
|
|
|
|
for key, func in pairs( HekiliSpecMixin ) do
|
|
spec[ key ] = func
|
|
end
|
|
|
|
class.specs[ id ] = spec
|
|
return spec
|
|
end
|
|
|
|
|
|
function Hekili:GetSpecialization( specID )
|
|
if not specID then return class.specs[ 0 ] end
|
|
return class.specs[ specID ]
|
|
end
|
|
|
|
|
|
class.file = UnitClassBase( "player" )
|
|
local all = Hekili:NewSpecialization( 0, "All", "Interface\\Addons\\Hekili\\Textures\\LOGO-WHITE.blp" )
|
|
|
|
------------------------------
|
|
-- SHARED SPELLS/BUFFS/ETC. --
|
|
------------------------------
|
|
|
|
all:RegisterAuras( {
|
|
|
|
enlisted_a = {
|
|
id = 282559,
|
|
duration = 3600,
|
|
},
|
|
|
|
enlisted_b = {
|
|
id = 289954,
|
|
duration = 3600,
|
|
},
|
|
|
|
enlisted_c = {
|
|
id = 269083,
|
|
duration = 3600,
|
|
},
|
|
|
|
enlisted = {
|
|
alias = { "enlisted_c", "enlisted_b", "enlisted_a" },
|
|
aliasMode = "first",
|
|
aliasType = "buff",
|
|
duration = 3600,
|
|
},
|
|
|
|
-- Can be used in GCD calculation.
|
|
shadowform = {
|
|
id = 232698,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
},
|
|
|
|
voidform = {
|
|
id = 194249,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
},
|
|
|
|
adrenaline_rush = {
|
|
id = 13750,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
},
|
|
|
|
-- Bloodlusts
|
|
ancient_hysteria = {
|
|
id = 90355,
|
|
shared = "player", -- use anyone's buff on the player, not just player's.
|
|
duration = 40,
|
|
max_stack = 1,
|
|
},
|
|
|
|
heroism = {
|
|
id = 32182,
|
|
shared = "player", -- use anyone's buff on the player, not just player's.
|
|
duration = 40,
|
|
max_stack = 1,
|
|
},
|
|
|
|
time_warp = {
|
|
id = 80353,
|
|
shared = "player", -- use anyone's buff on the player, not just player's.
|
|
duration = 40,
|
|
max_stack = 1,
|
|
},
|
|
|
|
netherwinds = {
|
|
id = 160452,
|
|
shared = "player", -- use anyone's buff on the player, not just player's.
|
|
duration = 40,
|
|
max_stack = 1,
|
|
},
|
|
|
|
primal_rage = {
|
|
id = 264667,
|
|
shared = "player", -- use anyone's buff on the player, not just player's.
|
|
duration = 40,
|
|
max_stack = 1,
|
|
},
|
|
|
|
drums_of_deathly_ferocity = {
|
|
id = 309658,
|
|
shared = "player", -- use anyone's buff on the player, not just player's.
|
|
duration = 40,
|
|
max_stack = 1,
|
|
},
|
|
|
|
bloodlust = {
|
|
alias = { "ancient_hysteria", "bloodlust_actual", "drums_of_deathly_ferocity", "fury_of_the_aspects", "heroism", "netherwinds", "primal_rage", "time_warp" },
|
|
aliasMode = "first",
|
|
aliasType = "buff",
|
|
duration = 3600,
|
|
},
|
|
|
|
bloodlust_actual = {
|
|
id = 2825,
|
|
duration = 40,
|
|
shared = "player",
|
|
max_stack = 1,
|
|
},
|
|
|
|
exhaustion = {
|
|
id = 57723,
|
|
duration = 600,
|
|
shared = "player",
|
|
max_stack = 1,
|
|
copy = 390435
|
|
},
|
|
|
|
insanity = {
|
|
id = 95809,
|
|
duration = 600,
|
|
shared = "player",
|
|
max_stack = 1
|
|
},
|
|
|
|
temporal_displacement = {
|
|
id = 80354,
|
|
duration = 600,
|
|
shared = "player",
|
|
max_stack = 1
|
|
},
|
|
|
|
fury_of_the_aspects = {
|
|
id = 390386,
|
|
duration = 40,
|
|
max_stack = 1,
|
|
shared = "player",
|
|
},
|
|
|
|
mark_of_the_wild = {
|
|
id = 1126,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player",
|
|
},
|
|
|
|
fatigued = {
|
|
id = 264689,
|
|
duration = 600,
|
|
shared = "player",
|
|
max_stack = 1
|
|
},
|
|
|
|
sated = {
|
|
alias = { "exhaustion", "fatigued", "insanity", "sated_actual", "temporal_displacement" },
|
|
aliasMode = "first",
|
|
aliasType = "debuff",
|
|
duration = 3600,
|
|
},
|
|
|
|
sated_actual = {
|
|
id = 57724,
|
|
duration = 600,
|
|
shared = "player",
|
|
max_stack = 1,
|
|
},
|
|
|
|
blessing_of_the_bronze = {
|
|
alias = {
|
|
"blessing_of_the_bronze_evoker",
|
|
"blessing_of_the_bronze_deathknight",
|
|
"blessing_of_the_bronze_demonhunter",
|
|
"blessing_of_the_bronze_druid",
|
|
"blessing_of_the_bronze_hunter",
|
|
"blessing_of_the_bronze_mage",
|
|
"blessing_of_the_bronze_monk",
|
|
"blessing_of_the_bronze_paladin",
|
|
"blessing_of_the_bronze_priest",
|
|
"blessing_of_the_bronze_rogue",
|
|
"blessing_of_the_bronze_shaman",
|
|
"blessing_of_the_bronze_warlock",
|
|
"blessing_of_the_bronze_warrior",
|
|
},
|
|
aliasType = "buff",
|
|
aliasMode = "longest"
|
|
},
|
|
-- Can always be seen and tracked by the Hunter.; Damage taken increased by $428402s4% while above $s3% health.
|
|
-- https://wowhead.com/beta/spell=257284
|
|
hunters_mark = {
|
|
id = 257284,
|
|
duration = 3600,
|
|
tick_time = 0.5,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
shared = "target"
|
|
},
|
|
chaos_brand = {
|
|
id = 1490,
|
|
duration = 3600,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
shared = "target"
|
|
},
|
|
blessing_of_the_bronze_deathknight = {
|
|
id = 381732,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_demonhunter = {
|
|
id = 381741,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_druid = {
|
|
id = 381746,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_evoker = {
|
|
id = 381748,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_hunter = {
|
|
id = 364342,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_mage = {
|
|
id = 381750,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_monk = {
|
|
id = 381751,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_paladin = {
|
|
id = 381752,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_priest = {
|
|
id = 381753,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_rogue = {
|
|
id = 381754,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_shaman = {
|
|
id = 381756,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_warlock = {
|
|
id = 381757,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
blessing_of_the_bronze_warrior = {
|
|
id = 381758,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player"
|
|
},
|
|
|
|
power_infusion = {
|
|
id = 10060,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
shared = "player",
|
|
dot = "buff"
|
|
},
|
|
|
|
battle_shout = {
|
|
id = 6673,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
shared = "player",
|
|
dot = "buff"
|
|
},
|
|
|
|
-- Mastery increased by $w1% and auto attacks have a $h% chance to instantly strike again.
|
|
skyfury = {
|
|
id = 462854,
|
|
duration = 3600.0,
|
|
max_stack = 1,
|
|
shared = "player",
|
|
dot = "buff"
|
|
},
|
|
|
|
-- SL Season 3
|
|
decrypted_urh_cypher = {
|
|
id = 368239,
|
|
duration = 10,
|
|
max_stack = 1,
|
|
},
|
|
|
|
old_war = {
|
|
id = 188028,
|
|
duration = 25,
|
|
},
|
|
|
|
deadly_grace = {
|
|
id = 188027,
|
|
duration = 25,
|
|
},
|
|
|
|
prolonged_power = {
|
|
id = 229206,
|
|
duration = 60,
|
|
},
|
|
|
|
dextrous = {
|
|
id = 146308,
|
|
duration = 20,
|
|
},
|
|
|
|
vicious = {
|
|
id = 148903,
|
|
duration = 10,
|
|
},
|
|
|
|
-- WoD Legendaries
|
|
archmages_incandescence_agi = {
|
|
id = 177161,
|
|
duration = 10,
|
|
},
|
|
|
|
archmages_incandescence_int = {
|
|
id = 177159,
|
|
duration = 10,
|
|
},
|
|
|
|
archmages_incandescence_str = {
|
|
id = 177160,
|
|
duration = 10,
|
|
},
|
|
|
|
archmages_greater_incandescence_agi = {
|
|
id = 177172,
|
|
duration = 10,
|
|
},
|
|
|
|
archmages_greater_incandescence_int = {
|
|
id = 177176,
|
|
duration = 10,
|
|
},
|
|
|
|
archmages_greater_incandescence_str = {
|
|
id = 177175,
|
|
duration = 10,
|
|
},
|
|
|
|
maalus = {
|
|
id = 187620,
|
|
duration = 15,
|
|
},
|
|
|
|
thorasus = {
|
|
id = 187619,
|
|
duration = 15,
|
|
},
|
|
|
|
sephuzs_secret = {
|
|
id = 208052,
|
|
duration = 10,
|
|
max_stack = 1,
|
|
},
|
|
|
|
str_agi_int = {
|
|
duration = 3600,
|
|
},
|
|
|
|
stamina = {
|
|
duration = 3600,
|
|
},
|
|
|
|
attack_power_multiplier = {
|
|
duration = 3600,
|
|
},
|
|
|
|
haste = {
|
|
duration = 3600,
|
|
},
|
|
|
|
spell_power_multiplier = {
|
|
duration = 3600,
|
|
},
|
|
|
|
critical_strike = {
|
|
duration = 3600,
|
|
},
|
|
|
|
mastery = {
|
|
duration = 3600,
|
|
},
|
|
|
|
versatility = {
|
|
duration = 3600,
|
|
},
|
|
|
|
casting = {
|
|
name = "Casting",
|
|
generate = function( t, auraType )
|
|
local unit = auraType == "debuff" and "target" or "player"
|
|
|
|
if unit == "player" or UnitCanAttack( "player", "target" ) then
|
|
local spell, _, _, startCast, endCast, _, _, notInterruptible, spellID = UnitCastingInfo( unit )
|
|
|
|
if spell then
|
|
startCast = startCast / 1000
|
|
endCast = endCast / 1000
|
|
|
|
t.name = spell
|
|
t.count = 1
|
|
t.expires = endCast
|
|
t.applied = startCast
|
|
t.duration = endCast - startCast
|
|
t.v1 = spellID
|
|
t.v2 = notInterruptible and 1 or 0
|
|
t.v3 = 0
|
|
t.caster = unit
|
|
|
|
if unit == "target" and Hekili.DB.profile.filterCasts then
|
|
local filters = Hekili.DB.profile.castFilters
|
|
local npcid = state.target.npcid
|
|
|
|
if npcid and filters[ npcid ] and not filters[ npcid ][ spellID ] then
|
|
if Hekili.ActiveDebug then Hekili:Debug( "Cast '%s' ignored per user preference.", spell ) end
|
|
removeDebuff( "casting" )
|
|
end
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
spell, _, _, startCast, endCast, _, notInterruptible, spellID = UnitChannelInfo( unit )
|
|
|
|
if spell then
|
|
startCast = startCast / 1000
|
|
endCast = endCast / 1000
|
|
|
|
t.name = spell
|
|
t.count = 1
|
|
t.expires = endCast
|
|
t.applied = startCast
|
|
t.duration = endCast - startCast
|
|
t.v1 = spellID
|
|
t.v2 = notInterruptible and 1 or 0
|
|
t.v3 = 1 -- channeled.
|
|
t.caster = unit
|
|
|
|
if class.abilities[ spellID ] and class.abilities[ spellID ].dontChannel then
|
|
removeBuff( "casting" )
|
|
elseif unit == "target" and Hekili.DB.profile.filterCasts then
|
|
local filters = Hekili.DB.profile.castFilters
|
|
local npcid = state.target.npcid
|
|
|
|
if npcid and filters[ npcid ] and not filters[ npcid ][ spellID ] then
|
|
if Hekili.ActiveDebug then Hekili:Debug( "Cast '%s' ignored per user preference.", spell ) end
|
|
removeDebuff( "casting" )
|
|
end
|
|
end
|
|
|
|
return
|
|
end
|
|
end
|
|
|
|
t.name = "Casting"
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.v1 = 0
|
|
t.v2 = 0
|
|
t.v3 = 0
|
|
t.caster = unit
|
|
end,
|
|
},
|
|
|
|
--[[ player_casting = {
|
|
name = "Casting",
|
|
generate = function ()
|
|
local aura = buff.player_casting
|
|
|
|
local name, _, _, startCast, endCast, _, _, notInterruptible, spell = UnitCastingInfo( "player" )
|
|
|
|
if name then
|
|
aura.name = name
|
|
aura.count = 1
|
|
aura.expires = endCast / 1000
|
|
aura.applied = startCast / 1000
|
|
aura.v1 = spell
|
|
aura.caster = "player"
|
|
return
|
|
end
|
|
|
|
name, _, _, startCast, endCast, _, _, notInterruptible, spell = UnitChannelInfo( "player" )
|
|
|
|
if notInterruptible == false then
|
|
aura.name = name
|
|
aura.count = 1
|
|
aura.expires = endCast / 1000
|
|
aura.applied = startCast / 1000
|
|
aura.v1 = spell
|
|
aura.caster = "player"
|
|
return
|
|
end
|
|
|
|
aura.name = "Casting"
|
|
aura.count = 0
|
|
aura.expires = 0
|
|
aura.applied = 0
|
|
aura.v1 = 0
|
|
aura.caster = "target"
|
|
end,
|
|
}, ]]
|
|
|
|
movement = {
|
|
duration = 5,
|
|
max_stack = 1,
|
|
generate = function ()
|
|
local m = buff.movement
|
|
|
|
if moving then
|
|
m.count = 1
|
|
m.expires = query_time + 5
|
|
m.applied = query_time
|
|
m.caster = "player"
|
|
return
|
|
end
|
|
|
|
m.count = 0
|
|
m.expires = 0
|
|
m.applied = 0
|
|
m.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
repeat_performance = {
|
|
id = 304409,
|
|
duration = 30,
|
|
max_stack = 1,
|
|
},
|
|
|
|
-- Why do we have this, again?
|
|
unknown_buff = {},
|
|
|
|
berserking = {
|
|
id = 26297,
|
|
duration = 10,
|
|
},
|
|
|
|
hyper_organic_light_originator = {
|
|
id = 312924,
|
|
duration = 6,
|
|
},
|
|
|
|
blood_fury = {
|
|
id = 20572,
|
|
duration = 15,
|
|
},
|
|
|
|
shadowmeld = {
|
|
id = 58984,
|
|
duration = 3600,
|
|
},
|
|
|
|
ferocity_of_the_frostwolf = {
|
|
id = 274741,
|
|
duration = 15,
|
|
},
|
|
|
|
might_of_the_blackrock = {
|
|
id = 274742,
|
|
duration = 15,
|
|
},
|
|
|
|
zeal_of_the_burning_blade = {
|
|
id = 274740,
|
|
duration = 15,
|
|
},
|
|
|
|
rictus_of_the_laughing_skull = {
|
|
id = 274739,
|
|
duration = 15,
|
|
},
|
|
|
|
ancestral_call = {
|
|
duration = 15,
|
|
alias = { "ferocity_of_the_frostwolf", "might_of_the_blackrock", "zeal_of_the_burning_blade", "rictus_of_the_laughing_skull" },
|
|
aliasMode = "first",
|
|
},
|
|
|
|
arcane_pulse = {
|
|
id = 260369,
|
|
duration = 12,
|
|
},
|
|
|
|
fireblood = {
|
|
id = 273104,
|
|
duration = 8,
|
|
},
|
|
|
|
out_of_range = {
|
|
generate = function ( oor )
|
|
oor.rangeSpell = rawget( oor, "rangeSpell" ) or settings.spec.rangeChecker or class.specs[ state.spec.id ].ranges[ 1 ]
|
|
|
|
if LSR.IsSpellInRange( class.abilities[ oor.rangeSpell ].name, "target" ) ~= 1 then
|
|
oor.count = 1
|
|
oor.applied = query_time
|
|
oor.expires = query_time + 3600
|
|
oor.caster = "player"
|
|
oor.v1 = oor.rangeSpell
|
|
return
|
|
end
|
|
|
|
oor.count = 0
|
|
oor.applied = 0
|
|
oor.expires = 0
|
|
oor.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
loss_of_control = {
|
|
duration = 10,
|
|
generate = function( t )
|
|
local max_events = GetActiveLossOfControlDataCount()
|
|
|
|
if max_events > 0 then
|
|
local spell, start, duration, remains = "none", 0, 0, 0
|
|
|
|
for i = 1, max_events do
|
|
local event = GetActiveLossOfControlData( i )
|
|
|
|
if event.lockoutSchool == 0 and event.startTime and event.startTime > 0 and event.timeRemaining and event.timeRemaining > 0 and event.timeRemaining > remains then
|
|
spell = event.spellID
|
|
start = event.startTime
|
|
duration = event.duration
|
|
remains = event.timeRemaining
|
|
end
|
|
end
|
|
|
|
if start + duration > query_time then
|
|
t.count = 1
|
|
t.expires = start + duration
|
|
t.applied = start
|
|
t.duration = duration
|
|
t.caster = "anybody"
|
|
t.v1 = spell
|
|
return
|
|
end
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.duration = 10
|
|
t.caster = "nobody"
|
|
t.v1 = 0
|
|
end,
|
|
},
|
|
|
|
rooted = {
|
|
duration = 10,
|
|
generate = function( t )
|
|
local max_events = GetActiveLossOfControlDataCount()
|
|
|
|
if max_events > 0 then
|
|
local spell, start, duration, remains = "none", 0, 0, 0
|
|
|
|
for i = 1, max_events do
|
|
local event = GetActiveLossOfControlData( i )
|
|
|
|
if event.locType == "ROOT" and event.startTime and event.startTime > 0 and event.timeRemaining and event.timeRemaining > 0 and event.timeRemaining > remains then
|
|
spell = event.spellID
|
|
start = event.startTime
|
|
duration = event.duration
|
|
remains = event.timeRemaining
|
|
end
|
|
end
|
|
|
|
if start + duration > query_time then
|
|
t.count = 1
|
|
t.expires = start + duration
|
|
t.applied = start
|
|
t.duration = duration
|
|
t.caster = "anybody"
|
|
t.v1 = spell
|
|
return
|
|
end
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.duration = 10
|
|
t.caster = "nobody"
|
|
t.v1 = 0
|
|
end,
|
|
},
|
|
|
|
snared = {
|
|
duration = 10,
|
|
generate = function( t )
|
|
local max_events = GetActiveLossOfControlDataCount()
|
|
|
|
if max_events > 0 then
|
|
local spell, start, duration, remains = "none", 0, 0, 0
|
|
|
|
for i = 1, max_events do
|
|
local event = GetActiveLossOfControlData( i )
|
|
|
|
if event.locType == "SNARE" and event.startTime and event.startTime > 0 and event.timeRemaining and event.timeRemaining > 0 and event.timeRemaining > remains then
|
|
spell = event.spellID
|
|
start = event.startTime
|
|
duration = event.duration
|
|
remains = event.timeRemaining
|
|
end
|
|
end
|
|
|
|
if start + duration > query_time then
|
|
t.count = 1
|
|
t.expires = start + duration
|
|
t.applied = start
|
|
t.duration = duration
|
|
t.caster = "anybody"
|
|
t.v1 = spell
|
|
return
|
|
end
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.duration = 10
|
|
t.caster = "nobody"
|
|
t.v1 = 0
|
|
end,
|
|
},
|
|
|
|
dispellable_curse = {
|
|
generate = function( t )
|
|
local i = 1
|
|
local name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
|
|
while( name ) do
|
|
if debuffType == "Curse" then break end
|
|
|
|
i = i + 1
|
|
name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
end
|
|
|
|
if name then
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime > 0 and expirationTime or query_time + 5
|
|
t.applied = expirationTime > 0 and ( expirationTime - duration ) or query_time
|
|
t.caster = "nobody"
|
|
return
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
dispellable_poison = {
|
|
generate = function( t )
|
|
local i = 1
|
|
local name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
|
|
while( name ) do
|
|
if debuffType == "Poison" then break end
|
|
|
|
i = i + 1
|
|
name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
end
|
|
|
|
if name then
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime > 0 and expirationTime or query_time + 5
|
|
t.applied = expirationTime > 0 and ( expirationTime - duration ) or query_time
|
|
t.caster = "nobody"
|
|
return
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
dispellable_disease = {
|
|
generate = function( t )
|
|
local i = 1
|
|
local name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
|
|
while( name ) do
|
|
if debuffType == "Disease" then break end
|
|
|
|
i = i + 1
|
|
name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
end
|
|
|
|
if name then
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime > 0 and expirationTime or query_time + 5
|
|
t.applied = expirationTime > 0 and ( expirationTime - duration ) or query_time
|
|
t.caster = "nobody"
|
|
return
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
dispellable_magic = {
|
|
generate = function( t, auraType )
|
|
if auraType == "buff" then
|
|
local i = 1
|
|
local name, _, count, debuffType, duration, expirationTime, _, canDispel = UnitBuff( "target", i )
|
|
|
|
while( name ) do
|
|
if debuffType == "Magic" and canDispel then break end
|
|
|
|
i = i + 1
|
|
name, _, count, debuffType, duration, expirationTime, _, canDispel = UnitBuff( "target", i )
|
|
end
|
|
|
|
if canDispel then
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime > 0 and expirationTime or query_time + 5
|
|
t.applied = expirationTime > 0 and ( expirationTime - duration ) or query_time
|
|
t.caster = "nobody"
|
|
return
|
|
end
|
|
|
|
else
|
|
local i = 1
|
|
local name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
|
|
while( name ) do
|
|
if debuffType == "Magic" then break end
|
|
|
|
i = i + 1
|
|
name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
end
|
|
|
|
if name then
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime > 0 and expirationTime or query_time + 5
|
|
t.applied = expirationTime > 0 and ( expirationTime - duration ) or query_time
|
|
t.caster = "nobody"
|
|
return
|
|
end
|
|
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
stealable_magic = {
|
|
generate = function( t )
|
|
if UnitCanAttack( "player", "target" ) then
|
|
local i = 1
|
|
local name, _, count, debuffType, duration, expirationTime, _, canDispel = UnitBuff( "target", i )
|
|
|
|
while( name ) do
|
|
if debuffType == "Magic" and canDispel then break end
|
|
|
|
i = i + 1
|
|
name, _, count, debuffType, duration, expirationTime, _, canDispel = UnitBuff( "target", i )
|
|
end
|
|
|
|
if canDispel then
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime > 0 and expirationTime or query_time + 5
|
|
t.applied = expirationTime > 0 and ( expirationTime - duration ) or query_time
|
|
t.caster = "nobody"
|
|
return
|
|
end
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
reversible_magic = {
|
|
generate = function( t )
|
|
local i = 1
|
|
local name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
|
|
while( name ) do
|
|
if debuffType == "Magic" then break end
|
|
|
|
i = i + 1
|
|
name, _, count, debuffType, duration, expirationTime = UnitDebuff( "player", i, "RAID" )
|
|
end
|
|
|
|
if name then
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime > 0 and expirationTime or query_time + 5
|
|
t.applied = expirationTime > 0 and ( expirationTime - duration ) or query_time
|
|
t.caster = "nobody"
|
|
return
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
dispellable_enrage = {
|
|
generate = function( t )
|
|
if UnitCanAttack( "player", "target" ) then
|
|
local i = 1
|
|
local name, _, count, debuffType, duration, expirationTime, _, canDispel = UnitBuff( "target", i )
|
|
|
|
while( name ) do
|
|
if debuffType == "" and canDispel then break end
|
|
|
|
i = i + 1
|
|
name, _, count, debuffType, duration, expirationTime, _, canDispel = UnitBuff( "target", i )
|
|
end
|
|
|
|
if canDispel then
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime > 0 and expirationTime or query_time + 5
|
|
t.applied = expirationTime > 0 and ( expirationTime - duration ) or query_time
|
|
t.caster = "nobody"
|
|
return
|
|
end
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
all_absorbs = {
|
|
duration = 15,
|
|
max_stack = 1,
|
|
-- TODO: Check if function works.
|
|
generate = function( t, auraType )
|
|
local unit = auraType == "debuff" and "target" or "player"
|
|
local amount = UnitGetTotalAbsorbs( unit )
|
|
|
|
if amount > 0 then
|
|
t.name = ABSORB
|
|
t.count = 1
|
|
t.expires = now + 10
|
|
t.applied = now - 5
|
|
t.caster = unit
|
|
return
|
|
end
|
|
|
|
t.count = 0
|
|
t.expires = 0
|
|
t.applied = 0
|
|
t.caster = "nobody"
|
|
end,
|
|
copy = "unravel_absorb"
|
|
},
|
|
} )
|
|
|
|
|
|
do
|
|
-- Dragonflight Potions
|
|
-- There are multiple items for each potion, and there are also Toxic potions that people may not want to use.
|
|
local df_potions = {
|
|
{
|
|
name = "elemental_potion_of_ultimate_power",
|
|
items = { 191914, 191913, 191912, 191383, 191382, 191381 }
|
|
},
|
|
{
|
|
name = "elemental_potion_of_power",
|
|
items = { 191907, 191906, 191905, 191389, 191388, 191387 }
|
|
},
|
|
}
|
|
|
|
all:RegisterAuras( {
|
|
elemental_potion_of_ultimate_power = {
|
|
id = 371028,
|
|
duration = 30,
|
|
max_stack = 1
|
|
},
|
|
elemental_potion_of_power = {
|
|
id = 371024,
|
|
duration = 30,
|
|
max_stack = 1
|
|
},
|
|
potion = {
|
|
alias = { "elemental_potion_of_ultimate_power", "elemental_potion_of_power" },
|
|
aliasMode = "first",
|
|
aliasType = "buff",
|
|
duration = 30
|
|
}
|
|
} )
|
|
|
|
local function getValidPotion()
|
|
for _, potion in ipairs( df_potions ) do
|
|
for _, item in ipairs( potion.items ) do
|
|
if GetItemCount( item, false ) > 0 then return item, potion.name end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
all:RegisterAbility( "potion", {
|
|
name = "Potion",
|
|
listName = '|T136243:0|t |cff00ccff[Potion]|r',
|
|
cast = 0,
|
|
cooldown = 300,
|
|
gcd = "off",
|
|
|
|
startsCombat = false,
|
|
toggle = "potions",
|
|
|
|
item = function ()
|
|
return getValidPotion()
|
|
end,
|
|
bagItem = true,
|
|
|
|
timeToReady = function ()
|
|
local item = getValidPotion()
|
|
|
|
if item then
|
|
local start, dur = GetItemCooldown( item )
|
|
return max( 0, start + dur - query_time )
|
|
end
|
|
|
|
return 3600
|
|
end,
|
|
|
|
handler = function ()
|
|
local item, effect = getValidPotion()
|
|
|
|
if item and effect then
|
|
applyBuff( effect )
|
|
end
|
|
end,
|
|
|
|
usable = function ()
|
|
if getValidPotion() ~= nil then return true end
|
|
return false, "no valid potions found in inventory"
|
|
end,
|
|
} )
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local gotn_classes = {
|
|
WARRIOR = 28880,
|
|
MONK = 121093,
|
|
DEATHKNIGHT = 59545,
|
|
SHAMAN = 59547,
|
|
HUNTER = 59543,
|
|
PRIEST = 59544,
|
|
MAGE = 59548,
|
|
PALADIN = 59542,
|
|
ROGUE = 370626
|
|
}
|
|
|
|
local baseClass = UnitClassBase( "player" ) or "WARRIOR"
|
|
|
|
all:RegisterAura( "gift_of_the_naaru", {
|
|
id = gotn_classes[ baseClass ],
|
|
duration = 5,
|
|
max_stack = 1,
|
|
copy = { 28800, 121093, 59545, 59547, 59543, 59544, 59548, 59542, 370626 }
|
|
} )
|
|
|
|
all:RegisterAbility( "gift_of_the_naaru", {
|
|
id = gotn_classes[ baseClass ],
|
|
cast = 0,
|
|
cooldown = 180,
|
|
gcd = "off",
|
|
|
|
handler = function ()
|
|
applyBuff( "gift_of_the_naaru" )
|
|
end,
|
|
} )
|
|
|
|
|
|
all:RegisterAbilities( {
|
|
global_cooldown = {
|
|
id = 61304,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
unlisted = true,
|
|
known = function () return true end,
|
|
},
|
|
|
|
ancestral_call = {
|
|
id = 274738,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
-- usable = function () return race.maghar_orc end,
|
|
handler = function ()
|
|
applyBuff( "ancestral_call" )
|
|
end,
|
|
},
|
|
|
|
arcane_pulse = {
|
|
id = 260364,
|
|
cast = 0,
|
|
cooldown = 180,
|
|
gcd = "spell",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
-- usable = function () return race.nightborne end,
|
|
handler = function ()
|
|
applyDebuff( "target", "arcane_pulse" )
|
|
end,
|
|
},
|
|
|
|
berserking = {
|
|
id = 26297,
|
|
cast = 0,
|
|
cooldown = 180,
|
|
gcd = "off",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
-- usable = function () return race.troll end,
|
|
handler = function ()
|
|
applyBuff( "berserking" )
|
|
end,
|
|
},
|
|
|
|
hyper_organic_light_originator = {
|
|
id = 312924,
|
|
cast = 0,
|
|
cooldown = 180,
|
|
gcd = "off",
|
|
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "hyper_organic_light_originator" )
|
|
end
|
|
},
|
|
|
|
bag_of_tricks = {
|
|
id = 312411,
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "spell",
|
|
|
|
toggle = "cooldowns",
|
|
},
|
|
|
|
haymaker = {
|
|
id = 287712,
|
|
cast = 1,
|
|
cooldown = 150,
|
|
gcd = "spell",
|
|
|
|
handler = function ()
|
|
if not target.is_boss then applyDebuff( "target", "haymaker" ) end
|
|
end,
|
|
|
|
auras = {
|
|
haymaker = {
|
|
id = 287712,
|
|
duration = 3,
|
|
max_stack = 1,
|
|
},
|
|
}
|
|
}
|
|
} )
|
|
|
|
|
|
-- Blood Fury spell IDs vary by class (whether you need AP/Int/both).
|
|
local bf_classes = {
|
|
DEATHKNIGHT = 20572,
|
|
HUNTER = 20572,
|
|
MAGE = 33702,
|
|
MONK = 33697,
|
|
ROGUE = 20572,
|
|
SHAMAN = 33697,
|
|
WARLOCK = 33702,
|
|
WARRIOR = 20572,
|
|
PRIEST = 33702
|
|
}
|
|
|
|
all:RegisterAbilities( {
|
|
blood_fury = {
|
|
id = function () return bf_classes[ class.file ] or 20572 end,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
-- usable = function () return race.orc end,
|
|
handler = function ()
|
|
applyBuff( "blood_fury", 15 )
|
|
end,
|
|
|
|
copy = { 33702, 20572, 33697 },
|
|
},
|
|
|
|
arcane_torrent = {
|
|
id = function ()
|
|
if class.file == "PALADIN" then return 155145 end
|
|
if class.file == "MONK" then return 129597 end
|
|
if class.file == "DEATHKNIGHT" then return 50613 end
|
|
if class.file == "WARRIOR" then return 69179 end
|
|
if class.file == "ROGUE" then return 25046 end
|
|
if class.file == "HUNTER" then return 80483 end
|
|
if class.file == "DEMONHUNTER" then return 202719 end
|
|
if class.file == "PRIEST" then return 232633 end
|
|
return 28730
|
|
end,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "spell",
|
|
|
|
-- It does start combat if there are enemies in range, but we often use it precombat for resources.
|
|
startsCombat = false,
|
|
|
|
-- usable = function () return race.blood_elf end,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
if class.file == "DEATHKNIGHT" then gain( 20, "runic_power" )
|
|
elseif class.file == "HUNTER" then gain( 15, "focus" )
|
|
elseif class.file == "MONK" then gain( 1, "chi" )
|
|
elseif class.file == "PALADIN" then gain( 1, "holy_power" )
|
|
elseif class.file == "ROGUE" then gain( 15, "energy" )
|
|
elseif class.file == "WARRIOR" then gain( 15, "rage" )
|
|
elseif class.file == "DEMONHUNTER" then gain( 15, "fury" )
|
|
elseif class.file == "PRIEST" and state.spec.shadow then gain( 15, "insanity" ) end
|
|
|
|
removeBuff( "dispellable_magic" )
|
|
end,
|
|
|
|
copy = { 155145, 129597, 50613, 69179, 25046, 80483, 202719, 232633 }
|
|
},
|
|
|
|
will_to_survive = {
|
|
id = 59752,
|
|
cast = 0,
|
|
cooldown = 180,
|
|
gcd = "off",
|
|
|
|
toggle = "defensives",
|
|
},
|
|
|
|
shadowmeld = {
|
|
id = 58984,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
usable = function ()
|
|
if not boss or solo then return false, "requires boss fight or group (to avoid resetting)" end
|
|
if moving then return false, "can't shadowmeld while moving" end
|
|
return true
|
|
end,
|
|
|
|
handler = function ()
|
|
applyBuff( "shadowmeld" )
|
|
end,
|
|
},
|
|
|
|
|
|
lights_judgment = {
|
|
id = 255647,
|
|
cast = 0,
|
|
cooldown = 150,
|
|
gcd = "spell",
|
|
|
|
-- usable = function () return race.lightforged_draenei end,
|
|
|
|
toggle = "cooldowns",
|
|
},
|
|
|
|
|
|
stoneform = {
|
|
id = 20594,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
toggle = "defensives",
|
|
|
|
buff = function()
|
|
local aura, remains = "dispellable_poison", buff.dispellable_poison.remains
|
|
|
|
for _, effect in pairs( { "dispellable_disease", "dispellable_curse", "dispellable_magic", "dispellable_bleed" } ) do
|
|
local rem = buff[ effect ].remains
|
|
if rem > remains then
|
|
aura = effect
|
|
remains = rem
|
|
end
|
|
end
|
|
|
|
return aura
|
|
end,
|
|
|
|
handler = function ()
|
|
removeBuff( "dispellable_poison" )
|
|
removeBuff( "dispellable_disease" )
|
|
removeBuff( "dispellable_curse" )
|
|
removeBuff( "dispellable_magic" )
|
|
removeBuff( "dispellable_bleed" )
|
|
|
|
applyBuff( "stoneform" )
|
|
end,
|
|
|
|
auras = {
|
|
stoneform = {
|
|
id = 65116,
|
|
duration = 8,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
fireblood = {
|
|
id = 265221,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
-- usable = function () return race.dark_iron_dwarf end,
|
|
handler = function () applyBuff( "fireblood" ) end,
|
|
},
|
|
|
|
|
|
-- INTERNAL HANDLERS
|
|
call_action_list = {
|
|
name = "|cff00ccff[Call Action List]|r",
|
|
listName = '|T136243:0|t |cff00ccff[Call Action List]|r',
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "off",
|
|
essential = true,
|
|
},
|
|
|
|
run_action_list = {
|
|
name = "|cff00ccff[Run Action List]|r",
|
|
listName = '|T136243:0|t |cff00ccff[Run Action List]|r',
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "off",
|
|
essential = true,
|
|
},
|
|
|
|
wait = {
|
|
name = "|cff00ccff[Wait]|r",
|
|
listName = '|T136243:0|t |cff00ccff[Wait]|r',
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "off",
|
|
essential = true,
|
|
},
|
|
|
|
pool_resource = {
|
|
name = "|cff00ccff[Pool Resource]|r",
|
|
listName = "|T136243:0|t |cff00ccff[Pool Resource]|r",
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "off",
|
|
},
|
|
|
|
cancel_action = {
|
|
name = "|cff00ccff[Cancel Action]|r",
|
|
listName = "|T136243:0|t |cff00ccff[Cancel Action]|r",
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "off",
|
|
|
|
usable = function ()
|
|
local a = args.action_name
|
|
local ability = class.abilities[ a ]
|
|
if not a or not ability then return false, "no action identified" end
|
|
if buff.casting.down or buff.casting.v3 ~= 1 then return false, "not channeling" end
|
|
if buff.casting.v1 ~= ability.id then return false, "not channeling " .. a end
|
|
return true
|
|
end,
|
|
timeToReady = function () return gcd.remains end,
|
|
},
|
|
|
|
variable = {
|
|
name = "|cff00ccff[Variable]|r",
|
|
listName = '|T136243:0|t |cff00ccff[Variable]|r',
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "off",
|
|
essential = true,
|
|
},
|
|
|
|
healthstone = {
|
|
name = function () return ( GetItemInfo( 5512 ) ) or "Healthstone" end,
|
|
listName = function ()
|
|
local _, link, _, _, _, _, _, _, _, tex = GetItemInfo( 5512 )
|
|
if link and tex then return "|T" .. tex .. ":0|t " .. link end
|
|
return "|cff00ccff[Healthstone]|r"
|
|
end,
|
|
cast = 0,
|
|
cooldown = function () return time > 0 and 3600 or 60 end,
|
|
gcd = "off",
|
|
|
|
item = 5512,
|
|
bagItem = true,
|
|
|
|
startsCombat = false,
|
|
texture = 538745,
|
|
|
|
usable = function ()
|
|
if GetItemCount( 5512 ) == 0 then return false, "requires healthstone in bags"
|
|
elseif not IsUsableItem( 5512 ) then return false, "healthstone on CD"
|
|
elseif health.current >= health.max then return false, "must be damaged" end
|
|
return true
|
|
end,
|
|
|
|
readyTime = function ()
|
|
local start, duration = GetItemCooldown( 5512 )
|
|
return max( 0, start + duration - query_time )
|
|
end,
|
|
|
|
handler = function ()
|
|
gain( 0.25 * health.max, "health" )
|
|
end,
|
|
},
|
|
|
|
weyrnstone = {
|
|
name = function () return ( GetItemInfo( 205146 ) ) or "Weyrnstone" end,
|
|
listName = function ()
|
|
local _, link, _, _, _, _, _, _, _, tex = GetItemInfo( 205146 )
|
|
if link and tex then return "|T" .. tex .. ":0|t " .. link end
|
|
return "|cff00ccff[Weyrnstone]|r"
|
|
end,
|
|
cast = 1.5,
|
|
cooldown = 120,
|
|
gcd = "spell",
|
|
|
|
item = 205146,
|
|
bagItem = true,
|
|
|
|
startsCombat = false,
|
|
texture = 5199618,
|
|
|
|
usable = function ()
|
|
if GetItemCount( 205146 ) == 0 then return false, "requires weyrnstone in bags" end
|
|
if solo then return false, "must have an ally to teleport" end
|
|
return true
|
|
end,
|
|
|
|
readyTime = function ()
|
|
local start, duration = GetItemCooldown( 205146 )
|
|
return max( 0, start + duration - query_time )
|
|
end,
|
|
|
|
handler = function ()
|
|
end,
|
|
|
|
copy = { "use_weyrnstone", "active_weyrnstone" }
|
|
},
|
|
|
|
cancel_buff = {
|
|
name = "|cff00ccff[Cancel Buff]|r",
|
|
listName = '|T136243:0|t |cff00ccff[Cancel Buff]|r',
|
|
cast = 0,
|
|
gcd = "off",
|
|
|
|
startsCombat = false,
|
|
|
|
buff = function () return args.buff_name or nil end,
|
|
|
|
indicator = "cancel",
|
|
texture = function ()
|
|
if not args.buff_name then return 134400 end
|
|
|
|
local a = class.auras[ args.buff_name ]
|
|
-- if not a then return 134400 end
|
|
if a.texture then return a.texture end
|
|
|
|
a = a and a.id
|
|
a = a and GetSpellTexture( a )
|
|
|
|
return a or 134400
|
|
end,
|
|
|
|
usable = function () return args.buff_name ~= nil, "no buff name detected" end,
|
|
timeToReady = function () return gcd.remains end,
|
|
handler = function ()
|
|
if not args.buff_name then return end
|
|
|
|
local cancel = args.buff_name and buff[ args.buff_name ]
|
|
cancel = cancel and rawget( cancel, "onCancel" )
|
|
|
|
if cancel then
|
|
cancel()
|
|
return
|
|
end
|
|
|
|
removeBuff( args.buff_name )
|
|
end,
|
|
},
|
|
|
|
null_cooldown = {
|
|
name = "|cff00ccff[Null Cooldown]|r",
|
|
listName = "|T136243:0|t |cff00ccff[Null Cooldown]|r",
|
|
cast = 0,
|
|
cooldown = 0.001,
|
|
gcd = "off",
|
|
|
|
startsCombat = false,
|
|
|
|
unlisted = true
|
|
},
|
|
|
|
trinket1 = {
|
|
name = "|cff00ccff[Trinket #1]|r",
|
|
listName = "|T136243:0|t |cff00ccff[Trinket #1]|r",
|
|
cast = 0,
|
|
cooldown = 600,
|
|
gcd = "off",
|
|
|
|
usable = false,
|
|
|
|
copy = "actual_trinket1",
|
|
},
|
|
|
|
trinket2 = {
|
|
name = "|cff00ccff[Trinket #2]|r",
|
|
listName = "|T136243:0|t |cff00ccff[Trinket #2]|r",
|
|
cast = 0,
|
|
cooldown = 600,
|
|
gcd = "off",
|
|
|
|
usable = false,
|
|
|
|
copy = "actual_trinket2",
|
|
},
|
|
|
|
main_hand = {
|
|
name = "|cff00ccff[" .. INVTYPE_WEAPONMAINHAND .. "]|r",
|
|
listName = "|T136243:0|t |cff00ccff[" .. INVTYPE_WEAPONMAINHAND .. "]|r",
|
|
cast = 0,
|
|
cooldown = 600,
|
|
gcd = "off",
|
|
|
|
usable = false,
|
|
|
|
copy = "actual_main_hand",
|
|
}
|
|
} )
|
|
|
|
|
|
-- Use Items
|
|
do
|
|
-- Should handle trinkets/items internally.
|
|
-- 1. Check APLs and don't try to recommend items that have their own APL entries.
|
|
-- 2. Respect item preferences registered in spec options.
|
|
|
|
all:RegisterAbility( "use_items", {
|
|
name = "Use Items",
|
|
listName = "|T136243:0|t |cff00ccff[Use Items]|r",
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
} )
|
|
|
|
all:RegisterAbility( "unusable_trinket", {
|
|
name = "Unusable Trinket",
|
|
listName = "|T136240:0|t |cff00ccff[Unusable Trinket]|r",
|
|
cast = 0,
|
|
cooldown = 180,
|
|
gcd = "off",
|
|
|
|
usable = false,
|
|
unlisted = true
|
|
} )
|
|
|
|
all:RegisterAbility( "heart_essence", {
|
|
name = function () return ( GetItemInfo( 158075 ) ) or "Heart Essence" end,
|
|
listName = function ()
|
|
local _, link, _, _, _, _, _, _, _, tex = GetItemInfo( 158075 )
|
|
if link and tex then return "|T" .. tex .. ":0|t " .. link end
|
|
return "|cff00ccff[Heart Essence]|r"
|
|
end,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "off",
|
|
|
|
item = 158075,
|
|
essence = true,
|
|
|
|
toggle = "essences",
|
|
|
|
usable = function () return false, "your equipped major essence is supported elsewhere in the priority or is not an active ability" end
|
|
} )
|
|
end
|
|
|
|
|
|
-- x.x - Heirloom Trinket(s)
|
|
all:RegisterAbility( "touch_of_the_void", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 128318,
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
-- 8.3 - WORLD
|
|
-- Corruption Curse that impacts resource costs.
|
|
|
|
all:RegisterAura( "hysteria", {
|
|
id = 312677,
|
|
duration = 30,
|
|
max_stack = 99
|
|
} )
|
|
|
|
|
|
-- BFA TRINKETS
|
|
-- EQUIPPED EFFECTS
|
|
all:RegisterAuras( {
|
|
-- Darkmoon Deck: Squalls
|
|
suffocating_squall = { id = 276132, duration = 26, max_stack = 1 }, -- I made up max duration (assume 13 card types and 2s per card).
|
|
|
|
-- Construct Overcharger
|
|
titanic_overcharge = { id = 278070, duration = 10, max_stack = 8 },
|
|
|
|
-- Xalzaix's Veiled Eye
|
|
xalzaixs_gaze = { id = 278158, duration = 20, max_stack = 1 },
|
|
|
|
-- Syringe of Bloodborne Infirmity
|
|
wasting_infection = { id = 278110, duration = 12, max_stack = 1 },
|
|
critical_prowess = { id = 278109, duration = 6, max_stack = 5 },
|
|
|
|
-- Frenetic Corpuscle
|
|
frothing_rage = { id = 278140, duration = 45, max_stack = 4 },
|
|
|
|
-- Tear of the Void
|
|
voidspark = { id = 278831, duration = 14, max_stack = 1 },
|
|
|
|
-- Prism of Dark Intensity
|
|
dark_intensity = { id = 278378, duration = 18, max_stack = 6,
|
|
meta = {
|
|
-- Stacks every 3 seconds until expiration; should generalize this kind of thing...
|
|
stacks = function ( aura )
|
|
if aura.up then return 1 + floor( ( query_time - aura.applied ) / 3 ) end
|
|
return 0
|
|
end
|
|
}
|
|
},
|
|
|
|
-- Plume of the Seaborne Avian
|
|
seaborne_tempest = { id = 278382, duration = 10, max_stack = 1 },
|
|
|
|
-- Drust-Runed Icicle
|
|
chill_of_the_runes = { id = 278862, duration = 12, max_stack = 1 },
|
|
|
|
-- Permafrost-Encrusted Heart
|
|
coldhearted_instincts = { id = 278388, duration = 15, max_stack = 5, copy = "cold_hearted_instincts",
|
|
meta = {
|
|
-- Stacks every 3 seconds until expiration; should generalize this kind of thing...
|
|
stacks = function ( aura )
|
|
if aura.up then return 1 + floor( ( query_time - aura.applied ) / 3 ) end
|
|
return 0
|
|
end
|
|
}
|
|
},
|
|
|
|
-- Spiritbound Voodoo Burl
|
|
coalesced_essence = { id = 278224, duration = 12, max_stack = 1 },
|
|
|
|
-- Wing Bone of the Budding Tempest
|
|
avian_tempest = { id = 278253, duration = 10, max_stack = 5 },
|
|
|
|
-- Razorcrest of the Enraged Matriarch
|
|
winged_tempest = { id = 278248, duration = 16, max_stack = 1 },
|
|
|
|
-- Hurricane Heart
|
|
hurricane_within = { id = 161416, duration = 12, max_stack = 6,
|
|
meta = {
|
|
-- Stacks every 2 seconds until expiration; should generalize this kind of thing...
|
|
stacks = function ( aura )
|
|
if aura.up then return 1 + floor( ( query_time - aura.applied ) / 2 ) end
|
|
return 0
|
|
end
|
|
}
|
|
},
|
|
|
|
-- Kraulok's Claw
|
|
krauloks_strength = { id = 278287, duration = 10, max_stack = 1 },
|
|
|
|
-- Doom's Hatred
|
|
blood_hatred = { id = 278356, duration = 10, max_stack = 1 },
|
|
|
|
-- Lion's Grace
|
|
lions_grace = { id = 278815, duration = 10, max_stack = 1 },
|
|
|
|
-- Landoi's Scrutiny
|
|
landois_scrutiny = { id = 281544, duration = 15, max_stack = 1 },
|
|
|
|
-- Leyshock's Grand Compilation
|
|
precision_module = { id = 281791, duration = 15, max_stack = 3 }, -- Crit.
|
|
iteration_capacitor = { id = 281792, duration = 15, max_stack = 3 }, -- Haste.
|
|
efficiency_widget = { id = 281794, duration = 15, max_stack = 3 }, -- Mastery.
|
|
adaptive_circuit = { id = 281795, duration = 15, max_stack = 3 }, -- Versatility.
|
|
leyshocks_grand_compilation = {
|
|
alias = { "precision_module", "iteration_capacitor", "efficiency_widget", "adaptive_circuit" },
|
|
aliasMode = "longest",
|
|
aliasType = "buff",
|
|
duration = 15,
|
|
},
|
|
|
|
-- Twitching Tentacle of Xalzaix
|
|
lingering_power_of_xalzaix = { id = 278155, duration = 30, max_stack = 5 },
|
|
uncontained_power = { id = 278156, duration = 12, max_stack = 1 },
|
|
|
|
-- Surging Alchemist Stone
|
|
-- I believe these buffs are recycled a lot...
|
|
agility = { id = 60233, duration = 15, max_stack = 1 },
|
|
intellect = { id = 60234, duration = 15, max_stack = 1 },
|
|
strength = { id = 60229, duration = 15, max_stack = 1 },
|
|
|
|
-- Harlan's Loaded Dice
|
|
loaded_die_mastery = { id = 267325, duration = 15, max_stack = 1 },
|
|
loaded_die_haste = { id = 267327, duration = 15, max_stack = 1 },
|
|
loaded_die_critical_strike = { id = 267331, duration = 15, max_stack = 1 },
|
|
loaded_die = {
|
|
alias = { "loaded_die_mastery", "loaded_die_haste", "loaded_die_critical_strike" },
|
|
aliasMode = "longest",
|
|
aliasType = "buff",
|
|
duration = 15,
|
|
},
|
|
|
|
-- Tiny Electromental in a Jar
|
|
phenomenal_power = { id = 267179, duration = 30, max_stack = 12 },
|
|
|
|
-- Rezan's Gleaming Eye
|
|
rezans_gleaming_eye = { id = 271103, duration = 15, max_stack = 1 },
|
|
|
|
-- Azerokk's Resonating Heart
|
|
resonating_elemental_heart = { id = 268441, duration = 15, max_stack = 1 },
|
|
|
|
-- Gore-Crusted Butcher's Block
|
|
butchers_eye = { id = 271104, duration = 15, max_stack = 1 },
|
|
|
|
-- Briny Barnacle
|
|
choking_brine = { id = 268194, duration = 6, max_stack = 1 },
|
|
|
|
-- Conch of Dark Whispers
|
|
conch_of_dark_whispers = { id = 271071, duration = 15, max_stack = 1 },
|
|
|
|
-- Dead Eye Spyglass
|
|
dead_ahead = { id = 268756, duration = 10, max_stack = 1 },
|
|
dead_ahead_crit = { id = 268769, duration = 10, max_stack = 5 },
|
|
|
|
-- Lingering Sporepods
|
|
lingering_spore_pods = { id = 268062, duration = 4, max_stack = 1 },
|
|
|
|
} )
|
|
|
|
|
|
-- BFA TRINKETS/ITEMS
|
|
-- Ny'alotha
|
|
|
|
all:RegisterAbility( "manifesto_of_madness", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 174103,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "manifesto_of_madness_chapter_one" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAuras( {
|
|
manifesto_of_madness_chapter_one = {
|
|
id = 313948,
|
|
duration = 10,
|
|
max_stack = 1
|
|
},
|
|
|
|
manifesto_of_madness_chapter_two = {
|
|
id = 314040,
|
|
duration = 10,
|
|
max_stack = 1
|
|
}
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "forbidden_obsidian_claw", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 173944,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "obsidian_claw" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "obsidian_claw", {
|
|
id = 313148,
|
|
duration = 8.5,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "sigil_of_warding", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 173940,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "stoneskin", 8 )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "stoneskin", {
|
|
id = 313060,
|
|
duration = 16,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "writhing_segment_of_drestagath", {
|
|
cast = 0,
|
|
cooldown = 80,
|
|
gcd = "off",
|
|
|
|
item = 173946,
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "lingering_psychic_shell", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 174277,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "psychic_shell", {
|
|
id = 314585,
|
|
duration = 8,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
|
|
|
|
-- Azshara's EP
|
|
all:RegisterAbility( "orgozoas_paralytic_barb", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 168899,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "paralytic_spines" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "paralytic_spines", {
|
|
id = 303350,
|
|
duration = 15,
|
|
max_stack = 1
|
|
} )
|
|
|
|
all:RegisterAbility( "azsharas_font_of_power", {
|
|
cast = 4,
|
|
channeled = true,
|
|
cooldown = 120,
|
|
gcd = "spell",
|
|
|
|
item = 169314,
|
|
toggle = "cooldowns",
|
|
|
|
start = function ()
|
|
applyBuff( "latent_arcana_channel" )
|
|
end,
|
|
|
|
breakchannel = function ()
|
|
removeBuff( "latent_arcana_channel" )
|
|
applyBuff( "latent_arcana" )
|
|
end,
|
|
|
|
finish = function ()
|
|
removeBuff( "latent_arcana_channel" )
|
|
applyBuff( "latent_arcana" )
|
|
end,
|
|
|
|
copy = { "latent_arcana" }
|
|
} )
|
|
|
|
all:RegisterAuras( {
|
|
latent_arcana = {
|
|
id = 296962,
|
|
duration = 30,
|
|
max_stack = 5
|
|
},
|
|
|
|
latent_arcana_channel = {
|
|
id = 296971,
|
|
duration = 4,
|
|
max_stack = 1
|
|
}
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "shiver_venom_relic", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "spell",
|
|
|
|
item = 168905,
|
|
toggle = "cooldowns",
|
|
|
|
usable = function ()
|
|
if debuff.shiver_venom.stack < 5 then return false, "shiver_venom is not at max stacks" end
|
|
return true
|
|
end,
|
|
|
|
aura = "shiver_venom",
|
|
cycle = "shiver_venom",
|
|
|
|
handler = function()
|
|
removeDebuff( "target", "shiver_venom" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "shiver_venom", {
|
|
id = 301624,
|
|
duration = 20,
|
|
max_stack = 5
|
|
} )
|
|
|
|
|
|
do
|
|
-- local coralGUID, coralApplied, coralStacks = "none", 0, 0
|
|
|
|
-- Ashvane's Razor Coral, 169311
|
|
all:RegisterAbility( "ashvanes_razor_coral", {
|
|
cast = 0,
|
|
cooldown = 20,
|
|
gcd = "off",
|
|
|
|
item = 169311,
|
|
toggle = "cooldowns",
|
|
|
|
--[[ usable = function ()
|
|
if active_dot.razor_coral > 0 and target.unit ~= coralGUID then
|
|
return false, "current target does not have razor_coral applied"
|
|
end
|
|
return true
|
|
end, ]]
|
|
|
|
handler = function ()
|
|
if active_dot.razor_coral > 0 then
|
|
removeDebuff( "target", "razor_coral" )
|
|
active_dot.razor_coral = 0
|
|
|
|
applyBuff( "razor_coral_crit" )
|
|
setCooldown( "ashvanes_razor_coral", 20 )
|
|
else
|
|
applyDebuff( "target", "razor_coral" )
|
|
end
|
|
end
|
|
} )
|
|
|
|
|
|
--[[
|
|
local HandleRazorCoral = function( event )
|
|
if not state.equipped.ashvanes_razor_coral then return end
|
|
|
|
if event == "COMBAT_LOG_EVENT_UNFILTERED" then
|
|
local _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName = CombatLogGetCurrentEventInfo()
|
|
|
|
if sourceGUID == state.GUID and ( subtype == "SPELL_AURA_APPLIED" or subtype == "SPELL_AURA_REFRESH" or subtype == "SPELL_AURA_APPLIED_DOSE" ) then
|
|
if spellID == 303568 and destGUID then
|
|
coralGUID = destGUID
|
|
coralApplied = GetTime()
|
|
coralStacks = ( subtype == "SPELL_AURA_APPLIED_DOSE" ) and ( coralStacks + 1 ) or 1
|
|
elseif spellID == 303570 then
|
|
-- Coral was removed.
|
|
coralGUID = "none"
|
|
coralApplied = 0
|
|
coralStacks = 0
|
|
end
|
|
end
|
|
else
|
|
coralGUID = "none"
|
|
coralApplied = 0
|
|
coralStacks = 0
|
|
end
|
|
end
|
|
|
|
RegisterEvent( "COMBAT_LOG_EVENT_UNFILTERED", HandleRazorCoral )
|
|
RegisterEvent( "PLAYER_REGEN_ENABLED", HandleRazorCoral )
|
|
|
|
all:RegisterStateExpr( "coral_time_to_30", function()
|
|
if coralGUID == 0 then return 3600 end
|
|
return Hekili:GetTimeToPctByGUID( coralGUID, 30 ) - ( offset + delay )
|
|
end ) ]]
|
|
|
|
all:RegisterAuras( {
|
|
razor_coral = {
|
|
id = 303568,
|
|
duration = 120,
|
|
max_stack = 100, -- ???
|
|
copy = "razor_coral_debuff",
|
|
generate = function( t, auraType )
|
|
local name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = FindUnitDebuffByID( "target", 303568, "PLAYER" )
|
|
|
|
if name then
|
|
-- It's on our actual target, trust it.
|
|
t.name = name
|
|
t.count = count > 0 and count or 1
|
|
t.expires = expirationTime
|
|
t.applied = expirationTime - duration
|
|
t.caster = "player"
|
|
return
|
|
|
|
--[[ elseif coralGUID ~= "none" then
|
|
t.name = class.auras.razor_coral.name
|
|
t.count = coralStacks > 0 and coralStacks or 1
|
|
t.applied = coralApplied > 0 and coralApplied or state.query_time
|
|
t.expires = coralApplied > 0 and ( coralApplied + 120 ) or ( state.query_time + Hekili:GetDeathClockByGUID( coralGUID ) )
|
|
t.caster = "player"
|
|
|
|
return ]]
|
|
end
|
|
|
|
t.name = class.auras.razor_coral.name
|
|
t.count = 0
|
|
t.applied = 0
|
|
t.expires = 0
|
|
|
|
t.caster = "nobody"
|
|
end,
|
|
},
|
|
|
|
razor_coral_crit = {
|
|
id = 303570,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
}
|
|
} )
|
|
end
|
|
|
|
-- Dribbling Inkpod
|
|
all:RegisterAura( "conductive_ink", {
|
|
id = 302565,
|
|
duration = 60,
|
|
max_stack = 999, -- ???
|
|
copy = "conductive_ink_debuff"
|
|
} )
|
|
|
|
|
|
-- Edicts of the Faithless, 169315
|
|
|
|
-- Vision of Demise, 169307
|
|
all:RegisterAbility( "vision_of_demise", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 169307,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "vision_of_demise" )
|
|
end
|
|
} )
|
|
|
|
all:RegisterAura( "vision_of_demise", {
|
|
id = 303431,
|
|
duration = 10,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Aquipotent Nautilus, 169305
|
|
all:RegisterAbility( "aquipotent_nautilus", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 169305,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "surging_flood" )
|
|
end
|
|
} )
|
|
|
|
all:RegisterAura( "surging_flood", {
|
|
id = 302580,
|
|
duration = 4,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Chain of Suffering, 169308
|
|
all:RegisterAbility( "chain_of_suffering", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 169308,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "chain_of_suffering" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "chain_of_suffering", {
|
|
id = 297036,
|
|
duration = 25,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Mechagon
|
|
do
|
|
all:RegisterGear( "pocketsized_computation_device", 167555 )
|
|
all:RegisterGear( "cyclotronic_blast", 167672 )
|
|
all:RegisterGear( "harmonic_dematerializer", 167677 )
|
|
|
|
all:RegisterAura( "cyclotronic_blast", {
|
|
id = 293491,
|
|
duration = function () return 2.5 * haste end,
|
|
max_stack = 1
|
|
} )
|
|
|
|
--[[ all:RegisterAbility( "pocketsized_computation_device", {
|
|
-- key = "pocketsized_computation_device",
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "spell",
|
|
|
|
-- item = 167555,
|
|
texture = 2115322,
|
|
bind = { "cyclotronic_blast", "harmonic_dematerializer", "inactive_red_punchcard" },
|
|
startsCombat = true,
|
|
|
|
unlisted = true,
|
|
|
|
usable = function() return false, "no supported red punchcard installed" end,
|
|
copy = "inactive_red_punchcard"
|
|
} ) ]]
|
|
|
|
all:RegisterAbility( "cyclotronic_blast", {
|
|
id = 293491,
|
|
known = function () return equipped.cyclotronic_blast end,
|
|
cast = function () return 1.5 * haste end,
|
|
channeled = function () return cooldown.cyclotronic_blast.remains > 0 end,
|
|
cooldown = function () return equipped.cyclotronic_blast and 120 or 0 end,
|
|
gcd = "spell",
|
|
|
|
item = 167672,
|
|
itemCd = 167555,
|
|
itemKey = "cyclotronic_blast",
|
|
|
|
texture = 2115322,
|
|
bind = { "pocketsized_computation_device", "inactive_red_punchcard", "harmonic_dematerializer" },
|
|
startsCombat = true,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
usable = function ()
|
|
return equipped.cyclotronic_blast, "punchcard not equipped"
|
|
end,
|
|
|
|
handler = function()
|
|
setCooldown( "global_cooldown", 2.5 * haste )
|
|
applyBuff( "casting", 2.5 * haste )
|
|
end,
|
|
|
|
copy = "pocketsized_computation_device"
|
|
} )
|
|
|
|
all:RegisterAura( "harmonic_dematerializer", {
|
|
id = 293512,
|
|
duration = 300,
|
|
max_stack = 99
|
|
} )
|
|
|
|
all:RegisterAbility( "harmonic_dematerializer", {
|
|
id = 293512,
|
|
known = function () return equipped.harmonic_dematerializer end,
|
|
cast = 0,
|
|
cooldown = 15,
|
|
gcd = "spell",
|
|
|
|
item = 167677,
|
|
itemCd = 167555,
|
|
itemKey = "harmonic_dematerializer",
|
|
|
|
texture = 2115322,
|
|
|
|
bind = { "pocketsized_computation_device", "cyclotronic_blast", "inactive_red_punchcard" },
|
|
|
|
startsCombat = true,
|
|
|
|
usable = function ()
|
|
return equipped.harmonic_dematerializer, "punchcard not equipped"
|
|
end,
|
|
|
|
handler = function ()
|
|
addStack( "harmonic_dematerializer", nil, 1 )
|
|
end
|
|
} )
|
|
|
|
|
|
-- Hyperthread Wristwraps
|
|
all:RegisterAbility( "hyperthread_wristwraps", {
|
|
id = 300142,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
handler = function ()
|
|
-- Gain 5 seconds of CD for the last 3 spells.
|
|
for i = 1, 3 do
|
|
local ability = prev[i].spell
|
|
|
|
if ability and ability ~= "no_action" then
|
|
gainChargeTime( ability, 5 )
|
|
end
|
|
end
|
|
end,
|
|
|
|
copy = "hyperthread_wristwraps_300142"
|
|
} )
|
|
|
|
local hyperthread_blocks = {
|
|
berserking = true,
|
|
arcane_torrent = true,
|
|
blood_fury = true,
|
|
shadowmeld = true,
|
|
stoneform = true,
|
|
darkflight = true,
|
|
rocket_barrage = true,
|
|
lights_judgment = true,
|
|
arcane_pulse = true,
|
|
fireblood = true,
|
|
ancestral_call = true,
|
|
haymaker = true,
|
|
bag_of_tricks = true
|
|
}
|
|
|
|
|
|
local first_remains = setmetatable( {}, {
|
|
__index = function( t, k )
|
|
if hyperthread_blocks[ k ] then return 0 end
|
|
|
|
local slot = 0
|
|
local counted = 0
|
|
for i = 1, 10 do
|
|
if not hyperthread_blocks[ state.prev[ i ].spell ] then
|
|
counted = counted + 1
|
|
end
|
|
|
|
if counted == 4 then break end
|
|
|
|
if state.prev[ i ].spell == k then
|
|
slot = i
|
|
end
|
|
end
|
|
|
|
if slot > 0 then return 3 - slot end
|
|
return 0
|
|
end
|
|
} )
|
|
|
|
all:RegisterStateTable( "hyperthread_wristwraps", setmetatable( {
|
|
}, {
|
|
__index = function( t, k )
|
|
if type( k ) == "string" then
|
|
if k == "first_remains" then return first_remains end
|
|
if k == "count" then return state.hyperthread_wristwraps end
|
|
|
|
if hyperthread_blocks[ k ] then return 0 end
|
|
|
|
local count = 0
|
|
local counted = 0
|
|
for i = 1, 10 do
|
|
if not hyperthread_blocks[ state.sprev[ i ].spell ] then
|
|
counted = counted + 1
|
|
end
|
|
|
|
if counted == 4 then break end
|
|
|
|
if state.prev[ i ].spell == k then
|
|
count = count + 1
|
|
end
|
|
end
|
|
|
|
return count
|
|
end
|
|
end,
|
|
} ) )
|
|
|
|
|
|
all:RegisterAbility( "neural_synapse_enhancer", {
|
|
cast = 0,
|
|
cooldown = 45,
|
|
gcd = "off",
|
|
|
|
item = 168973,
|
|
|
|
handler = function ()
|
|
applyBuff( "enhance_synapses" )
|
|
end,
|
|
|
|
copy = "enhance_synapses_300612"
|
|
} )
|
|
|
|
all:RegisterAura( "enhance_synapses", {
|
|
id = 300612,
|
|
duration = 15,
|
|
max_stack = 1
|
|
} )
|
|
|
|
all:RegisterAbility( "wraps_of_electrostatic_potential", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 169069,
|
|
|
|
handler = function()
|
|
applyDebuff( "target", "electrostatic_induction" )
|
|
end,
|
|
|
|
auras = {
|
|
electrostatic_induction = {
|
|
id = 300145,
|
|
duration = 8,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
} )
|
|
end
|
|
|
|
|
|
-- Shockbiter's Fang
|
|
all:RegisterAbility( "shockbiters_fang", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 169318,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function () applyBuff( "shockbitten" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "shockbitten", {
|
|
id = 303953,
|
|
duration = 12,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "living_oil_canister", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 158216,
|
|
|
|
copy = "living_oil_cannister"
|
|
} )
|
|
|
|
|
|
-- Remote Guidance Device, 169769
|
|
all:RegisterAbility( "remote_guidance_device", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 169769,
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
-- Modular Platinum Plating, 168965
|
|
all:RegisterAbility( "modular_platinum_plating", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 168965,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "platinum_plating", nil, 4 )
|
|
end
|
|
} )
|
|
|
|
all:RegisterAura( "platinum_plating", {
|
|
id = 299869,
|
|
duration = 30,
|
|
max_stack = 4
|
|
} )
|
|
|
|
|
|
-- Crucible
|
|
all:RegisterAbility( "pillar_of_the_drowned_cabal", {
|
|
cast = 0,
|
|
cooldown = 30,
|
|
gcd = "spell", -- ???
|
|
|
|
item = 167863,
|
|
toggle = "defensives", -- ???
|
|
|
|
handler = function () applyBuff( "mariners_ward" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "mariners_ward", {
|
|
id = 295411,
|
|
duration = 90,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
-- Abyssal Speaker's Guantlets (PROC)
|
|
all:RegisterAura( "ephemeral_vigor", {
|
|
id = 295431,
|
|
duration = 60,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Fathom Dredgers (PROC)
|
|
all:RegisterAura( "dredged_vitality", {
|
|
id = 295134,
|
|
duration = 8,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Gloves of the Undying Pact
|
|
all:RegisterAbility( "gloves_of_the_undying_pact", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 167219,
|
|
toggle = "defensives", -- ???
|
|
|
|
handler = function() applyBuff( "undying_pact" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "undying_pact", {
|
|
id = 295193,
|
|
duration = 6,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Insurgent's Scouring Chain (PROC)
|
|
all:RegisterAura( "scouring_wake", {
|
|
id = 295141,
|
|
duration = 20,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Mindthief's Eldritch Clasp (PROC)
|
|
all:RegisterAura( "phantom_pain", {
|
|
id = 295527,
|
|
duration = 180,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
-- Leggings of the Aberrant Tidesage
|
|
-- HoT spell ID not found.
|
|
|
|
-- Zaxasj's Deepstriders (EFFECT)
|
|
all:RegisterAura( "deepstrider", {
|
|
id = 295167,
|
|
duration = 3600,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Trident of Deep Ocean
|
|
-- Custody of the Deep (shield proc)
|
|
all:RegisterAura( "custody_of_the_deep_shield", {
|
|
id = 292675,
|
|
duration = 40,
|
|
max_stack = 1
|
|
} )
|
|
-- Custody of the Deep (mainstat proc)
|
|
all:RegisterAura( "custody_of_the_deep_buff", {
|
|
id = 292653,
|
|
duration = 60,
|
|
max_stack = 3
|
|
} )
|
|
|
|
|
|
-- Malformed Herald's Legwraps
|
|
all:RegisterAbility( "malformed_heralds_legwraps", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 167835,
|
|
toggle = "cooldowns",
|
|
|
|
usable = function () return buff.movement.down end,
|
|
handler = function () applyBuff( "void_embrace" ) end,
|
|
} )
|
|
|
|
all:RegisterAura( "void_embrace", {
|
|
id = 295174,
|
|
duration = 12,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
-- Idol of Indiscriminate Consumption
|
|
all:RegisterAbility( "idol_of_indiscriminate_consumption", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 167868,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function() gain( 2.5 * 7000 * active_enemies, "health" ) end,
|
|
} )
|
|
|
|
|
|
-- Lurker's Insidious Gift
|
|
all:RegisterAbility( "lurkers_insidious_gift", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 167866,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "insidious_gift" )
|
|
applyDebuff( "suffering" )
|
|
end
|
|
} )
|
|
|
|
all:RegisterAura( "insidious_gift", {
|
|
id = 295408,
|
|
duration = 30,
|
|
max_stack = 1
|
|
} )
|
|
all:RegisterAura( "suffering", {
|
|
id = 295413,
|
|
duration = 30,
|
|
max_stack = 30,
|
|
meta = {
|
|
stack = function ()
|
|
return buff.insidious_gift.up and floor( 30 - buff.insidious_gift.remains ) or 0
|
|
end
|
|
}
|
|
} )
|
|
|
|
|
|
-- Void Stone
|
|
all:RegisterAbility( "void_stone", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 167865,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "umbral_shell" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "umbral_shell", {
|
|
id = 295271,
|
|
duration = 12,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- ON USE
|
|
-- Kezan Stamped Bijou
|
|
all:RegisterAbility( "kezan_stamped_bijou", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 165662,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function () applyBuff( "kajamite_surge" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "kajamite_surge", {
|
|
id = 285475,
|
|
duration = 12,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
-- Sea Giant's Tidestone
|
|
all:RegisterAbility( "sea_giants_tidestone", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 165664,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function () applyBuff( "ferocity_of_the_skrog" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "ferocity_of_the_skrog", {
|
|
id = 285482,
|
|
duration = 12,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Ritual Feather
|
|
all:RegisterAbility( "ritual_feather_of_unng_ak", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 165665,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function () applyBuff( "might_of_the_blackpaw" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "might_of_the_blackpaw", {
|
|
id = 285489,
|
|
duration = 16,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Battle of Dazar'alor
|
|
all:RegisterAbility( "invocation_of_yulon", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 165568,
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "ward_of_envelopment", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 165569,
|
|
toggle = "defensives",
|
|
|
|
handler = function() applyBuff( "enveloping_protection" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "enveloping_protection", {
|
|
id = 287568,
|
|
duration = 10,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Everchill Anchor debuff.
|
|
all:RegisterAura( "everchill", {
|
|
id = 289525,
|
|
duration = 12,
|
|
max_stack = 10
|
|
} )
|
|
|
|
|
|
-- Incandescent Sliver
|
|
all:RegisterAura( "incandescent_luster", {
|
|
id = 289523,
|
|
duration = 20,
|
|
max_stack = 10
|
|
} )
|
|
|
|
all:RegisterAura( "incandescent_mastery", {
|
|
id = 289524,
|
|
duration = 20,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
|
|
all:RegisterAbility( "variable_intensity_gigavolt_oscillating_reactor", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 165572,
|
|
toggle = "cooldowns",
|
|
|
|
buff = "vigor_engaged",
|
|
usable = function ()
|
|
if buff.vigor_engaged.stack < 6 then return false, "has fewer than 6 stacks" end
|
|
return true
|
|
end,
|
|
handler = function() applyBuff( "oscillating_overload" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "vigor_engaged", {
|
|
id = 287916,
|
|
duration = 3600,
|
|
max_stack = 6
|
|
-- May need to emulate the stacking portion.
|
|
} )
|
|
|
|
all:RegisterAura( "vigor_cooldown", {
|
|
id = 287967,
|
|
duration = 6,
|
|
max_stack = 1
|
|
} )
|
|
|
|
all:RegisterAura( "oscillating_overload", {
|
|
id = 287917,
|
|
duration = 6,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Diamond-Laced Refracting Prism
|
|
all:RegisterAura( "diamond_barrier", {
|
|
id = 288034,
|
|
duration = 10,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "grongs_primal_rage", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 165574,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function()
|
|
applyBuff( "primal_rage" )
|
|
setCooldown( "global_cooldown", 4 )
|
|
end
|
|
} )
|
|
|
|
all:RegisterAura( "primal_rage", {
|
|
id = 288267,
|
|
duration = 4,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "tidestorm_codex", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 165576,
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
-- Bwonsamdi's Bargain
|
|
all:RegisterAura( "bwonsamdis_due", {
|
|
id = 288193,
|
|
duration = 300,
|
|
max_stack = 1
|
|
} )
|
|
|
|
all:RegisterAura( "bwonsamdis_bargain_fulfilled", {
|
|
id = 288194,
|
|
duration = 360,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "mirror_of_entwined_fate", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 165578,
|
|
toggle = "defensives",
|
|
|
|
handler = function() applyDebuff( "player", "mirror_of_entwined_fate" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "mirror_of_entwined_fate", {
|
|
id = 287999,
|
|
duration = 30,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Kimbul's Razor Claw
|
|
all:RegisterAura( "kimbuls_razor_claw", {
|
|
id = 288330,
|
|
duration = 6,
|
|
tick_time = 2,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "ramping_amplitude_gigavolt_engine", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 165580,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function() applyBuff( "r_a_g_e" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "rage", {
|
|
id = 288156,
|
|
duration = 18,
|
|
max_stack = 15,
|
|
copy = "r_a_g_e"
|
|
} )
|
|
|
|
|
|
-- Crest of Pa'ku
|
|
all:RegisterAura( "gift_of_wind", {
|
|
id = 288304,
|
|
duration = 15,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "endless_tincture_of_fractional_power", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 152636,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
-- I don't know the auras it applies...
|
|
end
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "mercys_psalter", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 155564,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "potency" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "potency", {
|
|
id = 268523,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "clockwork_resharpener", {
|
|
cast = 0,
|
|
cooldown = 60, -- no CD reported in-game yet.
|
|
gcd = "off",
|
|
|
|
item = 161375,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "resharpened" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "resharpened", {
|
|
id = 278376,
|
|
duration = 14,
|
|
max_stack = 7,
|
|
meta = {
|
|
-- Stacks every 2 seconds until expiration; should generalize this kind of thing...
|
|
stacks = function ( aura )
|
|
if aura.up then return 1 + floor( ( query_time - aura.applied ) / 2 ) end
|
|
return 0
|
|
end
|
|
}
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "azurethos_singed_plumage", {
|
|
cast = 0,
|
|
cooldown = 88,
|
|
gcd = "off",
|
|
|
|
item = 161377,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "ruffling_tempest" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "ruffling_tempest", {
|
|
id = 278383,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
-- Actually decrements but doesn't appear to use stacks to implement itself.
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "galecallers_beak", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 161379,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "gale_call" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "gale_call", {
|
|
id = 278385,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "sublimating_iceshard", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 161382,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "sublimating_power" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "sublimating_power", {
|
|
id = 278869,
|
|
duration = 14,
|
|
max_stack = 1,
|
|
-- Decrements after 6 sec but doesn't appear to use stacks to convey this...
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "tzanes_barkspines", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 161411,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "barkspines" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "barkspines", {
|
|
id = 278227,
|
|
duration = 10,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
--[[ Redundant Ancient Knot of Wisdom???
|
|
all:RegisterAbility( "sandscoured_idol", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 161417,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "secrets_of_the_sands" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "secrets_of_the_sands", {
|
|
id = 278267,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
} ) ]]
|
|
|
|
|
|
all:RegisterAbility( "deployable_vibro_enhancer", {
|
|
cast = 0,
|
|
cooldown = 105,
|
|
gcd = "off",
|
|
|
|
item = 161418,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "vibro_enhanced" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "vibro_enhanced", {
|
|
id = 278260,
|
|
duration = 12,
|
|
max_stack = 4,
|
|
meta = {
|
|
-- Stacks every 2 seconds until expiration; should generalize this kind of thing...
|
|
stacks = function ( aura )
|
|
if aura.up then return 1 + floor( ( query_time - aura.applied ) / 3 ) end
|
|
return 0
|
|
end
|
|
}
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "dooms_wake", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 161462,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "dooms_wake" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "dooms_wake", {
|
|
id = 278317,
|
|
duration = 16,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "dooms_fury", {
|
|
cast = 0,
|
|
cooldown = 105,
|
|
gcd = "off",
|
|
|
|
item = 161463,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "bristling_fury" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "bristling_fury", {
|
|
id = 278364,
|
|
duration = 18,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "lions_guile", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 161473,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "lions_guile" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "lions_guile", {
|
|
id = 278806,
|
|
duration = 16,
|
|
max_stack = 10,
|
|
meta = {
|
|
stack = function( t ) return t.down and 0 or min( 6, 1 + ( ( query_time - t.app ) / 2 ) ) end,
|
|
}
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "lions_strength", {
|
|
cast = 0,
|
|
cooldown = 105,
|
|
gcd = "off",
|
|
|
|
item = 161474,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "lions_strength" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "lions_strength", {
|
|
id = 278819,
|
|
duration = 18,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
all:RegisterAbility( "mr_munchykins", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 155567,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "tea_time" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "tea_time", {
|
|
id = 268504,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
all:RegisterAbility( "bygone_bee_almanac", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 163936,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "process_improvement" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "process_improvement", {
|
|
id = 281543,
|
|
duration = 12,
|
|
max_stack = 1,
|
|
} ) -- extends on spending resources, could hook here...
|
|
|
|
|
|
all:RegisterAbility( "mydas_talisman", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 158319,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "touch_of_gold" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "touch_of_gold", {
|
|
id = 265954,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "merekthas_fang", {
|
|
cast = 3,
|
|
channeled = true,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 158367,
|
|
toggle = "cooldowns",
|
|
|
|
-- not sure if this debuffs during the channel...
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "razdunks_big_red_button", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 159611,
|
|
toggle = "cooldowns",
|
|
|
|
velocity = 10,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "galecallers_boon", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 159614,
|
|
toggle = "cooldowns",
|
|
|
|
usable = function () return buff.movement.down end,
|
|
handler = function ()
|
|
applyBuff( "galecallers_boon" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "galecallers_boon", {
|
|
id = 268311,
|
|
duration = 10,
|
|
max_stack = 1,
|
|
meta = {
|
|
expires = function( t ) return max( 0, action.galecallers_boon.lastCast + 10 ) end
|
|
}
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "ignition_mages_fuse", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 159615,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "ignition_mages_fuse" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "ignition_mages_fuse", {
|
|
id = 271115,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "lustrous_golden_plumage", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 159617,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "golden_luster" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "golden_luster", {
|
|
id = 271107,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "mchimbas_ritual_bandages", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 159618,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "ritual_wraps" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "ritual_wraps", {
|
|
id = 265946,
|
|
duration = 6,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "rotcrusted_voodoo_doll", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 159624,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "rotcrusted_voodoo_doll" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "rotcrusted_voodoo_doll", {
|
|
id = 271465,
|
|
duration = 6,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "vial_of_animated_blood", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 159625,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "blood_of_my_enemies" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "blood_of_my_enemies", {
|
|
id = 268836,
|
|
duration = 18,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "jes_howler", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 159627,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "motivating_howl" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "motivating_howl", {
|
|
id = 266047,
|
|
duration = 12,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "balefire_branch", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 159630,
|
|
toggle = "cooldowns",
|
|
proc = "intellect",
|
|
|
|
handler = function ()
|
|
applyBuff( "kindled_soul" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "kindled_soul", {
|
|
id = 268998,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "sanguinating_totem", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 160753,
|
|
toggle = "defensives",
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "fetish_of_the_tormented_mind", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 160833,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "doubting_mind" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "doubting_mind", {
|
|
id = 273559,
|
|
duration = 5,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "whirlwings_plumage", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 158215,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "gryphons_pride" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "gryphons_pride", {
|
|
id = 268550,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
|
|
-- PvP Trinkets
|
|
-- Medallions
|
|
do
|
|
local pvp_medallions = {
|
|
{ "dread_aspirants_medallion", 162897 },
|
|
{ "dread_gladiators_medallion", 161674 },
|
|
{ "sinister_aspirants_medallion", 165220 },
|
|
{ "sinister_gladiators_medallion", 165055 },
|
|
{ "notorious_aspirants_medallion", 167525 },
|
|
{ "notorious_gladiators_medallion", 167377 },
|
|
{ "old_corrupted_gladiators_medallion", 172666 },
|
|
{ "corrupted_aspirants_medallion", 184058 },
|
|
{ "corrupted_gladiators_medallion", 184055 },
|
|
{ "sinful_aspirants_medallion", 184052 },
|
|
{ "sinful_gladiators_medallion", 181333 },
|
|
{ "unchained_aspirants_medallion", 185309 },
|
|
{ "unchained_gladiators_medallion", 185304 },
|
|
{ "cosmic_aspirants_medallion", 186966 },
|
|
{ "cosmic_gladiators_medallion", 186869 },
|
|
{ "eternal_aspirants_medallion", 192412 },
|
|
{ "eternal_gladiators_medallion", 192298 },
|
|
{ "obsidian_combatants_medallion", 204164 },
|
|
{ "obsidian_aspirants_medallion", 205779 },
|
|
{ "obsidian_gladiators_medallion", 205711 }
|
|
}
|
|
|
|
local pvp_medallions_copy = {}
|
|
|
|
for _, v in ipairs( pvp_medallions ) do
|
|
insert( pvp_medallions_copy, v[1] )
|
|
all:RegisterGear( v[1], v[2] )
|
|
all:RegisterGear( "gladiators_medallion", v[2] )
|
|
end
|
|
|
|
all:RegisterAbility( "gladiators_medallion", {
|
|
name = function ()
|
|
local data = GetSpellInfo( 277179 )
|
|
return data and data.name or "Gladiator's Medallion"
|
|
end,
|
|
listName = function ()
|
|
local data = GetSpellInfo( 277179 )
|
|
if data and data.iconID then return "|T" .. data.iconID .. ":0|t " .. ( GetSpellLink( 277179 ) ) end
|
|
end,
|
|
link = function () return ( GetSpellLink( 277179 ) ) end,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = function ()
|
|
local m
|
|
for _, medallion in ipairs( pvp_medallions ) do
|
|
m = medallion[ 2 ]
|
|
if equipped[ m ] then return m end
|
|
end
|
|
return m
|
|
end,
|
|
items = { 161674, 162897, 165055, 165220, 167377, 167525, 181333, 184052, 184055, 172666, 184058, 185309, 185304, 186966, 186869, 192412, 192298, 204164, 205779, 205711, 205779, 205711 },
|
|
toggle = "defensives",
|
|
|
|
usable = function () return debuff.loss_of_control.up, "requires loss of control effect" end,
|
|
|
|
handler = function ()
|
|
applyBuff( "gladiators_medallion" )
|
|
end,
|
|
|
|
copy = pvp_medallions_copy
|
|
} )
|
|
|
|
all:RegisterAura( "gladiators_medallion", {
|
|
id = 277179,
|
|
duration = 20,
|
|
max_stack = 1
|
|
} )
|
|
end
|
|
|
|
-- Badges
|
|
do
|
|
local pvp_badges = {
|
|
{ "dread_aspirants_badge", 162966 },
|
|
{ "dread_gladiators_badge", 161902 },
|
|
{ "sinister_aspirants_badge", 165223 },
|
|
{ "sinister_gladiators_badge", 165058 },
|
|
{ "notorious_aspirants_badge", 167528 },
|
|
{ "notorious_gladiators_badge", 167380 },
|
|
{ "corrupted_aspirants_badge", 172849 },
|
|
{ "corrupted_gladiators_badge", 172669 },
|
|
{ "sinful_aspirants_badge_of_ferocity", 175884 },
|
|
{ "sinful_gladiators_badge_of_ferocity", 175921 },
|
|
{ "unchained_aspirants_badge_of_ferocity", 185161 },
|
|
{ "unchained_gladiators_badge_of_ferocity", 185197 },
|
|
{ "cosmic_aspirants_badge_of_ferocity", 186906 },
|
|
{ "cosmic_gladiators_badge_of_ferocity", 186866 },
|
|
{ "eternal_aspirants_badge_of_ferocity", 192352 },
|
|
{ "eternal_gladiators_badge_of_ferocity", 192295 },
|
|
{ "crimson_aspirants_badge_of_ferocity", 201449 },
|
|
{ "crimson_gladiators_badge_of_ferocity", 201807 },
|
|
{ "obsidian_aspirants_badge_of_ferocity", 205778 },
|
|
{ "obsidian_gladiator_badge_of_ferocity", 205708 },
|
|
{ "verdant_aspirants_badge_of_ferocity", 209763 },
|
|
{ "verdant_gladiators_badge_of_ferocity", 209343 }
|
|
}
|
|
|
|
local pvp_badges_copy = {}
|
|
|
|
for _, v in ipairs( pvp_badges ) do
|
|
insert( pvp_badges_copy, v[1] )
|
|
all:RegisterGear( v[1], v[2] )
|
|
all:RegisterGear( "gladiators_badge", v[2] )
|
|
end
|
|
|
|
all:RegisterAbility( "gladiators_badge", {
|
|
name = function ()
|
|
local data = GetSpellInfo( 277185 )
|
|
return data and data.name or "Gladiator's Badge"
|
|
end,
|
|
listName = function ()
|
|
local data = GetSpellInfo( 277185 )
|
|
if data and data.iconID then return "|T" .. data.iconID .. ":0|t " .. ( GetSpellLink( 277185 ) ) end
|
|
end,
|
|
link = function () return ( GetSpellLink( 277185 ) ) end,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
items = { 162966, 161902, 165223, 165058, 167528, 167380, 172849, 172669, 175884, 175921, 185161, 185197, 186906, 186866, 192352, 192295, 201449, 201807, 205778, 205708, 209763, 209343 },
|
|
texture = 135884,
|
|
|
|
toggle = "cooldowns",
|
|
item = function ()
|
|
local b
|
|
|
|
for i = #pvp_badges, 1, -1 do
|
|
b = pvp_badges[ i ][ 2 ]
|
|
if equipped[ b ] then
|
|
break
|
|
end
|
|
end
|
|
return b
|
|
end,
|
|
|
|
usable = function () return set_bonus.gladiators_badge > 0, "requires Gladiator's Badge" end,
|
|
handler = function ()
|
|
applyBuff( "gladiators_badge" )
|
|
end,
|
|
|
|
copy = pvp_badges_copy
|
|
} )
|
|
|
|
all:RegisterAura( "gladiators_badge", {
|
|
id = 277185,
|
|
duration = 15,
|
|
max_stack = 1
|
|
} )
|
|
end
|
|
|
|
|
|
-- Insignias -- N/A, not on-use.
|
|
all:RegisterAura( "gladiators_insignia", {
|
|
id = 277181,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
copy = 345230
|
|
} )
|
|
|
|
|
|
-- Safeguard (equipped, not on-use)
|
|
all:RegisterAura( "gladiators_safeguard", {
|
|
id = 286342,
|
|
duration = 10,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Emblems
|
|
do
|
|
local pvp_emblems = {
|
|
-- dread_combatants_emblem = 161812,
|
|
dread_aspirants_emblem = 162898,
|
|
dread_gladiators_emblem = 161675,
|
|
sinister_aspirants_emblem = 165221,
|
|
sinister_gladiators_emblem = 165056,
|
|
notorious_gladiators_emblem = 167378,
|
|
notorious_aspirants_emblem = 167526,
|
|
corrupted_gladiators_emblem = 172667,
|
|
corrupted_aspirants_emblem = 172847,
|
|
sinful_aspirants_emblem = 178334,
|
|
sinful_gladiators_emblem = 178447,
|
|
unchained_aspirants_emblem = 185242,
|
|
unchained_gladiators_emblem = 185282,
|
|
cosmic_aspirants_emblem = 186946,
|
|
cosmic_gladiators_emblem = 186868,
|
|
eternal_aspirants_emblem = 192392,
|
|
eternal_gladiators_emblem = 192297,
|
|
crimson_aspirants_emblem = 201452,
|
|
crimson_gladiators_emblem = 201809,
|
|
obsidian_combatants_emblem = 204166,
|
|
obsidian_aspirants_emblem = 205781,
|
|
obsidian_gladiators_emblem = 205710,
|
|
verdant_aspirants_emblem = 209766,
|
|
verdant_combatants_emblem = 208309,
|
|
verdant_gladiators_emblem = 209345
|
|
}
|
|
|
|
local pvp_emblems_copy = {}
|
|
|
|
for k, v in pairs( pvp_emblems ) do
|
|
insert( pvp_emblems_copy, k )
|
|
all:RegisterGear( k, v )
|
|
all:RegisterGear( "gladiators_emblem", v )
|
|
end
|
|
|
|
|
|
all:RegisterAbility( "gladiators_emblem", {
|
|
name = function ()
|
|
local data = GetSpellInfo( 277187 )
|
|
return data and data.name or "Gladiator's Emblem"
|
|
end,
|
|
listName = function ()
|
|
local data = GetSpellInfo( 277187 )
|
|
if data and data.iconID then return "|T" .. data.iconID .. ":0|t " .. ( GetSpellLink( 277187 ) ) end
|
|
end,
|
|
link = function () return ( GetSpellLink( 277187 ) ) end,
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = function ()
|
|
local e
|
|
for _, emblem in pairs( pvp_emblems ) do
|
|
e = emblem
|
|
if equipped[ e ] then return e end
|
|
end
|
|
return e
|
|
end,
|
|
items = { 162898, 161675, 165221, 165056, 167378, 167526, 172667, 172847, 178334, 178447, 185242, 185282, 186946, 186868, 192392, 192297, 201452, 201809, 204166, 205781, 205710, 209766, 208309, 209345 },
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "gladiators_emblem" )
|
|
end,
|
|
|
|
copy = pvp_emblems_copy
|
|
} )
|
|
|
|
all:RegisterAura( "gladiators_emblem", {
|
|
id = 277187,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
} )
|
|
end
|
|
|
|
|
|
-- 8.3 Corrupted On-Use
|
|
|
|
-- DNI, because potentially you have no enemies w/ Corruption w/in range.
|
|
--[[
|
|
all:RegisterAbility( "corrupted_gladiators_breach", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 174276,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "void_jaunt" )
|
|
-- +Debuff?
|
|
end,
|
|
|
|
auras = {
|
|
void_jaunt = {
|
|
id = 314517,
|
|
duration = 6,
|
|
max_stack = 1,
|
|
}
|
|
}
|
|
} )
|
|
]]
|
|
|
|
|
|
all:RegisterAbility( "corrupted_gladiators_spite", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 174472,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "gladiators_spite" )
|
|
applyDebuff( "target", "lingering_spite" )
|
|
end,
|
|
|
|
auras = {
|
|
gladiators_spite = {
|
|
id = 315391,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
},
|
|
|
|
lingering_spite = {
|
|
id = 320297,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
}
|
|
}
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "corrupted_gladiators_maledict", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off", -- ???
|
|
|
|
item = 172672,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "gladiators_maledict" )
|
|
end,
|
|
|
|
auras = {
|
|
gladiators_maledict = {
|
|
id = 305252,
|
|
duration = 6,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
} )
|
|
|
|
|
|
--[[ WiP: Timewarped Trinkets
|
|
do
|
|
local timewarped_trinkets = {
|
|
{ "runed_fungalcap", 127184, "shell_of_deterrence", 31771, 20, 1 },
|
|
{ "icon_of_the_silver_crescent", 129850, "blessing_of_the_silver_crescent", 194645, 20, 1 },
|
|
{ "essence_of_the_martyr", 129851, "essence_of_the_martyr", 194637, 20, 1 },
|
|
{ "gnomeregan_autoblocker_601", 129849, "gnome_ingenuity", 194543, 40, 1 },
|
|
{ "emblem_of_fury", 129937, "lust_for_battle_str", 194638, 20, 1 },
|
|
{ "bloodlust_brooch", 129848, "lust_for_battle_agi", 194632, 20, 1 },
|
|
{}
|
|
|
|
}
|
|
|
|
{ "vial_of_the_sunwell", 133462, "vessel_of_the_naaru", 45059, 3600, 1 }, -- vessel_of_the_naaru on-use 45064, 120 sec CD.
|
|
end ]]
|
|
|
|
|
|
-- Galewind Chimes
|
|
all:RegisterAura( "galewind_chimes", {
|
|
id = 268518,
|
|
duration = 8,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
-- Gilded Loa Figurine
|
|
all:RegisterAura( "will_of_the_loa", {
|
|
id = 273974,
|
|
duration = 10,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
-- Emblem of Zandalar
|
|
all:RegisterAura( "speed_of_the_spirits", {
|
|
id = 273992,
|
|
duration = 8,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
-- Dinobone Charm
|
|
all:RegisterAura( "primal_instinct", {
|
|
id = 273988,
|
|
duration = 7,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "pearl_divers_compass", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 158162,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "true_north" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "true_north", {
|
|
id = 273935,
|
|
duration = 12,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "first_mates_spyglass", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 158163,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "spyglass_sight" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "spyglass_sight", {
|
|
id = 273955,
|
|
duration = 15,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "plunderbeards_flask", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 158164,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "bolstered_spirits" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "bolstered_spirits", {
|
|
id = 273942,
|
|
duration = 10,
|
|
max_stack = 10,
|
|
} )
|
|
|
|
|
|
all:RegisterAura( "sound_barrier", {
|
|
id = 268531,
|
|
duration = 8,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "vial_of_storms", {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 158224,
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
all:RegisterAura( "sirens_melody", {
|
|
id = 268512,
|
|
duration = 6,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAura( "tick", {
|
|
id = 274430,
|
|
duration = 6,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
all:RegisterAura( "tock", {
|
|
id = 274431,
|
|
duration = 6,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
all:RegisterAura( "soulguard", {
|
|
id = 274459,
|
|
duration = 12,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "berserkers_juju", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 161117,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "berserkers_frenzy" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "berserkers_frenzy", {
|
|
id = 274472,
|
|
duration = 10,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
all:RegisterGear( "ancient_knot_of_wisdom", 161417, 166793 )
|
|
|
|
all:RegisterAbility( "ancient_knot_of_wisdom", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = function ()
|
|
if equipped[161417] then return 161417 end
|
|
return 166793
|
|
end,
|
|
items = { 167417, 166793 },
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "wisdom_of_the_forest_lord" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "wisdom_of_the_forest_lord", {
|
|
id = 278267,
|
|
duration = 20,
|
|
max_stack = 5
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "knot_of_ancient_fury", {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 166795,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "fury_of_the_forest_lord" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "fury_of_the_forest_lord", {
|
|
id = 278231,
|
|
duration = 12,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- BREWFEST
|
|
all:RegisterAbility( "brawlers_statue", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 117357,
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "drunken_evasiveness" )
|
|
end
|
|
} )
|
|
|
|
all:RegisterAura( "drunken_evasiveness", {
|
|
id = 127967,
|
|
duration = 20,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
-- Various Timewalking Trinkets
|
|
all:RegisterAbility( "wrathstone", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 45263,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "wrathstone" )
|
|
end,
|
|
|
|
auras = {
|
|
wrathstone = {
|
|
id = 64800,
|
|
duration = 20,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "skardyns_grace", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 133282,
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "speed_of_thought" )
|
|
end,
|
|
|
|
auras = {
|
|
speed_of_thought = {
|
|
id = 92099,
|
|
duration = 35,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
} )
|
|
|
|
|
|
-- HALLOW'S END
|
|
all:RegisterAbility( "the_horsemans_sinister_slicer", {
|
|
cast = 0,
|
|
cooldown = 600,
|
|
gcd = "off",
|
|
|
|
item = 117356,
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
|
|
-- LEGION LEGENDARIES
|
|
all:RegisterGear( "rethus_incessant_courage", 146667 )
|
|
all:RegisterAura( "rethus_incessant_courage", { id = 241330 } )
|
|
|
|
all:RegisterGear( "vigilance_perch", 146668 )
|
|
all:RegisterAura( "vigilance_perch", { id = 241332, duration = 60, max_stack = 5 } )
|
|
|
|
all:RegisterGear( "the_sentinels_eternal_refuge", 146669 )
|
|
all:RegisterAura( "the_sentinels_eternal_refuge", { id = 241331, duration = 60, max_stack = 5 } )
|
|
|
|
all:RegisterGear( "prydaz_xavarics_magnum_opus", 132444 )
|
|
all:RegisterAura( "xavarics_magnum_opus", { id = 207428, duration = 30 } )
|
|
|
|
|
|
|
|
all:RegisterAbility( "draught_of_souls", {
|
|
cast = 0,
|
|
cooldown = 80,
|
|
gcd = "off",
|
|
|
|
item = 140808,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "fel_crazed_rage", 3 )
|
|
setCooldown( "global_cooldown", 3 )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "fel_crazed_rage", {
|
|
id = 225141,
|
|
duration = 3,
|
|
})
|
|
|
|
|
|
all:RegisterAbility( "faulty_countermeasure", {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 137539,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "sheathed_in_frost" )
|
|
end
|
|
} )
|
|
|
|
all:RegisterAura( "sheathed_in_frost", {
|
|
id = 214962,
|
|
duration = 30
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "feloiled_infernal_machine", {
|
|
cast = 0,
|
|
cooldown = 80,
|
|
gcd = "off",
|
|
|
|
item = 144482,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "grease_the_gears" )
|
|
end,
|
|
} )
|
|
|
|
all:RegisterAura( "grease_the_gears", {
|
|
id = 238534,
|
|
duration = 20
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "ring_of_collapsing_futures", {
|
|
item = 142173,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 15,
|
|
gcd = "off",
|
|
|
|
readyTime = function () return debuff.temptation.remains end,
|
|
handler = function ()
|
|
applyDebuff( "player", "temptation", 30, debuff.temptation.stack + 1 )
|
|
end
|
|
} )
|
|
|
|
all:RegisterAura( "temptation", {
|
|
id = 234143,
|
|
duration = 30,
|
|
max_stack = 20
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "forgefiends_fabricator", {
|
|
item = 151963,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 30,
|
|
gcd = "off",
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "horn_of_valor", {
|
|
item = 133642,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
toggle = "cooldowns",
|
|
handler = function () applyBuff( "valarjars_path" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "valarjars_path", {
|
|
id = 215956,
|
|
duration = 30,
|
|
max_stack = 1
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "kiljaedens_burning_wish", {
|
|
item = 144259,
|
|
|
|
cast = 0,
|
|
cooldown = 75,
|
|
gcd = "off",
|
|
|
|
texture = 1357805,
|
|
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "might_of_krosus", {
|
|
item = 140799,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 30,
|
|
gcd = "off",
|
|
handler = function () if active_enemies > 3 then setCooldown( "might_of_krosus", 15 ) end end
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "ring_of_collapsing_futures", {
|
|
item = 142173,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 15,
|
|
gcd = "off",
|
|
readyTime = function () return debuff.temptation.remains end,
|
|
handler = function () applyDebuff( "player", "temptation", 30, debuff.temptation.stack + 1 ) end
|
|
} )
|
|
|
|
all:RegisterAura( "temptation", {
|
|
id = 234143,
|
|
duration = 30,
|
|
max_stack = 20
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "specter_of_betrayal", {
|
|
item = 151190,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 45,
|
|
gcd = "off",
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "tiny_oozeling_in_a_jar", {
|
|
item = 137439,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 20,
|
|
gcd = "off",
|
|
usable = function () return buff.congealing_goo.stack == 6 end,
|
|
handler = function () removeBuff( "congealing_goo" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "congealing_goo", {
|
|
id = 215126,
|
|
duration = 60,
|
|
max_stack = 6
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "umbral_moonglaives", {
|
|
item = 147012,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
toggle = "cooldowns",
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "unbridled_fury", {
|
|
item = 139327,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
toggle = "cooldowns",
|
|
handler = function () applyBuff( "wild_gods_fury" ) end
|
|
} )
|
|
|
|
all:RegisterAura( "wild_gods_fury", {
|
|
id = 221695,
|
|
duration = 30
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "vial_of_ceaseless_toxins", {
|
|
item = 147011,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
toggle = "cooldowns",
|
|
handler = function () applyDebuff( "target", "ceaseless_toxin", 20 ) end
|
|
} )
|
|
|
|
all:RegisterAura( "ceaseless_toxin", {
|
|
id = 242497,
|
|
duration = 20
|
|
} )
|
|
|
|
|
|
all:RegisterAbility( "tome_of_unraveling_sanity", {
|
|
item = 147019,
|
|
spend = 0,
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
toggle = "cooldowns",
|
|
handler = function () applyDebuff( "target", "insidious_corruption", 12 ) end
|
|
} )
|
|
|
|
all:RegisterAura( "insidious_corruption", {
|
|
id = 243941,
|
|
duration = 12
|
|
} )
|
|
all:RegisterAura( "extracted_sanity", {
|
|
id = 243942,
|
|
duration = 24
|
|
} )
|
|
|
|
all:RegisterGear( "aggramars_stride", 132443 )
|
|
all:RegisterAura( "aggramars_stride", {
|
|
id = 207438,
|
|
duration = 3600
|
|
} )
|
|
|
|
all:RegisterGear( "sephuzs_secret", 132452 )
|
|
all:RegisterAura( "sephuzs_secret", {
|
|
id = 208051,
|
|
duration = 10
|
|
} )
|
|
all:RegisterAbility( "buff_sephuzs_secret", {
|
|
name = "Sephuz's Secret (ICD)",
|
|
cast = 0,
|
|
cooldown = 30,
|
|
gcd = "off",
|
|
|
|
unlisted = true,
|
|
usable = function () return false end,
|
|
} )
|
|
|
|
all:RegisterGear( "archimondes_hatred_reborn", 144249 )
|
|
all:RegisterAura( "archimondes_hatred_reborn", {
|
|
id = 235169,
|
|
duration = 10,
|
|
max_stack = 1
|
|
} )
|
|
|
|
all:RegisterGear( "amanthuls_vision", 154172 )
|
|
all:RegisterAura( "glimpse_of_enlightenment", {
|
|
id = 256818,
|
|
duration = 12
|
|
} )
|
|
all:RegisterAura( "amanthuls_grandeur", {
|
|
id = 256832,
|
|
duration = 15
|
|
} )
|
|
|
|
all:RegisterGear( "insignia_of_the_grand_army", 152626 )
|
|
|
|
all:RegisterGear( "eonars_compassion", 154172 )
|
|
all:RegisterAura( "mark_of_eonar", {
|
|
id = 256824,
|
|
duration = 12
|
|
} )
|
|
all:RegisterAura( "eonars_verdant_embrace", {
|
|
id = function ()
|
|
if class.file == "SHAMAN" then return 257475 end
|
|
if class.file == "DRUID" then return 257470 end
|
|
if class.file == "MONK" then return 257471 end
|
|
if class.file == "PALADIN" then return 257472 end
|
|
if class.file == "PRIEST" then
|
|
if spec.discipline then return 257473 end
|
|
if spec.holy then return 257474 end
|
|
end
|
|
return 257475
|
|
end,
|
|
duration = 20,
|
|
copy = { 257470, 257471, 257472, 257473, 257474, 257475 }
|
|
} )
|
|
all:RegisterAura( "verdant_embrace", {
|
|
id = 257444,
|
|
duration = 30
|
|
} )
|
|
|
|
|
|
all:RegisterGear( "aggramars_conviction", 154173 )
|
|
all:RegisterAura( "celestial_bulwark", {
|
|
id = 256816,
|
|
duration = 14
|
|
} )
|
|
all:RegisterAura( "aggramars_fortitude", {
|
|
id = 256831,
|
|
duration = 15
|
|
} )
|
|
|
|
all:RegisterGear( "golganneths_vitality", 154174 )
|
|
all:RegisterAura( "golganneths_thunderous_wrath", {
|
|
id = 256833,
|
|
duration = 15
|
|
} )
|
|
|
|
all:RegisterGear( "khazgoroths_courage", 154176 )
|
|
all:RegisterAura( "worldforgers_flame", {
|
|
id = 256826,
|
|
duration = 12
|
|
} )
|
|
all:RegisterAura( "khazgoroths_shaping", {
|
|
id = 256835,
|
|
duration = 15
|
|
} )
|
|
|
|
all:RegisterGear( "norgannons_prowess", 154177 )
|
|
all:RegisterAura( "rush_of_knowledge", {
|
|
id = 256828,
|
|
duration = 12
|
|
} )
|
|
all:RegisterAura( "norgannons_command", {
|
|
id = 256836,
|
|
duration = 15,
|
|
max_stack = 6
|
|
} )
|
|
|
|
|
|
-- Legion TW
|
|
all:RegisterAbilities( {
|
|
windscar_whetstone = {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 137486,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "slicing_maelstrom" )
|
|
end,
|
|
|
|
proc = "damage",
|
|
|
|
auras = {
|
|
slicing_maelstrom = {
|
|
id = 214980,
|
|
duration = 6,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
giant_ornamental_pearl = {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 137369,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "gaseous_bubble" )
|
|
end,
|
|
|
|
auras = {
|
|
gaseous_bubble = {
|
|
id = 214971,
|
|
duration = 8,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
bottled_hurricane = {
|
|
cast = 0,
|
|
gcd = "off",
|
|
|
|
item = 137369,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
buff = "gathering_clouds",
|
|
|
|
handler = function ()
|
|
removeBuff( "gathering_clouds" )
|
|
end,
|
|
|
|
auras = {
|
|
gathering_clouds = {
|
|
id = 215294,
|
|
duration = 60,
|
|
max_stack = 10
|
|
}
|
|
}
|
|
},
|
|
|
|
shard_of_rokmora = {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 137338,
|
|
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "crystalline_body" )
|
|
end,
|
|
|
|
auras = {
|
|
crystalline_body = {
|
|
id = 214366,
|
|
duration = 30,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
talisman_of_the_cragshaper = {
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "off",
|
|
|
|
item = 137344,
|
|
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyBuff( "stance_of_the_mountain" )
|
|
end,
|
|
|
|
auras = {
|
|
stance_of_the_mountain = {
|
|
id = 214423,
|
|
duration = 15,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
tirathons_betrayal = {
|
|
cast = 0,
|
|
cooldown = 75,
|
|
gcd = "off",
|
|
|
|
item = 137537,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "darkstrikes" )
|
|
end,
|
|
|
|
auras = {
|
|
darkstrikes = {
|
|
id = 215658,
|
|
duration = 15,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
orb_of_torment = {
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
item = 137538,
|
|
|
|
toggle = "defensives",
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "soul_sap" )
|
|
end,
|
|
|
|
auras = {
|
|
soul_sap = {
|
|
id = 215936,
|
|
duration = 20,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
moonlit_prism = {
|
|
cast = 0,
|
|
cooldown = 90,
|
|
gcd = "off",
|
|
|
|
item = 137541,
|
|
|
|
toggle = "cooldowns",
|
|
|
|
handler = function ()
|
|
applyBuff( "elunes_light" )
|
|
end,
|
|
|
|
auras = {
|
|
elunes_light = {
|
|
id = 215648,
|
|
duration = 20,
|
|
max_stack = 20
|
|
}
|
|
}
|
|
},
|
|
} )
|
|
|
|
|
|
ns.addToggle = function( name, default, optionName, optionDesc )
|
|
|
|
table.insert( class.toggles, {
|
|
name = name,
|
|
state = default,
|
|
option = optionName,
|
|
oDesc = optionDesc
|
|
} )
|
|
|
|
if Hekili.DB.profile[ 'Toggle State: ' .. name ] == nil then
|
|
Hekili.DB.profile[ 'Toggle State: ' .. name ] = default
|
|
end
|
|
|
|
end
|
|
|
|
|
|
ns.addSetting = function( name, default, options )
|
|
|
|
table.insert( class.settings, {
|
|
name = name,
|
|
state = default,
|
|
option = options
|
|
} )
|
|
|
|
if Hekili.DB.profile[ 'Class Option: ' .. name ] == nil then
|
|
Hekili.DB.profile[ 'Class Option: ' ..name ] = default
|
|
end
|
|
|
|
end
|
|
|
|
|
|
ns.addWhitespace = function( name, size )
|
|
|
|
table.insert( class.settings, {
|
|
name = name,
|
|
option = {
|
|
name = " ",
|
|
type = "description",
|
|
desc = " ",
|
|
width = size
|
|
}
|
|
} )
|
|
|
|
end
|
|
|
|
|
|
ns.addHook = function( hook, func )
|
|
class.hooks[ hook ] = func
|
|
end
|
|
|
|
|
|
do
|
|
local inProgress = {}
|
|
|
|
ns.callHook = function( hook, ... )
|
|
if class.hooks[ hook ] and not inProgress[ hook ] then
|
|
local a1, a2, a3, a4, a5
|
|
|
|
inProgress[ hook ] = true
|
|
for _, hook in ipairs( class.hooks[ hook ] ) do
|
|
a1, a2, a3, a4, a5 = hook ( ... )
|
|
end
|
|
inProgress[ hook ] = nil
|
|
|
|
if a1 ~= nil then
|
|
return a1, a2, a3, a4, a5
|
|
else
|
|
return ...
|
|
end
|
|
end
|
|
|
|
return ...
|
|
end
|
|
end
|
|
|
|
|
|
ns.registerCustomVariable = function( var, default )
|
|
state[ var ] = default
|
|
end
|
|
|
|
|
|
|
|
|
|
ns.setClass = function( name )
|
|
-- deprecated.
|
|
--class.file = name
|
|
end
|
|
|
|
|
|
function ns.setRange( value )
|
|
class.range = value
|
|
end
|
|
|
|
|
|
local function storeAbilityElements( key, values )
|
|
|
|
local ability = class.abilities[ key ]
|
|
|
|
if not ability then
|
|
ns.Error( "storeAbilityElements( " .. key .. " ) - no such ability in abilities table." )
|
|
return
|
|
end
|
|
|
|
for k, v in pairs( values ) do
|
|
ability.elem[ k ] = type( v ) == "function" and setfenv( v, state ) or v
|
|
end
|
|
|
|
end
|
|
ns.storeAbilityElements = storeAbilityElements
|
|
|
|
|
|
local function modifyElement( t, k, elem, value )
|
|
|
|
local entry = class[ t ][ k ]
|
|
|
|
if not entry then
|
|
ns.Error( "modifyElement() - no such key '" .. k .. "' in '" .. t .. "' table." )
|
|
return
|
|
end
|
|
|
|
if type( value ) == "function" then
|
|
entry.mods[ elem ] = setfenv( value, Hekili.State )
|
|
else
|
|
entry.elem[ elem ] = value
|
|
end
|
|
|
|
end
|
|
ns.modifyElement = modifyElement
|
|
|
|
|
|
|
|
local function setUsableItemCooldown( cd )
|
|
state.setCooldown( "usable_items", cd or 10 )
|
|
end
|
|
|
|
|
|
-- For Trinket Settings.
|
|
class.itemSettings = {}
|
|
|
|
local function addItemSettings( key, itemID, options )
|
|
|
|
options = options or {}
|
|
|
|
--[[ options.icon = {
|
|
type = "description",
|
|
name = function () return select( 2, GetItemInfo( itemID ) ) or format( "[%d]", itemID ) end,
|
|
order = 1,
|
|
image = function ()
|
|
local tex = select( 10, GetItemInfo( itemID ) )
|
|
if tex then
|
|
return tex, 50, 50
|
|
end
|
|
return nil
|
|
end,
|
|
imageCoords = { 0.1, 0.9, 0.1, 0.9 },
|
|
width = "full",
|
|
fontSize = "large"
|
|
} ]]
|
|
|
|
options.disabled = {
|
|
type = "toggle",
|
|
name = function () return format( "Disable %s via |cff00ccff[Use Items]|r", select( 2, GetItemInfo( itemID ) ) or ( "[" .. itemID .. "]" ) ) end,
|
|
desc = function( info )
|
|
local output = "If disabled, the addon will not recommend this item via the |cff00ccff[Use Items]|r action. " ..
|
|
"You can still manually include the item in your action lists with your own tailored criteria."
|
|
return output
|
|
end,
|
|
order = 25,
|
|
width = "full"
|
|
}
|
|
|
|
options.minimum = {
|
|
type = "range",
|
|
name = "Minimum Targets",
|
|
desc = "The addon will only recommend this trinket (via |cff00ccff[Use Items]|r) when there are at least this many targets available to hit.",
|
|
order = 26,
|
|
width = "full",
|
|
min = 1,
|
|
max = 10,
|
|
step = 1
|
|
}
|
|
|
|
options.maximum = {
|
|
type = "range",
|
|
name = "Maximum Targets",
|
|
desc = "The addon will only recommend this trinket (via |cff00ccff[Use Items]|r) when there are no more than this many targets detected.\n\n" ..
|
|
"This setting is ignored if set to 0.",
|
|
order = 27,
|
|
width = "full",
|
|
min = 0,
|
|
max = 10,
|
|
step = 1
|
|
}
|
|
|
|
class.itemSettings[ itemID ] = {
|
|
key = key,
|
|
name = function () return select( 2, GetItemInfo( itemID ) ) or ( "[" .. itemID .. "]" ) end,
|
|
item = itemID,
|
|
options = options,
|
|
}
|
|
|
|
end
|
|
|
|
|
|
--[[ local function addUsableItem( key, id )
|
|
class.items = class.items or {}
|
|
class.items[ key ] = id
|
|
|
|
addGearSet( key, id )
|
|
addItemSettings( key, id )
|
|
end
|
|
ns.addUsableItem = addUsableItem ]]
|
|
|
|
|
|
function Hekili:GetAbilityInfo( index )
|
|
|
|
local ability = class.abilities[ index ]
|
|
|
|
if not ability then return end
|
|
|
|
-- Decide if more details are needed later.
|
|
return ability.id, ability.name, ability.key, ability.item
|
|
end
|
|
|
|
class.interrupts = {}
|
|
|
|
|
|
local function addPet( key, permanent )
|
|
state.pet[ key ] = rawget( state.pet, key ) or {}
|
|
state.pet[ key ].name = key
|
|
state.pet[ key ].expires = 0
|
|
|
|
ns.commitKey( key )
|
|
end
|
|
ns.addPet = addPet
|
|
|
|
|
|
local function addStance( key, spellID )
|
|
class.stances[ key ] = spellID
|
|
ns.commitKey( key )
|
|
end
|
|
ns.addStance = addStance
|
|
|
|
|
|
local function setRole( key )
|
|
|
|
for k,v in pairs( state.role ) do
|
|
state.role[ k ] = nil
|
|
end
|
|
|
|
state.role[ key ] = true
|
|
|
|
end
|
|
ns.setRole = setRole
|
|
|
|
|
|
function Hekili:GetActiveSpecOption( opt )
|
|
if not self.currentSpecOpts then return end
|
|
return self.currentSpecOpts[ opt ]
|
|
end
|
|
|
|
|
|
function Hekili:GetActivePack()
|
|
return self:GetActiveSpecOption( "package" )
|
|
end
|
|
|
|
|
|
Hekili.SpecChangeHistory = {}
|
|
|
|
function Hekili:SpecializationChanged()
|
|
local currentSpec = GetSpecialization()
|
|
local currentID = GetSpecializationInfo( currentSpec )
|
|
|
|
if currentID == nil then
|
|
self.PendingSpecializationChange = true
|
|
return
|
|
end
|
|
|
|
self.PendingSpecializationChange = false
|
|
self:ForceUpdate( "ACTIVE_PLAYER_SPECIALIZATION_CHANGED" )
|
|
|
|
insert( self.SpecChangeHistory, {
|
|
spec = currentID,
|
|
time = GetTime(),
|
|
bt = debugstack()
|
|
} )
|
|
|
|
for k, _ in pairs( state.spec ) do
|
|
state.spec[ k ] = nil
|
|
end
|
|
|
|
for key in pairs( GetResourceInfo() ) do
|
|
state[ key ] = nil
|
|
class[ key ] = nil
|
|
end
|
|
|
|
class.primaryResource = nil
|
|
|
|
wipe( state.buff )
|
|
wipe( state.debuff )
|
|
|
|
wipe( class.auras )
|
|
wipe( class.abilities )
|
|
wipe( class.talents )
|
|
wipe( class.pvptalents )
|
|
wipe( class.powers )
|
|
wipe( class.gear )
|
|
wipe( class.setBonuses )
|
|
wipe( class.packs )
|
|
wipe( class.resources )
|
|
wipe( class.resourceAuras )
|
|
|
|
wipe( class.pets )
|
|
|
|
local specs = {}
|
|
|
|
-- If the player does not have a specialization, use their first spec instead.
|
|
if currentSpec == 5 then
|
|
currentSpec = 1
|
|
currentID = GetSpecializationInfo( 1 )
|
|
end
|
|
|
|
for i = 1, 4 do
|
|
local id, name, _, _, role = GetSpecializationInfo( i )
|
|
|
|
if not id then break end
|
|
|
|
if i == currentSpec then
|
|
insert( specs, 1, id )
|
|
|
|
state.spec.id = id
|
|
state.spec.name = name
|
|
state.spec.key = getSpecializationKey( id )
|
|
|
|
for k in pairs( state.role ) do
|
|
state.role[ k ] = false
|
|
end
|
|
|
|
if role == "DAMAGER" then
|
|
state.role.attack = true
|
|
elseif role == "TANK" then
|
|
state.role.tank = true
|
|
else
|
|
state.role.healer = true
|
|
end
|
|
|
|
state.spec[ state.spec.key ] = true
|
|
else
|
|
insert( specs, id )
|
|
end
|
|
end
|
|
|
|
insert( specs, 0 )
|
|
|
|
|
|
for key in pairs( GetResourceInfo() ) do
|
|
state[ key ] = nil
|
|
class[ key ] = nil
|
|
end
|
|
if rawget( state, "rune" ) then state.rune = nil; class.rune = nil; end
|
|
|
|
for k in pairs( class.resourceAuras ) do
|
|
class.resourceAuras[ k ] = nil
|
|
end
|
|
|
|
class.primaryResource = nil
|
|
|
|
for k in pairs( class.stateTables ) do
|
|
rawset( state, k, nil )
|
|
class.stateTables[ k ] = nil
|
|
end
|
|
|
|
for k in pairs( class.stateFuncs ) do
|
|
rawset( state, k, nil )
|
|
class.stateFuncs[ k ] = nil
|
|
end
|
|
|
|
for k in pairs( class.stateExprs ) do
|
|
class.stateExprs[ k ] = nil
|
|
end
|
|
|
|
self.currentSpec = nil
|
|
self.currentSpecOpts = nil
|
|
|
|
for i, specID in ipairs( specs ) do
|
|
local spec = class.specs[ specID ]
|
|
|
|
if spec then
|
|
if specID == currentID then
|
|
self.currentSpec = spec
|
|
self.currentSpecOpts = rawget( self.DB.profile.specs, specID )
|
|
state.settings.spec = self.currentSpecOpts
|
|
|
|
state.spec.can_dual_cast = spec.can_dual_cast
|
|
state.spec.dual_cast = spec.dual_cast
|
|
|
|
for res, model in pairs( spec.resources ) do
|
|
class.resources[ res ] = model
|
|
state[ res ] = model.state
|
|
end
|
|
if rawget( state, "runes" ) then state.rune = state.runes end
|
|
|
|
for k,v in pairs( spec.resourceAuras ) do
|
|
class.resourceAuras[ k ] = v
|
|
end
|
|
|
|
class.primaryResource = spec.primaryResource
|
|
|
|
for talent, id in pairs( spec.talents ) do
|
|
class.talents[ talent ] = id
|
|
end
|
|
|
|
for talent, id in pairs( spec.pvptalents ) do
|
|
class.pvptalents[ talent ] = id
|
|
end
|
|
|
|
class.hooks = spec.hooks or {}
|
|
--[[ for name, func in pairs( spec.hooks ) do
|
|
class.hooks[ name ] = func
|
|
end ]]
|
|
|
|
class.variables = spec.variables
|
|
|
|
class.potionList.default = "|cFFFFD100Default|r"
|
|
end
|
|
|
|
for res, model in pairs( spec.resources ) do
|
|
if not class.resources[ res ] then
|
|
class.resources[ res ] = model
|
|
state[ res ] = model.state
|
|
end
|
|
end
|
|
if rawget( state, "runes" ) then state.rune = state.runes end
|
|
|
|
for k, v in pairs( spec.auras ) do
|
|
if not class.auras[ k ] then class.auras[ k ] = v end
|
|
end
|
|
|
|
for k, v in pairs( spec.powers ) do
|
|
if not class.powers[ k ] then class.powers[ k ] = v end
|
|
end
|
|
|
|
for k, v in pairs( spec.abilities ) do
|
|
if not class.abilities[ k ] then class.abilities[ k ] = v end
|
|
end
|
|
|
|
for k, v in pairs( spec.gear ) do
|
|
if not class.gear[ k ] then class.gear[ k ] = v end
|
|
end
|
|
|
|
for k, v in pairs( spec.setBonuses ) do
|
|
if not class.setBonuses[ k ] then class.setBonuses[ k ] = v end
|
|
end
|
|
|
|
for k, v in pairs( spec.pets ) do
|
|
if not class.pets[ k ] then class.pets[ k ] = v end
|
|
end
|
|
|
|
for k, v in pairs( spec.totems ) do
|
|
if not class.totems[ k ] then class.totems[ k ] = v end
|
|
end
|
|
|
|
for k, v in pairs( spec.packs ) do
|
|
if not class.packs[ k ] then class.packs[ k ] = v end
|
|
end
|
|
|
|
for name, func in pairs( spec.stateExprs ) do
|
|
if not class.stateExprs[ name ] then
|
|
if rawget( state, name ) then state[ name ] = nil end
|
|
class.stateExprs[ name ] = func
|
|
end
|
|
end
|
|
|
|
for name, func in pairs( spec.stateFuncs ) do
|
|
if not class.stateFuncs[ name ] then
|
|
if rawget( state, name ) then
|
|
Hekili:Error( "Cannot RegisterStateFunc for an existing expression ( " .. spec.name .. " - " .. name .. " )." )
|
|
else
|
|
class.stateFuncs[ name ] = func
|
|
rawset( state, name, func )
|
|
-- Hekili:Error( "Not real error, registered " .. name .. " for " .. spec.name .. " (RSF)." )
|
|
end
|
|
end
|
|
end
|
|
|
|
for name, t in pairs( spec.stateTables ) do
|
|
if not class.stateTables[ name ] then
|
|
if rawget( state, name ) then
|
|
Hekili:Error( "Cannot RegisterStateTable for an existing expression ( " .. spec.name .. " - " .. name .. " )." )
|
|
else
|
|
class.stateTables[ name ] = t
|
|
rawset( state, name, t )
|
|
-- Hekili:Error( "Not real error, registered " .. name .. " for " .. spec.name .. " (RST)." )
|
|
end
|
|
end
|
|
end
|
|
|
|
if spec.id > 0 then
|
|
local s = rawget( Hekili.DB.profile.specs, spec.id )
|
|
|
|
if s then
|
|
for k, v in pairs( spec.settings ) do
|
|
if s.settings[ v.name ] == nil then s.settings[ v.name ] = v.default end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for k in pairs( class.abilityList ) do
|
|
local ability = class.abilities[ k ]
|
|
|
|
if ability and ability.id > 0 then
|
|
if not ability.texture or not ability.name then
|
|
local data = GetSpellInfo( ability.id )
|
|
|
|
if data and data.name and data.iconID then
|
|
ability.name = ability.name or data.name
|
|
class.abilityList[ k ] = "|T" .. data.iconID .. ":0|t " .. ability.name
|
|
end
|
|
else
|
|
class.abilityList[ k ] = "|T" .. ability.texture .. ":0|t " .. ability.name
|
|
end
|
|
end
|
|
end
|
|
|
|
state.GUID = UnitGUID( "player" )
|
|
state.player.unit = UnitGUID( "player" )
|
|
|
|
ns.callHook( "specializationChanged" )
|
|
|
|
ns.updateTalents()
|
|
ResetDisabledGearAndSpells()
|
|
|
|
state.swings.mh_speed, state.swings.oh_speed = UnitAttackSpeed( "player" )
|
|
|
|
HekiliEngine.activeThread = nil
|
|
self:UpdateDisplayVisibility()
|
|
self:UpdateDamageDetectionForCLEU()
|
|
end
|
|
|
|
|
|
do
|
|
RegisterEvent( "PLAYER_ENTERING_WORLD", function( event, login, reload )
|
|
if login or reload then
|
|
local currentSpec = GetSpecialization()
|
|
local currentID = GetSpecializationInfo( currentSpec )
|
|
|
|
if currentID ~= state.spec.id then
|
|
Hekili:SpecializationChanged()
|
|
end
|
|
end
|
|
end )
|
|
|
|
local SpellDisableEvents = {
|
|
CHALLENGE_MODE_START = 1,
|
|
CHALLENGE_MODE_RESET = 1,
|
|
CHALLENGE_MODE_COMPLETED = 1,
|
|
PLAYER_ALIVE = 1,
|
|
ZONE_CHANGED_NEW_AREA = 1,
|
|
QUEST_SESSION_CREATED = 1,
|
|
QUEST_SESSION_DESTROYED = 1,
|
|
QUEST_SESSION_ENABLED_STATE_CHANGED = 1,
|
|
QUEST_SESSION_JOINED = 1,
|
|
QUEST_SESSION_LEFT = 1
|
|
}
|
|
|
|
local WipeCovenantCache = ns.WipeCovenantCache
|
|
|
|
local function CheckSpellsAndGear()
|
|
WipeCovenantCache()
|
|
ResetDisabledGearAndSpells()
|
|
ns.updateGear()
|
|
end
|
|
|
|
for k in pairs( SpellDisableEvents ) do
|
|
RegisterEvent( k, function( event )
|
|
C_Timer.After( 1, CheckSpellsAndGear )
|
|
end )
|
|
end
|
|
end
|
|
|
|
|
|
class.trinkets = {
|
|
[0] = { -- for when nothing is equipped.
|
|
},
|
|
}
|
|
|
|
|
|
setmetatable( class.trinkets, {
|
|
__index = function( t, k )
|
|
return t[0]
|
|
end
|
|
} )
|