/
you entered combat and didn't use the charge, the timer will pick up where it paused prior to combat. |
Due to latency both between one self and the servers as well as difference when the game considers you out of combat to when you're |
actually out of combat (and Blizzards internal timer starts) it's not possible to make it 100% accurate. As such there's a flat .25 added |
onto the timer to account for this delay, but this number can be tweaked if wanted. |
actually out of combat (and Blizzards internal timer starts) it's not possible to make it 100% accurate, ergo it's guesstimating by |
adding the players current latency (which itself is a average value) onto the maximum-value of the bar. While not perfect, it makes |
it accurate enough to the point where you get a reliable indication of when it's going to expire. |
It also seems like UNIT_POWER doesn't fire for new charges when you're already at the max amount. The CLEU SPELL_ENERGIZE event does |
fire though. |
]] |
local _, PowerUp = ... |
-- local PowerUp = _G.PowerUp |
if PowerUp.class ~= "PALADIN" then return end |
local db, pguid |
-- Setup frames |
function PowerUp:Init() |
db = PowerUpDB |
db.max = 3 |
powerType = "HOLY_POWER" |
powerName = SPELL_POWER_HOLY_POWER |
for i=1, #self.bars do |
self.bars[i]:SetVertexColor(db.colors.paladin.r, db.colors.paladin.g, db.colors.paladin.b) |
end |
-- create overlay if enabled |
if db.showhptimer then |
self.timer = self.timer or CreateFrame("StatusBar", "PowerUpTimer", self) |
self.timer:SetAllPoints(self) |
self.timer:SetMinMaxValues(0, 10) |
self.timer:SetValue(0) |
self.timer:SetStatusBarTexture(self.bars[1]:GetTexture()) |
self.timer:SetStatusBarColor(db.colors.overlay.r, db.colors.overlay.g, db.colors.overlay.b, db.hptimeralpha) |
self.timer.text = self.timer.text or self.timer:CreateFontString(nil, "OVERLAY") |
self.timer.text:SetFontObject(GameFontHighlight) |
self.timer.text:SetPoint("LEFT", self.timer, "RIGHT", 5, 0) |
-- we need this to account for pausing and whatnot |
self:RegisterEvent("PLAYER_REGEN_ENABLED") |
self:RegisterEvent("PLAYER_REGEN_DISABLED") |
end |
self:Workshop() |
self:Paintshop() |
self:ToggleOverlay() |
self.last = 0 |
self:RegisterEvent("UNIT_POWER") |
self.UNIT_POWER = self.RefreshPowers |
self.prior = 0 |
-- update powers at login/zoning in case we have some already |
-- if a power disappears during loading screen, it wont update |
-- properly otherwise. |
self:RegisterEvent("PLAYER_ENTERING_WORLD") |
end |
function PowerUp:ToggleOverlay() |
if db.showOverlay then |
if not self.timer then |
self.timer = CreateFrame("Frame", nil, self.frame) |
self.timer.color = self.timer:CreateTexture(nil, "OVERLAY") |
self.timer.color:SetTexture(db.texture) |
self.timer.color:SetAllPoints(self.timer) |
self.timer:SetAllPoints(self.frame) |
self.overlay = self.timer:CreateAnimationGroup() |
self.animation = self.overlay:CreateAnimation("Scale") |
self.animation:SetDuration(10.25) |
self.animation:SetMaxFramerate(30) -- looks nice enough |
self.animation:SetOrigin("LEFT", 0, 0) |
self.animation:SetScale(0, 1) |
self.overlay.Restart = function() self.overlay:Stop() self.overlay:Play() end |
self.overlay:SetScript("OnFinished", function() self.timer:Hide() end) |
self.overlay:SetScript("OnPlay", function() self.timer:Show() end) |
-- Throttle? Not really necessary maybe, since it's not running for |
-- excessive amounts of time. |
local function onUpdate(self, elapsed) |
local value = self.timer:GetValue() |
local new = value - elapsed |
if value <= 0 then |
self.timer:Hide() |
self:SetScript("OnUpdate", nil) |
else |
self.timer:SetValue(new) |
if db.showtimertext then |
self.timer.text:SetFormattedText("%.1f", new) |
else |
self.timer.text:SetText("") |
end |
if db.useGradient then |
self.overlay:SetScript("OnUpdate", function(f, elapsed) |
local p = self.overlay:GetProgress() |
self.timer.color:SetVertexColor(p, 1-p, 0, db.overlayAlpha) |
end) |
else |
self.timer.color:SetVertexColor(db.colors.overlay.r, db.colors.overlay.g, db.colors.overlay.b, db.overlayAlpha) |
self.overlay:SetScript("OnUpdate", nil) |
end |
self:RegisterEvent("PLAYER_REGEN_ENABLED") |
self:RegisterEvent("PLAYER_REGEN_DISABLED") |
else |
if self.timer then self.timer:Hide() end |
self:UnregisterEvent("PLAYER_REGEN_ENABLED") |
self:UnregisterEvent("PLAYER_REGEN_DISABLED") |
if db.usehpgradient then |
self.timer:SetStatusBarColor(1*(1-(value/10)), 1*(value/10), 0, db.hptimeralpha) |
end |
end |
end |
function PowerUp:PLAYER_ENTERING_WORLD() |
pguid = UnitGUID("player") |
self:RefreshPowers("player", powerType) |
-- All the latency-stuff is there to offset possible fluctuations in lag causing the timer |
-- to not be exactly 10s every time. See note above for a few more details. That also |
-- explains why we're first checking if there was any active prior to combat. |
function PowerUp:StartTimer() |
if InCombatLockdown() then return end |
local prior = nil |
local num = UnitPower("player", SPELL_POWER_HOLY_POWER) |
for i=1, num do |
if self.bars[i].wasactive then |
prior = i |
end |
end |
local _, _, latency = GetNetStats() |
latency = format("%.02f", (latency/1000)) |
if not prior then |
self.timer:SetMinMaxValues(0, 10+latency) |
self.timer:SetValue(10+latency) |
else |
self.timer:SetMinMaxValues(0, (10+latency)-self.bars[prior].alreadyElapsed) |
self.timer:SetValue((10+latency) - self.bars[prior].alreadyElapsed) |
end |
self:SetScript("OnUpdate", onUpdate) |
self.timer:Show() |
end |
-- Timer was running, so pause it for now. |
-- Save how much has already elapsed if there are charges available |
function PowerUp:PLAYER_REGEN_DISABLED() |
if self.overlay:IsPlaying() then |
self.overlay:Pause() |
local num = UnitPower("player", SPELL_POWER_HOLY_POWER) |
if self.timer:IsVisible() and num > 0 then |
local min, max = self.timer:GetMinMaxValues() |
self:SetScript("OnUpdate", nil) |
self.timer:Hide() |
self.bars[num].wasactive = true |
self.bars[num].alreadyElapsed = 10 - self.timer:GetValue() |
self.timer:SetValue(0) |
end |
end |
-- Resume playing |
-- If there are any charges, start a timer! |
function PowerUp:PLAYER_REGEN_ENABLED() |
self.overlay:Play() |
local num = UnitPower("player", SPELL_POWER_HOLY_POWER) |
if num > 0 then |
self:StartTimer() |
end |
end |
-- Watch for changes when @ max |
function PowerUp:COMBAT_LOG_EVENT_UNFILTERED(timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, _, _, _, _, power) |
if sourceGUID == pguid and event == "SPELL_ENERGIZE" and power == 9 then |
self:RefreshPowers("player", powerType) |
function PowerUp:PLAYER_ENTERING_WORLD() |
pguid = UnitGUID("player") |
self:RefreshPowers("player", powerType) |
end |
-- use CLEU when at max charges |
function PowerUp:COMBAT_LOG_EVENT_UNFILTERED(timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, spellId, spellName, _, _, powerType) |
if sourceGUID == pguid and event == "SPELL_ENERGIZE" and powerType == 9 then |
if InCombatLockdown() then |
self.bars[db.max].wasactive = false |
else |
self:StartTimer() |
end |
end |
end |
function PowerUp:RefreshPowers(unit, power) |
if unit ~= "player" or (power and power ~= powerType) then return end |
local num = UnitPower("player", powerName) |
for i = 1, db.max do |
if(i <= num) then |
self.bars[i]:SetAlpha(1) |
-- we have more than 1, so check if the prior one was active before we |
-- entered combat and remove the flag if true. |
if num > 1 and db.showhptimer then |
if self.bars[num-1].wasactive then |
self.bars[num-1].wasactive = false |
end |
end |
else |
self.bars[i]:SetAlpha(0) |
-- Check if the charge was active before combat and make a note |
-- that it expired during combat. |
if db.showhptimer and self.bars[i].wasactive then |
self.bars[i].wasactive = false |
end |
end |
end |
if db.showOverlay then |
if num ~= self.prior or num == db.max then |
-- If paused it means it was running but is now invalid, so stop it |
if self.overlay:IsPaused() then self.overlay:Stop() end |
if not InCombatLockdown() then |
if num < self.prior then -- lost a charge, check if timer should still be running |
if num > 0 then |
self.overlay:Restart() |
end |
elseif num > self.prior then -- gained a charge, restart timer |
self.overlay:Restart() |
end |
if num == db.max then |
self.overlay:Restart() |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
else |
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
end |
end |
if db.showhptimer and not InCombatLockdown() then |
if num == 0 then -- hiiiide |
self.timer:SetValue(0) |
self.timer:Hide() |
self:SetScript("OnUpdate", nil) |
elseif num > self.last then |
-- increase, start new timer if applicable |
self:StartTimer() |
elseif num < self.last then |
-- decrease |
self:StartTimer() |
end |
elseif not db.showhptimer and self.timer:IsVisible() then |
self.timer:Hide() |
self:SetScript("OnUpdate", nil) |
end |
-- if we're at max, UNIT_POWER won't fire until one charge expires, |
-- which means the timer won't update properly. |
if num == db.max then |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
else |
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
end |
self.last = num |
if db.hidewhenempty and num == 0 then |
self.frame:Hide() |
self:Hide() |
else |
if num == 0 then self.timer:Hide() end |
self.frame:Show() |
self:Show() |
end |
self.prior = num |
end |
--[[ |
PowerUp - Warlock Module |
]] |
local _, PowerUp = ... |
-- local PowerUp = _G.PowerUp |
if PowerUp.class ~= "WARLOCK" then return end |
local db |
-- Setup frames |
function PowerUp:Init() |
db = PowerUpDB |
db.max = 3 |
powerType = "SOUL_SHARDS" |
powerName = SPELL_POWER_SOUL_SHARDS |
self:Workshop() |
self:Paintshop() |
for i=1, #self.bars do |
self.bars[i]:SetVertexColor(db.colors.warlock.r, db.colors.warlock.g, db.colors.warlock.b) |
end |
self:RegisterEvent("UNIT_POWER") |
self.UNIT_POWER = self.RefreshPowers |
local num = UnitPower("player", powerName) |
if db.hidewhenempty and num == 0 then |
self.frame:Hide() |
self:Hide() |
else |
self.frame:Show() |
self:Show() |
end |
for i = 1, db.max do |
local addon, PowerUp = ... |
local ADDON, ns = ... |
local db |
-- Defaults |
local defaults = { |
point = "CENTER", |
x = 0, |
y = 200, |
max = 3, |
hidewhenempty = true, -- hides the frame when empty |
colors = { |
WARLOCK = { |
warlock = { |
r = .58, |
g = .51, |
b = .79, |
}, |
PALADIN = { |
paladin = { |
r = 1, |
g = 1, |
b = 0, |
b = 0, |
} |
}, |
showOverlay = true, -- positions an overlay on top of the holypowers guesstimating when the next power will expire |
overlayAlpha = .5, -- the alpha of the overlay |
useGradient = true, -- if true, the overlay will fade from green (@max) to red (@min), otherwise the color below is used |
hideBlizz = true, -- hide default Blizzard powerbars |
showhptimer = true, -- positions an overlay on top of the holypowers guesstimating when the next power will expire |
showtimertext = false, -- shows a textual-timer next to the bar, only works if 'showhptimer' is true |
hptimeralpha = .5, -- the alpha of the overlay |
usehpgradient = true, -- if true, the overlay will fade from green (@max) to red (@min), otherwise the color below is used |
hideblizz = true, -- hide default Blizzard powerbars |
} |
PowerUp.defaults = defaults |
local title, desc = select(2, GetAddOnInfo(addon)) |
local f = CreateFrame("Frame", nil, InterfaceOptionsFramePanelContainer) |
PowerUp.panel = f |
f.name = title |
f:Hide() |
f:SetScript("OnShow", function(self) |
db = PowerUpDB |
-- Header |
local header = self:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge") |
header:SetPoint("TOPLEFT", 16, -16) |
header:SetText(title) |
-- Tagline |
local tag = self:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") |
tag:SetPoint("TOPLEFT", header, "BOTTOMLEFT", 0, -5) |
tag:SetText(desc) |
-- Hide when empty |
local hide = CreateFrame("CheckButton", "PowerUpHideCheck", f, "InterfaceOptionsCheckButtonTemplate") |
hide:SetChecked(db.hidewhenempty) |
hide:SetScript("OnClick", function(self) |
db.hidewhenempty = not db.hidewhenempty |
PowerUp:PLAYER_ENTERING_WORLD() |
end) |
hide:SetPoint("TOPLEFT", tag, "BOTTOMLEFT", 0, -20) |
_G[hide:GetName() .. "Text"]:SetText("Hide the bar when empty") |
hide.tooltipText = "Hide" |
hide.tooltipRequirement = "Toggle to hide the bar when you have no charges." |
-- Hide Blizzard |
local hb = CreateFrame("CheckButton", "PowerUpHideBlizzCheck", f, "InterfaceOptionsCheckButtonTemplate") |
hb:SetChecked(db.hideBlizz) |
hb:SetScript("OnClick", function(self) |
db.hideBlizz = not db.hideBlizz |
PowerUp:HideBlizzard() |
end) |
hb:SetPoint("TOPLEFT", hide, "BOTTOMLEFT", 0, -2) |
_G[hb:GetName() .. "Text"]:SetText("Hide Blizzard frames") |
hb.tooltipText = "Hide Blizzard" |
hb.tooltipRequirement = "Toggle to hide Blizzards own frames." |
local width = CreateFrame("Slider", "PowerUpWidthSlider", f, "OptionsSliderTemplate") |
width.text = _G[width:GetName() .. "Text"] |
width:SetValueStep(1) |
width:SetMinMaxValues(50, 300) |
width:SetScript("OnValueChanged", function(self) |
db.width = self:GetValue() |
width.text:SetText(string.format("Width: %d", db.width)) |
PowerUp:Paintshop() |
end) |
width:SetPoint("TOPLEFT", hb, "BOTTOMLEFT", 0, -20) |
width:SetValue(db.width) |
width.text:SetText(string.format("Width: %d", db.width)) |
width.tooltipText = "Width" |
width.tooltipRequirement = "Set the width of the frame." |
local height = CreateFrame("Slider", "PowerUpHeightSlider", f, "OptionsSliderTemplate") |
height.text = _G[height:GetName() .. "Text"] |
height:SetValueStep(1) |
height:SetMinMaxValues(5, 25) |
height:SetScript("OnValueChanged", function(self) |
db.height = self:GetValue() |
height.text:SetText(string.format("Height: %d", db.height)) |
PowerUp:Paintshop() |
end) |
height:SetPoint("LEFT", width, "RIGHT", 50, 0) |
height:SetValue(db.height) |
height.text:SetText(string.format("Height: %d", db.height)) |
height.tooltipText = "Height" |
height.tooltipRequirement = "Set the height of the frame." |
if PowerUp.class == "PALADIN" then |
local header = self:CreateFontString(nil, "ARTWORK", "GameFontNormal") |
header:SetText("Paladin") |
header:SetPoint("TOPLEFT", width, "BOTTOMLEFT", 0, -50) |
-- Overlay |
local overlay = CreateFrame("CheckButton", "PowerUpOverlayCheck", f, "InterfaceOptionsCheckButtonTemplate") |
overlay:SetChecked(db.showOverlay) |
overlay:SetScript("OnClick", function(self) |
db.showOverlay = not db.showOverlay |
PowerUp:ToggleOverlay() |
end) |
overlay:SetPoint("TOPLEFT", header, "BOTTOMLEFT", 0, -5) |
_G[overlay:GetName() .. "Text"]:SetText("Show overlay") |
overlay.tooltipText = "Overlay" |
overlay.tooltipRequirement = "Toggle to display the overlay. Please note that you might have to lose or gain a charge for this to take proper effect." |
-- Gradient |
local gradient = CreateFrame("CheckButton", "PowerUpGradientCheck", f, "InterfaceOptionsCheckButtonTemplate") |
gradient:SetChecked(db.useGradient) |
gradient:SetScript("OnClick", function(self) |
db.useGradient = not db.useGradient |
PowerUp:ToggleOverlay() |
end) |
gradient:SetPoint("TOPLEFT", overlay, "BOTTOMLEFT", 0, -2) |
_G[gradient:GetName() .. "Text"]:SetText("Color overlay with gradient") |
gradient.tooltipText = "Gradient" |
gradient.tooltipRequirement = "Toggle to color the overlay with a gradient ranging from green to red." |
end |
self:SetScript("OnShow", nil) |
end) |
InterfaceOptions_AddCategory(f) |
Thanks to lorelye over @ wowace forums for name suggestion |
]] |
local addon, PowerUp = ... |
local ADDON, ns = ... |
local db |
PowerUp = CreateFrame("Frame", "PowerUpFrame", UIParent) |
PowerUp.class = select(2, UnitClass("player")) |
PowerUp:SetScript("OnEvent", function(self, event, ...) |
self[event](self, ...) |
end) |
PowerUp:RegisterEvent("ADDON_LOADED") |
function PowerUp:ADDON_LOADED(name) |
if name ~= addon then return end |
if name ~= ADDON then return end |
if (self.class == "PALADIN" or self.class == "WARLOCK") then |
PowerUpDB = PowerUpDB or self.defaults |
if (PowerUp.class == "PALADIN" or PowerUp.class == "WARLOCK") then |
PowerUpDB = PowerUpDB or ns.defaults |
-- in case there's a new option (trial) |
for k,v in pairs(self.defaults) do |
for k,v in pairs(ns.defaults) do |
if type(PowerUpDB[k]) == "nil" then |
PowerUpDB[k] = v |
end |
end |
db = PowerUpDB |
self.frame:SetBackdrop({bgFile = "Interface\\ChatFrame\\ChatFrameBackground", insets = {top = -1, left = -1, bottom = -1, right = -1},}) |
self.frame:SetBackdropColor(0, 0, 0, .5) |
self.frame:SetPoint(db.point, UIParent, db.x, db.y) |
self.bars = {} |
self:SetBackdrop({bgFile = "Interface\\ChatFrame\\ChatFrameBackground", insets = {top = -1, left = -1, bottom = -1, right = -1},}) |
self:SetBackdropColor(0, 0, 0, .5) |
self:SetSize(db.width, db.height) |
self:SetPoint(db.point, UIParent, db.x, db.y) |
for i=1, db.max do |
local t = self:CreateTexture(nil, "OVERLAY") |
if i == 1 then |
t:SetPoint("LEFT", self, "LEFT") |
else |
t:SetPoint("TOPLEFT", self.bars[i - 1], "TOPRIGHT", 1, 0) |
end |
t:SetTexture(db.texture) |
t:SetVertexColor(0, 1, 0) |
t:SetWidth((db.width-2)/db.max) |
t:SetHeight(db.height) |
self.frame:SetMovable(true) |
self.frame:SetScript("OnMouseDown", function() |
t.wasactive = false |
self.bars[i] = t |
end |
self:SetMovable(true) |
self:SetScript("OnMouseDown", function() |
if(IsShiftKeyDown()) then |
self.frame:ClearAllPoints() |
self.frame:StartMoving() |
self:ClearAllPoints() |
self:StartMoving() |
end |
end) |
self.frame:SetScript("OnMouseUp", function() |
self.frame:StopMovingOrSizing() |
db.point, _, _, db.x, db.y = self.frame:GetPoint() |
self:SetScript("OnMouseUp", function() |
self:StopMovingOrSizing() |
db.point, _, _, db.x, db.y = self:GetPoint() |
end) |
self:HideBlizzard() |
-- Hide Blizzard if applicable |
if (PaladinPowerBar:IsVisible() or ShardBarFrame:IsVisible()) and db.hideblizz then |
PaladinPowerBar:Hide() |
PaladinPowerBar:UnregisterAllEvents() |
ShardBarFrame:Hide() |
ShardBarFrame:UnregisterAllEvents() |
end |
self:Init() |
end |
self:UnregisterEvent("ADDON_LOADED") |
end |
function PowerUp:HideBlizzard() |
if db.hideBlizz then |
PaladinPowerBar:Hide() |
PaladinPowerBar:UnregisterAllEvents() |
ShardBarFrame:Hide() |
ShardBarFrame:UnregisterAllEvents() |
else |
PaladinPowerBar_OnLoad(PaladinPowerBar) |
ShardBar_OnLoad(ShardBarFrame) |
end |
end |
-- Create frames |
function PowerUp:Workshop() |
self.bars = {} |
for i=1, db.max do |
local t = self.frame:CreateTexture(nil, "OVERLAY") |
if i == 1 then |
t:SetPoint("LEFT", self.frame, "LEFT") |
else |
t:SetPoint("TOPLEFT", self.bars[i - 1], "TOPRIGHT", 1, 0) |
end |
self.bars[i] = t |
end |
end |
-- Style bars |
function PowerUp:Paintshop() |
self.frame:SetSize(db.width, db.height) |
for i=1, #self.bars do |
self.bars[i]:SetTexture(db.texture) |
self.bars[i]:SetVertexColor(db.colors[self.class].r, db.colors[self.class].g, db.colors[self.class].b) |
self.bars[i]:SetWidth((db.width-2)/db.max) |
self.bars[i]:SetHeight(db.height) |
end |
end |
PowerUp.frame = CreateFrame("Frame", nil, UIParent) |
PowerUp.frame:SetScript("OnEvent", function(self, event, ...) return PowerUp[event] and PowerUp[event](PowerUp, ...) end) |
function PowerUp:RegisterEvent(event) return self.frame:RegisterEvent(event) end |
function PowerUp:UnregisterEvent(event) return self.frame:UnregisterEvent(event) end |
PowerUp:RegisterEvent("ADDON_LOADED") |