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