annotate Tek.lua @ 5:8a9a6637f082

focusbar data collection tracking db info declarations to main chunk
author Nenue
date Tue, 15 Dec 2015 10:10:22 -0500
parents 8ace773d6bfc
children
rev   line source
Nenue@1 1 addon, T_ = ...
Nenue@1 2 -- User: Krakyn
Nenue@1 3 -- Created: 12/4/2015 11:17 PM
Nenue@1 4 local T = LibStub("AceAddon-3.0"):GetAddon("Turok")
Nenue@4 5 local db = T.db
Nenue@1 6 local TL = 'Tek'
Nenue@1 7 local mod = T:NewModule("Tek")
Nenue@1 8 local print = function(...)
Nenue@1 9 _G.print('Tek', ...)
Nenue@1 10 end
Nenue@1 11 local time = _G.ct
Nenue@1 12
Nenue@1 13 -- events & units
Nenue@1 14 local TRACKED_UNITS = {'player', 'target', 'focus', 'pet'}
Nenue@1 15 local TRACKED_EVENTS = {'UNIT_SPELLCAST_START', 'UNIT_SPELLCAST_DELAYED', 'UNIT_SPELLCAST_STOP', 'UNIT_SPELLCAST_FAILED', 'UNIT_SPELLCAST_FAILED_QUIET',
Nenue@1 16 'UNIT_SPELLCAST_INTERRUPTED', 'UNIT_SPELLCAST_SUCCEEDED', 'UNIT_SPELLCAST_CHANNEL_START', 'UNIT_SPELLCAST_CHANNEL_STOP', 'UNIT_SPELLCAST_CHANNEL_UPDATE' }
Nenue@1 17
Nenue@4 18 -- animations are a little different here
Nenue@4 19 local FADE_OUT_TIME, FADE_IN_TIME = 1000, 200
Nenue@1 20
Nenue@1 21 -- values MUST be in chronological order
Nenue@1 22 local CHANNEL_START, CAST_START, CAST_INTERRUPTED, CAST_SUCCEEDED, CAST_STOPPED, CAST_FAILED, CHANNEL_STOPPED = 1, 2, 3, 4, 5, 6, 7
Nenue@1 23 local TEXTURE_SUFFIX = {
Nenue@1 24 [CHANNEL_START] = '_channeling',
Nenue@1 25 [CAST_START] = '_casting',
Nenue@1 26 [CAST_INTERRUPTED] = '_interrupted',
Nenue@1 27 [CAST_SUCCEEDED] = '_finished',
Nenue@1 28 [CAST_STOPPED] = '_failed',
Nenue@1 29 [CAST_FAILED] = '_failed',
Nenue@1 30 [CHANNEL_STOPPED] = ''
Nenue@1 31 }
Nenue@4 32
Nenue@4 33 -- defaults
Nenue@4 34 db.castbar = {
Nenue@4 35 --T.defaults.castbar = {
Nenue@4 36 width = 300, height = 24,
Nenue@4 37 anchor = 'CENTER', parent = 'UIParent', anchorTo = 'CENTER',
Nenue@4 38 posX = 0, posY = 0,
Nenue@4 39 foreground_casting = {0.80784313725, 0.87843137254, 0.0156862745, 1},
Nenue@4 40 background_casting = {0,0,0,0.4},
Nenue@4 41 background_interrupted = {1,0,0,1},
Nenue@4 42 background_failed = {0.60784313725, 0.87843137254, 0.0156862745, 1},
Nenue@4 43 foreground_interrupted = {1, 0.5, 0, 1},
Nenue@4 44 foreground_failed = {0,0,0,1},
Nenue@4 45 foreground_finished = {0.60784313725, 0.87843137254, 0.0156862745, 1},
Nenue@4 46 background_finished = {0.60784313725, 0.87843137254, 0.0156862745, 1},
Nenue@4 47 foreground_inset = -1,
Nenue@4 48 fill_direction = 'RIGHT',
Nenue@4 49 fill_inverse = false,
Nenue@4 50 glow_texture = "Tooltip-BigBorder",
Nenue@4 51 glow_size = 2,
Nenue@4 52 spark_texture = "Tooltip-BigBorder",
Nenue@4 53 spark_size = 2,
Nenue@4 54 ['player'] = {
Nenue@4 55 width = 400, height = 36,
Nenue@4 56 anchor = 'TOP', parent = 'TkFocusBar', anchorTo = 'BOTTOM',
Nenue@4 57 posX = 0, posY = 2,
Nenue@4 58 },
Nenue@4 59 ['target'] = {
Nenue@4 60 width = 400, height = 36,
Nenue@4 61 anchor = 'BOTTOMLEFT', parent = 'TkplayerCastBar', anchorTo = 'TOPRIGHT',
Nenue@4 62 posX = -30, posY = 4,
Nenue@4 63 },
Nenue@4 64 ['focus'] = {
Nenue@4 65 width = 200, height = 36,
Nenue@4 66 anchor = 'TOPLEFT', parent = 'TkplayerCastBar', anchorTo='BOTTOMRIGHT',
Nenue@4 67 posX = 2, posY = -2
Nenue@4 68 },
Nenue@4 69 ['pet'] = {
Nenue@4 70 width = 300, height = 24,
Nenue@4 71 anchor = 'TOPRIGHT', parent = 'TkFocusBar', anchorTo='TOPLEFT',
Nenue@4 72 posX = -2, posY = 0
Nenue@4 73 },
Nenue@4 74 }
Nenue@4 75
Nenue@1 76 local inv = T.anchor_inverse
Nenue@1 77 local d = T.direction_coord
Nenue@1 78 local a = T.anchor_direction
Nenue@1 79 local c = {}
Nenue@1 80 -- 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 81 setmetatable(c, {
Nenue@1 82 __index = function(t,k)
Nenue@1 83 t[k] = {}
Nenue@1 84 return t[k]
Nenue@1 85 end
Nenue@1 86 })
Nenue@1 87
Nenue@1 88 function mod:OnEnable()
Nenue@1 89 local db = T.db
Nenue@1 90
Nenue@1 91 self.casting = c
Nenue@1 92 self.castbar = {}
Nenue@1 93
Nenue@1 94
Nenue@1 95 for _, unit in pairs(TRACKED_UNITS) do
Nenue@1 96 local cdb = db.castbar[unit]
Nenue@1 97 print('|cFFFF44FF' .. unit .. '|r castbar creation')
Nenue@1 98
Nenue@1 99
Nenue@1 100 -- State info
Nenue@1 101 c[unit] = {}
Nenue@1 102
Nenue@1 103 -- Set frames
Nenue@1 104 local fn = 'Tk' .. unit .. 'CastBar'
Nenue@1 105 self.castbar[unit] = T:CreateBar(fn, cdb)
Nenue@1 106 local pc = self.castbar[unit]
Nenue@1 107 pc:Hide()
Nenue@1 108 pc:SetAlpha(0)
Nenue@1 109
Nenue@1 110 T:AddLabel(pc, cdb, 'spelltext')
Nenue@1 111 local casttime = T:AddLabel(pc, cdb, 'casttime')
Nenue@1 112 pc.foreground:SetWidth(0)
Nenue@1 113
Nenue@1 114 casttime:SetPoint('RIGHT', pc, 'RIGHT')
Nenue@1 115 casttime:SetJustifyH('RIGHT')
Nenue@4 116
Nenue@4 117 if unit == 'player' then
Nenue@4 118 local lb = pc:CreateTexture('latency', 'OVERLAY')
Nenue@4 119 local lt = T:AddLabel(pc, cdb, 'latency')
Nenue@4 120 lt:SetPoint('TOPRIGHT', pc.casttime, 'BOTTOMRIGHT')
Nenue@4 121 lt:SetJustifyH('RIGHT')
Nenue@4 122 pc.latency = lb
Nenue@4 123 lb.text = lt
Nenue@4 124 end
Nenue@1 125
Nenue@1 126 pc.transpt = a[cdb.fill_direction]
Nenue@1 127 pc.xtrans = function() return math.min((cdb.width + cdb.foreground_inset) * pc.percent * d[pc.transpt][1], cdb.width) end
Nenue@1 128 pc.ytrans = function() return math.min((cdb.height + cdb.foreground_inset) * pc.percent * d[pc.transpt][2], cdb.height) end
Nenue@1 129
Nenue@1 130 pc.movement_x = 1
Nenue@1 131 pc.moving_end = 'LEFT'
Nenue@1 132
Nenue@1 133 local glow = pc:CreateTexture('glow', 'OVERLAY')
Nenue@1 134 glow:SetTexture(CASTBAR_TAILGLOW)
Nenue@1 135 glow:SetPoint('TOPLEFT', pc, 'TOPLEFT', -5, 5)
Nenue@1 136 glow:SetPoint('BOTTOMRIGHT', pc, 'BOTTOMRIGHT', 5, -5)
Nenue@1 137 pc.glow = glow
Nenue@1 138
Nenue@1 139 local icon = pc:CreateTexture('icon', 'ARTWORK')
Nenue@1 140 icon:SetPoint('RIGHT', pc, 'LEFT', -2, 0)
Nenue@1 141 icon:SetWidth(pc.foreground:GetHeight())
Nenue@1 142 icon:SetHeight(pc.foreground:GetHeight())
Nenue@1 143 icon:SetTexture(0,0,0,1)
Nenue@1 144 pc.icon = icon
Nenue@1 145
Nenue@1 146 pc.unit = unit
Nenue@1 147 T:Bar_SetUpdateHandler(pc, self.TekUpdate)
Nenue@1 148 end
Nenue@1 149
Nenue@1 150 -- Casting table events
Nenue@1 151 for i, event in pairs(TRACKED_EVENTS) do
Nenue@1 152 print('listening to |cFF00FFFF' .. event .. '|r')
Nenue@1 153 self:RegisterEvent(event, 'TekEvent')
Nenue@1 154 end
Nenue@1 155
Nenue@1 156 -- Extra events
Nenue@1 157 self:RegisterEvent('PLAYER_TARGET_CHANGED')
Nenue@1 158 self:RegisterEvent('PLAYER_FOCUS_CHANGED')
Nenue@1 159
Nenue@4 160 -- kill default casting bar
Nenue@4 161 -- T.cbscripts = {CastingBarFrame:GetScript('OnUpdate'), CastingBarFrame:GetScript('OnEvent')}
Nenue@1 162 CastingBarFrame:SetScript('OnUpdate', nil)
Nenue@1 163 CastingBarFrame:SetScript('OnEvent', nil)
Nenue@1 164 CastingBarFrame:Hide()
Nenue@1 165 end
Nenue@1 166
Nenue@1 167 -- Update handler for castbar
Nenue@1 168 function mod:TekUpdate()
Nenue@1 169 local glow = self.glow
Nenue@1 170 local foreground = self.foreground
Nenue@1 171 local background = self.background
Nenue@1 172 local latency = self.latency
Nenue@1 173 local time = GetTime() * 1000
Nenue@1 174 local spelltext = self.spelltext
Nenue@1 175 local timetext = self.timetext
Nenue@1 176 local cdb = self.db
Nenue@1 177 if mod.casting[self.unit] ~= nil then
Nenue@1 178
Nenue@1 179 local u = self.unit
Nenue@1 180 local s = mod.casting[u]
Nenue@1 181 local alpha = self:GetAlpha()
Nenue@1 182
Nenue@1 183 -- is something casting at all?
Nenue@1 184 if s.casting <= CAST_START then
Nenue@1 185
Nenue@1 186 -- start = true whenever a new spell cast could be active (i.e. target switching)
Nenue@1 187 if s.init then
Nenue@1 188 print('|cFFDD77DD'..u..'|r init_cast (spell='..s.displayName..', startTime='..s.startTime..', endTime='..s.endTime..', channel=',s.channel,')')
Nenue@1 189 print(self:GetName(), self:IsVisible(), self:IsShown())
Nenue@1 190
Nenue@4 191 if u == 'player' then
Nenue@4 192 self.latency.text:SetText(s.latency)
Nenue@4 193 end
Nenue@4 194
Nenue@1 195 -- update translation point
Nenue@1 196 if s.casting == CHANNEL_START and not cdb.fill_inverse then
Nenue@1 197 self.transpt = inv[a[cdb.fill_direction]]
Nenue@1 198 else
Nenue@1 199 self.transpt = a[cdb.fill_direction]
Nenue@1 200 end
Nenue@1 201
Nenue@1 202 -- update frame contents
Nenue@1 203 foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] and cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] or cdb.foreground_color))
Nenue@1 204 background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[s.casting]] and cdb['background' .. TEXTURE_SUFFIX[s.casting]] or cdb.background_color))
Nenue@1 205
Nenue@1 206 self.icon:SetTexture(s.texture)
Nenue@1 207 spelltext:SetText(s.displayName)
Nenue@1 208 self.duration = s.endTime - s.startTime
Nenue@1 209 self.fadeIn = s.startTime + (1-alpha) * FADE_IN_TIME
Nenue@1 210 self.fadeOut = s.endTime + FADE_OUT_TIME -- needs to exist for target changes
Nenue@1 211
Nenue@1 212 print(s.startTime, self.fadeIn, self.fadeIn - s.startTime)
Nenue@1 213 -- clear 'start'
Nenue@1 214 s.init = nil
Nenue@1 215 end
Nenue@1 216
Nenue@1 217 -- if we're checking this and start was never flipped, then something happened
Nenue@1 218 if time <= self.fadeIn then
Nenue@1 219 alpha = 1- ((self.fadeIn - time) / FADE_IN_TIME)
Nenue@1 220 self:SetAlpha(alpha)
Nenue@1 221 elseif alpha ~= 1 then
Nenue@1 222 self:SetAlpha(1)
Nenue@1 223 end
Nenue@1 224 self.value = time - s.startTime
Nenue@1 225 self.percent = self.value / self.duration
Nenue@1 226 self.casttime:SetText(format("%.1f", self.value / 1000))
Nenue@1 227
Nenue@1 228 -- s.casting is nil when the spellcast has finished
Nenue@1 229 else
Nenue@1 230 -- something set a term flag
Nenue@1 231 if s.casting < CHANNEL_STOPPED and s.fade then
Nenue@1 232 print(TL, '|cFFDD77DD'..u..'|r init_fadeout (spell='..s.displayName..', startTime='..s.startTime..', endTime='..s.endTime..', channel=',s.channel,')')
Nenue@1 233 self.fadeOut = time + FADE_OUT_TIME
Nenue@1 234 foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] and cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] or cdb.foreground_color))
Nenue@1 235 background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[s.casting]] and cdb['background' .. TEXTURE_SUFFIX[s.casting]] or cdb.background_color))
Nenue@1 236 s.fade = nil
Nenue@1 237 self.value = self.duration
Nenue@1 238 self.percent = 1
Nenue@1 239 end
Nenue@1 240
Nenue@1 241 if time < self.fadeOut then
Nenue@1 242 alpha = (self.fadeOut - time) / FADE_OUT_TIME
Nenue@1 243 self:SetAlpha(alpha)
Nenue@1 244 else
Nenue@1 245 alpha = 0
Nenue@1 246 self:Hide()
Nenue@1 247 self:SetAlpha(alpha)
Nenue@1 248 self.casttime:SetText(nil)
Nenue@1 249 self.spelltext:SetText(nil)
Nenue@1 250 self.debugged = false
Nenue@1 251 self.percent = 0
Nenue@4 252 if u == 'player' then
Nenue@4 253 self.latency.text:SetText(nil)
Nenue@4 254 end
Nenue@1 255 mod.casting[self.unit] = nil
Nenue@1 256 print('|cFFDD77DD'..u..'|r work complete, hiding...')
Nenue@1 257 end
Nenue@1 258 -- hide and wait until we're pulled out again
Nenue@1 259 end
Nenue@1 260 self.foreground:SetPoint('RIGHT', self, self.transpt, self.xtrans(), self.ytrans())
Nenue@1 261 end
Nenue@1 262 end
Nenue@1 263
Nenue@1 264
Nenue@1 265 -- event stub, filters out unwanted cast events
Nenue@1 266 function mod:TekEvent(e, unit, ...)
Nenue@1 267 if not self.castbar[unit] then
Nenue@1 268 return
Nenue@1 269 end
Nenue@1 270 if not self[e] then
Nenue@1 271 error('No method signature for event ' .. tostring(e))
Nenue@1 272 end
Nenue@1 273 print('popped |cFF00FFFF' .. e .. '|r: ', unit, ...)
Nenue@1 274 self[e](self, e, unit, ...)
Nenue@1 275 end
Nenue@1 276
Nenue@1 277 function mod:PLAYER_TARGET_CHANGED (e, cause)
Nenue@1 278 print(e)
Nenue@1 279 self:UpdateUnit('target')
Nenue@1 280 end
Nenue@1 281 function mod:PLAYER_FOCUS_CHANGED (e, cause)
Nenue@1 282 print(e)
Nenue@1 283 self:UpdateUnit('focus')
Nenue@1 284 end
Nenue@1 285 function mod:UpdateUnit(unit)
Nenue@1 286 if UnitCastingInfo(unit) then
Nenue@1 287 mod:UNIT_SPELLCAST_START('UNIT_SPELLCAST_START', unit)
Nenue@1 288 elseif UnitChannelInfo(unit) then
Nenue@1 289 mod:UNIT_SPELLCAST_CHANNEL_START('UNIT_SPELLCAST_CHANNEL_START', unit)
Nenue@1 290 else
Nenue@1 291 mod.castbar[unit]:Hide()
Nenue@1 292 end
Nenue@1 293 end
Nenue@1 294
Nenue@1 295 -- Spell event handlers
Nenue@1 296 function mod:UNIT_SPELLCAST_SENT(e, unit, spellname, rank, target)
Nenue@1 297 -- triggered an action buttton tied to a cast
Nenue@1 298 c[unit].sentTime = math.floor(GetTime() * 1000)
Nenue@1 299 print('|cFF44FF44',e,':|r', unit, spellname, c[unit].sentTime)
Nenue@1 300 c[unit].spell = spellname
Nenue@1 301 c[unit].rank = rank
Nenue@1 302 c[unit].target = target
Nenue@1 303 end
Nenue@1 304
Nenue@1 305 function mod:UNIT_SPELLCAST_START(e, unit) -- Server says: someone started casting
Nenue@1 306 local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = UnitCastingInfo(unit)
Nenue@1 307 print('casting['..unit..'] state updated (=start): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime)
Nenue@1 308
Nenue@1 309 c[unit].castID = castID
Nenue@1 310 c[unit].spell = spellname
Nenue@1 311 c[unit].rank = rank
Nenue@1 312 c[unit].displayName = displayName
Nenue@1 313 c[unit].texture = texture
Nenue@1 314 c[unit].startTime = startTime
Nenue@1 315 c[unit].endTime = endTime
Nenue@1 316 c[unit].nonInterruptible = nonInterruptible
Nenue@1 317 c[unit].isTradeSkill = isTradeSkill
Nenue@1 318 if c[unit].sentTime then
Nenue@1 319 c[unit].latency = c[unit].startTime - c[unit].sentTime
Nenue@1 320 end
Nenue@1 321
Nenue@1 322 -- set state and show
Nenue@1 323 c[unit].casting = CAST_START
Nenue@1 324 c[unit].init = true
Nenue@1 325 self.castbar[unit]:Show()
Nenue@1 326 end
Nenue@1 327
Nenue@1 328 function mod:UNIT_SPELLCAST_DELAYED(e, unit, spellname, rank, castID, target) -- Server says: they're still casting but it'll take longer
Nenue@1 329 local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = UnitCastingInfo(unit)
Nenue@1 330 print('casting['..unit..'] state updated (=delayed): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime)
Nenue@1 331 c[unit].castID = castID
Nenue@1 332 c[unit].channel = false
Nenue@1 333 c[unit].startTime = startTime
Nenue@1 334 c[unit].endTime = endTime
Nenue@1 335 -- just update timing data, frame script will adjust
Nenue@1 336 end
Nenue@1 337
Nenue@1 338 -- set exit states
Nenue@1 339 function mod:UNIT_SPELLCAST_STOP(e, unit, spellname, rank, castID, target) -- Server says: someone stopped casting for some reason
Nenue@1 340 --c[unit].casting = CAST_STOPPED
Nenue@1 341 end
Nenue@1 342
Nenue@1 343 function mod:UNIT_SPELLCAST_INTERRUPTED(e, unit, spellname, rank, castID, target) -- Server says: someone got interrupted
Nenue@1 344 c[unit].casting = CAST_INTERRUPTED
Nenue@1 345 c[unit].fade = true
Nenue@1 346 end
Nenue@1 347
Nenue@1 348 function mod:UNIT_SPELLCAST_SUCCEEDED(e, unit, spellname, rank, castID, target) -- Server says: they stopped because they're done
Nenue@1 349
Nenue@4 350 if c[unit].castID == castID and c[unit].casting ~= CHANNEL_START then
Nenue@1 351 c[unit].casting = CAST_SUCCEEDED
Nenue@1 352 c[unit].fade = true
Nenue@1 353 end
Nenue@1 354 end
Nenue@1 355
Nenue@1 356 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 357 if c[unit].castID ~= castID then -- can fire from keybind spam
Nenue@1 358 return
Nenue@1 359 end
Nenue@1 360 c[unit].casting = CAST_FAILED
Nenue@1 361 c[unit].fade = true
Nenue@1 362 end
Nenue@1 363
Nenue@1 364 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 365 if c[unit].castID == castID and c[unit].casting == CAST_START then
Nenue@1 366 c[unit].casting = CAST_FAILED
Nenue@1 367 end
Nenue@1 368 end
Nenue@1 369
Nenue@1 370 function mod:UNIT_SPELLCAST_INTERRUPTIBLE(e, unit)
Nenue@1 371 c[unit].notInterruptible = false
Nenue@1 372 end
Nenue@1 373 function mod:UNIT_SPELLCAST_NOT_INTERRUPTIBLE(e, unit)
Nenue@1 374 c[unit].notInterruptible = true
Nenue@1 375 end
Nenue@1 376
Nenue@1 377 function mod:UNIT_SPELLCAST_CHANNEL_START(e, unit, spellname, rank, castID, spellID)
Nenue@1 378 local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, nonInterruptible = UnitChannelInfo(unit)
Nenue@1 379 displayName = spellname -- channels sometimes just appear as 'Channeling' according to Quartz author
Nenue@1 380 print('casting['..unit..'] state updated (=start, channel): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime)
Nenue@1 381 c[unit].casting = CHANNEL_START
Nenue@1 382 c[unit].spellname = spellname
Nenue@1 383 c[unit].rank = rank
Nenue@1 384 c[unit].displayName = displayName
Nenue@1 385 c[unit].texture = texture
Nenue@1 386 c[unit].startTime = startTime
Nenue@1 387 c[unit].endTime = endTime
Nenue@1 388 c[unit].nonInterruptible = nonInterruptible
Nenue@1 389 c[unit].isTradeSkill = isTradeSkill
Nenue@1 390 c[unit].init = true
Nenue@1 391 mod.castbar[unit]:Show()
Nenue@1 392 end -- start up
Nenue@1 393 function mod:UNIT_SPELLCAST_CHANNEL_STOP(e, unit, spellname, rank, castID, spellID)
Nenue@1 394 c[unit].casting = CHANNEL_STOPPED
Nenue@1 395 end
Nenue@1 396 function mod:UNIT_SPELLCAST_CHANNEL_UPDATE(e, unit, spellname, rank, castID, spellID)
Nenue@1 397 spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, nonInterruptible = UnitChannelInfo(unit)
Nenue@1 398 displayName = spellname -- channels sometimes just appear as 'Channeling' according to Quartz author
Nenue@1 399 c[unit].channel = true
Nenue@1 400 end -- recalc
Nenue@1 401
Nenue@1 402
Nenue@1 403
Nenue@1 404