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.

379 lines
10 KiB

-- Hekili.lua
-- April 2014
local addon, ns = ...
Hekili = LibStub("AceAddon-3.0"):NewAddon( "Hekili", "AceConsole-3.0", "AceSerializer-3.0" )
Hekili.Version = GetAddOnMetadata( "Hekili", "Version" )
Hekili.Flavor = GetAddOnMetadata( "Hekili", "X-Flavor" ) or "Retail"
local format = string.format
local insert, concat = table.insert, table.concat
local buildStr, _, _, buildNum = GetBuildInfo()
Hekili.CurrentBuild = buildNum
if Hekili.Version == ( "@" .. "project-version" .. "@" ) then
Hekili.Version = format( "Dev-%s (%s)", buildStr, date( "%Y%m%d" ) )
Hekili.IsDev = true
end
Hekili.AllowSimCImports = true
Hekili.IsRetail = function()
return Hekili.Flavor == "Retail"
end
Hekili.IsWrath = function()
return Hekili.Flavor == "Wrath"
end
Hekili.IsClassic = function()
return Hekili.IsWrath()
end
Hekili.IsDragonflight = function()
return buildNum >= 100000
end
Hekili.BuiltFor = 100205
Hekili.GameBuild = buildStr
ns.PTR = buildNum > 100205
ns.Patrons = "|cFFFFD100Current Status|r\n\n"
.. "All existing specializations are currently supported, though healer priorities are experimental and focused on rotational DPS only.\n\n"
.. "If you find odd recommendations or other issues, please follow the |cFFFFD100Issue Reporting|r link below and submit all the necessary information to have your issue investigated.\n\n"
.. "Please do not submit tickets for routine priority updates (i.e., from SimulationCraft). I will routinely update those when they are published. Thanks!"
do
local cpuProfileDB = {}
function Hekili:ProfileCPU( name, func )
cpuProfileDB[ name ] = func
end
ns.cpuProfile = cpuProfileDB
local frameProfileDB = {}
function Hekili:ProfileFrame( name, f )
frameProfileDB[ name ] = f
end
ns.frameProfile = frameProfileDB
end
ns.lib = {
Format = {}
}
-- 04072017: Let's go ahead and cache aura information to reduce overhead.
ns.auras = {
target = {
buff = {},
debuff = {}
},
player = {
buff = {},
debuff = {}
}
}
Hekili.Class = {
specs = {},
num = 0,
file = "NONE",
initialized = false,
resources = {},
resourceAuras = {},
talents = {},
pvptalents = {},
auras = {},
auraList = {},
powers = {},
gear = {},
setBonuses = {},
knownAuraAttributes = {},
stateExprs = {},
stateFuncs = {},
stateTables = {},
abilities = {},
abilityByName = {},
abilityList = {},
itemList = {},
itemMap = {},
itemPack = {
lists = {
items = {}
}
},
packs = {},
pets = {},
totems = {},
potions = {},
potionList = {},
hooks = {},
range = 8,
settings = {},
stances = {},
toggles = {},
variables = {},
}
Hekili.Scripts = {
DB = {},
Channels = {},
PackInfo = {},
}
Hekili.State = {}
ns.hotkeys = {}
ns.keys = {}
ns.queue = {}
ns.targets = {}
ns.TTD = {}
ns.UI = {
Displays = {},
Buttons = {}
}
ns.debug = {}
ns.snapshots = {}
function Hekili:Query( ... )
local output = ns
for i = 1, select( '#', ... ) do
output = output[ select( i, ... ) ]
end
return output
end
function Hekili:Run( ... )
local n = select( "#", ... )
local fn = select( n, ... )
local func = ns
for i = 1, fn - 1 do
func = func[ select( i, ... ) ]
end
return func( select( fn, ... ) )
end
local debug = ns.debug
local active_debug
local current_display
local lastIndent = 0
function Hekili:SetupDebug( display )
if not self.ActiveDebug then return end
if not display then return end
current_display = display
debug[ current_display ] = debug[ current_display ] or {
log = {},
index = 1
}
active_debug = debug[ current_display ]
active_debug.index = 1
lastIndent = 0
local pack = self.State.system.packName
if not pack then return end
self:Debug( "New Recommendations for [ %s ] requested at %s ( %.2f ); using %s( %s ) priority.", display, date( "%H:%M:%S"), GetTime(), self.DB.profile.packs[ pack ].builtIn and "built-in " or "", pack )
end
function Hekili:Debug( ... )
if not self.ActiveDebug then return end
if not active_debug then return end
local indent, text = ...
local start
if type( indent ) ~= "number" then
indent = lastIndent
text = ...
start = 2
else
lastIndent = indent
start = 3
end
local prepend = format( indent > 0 and ( "%" .. ( indent * 4 ) .. "s" ) or "%s", "" )
text = text:gsub("\n", "\n" .. prepend )
text = format( "%" .. ( indent > 0 and ( 4 * indent ) or "" ) .. "s", "" ) .. text
if select( start, ... ) ~= nil then
active_debug.log[ active_debug.index ] = format( text, select( start, ... ) )
else
active_debug.log[ active_debug.index ] = text
end
active_debug.index = active_debug.index + 1
end
local snapshots = ns.snapshots
local hasScreenshotted = false
function Hekili:SaveDebugSnapshot( dispName )
local snapped = false
local formatKey = ns.formatKey
local state = Hekili.State
for k, v in pairs( debug ) do
if not dispName or dispName == k then
for i = #v.log, v.index, -1 do
v.log[ i ] = nil
end
-- Store aura data.
local auraString = "\nplayer_buffs:"
local now = GetTime()
local class = Hekili.Class
for i = 1, 40 do
local name, _, count, debuffType, duration, expirationTime, source, _, _, spellId, canApplyAura, isBossDebuff, castByPlayer = UnitBuff( "player", i )
if not name then break end
local aura = class.auras[ spellId ]
local key = aura and aura.key
if key and not state.auras.player.buff[ key ] then key = key .. " [MISSING]" end
auraString = format( "%s\n %6d - %-40s - %3d - %-6.2f", auraString, spellId, key or ( "*" .. formatKey( name ) ), count > 0 and count or 1, expirationTime > 0 and ( expirationTime - now ) or 3600 )
end
auraString = auraString .. "\n\nplayer_debuffs:"
for i = 1, 40 do
local name, _, count, debuffType, duration, expirationTime, source, _, _, spellId, canApplyAura, isBossDebuff, castByPlayer = UnitDebuff( "player", i )
if not name then break end
local aura = class.auras[ spellId ]
local key = aura and aura.key
if key and not state.auras.player.debuff[ key ] then key = key .. " [MISSING]" end
auraString = format( "%s\n %6d - %-40s - %3d - %-6.2f", auraString, spellId, key or ( "*" .. formatKey( name ) ), count > 0 and count or 1, expirationTime > 0 and ( expirationTime - now ) or 3600 )
end
if not UnitExists( "target" ) then
auraString = auraString .. "\n\ntarget_auras: target does not exist"
else
auraString = auraString .. "\n\ntarget_buffs:"
for i = 1, 40 do
local name, _, count, debuffType, duration, expirationTime, source, _, _, spellId, canApplyAura, isBossDebuff, castByPlayer = UnitBuff( "target", i )
if not name then break end
local aura = class.auras[ spellId ]
local key = aura and aura.key
if key and not state.auras.target.buff[ key ] then key = key .. " [MISSING]" end
auraString = format( "%s\n %6d - %-40s - %3d - %-6.2f", auraString, spellId, key or ( "*" .. formatKey( name ) ), count > 0 and count or 1, expirationTime > 0 and ( expirationTime - now ) or 3600 )
end
auraString = auraString .. "\n\ntarget_debuffs:"
for i = 1, 40 do
local name, _, count, debuffType, duration, expirationTime, source, _, _, spellId, canApplyAura, isBossDebuff, castByPlayer = UnitDebuff( "target", i, "PLAYER" )
if not name then break end
local aura = class.auras[ spellId ]
local key = aura and aura.key
if key and not state.auras.target.debuff[ key ] then key = key .. " [MISSING]" end
auraString = format( "%s\n %6d - %-40s - %3d - %-6.2f", auraString, spellId, key or ( "*" .. formatKey( name ) ), count > 0 and count or 1, expirationTime > 0 and ( expirationTime - now ) or 3600 )
end
end
auraString = auraString .. "\n\n"
insert( v.log, 1, auraString )
insert( v.log, 1, "targets: " .. ( Hekili.TargetDebug or "no data" ) )
insert( v.log, 1, self:GenerateProfile() )
local custom = ""
local pack = self.DB.profile.packs[ state.system.packName ]
if not pack.builtIn then
custom = format( " |cFFFFA700(Custom: %s[%d])|r", state.spec.name, state.spec.id )
end
local overview = format( "%s%s; %s|r", state.system.packName, custom, dispName or state.display )
local recs = Hekili.DisplayPool[ dispName or state.display ].Recommendations
for i, rec in ipairs( recs ) do
if not rec.actionName then
if i == 1 then
overview = format( "%s - |cFF666666N/A|r", overview )
end
break
end
overview = format( "%s%s%s|cFFFFD100(%0.2f)|r", overview, ( i == 1 and " - " or ", " ), class.abilities[ rec.actionName ].name, rec.time )
end
insert( v.log, 1, overview )
local snap = {
header = "|cFFFFD100[" .. date( "%H:%M:%S" ) .. "]|r " .. overview,
log = concat( v.log, "\n" ),
data = ns.tableCopy( v.log ),
recs = {}
}
insert( snapshots, snap )
snapped = true
end
end
-- Limit screenshot to once per login.
if snapped then
if Hekili.DB.profile.screenshot and not hasScreenshotted then
Screenshot()
hasScreenshotted = true
end
return true
end
return false
end
Hekili.Snapshots = ns.snapshots
ns.Tooltip = CreateFrame( "GameTooltip", "HekiliTooltip", UIParent, "GameTooltipTemplate" )
Hekili:ProfileFrame( "HekiliTooltip", ns.Tooltip )