| 
Nenue@6
 | 
     1 --- Turok - Timer/Timer.lua
 | 
| 
Nenue@6
 | 
     2 -- @file-author@
 | 
| 
Nenue@6
 | 
     3 -- @project-revision@ @project-hash@
 | 
| 
Nenue@6
 | 
     4 -- @file-revision@ @file-hash@
 | 
| 
Nenue@6
 | 
     5 --- Defines common elements for the various timer HUDs
 | 
| 
Nenue@6
 | 
     6 local ADDON, _A = ...
 | 
| 
Nenue@6
 | 
     7 local _G, CreateFrame, tconcat, GetInventoryItemsForSlot, GetInventoryItemID = _G, CreateFrame, table.concat, GetInventoryItemsForSlot, GetInventoryItemID
 | 
| 
Nenue@9
 | 
     8 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
 | 
     9 local mod = T.modules.TimerControl
 | 
| 
Nenue@6
 | 
    10 local P = mod.prototype
 | 
| 
Nenue@6
 | 
    11 local db
 | 
| 
Nenue@6
 | 
    12 
 | 
| 
Nenue@9
 | 
    13 local pairs, ipairs, gsub, sub, setmetatable, wipe = pairs, ipairs, string.gsub, string.sub, setmetatable, wipe
 | 
| 
Nenue@6
 | 
    14 local INVTYPE_FINGER, INVSLOT_FINGER1, INVSLOT_FINGER2, INVTYPE_TRINKET, INVSLOT_TRINKET1, INVSLOT_TRINKET2 =
 | 
| 
Nenue@6
 | 
    15 INVTYPE_FINGER, INVSLOT_FINGER1, INVSLOT_FINGER2, INVTYPE_TRINKET, INVSLOT_TRINKET1, INVSLOT_TRINKET2
 | 
| 
Nenue@6
 | 
    16 --@debug@
 | 
| 
Nenue@6
 | 
    17 local DEBUG = true
 | 
| 
Nenue@6
 | 
    18 --@end-debug@
 | 
| 
Nenue@6
 | 
    19 local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
 | 
| 
Nenue@6
 | 
    20 local print = function(...)
 | 
| 
Nenue@6
 | 
    21   if not DEBUG then return end
 | 
| 
Nenue@6
 | 
    22   if _G.Devian and _G.DevianDB.workspace ~= 1 then
 | 
| 
Nenue@6
 | 
    23     _G.print('Timer', ...)
 | 
| 
Nenue@6
 | 
    24   end
 | 
| 
Nenue@6
 | 
    25 end
 | 
| 
Nenue@9
 | 
    26 local teprint = function(...)
 | 
| 
Nenue@9
 | 
    27   if not DEBUG then return end
 | 
| 
Nenue@9
 | 
    28   if _G.Devian and _G.DevianDB.workspace ~= 1 then
 | 
| 
Nenue@9
 | 
    29     _G.print('TimerEvent',...)
 | 
| 
Nenue@9
 | 
    30   end
 | 
| 
Nenue@9
 | 
    31 end
 | 
| 
Nenue@9
 | 
    32 local tfprint = function(...)
 | 
| 
Nenue@9
 | 
    33   if not DEBUG then return end
 | 
| 
Nenue@9
 | 
    34   if _G.Devian and _G.DevianDB.workspace ~= 1 then
 | 
| 
Nenue@9
 | 
    35     _G.print('TimerFocus',...)
 | 
| 
Nenue@9
 | 
    36   end
 | 
| 
Nenue@9
 | 
    37 end
 | 
| 
Nenue@6
 | 
    38 
 | 
| 
Nenue@6
 | 
    39 local Timer_GetPrintHandler = function(self)
 | 
| 
Nenue@6
 | 
    40   if self.trace then
 | 
| 
Nenue@6
 | 
    41   return function(...)
 | 
| 
Nenue@6
 | 
    42     print(...)
 | 
| 
Nenue@9
 | 
    43     tfprint(...)
 | 
| 
Nenue@6
 | 
    44   end else
 | 
| 
Nenue@6
 | 
    45     return print
 | 
| 
Nenue@6
 | 
    46   end
 | 
| 
Nenue@6
 | 
    47 end
 | 
| 
Nenue@6
 | 
    48 local pb_suppressed = {}
 | 
| 
Nenue@6
 | 
    49 
 | 
| 
Nenue@6
 | 
    50 function mod:OnInitialize()
 | 
| 
Nenue@6
 | 
    51 
 | 
| 
Nenue@6
 | 
    52   --@debug@
 | 
| 
Nenue@6
 | 
    53   TurokData.spirit.timers = Turok.defaults.spirit.timers
 | 
| 
Nenue@6
 | 
    54   --@end-debug@
 | 
| 
Nenue@6
 | 
    55   self.db = TurokData.spirit
 | 
| 
Nenue@6
 | 
    56   db = self.db
 | 
| 
Nenue@6
 | 
    57   self.active_cooldowns = {}
 | 
| 
Nenue@6
 | 
    58   self.cast_units = {}
 | 
| 
Nenue@6
 | 
    59   self.buff_units = {}
 | 
| 
Nenue@6
 | 
    60   self.loaded_types = {}
 | 
| 
Nenue@6
 | 
    61   self.loaded_triggers = {}
 | 
| 
Nenue@6
 | 
    62   self.equipped = {}
 | 
| 
Nenue@6
 | 
    63   self.containers = {}
 | 
| 
Nenue@6
 | 
    64   self.timers = {} -- active timers
 | 
| 
Nenue@6
 | 
    65   self.empty_frames = {} -- foster table for frames released by talent change
 | 
| 
Nenue@6
 | 
    66 
 | 
| 
Nenue@6
 | 
    67 
 | 
| 
Nenue@6
 | 
    68   T:RegisterChatCommand("tsp", self.Import_Open)
 | 
| 
Nenue@6
 | 
    69   T:RegisterChatCommand("tka", self.Dialog_Command)
 | 
| 
Nenue@6
 | 
    70   T:RegisterChatCommand("tki", self.CreateIndex)
 | 
| 
Nenue@6
 | 
    71   --T:Print("/tsp to import spells. /tka to open create dialog")
 | 
| 
Nenue@6
 | 
    72   -- suppress cacophony from all cooldowns activating at login
 | 
| 
Nenue@6
 | 
    73   self.quiet = true
 | 
| 
Nenue@6
 | 
    74   --self:ScheduleTimer(function() self:Dialog_Command() end, 4)
 | 
| 
Nenue@6
 | 
    75 
 | 
| 
Nenue@6
 | 
    76 end
 | 
| 
Nenue@6
 | 
    77 
 | 
| 
Nenue@6
 | 
    78 local mt_single = {
 | 
| 
Nenue@6
 | 
    79   __mode = "v",
 | 
| 
Nenue@6
 | 
    80   __newindex = function(t,k,v)
 | 
| 
Nenue@6
 | 
    81     rawset(t,k,v)
 | 
| 
Nenue@9
 | 
    82     --_G.print('DB', 'TCMeta: adding leaf', k, '=', v)
 | 
| 
Nenue@6
 | 
    83   end}
 | 
| 
Nenue@6
 | 
    84 local mt_double = {
 | 
| 
Nenue@6
 | 
    85   __index = function(t,k)
 | 
| 
Nenue@6
 | 
    86     t[k] = setmetatable({}, mt_single)
 | 
| 
Nenue@9
 | 
    87     --_G.print('DB', 'TCMeta: add layer', k, '=', t[k])
 | 
| 
Nenue@6
 | 
    88     return t[k]
 | 
| 
Nenue@6
 | 
    89   end,
 | 
| 
Nenue@6
 | 
    90   __newindex = function(t,k,v)
 | 
| 
Nenue@6
 | 
    91     rawset(t,k,v)
 | 
| 
Nenue@9
 | 
    92     --_G.print('DB', 'TCMeta: adding to top layer', k, '=', v)
 | 
| 
Nenue@6
 | 
    93   end
 | 
| 
Nenue@6
 | 
    94 }
 | 
| 
Nenue@6
 | 
    95 local mt_error = {
 | 
| 
Nenue@6
 | 
    96   __call =function (t, txt)
 | 
| 
Nenue@6
 | 
    97     t.disable = true
 | 
| 
Nenue@6
 | 
    98     tinsert(t, txt)
 | 
| 
Nenue@6
 | 
    99   end
 | 
| 
Nenue@6
 | 
   100 }
 | 
| 
Nenue@6
 | 
   101 
 | 
| 
Nenue@6
 | 
   102 --- Sets and cleans up index data used by event handlers
 | 
| 
Nenue@6
 | 
   103 local Timer_UpdateIndex = function(self, key)
 | 
| 
Nenue@6
 | 
   104 
 | 
| 
Nenue@6
 | 
   105   -- Is there already an entry for this key/value?
 | 
| 
Nenue@6
 | 
   106   if self.frames[key] then
 | 
| 
Nenue@6
 | 
   107     local lim = #mod.frames[key]
 | 
| 
Nenue@6
 | 
   108     --[[
 | 
| 
Nenue@6
 | 
   109     for i = self.frames[key]+1, lim, 1 do
 | 
| 
Nenue@6
 | 
   110       mod.frames[key][i] = mod.frames[key+1]
 | 
| 
Nenue@6
 | 
   111     end]]
 | 
| 
Nenue@6
 | 
   112     --self.frames[key] = nil
 | 
| 
Nenue@6
 | 
   113     print('     ', cText('mod.frames.')..cWord(key), '=', #mod.frames[key])
 | 
| 
Nenue@6
 | 
   114     print('     ', cText('self.frames.')..cWord(key), '=', cNum(self.frames[key]))
 | 
| 
Nenue@6
 | 
   115   end
 | 
| 
Nenue@6
 | 
   116 
 | 
| 
Nenue@6
 | 
   117   if key then
 | 
| 
Nenue@6
 | 
   118     local i = #mod.frames[key]+1
 | 
| 
Nenue@6
 | 
   119     --mod.frames[key][i] = self
 | 
| 
Nenue@6
 | 
   120     self.frames[key] = i
 | 
| 
Nenue@6
 | 
   121     print('     ', cText('self.frames.')..cWord(key), '=', #mod.frames[key])
 | 
| 
Nenue@6
 | 
   122   end
 | 
| 
Nenue@6
 | 
   123   mod.loaded_types[key] = (#mod.frames[key] == 0) and nil or true
 | 
| 
Nenue@6
 | 
   124   print('     ',cText(key..'_is_loaded'), '=', cBool(mod.loaded_types[key]))
 | 
| 
Nenue@6
 | 
   125 end
 | 
| 
Nenue@6
 | 
   126 
 | 
| 
Nenue@6
 | 
   127 --- Loading initators
 | 
| 
Nenue@6
 | 
   128 function mod:OnEnable()
 | 
| 
Nenue@6
 | 
   129   mod.LoadPresets()
 | 
| 
Nenue@6
 | 
   130   mod.GetIndex()
 | 
| 
Nenue@6
 | 
   131   -- setup indexes, use nested weak table for status since they implicitly have a key variable
 | 
| 
Nenue@6
 | 
   132   mod.frames = {}
 | 
| 
Nenue@6
 | 
   133   for class, p in pairs(mod.prototype.status) do
 | 
| 
Nenue@6
 | 
   134     print('nested index table', class)
 | 
| 
Nenue@6
 | 
   135     mod.frames[class] = setmetatable({}, mt_double)
 | 
| 
Nenue@6
 | 
   136   end
 | 
| 
Nenue@6
 | 
   137   mod.frames.spellName = setmetatable({}, mt_double)
 | 
| 
Nenue@6
 | 
   138   for class, p in pairs(mod.prototype.display) do
 | 
| 
Nenue@6
 | 
   139     mod.frames[class] = setmetatable({}, mt_single)
 | 
| 
Nenue@6
 | 
   140   end
 | 
| 
Nenue@6
 | 
   141   for class, p in pairs(mod.prototype.trigger) do
 | 
| 
Nenue@6
 | 
   142     mod.frames[class] = setmetatable({}, mt_single)
 | 
| 
Nenue@6
 | 
   143   end
 | 
| 
Nenue@6
 | 
   144 
 | 
| 
Nenue@6
 | 
   145   local srcIndex = mod.timers
 | 
| 
Nenue@6
 | 
   146   if T.playerClass and mod.index[T.playerClass] then
 | 
| 
Nenue@6
 | 
   147     srcIndex = mod.index[T.playerClass]
 | 
| 
Nenue@6
 | 
   148     print('*** Found index for '..tostring(T.playerClass)..', using that.')
 | 
| 
Nenue@6
 | 
   149   else
 | 
| 
Nenue@6
 | 
   150     print(cWord('*** Using global index.'))
 | 
| 
Nenue@6
 | 
   151   end
 | 
| 
Nenue@6
 | 
   152   mod.activeSpec = T.specID
 | 
| 
Nenue@6
 | 
   153 
 | 
| 
Nenue@6
 | 
   154   --- go through that list
 | 
| 
Nenue@6
 | 
   155   for id, timer in pairs(srcIndex) do
 | 
| 
Nenue@6
 | 
   156     local result, message = mod:EnableTimer(id, timer)
 | 
| 
Nenue@6
 | 
   157   end
 | 
| 
Nenue@6
 | 
   158 
 | 
| 
Nenue@6
 | 
   159   mod.InitTimers()
 | 
| 
Nenue@6
 | 
   160   --- Delay sound activations so there isn't a giant cacophony on load
 | 
| 
Nenue@6
 | 
   161   mod:ScheduleTimer(function()
 | 
| 
Nenue@6
 | 
   162     self.quiet = nil
 | 
| 
Nenue@6
 | 
   163   end, db.audio_delay or 2)
 | 
| 
Nenue@6
 | 
   164 end
 | 
| 
Nenue@6
 | 
   165 
 | 
| 
Nenue@6
 | 
   166 function mod:EnableTimer(id, dvars)
 | 
| 
Nenue@6
 | 
   167   local print = Timer_GetPrintHandler(dvars)
 | 
| 
Nenue@6
 | 
   168   print('-{', cPink(dvars.name))
 | 
| 
Nenue@6
 | 
   169   if not dvars then
 | 
| 
Nenue@6
 | 
   170     if not mod.index.global[id] then
 | 
| 
Nenue@6
 | 
   171       return false,  "Unable to resolve dvars table."
 | 
| 
Nenue@6
 | 
   172     end
 | 
| 
Nenue@6
 | 
   173     dvars = mod.index.global[id]
 | 
| 
Nenue@6
 | 
   174   end
 | 
| 
Nenue@6
 | 
   175   if dvars.virtual then
 | 
| 
Nenue@6
 | 
   176     return
 | 
| 
Nenue@6
 | 
   177   end
 | 
| 
Nenue@6
 | 
   178 
 | 
| 
Nenue@6
 | 
   179   local spirit, newFrame = mod:GetTimer(id, dvars)
 | 
| 
Nenue@6
 | 
   180   if not spirit then return spirit, newFrame end
 | 
| 
Nenue@6
 | 
   181 
 | 
| 
Nenue@6
 | 
   182   local cvars = spirit.cvars
 | 
| 
Nenue@6
 | 
   183   local dvars = spirit.dvars
 | 
| 
Nenue@6
 | 
   184   local trigger = P.trigger[cvars.type]
 | 
| 
Nenue@6
 | 
   185   local display = P.display[cvars.display]
 | 
| 
Nenue@6
 | 
   186   local cvars = spirit.cvars
 | 
| 
Nenue@6
 | 
   187   local index = mod.frames
 | 
| 
Nenue@6
 | 
   188   local print = Timer_GetPrintHandler(cvars)
 | 
| 
Nenue@6
 | 
   189 
 | 
| 
Nenue@6
 | 
   190   if spirit.disable then
 | 
| 
Nenue@6
 | 
   191     return false, "Manually disabled." -- nothing to do, nothing to say
 | 
| 
Nenue@6
 | 
   192   end
 | 
| 
Nenue@6
 | 
   193 
 | 
| 
Nenue@6
 | 
   194   --- Interpret STATUS vars
 | 
| 
Nenue@6
 | 
   195   print(cText('  *** Merging Status Data'))
 | 
| 
Nenue@6
 | 
   196   spirit.disable = dvars.disable
 | 
| 
Nenue@6
 | 
   197   local pcount = 1
 | 
| 
Nenue@6
 | 
   198   for k, handler in pairs(P.status) do
 | 
| 
Nenue@6
 | 
   199     if cvars[k] then
 | 
| 
Nenue@6
 | 
   200       if handler.Init then
 | 
| 
Nenue@6
 | 
   201         print(cWord('  * Firing ')..cKey(k)..cWord('.Init'), cNum(cvars[k]))
 | 
| 
Nenue@6
 | 
   202         handler.Init(spirit, cvars[k])
 | 
| 
Nenue@6
 | 
   203       else
 | 
| 
Nenue@6
 | 
   204         print('   ', cText('skipped'), cKey(k))
 | 
| 
Nenue@6
 | 
   205       end
 | 
| 
Nenue@6
 | 
   206       pcount = pcount + 1
 | 
| 
Nenue@6
 | 
   207     end
 | 
| 
Nenue@6
 | 
   208   end
 | 
| 
Nenue@6
 | 
   209 
 | 
| 
Nenue@6
 | 
   210   spirit.Event = trigger.Event
 | 
| 
Nenue@6
 | 
   211   spirit.Value = trigger.Value
 | 
| 
Nenue@6
 | 
   212   spirit.SetText = mod.SetText
 | 
| 
Nenue@6
 | 
   213   spirit.LoadText = mod.LoadText
 | 
| 
Nenue@6
 | 
   214   spirit.Query = trigger.Query
 | 
| 
Nenue@6
 | 
   215   spirit.Set = trigger.Set
 | 
| 
Nenue@6
 | 
   216 
 | 
| 
Nenue@6
 | 
   217   --- Display handler init
 | 
| 
Nenue@6
 | 
   218   if display.Init then
 | 
| 
Nenue@6
 | 
   219     print(cText('  * Display Init:'), cKey(dvars.display))
 | 
| 
Nenue@6
 | 
   220     display.Init(spirit)
 | 
| 
Nenue@6
 | 
   221   end
 | 
| 
Nenue@6
 | 
   222 
 | 
| 
Nenue@6
 | 
   223   --- Trigger handler and events Load()
 | 
| 
Nenue@6
 | 
   224   print(cText('  * Trigger Init:'), cKey(dvars.type))
 | 
| 
Nenue@6
 | 
   225   trigger.Init(spirit)
 | 
| 
Nenue@6
 | 
   226 
 | 
| 
Nenue@6
 | 
   227 
 | 
| 
Nenue@6
 | 
   228   if C_PetBattles.IsInBattle() then
 | 
| 
Nenue@6
 | 
   229     spirit.disable = true
 | 
| 
Nenue@6
 | 
   230     spirit.debug_info("Hidden for pet battle")
 | 
| 
Nenue@6
 | 
   231     pb_suppressed[id] = true
 | 
| 
Nenue@6
 | 
   232   end
 | 
| 
Nenue@6
 | 
   233 
 | 
| 
Nenue@6
 | 
   234 
 | 
| 
Nenue@6
 | 
   235   if spirit.disable then
 | 
| 
Nenue@6
 | 
   236     spirit:UnregisterAllEvents()
 | 
| 
Nenue@6
 | 
   237     spirit.displayState = nil
 | 
| 
Nenue@6
 | 
   238     spirit.prevState = nil
 | 
| 
Nenue@6
 | 
   239     spirit:Hide()
 | 
| 
Nenue@6
 | 
   240     return false, tconcat(spirit.debug_info,"\n")
 | 
| 
Nenue@6
 | 
   241   else
 | 
| 
Nenue@6
 | 
   242     print('--', self.disable and cPink('DISABLED') or cNum('ENABLED'), #spirit.debug_info > 0 and tconcat(spirit.debug_info,"\n"), '}')
 | 
| 
Nenue@6
 | 
   243     return true, tconcat(spirit.debug_info,"\n")
 | 
| 
Nenue@6
 | 
   244   end
 | 
| 
Nenue@6
 | 
   245 end
 | 
| 
Nenue@6
 | 
   246 
 | 
| 
Nenue@6
 | 
   247 function mod:GetTimer(id, dvars)
 | 
| 
Nenue@6
 | 
   248   local print = Timer_GetPrintHandler(dvars)
 | 
| 
Nenue@6
 | 
   249   local newFrame
 | 
| 
Nenue@6
 | 
   250   if not mod.timers[id] then
 | 
| 
Nenue@6
 | 
   251     print(cKey('  [[CreateTimer'))
 | 
| 
Nenue@6
 | 
   252     newFrame = true
 | 
| 
Nenue@6
 | 
   253     --- Compile the cvar table from the various config layers:
 | 
| 
Nenue@6
 | 
   254      -- Start with timer dvars, overwritten by any container settings, then a disable check, then merge in prototype values
 | 
| 
Nenue@6
 | 
   255     local cvars = T.Config_Push({}, dvars, nil, cKey('['..id..']')..'.'..cWord('cvars'))
 | 
| 
Nenue@6
 | 
   256     cvars.name = dvars.name -- push function ignores name keys
 | 
| 
Nenue@6
 | 
   257 
 | 
| 
Nenue@6
 | 
   258     if dvars.container and db.containers[dvars.container] then
 | 
| 
Nenue@6
 | 
   259       print(cText('    * Merging Container overrides'))
 | 
| 
Nenue@6
 | 
   260       T.Config_Push(cvars, db.containers[dvars.container], cvars, cKey('['..id..']')..'.'..cWord('cvars'))
 | 
| 
Nenue@6
 | 
   261     end
 | 
| 
Nenue@6
 | 
   262 
 | 
| 
Nenue@6
 | 
   263     --- Stop here if disabled via SavedVars
 | 
| 
Nenue@6
 | 
   264     if cvars.disable then
 | 
| 
Nenue@6
 | 
   265       return false, "Manually disabled"
 | 
| 
Nenue@6
 | 
   266     end
 | 
| 
Nenue@6
 | 
   267 
 | 
| 
Nenue@6
 | 
   268     --- Localize the stuff we are going to loop over
 | 
| 
Nenue@6
 | 
   269     local display = P.display[cvars.display]
 | 
| 
Nenue@6
 | 
   270     local trigger = P.trigger[cvars.type]
 | 
| 
Nenue@6
 | 
   271     local displayType = cvars.display
 | 
| 
Nenue@6
 | 
   272     local triggerType = cvars.type
 | 
| 
Nenue@6
 | 
   273     if not (display and trigger) then
 | 
| 
Nenue@6
 | 
   274       return nil, "Missing prototype data. Summary: "..tostring(displayType).."="..(display and 'OK' or 'MISSING') ..
 | 
| 
Nenue@6
 | 
   275           " "..tostring(triggerType).."="..(trigger and 'OK' or 'MISSING')
 | 
| 
Nenue@6
 | 
   276     end
 | 
| 
Nenue@6
 | 
   277 
 | 
| 
Nenue@6
 | 
   278     --- Establish the order in which values are merged
 | 
| 
Nenue@6
 | 
   279     print(cText('    * Merging object CVars'))
 | 
| 
Nenue@6
 | 
   280     local cvar_class = {cWord('db.'..displayType), cWord('db.'..triggerType), cWord('db.global')}
 | 
| 
Nenue@6
 | 
   281     local cvar_array = {
 | 
| 
Nenue@6
 | 
   282       db[displayType],
 | 
| 
Nenue@6
 | 
   283       db[triggerType],
 | 
| 
Nenue@6
 | 
   284       db.global,
 | 
| 
Nenue@6
 | 
   285     }
 | 
| 
Nenue@6
 | 
   286     local override_class = {cWord('trigger.'..cvars.type), cWord('display.'.. cvars.display)}
 | 
| 
Nenue@6
 | 
   287     local override_array = {
 | 
| 
Nenue@6
 | 
   288       display.cvars,
 | 
| 
Nenue@6
 | 
   289       trigger.cvars }
 | 
| 
Nenue@6
 | 
   290 
 | 
| 
Nenue@6
 | 
   291     --- Table merge user settings
 | 
| 
Nenue@6
 | 
   292     for i, p in pairs(cvar_array) do
 | 
| 
Nenue@6
 | 
   293       print('    '..cNum(i)..' merge ['..cvar_class[i]..']')
 | 
| 
Nenue@6
 | 
   294       T.Config_Merge(cvars, p, cvars, cKey('['..id..']')..'.'..cWord('cvars'))
 | 
| 
Nenue@6
 | 
   295     end
 | 
| 
Nenue@6
 | 
   296 
 | 
| 
Nenue@6
 | 
   297     --- Overwrite with anything defined by the prototype structure because it's important
 | 
| 
Nenue@6
 | 
   298     local _, odiff
 | 
| 
Nenue@6
 | 
   299     for i, p in ipairs(override_array) do
 | 
| 
Nenue@6
 | 
   300       _, odiff = T.Config_Push(cvars, p, cvars, cKey('['..id..']')..'.'..cWord('cvars'))
 | 
| 
Nenue@6
 | 
   301     end
 | 
| 
Nenue@6
 | 
   302     local print = Timer_GetPrintHandler(cvars)
 | 
| 
Nenue@6
 | 
   303 
 | 
| 
Nenue@6
 | 
   304     --- Create the UI frame and seed it with the data we just composed
 | 
| 
Nenue@6
 | 
   305     local spirit =  CreateFrame('Frame', 'TurokTimerFrame'..gsub(dvars.name, "[^%a%d]", ''), UIParent, display.inherits)
 | 
| 
Nenue@6
 | 
   306     spirit.trace = cvars.trace
 | 
| 
Nenue@6
 | 
   307     spirit.timerID = id
 | 
| 
Nenue@6
 | 
   308     spirit.timerName = dvars.name
 | 
| 
Nenue@6
 | 
   309     spirit.container = dvars.container
 | 
| 
Nenue@6
 | 
   310     spirit.cvars = cvars
 | 
| 
Nenue@6
 | 
   311     spirit.dvars = dvars
 | 
| 
Nenue@6
 | 
   312     spirit.Update = display.Update
 | 
| 
Nenue@6
 | 
   313     spirit.SetState = display.SetState
 | 
| 
Nenue@6
 | 
   314     spirit.Report = mod.Report
 | 
| 
Nenue@6
 | 
   315     spirit.Stats = trigger.Stats
 | 
| 
Nenue@6
 | 
   316 
 | 
| 
Nenue@6
 | 
   317     --- Set Layout Statics
 | 
| 
Nenue@6
 | 
   318     T.SetFrameLayout(spirit, cvars)
 | 
| 
Nenue@6
 | 
   319 
 | 
| 
Nenue@6
 | 
   320     --- Create troubleshooting collection
 | 
| 
Nenue@6
 | 
   321     spirit.debug_info = setmetatable({}, mt_error)
 | 
| 
Nenue@6
 | 
   322 
 | 
| 
Nenue@6
 | 
   323     --- Add the frame to corresponding prototype indexes
 | 
| 
Nenue@6
 | 
   324     spirit.frames = {}
 | 
| 
Nenue@6
 | 
   325     spirit.events = {}
 | 
| 
Nenue@6
 | 
   326 
 | 
| 
Nenue@6
 | 
   327     if spirit.display ~= displayType then
 | 
| 
Nenue@6
 | 
   328       spirit.display = displayType
 | 
| 
Nenue@6
 | 
   329       Timer_UpdateIndex(spirit, displayType)
 | 
| 
Nenue@6
 | 
   330     end
 | 
| 
Nenue@6
 | 
   331     if spirit.type ~= triggerType then
 | 
| 
Nenue@6
 | 
   332       spirit.type = triggerType
 | 
| 
Nenue@6
 | 
   333       Timer_UpdateIndex(spirit, triggerType)
 | 
| 
Nenue@6
 | 
   334     end
 | 
| 
Nenue@6
 | 
   335     --- Add the frame to global index
 | 
| 
Nenue@6
 | 
   336     mod.timers[id] = spirit
 | 
| 
Nenue@6
 | 
   337   end
 | 
| 
Nenue@6
 | 
   338 
 | 
| 
Nenue@6
 | 
   339   return mod.timers[id], newFrame
 | 
| 
Nenue@6
 | 
   340 end
 | 
| 
Nenue@6
 | 
   341 
 | 
| 
Nenue@6
 | 
   342 function mod.InitTimers()
 | 
| 
Nenue@9
 | 
   343   teprint('INIT TIMERS ====================')
 | 
| 
Nenue@6
 | 
   344   for id, spirit in pairs(mod.timers) do
 | 
| 
Nenue@6
 | 
   345     if spirit.disable then
 | 
| 
Nenue@9
 | 
   346       teprint(id, 'disabled:', tconcat(spirit.debug_info or {}, ', '))
 | 
| 
Nenue@6
 | 
   347     else
 | 
| 
Nenue@6
 | 
   348 
 | 
| 
Nenue@9
 | 
   349     teprint(cText('init'), cNum(id), cWord(spirit.name))
 | 
| 
Nenue@6
 | 
   350     --- Throw a dry event to initialize values
 | 
| 
Nenue@9
 | 
   351     teprint(cText(' *'), cWord('prototype.'..cKey(spirit.dvars.type)..'.'..cWord('Load')))
 | 
| 
Nenue@6
 | 
   352     P.trigger[spirit.dvars.type].Event(spirit)
 | 
| 
Nenue@6
 | 
   353 
 | 
| 
Nenue@6
 | 
   354     --- Set loose
 | 
| 
Nenue@9
 | 
   355     teprint(cText(' *'), cWord('prototype')..'.'..cKey('events')..'.'..cWord('Load'))
 | 
| 
Nenue@6
 | 
   356     mod.UpdateEvents(spirit, P.trigger[spirit.dvars.type].events)
 | 
| 
Nenue@6
 | 
   357     end
 | 
| 
Nenue@6
 | 
   358   end
 | 
| 
Nenue@9
 | 
   359   teprint('INIT DONE =========================')
 | 
| 
Nenue@6
 | 
   360 end
 | 
| 
Nenue@6
 | 
   361 
 | 
| 
Nenue@6
 | 
   362 function mod:DisableTimer(name, timer)
 | 
| 
Nenue@6
 | 
   363   local timer_frame = mod.db.timers[name]
 | 
| 
Nenue@6
 | 
   364   if timer_frame and not timer_frame.disable then
 | 
| 
Nenue@6
 | 
   365     timer_frame.disable = true
 | 
| 
Nenue@6
 | 
   366     timer_frame:UnregisterAllEvents()
 | 
| 
Nenue@6
 | 
   367     timer_frame:Hide()
 | 
| 
Nenue@6
 | 
   368   end
 | 
| 
Nenue@6
 | 
   369 end
 | 
| 
Nenue@6
 | 
   370 
 | 
| 
Nenue@6
 | 
   371 function mod.UpdateEvents(self, events)
 | 
| 
Nenue@6
 | 
   372   local print = Timer_GetPrintHandler(self)
 | 
| 
Nenue@6
 | 
   373 
 | 
| 
Nenue@6
 | 
   374   self:SetScript('OnEvent', nil)
 | 
| 
Nenue@6
 | 
   375   self:UnregisterAllEvents()
 | 
| 
Nenue@6
 | 
   376 
 | 
| 
Nenue@6
 | 
   377   local proxy, listen = {}, {}
 | 
| 
Nenue@6
 | 
   378   for event, handler in pairs(events) do
 | 
| 
Nenue@6
 | 
   379     if mod[event] then
 | 
| 
Nenue@6
 | 
   380       tinsert(proxy, cNum(event))
 | 
| 
Nenue@6
 | 
   381     else
 | 
| 
Nenue@6
 | 
   382       tinsert(listen, cWord(event))
 | 
| 
Nenue@6
 | 
   383       self:RegisterEvent(event)
 | 
| 
Nenue@6
 | 
   384     end
 | 
| 
Nenue@6
 | 
   385     self.events[event] = handler
 | 
| 
Nenue@6
 | 
   386   end
 | 
| 
Nenue@6
 | 
   387 
 | 
| 
Nenue@6
 | 
   388   if #proxy > 0 then
 | 
| 
Nenue@6
 | 
   389     print( '  -', cKey(self.name), cWord('receiving'), tconcat(proxy, ', '))
 | 
| 
Nenue@6
 | 
   390   end
 | 
| 
Nenue@6
 | 
   391   if #listen > 0 then
 | 
| 
Nenue@6
 | 
   392     print( '  -', cKey(self.name), cText('listening'), tconcat(listen, ', '))
 | 
| 
Nenue@6
 | 
   393   end
 | 
| 
Nenue@6
 | 
   394 
 | 
| 
Nenue@6
 | 
   395   self:SetScript('OnEvent', self.Event)
 | 
| 
Nenue@6
 | 
   396 end
 | 
| 
Nenue@6
 | 
   397 
 | 
| 
Nenue@6
 | 
   398 local match_sub = {
 | 
| 
Nenue@6
 | 
   399   {'%%c', "' .. tostring(t.caster).. '"},
 | 
| 
Nenue@6
 | 
   400   {'%%h', "' .. tostring((t.valueFull >= 60) and (math.floor(t.valueFull/60)) or t.value) .. '"},
 | 
| 
Nenue@9
 | 
   401   {'%%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
 | 
   402   {'%%n', "' .. tostring(t.spellName) .. '"},
 | 
| 
Nenue@6
 | 
   403   {'%%p', "' .. tostring(t.value) .. '"},
 | 
| 
Nenue@6
 | 
   404   {'%%d', "' .. tostring(t.chargeDuration or t.duration) .. '"},
 | 
| 
Nenue@6
 | 
   405   {'%%%.p', "' .. string.sub(tostring((t.valueFull %% 1) * 100),0,1) .. '"},
 | 
| 
Nenue@6
 | 
   406   {"%%s", "' .. (t.stacks or t.charges or '') .. '"},
 | 
| 
Nenue@6
 | 
   407 }
 | 
| 
Nenue@6
 | 
   408 
 | 
| 
Nenue@6
 | 
   409 -- dot syntax implies use as embedded method
 | 
| 
Nenue@6
 | 
   410 function mod.LoadText(self)
 | 
| 
Nenue@6
 | 
   411   print(cKey('parsing textRegions for'), self.timerName, self.timerID)
 | 
| 
Nenue@6
 | 
   412   self.textTypes = {}
 | 
| 
Nenue@6
 | 
   413   self.textValues = {}
 | 
| 
Nenue@6
 | 
   414   for name, region in pairs(self.textRegions) do
 | 
| 
Nenue@6
 | 
   415     print('  ', cWord('textRegions')..'["'.. cType(self.timerName)..'"].'..cType(name))
 | 
| 
Nenue@6
 | 
   416     if self.cvars[name..'Text'] then
 | 
| 
Nenue@6
 | 
   417 
 | 
| 
Nenue@6
 | 
   418       -- todo: collect match counts and index the text fields by match types
 | 
| 
Nenue@6
 | 
   419       local str = self.cvars[name..'Text']
 | 
| 
Nenue@6
 | 
   420       for i, args in ipairs(match_sub) do
 | 
| 
Nenue@6
 | 
   421         if str:match(args[1]) then
 | 
| 
Nenue@6
 | 
   422           if not self.textTypes[args[1]] then
 | 
| 
Nenue@6
 | 
   423             self.textTypes[args[1]] = {}
 | 
| 
Nenue@6
 | 
   424           end
 | 
| 
Nenue@6
 | 
   425           tinsert(self.textTypes[args[1]], region)
 | 
| 
Nenue@6
 | 
   426           str = str:gsub(args[1], args[2])
 | 
| 
Nenue@6
 | 
   427         end
 | 
| 
Nenue@6
 | 
   428       end
 | 
| 
Nenue@6
 | 
   429       str = "local t = _G.Turok.modules.TimerControl.timers["..self.timerID.."]\n"
 | 
| 
Nenue@6
 | 
   430           .. "\n return '" .. str .. "'"
 | 
| 
Nenue@6
 | 
   431       local func = assert(loadstring(str))
 | 
| 
Nenue@6
 | 
   432       self.textValues[name] = func
 | 
| 
Nenue@6
 | 
   433     end
 | 
| 
Nenue@6
 | 
   434   end
 | 
| 
Nenue@6
 | 
   435 
 | 
| 
Nenue@6
 | 
   436   --mod.SetText(self)
 | 
| 
Nenue@6
 | 
   437 end
 | 
| 
Nenue@6
 | 
   438 
 | 
| 
Nenue@6
 | 
   439 --- generic text setter
 | 
| 
Nenue@6
 | 
   440 local HIDDEN, PASSIVE, ACTIVE = 0, 1, 2
 | 
| 
Nenue@6
 | 
   441 mod.SetText = function(self)
 | 
| 
Nenue@6
 | 
   442   if self.displayState ~= ACTIVE then
 | 
| 
Nenue@6
 | 
   443     for name, region in pairs(self.textRegions) do
 | 
| 
Nenue@6
 | 
   444       region:SetText(nil)
 | 
| 
Nenue@6
 | 
   445     end
 | 
| 
Nenue@6
 | 
   446     return
 | 
| 
Nenue@6
 | 
   447   end
 | 
| 
Nenue@6
 | 
   448 
 | 
| 
Nenue@6
 | 
   449   if not self.textValues then
 | 
| 
Nenue@6
 | 
   450     self.textValues = {}
 | 
| 
Nenue@6
 | 
   451     mod.LoadText(self, self.cvars)
 | 
| 
Nenue@6
 | 
   452   end
 | 
| 
Nenue@6
 | 
   453 
 | 
| 
Nenue@6
 | 
   454   -- hide when above a certain number
 | 
| 
Nenue@6
 | 
   455 
 | 
| 
Nenue@6
 | 
   456   if self.spiral and self.spiral.subCounter then
 | 
| 
Nenue@6
 | 
   457     if self.valueFull > 6 then
 | 
| 
Nenue@6
 | 
   458       if self.textValues.subCounter then
 | 
| 
Nenue@6
 | 
   459         --print('hiding milliseconds')
 | 
| 
Nenue@6
 | 
   460         self.textRegions.subCounter:Hide()
 | 
| 
Nenue@6
 | 
   461         self.textRegionsSub = self.textRegions.subCounter
 | 
| 
Nenue@6
 | 
   462         self.textValuesSub = self.textValues.subCounter
 | 
| 
Nenue@6
 | 
   463         self.textRegions.subCounter = nil
 | 
| 
Nenue@6
 | 
   464         self.textValues.subCounter = nil
 | 
| 
Nenue@6
 | 
   465       end
 | 
| 
Nenue@6
 | 
   466     else
 | 
| 
Nenue@6
 | 
   467       if not self.textValues.subCounter then
 | 
| 
Nenue@6
 | 
   468         --print('showing milliseconds')
 | 
| 
Nenue@6
 | 
   469         self.textValues.subCounter = self.textValuesSub
 | 
| 
Nenue@6
 | 
   470         self.textRegions.subCounter = self.textRegionsSub
 | 
| 
Nenue@6
 | 
   471         self.textRegions.subCounter:Show()
 | 
| 
Nenue@6
 | 
   472       end
 | 
| 
Nenue@6
 | 
   473     end
 | 
| 
Nenue@6
 | 
   474   end
 | 
| 
Nenue@6
 | 
   475 
 | 
| 
Nenue@6
 | 
   476   for name, region in pairs(self.textRegions) do
 | 
| 
Nenue@6
 | 
   477     --print(name)
 | 
| 
Nenue@6
 | 
   478     --print(name, self.timerName, self.textValues[name](self))
 | 
| 
Nenue@6
 | 
   479     region:SetText(self.textValues[name](self))
 | 
| 
Nenue@6
 | 
   480   end
 | 
| 
Nenue@6
 | 
   481 end
 | 
| 
Nenue@6
 | 
   482 
 | 
| 
Nenue@6
 | 
   483 
 | 
| 
Nenue@6
 | 
   484 -------------------------------------------------------------------------
 | 
| 
Nenue@6
 | 
   485 --- Second-tier handlers to cut down on the number of Status:Event() polls
 | 
| 
Nenue@6
 | 
   486 
 | 
| 
Nenue@6
 | 
   487 --- UNIT_SPELLCAST_*** use args to filter out the number of full handler runs
 | 
| 
Nenue@6
 | 
   488 function mod:UNIT_SPELLCAST_SUCCEEDED (e, unit, spellName, rank, castID, spellID)
 | 
| 
Nenue@6
 | 
   489   if not mod.frames.unit[unit] then
 | 
| 
Nenue@6
 | 
   490     return
 | 
| 
Nenue@6
 | 
   491   end
 | 
| 
Nenue@6
 | 
   492 
 | 
| 
Nenue@6
 | 
   493   if #mod.frames.spellName[spellName] > 0 then
 | 
| 
Nenue@6
 | 
   494     print('spellName-ID relation detected:', cWord(spellName), cNum(spellID))
 | 
| 
Nenue@6
 | 
   495     for i, frame in pairs(mod.frames.spellName[spellName]) do
 | 
| 
Nenue@6
 | 
   496       if not frame.frames.spellID then
 | 
| 
Nenue@6
 | 
   497         frame.frames.spellID = {}
 | 
| 
Nenue@6
 | 
   498       end
 | 
| 
Nenue@6
 | 
   499       if not frame.frames.spellID[spellID] then
 | 
| 
Nenue@6
 | 
   500 
 | 
| 
Nenue@6
 | 
   501         tinsert(mod.frames.spellID[spellID], frame)
 | 
| 
Nenue@6
 | 
   502         frame.frames.spellID[spellID] = #mod.frames.spellID[spellID]
 | 
| 
Nenue@6
 | 
   503         print(cText('  updating'), cKey(frame.timerName))
 | 
| 
Nenue@6
 | 
   504       end
 | 
| 
Nenue@6
 | 
   505     end
 | 
| 
Nenue@6
 | 
   506     mod.frames.spellName[spellName] = nil
 | 
| 
Nenue@6
 | 
   507   end
 | 
| 
Nenue@6
 | 
   508 
 | 
| 
Nenue@6
 | 
   509 
 | 
| 
Nenue@6
 | 
   510 
 | 
| 
Nenue@6
 | 
   511   if mod.frames.spellID[spellID] then
 | 
| 
Nenue@6
 | 
   512     for i, timer_frame in pairs(mod.frames.spellID[spellID]) do
 | 
| 
Nenue@6
 | 
   513       print(cText('caught spell'), cWord(spellName), 'for', timer_frame:GetName())
 | 
| 
Nenue@6
 | 
   514       timer_frame:Event(e, unit, spellName, rank, castID, spellID)
 | 
| 
Nenue@6
 | 
   515     end
 | 
| 
Nenue@6
 | 
   516   end
 | 
| 
Nenue@6
 | 
   517 end
 | 
| 
Nenue@6
 | 
   518 mod.UNIT_SPELLCAST_CHANNEL_START = mod.UNIT_SPELLCAST_SUCCEEDED
 | 
| 
Nenue@6
 | 
   519 
 | 
| 
Nenue@6
 | 
   520 --- Fire a dry event to force status updates on units with changing GUID's
 | 
| 
Nenue@6
 | 
   521 function mod:PLAYER_TARGET_CHANGED(e, unit)
 | 
| 
Nenue@6
 | 
   522   print('doing a target swap thing')
 | 
| 
Nenue@6
 | 
   523   for k, v in pairs( self.frames.unit.target) do
 | 
| 
Nenue@6
 | 
   524     print(k, v)
 | 
| 
Nenue@6
 | 
   525     v:Event(nil, 'target')
 | 
| 
Nenue@6
 | 
   526   end
 | 
| 
Nenue@6
 | 
   527 end
 | 
| 
Nenue@6
 | 
   528 
 | 
| 
Nenue@6
 | 
   529 --- Same thing but for talent/spec-driven
 | 
| 
Nenue@9
 | 
   530 local update_queue = {}
 | 
| 
Nenue@9
 | 
   531 function mod.ResetTimers(heading)
 | 
| 
Nenue@9
 | 
   532   print(cText('*** Flushing update queue for'), cWord(heading))
 | 
| 
Nenue@9
 | 
   533   for id, frame in pairs(update_queue) do
 | 
| 
Nenue@9
 | 
   534     print('  ', cNum(id), cKey(frame.timerName))
 | 
| 
Nenue@9
 | 
   535     frame.disable = nil
 | 
| 
Nenue@9
 | 
   536     wipe(frame.debug_info)
 | 
| 
Nenue@9
 | 
   537     local res, msg = mod:EnableTimer(id, frame.dvars)
 | 
| 
Nenue@9
 | 
   538   end
 | 
| 
Nenue@9
 | 
   539   wipe(update_queue)
 | 
| 
Nenue@9
 | 
   540 end
 | 
| 
Nenue@9
 | 
   541 
 | 
| 
Nenue@6
 | 
   542 function mod:PLAYER_TALENT_UPDATE(e, unit)
 | 
| 
Nenue@6
 | 
   543   print('')
 | 
| 
Nenue@6
 | 
   544   print('')
 | 
| 
Nenue@6
 | 
   545   print(cText(e), T.specPage, T.specName)
 | 
| 
Nenue@6
 | 
   546 
 | 
| 
Nenue@6
 | 
   547   for _, k in ipairs({'talentID', 'talentRow', 'specPage'}) do
 | 
| 
Nenue@6
 | 
   548     for value, frameSet in pairs(mod.frames.talentID) do
 | 
| 
Nenue@6
 | 
   549       for id, frame in ipairs(frameSet) do
 | 
| 
Nenue@6
 | 
   550         print(frame.timerID, frame.timerName)
 | 
| 
Nenue@6
 | 
   551         update_queue[frame.timerID] = frame
 | 
| 
Nenue@6
 | 
   552       end
 | 
| 
Nenue@6
 | 
   553     end
 | 
| 
Nenue@6
 | 
   554   end
 | 
| 
Nenue@9
 | 
   555   mod.resetTimers('Talent')
 | 
| 
Nenue@6
 | 
   556 end
 | 
| 
Nenue@6
 | 
   557 
 | 
| 
Nenue@6
 | 
   558 function mod:PLAYER_EQUIPMENT_CHANGED(e, slot, hasItem)
 | 
| 
Nenue@6
 | 
   559   print(e, slot, hasItem)
 | 
| 
Nenue@9
 | 
   560   if mod.frames.inventoryID and mod.frames.inventoryID[slot] then
 | 
| 
Nenue@9
 | 
   561     print('  Inventory slot:', cNum(slot))
 | 
| 
Nenue@9
 | 
   562     for i, slotFrame in ipairs(mod.frames.inventoryID[slot]) do
 | 
| 
Nenue@9
 | 
   563       print('   ', cNum(i), cText(slotFrame.timerName))
 | 
| 
Nenue@9
 | 
   564       update_queue[slotFrame.timerID] = slotFrame
 | 
| 
Nenue@9
 | 
   565       if mod.frames.itemID then
 | 
| 
Nenue@9
 | 
   566         local itemsForSlot = GetInventoryItemsForSlot(slot, {}, false)
 | 
| 
Nenue@9
 | 
   567         for _, itemID in pairs(itemsForSlot) do
 | 
| 
Nenue@9
 | 
   568           if mod.frames.itemID[itemID] then
 | 
| 
Nenue@9
 | 
   569             print('    Frames for equippable item:', cNum(itemID))
 | 
| 
Nenue@9
 | 
   570             for j, itemFrame in ipairs(mod.frames.itemID[itemID]) do
 | 
| 
Nenue@9
 | 
   571               print('     ', cNum(j), cText(itemFrame.timerName))
 | 
| 
Nenue@9
 | 
   572               update_queue[itemFrame.timerID] = itemFrame
 | 
| 
Nenue@9
 | 
   573             end
 | 
| 
Nenue@9
 | 
   574           end
 | 
| 
Nenue@9
 | 
   575         end
 | 
| 
Nenue@6
 | 
   576       end
 | 
| 
Nenue@6
 | 
   577     end
 | 
| 
Nenue@6
 | 
   578   end
 | 
| 
Nenue@9
 | 
   579   mod.ResetTimers('Equipment')
 | 
| 
Nenue@6
 | 
   580 end
 | 
| 
Nenue@6
 | 
   581 function mod:PET_BATTLE_OPENING_START ()
 | 
| 
Nenue@6
 | 
   582   for i, v in pairs(mod.timers) do
 | 
| 
Nenue@6
 | 
   583     if not v.disable then
 | 
| 
Nenue@6
 | 
   584       print('suppressing', v:GetName())
 | 
| 
Nenue@6
 | 
   585       v.disable = true
 | 
| 
Nenue@6
 | 
   586       v:Hide()
 | 
| 
Nenue@6
 | 
   587       pb_suppressed[i] = true
 | 
| 
Nenue@6
 | 
   588     end
 | 
| 
Nenue@6
 | 
   589   end
 | 
| 
Nenue@6
 | 
   590 end
 | 
| 
Nenue@6
 | 
   591 function mod:PET_BATTLE_CLOSE()
 | 
| 
Nenue@6
 | 
   592   for id, v in pairs(mod.timers) do
 | 
| 
Nenue@6
 | 
   593     if pb_suppressed[id] then
 | 
| 
Nenue@6
 | 
   594       print('restoring', v:GetName())
 | 
| 
Nenue@6
 | 
   595       mod:EnableTimer(id)
 | 
| 
Nenue@6
 | 
   596       pb_suppressed[id] = nil
 | 
| 
Nenue@6
 | 
   597     end
 | 
| 
Nenue@6
 | 
   598   end
 | 
| 
Nenue@6
 | 
   599 end |