annotate Modules/BuffFrame.lua @ 59:07ef62fe201f

Re-write of BuffFrame module: - uses secure hooks on blizzard BuffFrame.lua functions to determine needed action - make use of built-in table behavior to reduce unnecessary frame updates
author Nenue
date Thu, 28 Jul 2016 18:27:56 -0400
parents
children 2a636b00c31e
rev   line source
Nenue@59 1 -- Veneer
Nenue@59 2 -- BuffFrame.lua
Nenue@59 3 -- Created: 7/27/2016 8:08 PM
Nenue@59 4 -- %file-revision%
Nenue@59 5 --
Nenue@59 6 local PLUGIN_NAME = 'BuffFrame'
Nenue@59 7 local plugin = {}
Nenue@59 8 local vn, print = LibStub("LibKraken").register(VeneerController, plugin)
Nenue@59 9
Nenue@59 10
Nenue@59 11
Nenue@59 12 local buttons = {}
Nenue@59 13 local buffTypes = {
Nenue@59 14 {
Nenue@59 15 name = 'buff',
Nenue@59 16 pattern = 'BuffButton(%d)',
Nenue@59 17 filters = 'HELPFUL',
Nenue@59 18 },
Nenue@59 19 {
Nenue@59 20 name = 'debuff',
Nenue@59 21 pattern = 'DebuffButton(%d)',
Nenue@59 22 filters = 'HARMFUL',
Nenue@59 23 },
Nenue@59 24 {
Nenue@59 25 name = 'tempenchant',
Nenue@59 26 pattern = 'TempEnchant(%d)',
Nenue@59 27 filters = 'TEMPENCHANT'
Nenue@59 28 }
Nenue@59 29 }
Nenue@59 30
Nenue@59 31 local textureMapping = {
Nenue@59 32 [1] = 16, --Main hand
Nenue@59 33 [2] = 17, --Off-hand
Nenue@59 34 [3] = 18, --Ranged
Nenue@59 35 }
Nenue@59 36
Nenue@59 37 local tickCounter = {}
Nenue@59 38 local aurasCache = {}
Nenue@59 39 local skinnedFrames = {}
Nenue@59 40 local pendingFrames = {}
Nenue@59 41 local anchors = {}
Nenue@59 42 local expirationCache = {}
Nenue@59 43
Nenue@59 44 local VeneerButton_OnHide = function(self)
Nenue@59 45 self:SetScript('OnDragStart', self.StartMoving)
Nenue@59 46 self:SetScript('OnDragStop', self.StopMovingOrSizing)
Nenue@59 47 self:SetMovable(true)
Nenue@59 48 self:EnableMouse(true)
Nenue@59 49 self:RegisterForDrag('LeftButton')
Nenue@59 50 end
Nenue@59 51 local VeneerButton_OnShow = function(self)
Nenue@59 52 self:SetScript('OnDragStart', self.StartMoving)
Nenue@59 53 self:SetScript('OnDragStop', self.StopMovingOrSizing)
Nenue@59 54 self:SetMovable(true)
Nenue@59 55 self:EnableMouse(true)
Nenue@59 56 self:RegisterForDrag('LeftButton')
Nenue@59 57 end
Nenue@59 58
Nenue@59 59
Nenue@59 60 local GetVeneer = function(frame)
Nenue@59 61 local name = frame:GetName()
Nenue@59 62 if not _G[name..'Veneer'] then
Nenue@59 63
Nenue@59 64 local veneer = CreateFrame('Frame', name..'Veneer', UIParent)
Nenue@59 65 veneer:SetAllPoints(frame)
Nenue@59 66 veneer.bg = veneer:CreateTexture()
Nenue@59 67 veneer.bg:SetColorTexture(0,1,0,0.5)
Nenue@59 68 veneer.bg:SetAllPoints(veneer)
Nenue@59 69 veneer.bg:Hide()
Nenue@59 70 veneer:Hide()
Nenue@59 71
Nenue@59 72 veneer:SetScript('OnShow', VeneerButton_OnShow)
Nenue@59 73 veneer:SetScript('OnHide', VeneerButton_OnHide)
Nenue@59 74
Nenue@59 75 local position = tonumber(name:match("%d"))
Nenue@59 76 if position == 1 then
Nenue@59 77 veneer:Show()
Nenue@59 78 end
Nenue@59 79
Nenue@59 80 veneer.progress = CreateFrame('Frame', name .. 'VeneerProgress', veneer)
Nenue@59 81 veneer.progress:Hide()
Nenue@59 82 veneer.progress:SetPoint('BOTTOMLEFT', veneer, 'BOTTOMLEFT', 3, -6)
Nenue@59 83 veneer.progress:SetPoint('TOPRIGHT', veneer, 'BOTTOMRIGHT', -3, -1)
Nenue@59 84
Nenue@59 85 veneer.progress.bg = veneer.progress:CreateTexture(nil, 'BACKGROUND')
Nenue@59 86 veneer.progress.bg:SetColorTexture(0,0,0,0.5)
Nenue@59 87 veneer.progress.bg:SetAllPoints(veneer.progress)
Nenue@59 88
Nenue@59 89 veneer.progress.fg = veneer.progress:CreateTexture(nil, 'ARTWORK')
Nenue@59 90 veneer.progress.fg:SetColorTexture(0,1,0,1)
Nenue@59 91 veneer.progress.fg:SetPoint('BOTTOMLEFT', 1,1)
Nenue@59 92 veneer.progress.fg:SetPoint('TOP', 0, -1)
Nenue@59 93
Nenue@59 94 veneer.progress.status = veneer.progress:CreateFontString()
Nenue@59 95 veneer.progress.status:SetFontObject(VeneerNumberFont)
Nenue@59 96 veneer.progress.status:SetPoint('TOP')
Nenue@59 97
Nenue@59 98 end
Nenue@59 99
Nenue@59 100
Nenue@59 101 return _G[name..'Veneer']
Nenue@59 102 end
Nenue@59 103
Nenue@59 104 local UpdateVeneer = function (frame, duration, expires)
Nenue@59 105 local veneer = GetVeneer(frame)
Nenue@59 106
Nenue@59 107 if expires and duration then
Nenue@59 108 veneer.progress:Show()
Nenue@59 109
Nenue@59 110 local startTime = (expires - duration)
Nenue@59 111 local endTime = expires or 0
Nenue@59 112 print('|cFF0088FF'..frame:GetName()..'|r', 'has expiration', startTime, 'to', endTime, 'over', duration, 'frame', veneer.progress:GetWidth())
Nenue@59 113 veneer.progress:SetScript('OnUpdate', function(self)
Nenue@59 114 local w = floor(veneer.progress:GetWidth()+.5)
Nenue@59 115 local t = GetTime()
Nenue@59 116 local progress = (t - startTime) / duration
Nenue@59 117 if t >= endTime or not frame:IsVisible() then
Nenue@59 118 veneer.startTime = nil
Nenue@59 119 self:SetScript('OnUpdate', nil)
Nenue@59 120 self:Hide()
Nenue@59 121 else
Nenue@59 122 self.fg:SetWidth(w - ceil(w * progress) - 2)
Nenue@59 123 end
Nenue@59 124 end)
Nenue@59 125 end
Nenue@59 126
Nenue@59 127
Nenue@59 128
Nenue@59 129 veneer:Show()
Nenue@59 130 end
Nenue@59 131
Nenue@59 132
Nenue@59 133 -- Associates skinning elements with said button
Nenue@59 134 local SkinFrame = function(name)
Nenue@59 135 local frame = _G[name ]
Nenue@59 136 if skinnedFrames[frame] then
Nenue@59 137 print('|cFFFF4400Attempting to skin a frame that already went through.|r')
Nenue@59 138 return
Nenue@59 139 end
Nenue@59 140
Nenue@59 141 local icon = _G[name .. 'Icon']
Nenue@59 142 local border = _G[name .. 'Border']
Nenue@59 143 local duration = _G[name .. 'Duration']
Nenue@59 144 local slot = frame:GetID() or 0
Nenue@59 145
Nenue@59 146 tickCounter[frame] = (tickCounter[frame] or 0) + 1
Nenue@59 147
Nenue@59 148
Nenue@59 149 print(tickCounter[frame], frame:GetName(), '|cFFFFFF00'..slot..'|r')
Nenue@59 150 skinnedFrames[frame] = frame
Nenue@59 151 frame:SetSize(48,48)
Nenue@59 152 icon:SetTexCoord(0,1,0,1)
Nenue@59 153 if border then
Nenue@59 154 border:SetSize(50,50)
Nenue@59 155 end
Nenue@59 156 if duration then
Nenue@59 157 duration:ClearAllPoints()
Nenue@59 158 duration:SetPoint('BOTTOM')
Nenue@59 159 duration:SetFontObject(VeneerNumberFont)
Nenue@59 160 duration:SetDrawLayer('OVERLAY')
Nenue@59 161
Nenue@59 162 end
Nenue@59 163
Nenue@59 164 GetVeneer(frame)
Nenue@59 165
Nenue@59 166 anchors[frame] = veneer
Nenue@59 167 print('Initializing', name)
Nenue@59 168 end
Nenue@59 169
Nenue@59 170
Nenue@59 171 --- Provides the number of changed indices for use in deciding between partial and full veneer updates
Nenue@59 172 local CacheCheck = function(frame, ...)
Nenue@59 173 aurasCache[frame] = aurasCache[frame] or {}
Nenue@59 174 local hasChange = 0
Nenue@59 175 local numVals = select('#',...)
Nenue@59 176 for i = 1, numVals do
Nenue@59 177 local arg = select(i, ...)
Nenue@59 178 if aurasCache[frame][i] ~= arg then
Nenue@59 179 hasChange = hasChange + 1
Nenue@59 180 end
Nenue@59 181 aurasCache[frame][i] = arg
Nenue@59 182 end
Nenue@59 183 return hasChange
Nenue@59 184 end
Nenue@59 185
Nenue@59 186 local AuraButton_Update = function(name, index, filter)
Nenue@59 187 local bName = name..index
Nenue@59 188 local frame = _G[bName]
Nenue@59 189 if frame and frame:IsVisible() then
Nenue@59 190 tickCounter[frame] = (tickCounter[frame] or 0) + 1
Nenue@59 191 local cacheDiff = CacheCheck(frame, UnitAura(frame.unit, frame:GetID(), frame.filter))
Nenue@59 192 -- did something change?
Nenue@59 193 if (cacheDiff >= 1) or not skinnedFrames[frame] then
Nenue@59 194 print(frame:GetName(), 'diff:', cacheDiff)
Nenue@59 195 tinsert(pendingFrames, frame)
Nenue@59 196 end
Nenue@59 197
Nenue@59 198
Nenue@59 199 if frame.expirationTime ~= expirationCache[name] then
Nenue@59 200 print('|cFFBBFF00expirationTime|r', name, frame.expirationTime)
Nenue@59 201 expirationCache[name] = frame.expirationTime
Nenue@59 202 print(unpack(aurasCache[frame]))
Nenue@59 203 UpdateVeneer(frame, aurasCache[frame][6], aurasCache[frame][7])
Nenue@59 204 end
Nenue@59 205
Nenue@59 206 -- is it a new button?
Nenue@59 207 if not skinnedFrames[frame] then
Nenue@59 208 SkinFrame(bName)
Nenue@59 209 end
Nenue@59 210 end
Nenue@59 211 end
Nenue@59 212
Nenue@59 213 local BuffFrame_UpdateAllBuffAnchors = function()
Nenue@59 214 local todo = {}
Nenue@59 215 if #pendingFrames >= 1 then
Nenue@59 216
Nenue@59 217 print('|cFFBBFF00AllBuffAnchors|r', #pendingFrames)
Nenue@59 218 while pendingFrames[1] do
Nenue@59 219 local frame = tremove(pendingFrames)
Nenue@59 220 tinsert(todo, frame:GetName())
Nenue@59 221
Nenue@59 222 UpdateVeneer(frame)
Nenue@59 223
Nenue@59 224
Nenue@59 225 end
Nenue@59 226 print(table.concat(todo, ', '))
Nenue@59 227 end
Nenue@59 228 --BuffButton1
Nenue@59 229 --DebuffButton1
Nenue@59 230 TempEnchant1:SetPoint('TOPRIGHT', BuffButton1, 'TOPRIGHT', BuffButton1:GetWidth()+4, 0)
Nenue@59 231 end
Nenue@59 232
Nenue@59 233 local AuraButton_UpdateDuration = function(frame, timeLeft)
Nenue@59 234 local ts = ''
Nenue@59 235 if timeLeft > 3600 then
Nenue@59 236 ts = ts .. floor(timeLeft/3600) .. ':'
Nenue@59 237 timeLeft = mod(timeLeft, 3600)
Nenue@59 238 end
Nenue@59 239 if timeLeft > 60 then
Nenue@59 240 ts = ts .. floor(timeLeft/60) .. '\''
Nenue@59 241 timeLeft = mod(timeLeft, 60)
Nenue@59 242 end
Nenue@59 243 ts = ts .. floor(timeLeft)..'"'
Nenue@59 244
Nenue@59 245 frame.duration:SetText(ts)
Nenue@59 246 frame.duration:SetVertexColor(1,1,1)
Nenue@59 247
Nenue@59 248 end
Nenue@59 249
Nenue@59 250 local visibility = {}
Nenue@59 251 local TempEnchantButton_OnHide = function(self)
Nenue@59 252 local isVisible = self:IsVisible()
Nenue@59 253 if isVisible ~= visibility[self] then
Nenue@59 254 print('|cFFFFFF00HIDE|r', self:GetName())
Nenue@59 255 visibility[self] = isVisible
Nenue@59 256 end
Nenue@59 257 end
Nenue@59 258
Nenue@59 259 -- Obtains the first instance of Tenchant use
Nenue@59 260
Nenue@59 261 local TemporaryEnchantFrame_Update = function(...)
Nenue@59 262 local numVals = select('#', ...)
Nenue@59 263 local numItems = numVals / 4
Nenue@59 264 if numItems >= 1 then
Nenue@59 265 for itemIndex = numItems, 1, -1 do
Nenue@59 266 local frame = _G['TempEnchant'..itemIndex]
Nenue@59 267 local hasEnchant, timeRemaining, enchantCharges = select((4 * (itemIndex -1)) + 1, ...)
Nenue@59 268
Nenue@59 269
Nenue@59 270 if hasEnchant then
Nenue@59 271 local endTime = floor(GetTime()*1000) + timeRemaining
Nenue@59 272
Nenue@59 273
Nenue@59 274 --print(endTime)
Nenue@59 275 if endTime ~= expirationCache[frame] then
Nenue@59 276 if expirationCache[frame] then
Nenue@59 277 print(endTime, expirationCache[frame], endTime - expirationCache[frame])
Nenue@59 278 end
Nenue@59 279 expirationCache[frame] = endTime
Nenue@59 280 print('push tempenchant timer update', timeRemaining / 1000, GetTime()+(timeRemaining/1000))
Nenue@59 281 UpdateVeneer(frame, timeRemaining/1000, GetTime()+(timeRemaining/1000))
Nenue@59 282 end
Nenue@59 283 else
Nenue@59 284 GetVeneer(frame):Hide()
Nenue@59 285 end
Nenue@59 286
Nenue@59 287 end
Nenue@59 288
Nenue@59 289 end
Nenue@59 290
Nenue@59 291 end
Nenue@59 292
Nenue@59 293 local BuffFrame_Update = function(...)
Nenue@59 294 --print('Time for udpate!', ...)
Nenue@59 295 end
Nenue@59 296
Nenue@59 297
Nenue@59 298 hooksecurefunc("BuffFrame_Update", BuffFrame_Update)
Nenue@59 299 hooksecurefunc("AuraButton_UpdateDuration", AuraButton_UpdateDuration)
Nenue@59 300 hooksecurefunc("AuraButton_Update", AuraButton_Update)
Nenue@59 301 hooksecurefunc("BuffFrame_UpdateAllBuffAnchors", BuffFrame_UpdateAllBuffAnchors)
Nenue@59 302 hooksecurefunc("TemporaryEnchantFrame_Update", TemporaryEnchantFrame_Update)
Nenue@59 303
Nenue@59 304 -- The TempEnchant frames are hardcoded in the base FrameXML, so get them now
Nenue@59 305 for i = 1, 3 do
Nenue@59 306
Nenue@59 307 SkinFrame('TempEnchant'..i)
Nenue@59 308 hooksecurefunc(_G['TempEnchant'..i], "Hide", TempEnchantButton_OnHide)
Nenue@59 309
Nenue@59 310
Nenue@59 311 end
Nenue@59 312
Nenue@59 313 plugin.init = function ()
Nenue@59 314 plugin.db = vn.db[PLUGIN_NAME]
Nenue@59 315 end