Nenue@6
|
1
|
Nenue@6
|
2 --- ${PACKAGE_NAME}
|
Nenue@6
|
3 -- @file-author@
|
Nenue@6
|
4 -- @project-revision@ @project-hash@
|
Nenue@6
|
5 -- @file-revision@ @file-hash@
|
Nenue@6
|
6 -- Created: 12/25/2015 5:33 AM
|
Nenue@6
|
7 --- Spell cooldown tracking resides here.
|
Nenue@6
|
8 --
|
Nenue@6
|
9 -- Workflow of cooldown tracking:
|
Nenue@6
|
10 -- A tracked spell cast is detected. (UNIT_SPELLCAST_*)
|
Nenue@6
|
11 -- That spell ID enters the timer table.
|
Nenue@6
|
12 -- The table is read by a handler that fires on the next frame, when cooldown information is available. (COOLDOWN_*)
|
Nenue@6
|
13 -- Set() is called on the corresponding timer frame, and frame script takes over.
|
Nenue@6
|
14 -- Timer table spells are polled on each COOLDOWN_* event, re-applying Set() when certain conditions are met.
|
Nenue@6
|
15 -- The framescript or certain handlers will remove the timer table entry when there are no more positive conditions.
|
Nenue@6
|
16 --
|
Nenue@6
|
17 local tostring, tonumber, tinsert = tostring, tonumber, tinsert
|
Nenue@6
|
18 local GetTime, GetSpellInfo, GetInventoryItemCooldown, GetSpellCooldown, PlaySoundFile = GetTime, GetSpellInfo, GetInventoryItemCooldown, GetSpellCooldown, PlaySoundFile
|
Nenue@6
|
19 local GetSpellCharges, GetSpellCount, GetInventoryItemCount, UnitAura = GetSpellCharges, GetSpellCount, GetInventoryItemCount, UnitAura
|
Nenue@6
|
20 local IsUsableItem, IsUsableSpell, GetItemSpell = IsUsableItem, IsUsableSpell, GetItemSpell
|
Nenue@6
|
21 local xpcall = xpcall
|
Nenue@6
|
22
|
Nenue@6
|
23 local CD_SLOT, CD_ITEM, CD_SPELL = 1, 2, 3
|
Nenue@6
|
24 local HIDDEN, PASSIVE, ACTIVE = 0, 1, 2
|
Nenue@6
|
25 local format, ceil = string.format, math.ceil
|
Nenue@6
|
26 local strrep, gsub, pairs = string.rep, string.gsub, pairs
|
Nenue@6
|
27 local mod = Turok.modules.TimerControl
|
Nenue@6
|
28 local T = Turok
|
Nenue@6
|
29 local db
|
Nenue@6
|
30 local FADE_TIME = 0.2
|
Nenue@6
|
31 --@debug@
|
Nenue@6
|
32 local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
|
Nenue@6
|
33 local print = function(...) print('Cooldown', ...) end
|
Nenue@6
|
34 local function GetPrint (trace)
|
Nenue@6
|
35 return trace and print or function() end
|
Nenue@6
|
36 end
|
Nenue@6
|
37
|
Nenue@6
|
38
|
Nenue@6
|
39 local item_spells = {
|
Nenue@6
|
40 ['PvP Trinket'] = 42292,
|
Nenue@6
|
41 ['Burning Mirror'] = 184270,
|
Nenue@6
|
42 }
|
Nenue@6
|
43
|
Nenue@6
|
44 T.defaults.spirit.cooldown = {
|
Nenue@6
|
45
|
Nenue@6
|
46 alpha = 1,
|
Nenue@6
|
47 alpha_ooc = 0.2,
|
Nenue@6
|
48 inverse = false,
|
Nenue@6
|
49 persist = false,
|
Nenue@6
|
50 desaturated = false,
|
Nenue@6
|
51 fill_inverse = true,
|
Nenue@6
|
52 size = 24,
|
Nenue@6
|
53 counterText = "%p",
|
Nenue@6
|
54 subCounterText = "%.p",
|
Nenue@6
|
55 chargesText = "%s",
|
Nenue@6
|
56 justifyH = 'CENTER',
|
Nenue@6
|
57 justifyV = 'TOP',
|
Nenue@6
|
58
|
Nenue@6
|
59 iconText = "%p",
|
Nenue@6
|
60 leftText = "%p",
|
Nenue@6
|
61 rightText = "%n / %d",
|
Nenue@6
|
62 passive = {
|
Nenue@6
|
63 icon = {
|
Nenue@6
|
64 desaturated = false,
|
Nenue@6
|
65 color = {1, 1, 1, 1},
|
Nenue@6
|
66 blend = 'BLEND'
|
Nenue@6
|
67 }
|
Nenue@6
|
68 },
|
Nenue@6
|
69 active = {
|
Nenue@6
|
70 icon = {
|
Nenue@6
|
71 desaturated = false,
|
Nenue@6
|
72 color = {1, 1, 1, .6},
|
Nenue@6
|
73 blend = 'ADD'
|
Nenue@6
|
74 }
|
Nenue@6
|
75 },
|
Nenue@6
|
76
|
Nenue@6
|
77 --- control displays of aura information in cooldown displays
|
Nenue@6
|
78 showAura = false,
|
Nenue@6
|
79 cooldownAura = {
|
Nenue@6
|
80 icon = {
|
Nenue@6
|
81 desaturated = true,
|
Nenue@6
|
82 color = {0,1,0,1},
|
Nenue@6
|
83 }
|
Nenue@6
|
84 }
|
Nenue@6
|
85 }
|
Nenue@6
|
86
|
Nenue@6
|
87 local p = mod.prototype.trigger.cooldown
|
Nenue@6
|
88 --@end-debug@
|
Nenue@6
|
89 p.class = 'trigger'
|
Nenue@6
|
90 p.type = 'cooldown'
|
Nenue@6
|
91 p.cvars = {
|
Nenue@6
|
92 }
|
Nenue@6
|
93 --- Sets initial values before dry Event is fired to check for presence
|
Nenue@6
|
94 p.Init = function(self, spellID, caster, tristate, minValue, maxValue)
|
Nenue@6
|
95 local print = GetPrint(self.trace)
|
Nenue@6
|
96
|
Nenue@6
|
97 self.spellID = spellID and spellID or self.spellID
|
Nenue@6
|
98 self.unit = caster and caster or self.unit
|
Nenue@6
|
99 self.persist = tristate and tristate or self.persist
|
Nenue@6
|
100 self.minValue = minValue and minValue or tonumber(self.minValue)
|
Nenue@6
|
101 self.maxValue = maxValue and maxValue or tonumber(self.maxValue)
|
Nenue@6
|
102
|
Nenue@6
|
103 --- current and last state values need to be flipped for inverted conditional
|
Nenue@6
|
104 --- last state is defined in case it needs to be overridden to ensure proper frame update
|
Nenue@6
|
105 print(cWord('Load:'),cNum(self.spellID or self.inventoryID or self.itemID), cText(self.spellName or GetItemSpell('player', self.inventoryID or self.itemID)) )
|
Nenue@6
|
106 print(cWord(' inverse=')..cBool(self.cvars.inverse))
|
Nenue@6
|
107 if self.cvars.inverse then
|
Nenue@6
|
108 self.flags = {
|
Nenue@6
|
109 active = HIDDEN,
|
Nenue@6
|
110 active_prev = ACTIVE,
|
Nenue@6
|
111 passive = PASSIVE,
|
Nenue@6
|
112 passive_prev = PASSIVE,
|
Nenue@6
|
113 hidden = PASSIVE,
|
Nenue@6
|
114 hidden_prev = HIDDEN,
|
Nenue@6
|
115 }
|
Nenue@6
|
116 else
|
Nenue@6
|
117 self.flags = {
|
Nenue@6
|
118 active = ACTIVE,
|
Nenue@6
|
119 active_prev = HIDDEN,
|
Nenue@6
|
120 passive = PASSIVE,
|
Nenue@6
|
121 passive_prev = HIDDEN,
|
Nenue@6
|
122 hidden = HIDDEN,
|
Nenue@6
|
123 hidden_prev = PASSIVE
|
Nenue@6
|
124 }
|
Nenue@6
|
125 end
|
Nenue@6
|
126 if not (self.spellID or self.spellName) then
|
Nenue@6
|
127 self.debug_info('No valid spell ID or Name')
|
Nenue@6
|
128 end
|
Nenue@6
|
129 end
|
Nenue@6
|
130
|
Nenue@6
|
131 local GetItemCooldown, GetItemInfo = GetItemCooldown, GetItemInfo
|
Nenue@6
|
132 p.Query = function(self)
|
Nenue@6
|
133 local print = GetPrint(self.trace)
|
Nenue@6
|
134 local id = self.inventoryID or self.itemID or self.spellID
|
Nenue@6
|
135 local name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count
|
Nenue@6
|
136
|
Nenue@6
|
137 --- checked in order of precedence
|
Nenue@6
|
138 if self.inventoryID then
|
Nenue@6
|
139 print(cText(' type'), cWord('inventory slot'), cNum(id))
|
Nenue@6
|
140 self.cooldownType = CD_SLOT
|
Nenue@6
|
141 start, duration, enabled = GetInventoryItemCooldown('player', id)
|
Nenue@6
|
142 charges, maxCharges, chargeStart, chargeDuration = nil, nil, nil, nil
|
Nenue@6
|
143 count = GetInventoryItemCount('player', id)
|
Nenue@6
|
144 usable = name and true or false
|
Nenue@6
|
145 elseif self.itemID then
|
Nenue@6
|
146 self.cooldownType = CD_ITEM
|
Nenue@6
|
147
|
Nenue@6
|
148 start, duration, enabled = GetItemCooldown(self.itemID)
|
Nenue@6
|
149 print(GetItemCooldown(self.itemID))
|
Nenue@6
|
150 print(GetItemInfo(id))
|
Nenue@6
|
151
|
Nenue@6
|
152 elseif self.spellID then
|
Nenue@6
|
153 self.cooldownType = CD_SPELL
|
Nenue@6
|
154 name = GetSpellInfo(self.spellID)
|
Nenue@6
|
155 start, duration, enabled = GetSpellCooldown(self.spellID)
|
Nenue@6
|
156 charges, maxCharges, chargeStart, chargeDuration = GetSpellCharges(self.spellID)
|
Nenue@6
|
157 count = GetSpellCount(self.spellID)
|
Nenue@6
|
158 usable = true -- they still exist even when dead
|
Nenue@6
|
159 else
|
Nenue@6
|
160 self.unit = 'notaunit'
|
Nenue@6
|
161 T:Print('Timer \''..tostring(self.timerName)..'\' doesn\'t have a valid status ID.')
|
Nenue@6
|
162 end
|
Nenue@6
|
163
|
Nenue@6
|
164 -- may not have been stored for some reason
|
Nenue@6
|
165 if charges and not self.maxCharges then
|
Nenue@6
|
166 self.maxCharges = maxCharges
|
Nenue@6
|
167 end
|
Nenue@6
|
168
|
Nenue@6
|
169 print('cooldown.Query(',id,')', name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration)
|
Nenue@6
|
170 return name, usable, start, duration, enabled, charges, chargeStart, chargeDuration, count
|
Nenue@6
|
171 end
|
Nenue@6
|
172
|
Nenue@6
|
173 p.Set = function(self, ...)
|
Nenue@6
|
174 local print = GetPrint(self.trace)
|
Nenue@6
|
175
|
Nenue@6
|
176 --name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count
|
Nenue@6
|
177 local name
|
Nenue@6
|
178 name, self.usable, self.start, self.duration, self.enabled, self.charges, self.chargeStart, self.chargeDuration, self.count = ...
|
Nenue@6
|
179 if name then
|
Nenue@6
|
180 self.spellName = name
|
Nenue@6
|
181 end
|
Nenue@6
|
182
|
Nenue@6
|
183 if self.duration and self.start then
|
Nenue@6
|
184 self.expires = self.start + self.duration
|
Nenue@6
|
185 else
|
Nenue@6
|
186 self.expires = 0
|
Nenue@6
|
187 end
|
Nenue@6
|
188 end
|
Nenue@6
|
189
|
Nenue@6
|
190 p.Value = function(self)
|
Nenue@6
|
191 return (self.charges and self.charges < self.maxCharges) and ((GetTime() - self.chargeStart) / self.chargeDuration) or ((GetTime() - self.start) / self.duration)
|
Nenue@6
|
192 end
|
Nenue@6
|
193
|
Nenue@6
|
194 p.SetText = mod.SetText
|
Nenue@6
|
195
|
Nenue@6
|
196 --- Assign where meaning won't be amibiguous
|
Nenue@6
|
197 local Cooldown_OnCast = function(self)
|
Nenue@6
|
198 self.triggerState = true
|
Nenue@6
|
199 end
|
Nenue@6
|
200
|
Nenue@6
|
201 local Cooldown_OnUpdate = function(self, event)
|
Nenue@6
|
202 local print = GetPrint(self.trace)
|
Nenue@6
|
203
|
Nenue@6
|
204 if self.triggerState then
|
Nenue@6
|
205 print(cWord('Event'), cText(self.timerName))
|
Nenue@6
|
206 if not event then
|
Nenue@6
|
207 print(' *', cWord('Poke'))
|
Nenue@6
|
208 end
|
Nenue@6
|
209 local diff = 'start='..cText(self.start)..' duration='..cText(self.duration)..' charges='..
|
Nenue@6
|
210 cText(self.charges).. ' chargeStart='..cText(self.chargeStart).. ' chargeDuration='..cText(self.chargeDuration)
|
Nenue@6
|
211 local name, usable, start, duration, enabled, charges, chargeStart, chargeDuration, count = self:Query()
|
Nenue@6
|
212
|
Nenue@6
|
213 -- If we want and can, pull aura data and use that in place of cooldown information
|
Nenue@6
|
214 local expires, hasAura, _
|
Nenue@6
|
215 if self.cvars.showAura then
|
Nenue@6
|
216 print(cText('UnitAura'), self.unit, self.spellName, nil, 'HELPFUL')
|
Nenue@6
|
217 local name, _, _, count, _, auraDuration, auraExpires = UnitAura(self.unit , self.spellName, nil, 'HELPFUL')
|
Nenue@6
|
218 if name and (auraDuration ~= self.auraDuration or auraExpires ~= self.auraExpires) then
|
Nenue@6
|
219
|
Nenue@6
|
220 print(cText('aura check ='), cBool(name), 's='..cNum(count), 'd='..cNum(auraDuration), 'e='..cNum(auraExpires))
|
Nenue@6
|
221 start = auraExpires - auraDuration
|
Nenue@6
|
222 duration = auraDuration
|
Nenue@6
|
223 expires = auraExpires
|
Nenue@6
|
224 end
|
Nenue@6
|
225 end
|
Nenue@6
|
226
|
Nenue@6
|
227 -- print(name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count)
|
Nenue@6
|
228 if duration ~= self.duration or
|
Nenue@6
|
229 start ~= self.start or
|
Nenue@6
|
230 chargeStart ~= self.chargeStart
|
Nenue@6
|
231 or charges ~= self.charges then
|
Nenue@6
|
232 print('a variable has changed')
|
Nenue@6
|
233 local state
|
Nenue@6
|
234
|
Nenue@6
|
235
|
Nenue@6
|
236 if duration == 0 and charges == self.maxCharges then
|
Nenue@6
|
237 print(cText(' cooldown has reset, drop it from the queue'))
|
Nenue@6
|
238 state = self.cvars.persist and self.flags.passive or self.flags.hidden
|
Nenue@6
|
239 self.triggerState = nil
|
Nenue@6
|
240 print(' ', cText('dropping'), cWord(self.timerName), cText('from spell tracking'))
|
Nenue@6
|
241
|
Nenue@6
|
242
|
Nenue@6
|
243 self:Stats(state)
|
Nenue@6
|
244 else
|
Nenue@6
|
245 if duration ~= 0 then
|
Nenue@6
|
246 print(cText(' cooldown has a hard duration'))
|
Nenue@6
|
247 if duration > T.GCD and self.displayState ~= self.flags.active then
|
Nenue@6
|
248 print(cText(' and its > GCD, update it'))
|
Nenue@6
|
249 state = self.flags.active
|
Nenue@6
|
250 end
|
Nenue@6
|
251 end
|
Nenue@6
|
252
|
Nenue@6
|
253 if charges then
|
Nenue@6
|
254 print(cText(' cooldown has charges'))
|
Nenue@6
|
255 if charges ~= self.charges or chargeStart ~= self.chargeStart then
|
Nenue@6
|
256 print(cText(' charges count or starting time has changed'))
|
Nenue@6
|
257 state = self.flags.active
|
Nenue@6
|
258 end
|
Nenue@6
|
259 end
|
Nenue@6
|
260
|
Nenue@6
|
261 self:Stats(state)
|
Nenue@6
|
262 end
|
Nenue@6
|
263
|
Nenue@6
|
264
|
Nenue@6
|
265 -- form ID, id type, displayState, prevState
|
Nenue@6
|
266 --T:Dispatch('TK_COOLDOWN_UPDATE', self.spellID, self.cooldownType, state, self.displayState)
|
Nenue@6
|
267 if state then
|
Nenue@6
|
268 self:Set(name, usable, start, duration, enabled, charges, chargeStart, chargeDuration, count)
|
Nenue@6
|
269 self.expires = charges and (self.chargeStart + self.chargeDuration) or (self.start + self.duration)
|
Nenue@6
|
270 self:SetState(state)
|
Nenue@6
|
271 --print(' ', cText('SetState'), cNum(self.displayState), 'from', cNum(self.prevState), cWord(self.timerName))
|
Nenue@6
|
272 print(' ',diff)
|
Nenue@6
|
273 print(' start='..cText(self.start)..' duration='..cText(self.duration)..' charges='..
|
Nenue@6
|
274 cText(self.charges).. ' chargeStart='..cText(self.chargeStart).. ' chargeDuration='..cText(self.chargeDuration))
|
Nenue@6
|
275 end
|
Nenue@6
|
276 elseif self.cooldownType == CD_SPELL then
|
Nenue@6
|
277 if duration == 0 and charges == self.maxCharges and self.displayState == HIDDEN then
|
Nenue@6
|
278 print(cKey(self.timerName), cText('post-framescript clean-up'))
|
Nenue@6
|
279 self.triggerState = nil
|
Nenue@6
|
280 end
|
Nenue@6
|
281 end
|
Nenue@6
|
282 end
|
Nenue@6
|
283 --self:DumpMessages()
|
Nenue@6
|
284
|
Nenue@6
|
285 end
|
Nenue@6
|
286
|
Nenue@6
|
287 p.Stats = function(self, state)
|
Nenue@6
|
288 print(self.unit, self.spellName)
|
Nenue@6
|
289 local auraName, _, _, auraCharges, _, auraDuration, auraExpires = UnitAura(self.unit, self.spellName, nil, 'HELPFUL')
|
Nenue@6
|
290 local name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count = self:Query()
|
Nenue@6
|
291 print('# GCD =', T.GCD)
|
Nenue@6
|
292 print('# SpellCooldown', 's =', start, 'd =', duration, 's =', charges and (charges..'/'..maxCharges) or 'NA')
|
Nenue@6
|
293 print('# Aura', auraName and 'yes' or 'no', auraName and ('d='..cNum(auraDuration)) or '', auraName and ('e='..auraExpires))
|
Nenue@6
|
294 print('# Frame', 'state1 =', self.displayState, 'state2 =', self.previousState, state and ('change to '..cWord(state)) or '')
|
Nenue@6
|
295
|
Nenue@6
|
296
|
Nenue@6
|
297 end
|
Nenue@6
|
298
|
Nenue@6
|
299 --- Event pointers
|
Nenue@6
|
300 p.events = {
|
Nenue@6
|
301 ['UNIT_SPELLCAST_SUCCEEDED'] = Cooldown_OnCast,
|
Nenue@6
|
302 ['UNIT_SPELLCAST_CHANNEL_START'] = Cooldown_OnCast,
|
Nenue@6
|
303 ['BAG_UPDATE_COOLDOWN'] = Cooldown_OnUpdate,
|
Nenue@6
|
304 ['SPELL_UPDATE_COOLDOWN'] = Cooldown_OnUpdate,
|
Nenue@6
|
305 ['SPELL_UPDATE_USABLE'] = Cooldown_OnUpdate,
|
Nenue@6
|
306 ['SPELL_UPDATE_CHARGES'] = Cooldown_OnUpdate,
|
Nenue@6
|
307 }
|
Nenue@6
|
308
|
Nenue@6
|
309 local unpack, select, debugstack = unpack, select, debugstack
|
Nenue@6
|
310 p.Event = function(self, e, ...)
|
Nenue@6
|
311 local print = GetPrint(self.trace)
|
Nenue@6
|
312
|
Nenue@6
|
313 self.event = e
|
Nenue@6
|
314 --- dry event case
|
Nenue@6
|
315 if not e then
|
Nenue@6
|
316 print('onEvent', self.timerName, e)
|
Nenue@6
|
317 self.triggerState = true
|
Nenue@6
|
318 Cooldown_OnUpdate(self, e, ...)
|
Nenue@6
|
319 else
|
Nenue@6
|
320 local unit = select(1,...)
|
Nenue@6
|
321 if self.unit and unit ~= self.unit and e ~= 'SPELL_UPDATE_COOLDOWN' then
|
Nenue@6
|
322 return
|
Nenue@6
|
323 end
|
Nenue@6
|
324 local args = {...}
|
Nenue@6
|
325 local success, d = xpcall(function() self.events[e](self, e, unpack(args)) end, function(m) print(self.name .."\n".. m .. "\n".. debugstack(3)) end)
|
Nenue@6
|
326
|
Nenue@6
|
327 end
|
Nenue@6
|
328 end
|
Nenue@6
|
329
|
Nenue@6
|
330 p.triggerList = {
|
Nenue@6
|
331 ['on_cooldown'] = function(self)
|
Nenue@6
|
332 local start, duration, enabled = GetSpellCooldown(self.spellID)
|
Nenue@6
|
333 local charges, _, cStart, cDuration = GetSpellCharges(self.spellID)
|
Nenue@6
|
334 return (enabled and (duration ~= 0 or start ~= 0 or charges ~= self.maxCharges)), start, duration, enabled, charges, cStart, cDuration
|
Nenue@6
|
335 end,
|
Nenue@6
|
336 ['not_on_cooldown'] = function(self)
|
Nenue@6
|
337 local start, duration, enabled = GetSpellCooldown(self.spellID)
|
Nenue@6
|
338 local charges, _, cStart, cDuration = GetSpellCharges(self.spellID)
|
Nenue@6
|
339 return (duration == 0 and start == 0 and charges == self.maxCharges), start, duration, enabled, charges, cStart, cDuration
|
Nenue@6
|
340 end,
|
Nenue@6
|
341 ['buff_active'] = function(self)
|
Nenue@6
|
342 local name, rank, icon, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff, value1, value2, value3 = UnitAura(self.unit, self.spellName, self.filters)
|
Nenue@6
|
343 return (name and true or false), expires - duration, duration, true, count, 0, 0
|
Nenue@6
|
344 end
|
Nenue@6
|
345 } |