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