| 
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 |