Mercurial > wow > turok
view Turok/Modules/Combat/Castbar.lua @ 9:9400a0ff8540
Ugh
Timer:
- container update directionality
- talent update iterates over a non-volatile table to carry out updates
- index management steps organized
- talentRow status implemented, returns the spell associated with the talent chosen from that row
CombatLog:
- sort out font controls and unbork arguments
author | Nenue |
---|---|
date | Sun, 21 Feb 2016 13:08:30 -0500 |
parents | a9b8b0866ece |
children |
line wrap: on
line source
--- 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, _G = Turok, _G 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 uprint = function(...) if _G.Devian and _G.DevianDB.workspace ~= 1 then _G.print('Update', ...) end end local mprint = function(...) if _G.Devian and _G.DevianDB.workspace ~= 1 then _G.print('Main', ...) 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 uprint(' ',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(...) uprint(...) 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, ...) mprint(cPink(self),...) self:UpdateUnit('target') end mod.PLAYER_FOCUS_CHANGED = function(self) self:UpdateUnit('focus') end