Compare with Previous | Blame | View Log
--[[ Class bars Runes, shards, burning embers, etc. By Kesava for oUF Kui ]] local addon, ns = ... local kui = ns.kui local cb -------------------------------------------------------- Utility funtions -- -- Arranges bars into columns depending on which bars are visible local function ArrangeBars() local shown, pv, pc = {} -- pv = previous visible bar -- pc = previous visible bar which starts a column for groupId,group in pairs(cb.container.groups) do if not shown[groupId] then shown[groupId] = 0 end if not group.rows then group.rows = #group.bars end for _,bar in ipairs(group.bars) do if bar.bg:IsShown() then shown[groupId] = shown[groupId] + 1 end end if shown[groupId] > 0 then if group.rows == #group.bars then -- automatically adjust bar width to fit container (i.e. when one -- bar is hidden, expand the others to fit the total width) group.autorows = shown[groupId] else -- use given number of rows to group bars together group.autorows = group.rows or #group.bars end local rows = group.autorows group.width = floor((cb.width / rows) - (cb.container.space * (rows - 1)) / rows) -- add 1 to the width of each bar until remainingWidth is 0 -- i.e ensure bar width matches up correctly with the width of the container group.totalWidth = (group.width * rows) + (cb.container.space * (rows - 1)) group.remainingWidth = cb.width - group.totalWidth local vid = 0 -- visible id, [k] - [number of non-shown bars] for _,bar in ipairs(group.bars) do local group = cb.container.groups[bar.group] if bar.bg:IsShown() then vid = vid + 1 bar.bg:ClearAllPoints() bar.bg:SetWidth( group.width + (group.remainingWidth > 0 and 1 or 0) + (bar.hasBorder * 2) ) if group.remainingWidth > 0 then group.remainingWidth = group.remainingWidth - 1 end if not pv then -- first bar -- anchor to the unit frame bar.bg:SetPoint('BOTTOMLEFT', -bar.hasBorder, -bar.hasBorder) pc = bar else -- subsequent bars, find correct position if (vid-1) % group.autorows == 0 then -- first bar in this row -- above the previous row bar.bg:SetPoint('BOTTOMLEFT', pc.bg, 'TOPLEFT', (0 + pc.hasBorder) - bar.hasBorder, (cb.container.space - pc.hasBorder) - bar.hasBorder) pc = bar else -- right of the previous bar bar.bg:SetPoint('LEFT', pv.bg, 'RIGHT', (cb.container.space + pv.hasBorder) - bar.hasBorder, 0) end end pv = bar end end end end end -- Hides the given bar and calls ArrangeBars local function HideBar(self, bar) bar.bg:Hide() ArrangeBars() end -- Shows the given bar and calls ArrangeBars local function ShowBar(self, bar) bar.bg:Show() ArrangeBars() end ----------------------------------------------------------- Tooltip functions -- --------------------------------------------------------------- Totem tooltip -- local function ShamanTooltip(bar) if bar.name then local tl, delim = bar.tl, 's' if tl > 60 then tl = tl / 60 delim = 'm' end tl = floor(tl) return { bar.name, { t = tl .. delim, c = { r = .6, g = .6, b = .6 } } } end end ------------------------------------------------------------ Update functions -- ------------------------------------------------------- Update generic powers -- -- must be used as update.f local function UpdateGeneric(self, event, ...) local visible, curr = 0 curr = UnitPower('player', cb.type) for k, bar in ipairs(cb.bars) do if curr >= k then visible = visible + 1 bar.bg:SetAlpha(1) else bar.bg:SetAlpha(.3) end end end ----------------------------------------------------------- Update Totem Bars -- local function UpdateShamanBar(self, bar) local haveTotem, totemName, startTime, duration = GetTotemInfo(bar.id) if haveTotem then bar.name = totemName bar.tl = (startTime + duration - GetTime()) if bar.tl >= 0 then local total = 0 ShowBar(self, bar) bar:SetValue(1 - ((GetTime() - startTime) / duration)) bar:SetScript('OnUpdate', function(self, elapsed) total = total + elapsed if total > 0.1 then total = 0 local haveTotem, totemName, startTime, duration = GetTotemInfo(self.id) self:SetValue(1 - ((GetTime() - startTime) / duration)) self.tl = (startTime + duration - GetTime()) end end) else bar:SetScript('OnUpdate', nil) HideBar(self, bar) end else HideBar(self, bar) end end ---------------------------------------------------- Update druid shroom bars -- local function UpdateDruidShrooms(self, bar) if bar.id > 3 then return end UpdateShamanBar(self, bar) end ---------------------------------------------------------------- Update Runes -- local function UpdateDeathKnightBar(self, bar) local type, startTime, duration, charged = GetRuneType(bar.id), GetRuneCooldown(bar.id) if not type then return end bar:SetStatusBarColor(unpack(self.ClassBars.colours[type])) bar.tl = startTime + duration - GetTime() if not charged then bar:SetValue((GetTime() - startTime) / duration) if not bar:GetScript('OnUpdate') then bar.bg:SetAlpha(.3) bar:SetScript('OnUpdate', function(self, elapsed) self.elapsed = (self.elapsed or 0) + elapsed if self.elapsed > 0.1 then self.elapsed = 0 local startTime, duration = GetRuneCooldown(bar.id) bar:SetValue((GetTime() - startTime) / duration) end end) end else bar.bg:SetAlpha(1) bar:SetValue(1) bar:SetScript('OnUpdate', nil) end end ----------------------------------------------------------- Update druid bars -- local function UpdateDruid(self, event) -- Eclipse ----------------------------------------------------------------- if event == 'UPDATE_SHAPESHIFT_FORM' or event == 'PLAYER_ENTERING_WORLD' or not event then -- toggle bars depending on form if GetShapeshiftFormID() == MOONKIN_FORM then ShowBar(self, cb.bars[4]) else HideBar(self, cb.bars[4]) end end if cb.bars[4].bg:IsShown() then -- set colour based on current direction local curr = UnitPower('player', SPELL_POWER_ECLIPSE) cb.bars[4]:SetValue(curr < 0 and -curr or curr) if curr > 0 then -- solar cb.bars[4]:SetStatusBarColor(238/255, 200/255, 77/255) cb.bars[4].col:SetVertexColor(238/255, 200/255, 77/255, .2) else -- lunar cb.bars[4]:SetStatusBarColor(60/255, 150/255, 220/255) cb.bars[4].col:SetVertexColor(60/255, 150/255, 220/255, .2) end -- get eclipse status local hasEclipse = false for i=0,40 do local _,_,_,_,_,_,_,_,_,_, spellID = UnitBuff('player', i) if spellID == ECLIPSE_BAR_SOLAR_BUFF_ID then hasEclipse = { 238/255, 200/255, 77/255, .5 } break elseif spellID == ECLIPSE_BAR_LUNAR_BUFF_ID then hasEclipse = { 60/255, 150/255, 220/255, .5 } break end end if hasEclipse then cb.bars[4].bg:SetBackdropBorderColor(unpack(hasEclipse)) else cb.bars[4].bg:SetBackdropBorderColor(0,0,0,0) end end -- Mana -------------------------------------------------------------------- local shown = false if UnitPowerType('player') ~= 0 then local curr, max = UnitPower('player', 0), UnitPowerMax('player', 0) if curr ~= max then cb.bars[5]:SetMinMaxValues(0, max) cb.bars[5]:SetValue(curr) ShowBar(self, cb.bars[5]) shown = true end end if not shown and cb.bars[5].bg:IsShown() then HideBar(self, cb.bars[5]) end end ---------------------------------------------------------------- Demonic fury -- local function UpdateDemonicFury(self, event) cb.bars[1]:SetValue(UnitPower('player', SPELL_POWER_DEMONIC_FURY)) end -------------------------------------------------------------- Burning embers -- -- each bar fills to 10 local function UpdateBurningEmbers(self, event) local power, bpower = UnitPower('player', SPELL_POWER_BURNING_EMBERS, true) for k, bar in ipairs(cb.bars) do bpower = power - ((k-1)*10) bar:SetValue(bpower) end end ------------------------------------------------------- Post-create functions -- ------------------------------------------------ Post-create for generic bars -- local function CreateGenericBar(self) for k, bar in ipairs(cb.bars) do if not bar.col then -- create bar background bar.col = bar:CreateTexture(nil, 'BACKGROUND') bar.col:SetTexture(kui.m.t.sbar) bar.col:SetAllPoints(bar) end local r,g,b = bar:GetStatusBarColor() bar.col:SetVertexColor(r, g, b, .2) end end --------------------------------------------------------------------- Eclipse -- local function CreateEclipseBar(self) local bar = cb.bars[4] local bg = bar.bg bg:SetBackdrop({ bgFile = kui.m.t.solid, edgeFile = kui.m.t.shadow, edgeSize = 2, insets = { top = 2, right = 2, bottom = 2, left = 2 } }) bar.hasBorder = 2 bg:SetBackdropColor(0,0,0,1) bg:SetBackdropBorderColor(1,1,1,0) bg:SetWidth(bg:GetWidth()+4) bg:SetHeight(bg:GetHeight()+4) bar:SetPoint('TOPLEFT', bg, 'TOPLEFT', 3, -3) bar:SetPoint('BOTTOMRIGHT', bg, 'BOTTOMRIGHT', -3, 3) CreateGenericBar(self) end --[[--------------------------------------------------------- Update function -- Calls cb.o.update.f(self) Calls cb.o.update.b(self, bar) for each bar created by Create() ]] local function Update(self, event, ...) if event == 'UNIT_POWER' or event == 'UNIT_POWER_FREQUENT' or event == 'UNIT_AURA' then local unit = ... if unit ~= self.unit then return end end cb = cb or self.ClassBars if not cb.o then return end if cb.o.update.f then cb.o.update.f(self, event, ...) end if cb.o.update.b then for k, b in pairs(cb.bars) do cb.o.update.b(self, b) end end end --[[------------------------------------------------------------- Create bars -- Creates bars from cb.o object Calls cb.o.create(self) upon completion Also forces Update with nil event ]] local function CreateBars(self) cb.bars = cb.bars or {} --print('CreateBars') local space = 1 local groups = cb.o.groups -- parse groups if groups then groups[0] = { bars = {} } else groups = { [0] = { bars = {} } } end cb.container.groups = groups cb.container.space = space -- create/update bars for k,group in pairs(groups) do if not group.bars then group.bars = {} end end -- create/update bars, default method for k, v in ipairs(cb.o.bars) do local b, bg local group = groups[v.group or 0] if cb.bars[k] then -- update bar size, position... b = cb.bars[k] bg = b.bg bg:ClearAllPoints() else -- create a bar bg = CreateFrame("Frame", nil, cb.container) b = CreateFrame("StatusBar", nil, bg) bg:SetBackdrop({ bgFile = kui.m.t.sbar }) bg:SetBackdropColor(.05, .05, .05) b:SetPoint('TOPLEFT', bg, 'TOPLEFT', 1, -1) b:SetPoint('BOTTOMRIGHT', bg, 'BOTTOMRIGHT', -1, 1) b:SetStatusBarTexture(kui.m.t.sbar) if cb.o.tooltip then -- enable tooltip b:EnableMouse(true) b:SetScript('OnEnter', function(self) lines = cb.o.tooltip(self) if lines and #lines > 0 then GameTooltip:SetOwner(self.bg, 'ANCHOR_CURSOR') for _, line in pairs(lines) do if line.t and line.c then GameTooltip:AddLine(line.t, line.c.r, line.c.g, line.c.b) else GameTooltip:AddLine(line) end end GameTooltip:Show() end end) b:SetScript('OnLeave', function() GameTooltip:Hide() end) end b.bg = bg b.id = v.id or k b.group = v.group or 0 b.hasBorder = 0 cb.bars[k] = b end tinsert(group.bars, b) b:SetStatusBarColor(unpack(v.colour or cb.o.colour or {1,1,1})) b:SetMinMaxValues(unpack(v.minmax or cb.o.minmax or {0,1})) if not v.minmax and not cb.o.minmax then b:SetValue(1) end bg:SetHeight(cb.height) end -- register events for k, e in pairs(cb.o.events) do --self:RegisterEvent(e, Update) -- TODO TEMP WORKAROUND FOR OUF BUG (possibly) cb.container:RegisterEvent(e) end -- call post create if cb.o.create then cb.o.create(self) end Update(self) -- size/position bars ArrangeBars() end --------------------------------------------------------------- Update object -- -- updates the bar object when power type or max power changes local function UpdateObject(self, event, unit, resource) --print('UpdateObject') cb = cb or self.ClassBars if cb.level and UnitLevel('player') < cb.level then return end if event == 'UNIT_MAXPOWER' then if unit ~= 'player' then return end resource = _G['SPELL_POWER_'..resource] if resource ~= cb.type then return end else -- power type has changed, or set up for initial display --print('Power type update (or initial call)') if cb.types then cb.type = cb.PowerTypes[GetSpecialization()] end if cb.types then cb.o = cb.types[cb.type] end if not cb.o then -- this specialization has no object attached to it cb.container:Hide() cb.container:UnregisterAllEvents() return else cb.container:Show() end if not cb.o.bars then cb.o.dynamic = true cb.o.bars = {} end resource = cb.type end local powerMax = UnitPowerMax('player', resource) if cb.bars then -- cycle existing bars to destroy (in case powerMax has decreased) for k, bar in ipairs(cb.bars) do if (cb.o.dynamic and k > powerMax) or (not cb.o.dynamic and k > #cb.o.bars) then -- destroy bars which are no longer needed bar:UnregisterAllEvents() HideBar(self, bar) -- they'll be garbage collected... right? cb.bars[k] = nil cb.o.bars[k] = nil end end end if cb.o.dynamic then --print('Dynamic bars') -- determine bars to add to object (in case powerMax has increased) for k=1,powerMax do if not cb.o.bars[k] then --print('Will create bar '..k) cb.o.bars[k] = {} end end end if #cb.o.bars > 0 then -- create/update bars CreateBars(self) end end -------------------------------------------------------- Enable ClassBars -- local function Enable(self, unit) cb = self.ClassBars or nil if not cb then return end if cb.class ~= "SHAMAN" and cb.class ~= "DEATHKNIGHT" and cb.class ~= "DRUID" and cb.class ~= "PALADIN" and cb.class ~= "WARLOCK" and cb.class ~= "MONK" and cb.class ~= 'PRIEST' then cb = nil return end if cb.class == 'WARLOCK' then cb.PowerTypes = { [1] = SPELL_POWER_SOUL_SHARDS, [2] = SPELL_POWER_DEMONIC_FURY, [3] = SPELL_POWER_BURNING_EMBERS } elseif cb.class == 'PRIEST' then cb.PowerTypes = { [3] = SPELL_POWER_SHADOW_ORBS } end -- create container cb.container = CreateFrame("Frame", nil, self) cb.container:SetWidth(cb.width) cb.container:SetHeight(cb.height) -- TODO do this? --if cb.horizcenter == nil or cb.horizcenter == true then cb.container:SetPoint('BOTTOMLEFT', self, 'TOPLEFT', floor((self:GetWidth()/2) - (cb.width/2)), 1) --end -- TODO TEMP WORKAROUND FOR OUF BUG (possibly) cb.container:SetScript('OnEvent', function(_, event, ...) Update(self, event, ...) end) cb.o = {} ------------------------------------------------------- Create Totem Bars -- if cb.class == 'SHAMAN' then cb.o.bars = { [1] = { colour = {255/255, 109/255, 22/255} }, -- fire [2] = { colour = {120/255, 255/255, 60/255} }, -- earth [3] = { colour = {112/255, 65/255, 255/255} }, -- air [4] = { colour = {112/255, 255/255, 255/255} }, -- water } cb.o.events = { 'PLAYER_TOTEM_UPDATE' } cb.o.update = { ['b'] = UpdateShamanBar } cb.o.create = CreateGenericBar cb.o.tooltip = ShamanTooltip ------------------------------------------------------------ Create Runes -- elseif cb.class == 'DEATHKNIGHT' then cb.o.bars = { [1] = {}, [2] = {}, [3] = { id = 5 }, [4] = { id = 6 }, [5] = { id = 3 }, [6] = { id = 4 }, } cb.o.events = { 'RUNE_POWER_UPDATE', 'RUNE_TYPE_UPDATE' } cb.colours = { [1] = { .7, 0, 0 }, -- blood [2] = { .3, .7, 0 }, -- unholy [3] = { .2, .2, .8 }, -- frost [4] = { 1, .5, 0 } -- death } cb.o.create = function(self) RuneFrame:Hide() RuneFrame.Show = function() return end RuneFrame:UnregisterAllEvents() end cb.o.update = { ['b'] = UpdateDeathKnightBar } ---------------------------------------------------------- Create Eclipse -- elseif cb.class == 'DRUID' then cb.o.rows = 1 cb.o.groups = { [1] = { rows = 1 }, [2] = { rows = 3 } } cb.o.bars = { [1] = { group = 2, colour = { 0, 1, .6 } }, [2] = { group = 2, colour = { 0, 1, .6 } }, [3] = { group = 2, colour = { 0, 1, .6 } }, [4] = { group = 1, colour = { 111/255, 186/255, 245/255 }, minmax = { 0, 100 } }, -- lunar [5] = { group = 1, colour = { 78/255, 95/255, 190/255 } }, -- mana } cb.o.events = { 'PLAYER_TOTEM_UPDATE', 'UPDATE_SHAPESHIFT_FORM', 'ECLIPSE_DIRECTION_CHANGE', 'UNIT_POWER', 'UNIT_MAXPOWER', 'UNIT_AURA' } cb.o.create = CreateEclipseBar cb.o.update = { ['f'] = UpdateDruid, ['b'] = UpdateDruidShrooms } cb.o.tooltip = ShamanTooltip ----------------------------------------------------------- Create holy power -- elseif cb.class == 'PALADIN' then cb.type = SPELL_POWER_HOLY_POWER cb.o.colour = { 1, 1, 0 } cb.o.events = { 'UNIT_POWER' } cb.o.update = { ['f'] = UpdateGeneric } ------------------------------------------------------- Create warlock powers -- elseif cb.class == 'WARLOCK' then cb.level = 10 cb.types = { [SPELL_POWER_DEMONIC_FURY] = { bars = { [1] = { minmax = { 0, 1000 } } }, colour = { .4, 1, 0 }, update = { ['f'] = UpdateDemonicFury }, create = CreateGenericBar, events = { 'UNIT_POWER' } }, [SPELL_POWER_BURNING_EMBERS] = { colour = { 1, .3, 0 }, update = { ['f'] = UpdateBurningEmbers }, create = CreateGenericBar, minmax = { 0, 10 }, events = { 'UNIT_POWER' } }, [SPELL_POWER_SOUL_SHARDS] = { colour = { .5, 0, 1 }, update = { ['f'] = UpdateGeneric }, events = { 'UNIT_POWER' } } } ------------------------------------------------------------ Shadow orbs (13) -- elseif cb.class == 'PRIEST' then cb.types = { [SPELL_POWER_SHADOW_ORBS] = { colour = { .5, 0, 1 }, update = { ['f'] = UpdateGeneric }, events = { 'UNIT_POWER' } } } -------------------------------------------------------------------- Chi (12) -- elseif cb.class == 'MONK' then cb.type = SPELL_POWER_CHI cb.o.colour = { .5, 1, 1 } cb.o.events = { 'UNIT_POWER' } cb.o.update = { ['f'] = UpdateGeneric } end self:RegisterEvent('PLAYER_ENTERING_WORLD', Update) if cb.type or cb.types then self:RegisterEvent('UNIT_MAXPOWER', UpdateObject) self:RegisterEvent('PLAYER_TALENT_UPDATE', UpdateObject) end if cb.type or cb.types then UpdateObject(self) -- parse object else CreateBars(self) end end oUF:AddElement("ClassBars", nil, Enable, nil)