Nenue@6: --- Turok - Timer/Timer.lua Nenue@6: -- @file-author@ Nenue@6: -- @project-revision@ @project-hash@ Nenue@6: -- @file-revision@ @file-hash@ Nenue@6: --- Defines common elements for the various timer HUDs Nenue@6: local ADDON, _A = ... Nenue@6: local _G, CreateFrame, tconcat, GetInventoryItemsForSlot, GetInventoryItemID = _G, CreateFrame, table.concat, GetInventoryItemsForSlot, GetInventoryItemID Nenue@9: local T, F, tostring, type, max, tinsert, unpack, UIParent, loadstring, rawset = _A.Addon, _A.LibFog, tostring, type, max, table.insert, unpack, _G.UIParent, loadstring, rawset Nenue@6: local mod = T.modules.TimerControl Nenue@6: local P = mod.prototype Nenue@6: local db Nenue@6: Nenue@9: local pairs, ipairs, gsub, sub, setmetatable, wipe = pairs, ipairs, string.gsub, string.sub, setmetatable, wipe Nenue@6: local INVTYPE_FINGER, INVSLOT_FINGER1, INVSLOT_FINGER2, INVTYPE_TRINKET, INVSLOT_TRINKET1, INVSLOT_TRINKET2 = Nenue@6: INVTYPE_FINGER, INVSLOT_FINGER1, INVSLOT_FINGER2, INVTYPE_TRINKET, INVSLOT_TRINKET1, INVSLOT_TRINKET2 Nenue@6: --@debug@ Nenue@6: local DEBUG = true Nenue@6: --@end-debug@ Nenue@6: local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool Nenue@6: local print = function(...) Nenue@6: if not DEBUG then return end Nenue@6: if _G.Devian and _G.DevianDB.workspace ~= 1 then Nenue@6: _G.print('Timer', ...) Nenue@6: end Nenue@6: end Nenue@9: local teprint = function(...) Nenue@9: if not DEBUG then return end Nenue@9: if _G.Devian and _G.DevianDB.workspace ~= 1 then Nenue@9: _G.print('TimerEvent',...) Nenue@9: end Nenue@9: end Nenue@9: local tfprint = function(...) Nenue@9: if not DEBUG then return end Nenue@9: if _G.Devian and _G.DevianDB.workspace ~= 1 then Nenue@9: _G.print('TimerFocus',...) Nenue@9: end Nenue@9: end Nenue@6: Nenue@6: local Timer_GetPrintHandler = function(self) Nenue@6: if self.trace then Nenue@6: return function(...) Nenue@6: print(...) Nenue@9: tfprint(...) Nenue@6: end else Nenue@6: return print Nenue@6: end Nenue@6: end Nenue@6: local pb_suppressed = {} Nenue@6: Nenue@6: function mod:OnInitialize() Nenue@6: Nenue@6: --@debug@ Nenue@6: TurokData.spirit.timers = Turok.defaults.spirit.timers Nenue@6: --@end-debug@ Nenue@6: self.db = TurokData.spirit Nenue@6: db = self.db Nenue@6: self.active_cooldowns = {} Nenue@6: self.cast_units = {} Nenue@6: self.buff_units = {} Nenue@6: self.loaded_types = {} Nenue@6: self.loaded_triggers = {} Nenue@6: self.equipped = {} Nenue@6: self.containers = {} Nenue@6: self.timers = {} -- active timers Nenue@6: self.empty_frames = {} -- foster table for frames released by talent change Nenue@6: Nenue@6: Nenue@6: T:RegisterChatCommand("tsp", self.Import_Open) Nenue@6: T:RegisterChatCommand("tka", self.Dialog_Command) Nenue@6: T:RegisterChatCommand("tki", self.CreateIndex) Nenue@6: --T:Print("/tsp to import spells. /tka to open create dialog") Nenue@6: -- suppress cacophony from all cooldowns activating at login Nenue@6: self.quiet = true Nenue@6: --self:ScheduleTimer(function() self:Dialog_Command() end, 4) Nenue@6: Nenue@6: end Nenue@6: Nenue@6: local mt_single = { Nenue@6: __mode = "v", Nenue@6: __newindex = function(t,k,v) Nenue@6: rawset(t,k,v) Nenue@9: --_G.print('DB', 'TCMeta: adding leaf', k, '=', v) Nenue@6: end} Nenue@6: local mt_double = { Nenue@6: __index = function(t,k) Nenue@6: t[k] = setmetatable({}, mt_single) Nenue@9: --_G.print('DB', 'TCMeta: add layer', k, '=', t[k]) Nenue@6: return t[k] Nenue@6: end, Nenue@6: __newindex = function(t,k,v) Nenue@6: rawset(t,k,v) Nenue@9: --_G.print('DB', 'TCMeta: adding to top layer', k, '=', v) Nenue@6: end Nenue@6: } Nenue@6: local mt_error = { Nenue@6: __call =function (t, txt) Nenue@6: t.disable = true Nenue@6: tinsert(t, txt) Nenue@6: end Nenue@6: } Nenue@6: Nenue@6: --- Sets and cleans up index data used by event handlers Nenue@6: local Timer_UpdateIndex = function(self, key) Nenue@6: Nenue@6: -- Is there already an entry for this key/value? Nenue@6: if self.frames[key] then Nenue@6: local lim = #mod.frames[key] Nenue@6: --[[ Nenue@6: for i = self.frames[key]+1, lim, 1 do Nenue@6: mod.frames[key][i] = mod.frames[key+1] Nenue@6: end]] Nenue@6: --self.frames[key] = nil Nenue@6: print(' ', cText('mod.frames.')..cWord(key), '=', #mod.frames[key]) Nenue@6: print(' ', cText('self.frames.')..cWord(key), '=', cNum(self.frames[key])) Nenue@6: end Nenue@6: Nenue@6: if key then Nenue@6: local i = #mod.frames[key]+1 Nenue@6: --mod.frames[key][i] = self Nenue@6: self.frames[key] = i Nenue@6: print(' ', cText('self.frames.')..cWord(key), '=', #mod.frames[key]) Nenue@6: end Nenue@6: mod.loaded_types[key] = (#mod.frames[key] == 0) and nil or true Nenue@6: print(' ',cText(key..'_is_loaded'), '=', cBool(mod.loaded_types[key])) Nenue@6: end Nenue@6: Nenue@6: --- Loading initators Nenue@6: function mod:OnEnable() Nenue@6: mod.LoadPresets() Nenue@6: mod.GetIndex() Nenue@6: -- setup indexes, use nested weak table for status since they implicitly have a key variable Nenue@6: mod.frames = {} Nenue@6: for class, p in pairs(mod.prototype.status) do Nenue@6: print('nested index table', class) Nenue@6: mod.frames[class] = setmetatable({}, mt_double) Nenue@6: end Nenue@6: mod.frames.spellName = setmetatable({}, mt_double) Nenue@6: for class, p in pairs(mod.prototype.display) do Nenue@6: mod.frames[class] = setmetatable({}, mt_single) Nenue@6: end Nenue@6: for class, p in pairs(mod.prototype.trigger) do Nenue@6: mod.frames[class] = setmetatable({}, mt_single) Nenue@6: end Nenue@6: Nenue@6: local srcIndex = mod.timers Nenue@6: if T.playerClass and mod.index[T.playerClass] then Nenue@6: srcIndex = mod.index[T.playerClass] Nenue@6: print('*** Found index for '..tostring(T.playerClass)..', using that.') Nenue@6: else Nenue@6: print(cWord('*** Using global index.')) Nenue@6: end Nenue@6: mod.activeSpec = T.specID Nenue@6: Nenue@6: --- go through that list Nenue@6: for id, timer in pairs(srcIndex) do Nenue@6: local result, message = mod:EnableTimer(id, timer) Nenue@6: end Nenue@6: Nenue@6: mod.InitTimers() Nenue@6: --- Delay sound activations so there isn't a giant cacophony on load Nenue@6: mod:ScheduleTimer(function() Nenue@6: self.quiet = nil Nenue@6: end, db.audio_delay or 2) Nenue@6: end Nenue@6: Nenue@6: function mod:EnableTimer(id, dvars) Nenue@6: local print = Timer_GetPrintHandler(dvars) Nenue@6: print('-{', cPink(dvars.name)) Nenue@6: if not dvars then Nenue@6: if not mod.index.global[id] then Nenue@6: return false, "Unable to resolve dvars table." Nenue@6: end Nenue@6: dvars = mod.index.global[id] Nenue@6: end Nenue@6: if dvars.virtual then Nenue@6: return Nenue@6: end Nenue@6: Nenue@6: local spirit, newFrame = mod:GetTimer(id, dvars) Nenue@6: if not spirit then return spirit, newFrame end Nenue@6: Nenue@6: local cvars = spirit.cvars Nenue@6: local dvars = spirit.dvars Nenue@6: local trigger = P.trigger[cvars.type] Nenue@6: local display = P.display[cvars.display] Nenue@6: local cvars = spirit.cvars Nenue@6: local index = mod.frames Nenue@6: local print = Timer_GetPrintHandler(cvars) Nenue@6: Nenue@6: if spirit.disable then Nenue@6: return false, "Manually disabled." -- nothing to do, nothing to say Nenue@6: end Nenue@6: Nenue@6: --- Interpret STATUS vars Nenue@6: print(cText(' *** Merging Status Data')) Nenue@6: spirit.disable = dvars.disable Nenue@6: local pcount = 1 Nenue@6: for k, handler in pairs(P.status) do Nenue@6: if cvars[k] then Nenue@6: if handler.Init then Nenue@6: print(cWord(' * Firing ')..cKey(k)..cWord('.Init'), cNum(cvars[k])) Nenue@6: handler.Init(spirit, cvars[k]) Nenue@6: else Nenue@6: print(' ', cText('skipped'), cKey(k)) Nenue@6: end Nenue@6: pcount = pcount + 1 Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: spirit.Event = trigger.Event Nenue@6: spirit.Value = trigger.Value Nenue@6: spirit.SetText = mod.SetText Nenue@6: spirit.LoadText = mod.LoadText Nenue@6: spirit.Query = trigger.Query Nenue@6: spirit.Set = trigger.Set Nenue@6: Nenue@6: --- Display handler init Nenue@6: if display.Init then Nenue@6: print(cText(' * Display Init:'), cKey(dvars.display)) Nenue@6: display.Init(spirit) Nenue@6: end Nenue@6: Nenue@6: --- Trigger handler and events Load() Nenue@6: print(cText(' * Trigger Init:'), cKey(dvars.type)) Nenue@6: trigger.Init(spirit) Nenue@6: Nenue@6: Nenue@6: if C_PetBattles.IsInBattle() then Nenue@6: spirit.disable = true Nenue@6: spirit.debug_info("Hidden for pet battle") Nenue@6: pb_suppressed[id] = true Nenue@6: end Nenue@6: Nenue@6: Nenue@6: if spirit.disable then Nenue@6: spirit:UnregisterAllEvents() Nenue@6: spirit.displayState = nil Nenue@6: spirit.prevState = nil Nenue@6: spirit:Hide() Nenue@6: return false, tconcat(spirit.debug_info,"\n") Nenue@6: else Nenue@6: print('--', self.disable and cPink('DISABLED') or cNum('ENABLED'), #spirit.debug_info > 0 and tconcat(spirit.debug_info,"\n"), '}') Nenue@6: return true, tconcat(spirit.debug_info,"\n") Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: function mod:GetTimer(id, dvars) Nenue@6: local print = Timer_GetPrintHandler(dvars) Nenue@6: local newFrame Nenue@6: if not mod.timers[id] then Nenue@6: print(cKey(' [[CreateTimer')) Nenue@6: newFrame = true Nenue@6: --- Compile the cvar table from the various config layers: Nenue@6: -- Start with timer dvars, overwritten by any container settings, then a disable check, then merge in prototype values Nenue@6: local cvars = T.Config_Push({}, dvars, nil, cKey('['..id..']')..'.'..cWord('cvars')) Nenue@6: cvars.name = dvars.name -- push function ignores name keys Nenue@6: Nenue@6: if dvars.container and db.containers[dvars.container] then Nenue@6: print(cText(' * Merging Container overrides')) Nenue@6: T.Config_Push(cvars, db.containers[dvars.container], cvars, cKey('['..id..']')..'.'..cWord('cvars')) Nenue@6: end Nenue@6: Nenue@6: --- Stop here if disabled via SavedVars Nenue@6: if cvars.disable then Nenue@6: return false, "Manually disabled" Nenue@6: end Nenue@6: Nenue@6: --- Localize the stuff we are going to loop over Nenue@6: local display = P.display[cvars.display] Nenue@6: local trigger = P.trigger[cvars.type] Nenue@6: local displayType = cvars.display Nenue@6: local triggerType = cvars.type Nenue@6: if not (display and trigger) then Nenue@6: return nil, "Missing prototype data. Summary: "..tostring(displayType).."="..(display and 'OK' or 'MISSING') .. Nenue@6: " "..tostring(triggerType).."="..(trigger and 'OK' or 'MISSING') Nenue@6: end Nenue@6: Nenue@6: --- Establish the order in which values are merged Nenue@6: print(cText(' * Merging object CVars')) Nenue@6: local cvar_class = {cWord('db.'..displayType), cWord('db.'..triggerType), cWord('db.global')} Nenue@6: local cvar_array = { Nenue@6: db[displayType], Nenue@6: db[triggerType], Nenue@6: db.global, Nenue@6: } Nenue@6: local override_class = {cWord('trigger.'..cvars.type), cWord('display.'.. cvars.display)} Nenue@6: local override_array = { Nenue@6: display.cvars, Nenue@6: trigger.cvars } Nenue@6: Nenue@6: --- Table merge user settings Nenue@6: for i, p in pairs(cvar_array) do Nenue@6: print(' '..cNum(i)..' merge ['..cvar_class[i]..']') Nenue@6: T.Config_Merge(cvars, p, cvars, cKey('['..id..']')..'.'..cWord('cvars')) Nenue@6: end Nenue@6: Nenue@6: --- Overwrite with anything defined by the prototype structure because it's important Nenue@6: local _, odiff Nenue@6: for i, p in ipairs(override_array) do Nenue@6: _, odiff = T.Config_Push(cvars, p, cvars, cKey('['..id..']')..'.'..cWord('cvars')) Nenue@6: end Nenue@6: local print = Timer_GetPrintHandler(cvars) Nenue@6: Nenue@6: --- Create the UI frame and seed it with the data we just composed Nenue@6: local spirit = CreateFrame('Frame', 'TurokTimerFrame'..gsub(dvars.name, "[^%a%d]", ''), UIParent, display.inherits) Nenue@6: spirit.trace = cvars.trace Nenue@6: spirit.timerID = id Nenue@6: spirit.timerName = dvars.name Nenue@6: spirit.container = dvars.container Nenue@6: spirit.cvars = cvars Nenue@6: spirit.dvars = dvars Nenue@6: spirit.Update = display.Update Nenue@6: spirit.SetState = display.SetState Nenue@6: spirit.Report = mod.Report Nenue@6: spirit.Stats = trigger.Stats Nenue@6: Nenue@6: --- Set Layout Statics Nenue@6: T.SetFrameLayout(spirit, cvars) Nenue@6: Nenue@6: --- Create troubleshooting collection Nenue@6: spirit.debug_info = setmetatable({}, mt_error) Nenue@6: Nenue@6: --- Add the frame to corresponding prototype indexes Nenue@6: spirit.frames = {} Nenue@6: spirit.events = {} Nenue@6: Nenue@6: if spirit.display ~= displayType then Nenue@6: spirit.display = displayType Nenue@6: Timer_UpdateIndex(spirit, displayType) Nenue@6: end Nenue@6: if spirit.type ~= triggerType then Nenue@6: spirit.type = triggerType Nenue@6: Timer_UpdateIndex(spirit, triggerType) Nenue@6: end Nenue@6: --- Add the frame to global index Nenue@6: mod.timers[id] = spirit Nenue@6: end Nenue@6: Nenue@6: return mod.timers[id], newFrame Nenue@6: end Nenue@6: Nenue@6: function mod.InitTimers() Nenue@9: teprint('INIT TIMERS ====================') Nenue@6: for id, spirit in pairs(mod.timers) do Nenue@6: if spirit.disable then Nenue@9: teprint(id, 'disabled:', tconcat(spirit.debug_info or {}, ', ')) Nenue@6: else Nenue@6: Nenue@9: teprint(cText('init'), cNum(id), cWord(spirit.name)) Nenue@6: --- Throw a dry event to initialize values Nenue@9: teprint(cText(' *'), cWord('prototype.'..cKey(spirit.dvars.type)..'.'..cWord('Load'))) Nenue@6: P.trigger[spirit.dvars.type].Event(spirit) Nenue@6: Nenue@6: --- Set loose Nenue@9: teprint(cText(' *'), cWord('prototype')..'.'..cKey('events')..'.'..cWord('Load')) Nenue@6: mod.UpdateEvents(spirit, P.trigger[spirit.dvars.type].events) Nenue@6: end Nenue@6: end Nenue@9: teprint('INIT DONE =========================') Nenue@6: end Nenue@6: Nenue@6: function mod:DisableTimer(name, timer) Nenue@6: local timer_frame = mod.db.timers[name] Nenue@6: if timer_frame and not timer_frame.disable then Nenue@6: timer_frame.disable = true Nenue@6: timer_frame:UnregisterAllEvents() Nenue@6: timer_frame:Hide() Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: function mod.UpdateEvents(self, events) Nenue@6: local print = Timer_GetPrintHandler(self) Nenue@6: Nenue@6: self:SetScript('OnEvent', nil) Nenue@6: self:UnregisterAllEvents() Nenue@6: Nenue@6: local proxy, listen = {}, {} Nenue@6: for event, handler in pairs(events) do Nenue@6: if mod[event] then Nenue@6: tinsert(proxy, cNum(event)) Nenue@6: else Nenue@6: tinsert(listen, cWord(event)) Nenue@6: self:RegisterEvent(event) Nenue@6: end Nenue@6: self.events[event] = handler Nenue@6: end Nenue@6: Nenue@6: if #proxy > 0 then Nenue@6: print( ' -', cKey(self.name), cWord('receiving'), tconcat(proxy, ', ')) Nenue@6: end Nenue@6: if #listen > 0 then Nenue@6: print( ' -', cKey(self.name), cText('listening'), tconcat(listen, ', ')) Nenue@6: end Nenue@6: Nenue@6: self:SetScript('OnEvent', self.Event) Nenue@6: end Nenue@6: Nenue@6: local match_sub = { Nenue@6: {'%%c', "' .. tostring(t.caster).. '"}, Nenue@6: {'%%h', "' .. tostring((t.valueFull >= 60) and (math.floor(t.valueFull/60)) or t.value) .. '"}, Nenue@9: {'%%i', "' .. tostring((t.valueFull >= 60) and (math.floor(t.valueFull/60) .. ':' .. ((t.value %% 60) < 10 and '0' or '').. (t.value %% 60)) or ((t.valueFull < 6) and (t.value .. '.' .. math.floor((t.valueFull * 10) %% 10)) or t.value)) .. '"}, Nenue@6: {'%%n', "' .. tostring(t.spellName) .. '"}, Nenue@6: {'%%p', "' .. tostring(t.value) .. '"}, Nenue@6: {'%%d', "' .. tostring(t.chargeDuration or t.duration) .. '"}, Nenue@6: {'%%%.p', "' .. string.sub(tostring((t.valueFull %% 1) * 100),0,1) .. '"}, Nenue@6: {"%%s", "' .. (t.stacks or t.charges or '') .. '"}, Nenue@6: } Nenue@6: Nenue@6: -- dot syntax implies use as embedded method Nenue@6: function mod.LoadText(self) Nenue@6: print(cKey('parsing textRegions for'), self.timerName, self.timerID) Nenue@6: self.textTypes = {} Nenue@6: self.textValues = {} Nenue@6: for name, region in pairs(self.textRegions) do Nenue@6: print(' ', cWord('textRegions')..'["'.. cType(self.timerName)..'"].'..cType(name)) Nenue@6: if self.cvars[name..'Text'] then Nenue@6: Nenue@6: -- todo: collect match counts and index the text fields by match types Nenue@6: local str = self.cvars[name..'Text'] Nenue@6: for i, args in ipairs(match_sub) do Nenue@6: if str:match(args[1]) then Nenue@6: if not self.textTypes[args[1]] then Nenue@6: self.textTypes[args[1]] = {} Nenue@6: end Nenue@6: tinsert(self.textTypes[args[1]], region) Nenue@6: str = str:gsub(args[1], args[2]) Nenue@6: end Nenue@6: end Nenue@6: str = "local t = _G.Turok.modules.TimerControl.timers["..self.timerID.."]\n" Nenue@6: .. "\n return '" .. str .. "'" Nenue@6: local func = assert(loadstring(str)) Nenue@6: self.textValues[name] = func Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: --mod.SetText(self) Nenue@6: end Nenue@6: Nenue@6: --- generic text setter Nenue@6: local HIDDEN, PASSIVE, ACTIVE = 0, 1, 2 Nenue@6: mod.SetText = function(self) Nenue@6: if self.displayState ~= ACTIVE then Nenue@6: for name, region in pairs(self.textRegions) do Nenue@6: region:SetText(nil) Nenue@6: end Nenue@6: return Nenue@6: end Nenue@6: Nenue@6: if not self.textValues then Nenue@6: self.textValues = {} Nenue@6: mod.LoadText(self, self.cvars) Nenue@6: end Nenue@6: Nenue@6: -- hide when above a certain number Nenue@6: Nenue@6: if self.spiral and self.spiral.subCounter then Nenue@6: if self.valueFull > 6 then Nenue@6: if self.textValues.subCounter then Nenue@6: --print('hiding milliseconds') Nenue@6: self.textRegions.subCounter:Hide() Nenue@6: self.textRegionsSub = self.textRegions.subCounter Nenue@6: self.textValuesSub = self.textValues.subCounter Nenue@6: self.textRegions.subCounter = nil Nenue@6: self.textValues.subCounter = nil Nenue@6: end Nenue@6: else Nenue@6: if not self.textValues.subCounter then Nenue@6: --print('showing milliseconds') Nenue@6: self.textValues.subCounter = self.textValuesSub Nenue@6: self.textRegions.subCounter = self.textRegionsSub Nenue@6: self.textRegions.subCounter:Show() Nenue@6: end Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: for name, region in pairs(self.textRegions) do Nenue@6: --print(name) Nenue@6: --print(name, self.timerName, self.textValues[name](self)) Nenue@6: region:SetText(self.textValues[name](self)) Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: Nenue@6: ------------------------------------------------------------------------- Nenue@6: --- Second-tier handlers to cut down on the number of Status:Event() polls Nenue@6: Nenue@6: --- UNIT_SPELLCAST_*** use args to filter out the number of full handler runs Nenue@6: function mod:UNIT_SPELLCAST_SUCCEEDED (e, unit, spellName, rank, castID, spellID) Nenue@6: if not mod.frames.unit[unit] then Nenue@6: return Nenue@6: end Nenue@6: Nenue@6: if #mod.frames.spellName[spellName] > 0 then Nenue@6: print('spellName-ID relation detected:', cWord(spellName), cNum(spellID)) Nenue@6: for i, frame in pairs(mod.frames.spellName[spellName]) do Nenue@6: if not frame.frames.spellID then Nenue@6: frame.frames.spellID = {} Nenue@6: end Nenue@6: if not frame.frames.spellID[spellID] then Nenue@6: Nenue@6: tinsert(mod.frames.spellID[spellID], frame) Nenue@6: frame.frames.spellID[spellID] = #mod.frames.spellID[spellID] Nenue@6: print(cText(' updating'), cKey(frame.timerName)) Nenue@6: end Nenue@6: end Nenue@6: mod.frames.spellName[spellName] = nil Nenue@6: end Nenue@6: Nenue@6: Nenue@6: Nenue@6: if mod.frames.spellID[spellID] then Nenue@6: for i, timer_frame in pairs(mod.frames.spellID[spellID]) do Nenue@6: print(cText('caught spell'), cWord(spellName), 'for', timer_frame:GetName()) Nenue@6: timer_frame:Event(e, unit, spellName, rank, castID, spellID) Nenue@6: end Nenue@6: end Nenue@6: end Nenue@6: mod.UNIT_SPELLCAST_CHANNEL_START = mod.UNIT_SPELLCAST_SUCCEEDED Nenue@6: Nenue@6: --- Fire a dry event to force status updates on units with changing GUID's Nenue@6: function mod:PLAYER_TARGET_CHANGED(e, unit) Nenue@6: print('doing a target swap thing') Nenue@6: for k, v in pairs( self.frames.unit.target) do Nenue@6: print(k, v) Nenue@6: v:Event(nil, 'target') Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: --- Same thing but for talent/spec-driven Nenue@9: local update_queue = {} Nenue@9: function mod.ResetTimers(heading) Nenue@9: print(cText('*** Flushing update queue for'), cWord(heading)) Nenue@9: for id, frame in pairs(update_queue) do Nenue@9: print(' ', cNum(id), cKey(frame.timerName)) Nenue@9: frame.disable = nil Nenue@9: wipe(frame.debug_info) Nenue@9: local res, msg = mod:EnableTimer(id, frame.dvars) Nenue@9: end Nenue@9: wipe(update_queue) Nenue@9: end Nenue@9: Nenue@6: function mod:PLAYER_TALENT_UPDATE(e, unit) Nenue@6: print('') Nenue@6: print('') Nenue@6: print(cText(e), T.specPage, T.specName) Nenue@6: Nenue@6: for _, k in ipairs({'talentID', 'talentRow', 'specPage'}) do Nenue@6: for value, frameSet in pairs(mod.frames.talentID) do Nenue@6: for id, frame in ipairs(frameSet) do Nenue@6: print(frame.timerID, frame.timerName) Nenue@6: update_queue[frame.timerID] = frame Nenue@6: end Nenue@6: end Nenue@6: end Nenue@9: mod.resetTimers('Talent') Nenue@6: end Nenue@6: Nenue@6: function mod:PLAYER_EQUIPMENT_CHANGED(e, slot, hasItem) Nenue@6: print(e, slot, hasItem) Nenue@9: if mod.frames.inventoryID and mod.frames.inventoryID[slot] then Nenue@9: print(' Inventory slot:', cNum(slot)) Nenue@9: for i, slotFrame in ipairs(mod.frames.inventoryID[slot]) do Nenue@9: print(' ', cNum(i), cText(slotFrame.timerName)) Nenue@9: update_queue[slotFrame.timerID] = slotFrame Nenue@9: if mod.frames.itemID then Nenue@9: local itemsForSlot = GetInventoryItemsForSlot(slot, {}, false) Nenue@9: for _, itemID in pairs(itemsForSlot) do Nenue@9: if mod.frames.itemID[itemID] then Nenue@9: print(' Frames for equippable item:', cNum(itemID)) Nenue@9: for j, itemFrame in ipairs(mod.frames.itemID[itemID]) do Nenue@9: print(' ', cNum(j), cText(itemFrame.timerName)) Nenue@9: update_queue[itemFrame.timerID] = itemFrame Nenue@9: end Nenue@9: end Nenue@9: end Nenue@6: end Nenue@6: end Nenue@6: end Nenue@9: mod.ResetTimers('Equipment') Nenue@6: end Nenue@6: function mod:PET_BATTLE_OPENING_START () Nenue@6: for i, v in pairs(mod.timers) do Nenue@6: if not v.disable then Nenue@6: print('suppressing', v:GetName()) Nenue@6: v.disable = true Nenue@6: v:Hide() Nenue@6: pb_suppressed[i] = true Nenue@6: end Nenue@6: end Nenue@6: end Nenue@6: function mod:PET_BATTLE_CLOSE() Nenue@6: for id, v in pairs(mod.timers) do Nenue@6: if pb_suppressed[id] then Nenue@6: print('restoring', v:GetName()) Nenue@6: mod:EnableTimer(id) Nenue@6: pb_suppressed[id] = nil Nenue@6: end Nenue@6: end Nenue@6: end