comparison Turok/Modules/Combat/Castbar.lua @ 6:a9b8b0866ece

clear out log jam
author Nenue
date Sun, 21 Feb 2016 08:32:53 -0500
parents
children 9400a0ff8540
comparison
equal deleted inserted replaced
5:8a9a6637f082 6:a9b8b0866ece
1 --- Turok
2 -- Castbar.lua
3 -- Created: 12/4/2015 11:17 PM
4 -- @file-author@
5 -- @project-revision@ @project-hash@
6 -- @file-revision@ @file-hash@
7 -- Deals with casting events and works the castingbar interface
8 local print, tostring, tonumber, rep, pairs, min, format, unpack, select, strpad = print, tostring, tonumber, string.rep, pairs, math.min, string.format, unpack, select, string.rep
9 local GetTime, UnitCastingInfo, UnitChannelInfo, GetSpellInfo, GetSpellTabInfo = GetTime, UnitCastingInfo, UnitChannelInfo, GetSpellInfo, GetSpellTabInfo
10 local floor = math.floor
11 local T = Turok
12 local db
13 local PING_CEILING = 190 -- transpacific
14 local PING_MIDLINE = 100
15 local PING_FLOOR = 10 -- transcontinental
16 --@debug
17 local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
18 local print = function(...)
19 if _G.Devian and _G.DevianDB.workspace ~= 1 then
20 _G.print('Tek', ...)
21 end
22 end
23
24 local EVENT_COLOR = '|cFF44FF44'
25 local EVENT_COLOR2 = '|cFF88FF88'
26 local DATA_COLOR = '|cFFDD77DD'
27 --@end-debug@
28
29 local mod = T:NewModule("Tek")
30 function mod:OnInitialize()
31 self.db = T.db.tek
32 self.castbar = {}
33 self.UNIT_SPELLCAST_SENT = self.SpellCastRequest
34 self.UNIT_SPELLCAST_START = self.SpellCastEvent
35 self.UNIT_SPELLCAST_DELAYED = self.SpellCastEvent
36 self.UNIT_SPELLCAST_SUCCEEDED = self.SpellCastEvent
37 self.UNIT_SPELLCAST_STOP = self.SpellCastEvent
38 self.UNIT_SPELLCAST_FAILED = self.SpellCastEvent
39 self.UNIT_SPELLCAST_INTERRUPTED = self.SpellCastEvent
40 self.UNIT_SPELLCAST_CHANNEL_START = self.SpellCastEvent
41 self.UNIT_SPELLCAST_CHANNEL_STOP = self.SpellCastEvent
42 self.UNIT_SPELLCAST_CHANNEL_UPDATE = self.SpellCastEvent
43 self.UNIT_SPELLCAST_INTERRUPTIBLE = self.SpellCastEvent
44 self.UNIT_SPELLCAST_UNINTERRUPTIBLE = self.SpellCastEvent
45 end
46
47 --- events & units
48 local TRACKED_UNITS = {'player', 'target', 'focus', 'pet'}
49 local FADE_OUT_TIME, FADE_IN_TIME = 1, .2
50
51 local TEXTURE_SUFFIX = {
52 ['CHANNEL_START'] = '_channeling',
53 ['CHANNEL_UPDATE'] = '_channeling',
54 ['CHANNEL_STOPPED'] = '_channeling',
55 ['START'] = '_casting',
56 ['INTERRUPTED'] = '_interrupted',
57 ['SUCCEEDED'] = '_finished',
58 }
59
60 function mod:OnEnable()
61 db = self.db
62 -- setup castingbar frames
63 local c = self.castbar
64 for _, unit in pairs(TRACKED_UNITS) do
65 local cdb = db[unit]
66 --@debug@
67 print(DATA_COLOR .. unit .. '|r castbar creation')--@end-debug@
68
69 -- Set frames
70 local fn = 'Tek' .. unit .. 'CastBar'
71 c[unit] = CreateFrame('Frame', fn, _G[cdb.parent], 'TurokCastingBar')
72
73 local pc = c[unit]
74 T.SetFrameLayout(pc, cdb)
75 T.SetTextureLayout(pc.icon, cdb.icon or db.icon)
76 T.SetStatusTextures(pc, cdb.statusbar or db.statusbar)
77
78 if unit == 'player' then
79 T.SetFontLayout(pc.ping, db.ping)
80 T.SetFontLayout(pc.downtime, db.downtime)
81 pc.pingbar:ClearAllPoints()
82 pc.pingbar:SetPoint('TOPRIGHT', pc.background, 'TOPRIGHT', 0, 0)
83 pc.pingbar:SetHeight(pc.background:GetHeight())
84 else
85 if pc.interrupt then
86 pc.interrupt:SetSize(pc.icon.size*3, pc.icon.size* 3)
87 end
88
89 end
90
91 T.SetFontLayout(pc.casttime, cdb.casttime or db.casttime)
92 T.SetFontLayout(pc.spelltext, cdb.spelltext or db.spelltext)
93
94 pc:SetAlpha(0)
95 pc.last = nil
96 pc.sent = {}
97 pc.unit = unit
98 pc.SetSpell = self.SetSpell
99 pc.SetState = self.SetState
100 pc.Update = self.Update
101 end
102
103
104 -- kill default casting bar
105 -- T.cbscripts = {CastingBarFrame:GetScript('OnUpdate'), CastingBarFrame:GetScript('OnEvent')}
106 CastingBarFrame:SetScript('OnUpdate', nil)
107 CastingBarFrame:SetScript('OnEvent', nil)
108 CastingBarFrame:Hide()
109 PetCastingBarFrame:SetScript('OnUpdate', nil)
110 PetCastingBarFrame:SetScript('OnEvent', nil)
111 PetCastingBarFrame:Hide()
112 end
113
114 function mod:UpdateLocked()
115 for k, v in pairs(self.castbar) do
116 if T.unlocked then
117 local name, texture, offset, numSpells = GetSpellTabInfo(T.specPage)
118 v.value = offset
119 v.duration = numSpells
120 v.spelltext:SetText(k)
121 v.icon:SetTexture(texture)
122 v:SetAlpha(1)
123 v:Show()
124 v:EnableMouse(true)
125 v:SetMovable(true)
126 v:RegisterForDrag("LeftButton")
127 v:SetScript('OnDragStart', function(self) self:StartMoving() end)
128 v:SetScript('OnDragStop', function(self) self:StopMovingOrSizing() end)
129 else
130
131 v:SetScript('OnDragStart', nil)
132 v:SetScript('OnDragStop', nil)
133 v:EnableMouse(false)
134 v:SetMovable(false)
135
136 print('Saving bar coordinates post unlock')
137
138 db[k].x = v:GetLeft()
139 db[k].y = v:GetTop()
140 db[k].anchor = 'BOTTOMLEFT'
141 db[k].anchorTo = 'BOTTOMLEFT'
142 db[k].parent = 'UIParent'
143 end
144 end
145 end
146
147 --- Store spell requests for use in tracking lag time
148 function mod:SpellCastRequest(e, unit, spellName, rank, target, castID)
149 print('|cFFFF4400Request sent:|r ', spellName, castID, target)
150 self.castbar.player.sent[castID] = {
151 spellName = spellName,
152 castID = castID,
153 sendTime = GetTime()*1000,
154 }
155 end
156
157 --- Handle events pertaining to a cast bar
158 function mod:SpellCastEvent(event, unit, ...)
159 if not self.castbar[unit] then
160 return
161 end
162
163 local u = T.unit[unit]
164 local c = self.castbar[unit]
165
166 --- doubling as an invocation source test
167 local spellName, rank, castID, spellID = ...
168
169 local channelinfo = T.unit[unit].channeling
170 local castinginfo = T.unit[unit].casting
171 local sendq = c.sent
172 local castd = c.last
173 local timestamp = GetTime()*1000
174
175 --todo: remove truncation
176 local e = event:match("UNIT_SPELLCAST_([%a_]+)")
177 print(GetTime(), '|cFFFF8747'..e..(strpad(' ',12-#e))..'|r |cFFDDFF00#'..tostring(castID)..'|r', spellName, spellID)
178
179 c.channeling = channelinfo and channelinfo[1] or nil
180 c.casting = castinginfo and castinginfo[1] or nil
181 if c.channeling then
182 -- {name, subText, text, texture, startTime, endTime, isTradeSkill, notInterruptible}
183 c.startTime = channelinfo[5]
184 c.endTime = channelinfo[6]
185 c.nonInterruptible = channelinfo[8]
186 elseif c.casting then
187 --name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible
188 c.startTime = castinginfo[5]
189 c.endTime = castinginfo[6]
190 c.castID = castinginfo[8]
191 c.nonInterruptible = castinginfo[9]
192 else
193 print(' |cFFFF7700internal data was nil|r')
194 end
195
196 --- process the "event"
197 local setSpell, setState
198 if e == 'START' then
199 setSpell = true
200 setState = true
201
202 -- player data calculations
203 if unit == 'player' then
204 if sendq[castID] and sendq[castID].spellName == spellName then
205 local sent = sendq[castID]
206 print(' |cFF00AAFFmatched:|r', sent.spellName, castID)
207 c.pingTime = c.startTime - sent.sendTime
208 print(' ping:', c.pingTime)
209 for cid, cdata in pairs(sendq) do
210 if cdata.sendTime <= sent.sendTime then
211 --print(' forgetting #'..cdata.sendTime, cid, cdata.spellName)
212 sendq[cid] = nil
213 end
214 end
215
216 if castd ~= nil then
217 print(' downtime calc hit')
218 c.downTime = timestamp - castd.endTime
219 castd = nil
220 else
221 c.downTime = nil
222 end
223 c.castID = castID
224 end
225 end
226 elseif e == 'CHANNEL_START' or e == 'CHANNEL_UPDATE' then
227 setSpell = true
228 setState = true
229 c.event = e
230
231 elseif e == 'STOP' then
232 setState = true
233 c.casting = nil
234 castd = {
235 spellName = spellName,
236 spellID = spellID,
237 castID = castID,
238 endTime = timestamp,
239 }
240 print(' |cFFFFFF00found', timestamp, spellName)
241
242 elseif e == 'CHANNEL_STOP' then
243 setState = true
244 c.channeling = nil
245 castd = {
246 spellName = spellName,
247 spellID = spellID,
248 castID = castID,
249 endTime = timestamp,
250 }
251 print(' |cFFFF0088found', timestamp, spellName)
252
253 elseif e == 'SUCCEEDED' then
254 if c.spellName == spellName and not c.channeling then
255 setState = true
256 print(' |cFFFF4400',c.spellName,'=',spellName,' pushing event', e)
257 end
258 -- if there are 'send' args when this fires, it means it's an instant cast
259 if sendq[castID] and sendq[castID].spellName == spellName then
260 castd = {
261 spellName = spellName,
262 spellID = spellID,
263 castID = castID,
264 endTime = timestamp,
265 }
266 print(' |cFFFFAA00found', timestamp, spellName)
267 end
268
269 elseif e =='FAILED' then
270 if spellName == c.spellName and not c.channeling then
271 setState = true
272 end
273
274 elseif e == 'INTERRUPTED' then
275 setState = true
276
277 end
278
279
280
281
282 if setSpell then
283 print(' |cFF44FF00setting spell', spellName)
284 end
285 if setState then
286 print(' |cFFFF4400pushing event', e)
287 end
288
289 if setSpell then
290 c:SetSpell(e)
291 end
292 if setState then
293 c:SetState(e)
294 end
295 end
296
297
298 --- Sets all the static elements of the casting bar, such as config values, icon texture, and name
299 function mod.SetSpell(c, event)
300 local _
301 local time, alpha = GetTime(), c:GetAlpha()
302 local u, cdb = T.unit[c.unit], c.db
303
304 c.stopped = nil
305 c.stopping = nil
306 print('New spell:', c.spellName)
307 --- use only internal info
308 local data = (event == 'CHANNEL_START' or event == 'CHANNEL_UPDATE' or event == 'CHANNEL_STOP') and u.channeling or u.casting
309 local spellName, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = unpack(data)
310
311
312 if spellName == nil then
313 print(" Can't do arithmetic, no data.")
314 else
315 print(cText(" Arithmetic vars:"), cNum(startTime), cNum(endTime))
316 end
317
318 c.spellName = spellName
319 c.castID = castID or 0
320 c.rank = rank
321 c.displayName = displayName
322 c.texture = texture
323 c.nonInterruptible = nonInterruptible
324 c.isTradeSkill = isTradeSkill
325 c.endTime = endTime or 0
326 c.startTime = startTime or 0
327 c.duration = c.endTime - c.startTime
328
329 if c.unit == 'player' then
330 if c.pingTime then
331 print(' ping',c.pingTime, endTime, startTime, c.duration)
332 local draw_dist = (c.pingTime / c.duration) * (c.fill_width)
333 if draw_dist > c.fill_width then
334 draw_dist = c.fill_width
335 end
336 print('SET AND SHOW PING ', c.pingTime,'on', c.pingbar:GetName())
337 c.pingbar:SetWidth(draw_dist)
338 c.pingbar:Show()
339
340 local rv = c.pingTime - PING_FLOOR
341 local rr = PING_MIDLINE - PING_FLOOR
342 local gv = c.pingTime - PING_MIDLINE
343 local gr = PING_CEILING - PING_MIDLINE
344 local r = c.pingTime > PING_MIDLINE and 1 or (c.pingTime < PING_FLOOR and 0 or (rv / rr))
345 local g = c.pingTime < PING_MIDLINE and 1 or (c.pingTime > PING_CEILING and 0 or 1-(gv / gr))
346 print(c.pingTime, rv, '/', rr, '=', r)
347 print(c.pingTime, gv, '/', gr, '=', g)
348
349 c.ping:SetText(floor(c.pingTime))
350 c.ping:SetTextColor(r,g,0)
351 c.ping:Show()
352 else
353 c.pingbar:Hide()
354 c.ping:Hide()
355 end
356 if c.downTime then
357 c.downtime:Show()
358 if c.downTime > 1500 then
359 c.downtime:SetText(nil)
360 else
361 c.downtime:SetText(c.downTime)
362 end
363 else
364 c.downtime:Hide()
365 end
366 else
367 if c.nonInterruptible then
368 c.interrupt:Show()
369 else
370 c.interrupt:Hide()
371 end
372 end
373
374 c.icon:SetTexture(texture)
375 c.spelltext:Show()
376 c.spelltext:SetText(spellName)
377
378 c.fill_inverse = c.channeling
379 -- set timers
380 end
381
382 --- Deals with failed/succeeded/interrupted visuals and fading cues
383 -- Fired by one of those events, or cast/channel info is returning nothing
384 function mod:SetState(event)
385 if T.unlocked then
386 return
387 end
388
389 local time = GetTime() * 1000
390 print(' ',self:GetName(), '|cFF44FF00event trigger:|r', event)
391
392 -- We want these to be updating no matter what is happening
393 if TEXTURE_SUFFIX[event] then
394 local cdb = self.db
395 if cdb.foreground_texture then
396 print(' |cFF00AAFFevent|r '..event..', |cFF00AAFFtexture|r ', cdb.foreground_texture)
397 self.foreground:SetVertexColor(unpack(cdb['foreground' .. TEXTURE_SUFFIX[event]] and cdb['foreground' .. TEXTURE_SUFFIX[event]] or cdb.foreground_color))
398 else
399 self.foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[event]] and cdb['foreground' .. TEXTURE_SUFFIX[event]] or cdb.foreground_color))
400 end
401
402 if cdb.background_texture then
403 print(' texture=', cdb.background_texture)
404 self.background:SetVertexColor(unpack(cdb['background' .. TEXTURE_SUFFIX[event]] and cdb['background' .. TEXTURE_SUFFIX[event]] or cdb.background_color))
405 else
406 self.background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[event]] and cdb['background' .. TEXTURE_SUFFIX[event]] or cdb.background_color))
407 end
408 end
409
410 -- are we starting or stopping
411 if event == 'START' or event == 'CHANNEL_START' then
412 print(' |cFF00AAFFStarting display|r ', self.spellName)
413 self:Show()
414 if self.__fade:IsPlaying() then
415 self.__fade:Stop()
416 end
417
418 self:Fade(FADE_IN_TIME, self.alpha)
419 else
420 if event == 'SUCCEEDED' and not self.channeling then
421 print(' |cFF00AAFFsuccess deduced values|r', self.value, 'to', self.duration)
422 self.percent = 1
423 self.value = self.duration
424 end
425
426 -- Actual fading out begins here, anything else is statistical sugar
427 if event == 'STOP' or event == 'CHANNEL_STOP' then
428 print('yeah we done')
429 if self.__fade:IsPlaying() then
430 self.__fade:Stop()
431 end
432 self:Fade(FADE_OUT_TIME, 0)
433 end
434 end
435 end
436
437 --- Animation loop
438 function mod:Update()
439 local time = GetTime() * 1000
440 local u = self.unit
441 local s = self
442
443 -- update vals
444 if s.casting or s.channeling then
445 self.value = s.casting and (time - s.startTime) or (s.endTime - time)
446 self.percent = (time - s.startTime) / self.duration
447 if time > s.endTime then
448 self.value = self.fill_inverse and 0 or s.duration
449 self.percent = self.fill_inverse and 0 or 1
450 self.elapsed = time - s.endTime
451 end
452 --_G.print('Update',' ',self.unit, self.value, self.percent)
453 else
454 self.value = 1000
455 self.percent = 1
456 end
457 self.casttime:SetText(format("%.2f", self.value / 1000))
458
459 self:SetProgress(self.percent)
460 end
461
462 --- Refresh the CastingBar for things like target change, pet despawn, etc.
463 -- Will attempt to flush out any existing cast action data, then re-invoke SpellCastEvent() with as much data can be acquired.
464 -- There is no event data such as spellID directly available, so this is essentially the biggest constraint we have on what
465 -- the castingbar can depend on.
466 function mod:UpdateUnit(unit)
467 local print = function(...) _G.print('Update', ...) end
468 print(cText 'GUID changed:|cFFFFFF99', unit)
469 local c = mod.castbar[unit]
470 if c.channeling or c.casting then
471 print(cText ' was casting a spell, run STOP event and hide completely')
472 c:SetState(c.casting and 'STOP' or 'CHANNEL_STOP')
473 c.channeling = nil
474 c.casting = nil
475 c:Hide()
476 end
477
478 if T.unit[unit].casting then
479 print(cText(' new target is CASTING'))
480 mod:SpellCastEvent('UNIT_SPELLCAST_START', unit)
481 end
482 if T.unit[unit].channeling then
483 print(cText(' new target is CHANNELING'))
484 mod:SpellCastEvent('UNIT_SPELLCAST_CHANNEL_START', unit)
485 end
486
487 T:UNIT_SPELLCAST(unit)-- name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible
488 T:UNIT_CHANNEL(unit) -- name, subText, text, texture, startTime, endTime, isTradeSkill, notInterruptible
489
490 end
491
492 function mod:UpdateInterrupt(e, unit, ...)
493 mod.castbar[unit].nonInterruptible = (e == 'UNIT_SPELLCAST_UNINTERRUPTIBLE') and true or false
494 if unit ~= 'player' and unit ~='pet' then
495
496 end
497 end
498
499 mod.UNIT_PET = function(self, e, unit)
500 if unit == 'player' then
501 self:UpdateUnit('pet')
502 end
503 end
504 mod.PLAYER_TARGET_CHANGED = function(self, ...)
505 _G.print('Main', cPink(self),...)
506 self:UpdateUnit('target')
507 end
508 mod.PLAYER_FOCUS_CHANGED = function(self)
509 self:UpdateUnit('focus')
510 end