Mercurial > wow > turok
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Turok/Modules/Combat/Castbar.lua Sun Feb 21 08:32:53 2016 -0500 @@ -0,0 +1,510 @@ +--- Turok +-- Castbar.lua +-- Created: 12/4/2015 11:17 PM +-- @file-author@ +-- @project-revision@ @project-hash@ +-- @file-revision@ @file-hash@ +-- Deals with casting events and works the castingbar interface +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 +local GetTime, UnitCastingInfo, UnitChannelInfo, GetSpellInfo, GetSpellTabInfo = GetTime, UnitCastingInfo, UnitChannelInfo, GetSpellInfo, GetSpellTabInfo +local floor = math.floor +local T = Turok +local db +local PING_CEILING = 190 -- transpacific +local PING_MIDLINE = 100 +local PING_FLOOR = 10 -- transcontinental +--@debug +local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool +local print = function(...) + if _G.Devian and _G.DevianDB.workspace ~= 1 then + _G.print('Tek', ...) + end +end + +local EVENT_COLOR = '|cFF44FF44' +local EVENT_COLOR2 = '|cFF88FF88' +local DATA_COLOR = '|cFFDD77DD' +--@end-debug@ + +local mod = T:NewModule("Tek") +function mod:OnInitialize() + self.db = T.db.tek + self.castbar = {} + self.UNIT_SPELLCAST_SENT = self.SpellCastRequest + self.UNIT_SPELLCAST_START = self.SpellCastEvent + self.UNIT_SPELLCAST_DELAYED = self.SpellCastEvent + self.UNIT_SPELLCAST_SUCCEEDED = self.SpellCastEvent + self.UNIT_SPELLCAST_STOP = self.SpellCastEvent + self.UNIT_SPELLCAST_FAILED = self.SpellCastEvent + self.UNIT_SPELLCAST_INTERRUPTED = self.SpellCastEvent + self.UNIT_SPELLCAST_CHANNEL_START = self.SpellCastEvent + self.UNIT_SPELLCAST_CHANNEL_STOP = self.SpellCastEvent + self.UNIT_SPELLCAST_CHANNEL_UPDATE = self.SpellCastEvent + self.UNIT_SPELLCAST_INTERRUPTIBLE = self.SpellCastEvent + self.UNIT_SPELLCAST_UNINTERRUPTIBLE = self.SpellCastEvent +end + +--- events & units +local TRACKED_UNITS = {'player', 'target', 'focus', 'pet'} +local FADE_OUT_TIME, FADE_IN_TIME = 1, .2 + +local TEXTURE_SUFFIX = { + ['CHANNEL_START'] = '_channeling', + ['CHANNEL_UPDATE'] = '_channeling', + ['CHANNEL_STOPPED'] = '_channeling', + ['START'] = '_casting', + ['INTERRUPTED'] = '_interrupted', + ['SUCCEEDED'] = '_finished', +} + +function mod:OnEnable() + db = self.db + -- setup castingbar frames + local c = self.castbar + for _, unit in pairs(TRACKED_UNITS) do + local cdb = db[unit] + --@debug@ + print(DATA_COLOR .. unit .. '|r castbar creation')--@end-debug@ + + -- Set frames + local fn = 'Tek' .. unit .. 'CastBar' + c[unit] = CreateFrame('Frame', fn, _G[cdb.parent], 'TurokCastingBar') + + local pc = c[unit] + T.SetFrameLayout(pc, cdb) + T.SetTextureLayout(pc.icon, cdb.icon or db.icon) + T.SetStatusTextures(pc, cdb.statusbar or db.statusbar) + + if unit == 'player' then + T.SetFontLayout(pc.ping, db.ping) + T.SetFontLayout(pc.downtime, db.downtime) + pc.pingbar:ClearAllPoints() + pc.pingbar:SetPoint('TOPRIGHT', pc.background, 'TOPRIGHT', 0, 0) + pc.pingbar:SetHeight(pc.background:GetHeight()) + else + if pc.interrupt then + pc.interrupt:SetSize(pc.icon.size*3, pc.icon.size* 3) + end + + end + + T.SetFontLayout(pc.casttime, cdb.casttime or db.casttime) + T.SetFontLayout(pc.spelltext, cdb.spelltext or db.spelltext) + + pc:SetAlpha(0) + pc.last = nil + pc.sent = {} + pc.unit = unit + pc.SetSpell = self.SetSpell + pc.SetState = self.SetState + pc.Update = self.Update + end + + + -- kill default casting bar + -- T.cbscripts = {CastingBarFrame:GetScript('OnUpdate'), CastingBarFrame:GetScript('OnEvent')} + CastingBarFrame:SetScript('OnUpdate', nil) + CastingBarFrame:SetScript('OnEvent', nil) + CastingBarFrame:Hide() + PetCastingBarFrame:SetScript('OnUpdate', nil) + PetCastingBarFrame:SetScript('OnEvent', nil) + PetCastingBarFrame:Hide() +end + +function mod:UpdateLocked() + for k, v in pairs(self.castbar) do + if T.unlocked then + local name, texture, offset, numSpells = GetSpellTabInfo(T.specPage) + v.value = offset + v.duration = numSpells + v.spelltext:SetText(k) + v.icon:SetTexture(texture) + v:SetAlpha(1) + v:Show() + v:EnableMouse(true) + v:SetMovable(true) + v:RegisterForDrag("LeftButton") + v:SetScript('OnDragStart', function(self) self:StartMoving() end) + v:SetScript('OnDragStop', function(self) self:StopMovingOrSizing() end) + else + + v:SetScript('OnDragStart', nil) + v:SetScript('OnDragStop', nil) + v:EnableMouse(false) + v:SetMovable(false) + + print('Saving bar coordinates post unlock') + + db[k].x = v:GetLeft() + db[k].y = v:GetTop() + db[k].anchor = 'BOTTOMLEFT' + db[k].anchorTo = 'BOTTOMLEFT' + db[k].parent = 'UIParent' + end + end +end + +--- Store spell requests for use in tracking lag time +function mod:SpellCastRequest(e, unit, spellName, rank, target, castID) + print('|cFFFF4400Request sent:|r ', spellName, castID, target) + self.castbar.player.sent[castID] = { + spellName = spellName, + castID = castID, + sendTime = GetTime()*1000, + } +end + +--- Handle events pertaining to a cast bar +function mod:SpellCastEvent(event, unit, ...) + if not self.castbar[unit] then + return + end + + local u = T.unit[unit] + local c = self.castbar[unit] + + --- doubling as an invocation source test + local spellName, rank, castID, spellID = ... + + local channelinfo = T.unit[unit].channeling + local castinginfo = T.unit[unit].casting + local sendq = c.sent + local castd = c.last + local timestamp = GetTime()*1000 + + --todo: remove truncation + local e = event:match("UNIT_SPELLCAST_([%a_]+)") + print(GetTime(), '|cFFFF8747'..e..(strpad(' ',12-#e))..'|r |cFFDDFF00#'..tostring(castID)..'|r', spellName, spellID) + + c.channeling = channelinfo and channelinfo[1] or nil + c.casting = castinginfo and castinginfo[1] or nil + if c.channeling then + -- {name, subText, text, texture, startTime, endTime, isTradeSkill, notInterruptible} + c.startTime = channelinfo[5] + c.endTime = channelinfo[6] + c.nonInterruptible = channelinfo[8] + elseif c.casting then + --name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible + c.startTime = castinginfo[5] + c.endTime = castinginfo[6] + c.castID = castinginfo[8] + c.nonInterruptible = castinginfo[9] + else + print(' |cFFFF7700internal data was nil|r') + end + + --- process the "event" + local setSpell, setState + if e == 'START' then + setSpell = true + setState = true + + -- player data calculations + if unit == 'player' then + if sendq[castID] and sendq[castID].spellName == spellName then + local sent = sendq[castID] + print(' |cFF00AAFFmatched:|r', sent.spellName, castID) + c.pingTime = c.startTime - sent.sendTime + print(' ping:', c.pingTime) + for cid, cdata in pairs(sendq) do + if cdata.sendTime <= sent.sendTime then + --print(' forgetting #'..cdata.sendTime, cid, cdata.spellName) + sendq[cid] = nil + end + end + + if castd ~= nil then + print(' downtime calc hit') + c.downTime = timestamp - castd.endTime + castd = nil + else + c.downTime = nil + end + c.castID = castID + end + end + elseif e == 'CHANNEL_START' or e == 'CHANNEL_UPDATE' then + setSpell = true + setState = true + c.event = e + + elseif e == 'STOP' then + setState = true + c.casting = nil + castd = { + spellName = spellName, + spellID = spellID, + castID = castID, + endTime = timestamp, + } + print(' |cFFFFFF00found', timestamp, spellName) + + elseif e == 'CHANNEL_STOP' then + setState = true + c.channeling = nil + castd = { + spellName = spellName, + spellID = spellID, + castID = castID, + endTime = timestamp, + } + print(' |cFFFF0088found', timestamp, spellName) + + elseif e == 'SUCCEEDED' then + if c.spellName == spellName and not c.channeling then + setState = true + print(' |cFFFF4400',c.spellName,'=',spellName,' pushing event', e) + end + -- if there are 'send' args when this fires, it means it's an instant cast + if sendq[castID] and sendq[castID].spellName == spellName then + castd = { + spellName = spellName, + spellID = spellID, + castID = castID, + endTime = timestamp, + } + print(' |cFFFFAA00found', timestamp, spellName) + end + + elseif e =='FAILED' then + if spellName == c.spellName and not c.channeling then + setState = true + end + + elseif e == 'INTERRUPTED' then + setState = true + + end + + + + + if setSpell then + print(' |cFF44FF00setting spell', spellName) + end + if setState then + print(' |cFFFF4400pushing event', e) + end + + if setSpell then + c:SetSpell(e) + end + if setState then + c:SetState(e) + end +end + + +--- Sets all the static elements of the casting bar, such as config values, icon texture, and name +function mod.SetSpell(c, event) + local _ + local time, alpha = GetTime(), c:GetAlpha() + local u, cdb = T.unit[c.unit], c.db + + c.stopped = nil + c.stopping = nil + print('New spell:', c.spellName) + --- use only internal info + local data = (event == 'CHANNEL_START' or event == 'CHANNEL_UPDATE' or event == 'CHANNEL_STOP') and u.channeling or u.casting + local spellName, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = unpack(data) + + + if spellName == nil then + print(" Can't do arithmetic, no data.") + else + print(cText(" Arithmetic vars:"), cNum(startTime), cNum(endTime)) + end + + c.spellName = spellName + c.castID = castID or 0 + c.rank = rank + c.displayName = displayName + c.texture = texture + c.nonInterruptible = nonInterruptible + c.isTradeSkill = isTradeSkill + c.endTime = endTime or 0 + c.startTime = startTime or 0 + c.duration = c.endTime - c.startTime + + if c.unit == 'player' then + if c.pingTime then + print(' ping',c.pingTime, endTime, startTime, c.duration) + local draw_dist = (c.pingTime / c.duration) * (c.fill_width) + if draw_dist > c.fill_width then + draw_dist = c.fill_width + end + print('SET AND SHOW PING ', c.pingTime,'on', c.pingbar:GetName()) + c.pingbar:SetWidth(draw_dist) + c.pingbar:Show() + + local rv = c.pingTime - PING_FLOOR + local rr = PING_MIDLINE - PING_FLOOR + local gv = c.pingTime - PING_MIDLINE + local gr = PING_CEILING - PING_MIDLINE + local r = c.pingTime > PING_MIDLINE and 1 or (c.pingTime < PING_FLOOR and 0 or (rv / rr)) + local g = c.pingTime < PING_MIDLINE and 1 or (c.pingTime > PING_CEILING and 0 or 1-(gv / gr)) + print(c.pingTime, rv, '/', rr, '=', r) + print(c.pingTime, gv, '/', gr, '=', g) + + c.ping:SetText(floor(c.pingTime)) + c.ping:SetTextColor(r,g,0) + c.ping:Show() + else + c.pingbar:Hide() + c.ping:Hide() + end + if c.downTime then + c.downtime:Show() + if c.downTime > 1500 then + c.downtime:SetText(nil) + else + c.downtime:SetText(c.downTime) + end + else + c.downtime:Hide() + end + else + if c.nonInterruptible then + c.interrupt:Show() + else + c.interrupt:Hide() + end + end + + c.icon:SetTexture(texture) + c.spelltext:Show() + c.spelltext:SetText(spellName) + + c.fill_inverse = c.channeling + -- set timers +end + +--- Deals with failed/succeeded/interrupted visuals and fading cues +-- Fired by one of those events, or cast/channel info is returning nothing +function mod:SetState(event) + if T.unlocked then + return + end + + local time = GetTime() * 1000 + print(' ',self:GetName(), '|cFF44FF00event trigger:|r', event) + + -- We want these to be updating no matter what is happening + if TEXTURE_SUFFIX[event] then + local cdb = self.db + if cdb.foreground_texture then + print(' |cFF00AAFFevent|r '..event..', |cFF00AAFFtexture|r ', cdb.foreground_texture) + self.foreground:SetVertexColor(unpack(cdb['foreground' .. TEXTURE_SUFFIX[event]] and cdb['foreground' .. TEXTURE_SUFFIX[event]] or cdb.foreground_color)) + else + self.foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[event]] and cdb['foreground' .. TEXTURE_SUFFIX[event]] or cdb.foreground_color)) + end + + if cdb.background_texture then + print(' texture=', cdb.background_texture) + self.background:SetVertexColor(unpack(cdb['background' .. TEXTURE_SUFFIX[event]] and cdb['background' .. TEXTURE_SUFFIX[event]] or cdb.background_color)) + else + self.background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[event]] and cdb['background' .. TEXTURE_SUFFIX[event]] or cdb.background_color)) + end + end + + -- are we starting or stopping + if event == 'START' or event == 'CHANNEL_START' then + print(' |cFF00AAFFStarting display|r ', self.spellName) + self:Show() + if self.__fade:IsPlaying() then + self.__fade:Stop() + end + + self:Fade(FADE_IN_TIME, self.alpha) + else + if event == 'SUCCEEDED' and not self.channeling then + print(' |cFF00AAFFsuccess deduced values|r', self.value, 'to', self.duration) + self.percent = 1 + self.value = self.duration + end + + -- Actual fading out begins here, anything else is statistical sugar + if event == 'STOP' or event == 'CHANNEL_STOP' then + print('yeah we done') + if self.__fade:IsPlaying() then + self.__fade:Stop() + end + self:Fade(FADE_OUT_TIME, 0) + end + end +end + +--- Animation loop +function mod:Update() + local time = GetTime() * 1000 + local u = self.unit + local s = self + + -- update vals + if s.casting or s.channeling then + self.value = s.casting and (time - s.startTime) or (s.endTime - time) + self.percent = (time - s.startTime) / self.duration + if time > s.endTime then + self.value = self.fill_inverse and 0 or s.duration + self.percent = self.fill_inverse and 0 or 1 + self.elapsed = time - s.endTime + end + --_G.print('Update',' ',self.unit, self.value, self.percent) + else + self.value = 1000 + self.percent = 1 + end + self.casttime:SetText(format("%.2f", self.value / 1000)) + + self:SetProgress(self.percent) +end + +--- Refresh the CastingBar for things like target change, pet despawn, etc. +-- Will attempt to flush out any existing cast action data, then re-invoke SpellCastEvent() with as much data can be acquired. +-- There is no event data such as spellID directly available, so this is essentially the biggest constraint we have on what +-- the castingbar can depend on. +function mod:UpdateUnit(unit) + local print = function(...) _G.print('Update', ...) end + print(cText 'GUID changed:|cFFFFFF99', unit) + local c = mod.castbar[unit] + if c.channeling or c.casting then + print(cText ' was casting a spell, run STOP event and hide completely') + c:SetState(c.casting and 'STOP' or 'CHANNEL_STOP') + c.channeling = nil + c.casting = nil + c:Hide() + end + + if T.unit[unit].casting then + print(cText(' new target is CASTING')) + mod:SpellCastEvent('UNIT_SPELLCAST_START', unit) + end + if T.unit[unit].channeling then + print(cText(' new target is CHANNELING')) + mod:SpellCastEvent('UNIT_SPELLCAST_CHANNEL_START', unit) + end + + T:UNIT_SPELLCAST(unit)-- name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible + T:UNIT_CHANNEL(unit) -- name, subText, text, texture, startTime, endTime, isTradeSkill, notInterruptible + +end + +function mod:UpdateInterrupt(e, unit, ...) + mod.castbar[unit].nonInterruptible = (e == 'UNIT_SPELLCAST_UNINTERRUPTIBLE') and true or false + if unit ~= 'player' and unit ~='pet' then + + end +end + +mod.UNIT_PET = function(self, e, unit) + if unit == 'player' then + self:UpdateUnit('pet') + end +end +mod.PLAYER_TARGET_CHANGED = function(self, ...) + _G.print('Main', cPink(self),...) + self:UpdateUnit('target') +end +mod.PLAYER_FOCUS_CHANGED = function(self) + self:UpdateUnit('focus') +end \ No newline at end of file