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@9
|
33
|
Nenue@9
|
34 local print = function(...)
|
Nenue@9
|
35 if _G.Devian and _G.DevianDB.workspace ~= 1 then
|
Nenue@9
|
36 _G.print('Cooldown', ...)
|
Nenue@9
|
37 end
|
Nenue@9
|
38 end
|
Nenue@6
|
39 local function GetPrint (trace)
|
Nenue@6
|
40 return trace and print or function() end
|
Nenue@6
|
41 end
|
Nenue@6
|
42
|
Nenue@6
|
43
|
Nenue@6
|
44 local item_spells = {
|
Nenue@6
|
45 ['PvP Trinket'] = 42292,
|
Nenue@6
|
46 ['Burning Mirror'] = 184270,
|
Nenue@6
|
47 }
|
Nenue@6
|
48
|
Nenue@6
|
49 T.defaults.spirit.cooldown = {
|
Nenue@6
|
50
|
Nenue@6
|
51 alpha = 1,
|
Nenue@6
|
52 alpha_ooc = 0.2,
|
Nenue@6
|
53 inverse = false,
|
Nenue@6
|
54 persist = false,
|
Nenue@6
|
55 desaturated = false,
|
Nenue@6
|
56 fill_inverse = true,
|
Nenue@6
|
57 size = 24,
|
Nenue@9
|
58 counterText = "%i",
|
Nenue@6
|
59 subCounterText = "%.p",
|
Nenue@6
|
60 chargesText = "%s",
|
Nenue@6
|
61 justifyH = 'CENTER',
|
Nenue@6
|
62 justifyV = 'TOP',
|
Nenue@6
|
63
|
Nenue@6
|
64 iconText = "%p",
|
Nenue@6
|
65 leftText = "%p",
|
Nenue@6
|
66 rightText = "%n / %d",
|
Nenue@6
|
67 passive = {
|
Nenue@6
|
68 icon = {
|
Nenue@6
|
69 desaturated = false,
|
Nenue@6
|
70 color = {1, 1, 1, 1},
|
Nenue@6
|
71 }
|
Nenue@6
|
72 },
|
Nenue@6
|
73 active = {
|
Nenue@6
|
74 icon = {
|
Nenue@9
|
75 desaturated = true,
|
Nenue@6
|
76 color = {1, 1, 1, .6},
|
Nenue@6
|
77 }
|
Nenue@6
|
78 },
|
Nenue@6
|
79
|
Nenue@6
|
80 --- control displays of aura information in cooldown displays
|
Nenue@9
|
81 overrideAura = false,
|
Nenue@9
|
82 overrideDuration = false,
|
Nenue@9
|
83 override = {
|
Nenue@6
|
84 icon = {
|
Nenue@6
|
85 desaturated = true,
|
Nenue@6
|
86 color = {0,1,0,1},
|
Nenue@6
|
87 }
|
Nenue@6
|
88 }
|
Nenue@6
|
89 }
|
Nenue@6
|
90
|
Nenue@6
|
91 local p = mod.prototype.trigger.cooldown
|
Nenue@6
|
92 --@end-debug@
|
Nenue@6
|
93 p.class = 'trigger'
|
Nenue@6
|
94 p.type = 'cooldown'
|
Nenue@6
|
95 p.cvars = {
|
Nenue@6
|
96 }
|
Nenue@6
|
97 --- Sets initial values before dry Event is fired to check for presence
|
Nenue@6
|
98 p.Init = function(self, spellID, caster, tristate, minValue, maxValue)
|
Nenue@6
|
99 local print = GetPrint(self.trace)
|
Nenue@6
|
100
|
Nenue@6
|
101 self.spellID = spellID and spellID or self.spellID
|
Nenue@6
|
102 self.unit = caster and caster or self.unit
|
Nenue@6
|
103 self.persist = tristate and tristate or self.persist
|
Nenue@6
|
104 self.minValue = minValue and minValue or tonumber(self.minValue)
|
Nenue@6
|
105 self.maxValue = maxValue and maxValue or tonumber(self.maxValue)
|
Nenue@6
|
106
|
Nenue@9
|
107 self.start = 0
|
Nenue@9
|
108 self.duration = 0
|
Nenue@9
|
109 self.expires = 0
|
Nenue@9
|
110 self.charges = nil
|
Nenue@9
|
111 self.charges_max = 0
|
Nenue@9
|
112 self.charge_start = 0
|
Nenue@9
|
113 self.charge_duration = 0
|
Nenue@9
|
114 self.charge_expires = 0
|
Nenue@9
|
115 self.override = false
|
Nenue@9
|
116 self.override_start = 0
|
Nenue@9
|
117 self.override_duration = 0
|
Nenue@9
|
118 self.override_expires = 0
|
Nenue@9
|
119
|
Nenue@6
|
120 --- current and last state values need to be flipped for inverted conditional
|
Nenue@6
|
121 --- last state is defined in case it needs to be overridden to ensure proper frame update
|
Nenue@6
|
122 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
|
123 print(cWord(' inverse=')..cBool(self.cvars.inverse))
|
Nenue@6
|
124 if self.cvars.inverse then
|
Nenue@6
|
125 self.flags = {
|
Nenue@6
|
126 active = HIDDEN,
|
Nenue@6
|
127 active_prev = ACTIVE,
|
Nenue@6
|
128 passive = PASSIVE,
|
Nenue@6
|
129 passive_prev = PASSIVE,
|
Nenue@6
|
130 hidden = PASSIVE,
|
Nenue@6
|
131 hidden_prev = HIDDEN,
|
Nenue@6
|
132 }
|
Nenue@6
|
133 else
|
Nenue@6
|
134 self.flags = {
|
Nenue@6
|
135 active = ACTIVE,
|
Nenue@6
|
136 active_prev = HIDDEN,
|
Nenue@6
|
137 passive = PASSIVE,
|
Nenue@6
|
138 passive_prev = HIDDEN,
|
Nenue@6
|
139 hidden = HIDDEN,
|
Nenue@6
|
140 hidden_prev = PASSIVE
|
Nenue@6
|
141 }
|
Nenue@6
|
142 end
|
Nenue@6
|
143 if not (self.spellID or self.spellName) then
|
Nenue@6
|
144 self.debug_info('No valid spell ID or Name')
|
Nenue@6
|
145 end
|
Nenue@6
|
146 end
|
Nenue@6
|
147
|
Nenue@6
|
148 local GetItemCooldown, GetItemInfo = GetItemCooldown, GetItemInfo
|
Nenue@6
|
149 p.Query = function(self)
|
Nenue@6
|
150 local print = GetPrint(self.trace)
|
Nenue@6
|
151 local id = self.inventoryID or self.itemID or self.spellID
|
Nenue@9
|
152 local name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration = nil, false, 0, 0, nil, nil, nil, 0, 0
|
Nenue@9
|
153 local override, overrideStart, overrideDuration = false, 0, 0
|
Nenue@6
|
154
|
Nenue@6
|
155 --- checked in order of precedence
|
Nenue@6
|
156 if self.inventoryID then
|
Nenue@6
|
157 print(cText(' type'), cWord('inventory slot'), cNum(id))
|
Nenue@6
|
158 self.cooldownType = CD_SLOT
|
Nenue@6
|
159 start, duration, enabled = GetInventoryItemCooldown('player', id)
|
Nenue@6
|
160 charges, maxCharges, chargeStart, chargeDuration = nil, nil, nil, nil
|
Nenue@6
|
161 usable = name and true or false
|
Nenue@6
|
162 elseif self.itemID then
|
Nenue@6
|
163 self.cooldownType = CD_ITEM
|
Nenue@6
|
164
|
Nenue@6
|
165 start, duration, enabled = GetItemCooldown(self.itemID)
|
Nenue@6
|
166 print(GetItemCooldown(self.itemID))
|
Nenue@6
|
167 print(GetItemInfo(id))
|
Nenue@6
|
168
|
Nenue@6
|
169 elseif self.spellID then
|
Nenue@6
|
170 self.cooldownType = CD_SPELL
|
Nenue@6
|
171 name = GetSpellInfo(self.spellID)
|
Nenue@6
|
172 start, duration, enabled = GetSpellCooldown(self.spellID)
|
Nenue@6
|
173 charges, maxCharges, chargeStart, chargeDuration = GetSpellCharges(self.spellID)
|
Nenue@6
|
174 usable = true -- they still exist even when dead
|
Nenue@6
|
175 else
|
Nenue@6
|
176 self.unit = 'notaunit'
|
Nenue@6
|
177 T:Print('Timer \''..tostring(self.timerName)..'\' doesn\'t have a valid status ID.')
|
Nenue@6
|
178 end
|
Nenue@6
|
179
|
Nenue@6
|
180 -- may not have been stored for some reason
|
Nenue@6
|
181 if charges and not self.maxCharges then
|
Nenue@6
|
182 self.maxCharges = maxCharges
|
Nenue@6
|
183 end
|
Nenue@6
|
184
|
Nenue@9
|
185 if self.spellName and self.cvars.overrideAura then
|
Nenue@9
|
186 local name, _, _, _, _, d, e = UnitAura(self.unit , self.spellName, nil, 'HELPFUL')
|
Nenue@9
|
187 if name then
|
Nenue@9
|
188 override = true
|
Nenue@9
|
189 overrideDuration = d
|
Nenue@9
|
190 overrideStart = e - d
|
Nenue@9
|
191 end
|
Nenue@9
|
192 end
|
Nenue@9
|
193
|
Nenue@9
|
194 if self.overrideDuration and start > 0 then
|
Nenue@9
|
195 override = true
|
Nenue@9
|
196 overrideDuration = self.overrideDuration
|
Nenue@9
|
197 overrideStart = start
|
Nenue@9
|
198 end
|
Nenue@9
|
199
|
Nenue@9
|
200 print('cooldown.Query(',id,')', 'name:'..cText(name), 'usable:'..cBool(usable), 'start:'..cNum(start), 'duration:'..cNum(duration), 'enabled:'..cWord(enabled),
|
Nenue@9
|
201 'charges:'.. cNum(charges), 'charge_start:'.. cNum(chargeStart), 'charge_duration:'.. cNum(chargeDuration),
|
Nenue@9
|
202 'override:' .. cBool(override), 'override_start'..cNum(overrideStart), 'override_duration:'.. cNum(overrideDuration))
|
Nenue@9
|
203 return name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, override, overrideStart, overrideDuration
|
Nenue@6
|
204 end
|
Nenue@6
|
205
|
Nenue@6
|
206 p.Set = function(self, ...)
|
Nenue@6
|
207 local print = GetPrint(self.trace)
|
Nenue@6
|
208
|
Nenue@6
|
209 --name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count
|
Nenue@6
|
210 local name
|
Nenue@9
|
211 name, self.usable, self.start, self.duration, self.enabled, self.charges, self.charges_max, self.charge_start, self.charge_duration, self.override, self.override_start, self.override_duration = ...
|
Nenue@6
|
212 if name then
|
Nenue@6
|
213 self.spellName = name
|
Nenue@6
|
214 end
|
Nenue@6
|
215
|
Nenue@6
|
216 if self.duration and self.start then
|
Nenue@6
|
217 self.expires = self.start + self.duration
|
Nenue@6
|
218 else
|
Nenue@6
|
219 self.expires = 0
|
Nenue@6
|
220 end
|
Nenue@9
|
221
|
Nenue@9
|
222 if self.charges then
|
Nenue@9
|
223 self.charge_expires = self.charge_start + self.charge_duration
|
Nenue@9
|
224 end
|
Nenue@9
|
225 if self.override then
|
Nenue@9
|
226 self.override_expires = self.override_start + self.override_duration
|
Nenue@9
|
227 end
|
Nenue@9
|
228
|
Nenue@6
|
229 end
|
Nenue@6
|
230
|
Nenue@6
|
231 p.Value = function(self)
|
Nenue@6
|
232 return (self.charges and self.charges < self.maxCharges) and ((GetTime() - self.chargeStart) / self.chargeDuration) or ((GetTime() - self.start) / self.duration)
|
Nenue@6
|
233 end
|
Nenue@6
|
234
|
Nenue@6
|
235 p.SetText = mod.SetText
|
Nenue@6
|
236
|
Nenue@6
|
237 --- Assign where meaning won't be amibiguous
|
Nenue@6
|
238 local Cooldown_OnCast = function(self)
|
Nenue@6
|
239 self.triggerState = true
|
Nenue@6
|
240 end
|
Nenue@6
|
241
|
Nenue@6
|
242 local Cooldown_OnUpdate = function(self, event)
|
Nenue@6
|
243 local print = GetPrint(self.trace)
|
Nenue@6
|
244
|
Nenue@6
|
245 if self.triggerState then
|
Nenue@6
|
246 print(cWord('Event'), cText(self.timerName))
|
Nenue@6
|
247 if not event then
|
Nenue@6
|
248 print(' *', cWord('Poke'))
|
Nenue@6
|
249 end
|
Nenue@6
|
250 local diff = 'start='..cText(self.start)..' duration='..cText(self.duration)..' charges='..
|
Nenue@6
|
251 cText(self.charges).. ' chargeStart='..cText(self.chargeStart).. ' chargeDuration='..cText(self.chargeDuration)
|
Nenue@9
|
252 local name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, override, overrideStart, overrideDuration = self:Query()
|
Nenue@6
|
253
|
Nenue@6
|
254 -- print(name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count)
|
Nenue@6
|
255 if duration ~= self.duration or
|
Nenue@6
|
256 start ~= self.start or
|
Nenue@9
|
257 chargeStart ~= self.charge_start or
|
Nenue@9
|
258 charges ~= self.charges or
|
Nenue@9
|
259 override ~= self.override
|
Nenue@9
|
260 then
|
Nenue@6
|
261 print('a variable has changed')
|
Nenue@6
|
262 local state
|
Nenue@6
|
263
|
Nenue@6
|
264
|
Nenue@6
|
265 if duration == 0 and charges == self.maxCharges then
|
Nenue@6
|
266 print(cText(' cooldown has reset, drop it from the queue'))
|
Nenue@6
|
267 state = self.cvars.persist and self.flags.passive or self.flags.hidden
|
Nenue@6
|
268 self.triggerState = nil
|
Nenue@6
|
269 print(' ', cText('dropping'), cWord(self.timerName), cText('from spell tracking'))
|
Nenue@6
|
270
|
Nenue@6
|
271
|
Nenue@6
|
272 self:Stats(state)
|
Nenue@6
|
273 else
|
Nenue@6
|
274 if duration ~= 0 then
|
Nenue@6
|
275 print(cText(' cooldown has a hard duration'))
|
Nenue@6
|
276 if duration > T.GCD and self.displayState ~= self.flags.active then
|
Nenue@6
|
277 print(cText(' and its > GCD, update it'))
|
Nenue@6
|
278 state = self.flags.active
|
Nenue@6
|
279 end
|
Nenue@6
|
280 end
|
Nenue@6
|
281
|
Nenue@6
|
282 if charges then
|
Nenue@6
|
283 print(cText(' cooldown has charges'))
|
Nenue@6
|
284 if charges ~= self.charges or chargeStart ~= self.chargeStart then
|
Nenue@6
|
285 print(cText(' charges count or starting time has changed'))
|
Nenue@6
|
286 state = self.flags.active
|
Nenue@6
|
287 end
|
Nenue@6
|
288 end
|
Nenue@6
|
289
|
Nenue@6
|
290 self:Stats(state)
|
Nenue@6
|
291 end
|
Nenue@6
|
292
|
Nenue@6
|
293 if state then
|
Nenue@9
|
294 self:Set(name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, override, overrideStart, overrideDuration)
|
Nenue@6
|
295 self.expires = charges and (self.chargeStart + self.chargeDuration) or (self.start + self.duration)
|
Nenue@6
|
296 self:SetState(state)
|
Nenue@6
|
297 print(' ',diff)
|
Nenue@6
|
298 print(' start='..cText(self.start)..' duration='..cText(self.duration)..' charges='..
|
Nenue@6
|
299 cText(self.charges).. ' chargeStart='..cText(self.chargeStart).. ' chargeDuration='..cText(self.chargeDuration))
|
Nenue@6
|
300 end
|
Nenue@9
|
301 else
|
Nenue@9
|
302 if self.cooldownType == CD_SPELL then
|
Nenue@9
|
303 if duration == 0 and charges == self.maxCharges and self.displayState == HIDDEN then
|
Nenue@9
|
304 print(cKey(self.timerName), cText('post-framescript clean-up'))
|
Nenue@9
|
305 self.triggerState = nil
|
Nenue@9
|
306 end
|
Nenue@6
|
307 end
|
Nenue@6
|
308 end
|
Nenue@6
|
309 end
|
Nenue@6
|
310 end
|
Nenue@6
|
311
|
Nenue@6
|
312 p.Stats = function(self, state)
|
Nenue@6
|
313 print(self.unit, self.spellName)
|
Nenue@9
|
314 if self.spellName then
|
Nenue@9
|
315 local auraName, _, _, auraCharges, _, auraDuration, auraExpires = UnitAura(self.unit, self.spellName, nil, 'HELPFUL')
|
Nenue@9
|
316 print(' # Aura', auraName and 'yes' or 'no', auraName and ('d='..cNum(auraDuration)) or '', auraName and ('e='..auraExpires))
|
Nenue@9
|
317 end
|
Nenue@9
|
318
|
Nenue@6
|
319 local name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count = self:Query()
|
Nenue@9
|
320 print(' # GCD =', T.GCD)
|
Nenue@9
|
321 print(' # SpellCooldown', 's =', start, 'd =', duration, 's =', charges and (charges..'/'..maxCharges) or 'NA')
|
Nenue@9
|
322 print(' # Frame', 'state1 =', self.displayState, 'state2 =', self.previousState, state and ('change to '..cWord(state)) or '')
|
Nenue@6
|
323
|
Nenue@6
|
324
|
Nenue@6
|
325 end
|
Nenue@6
|
326
|
Nenue@6
|
327 --- Event pointers
|
Nenue@6
|
328 p.events = {
|
Nenue@6
|
329 ['UNIT_SPELLCAST_SUCCEEDED'] = Cooldown_OnCast,
|
Nenue@6
|
330 ['UNIT_SPELLCAST_CHANNEL_START'] = Cooldown_OnCast,
|
Nenue@6
|
331 ['BAG_UPDATE_COOLDOWN'] = Cooldown_OnUpdate,
|
Nenue@6
|
332 ['SPELL_UPDATE_COOLDOWN'] = Cooldown_OnUpdate,
|
Nenue@6
|
333 ['SPELL_UPDATE_USABLE'] = Cooldown_OnUpdate,
|
Nenue@6
|
334 ['SPELL_UPDATE_CHARGES'] = Cooldown_OnUpdate,
|
Nenue@6
|
335 }
|
Nenue@6
|
336
|
Nenue@6
|
337 local unpack, select, debugstack = unpack, select, debugstack
|
Nenue@6
|
338 p.Event = function(self, e, ...)
|
Nenue@6
|
339 local print = GetPrint(self.trace)
|
Nenue@6
|
340
|
Nenue@6
|
341 self.event = e
|
Nenue@6
|
342 --- dry event case
|
Nenue@6
|
343 if not e then
|
Nenue@6
|
344 print('onEvent', self.timerName, e)
|
Nenue@6
|
345 self.triggerState = true
|
Nenue@6
|
346 Cooldown_OnUpdate(self, e, ...)
|
Nenue@6
|
347 else
|
Nenue@6
|
348 local unit = select(1,...)
|
Nenue@6
|
349 if self.unit and unit ~= self.unit and e ~= 'SPELL_UPDATE_COOLDOWN' then
|
Nenue@6
|
350 return
|
Nenue@6
|
351 end
|
Nenue@6
|
352 local args = {...}
|
Nenue@6
|
353 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
|
354
|
Nenue@6
|
355 end
|
Nenue@6
|
356 end
|
Nenue@6
|
357
|
Nenue@6
|
358 p.triggerList = {
|
Nenue@6
|
359 ['on_cooldown'] = function(self)
|
Nenue@6
|
360 local start, duration, enabled = GetSpellCooldown(self.spellID)
|
Nenue@6
|
361 local charges, _, cStart, cDuration = GetSpellCharges(self.spellID)
|
Nenue@6
|
362 return (enabled and (duration ~= 0 or start ~= 0 or charges ~= self.maxCharges)), start, duration, enabled, charges, cStart, cDuration
|
Nenue@6
|
363 end,
|
Nenue@6
|
364 ['not_on_cooldown'] = function(self)
|
Nenue@6
|
365 local start, duration, enabled = GetSpellCooldown(self.spellID)
|
Nenue@6
|
366 local charges, _, cStart, cDuration = GetSpellCharges(self.spellID)
|
Nenue@6
|
367 return (duration == 0 and start == 0 and charges == self.maxCharges), start, duration, enabled, charges, cStart, cDuration
|
Nenue@6
|
368 end,
|
Nenue@6
|
369 ['buff_active'] = function(self)
|
Nenue@6
|
370 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
|
371 return (name and true or false), expires - duration, duration, true, count, 0, 0
|
Nenue@6
|
372 end
|
Nenue@6
|
373 } |