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.

1879 lines
72 KiB

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--global name declaration
--use lua-language-server annotations to help the linter:
--https://github.com/LuaLS/lua-language-server/wiki/Annotations#documenting-types
--follow definitions declared in the file definitions.lua
--follow game api definitions in the file LibLuaServer.lua
_ = nil
_G.Details = LibStub("AceAddon-3.0"):NewAddon("_detalhes", "AceTimer-3.0", "AceComm-3.0", "AceSerializer-3.0", "NickTag-1.0")
--add the original name to the global namespace
_detalhes = _G.Details --[[GLOBAL]]
__details_debug = __details_debug or {}
if (__details_debug.prescience_timeline) then
wipe(__details_debug.prescience_timeline)
end
local addonName, Details222 = ...
local version, build, date, tvs = GetBuildInfo()
Details.build_counter = 13642
Details.alpha_build_counter = 13642 --if this is higher than the regular counter, use it instead
Details.dont_open_news = true
Details.game_version = version
Details.userversion = version .. " " .. Details.build_counter
Details.realversion = 165 --core version, this is used to check API version for scripts and plugins (see alias below)
Details.gametoc = tvs
Details.APIVersion = Details.realversion --core version
Details.version = Details.userversion .. " (core " .. Details.realversion .. ")" --simple stirng to show to players
Details.acounter = 1 --in case of a second release with the same .build_counter
Details.curseforgeVersion = C_AddOns and C_AddOns.GetAddOnMetadata and C_AddOns.GetAddOnMetadata("Details", "Version")
if (not Details.curseforgeVersion and GetAddOnMetadata) then
Details.curseforgeVersion = GetAddOnMetadata("Details", "Version")
end
function Details:GetCoreVersion()
return Details.realversion
end
Details.BFACORE = 131 --core version on BFA launch
Details.SHADOWLANDSCORE = 143 --core version on Shadowlands launch
Details.DRAGONFLIGHT = 147 --core version on Dragonflight launch
Details.V11CORE = 160 --core version on V11 launch
Details = Details
local gameVersionPrefix = "VCT" --v1, v4, v11
Details.gameVersionPrefix = gameVersionPrefix
pcall(function() Details.version_alpha_id = tonumber(Details.curseforgeVersion:match("%-(%d+)%-")) end)
--WD 10288 RELEASE 10.0.2
--WD 10288 ALPHA 21 10.0.2
function Details.GetVersionString()
local curseforgeVersion = Details.curseforgeVersion or ""
local alphaId = curseforgeVersion:match("%-(%d+)%-")
if (not alphaId) then
--this is a release version
alphaId = "RELEASE"
else
alphaId = "ALPHA " .. alphaId
end
return Details.gameVersionPrefix .. " " .. Details.build_counter .. " " .. alphaId .. " " .. Details.game_version .. ""
end
Details.DefaultTooltipIconSize = 20
--namespace for the player breakdown window
Details.PlayerBreakdown = {}
Details222.PlayerBreakdown = {
DamageSpellsCache = {}
}
Details222.StartUp = {}
Details222.Unknown = _G["UNKNOWN"]
--namespace color
Details222.ColorScheme = {
["gradient-background"] = {0.1215, 0.1176, 0.1294, 0.8},
}
function Details222.ColorScheme.GetColorFor(colorScheme)
return Details222.ColorScheme[colorScheme]
end
function Details222.DebugMsg(...)
if (Details.debug) then
print("|cFFCCAAAADetails! Debug:|r", ...)
end
end
--cache of frames to call :SetColor() when the color scheme changes
Details222.RegisteredFramesToColor = {}
Details222.TrainingDummiesNpcId = {
[194649] = true, --valdraken
[189617] = true, --valdraken
[194644] = true, --valdraken
[198594] = true, --valdraken
[194643] = true, --valdraken
[189632] = true, --valdraken
[194648] = true, --valdraken
[194646] = true, --valdraken
[197834] = true, --valdraken
[31146] = true, --orgrimmar
[153285] = true, --orgrimmar
[114840] = true, --orgrimmar
[114832] = true, --stormwind
[153292] = true, --stormwind
}
---@type details_storage_feature
---@diagnostic disable-next-line: missing-fields
local storage = {
DiffNames = {"normal", "heroic", "mythic", "raidfinder", "10player", "25player", "10playerheroic", "25playerheroic", "raidfinderclassic", "raidfindertimewalking", "timewalking"},
DiffNamesHash = {normal = 14, heroic = 15, mythic = 16, raidfinder = 17, ["10player"] = 3, ["25player"] = 4, ["10playerheroic"] = 5, ["25playerheroic"] = 6, raidfinderclassic = 7, raidfindertimewalking = 151, timewalking = 33},
DiffIdToName = {
[14] = "normal",
[15] = "heroic",
[16] = "mythic",
[17] = "raidfinder",
[3] = "10player",
[4] = "25player",
[5] = "10playerheroic",
[6] = "25playerheroic",
[7] = "raidfinderclassic",
[8] = "mythicdungeon",
[151] = "raidfindertimewalking",
[33] = "timewalking"
},
IsDebug = false
}
Details222.storage = storage
--namespace for damage spells (spellTable)
Details222.DamageSpells = {}
--namespace for texture
Details222.Textures = {}
Details222.Debug = {
DebugPets = false,
DebugPlayerPets = false,
DebugBuff = false,
}
Details222.Tvs = tvs
--namespace for pet
Details222.Pets = {}
Details222.PetContainer = {
---@type table<guid, petdata>
Pets = {},
---@type table<guid, boolean>
IgnoredActors = {},
---table that stores the player guid as keys and their petguid as values
---this is useful to know which pets are the legit class pet from the UNIT_PET event
---@type table<guid, guid>
UnitPetCache = {},
}
--auto run code
Details222.AutoRunCode = {}
--options panel
Details222.OptionsPanel = {}
--store bar icons (left side of the damage bar)
Details222.BarIconSetList = {}
Details222.Instances = {}
Details222.Combat = {}
Details222.MythicPlus = {
Charts = {},
Frames = {},
}
Details222.Notes = {}
Details222.MythicPlusBreakdown = {}
Details222.EJCache = {}
Details222.Segments = {}
Details222.Tables = {}
Details222.Mixins = {}
Details222.Cache = {}
Details222.Perf = {}
Details222.Cooldowns = {}
Details222.GarbageCollector = {}
Details222.BreakdownWindow = {}
Details222.PlayerStats = {}
Details222.LoadSavedVariables = {}
Details222.SaveVariables = {}
Details222.GuessSpecSchedules = {
Schedules = {},
}
Details222.Profiling = {}
Details222.ProfilingCache = {}
Details222.TimeMachine = {}
Details222.OnUseItem = {Trinkets = {}}
Details222.Date = {
GetDateForLogs = function()
return _G.date("%Y-%m-%d %H:%M:%S")
end,
}
Details222.ClassCache = {}
Details222.ClassCache.ByName = {}
Details222.ClassCache.ByGUID = {}
Details222.UnitIdCache = {}
Details222.Roskash = {}
Details222.SpecHelpers = {
[1473] = {},
}
Details222.IgnoredWorldAuras = {}
Details222.OneHourAuras = {}
Details222.Parser = {}
Details222.Actors = {}
Details222.CurrentDPS = {
Cache = {}
}
--store all data from the encounter journal
Details222.EncounterJournalDump = {}
--aura scanner
Details222.AuraScan = {}
---@type instancedifficulty
Details222.InstanceDifficulty = {
["DungeonNormal"] = 1,
["DungeonHeroic"] = 2,
["DungeonMythic"] = 23,
["DungeonMythicPlus"] = 8,
["RaidLFR"] = 17,
["RaidNormal"] = 14,
["RaidHeroic"] = 15,
["RaidMythic"] = 16,
}
Details222.DHook = hooksecurefunc
local emptyFunction = function()end
local emptyTable = {}
---context manager is a system that evaluates where the player is and create a set of extra rules that fit the content the player is doing
---@class contextmanager : table
---@field instanceType string
---@field instanceName string
---@field instanceId number
---@field instanceDifficulty number
---@field lastInstanceType string
---@field lastInstanceName string
---@field lastInstanceDifficulty number
---@field contextId string
---@field bContextStarted boolean
---@field bContextFinished boolean
---@field bHasContext boolean
---@field fHasLostInterest function
---@field fOnContextFinished function
---@field fOnCombatFinished function
---@field eventFrame frame
---@field DetailsEventListener table
---@field contextEventTable table
---@field StartContext function
---@field CheckContextInterest function
---@field FinishContext function
---@field GetContext function
--tells what is the activity the player is doing
Details222.ContextManager = {
instanceType = "INIT",
instanceName = "INIT",
instanceDifficulty = 0,
lastInstanceType = "INIT",
lastInstanceName = "INIT",
lastInstanceDifficulty = 0,
contextId = "INIT",
bContextStarted = false,
bContextFinished = false,
bHasContext = false,
fOnContextFinished = emptyFunction,
fHasLostInterest = emptyFunction,
fOnCombatFinished = emptyFunction,
contextEventTable = emptyTable,
eventFrame = CreateFrame("frame"),
---start a new context, this is called from the CheckContextInterest() function
---@param self contextmanager
---@param instanceId number
---@param instanceName string
---@param instanceType string
---@param difficultyId number
---@param contextEventTable table
---@param fOnCombatFinished function run when details! finishes a combat
---@param fOnContextFinished function run when the context is finished
---@param fHasLostInterest function run when CheckContextInterest() fails to find a context
StartContext = function(self, instanceId, instanceName, instanceType, difficultyId, contextEventTable, fOnCombatFinished, fOnContextFinished, fHasLostInterest)
self.instanceType = instanceType
self.instanceName = instanceName
self.instanceId = instanceId
self.instanceDifficulty = difficultyId
self.bContextStarted = true
self.bContextFinished = false
self.bHasContext = true
self.fOnContextFinished = fOnContextFinished
self.fHasLostInterest = fHasLostInterest
self.fOnCombatFinished = fOnCombatFinished
self.contextEventTable = contextEventTable
--create an event listener to grab the event when Details! finishes a combat
if (not self.DetailsEventListener) then
self.DetailsEventListener = Details:CreateEventListener()
end
self.DetailsEventListener:UnregisterEvent("COMBAT_PLAYER_LEAVE")
--register the onFinishCombat for the context
self.DetailsEventListener:RegisterEvent("COMBAT_PLAYER_LEAVE", fOnCombatFinished)
--unregister all events
self.eventFrame:UnregisterAllEvents()
--register the events that the context require
for i = 1, #contextEventTable.events do
self.eventFrame:RegisterEvent(contextEventTable.events[i])
end
--if the callback function returns true, the context is finished
self.eventFrame:SetScript("OnEvent", function(eventFrame, event, ...)
if (contextEventTable.callback(event, ...)) then
Details222.DebugMsg("context manager event", event)
--context completed
Details222.DebugMsg("Context Completed!")
C_Timer.After(1, fOnContextFinished)
C_Timer.After(1.1, function() self:FinishContext() end)
end
end)
Details222.DebugMsg("a new context has been set.")
end,
---check if the player is in a context of interest
---@param self contextmanager
---@param instanceId number
---@param instanceName string
---@param instanceType string
---@param difficultyId number
CheckContextInterest = function(self, instanceId, instanceName, instanceType, difficultyId)
Details222.DebugMsg("Checking for new context:", instanceId, instanceName, instanceType, difficultyId)
--normal, heroic and mythic0 dungeons on Retail
local diffTable = Details222.InstanceDifficulty
if (difficultyId == diffTable.DungeonNormal or difficultyId == diffTable.DungeonHeroic or difficultyId == diffTable.DungeonMythic) then
if (DetailsFramework.IsDragonflightAndBeyond()) then
--check if the player is in the same context
if (self.bHasContext and self.instanceId == instanceId and self.instanceType == instanceType and self.instanceName == instanceName and self.instanceDifficulty == difficultyId) then
return
end
do return end
--if a context is found, finishes it before a new one is created
if (self.bHasContext) then
--discard the context
Details222.DebugMsg("had an active context, finishing it.")
self:FinishContext()
end
--set a new context where at the end of the dungeon it creates an overall segment for the run
--function to verify if context is finished, in this case if all objectives of the dungeon has been completed by listening to the SCENARIO_COMPLETED event
local contextEventTable = {
events = {"SCENARIO_COMPLETED"},
callback = function(...)
--when a context return true, the context is finished and will trigger a call on the fOnContextFinished function
return true
end
}
--create a contextId to tag combats that are part of the same context
self.contextId = instanceName .. tostring(time())
--called when a combat finishes and this context is still active
local fOnCombatFinished = function()
local currentCombat = Details:GetCurrentCombat()
currentCombat.context = self.contextId
end
---this function evaluates if this context has lost its interest and should be discarded, return true if the context is no longer valid
local fHasLostInterest = function(instanceId, instanceName, instanceType, difficultyId)
--check if the player is still in the same context
if (self.instanceId ~= instanceId or self.instanceType ~= instanceType or self.instanceName ~= instanceName or self.instanceDifficulty ~= difficultyId) then
return true
end
end
--will ba called when the context finishes, in this case when the SCENARIO_COMPLETED event is triggered
local fOnContextFinished = function()
--check if this is not a mythic+ run
if (C_ChallengeMode) then
if (C_ChallengeMode.GetActiveChallengeMapID() or C_ChallengeMode.GetActiveKeystoneInfo() or C_ChallengeMode.IsChallengeModeActive()) then
print("did not start as this is a m+ run")
return
else
print("this is not a m+ run")
end
end
---@type combat[]
local interestCombats = {}
--get all segments
local segments = Details:GetCombatSegments()
for i = 1, #segments do
local segment = segments[i]
if (segment.context == self.contextId) then
interestCombats[#interestCombats+1] = segment
end
end
if (#interestCombats > 0) then
--start a new combat
Details222.StartCombat()
Details222.DebugMsg("merging", #interestCombats, "combats into a single combat.")
---@type combat
local currentCombat = Details:GetCurrentCombat()
--iterate over all interest combats
for i = 1, #interestCombats do
local interestCombat = interestCombats[i]
--add the combat to the new combat
currentCombat:AddCombat(interestCombat, i == 1, i == #interestCombats)
end
Details222.DebugMsg("combat time:", currentCombat:GetCombatTime())
--finish the new combat
Details:EndCombat()
currentCombat.is_trash = false
currentCombat.combat_type = DETAILS_SEGMENTTYPE_DUNGEON_OVERALL
currentCombat.is_dungeon_overall = true
end
Details222.DebugMsg("overall segment has been created.")
end
self:StartContext(instanceId, instanceName, instanceType, difficultyId, contextEventTable, fOnCombatFinished, fOnContextFinished, fHasLostInterest)
return
end
else
--if no context is found, check if there is a current context and check if it lost its interest
if (self.bHasContext) then
if (self.fHasLostInterest(self, instanceId, instanceName, instanceType, difficultyId)) then
Details222.DebugMsg("no context found, but context is active, finishing the current context.")
--discard the context
self:FinishContext()
end
end
end
end,
---finish the current context
---@param self contextmanager
FinishContext = function(self)
if (not self.bHasContext or not self.bContextStarted or self.bContextFinished) then
return
end
--mark this context as finished
self.bContextFinished = true
--reset context
self.instanceType = "INIT"
self.instanceName = "INIT"
self.contextId = "INIT"
self.instanceId = -1
self.instanceDifficulty = 0
self.bContextStarted = false
self.bHasContext = false
self.fOnContextFinished = emptyFunction
self.fHasLostInterest = emptyFunction
self.fOnCombatFinished = emptyFunction
self.contextEventTable = emptyTable
end,
---return the current contextIndex
---@param self contextmanager
---@return number|boolean, string?, string?, number?
GetContext = function(self)
if (self.bHasContext) then
return self.instanceId, self.instanceName, self.instanceType, self.instanceDifficulty
end
return false
end,
}
local GetSpellInfo = C_Spell and C_Spell.GetSpellInfo or GetSpellInfo
Details222.GetSpellInfo = GetSpellInfo
local UnitBuff = C_UnitAuras and C_UnitAuras.GetBuffDataByIndex or UnitBuff
Details222.UnitBuff = UnitBuff
local UnitDebuff = C_UnitAuras and C_UnitAuras.GetDebuffDataByIndex or UnitDebuff
Details222.UnitDebuff = UnitDebuff
if (C_Spell and C_Spell.GetSpellInfo) then
Details222.GetSpellInfo = function(...)
local result = GetSpellInfo(...)
if result then
return result.name, 1, result.iconID
end
end
end
if (C_UnitAuras and C_UnitAuras.GetAuraDataByIndex) then
Details222.UnitBuff = function(unitToken, index, filter)
local auraData = UnitBuff(unitToken, index, filter)
if (not auraData) then
return nil
end
return AuraUtil.UnpackAuraData(auraData)
end
Details222.UnitDebuff = function(unitToken, index, filter)
local auraData = UnitDebuff(unitToken, index, filter)
if (not auraData) then
return nil
end
return AuraUtil.UnpackAuraData(auraData)
end
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--initialization stuff
local _
do
local _detalhes = _G.Details
_detalhes.resize_debug = {}
local Loc = _G.LibStub("AceLocale-3.0"):GetLocale("Details")
local news = {
{"v11.0.7.13388.161", "January 19th, 2025"},
"Check Details! Damage Meter github page for update notes.",
}
local newsString = "|cFFF1F1F1"
for i = 1, #news do
local line = news[i]
if (type(line) == "table") then
local version = line[1]
local date = line[2]
newsString = newsString .. "|cFFFFFF00" .. version .. " (|cFFFF8800" .. date .. "|r):|r\n\n"
else
if (line ~= "") then
newsString = newsString .. "|cFFFFFF00-|r " .. line .. "\n\n"
else
newsString = newsString .. " \n"
end
end
end
Loc["STRING_VERSION_LOG"] = newsString
Loc ["STRING_DETAILS1"] = "|cffffaeaeDetails!:|r "
--startup
_detalhes.max_windowline_columns = 11
_detalhes.initializing = true
_detalhes.enabled = true
_detalhes.__index = _detalhes
_detalhes._tempo = time()
_detalhes.debug = false
_detalhes.debug_chr = false
_detalhes.opened_windows = 0
_detalhes.last_combat_time = 0
_detalhes.last_zone_type = "INIT"
_detalhes.last_zone_id = -1
--store functions to create options frame
Details.optionsSection = {}
--containers
--armazenas as fun��es do parser - All parse functions
_detalhes.parser = {}
_detalhes.parser_functions = {}
Details222.parser_frame = CreateFrame("Frame")
Details222.parser_frame:Hide()
_detalhes.pvp_parser_frame = CreateFrame("Frame")
_detalhes.MacroList = {
{Name = "Click on Your Own Bar", Desc = "To open the player details window on your character, like if you click on your bar in the damage window. The number '1' is the window number where it'll click.", MacroText = "/script Details:OpenPlayerDetails(1)"},
{Name = "Open Encounter Breakdown", Desc = "Open the encounter breakdown plugin. Details! Encounter Breakdown (plugin) must be enabled.", MacroText = "/script Details:OpenPlugin ('Encounter Breakdown')"},
{Name = "Open Damage per Phase", Desc = "Open the encounter breakdown plugin in the phase tab. Details! Encounter Breakdown (plugin) must be enabled.", MacroText = "/script Details:OpenPlugin ('Encounter Breakdown'); local a=Details_EncounterDetails and Details_EncounterDetails.buttonSwitchPhases:Click()"},
{Name = "Reset Data", Desc = "Reset the overall and regular segments data. Use 'ResetSegmentOverallData' to reset only the overall.", MacroText = "/script Details:ResetSegmentData()"},
{Name = "Change What the Window Shows", Desc = "Make a window show different data. SetDisplay uses (segment, displayGroup, displayID), the menu from the sword icon is in order (damage = group 1, overheal is: displayGroup 2 displayID 3.", MacroText = "/script Details:GetWindow(1):SetDisplay( DETAILS_SEGMENTID_CURRENT, 4, 5 )"},
{Name = "Toggle Window Height to Max Size", Desc = "Make a window be 450 pixel height, pressing the macro again toggle back to the original size. The number '1' if the window number. Hold a click in any window to show their number.", MacroText = "/script Details:GetWindow(1):ToggleMaxSize()"},
-- /script Details:OpenPlugin ('Advanced Death Logs'); local a = Details_DeathGraphsModeEnduranceButton and Details_DeathGraphsModeEnduranceButton.MyObject:Click()
{Name = "Report What is Shown In the Window", Desc = "Report the current data shown in the window, the number 1 is the window number, replace it to report another window.", MacroText = "/script Details:FastReportWindow(1)"},
}
--current instances of the exp (need to maintain) - deprecated july 2024 - should do this automatically
Details.InstancesToStoreData = { --mapId
[2769] = true, --Liberation of Undermine v11.1 T2
[2657] = true, --Nerub-ar Palace v11 T1
[2294] = true, --Nerub-ar Palace v11 T1
}
--store shield information for absorbs
_detalhes.ShieldCache = {}
--armazena as fun��es dos frames - Frames functions
_detalhes.gump = _G ["DetailsFramework"]
function _detalhes:GetFramework()
return self.gump
end
GameCooltip = GameCooltip2
--anima��es dos icones
_detalhes.icon_animations = {
load = {
in_use = {},
available = {},
},
}
--make a color namespace
Details.Colors = {}
function Details.Colors.GetMenuTextColor()
return "orange"
end
function Details:GetTextureAtlasTable()
return Details.TextureAtlas
end
--armazena as fun��es para inicializa��o dos dados - Metatable functions
_detalhes.refresh = {}
--armazena as fun��es para limpar e guardas os dados - Metatable functions
_detalhes.clear = {}
--armazena a config do painel de fast switch
_detalhes.switch = {}
--armazena os estilos salvos
_detalhes.savedStyles = {}
--armazena quais atributos possue janela de atributos - contain attributes and sub attributos wich have a detailed window (left click on a row)
_detalhes.row_singleclick_overwrite = {}
--report
_detalhes.ReportOptions = {}
--armazena os buffs registrados - store buffs ids and functions
_detalhes.Buffs = {} --initialize buff table
-- cache de grupo
_detalhes.cache_damage_group = {}
_detalhes.cache_healing_group = {}
_detalhes.cache_npc_ids = {}
--cache de specs
_detalhes.cached_specs = {}
_detalhes.cached_talents = {}
--ignored pets
_detalhes.pets_ignored = {}
_detalhes.pets_no_owner = {}
--dual candidates
_detalhes.duel_candidates = {}
--armazena as skins dispon�veis para as janelas
_detalhes.skins = {}
--armazena os hooks das fun��es do parser
---@type table<detailshook, function[]>
_detalhes.hooks = {}
--informa��es sobre a luta do boss atual
_detalhes.encounter_end_table = {}
_detalhes.encounter_table = {}
_detalhes.encounter_counter = {}
_detalhes.encounter_dungeons = {}
--unitId dos inimigos dentro de uma arena
_detalhes.arena_enemies = {}
--reliable char data sources
--actors that are using details! and sent character data, we don't need query inspect on these actors
_detalhes.trusted_characters = {}
--informa��es sobre a arena atual
_detalhes.arena_table = {}
_detalhes.arena_info = {
--need to get the new mapID for 8.0.1
[562] = {file = "LoadScreenBladesEdgeArena", coords = {0, 1, 0.29296875, 0.9375}}, -- Circle of Blood Arena
[617] = {file = "LoadScreenDalaranSewersArena", coords = {0, 1, 0.29296875, 0.857421875}}, --Dalaran Arena
[559] = {file = "LoadScreenNagrandArenaBattlegrounds", coords = {0, 1, 0.341796875, 1}}, --Ring of Trials
[980] = {file = "LoadScreenTolvirArena", coords = {0, 1, 0.29296875, 0.857421875}}, --Tol'Viron Arena
[572] = {file = "LoadScreenRuinsofLordaeronBattlegrounds", coords = {0, 1, 0.341796875, 1}}, --Ruins of Lordaeron
[1134] = {file = "LoadingScreen_Shadowpan_bg", coords = {0, 1, 0.29296875, 0.857421875}}, -- Tiger's Peak
--legion, thanks @pas06 on curse forge for the mapIds
[1552] = {file = "LoadingScreen_ArenaValSharah_wide", coords = {0, 1, 0.29296875, 0.857421875}}, -- Ashmane's Fall
[1504] = {file = "LoadingScreen_BlackrookHoldArena_wide", coords = {0, 1, 0.29296875, 0.857421875}}, --Black Rook Hold
--"LoadScreenOrgrimmarArena", --Ring of Valor
}
Details.IgnoredEnemyNpcsTable = {
[31216] = true, --mirror image
[53006] = true, --spirit link totem
[63508] = true, --xuen
[73967] = true, --xuen
}
function _detalhes:GetArenaInfo (mapid)
local t = _detalhes.arena_info [mapid]
if (t) then
return t.file, t.coords
end
end
_detalhes.battleground_info = {
--need to get the nwee mapID for 8.0.1
[489] = {file = "LoadScreenWarsongGulch", coords = {0, 1, 121/512, 484/512}}, --warsong gulch
[727] = {file = "LoadScreenSilvershardMines", coords = {0, 1, 251/1024, 840/1024}}, --silvershard mines
[529] = {file = "LoadscreenArathiBasin", coords = {0, 1, 126/512, 430/512}}, --arathi basin
[566] = {file = "LoadScreenNetherBattlegrounds", coords = {0, 1, 142/512, 466/512}}, --eye of the storm
[30] = {file = "LoadScreenPvpBattleground", coords = {0, 1, 127/512, 500/512}}, --alterac valley
[761] = {file = "LoadScreenGilneasBG2", coords = {0, 1, 281/1024, 878/1024}}, --the battle for gilneas
[726] = {file = "LoadScreenTwinPeaksBG", coords = {0, 1, 294/1024, 876/1024}}, --twin peaks
[998] = {file = "LoadScreenValleyofPower", coords = {0, 1, 257/1024, 839/1024}}, --temple of kotmogu
[1105] = {file = "LoadScreen_GoldRush", coords = {0, 1, 264/1024, 840/1024}}, --deepwind gorge
[607] = {file = "LoadScreenNorthrendBG", coords = {0, 1, 302/1024, 879/1024}}, --strand of the ancients
[628] = {file = "LOADSCREENISLEOFCONQUEST", coords = {0, 1, 297/1024, 878/1024}}, --isle of conquest
--[] = {file = "", coords = {0, 1, 0, 0}}, --
}
function _detalhes:GetBattlegroundInfo(mapid)
local battlegroundInfo = _detalhes.battleground_info[mapid]
if (battlegroundInfo) then
return battlegroundInfo.file, battlegroundInfo.coords
end
end
--tokenid
_detalhes.TokenID = {
["SPELL_PERIODIC_DAMAGE"] = 1,
["SPELL_EXTRA_ATTACKS"] = 2,
["SPELL_DAMAGE"] = 3,
["SPELL_BUILDING_DAMAGE"] = 4,
["SWING_DAMAGE"] = 5,
["RANGE_DAMAGE"] = 6,
["DAMAGE_SHIELD"] = 7,
["DAMAGE_SPLIT"] = 8,
["RANGE_MISSED"] = 9,
["SWING_MISSED"] = 10,
["SPELL_MISSED"] = 11,
["SPELL_PERIODIC_MISSED"] = 12,
["SPELL_BUILDING_MISSED"] = 13,
["DAMAGE_SHIELD_MISSED"] = 14,
["ENVIRONMENTAL_DAMAGE"] = 15,
["SPELL_HEAL"] = 16,
["SPELL_PERIODIC_HEAL"] = 17,
["SPELL_HEAL_ABSORBED"] = 18,
["SPELL_ABSORBED"] = 19,
["SPELL_AURA_APPLIED"] = 20,
["SPELL_AURA_REMOVED"] = 21,
["SPELL_AURA_REFRESH"] = 22,
["SPELL_AURA_APPLIED_DOSE"] = 23,
["SPELL_ENERGIZE"] = 24,
["SPELL_PERIODIC_ENERGIZE"] = 25,
["SPELL_CAST_SUCCESS"] = 26,
["SPELL_DISPEL"] = 27,
["SPELL_STOLEN"] = 28,
["SPELL_AURA_BROKEN"] = 29,
["SPELL_AURA_BROKEN_SPELL"] = 30,
["SPELL_RESURRECT"] = 31,
["SPELL_INTERRUPT"] = 32,
["UNIT_DIED"] = 33,
["UNIT_DESTROYED"] = 34,
}
---@type table<npcid, textureid>
local npcIdToIcon = {
[98035] = 1378282, --dreadstalker
[17252] = 136216, --felguard
[136404] = 132182, --bilescourge
[136398] = 626007, --illidari satyr
[136403] = 1100177, --void terror
[136402] = 1581747, --ur'zyk
[136399] = 1709931, --visious hellhound
[136406] = 615148, --shivarra
[136407] = 615025, --wrathguard
[136408] = 1709932, --darkhound
}
_detalhes.NpcIdToIcon = npcIdToIcon
--armazena instancias inativas
_detalhes.unused_instances = {}
_detalhes.default_skin_to_use = "Minimalistic"
_detalhes.instance_title_text_timer = {}
--player detail skin
_detalhes.playerdetailwindow_skins = {}
_detalhes.BitfieldSwapDebuffsIDs = {265646, 272407, 269691, 273401, 269131, 260900, 260926, 284995, 292826, 311367, 310567, 308996, 307832, 327414, 337253,
36797, 37122, 362397}
_detalhes.BitfieldSwapDebuffsSpellIDs = {
[360418] = true
}
--auto run code
_detalhes.RunCodeTypes = {
{Name = "On Initialization", Desc = "Run code when Details! initialize or when a profile is changed.", Value = 1, ProfileKey = "on_init"},
{Name = "On Zone Changed", Desc = "Run code when the zone where the player is in has changed (e.g. entered in a raid).", Value = 2, ProfileKey = "on_zonechanged"},
{Name = "On Enter Combat", Desc = "Run code when the player enters in combat.", Value = 3, ProfileKey = "on_entercombat"},
{Name = "On Leave Combat", Desc = "Run code when the player left combat.", Value = 4, ProfileKey = "on_leavecombat"},
{Name = "On Spec Change", Desc = "Run code when the player has changed its specialization.", Value = 5, ProfileKey = "on_specchanged"},
{Name = "On Enter/Leave Group", Desc = "Run code when the player has entered or left a party or raid group.", Value = 6, ProfileKey = "on_groupchange"},
}
--run a function without stopping the execution in case of an error
function Details.SafeRun(func, executionName, ...)
local runToCompletion, errorText = pcall(func, ...)
if (not runToCompletion) then
if (Details.debug) then
Details:Msg("Safe run failed:", executionName, errorText)
end
return false
end
return true
end
--tooltip
_detalhes.tooltip_backdrop = {
bgFile = [[Interface\DialogFrame\UI-DialogBox-Background-Dark]],
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]],
tile = true,
edgeSize = 16,
tileSize = 16,
insets = {left = 3, right = 3, top = 4, bottom = 4}
}
_detalhes.tooltip_border_color = {1, 1, 1, 1}
_detalhes.tooltip_spell_icon = {file = [[Interface\CHARACTERFRAME\UI-StateIcon]], coords = {36/64, 58/64, 7/64, 26/64}}
_detalhes.tooltip_target_icon = {file = [[Interface\Addons\Details\images\icons]], coords = {0, 0.03125, 0.126953125, 0.15625}}
--icons
_detalhes.attribute_icons = [[Interface\AddOns\Details\images\atributos_icones]]
function _detalhes:GetAttributeIcon (attribute)
return _detalhes.attribute_icons, 0.125 * (attribute - 1), 0.125 * attribute, 0, 1
end
--colors
_detalhes.default_backdropcolor = {.094117, .094117, .094117, .8}
_detalhes.default_backdropbordercolor = {0, 0, 0, 1}
--Plugins
--plugin templates
DetailsFramework:NewColor("DETAILS_PLUGIN_BUTTONTEXT_COLOR", 0.9999, 0.8196, 0, 1)
DetailsFramework:NewColor("DETAILS_HEADER_YELLOW", 227/255, 186/255, 4/255)
DetailsFramework:InstallTemplate("button", "DETAILS_PLUGINPANEL_BUTTON_TEMPLATE",
{
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {0, 0, 0, .5},
backdropbordercolor = {0, 0, 0, .5},
onentercolor = {0.3, 0.3, 0.3, .5},
}
)
DetailsFramework:InstallTemplate("button", "DETAILS_PLUGINPANEL_BUTTONSELECTED_TEMPLATE",
{
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {0, 0, 0, .5},
backdropbordercolor = {1, 1, 0, 1},
onentercolor = {0.3, 0.3, 0.3, .5},
}
)
DetailsFramework:InstallTemplate("button", "DETAILS_PLUGIN_BUTTON_TEMPLATE",
{
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {1, 1, 1, .5},
backdropbordercolor = {0, 0, 0, 1},
onentercolor = {1, 1, 1, .9},
textcolor = "DETAILS_PLUGIN_BUTTONTEXT_COLOR",
textsize = 10,
width = 120,
height = 20,
}
)
DetailsFramework:InstallTemplate("button", "DETAILS_PLUGIN_BUTTONSELECTED_TEMPLATE",
{
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {1, 1, 1, .5},
backdropbordercolor = {1, .7, 0, 1},
onentercolor = {1, 1, 1, .9},
textcolor = "DETAILS_PLUGIN_BUTTONTEXT_COLOR",
textsize = 10,
width = 120,
height = 20,
}
)
DetailsFramework:InstallTemplate("button", "DETAILS_TAB_BUTTON_TEMPLATE",
{
width = 100,
height = 20,
},
"DETAILS_PLUGIN_BUTTON_TEMPLATE"
)
DetailsFramework:InstallTemplate("button","DETAILS_TAB_BUTTONSELECTED_TEMPLATE",
{
width = 100,
height = 20,
},
"DETAILS_PLUGIN_BUTTONSELECTED_TEMPLATE"
)
Details.PluginsGlobalNames = {}
Details.PluginsLocalizedNames = {}
--raid -------------------------------------------------------------------
--general function for raid mode plugins
Details.RaidTables = {}
--menu for raid modes
Details.RaidTables.Menu = {}
--plugin objects for raid mode
Details.RaidTables.Plugins = {}
--name to plugin object
Details.RaidTables.NameTable = {}
--using by
Details.RaidTables.InstancesInUse = {}
Details.RaidTables.PluginsInUse = {}
--solo -------------------------------------------------------------------
--general functions for solo mode plugins
Details.SoloTables = {}
--maintain plugin menu
Details.SoloTables.Menu = {}
--plugins objects for solo mode
Details.SoloTables.Plugins = {}
--name to plugin object
Details.SoloTables.NameTable = {}
--toolbar -------------------------------------------------------------------
--plugins container
Details.ToolBar = {}
--current showing icons
Details.ToolBar.Shown = {}
Details.ToolBar.AllButtons = {}
--plugin objects
Details.ToolBar.Plugins = {}
--name to plugin object
Details.ToolBar.NameTable = {}
Details.ToolBar.Menu = {}
--statusbar -------------------------------------------------------------------
--plugins container
Details.StatusBar = {}
--maintain plugin menu
Details.StatusBar.Menu = {}
--plugins object
Details.StatusBar.Plugins = {}
--name to plugin object
Details.StatusBar.NameTable = {}
--constants
if (DetailsFramework.IsWotLKWow()) then
--[[global]] DETAILS_HEALTH_POTION_ID = 33447 -- Runic Healing Potion
--[[global]] DETAILS_HEALTH_POTION2_ID = 41166 -- Runic Healing Injector
--[[global]] DETAILS_REJU_POTION_ID = 40087 -- Powerful Rejuvenation Potion
--[[global]] DETAILS_REJU_POTION2_ID = 40077 -- Crazy Alchemist's Potion
--[[global]] DETAILS_MANA_POTION_ID = 33448 -- Runic Mana Potion
--[[global]] DETAILS_MANA_POTION2_ID = 42545 -- Runic Mana Injector
--[[global]] DETAILS_FOCUS_POTION_ID = 307161
--[[global]] DETAILS_HEALTHSTONE_ID = 47875 --Warlock's Healthstone
--[[global]] DETAILS_HEALTHSTONE2_ID = 47876 --Warlock's Healthstone (1/2 Talent)
--[[global]] DETAILS_HEALTHSTONE3_ID = 47877 --Warlock's Healthstone (2/2 Talent)
--[[global]] DETAILS_INT_POTION_ID = 40212 --Potion of Wild Magic
--[[global]] DETAILS_AGI_POTION_ID = 40211 --Potion of Speed
--[[global]] DETAILS_STR_POTION_ID = 307164
--[[global]] DETAILS_STAMINA_POTION_ID = 40093 --Indestructible Potion
--[[global]] DETAILS_HEALTH_POTION_LIST = {
[DETAILS_HEALTH_POTION_ID] = true, -- Runic Healing Potion
[DETAILS_HEALTH_POTION2_ID] = true, -- Runic Healing Injector
[DETAILS_HEALTHSTONE_ID] = true, --Warlock's Healthstone
[DETAILS_HEALTHSTONE2_ID] = true, --Warlock's Healthstone (1/2 Talent)
[DETAILS_HEALTHSTONE3_ID] = true, --Warlock's Healthstone (2/2 Talent)
[DETAILS_REJU_POTION_ID] = true, -- Powerful Rejuvenation Potion
[DETAILS_REJU_POTION2_ID] = true, -- Crazy Alchemist's Potion
[DETAILS_MANA_POTION_ID] = true, -- Runic Mana Potion
[DETAILS_MANA_POTION2_ID] = true, -- Runic Mana Injector
}
else
--[[global]] DETAILS_HEALTH_POTION_ID = 307192 -- spiritual healing potion
--[[global]] DETAILS_HEALTH_POTION2_ID = 359867 --cosmic healing potion
--[[global]] DETAILS_REJU_POTION_ID = 307194
--[[global]] DETAILS_MANA_POTION_ID = 307193
--[[global]] DETAILS_FOCUS_POTION_ID = 307161
--[[global]] DETAILS_HEALTHSTONE_ID = 6262
--[[global]] DETAILS_INT_POTION_ID = 307162
--[[global]] DETAILS_AGI_POTION_ID = 307159
--[[global]] DETAILS_STR_POTION_ID = 307164
--[[global]] DETAILS_STAMINA_POTION_ID = 307163
--[[global]] DETAILS_HEALTH_POTION_LIST = {
[DETAILS_HEALTH_POTION_ID] = true, --Healing Potion
[DETAILS_HEALTHSTONE_ID] = true, --Warlock's Healthstone
[DETAILS_REJU_POTION_ID] = true, --Rejuvenation Potion
[DETAILS_MANA_POTION_ID] = true, --Mana Potion
[323436] = true, --Phial of Serenity (from Kyrians)
[DETAILS_HEALTH_POTION2_ID] = true,
}
end
--[[global]] DETAILS_MODE_GROUP = 2
--[[global]] DETAILS_MODE_ALL = 3
Details._detalhes_props = {
DATA_TYPE_START = 1, --Something on start
DATA_TYPE_END = 2, --Something on end
MODO_ALONE = 1, --Solo
MODO_GROUP = 2, --Group
MODO_ALL = 3, --Everything
MODO_RAID = 4, --Raid
}
Details.modos = {
alone = 1, --Solo
group = 2, --Group
all = 3, --Everything
raid = 4 --Raid
}
Details.divisores = {
abre = "(", --open
fecha = ")", --close
colocacao = ". " --dot
}
Details.role_texcoord = {
DAMAGER = "72:130:69:127",
HEALER = "72:130:2:60",
TANK = "5:63:69:127",
NONE = "139:196:69:127",
}
Details.role_texcoord_normalized = {
DAMAGER = {72/256, 130/256, 69/256, 127/256},
HEALER = {72/256, 130/256, 2/256, 60/256},
TANK = {5/256, 63/256, 69/256, 127/256},
NONE = {139/256, 196/256, 69/256, 127/256},
}
Details.player_class = {
["HUNTER"] = true,
["WARRIOR"] = true,
["PALADIN"] = true,
["SHAMAN"] = true,
["MAGE"] = true,
["ROGUE"] = true,
["PRIEST"] = true,
["WARLOCK"] = true,
["DRUID"] = true,
["MONK"] = true,
["DEATHKNIGHT"] = true,
["DEMONHUNTER"] = true,
}
Details.classstring_to_classid = {
["WARRIOR"] = 1,
["PALADIN"] = 2,
["HUNTER"] = 3,
["ROGUE"] = 4,
["PRIEST"] = 5,
["DEATHKNIGHT"] = 6,
["SHAMAN"] = 7,
["MAGE"] = 8,
["WARLOCK"] = 9,
["MONK"] = 10,
["DRUID"] = 11,
["DEMONHUNTER"] = 12,
}
Details.classid_to_classstring = {
[1] = "WARRIOR",
[2] = "PALADIN",
[3] = "HUNTER",
[4] = "ROGUE",
[5] = "PRIEST",
[6] = "DEATHKNIGHT",
[7] = "SHAMAN",
[8] = "MAGE",
[9] = "WARLOCK",
[10] = "MONK",
[11] = "DRUID",
[12] = "DEMONHUNTER",
}
local Loc = LibStub("AceLocale-3.0"):GetLocale ("Details")
Details.segmentos = {
label = Loc ["STRING_SEGMENT"]..": ",
overall = Loc ["STRING_TOTAL"],
overall_standard = Loc ["STRING_OVERALL"],
current = Loc ["STRING_CURRENT"],
current_standard = Loc ["STRING_CURRENTFIGHT"],
past = Loc ["STRING_FIGHTNUMBER"]
}
Details._detalhes_props["modo_nome"] = {
[_detalhes._detalhes_props["MODO_ALONE"]] = Loc ["STRING_MODE_SELF"],
[_detalhes._detalhes_props["MODO_GROUP"]] = Loc ["STRING_MODE_GROUP"],
[_detalhes._detalhes_props["MODO_ALL"]] = Loc ["STRING_MODE_ALL"],
[_detalhes._detalhes_props["MODO_RAID"]] = Loc ["STRING_MODE_RAID"]
}
--[[global]] DETAILS_MODE_SOLO = 1
--[[global]] DETAILS_MODE_RAID = 4
--[[global]] DETAILS_MODE_GROUP = 2
--[[global]] DETAILS_MODE_ALL = 3
Details.icones = {
--report window
report = {
up = "Interface\\FriendsFrame\\UI-Toast-FriendOnlineIcon",
down = "Interface\\ItemAnimations\\MINIMAP\\TRACKING\\Profession",
disabled = "Interface\\ItemAnimations\\MINIMAP\\TRACKING\\Profession",
highlight = nil
}
}
Details.missTypes = {"ABSORB", "BLOCK", "DEFLECT", "DODGE", "EVADE", "IMMUNE", "MISS", "PARRY", "REFLECT", "RESIST"} --do not localize-me
function Details.SendHighFive()
Details.users = {{UnitName("player"), GetRealmName(), (Details.userversion or "") .. " (" .. Details.APIVersion .. ")"}}
Details.sent_highfive = GetTime()
if (IsInRaid()) then
Details:SendRaidData(Details.network.ids.HIGHFIVE_REQUEST)
else
Details:SendPartyData(Details.network.ids.HIGHFIVE_REQUEST)
end
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--frames
local CreateFrame = CreateFrame --api locals
local UIParent = UIParent --api locals
--create the breakdown window frame
---@type breakdownwindow
Details.BreakdownWindowFrame = CreateFrame("Frame", "DetailsBreakdownWindow", UIParent, "BackdropTemplate")
Details.PlayerDetailsWindow = Details.BreakdownWindowFrame
Details.BreakdownWindow = Details.BreakdownWindowFrame
--Event Frame
Details.listener = CreateFrame("Frame", nil, UIParent)
Details.listener:RegisterEvent("ADDON_LOADED")
Details.listener:SetFrameStrata("LOW")
Details.listener:SetFrameLevel(9)
Details.listener.FrameTime = 0
Details.overlay_frame = CreateFrame("Frame", nil, UIParent)
Details.overlay_frame:SetFrameStrata("TOOLTIP")
--Pet Owner Finder
CreateFrame("GameTooltip", "DetailsPetOwnerFinder", nil, "GameTooltipTemplate")
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--plugin defaults
--backdrop
Details.PluginDefaults = {}
Details.PluginDefaults.Backdrop = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16,
edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1,
insets = {left = 1, right = 1, top = 1, bottom = 1}}
Details.PluginDefaults.BackdropColor = {0, 0, 0, .6}
Details.PluginDefaults.BackdropBorderColor = {0, 0, 0, 1}
function Details.GetPluginDefaultBackdrop()
return Details.PluginDefaults.Backdrop, Details.PluginDefaults.BackdropColor, Details.PluginDefaults.BackdropBorderColor
end
------------------------------------------------------------------------------------------
-- welcome panel
function _detalhes:CreateWelcomePanel(name, parent, width, height, makeMovable)
local newWelcomePanel = CreateFrame("frame", name, parent or UIParent, "BackdropTemplate")
DetailsFramework:ApplyStandardBackdrop(newWelcomePanel)
newWelcomePanel:SetSize(width or 1, height or 1)
if (makeMovable) then
newWelcomePanel:SetScript("OnMouseDown", function(self, button)
if (self.isMoving) then
return
end
if (button == "RightButton") then
self:Hide()
else
self:StartMoving()
self.isMoving = true
end
end)
newWelcomePanel:SetScript("OnMouseUp", function(self, button)
if (self.isMoving and button == "LeftButton") then
self:StopMovingOrSizing()
self.isMoving = nil
end
end)
newWelcomePanel:SetToplevel(true)
newWelcomePanel:SetMovable(true)
end
return newWelcomePanel
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--functions
_detalhes.empty_function = function() end
_detalhes.empty_table = {}
--register textures and fonts for shared media
---@type table
local SharedMedia = LibStub:GetLibrary ("LibSharedMedia-3.0")
--default bars
SharedMedia:Register("statusbar", "Details Hyanda Reverse", [[Interface\AddOns\Details\images\bar_textures\bar_hyanda_reverse.png]])
SharedMedia:Register("statusbar", "You Are the Best!", [[Interface\AddOns\Details\images\bar_textures\bar_best.png]])
SharedMedia:Register("statusbar", "Details Hyanda", [[Interface\AddOns\Details\images\bar_hyanda]])
SharedMedia:Register("statusbar", "Details D'ictum", [[Interface\AddOns\Details\images\bar4]])
SharedMedia:Register("statusbar", "Details Vidro", [[Interface\AddOns\Details\images\bar4_vidro]])
SharedMedia:Register("statusbar", "Details D'ictum (reverse)", [[Interface\AddOns\Details\images\bar4_reverse]])
--flat bars
SharedMedia:Register("statusbar", "Skyline", [[Interface\AddOns\Details\images\bar_skyline]])
SharedMedia:Register("statusbar", "Details Serenity", [[Interface\AddOns\Details\images\bar_serenity]])
SharedMedia:Register("statusbar", "BantoBar", [[Interface\AddOns\Details\images\BantoBar]])
SharedMedia:Register("statusbar", "Skyline Compact", [[Interface\AddOns\Details\images\bar_textures\bar_skyline_compact.png]])
SharedMedia:Register("statusbar", "WorldState Score", [[Interface\WorldStateFrame\WORLDSTATEFINALSCORE-HIGHLIGHT]])
SharedMedia:Register("statusbar", "DGround", [[Interface\AddOns\Details\images\bar_background]])
SharedMedia:Register("statusbar", "Details Flat", [[Interface\AddOns\Details\images\bar_background]])
SharedMedia:Register("statusbar", "Splitbar", [[Interface\AddOns\Details\images\bar_textures\split_bar]])
SharedMedia:Register("statusbar", "Details2020", [[Interface\AddOns\Details\images\bar_textures\texture2020]])
SharedMedia:Register("statusbar", "Left White Gradient", [[Interface\AddOns\Details\images\bar_textures\gradient_white_10percent_left]])
SharedMedia:Register("statusbar", "Details! Slash", [[Interface\AddOns\Details\images\bar_textures\bar_of_bars.png]])
--window bg and bar order
SharedMedia:Register("background", "Details Ground", [[Interface\AddOns\Details\images\background]])
SharedMedia:Register("border", "Details BarBorder 1", [[Interface\AddOns\Details\images\border_1]])
SharedMedia:Register("border", "Details BarBorder 2", [[Interface\AddOns\Details\images\border_2]])
SharedMedia:Register("border", "Details BarBorder 3", [[Interface\AddOns\Details\images\border_3]])
SharedMedia:Register("border", "1 Pixel", [[Interface\Buttons\WHITE8X8]])
--misc fonts
SharedMedia:Register("font", "Oswald", [[Interface\Addons\Details\fonts\Oswald-Regular.ttf]])
SharedMedia:Register("font", "Nueva Std Cond", [[Interface\Addons\Details\fonts\Nueva Std Cond.ttf]])
SharedMedia:Register("font", "Accidental Presidency", [[Interface\Addons\Details\fonts\Accidental Presidency.ttf]])
SharedMedia:Register("font", "TrashHand", [[Interface\Addons\Details\fonts\TrashHand.TTF]])
SharedMedia:Register("font", "Harry P", [[Interface\Addons\Details\fonts\HARRYP__.TTF]])
SharedMedia:Register("font", "FORCED SQUARE", [[Interface\Addons\Details\fonts\FORCED SQUARE.ttf]])
SharedMedia:Register("sound", "Details Gun1", [[Interface\Addons\Details\sounds\sound_gun2.ogg]])
SharedMedia:Register("sound", "Details Gun2", [[Interface\Addons\Details\sounds\sound_gun3.ogg]])
SharedMedia:Register("sound", "Details Jedi1", [[Interface\Addons\Details\sounds\sound_jedi1.ogg]])
SharedMedia:Register("sound", "Details Whip1", [[Interface\Addons\Details\sounds\sound_whip1.ogg]])
SharedMedia:Register("sound", "Details Horn", [[Interface\Addons\Details\sounds\Details Horn.ogg]])
SharedMedia:Register("sound", "Details Warning", [[Interface\Addons\Details\sounds\Details Warning 100.ogg]])
SharedMedia:Register("sound", "Details Truck", [[Interface\Addons\Details\sounds\Details Truck.ogg]])
SharedMedia:Register("sound", "Details Bass Drop", [[Interface\Addons\Details\sounds\bassdrop2.mp3]])
--dump table contents over chat panel
function Details.VarDump(t)
if (type(t) ~= "table") then
return
end
for a,b in pairs(t) do
print(a,b)
end
end
local bIsDump = false
local waitForSpellLoad = CreateFrame("frame")
if (C_EventUtils.IsEventValid("SPELL_TEXT_UPDATE")) then
waitForSpellLoad:RegisterEvent("SPELL_TEXT_UPDATE")
waitForSpellLoad:SetScript("OnEvent", function(self, event, spellId)
if (bIsDump) then
dumpt(spellId)
end
end)
end
function dumpt(value) --[[GLOBAL]]
--check if this is a spellId
local spellId = tonumber(value)
if (spellId) then
local spellInfo = {Details222.GetSpellInfo(spellId)}
if (type(spellInfo[1]) == "string") then
local desc = C_Spell.GetSpellDescription and C_Spell.GetSpellDescription(spellId) or GetSpellDescription(spellId)
if (not desc or desc == "") then
bIsDump = true
return
end
if (C_Spell.GetSpellInfo) then
Details:Dump({desc, C_Spell.GetSpellInfo(spellId)})
return
else
return Details:Dump({desc, spellInfo})
end
bIsDump = false
end
end
--check if is an atlas texture
local atlas
if (type(value) == "string") then
atlas = C_Texture.GetAtlasInfo(value)
if (atlas) then
return Details:Dump(atlas)
end
end
if (value == nil) then
local allTooltips = {"GameTooltip", "GameTooltipTooltip", "EventTraceTooltip", "FrameStackTooltip", "GarrisonMissionMechanicTooltip", "GarrisonMissionMechanicFollowerCounterTooltip", "ItemSocketingDescription", "NamePlateTooltip", "PrivateAurasTooltip", "RuneforgeFrameResultTooltip", "ItemRefTooltip", "QuickKeybindTooltip", "SettingsTooltip"}
for i = 1, #allTooltips do
local tooltipName = allTooltips[i]
local tooltip = _G[tooltipName]
if (tooltip and tooltip:IsVisible()) then
if (tooltip.GetTooltipData) then
local tooltipData = tooltip:GetTooltipData()
if (tooltipData) then
if (tooltip.ItemTooltip and tooltip.ItemTooltip:IsVisible()) then
local icon = tooltip.ItemTooltip.Icon
if (icon) then
local texture = icon:GetTexture()
local atlas = icon:GetAtlas()
if (texture or atlas) then
tooltipData.IconTexture = texture
tooltipData.IconAtlas = atlas
end
end
end
if (tooltipData.hyperlink) then
local itemName, itemLink, itemQuality, itemLevel, itemMinLevel, itemType, itemSubType,
itemStackCount, itemEquipLoc, itemTexture, sellPrice, classID, subclassID, bindType,
expacID, setID, isCraftingReagent = GetItemInfo(tooltipData.hyperlink)
local itemInfo = {
itemName = itemName,
itemLink = itemLink,
itemQuality = itemQuality,
itemLevel = itemLevel,
itemMinLevel = itemMinLevel,
itemType = itemType,
itemSubType = itemSubType,
itemStackCount = itemStackCount,
itemEquipLoc = itemEquipLoc,
itemTexture = itemTexture,
sellPrice = sellPrice,
classID = classID,
subclassID = subclassID,
bindType = bindType,
expacID = expacID,
setID = setID,
isCraftingReagent = isCraftingReagent
}
DetailsFramework.table.deploy(tooltipData, itemInfo)
end
return Details:Dump(tooltipData)
end
else
local outputTable = {}
for lineNumber = 1, 10 do
local leftText = _G[tooltipName..'TextLeft'..lineNumber]
local rightText = _G[tooltipName..'TextRight'..lineNumber]
if not (leftText and rightText) then
break
end
outputTable[#outputTable+1] = {left = leftText:GetText(), right = rightText:GetText()}
end
return Details:Dump(outputTable)
end
end
end
end
return Details:Dump(value)
end
function FindSpellByName(spellName) --[[GLOBAL]]
if (spellName and type(spellName) == "string") then
local GSI = GetSpellInfo
local foundSpells = {}
spellName = spellName:lower()
for i = 1, 450000 do
local thisSpellName = GSI(i)
if (thisSpellName) then
thisSpellName = thisSpellName:lower()
if (spellName == thisSpellName) then
foundSpells[#foundSpells+1] = {GSI(i)}
end
end
end
if (#foundSpells > 0) then
dumpt(foundSpells)
else
Details:Msg("spell", spellName, "not found.")
end
end
end
--copies a full table
function Details.CopyTable(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
--print(orig_key, orig_value)
copy[Details.CopyTable(orig_key)] = Details.CopyTable(orig_value)
end
else
copy = orig
end
return copy
end
--delay messages
function _detalhes:DelayMsg(msg)
_detalhes.delaymsgs = _detalhes.delaymsgs or {}
_detalhes.delaymsgs[#_detalhes.delaymsgs+1] = msg
end
function _detalhes:ShowDelayMsg()
if (_detalhes.delaymsgs and #_detalhes.delaymsgs > 0) then
for _, msg in ipairs(_detalhes.delaymsgs) do
print(msg)
end
end
_detalhes.delaymsgs = {}
end
--print messages
function _detalhes:Msg(str, arg1, arg2, arg3, arg4)
if (self.__name) then
print("|cffffaeae" .. self.__name .. "|r |cffcc7c7c(plugin)|r: " .. (str or ""), arg1 or "", arg2 or "", arg3 or "", arg4 or "")
else
print(Loc ["STRING_DETAILS1"] .. (str or ""), arg1 or "", arg2 or "", arg3 or "", arg4 or "")
end
end
--welcome
function _detalhes:WelcomeMsgLogon()
_detalhes:Msg("you can always reset the addon running the command |cFFFFFF00'/details reinstall'|r if it does fail to load after being updated.")
function _detalhes:wipe_combat_after_failed_load()
_detalhes.tabela_historico = _detalhes.historico:CreateNewSegmentDatabase()
_detalhes.tabela_overall = _detalhes.combate:NovaTabela()
_detalhes.tabela_vigente = _detalhes.combate:NovaTabela (_, _detalhes.tabela_overall)
Details222.PetContainer.Reset()
_detalhes_database.tabela_overall = nil
_detalhes_database.tabela_historico = nil
_detalhes:Msg("seems failed to load, please type /reload to try again.")
end
Details.Schedules.After(5, _detalhes.wipe_combat_after_failed_load)
end
Details.failed_to_load = C_Timer.NewTimer(1, function() Details.Schedules.NewTimer(20, _detalhes.WelcomeMsgLogon) end)
--key binds
--[=
--header
_G ["BINDING_HEADER_Details"] = "Details!"
_G ["BINDING_HEADER_DETAILS_KEYBIND_SEGMENTCONTROL"] = Loc ["STRING_KEYBIND_SEGMENTCONTROL"]
_G ["BINDING_HEADER_DETAILS_KEYBIND_SCROLLING"] = Loc ["STRING_KEYBIND_SCROLLING"]
_G ["BINDING_HEADER_DETAILS_KEYBIND_WINDOW_CONTROL"] = Loc ["STRING_KEYBIND_WINDOW_CONTROL"]
_G ["BINDING_HEADER_DETAILS_KEYBIND_BOOKMARK"] = Loc ["STRING_KEYBIND_BOOKMARK"]
_G ["BINDING_HEADER_DETAILS_KEYBIND_REPORT"] = Loc ["STRING_KEYBIND_WINDOW_REPORT_HEADER"]
--keys
_G ["BINDING_NAME_DETAILS_TOGGLE_ALL"] = Loc ["STRING_KEYBIND_TOGGLE_WINDOWS"]
_G ["BINDING_NAME_DETAILS_RESET_SEGMENTS"] = Loc ["STRING_KEYBIND_RESET_SEGMENTS"]
_G ["BINDING_NAME_DETAILS_SCROLL_UP"] = Loc ["STRING_KEYBIND_SCROLL_UP"]
_G ["BINDING_NAME_DETAILS_SCROLL_DOWN"] = Loc ["STRING_KEYBIND_SCROLL_DOWN"]
_G ["BINDING_NAME_DETAILS_REPORT_WINDOW1"] = string.format(Loc ["STRING_KEYBIND_WINDOW_REPORT"], 1)
_G ["BINDING_NAME_DETAILS_REPORT_WINDOW2"] = string.format(Loc ["STRING_KEYBIND_WINDOW_REPORT"], 2)
_G ["BINDING_NAME_DETAILS_TOOGGLE_WINDOW1"] = string.format(Loc ["STRING_KEYBIND_TOGGLE_WINDOW"], 1)
_G ["BINDING_NAME_DETAILS_TOOGGLE_WINDOW2"] = string.format(Loc ["STRING_KEYBIND_TOGGLE_WINDOW"], 2)
_G ["BINDING_NAME_DETAILS_TOOGGLE_WINDOW3"] = string.format(Loc ["STRING_KEYBIND_TOGGLE_WINDOW"], 3)
_G ["BINDING_NAME_DETAILS_TOOGGLE_WINDOW4"] = string.format(Loc ["STRING_KEYBIND_TOGGLE_WINDOW"], 4)
_G ["BINDING_NAME_DETAILS_TOOGGLE_WINDOW5"] = string.format(Loc ["STRING_KEYBIND_TOGGLE_WINDOW"], 5)
_G ["BINDING_NAME_DETAILS_BOOKMARK1"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 1)
_G ["BINDING_NAME_DETAILS_BOOKMARK2"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 2)
_G ["BINDING_NAME_DETAILS_BOOKMARK3"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 3)
_G ["BINDING_NAME_DETAILS_BOOKMARK4"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 4)
_G ["BINDING_NAME_DETAILS_BOOKMARK5"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 5)
_G ["BINDING_NAME_DETAILS_BOOKMARK6"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 6)
_G ["BINDING_NAME_DETAILS_BOOKMARK7"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 7)
_G ["BINDING_NAME_DETAILS_BOOKMARK8"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 8)
_G ["BINDING_NAME_DETAILS_BOOKMARK9"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 9)
_G ["BINDING_NAME_DETAILS_BOOKMARK10"] = string.format(Loc ["STRING_KEYBIND_BOOKMARK_NUMBER"], 10)
--]=]
end
if (select(4, GetBuildInfo()) >= 100000) then
local f = CreateFrame("frame")
f:RegisterEvent("ADDON_ACTION_FORBIDDEN")
f:SetScript("OnEvent", function()
local text = StaticPopup1 and StaticPopup1.text and StaticPopup1.text:GetText()
if (text and text:find("Details")) then
--fix false-positive taints that are being attributed to random addons
StaticPopup1.button2:Click()
end
end)
end
local classCacheName = Details222.ClassCache.ByName
local classCacheGUID = Details222.ClassCache.ByGUID
function Details222.ClassCache.GetClassFromCache(value)
return classCacheName[value] or classCacheGUID[value]
end
function Details222.ClassCache.AddClassToCache(value, whichCache)
if (whichCache == "name") then
classCacheName[value] = true
elseif (whichCache == "guid") then
classCacheGUID[value] = true
end
end
function Details222.ClassCache.GetClass(value)
local className = Details222.ClassCache.ByName[value] or Details222.ClassCache.ByGUID[value]
if (className) then
return className
end
local _, unitClass = UnitClass(value)
return unitClass
end
function Details222.ClassCache.MakeCache()
--iterage among all segments in the container history, get the damage container and get the actor list, check if the actor is a player and if it is, get the class and store it in the cache
local segmentsTable = Details:GetCombatSegments()
for _, combatObject in ipairs(segmentsTable) do
for _, actorObject in combatObject:GetContainer(DETAILS_ATTRIBUTE_DAMAGE):ListActors() do
if (actorObject:IsPlayer()) then
local actorName = actorObject.nome
local actorClass = actorObject.classe
local actorGUID = actorObject.serial
Details222.ClassCache.ByName[actorName] = actorClass
Details222.ClassCache.ByGUID[actorGUID] = actorClass
end
end
end
end
Details222.UnitIdCache.Party = {"player"}
Details222.UnitIdCache.PartyPet = {"playerpet"}
for i = 1, 4 do
table.insert(Details222.UnitIdCache.Party, "party" .. i)
table.insert(Details222.UnitIdCache.PartyPet, "partypet" .. i)
end
Details.PartyUnits = Details222.UnitIdCache.Party
Details.PartyPetUnits = Details222.UnitIdCache.PartyPet
Details222.UnitIdCache.Raid = {}
Details222.UnitIdCache.RaidPet = {}
Details222.UnitIdCache.RaidTargets = {}
for i = 1, 40 do
Details222.UnitIdCache.Raid[i] = "raid" .. i
Details222.UnitIdCache.RaidPet[i] = "raidpet" .. i
Details222.UnitIdCache.RaidTargets[i] = "raidtarget" .. i
end
Details.RaidUnits = Details222.UnitIdCache.Raid
Details.RaidPetUnits = Details222.UnitIdCache.RaidPet
Details.RaidTargetUnits = Details222.UnitIdCache.RaidTargets
Details222.UnitIdCache.Boss = {}
for i = 1, 9 do
Details222.UnitIdCache.Boss[i] = "boss" .. i
end
Details.BossUnits = Details222.UnitIdCache.Boss
Details222.UnitIdCache.Nameplate = {}
for i = 1, 40 do
Details222.UnitIdCache.Nameplate[i] = "nameplate" .. i
end
Details.NameplateUnits = Details222.UnitIdCache.Nameplate
Details222.UnitIdCache.Arena = {}
for i = 1, 5 do
Details222.UnitIdCache.Arena[i] = "arena" .. i
end
Details.ArenaUnits = Details222.UnitIdCache.Arena
function Details222.Tables.MakeWeakTable(mode)
local newTable = {}
setmetatable(newTable, {__mode = mode or "v"})
return newTable
end
--STRING_CUSTOM_POT_DEFAULT
---add a statistic, log, or any other data to the player stat table
---@param statName string
---@param value number
function Details222.PlayerStats:AddStat(statName, value)
Details.player_stats[statName] = (Details.player_stats[statName] or 0) + value
end
---get the value of a saved stat
---@param statName string
---@return any
function Details222.PlayerStats:GetStat(statName)
return Details.player_stats[statName]
end
---same thing as above but set the value instead of adding
---@param statName string
---@param value number
function Details222.PlayerStats:SetStat(statName, value)
Details.player_stats[statName] = value
end
local profileStartFunc = function(functionName)
local profile = Details222.ProfilingCache[functionName]
if (not profile) then
Details222.ProfilingCache[functionName] = {elapsed = 0, startTime = 0, runs = 0}
profile = Details222.ProfilingCache[functionName]
end
profile.startTime = debugprofilestop()
profile.runs = profile.runs + 1
end
local profileStopFunc = function(functionName)
local profile = Details222.ProfilingCache[functionName]
if (profile) then
profile.elapsed = profile.elapsed + debugprofilestop() - profile.startTime
end
end
function Details222.Profiling.ProfileStart()end
function Details222.Profiling.ProfileStop()end
function Details222.Profiling.EnableProfiler()
Details222.Profiling.ProfileStart = profileStartFunc
Details222.Profiling.ProfileStop = profileStopFunc
end
function Details222.Profiling.DisableProfiler()
Details222.Profiling.ProfileStart = function()end
Details222.Profiling.ProfileStop = function()end
end
function Details222.Profiling.ResetProfiler()
table.wipe(Details222.ProfilingCache)
end
if (select(4, GetBuildInfo()) >= 100000) then
Details222.Profiling.EnableProfiler()
end
function Details:ProfilerResult()
local resultTable = {}
local total = 0
for functionName, profile in pairs(Details222.ProfilingCache) do
local runTime = string.format("%.3f", profile.elapsed / 1000)
resultTable[functionName] = runTime .. " ms | runs: " .. profile.runs
total = total + profile.elapsed
end
resultTable["Total"] = string.format("%.3f", total / 1000) .. " ms"
dumpt(resultTable)
end
function Details:ResetProfilerResult()
end
---destroy a table and remove it from the object, if the key isn't passed, the object itself is destroyed
---@param object any
---@param key string|nil
function Details:Destroy(object, key)
if (key) then
if (getmetatable(object[key])) then
setmetatable(object[key], nil)
end
object[key].__index = nil
table.wipe(object[key])
object[key] = nil
else
if (getmetatable(object)) then
setmetatable(object, nil)
end
object.__index = nil
table.wipe(object)
end
end
function Details:DestroyCombat(combatObject)
--destroy each individual actor, hence more cleanups are done
for i = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
local actorContainer = combatObject:GetContainer(i)
for index, actorObject in actorContainer:ListActors() do
Details:DestroyActor(actorObject, actorContainer, combatObject, 3)
end
end
setmetatable(combatObject, nil)
combatObject.__index = nil
combatObject.__newindex = nil
combatObject.__call = nil
Details:Destroy(combatObject)
--leave a trace that the actor has been deleted
combatObject.__destroyed = true
combatObject.__destroyedBy = debugstack(2, 1, 0)
end
---destroy the actor, also calls container:RemoveActor(actor)
---@param self details
---@param actorObject actor
---@param actorContainer actorcontainer
---@param combatObject combat
function Details:DestroyActor(actorObject, actorContainer, combatObject, callStackDepth)
local containerType = actorContainer:GetType()
local combatTotalsTable = combatObject.totals[containerType] --without group
local combatTotalsTableInGroup = combatObject.totals_grupo[containerType] --with group
--remove the actor from the parser cache
local c1, c2, c3, c4 = Details222.Cache.GetParserCacheTables()
c1[actorObject.serial] = nil
c2[actorObject.serial] = nil
c3[actorObject.serial] = nil
c4[actorObject.serial] = nil
if (not actorObject.ownerName) then --not a pet
if (containerType == 1 or containerType == 2) then --damage|healing done
combatTotalsTable = combatTotalsTable - actorObject.total
if (actorObject.grupo) then
combatTotalsTableInGroup = combatTotalsTableInGroup - actorObject.total
end
elseif (containerType == 3) then
---@cast actorObject actorresource
if (actorObject.total and actorObject.total > 0) then
if (actorObject.powertype) then
combatTotalsTable[actorObject.powertype] = combatTotalsTable[actorObject.powertype] - actorObject.total
combatTotalsTableInGroup[actorObject.powertype] = combatTotalsTableInGroup[actorObject.powertype] - actorObject.total
end
end
if (actorObject.alternatepower and actorObject.alternatepower > 0) then
combatTotalsTable.alternatepower = combatTotalsTable.alternatepower - actorObject.alternatepower
combatTotalsTableInGroup.alternatepower = combatTotalsTableInGroup.alternatepower - actorObject.alternatepower
end
elseif (containerType == 4) then
---@cast actorObject actorutility
--decrease the amount of CC break from the combat totals
if (actorObject.cc_break and actorObject.cc_break > 0) then
if (combatTotalsTable.cc_break) then
combatTotalsTable.cc_break = combatTotalsTable.cc_break - actorObject.cc_break
end
if (combatTotalsTableInGroup.cc_break) then
combatTotalsTableInGroup.cc_break = combatTotalsTableInGroup.cc_break - actorObject.cc_break
end
end
--decrease the amount of dispell from the combat totals
if (actorObject.dispell and actorObject.dispell > 0) then
if (combatTotalsTable.dispell) then
combatTotalsTable.dispell = combatTotalsTable.dispell - actorObject.dispell
end
if (combatTotalsTableInGroup.dispell) then
combatTotalsTableInGroup.dispell = combatTotalsTableInGroup.dispell - actorObject.dispell
end
end
--decrease the amount of interrupt from the combat totals
if (actorObject.interrupt and actorObject.interrupt > 0) then
if (combatTotalsTable.interrupt) then
combatTotalsTable.interrupt = combatTotalsTable.interrupt - actorObject.interrupt
end
if (combatTotalsTableInGroup.interrupt) then
combatTotalsTableInGroup.interrupt = combatTotalsTableInGroup.interrupt - actorObject.interrupt
end
end
--decrease the amount of ress from the combat totals
if (actorObject.ress and actorObject.ress > 0) then
if (combatTotalsTable.ress) then
combatTotalsTable.ress = combatTotalsTable.ress - actorObject.ress
end
if (combatTotalsTableInGroup.ress) then
combatTotalsTableInGroup.ress = combatTotalsTableInGroup.ress - actorObject.ress
end
end
--decrease the amount of dead from the combat totals
if (actorObject.dead and actorObject.dead > 0) then
if (combatTotalsTable.dead) then
combatTotalsTable.dead = combatTotalsTable.dead - actorObject.dead
end
if (combatTotalsTableInGroup.dead) then
combatTotalsTableInGroup.dead = combatTotalsTableInGroup.dead - actorObject.dead
end
end
--decreate the amount of cooldowns used from the combat totals
if (actorObject.cooldowns_defensive and actorObject.cooldowns_defensive > 0) then
if (combatTotalsTable.cooldowns_defensive) then
combatTotalsTable.cooldowns_defensive = combatTotalsTable.cooldowns_defensive - actorObject.cooldowns_defensive
end
if (combatTotalsTableInGroup.cooldowns_defensive) then
combatTotalsTableInGroup.cooldowns_defensive = combatTotalsTableInGroup.cooldowns_defensive - actorObject.cooldowns_defensive
end
end
--decrease the amount of buff uptime from the combat totals
if (actorObject.buff_uptime and actorObject.buff_uptime > 0) then
if (combatTotalsTable.buff_uptime) then
combatTotalsTable.buff_uptime = combatTotalsTable.buff_uptime - actorObject.buff_uptime
end
if (combatTotalsTableInGroup.buff_uptime) then
combatTotalsTableInGroup.buff_uptime = combatTotalsTableInGroup.buff_uptime - actorObject.buff_uptime
end
end
--decrease the amount of debuff uptime from the combat totals
if (actorObject.debuff_uptime and actorObject.debuff_uptime > 0) then
if (combatTotalsTable.debuff_uptime) then
combatTotalsTable.debuff_uptime = combatTotalsTable.debuff_uptime - actorObject.debuff_uptime
end
if (combatTotalsTableInGroup.debuff_uptime) then
combatTotalsTableInGroup.debuff_uptime = combatTotalsTableInGroup.debuff_uptime - actorObject.debuff_uptime
end
end
end
end
Details222.TimeMachine.RemoveActor(actorObject)
if (not actorObject.Name) then
print("error: actorObject.Name is nil", actorObject.tipo, actorObject.serial, actorObject.nome)
end
local actorName = actorObject:Name()
combatObject:RemoveActorFromSpellCastTable(actorName)
setmetatable(actorObject, nil)
actorObject.__index = nil
actorObject.__newindex = nil
Details:Destroy(actorObject)
--leave a trace that the actor has been deleted
actorObject.__destroyed = true
actorObject.__destroyedBy = debugstack(callStackDepth or 2, 1, 0)
end
C_Timer.After(5, function()
--TutorialPointerFrame_1:HookScript("OnShow", function(self) self:Hide() end) --remove on v11 launch
end)