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:
@@ -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
@@ -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 recupera��o 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
|
||||
@@ -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
|
||||
Executable
+6787
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
local Details = _G.Details
|
||||
local _
|
||||
local helloWorld
|
||||
@@ -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
|
||||
@@ -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 al�m 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
|
||||
|
||||
|
||||
@@ -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, n�o 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 inst�ncia [inst�ncia [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 inst�ncia
|
||||
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 inst�ncias
|
||||
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 n�o 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
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user