Nenue@1: addon, T_ = ... Nenue@1: -- User: Krakyn Nenue@1: -- Created: 12/4/2015 11:17 PM Nenue@1: local T = LibStub("AceAddon-3.0"):GetAddon("Turok") Nenue@1: local TL = 'Tek' Nenue@1: local mod = T:NewModule("Tek") Nenue@1: local print = function(...) Nenue@1: _G.print('Tek', ...) Nenue@1: end Nenue@1: local time = _G.ct Nenue@1: Nenue@1: -- events & units Nenue@1: local TRACKED_UNITS = {'player', 'target', 'focus', 'pet'} Nenue@1: local TRACKED_EVENTS = {'UNIT_SPELLCAST_START', 'UNIT_SPELLCAST_DELAYED', 'UNIT_SPELLCAST_STOP', 'UNIT_SPELLCAST_FAILED', 'UNIT_SPELLCAST_FAILED_QUIET', Nenue@1: 'UNIT_SPELLCAST_INTERRUPTED', 'UNIT_SPELLCAST_SUCCEEDED', 'UNIT_SPELLCAST_CHANNEL_START', 'UNIT_SPELLCAST_CHANNEL_STOP', 'UNIT_SPELLCAST_CHANNEL_UPDATE' } Nenue@1: Nenue@1: local FADE_OUT_TIME, FADE_IN_TIME = 1000, 200 -- animation timings Nenue@1: Nenue@1: -- values MUST be in chronological order Nenue@1: local CHANNEL_START, CAST_START, CAST_INTERRUPTED, CAST_SUCCEEDED, CAST_STOPPED, CAST_FAILED, CHANNEL_STOPPED = 1, 2, 3, 4, 5, 6, 7 Nenue@1: local TEXTURE_SUFFIX = { Nenue@1: [CHANNEL_START] = '_channeling', Nenue@1: [CAST_START] = '_casting', Nenue@1: [CAST_INTERRUPTED] = '_interrupted', Nenue@1: [CAST_SUCCEEDED] = '_finished', Nenue@1: [CAST_STOPPED] = '_failed', Nenue@1: [CAST_FAILED] = '_failed', Nenue@1: [CHANNEL_STOPPED] = '' Nenue@1: } Nenue@1: local inv = T.anchor_inverse Nenue@1: local d = T.direction_coord Nenue@1: local a = T.anchor_direction Nenue@1: local c = {} Nenue@1: -- c[unit]=nil is used by frame update, thus any indexing from a valid frame is implied to be for new casting info Nenue@1: setmetatable(c, { Nenue@1: __index = function(t,k) Nenue@1: t[k] = {} Nenue@1: return t[k] Nenue@1: end Nenue@1: }) Nenue@1: Nenue@1: function mod:OnEnable() Nenue@1: local db = T.db Nenue@1: Nenue@1: db.castbar = { Nenue@1: --T.defaults.castbar = { Nenue@1: width = 300, height = 24, Nenue@1: anchor = 'TOP', parent = 'TkFocusBar', anchorTo = 'BOTTOM', Nenue@1: posX = 0, posY = -2, Nenue@1: foreground_casting = {0.80784313725, 0.87843137254, 0.0156862745, 1}, Nenue@1: background_casting = {0,0,0,0.4}, Nenue@1: background_interrupted = {1,0,0,1}, Nenue@1: background_failed = {0.60784313725, 0.87843137254, 0.0156862745, 1}, Nenue@1: foreground_interrupted = {1, 0.5, 0, 1}, Nenue@1: foreground_failed = {0,0,0,1}, Nenue@1: foreground_finished = {0.60784313725, 0.87843137254, 0.0156862745, 1}, Nenue@1: background_finished = {0.60784313725, 0.87843137254, 0.0156862745, 1}, Nenue@1: foreground_inset = -1, Nenue@1: fill_direction = 'RIGHT', Nenue@1: fill_inverse = false, Nenue@1: glow_texture = "Tooltip-BigBorder", Nenue@1: glow_size = 2, Nenue@1: spark_texture = "Tooltip-BigBorder", Nenue@1: spark_size = 2, Nenue@1: ['player'] = {}, Nenue@1: ['target'] = { Nenue@1: width = 300, height = 24, Nenue@1: anchor = 'BOTTOMLEFT', parent = 'TkplayerCastBar', anchorTo = 'TOPRIGHT', Nenue@1: posX = 4, posY = 4, Nenue@1: }, Nenue@1: ['focus'] = { Nenue@1: width = 300, height = 24, Nenue@1: anchor = 'TOPLEFT', parent = 'TkplayerCastBar', anchorTo='BOTTOMRIGHT', Nenue@1: posX = 2, posY = -2 Nenue@1: }, Nenue@1: ['pet'] = { Nenue@1: width = 300, height = 24, Nenue@1: anchor = 'TOPRIGHT', parent = 'TkFocusBar', anchorTo='TOPLEFT', Nenue@1: posX = -2, posY = 0 Nenue@1: }, Nenue@1: } Nenue@1: Nenue@1: self.casting = c Nenue@1: self.castbar = {} Nenue@1: Nenue@1: Nenue@1: for _, unit in pairs(TRACKED_UNITS) do Nenue@1: local cdb = db.castbar[unit] Nenue@1: print('|cFFFF44FF' .. unit .. '|r castbar creation') Nenue@1: Nenue@1: Nenue@1: -- State info Nenue@1: c[unit] = {} Nenue@1: Nenue@1: -- Set frames Nenue@1: local fn = 'Tk' .. unit .. 'CastBar' Nenue@1: self.castbar[unit] = T:CreateBar(fn, cdb) Nenue@1: local pc = self.castbar[unit] Nenue@1: pc:Hide() Nenue@1: pc:SetAlpha(0) Nenue@1: Nenue@1: T:AddLabel(pc, cdb, 'spelltext') Nenue@1: local casttime = T:AddLabel(pc, cdb, 'casttime') Nenue@1: local latency = T:AddLabel(pc, cdb, 'latency') Nenue@1: pc.foreground:SetWidth(0) Nenue@1: Nenue@1: casttime:SetPoint('RIGHT', pc, 'RIGHT') Nenue@1: casttime:SetJustifyH('RIGHT') Nenue@1: latency:SetPoint('TOPRIGHT', pc.casttime, 'BOTTOMRIGHT') Nenue@1: latency:SetJustifyH('RIGHT') Nenue@1: Nenue@1: pc.transpt = a[cdb.fill_direction] Nenue@1: pc.xtrans = function() return math.min((cdb.width + cdb.foreground_inset) * pc.percent * d[pc.transpt][1], cdb.width) end Nenue@1: pc.ytrans = function() return math.min((cdb.height + cdb.foreground_inset) * pc.percent * d[pc.transpt][2], cdb.height) end Nenue@1: Nenue@1: pc.movement_x = 1 Nenue@1: pc.moving_end = 'LEFT' Nenue@1: Nenue@1: local glow = pc:CreateTexture('glow', 'OVERLAY') Nenue@1: glow:SetTexture(CASTBAR_TAILGLOW) Nenue@1: glow:SetPoint('TOPLEFT', pc, 'TOPLEFT', -5, 5) Nenue@1: glow:SetPoint('BOTTOMRIGHT', pc, 'BOTTOMRIGHT', 5, -5) Nenue@1: pc.glow = glow Nenue@1: Nenue@1: local icon = pc:CreateTexture('icon', 'ARTWORK') Nenue@1: icon:SetPoint('RIGHT', pc, 'LEFT', -2, 0) Nenue@1: icon:SetWidth(pc.foreground:GetHeight()) Nenue@1: icon:SetHeight(pc.foreground:GetHeight()) Nenue@1: icon:SetTexture(0,0,0,1) Nenue@1: pc.icon = icon Nenue@1: Nenue@1: pc.unit = unit Nenue@1: T:Bar_SetUpdateHandler(pc, self.TekUpdate) Nenue@1: Nenue@1: Nenue@1: Nenue@1: --print('created', fn) Nenue@1: end Nenue@1: Nenue@1: -- Casting table events Nenue@1: for i, event in pairs(TRACKED_EVENTS) do Nenue@1: print('listening to |cFF00FFFF' .. event .. '|r') Nenue@1: self:RegisterEvent(event, 'TekEvent') Nenue@1: end Nenue@1: Nenue@1: -- Extra events Nenue@1: self:RegisterEvent('PLAYER_TARGET_CHANGED') Nenue@1: self:RegisterEvent('PLAYER_FOCUS_CHANGED') Nenue@1: Nenue@1: CastingBarFrame:SetScript('OnUpdate', nil) Nenue@1: CastingBarFrame:SetScript('OnEvent', nil) Nenue@1: CastingBarFrame:Hide() Nenue@1: end Nenue@1: Nenue@1: -- Update handler for castbar Nenue@1: function mod:TekUpdate() Nenue@1: local glow = self.glow Nenue@1: local foreground = self.foreground Nenue@1: local background = self.background Nenue@1: local latency = self.latency Nenue@1: local time = GetTime() * 1000 Nenue@1: local spelltext = self.spelltext Nenue@1: local timetext = self.timetext Nenue@1: local cdb = self.db Nenue@1: if mod.casting[self.unit] ~= nil then Nenue@1: Nenue@1: local u = self.unit Nenue@1: local s = mod.casting[u] Nenue@1: local alpha = self:GetAlpha() Nenue@1: Nenue@1: -- is something casting at all? Nenue@1: if s.casting <= CAST_START then Nenue@1: Nenue@1: -- start = true whenever a new spell cast could be active (i.e. target switching) Nenue@1: if s.init then Nenue@1: print('|cFFDD77DD'..u..'|r init_cast (spell='..s.displayName..', startTime='..s.startTime..', endTime='..s.endTime..', channel=',s.channel,')') Nenue@1: print(self:GetName(), self:IsVisible(), self:IsShown()) Nenue@1: Nenue@1: -- update translation point Nenue@1: if s.casting == CHANNEL_START and not cdb.fill_inverse then Nenue@1: self.transpt = inv[a[cdb.fill_direction]] Nenue@1: else Nenue@1: self.transpt = a[cdb.fill_direction] Nenue@1: end Nenue@1: Nenue@1: -- update frame contents Nenue@1: foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] and cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] or cdb.foreground_color)) Nenue@1: background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[s.casting]] and cdb['background' .. TEXTURE_SUFFIX[s.casting]] or cdb.background_color)) Nenue@1: Nenue@1: self.icon:SetTexture(s.texture) Nenue@1: spelltext:SetText(s.displayName) Nenue@1: latency:SetText(s.latency) Nenue@1: self.duration = s.endTime - s.startTime Nenue@1: self.fadeIn = s.startTime + (1-alpha) * FADE_IN_TIME Nenue@1: self.fadeOut = s.endTime + FADE_OUT_TIME -- needs to exist for target changes Nenue@1: Nenue@1: print(s.startTime, self.fadeIn, self.fadeIn - s.startTime) Nenue@1: -- clear 'start' Nenue@1: s.init = nil Nenue@1: end Nenue@1: Nenue@1: -- if we're checking this and start was never flipped, then something happened Nenue@1: if time <= self.fadeIn then Nenue@1: alpha = 1- ((self.fadeIn - time) / FADE_IN_TIME) Nenue@1: self:SetAlpha(alpha) Nenue@1: elseif alpha ~= 1 then Nenue@1: self:SetAlpha(1) Nenue@1: end Nenue@1: self.value = time - s.startTime Nenue@1: self.percent = self.value / self.duration Nenue@1: self.casttime:SetText(format("%.1f", self.value / 1000)) Nenue@1: Nenue@1: -- s.casting is nil when the spellcast has finished Nenue@1: else Nenue@1: -- something set a term flag Nenue@1: if s.casting < CHANNEL_STOPPED and s.fade then Nenue@1: print(TL, '|cFFDD77DD'..u..'|r init_fadeout (spell='..s.displayName..', startTime='..s.startTime..', endTime='..s.endTime..', channel=',s.channel,')') Nenue@1: self.fadeOut = time + FADE_OUT_TIME Nenue@1: foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] and cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] or cdb.foreground_color)) Nenue@1: background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[s.casting]] and cdb['background' .. TEXTURE_SUFFIX[s.casting]] or cdb.background_color)) Nenue@1: s.fade = nil Nenue@1: self.value = self.duration Nenue@1: self.percent = 1 Nenue@1: end Nenue@1: Nenue@1: if time < self.fadeOut then Nenue@1: alpha = (self.fadeOut - time) / FADE_OUT_TIME Nenue@1: self:SetAlpha(alpha) Nenue@1: else Nenue@1: alpha = 0 Nenue@1: self:Hide() Nenue@1: self:SetAlpha(alpha) Nenue@1: self.casttime:SetText(nil) Nenue@1: self.spelltext:SetText(nil) Nenue@1: self.latency:SetText(nil) Nenue@1: self.debugged = false Nenue@1: self.percent = 0 Nenue@1: mod.casting[self.unit] = nil Nenue@1: print('|cFFDD77DD'..u..'|r work complete, hiding...') Nenue@1: end Nenue@1: -- hide and wait until we're pulled out again Nenue@1: end Nenue@1: self.foreground:SetPoint('RIGHT', self, self.transpt, self.xtrans(), self.ytrans()) Nenue@1: end Nenue@1: end Nenue@1: Nenue@1: Nenue@1: -- event stub, filters out unwanted cast events Nenue@1: function mod:TekEvent(e, unit, ...) Nenue@1: if not self.castbar[unit] then Nenue@1: return Nenue@1: end Nenue@1: if not self[e] then Nenue@1: error('No method signature for event ' .. tostring(e)) Nenue@1: end Nenue@1: print('popped |cFF00FFFF' .. e .. '|r: ', unit, ...) Nenue@1: self[e](self, e, unit, ...) Nenue@1: end Nenue@1: Nenue@1: function mod:PLAYER_TARGET_CHANGED (e, cause) Nenue@1: print(e) Nenue@1: self:UpdateUnit('target') Nenue@1: end Nenue@1: function mod:PLAYER_FOCUS_CHANGED (e, cause) Nenue@1: print(e) Nenue@1: self:UpdateUnit('focus') Nenue@1: end Nenue@1: function mod:UpdateUnit(unit) Nenue@1: if UnitCastingInfo(unit) then Nenue@1: mod:UNIT_SPELLCAST_START('UNIT_SPELLCAST_START', unit) Nenue@1: elseif UnitChannelInfo(unit) then Nenue@1: mod:UNIT_SPELLCAST_CHANNEL_START('UNIT_SPELLCAST_CHANNEL_START', unit) Nenue@1: else Nenue@1: mod.castbar[unit]:Hide() Nenue@1: end Nenue@1: end Nenue@1: Nenue@1: -- Spell event handlers Nenue@1: function mod:UNIT_SPELLCAST_SENT(e, unit, spellname, rank, target) Nenue@1: -- triggered an action buttton tied to a cast Nenue@1: c[unit].sentTime = math.floor(GetTime() * 1000) Nenue@1: print('|cFF44FF44',e,':|r', unit, spellname, c[unit].sentTime) Nenue@1: c[unit].spell = spellname Nenue@1: c[unit].rank = rank Nenue@1: c[unit].target = target Nenue@1: end Nenue@1: Nenue@1: function mod:UNIT_SPELLCAST_START(e, unit) -- Server says: someone started casting Nenue@1: local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = UnitCastingInfo(unit) Nenue@1: print('casting['..unit..'] state updated (=start): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime) Nenue@1: Nenue@1: c[unit].castID = castID Nenue@1: c[unit].spell = spellname Nenue@1: c[unit].rank = rank Nenue@1: c[unit].displayName = displayName Nenue@1: c[unit].texture = texture Nenue@1: c[unit].startTime = startTime Nenue@1: c[unit].endTime = endTime Nenue@1: c[unit].nonInterruptible = nonInterruptible Nenue@1: c[unit].isTradeSkill = isTradeSkill Nenue@1: if c[unit].sentTime then Nenue@1: c[unit].latency = c[unit].startTime - c[unit].sentTime Nenue@1: end Nenue@1: Nenue@1: -- set state and show Nenue@1: c[unit].casting = CAST_START Nenue@1: c[unit].init = true Nenue@1: self.castbar[unit]:Show() Nenue@1: end Nenue@1: Nenue@1: function mod:UNIT_SPELLCAST_DELAYED(e, unit, spellname, rank, castID, target) -- Server says: they're still casting but it'll take longer Nenue@1: local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = UnitCastingInfo(unit) Nenue@1: print('casting['..unit..'] state updated (=delayed): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime) Nenue@1: c[unit].castID = castID Nenue@1: c[unit].channel = false Nenue@1: c[unit].startTime = startTime Nenue@1: c[unit].endTime = endTime Nenue@1: -- just update timing data, frame script will adjust Nenue@1: end Nenue@1: Nenue@1: -- set exit states Nenue@1: function mod:UNIT_SPELLCAST_STOP(e, unit, spellname, rank, castID, target) -- Server says: someone stopped casting for some reason Nenue@1: --c[unit].casting = CAST_STOPPED Nenue@1: end Nenue@1: Nenue@1: function mod:UNIT_SPELLCAST_INTERRUPTED(e, unit, spellname, rank, castID, target) -- Server says: someone got interrupted Nenue@1: c[unit].casting = CAST_INTERRUPTED Nenue@1: c[unit].fade = true Nenue@1: end Nenue@1: Nenue@1: function mod:UNIT_SPELLCAST_SUCCEEDED(e, unit, spellname, rank, castID, target) -- Server says: they stopped because they're done Nenue@1: Nenue@1: if c[unit].castID == castID and c[unit].casting ~= CHANNEL_START then Nenue@1: c[unit].casting = CAST_SUCCEEDED Nenue@1: c[unit].fade = true Nenue@1: end Nenue@1: end Nenue@1: Nenue@1: function mod:UNIT_SPELLCAST_FAILED(e, unit, spellname, rank, castID, target) -- Server says: someone tried to cast something but they weren't allowed Nenue@1: if c[unit].castID ~= castID then -- can fire from keybind spam Nenue@1: return Nenue@1: end Nenue@1: c[unit].casting = CAST_FAILED Nenue@1: c[unit].fade = true Nenue@1: end Nenue@1: Nenue@1: function mod:UNIT_SPELLCAST_FAILED_QUIET(e, unit, spellname, rank, castID, target) -- Server says: someone tried to cast something but they weren't allowed Nenue@1: if c[unit].castID == castID and c[unit].casting == CAST_START then Nenue@1: c[unit].casting = CAST_FAILED Nenue@1: end Nenue@1: end Nenue@1: Nenue@1: function mod:UNIT_SPELLCAST_INTERRUPTIBLE(e, unit) Nenue@1: c[unit].notInterruptible = false Nenue@1: end Nenue@1: function mod:UNIT_SPELLCAST_NOT_INTERRUPTIBLE(e, unit) Nenue@1: c[unit].notInterruptible = true Nenue@1: end Nenue@1: Nenue@1: function mod:UNIT_SPELLCAST_CHANNEL_START(e, unit, spellname, rank, castID, spellID) Nenue@1: local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, nonInterruptible = UnitChannelInfo(unit) Nenue@1: displayName = spellname -- channels sometimes just appear as 'Channeling' according to Quartz author Nenue@1: print('casting['..unit..'] state updated (=start, channel): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime) Nenue@1: c[unit].casting = CHANNEL_START Nenue@1: c[unit].spellname = spellname Nenue@1: c[unit].rank = rank Nenue@1: c[unit].displayName = displayName Nenue@1: c[unit].texture = texture Nenue@1: c[unit].startTime = startTime Nenue@1: c[unit].endTime = endTime Nenue@1: c[unit].nonInterruptible = nonInterruptible Nenue@1: c[unit].isTradeSkill = isTradeSkill Nenue@1: c[unit].init = true Nenue@1: mod.castbar[unit]:Show() Nenue@1: end -- start up Nenue@1: function mod:UNIT_SPELLCAST_CHANNEL_STOP(e, unit, spellname, rank, castID, spellID) Nenue@1: c[unit].casting = CHANNEL_STOPPED Nenue@1: end Nenue@1: function mod:UNIT_SPELLCAST_CHANNEL_UPDATE(e, unit, spellname, rank, castID, spellID) Nenue@1: spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, nonInterruptible = UnitChannelInfo(unit) Nenue@1: displayName = spellname -- channels sometimes just appear as 'Channeling' according to Quartz author Nenue@1: c[unit].channel = true Nenue@1: end -- recalc Nenue@1: Nenue@1: Nenue@1: Nenue@1: