chore: hoist plugins to root and move main into Details/

Each Details_* plugin and the main Details addon now lives in its own
repo-root folder, matching the Exiles fork-layout convention.
This commit is contained in:
2026-05-25 10:59:28 +02:00
parent 0b0a5004eb
commit 5bb7be4968
421 changed files with 7 additions and 0 deletions
+384
View File
@@ -0,0 +1,384 @@
local Details = _G.Details
local addonName, Details222 = ...
local detailsFramework = DetailsFramework
local _
local AuraUtil, wipe, GetSpellInfo, GetTime, UnitGUID, UnitExists = AuraUtil, table.wipe, GetSpellInfo, GetTime, UnitGUID, UnitExists
local AuraScan = Details222.AuraScan
AuraScan.Enabled = false
AuraScan.Callbacks = {}
AuraScan.AurasToScan = {}
---store the auras applied to the unit, format: [unitGUID] = {auraInstanceId = aurainfo}
---@type table<guid, table<number, aurainfo>>
AuraScan.UnitAurasStorage = {}
AuraScan.AurasToTimeline = {} --which spells should be added to the timeline
AuraScan.AuraTimelineStorage = {} --store the timeline here
function AuraScan.FindAndIgnoreWorldAuras()
for buffIndex = 1, 41 do
---@type aurainfo
local auraName, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId = UnitAura("player", buffIndex, "HELPFUL")
if (auraName) then
if (auraName and unitCaster and UnitExists(unitCaster) and UnitIsUnit(unitCaster, "player")) then
if (duration == 0 and expirationTime) then --this aura is a world buff, probably a weekly buff
Details222.IgnoredWorldAuras[spellId] = true
end
end
else
break
end
end
end
function AuraScan.CheckForOneHourBuffs()
---@type combat
local currentCombat = Details:GetCurrentCombat()
---@type actorcontainer
local utilityContainer = currentCombat:GetContainer(DETAILS_ATTRIBUTE_MISC)
---@type combattime
local combatTime = floor(currentCombat:GetCombatTime())
for _, utilityActor in utilityContainer:ListActors() do
---@cast utilityActor actorutility
if (utilityActor:IsPlayer()) then
--get the buff container
---@type spellcontainer
local buffUptimeContainer = utilityActor.buff_uptime_spells
if (buffUptimeContainer) then
for spellId, spellTable in buffUptimeContainer:ListSpells() do
---@cast spellTable spelltable
if (Details222.OneHourAuras[spellId]) then
--is this buff have 100% uptime?
if (spellTable.uptime == combatTime) then
utilityActor.buff_uptime = utilityActor.buff_uptime - spellTable.uptime
utilityActor.buff_uptime_spells._ActorTable[spellId] = nil
end
end
end
end
end
end
end
function AuraScan.RegisterCallback(callback)
AuraScan.Callbacks[callback] = true
end
function AuraScan.UnregisterCallback(callback)
AuraScan.Callbacks[callback] = nil
end
---return a table with all auras applied to the unit, format: [unitGUID] = {auraInstanceId = aurainfo}
function AuraScan.GetOrCreateUnitAuraTable(unitGUID)
local auras = AuraScan.UnitAurasStorage[unitGUID]
if (not auras) then
auras = {}
AuraScan.UnitAurasStorage[unitGUID] = auras
end
return auras
end
function AuraScan.WipeAllUnitAuraTables()
wipe(AuraScan.UnitAurasStorage)
end
function AuraScan.WipeUnitAuraTable(unitGUID)
local auras = AuraScan.GetOrCreateUnitAuraTable(unitGUID)
wipe(auras)
end
function AuraScan.GetAura(unitGUID, spellId)
if (not unitGUID or not spellId) then
return false
end
local auraTbl = AuraScan.GetOrCreateUnitAuraTable(unitGUID)
if (not auraTbl) then
--happens if the guid is invalid
return false
end
return auraTbl[spellId]
end
function AuraScan.AddAura(spellId, bAddToTimeLine)
if (not spellId or type(spellId) ~= "number") then
Details:Msg("AuraScan.AddAura() called, but spellId is not a number.")
return
end
local spellName = GetSpellInfo(spellId)
if (spellName) then
AuraScan.AurasToScan[spellId] = true
if (bAddToTimeLine) then
AuraScan.AurasToTimeline[spellId] = true
end
else
Details:Msg("AuraScan.AddAura() called, but spellId is not a valid spell.")
end
end
--is the aura added?
function AuraScan.IsAuraAdded(spellId)
if (not spellId or type(spellId) ~= "number") then
Details:Msg("AuraScan.IsAuraAdded() called, but spellId is not a number.")
return
end
return AuraScan.AurasToScan[spellId]
end
function AuraScan.RemoveAura(spellId)
if (not spellId or type(spellId) ~= "number") then
Details:Msg("AuraScan.RemoveAura() called, but spellId is not a number.")
return
end
AuraScan.AurasToScan[spellId] = nil
AuraScan.AurasToTimeline[spellId] = nil
end
function AuraScan.RemoveAllAuras()
wipe(AuraScan.AurasToScan)
end
------------------------------------------------------------------------------------------------------------------------------
--aura parser
---@class details_auratimeline : table
---@field time number time when this aura had its status changed
---@field appliedTime number time when this aura was applied
---@field removedTime number time when this aura was removed
---@field expireTime number
---@field event string
---@field sourceName string
---@field targetName string
---@field targetGUID string
---@field spellId number
---@field closed boolean when true this received received aura In and Out
---@field duration number
---@field elapsedTime number
---@field auraInstanceID number
---@field addTimeLineTable details_auratimeline
---@type table<number, aurainfo>
local unitAuraTable
local targetUnitGUID
local targetName
local bIsInitialScan = false
local timeLastAuraRemovedFromTimeLine = 0
local sourceNameLastAuraRemovedFromTimeLine = 0
local auraInstanceIdLastAuraRemovedFromTimeLine = 0
local fAddAura = function(auraInfo)
---@cast auraInfo aurainfo
local spellId = auraInfo.spellId
if (auraInfo and auraInfo.auraInstanceID and spellId) then
if (AuraScan.IsAuraAdded(spellId)) then
unitAuraTable[auraInfo.auraInstanceID] = auraInfo
auraInfo.targetName = targetName
auraInfo.targetGUID = targetUnitGUID
if (bIsInitialScan) then
if (auraInfo.name == "Prescience") then
Details222.DebugMsg("|cFFFFFF00INIT! Prescience Added.")
end
end
--callback
for callback in pairs(AuraScan.Callbacks) do
callback("AURA_UPDATE", targetUnitGUID, auraInfo, "BUFF_UPTIME_IN")
end
if (AuraScan.AurasToTimeline[spellId]) then
local sourceName = Details:GetFullName(auraInfo.sourceUnit)
local lastestEventAdded = AuraScan.AuraTimelineStorage[#AuraScan.AuraTimelineStorage]
if (not lastestEventAdded or lastestEventAdded.time ~= GetTime() or lastestEventAdded.targetName ~= targetName) then
---@type details_auratimeline
---@diagnostic disable-next-line: missing-fields
local auraTimelineTable = {
appliedTime = GetTime(),
time = GetTime(),
removedTime = 0,
expireTime = auraInfo.expirationTime, --the format is GetTime()
event = "BUFF_UPTIME_IN",
sourceName = sourceName,
targetName = targetName,
targetGUID = targetUnitGUID,
spellId = spellId,
closed = false,
duration = auraInfo.duration,
elapsedTime = 0,
auraInstanceID = auraInfo.auraInstanceID,
name = auraInfo.name,
combatTime = Details:GetCurrentCombat():GetCombatTime(),
}
AuraScan.AuraTimelineStorage[#AuraScan.AuraTimelineStorage+1] = auraTimelineTable
end
end
end
end
end
local fUpdateAura = function(auraInfo)
if (AuraScan.AurasToTimeline[auraInfo.spellId]) then
--find the aura in the timeline and update the expiration time
for i = #AuraScan.AuraTimelineStorage, 1, -1 do
local auraTimelineTable = AuraScan.AuraTimelineStorage[i]
if (auraTimelineTable.auraInstanceID == auraInfo.auraInstanceID) then
local elapsedTime = GetTime() - auraTimelineTable.time
auraTimelineTable.elapsedTime = auraTimelineTable.elapsedTime + elapsedTime
auraTimelineTable.time = GetTime()
auraTimelineTable.expireTime = auraInfo.expirationTime
auraTimelineTable.duration = auraInfo.duration
auraTimelineTable.closed = false
Details222.DebugMsg("|cFFFFFF00REFRESH! Prescience Updated. Duration:", auraTimelineTable.duration)
break
end
end
end
end
local fRemoveAura = function(auraInstanceId)
local auraInfo = unitAuraTable[auraInstanceId]
if (auraInfo) then
unitAuraTable[auraInstanceId] = nil
--callback
for callback in pairs(AuraScan.Callbacks) do
callback("AURA_UPDATE", targetUnitGUID, auraInfo, "BUFF_UPTIME_OUT")
end
if (AuraScan.AurasToTimeline[auraInfo.spellId]) then
local sourceName = Details:GetFullName(auraInfo.sourceUnit)
if (timeLastAuraRemovedFromTimeLine ~= GetTime() or auraInstanceIdLastAuraRemovedFromTimeLine ~= auraInstanceId) then
--find the aura in the timeline and update the elapsedTime
local auraTimelineTableWhenAdded
for i = #AuraScan.AuraTimelineStorage, 1, -1 do
local auraTimelineTable = AuraScan.AuraTimelineStorage[i]
if (auraTimelineTable.auraInstanceID == auraInstanceId) then
local elapsedTime = GetTime() - auraTimelineTable.time
auraTimelineTable.elapsedTime = auraTimelineTable.elapsedTime + elapsedTime
auraTimelineTableWhenAdded = auraTimelineTable
break
end
end
if (not auraTimelineTableWhenAdded) then
Details:Msg("|cFFFF9900AuraScan: fRemoveAura() addedAura was not found in the timeline.")
return
end
--create a new table with the information when the aura was removed
---@type details_auratimeline
local auraClosure = {
time = GetTime(),
appliedTime = auraTimelineTableWhenAdded.appliedTime,
removedTime = GetTime(),
elapsedTime = auraTimelineTableWhenAdded.elapsedTime,
expireTime = 0,
event = "BUFF_UPTIME_OUT",
sourceName = sourceName,
targetName = targetName,
targetGUID = targetUnitGUID,
spellId = auraInfo.spellId,
closed = true,
duration = auraInfo.expirationTime,
auraInstanceID = auraInstanceId,
addTimeLineTable = auraTimelineTableWhenAdded,
name = auraInfo.name,
combatTime = Details:GetCurrentCombat():GetCombatTime(),
}
AuraScan.AuraTimelineStorage[#AuraScan.AuraTimelineStorage+1] = auraClosure
timeLastAuraRemovedFromTimeLine = GetTime()
sourceNameLastAuraRemovedFromTimeLine = sourceName
auraInstanceIdLastAuraRemovedFromTimeLine = auraInstanceId
end
end
end
end
local fFullAuraScan = function(unitId, unitGUID)
local maxCount = nil
local bUsePackedAura = true
unitAuraTable = AuraScan.GetOrCreateUnitAuraTable(unitGUID)
AuraUtil.ForEachAura(unitId, "HELPFUL", maxCount, fAddAura, bUsePackedAura)
end
function AuraScan.OnEvent(frame, eventName, unitId, updateInfo)
--get the unit guid
local unitGUID = UnitGUID(unitId)
if (not unitGUID) then
return
end
-- aura updates dont have updateInfo in 3.3.5, this might be expensive
AuraScan.WipeUnitAuraTable(unitGUID)
unitAuraTable = AuraScan.GetOrCreateUnitAuraTable(unitGUID)
fFullAuraScan(unitId, unitGUID)
return
end
------------------------------------------------------------------------------------------------------------------------------
local scanFrame = CreateFrame("frame", "DetailsAuraScanFrame", UIParent)
function AuraScan.Start()
AuraScan.Enabled = true
--clear the up table holding the current player being scanned
wipe(unitAuraTable or {})
--clear the cache of auras
AuraScan.WipeAllUnitAuraTables()
bIsInitialScan = true
--do the initial aura scan
for i = 1, 4 do --need to change this on raid groups, atm it's only for party
local unitId = "party" .. i
if (UnitExists(unitId)) then
local unitGUID = UnitGUID(unitId)
fFullAuraScan(unitId, unitGUID)
end
end
local unitId = "player"
local unitGUID = UnitGUID(unitId)
fFullAuraScan(unitId, unitGUID)
bIsInitialScan = false
DetailsAuraScanFrame:RegisterEvent("UNIT_AURA")
DetailsAuraScanFrame:SetScript("OnEvent", AuraScan.OnEvent)
end
function AuraScan.Stop()
if (AuraScan.Enabled) then
AuraScan.Enabled = false
DetailsAuraScanFrame:UnregisterEvent("UNIT_AURA")
DetailsAuraScanFrame:SetScript("OnEvent", nil)
--close all opened auras (by running the remove function)
for targetGUID, auras in pairs(AuraScan.UnitAurasStorage) do
unitAuraTable = AuraScan.GetOrCreateUnitAuraTable(targetGUID)
for auraInstanceID, auraInfo in pairs(auras) do
targetUnitGUID = targetGUID
targetName = auraInfo["targetName"]
fRemoveAura(auraInstanceID)
end
end
--callback
for callback in pairs(AuraScan.Callbacks) do
callback("TIMELINE_READY", AuraScan.AuraTimelineStorage)
end
else
Details:Msg("AuraScan.Stop() called, but AuraScan is not enabled.")
end
end
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+906
View File
@@ -0,0 +1,906 @@
local Details = _G.Details
local tocName, Details222 = ...
local _
local pairs = pairs --lua local
local ipairs = ipairs --lua local
local rawget = rawget --lua local
local setmetatable = setmetatable --lua local
local _table_remove = table.remove --lua local
local _bit_band = bit.band --lua local
local _time = time --lua local
local InCombatLockdown = InCombatLockdown --wow api local
local classDamage = Details.atributo_damage --details local
local classHeal = Details.atributo_heal --details local
local classEnergy = Details.atributo_energy --details local
local classUtility = Details.atributo_misc --details local
local classTypeDamage = Details.atributos.dano
local classTypeHeal = Details.atributos.cura
local classTypeEnergy = Details.atributos.e_energy
local classTypeUtility = Details.atributos.misc
--restore actor containers indexes e metatables
function Details:RestoreOverallMetatables()
local bIsInInstance = select(1, IsInInstance())
---@type combat
local combatObjectOverall = Details.tabela_overall
combatObjectOverall.overall_refreshed = true
combatObjectOverall.__call = Details.call_combate
Details.refresh:r_combate(combatObjectOverall)
Details.refresh:r_container_combatentes(combatObjectOverall[classTypeDamage])
Details.refresh:r_container_combatentes(combatObjectOverall[classTypeHeal])
Details.refresh:r_container_combatentes(combatObjectOverall[classTypeEnergy])
Details.refresh:r_container_combatentes(combatObjectOverall[classTypeUtility])
Details.refresh:r_container_combatentes(combatObjectOverall[5]) --ghost container
local todos_atributos = {
combatObjectOverall[classTypeDamage]._ActorTable,
combatObjectOverall[classTypeHeal]._ActorTable,
combatObjectOverall[classTypeEnergy]._ActorTable,
combatObjectOverall[classTypeUtility]._ActorTable
}
for classType = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
local actorContainer = combatObjectOverall[classType]
local actorTable = actorContainer._ActorTable
for i = 1, #actorTable do
---@type actor
local thisActor = actorTable[i]
local actorName = thisActor.nome
if (bIsInInstance and Details.remove_realm_from_name) then
thisActor.displayName = actorName:gsub(("%-.*"), "")
elseif (Details.remove_realm_from_name) then
thisActor.displayName = actorName:gsub(("%-.*"), "") --"%*"
else
thisActor.displayName = actorName
end
if (classType == classTypeDamage) then
Details.refresh:r_atributo_damage(thisActor)
elseif (classType == classTypeHeal) then
Details.refresh:r_atributo_heal(thisActor)
elseif (classType == classTypeEnergy) then
Details.refresh:r_atributo_energy(thisActor)
elseif (classType == classTypeUtility) then
Details.refresh:r_atributo_misc(thisActor)
end
if (thisActor.ownerName) then
thisActor.owner = combatObjectOverall(classType, thisActor.ownerName)
if (not thisActor.owner) then
Details:Msg("found orphan pet (overall), owner not found: ", thisActor.ownerName, " - ", thisActor.nome)
end
end
end
end
end
function Details:RestoreMetatables() --called from Details222.LoadSavedVariables.CombatSegments() --restore actor containers indexes e metatables
--segment container
setmetatable(Details.tabela_historico, Details.historico)
---@type combat
local overallCombatObject = Details.tabela_overall
---@type combat[]
local segmentsTable = Details:GetCombatSegments()
--retore the call "combat()" functionality
for _, combatObject in ipairs(segmentsTable) do
combatObject.__call = Details.call_combate
end
--true if the overall data was saved and restored
local bHadOverallDataSaved = overallCombatObject.overall_refreshed
if (not bHadOverallDataSaved) then
overallCombatObject.start_time = GetTime()
overallCombatObject.end_time = GetTime()
end
overallCombatObject.segments_added = overallCombatObject.segments_added or {}
local bIsInInstance = IsInInstance()
--inicia a recuperao das tabelas e montagem do overall
if (#segmentsTable > 0) then
for index, thisCombatObject in ipairs(segmentsTable) do
---@cast thisCombatObject combat
--set the metatable, __call and __index
Details.refresh:r_combate(thisCombatObject)
--related to overall data
if (not bHadOverallDataSaved and thisCombatObject.overall_added) then
--overall data endTime
if (thisCombatObject.end_time and thisCombatObject.start_time) then
overallCombatObject.start_time = overallCombatObject.start_time - (thisCombatObject.end_time - thisCombatObject.start_time)
end
--overall data startTime
if (overallCombatObject:GetDate() == 0) then
overallCombatObject:SetDate(thisCombatObject:GetDate() or 0)
end
--overall data finished time
local thisCombatDateStart, thisCombaDateEnd = thisCombatObject:GetDate()
local overallDateStart, overallDateEnd = overallCombatObject:GetDate()
overallCombatObject:SetDate(nil, thisCombaDateEnd or overallDateEnd)
--overall data enemy name
if (not Details.tabela_overall.overall_enemy_name) then
Details.tabela_overall.overall_enemy_name = thisCombatObject.is_boss and thisCombatObject.is_boss.name or thisCombatObject.enemy
else
if (Details.tabela_overall.overall_enemy_name ~= (thisCombatObject.is_boss and thisCombatObject.is_boss.name or thisCombatObject.enemy)) then
Details.tabela_overall.overall_enemy_name = "-- x -- x --"
end
end
--overall data segments added
local dateStart, dateEnd = thisCombatObject:GetDate()
table.insert(overallCombatObject.segments_added, {name = thisCombatObject:GetCombatName(false, true), elapsed = thisCombatObject:GetCombatTime(), clock = dateStart})
end
--ghost container (container for custom displays, this is not a real container)
if (thisCombatObject[5]) then
Details.refresh:r_container_combatentes(thisCombatObject[5])
end
local damageActorContainer = thisCombatObject[classTypeDamage]
local healActorContainer = thisCombatObject[classTypeHeal]
local resourcesActorContainer = thisCombatObject[classTypeEnergy]
local utilityActorContainer = thisCombatObject[classTypeUtility]
--recupera a meta e indexes dos 4 container
Details.refresh:r_container_combatentes(damageActorContainer)
Details.refresh:r_container_combatentes(healActorContainer)
Details.refresh:r_container_combatentes(resourcesActorContainer)
Details.refresh:r_container_combatentes(utilityActorContainer)
for classType = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
local actorContainer = thisCombatObject[classType]
local actorTable = actorContainer._ActorTable
for i = 1, #actorTable do
---@type actor
local actorObject = actorTable[i]
local actorName = actorObject.nome
--set back the display name (isn't saved with the object)
if (bIsInInstance and Details.remove_realm_from_name) then
actorObject.displayName = actorName:gsub(("%-.*"), "")
elseif (Details.remove_realm_from_name) then
actorObject.displayName = actorName:gsub(("%-.*"), "")
else
actorObject.displayName = actorName
end
if (classType == classTypeDamage) then
if (thisCombatObject.overall_added and not bHadOverallDataSaved) then
--add the actorObject into another combat, if does not exists there, create it, if exists sum the values
local bRefreshActor = true
classDamage:AddToCombat(actorObject, bRefreshActor, overallCombatObject)
else
Details.refresh:r_atributo_damage(actorObject)
end
elseif (classType == classTypeHeal) then
if (thisCombatObject.overall_added and not bHadOverallDataSaved) then
local bRefreshActor = true
classHeal:AddToCombat(actorObject, bRefreshActor, overallCombatObject)
else
Details.refresh:r_atributo_heal(actorObject)
end
elseif (classType == classTypeEnergy) then
if (thisCombatObject.overall_added and not bHadOverallDataSaved) then
classEnergy:r_connect_shadow (actorObject)
else
classEnergy:r_onlyrefresh_shadow (actorObject)
end
elseif (classType == classTypeUtility) then
if (thisCombatObject.overall_added and not bHadOverallDataSaved) then
classUtility:r_connect_shadow (actorObject)
else
classUtility:r_onlyrefresh_shadow (actorObject)
end
end
end
end
--link pets to owners
for class_type = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
local actorContainer = thisCombatObject[class_type]
local actorTable = actorContainer._ActorTable
for i = 1, #actorTable do
---@type actor
local actorObject = actorTable[i]
if (actorObject.ownerName) then --name of the pet owner
actorObject.owner = thisCombatObject(class_type, actorObject.ownerName)
--technically, if the owner isn't found, this is an orphan and it could be removed from the combat
end
end
end
end
end
end
function Details:DoInstanceCleanup()
for _, instanceObject in ipairs(Details.tabela_instancias) do
---@cast instanceObject instance
if (instanceObject.StatusBar.left) then
instanceObject.StatusBarSaved = {
["left"] = instanceObject.StatusBar.left.real_name or "NONE",
["center"] = instanceObject.StatusBar.center.real_name or "NONE",
["right"] = instanceObject.StatusBar.right.real_name or "NONE",
}
instanceObject.StatusBarSaved.options = {
[instanceObject.StatusBarSaved.left] = instanceObject.StatusBar.left.options,
[instanceObject.StatusBarSaved.center] = instanceObject.StatusBar.center.options,
[instanceObject.StatusBarSaved.right] = instanceObject.StatusBar.right.options
}
end
--erase all widgets frames
instanceObject.scroll = nil
instanceObject.baseframe = nil
instanceObject.bgframe = nil
instanceObject.bgdisplay = nil
instanceObject.freeze_icon = nil
instanceObject.freeze_texto = nil
instanceObject.barras = nil
instanceObject.showing = nil
instanceObject.agrupada_a = nil
instanceObject.grupada_pos = nil
instanceObject.agrupado = nil
instanceObject._version = nil
instanceObject.h_baixo = nil
instanceObject.h_esquerda = nil
instanceObject.h_direita = nil
instanceObject.h_cima = nil
instanceObject.break_snap_button = nil
instanceObject.alert = nil
instanceObject.StatusBar = nil
instanceObject.consolidateFrame = nil
instanceObject.consolidateButtonTexture = nil
instanceObject.consolidateButton = nil
instanceObject.lastIcon = nil
instanceObject.firstIcon = nil
instanceObject.menu_attribute_string = nil
instanceObject.wait_for_plugin_created = nil
instanceObject.waiting_raid_plugin = nil
instanceObject.waiting_pid = nil
end
--unused instances
for _, instanceObject in ipairs(Details.unused_instances) do
---@cast instanceObject instance
if (instanceObject.StatusBar.left) then
instanceObject.StatusBarSaved = {
["left"] = instanceObject.StatusBar.left.real_name or "NONE",
["center"] = instanceObject.StatusBar.center.real_name or "NONE",
["right"] = instanceObject.StatusBar.right.real_name or "NONE",
}
instanceObject.StatusBarSaved.options = {
[instanceObject.StatusBarSaved.left] = instanceObject.StatusBar.left.options,
[instanceObject.StatusBarSaved.center] = instanceObject.StatusBar.center.options,
[instanceObject.StatusBarSaved.right] = instanceObject.StatusBar.right.options
}
end
--erase all widgets frames
instanceObject.scroll = nil
instanceObject.baseframe = nil
instanceObject.bgframe = nil
instanceObject.bgdisplay = nil
instanceObject.freeze_icon = nil
instanceObject.freeze_texto = nil
instanceObject.barras = nil
instanceObject.showing = nil
instanceObject.agrupada_a = nil
instanceObject.grupada_pos = nil
instanceObject.agrupado = nil
instanceObject._version = nil
instanceObject.h_baixo = nil
instanceObject.h_esquerda = nil
instanceObject.h_direita = nil
instanceObject.h_cima = nil
instanceObject.break_snap_button = nil
instanceObject.alert = nil
instanceObject.StatusBar = nil
instanceObject.consolidateFrame = nil
instanceObject.consolidateButtonTexture = nil
instanceObject.consolidateButton = nil
instanceObject.lastIcon = nil
instanceObject.firstIcon = nil
instanceObject.menu_attribute_string = nil
instanceObject.wait_for_plugin_created = nil
instanceObject.waiting_raid_plugin = nil
instanceObject.waiting_pid = nil
end
end
---remove all .owner references from actors, this unlink pets from owners but still leave the actor.ownerName member to rebuild later
function Details:RemoveOwnerFromPets()
---@type combat[]
local segmentsTable = Details:GetCombatSegments() or {}
local bOverallAdded
if (not Details.overall_clear_logout) then
table.insert(segmentsTable, Details.tabela_overall)
bOverallAdded = true
end
for _, combatObject in ipairs(segmentsTable) do
---@cast combatObject combat
for _, actorContainer in ipairs(combatObject) do
---@cast actorContainer actorcontainer
for _, actorObject in ipairs(actorContainer._ActorTable) do
---@cast actorObject actor
actorObject.owner = nil
end
end
end
if (bOverallAdded) then
table.remove(segmentsTable, #segmentsTable)
end
end
function Details:DoClassesCleanup()
---@type combat[]
local segmentsTable = Details:GetCombatSegments() or {}
local bOverallAdded = false
if (not Details.overall_clear_logout) then
--add the overall segment to the cleanup within the other segments
--it is removed after the cleanup
table.insert(segmentsTable, Details.tabela_overall)
bOverallAdded = true
end
for index, combatObject in ipairs(segmentsTable) do
---@cast combatObject combat
for classType, actorContainer in ipairs(combatObject) do
---@cast actorContainer actorcontainer
for _, actorObject in ipairs(actorContainer._ActorTable) do --low level loop for performance
---@cast actorObject actor
actorObject.displayName = nil
actorObject.minha_barra = nil
if (classType == classTypeDamage) then
Details.clear:c_atributo_damage(actorObject)
elseif (classType == classTypeHeal) then
Details.clear:c_atributo_heal(actorObject)
elseif (classType == classTypeEnergy) then
Details.clear:c_atributo_energy(actorObject)
elseif (classType == classTypeUtility) then
Details.clear:c_atributo_misc(actorObject)
end
end
end
end
if (bOverallAdded) then
--remove the overall segment from the regular segments
table.remove(segmentsTable, #segmentsTable)
end
end
function Details:DoContainerCleanup()
---@type combat[]
local segmentsTable = Details:GetCombatSegments() or {}
local bOverallAdded
if (not Details.overall_clear_logout) then
table.insert(segmentsTable, Details.tabela_overall)
bOverallAdded = true
end
for _, combatObject in ipairs(segmentsTable) do
---@cast combatObject combat
Details.clear:c_combate(combatObject)
for _, actorContainer in ipairs(combatObject) do
---@cast actorContainer actorcontainer
Details.clear:c_container_combatentes(actorContainer)
end
end
if (bOverallAdded) then
table.remove(segmentsTable, #segmentsTable)
end
end
function Details:DoContainerIndexCleanup()
---@type combat[]
local segmentsTable = Details:GetCombatSegments() or {}
local bOverallAdded
if (not Details.overall_clear_logout) then
table.insert(segmentsTable, Details.tabela_overall)
bOverallAdded = true
end
for _, combatObject in ipairs(segmentsTable) do
for _, actorContainer in ipairs(combatObject) do
Details.clear:c_container_combatentes_index(actorContainer)
end
end
if (bOverallAdded) then
table.remove(segmentsTable, #segmentsTable)
end
end
--limpa indexes e metatables
function Details:PrepareTablesForSave()
Details.clear_ungrouped = true
--clear instances
Details:DoInstanceCleanup()
Details:DoClassesCleanup()
Details:DoContainerCleanup()
--clear combats
---@type combat[]
local combatTables = {}
---@type combat[]
local segmentsTable = Details:GetCombatSegments() or {}
for i = #segmentsTable, 1, -1 do
---@type combat
local combatObject = segmentsTable[i]
if (combatObject.__destroyed) then
table.remove(segmentsTable, i)
end
end
--remove segments marked as 'trash'
for i = #segmentsTable, 1, -1 do
---@type combat
local combatObject = segmentsTable[i]
if (combatObject:IsTrash()) then
table.remove(segmentsTable, i)
end
end
segmentsTable = Details:GetCombatSegments() or {}
--remove segments > of the segment limit to save
if (Details.segments_amount_to_save and Details.segments_amount_to_save < Details.segments_amount) then
for i = Details.segments_amount, Details.segments_amount_to_save + 1, -1 do
if (segmentsTable[i]) then
table.remove(segmentsTable, i)
end
end
end
--clear overall segment
if (Details.overall_clear_logout) then
Details.tabela_overall = nil
_detalhes_database.tabela_overall = nil
else
---@type combat
local overallCombatObject = Details.tabela_overall
--this is a cleanup for overall data (overall)
if (Details.clear_ungrouped) then
--deal with actor which could potentially be removed from the database
for containerId = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
local actorContainer = overallCombatObject:GetContainer(containerId)
local actorTable = actorContainer:GetActorTable()
for actorIndex = #actorTable, 1, -1 do
---@type actor
local actorObject = actorTable[actorIndex]
for funcName in pairs(Details222.Mixins.ActorMixin) do
actorObject[funcName] = nil
end
if (Details222.Actors.IsDisposable(actorObject) and not actorObject.owner) then
Details222.SaveVariables.LogEvent("actor removed " .. actorObject.nome .. " (disposable)")
Details:DestroyActor(actorObject, actorContainer, overallCombatObject)
end
end
actorContainer:Cleanup()
end
end
--find orphans, finding orphans should be done when deleting an actor, it should iterate among the actor pets and delete them as well
--now deal with pets without owners (overall)
for containerId = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
local actorContainer = overallCombatObject:GetContainer(containerId)
local actorTable = actorContainer:GetActorTable()
for actorIndex = #actorTable, 1, -1 do
---@type actor
local actorObject = actorTable[actorIndex]
if (actorObject.owner) then
--does this pet owner got removed from the database?
if (not actorObject.owner.serial) then
Details222.SaveVariables.LogEvent("actor removed " .. actorObject.nome .. " (owner not found)")
Details:DestroyActor(actorObject, actorContainer, overallCombatObject)
end
end
end
actorContainer:Cleanup()
end
end
for i, combatObject in ipairs(segmentsTable) do
---@cast combatObject combat
combatTables[#combatTables+1] = combatObject
end
--this is a cleanup for combat stored in the segment list
for combatIndex, combatObject in ipairs(combatTables) do
---@cast combatObject combat
--clear the time data (chart data) - if the option to cleanup on logout is enabled
if (Details.clear_graphic) then
Details:Destroy(combatObject.TimeData)
combatObject.TimeData = {}
end
local bIsBossEncounter = combatObject.is_boss
if (bIsBossEncounter) then
if (combatObject.pvp) then
bIsBossEncounter = false
end
end
if (not combatObject.is_mythic_dungeon_segment and Details.clear_ungrouped) then
for i = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
---@type actorcontainer
local actorContainer = combatObject:GetContainer(i)
if (actorContainer) then
local actorTable = actorContainer:GetActorTable()
for o = #actorTable, 1, -1 do
---@type actor
local actorObject = actorTable[o]
for funcName in pairs(Details222.Mixins.ActorMixin) do
actorObject[funcName] = nil
end
if (not actorObject.owner and not actorObject.grupo and not actorObject.boss and not actorObject.boss_fight_component and not bIsBossEncounter and not actorObject.pvp_component and not actorObject.fight_component) then
Details222.SaveVariables.LogEvent("actor removed " .. actorObject.nome .. " (ungrouped)")
Details:DestroyActor(actorObject, actorContainer, combatObject)
end
end
actorContainer:Cleanup()
--find orphans
for o = #actorTable, 1, -1 do
---@type actor
local actorObject = actorTable[o]
if (actorObject.owner) then
--does this pet owner got removed from the database?
if (not actorObject.owner.serial) then
Details222.SaveVariables.LogEvent("actor removed " .. actorObject.nome .. " (orphan)")
Details:DestroyActor(actorObject, actorContainer, combatObject)
end
end
end
actorContainer:Cleanup()
end
end
else
if (combatObject.is_mythic_dungeon_segment) then
for i = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
---@type actorcontainer
local actorContainer = combatObject:GetContainer(i)
if (actorContainer) then
local actorTable = actorContainer:GetActorTable()
for o = #actorTable, 1, -1 do
---@type actor
local actorObject = actorTable[o]
for funcName in pairs(Details222.Mixins.ActorMixin) do
actorObject[funcName] = nil
end
end
end
end
end
end
end
--panic mode (in case the player disconnets during a boss encounter, drop all tables to speedup the login and login back process)
if (Details.segments_panic_mode and Details.can_panic_mode) then
if (Details.tabela_vigente.is_boss) then
Details.tabela_historico = Details.historico:CreateNewSegmentDatabase()
end
end
--clear all segments on logoff
if (Details.data_cleanup_logout) then
Details.tabela_historico = Details.historico:CreateNewSegmentDatabase()
Details.tabela_overall = nil
_detalhes_database.tabela_overall = nil
end
--clear customs
Details.clear:c_atributo_custom()
--clear owners
Details:RemoveOwnerFromPets()
--clear container indexes
Details:DoContainerIndexCleanup()
end
function Details:reset_window(instancia)
if (instancia.segmento == -1) then
instancia.showing[instancia.atributo].need_refresh = true
instancia.v_barras = true
instancia:ResetaGump()
instancia:RefreshMainWindow(true)
end
end
---start/restart the internal garbage collector runtime ~garbage
---@param bShouldForceCollect boolean if true, the garbage collector will run regardless of the time interval
---@param lastEvent unixtime no call is passing lastEvent at the moment
function Details222.GarbageCollector.RestartInternalGarbageCollector(bShouldForceCollect, lastEvent)
--print("d! debug: running garbage collector...")
if (not bShouldForceCollect) then
local thisTime = Details222.GarbageCollector.lastCollectTime + Details222.GarbageCollector.intervalTime
if (thisTime > Details._tempo + 1) then
return
elseif (Details.in_combat or InCombatLockdown() or Details:IsInInstance()) then
Details.Schedules.After(5, Details222.GarbageCollector.RestartInternalGarbageCollector, false, lastEvent)
return
end
else
if (type(bShouldForceCollect) ~= "boolean") then
if (bShouldForceCollect == 1) then
if (Details.in_combat or InCombatLockdown()) then
Details.Schedules.After(5, Details222.GarbageCollector.RestartInternalGarbageCollector, bShouldForceCollect, lastEvent)
return
end
end
end
end
if (Details.debug) then
if (bShouldForceCollect) then
--Details:Msg("(debug) collecting garbage with forced state:", bShouldForceCollect)
else
--Details:Msg("(debug) collecting garbage.")
end
end
--cleanup all the parser caches
Details:ClearParserCache()
--cleanup lines which isn't shown but has an actor attached to
for instanceId, instanceObject in Details:ListInstances() do
if (instanceObject.barras and instanceObject.barras[1]) then
for i, lineRow in ipairs(instanceObject.barras) do
if (not lineRow:IsShown()) then
lineRow.minha_tabela = nil
end
end
end
end
--print("d! debug: RunGarbageCollector() Start")
---@type number
local amountActorRemoved = Details222.GarbageCollector.RunGarbageCollector(lastEvent)
--print("d! debug: RunGarbageCollector() Ended, cleanup:", amountActorRemoved, "actors.") --139 actor removed, but don't remove anything (/reload it remove again)
--UpdateAddOnMemoryUsage()
--local memoryUsage = GetAddOnMemoryUsage("Details")
--print("Memory:", floor(memoryUsage)/1000, "MBytes")
--refresh nas janelas
if (amountActorRemoved > 0) then
Details:InstanceCallDetailsFunc(Details.reset_window)
end
--cleanup backlisted pets within the handler of actor containers
Details222.PetContainer.DoMaintenance()
Details:ClearCCPetsBlackList()
--cleanup spec cache
Details:ResetSpecCache()
--cleanup the shield cache
Details:Destroy(Details.ShieldCache)
--set the time of the last run
Details222.GarbageCollector.lastCollectTime = Details._tempo
if (Details.debug) then
--Details:Msg("(debug) executing: collectgarbage().")
--collectgarbage()
end
end
---check all the actors and remove the ones which are not in use
---@param combatObject combat
---@param overriteInterval unixtime
---@return integer
local collectGarbage = function(combatObject, overriteInterval)
--amount of actors removed
local amountCleaned = 0
--do not collect things in a mythic+ dungeon segment
if (combatObject.is_mythic_dungeon_run_id or combatObject.is_mythic_dungeon_segment) then
return amountCleaned
end
if (combatObject.__destroyed) then
Details:Msg("a deleted combat object was found on g2.collector, please report this bug on discord:")
Details:Msg("combat destroyed by:", combatObject.__destroyedBy)
return 0
end
---@type number
local _tempo = _time()
---@type number
for containerId = 1, 4 do
---@type actorcontainer
local actorContainer = combatObject:GetContainer(containerId)
---@type table<number, actor>
local actorList = actorContainer:GetActorTable()
for actorIndex = #actorList, 1, -1 do
---@type actor
local actorObject = actorList[actorIndex]
if (Details222.Actors.IsDisposable(actorObject) and not actorObject.owner) then
local canCollect = false
--check the time of the last seen event coming from the actor
---@type unixtime
local lastSeenEventTime = actorObject.last_event
---@type number
local nextGarbageCollection
if (overriteInterval) then
nextGarbageCollection = lastSeenEventTime + overriteInterval
else
nextGarbageCollection = lastSeenEventTime + Details222.GarbageCollector.intervalTime
end
if (nextGarbageCollection - 1 < _tempo) then
canCollect = true
end
if (canCollect) then
amountCleaned = amountCleaned + 1
if (containerId == 1 or containerId == 2) then --damage or healing
Details222.TimeMachine.RemoveActor(actorObject)
end
--remove the actor from the container
Details:DestroyActor(actorObject, actorContainer, combatObject) --a window showing 'Auras & Void Zones' did not refreshed and had an actor pointing to here
end
end
end
actorContainer:Cleanup()
if (amountCleaned > 0) then
--destroy orphans
local orphansCleaned = 0
for actorIndex = #actorList, 1, -1 do
---@type actor
local actorObject = actorList[actorIndex]
if (actorObject.owner and not actorObject.owner.serial) then
Details:DestroyActor(actorObject, actorContainer, combatObject)
orphansCleaned = orphansCleaned + 1
end
end
actorContainer:Cleanup()
--refresh the breakdown window
if (Details.BreakdownWindowFrame:IsShown()) then
Details222.BreakdownWindow.RefreshPlayerScroll()
end
end
actorContainer.need_refresh = true
end --end of containerId loop
return amountCleaned
end --end of collectGarbage function
---run the garbage collector
---@param overriteLastEvent unixtime
function Details222.GarbageCollector.RunGarbageCollector(overriteLastEvent)
---@type number
local amountRemoved = 0
---@type combat
local currentCombat = Details:GetCurrentCombat()
--create a list of all combats except the current one
---@type table<number, combat>
local segmentsTable = Details:GetCombatSegments()
--collect destroyed combat objects
local bGotSegmentsRemoved = false
for i = #segmentsTable, 1, -1 do
local combatObject = segmentsTable[i]
if (combatObject ~= currentCombat) then
if (combatObject.__destroyed) then
table.remove(segmentsTable, i)
bGotSegmentsRemoved = true
end
end
end
if (bGotSegmentsRemoved) then
Details:SendEvent("DETAILS_DATA_SEGMENTREMOVED")
end
---@type table
local segmentsList = {}
--add all segments except the current one
for _, combatObject in ipairs(segmentsTable) do
if (combatObject ~= currentCombat) then
segmentsList[#segmentsList+1] = combatObject
end
end
--add the current segment at the end of the list
segmentsList[#segmentsList+1] = currentCombat
--collect the garbage
for i, combatObject in ipairs(segmentsList) do
if (combatObject.__destroyed) then
Details:Msg("a deleted combat object was found by the g.collector, please report this bug on discord:")
Details:Msg("combat destroyed by:", combatObject.__destroyedBy)
end
local removedActors = collectGarbage(combatObject, overriteLastEvent)
if (i == #segmentsList) then
--print("current segment removed:", removedActors, "actors.")
end
amountRemoved = amountRemoved + removedActors
end
---@type combat
local overallCombatObject = Details.tabela_overall
amountRemoved = amountRemoved + collectGarbage(overallCombatObject, overriteLastEvent)
if (amountRemoved > 0) then
Details:InstanceCallDetailsFunc(Details.ScheduleUpdate)
Details:RefreshMainWindow(-1)
end
return amountRemoved
end
---return true if the actor is disposable, in other words, if it can be removed from the combat without affecting the results
---@param actor actor
---@return boolean
function Details222.Actors.IsDisposable(actor)
if (not actor.grupo and not actor.boss and not actor.boss_fight_component and not actor.fight_component and not actor.pvp_component and not actor.arena_enemy and not actor.enemy) then
return true
else
return false
end
end
+574
View File
@@ -0,0 +1,574 @@
local Details = _G.Details
local Loc = LibStub("AceLocale-3.0"):GetLocale( "Details" )
local _
---@type detailsframework
local detailsFramework = DetailsFramework
--register namespace
Details.network = {}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--local pointers
local UnitName = UnitName
local GetRealmName = GetRealmName
local select = select
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--constants
_G.DETAILS_PREFIX_NETWORK = "DTLS"
local CONST_HIGHFIVE_REQUEST = "HI"
local CONST_HIGHFIVE_DATA = "HF"
local CONST_VERSION_CHECK = "CV"
local CONST_ITEMLEVEL_DATA = "IL"
local CONST_WIPE_CALL = "WI"
local CONST_GUILD_SYNC = "GS"
local CONST_CLOUD_REQUEST = "CR"
local CONST_CLOUD_FOUND = "CF"
local CONST_CLOUD_DATARQ = "CD"
local CONST_CLOUD_DATARC = "CE"
local CONST_CLOUD_EQUALIZE = "EQ"
local CONST_CLOUD_SHAREDATA = "SD"
local CONST_PVP_ENEMY = "PP"
local CONST_ROGUE_SR = "SR" --soul rip from akaari's soul (LEGION ONLY)
_G.DETAILS_PREFIX_COACH = "CO" --coach feature
Details.network.ids = {
["HIGHFIVE_REQUEST"] = CONST_HIGHFIVE_REQUEST,
["HIGHFIVE_DATA"] = CONST_HIGHFIVE_DATA,
["VERSION_CHECK"] = CONST_VERSION_CHECK,
["ITEMLEVEL_DATA"] = CONST_ITEMLEVEL_DATA,
["CLOUD_REQUEST"] = CONST_CLOUD_REQUEST,
["CLOUD_FOUND"] = CONST_CLOUD_FOUND,
["CLOUD_DATARQ"] = CONST_CLOUD_DATARQ,
["CLOUD_DATARC"] = CONST_CLOUD_DATARC,
["CLOUD_EQUALIZE"] = CONST_CLOUD_EQUALIZE,
["WIPE_CALL"] = CONST_WIPE_CALL,
["GUILD_SYNC"] = CONST_GUILD_SYNC,
["PVP_ENEMY"] = CONST_PVP_ENEMY,
["MISSDATA_ROGUE_SOULRIP"] = CONST_ROGUE_SR, --soul rip from akaari's soul (LEGION ONLY)
["CLOUD_SHAREDATA"] = CONST_CLOUD_SHAREDATA,
["COACH_FEATURE"] = DETAILS_PREFIX_COACH, --ask the raid leader is the coach is enbaled
}
local registredPlugins = {}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--comm functions
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--item level
local getHorizontalTalentsAsString = function()
local talents = ""
for i = 1, 7 do
for o = 1, 3 do
local talentID, name, texture, selected, available = GetTalentInfo(i, o, 1)
if (selected) then
talents = "" .. talentID .. ","
break
end
end
end
--remove the comma after the last talent id
if (talents:sub(-1) == ",") then
talents = talents:sub(1, -2)
end
return talents
end
---send item level data to the group the player is in
---@param self details
---@return nil
function Details:SendCharacterData()
--only send if in group
if (not IsInGroup() and not IsInRaid()) then
return
end
--check the player level to be at least 60
---@type number
local playerLevel = UnitLevel("player")
if (not playerLevel) then
return
elseif (playerLevel < 60) then
return
end
--delay to sent information again
if (Details.LastPlayerInfoSync and Details.LastPlayerInfoSync + 10 > GetTime()) then
--do not send info if it was recently sent
return
end
--get the equipped player item level
local equipped = GetAverageItemLevel()
local talentsAsString = C_CharacterAdvancement.ExportBuild(true)
--get the spec ID
local spec = DetailsFramework.GetSpecialization()
local currentSpec
if (spec) then
local specID = DetailsFramework.GetSpecializationInfo(spec)
if (specID and specID ~= 0) then
currentSpec = specID
end
end
--get the character serial number
local serial = UnitGUID("player")
if (IsInRaid()) then
Details:SendRaidData(CONST_ITEMLEVEL_DATA, serial, equipped, talentsAsString, currentSpec)
if (Details.debugnet) then
Details:Msg("(debug) sent ilevel data to Raid")
end
elseif (IsInGroup()) then
Details:SendPartyData(CONST_ITEMLEVEL_DATA, serial, equipped, talentsAsString, currentSpec)
if (Details.debugnet) then
Details:Msg("(debug) sent ilevel data to Party")
end
end
Details.LastPlayerInfoSync = GetTime()
end
function Details.network.ItemLevel_Received(player, realm, coreVersion, serial, itemlevel, talents, spec)
Details:IlvlFromNetwork(player, realm, coreVersion, serial, itemlevel, talents, spec)
end
--high five
function Details.network.HighFive_Request()
return Details:SendRaidData(CONST_HIGHFIVE_DATA, Details.userversion)
end
function Details.network.HighFive_DataReceived(player, realm, coreVersion, userVersion)
if (Details.sent_highfive and Details.sent_highfive + 30 > GetTime()) then
Details.users[#Details.users+1] = {player, realm, (userVersion or "") .. " (" .. coreVersion .. ")"}
end
end
function Details.network.Update_VersionReceived(player, realm, coreVersion, buildNumber)
if (Details.debugnet) then
Details:Msg("(debug) received version alert ", buildNumber)
end
if (Details.streamer_config.no_alerts) then
return
end
buildNumber = tonumber(buildNumber)
if (not Details.build_counter or not Details.lastUpdateWarning or not buildNumber) then
return
end
if (buildNumber > Details.build_counter) then
if (time() > Details.lastUpdateWarning + 72000) then
local lowerInstanceId = Details:GetLowerInstanceNumber()
if (lowerInstanceId) then
local instance = Details:GetInstance(lowerInstanceId)
if (instance) then
instance:InstanceAlert("Update Available!", {[[Interface\GossipFrame\AvailableQuestIcon]], 16, 16, false}, Details.update_warning_timeout, {Details.OpenUpdateWindow})
end
end
Details:Msg(Loc["STRING_VERSION_AVAILABLE"])
Details.lastUpdateWarning = time()
end
end
end
function Details.network.Cloud_Request(player, realm, coreVersion, ...)
--deprecated
end
function Details.network.Cloud_Found(player, realm, coreVersion, ...)
--deprecated
end
function Details.network.Cloud_DataRequest(player, realm, coreVersion, ...)
--deprecated
end
function Details.network.Cloud_DataReceived(player, realm, coreVersion, ...)
--deprecated
end
function Details.network.Cloud_Equalize(player, realm, coreVersion, data)
--deprecated
end
function Details.network.Wipe_Call(player, realm, coreVersion, ...)
local chr_name = Ambiguate(player, "none")
if (UnitIsGroupLeader (chr_name)) then
if (UnitIsInMyGuild (chr_name)) then
Details:CallWipe()
end
end
end
function Details.network.Cloud_SharedData(player, realm, coreVersion, data)
--deprecated
end
--"CIEA" Coach Is Enabled Ask (client > server)
--"CIER" Coach Is Enabled Response (server > client)
--"CCS" Coach Combat Start (client > server)
function Details.network.Coach(player, realm, coreVersion, msgType, data)
if (not IsInRaid()) then
return
end
if (Details.debugnet) then
print("Details Coach Received Comm", player, realm, coreVersion, msgType, data)
end
local sourcePlayer = Ambiguate(player, "none")
local playerName = UnitName("player")
if (playerName == sourcePlayer) then
if (Details.debugnet) then
print("Details Coach Received Comm | RETURN | playerName == sourcePlayer", playerName , sourcePlayer)
end
return
end
if (msgType == "CIEA") then --Is Coach Enabled Ask (regular player asked to raid leader)
Details.Coach.Server.CoachIsEnabled_Answer(sourcePlayer)
elseif (msgType == "CIER") then --Coach Is Enabled Response (regular player received a raid leader response)
local isEnabled = data
Details.Coach.Client.CoachIsEnabled_Response(isEnabled, sourcePlayer)
elseif (msgType == "CCS") then --Coach Combat Start (raid assistant told the raid leader a combat started)
Details.Coach.Server.CombatStarted()
elseif (msgType == "CCE") then --Coach Combat End (raid assistant told the raid leader a combat ended)
Details.Coach.Server.CombatEnded()
elseif (msgType == "CS") then --Coach Start (raid leader notifying other members of the group)
if (Details.debugnet) then
print("Details Coach received 'CE' a new coach is active, coach name:", sourcePlayer)
end
Details.Coach.Client.EnableCoach(sourcePlayer)
elseif (msgType == "CE") then --Coach End (raid leader notifying other members of the group)
Details.Coach.Client.CoachEnd()
elseif (msgType == "CDT") then --Coach Data (a player in the raid sent to raid leader combat data)
if (Details.Coach.Server.IsEnabled()) then
--update the current combat with new information
Details.packFunctions.DeployPackedCombatData(data)
end
elseif (msgType == "CDD") then --Coach Death (a player in the raid sent to raid leader his death log)
if (Details.Coach.Server.IsEnabled()) then
Details.Coach.Server.AddPlayerDeath(sourcePlayer, data)
end
end
end
--guild sync R = someone pressed the sync button
--guild sync L = list of fights IDs
--guild sync G = requested a list of encounters
--guild sync A = received missing encounters, add them
function Details.network.GuildSync(sourceName, realm, coreVersion, type, data)
local characterName = Ambiguate(sourceName, "none")
if (UnitName("player") == sourceName) then
--return
end
if (coreVersion ~= Details.realversion) then
--return false
end
if (type == "R") then --RoS - somebody requested IDs of stored encounters
Details.LastGuildSyncDataTime1 = Details.LastGuildSyncDataTime1 or 0
--build our table and send to the player
if (Details.LastGuildSyncDataTime1 > GetTime()) then
--return false
end
local IDs = Details222.storage.GetIDsToGuildSync()
if (IDs and IDs [1]) then
local from = UnitName("player")
local realm = GetRealmName()
Details:SendCommMessage(DETAILS_PREFIX_NETWORK, Details:Serialize(CONST_GUILD_SYNC, from, realm, Details.realversion, "L", IDs), "WHISPER", sourceName)
end
Details.LastGuildSyncDataTime1 = GetTime() + 60
return true
elseif (type == "L") then --RoC - the player received the IDs list and send back which IDs he doesn't have
local missingIds = Details222.storage.CheckMissingIDsToGuildSync(data)
if (missingIds and missingIds[1]) then
local from = UnitName ("player")
local realm = GetRealmName()
Details:SendCommMessage(DETAILS_PREFIX_NETWORK, Details:Serialize(CONST_GUILD_SYNC, from, realm, Details.realversion, "G", missingIds), "WHISPER", sourceName)
end
return true
elseif (type == "G") then --RoS - the 'server' send the encounter dps table to the player which requested
local encounterData = Details222.storage.BuildEncounterDataToGuildSync(data)
if (encounterData and encounterData[1]) then
local task = C_Timer.NewTicker(4, function(task)
task.TickID = task.TickID + 1
local data = task.EncounterData[task.TickID]
if (not data) then
task:Cancel()
return
end
local from = UnitName("player")
local realm = GetRealmName()
--todo: need to check if the target is still online
Details:SendCommMessage(DETAILS_PREFIX_NETWORK, Details:Serialize(CONST_GUILD_SYNC, from, realm, Details.realversion, "A", data), "WHISPER", task.Target)
if (Details.debugnet) then
Details:Msg("(debug) [RoS-EncounterSync] send-task sending data #" .. task.TickID .. ".")
end
end)
task.TickID = 0
task.EncounterData = encounterData
task.Target = characterName
end
return true
elseif (type == "A") then --RoC - the player received the dps table and should now add it to the db
Details222.storage.AddGuildSyncData(data, player)
return true
end
end
function Details.network.ReceivedEnemyPlayer(player, realm, coreVersion, data)
--deprecated
end
Details.network.functions = {
[CONST_HIGHFIVE_REQUEST] = Details.network.HighFive_Request,
[CONST_HIGHFIVE_DATA] = Details.network.HighFive_DataReceived,
[CONST_VERSION_CHECK] = Details.network.Update_VersionReceived,
[CONST_ITEMLEVEL_DATA] = Details.network.ItemLevel_Received,
[CONST_CLOUD_REQUEST] = Details.network.Cloud_Request,
[CONST_CLOUD_FOUND] = Details.network.Cloud_Found,
[CONST_CLOUD_DATARQ] = Details.network.Cloud_DataRequest,
[CONST_CLOUD_DATARC] = Details.network.Cloud_DataReceived,
[CONST_CLOUD_EQUALIZE] = Details.network.Cloud_Equalize,
[CONST_WIPE_CALL] = Details.network.Wipe_Call,
[CONST_GUILD_SYNC] = Details.network.GuildSync,
[CONST_PVP_ENEMY] = Details.network.ReceivedEnemyPlayer,
[DETAILS_PREFIX_COACH] = Details.network.Coach, --coach feature
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--register comm
function Details:CommReceived(commPrefix, data, channel, source)
local deserializedTable = {Details:Deserialize(data)}
if (not deserializedTable[1]) then
if (Details.debugnet) then
Details:Msg("(debug) network deserialize |cFFFF0000failed|r, from:", source, Details:Deserialize(data))
end
return
end
tremove(deserializedTable, 1)
local prefix, player, realm, coreVersion, arg6, arg7, arg8, arg9 = unpack(deserializedTable)
player = source
if (Details.debugnet) then
Details:Msg("(debug) network received prefix:", prefix, "length:", string.len(data), source)
end
if (type(prefix) ~= "string") then
if (Details.debugnet) then
Details:Msg("(debug) network |cFFFF0000failed|r: prefix isn't a string", prefix, "length:", string.len(data))
end
return
elseif (type(coreVersion) ~= "number") then
if (Details.debugnet) then
Details:Msg("(debug) network |cFFFF0000failed|r: coreVersion isn't a number", prefix, "length:", string.len(data))
end
return
end
--event
Details:SendEvent("COMM_EVENT_RECEIVED", nil, string.len(data), prefix, player, realm, coreVersion, arg6, arg7, arg8, arg9)
local func = Details.network.functions[prefix]
if (func) then
local callName = "CommReceived|" .. prefix .. "|" .. coreVersion .. "|" .. Details:GetCoreVersion()
Details.SafeRun(func, callName, player, realm, coreVersion, arg6, arg7, arg8, arg9)
else
func = registredPlugins[prefix]
if (func) then
local callName = "CommReceived|Plugin|" .. prefix .. "|" .. coreVersion .. "|" .. Details:GetCoreVersion()
Details.SafeRun(func, callName, player, realm, coreVersion, arg6, arg7, arg8, arg9)
else
if (Details.debugnet) then
Details:Msg("comm prefix not found:", prefix)
end
end
end
end
Details:RegisterComm("DTLS", "CommReceived")
--hook the send comm message so we can trigger events when sending data
--this adds overhead, but easily catches all outgoing comm messages
hooksecurefunc(Details, "SendCommMessage", function(context, addonPrefix, serializedData, channel)
--unpack data
local prefix, player, realm, coreVersion, arg6, arg7, arg8, arg9 = select(2, Details:Deserialize(serializedData))
--send the event
Details:SendEvent("COMM_EVENT_SENT", nil, string.len(serializedData), prefix, player, realm, coreVersion, arg6, arg7, arg8, arg9)
end)
function Details:RegisterPluginComm(prefix, func)
assert(type(prefix) == "string" and string.len(prefix) >= 2 and string.len(prefix) <= 4, "RegisterPluginComm expects a string with 2-4 characters on #1 argument.")
assert(type(func) == "function" or (type(func) == "string" and type(self[func]) == "function"), "RegisterPluginComm expects a function or function name on #2 argument.")
assert(registredPlugins[prefix] == nil, "Prefix " .. prefix .. " already in use 1.")
assert(Details.network.functions[prefix] == nil, "Prefix " .. prefix .. " already in use 2.")
if (type(func) == "string") then
registredPlugins[prefix] = self[func]
else
registredPlugins[prefix] = func
end
return true
end
function Details:UnregisterPluginComm(prefix)
registredPlugins[prefix] = nil
return true
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--send functions
function Details:GetChannelId(channel)
--deprecated
end
function Details.parser_functions:CHAT_MSG_CHANNEL(...)
--deprecated
end
function Details:SendPluginCommMessage(prefix, channel, ...)
if (channel == "RAID") then
if (IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and IsInInstance()) then
Details:SendCommMessage(prefix, Details:Serialize(self.__version, ...), "INSTANCE_CHAT")
else
Details:SendCommMessage(prefix, Details:Serialize(self.__version, ...), "RAID")
end
elseif (channel == "PARTY") then
if (IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and IsInInstance()) then
Details:SendCommMessage(prefix, Details:Serialize(self.__version, ...), "INSTANCE_CHAT")
else
Details:SendCommMessage(prefix, Details:Serialize(self.__version, ...), "PARTY")
end
else
Details:SendCommMessage(prefix, Details:Serialize(self.__version, ...), channel)
end
return true
end
--send as
function Details:SendRaidDataAs(type, player, realm, ...)
--deprecated
end
function Details:SendHomeRaidData(type, ...)
Details:SendCommMessage(DETAILS_PREFIX_NETWORK, Details:Serialize(type, UnitName("player"), GetRealmName(), Details.realversion, ...), "RAID")
end
function Details:SendRaidData(type, ...)
Details:SendCommMessage(DETAILS_PREFIX_NETWORK, Details:Serialize(type, UnitName("player"), GetRealmName(), Details.realversion, ...), "RAID")
end
function Details:SendPartyData(type, ...)
local prefix = DETAILS_PREFIX_NETWORK
local data = Details:Serialize(type, UnitName("player"), GetRealmName(), Details.realversion, ...)
local channel = "PARTY"
Details:SendCommMessage(prefix, data, channel)
end
function Details:SendRaidOrPartyData(type, ...)
if (IsInRaid()) then
Details:SendRaidData(type, ...)
elseif (IsInGroup()) then
Details:SendPartyData(type, ...)
end
end
function Details:SendGuildData(type, ...)
if not IsInGuild() then return end --fix from Tim@WoWInterface
Details:SendCommMessage(DETAILS_PREFIX_NETWORK, Details:Serialize(type, UnitName("player"), GetRealmName(), Details.realversion, ...), "GUILD")
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--cloud
function Details:SendCloudRequest()
--deprecated
end
function Details:ScheduleSendCloudRequest()
--deprecated
end
function Details:RequestCloudData()
--deprecated
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--update
function Details:CheckVersion(sendToGuild)
if (IsInRaid()) then
Details:SendRaidData(Details.network.ids.VERSION_CHECK, Details.build_counter)
elseif (IsInGroup()) then
Details:SendPartyData(Details.network.ids.VERSION_CHECK, Details.build_counter)
end
if (sendToGuild) then
Details:SendGuildData(Details.network.ids.VERSION_CHECK, Details.build_counter)
end
end
+6787
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
local Details = _G.Details
local _
local helloWorld
+897
View File
@@ -0,0 +1,897 @@
local Loc = LibStub("AceLocale-3.0"):GetLocale( "Details" )
local Details = _G.Details
local PixelUtil = PixelUtil or DFPixelUtil
local addonName, Details222 = ...
local CreateFrame = CreateFrame
---@type detailsframework
local detailsFramework = DetailsFramework
local UIParent = UIParent
local UISpecialFrames = UISpecialFrames
local breakdownWindowFrame = Details.BreakdownWindowFrame
DETAILSPLUGIN_ALWAYSENABLED = 0x1 --[[GLOBAL]]
local CONST_PLUGINWINDOW_MENU_WIDTH = 150
local CONST_PLUGINWINDOW_MENU_HEIGHT = 22
local CONST_PLUGINWINDOW_MENU_X = -5
local CONST_PLUGINWINDOW_MENU_Y = -26
local CONST_PLUGINWINDOW_WIDTH = 925
local CONST_PLUGINWINDOW_HEIGHT = 600
---default cooltip appearance for plugin tooltips
function Details:SetCooltipForPlugins()
local gameCooltip = GameCooltip
gameCooltip:Preset(2)
gameCooltip:SetOption("TextSize", Details.font_sizes.menus)
gameCooltip:SetOption("TextFont", Details.font_faces.menus)
gameCooltip:SetOption("LineHeightSizeOffset", 0)
gameCooltip:SetOption("LineYOffset", 0)
gameCooltip:SetOption("LinePadding", -1)
gameCooltip:SetOption("FrameHeightSizeOffset", 0)
gameCooltip:SetOption("FixedWidth", 280)
gameCooltip:SetOption("StatusBarTexture", [[Interface\AddOns\Details\images\bar_serenity]])
gameCooltip:SetOption("LeftTextWidth", 280 - 22 - 90)
gameCooltip:SetOption("LeftTextHeight", 14)
Details:SetTooltipMinWidth()
end
---comment
---@param pluginAbsoluteName string
---@return unknown
function Details:GetPlugin(pluginAbsoluteName)
return Details.SoloTables.NameTable[pluginAbsoluteName] or Details.RaidTables.NameTable[pluginAbsoluteName] or Details.ToolBar.NameTable[pluginAbsoluteName] or Details.StatusBar.NameTable[pluginAbsoluteName] or Details.PluginsLocalizedNames[pluginAbsoluteName] or Details.PluginsGlobalNames[pluginAbsoluteName]
end
---comment
---@param pluginAbsoluteName string
---@return unknown
function Details:GetPluginSavedTable(pluginAbsoluteName)
return Details.plugin_database[pluginAbsoluteName]
end
---comment
function Details:UpdatePluginBarsConfig()
---@type instance
local instanceObject = self:GetPluginInstance()
if (instanceObject) then
self.row_info = self.row_info or {}
Details.table.copy(self.row_info, instanceObject.row_info)
self.bars_grow_direction = instanceObject.bars_grow_direction
self.row_height = instanceObject.row_height
self:SetBarGrowDirection()
end
end
function Details:AttachToInstance()
---@type instance
local instanceObject = self:GetPluginInstance()
if (instanceObject) then
local width, height = instanceObject:GetSize()
self.Frame:SetSize(width, height)
end
end
---comment
---@param pluginAbsoluteName string|nil
---@return any
function Details:GetPluginInstance(pluginAbsoluteName)
local plugin = self
if (pluginAbsoluteName) then
plugin = Details:GetPlugin(pluginAbsoluteName)
end
local id = plugin.instance_id
if (id) then
return Details:GetInstance(id)
end
end
function Details:IsPluginEnabled(pluginAbsoluteName)
if (pluginAbsoluteName) then
local plugin = Details.plugin_database[pluginAbsoluteName]
if (plugin) then
return plugin.enabled
end
else
return self.__enabled
end
end
---comment
---@param desc string
function Details:SetPluginDescription(desc)
self.__description = desc
end
---get the description of a plugin
---@return string
function Details:GetPluginDescription()
return self.__description or ""
end
---disable a plugin
---@param pluginAbsoluteName string
---@return boolean
function Details:DisablePlugin(pluginAbsoluteName)
local plugin = Details:GetPlugin(pluginAbsoluteName)
if (plugin) then
local savedTable = Details:GetPluginSavedTable(pluginAbsoluteName)
savedTable.enabled = false
plugin.__enabled = false
Details:SendEvent("PLUGIN_DISABLED", plugin)
Details:DelayOptionsRefresh()
return true
end
return false
end
---check if the plugin saved table has all the default key and values
---@param savedTable table
---@param defaultSavedTable table
function Details:CheckDefaultTable(savedTable, defaultSavedTable)
for key, value in pairs(defaultSavedTable) do
if (type(value) == "table") then
if (type(savedTable[key]) ~= "table") then
savedTable[key] = Details.CopyTable(value)
else
Details:CheckDefaultTable(savedTable[key], value)
end
else
if (savedTable[key] == nil) then
savedTable[key] = value
end
end
end
end
function Details:InstallPlugin(pluginType, pluginName, pluginIcon, pluginObject, pluginAbsoluteName, minVersion, authorName, version, defaultSavedTable)
if (minVersion and minVersion > Details.realversion) then
print(pluginName, Loc["STRING_TOOOLD"])
return Details:NewError("Details version is out of date.")
end
if (pluginType == "TANK") then
pluginType = "RAID"
end
if (not pluginType) then
return Details:NewError("InstallPlugin parameter 1 (plugin type) not especified")
elseif (not pluginName) then
return Details:NewError("InstallPlugin parameter 2 (plugin name) can't be nil")
elseif (not pluginIcon) then
return Details:NewError("InstallPlugin parameter 3 (plugin icon) can't be nil")
elseif (not pluginObject) then
return Details:NewError("InstallPlugin parameter 4 (plugin object) can't be nil")
elseif (not pluginAbsoluteName) then
return Details:NewError("InstallPlugin parameter 5 (plugin absolut name) can't be nil")
end
if (_G[pluginAbsoluteName]) then
print(Loc["STRING_PLUGIN_NAMEALREADYTAKEN"] .. ": " .. pluginName .. " name: " .. pluginAbsoluteName)
return
else
_G[pluginAbsoluteName] = pluginObject
pluginObject.real_name = pluginAbsoluteName
end
pluginObject.__name = pluginName
pluginObject.__author = authorName or "--------"
pluginObject.__version = version or "v1.0.0"
pluginObject.__icon = pluginIcon or[[Interface\ICONS\Trade_Engineering]]
pluginObject.real_name = pluginAbsoluteName
Details.PluginsGlobalNames[pluginAbsoluteName] = pluginObject
Details.PluginsLocalizedNames[pluginName] = pluginObject
local savedTable
if (pluginType ~= "STATUSBAR") then
savedTable = Details.plugin_database[pluginAbsoluteName]
if (not savedTable) then
savedTable = {enabled = true, author = authorName or "--------"}
Details.plugin_database[pluginAbsoluteName] = savedTable
end
if (defaultSavedTable) then
Details:CheckDefaultTable(savedTable, defaultSavedTable)
end
pluginObject.__enabled = savedTable.enabled
end
if (pluginType == "SOLO") then
if (not pluginObject.Frame) then
return Details:NewError("plugin doesn't have a Frame, please check case-sensitive member name: Frame")
end
Details.SoloTables.Plugins[#Details.SoloTables.Plugins+1] = pluginObject
Details.SoloTables.Menu[#Details.SoloTables.Menu+1] = {pluginName, pluginIcon, pluginObject, pluginAbsoluteName}
Details.SoloTables.NameTable[pluginAbsoluteName] = pluginObject
Details:SendEvent("INSTALL_OKEY", pluginObject)
Details.PluginCount.SOLO = Details.PluginCount.SOLO + 1
elseif (pluginType == "RAID") then
Details.RaidTables.Plugins[#Details.RaidTables.Plugins+1] = pluginObject
Details.RaidTables.Menu[#Details.RaidTables.Menu+1] = {pluginName, pluginIcon, pluginObject, pluginAbsoluteName}
Details.RaidTables.NameTable[pluginAbsoluteName] = pluginObject
Details:SendEvent("INSTALL_OKEY", pluginObject)
Details.PluginCount.RAID = Details.PluginCount.RAID + 1
Details:InstanceCall("RaidPluginInstalled", pluginAbsoluteName)
elseif (pluginType == "TOOLBAR") then
Details.ToolBar.Plugins[#Details.ToolBar.Plugins+1] = pluginObject
Details.ToolBar.Menu[#Details.ToolBar.Menu+1] = {pluginName, pluginIcon, pluginObject, pluginAbsoluteName}
Details.ToolBar.NameTable[pluginAbsoluteName] = pluginObject
Details:SendEvent("INSTALL_OKEY", pluginObject)
Details.PluginCount.TOOLBAR = Details.PluginCount.TOOLBAR + 1
elseif (pluginType == "STATUSBAR") then
Details.StatusBar.Plugins[#Details.StatusBar.Plugins+1] = pluginObject
Details.StatusBar.Menu[#Details.StatusBar.Menu+1] = {pluginName, pluginIcon}
Details.StatusBar.NameTable[pluginAbsoluteName] = pluginObject
Details:SendEvent("INSTALL_OKEY", pluginObject)
Details.PluginCount.STATUSBAR = Details.PluginCount.STATUSBAR + 1
end
if (savedTable) then
pluginObject.db = savedTable
end
if (pluginObject.__enabled) then
return true, savedTable, true
else
return true, savedTable, false
end
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--internal functions
---@type table<plugintype, number>
Details.PluginCount = {
["SOLO"] = 0,
["RAID"] = 0,
["TOOLBAR"] = 0,
["STATUSBAR"] = 0
}
local onEnableFunction = function(self)
self.__parent.Enabled = true
---@type instance
local instanceObject = Details:GetInstance(self.__parent.instance_id)
if (instanceObject) then
self:SetParent(instanceObject.baseframe)
end
Details:SendEvent("SHOW", self.__parent)
end
local onDisableFunction = function(self)
Details:SendEvent("HIDE", self.__parent)
if (bit.band(self.__parent.__options, DETAILSPLUGIN_ALWAYSENABLED) == 0) then
self.__parent.Enabled = false
end
end
local buildDefaultStatusBarMembers = function(self)
self.childs = {}
self.__index = self
function self:Setup()
Details.StatusBar:OpenOptionsForChild(self)
end
end
local temp_event_function = function()
print("=====================")
print("Hello There plugin developer!")
print("Please make sure you are declaring")
print("A member called 'OnDetailsEvent' on your plugin object")
print("With a function to receive the events like bellow:")
print("function PluginObject:OnDetailsEvent(event, ...) end")
print("Thank You Sir!===================")
end
local registerEventFunc = function(self, event)
self.Frame:RegisterEvent(event)
end
local unregisterEventFunc = function(self, event)
self.Frame:UnregisterEvent(event)
end
---@param frameName string
---@param pluginFlag number
---@param pluginType plugintype
function Details:NewPluginObject(frameName, pluginFlag, pluginType)
pluginFlag = pluginFlag or 0x0
local newPluginObject = {__options = pluginFlag, __enabled = true, RegisterEvent = registerEventFunc, UnregisterEvent = unregisterEventFunc}
local pluginFrame = CreateFrame("Frame", frameName, UIParent, "BackdropTemplate")
pluginFrame:RegisterEvent("PLAYER_LOGIN")
pluginFrame:RegisterEvent("PLAYER_LOGOUT")
pluginFrame:SetFrameStrata("HIGH")
pluginFrame:SetFrameLevel(6)
pluginFrame:Hide()
pluginFrame:SetScript("OnShow", onEnableFunction)
pluginFrame:SetScript("OnHide", onDisableFunction)
pluginFrame.__parent = newPluginObject
pluginFrame:SetScript("OnEvent", function(self, event, ...)
if (newPluginObject.OnEvent) then
if (event == "PLAYER_LOGIN") then
newPluginObject:OnEvent(self, "ADDON_LOADED", newPluginObject.Frame:GetName())
newPluginObject.Frame:Hide()
return
end
return newPluginObject:OnEvent(self, event, ...)
end
end)
if (bit.band(pluginFlag, DETAILSPLUGIN_ALWAYSENABLED) ~= 0) then
newPluginObject.Enabled = true
else
newPluginObject.Enabled = false
end
--default members
if (pluginType == "STATUSBAR") then
buildDefaultStatusBarMembers(newPluginObject)
end
newPluginObject.Frame = pluginFrame
newPluginObject.OnDetailsEvent = temp_event_function
setmetatable(newPluginObject, Details)
return newPluginObject
end
---create a window for plugin options
---@param name string
---@param title string
---@param template number? @1 = standard backdrop, @2 = buttonframe, @3 = rounded corners
---@param pluginIcon string?
---@param pluginIconCoords table?
function Details:CreatePluginOptionsFrame(name, title, template, pluginIcon, pluginIconCoords)
template = template or 3
if (template == 3) then
local optionsFrame = CreateFrame("frame", name, UIParent)
table.insert(UISpecialFrames, name)
optionsFrame:SetSize(500, 200)
optionsFrame:SetMovable(true)
optionsFrame:EnableMouse(true)
optionsFrame:SetFrameStrata("DIALOG")
optionsFrame:SetToplevel(true)
optionsFrame:SetPoint("center", UIParent, "center")
optionsFrame:Hide()
detailsFramework:AddRoundedCornersToFrame(optionsFrame, Details.PlayerBreakdown.RoundedCornerPreset)
Details:RegisterFrameToColor(optionsFrame)
--create a an icon to display the pluginIcon
local pluginIconTexture = detailsFramework:CreateTexture(optionsFrame, pluginIcon, 20, 20, "artwork", pluginIconCoords or {0, 1, 0, 1}, "pluginIconTexture", "$parentPluginIconTexture")
pluginIconTexture:SetPoint("topleft", optionsFrame, "topleft", 5, -5)
if (not pluginIcon) then
pluginIconTexture:SetSize(1, 20)
end
--create a font string in the topleft corner for plugin name
local pluginNameLabel = detailsFramework:CreateLabel(optionsFrame, title, 20, "yellow")
pluginNameLabel:SetPoint("left", pluginIconTexture, "right", 2, 0)
--create a close button at the right top corner
local closeButton = detailsFramework:CreateCloseButton(optionsFrame)
closeButton:SetPoint("topright", optionsFrame, "topright", -5, -5)
local bigDogTexture = detailsFramework:CreateTexture(optionsFrame, [[Interface\MainMenuBar\UI-MainMenuBar-EndCap-Human]], 110, 120, nil, {1, 0, 0, 1}, "backgroundBigDog", "$parentBackgroundBigDog")
bigDogTexture:SetPoint("bottomright", optionsFrame, "bottomright", -3, 0)
bigDogTexture:SetAlpha(.25)
optionsFrame:SetScript("OnMouseDown", function(self, button)
if (button == "RightButton") then
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
return optionsFrame:Hide()
elseif (button == "LeftButton" and not self.moving) then
self.moving = true
self:StartMoving()
end
end)
optionsFrame:SetScript("OnMouseUp", function(self)
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
end)
return optionsFrame
elseif (template == 2) then
local optionsFrame = CreateFrame("frame", name, UIParent, "ButtonFrameTemplate, BackdropTemplate")
table.insert(UISpecialFrames, name)
optionsFrame:SetSize(500, 200)
optionsFrame:SetMovable(true)
optionsFrame:EnableMouse(true)
optionsFrame:SetFrameStrata("DIALOG")
optionsFrame:SetToplevel(true)
optionsFrame:SetPoint("center", UIParent, "center")
optionsFrame:Hide()
optionsFrame:SetScript("OnMouseDown", function(self, button)
if (button == "RightButton") then
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
return optionsFrame:Hide()
elseif (button == "LeftButton" and not self.moving) then
self.moving = true
self:StartMoving()
end
end)
optionsFrame:SetScript("OnMouseUp", function(self)
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
end)
return optionsFrame
elseif (template == 1) then
local optionsFrame = CreateFrame("frame", name, UIParent, "BackdropTemplate")
table.insert(UISpecialFrames, name)
optionsFrame:SetSize(500, 200)
optionsFrame:SetMovable(true)
optionsFrame:EnableMouse(true)
optionsFrame:SetFrameStrata("DIALOG")
optionsFrame:SetToplevel(true)
optionsFrame:SetPoint("center", UIParent, "center", 0, 0)
optionsFrame:Hide()
optionsFrame:SetScript("OnMouseDown", function(self, button)
if (button == "RightButton") then
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
return optionsFrame:Hide()
elseif (button == "LeftButton" and not self.moving) then
self.moving = true
self:StartMoving()
end
end)
optionsFrame:SetScript("OnMouseUp", function(self)
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
end)
optionsFrame:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16,
edgeFile = [[Interface\AddOns\Details\images\border_2]], edgeSize = 32,
insets = {left = 1, right = 1, top = 1, bottom = 1}})
optionsFrame:SetBackdropColor(0, 0, 0, .7)
detailsFramework:ApplyStandardBackdrop(optionsFrame)
detailsFramework:CreateTitleBar(optionsFrame, title)
local bigDogTexture = detailsFramework:NewImage(optionsFrame, [[Interface\MainMenuBar\UI-MainMenuBar-EndCap-Human]], 110, 120, nil, {1, 0, 0, 1}, "backgroundBigDog", "$parentBackgroundBigDog")
bigDogTexture:SetPoint("bottomright", optionsFrame, "bottomright", -3, 0)
bigDogTexture:SetAlpha(.25)
return optionsFrame
end
end
function Details:CreateRightClickToCloseLabel(parent)
local mouseIcon = detailsFramework:CreateAtlasString(Details:GetTextureAtlas("right-mouse-click"), 12, 9)
local rightClickToBackLabel = detailsFramework:CreateLabel(parent, mouseIcon .. " right click to close", "GameFontNormal")
rightClickToBackLabel:SetAlpha(0.834)
rightClickToBackLabel.textcolor = "gray"
parent.RightClickLabel = rightClickToBackLabel
return rightClickToBackLabel
end
function Details:CreatePluginWindowContainer()
local pluginContainerFrame = CreateFrame("frame", "DetailsPluginContainerWindow", UIParent, "BackdropTemplate")
pluginContainerFrame:EnableMouse(true)
pluginContainerFrame:SetMovable(true)
pluginContainerFrame:SetPoint("center", UIParent, "center", 0, 0)
pluginContainerFrame:SetClampedToScreen(true)
table.insert(UISpecialFrames, "DetailsPluginContainerWindow")
pluginContainerFrame:Hide()
--members
pluginContainerFrame.MenuX = CONST_PLUGINWINDOW_MENU_X
pluginContainerFrame.MenuY = CONST_PLUGINWINDOW_MENU_Y
pluginContainerFrame.MenuButtonWidth = CONST_PLUGINWINDOW_MENU_WIDTH
pluginContainerFrame.MenuButtonHeight = CONST_PLUGINWINDOW_MENU_HEIGHT
pluginContainerFrame.FrameWidth = CONST_PLUGINWINDOW_WIDTH
pluginContainerFrame.FrameHeight = CONST_PLUGINWINDOW_HEIGHT
pluginContainerFrame.TitleHeight = 20
--store button references for the left menu
pluginContainerFrame.MenuButtons = {}
--store all plugins embed
pluginContainerFrame.EmbedPlugins = {}
--lib window
pluginContainerFrame:SetSize(pluginContainerFrame.FrameWidth, pluginContainerFrame.FrameHeight)
local LibWindow = LibStub("LibWindow-1.1")
LibWindow.RegisterConfig(pluginContainerFrame, Details.plugin_window_pos)
LibWindow.RestorePosition(pluginContainerFrame)
LibWindow.MakeDraggable(pluginContainerFrame)
LibWindow.SavePosition(pluginContainerFrame)
local scaleBar = DetailsFramework:CreateScaleBar(pluginContainerFrame, Details.options_window, true)
scaleBar:SetFrameStrata("fullscreen")
pluginContainerFrame:SetScale(Details.options_window.scale)
pluginContainerFrame.scaleBar = scaleBar
--left side bar menu
local optionsLeftSideBarMenu = CreateFrame("frame", "$parentMenuFrame", pluginContainerFrame, "BackdropTemplate")
detailsFramework:AddRoundedCornersToFrame(optionsLeftSideBarMenu, Details.PlayerBreakdown.RoundedCornerPreset)
optionsLeftSideBarMenu:SetPoint("topright", pluginContainerFrame, "topleft", -2, 0)
optionsLeftSideBarMenu:SetPoint("bottomright", pluginContainerFrame, "bottomleft", -2, 0)
optionsLeftSideBarMenu:SetWidth(pluginContainerFrame.MenuButtonWidth + 6)
pluginContainerFrame.optionsLeftSideBarMenu = optionsLeftSideBarMenu
--statusbar
local statusBar = CreateFrame("frame", nil, optionsLeftSideBarMenu, "BackdropTemplate")
statusBar:SetPoint("bottomleft", pluginContainerFrame, "bottomleft", 7, 5)
statusBar:SetPoint("bottomright", pluginContainerFrame, "bottomright", 0, 5)
statusBar:SetHeight(16)
statusBar:SetAlpha(1)
DetailsFramework:BuildStatusbarAuthorInfo(statusBar)
local rightClickToBackLabel = Details:CreateRightClickToCloseLabel(statusBar)
rightClickToBackLabel:SetPoint("bottomright", statusBar, "bottomright", -150, 5)
local bigDogTexture = detailsFramework:NewImage(optionsLeftSideBarMenu, [[Interface\MainMenuBar\UI-MainMenuBar-EndCap-Human]], 180*0.7, 200*0.7, "overlay", {0, 1, 0, 1}, "backgroundBigDog", "$parentBackgroundBigDog")
bigDogTexture:SetPoint("bottomleft", custom_window, "bottomleft", 0, 1)
bigDogTexture:SetAlpha(0)
local gradientBelowTheLine = DetailsFramework:CreateTexture(optionsLeftSideBarMenu, {gradient = "vertical", fromColor = {0, 0, 0, 0.45}, toColor = "transparent"}, 1, 95, "artwork", {0, 1, 0, 1}, "dogGradient")
gradientBelowTheLine:SetPoint("bottoms")
gradientBelowTheLine:Hide()
local bigDogRowTexture = optionsLeftSideBarMenu:CreateTexture(nil, "artwork")
bigDogRowTexture:SetPoint("bottomleft", optionsLeftSideBarMenu, "bottomleft", 1, 1)
bigDogRowTexture:SetPoint("bottomright", optionsLeftSideBarMenu, "bottomright", -1, 1)
bigDogRowTexture:SetHeight(20)
bigDogRowTexture:SetTexture(.5, .5, .5, .1)
bigDogRowTexture:Hide()
--tools title bar
local titleBarTools = CreateFrame("frame", "$parentToolsHeader", optionsLeftSideBarMenu, "BackdropTemplate")
PixelUtil.SetPoint(titleBarTools, "topleft", optionsLeftSideBarMenu, "topleft", 2, -3)
PixelUtil.SetPoint(titleBarTools, "topright", optionsLeftSideBarMenu, "topright", -2, -3)
titleBarTools:SetHeight(pluginContainerFrame.TitleHeight)
--tools title label
local titleBarTools_TitleLabel = detailsFramework:NewLabel(titleBarTools, titleBarTools, nil, "titulo", "Tools", "GameFontHighlightLeft", 12, {227/255, 186/255, 4/255})
PixelUtil.SetPoint(titleBarTools_TitleLabel, "center", titleBarTools , "center", 0, 0)
PixelUtil.SetPoint(titleBarTools_TitleLabel, "top", titleBarTools , "top", 0, -5)
--check if the window isn't out of screen
pluginContainerFrame:SetScript("OnShow", function()
C_Timer.After(1, function()
local right = pluginContainerFrame:GetRight()
if (right and right > GetScreenWidth() + 500) then
pluginContainerFrame:ClearAllPoints()
pluginContainerFrame:SetPoint("center", UIParent, "center", 0, 0)
LibWindow.SavePosition(pluginContainerFrame)
Details:Msg("detected options panel out of screen, position has reset")
end
local scaleFactor = pluginContainerFrame:GetScale()
if (scaleFactor < 0.65) then
pluginContainerFrame:SetScale(0.65)
Details:Msg("detected options panel scale issue, scale has reset, please reload the UI")
end
end)
end)
pluginContainerFrame:SetScript("OnHide", function() end)
pluginContainerFrame:SetScript("OnMouseDown", function(self, button)
if (button == "RightButton") then
pluginContainerFrame.ClosePlugin()
end
end)
pluginContainerFrame.Debug = false
function pluginContainerFrame.DebugMsg(...)
if (pluginContainerFrame.Debug) then
print("[Details! Debug]", ...)
end
end
local getPluginObject = function(pluginAbsoluteName)
local pluginObject = Details:GetPlugin(pluginAbsoluteName)
if (not pluginObject) then
for index, plugin in ipairs(pluginContainerFrame.EmbedPlugins) do
if (plugin.real_name == pluginAbsoluteName) then
pluginObject = plugin
end
end
if (not pluginObject) then
pluginContainerFrame.DebugMsg("Plugin not found")
return
end
end
return pluginObject
end
local hideOtherPluginFrames = function(pluginObject)
local bIsShowingAPlugin = Details222.BreakdownWindow.IsPluginShown()
local pluginShownInBreakdownWindow = breakdownWindowFrame.GetShownPluginObject()
for index, thisPluginObject in ipairs(pluginContainerFrame.EmbedPlugins) do
if (thisPluginObject ~= pluginObject) then
if (thisPluginObject.__isUtility) then
--hide this plugin
if (thisPluginObject.Frame:IsShown()) then
thisPluginObject.Frame:Hide()
end
else
if (bIsShowingAPlugin) then
if (pluginShownInBreakdownWindow == thisPluginObject) then
--do nothing yet
else
--hide this plugin
if (thisPluginObject.Frame:IsShown()) then
thisPluginObject.Frame:Hide()
end
end
else
--hide this plugin
if (thisPluginObject.Frame:IsShown()) then
thisPluginObject.Frame:Hide()
end
end
end
end
end
end
local highlightPluginButton = function(pluginAbsoluteName)
for index, button in ipairs(pluginContainerFrame.MenuButtons) do
button:Show()
if (button.PluginAbsName == pluginAbsoluteName) then
button:SetTemplate(detailsFramework:GetTemplate("button", "DETAILS_PLUGINPANEL_BUTTONSELECTED_TEMPLATE"))
else
button:SetTemplate(detailsFramework:GetTemplate("button", "DETAILS_PLUGINPANEL_BUTTON_TEMPLATE"))
end
end
end
function pluginContainerFrame.OnMenuClick(_, _, pluginAbsoluteName, callRefresh)
local pluginObject = getPluginObject(pluginAbsoluteName)
if (not pluginObject) then
return
end
--hide other plugin windows
hideOtherPluginFrames(pluginObject)
--re set the point of the frame within the main plugin window
pluginContainerFrame.RefreshFrame(pluginObject.__var_Frame)
C_Timer.After(0, function()
pluginContainerFrame.RefreshFrame(pluginObject.__var_Frame)
end)
--show the plugin window
if (pluginObject.RefreshWindow and callRefresh) then
DetailsFramework:QuickDispatch(pluginObject.RefreshWindow)
end
--highlight the plugin button on the menu
highlightPluginButton(pluginAbsoluteName)
--show the container
pluginContainerFrame:Show()
--check if the plugin has a callback for when showing the frame
if (pluginObject.__OnClickFromOptionsCallback) then
--safe run the plugin callback
DetailsFramework:QuickDispatch(pluginObject.__OnClickFromOptionsCallback)
end
return true
end
---create a button fro the plugin be selected in the options panel
---@param self details
---@param pluginObject any
---@param bIsUtility any
---@param parent frame
---@param onClickFunc function
---@param width number
---@param height number
---@return button
function Details:CreatePluginMenuButton(pluginObject, bIsUtility, parent, onClickFunc, width, height)
local newButton = detailsFramework:CreateButton(parent, onClickFunc, width, height, pluginObject.__name, pluginObject.real_name, true)
newButton.PluginAbsName = pluginObject.real_name
newButton.PluginName = pluginObject.__name
newButton.IsUtility = bIsUtility
pluginObject.__isUtility = bIsUtility
newButton:SetTemplate("STANDARD_GRAY")
newButton:SetText(pluginObject.__name)
newButton.textsize = 10
newButton:SetIcon(pluginObject.__icon, nil, nil, nil, pluginObject.__iconcoords, pluginObject.__iconcolor, 4)
return newButton
end
local onHide = function(self)
DetailsPluginContainerWindow.ClosePlugin()
end
local setupFrameFunctions = function(frame)
frame:SetScript("OnMouseDown", nil)
frame:SetScript("OnMouseUp", nil)
frame:HookScript("OnHide", onHide)
end
function pluginContainerFrame.RefreshFrame(frame, parent)
frame:EnableMouse(false)
frame:SetSize(pluginContainerFrame.FrameWidth, pluginContainerFrame.FrameHeight)
frame:ClearAllPoints()
PixelUtil.SetPoint(frame, "topleft", parent or pluginContainerFrame, "topleft", 0, 0)
frame:SetParent(parent or pluginContainerFrame)
frame:Show()
end
---a plugin has request to be embed into the main plugin window
---@param pluginObject table can be the plugin object or any frame
---@param frame frame any frame
---@param bIsUtility boolean if true, the plugin is in fact a regular panel in the options panel
---@param callback function a callback to run when the plugin is clicked
function pluginContainerFrame.EmbedPlugin(pluginObject, frame, bIsUtility, callback)
--check if the plugin has a frame
if (not pluginObject.Frame) then
pluginContainerFrame.DebugMsg("plugin doesn't have a frame.")
return
end
--add it to menu table
if (bIsUtility) then
--create a button for this plugin
local pluginButton = Details:CreatePluginMenuButton(pluginObject, bIsUtility, pluginContainerFrame, pluginContainerFrame.OnMenuClick, pluginContainerFrame.MenuButtonWidth, pluginContainerFrame.MenuButtonHeight)
--only register button if it's a utility, plugins now are placed into the breakdown window
table.insert(pluginContainerFrame.MenuButtons, pluginButton)
pluginObject.__var_Frame = frame
pluginObject.__var_Utility = true
--sort buttons alphabetically, put utilities at the end
table.sort(pluginContainerFrame.MenuButtons, function(t1, t2)
if (t1.IsUtility and t2.IsUtility) then
return t1.PluginName < t2.PluginName
elseif (t1.IsUtility) then
return false
elseif (t2.IsUtility) then
return true
else
return t1.PluginName < t2.PluginName
end
end)
--reset the buttons points
for index, button in ipairs(pluginContainerFrame.MenuButtons) do
button:ClearAllPoints()
PixelUtil.SetPoint(button, "center", optionsLeftSideBarMenu, "center", 0, 0)
PixelUtil.SetPoint(button, "top", optionsLeftSideBarMenu, "top", 0, pluginContainerFrame.MenuY +((index-1) * -pluginContainerFrame.MenuButtonHeight ) - index)
detailsFramework:SetTemplate(button, "STANDARD_GRAY")
end
--format the plugin main frame
pluginContainerFrame.RefreshFrame(frame)
setupFrameFunctions(frame)
--save the callback function for when clicking in the button from the options panel
pluginObject.__OnClickFromOptionsCallback = callback
--add the plugin to embed table
table.insert(pluginContainerFrame.EmbedPlugins, pluginObject)
frame:SetParent(pluginContainerFrame)
pluginContainerFrame.DebugMsg("plugin added", pluginObject.__name)
end
end
function pluginContainerFrame.OpenPlugin(pluginObject)
if (pluginObject.__breakdownwindow) then
breakdownWindowFrame.ShowPluginOnBreakdown(pluginObject)
return
end
--simulate a click on the menu button
pluginContainerFrame.OnMenuClick(_, _, pluginObject.real_name)
end
---hide all embed plugins
function pluginContainerFrame.ClosePlugin()
for index, plugin in ipairs(pluginContainerFrame.EmbedPlugins) do
plugin.Frame:Hide()
end
--hide the main frame
pluginContainerFrame:Hide()
end
--[=[
Function to be used on macros to open a plugin, signature:
Details:OpenPlugin(PLUGIN_ABSOLUTE_NAME)
Details:OpenPlugin(PluginObject)
Details:OpenPlugin("Plugin Name")
Example: /run Details:OpenPlugin("Time Line")
--]=]
---function used when the user uses the macro command /run Details:OpenPlugin("Plugin Name")
---@param wildCard any
---@return any
function Details:OpenPlugin(wildCard)
local originalName = wildCard
if (type(wildCard) == "string") then
--check if passed a plugin absolute name
local pluginObject = Details:GetPlugin(wildCard)
if (pluginObject) then
if (pluginObject.__breakdownwindow) then
breakdownWindowFrame.ShowPluginOnBreakdown(pluginObject)
return
end
pluginContainerFrame.OpenPlugin(pluginObject)
return true
end
--check if passed a plugin name, remove spaces and make it lower case
wildCard = string.lower(wildCard)
wildCard = wildCard:gsub("%s", "")
for index, pluginInfoTable in ipairs(Details.ToolBar.Menu) do
local pluginName = pluginInfoTable[1]
pluginName = string.lower(pluginName)
pluginName = pluginName:gsub("%s", "")
if (pluginName == wildCard) then
local pluginObject = pluginInfoTable[3]
if (pluginObject.__breakdownwindow) then
breakdownWindowFrame.ShowPluginOnBreakdown(pluginObject)
return
end
pluginContainerFrame.OpenPlugin(pluginObject)
return true
end
end
--check if passed a plugin object
elseif (type(wildCard) == "table") then
if (wildCard.__name) then
pluginContainerFrame.OpenPlugin(wildCard)
return true
end
end
Details:Msg("|cFFFF7700plugin not found|r:|cFFFFFF00",(originalName or wildCard), "|rcheck if it is enabled in the addons control panel.") --localize-me
end
end
+644
View File
@@ -0,0 +1,644 @@
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
local _detalhes = _G.Details
local Loc = LibStub("AceLocale-3.0"):GetLocale ( "Details" )
local _
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--local pointers
local _math_floor = math.floor --lua local
local _cstr = string.format --lua local
local _UnitClass = UnitClass
local GetSpellLink = GetSpellLink or C_Spell.GetSpellLink --api local
local gump = _detalhes.gump --details local
local _GetSpellInfo = _detalhes.getspellinfo --details api
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--constants
local modo_raid = _detalhes._detalhes_props["MODO_RAID"]
local modo_alone = _detalhes._detalhes_props["MODO_ALONE"]
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--internal functions
function _detalhes.RaidTables:DisableRaidMode (instance)
--free
self:SetInUse (instance.current_raid_plugin, nil)
--hide
local current_plugin_object = _detalhes:GetPlugin (instance.current_raid_plugin)
if (current_plugin_object) then
current_plugin_object.Frame:Hide()
end
instance.current_raid_plugin = nil
end
function _detalhes:RaidPluginInstalled (plugin_name)
if (self.waiting_raid_plugin) then
--print(self.meu_id, 2, self.last_raid_plugin, " == ", plugin_name)
if (self.last_raid_plugin == plugin_name) then
if (self.waiting_pid) then
self:CancelTimer(self.waiting_pid, true)
end
self:CancelWaitForPlugin()
_detalhes.RaidTables:EnableRaidMode (self, plugin_name)
end
end
--force hide wait for plugins
if (_G["DetailsWaitFrameBG"..self.meu_id] and _G["DetailsWaitForPluginFrame" .. self.meu_id]) then
_G["DetailsWaitForPluginFrame" .. self.meu_id]:Hide()
_G["DetailsWaitFrameBG"..self.meu_id]:Hide()
end
end
function _detalhes.RaidTables:EnableRaidMode (instance, plugin_name, from_cooltip, from_mode_menu)
--check if came from cooltip
if (from_cooltip) then
self = _detalhes.RaidTables
instance = plugin_name
plugin_name = from_cooltip
end
--set the mode
if (instance.modo == modo_alone) then
instance:SoloMode (false)
end
instance.modo = modo_raid
--hide rows, scrollbar
Details.FadeHandler.Fader(instance, 1, nil, "barras")
if (instance.rolagem) then
instance:EsconderScrollBar (true) --hida a scrollbar
end
_detalhes:ResetaGump (instance)
instance:RefreshMainWindow(true)
--get the plugin name
--if the desired plugin isn't passed, try to get the latest used.
if (not plugin_name) then
local last_plugin_used = instance.last_raid_plugin
if (last_plugin_used) then
if (self:IsAvailable (last_plugin_used, instance)) then
plugin_name = last_plugin_used
end
end
end
--if we still doesnt have a name, try to get the first available
if (not plugin_name) then
local available = self:GetAvailablePlugins()
if (#available == 0) then
if (not instance.wait_for_plugin_created or not instance.WaitForPlugin) then
instance:CreateWaitForPlugin()
end
return instance:WaitForPlugin()
end
plugin_name = available [1] [4]
end
--last check if the name is okey
if (self:IsAvailable (plugin_name, instance)) then
self:switch(nil, plugin_name, instance)
if (from_mode_menu) then
--refresh
instance.baseframe.cabecalho.modo_selecao:GetScript("OnEnter")(instance.baseframe.cabecalho.modo_selecao, _, true)
end
else
if (not instance.wait_for_plugin) then
instance:CreateWaitForPlugin()
end
return instance:WaitForPlugin()
end
end
function _detalhes.RaidTables:GetAvailablePlugins()
local available = {}
for index, plugin in ipairs(self.Menu) do
if (not self.PluginsInUse [ plugin [4] ] and plugin [3].__enabled) then -- 3 = plugin object 4 = absolute name
tinsert(available, plugin)
end
end
return available
end
function _detalhes.RaidTables:IsAvailable (plugin_name, instance)
--check if is installed
if (not self.NameTable [plugin_name]) then
return false
end
--check if is enabled
if (not self.NameTable [plugin_name].__enabled) then
return false
end
--check if is available
local in_use = self.PluginsInUse [ plugin_name ]
if (in_use and in_use ~= instance:GetId()) then
return false
else
return true
end
end
function _detalhes.RaidTables:SetInUse (absolute_name, instance_number)
if (absolute_name) then
self.PluginsInUse [ absolute_name ] = instance_number
end
end
----------------
function _detalhes.RaidTables:switch(_, plugin_name, instance)
local update_menu = false
if (not self) then --came from cooltip
self = _detalhes.RaidTables
update_menu = true
end
--only hide the current plugin shown
if (not plugin_name) then
if (instance.current_raid_plugin) then
--free
self:SetInUse (instance.current_raid_plugin, nil)
--hide
local current_plugin_object = _detalhes:GetPlugin (instance.current_raid_plugin)
if (current_plugin_object) then
current_plugin_object.Frame:Hide()
end
instance.current_raid_plugin = nil
end
return
end
--check if is realy available
if (not self:IsAvailable (plugin_name, instance)) then
instance.last_raid_plugin = plugin_name
if (not instance.wait_for_plugin) then
instance:CreateWaitForPlugin()
end
return instance:WaitForPlugin()
end
--hide current shown plugin
if (instance.current_raid_plugin) then
--free
self:SetInUse (instance.current_raid_plugin, nil)
--hide
local current_plugin_object = _detalhes:GetPlugin (instance.current_raid_plugin)
if (current_plugin_object) then
current_plugin_object.Frame:Hide()
end
end
local plugin_object = _detalhes:GetPlugin (plugin_name)
if (plugin_object and plugin_object.__enabled and plugin_object.Frame) then
instance.last_raid_plugin = plugin_name
instance.current_raid_plugin = plugin_name
self:SetInUse (plugin_name, instance:GetId())
plugin_object.instance_id = instance:GetId()
plugin_object.Frame:SetPoint("TOPLEFT", instance.bgframe)
plugin_object.Frame:Show()
instance:ChangeIcon (plugin_object.__icon)--; print(instance:GetId(),"icon",plugin_object.__icon)
_detalhes:SendEvent("DETAILS_INSTANCE_CHANGEATTRIBUTE", nil, instance, instance.atributo, instance.sub_atributo)
if (update_menu) then
GameCooltip:ExecFunc (instance.baseframe.cabecalho.atributo)
--instance _detalhes.popup:ExecFunc (DeleteButton)
end
--force hide wait for plugins
if (_G["DetailsWaitFrameBG"..instance.meu_id] and _G["DetailsWaitForPluginFrame" .. instance.meu_id]) then
_G["DetailsWaitForPluginFrame" .. instance.meu_id]:Hide()
_G["DetailsWaitFrameBG"..instance.meu_id]:Hide()
end
else
if (not instance.wait_for_plugin) then
instance:CreateWaitForPlugin()
end
return instance:WaitForPlugin()
end
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--built in announcers
function _detalhes:SendMsgToChannel (msg, channel, towho)
if (channel == "RAID" or channel == "PARTY") then
if (GetNumGroupMembers (LE_PARTY_CATEGORY_INSTANCE) > 0) then
channel = "INSTANCE_CHAT"
end
SendChatMessage (msg, channel)
elseif (channel == "BNET") then
if (type(towho) == "number") then
BNSendWhisper (towho, msg)
elseif (type(towho) == "string") then
--local BnetFriends = BNGetNumFriends()
--for i = 1, BnetFriends do
-- local presenceID, presenceName, battleTag, isBattleTagPresence, toonName, toonID, client, isOnline, lastOnline, isAFK, isDND, messageText, noteText, isRIDFriend, broadcastTime, canSoR = BNGetFriendInfo (i)
-- if ((presenceName == towho or toonName == towho) and isOnline) then
-- BNSendWhisper (presenceID, msg)
-- break
-- end
--end
end
elseif (channel == "CHANNEL") then
SendChatMessage (msg, channel, nil, GetChannelName (towho))
elseif (channel == "WHISPER") then
SendChatMessage (msg, channel, nil, towho)
elseif (channel == "PRINT") then
print(msg)
else --say channel?
if (IsInInstance()) then --patch 80205 cannot use 'say' channel outside instances
SendChatMessage (msg, channel)
end
--elseif (channel == "SAY" or channel == "YELL" or channel == "RAID_WARNING" or channel == "OFFICER" or channel == "GUILD" or channel == "EMOTE") then
end
end
--/run local s="teste {spell}"; s=s:gsub("{spell}", "tercio");print(s)
function _detalhes:interrupt_announcer (token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, spellid, spellname, spelltype, extraSpellID, extraSpellName, extraSchool)
-- add novo canal Self.
-- no canal self ele mostra todos os interrupts alm do meu.
-- add canal self pras mortes tbm?
local channel = _detalhes.announce_interrupts.channel
if (channel ~= "PRINT" and who_name == _detalhes.playername) then
local next = _detalhes.announce_interrupts.next
local custom = _detalhes.announce_interrupts.custom
local spellname
if (spellid > 10) then
spellname = GetSpellLink(extraSpellID)
else
spellname = _GetSpellInfo(extraSpellID)
end
if (channel == "RAID") then
local zone = _detalhes:GetZoneType()
if (zone ~= "party" and zone ~= "raid") then
return
end
if (zone == "raid") then
channel = "RAID"
elseif (zone == "party") then
channel = "PARTY"
end
if (GetNumGroupMembers (LE_PARTY_CATEGORY_INSTANCE) > 0) then
channel = "INSTANCE_CHAT"
end
end
if (custom ~= "") then
custom = custom:gsub("{spell}", spellname)
custom = custom:gsub("{target}", alvo_name or "")
custom = custom:gsub("{next}", next)
_detalhes:SendMsgToChannel (custom, channel, _detalhes.announce_interrupts.whisper)
else
local msg = _cstr (Loc ["STRING_OPTIONS_RT_INTERRUPT"], spellname)
if (next ~= "") then
msg = msg .. " " .. _cstr (Loc ["STRING_OPTIONS_RT_INTERRUPT_NEXT"], next)
end
_detalhes:SendMsgToChannel (msg, channel, _detalhes.announce_interrupts.whisper)
end
elseif (channel == "PRINT") then
local custom = _detalhes.announce_interrupts.custom
local spellname
if (spellid > 10) then
spellname = GetSpellLink(extraSpellID)
else
spellname = _GetSpellInfo(extraSpellID)
end
if (custom ~= "") then
custom = custom:gsub("{spell}", spellname)
custom = custom:gsub("{next}", who_name)
custom = custom:gsub("{target}", alvo_name or "")
_detalhes:SendMsgToChannel (custom, "PRINT")
else
local minute, second = _detalhes:GetCombat():GetMSTime()
local class = Details:GetUnitClass(who_name)
local class_color = "|cFFFF3333"
if (class) then
local coords = Details.class_coords[class]
class_color = "|TInterface\\AddOns\\Details\\images\\classes_small_alpha:12:12:0:0:128:128:" .. coords[1]/2*128 .. ":" .. coords[2]/2*128 .. ":" .. coords[3]/2*128 .. ":" .. coords[4]/2*128 .. "|t |c" .. RAID_CLASS_COLORS [class].colorStr
end
if (second < 10) then
second = "0" .. second
end
local msg = "|cFFFFFF00[|r".. minute .. ":" .. second .. "|cFFFFFF00]|r Interrupt: " .. spellname .. " (" .. class_color .. _detalhes:GetOnlyName(who_name) .. "|r)"
_detalhes:SendMsgToChannel (msg, "PRINT")
end
end
end
local ignored_self_cooldowns = {
[119582] = true, -- Purifying Brew
[115308] = true, --Elusive Brew
[115203 ] = true, --Fortifying Brew
}
function _detalhes:cooldown_announcer (token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, spellid, spellname)
local channel = _detalhes.announce_cooldowns.channel
if (channel ~= "PRINT" and who_name == _detalhes.playername) then
local ignored = _detalhes.announce_cooldowns.ignored_cooldowns
if (ignored [spellid]) then
return
end
if (channel == "WHISPER") then
if (alvo_name == Loc ["STRING_RAID_WIDE"]) then
channel = "RAID"
end
end
if (channel == "RAID") then
local zone = _detalhes:GetZoneType()
if (zone ~= "party" and zone ~= "raid") then
return
end
if (zone == "raid") then
channel = "RAID"
elseif (zone == "party") then
channel = "PARTY"
end
if (GetNumGroupMembers (LE_PARTY_CATEGORY_INSTANCE) > 0) then
channel = "INSTANCE_CHAT"
end
end
local spellname
if (spellid > 10) then
spellname = GetSpellLink(spellid)
else
spellname = _GetSpellInfo(spellid)
end
local custom = _detalhes.announce_cooldowns.custom
if (custom ~= "") then
custom = custom:gsub("{spell}", spellname)
custom = custom:gsub("{target}", alvo_name or "")
_detalhes:SendMsgToChannel (custom, channel, _detalhes.announce_interrupts.whisper)
else
local msg
if (alvo_name == Loc ["STRING_RAID_WIDE"]) then
msg = _cstr (Loc ["STRING_OPTIONS_RT_COOLDOWN2"], spellname)
else
msg = _cstr (Loc ["STRING_OPTIONS_RT_COOLDOWN1"], spellname, alvo_name)
end
_detalhes:SendMsgToChannel (msg, channel, _detalhes.announce_interrupts.whisper)
end
elseif (channel == "PRINT") then
local ignored = _detalhes.announce_cooldowns.ignored_cooldowns
if (ignored [spellid]) then
return
end
if (ignored_self_cooldowns [spellid]) then
return
end
if (who_name == alvo_name and who_name ~= _detalhes.playername) then
return
end
local msg
local minute, second = _detalhes:GetCombat():GetMSTime()
local class = Details:GetUnitClass(who_name)
local class_color = "|cFFFFFFFF"
local class2 = Details:GetUnitClass(alvo_name)
local class_color2 = "|cFFFFFFFF"
if (class) then
local coords = Details.class_coords[class]
class_color = "|TInterface\\AddOns\\Details\\images\\classes_small_alpha:12:12:0:0:128:128:" .. coords[1]/2*128 .. ":" .. coords[2]/2*128 .. ":" .. coords[3]/2*128 .. ":" .. coords[4]/2*128 .. "|t |c" .. RAID_CLASS_COLORS [class].colorStr
end
if (class2) then
local coords = Details.class_coords[class2]
class_color2 = " -> |TInterface\\AddOns\\Details\\images\\classes_small_alpha:12:12:0:0:128:128:" .. coords[1]/2*128 .. ":" .. coords[2]/2*128 .. ":" .. coords[3]/2*128 .. ":" .. coords[4]/2*128 .. "|t |c" .. RAID_CLASS_COLORS [class2].colorStr
alvo_name = _detalhes:GetOnlyName(alvo_name)
else
alvo_name = ""
end
local spellname
if (spellid > 10) then
spellname = GetSpellLink(spellid)
else
spellname = _GetSpellInfo(spellid)
end
if (second < 10) then
second = "0" .. second
end
msg = "|cFF8F8FFF[|r".. minute .. ":" .. second .. "|cFF8F8FFF]|r Cooldown: " .. spellname .. " (" .. class_color .. _detalhes:GetOnlyName(who_name) .. "|r" .. class_color2 .. alvo_name .. "|r)"
_detalhes:SendMsgToChannel (msg, "PRINT")
end
end
function _detalhes:death_announcer (token, time, who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags, death_table, last_cooldown, death_at, max_health)
local where = _detalhes.announce_deaths.where
local zone = _detalhes:GetZoneType()
local channel = ""
if (where == 1) then
if (zone ~= "party" and zone ~= "raid") then
return
end
if (zone == "raid") then
channel = "RAID"
elseif (zone == "party") then
channel = "PARTY"
end
if (GetNumGroupMembers (LE_PARTY_CATEGORY_INSTANCE) > 0) then
channel = "INSTANCE_CHAT"
end
elseif (where == 2) then
if (zone ~= "raid") then
return
end
channel = "RAID"
if (GetNumGroupMembers (LE_PARTY_CATEGORY_INSTANCE) > 0) then
channel = "INSTANCE_CHAT"
end
elseif (where == 3) then
if (zone ~= "party") then
return
end
channel = "PARTY"
if (GetNumGroupMembers (LE_PARTY_CATEGORY_INSTANCE) > 0) then
channel = "INSTANCE_CHAT"
end
elseif (where == 4) then --observer
if (zone ~= "raid" and zone ~= "party") then
return
end
channel = "PRINT"
elseif (where == 5) then --officers
if (IsInGuild()) then
channel = "OFFICER"
end
end
local only_first = _detalhes.announce_deaths.only_first
--_detalhes:GetCombat("current"):GetDeaths() is the same thing, but, it's faster without using the API.
if (zone == "raid" and not _detalhes.tabela_vigente.is_boss) then
return
end
if (only_first > 0 and #_detalhes.tabela_vigente.last_events_tables > only_first) then
return
end
alvo_name = _detalhes:GetOnlyName(alvo_name)
local msg
if (where == 4) then --observer
local class = Details:GetUnitClass(alvo_name)
local class_color = "|cFFFFFFFF"
if (class) then
local coords = Details.class_coords [class]
class_color = "|TInterface\\AddOns\\Details\\images\\classes_small_alpha:12:12:0:0:256:256:" .. coords[1]/2*256 .. ":" .. coords[2]/2*256 .. ":" .. coords[3]/2*256 .. ":" .. coords[4]/2*256 .. "|t |c" .. RAID_CLASS_COLORS [class].colorStr
end
msg = "Death: " .. class_color .. alvo_name .. "|r ->"
else
msg = _cstr (Loc ["STRING_OPTIONS_RT_DEATH_MSG"], alvo_name) .. ":"
end
local spells = ""
death_table = death_table[1]
local last = #death_table
for i = 1, _detalhes.announce_deaths.last_hits do
for o = last, 1, -1 do
local this_death = death_table [o]
if (type(this_death[1]) == "boolean" and this_death[1] and this_death[4]+5 > time) then
local spelllink
if (this_death [2] > 10) then
spelllink = GetSpellLink(this_death [2])
else
spelllink = "[" .. _GetSpellInfo(this_death [2]) .. "]"
end
spells = spelllink .. ": " .. _detalhes:ToK2 (_math_floor(this_death [3])) .. " " .. spells
last = o-1
break
end
end
end
msg = msg .. " " .. spells
if (where == 4) then --observer
local minute, second = _detalhes:GetCombat():GetMSTime()
if (second < 10) then
second = "0" .. second
end
msg = "|cFFFF8800[|r".. minute .. ":" .. second .. "|cFFFF8800]|r " .. msg
end
_detalhes:SendMsgToChannel (msg, channel)
end
function _detalhes:StartAnnouncers()
if (_detalhes.announce_interrupts.enabled) then
_detalhes:InstallHook (DETAILS_HOOK_INTERRUPT, _detalhes.interrupt_announcer)
end
if (_detalhes.announce_cooldowns.enabled) then
_detalhes:InstallHook (DETAILS_HOOK_COOLDOWN, _detalhes.cooldown_announcer)
end
if (_detalhes.announce_deaths.enabled) then
_detalhes:InstallHook (DETAILS_HOOK_DEATH, _detalhes.death_announcer)
end
end
function _detalhes:EnableInterruptAnnouncer()
_detalhes.announce_interrupts.enabled = true
_detalhes:InstallHook (DETAILS_HOOK_INTERRUPT, _detalhes.interrupt_announcer)
end
function _detalhes:DisableInterruptAnnouncer()
_detalhes.announce_interrupts.enabled = false
_detalhes:UnInstallHook (DETAILS_HOOK_INTERRUPT, _detalhes.interrupt_announcer)
end
function _detalhes:EnableCooldownAnnouncer()
_detalhes.announce_cooldowns.enabled = true
_detalhes:InstallHook (DETAILS_HOOK_COOLDOWN, _detalhes.cooldown_announcer)
end
function _detalhes:DisableCooldownAnnouncer()
_detalhes.announce_cooldowns.enabled = false
_detalhes:UnInstallHook (DETAILS_HOOK_COOLDOWN, _detalhes.cooldown_announcer)
end
function _detalhes:EnableDeathAnnouncer()
_detalhes.announce_deaths.enabled = true
_detalhes:InstallHook (DETAILS_HOOK_DEATH, _detalhes.death_announcer)
end
function _detalhes:DisableDeathAnnouncer()
_detalhes.announce_deaths.enabled = false
_detalhes:UnInstallHook (DETAILS_HOOK_DEATH, _detalhes.death_announcer)
end
+354
View File
@@ -0,0 +1,354 @@
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
local _detalhes = _G.Details
local Loc = LibStub("AceLocale-3.0"):GetLocale ( "Details" )
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--local pointers
local pairs = pairs --lua locals
local _math_floor = math.floor --lua locals
local _UnitAura = UnitAura
local _
local gump = _detalhes.gump --details local
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--constants
local modo_alone = _detalhes._detalhes_props["MODO_ALONE"]
local modo_grupo = _detalhes._detalhes_props["MODO_GROUP"]
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--internal functions
--When a combat start
function _detalhes:UpdateSolo()
local SoloInstance = _detalhes.tabela_instancias[_detalhes.solo]
_detalhes.SoloTables.CombatIDLast = _detalhes.SoloTables.CombatID
_detalhes.SoloTables.CombatID = _detalhes:GetOrSetCombatId()
_detalhes.SoloTables.Attribute = SoloInstance.atributo
end
function _detalhes:CreateSoloCloseButton()
local plugin, frame = self, self.Frame
local button = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
button:SetScript("OnClick", function()
if (not button.close_confirmation) then
button.close_confirmation = gump:CreateSimplePanel(button, 296, 60, "", plugin.real_name .. "CloseConfirmation")
button.close_confirmation:SetPoint("center", frame, 0, 0)
_G [button.close_confirmation:GetName() .. "TitleBar"]:Hide()
local fade_background = button.close_confirmation:CreateTexture(nil, "background")
fade_background:SetPoint("topleft", frame, 0, 0)
fade_background:SetPoint("bottomright", frame, 0, 0)
fade_background:SetTexture(0, 0, 0, 0.7)
local close_func = function()
local instance = plugin:GetPluginInstance()
instance:ShutDown()
button.close_confirmation:Hide()
end
local group_func = function()
local instance = plugin:GetPluginInstance()
instance:SetMode(DETAILS_MODE_GROUP)
button.close_confirmation:Hide()
instance.baseframe.cabecalho.modo_selecao:GetScript("OnEnter")(instance.baseframe.cabecalho.modo_selecao)
end
local close_window = gump:NewButton(button.close_confirmation, nil, "$parentCloseWindowButton", "CloseWindowButton", 140, 20, close_func, nil, nil, nil, Loc ["STRING_MENU_CLOSE_INSTANCE"], 1, gump:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
local back_to_group_and_raid = gump:NewButton(button.close_confirmation, nil, "$parentBackToGroupButton", "BackToGroupButton", 140, 20, group_func, nil, nil, nil, Loc ["STRING_SWITCH_TO"] .. ": " .. Loc ["STRING_MODE_GROUP"], 2, gump:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
close_window:SetIcon ([[Interface\Buttons\UI-Panel-MinimizeButton-Up]], nil, nil, nil, {0.143125, 0.8653125, 0.1446875, 0.8653125}, nil, nil, 2)
back_to_group_and_raid:SetIcon ([[Interface\AddOns\Details\images\modo_icones]], nil, nil, nil, {32/256, 32/256*2, 0, 1}, nil, nil, 2)
close_window:SetPoint("topleft", 3, -4)
close_window:SetPoint("bottomright", -3, 31)
back_to_group_and_raid:SetPoint("topleft", 3, -31)
back_to_group_and_raid:SetPoint("bottomright", -3, 4)
end
button.close_confirmation:Show()
end)
button:SetWidth(20)
button:SetHeight(20)
--button:GetNormalTexture():SetDesaturated(true)
return button
end
--enable and disable Solo Mode for an Instance
function _detalhes:SoloMode (show)
if (show) then
--salvar a janela normal
if (self.mostrando ~= "solo") then --caso o addon tenha ligado ja no painel solo, no precisa rodar isso aqui
self:SaveMainWindowPosition()
if (self.rolagem) then
self:EsconderScrollBar() --hida a scrollbar
end
self.need_rolagem = false
self.baseframe:EnableMouseWheel(false)
Details.FadeHandler.Fader(self, 1, nil, "barras") --escondendo a janela da instncia [instncia [force hide [velocidade [hidar o que]]]]
self.mostrando = "solo"
end
_detalhes.SoloTables.instancia = self
--default plugin
if (not _detalhes.SoloTables.built) then
gump:PrepareSoloMode (self)
end
self.modo = _detalhes._detalhes_props["MODO_ALONE"]
_detalhes.solo = self.meu_id
--self:AtualizaSliderSolo (0)
if (not self.posicao.solo.w) then --primeira vez que o solo mode executado nessa instncia
self.baseframe:SetWidth(300)
self.baseframe:SetHeight(300)
self:SaveMainWindowPosition()
else
self:RestoreMainWindowPosition()
local w, h = self:GetSize()
if (w ~= 300 or h ~= 300) then
self.baseframe:SetWidth(300)
self.baseframe:SetHeight(300)
self:SaveMainWindowPosition()
end
end
local first_enabled_plugin, first_enabled_plugin_index
for index, plugin in ipairs(_detalhes.SoloTables.Plugins) do
if (plugin.__enabled) then
first_enabled_plugin = plugin
first_enabled_plugin_index = index
end
end
if (not first_enabled_plugin) then
_detalhes:WaitForSoloPlugin (self)
else
if (not _detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode]) then
_detalhes.SoloTables.Mode = first_enabled_plugin_index
end
_detalhes.SoloTables:switch(nil, _detalhes.SoloTables.Mode)
end
else
--print("--------------------------------")
--print(debugstack())
if (_detalhes.PluginCount.SOLO > 0) then
local solo_frame = _detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode].Frame
if (solo_frame) then
_detalhes.SoloTables:switch()
end
end
_detalhes.solo = nil --destranca a janela solo para ser usada em outras instncias
self.mostrando = "normal"
self:RestoreMainWindowPosition()
if (_G.DetailsWaitForPluginFrame:IsShown()) then
_detalhes:CancelWaitForPlugin()
end
Details.FadeHandler.Fader(self, 1, nil, "barras")
Details.FadeHandler.Fader(self.scroll, 0)
if (self.need_rolagem) then
self:MostrarScrollBar (true)
else
--precisa verificar se ele precisa a rolagem certo?
self:ReajustaGump()
end
--calcula se existem barras, etc...
if (not self.rows_fit_in_window) then --as barras no forma iniciadas ainda
self.rows_fit_in_window = _math_floor(self.baseframe.BoxBarrasAltura / self.row_height)
if (self.rows_created < self.rows_fit_in_window) then
for i = #self.barras+1, self.rows_fit_in_window do
local nova_barra = gump:CriaNovaBarra (self, i, 30) --cria nova barra
nova_barra.lineText1:SetText(Loc ["STRING_NEWROW"])
nova_barra.statusbar:SetValue(100)
self.barras [i] = nova_barra
end
self.rows_created = #self.barras
end
end
end
end
function _detalhes.SoloTables:EnableSoloMode (instance, plugin_name, from_cooltip)
--check if came from cooltip
if (from_cooltip) then
self = _detalhes.SoloTables
instance = plugin_name
plugin_name = from_cooltip
end
instance:SoloMode (true)
_detalhes.SoloTables:switch(nil, plugin_name)
end
--Build Solo Mode Tables and Functions
function gump:PrepareSoloMode (instancia)
_detalhes.SoloTables.built = true
_detalhes.SoloTables.SpellCastTable = {} --not used
_detalhes.SoloTables.TimeTable = {} --not used
_detalhes.SoloTables.Mode = _detalhes.SoloTables.Mode or 1 --solo mode
function _detalhes.SoloTables:GetActiveIndex()
return _detalhes.SoloTables.Mode
end
function _detalhes.SoloTables:switch(_, _switchTo)
--just hide all
if (not _switchTo) then
if (#_detalhes.SoloTables.Plugins > 0) then --have at least one plugin
_detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode].Frame:Hide()
end
return
end
--if passed the absolute plugin name
if (type(_switchTo) == "string") then
for index, ptable in ipairs(_detalhes.SoloTables.Menu) do
if (ptable [3].__enabled and ptable [4] == _switchTo) then
_switchTo = index
break
end
end
elseif (_switchTo == -1) then
_switchTo = _detalhes.SoloTables.Mode + 1
if (_switchTo > #_detalhes.SoloTables.Plugins) then
_switchTo = 1
end
end
local ThisFrame = _detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode]
if (not ThisFrame or not ThisFrame.__enabled) then
--frame not found, try in few second again
_detalhes.SoloTables.Mode = _switchTo
_detalhes:WaitForSoloPlugin (instancia)
return
end
--hide current frame
_detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode].Frame:Hide()
--switch mode
_detalhes.SoloTables.Mode = _switchTo
--show and setpoint new frame
_detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode].Frame:Show()
_detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode].Frame:SetPoint("TOPLEFT",_detalhes.SoloTables.instancia.bgframe)
_detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode].Frame:SetFrameLevel(20)
_detalhes.SoloTables.instancia:ChangeIcon (_detalhes.SoloTables.Menu [_detalhes.SoloTables.Mode] [2])
_detalhes.SoloTables.Plugins [_detalhes.SoloTables.Mode].instance_id = _detalhes.SoloTables.instancia:GetId()
_detalhes:SendEvent("DETAILS_INSTANCE_CHANGEATTRIBUTE", nil, _detalhes.SoloTables.instancia, _detalhes.SoloTables.instancia.atributo, _detalhes.SoloTables.instancia.sub_atributo)
end
return true
end
function _detalhes:CloseSoloDebuffs()
local SoloDebuffUptime = _detalhes.tabela_vigente.SoloDebuffUptime
if (not SoloDebuffUptime) then
return
end
for SpellId, DebuffTable in pairs(SoloDebuffUptime) do
if (DebuffTable.start) then
DebuffTable.duration = DebuffTable.duration + (_detalhes._tempo - DebuffTable.start) --time do parser ser igual ao time()?
DebuffTable.start = nil
end
DebuffTable.Active = false
end
end
--Buffs ter em todos os Solo Modes
function _detalhes.SoloTables:CatchBuffs()
--reset bufftables
_detalhes.SoloTables.SoloBuffUptime = _detalhes.SoloTables.SoloBuffUptime or {}
for spellname, BuffTable in pairs(_detalhes.SoloTables.SoloBuffUptime) do
--local BuffEntryTable = _detalhes.SoloTables.BuffTextEntry [BuffTable.tableIndex]
if (BuffTable.Active) then
BuffTable.start = _detalhes._tempo
BuffTable.castedAmt = 1
BuffTable.appliedAt = {}
--BuffEntryTable.backgroundFrame:Active()
else
BuffTable.start = nil
BuffTable.castedAmt = 0
BuffTable.appliedAt = {}
--BuffEntryTable.backgroundFrame:Desactive()
end
BuffTable.duration = 0
BuffTable.refreshAmt = 0
BuffTable.droppedAmt = 0
end
--catch buffs untracked yet
for buffIndex = 1, 41 do
local name = _UnitAura ("player", buffIndex)
if (name) then
for index, BuffName in pairs(_detalhes.SoloTables.BuffsTableNameCache) do
if (BuffName == name) then
local BuffObject = _detalhes.SoloTables.SoloBuffUptime [name]
if (not BuffObject) then
_detalhes.SoloTables.SoloBuffUptime [name] = {name = name, duration = 0, start = nil, castedAmt = 1, refreshAmt = 0, droppedAmt = 0, Active = true, tableIndex = index, appliedAt = {}}
end
end
end
end
end
end
function _detalhes:InstanciaCheckForDisabledSolo(instance)
if (not instance) then
instance = self
end
if (instance.modo == modo_alone) then
--print("arrumando a instancia "..instancia.meu_id)
if (instance.iniciada) then
instance:SetMode(modo_grupo)
instance:SoloMode(false)
_detalhes:ResetaGump(instance)
else
instance.modo = modo_grupo
instance.last_modo = modo_grupo
end
end
end
function _detalhes:AtualizaSoloMode_AfertReset (instancia)
if (_detalhes.SoloTables.CombatIDLast) then
_detalhes.SoloTables.CombatIDLast = nil
end
if (_detalhes.SoloTables.CombatID) then
_detalhes.SoloTables.CombatID = 0
end
end
File diff suppressed because it is too large Load Diff
+279
View File
@@ -0,0 +1,279 @@
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
local _detalhes = _G.Details
local Loc = LibStub("AceLocale-3.0"):GetLocale ( "Details" )
local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
local _
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--local pointers
-- none
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--details api functions
--create a button which will be displayed on tooltip
function _detalhes.ToolBar:NewPluginToolbarButton (func, icon, pluginname, tooltip, w, h, framename, menu_function)
--random name if nameless
if (not framename) then
framename = "DetailsToolbarButton" .. math.random(1, 100000)
end
--create button from template
local button = CreateFrame("button", framename, _detalhes.listener, "DetailsToolbarButton")
--sizes
if (w) then
button:SetWidth(w)
end
if (h) then
button:SetHeight(h)
end
button.x = 0
button.y = 0
--tooltip and function on click
button.tooltip = tooltip
button.menu = menu_function
button:SetScript("OnClick", func)
--textures
button:SetNormalTexture(icon)
button:SetPushedTexture(icon)
button:SetDisabledTexture(icon)
button:SetHighlightTexture(icon, "ADD")
button.__icon = icon
button.__name = pluginname
--blizzard built-in animation
--local FourCornerAnimeFrame = CreateFrame("frame", framename.."Blink", button) --, "IconIntroAnimTemplate" --stop using 'IconIntroAnimTemplate' as older versions of the game doesn't have it
--FourCornerAnimeFrame:SetPoint("center", button)
--FourCornerAnimeFrame:SetWidth(w or 14)
--FourCornerAnimeFrame:SetHeight(w or 14)
--FourCornerAnimeFrame.glow:SetScript("OnFinished", nil)
--button.blink = FourCornerAnimeFrame
_detalhes.ToolBar.AllButtons [#_detalhes.ToolBar.AllButtons+1] = button
return button
end
--show your plugin icon on tooltip
function _detalhes:ShowToolbarIcon (Button, Effect)
local LastIcon
--get the lower number instance
local lower_instance = _detalhes:GetLowerInstanceNumber()
if (not lower_instance) then
return
end
local instance = _detalhes:GetInstance(lower_instance)
if (#_detalhes.ToolBar.Shown > 0) then
--already shown
if (_detalhes:tableIN (_detalhes.ToolBar.Shown, Button)) then
return
end
LastIcon = _detalhes.ToolBar.Shown [#_detalhes.ToolBar.Shown]
else
LastIcon = instance.baseframe.cabecalho.report
end
_detalhes.ToolBar.Shown [#_detalhes.ToolBar.Shown+1] = Button
Button:SetPoint("left", LastIcon.widget or LastIcon, "right", Button.x, Button.y)
Button:Show()
if (instance.auto_hide_menu.left and not instance.is_interacting) then
instance:ToolbarMenuButtons()
Button:SetAlpha(0)
return
end
if (Effect) then
if (type(Effect) == "string") then
if (Effect == "blink") then
--Button.blink.glow:Play() --.blink and .glow doesn't exists anymore due to removal of the template 'IconIntroAnimTemplate'
elseif (Effect == "star") then
--Button.StarAnim:Play()
end
elseif (Effect) then
--Button.blink.glow:Play()
end
end
_detalhes.ToolBar:ReorganizeIcons (true)
return true
end
--hide your plugin icon from toolbar
function _detalhes:HideToolbarIcon (Button)
local index = _detalhes:tableIN (_detalhes.ToolBar.Shown, Button)
if (not index) then
--current not shown
return
end
Button:Hide()
table.remove (_detalhes.ToolBar.Shown, index)
--reorganize icons
_detalhes.ToolBar:ReorganizeIcons (true)
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--internal functions
do
local PluginDescPanel = CreateFrame("frame", "DetailsPluginDescPanel", UIParent)
PluginDescPanel:SetFrameStrata("tooltip")
PluginDescPanel:Hide()
PluginDescPanel:SetWidth(205)
PluginDescPanel.BackdropTable = {}
local background = PluginDescPanel:CreateTexture(nil, "artwork")
background:SetPoint("topleft", 0, 0)
background:SetPoint("bottomright", 0, 0)
PluginDescPanel.background = background
local icon, title, desc = PluginDescPanel:CreateTexture(nil, "overlay"), PluginDescPanel:CreateFontString(nil, "overlay", "GameFontNormal"), PluginDescPanel:CreateFontString(nil, "overlay", "GameFontNormal")
icon:SetPoint("topleft", 10, -10)
icon:SetSize(16, 16)
title:SetPoint("left", icon, "right", 2, 0)
desc:SetPoint("topleft", 13, -30)
desc:SetWidth(180)
desc:SetJustifyH("left")
_detalhes:SetFontColor(desc, "white")
PluginDescPanel.icon = icon
PluginDescPanel.title = title
PluginDescPanel.desc = desc
end
--[[global]] function DetailsToolbarButtonOnEnter (button)
local lower_instance = _detalhes:GetLowerInstanceNumber()
if (lower_instance) then
_detalhes.OnEnterMainWindow(_detalhes:GetInstance(lower_instance), button, 3)
end
if (button.tooltip) then
if (button.menu) then
_detalhes.gump:QuickDispatch(button.menu)
local next_check = 0.8
--check if the mouse is still interacting with the menu or with the button
button:SetScript("OnUpdate", function(self, elapsed)
next_check = next_check - elapsed
if (next_check < 0) then
if (not GameCooltipFrame1:IsMouseOver() and not button:IsMouseOver()) then
GameCooltip2:Hide()
button:SetScript("OnUpdate", nil)
return
end
next_check = 0.8
end
end)
--disable the hider menu if the cooltip is required in another place
hooksecurefunc (GameCooltip2, "ShowCooltip", function()
button:SetScript("OnUpdate", nil)
end)
return
end
GameCooltip:Hide()
local plugin_object = _detalhes:GetPlugin (button.__name)
local f = DetailsPluginDescPanel
f.icon:SetTexture(button.__icon)
f.title:SetText(button.__name)
f.desc:SetText(plugin_object:GetPluginDescription())
_detalhes:SetFontSize(f.desc, _detalhes.font_sizes.menus)
_detalhes:SetFontFace (f.desc, _detalhes.font_faces.menus)
--f.background:SetTexture(_detalhes.tooltip.menus_bg_texture)
f.background:SetTexCoord(unpack(_detalhes.tooltip.menus_bg_coords))
f.background:SetVertexColor(unpack(_detalhes.tooltip.menus_bg_color))
--f.background:SetDesaturated(true)
f.BackdropTable.bgFile = _detalhes.tooltip_backdrop.bgFile
f.BackdropTable.edgeFile = [[Interface\Buttons\WHITE8X8]] --_detalhes.tooltip_backdrop.edgeFile
f.BackdropTable.tile = _detalhes.tooltip_backdrop.tile
f.BackdropTable.edgeSize = 1 --_detalhes.tooltip_backdrop.edgeSize
f.BackdropTable.tileSize = _detalhes.tooltip_backdrop.tileSize
f:SetBackdrop(f.BackdropTable)
local r, g, b, a = _detalhes.gump:ParseColors(_detalhes.tooltip_border_color)
f:SetBackdropBorderColor(r, g, b, a)
f:SetHeight(40 + f.desc:GetStringHeight())
f:SetPoint("bottom", button, "top", 0, 10)
f:Show()
--SharedMedia:Fetch ("font", "Friz Quadrata TT")
end
end
--[[global]] function DetailsToolbarButtonOnLeave (button)
local lower_instance = _detalhes:GetLowerInstanceNumber()
if (lower_instance) then
_detalhes.OnLeaveMainWindow(_detalhes:GetInstance(lower_instance), button, 3)
end
if (button.tooltip) then
DetailsPluginDescPanel:Hide()
end
end
_detalhes:RegisterEvent(_detalhes.ToolBar, "DETAILS_INSTANCE_OPEN", "OnInstanceOpen")
_detalhes:RegisterEvent(_detalhes.ToolBar, "DETAILS_INSTANCE_CLOSE", "OnInstanceClose")
_detalhes.ToolBar.Enabled = true --must have this member or wont receive the event
_detalhes.ToolBar.__enabled = true
function _detalhes.ToolBar:OnInstanceOpen()
_detalhes.ToolBar:ReorganizeIcons (true)
end
function _detalhes.ToolBar:OnInstanceClose()
_detalhes.ToolBar:ReorganizeIcons (true)
end
function _detalhes.ToolBar:ReorganizeIcons (just_refresh)
--get the lower number instance
local lower_instance = _detalhes:GetLowerInstanceNumber()
if (not lower_instance) then
for _, ThisButton in ipairs(_detalhes.ToolBar.Shown) do
ThisButton:Hide()
end
return
end
local instance = _detalhes:GetInstance(lower_instance)
if (not just_refresh) then
for _, instancia in pairs(_detalhes.tabela_instancias) do
if (instancia.baseframe and instancia:IsAtiva()) then
instancia:ReajustaGump()
end
end
instance:ChangeSkin()
else
instance:ToolbarMenuButtons()
instance:SetAutoHideMenu(nil, nil, true)
end
return true
end
+249
View File
@@ -0,0 +1,249 @@
local addonName, Details222 = ...
local Details = _G.Details
local _
local ipairs = ipairs
local _time = _G.time
local timeMachine = Details.timeMachine
local _tempo = _time()
timeMachine.bIsEnabled = false
local calculateTimeFor_PvP = function(self)
for attributeType, thisDatabase in ipairs(self.playerDatabase) do
for actorObject in pairs(thisDatabase) do
if (not actorObject.last_event) then
print("actor without last event, is destroyed?", actorObject.__destroyed, actorObject.__destroyedBy)
end
if (actorObject.last_event + 3 > _tempo) then
if (actorObject.on_hold) then --the timer is on pause, turn it on
Details222.TimeMachine.SetOrGetPauseState(actorObject, false)
end
else
if (not actorObject.on_hold) then --not in pause, need to pause
--check if the player is casting something that takes more than 3 seconds
Details222.TimeMachine.SetOrGetPauseState(actorObject, true)
end
end
end
end
end
local calculateTimeFor_PvE = function(self)
for attributeType, thisDatabase in ipairs(self.playerDatabase) do
for actorObject in pairs(thisDatabase) do
if (not actorObject.last_event) then
print("actor without last event, is destroyed?", actorObject.__destroyed, actorObject.__destroyedBy)
end
if (actorObject.last_event + 10 > _tempo) then
if (actorObject.on_hold) then --the timer is on pause, turn it on
Details222.TimeMachine.SetOrGetPauseState(actorObject, false)
end
else
if (not actorObject.on_hold) then --not in pause, need to pause
--check if the player is casting something that takes more than 3 seconds
Details222.TimeMachine.SetOrGetPauseState(actorObject, true)
end
end
end
end
end
function Details222.TimeMachine.Ticker()
_tempo = _time()
Details._tempo = _tempo
Details:UpdateGears()
if (Details.is_in_battleground or Details.is_in_arena) then
return calculateTimeFor_PvP(timeMachine)
else
return calculateTimeFor_PvE(timeMachine)
end
end
function Details222.TimeMachine.Start()
timeMachine.updateTicker = Details.Schedules.NewTicker(1, Details222.TimeMachine.Ticker)
timeMachine.bIsEnabled = true
---@type table<actor, boolean>
local storeDamageActors = setmetatable({}, Details.weaktable)
---@type table<actor, boolean>
local storeHealingActors = setmetatable({}, Details.weaktable)
---@type {key1: table<actor, boolean>, key2: table<actor, boolean>}
timeMachine.playerDatabase = {
storeDamageActors, --store damage actors
storeHealingActors --store healing actors
}
---@type combat
local currentCombat = Details:GetCurrentCombat()
---@type actorcontainer
local damageContainer = currentCombat:GetContainer(DETAILS_ATTRIBUTE_DAMAGE)
for _, actorObject in damageContainer:ListActors() do
---@cast actorObject actor
if (actorObject.dps_started) then
Details222.TimeMachine.AddActor(actorObject)
end
end
end
---remove actors with __destroyed flag
function Details222.TimeMachine.Cleanup()
for attributeType, thisDatabase in ipairs(timeMachine.playerDatabase) do
for actorObject in pairs(thisDatabase) do
if (actorObject.__destroyed) then
thisDatabase[actorObject] = nil
end
end
end
end
function Details222.TimeMachine.Restart()
Details:Destroy(timeMachine.playerDatabase[1])
Details:Destroy(timeMachine.playerDatabase[2])
---@type table<actor, boolean>
local storeDamageActors = setmetatable({}, Details.weaktable)
---@type table<actor, boolean>
local storeHealingActors = setmetatable({}, Details.weaktable)
---@type {key1: table<actor, boolean>, key2: table<actor, boolean>}
timeMachine.playerDatabase = {
storeDamageActors, --store damage actors
storeHealingActors --store healing actors
}
end
---@param actorObject actor
function Details222.TimeMachine.RemoveActor(actorObject)
local thisDatabase = timeMachine.playerDatabase[actorObject.tipo]
--check if the database exists, the type could be wrong due to passing an resource or utility actor
if (thisDatabase) then
if (thisDatabase[actorObject]) then
thisDatabase[actorObject] = nil
end
end
end
function Details222.TimeMachine.AddActor(actorObject)
local thisDatabase = timeMachine.playerDatabase[actorObject.tipo]
if (thisDatabase) then
thisDatabase[actorObject] = true
end
end
function Details222.TimeMachine.StopTime(actorObject)
if (actorObject.end_time) then
return
end
if (actorObject.on_hold) then
Details222.TimeMachine.SetOrGetPauseState(actorObject, false)
end
actorObject.end_time = _tempo
end
---get the pause state or pause/unpause the timer of the player
---@param actorObject actor
---@param bIsPaused boolean|nil
function Details222.TimeMachine.SetOrGetPauseState(actorObject, bIsPaused)
if (bIsPaused == nil) then
return actorObject.on_hold --return if the timer is paused or not
elseif (bIsPaused) then --if true - pause the timer
if (not actorObject.last_event) then
print("actor without last event, is destroyed?", actorObject.__destroyed, actorObject.__destroyedBy)
end
actorObject.delay = math.floor(actorObject.last_event) --_tempo - 10
if (actorObject.delay < actorObject.start_time) then
actorObject.delay = actorObject.start_time
end
actorObject.on_hold = true
else --if false - unpause the timer
local diff = _tempo - actorObject.delay - 1
if (diff > 0) then
actorObject.start_time = actorObject.start_time + diff
end
actorObject.on_hold = false
end
end
---@param self actor
function Details:Tempo()
if (self.pvp) then
--pvp timer
if (self.end_time) then --the timer of the player is locked
local timer = self.end_time - self.start_time
if (timer < 3) then
timer = 3
end
return timer
elseif (self.on_hold) then --the timer is paused
local timer = self.delay - self.start_time
if (timer < 3) then
timer = 3
end
return timer
else
if (self.start_time == 0) then
return 3
end
local timer = _tempo - self.start_time
if (timer < 3) then
if (Details.in_combat) then
local combat_time = Details.tabela_vigente:GetCombatTime()
if (combat_time < 3) then
return combat_time
end
end
timer = 3
end
return timer
end
else
--pve timer
if (self.end_time) then --the timer of the player is locked
local timer = self.end_time - self.start_time
if (timer < 10) then
timer = 10
end
return timer
elseif (self.on_hold) then --the timer is paused
local timer = self.delay - self.start_time
if (timer < 10) then
timer = 10
end
return timer
else
if (self.start_time == 0) then
return 10
end
local timer = _tempo - self.start_time
if (timer < 10) then
if (Details.in_combat) then
---@type combat
local currentCombat = Details:GetCurrentCombat()
local combatTime = currentCombat:GetCombatTime()
if (combatTime < 10) then
return combatTime
end
end
timer = 10
end
return timer
end
end
end
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff