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.

1050 lines
36 KiB

-- Utils.lua
-- June 2014
local addon, ns = ...
local Hekili = _G[ addon ]
local format, gsub, lower = string.format, string.gsub, string.lower
local insert, remove = table.insert, table.remove
local class = Hekili.Class
local state = Hekili.State
local GetPlayerAuraBySpellID = C_UnitAuras.GetPlayerAuraBySpellID
local errors = {}
local eIndex = {}
ns.Error = function( output, ... )
if ... then
output = format( output, ... )
end
if not errors[ output ] then
errors[ output ] = {
n = 1,
last = date( "%X", time() )
}
eIndex[ #eIndex + 1 ] = output
-- if Hekili.DB.profile.Verbose then Hekili:Print( output ) end
else
errors[ output ].n = errors[ output ].n + 1
errors[ output ].last = date( "%X", time() )
end
end
function Hekili:Error( ... )
ns.Error( ... )
end
Hekili.ErrorKeys = eIndex
Hekili.ErrorDB = errors
function Hekili:GetErrors()
for i = 1, #eIndex do
Hekili:Print( eIndex[i] .. " (n = " .. errors[ eIndex[i] ].n .. "), last at " .. errors[ eIndex[i] ].last .. "." )
end
end
function ns.SpaceOut( str )
str = str:gsub( "([!<>=|&()*%-%+/][?]?)", " %1 " ):gsub("%s+", " ")
str = str:gsub( "([^%%])([%%]+)([^%%])", "%1 %2 %3" )
str = str:gsub( "%.%s+%(", ".(" )
str = str:gsub( "%)%s+%.", ")." )
str = str:gsub( "([<>~!|]) ([|=])", "%1%2" )
str = str:trim()
return str
end
local LT = LibStub( "LibTranslit-1.0" )
-- Converts `s' to a SimC-like key: strip non alphanumeric characters, replace spaces with _, convert to lower case.
function ns.formatKey( s )
s = s:gsub( "|c........", "" ):gsub( "|r", "" )
s = LT:Transliterate( s )
s = lower( s or '' ):gsub( "[^a-z0-9_ ]", "" ):gsub( "%s+", "_" )
return s
end
ns.titleCase = function( s )
local helper = function( first, rest )
return first:upper()..rest:lower()
end
return s:gsub( "_", " " ):gsub( "(%a)([%w_']*)", helper ):gsub( "[Aa]oe", "AOE" ):gsub( "[Rr]jw", "RJW" ):gsub( "[Cc]hix", "ChiX" ):gsub( "(%W?)[Ss]t(%W?)", "%1ST%2" )
end
local replacements = {
['_'] = " ",
aoe = "AOE",
rjw = "RJW",
chix = "ChiX",
st = "ST",
cd = "CD",
cds = "CDs"
}
ns.titlefy = function( s )
for k, v in pairs( replacements ) do
s = s:gsub( '%f[%w]' .. k .. '%f[%W]', v ):gsub( "_", " " )
end
return s
end
ns.fsub = function( s, pattern, repl )
return s:gsub( "%f[%w]" .. s .. "%f[%W]", repl )
end
ns.escapeMagic = function( s )
return s:gsub( "([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1" )
end
local tblUnpack = {}
ns.multiUnpack = function( ... )
table.wipe( tblUnpack )
for i = 1, select( '#', ... ) do
for _, value in ipairs( select( i, ... ) ) do
tblUnpack[ #tblUnpack + 1 ] = value
end
end
return unpack( tblUnpack )
end
ns.round = function( num, places )
return tonumber( format( "%." .. ( places or 0 ) .. "f", num ) )
end
function ns.roundUp( num, places )
num = num or 0
local tens = 10 ^ ( places or 0 )
return ceil( num * tens ) / tens
end
function ns.roundDown( num, places )
num = num or 0
local tens = 10 ^ ( places or 0 )
return floor( num * tens ) / tens
end
-- Deep Copy
-- from http://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value
local function tableCopy( obj, seen )
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do res[ tableCopy(k, s) ] = tableCopy(v, s) end
return res
end
ns.tableCopy = tableCopy
local toc = {}
local exclusions = { min = true, max = true, _G = true }
ns.commitKey = function( key )
if not toc[ key ] and not exclusions[ key ] then
ns.keys[ #ns.keys + 1 ] = key
toc[ key ] = 1
end
end
local orderedIndex = {}
local sortHelper = function( a, b )
local a1, b1 = tostring(a), tostring(b)
return a1 < b1
end
local function __genOrderedIndex( t )
for i = #orderedIndex, 1, -1 do
orderedIndex[i] = nil
end
for key in pairs( t ) do
table.insert( orderedIndex, key )
end
table.sort( orderedIndex, sortHelper )
return orderedIndex
end
local function orderedNext( t, state )
local key = nil
if state == nil then
t.__orderedIndex = __genOrderedIndex( t )
key = t.__orderedIndex[ 1 ]
else
for i = 1, table.getn( t.__orderedIndex ) do
if t.__orderedIndex[ i ] == state then
key = t.__orderedIndex[ i+1 ]
end
end
end
if key then
return key, t[ key ]
end
t.__orderedIndex = nil
return
end
function ns.orderedPairs( t )
return orderedNext, t, nil
end
function ns.safeMin( ... )
local result
for i = 1, select( "#", ... ) do
local val = select( i, ... )
if val then result = ( not result or val < result ) and val or result end
end
return result or 0
end
function ns.safeMax( ... )
local result
for i = 1, select( "#", ... ) do
local val = select( i, ... )
if val and type(val) == 'number' then result = ( not result or val > result ) and val or result end
end
return result or 0
end
function ns.safeAbs( val )
val = tonumber( val )
if val < 0 then return -val end
return val
end
-- Rivers' iterator for group members.
function ns.GroupMembers( reversed, forceParty )
local unit = ( not forceParty and IsInRaid() ) and 'raid' or 'party'
local numGroupMembers = forceParty and GetNumSubgroupMembers() or GetNumGroupMembers()
local i = reversed and numGroupMembers or ( unit == 'party' and 0 or 1 )
return function()
local ret
if i == 0 and unit == 'party' then
ret = 'player'
elseif i <= numGroupMembers and i > 0 then
ret = unit .. i
end
i = i + ( reversed and -1 or 1 )
return ret
end
end
-- Use C_Timer.After but allow for function args.
function Hekili:After( time, func, ... )
local args = { ... }
local function delayfunc()
func( unpack( args ) )
end
C_Timer.After( time, delayfunc )
end
function ns.FindRaidBuffByID(id)
local unitName
local buffCounter = 0
local buffIterator = 1
local name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3
if IsInRaid() or IsInGroup() then
if IsInRaid() then
unitName = "raid"
for numGroupMembers=1, GetNumGroupMembers() do
buffIterator = 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
while( spellID ) do
if spellID == id then buffCounter = buffCounter + 1 break end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
end
end
elseif IsInGroup() then
unitName = "party"
for numGroupMembers=1, GetNumGroupMembers() do
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
while( spellID ) do
if spellID == id then buffCounter = buffCounter + 1 break end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
end
end
buffIterator = 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( "player", buffIterator )
while( spellID ) do
if spellID == id then buffCounter = buffCounter + 1 break end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( "player", buffIterator )
end
else
unitName = "player"
end
end
return buffCounter
end
function ns.FindLowHpPlayerWithoutBuffByID(id)
local unitName
local playerWithoutBuff = 0
local buffFound = false
local buffIterator = 1
local name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3
if IsInRaid() or IsInGroup() then
if IsInRaid() then
unitName = "raid"
for numGroupMembers=1, GetNumGroupMembers() do
buffFound = false
buffIterator = 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
while( name ) do
if spellID == id then buffFound = true break end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
end
if not buffFound then
local player = unitName..numGroupMembers
local Health = (UnitHealth(player))/1000
local HealthMax = (UnitHealthMax(player))/1000
local HealthPercent = (UnitHealth(player)/UnitHealthMax(player))*100
if HealthPercent <= 80 and UnitName(player) then
playerWithoutBuff = playerWithoutBuff + 1
end
end
end
elseif IsInGroup() then
unitName = "party"
for numGroupMembers=1, GetNumGroupMembers() do
buffFound = false
buffIterator = 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
while( name ) do
if spellID == id then buffFound = true break end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
end
if not buffFound then
local player = unitName..numGroupMembers
local Health = (UnitHealth(player))/1000
local HealthMax = (UnitHealthMax(player))/1000
local HealthPercent = (UnitHealth(player)/UnitHealthMax(player))*100
if HealthPercent <= 80 and UnitName(player) then
playerWithoutBuff = playerWithoutBuff + 1
end
end
end
buffFound = false
buffIterator = 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( "player", buffIterator )
while( name ) do
if spellID == id then buffFound = true break end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( "player", buffIterator )
end
if not buffFound then
local player = "player"
local Health = (UnitHealth(player))/1000
local HealthMax = (UnitHealthMax(player))/1000
local HealthPercent = (UnitHealth(player)/UnitHealthMax(player))*100
if HealthPercent <= 80 then
playerWithoutBuff = playerWithoutBuff + 1
end
end
else
unitName = "player"
end
end
return playerWithoutBuff
end
function ns.FindRaidBuffLowestRemainsByID(id)
local buffRemainsOld
local buffRemainsNew
local buffRemainsReturn
local unitName = "player"
local buffIterator = 1
local name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3
if IsInRaid() or IsInGroup() then
if IsInRaid() then
unitName = "raid"
for numGroupMembers=1, GetNumGroupMembers() do
buffIterator = 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
while( name ) do
if spellID == id then
if buffRemainsOld == nil then
buffRemainsOld = expirationTime - GetTime()
end
local buffRemainsNew = expirationTime - GetTime()
if buffRemainsNew < buffRemainsOld then
buffRemainsReturn = buffRemainsNew
else
buffRemainsReturn = buffRemainsOld
end
break
end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
end
end
elseif IsInGroup() then
unitName = "party"
for numGroupMembers=1, GetNumGroupMembers() do
buffIterator = 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
while( name ) do
if spellID == id then
if buffRemainsOld == nil then
buffRemainsOld = expirationTime - GetTime()
end
local buffRemainsNew = expirationTime - GetTime()
if buffRemainsNew < buffRemainsOld then
buffRemainsReturn = buffRemainsNew
else
buffRemainsReturn = buffRemainsOld
end
break
end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unitName..numGroupMembers, buffIterator )
end
end
buffIterator = 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( "player", buffIterator )
while( name ) do
if spellID == id then
if buffRemainsOld == nil then
buffRemainsOld = expirationTime - GetTime()
end
local buffRemainsNew = expirationTime - GetTime()
if buffRemainsNew < buffRemainsOld then
buffRemainsReturn = buffRemainsNew
else
buffRemainsReturn = buffRemainsOld
end
break
end
buffIterator = buffIterator + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( "player", buffIterator )
end
end
end
return buffRemainsReturn == nil and 0 or buffRemainsReturn
end
local function FindPlayerAuraByID( id )
local aura = GetPlayerAuraBySpellID( id )
if aura and aura.name then
return aura.name, aura.icon, aura.applications, aura.dispelName, aura.expirationTime, aura.sourceUnit, aura.isStealable, aura.nameplateShowPersonal, aura.spellId, aura.canApplyAura, aura.isBossAura, aura.nameplateShowAll, aura.timeMod, unpack( aura.points )
end
end
ns.FindPlayerAuraByID = FindPlayerAuraByID
-- Duplicate spell info lookup.
function ns.FindUnitBuffByID( unit, id, filter )
if unit == "player" then return FindPlayerAuraByID( id ) end
local playerOrPet = false
if filter == "PLAYER|PET" then
playerOrPet = true
filter = nil
end
local i = 1
local name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unit, i, filter )
while( name ) do
if spellID == id and ( not playerOrPet or UnitIsUnit( caster, "player" ) or UnitIsUnit( caster, "pet" ) ) then break end
i = i + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitBuff( unit, i, filter )
end
return name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3
end
function ns.FindUnitDebuffByID( unit, id, filter )
if unit == "player" then return FindPlayerAuraByID( id ) end
local playerOrPet = false
if filter == "PLAYER|PET" then
playerOrPet = true
filter = nil
end
local i = 1
local name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitDebuff( unit, i, filter )
while( name ) do
if spellID == id and ( not playerOrPet or UnitIsUnit( caster, "player" ) or UnitIsUnit( caster, "pet" ) ) then break end
i = i + 1
name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3 = UnitDebuff( unit, i, filter )
end
if name and name ~= nil then
return name, icon, count, debuffType, duration, expirationTime, caster, stealable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff, nameplateShowAll, timeMod, value1, value2, value3
end
end
function ns.IsActiveSpell( id )
local slot = FindSpellBookSlotBySpellID( id )
if not slot then return false end
local _, _, spellID = GetSpellBookItemName( slot, "spell" )
return id == spellID
end
function Hekili:GetSpellLinkWithTexture( id, size, color )
if not id then return "" end
local name, _, _, _, _, _, _, icon = GetSpellInfo( id )
if name and icon then
if type( color ) == "boolean" then
color = color and "ff00ff00" or "ffff0000"
end
if color == nil then color = "ff71d5ff" end
return "|W|T" .. icon .. ":" .. ( size or 0 ) .. ":" .. ( size or "" ) .. ":::64:64:4:60:4:60|t " .. ( color and ( "|c" .. color ) or "" ) .. name .. ( color and "|r" or "" ) .. "|w"
end
return tostring( id )
end
function Hekili:ZoomedTextureWithText( texture, text )
if not texture or not text then return end
return "|W|T" .. texture .. ":0::::64:64:4:60:4:60|t " .. text .. "|w"
end
function state.debugformat( val )
if val == nil then return "nil" end
if type( val ) == "number" then return format( "%.2f", val ) end
return tostring( val )
end
-- Tooltip Parsing Utilities (10.0.2)
do
local CurrentBuild = Hekili.CurrentBuild
local tooltip = ns.Tooltip
local DisableText = {
_G.SPELL_FAILED_NOT_HERE,
_G.SPELL_FAILED_INCORRECT_AREA,
_G.SPELL_FAILED_NOT_IN_MAGE_TOWER,
_G.TOOLTIP_NOT_IN_MAGE_TOWER,
_G.LEVEL_LINKED_NOT_USABLE
}
local FindStringInTooltip = function( str, id, ttType, reverse, useMatch )
local data
if ttType == "spell" then data = C_TooltipInfo.GetSpellByID( id )
elseif ttType == "item" then data = C_TooltipInfo.GetItemByID( id )
elseif ttType == "inventory" then data = C_TooltipInfo.GetInventoryItem( "player", id )
elseif ttType == "conduit" then data = C_TooltipInfo.GetConduit( id, 0 )
else
Hekili:Error( "Usage: FindStringInTooltip( str, id, { spell | item | inventory | conduit }, reverse, useMatch )\n" ..
"Invalid tooltip type: '%s'", ttType or "nil" )
return
end
if not data then return end
if reverse then
for i = #data.lines, 1, -1 do
local line = data.lines[ i ]
if type( str ) == "table" then
for _, seek in ipairs( str ) do
if ( useMatch and line.leftText:match( seek ) ) or ( not useMatch and line.leftText == seek ) then return true end
end
else
if ( useMatch and line.leftText:match( str ) ) or ( not useMatch and line.leftText == str ) then return true end
end
end
return false
end
for _, line in ipairs( data ) do
if type( str ) == "table" then
for _, seek in ipairs( str ) do
if ( useMatch and line.leftText:match( seek ) ) or ( not useMatch and line.leftText == seek ) then return true end
end
else
if ( useMatch and line.leftText:match( str ) ) or ( not useMatch and line.leftText == str ) then return true end
end
end
return false
end
ns.FindStringInTooltip = FindStringInTooltip
local FindStringInSpellTooltip = function( str, spellID, reverse, useMatch )
return FindStringInTooltip( str, spellID, "spell", reverse, useMatch )
end
ns.FindStringInSpellTooltip = FindStringInSpellTooltip
local FindStringInItemTooltip = function( str, itemID, reverse, useMatch )
return FindStringInTooltip( str, itemID, "item", reverse, useMatch )
end
ns.FindStringInItemTooltip = FindStringInItemTooltip
-- Note, this is written to assume we're dealing with the player's inventory only; I'm not messing with inspect right now.
local FindStringInInventoryItemTooltip = function( str, slot, reverse, useMatch )
return FindStringInTooltip( str, slot, "inventory", reverse, useMatch )
end
ns.FindStringInInventoryItemTooltip = FindStringInInventoryItemTooltip
local FindStringInConduitTooltip = function( str, conduit, reverse, useMatch )
return FindStringInTooltip( str, conduit, "conduit", reverse, useMatch )
end
ns.FindStringInConduitTooltip = FindStringInConduitTooltip
local DisabledSpells = {}
local IsSpellDisabled = function( spellID )
if DisabledSpells[ spellID ] ~= nil then return DisabledSpells[ spellID ] end
local isDisabled = FindStringInSpellTooltip( DisableText, spellID, true, true )
DisabledSpells[ spellID ] = isDisabled
return isDisabled
end
ns.IsSpellDisabled = IsSpellDisabled
local DisabledItems = {}
local IsItemDisabled = function( itemID )
if DisabledItems[ itemID ] ~= nil then return DisabledItems[ itemID ] end
local isDisabled = FindStringInItemTooltip( DisableText, itemID, true, true )
DisabledItems[ itemID ] = isDisabled
return isDisabled
end
ns.IsItemDisabled = IsItemDisabled
local DisabledGear = {}
local IsInventoryItemDisabled = function( slot )
if DisabledGear[ slot ] ~= nil then return DisabledGear[ slot ] end
local isDisabled = FindStringInInventoryItemTooltip( DisableText, slot, true, true )
DisabledGear[ slot ] = isDisabled
return isDisabled
end
ns.IsInventoryItemDisabled = IsInventoryItemDisabled
local function IsAbilityDisabled( ability )
if ability.item then return IsItemDisabled( ability.item ) end
if ability.id > 0 then return IsSpellDisabled( ability.id ) end
return false
end
ns.IsAbilityDisabled = IsAbilityDisabled
local ResetDisabledGearAndSpells = function()
wipe( DisabledSpells )
wipe( DisabledItems )
wipe( DisabledGear )
end
ns.ResetDisabledGearAndSpells = ResetDisabledGearAndSpells
Hekili.FindStringInTooltip = FindStringInTooltip
Hekili.FindStringInSpellTooltip = FindStringInSpellTooltip
Hekili.FindStringInItemTooltip = FindStringInItemTooltip
Hekili.FindStringInInventoryItemTooltip = FindStringInInventoryItemTooltip
Hekili.FindStringInConduitTooltip = FindStringInConduitTooltip
Hekili.IsSpellDisabled = IsSpellDisabled
Hekili.IsItemDisabled = IsItemDisabled
Hekili.IsInventoryItemDisabled = IsInventoryItemDisabled
-- Check Covenant spells to disable them.
local CovenantSpells = {
[300728] = 1,
[304971] = 1,
[306830] = 1,
[307443] = 1,
[307865] = 1,
[308491] = 1,
[310454] = 1,
[311648] = 1,
[312202] = 1,
[312321] = 1,
[314791] = 1,
[314793] = 1,
[315443] = 1,
[316958] = 1,
[317009] = 1,
[317485] = 1,
[320674] = 1,
[321792] = 1,
[323546] = 1,
[323547] = 1,
[323639] = 1,
[323654] = 1,
[323673] = 1,
[323764] = 1,
[324128] = 1,
[324143] = 1,
[324149] = 1,
[324220] = 1,
[324386] = 1,
[324631] = 1,
[324724] = 1,
[325013] = 1,
[325020] = 1,
[325028] = 1,
[325216] = 1,
[325283] = 1,
[325289] = 1,
[325640] = 1,
[325886] = 1,
[326059] = 1,
[326434] = 1,
[326647] = 1,
[326860] = 1,
[327104] = 1,
[327661] = 1,
[328204] = 1,
[328231] = 1,
[328281] = 1,
[328282] = 1,
[328305] = 1,
[328547] = 1,
[328620] = 1,
[328622] = 1,
[328923] = 1,
[328930] = 1,
[330325] = 1,
[355589] = 1,
[356532] = 1,
}
local IsCovenantSpell = function( spellID )
return CovenantSpells[ spellID ] == 1
end
ns.IsCovenantSpell = IsCovenantSpell
local covenantsDisabled = nil
local AreCovenantsDisabled = function()
if covenantsDisabled == nil then
if FindStringInConduitTooltip( DisableText, 5, true, true ) then
covenantsDisabled = true
return true
end
covenantsDisabled = false
return false
end
return covenantsDisabled
end
ns.AreCovenantsDisabled = AreCovenantsDisabled
local IsDisabledCovenantSpell = function( spellID )
return CovenantSpells[ spellID ] and AreCovenantsDisabled()
end
ns.IsDisabledCovenantSpell = IsDisabledCovenantSpell
local WipeCovenantCache = function()
covenantsDisabled = nil
end
ns.WipeCovenantCache = WipeCovenantCache
end
do
local itemCache = {}
function ns.CachedGetItemInfo( id )
if itemCache[ id ] then
return unpack( itemCache[ id ] )
end
local item = { GetItemInfo( id ) }
if item and item[ 1 ] then
itemCache[ id ] = item
return unpack( item )
end
end
end
-- Atlas -> Texture Stuff
do
local db = {}
local function AddTexString( name, file, width, height, left, right, top, bottom )
local pctWidth = right - left
local realWidth = width / pctWidth
local lPoint = left * realWidth
local pctHeight = bottom - top
local realHeight = height / pctHeight
local tPoint = top * realHeight
db[ name ] = format( "|T%s:%%d:%%d:%%d:%%d:%d:%d:%d:%d:%d:%d:%%s|t", file, realWidth, realHeight, lPoint, lPoint + width, tPoint, tPoint + height )
end
local function GetTexString( name, width, height, x, y, r, g, b )
return db[ name ] and format( db[ name ], width or 0, height or 0, x or 0, y or 0, ( r and g and b and ( r .. ":" .. g .. ":" .. b ) or "" ) ) or ""
end
local function AtlasToString( atlas, width, height, x, y, r, g, b )
if db[ atlas ] then
return GetTexString( atlas, width, height, x, y, r, g, b )
end
local a = C_Texture.GetAtlasInfo( atlas )
if not a then return atlas end
AddTexString( atlas, a.file, a.width, a.height, a.leftTexCoord, a.rightTexCoord, a.topTexCoord, a.bottomTexCoord )
return GetTexString( atlas, width, height, x, y, r, g, b )
end
local function GetAtlasFile( atlas )
local a = C_Texture.GetAtlasInfo( atlas )
return a and a.file or atlas
end
local function GetAtlasCoords( atlas )
local a = C_Texture.GetAtlasInfo( atlas )
return a and { a.leftTexCoord, a.rightTexCoord, a.topTexCoord, a.bottomTexCoord }
end
ns.AddTexString, ns.GetTexString, ns.AtlasToString, ns.GetAtlasFile, ns.GetAtlasCoords = AddTexString, GetTexString, AtlasToString, GetAtlasFile, GetAtlasCoords
end
function Hekili:GetSpec()
return state.spec.id and class.specs[ state.spec.id ]
end
function Hekili:IsValidSpec()
return state.spec.id and class.specs[ state.spec.id ] ~= nil
end
function Hekili:GetLoadoutExportString()
-- Current as of 10.1.0.48480
local bitWidthHeaderVersion = 8
local bitWidthSpecID = 16
local bitWidthRanksPurchased = 6
-- Cannot force-load as needed without causing taint, so this simply replicates existing Blizzard functionality.
if IsAddOnLoaded( "Blizzard_ClassTalentUI" ) then
bitWidthHeaderVersion = ClassTalentImportExportMixin.bitWidthHeaderVersion
bitWidthSpecID = ClassTalentImportExportMixin.bitWidthSpecID
bitWidthRanksPurchased = ClassTalentImportExportMixin.bitWidthRanksPurchased
end
local es = ExportUtil.MakeExportDataStream()
local configID = C_ClassTalents.GetActiveConfigID() or -1
local configInfo = C_Traits.GetConfigInfo( configID )
if not configInfo then return "Export Unavailable" end
local currentSpecID = PlayerUtil.GetCurrentSpecID()
local treeID = configInfo.treeIDs[ 1 ]
local treeHash = C_Traits.GetTreeHash( treeID )
local serializationVersion = C_Traits.GetLoadoutSerializationVersion()
es:AddValue( bitWidthHeaderVersion, serializationVersion )
es:AddValue( bitWidthSpecID, currentSpecID )
-- treeHash is a 128bit hash, passed as an array of 16, 8-bit values
for i, hashVal in ipairs( treeHash ) do
es:AddValue( 8, hashVal )
end
local treeNodes = C_Traits.GetTreeNodes( treeID )
for i, treeNodeID in ipairs( treeNodes ) do
local treeNode = C_Traits.GetNodeInfo( configID, treeNodeID )
local isNodeSelected = treeNode.ranksPurchased > 0
local isPartiallyRanked = treeNode.ranksPurchased ~= treeNode.maxRanks
local isChoiceNode = treeNode.type == Enum.TraitNodeType.Selection
es:AddValue( 1, isNodeSelected and 1 or 0 )
if ( isNodeSelected ) then
es:AddValue( 1, isPartiallyRanked and 1 or 0 )
if ( isPartiallyRanked ) then
es:AddValue( bitWidthRanksPurchased, treeNode.ranksPurchased )
end
es:AddValue( 1, isChoiceNode and 1 or 0 )
if ( isChoiceNode) then
local entryIndex = 0
for i, entryID in ipairs( treeNode.entryIDs ) do
if ( entryID == treeNode.activeEntry.entryID ) then
entryIndex = i
break
end
end
if ( entryIndex <= 0 or entryIndex > 4 ) then
return "error: unable to generate"
end
-- store entry index as zero-index
es:AddValue( 2, entryIndex - 1 )
end
end
end
return es:GetExportString()
end
do
local cache = {}
function Hekili:Loadstring( str )
if cache[ str ] then return cache[ str ][ 1 ], cache[ str ][ 2 ] end
local func, warn = loadstring( str )
cache[ str ] = { func, warn }
return func, warn
end
end
do
local marked = {}
local supermarked = {}
local pool = {}
function ns.Mark( table, key )
local data = remove( pool ) or {}
data.t = table
data.k = key
insert( marked, data )
end
function ns.SuperMark( table, keys )
supermarked[ table ] = keys
end
function ns.AddToSuperMark( table, key )
local sm = supermarked[ table ]
if sm then
insert( sm, key )
end
end
function ns.ClearMarks( super )
if super then
for t, keys in pairs( supermarked ) do
for key in pairs( keys ) do
rawset( t, key, nil )
end
end
return
end
local data = remove( marked )
while( data ) do
rawset( data.t, data.k, nil )
insert( pool, data )
data = remove( marked )
end
end
Hekili.Maintenance = {
Dirty = marked,
Cleaned = pool
}
end