mirror of
https://github.com/brues-code/pfUI.git
synced 2026-11-03 17:36:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee9bd9e31a | ||
|
|
df7c13651b | ||
|
|
646174fc60 | ||
|
|
1c1be19711 | ||
|
|
eb68642d21 | ||
|
|
d2088c455f | ||
|
|
9e2a89de62 | ||
|
|
1a6314aa60 | ||
|
|
70bfc1c443 | ||
|
|
d143e91f81 | ||
|
|
14ae659f2a | ||
|
|
9187155ea6 | ||
|
|
465e87d7d2 | ||
|
|
dd4ebda0a9 | ||
|
|
1abeafcec2 | ||
|
|
cde16e7fbe | ||
|
|
a376afb2d7 | ||
|
|
c8cc7bde79 | ||
|
|
d7e732981b | ||
|
|
2909679d62 |
+1
-1
@@ -13,7 +13,7 @@ do
|
||||
-- on. There pfUI does load, and will throw wherever it reaches for something
|
||||
-- the installed DLL doesn't have yet -- the popup names the cause so those
|
||||
-- errors aren't a mystery.
|
||||
local PFUI_CLASSIC_API_MIN = 11813 -- (X*10000 + Y*100 + Z)
|
||||
local PFUI_CLASSIC_API_MIN = 11901 -- (X*10000 + Y*100 + Z)
|
||||
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
|
||||
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
|
||||
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
|
||||
|
||||
@@ -23,7 +23,7 @@ Provides:
|
||||
- C_NamePlate - Event-driven nameplates (no more per-frame WorldFrame scanning) with real per-plate unit tokens and GUIDs
|
||||
- C_UnitAuras - Replaces over 3k lines of hand-rolled aura tracking
|
||||
- C_Spell - Replaces thousands of hardcoded spell names for each locale and powers castbar
|
||||
- Real cast bars - Actual cast/channel timing, spellID and interrupt state for any unit (C_Spell.UnitCastingInfo)
|
||||
- Real cast bars - Actual cast/channel timing, spellID and interrupt state for any unit (C_Spell.UnitCastingInfo), filled and counted down engine-side (UnitCastingDuration, StatusBar:SetTimerDuration, duration text bindings) with no Lua OnUpdate
|
||||
- Focus - A genuine `focus` unit plus `/focus` and `/clearfocus` (FocusUnit / ClearFocus)
|
||||
- C_EquipmentSet - Full Equipment Manager with GUID-tracked gear sets (tells apart duplicate/enchanted items)
|
||||
- Instant Bag Sorting - C_Container.MoveItem / SwapItems instead of cursor pickup/drop
|
||||
|
||||
+64
-27
@@ -992,28 +992,53 @@ function pfUI.api.GetBorderSize(pref)
|
||||
end
|
||||
end
|
||||
|
||||
-- The scale UIParent ends up with: one UI unit per screen pixel when pixel
|
||||
-- perfect, else the game's own. The game applies its scale only after the
|
||||
-- add-ons and their saved variables are loaded (CGGameUI::Initialize ends in
|
||||
-- OnDisplaySizeChanged), so at load time it is worked out from the same
|
||||
-- options and rules: uiScale while useUiScale is on and it is at least 0.64,
|
||||
-- else 768 over the window height kept within 0.9 and 1. Windows narrower
|
||||
-- than 4:3 cap either at 3/4 of their aspect.
|
||||
local function GetUIScale()
|
||||
if pfUI_config.global.pixelperfect == "1" then return PixelUtil.GetPixelToUIUnitFactor() end
|
||||
|
||||
local width, height = GetPhysicalScreenSize()
|
||||
local aspect = GetCVar("widescreen") == "1" and width / height or 4/3
|
||||
local function fit(scale)
|
||||
return aspect < 4/3 and math.min(scale, aspect * .75) or scale
|
||||
end
|
||||
|
||||
if GetCVar("useUiScale") == "1" then
|
||||
local scale = fit(tonumber(GetCVar("uiScale")))
|
||||
if scale >= .64 then return scale end
|
||||
end
|
||||
|
||||
return math.max(fit(height > 768 and 768 / height or 1), .9)
|
||||
end
|
||||
|
||||
-- [ UpdateUIScale ]
|
||||
-- Gives UIParent the pixel perfect scale when enabled, else leaves the game's
|
||||
-- own. The engine puts its own scale back on every window resize and scale
|
||||
-- option change, so this runs again on each UI_SCALE_CHANGED. Borders keep
|
||||
-- the pixel size they were built with, so a change of it offers a reload once.
|
||||
function pfUI.api.UpdateUIScale()
|
||||
if pfUI_config.global.pixelperfect == "1" then
|
||||
UIParent:SetScale(PixelUtil.GetPixelToUIUnitFactor())
|
||||
end
|
||||
|
||||
if not pfUI.pixel or pfUI.scaleprompt then return end
|
||||
if math.abs(PixelUtil.GetPixelToUIUnitFactor() / UIParent:GetScale() - pfUI.pixel) < .0001 then return end
|
||||
|
||||
pfUI.scaleprompt = true
|
||||
CreateQuestionDialog(T["The UI-Scale has changed. Reload the UI to redraw the borders?"], ReloadUI)
|
||||
end
|
||||
|
||||
-- [ GetPerfectPixel ]
|
||||
-- Returns a number that equals a real pixel on regular scaled frames.
|
||||
-- Respects the current UI-scale and calculates a real pixel based on
|
||||
-- the screen resolution and the 768px sized drawlayer.
|
||||
-- Returns the size of one physical pixel in UIParent units.
|
||||
function pfUI.api.GetPerfectPixel()
|
||||
if pfUI.pixel then return pfUI.pixel end
|
||||
|
||||
if pfUI_config.appearance.border.pixelperfect == "1" then
|
||||
local scale = GetCVar("useUiScale") == "1" and GetCVar("uiScale") or "1"
|
||||
local resolution = GetCVar("gxResolution")
|
||||
local _, _, screenwidth, screenheight = strfind(resolution, "(.+)x(.+)")
|
||||
|
||||
pfUI.pixel = 768 / screenheight / scale
|
||||
pfUI.pixel = pfUI.pixel > 1 and 1 or pfUI.pixel
|
||||
|
||||
-- autodetect and zoom for HiDPI displays
|
||||
if pfUI_config.appearance.border.hidpi == "1" then
|
||||
pfUI.pixel = pfUI.pixel < .5 and pfUI.pixel * 2 or pfUI.pixel
|
||||
end
|
||||
else
|
||||
pfUI.pixel = .7
|
||||
end
|
||||
pfUI.pixel = PixelUtil.GetPixelToUIUnitFactor() / GetUIScale()
|
||||
|
||||
pfUI.backdrop = {
|
||||
bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0,
|
||||
@@ -1158,8 +1183,11 @@ end
|
||||
-- [ Set Castbar Shield ]
|
||||
-- Colors a castbar for a cast that none of the player's own interrupts or
|
||||
-- silences can stop. The bar and the icon's border both take the shield color,
|
||||
-- so the state still reads on a bar configured without an icon. Not shielded
|
||||
-- restores the normal cast/channel color and the configured border.
|
||||
-- so the state still reads on a bar configured without an icon. The fill also
|
||||
-- gets diagonal stripes, tiled at the image's own size so they keep their
|
||||
-- spacing as the fill grows or shrinks. They sit on OVERLAY, under the bar's
|
||||
-- text on the same layer. Not shielded restores the normal cast/channel color
|
||||
-- and the configured border.
|
||||
-- 'bar' [frame] the castbar statusbar
|
||||
-- 'icon' [frame] the icon frame, or nil for a bar without one
|
||||
-- 'shielded' [bool] whether the cast is uninterruptible
|
||||
@@ -1171,6 +1199,18 @@ function pfUI.api.SetCastbarShield(bar, icon, shielded, isChannel)
|
||||
bar:SetStatusBarColor(pfUI.api.GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
|
||||
end
|
||||
|
||||
if shielded and not bar.shieldstripes then
|
||||
bar.shieldstripes = bar:CreateTexture(nil, "OVERLAY")
|
||||
bar.shieldstripes:SetTexture(pfUI.media["img:stripes"])
|
||||
bar.shieldstripes:SetHorizTile(true)
|
||||
bar.shieldstripes:SetVertTile(true)
|
||||
bar.shieldstripes:SetVertexColor(1, 1, 1, .3)
|
||||
bar.shieldstripes:SetAllPoints(bar:GetStatusBarTexture())
|
||||
end
|
||||
if bar.shieldstripes then
|
||||
bar.shieldstripes:SetShown(shielded)
|
||||
end
|
||||
|
||||
local iconborder = icon and icon.backdrop
|
||||
if not iconborder then return end
|
||||
|
||||
@@ -1364,10 +1404,10 @@ function pfUI.api.EnableAutohide(frame, timeout, combat)
|
||||
|
||||
if not frame.hover.fade then
|
||||
frame.hover.fade = frame:CreateAnimationGroup()
|
||||
frame.hover.fade:SetToFinalAlpha(true)
|
||||
local fade = frame.hover.fade:CreateAnimation("Alpha")
|
||||
fade:SetChange(-1)
|
||||
fade:SetDuration(1)
|
||||
frame.hover.fade:SetScript("OnFinished", function() frame:SetAlpha(0) end)
|
||||
end
|
||||
|
||||
if combat then
|
||||
@@ -1469,13 +1509,10 @@ end
|
||||
-- return object
|
||||
function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
|
||||
-- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren()
|
||||
-- below, and the traceback stops here — useless, since all callers share this
|
||||
-- line. pfUI shadows Lua's `error` (dropping the level arg and not throwing),
|
||||
-- so fold the caller frame in via debugstack to name the offending skin, then
|
||||
-- bail so we don't fall through and crash on frame:GetRegions() anyway.
|
||||
-- below, where the message would name this line, shared by every caller;
|
||||
-- level 2 names the offending skin instead.
|
||||
if type(frame) ~= "table" or not frame.GetRegions then
|
||||
error("GetNoNameObject: invalid parent frame\n" .. debugstack(2, 3, 0))
|
||||
return
|
||||
error("GetNoNameObject: invalid parent frame", 2)
|
||||
end
|
||||
|
||||
local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
|
||||
|
||||
+1
-2
@@ -129,9 +129,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("appearance", "border", "color", "0.2,0.2,0.2,1")
|
||||
pfUI:UpdateConfig("appearance", "border", "shadow", "0")
|
||||
pfUI:UpdateConfig("appearance", "border", "shadow_intensity", ".35")
|
||||
pfUI:UpdateConfig("appearance", "border", "pixelperfect", "1")
|
||||
pfUI:UpdateConfig("appearance", "border", "force_blizz", "0")
|
||||
pfUI:UpdateConfig("appearance", "border", "hidpi", "1")
|
||||
pfUI:UpdateConfig("appearance", "border", "default", "3")
|
||||
pfUI:UpdateConfig("appearance", "border", "nameplates", "-1")
|
||||
pfUI:UpdateConfig("appearance", "border", "actionbars", "-1")
|
||||
@@ -677,6 +675,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("bars", nil, "oomcolor", ".2,.2,1,1")
|
||||
pfUI:UpdateConfig("bars", nil, "showna", "1")
|
||||
pfUI:UpdateConfig("bars", nil, "nacolor", ".3,.3,.3,1")
|
||||
pfUI:UpdateConfig("bars", nil, "procglow", "1")
|
||||
pfUI:UpdateConfig("bars", nil, "showequipped", "1")
|
||||
pfUI:UpdateConfig("bars", nil, "eqcolor", ".2,.8,.2,.2")
|
||||
pfUI:UpdateConfig("bars", nil, "shiftdrag", "1")
|
||||
|
||||
+8
-7
@@ -85,7 +85,7 @@ local function SetupCooldown(cooldown)
|
||||
cooldown:SetReverse(kind == "aura")
|
||||
cooldown:SetMinimumCountdownDuration(kind == "spell" and tonumber(C.cooldown.threshold) * 1000 or 0)
|
||||
cooldown:SetCountdownFormatter(GetFormatter())
|
||||
UpdateFont(cooldown, cooldown:GetParent():GetHeight())
|
||||
UpdateFont(cooldown, cooldown:GetParent():GetUnscaledHeight())
|
||||
end
|
||||
|
||||
-- applies the "foreign" timers to another addon's cooldown. They only take
|
||||
@@ -107,7 +107,7 @@ local function SetupForeign(cooldown)
|
||||
|
||||
cooldown:SetMinimumCountdownDuration(tonumber(C.cooldown.threshold) * 1000)
|
||||
cooldown:SetCountdownFormatter(GetFormatter())
|
||||
UpdateFont(cooldown, (cooldown:GetParent() or cooldown):GetHeight())
|
||||
UpdateFont(cooldown, (cooldown:GetParent() or cooldown):GetUnscaledHeight())
|
||||
end
|
||||
|
||||
-- runs as a pfUI cooldown's timer starts: a timer with a total brings back
|
||||
@@ -196,9 +196,10 @@ function pfUI.api.GetAuraTimer(duration, expirationTime)
|
||||
return start, dur
|
||||
end
|
||||
|
||||
-- [ GetColoredTimeString ] --
|
||||
-- 'remaining' [number] the time in seconds that should be converted
|
||||
-- return [string] the time as cooldown numbers show it
|
||||
function pfUI.api.GetColoredTimeString(remaining)
|
||||
return GetFormatter():FormatNumber(remaining)
|
||||
-- [ GetTimeFormatter ] --
|
||||
-- The formatter cooldown numbers use, for other timers to show time the same
|
||||
-- way. Hold it rather than a copy: a settings change updates it in place.
|
||||
-- return [formatter] a NumericRuleFormatter
|
||||
function pfUI.api.GetTimeFormatter()
|
||||
return GetFormatter()
|
||||
end
|
||||
|
||||
+5
-10
@@ -601,7 +601,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
|
||||
|
||||
button:HookScript("OnClick", function()
|
||||
local DropDownListWidth = DropDownList1:GetWidth()
|
||||
local DropDownFrameWidth = frame.backdrop:GetWidth()
|
||||
local DropDownFrameWidth = frame.backdrop:GetUnscaledWidth()
|
||||
if DropDownListWidth < DropDownFrameWidth then
|
||||
local diff = DropDownFrameWidth - DropDownListWidth
|
||||
DropDownList1:SetWidth(DropDownList1:GetWidth() + diff)
|
||||
@@ -834,10 +834,7 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
|
||||
end)
|
||||
|
||||
infobox:SetScript("OnUpdate", function()
|
||||
local time = infobox.lastshow + infobox.duration - GetTime()
|
||||
infobox.timeout:SetValue(time)
|
||||
|
||||
if GetTime() > infobox.lastshow + infobox.duration then
|
||||
if infobox.duration:HasExpired() then
|
||||
if not infobox.fade:IsPlaying() then infobox.fade:Play() end
|
||||
elseif MouseIsOverHitRect(this) then
|
||||
this:SetAlpha(max(0.4, this:GetAlpha() - .1))
|
||||
@@ -857,6 +854,8 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
|
||||
infobox.timeout = CreateFrame("StatusBar", nil, infobox)
|
||||
infobox.timeout:SetStatusBarTexture(pfUI.media["img:bar"])
|
||||
infobox.timeout:SetStatusBarColor(.3,1,.8,1)
|
||||
infobox.duration = C_DurationUtil.CreateDuration()
|
||||
infobox.timeout:SetTimerDuration(infobox.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
|
||||
|
||||
infobox:ClearAllPoints()
|
||||
infobox.text:SetAllPoints(infobox)
|
||||
@@ -872,11 +871,7 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
|
||||
end
|
||||
|
||||
infobox.text:SetText(text)
|
||||
infobox.timeout:SetMinMaxValues(0, time)
|
||||
infobox.timeout:SetValue(time)
|
||||
|
||||
infobox.duration = time
|
||||
infobox.lastshow = GetTime()
|
||||
infobox.duration:SetTimeFromStart(GetTime(), time)
|
||||
|
||||
infobox:SetSize(infobox.text:GetStringWidth() + 50, height)
|
||||
infobox:SetParent(parent)
|
||||
|
||||
+180
-195
@@ -362,7 +362,7 @@ pfUI.events:RegisterCallback("aggro:changed", function(_, old, new)
|
||||
if frame.aggro then
|
||||
local guid = UnitGUID(frame.label .. frame.id)
|
||||
if guid and (guid == old or guid == new) then
|
||||
frame.update_combat = true
|
||||
pfUI.uf:QueueUpdate(frame, "combat")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -498,10 +498,18 @@ function pfUI.uf:UpdateVisibility()
|
||||
self.chain = visibility ~= "hide" and unitstr:match("^(.+)target$") or nil
|
||||
if self.chain then
|
||||
local guid = UnitName(unitstr) and UnitGUID(unitstr)
|
||||
if guid ~= self.unitguid then self.update_full = true end
|
||||
if guid ~= self.unitguid then pfUI.uf:QueueUpdate(self, "full") end
|
||||
self.unitguid = guid
|
||||
self:RegisterUnitEvents(guid and CHAIN_TOKENS[unitstr] and unitstr or guid)
|
||||
else
|
||||
-- A pet frame's token gets the pet's unit events, but a new pet behind it
|
||||
-- (summoned, or remapped by raid.lua) comes with none of its own; the
|
||||
-- visibility timer's GUID compare turns it into the full refresh.
|
||||
if self.tick then
|
||||
local guid = UnitGUID(unitstr)
|
||||
if guid ~= self.unitguid then pfUI.uf:QueueUpdate(self, "full") end
|
||||
self.unitguid = guid
|
||||
end
|
||||
self:RegisterUnitEvents(visibility ~= "hide" and unitstr or nil)
|
||||
end
|
||||
self:RegisterChainEvents(self.chain)
|
||||
@@ -773,7 +781,6 @@ function pfUI.uf:UpdateConfig()
|
||||
if f.config.hitindicator == "1" then
|
||||
f.feedbackText:SetFont(pfUI.media[f.config.hitindicatorfont], f.config.hitindicatorsize, "OUTLINE")
|
||||
f.feedbackFontHeight = f.config.hitindicatorsize
|
||||
f.feedbackStartTime = GetTime()
|
||||
if f.config.portrait == "bar" or f.config.portrait == "off" then
|
||||
f.feedbackText:SetParent(f.texts)
|
||||
f.feedbackText:ClearAllPoints()
|
||||
@@ -785,6 +792,7 @@ function pfUI.uf:UpdateConfig()
|
||||
end
|
||||
f.combatfeedback = true
|
||||
else
|
||||
f.feedback:Stop()
|
||||
f.feedbackText:Hide()
|
||||
f.combatfeedback = nil
|
||||
end
|
||||
@@ -853,12 +861,16 @@ function pfUI.uf:UpdateConfig()
|
||||
f.incHeal.texture:SetVertexColor(cr, cg, cb, ca)
|
||||
f.incHeal:Hide()
|
||||
|
||||
if f.config.verticalbar == "0" then
|
||||
f.incHeal:ClearAllPoints()
|
||||
f.incHeal:SetPoint("TOPLEFT", f.hp.bar, "TOPLEFT", 0, 0)
|
||||
-- the bar texture covers only the fill, so a prediction anchored to it
|
||||
-- starts at the edge of the health and follows it as the bar animates.
|
||||
-- An inverted bar fills with the missing health, so it starts at the bar.
|
||||
f.incHeal:ClearAllPoints()
|
||||
if f.config.invert_healthbar == "1" then
|
||||
f.incHeal:SetPoint(f.config.verticalbar == "0" and "TOPLEFT" or "BOTTOM", f.hp.bar)
|
||||
elseif f.config.verticalbar == "0" then
|
||||
f.incHeal:SetPoint("TOPLEFT", f.hp.bar:GetStatusBarTexture(), "TOPRIGHT")
|
||||
else
|
||||
f.incHeal:ClearAllPoints()
|
||||
f.incHeal:SetPoint("BOTTOM", f.hp.bar, "BOTTOM", 0, 0)
|
||||
f.incHeal:SetPoint("BOTTOMLEFT", f.hp.bar:GetStatusBarTexture(), "TOPLEFT")
|
||||
end
|
||||
|
||||
f.ressIcon:SetFrameLevel(16)
|
||||
@@ -1031,6 +1043,8 @@ end
|
||||
function pfUI.uf.OnShow()
|
||||
RegisterShownEvents(this)
|
||||
pfUI.uf:RefreshUnit(this, "all")
|
||||
-- flags left from while the frame was off screen, and the portrait's model
|
||||
pfUI.uf:QueueUpdate(this)
|
||||
end
|
||||
|
||||
function pfUI.uf.OnHide()
|
||||
@@ -1047,7 +1061,6 @@ function pfUI.uf.OnEvent()
|
||||
if event == "PLAYER_LOGOUT" then
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
this:SetScript("OnUpdate", nil)
|
||||
-- the visibility timer keeps ticking, so leaving this frame on its list
|
||||
-- would have UpdateVisibility re-register the unit events we just
|
||||
-- dropped -- straight back into the crash 132 this branch prevents
|
||||
@@ -1063,13 +1076,13 @@ function pfUI.uf.OnEvent()
|
||||
event == "RAID_ROSTER_UPDATE" or
|
||||
event == "PLAYER_UPDATE_RESTING"
|
||||
then
|
||||
this.update_indicators = true
|
||||
pfUI.uf:QueueUpdate(this, "indicators")
|
||||
end
|
||||
|
||||
-- abort on broken unitframes (e.g focus)
|
||||
if not this.label then return end
|
||||
|
||||
-- a hidden frame runs no OnUpdate, so its unit coming into existence has to
|
||||
-- a hidden frame does not refresh, so its unit coming into existence has to
|
||||
-- show it from here
|
||||
if VISIBILITY_EVENTS[event] then
|
||||
this:UpdateVisibility()
|
||||
@@ -1077,11 +1090,11 @@ function pfUI.uf.OnEvent()
|
||||
|
||||
-- update regular frames
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
this.update_full = true
|
||||
pfUI.uf:QueueUpdate(this, "full")
|
||||
-- Clear GUID tracking on zone change for full rebuild
|
||||
if pfUI.uf.ClearGuidTracking then pfUI.uf.ClearGuidTracking() end
|
||||
elseif this.label == "target" and event == "PLAYER_TARGET_CHANGED" then
|
||||
this.update_full = true
|
||||
pfUI.uf:QueueUpdate(this, "full")
|
||||
-- immediately update raid icon to avoid delay on target swap
|
||||
if this.raidIcon and this.config and this.config.raidicon == "1" then
|
||||
local raidIcon = UnitName("target") and GetRaidTargetIndex("target")
|
||||
@@ -1100,26 +1113,26 @@ function pfUI.uf.OnEvent()
|
||||
local oldGuid = pfUI.uf.guidTracker.frameToGuid[this]
|
||||
if newGuid ~= oldGuid then
|
||||
pfUI.uf.guidTracker.frameToGuid[this] = newGuid
|
||||
this.update_full = true
|
||||
pfUI.uf:QueueUpdate(this, "full")
|
||||
end
|
||||
else
|
||||
this.update_full = true
|
||||
pfUI.uf:QueueUpdate(this, "full")
|
||||
end
|
||||
elseif ( this.label == "raid" or this.label == "party" ) and event == "RAID_ROSTER_UPDATE" then
|
||||
-- Note: Smart GUID-based updates are handled in raid.lua OnUpdate
|
||||
-- Note: Smart GUID-based updates are handled in raid.lua UpdateRoster
|
||||
-- after frame IDs are reassigned. We don't set update_full here anymore
|
||||
-- for raid frames to avoid the freeze.
|
||||
if this.label == "party" then
|
||||
-- Party frames still need the old logic (no smart tracking yet)
|
||||
this.update_full = true
|
||||
pfUI.uf:QueueUpdate(this, "full")
|
||||
end
|
||||
-- Raid frames: update_full is set by raid.lua GUID tracker
|
||||
elseif this.label == "pet" and event == "UNIT_PET" then
|
||||
this.update_full = true
|
||||
pfUI.uf:QueueUpdate(this, "full")
|
||||
elseif this.label == "player" and (event == "PLAYER_AURAS_CHANGED" or event == "PLAYER_EQUIPMENT_CHANGED") then
|
||||
this.update_aura = true
|
||||
pfUI.uf:QueueUpdate(this, "aura")
|
||||
elseif this.label == "pet" and event == "UNIT_HAPPINESS" then
|
||||
this.update_full = true
|
||||
pfUI.uf:QueueUpdate(this, "full")
|
||||
elseif event == "UNIT_IN_RANGE_UPDATE" then
|
||||
-- registered for the frame's unit alone, and fired in the filter's lower
|
||||
-- case spelling, which a GUID's compare below would miss
|
||||
@@ -1131,224 +1144,180 @@ function pfUI.uf.OnEvent()
|
||||
-- GUID, so filtering on one of the two never wakes the frame twice.
|
||||
elseif arg1 and arg1 == this.eventunit then
|
||||
if event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" then
|
||||
this.update_portrait = true
|
||||
pfUI.uf:QueueUpdate(this, "portrait")
|
||||
elseif event == "UNIT_AURA" then
|
||||
this.update_aura = true
|
||||
pfUI.uf:QueueUpdate(this, "aura")
|
||||
elseif event == "UNIT_AURA_DELTA" then
|
||||
-- an aura that only refreshed or restacked leaves every aura in its
|
||||
-- slot, so the icons need their timers and stacks, not a rebuild
|
||||
if arg2.isFullUpdate or arg2.addedAuraInstanceIDs or arg2.removedAuraInstanceIDs then
|
||||
this.update_aura = true
|
||||
pfUI.uf:QueueUpdate(this, "aura")
|
||||
else
|
||||
this.update_auratime = true
|
||||
pfUI.uf:QueueUpdate(this, "auratime")
|
||||
end
|
||||
elseif event == "UNIT_POWER_UPDATE" or event == "UNIT_MAXPOWER" then
|
||||
-- every power of the unit reports, a cat druid's mana regen too; redraw
|
||||
-- for the one the bar shows, and for mana under a druid mana bar
|
||||
local _, shown = UnitPowerType(arg1)
|
||||
if arg2 == shown or (arg2 == "MANA" and this.druidmana) then
|
||||
this.update_base = true
|
||||
pfUI.uf:QueueUpdate(this, "base")
|
||||
end
|
||||
elseif event == "UNIT_FACTION" then
|
||||
-- the PvP flag, the reaction colour and the tapped grey
|
||||
this.update_indicators = true
|
||||
this.update_base = true
|
||||
pfUI.uf:QueueUpdate(this, "indicators")
|
||||
pfUI.uf:QueueUpdate(this, "base")
|
||||
elseif event == "UNIT_FLAGS" then
|
||||
this.update_combat = true
|
||||
pfUI.uf:QueueUpdate(this, "combat")
|
||||
elseif event == "UNIT_DISTANCE_CHECK_UPDATE" then
|
||||
-- the portrait trades the question mark for the model, the frame fades
|
||||
-- by sight, and an attacker that left with the member is let go
|
||||
this.update_portrait = true
|
||||
this.update_combat = true
|
||||
pfUI.uf:QueueUpdate(this, "portrait")
|
||||
pfUI.uf:QueueUpdate(this, "combat")
|
||||
elseif event == "UNIT_CONNECTION" then
|
||||
this.update_full = true
|
||||
pfUI.uf:QueueUpdate(this, "full")
|
||||
elseif event == "UNIT_COMBAT" then
|
||||
-- every hit starts the fade over
|
||||
this.feedback:Stop()
|
||||
CombatFeedback_OnCombatEvent(arg2, arg3, arg4, arg5)
|
||||
this.feedback:Play()
|
||||
elseif event == "INCOMING_RESURRECT_CHANGED" then
|
||||
this.update_indicators = true
|
||||
pfUI.uf:QueueUpdate(this, "indicators")
|
||||
else
|
||||
this.update_base = true
|
||||
pfUI.uf:QueueUpdate(this, "base")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Local reference for performance
|
||||
local _GetTime = GetTime
|
||||
-- Drain the update flags the events set, then hand a 3D portrait its unit.
|
||||
-- A frame that is not on screen keeps its flags; OnShow flushes them.
|
||||
local function Flush(frame)
|
||||
frame.queued = nil
|
||||
if not frame:IsVisible() then return end
|
||||
|
||||
-- Global cached time for libpredict and other functions
|
||||
pfUI.uf.now = 0
|
||||
|
||||
-- ============================================================================
|
||||
-- OnUpdate - draining the event-set update flags, heal prediction, and the
|
||||
-- two polls no event replaces (a UnitXP range threshold, the pet frames).
|
||||
-- ============================================================================
|
||||
function pfUI.uf.OnUpdate()
|
||||
local now = _GetTime()
|
||||
pfUI.uf.now = now
|
||||
|
||||
-- update combat feedback (no throttle - needs immediate feedback)
|
||||
if this.feedbackText then CombatFeedback_OnUpdate(arg1) end
|
||||
|
||||
-- Throttle raid/party frames for performance
|
||||
if this.label == "raid" or this.label == "party" then
|
||||
if (this.throttleTick or 0) > now then
|
||||
return
|
||||
end
|
||||
this.throttleTick = now + 0.1 -- Default: 10 FPS
|
||||
end
|
||||
|
||||
if this.label then
|
||||
-- what no event reports: a UnitXP range threshold, and the pet frames
|
||||
-- (created with a tick)
|
||||
if this.rangepoll or this.tick then
|
||||
-- Validate tick value - it should be an interval (e.g. 0.5), not a timestamp
|
||||
local tickInterval = this.tick
|
||||
if tickInterval and tickInterval > 10 then
|
||||
-- tick is a timestamp, not an interval - ignore it
|
||||
tickInterval = nil
|
||||
end
|
||||
-- Reset lastTick if it's invalid (much larger than now, e.g. from corrupted state)
|
||||
if this.lastTick and this.lastTick > now + 10 then
|
||||
this.lastTick = nil
|
||||
end
|
||||
if not this.lastTick then this.lastTick = now + (tickInterval or .5) end
|
||||
if this.lastTick < now then
|
||||
local unitstr = this.label .. this.id
|
||||
this.lastTick = now + (tickInterval or .5)
|
||||
|
||||
if this.rangepoll then pfUI.uf:RefreshUnitState(this) end
|
||||
|
||||
-- A pet's full refresh rescans every aura and the portrait, so only
|
||||
-- do it when the unit behind the frame changes or once a second;
|
||||
-- other ticks refresh the bars only.
|
||||
if this.tick then
|
||||
local guid = UnitGUID(unitstr)
|
||||
if guid ~= this.tickGuid or (this.tickFull or 0) < now then
|
||||
this.tickGuid = guid
|
||||
this.tickFull = now + 1
|
||||
pfUI.uf:RefreshUnit(this, "all")
|
||||
else
|
||||
pfUI.uf:RefreshUnit(this, "base")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Heal Prediction (throttled to 0.1s for responsiveness)
|
||||
if libpredict and this.incHeal then
|
||||
if not this.lastHealTick then this.lastHealTick = now end
|
||||
if this.lastHealTick < now then
|
||||
this.lastHealTick = now + 0.1
|
||||
|
||||
local unit = this.label .. this.id
|
||||
local heal = libpredict:UnitGetIncomingHeals(unit)
|
||||
|
||||
local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
|
||||
|
||||
if heal - health - maxHealth ~= this.predictstate then
|
||||
local overhealperc = tonumber(this.config.overhealperc)
|
||||
this.predictstate = heal - health - maxHealth
|
||||
|
||||
if heal > 0 and (health < maxHealth or overhealperc > 0 ) then
|
||||
local width = this.config.width
|
||||
local height = this.config.height
|
||||
|
||||
if this.config.verticalbar == "0" then
|
||||
local healthWidth = width * (health / maxHealth)
|
||||
local incWidth = width * heal / maxHealth
|
||||
if healthWidth + incWidth > width * (1+(overhealperc/100)) then
|
||||
incWidth = width * (1+overhealperc/100) - healthWidth
|
||||
end
|
||||
|
||||
if this.config.invert_healthbar == "1" then
|
||||
this.incHeal:SetWidth(incWidth)
|
||||
else
|
||||
this.incHeal:SetWidth(incWidth + healthWidth)
|
||||
end
|
||||
else
|
||||
local healthHeight = height * (health / maxHealth)
|
||||
local incHeight = height * heal / maxHealth
|
||||
if healthHeight + incHeight > height * (1+(overhealperc/100)) then
|
||||
incHeight = height * (1+overhealperc/100) - healthHeight
|
||||
end
|
||||
|
||||
if this.config.invert_healthbar == "1" then
|
||||
this.incHeal:SetHeight(incHeight)
|
||||
else
|
||||
this.incHeal:SetHeight(incHeight + healthHeight)
|
||||
end
|
||||
end
|
||||
|
||||
this.incHeal:Show()
|
||||
else
|
||||
this.incHeal:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- EVENT-BASED UPDATES (Health, Mana, Auras, etc.)
|
||||
-- ============================================================================
|
||||
|
||||
|
||||
-- process all queued unit events
|
||||
if this.update_full then
|
||||
if frame.update_full then
|
||||
-- process full updates, indicators and combat state included
|
||||
pfUI.uf:RefreshUnit(this, "all")
|
||||
pfUI.uf:RefreshUnit(frame, "all")
|
||||
|
||||
-- clear update caches
|
||||
this.update_full = nil
|
||||
this.update_base = nil
|
||||
this.update_aura = nil
|
||||
this.update_auratime = nil
|
||||
this.update_portrait = nil
|
||||
this.update_indicators = nil
|
||||
this.update_combat = nil
|
||||
frame.update_full = nil
|
||||
frame.update_base = nil
|
||||
frame.update_aura = nil
|
||||
frame.update_auratime = nil
|
||||
frame.update_portrait = nil
|
||||
frame.update_indicators = nil
|
||||
frame.update_combat = nil
|
||||
else
|
||||
if this.update_indicators then
|
||||
pfUI.uf:RefreshIndicators(this)
|
||||
this.update_indicators = nil
|
||||
if frame.update_indicators then
|
||||
pfUI.uf:RefreshIndicators(frame)
|
||||
frame.update_indicators = nil
|
||||
end
|
||||
|
||||
if this.update_combat then
|
||||
pfUI.uf:RefreshCombat(this)
|
||||
this.update_combat = nil
|
||||
if frame.update_combat then
|
||||
pfUI.uf:RefreshCombat(frame)
|
||||
frame.update_combat = nil
|
||||
end
|
||||
|
||||
-- process individual events; a rebuild re-times the icons as well
|
||||
if this.update_aura then
|
||||
pfUI.uf:RefreshUnit(this, "aura")
|
||||
this.update_aura = nil
|
||||
this.update_auratime = nil
|
||||
this.update_base = true
|
||||
elseif this.update_auratime then
|
||||
pfUI.uf:RefreshUnit(this, "auratime")
|
||||
this.update_auratime = nil
|
||||
if frame.update_aura then
|
||||
pfUI.uf:RefreshUnit(frame, "aura")
|
||||
frame.update_aura = nil
|
||||
frame.update_auratime = nil
|
||||
frame.update_base = true
|
||||
elseif frame.update_auratime then
|
||||
pfUI.uf:RefreshUnit(frame, "auratime")
|
||||
frame.update_auratime = nil
|
||||
end
|
||||
|
||||
if this.update_portrait then
|
||||
pfUI.uf:RefreshUnit(this, "portrait")
|
||||
this.update_portrait = nil
|
||||
this.update_base = true
|
||||
if frame.update_portrait then
|
||||
pfUI.uf:RefreshUnit(frame, "portrait")
|
||||
frame.update_portrait = nil
|
||||
frame.update_base = true
|
||||
end
|
||||
|
||||
if this.update_base then
|
||||
pfUI.uf:RefreshUnit(this, "base")
|
||||
this.update_base = nil
|
||||
if frame.update_base then
|
||||
pfUI.uf:RefreshUnit(frame, "base")
|
||||
frame.update_base = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- update portrait on first visible frame
|
||||
if this.portrait and this.portrait.model and this.portrait.model.update then
|
||||
this.portrait.model.lastUnit = UnitName(this.portrait.model.update)
|
||||
this.portrait.model:SetUnit(this.portrait.model.update)
|
||||
this.portrait.model:SetCamera(0)
|
||||
this.portrait.model.update = nil
|
||||
-- a model takes its unit only once it is on screen, so it waits for this
|
||||
local model = frame.portrait and frame.portrait.model
|
||||
if model and model.update then
|
||||
model.lastUnit = UnitName(model.update)
|
||||
model:SetUnit(model.update)
|
||||
model:SetCamera(0)
|
||||
model.update = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Flag `kind` ("full", "base", "aura", "auratime", "portrait", "indicators",
|
||||
-- "combat", or nil for none) for the frame's next flush, and schedule that
|
||||
-- flush: the next frame, or within 0.1s for the raid and party frames, which
|
||||
-- batch their bursts.
|
||||
function pfUI.uf:QueueUpdate(frame, kind)
|
||||
if kind then frame["update_" .. kind] = true end
|
||||
if frame.queued then return end
|
||||
frame.queued = true
|
||||
if frame.label == "raid" or frame.label == "party" then
|
||||
C_Timer.After(.1, function() Flush(frame) end)
|
||||
else
|
||||
RunNextFrame(function() Flush(frame) end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Size the incoming-heal overlay from libpredict's heals and the unit's
|
||||
-- health. Runs with the bars (RefreshUnit "base") and when libpredict reports
|
||||
-- the heals on the unit changed.
|
||||
function pfUI.uf:RefreshIncomingHeal(frame)
|
||||
local unit = frame.label .. frame.id
|
||||
local heal = libpredict:UnitGetIncomingHeals(unit)
|
||||
local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
|
||||
|
||||
if heal - health - maxHealth == frame.predictstate then return end
|
||||
frame.predictstate = heal - health - maxHealth
|
||||
|
||||
local overhealperc = tonumber(frame.config.overhealperc)
|
||||
if heal > 0 and (health < maxHealth or overhealperc > 0 ) then
|
||||
local width = frame.config.width
|
||||
local height = frame.config.height
|
||||
|
||||
if frame.config.verticalbar == "0" then
|
||||
local healthWidth = width * (health / maxHealth)
|
||||
local incWidth = width * heal / maxHealth
|
||||
if healthWidth + incWidth > width * (1+(overhealperc/100)) then
|
||||
incWidth = width * (1+overhealperc/100) - healthWidth
|
||||
end
|
||||
frame.incHeal:SetWidth(incWidth)
|
||||
else
|
||||
local healthHeight = height * (health / maxHealth)
|
||||
local incHeight = height * heal / maxHealth
|
||||
if healthHeight + incHeight > height * (1+(overhealperc/100)) then
|
||||
incHeight = height * (1+overhealperc/100) - healthHeight
|
||||
end
|
||||
frame.incHeal:SetHeight(incHeight)
|
||||
end
|
||||
|
||||
frame.incHeal:Show()
|
||||
else
|
||||
frame.incHeal:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
pfUI.events:RegisterCallback("libpredict:heals", function(_, target)
|
||||
for frame in pairs(visibilityframes) do
|
||||
if frame.namecache == target and frame:IsVisible() then
|
||||
pfUI.uf:RefreshIncomingHeal(frame)
|
||||
end
|
||||
end
|
||||
end, "unitframes")
|
||||
|
||||
-- A UnitXP range threshold has no event, so the frames fading by it poll.
|
||||
C_Timer.NewTicker(.5, function()
|
||||
for frame in pairs(visibilityframes) do
|
||||
if frame.rangepoll and frame:IsVisible() then pfUI.uf:RefreshUnitState(frame) end
|
||||
end
|
||||
end)
|
||||
|
||||
-- The unit events whose arg1 is the frame's OWN unit -- everything the OnEvent
|
||||
-- routes through its "UNIT_XXX Events" branch. These are not registered here:
|
||||
-- UpdateVisibility owns them, because it owns the frame's unit (below).
|
||||
@@ -1534,7 +1503,6 @@ function pfUI.uf:EnableScripts()
|
||||
f:SetScript("OnShow", pfUI.uf.OnShow)
|
||||
f:SetScript("OnHide", pfUI.uf.OnHide)
|
||||
f:SetScript("OnEvent", pfUI.uf.OnEvent)
|
||||
f:SetScript("OnUpdate", pfUI.uf.OnUpdate)
|
||||
|
||||
visibilityframes[f] = true
|
||||
end
|
||||
@@ -1606,11 +1574,11 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
|
||||
f.glow = CreateFrame("Frame", nil, f)
|
||||
-- Pulse between .8 and .4 for good. The group only advances while the glow
|
||||
-- is shown, so a hidden glow costs nothing.
|
||||
f.glow:SetAlpha(.8)
|
||||
f.glow.pulse = f.glow:CreateAnimationGroup()
|
||||
f.glow.pulse:SetLooping("BOUNCE")
|
||||
local fade = f.glow.pulse:CreateAnimation("Alpha")
|
||||
fade:SetChange(-.4)
|
||||
fade:SetFromAlpha(.8)
|
||||
fade:SetToAlpha(.4)
|
||||
fade:SetDuration(1.3)
|
||||
fade:SetSmoothing("IN_OUT")
|
||||
f.glow.pulse:Play()
|
||||
@@ -1669,6 +1637,21 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
|
||||
f.portrait.model.next = CreateFrame("PlayerModel", nil, nil)
|
||||
f.feedbackText = f:CreateFontString("pfHitIndicator" .. f.label .. f.id, "OVERLAY", "NumberFontNormalHuge")
|
||||
|
||||
-- Fade the combat feedback text in, hold it, and fade it out -- the timing
|
||||
-- of CombatFeedback_OnUpdate -- then hide it.
|
||||
f.feedback = f.feedbackText:CreateAnimationGroup()
|
||||
local fadein = f.feedback:CreateAnimation("Alpha")
|
||||
fadein:SetOrder(1)
|
||||
fadein:SetDuration(COMBATFEEDBACK_FADEINTIME)
|
||||
fadein:SetToAlpha(1)
|
||||
local fadeout = f.feedback:CreateAnimation("Alpha")
|
||||
fadeout:SetOrder(2)
|
||||
fadeout:SetStartDelay(COMBATFEEDBACK_HOLDTIME)
|
||||
fadeout:SetDuration(COMBATFEEDBACK_FADEOUTTIME)
|
||||
fadeout:SetFromAlpha(1)
|
||||
fadeout:SetToAlpha(0)
|
||||
f.feedback:SetScript("OnFinished", function() f.feedbackText:Hide() end)
|
||||
|
||||
if f.label == "raid" and mod(f.id, 5) == 1 then
|
||||
local group = math.ceil(f.id/5)
|
||||
f.group = f.group or f.hp.bar:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall")
|
||||
@@ -2042,7 +2025,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
local size
|
||||
|
||||
if disptype == "4" or disptype == "3" then
|
||||
size = unit.hp.bar:GetHeight() * tonumber(unit.config.debuff_ind_size)
|
||||
size = unit.hp.bar:GetUnscaledHeight() * tonumber(unit.config.debuff_ind_size)
|
||||
if size ~= indicator.size or disptype ~= indicator.disp or indipos ~= indicator.ipos then
|
||||
indicator:ClearAllPoints()
|
||||
indicator:SetPoint(indipos, 0, 0)
|
||||
@@ -2257,6 +2240,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
unit.portrait.model.update = unitstr
|
||||
end
|
||||
end
|
||||
if unit.portrait.model.update then pfUI.uf:QueueUpdate(unit) end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2397,6 +2381,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
|
||||
if unit.druidmana then pfUI.uf:UpdateDruidMana(unit) end
|
||||
|
||||
pfUI.uf:RefreshIncomingHeal(unit)
|
||||
pfUI.uf:RefreshUnitState(unit)
|
||||
end
|
||||
end
|
||||
@@ -2447,7 +2432,7 @@ function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
|
||||
|
||||
if not frame.hp then return end
|
||||
local frame = frame.hp.bar
|
||||
if pos > 6 or pos > ceil(frame:GetWidth() / iconsize) then return end
|
||||
if pos > 6 or pos > ceil(frame:GetUnscaledWidth() / iconsize) then return end
|
||||
|
||||
frame.icon = frame.icon or CreateFrame("Frame", nil, frame)
|
||||
|
||||
|
||||
Vendored
+1
-4
@@ -935,14 +935,11 @@ pfUI_profiles["Adapta"].new_module_positions = pfUI_profiles["Modern"].new_modul
|
||||
|
||||
|
||||
-- overwrite core profiles in userdata
|
||||
local profile_loader = CreateFrame("Frame")
|
||||
profile_loader:RegisterEvent("VARIABLES_LOADED")
|
||||
profile_loader:SetScript("OnEvent", function()
|
||||
EventUtil.RegisterOnceFrameEventAndCallback("VARIABLES_LOADED", function()
|
||||
pfUI_profiles["Modern"] = modern
|
||||
pfUI_profiles["Nostalgia"] = nostalgia
|
||||
pfUI_profiles["Legacy"] = legacy
|
||||
pfUI_profiles["Adapta"] = adapta
|
||||
pfUI_profiles["Light"] = light
|
||||
pfUI_profiles["Slim"] = slim
|
||||
this:UnregisterAllEvents()
|
||||
end)
|
||||
Vendored
-1
@@ -907,7 +907,6 @@ pfUI_translation["deDE"] = {
|
||||
["Chat Tab"] = "Chat Tab",
|
||||
["Libpredict"] = "Libpredict",
|
||||
["Panel Alignment"] = "Panel Ausrichtung",
|
||||
["Nameplate Update Rate"] = "Namensplaketten Aktualisierungsrate",
|
||||
["Target/Casting Plates"] = "Ziel/Cast Plaketten",
|
||||
["Tooltip Update Rate"] = "Tooltip Aktualisierungsrate",
|
||||
["Cursor Follow"] = "Cursor Folgen",
|
||||
|
||||
Vendored
+1
-1
@@ -433,6 +433,7 @@ pfUI_translation["enUS"] = {
|
||||
["Highlight Out Of Mana Spells"] = nil,
|
||||
["Highlight New Items"] = nil,
|
||||
["Highlight Out Of Range Spells"] = nil,
|
||||
["Highlight Proc And Reactive Spells"] = nil,
|
||||
["Highlight Settings That Require Reload"] = nil,
|
||||
["Highlight Unusable Items"] = nil,
|
||||
["Hold [Shift] to use item."] = nil,
|
||||
@@ -939,7 +940,6 @@ pfUI_translation["enUS"] = {
|
||||
["Chat Tab"] = nil,
|
||||
["Libpredict"] = nil,
|
||||
["Panel Alignment"] = nil,
|
||||
["Nameplate Update Rate"] = nil,
|
||||
["Target/Casting Plates"] = nil,
|
||||
["Tooltip Update Rate"] = nil,
|
||||
["Cursor Follow"] = nil,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -54,7 +54,6 @@
|
||||
<Include file="..\modules\gm.lua"/>
|
||||
<Include file="..\modules\mirrortimers.lua"/>
|
||||
<Include file="..\modules\feigndeath.lua"/>
|
||||
<Include file="..\modules\pixelperfect.lua"/>
|
||||
<Include file="..\modules\hdgraphic.lua"/>
|
||||
<Include file="..\modules\share.lua"/>
|
||||
<Include file="..\modules\tracking.lua"/>
|
||||
|
||||
+29
-15
@@ -36,6 +36,18 @@ local sentTargets = {} -- [castGUID] = target name of an own cast, from UNIT_
|
||||
local resOffers = {} -- [guid] = expiry of a landed resurrection the target has yet to accept
|
||||
local RES_OFFER_TIMEOUT = 60 -- the target's resurrect popup declines itself after 60s
|
||||
|
||||
-- Tell the listeners of "libpredict:heals" that the heals on `target` (a
|
||||
-- name) changed, and again at `expiry`, when a heal that is never stopped
|
||||
-- drops out of UnitGetIncomingHeals.
|
||||
local function HealsChanged(target, expiry)
|
||||
pfUI.events:TriggerEvent("libpredict:heals", target)
|
||||
if expiry then
|
||||
C_Timer.After(expiry - GetTime(), function()
|
||||
pfUI.events:TriggerEvent("libpredict:heals", target)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Localized spell names resolved once from canonical rank-1 spellIDs.
|
||||
-- Every rank shares the same name, so per-rank comparisons elsewhere can
|
||||
-- be done against these constants without per-locale or per-rank tables.
|
||||
@@ -309,7 +321,7 @@ end
|
||||
local function RefreshResFrames(guid)
|
||||
for _, frame in pairs(pfUI.uf.frames) do
|
||||
if frame.unitstr and UnitGUID(frame.unitstr) == guid then
|
||||
frame.update_indicators = true
|
||||
pfUI.uf:QueueUpdate(frame, "indicators")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -439,7 +451,7 @@ function libpredict:ParseChatMessage(sender, msg)
|
||||
end
|
||||
elseif msgtype == "Hot" then
|
||||
-- Duplicate check: ignore same sender+target+spell within DUPLICATE_WINDOW
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
local key = sender .. target .. heal
|
||||
if recentHots[key] and (now - recentHots[key]) < DUPLICATE_WINDOW then
|
||||
if libpredict.debug then
|
||||
@@ -484,11 +496,11 @@ function libpredict:Heal(sender, target, amount, duration, senderGuid)
|
||||
return
|
||||
end
|
||||
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local timeout = duration/1000 + now
|
||||
local timeout = duration/1000 + GetTime()
|
||||
heals[target] = heals[target] or {}
|
||||
heals[target][sender] = { amount, timeout }
|
||||
if senderGuid then healGuidToName[senderGuid] = sender end
|
||||
HealsChanged(target, timeout)
|
||||
end
|
||||
|
||||
-- Debug flag
|
||||
@@ -512,7 +524,7 @@ function libpredict:Hot(sender, target, spell, duration, startTime, source, rank
|
||||
local existingRank = tonumber(existing.rank) or 0
|
||||
local newRank = tonumber(rank) or 0
|
||||
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
local existingTimeleft = (existing.start + existing.duration) - now
|
||||
|
||||
-- If existing HoT is still active and has higher rank, don't overwrite
|
||||
@@ -525,7 +537,7 @@ function libpredict:Hot(sender, target, spell, duration, startTime, source, rank
|
||||
end
|
||||
end
|
||||
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
hots[target][spell].duration = duration
|
||||
hots[target][spell].start = startTime or now
|
||||
hots[target][spell].rank = rank -- Store rank for protection
|
||||
@@ -544,7 +556,7 @@ function libpredict:Hot(sender, target, spell, duration, startTime, source, rank
|
||||
if pfUI and pfUI.uf and pfUI.uf.frames then
|
||||
for _, frame in pairs(pfUI.uf.frames) do
|
||||
if frame.namecache == target then
|
||||
frame.update_aura = true
|
||||
pfUI.uf:QueueUpdate(frame, "aura")
|
||||
if libpredict.debug then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(" |cff00ff00-> Frame update triggered for " .. (frame:GetName() or "?") .. "|r")
|
||||
end
|
||||
@@ -558,6 +570,7 @@ function libpredict:HealStop(sender)
|
||||
for tsender in pairs(heals[ttarget]) do
|
||||
if sender == tsender then
|
||||
heals[ttarget][tsender] = nil
|
||||
HealsChanged(ttarget)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -573,6 +586,7 @@ function libpredict:HealDelay(sender, delay)
|
||||
for tsender, amount in pairs(heals[target]) do
|
||||
if sender == tsender then
|
||||
amount[2] = amount[2] + delay
|
||||
HealsChanged(target, amount[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -588,7 +602,7 @@ function libpredict:UnitGetIncomingHeals(unit)
|
||||
if not heals[name] then
|
||||
return sumheal
|
||||
else
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
for sender, amount in pairs(heals[name]) do
|
||||
if amount[2] <= now then
|
||||
heals[name][sender] = nil
|
||||
@@ -720,7 +734,7 @@ hooksecurefunc("CastSpell", function(id, bookType)
|
||||
|
||||
if effect == REJUVENATION then
|
||||
local target = spell_queue[3]
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
local key = "Reju" .. target
|
||||
|
||||
-- Cooldown-Check
|
||||
@@ -737,7 +751,7 @@ hooksecurefunc("CastSpell", function(id, bookType)
|
||||
libpredict.sender:SendHealCommMsg("Reju/"..target.."/"..rejuvDuration.."/"..rankStr.."/")
|
||||
elseif effect == RENEW then
|
||||
local target = spell_queue[3]
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
local key = "Renew" .. target
|
||||
|
||||
-- Cooldown-Check
|
||||
@@ -783,7 +797,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
|
||||
|
||||
if effect == REJUVENATION then
|
||||
local hotTarget = target or default
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
local key = "Reju" .. hotTarget
|
||||
|
||||
-- Cooldown-Check
|
||||
@@ -800,7 +814,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
|
||||
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
|
||||
elseif effect == RENEW then
|
||||
local hotTarget = target or default
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
local key = "Renew" .. hotTarget
|
||||
|
||||
-- Cooldown-Check
|
||||
@@ -843,7 +857,7 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
|
||||
|
||||
if effect == REJUVENATION then
|
||||
local hotTarget = spell_queue[3]
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
local key = "Reju" .. hotTarget
|
||||
|
||||
-- Cooldown-Check
|
||||
@@ -860,7 +874,7 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
|
||||
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
|
||||
elseif effect == RENEW then
|
||||
local hotTarget = spell_queue[3]
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
local key = "Renew" .. hotTarget
|
||||
|
||||
-- Cooldown-Check
|
||||
@@ -1083,7 +1097,7 @@ function libpredict:GetHotDuration(unit, spell)
|
||||
|
||||
-- FALLBACK: the HealComm/cast-fed table, for HoTs C_UnitAuras has no timer for
|
||||
local start, duration, timeleft
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local now = GetTime()
|
||||
|
||||
local unitName = UnitName(unit)
|
||||
local unitdata = hots[unitName]
|
||||
|
||||
+1
-11
@@ -31,10 +31,8 @@ libthrottle.presetNames = {
|
||||
|
||||
-- Default throttle categories
|
||||
libthrottle.defaults = {
|
||||
nameplates_castbar = "custom",
|
||||
tooltip_cursor = "fastest",
|
||||
chat_tab = "custom",
|
||||
swingtimer = "custom",
|
||||
}
|
||||
|
||||
-- Convert FPS to throttle delay in seconds
|
||||
@@ -141,8 +139,7 @@ function libthrottle:ResetAllToDefaults()
|
||||
end
|
||||
|
||||
-- Initialize - set defaults if config doesn't exist
|
||||
libthrottle:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
|
||||
if not _G.pfUI_throttle then
|
||||
_G.pfUI_throttle = {}
|
||||
end
|
||||
@@ -155,16 +152,9 @@ libthrottle:SetScript("OnEvent", function()
|
||||
end
|
||||
|
||||
-- Set defaults for custom fields if missing
|
||||
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "100" end
|
||||
if not _G.pfUI_throttle.tooltip_cursor_custom then _G.pfUI_throttle.tooltip_cursor_custom = "10" end
|
||||
if not _G.pfUI_throttle.chat_tab_custom then _G.pfUI_throttle.chat_tab_custom = "10" end
|
||||
if not _G.pfUI_throttle.swingtimer_custom then _G.pfUI_throttle.swingtimer_custom = "50" end
|
||||
|
||||
this:UnregisterEvent("PLAYER_ENTERING_WORLD")
|
||||
end
|
||||
end)
|
||||
|
||||
libthrottle:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
|
||||
-- Export to pfUI namespace for easier access
|
||||
pfUI.throttle = libthrottle
|
||||
|
||||
+45
-66
@@ -2,20 +2,17 @@
|
||||
setfenv(1, pfUI:GetEnvironment())
|
||||
|
||||
--[[ libunitscan ]]--
|
||||
-- A pfUI library that remembers the class, level and guild of every player it
|
||||
-- sees: in the friends, guild, raid and party rosters, in /who results, and as
|
||||
-- target, mouseover or nameplate. The data is persisted within pfUI_playerDB,
|
||||
-- for the places that only have a player's name, such as chat links and
|
||||
-- offline friends. Anything with a unit reads the unit itself instead.
|
||||
-- A pfUI library that remembers the level of every player it sees: in the
|
||||
-- friends, guild, raid and party rosters, in /who results, and as target,
|
||||
-- mouseover or nameplate. The levels are persisted within pfUI_playerDB, for
|
||||
-- the places that only have a player's name, such as chat links and offline
|
||||
-- friends. Anything with a unit reads the unit itself instead. A player's
|
||||
-- class comes from C_PlayerCache.GetPlayerInfoByName, not from here.
|
||||
--
|
||||
-- External functions:
|
||||
-- GetUnitInfo(name)
|
||||
-- Returns information of the given player name. Returns nil if the player
|
||||
-- was never seen.
|
||||
--
|
||||
-- class[String] - The class of the player
|
||||
-- level[Number] - The level of the player
|
||||
-- guild[String] - The guild name of the player
|
||||
-- GetPlayerLevel(name)
|
||||
-- Returns the last seen level of the given player name, or nil if it was
|
||||
-- never seen.
|
||||
--
|
||||
|
||||
-- return instantly when another libunitscan is already active
|
||||
@@ -23,33 +20,33 @@ if pfUI.api.libunitscan then return end
|
||||
|
||||
local players = {}
|
||||
|
||||
-- Feed (guid, name, classToken) into ClassicAPI's persistent name
|
||||
-- cache when the unit's GUID is resolvable. Roster-style packets
|
||||
-- (party / raid / inspect on glance) carry class inline, so the
|
||||
-- engine often doesn't issue a separate name query and the DLL's
|
||||
-- NameCache hook never fires for them — this is where we close that gap.
|
||||
local function RememberByUnit(unit, name, class)
|
||||
if not (name and class) then return end
|
||||
-- Feed a unit into ClassicAPI's persistent name cache. Roster-style packets
|
||||
-- (party / raid / inspect on glance) carry class inline, so the engine often
|
||||
-- doesn't issue a separate name query and the DLL's NameCache hook never
|
||||
-- fires for them — this is where we close that gap.
|
||||
local function RememberByUnit(unit)
|
||||
local guid = UnitGUID(unit)
|
||||
if not guid then return end
|
||||
C_PlayerCache.RememberPlayer(guid, name, class, UnitRace(unit), UnitSex(unit))
|
||||
local _, race = UnitRace(unit)
|
||||
C_PlayerCache.RememberPlayer(guid, UnitName(unit), UnitClassBase(unit), race, UnitSex(unit) - 2)
|
||||
end
|
||||
|
||||
function GetUnitInfo(name)
|
||||
local ret = players[name]
|
||||
if ret then
|
||||
return ret.class, ret.level, ret.guild
|
||||
function GetPlayerLevel(name)
|
||||
local entry = players[name]
|
||||
return entry and entry.level
|
||||
end
|
||||
|
||||
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
|
||||
local function AddLevel(name, level)
|
||||
if not name or not level or level <= 0 then return end
|
||||
local entry = players[name]
|
||||
if entry then
|
||||
entry.level = level
|
||||
else
|
||||
players[name] = { level = level }
|
||||
end
|
||||
end
|
||||
|
||||
local function AddData(name, class, level, guild)
|
||||
if not name then return end
|
||||
players[name] = players[name] or {}
|
||||
players[name].class = class or players[name].class
|
||||
players[name].level = level or players[name].level
|
||||
players[name].guild = guild or players[name].guild
|
||||
end
|
||||
|
||||
local libunitscan = CreateFrame("Frame", "pfUnitScan", UIParent)
|
||||
libunitscan:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
libunitscan:RegisterEvent("FRIENDLIST_UPDATE")
|
||||
@@ -68,58 +65,45 @@ libunitscan:SetScript("OnEvent", function()
|
||||
players = pfUI_playerDB
|
||||
|
||||
-- update own character details
|
||||
local name = UnitName("player")
|
||||
local class = UnitClassBase("player")
|
||||
local level = UnitLevel("player")
|
||||
local guild = GetGuildInfo("player")
|
||||
AddData(name, class, level, guild)
|
||||
RememberByUnit("player", name, class)
|
||||
AddLevel(UnitName("player"), UnitLevel("player"))
|
||||
RememberByUnit("player")
|
||||
|
||||
elseif event == "FRIENDLIST_UPDATE" then
|
||||
for i = 1, GetNumFriends() do
|
||||
local info = C_FriendList.GetFriendInfoByIndex(i)
|
||||
if info then
|
||||
local level = info.level > 0 and info.level or nil
|
||||
AddData(info.name, info.classFilename, level)
|
||||
AddLevel(info.name, info.level)
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == "GUILD_ROSTER_UPDATE" then
|
||||
local guild = GetGuildInfo("player")
|
||||
local name, class, level, _
|
||||
local name, level, _
|
||||
for i = 1, GetNumGuildMembers() do
|
||||
name, _, _, level, class = GetGuildRosterInfo(i)
|
||||
class = L["class"][class] or nil
|
||||
AddData(name, class, level, guild)
|
||||
name, _, _, level = GetGuildRosterInfo(i)
|
||||
AddLevel(name, level)
|
||||
end
|
||||
|
||||
elseif event == "RAID_ROSTER_UPDATE" then
|
||||
local name, class, level, unit, _
|
||||
local name, level, _
|
||||
for i = 1, GetNumRaidMembers() do
|
||||
unit = "raid" .. i
|
||||
name, _, _, level, class = GetRaidRosterInfo(i)
|
||||
class = L["class"][class] or nil
|
||||
AddData(name, class, level)
|
||||
RememberByUnit(unit, name, class)
|
||||
name, _, _, level = GetRaidRosterInfo(i)
|
||||
AddLevel(name, level)
|
||||
RememberByUnit("raid" .. i)
|
||||
end
|
||||
|
||||
elseif event == "PARTY_MEMBERS_CHANGED" then
|
||||
local name, class, level, unit, guild
|
||||
local unit
|
||||
for i = 1, GetNumPartyMembers() do
|
||||
unit = "party" .. i
|
||||
class = UnitClassBase(unit)
|
||||
name = UnitName(unit)
|
||||
level = UnitLevel(unit)
|
||||
guild = GetGuildInfo(unit)
|
||||
AddData(name, class, level, guild)
|
||||
RememberByUnit(unit, name, class)
|
||||
AddLevel(UnitName(unit), UnitLevel(unit))
|
||||
RememberByUnit(unit)
|
||||
end
|
||||
|
||||
elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then
|
||||
for i = 1, C_FriendList.GetNumWhoResults() do
|
||||
local info = C_FriendList.GetWhoInfo(i)
|
||||
if info then
|
||||
AddData(info.fullName, info.filename, info.level, info.fullGuildName)
|
||||
AddLevel(info.fullName, info.level)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -128,13 +112,8 @@ libunitscan:SetScript("OnEvent", function()
|
||||
or event == "NAME_PLATE_UNIT_ADDED" and arg1
|
||||
or "mouseover"
|
||||
if UnitIsPlayer(scan) then
|
||||
local name = UnitName(scan)
|
||||
local class = UnitClassBase(scan)
|
||||
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
|
||||
local level = UnitLevel(scan)
|
||||
level = level > 0 and level or nil
|
||||
AddData(name, class, level, GetGuildInfo(scan))
|
||||
RememberByUnit(scan, name, class)
|
||||
AddLevel(UnitName(scan), UnitLevel(scan))
|
||||
RememberByUnit(scan)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
+338
-255
@@ -9,7 +9,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
local rawborder, border = GetBorderSize("actionbars")
|
||||
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
|
||||
|
||||
local eventcache = { } -- contains a list of events that shall be processed later -> [event] = true
|
||||
local passcache = { } -- contains the passes over the visible buttons that shall run later -> [pass] = true
|
||||
local updatecache = { } -- contains a list of buttons slots that shall be refreshed later -> [slot] = true
|
||||
local buttoncache = { } -- contains a list of all buttons ever created -> [slot] = frame
|
||||
|
||||
@@ -54,18 +54,21 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
if f.Hide then f:Hide() end
|
||||
end
|
||||
|
||||
-- also abbreviate mouse buttons
|
||||
local OrigGetBindingText = GetBindingText
|
||||
local function GetBindingText(msg, mod, abbrev)
|
||||
local txt = OrigGetBindingText(msg, mod, abbrev)
|
||||
if abbrev then
|
||||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON3")], "MB3")
|
||||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON4")], "MB4")
|
||||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON5")], "MB5")
|
||||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "MOUSEWHEELDOWN")], "MWD")
|
||||
txt = string.gsub(txt, _G[string.format("%s%s", mod, "MOUSEWHEELUP")], "MWU")
|
||||
-- the abbreviated text of the key bound to a command, mouse buttons included
|
||||
local mouse_abbrevs = {
|
||||
[_G.KEY_BUTTON3] = "MB3",
|
||||
[_G.KEY_BUTTON4] = "MB4",
|
||||
[_G.KEY_BUTTON5] = "MB5",
|
||||
[_G.KEY_MOUSEWHEELDOWN] = "MWD",
|
||||
[_G.KEY_MOUSEWHEELUP] = "MWU",
|
||||
}
|
||||
|
||||
local function GetKeybindText(command)
|
||||
local text = GetBindingText(GetBindingKey(command), "KEY_", 1)
|
||||
for name, abbrev in pairs(mouse_abbrevs) do
|
||||
text = text:gsub(name, abbrev)
|
||||
end
|
||||
return txt
|
||||
return text
|
||||
end
|
||||
|
||||
kill(MainMenuBar)
|
||||
@@ -83,33 +86,34 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
_G.SHOW_MULTI_ACTIONBAR_3 = nil
|
||||
_G.SHOW_MULTI_ACTIONBAR_4 = nil
|
||||
|
||||
-- events that provide special updater functions
|
||||
local special_events = {
|
||||
["ACTIONBAR_UPDATE_COOLDOWN"] = true,
|
||||
["ACTIONBAR_UPDATE_USABLE"] = true,
|
||||
["ACTIONBAR_UPDATE_STATE"] = true,
|
||||
-- events that refresh one part of every visible button -> [event] = pass.
|
||||
-- Page changes only touch the main bar, whose pager handles them.
|
||||
local pass_events = {
|
||||
["ACTIONBAR_UPDATE_COOLDOWN"] = "cooldown",
|
||||
-- action slots take ACTION_USABLE_CHANGED; the stance buttons still
|
||||
-- need a pass on this one
|
||||
["ACTIONBAR_UPDATE_USABLE"] = "stance",
|
||||
["ACTIONBAR_UPDATE_STATE"] = "state",
|
||||
-- the active border of attack, auto repeat and tradeskill actions
|
||||
["PLAYER_ENTER_COMBAT"] = "state",
|
||||
["PLAYER_LEAVE_COMBAT"] = "state",
|
||||
["START_AUTOREPEAT_SPELL"] = "state",
|
||||
["STOP_AUTOREPEAT_SPELL"] = "state",
|
||||
["CRAFT_SHOW"] = "state",
|
||||
["CRAFT_CLOSE"] = "state",
|
||||
["TRADE_SKILL_SHOW"] = "state",
|
||||
["TRADE_SKILL_CLOSE"] = "state",
|
||||
-- fires on every ammo shot / consumable use, but only counts, equipped
|
||||
-- state, cooldowns and usability can change from it
|
||||
["UNIT_INVENTORY_CHANGED"] = true,
|
||||
["UNIT_INVENTORY_CHANGED"] = "inventory",
|
||||
-- the keybind text of every button, shown or not
|
||||
["UPDATE_BINDINGS"] = "bindings",
|
||||
}
|
||||
|
||||
-- events that are shared across all buttons
|
||||
-- events that refresh every button
|
||||
local global_events = {
|
||||
-- slot/button updates
|
||||
["PLAYER_ENTERING_WORLD"] = true,
|
||||
["ACTIONBAR_SLOT_CHANGED"] = true,
|
||||
["UPDATE_BINDINGS"] = true,
|
||||
["ACTIONBAR_PAGE_CHANGED"] = true,
|
||||
["UPDATE_BONUS_ACTIONBAR"] = true,
|
||||
["CRAFT_SHOW"] = true,
|
||||
["CRAFT_CLOSE"] = true,
|
||||
["TRADE_SKILL_SHOW"] = true,
|
||||
["TRADE_SKILL_CLOSE"] = true,
|
||||
["PLAYER_ENTER_COMBAT"] = true,
|
||||
["PLAYER_LEAVE_COMBAT"] = true,
|
||||
-- auto repeat action
|
||||
["START_AUTOREPEAT_SPELL"] = true,
|
||||
["STOP_AUTOREPEAT_SPELL"] = true,
|
||||
}
|
||||
|
||||
-- events that are used for the aura/shapeshift bar
|
||||
@@ -394,37 +398,85 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
self.equipped:SetShown(IsEquippedAction(sid) and C.bars.showequipped == "1")
|
||||
end
|
||||
|
||||
local start, duration, enable, castable, autocast, token
|
||||
local grid, sid, id, bar, active, texture, _
|
||||
-- the keybind text, which an empty hidden button hides as well. The binding
|
||||
-- itself is only read on UPDATE_BINDINGS (the "bindings" pass).
|
||||
local function ButtonKeybindUpdate(self)
|
||||
self.keybind:SetText(not self.hide and self.binding or "")
|
||||
end
|
||||
|
||||
-- the active border of current, attack and auto repeat actions, which the
|
||||
-- "statechange" animation follows. A full update applies it unconditionally.
|
||||
local function ButtonActiveUpdate(self, active, force)
|
||||
if active then
|
||||
if not self.isactive and C.bars.animmode == "statechange" then
|
||||
PlayButtonAnimation(self)
|
||||
end
|
||||
|
||||
if force or not self.isactive then
|
||||
self.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
|
||||
self.active:Show()
|
||||
self.isactive = true
|
||||
end
|
||||
elseif force or self.isactive then
|
||||
self.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
|
||||
self.active:Hide()
|
||||
self.isactive = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- the pulsing border of a spell whose proc glow is lit
|
||||
local function ButtonProcShow(self, glow)
|
||||
if glow then
|
||||
if not self.proc:IsShown() then
|
||||
self.proc:Show()
|
||||
self.proc.pulse:Play()
|
||||
end
|
||||
elseif self.proc:IsShown() then
|
||||
self.proc.pulse:Stop()
|
||||
self.proc:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- the spell an action slot casts, directly or through a macro, and its
|
||||
-- proc glow. The glow events name that spell.
|
||||
local function ButtonProcUpdate(self)
|
||||
local kind, id, spellid, _
|
||||
if C.bars.procglow == "1" then
|
||||
kind, id = GetActionInfo(self.id)
|
||||
if kind == "spell" then
|
||||
spellid = id
|
||||
elseif kind == "macro" then
|
||||
_, _, spellid = GetMacroSpell(id)
|
||||
end
|
||||
end
|
||||
|
||||
self.spellid = spellid
|
||||
ButtonProcShow(self, spellid and IsSpellOverlayed(spellid))
|
||||
end
|
||||
|
||||
local castable, autocast, token
|
||||
local grid, sid, active, texture, _
|
||||
local function ButtonSlotUpdate(self)
|
||||
if not self then return end
|
||||
self = self or this
|
||||
sid = self.id -- 1 to 120
|
||||
|
||||
-- reset shared variables
|
||||
castable, autocast, token = nil, nil, nil
|
||||
|
||||
-- set the own ID for compatibility to some vanilla addons
|
||||
self:SetID(self.id)
|
||||
self:SetID(sid)
|
||||
|
||||
grid = self.bar == 12 and showgrid_pet or showgrid
|
||||
|
||||
if self.bar == 11 then
|
||||
-- stance button
|
||||
bar = self.bar
|
||||
id = sid
|
||||
texture, _, active = GetShapeshiftFormInfo(id)
|
||||
texture, _, active = GetShapeshiftFormInfo(sid)
|
||||
elseif self.bar == 12 then
|
||||
-- pet button
|
||||
bar = self.bar
|
||||
id = sid
|
||||
_, _, texture, token, active, castable, autocast = GetPetActionInfo(id)
|
||||
_, _, texture, token, active, castable, autocast = GetPetActionInfo(sid)
|
||||
texture = token and _G[texture] or texture
|
||||
else
|
||||
active = IsCurrentAction(sid) or IsAutoRepeatAction(sid)
|
||||
texture = GetActionTexture(sid)
|
||||
bar = GetActiveBar()
|
||||
id = self.bar == 1 and self.slot or sid-((self.bar)-1)*12
|
||||
end
|
||||
|
||||
if not self.showempty and self.backdrop and not texture and grid == 0 then
|
||||
@@ -435,19 +487,6 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
self.hide = nil
|
||||
end
|
||||
|
||||
-- active border
|
||||
if active then
|
||||
if C.bars.animmode == "statechange" and not self.active:IsShown() then
|
||||
PlayButtonAnimation(self)
|
||||
end
|
||||
|
||||
self.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
|
||||
self.active:Show()
|
||||
else
|
||||
self.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
|
||||
self.active:Hide()
|
||||
end
|
||||
|
||||
ButtonInventoryUpdate(self)
|
||||
|
||||
if self.bar ~= 11 and self.bar ~= 12 then
|
||||
@@ -457,6 +496,8 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
else
|
||||
self.macro:SetText("")
|
||||
end
|
||||
|
||||
ButtonProcUpdate(self)
|
||||
end
|
||||
|
||||
-- icon
|
||||
@@ -481,14 +522,8 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
self.autocastable:SetShown(castable and C.bars.showcastable == "1")
|
||||
end
|
||||
|
||||
-- keybinds
|
||||
if not self.hide and self.bar == 1 and self.bar ~= 11 and self.bar ~= 12 then
|
||||
self.keybind:SetText(GetBindingText(GetBindingKey("ACTIONBUTTON"..id), "KEY_", 1))
|
||||
elseif not self.hide and buttontypes[self.bar] then
|
||||
self.keybind:SetText(GetBindingText(GetBindingKey(buttontypes[self.bar]..id), "KEY_", 1))
|
||||
else
|
||||
self.keybind:SetText("")
|
||||
end
|
||||
ButtonActiveUpdate(self, active, true)
|
||||
ButtonKeybindUpdate(self)
|
||||
end
|
||||
|
||||
local sid, usable, oom, _
|
||||
@@ -498,8 +533,9 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
if self.bar == 11 then
|
||||
_, _, _, usable = GetShapeshiftFormInfo(sid)
|
||||
oom = nil
|
||||
elseif self.bar == 12 then
|
||||
usable = true
|
||||
usable, oom = true, nil
|
||||
else
|
||||
usable, oom = IsUsableAction(sid)
|
||||
end
|
||||
@@ -528,25 +564,22 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
end
|
||||
|
||||
local function ButtonRangeUpdate(self)
|
||||
self = self or this
|
||||
-- Keeps the range check of the slot a shown action button displays
|
||||
-- enabled, so ACTION_RANGE_CHECK_UPDATE reports it. Each button holds its
|
||||
-- own enable, as the client counts them per slot. An enable fires the
|
||||
-- event with the current state, so a new slot starts in range until then.
|
||||
local function ButtonRangeWatch(button)
|
||||
local slot = C.bars.glowrange == "1" and button.bar <= 10 and button:IsVisible() and button.id or nil
|
||||
if slot == button.rangeslot then return end
|
||||
|
||||
-- update range display
|
||||
if C.bars.glowrange == "1" and self.bar ~= 11 and self.bar ~= 12 and HasAction(self.id) and ActionHasRange(self.id) and IsActionInRange(self.id) == 0 then
|
||||
if not self.outofrange then
|
||||
self.outofrange = true
|
||||
ButtonUsableUpdate(self)
|
||||
end
|
||||
elseif self.outofrange then
|
||||
self.outofrange = nil
|
||||
ButtonUsableUpdate(self)
|
||||
end
|
||||
if button.rangeslot then C_ActionBar.EnableActionRangeCheck(button.rangeslot, false) end
|
||||
if slot then C_ActionBar.EnableActionRangeCheck(slot, true) end
|
||||
button.rangeslot = slot
|
||||
button.outofrange = nil
|
||||
end
|
||||
|
||||
local start, duration, enable
|
||||
local function ButtonCooldownUpdate(button)
|
||||
if not button then return end
|
||||
|
||||
if button.bar == 11 then
|
||||
start, duration, enable = GetShapeshiftFormCooldown(button.id)
|
||||
elseif button.bar == 12 then
|
||||
@@ -556,183 +589,205 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
-- Nil-protect: GetActionCooldown can return nil during macro parsing/indexing
|
||||
if start and duration then
|
||||
CooldownFrame_SetTimer(button.cd, start, duration, enable or 1)
|
||||
end
|
||||
if not start or not duration then return end
|
||||
enable = enable or 1
|
||||
|
||||
-- most buttons keep their cooldown through a cooldown event
|
||||
if start == button.cdstart and duration == button.cdduration and enable == button.cdenable then return end
|
||||
button.cdstart, button.cdduration, button.cdenable = start, duration, enable
|
||||
|
||||
CooldownFrame_SetTimer(button.cd, start, duration, enable)
|
||||
end
|
||||
|
||||
local _, active
|
||||
local function ButtonIsActiveUpdate(button)
|
||||
if not button then return end
|
||||
|
||||
if button.bar == 11 then
|
||||
_, _, active, _ = GetShapeshiftFormInfo(button.id)
|
||||
_, _, active = GetShapeshiftFormInfo(button.id)
|
||||
elseif button.bar == 12 then
|
||||
_, _, _, _, active, _, _ = GetPetActionInfo(button.id)
|
||||
_, _, _, _, active = GetPetActionInfo(button.id)
|
||||
else
|
||||
active = IsCurrentAction(button.id) or IsAutoRepeatAction(button.id)
|
||||
end
|
||||
|
||||
-- active border
|
||||
if active then
|
||||
if not button.border_active then
|
||||
button.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
|
||||
button.border_active = true
|
||||
end
|
||||
|
||||
button.active:Show()
|
||||
else
|
||||
if button.border_active then
|
||||
button.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
|
||||
button.border_active = nil
|
||||
end
|
||||
|
||||
button.active:Hide()
|
||||
end
|
||||
ButtonActiveUpdate(button, active)
|
||||
end
|
||||
|
||||
|
||||
-- everything of a button; the slot update covers its active state
|
||||
local function ButtonFullUpdate(button)
|
||||
if not button then return end
|
||||
|
||||
ButtonSlotUpdate(button)
|
||||
ButtonRangeUpdate(button)
|
||||
ButtonUsableUpdate(button)
|
||||
ButtonCooldownUpdate(button)
|
||||
ButtonIsActiveUpdate(button)
|
||||
end
|
||||
|
||||
local function BarsEvent(self)
|
||||
self = self or this
|
||||
-- the pfActionBar frame handles the events of every button; its array part
|
||||
-- holds the bar frames, bars[1] to bars[12]
|
||||
local bars = CreateFrame("Frame", "pfActionBar", UIParent)
|
||||
|
||||
-- Updates wait for the next frame, so the events of one frame share one
|
||||
-- pass. Passes and full updates skip hidden bars, which catch up as they
|
||||
-- show (BarShowHide). The shown buttons of a bar are its first `count` ones.
|
||||
local scheduled
|
||||
|
||||
-- calls func on the buttons that show a slot: its own button and the main
|
||||
-- bar button paged to it
|
||||
local function ForSlotButtons(slot, func, a1)
|
||||
local button = buttoncache[slot]
|
||||
if button then func(button, a1) end
|
||||
|
||||
local page = bars[1][1].id - 1
|
||||
local paged = slot > page and slot <= page + 12 and bars[1][slot - page]
|
||||
if paged and paged ~= button then func(paged, a1) end
|
||||
end
|
||||
|
||||
-- the range watch follows hidden buttons too, so a hidden bar lets go of
|
||||
-- its slots
|
||||
local function SlotButtonUpdate(button)
|
||||
ButtonRangeWatch(button)
|
||||
if button:IsVisible() then pfUI.bars.ButtonFullUpdate(button) end
|
||||
end
|
||||
|
||||
local function Flush()
|
||||
scheduled = nil
|
||||
|
||||
local inventory, cooldown, state = passcache.inventory, passcache.cooldown, passcache.state
|
||||
local stance, bindings = passcache.stance, passcache.bindings
|
||||
table.wipe(passcache)
|
||||
|
||||
if inventory or cooldown or state then
|
||||
for i = 1, 12 do
|
||||
local bar = bars[i]
|
||||
if bar:IsVisible() then
|
||||
for j = 1, bar.count do
|
||||
local button = bar[j]
|
||||
if inventory then ButtonInventoryUpdate(button) end
|
||||
if inventory or cooldown then ButtonCooldownUpdate(button) end
|
||||
if inventory then ButtonUsableUpdate(button) end
|
||||
if state then ButtonIsActiveUpdate(button) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if stance and not inventory and bars[11]:IsVisible() then
|
||||
for j = 1, bars[11].count do
|
||||
ButtonUsableUpdate(bars[11][j])
|
||||
end
|
||||
end
|
||||
|
||||
-- the keybinds of hidden buttons as well, as they don't catch up on them
|
||||
if bindings then
|
||||
for _, button in pairs(buttoncache) do
|
||||
button.binding = GetKeybindText(button.bindcommand)
|
||||
ButtonKeybindUpdate(button)
|
||||
end
|
||||
end
|
||||
|
||||
for slot in pairs(updatecache) do
|
||||
updatecache[slot] = nil
|
||||
ForSlotButtons(slot, SlotButtonUpdate)
|
||||
end
|
||||
end
|
||||
|
||||
local function Schedule()
|
||||
if scheduled then return end
|
||||
scheduled = true
|
||||
RunNextFrame(Flush)
|
||||
end
|
||||
|
||||
-- queues a full update of the button in a slot
|
||||
local function QueueSlot(slot)
|
||||
updatecache[slot] = true
|
||||
Schedule()
|
||||
end
|
||||
|
||||
-- queues a pass over the visible buttons
|
||||
local function QueuePass(pass)
|
||||
passcache[pass] = true
|
||||
Schedule()
|
||||
end
|
||||
|
||||
-- passes skip hidden bars, so a bar refreshes its buttons as it shows;
|
||||
-- as it hides, its buttons let go of their range checks
|
||||
local function BarShowHide()
|
||||
local offset = (this:GetID() - 1) * 12
|
||||
for j = 1, this.count do
|
||||
QueueSlot(offset + j)
|
||||
end
|
||||
end
|
||||
|
||||
local function ButtonRangeEvent(button, outofrange)
|
||||
if button.rangeslot ~= arg1 or button.outofrange == outofrange then return end
|
||||
button.outofrange = outofrange
|
||||
ButtonUsableUpdate(button)
|
||||
end
|
||||
|
||||
local function ButtonUsableEvent(button)
|
||||
if button:IsVisible() then ButtonUsableUpdate(button) end
|
||||
end
|
||||
|
||||
local function BarsEvent()
|
||||
-- slot, isInRange, checksRange of a slot a button watches
|
||||
if event == "ACTION_RANGE_CHECK_UPDATE" then
|
||||
ForSlotButtons(arg1, ButtonRangeEvent, arg3 and not arg2 or nil)
|
||||
return
|
||||
end
|
||||
|
||||
-- the slots whose usable state changed
|
||||
if event == "ACTION_USABLE_CHANGED" then
|
||||
for _, change in ipairs(arg1) do
|
||||
ForSlotButtons(change.slot, ButtonUsableEvent)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- the spell whose glow started or ended; each rank has its own event
|
||||
if event == "SPELL_ACTIVATION_OVERLAY_GLOW_SHOW" or event == "SPELL_ACTIVATION_OVERLAY_GLOW_HIDE" then
|
||||
local glow = IsSpellOverlayed(arg1)
|
||||
for _, button in pairs(buttoncache) do
|
||||
if button.spellid == arg1 then ButtonProcShow(button, glow) end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- refresh only specific slots
|
||||
if event == "ACTIONBAR_SLOT_CHANGED" and arg1 and arg1 ~= 0 then
|
||||
updatecache[arg1] = true
|
||||
QueueSlot(arg1)
|
||||
return
|
||||
end
|
||||
|
||||
-- run special refresh functions on next update
|
||||
if special_events[event] then
|
||||
eventcache[event] = true
|
||||
-- refresh one part of every visible button
|
||||
if pass_events[event] then
|
||||
QueuePass(pass_events[event])
|
||||
return
|
||||
end
|
||||
|
||||
-- handle aura events
|
||||
-- auras and forms change the stance buttons
|
||||
if aura_events[event] then
|
||||
for j=1,12 do
|
||||
if self[11] and self[11][j] then
|
||||
updatecache[self[11][j].slot] = true
|
||||
end
|
||||
for j = 1, bars[11].count do
|
||||
QueueSlot(bars[11][j].slot)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- handle pet events
|
||||
if pet_events[event] then
|
||||
for j=1,12 do
|
||||
if self[12] and self[12][j] then
|
||||
updatecache[self[12][j].slot] = true
|
||||
end
|
||||
for j = 1, bars[12].count do
|
||||
QueueSlot(bars[12][j].slot)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- handle global events
|
||||
for id in pairs(buttoncache) do
|
||||
updatecache[id] = true
|
||||
end
|
||||
end
|
||||
|
||||
local self, button, unlock
|
||||
-- Main update loop with throttle for performance optimization
|
||||
local function BarsUpdate(self)
|
||||
self = self or this
|
||||
|
||||
-- Throttle for performance
|
||||
if (this.tick_main or 0) > GetTime() then return end
|
||||
this.tick_main = GetTime() + 0.025
|
||||
|
||||
if pfUI.unlock then
|
||||
-- update all bars when entering unlock
|
||||
if pfUI.unlock:IsShown() ~= unlock then
|
||||
pfUI.bars:UpdateConfig()
|
||||
unlock = pfUI.unlock:IsShown()
|
||||
end
|
||||
end
|
||||
|
||||
-- The cached passes below only touch visible buttons. Hidden ones (other
|
||||
-- pages, hidden bars) are flagged stale and get a full update once the
|
||||
-- range pass sees them visible again.
|
||||
|
||||
-- run cached inventory changes (ammo, reagents, consumables)
|
||||
if eventcache["UNIT_INVENTORY_CHANGED"] then
|
||||
eventcache["UNIT_INVENTORY_CHANGED"] = nil
|
||||
for id, button in pairs(buttoncache) do
|
||||
if button:IsVisible() then
|
||||
ButtonInventoryUpdate(button)
|
||||
ButtonCooldownUpdate(button)
|
||||
ButtonUsableUpdate(button)
|
||||
else
|
||||
button.stale = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- run cached usable usable actions
|
||||
if eventcache["ACTIONBAR_UPDATE_USABLE"] then
|
||||
eventcache["ACTIONBAR_UPDATE_USABLE"] = nil
|
||||
for id, button in pairs(buttoncache) do
|
||||
if button:IsVisible() then ButtonUsableUpdate(button) else button.stale = true end
|
||||
end
|
||||
end
|
||||
|
||||
-- run cached cooldown events
|
||||
if eventcache["ACTIONBAR_UPDATE_COOLDOWN"] then
|
||||
eventcache["ACTIONBAR_UPDATE_COOLDOWN"] = nil
|
||||
for id, button in pairs(buttoncache) do
|
||||
if button:IsVisible() then ButtonCooldownUpdate(button) else button.stale = true end
|
||||
end
|
||||
end
|
||||
|
||||
-- run cached action state events
|
||||
if eventcache["ACTIONBAR_UPDATE_STATE"] then
|
||||
eventcache["ACTIONBAR_UPDATE_STATE"] = nil
|
||||
for id, button in pairs(buttoncache) do
|
||||
if button:IsVisible() then ButtonIsActiveUpdate(button) else button.stale = true end
|
||||
end
|
||||
end
|
||||
|
||||
for id in pairs(updatecache) do
|
||||
-- run updates based on slot
|
||||
pfUI.bars.ButtonFullUpdate(buttoncache[id])
|
||||
|
||||
-- run updates on paging actionbar if required
|
||||
for i=1,12 do
|
||||
if pfUI.bars[1][i].id == id then
|
||||
pfUI.bars.ButtonFullUpdate(pfUI.bars[1][i])
|
||||
end
|
||||
end
|
||||
|
||||
-- clear update cache
|
||||
updatecache[id] = nil
|
||||
end
|
||||
|
||||
if ( this.tick or .2) > GetTime() then return else this.tick = GetTime() + .2 end
|
||||
|
||||
for id, button in pairs(buttoncache) do
|
||||
if button.stale and button:IsVisible() then
|
||||
-- catch up on updates skipped while hidden
|
||||
button.stale = nil
|
||||
ButtonFullUpdate(button)
|
||||
elseif button:IsShown() then
|
||||
ButtonRangeUpdate(button)
|
||||
-- refresh every button and its keybind
|
||||
if global_events[event] then
|
||||
QueuePass("bindings")
|
||||
for slot in pairs(buttoncache) do
|
||||
QueueSlot(slot)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- create the main event and update handler for pfUI actionbars
|
||||
local bars = CreateFrame("Frame", "pfActionBar", UIParent)
|
||||
|
||||
-- The only unit events in the tables above; both concern the player alone.
|
||||
-- A registration keeps its kind, so these have to go in unit-filtered from
|
||||
-- the start -- RegisterUnitEvent over a plain registration stays plain.
|
||||
@@ -750,17 +805,18 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
end
|
||||
|
||||
for event in pairs(special_events) do RegisterBarEvent(event) end
|
||||
for event in pairs(pass_events) do RegisterBarEvent(event) end
|
||||
for event in pairs(global_events) do RegisterBarEvent(event) end
|
||||
for event in pairs(aura_events) do RegisterBarEvent(event) end
|
||||
for event in pairs(pet_events) do RegisterBarEvent(event) end
|
||||
bars:RegisterEvent("ACTION_RANGE_CHECK_UPDATE")
|
||||
bars:RegisterEvent("ACTION_USABLE_CHANGED")
|
||||
bars:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_SHOW")
|
||||
bars:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_HIDE")
|
||||
|
||||
-- refresh actionbar buttons on event
|
||||
bars:SetScript("OnEvent", BarsEvent)
|
||||
|
||||
-- update actionbar buttons
|
||||
bars:SetScript("OnUpdate", BarsUpdate)
|
||||
|
||||
-- enable bar paging via secure functions
|
||||
local function EnablePaging(bar)
|
||||
if not bar.pager then
|
||||
@@ -776,9 +832,8 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
local active = GetActiveBar()
|
||||
for i=1,12 do
|
||||
local id = i + (active-1)*12
|
||||
bar[i].id = id
|
||||
updatecache[i] = true
|
||||
bar[i].id = i + (active-1)*12
|
||||
QueueSlot(i)
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -1006,6 +1061,9 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
-- set a static slot
|
||||
f.slot = id
|
||||
|
||||
-- the binding command of the button, fixed on the paged main bar too
|
||||
f.bindcommand = (bar == 1 and "ACTIONBUTTON" or buttontypes[bar]) .. button
|
||||
|
||||
-- cooldown
|
||||
f.cd = CreateCooldown(f, "spell", C.bars, "timers", f:GetName() .. "Cooldown")
|
||||
|
||||
@@ -1065,6 +1123,23 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
f.active:SetAllPoints()
|
||||
f.active:Hide()
|
||||
|
||||
-- proc glow
|
||||
if bar ~= 11 and bar ~= 12 then
|
||||
f.proc = CreateFrame("Frame", nil, f)
|
||||
f.proc:SetBackdrop(backdrop_highlight)
|
||||
f.proc:SetBackdropBorderColor(1,.8,.2,1)
|
||||
f.proc:SetAllPoints()
|
||||
f.proc:Hide()
|
||||
|
||||
f.proc.pulse = f.proc:CreateAnimationGroup()
|
||||
f.proc.pulse:SetLooping("BOUNCE")
|
||||
local fade = f.proc.pulse:CreateAnimation("Alpha")
|
||||
fade:SetFromAlpha(1)
|
||||
fade:SetToAlpha(.3)
|
||||
fade:SetDuration(.5)
|
||||
fade:SetSmoothing("IN_OUT")
|
||||
end
|
||||
|
||||
-- add to buttoncache
|
||||
buttoncache[id] = f
|
||||
end
|
||||
@@ -1168,6 +1243,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- don't process empty bars
|
||||
if buttons == 0 then
|
||||
bars[i].count = 0
|
||||
bars[i]:Hide()
|
||||
return
|
||||
end
|
||||
@@ -1186,6 +1262,11 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
local init = not bars[i]
|
||||
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent)
|
||||
bars[i]:SetID(i)
|
||||
bars[i].count = buttons
|
||||
if init then
|
||||
bars[i]:SetScript("OnShow", BarShowHide)
|
||||
if i <= 10 then bars[i]:SetScript("OnHide", BarShowHide) end
|
||||
end
|
||||
|
||||
-- autohide
|
||||
if autohide == "1" then
|
||||
@@ -1201,18 +1282,12 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
-- only show when pet actions exists
|
||||
bars[i]:SetShown(PetHasActionBar() or pfUI.unlock and pfUI.unlock:IsShown())
|
||||
|
||||
-- show/hide petbar on petbar updates
|
||||
-- show/hide petbar on petbar updates; the pet events of the
|
||||
-- pfActionBar frame refresh its buttons
|
||||
if init then
|
||||
bars[i]:RegisterEvent("PET_BAR_UPDATE")
|
||||
bars[i]:SetScript("OnEvent", function()
|
||||
-- hide obsolete buttons
|
||||
for i=1, NUM_PET_ACTION_SLOTS do
|
||||
if not PetHasActionBar() and bars[12][i] then
|
||||
bars[12][i]:Hide()
|
||||
end
|
||||
end
|
||||
-- refresh layout
|
||||
CreateActionBar(12)
|
||||
this:SetShown(PetHasActionBar() or pfUI.unlock and pfUI.unlock:IsShown())
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -1270,7 +1345,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
-- refresh button
|
||||
updatecache[bars[i][j].slot] = true
|
||||
QueueSlot(bars[i][j].slot)
|
||||
end
|
||||
|
||||
for j=buttons+1,12 do
|
||||
@@ -1303,25 +1378,27 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
elseif i == 12 then -- pet
|
||||
bars[i]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
|
||||
|
||||
-- make stance bar dodge by default
|
||||
bars[i]:SetScript("OnShow", function()
|
||||
if pfUI.unlock and pfUI.unlock:IsShown() then return end
|
||||
if bars[11] and bars[11]:IsShown() then
|
||||
bars[11]:ClearAllPoints()
|
||||
bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border)
|
||||
UpdateMovable(bars[11], true)
|
||||
end
|
||||
end)
|
||||
if init then
|
||||
-- make stance bar dodge by default
|
||||
bars[i]:HookScript("OnShow", function()
|
||||
if pfUI.unlock and pfUI.unlock:IsShown() then return end
|
||||
if bars[11] and bars[11]:IsShown() then
|
||||
bars[11]:ClearAllPoints()
|
||||
bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border)
|
||||
UpdateMovable(bars[11], true)
|
||||
end
|
||||
end)
|
||||
|
||||
-- restore old stance bar position
|
||||
bars[i]:SetScript("OnHide", function()
|
||||
if pfUI.unlock and pfUI.unlock:IsShown() then return end
|
||||
if bars[11] and bars[11]:IsShown() then
|
||||
bars[11]:ClearAllPoints()
|
||||
bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
|
||||
UpdateMovable(bars[11], true)
|
||||
end
|
||||
end)
|
||||
-- restore old stance bar position
|
||||
bars[i]:SetScript("OnHide", function()
|
||||
if pfUI.unlock and pfUI.unlock:IsShown() then return end
|
||||
if bars[11] and bars[11]:IsShown() then
|
||||
bars[11]:ClearAllPoints()
|
||||
bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
|
||||
UpdateMovable(bars[11], true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
else -- others
|
||||
bars[i]:SetPoint("TOP", 0, -i*50)
|
||||
end
|
||||
@@ -1382,7 +1459,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- create actionbars
|
||||
pfUI.bars = bars
|
||||
pfUI.bars.update = updatecache
|
||||
pfUI.bars.QueueSlot = QueueSlot
|
||||
pfUI.bars.buttons = buttoncache
|
||||
pfUI.bars.ButtonFullUpdate = ButtonFullUpdate
|
||||
pfUI.bars.ButtonEnter = ButtonEnter
|
||||
@@ -1392,13 +1469,13 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
if not typ then
|
||||
showgrid = state
|
||||
|
||||
for id in pairs(buttoncache) do
|
||||
updatecache[id] = true
|
||||
for slot in pairs(buttoncache) do
|
||||
QueueSlot(slot)
|
||||
end
|
||||
elseif typ == "PET" then
|
||||
showgrid_pet = state
|
||||
for slot=133,142 do
|
||||
updatecache[slot] = true
|
||||
QueueSlot(slot)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1411,6 +1488,12 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
pfUI.bars:UpdateConfig()
|
||||
|
||||
-- update all bars when entering or leaving unlock mode
|
||||
if pfUI.unlock then
|
||||
pfUI.unlock:HookScript("OnShow", function() pfUI.bars:UpdateConfig() end)
|
||||
pfUI.unlock:HookScript("OnHide", function() pfUI.bars:UpdateConfig() end)
|
||||
end
|
||||
|
||||
-- Localize custom keybinds for additional actionbars (see Bindings.xml)
|
||||
local names = {
|
||||
["PAGING"] = T["Paging Actionbar"],
|
||||
@@ -1492,7 +1575,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
if newID then
|
||||
reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID)
|
||||
end
|
||||
updatecache[slot] = true
|
||||
QueueSlot(slot)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1502,7 +1585,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
|
||||
end
|
||||
for slot in pairs(reagent_slots) do
|
||||
updatecache[slot] = true
|
||||
QueueSlot(slot)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+30
-16
@@ -89,42 +89,56 @@ pfUI:RegisterModule("bags", function ()
|
||||
|
||||
pfUI.bag.delay = { UpdateBag = {} }
|
||||
|
||||
pfUI.bag:SetScript("OnUpdate", function()
|
||||
-- update delayed ones every 0.1s
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
|
||||
-- run the updates queued by events, batched into one pass per 0.1s
|
||||
local function FlushDelayed()
|
||||
local delay = pfUI.bag.delay
|
||||
delay.pending = nil
|
||||
|
||||
if this.delay.RefreshSpells then
|
||||
this.delay.RefreshSpells = nil
|
||||
if delay.RefreshSpells then
|
||||
delay.RefreshSpells = nil
|
||||
pfUI.bag:RefreshSpells()
|
||||
end
|
||||
|
||||
if this.delay.CheckFullUpdate then
|
||||
this.delay.CheckFullUpdate = nil
|
||||
if delay.CheckFullUpdate then
|
||||
delay.CheckFullUpdate = nil
|
||||
pfUI.bag:CheckFullUpdate()
|
||||
end
|
||||
|
||||
if this.delay.UpdateCooldowns then
|
||||
this.delay.UpdateCooldowns = nil
|
||||
if delay.UpdateCooldowns then
|
||||
delay.UpdateCooldowns = nil
|
||||
pfUI.bag:UpdateCooldowns()
|
||||
end
|
||||
|
||||
if this.delay.UpdateItemLock then
|
||||
this.delay.UpdateItemLock = nil
|
||||
if delay.UpdateItemLock then
|
||||
delay.UpdateItemLock = nil
|
||||
pfUI.bag:UpdateItemLock()
|
||||
end
|
||||
|
||||
if this.delay.UpdateSearch then
|
||||
this.delay.UpdateSearch = nil
|
||||
if delay.UpdateSearch then
|
||||
delay.UpdateSearch = nil
|
||||
pfUI.bag:UpdateSearch()
|
||||
end
|
||||
|
||||
for bag in pairs(this.delay.UpdateBag) do
|
||||
this.delay.UpdateBag[bag] = nil
|
||||
for bag in pairs(delay.UpdateBag) do
|
||||
delay.UpdateBag[bag] = nil
|
||||
pfUI.bag:UpdateBag(bag)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function ScheduleFlush()
|
||||
if pfUI.bag.delay.pending then return end
|
||||
pfUI.bag.delay.pending = true
|
||||
C_Timer.After(.1, FlushDelayed)
|
||||
end
|
||||
|
||||
function pfUI.bag:QueueBagUpdate(bag)
|
||||
self.delay.UpdateBag[bag] = true
|
||||
ScheduleFlush()
|
||||
end
|
||||
|
||||
pfUI.bag:SetScript("OnEvent", function()
|
||||
ScheduleFlush()
|
||||
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
pfUI.bag:CreateBags()
|
||||
pfUI.bag.right:Hide()
|
||||
|
||||
+24
-15
@@ -130,15 +130,14 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
GameTooltip:Hide()
|
||||
end
|
||||
|
||||
local function StatusBarOnUpdate()
|
||||
local remaining = this.endtime - GetTime()
|
||||
this.bar:SetValue(remaining > 0 and remaining or 0)
|
||||
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
|
||||
this.time:SetText(remaining > 0 and GetColoredTimeString(remaining) or "")
|
||||
-- an enabled text binding writes every frame, shown or not
|
||||
local function StatusBarOnShow()
|
||||
this.timer:Enable()
|
||||
this.parent:RefreshPosition()
|
||||
end
|
||||
|
||||
local function StatusBarRefreshParent()
|
||||
local function StatusBarOnHide()
|
||||
this.timer:Disable()
|
||||
this.parent:RefreshPosition()
|
||||
end
|
||||
|
||||
@@ -183,6 +182,16 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
frame.time:SetTextColor(1,1,1,1)
|
||||
frame.time:SetJustifyH("RIGHT")
|
||||
|
||||
-- the bar drains and the time counts down from this, engine-side
|
||||
frame.duration = C_DurationUtil.CreateDuration()
|
||||
frame.bar:SetTimerDuration(frame.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
|
||||
frame.timer = C_DurationUtil.CreateDurationTextBinding()
|
||||
frame.timer:SetFontString(frame.time)
|
||||
frame.timer:SetFormatter(GetTimeFormatter())
|
||||
frame.timer:SetDuration(frame.duration)
|
||||
frame.timer:SetExpiredText("")
|
||||
frame.timer:SetZeroDurationText("")
|
||||
|
||||
frame.icon = frame:CreateTexture(nil, "OVERLAY")
|
||||
frame.icon:SetSize(height, height)
|
||||
frame.icon:SetPoint("LEFT", frame, "LEFT", 0, 0)
|
||||
@@ -195,9 +204,8 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
frame.stacks:SetJustifyV("CENTER")
|
||||
|
||||
frame.parent = parent
|
||||
frame:SetScript("OnUpdate", StatusBarOnUpdate)
|
||||
frame:SetScript("OnShow", StatusBarRefreshParent)
|
||||
frame:SetScript("OnHide", StatusBarRefreshParent)
|
||||
frame:SetScript("OnShow", StatusBarOnShow)
|
||||
frame:SetScript("OnHide", StatusBarOnHide)
|
||||
|
||||
frame:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||||
frame:SetScript("OnClick", StatusBarOnClick)
|
||||
@@ -261,7 +269,6 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
frame.bars[bar].id = data[2]
|
||||
frame.bars[bar].unit = frame.unit
|
||||
frame.bars[bar].type = frame.type
|
||||
frame.bars[bar].endtime = GetTime() + ( data[1] > 0 and data[1] or -1 )
|
||||
|
||||
-- update max duration the cached remaining values is less than
|
||||
-- the real one, indicates a buff renewal
|
||||
@@ -313,10 +320,12 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
frame.bars[bar].icon:SetTexture(data[4])
|
||||
end
|
||||
|
||||
-- cache maxduration
|
||||
if frame.bars[bar].cacheMaxDuration ~= frame.durations[uuid][2] then
|
||||
frame.bars[bar].cacheMaxDuration = frame.durations[uuid][2]
|
||||
frame.bars[bar].bar:SetMinMaxValues(0, frame.durations[uuid][2])
|
||||
-- the bar spans the longest time left seen for this aura; one without
|
||||
-- a time left (threshold -1) shows an empty bar and no time
|
||||
if data[1] > 0 then
|
||||
frame.bars[bar].duration:SetTimeFromEnd(GetTime() + data[1], frame.durations[uuid][2])
|
||||
else
|
||||
frame.bars[bar].duration:Reset()
|
||||
end
|
||||
|
||||
-- set stacks
|
||||
|
||||
+122
-161
@@ -5,37 +5,68 @@ pfUI:RegisterModule("castbar", function ()
|
||||
local rawborder, default_border = GetBorderSize("unitframes")
|
||||
local cbtexture = pfUI.media[C.appearance.castbar.texture]
|
||||
|
||||
-- Helper function for castbar timer formatting
|
||||
local function FormatCastbarTime(value)
|
||||
if C.unitframes.castbardecimals == "1" then
|
||||
-- 1 decimal, round half up (matches Blizzard spellbook display)
|
||||
return string.format("%.1f", floor(value * 10 + 0.5) / 10)
|
||||
else
|
||||
-- 2 decimals (default)
|
||||
return string.format("%.2f", value)
|
||||
end
|
||||
-- Castbar timer text: 1 decimal rounded half up (matches Blizzard spellbook
|
||||
-- display), or 2 decimals (default)
|
||||
local formatter = C_StringUtil.CreateNumericRuleFormatter()
|
||||
if C.unitframes.castbardecimals == "1" then
|
||||
formatter:AddBreakpoint({ threshold = 0, step = .1, format = "%.1f" })
|
||||
else
|
||||
formatter:AddBreakpoint({ threshold = 0, format = "%.2f" })
|
||||
end
|
||||
|
||||
-- Clear cast state on the bar and start the fade-out. On a normal end the bar
|
||||
-- is left at its current fill (a completed cast is already ~full; nothing
|
||||
-- snaps to full, which used to flash for a frame when the next cast stamped
|
||||
-- the bar). When `failed` is set for a cast that was actually in progress, the
|
||||
-- bar flashes full red before fading — the cancelled-cast indicator.
|
||||
-- "cur / max": a cast counts up, a channel counts down
|
||||
local castText = {
|
||||
{ property = Enum.DurationTextBindingProperty.ElapsedDuration, formatter = formatter },
|
||||
{ property = Enum.DurationTextBindingProperty.TotalDuration, formatter = formatter },
|
||||
}
|
||||
local channelText = {
|
||||
{ property = Enum.DurationTextBindingProperty.RemainingDuration, formatter = formatter },
|
||||
{ property = Enum.DurationTextBindingProperty.TotalDuration, formatter = formatter },
|
||||
}
|
||||
|
||||
-- The bar's fill and timer text both follow cb.duration on their own; only
|
||||
-- the pushback total in front of the text needs a refresh.
|
||||
local function UpdateTimerText(cb)
|
||||
if not cb.showtimer then return end
|
||||
local prefix = ""
|
||||
if cb.delay > 0 then
|
||||
prefix = "|cffffaaaa" .. (cb.isChannel and "-" or "+") .. formatter:FormatNumber(cb.delay) .. " |r "
|
||||
end
|
||||
cb.timer:SetTextFormat(prefix .. "{} / {}", cb.isChannel and channelText or castText)
|
||||
cb.timer:Enable()
|
||||
end
|
||||
|
||||
-- STOP doesn't arrive for every cast (a synthetic cast has none, a remote
|
||||
-- unit's can be missed out of range), so the bar also clears at its end time.
|
||||
local function ArmExpiry(cb)
|
||||
if cb.expiry then cb.expiry:Cancel() end
|
||||
cb.expiry = C_Timer.NewTimer(cb.duration:GetRemainingDuration(), cb.onExpire)
|
||||
end
|
||||
|
||||
-- Clear cast state on the bar and start the fade-out. The fill freezes where
|
||||
-- the cast stopped (SetMinMaxValues drops the timer). When `failed` is set for
|
||||
-- a cast that was actually in progress, the bar flashes full red before
|
||||
-- fading — the cancelled-cast indicator.
|
||||
local function ClearBar(cb, failed)
|
||||
local wasActive = cb.endTime ~= nil
|
||||
cb.startTime, cb.endTime, cb.isChannel = nil, nil, nil
|
||||
if cb.expiry then cb.expiry:Cancel() end
|
||||
cb.expiry = nil
|
||||
cb.timer:Disable()
|
||||
if cb.active then
|
||||
cb.bar:SetMinMaxValues(0, 1)
|
||||
if failed then
|
||||
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar.failcolor))
|
||||
cb.bar:SetValue(1)
|
||||
else
|
||||
cb.bar:SetValue(cb.isChannel and cb.duration:GetRemainingPercent() or cb.duration:GetElapsedPercent())
|
||||
end
|
||||
end
|
||||
cb.active, cb.isChannel = nil, nil
|
||||
cb.activeName, cb.spellID = nil, nil
|
||||
cb.isTradeskill = nil
|
||||
cb.tradeskillTotal, cb.tradeskillCompleted, cb.tradeskillSpellID = nil, nil, nil
|
||||
cb.tradeskillSingleMs, cb.currentCraftStart = nil, nil
|
||||
cb.lastMax = nil
|
||||
cb.tradeskillSingle = nil
|
||||
cb.delay = 0
|
||||
if failed and wasActive then
|
||||
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar.failcolor))
|
||||
cb.bar:SetMinMaxValues(0, 1)
|
||||
cb.bar:SetValue(1)
|
||||
end
|
||||
if cb.bar.spark then cb.bar.spark:Hide() end
|
||||
if cb.craftbar then cb.craftbar:Hide() end
|
||||
if cb:IsShown() and not cb.fade:IsPlaying() then cb.fade:Play() end
|
||||
end
|
||||
|
||||
@@ -55,40 +86,35 @@ pfUI:RegisterModule("castbar", function ()
|
||||
-- fills continuously across the chain instead of resetting per craft.
|
||||
-- Also arms the per-craft spark — a thin marker that crosses the bar once
|
||||
-- per craft (so it moves N× faster than the main fill on an N-stack).
|
||||
local function EnterTradeskillMerge(cb, startMs, endMs, count)
|
||||
local function EnterTradeskillMerge(cb, count)
|
||||
local start, single = cb.duration:GetStartTime(), cb.duration:GetTotalDuration()
|
||||
cb.tradeskillTotal = count
|
||||
cb.tradeskillCompleted = 0
|
||||
cb.tradeskillSpellID = cb.spellID
|
||||
local single = endMs - startMs
|
||||
cb.tradeskillSingleMs = single
|
||||
cb.currentCraftStart = startMs
|
||||
local mergedEnd = startMs + single * count
|
||||
cb.endTime = mergedEnd
|
||||
local duration = (mergedEnd - startMs) / 1000
|
||||
cb.bar:SetMinMaxValues(0, duration)
|
||||
cb.lastMax = duration
|
||||
if cb.bar.spark then cb.bar.spark:Show() end
|
||||
cb.tradeskillSingle = single
|
||||
cb.craft:SetTimeFromStart(start, single)
|
||||
cb.duration:SetTimeFromStart(start, single * count)
|
||||
ArmExpiry(cb)
|
||||
cb.craftbar:Show()
|
||||
UpdateTradeskillLabel(cb)
|
||||
end
|
||||
|
||||
-- Mark the start of craft 2..N within an active merge (called from the
|
||||
-- UNIT_SPELLCAST_START handler). Resets the per-craft spark to the left
|
||||
-- edge of the bar.
|
||||
-- UNIT_SPELLCAST_START handler) and re-fit the bar's end to the crafts left.
|
||||
local function StartTradeskillCraft(cb)
|
||||
cb.currentCraftStart = GetTime() * 1000
|
||||
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
|
||||
cb.endTime = cb.currentCraftStart + cb.tradeskillSingleMs * remaining
|
||||
local duration = (cb.endTime - cb.startTime) / 1000
|
||||
cb.bar:SetMinMaxValues(0, duration)
|
||||
cb.lastMax = duration
|
||||
local now = GetTime()
|
||||
local remaining = cb.tradeskillTotal - cb.tradeskillCompleted
|
||||
cb.craft:SetTimeFromStart(now, cb.tradeskillSingle)
|
||||
cb.duration:SetTimeSpan(cb.duration:GetStartTime(), now + cb.tradeskillSingle * remaining)
|
||||
ArmExpiry(cb)
|
||||
UpdateTradeskillLabel(cb)
|
||||
end
|
||||
|
||||
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
|
||||
-- then animates the fill from this state without touching C_Spell.
|
||||
-- Stamp the bar with cast data and render text/icon/lag once. From here the
|
||||
-- bar's timer and text binding follow cb.duration without any Lua.
|
||||
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank, noInterrupt)
|
||||
cb.startTime = startMs
|
||||
cb.endTime = endMs
|
||||
cb.duration:SetTimeSpan(startMs / 1000, endMs / 1000)
|
||||
cb.active = true
|
||||
cb.isChannel = isChannel
|
||||
cb.spellID = spellID
|
||||
cb.activeName = name
|
||||
@@ -124,27 +150,18 @@ pfUI:RegisterModule("castbar", function ()
|
||||
cb.icon:Hide()
|
||||
end
|
||||
|
||||
local duration = (endMs - startMs) / 1000
|
||||
if cb.showlag then
|
||||
local _, _, lag = GetNetStats()
|
||||
cb.bar.lag:SetWidth(math.min(cb:GetWidth(), cb:GetWidth() / duration * (lag/1000)))
|
||||
cb.bar.lag:SetWidth(math.min(cb:GetWidth(), cb:GetWidth() / cb.duration:GetTotalDuration() * (lag/1000)))
|
||||
cb.bar.lag:Show()
|
||||
else
|
||||
cb.bar.lag:Hide()
|
||||
end
|
||||
|
||||
cb.bar:SetMinMaxValues(0, duration)
|
||||
cb.lastMax = duration
|
||||
|
||||
-- Prime the fill on this frame. StampBar otherwise leaves the previous
|
||||
-- value in place (ClearBar, run on the prior cast's STOP, leaves it full),
|
||||
-- so the bar would flash full for the frame between here and the next
|
||||
-- OnUpdate tick. Reset the throttle too so the timer text updates promptly.
|
||||
local nowSec = GetTime()
|
||||
local cur = isChannel and (endMs / 1000 - nowSec) or (nowSec - startMs / 1000)
|
||||
if cur < 0 then cur = 0 elseif cur > duration then cur = duration end
|
||||
cb.bar:SetValue(cur)
|
||||
cb.tick = 0
|
||||
cb.bar:SetTimerDuration(cb.duration, Enum.StatusBarInterpolation.Immediate,
|
||||
isChannel and Enum.StatusBarTimerDirection.RemainingTime or Enum.StatusBarTimerDirection.ElapsedTime)
|
||||
UpdateTimerText(cb)
|
||||
ArmExpiry(cb)
|
||||
end
|
||||
|
||||
-- One-shot poll: read C_Spell for the bar's unit, stamp or clear. Called
|
||||
@@ -186,6 +203,10 @@ pfUI:RegisterModule("castbar", function ()
|
||||
cb:SetFrameLevel(8)
|
||||
|
||||
cb.unitstr = unitstr
|
||||
cb.delay = 0
|
||||
cb.duration = C_DurationUtil.CreateDuration()
|
||||
cb.craft = C_DurationUtil.CreateDuration() -- the current craft of a tradeskill merge
|
||||
cb.onExpire = function() ClearBar(cb) end
|
||||
|
||||
-- icon
|
||||
cb.icon = CreateFrame("Frame", nil, cb)
|
||||
@@ -210,7 +231,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
CreateBackdropShadow(cb.bar)
|
||||
|
||||
-- text left
|
||||
cb.bar.left = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
cb.bar.left = cb.bar:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
cb.bar.left:ClearAllPoints()
|
||||
cb.bar.left:SetPoint("TOPLEFT", cb.bar, "TOPLEFT", 3 + C.castbar[unitstr].txtleftoffx, C.castbar[unitstr].txtleftoffy)
|
||||
cb.bar.left:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", -3 + C.castbar[unitstr].txtleftoffx, C.castbar[unitstr].txtleftoffy)
|
||||
@@ -221,7 +242,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
cb.bar.left:SetJustifyH(C.castbar[unitstr].namealign or "LEFT")
|
||||
|
||||
-- text right
|
||||
cb.bar.right = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
cb.bar.right = cb.bar:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
cb.bar.right:ClearAllPoints()
|
||||
cb.bar.right:SetPoint("TOPLEFT", cb.bar, "TOPLEFT", 3 + C.castbar[unitstr].txtrightoffx, C.castbar[unitstr].txtrightoffy)
|
||||
cb.bar.right:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", -3 + C.castbar[unitstr].txtrightoffx, C.castbar[unitstr].txtrightoffy)
|
||||
@@ -231,6 +252,10 @@ pfUI:RegisterModule("castbar", function ()
|
||||
cb.bar.right:SetFont(font, font_size, "OUTLINE")
|
||||
cb.bar.right:SetJustifyH(C.castbar[unitstr].timealign or "RIGHT")
|
||||
|
||||
cb.timer = C_DurationUtil.CreateDurationTextBinding()
|
||||
cb.timer:SetFontString(cb.bar.right)
|
||||
cb.timer:SetDuration(cb.duration)
|
||||
|
||||
cb.bar.lag = cb.bar:CreateTexture(nil, "OVERLAY")
|
||||
cb.bar.lag:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0)
|
||||
cb.bar.lag:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", 0, 0)
|
||||
@@ -238,88 +263,29 @@ pfUI:RegisterModule("castbar", function ()
|
||||
|
||||
-- Per-craft progress spark for tradeskill merge — a thin vertical line
|
||||
-- that crosses the bar once per craft in the chain (so on a 5-stack it
|
||||
-- moves 5x faster than the main fill). Position is updated by OnUpdate
|
||||
-- while a merge is active; hidden otherwise.
|
||||
cb.bar.spark = cb.bar:CreateTexture(nil, "OVERLAY")
|
||||
cb.bar.spark:SetColorTexture(1, 1, 1, 0.8)
|
||||
cb.bar.spark:SetWidth(2)
|
||||
cb.bar.spark:SetPoint("TOP", cb.bar, "TOPLEFT", 0, 0)
|
||||
cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0)
|
||||
cb.bar.spark:Hide()
|
||||
-- moves 5x faster than the main fill). It rides the edge of an invisible
|
||||
-- fill that follows cb.craft, shown while a merge is active, and snaps
|
||||
-- back to the left each time StartTradeskillCraft restarts the craft.
|
||||
cb.craftbar = CreateFrame("StatusBar", nil, cb.bar)
|
||||
cb.craftbar:SetAllPoints(cb.bar)
|
||||
cb.craftbar:SetColorFill(1, 1, 1, 0)
|
||||
cb.craftbar:SetTimerDuration(cb.craft, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
|
||||
cb.craftbar:Hide()
|
||||
|
||||
-- Fade-out after a cast ends. The bar is hidden while idle, so its OnUpdate
|
||||
-- only runs during a cast; unlock mode keeps it shown for dragging.
|
||||
local function unlocked() return cb.drag and cb.drag:IsShown() end
|
||||
cb.craftbar.spark = cb.craftbar:CreateTexture(nil, "OVERLAY")
|
||||
cb.craftbar.spark:SetColorTexture(1, 1, 1, 0.8)
|
||||
cb.craftbar.spark:SetWidth(2)
|
||||
cb.craftbar.spark:SetPoint("TOPRIGHT", cb.craftbar:GetStatusBarTexture(), "TOPRIGHT")
|
||||
cb.craftbar.spark:SetPoint("BOTTOMRIGHT", cb.craftbar:GetStatusBarTexture(), "BOTTOMRIGHT")
|
||||
|
||||
-- Fade-out after a cast ends. The bar is hidden while idle; unlock mode
|
||||
-- keeps it shown for dragging.
|
||||
cb.fade = cb:CreateAnimationGroup()
|
||||
local fade = cb.fade:CreateAnimation("Alpha")
|
||||
fade:SetChange(-1)
|
||||
fade:SetDuration(.4)
|
||||
cb.fade:SetScript("OnFinished", function()
|
||||
if not unlocked() then cb:Hide() end
|
||||
end)
|
||||
|
||||
-- OnUpdate animates the bar fill. All state transitions (cast start/stop/interrupt, channel start/stop,
|
||||
-- pushback) come from the event handler below — we never poll C_Spell
|
||||
-- here.
|
||||
cb:SetScript("OnUpdate", function()
|
||||
if (this.tick or 0) > GetTime() then return end
|
||||
this.tick = GetTime() + 0.020 -- ~50 FPS
|
||||
|
||||
if unlocked() then
|
||||
this.fade:Stop()
|
||||
return
|
||||
end
|
||||
|
||||
if this.fade:IsPlaying() then return end
|
||||
|
||||
if not this.endTime then
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- Non-player bars: if the unit disappears (target died / detarget),
|
||||
-- drop the bar immediately.
|
||||
if this.unitstr ~= "player" and not UnitExists(this.unitstr) then
|
||||
ClearBar(this)
|
||||
return
|
||||
end
|
||||
|
||||
local now = GetTime()
|
||||
local endSec = this.endTime / 1000
|
||||
if now >= endSec then
|
||||
ClearBar(this)
|
||||
return
|
||||
end
|
||||
|
||||
local startSec = this.startTime / 1000
|
||||
local max = endSec - startSec
|
||||
local cur = this.isChannel and (endSec - now) or (now - startSec)
|
||||
if cur > max then cur = max end
|
||||
if cur < 0 then cur = 0 end
|
||||
|
||||
this.bar:SetValue(cur)
|
||||
|
||||
-- Per-craft spark for a tradeskill merge: position by (now - current
|
||||
-- craft's start) / single-craft duration, clamped to [0, 1]. Snaps back
|
||||
-- to the left edge each time StartTradeskillCraft fires for craft N+1.
|
||||
if this.tradeskillTotal and this.tradeskillSingleMs and this.currentCraftStart then
|
||||
local craftElapsed = now * 1000 - this.currentCraftStart
|
||||
local p = craftElapsed / this.tradeskillSingleMs
|
||||
if p < 0 then p = 0 elseif p > 1 then p = 1 end
|
||||
local x = p * this.bar:GetWidth() - 1
|
||||
this.bar.spark:ClearAllPoints()
|
||||
this.bar.spark:SetPoint("TOP", this.bar, "TOPLEFT", x, 0)
|
||||
this.bar.spark:SetPoint("BOTTOM", this.bar, "BOTTOMLEFT", x, 0)
|
||||
end
|
||||
|
||||
if this.showtimer then
|
||||
if (this.delay or 0) > 0 then
|
||||
local prefix = "|cffffaaaa" .. (this.isChannel and "-" or "+") .. FormatCastbarTime(this.delay) .. " |r "
|
||||
this.bar.right:SetText(prefix .. FormatCastbarTime(cur) .. " / " .. FormatCastbarTime(max))
|
||||
else
|
||||
this.bar.right:SetText(FormatCastbarTime(cur) .. " / " .. FormatCastbarTime(max))
|
||||
end
|
||||
end
|
||||
if not (cb.drag and cb.drag:IsShown()) then cb:Hide() end
|
||||
end)
|
||||
|
||||
-- Cast lifecycle, entirely on ClassicAPI's UNIT_SPELLCAST_* events. They
|
||||
@@ -383,7 +349,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
RefreshBar(this, arg5)
|
||||
if this.isTradeskill and (this.pendingTradeskillCount or 0) > 1
|
||||
and C.castbar.player.mergetradeskill == "1" then
|
||||
EnterTradeskillMerge(this, this.startTime, this.endTime, this.pendingTradeskillCount)
|
||||
EnterTradeskillMerge(this, this.pendingTradeskillCount)
|
||||
end
|
||||
this.pendingTradeskillCount = nil
|
||||
end
|
||||
@@ -392,7 +358,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
-- count and clear when the chain is done. A no-op for normal casts,
|
||||
-- which are cleared by UNIT_SPELLCAST_STOP.
|
||||
if this.tradeskillTotal and arg3 == this.tradeskillSpellID then
|
||||
this.tradeskillCompleted = (this.tradeskillCompleted or 0) + 1
|
||||
this.tradeskillCompleted = this.tradeskillCompleted + 1
|
||||
if this.tradeskillCompleted >= this.tradeskillTotal then
|
||||
ClearBar(this)
|
||||
else
|
||||
@@ -412,35 +378,30 @@ pfUI:RegisterModule("castbar", function ()
|
||||
elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
|
||||
ClearBar(this, true)
|
||||
elseif event == "UNIT_SPELLCAST_INTERRUPTIBLE" or event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE" then
|
||||
if this.endTime then
|
||||
if this.active then
|
||||
SetCastbarShield(this.bar, this.icon, event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE", this.isChannel)
|
||||
end
|
||||
elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then
|
||||
-- Pushback: a cast delayed later or a channel shortened. Following
|
||||
-- Quartz, re-poll just the times and accumulate the shift into a running
|
||||
-- Quartz, re-read just the times and accumulate the shift into a running
|
||||
-- this.delay for the +/- indicator, rather than a full restamp (which
|
||||
-- would reset that total and re-render icon/text). ClassicAPI moves
|
||||
-- endMs (SpellDelayed_h bumps g_cast.endMs; the MSG_CHANNEL_UPDATE
|
||||
-- co-hook rewrites g_channel.endMs) while startMs stays put, so we diff
|
||||
-- endMs. delay is a positive magnitude; OnUpdate signs it "+" for casts
|
||||
-- and "-" for channels. Skipped during a tradeskill merge.
|
||||
if this.endTime and not this.tradeskillTotal then
|
||||
local startMs, endMs
|
||||
-- would reset that total and re-render icon/text). Pushback moves the
|
||||
-- end time while the start stays put, so we diff the end. delay is a
|
||||
-- positive magnitude; UpdateTimerText signs it "+" for casts and "-"
|
||||
-- for channels. Skipped during a tradeskill merge.
|
||||
if this.active and not this.tradeskillTotal then
|
||||
local duration
|
||||
if this.isChannel then
|
||||
local _, _, _, s, e = C_Spell.UnitChannelInfo(unit)
|
||||
startMs, endMs = s, e
|
||||
duration = UnitChannelDuration(unit)
|
||||
else
|
||||
local _, _, _, s, e = C_Spell.UnitCastingInfo(unit)
|
||||
startMs, endMs = s, e
|
||||
duration = UnitCastingDuration(unit)
|
||||
end
|
||||
if startMs and endMs then
|
||||
local shift = this.isChannel and (this.endTime - endMs) or (endMs - this.endTime)
|
||||
this.delay = (this.delay or 0) + shift / 1000
|
||||
this.startTime = startMs
|
||||
this.endTime = endMs
|
||||
local newDuration = (endMs - startMs) / 1000
|
||||
this.bar:SetMinMaxValues(0, newDuration)
|
||||
this.lastMax = newDuration
|
||||
if duration then
|
||||
local shift = duration:GetEndTime() - this.duration:GetEndTime()
|
||||
this.delay = this.delay + (this.isChannel and -shift or shift)
|
||||
this.duration:Assign(duration)
|
||||
UpdateTimerText(this)
|
||||
ArmExpiry(this)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+2
-14
@@ -701,12 +701,6 @@ pfUI:RegisterModule("chat", function ()
|
||||
local r,g,b = GetStringColor(C.chat.text.unknowncolor)
|
||||
local unknowncolorhex = rgbhex(r,g,b)
|
||||
|
||||
local function GetPlayerLevel(name)
|
||||
if not pfUI_playerDB then return nil end
|
||||
if not pfUI_playerDB[name] then return nil end
|
||||
return pfUI_playerDB[name].level
|
||||
end
|
||||
|
||||
-- the sender of the line AddMessage is reading, from its chat event
|
||||
local lineSender, lineSenderClass, lineSenderRace, lineSenderSex
|
||||
|
||||
@@ -727,10 +721,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
else
|
||||
_, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real)
|
||||
end
|
||||
if not class then
|
||||
class = GetUnitInfo(real)
|
||||
end
|
||||
if class and class ~= UNKNOWN then
|
||||
if class then
|
||||
color = "|c" .. PFUI_CLASS_COLORS[class].colorStr
|
||||
if C.chat.text.factionicons == "1" then
|
||||
icon = GetFactionIcon(raceKey)
|
||||
@@ -745,11 +736,8 @@ pfUI:RegisterModule("chat", function ()
|
||||
|
||||
local suffix = ""
|
||||
local level = C.chat.text.playerlevel == "1" and GetPlayerLevel(real)
|
||||
if level and level > 0 then
|
||||
if level then
|
||||
suffix = " " .. rgbhex(GetDifficultyColor(level)) .. level .. "|r"
|
||||
elseif level then
|
||||
-- Show ?? for unknown levels (e.g. -1 from UnitLevel)
|
||||
suffix = " |cffff0000??|r"
|
||||
end
|
||||
|
||||
if color == "" and suffix == "" then return link end
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
pfUI:RegisterModule("chatcopy", function ()
|
||||
local f = CreateFrame("Frame")
|
||||
f:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
f:SetScript("OnEvent", function()
|
||||
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
|
||||
if not pfUI.chat then return end
|
||||
|
||||
local button = CreateFrame("Button", "pfChatCopyButton", pfUI.chat.left.panelTop)
|
||||
@@ -99,7 +97,5 @@ pfUI:RegisterModule("chatcopy", function ()
|
||||
|
||||
frame.scroll = scroll
|
||||
end
|
||||
|
||||
this:UnregisterAllEvents()
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -2509,13 +2509,7 @@ pfUI:RegisterModule("combatlog", function ()
|
||||
local buttonName, button, textWidth;
|
||||
local buttonIndex = 1;
|
||||
-- subtract the width of the dropdown button
|
||||
local clogleft, clogright = COMBATLOG:GetRight(), COMBATLOG:GetLeft();
|
||||
local maxWidth;
|
||||
if ( clogleft and clogright ) then
|
||||
maxWidth = (COMBATLOG:GetRight()-COMBATLOG:GetLeft())-31; --Hacky hacky because GetWidth goes crazy when it is docked
|
||||
else
|
||||
maxWidth = COMBATLOG:GetWidth() - 31;
|
||||
end
|
||||
local maxWidth = COMBATLOG:GetUnscaledWidth() - 31;
|
||||
|
||||
local additionalFilterButton = CombatLogQuickButtonFrame.AdditionalFilterButton;
|
||||
|
||||
|
||||
+12
-9
@@ -30,15 +30,15 @@ pfUI:RegisterModule("easteregg", function ()
|
||||
scene.dtext:SetPoint("TOP", 0, -430)
|
||||
scene.dtext:SetText(info and info .. "\n\n" .. exit or exit)
|
||||
|
||||
-- Fade the night in by playing a fade-out backwards. The scene keeps an
|
||||
-- alpha of 1, which comes back when the fade ends.
|
||||
-- Fade the night in. The scene keeps an alpha of 1, which comes back
|
||||
-- when the fade ends.
|
||||
scene.dusk = scene:CreateAnimationGroup()
|
||||
local fade = scene.dusk:CreateAnimation("Alpha")
|
||||
fade:SetChange(-1)
|
||||
fade:SetToAlpha(1)
|
||||
fade:SetDuration(.8)
|
||||
|
||||
scene:SetScript("OnShow", function()
|
||||
this.dusk:Play(true)
|
||||
this.dusk:Play()
|
||||
end)
|
||||
|
||||
-- start over on the next AFK
|
||||
@@ -159,7 +159,7 @@ pfUI:RegisterModule("easteregg", function ()
|
||||
halloween.text:SetTextColor(1, .5, 0)
|
||||
|
||||
-- Candlelight glows in from the screen edges. It flickers in uneven
|
||||
-- steps of { duration, change } that end where they began, so the loop
|
||||
-- steps of { duration, alpha } that end where they began, so the loop
|
||||
-- never jumps.
|
||||
local candle = CreateFrame("Frame", nil, halloween)
|
||||
candle:SetAllPoints()
|
||||
@@ -169,16 +169,19 @@ pfUI:RegisterModule("easteregg", function ()
|
||||
candle.flicker = candle:CreateAnimationGroup()
|
||||
candle.flicker:SetLooping("REPEAT")
|
||||
local steps = {
|
||||
{ .18, -.25 }, { .09, .1 }, { .22, -.2 }, { .14, .3 }, { .3, -.1 },
|
||||
{ .11, -.15 }, { .2, .2 }, { .16, -.3 }, { .26, .25 }, { .12, .15 },
|
||||
{ .18, .75 }, { .09, .85 }, { .22, .65 }, { .14, .95 }, { .3, .85 },
|
||||
{ .11, .7 }, { .2, .9 }, { .16, .6 }, { .26, .85 }, { .12, 1 },
|
||||
}
|
||||
local from = 1
|
||||
for order, step in ipairs(steps) do
|
||||
local duration, change = unpack(step)
|
||||
local duration, alpha = unpack(step)
|
||||
local fade = candle.flicker:CreateAnimation("Alpha")
|
||||
fade:SetOrder(order)
|
||||
fade:SetDuration(duration)
|
||||
fade:SetChange(change)
|
||||
fade:SetFromAlpha(from)
|
||||
fade:SetToAlpha(alpha)
|
||||
fade:SetSmoothing("IN_OUT")
|
||||
from = alpha
|
||||
end
|
||||
|
||||
-- Once the night has fallen, undead rise along the bottom one after
|
||||
|
||||
+79
-84
@@ -84,11 +84,6 @@ pfUI:RegisterModule("energytick", function()
|
||||
return
|
||||
end
|
||||
|
||||
-- inside the module body on purpose: C is on pfUI.env, not _G
|
||||
local function getBarWidth()
|
||||
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
|
||||
end
|
||||
|
||||
local rawborder, default_border = GetBorderSize("unitframes")
|
||||
local cbtexture = pfUI.media[C.appearance.castbar.texture]
|
||||
|
||||
@@ -128,8 +123,13 @@ pfUI:RegisterModule("energytick", function()
|
||||
return true
|
||||
end
|
||||
|
||||
local energytick = CreateFrame("Frame", nil, pfUI.uf.player.power.bar)
|
||||
-- the bar sweeps its fill over one tick period from a duration object; the
|
||||
-- fill is invisible and carries the spark on its edge
|
||||
local energytick = CreateFrame("StatusBar", nil, pfUI.uf.player.power.bar)
|
||||
energytick:SetAllPoints(pfUI.uf.player.power.bar)
|
||||
energytick:SetColorFill(1, 1, 1, 0)
|
||||
energytick.sweep = C_DurationUtil.CreateDuration()
|
||||
energytick:SetTimerDuration(energytick.sweep, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
|
||||
energytick:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
energytick:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player")
|
||||
energytick:RegisterUnitEvent("UNIT_POWER_UPDATE", "player")
|
||||
@@ -138,21 +138,61 @@ pfUI:RegisterModule("energytick", function()
|
||||
energytick:RegisterEvent("SPELLS_CHANGED")
|
||||
energytick:RegisterEvent("PLAYER_AURAS_CHANGED")
|
||||
|
||||
-- the spark hides until a tick is seen, and dims while the rule is up and
|
||||
-- nothing has ticked inside it yet
|
||||
local function UpdateRule()
|
||||
local fsrUp = energytick.mode == "MANA" and energytick.fsr:IsActive()
|
||||
energytick.fsrbar:SetShown(fsrUp and energytick:IsShown() and C.unitframes.player.showfsr == "1")
|
||||
energytick.spark:SetAlpha(not energytick.start and 0 or (fsrUp and not energytick.fsrGain) and .4 or 1)
|
||||
end
|
||||
|
||||
-- the window closed without being re-armed
|
||||
local function RuleEnd()
|
||||
energytick.fsrSpell, energytick.fsrGain = nil, nil
|
||||
energytick.fsr:Reset()
|
||||
UpdateRule()
|
||||
end
|
||||
|
||||
local function ArmRule()
|
||||
energytick.fsr:SetTimeFromStart(GetTime(), FIVE_SECOND_RULE)
|
||||
if energytick.ruletimer then energytick.ruletimer:Cancel() end
|
||||
energytick.ruletimer = C_Timer.NewTimer(FIVE_SECOND_RULE, RuleEnd)
|
||||
end
|
||||
|
||||
-- sweeps the period that holds now and wakes at its end. Rolls over by
|
||||
-- whole periods, not from now: restarting bakes timer overshoot into the
|
||||
-- phase as drift the lock then has to chase. Runs only while shown; a
|
||||
-- hidden bar keeps its phase and catches up as it shows.
|
||||
local Sweep
|
||||
local function Wrap() Sweep() end
|
||||
function Sweep()
|
||||
if energytick.wrap then energytick.wrap:Cancel() end
|
||||
if not energytick.start or not energytick:IsShown() then return end
|
||||
|
||||
local now = GetTime()
|
||||
while now - energytick.start >= energytick.max do
|
||||
energytick.start = energytick.start + energytick.max * math.floor((now - energytick.start) / energytick.max)
|
||||
energytick.max = getAdjustedTickTimer()
|
||||
end
|
||||
|
||||
energytick.sweep:SetTimeFromStart(energytick.start, energytick.max)
|
||||
energytick.wrap = C_Timer.NewTimer(energytick.start + energytick.max - now, Wrap)
|
||||
end
|
||||
|
||||
energytick:SetScript("OnEvent", function()
|
||||
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
|
||||
this.mode = "MANA"
|
||||
this:Show()
|
||||
elseif UnitPowerType("player") == Enum.PowerType.Energy and C.unitframes.player.energy == "1" then
|
||||
this.mode = "ENERGY"
|
||||
this:Show()
|
||||
else
|
||||
this.mode = nil
|
||||
this:Hide()
|
||||
end
|
||||
|
||||
-- mana regen stops showing at full mana
|
||||
this:SetShown(this.mode and not (this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player")))
|
||||
|
||||
if event == "SPELLS_CHANGED" or event == "PLAYER_AURAS_CHANGED" then
|
||||
energyRegenTimeMod = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- a shift swaps the power the gains are measured on
|
||||
@@ -166,18 +206,17 @@ pfUI:RegisterModule("energytick", function()
|
||||
local cost = C_Spell.GetSpellPowerCost(arg3)
|
||||
cost = cost and cost[1]
|
||||
if cost and cost.type == Enum.PowerType.Mana and cost.cost > 0 then
|
||||
this.fsrSpell, this.fsrEnd = arg3, GetTime() + FIVE_SECOND_RULE
|
||||
this.fsrGain = nil
|
||||
this.fsrSpell, this.fsrGain = arg3, nil
|
||||
ArmRule()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Unit::Update won't expire the rule while the spending spell still channels
|
||||
if event == "UNIT_SPELLCAST_CHANNEL_STOP" and arg1 == "player" then
|
||||
if this.fsrSpell and this.fsrSpell == arg3 then
|
||||
this.fsrEnd, this.fsrGain = GetTime() + FIVE_SECOND_RULE, nil
|
||||
this.fsrGain = nil
|
||||
ArmRule()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- only the shown power: a cat druid's mana regen is not an energy gain
|
||||
@@ -189,89 +228,31 @@ pfUI:RegisterModule("energytick", function()
|
||||
-- only a gain can be the tick; a spend never touches the phase
|
||||
if diff > 0 and lockTick(this) then
|
||||
-- a tick inside the window proves regen continues through it
|
||||
if this.fsrEnd and this.fsrEnd > GetTime() then
|
||||
if this.fsr:IsActive() then
|
||||
this.fsrGain = true
|
||||
end
|
||||
end
|
||||
|
||||
-- phase is kept while hidden; OnUpdate catches up by whole periods
|
||||
if this.mode == "MANA" and power >= UnitPowerMax("player") then
|
||||
this:Hide()
|
||||
Sweep()
|
||||
end
|
||||
end
|
||||
|
||||
UpdateRule()
|
||||
end)
|
||||
|
||||
energytick:SetScript("OnUpdate", function()
|
||||
-- Throttle for performance
|
||||
if (this.tick or 0) > GetTime() then
|
||||
return
|
||||
end
|
||||
this.tick = GetTime() + 0.020 -- ~50 FPS
|
||||
|
||||
-- five-second rule drains to nothing. The display is cosmetic and can be
|
||||
-- hidden on its own; the window itself (fsrEnd/fsrGain) keeps running
|
||||
-- either way -- it still dims the sweep spark below.
|
||||
local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0
|
||||
if this.mode == "MANA" and remaining > 0 then
|
||||
if C.unitframes.player.showfsr == "1" then
|
||||
if fsrOverlay then
|
||||
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE)
|
||||
else
|
||||
this.fsrbar:SetValue(remaining)
|
||||
end
|
||||
this.fsrbar:Show()
|
||||
else
|
||||
this.fsrbar:Hide()
|
||||
end
|
||||
else
|
||||
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
|
||||
this.fsrbar:Hide()
|
||||
end
|
||||
|
||||
if not this.start then
|
||||
this.spark:SetAlpha(0)
|
||||
return
|
||||
end
|
||||
|
||||
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
|
||||
this.spark:SetAlpha(0)
|
||||
return
|
||||
end
|
||||
|
||||
this.current = GetTime() - this.start
|
||||
|
||||
-- roll over by whole periods, not from now: restarting bakes frame
|
||||
-- overshoot into the phase as drift the lock then has to chase
|
||||
if this.current > this.max then
|
||||
this.start = this.start + this.max * math.floor(this.current / this.max)
|
||||
this.max = getAdjustedTickTimer()
|
||||
this.current = GetTime() - this.start
|
||||
end
|
||||
|
||||
-- dim while the rule is up and nothing has ticked inside it yet
|
||||
this.spark:SetAlpha((remaining > 0 and not this.fsrGain) and .4 or 1)
|
||||
|
||||
if not C.unitframes.player.pheight then
|
||||
return
|
||||
end
|
||||
|
||||
local pos = getBarWidth() * (this.current / this.max)
|
||||
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
|
||||
end)
|
||||
energytick.fsr = C_DurationUtil.CreateDuration()
|
||||
|
||||
if fsrOverlay then
|
||||
-- classic style: a translucent shade on top of the power bar's own fill
|
||||
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
|
||||
energytick.fsrbar:SetColorTexture(1, 1, 1, .15)
|
||||
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0)
|
||||
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0)
|
||||
-- classic style: a translucent shade on top of the power bar's own fill,
|
||||
-- under the spark
|
||||
energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player.power.bar)
|
||||
energytick.fsrbar:SetAllPoints(energytick)
|
||||
energytick.fsrbar:SetColorFill(1, 1, 1, .15)
|
||||
energytick:SetFrameLevel(energytick.fsrbar:GetFrameLevel() + 1)
|
||||
else
|
||||
-- a real bar below the power bar, not an overlay on top of it -- the same
|
||||
-- treatment nameplate castbars get relative to the health bar, so the
|
||||
-- window can't be mistaken for missing mana
|
||||
energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player)
|
||||
energytick.fsrbar:SetStatusBarTexture(cbtexture)
|
||||
energytick.fsrbar:SetMinMaxValues(0, FIVE_SECOND_RULE)
|
||||
energytick.fsrbar:SetPoint("TOPLEFT", pfUI.uf.player.power.bar, "BOTTOMLEFT", 0, -default_border * 2)
|
||||
energytick.fsrbar:SetPoint("TOPRIGHT", pfUI.uf.player.power.bar, "BOTTOMRIGHT", 0, -default_border * 2)
|
||||
energytick.fsrbar:SetHeight(tonumber(C.unitframes.player.fsrheight) or 4)
|
||||
@@ -279,13 +260,27 @@ pfUI:RegisterModule("energytick", function()
|
||||
CreateBackdrop(energytick.fsrbar, default_border)
|
||||
CreateBackdropShadow(energytick.fsrbar)
|
||||
end
|
||||
energytick.fsrbar:SetTimerDuration(energytick.fsr, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
|
||||
energytick.fsrbar:Hide()
|
||||
|
||||
-- the bar isn't parented to energytick, so it follows it by hand
|
||||
energytick:SetScript("OnShow", function()
|
||||
Sweep()
|
||||
UpdateRule()
|
||||
end)
|
||||
|
||||
energytick:SetScript("OnHide", function()
|
||||
Sweep()
|
||||
this.fsrbar:Hide()
|
||||
end)
|
||||
|
||||
energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
|
||||
energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
|
||||
energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
|
||||
energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
|
||||
energytick.spark:SetBlendMode("ADD")
|
||||
energytick.spark:SetPoint("CENTER", energytick:GetStatusBarTexture(), "RIGHT")
|
||||
energytick.spark:SetAlpha(0)
|
||||
|
||||
local hookUpdateConfig = pfUI.uf.player.UpdateConfig
|
||||
function pfUI.uf.player.UpdateConfig()
|
||||
|
||||
+13
-26
@@ -136,17 +136,19 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
local f = CreateFirstRunPage()
|
||||
f.text:SetText(T["A new installation of |cff33ffccpf|rUI ships with 4 prebuilt design profiles. Click below if you wish to load one of these profiles."])
|
||||
|
||||
local function LoadProfile(name)
|
||||
_G["pfUI_config"] = CopyTable(pfUI_profiles[name])
|
||||
pfUI_init.selected_profile = name
|
||||
pfUI:LoadConfig()
|
||||
ReloadUI()
|
||||
end
|
||||
|
||||
f.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Modern:SetSize(120, 20)
|
||||
f.Modern:SetPoint("BOTTOM", -65, 100)
|
||||
f.Modern:SetTextColor(1,1,1)
|
||||
f.Modern:SetText("Modern")
|
||||
f.Modern:SetScript("OnClick", function()
|
||||
_G["pfUI_config"] = CopyTable(pfUI_profiles["Modern"])
|
||||
pfUI_init.selected_profile = "Modern"
|
||||
pfUI:LoadConfig()
|
||||
ReloadUI()
|
||||
end)
|
||||
f.Modern:SetScript("OnClick", function() LoadProfile("Modern") end)
|
||||
SkinButton(f.Modern)
|
||||
|
||||
f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
@@ -154,12 +156,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
f.Nostalgia:SetPoint("BOTTOM", 65, 100)
|
||||
f.Nostalgia:SetTextColor(1,1,1)
|
||||
f.Nostalgia:SetText("Nostalgia")
|
||||
f.Nostalgia:SetScript("OnClick", function()
|
||||
_G["pfUI_config"] = CopyTable(pfUI_profiles["Nostalgia"])
|
||||
pfUI_init.selected_profile = "Nostalgia"
|
||||
pfUI:LoadConfig()
|
||||
ReloadUI()
|
||||
end)
|
||||
f.Nostalgia:SetScript("OnClick", function() LoadProfile("Nostalgia") end)
|
||||
SkinButton(f.Nostalgia)
|
||||
|
||||
f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
@@ -167,12 +164,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
f.Legacy:SetPoint("BOTTOM", 65, 75)
|
||||
f.Legacy:SetTextColor(1,1,1)
|
||||
f.Legacy:SetText("Legacy")
|
||||
f.Legacy:SetScript("OnClick", function()
|
||||
_G["pfUI_config"] = CopyTable(pfUI_profiles["Legacy"])
|
||||
pfUI_init.selected_profile = "Legacy"
|
||||
pfUI:LoadConfig()
|
||||
ReloadUI()
|
||||
end)
|
||||
f.Legacy:SetScript("OnClick", function() LoadProfile("Legacy") end)
|
||||
SkinButton(f.Legacy)
|
||||
|
||||
f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
@@ -180,12 +172,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
f.Slim:SetPoint("BOTTOM", -65, 75)
|
||||
f.Slim:SetTextColor(1,1,1)
|
||||
f.Slim:SetText("Slim")
|
||||
f.Slim:SetScript("OnClick", function()
|
||||
_G["pfUI_config"] = CopyTable(pfUI_profiles["Slim"])
|
||||
pfUI_init.selected_profile = "Slim"
|
||||
pfUI:LoadConfig()
|
||||
ReloadUI()
|
||||
end)
|
||||
f.Slim:SetScript("OnClick", function() LoadProfile("Slim") end)
|
||||
SkinButton(f.Slim)
|
||||
|
||||
f.Slider = CreateFrame("Slider", "pfFirstRunWizardScaleSlider", f, "OptionsSliderTemplate")
|
||||
@@ -196,14 +183,14 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
f.Slider:SetSize(240, 20)
|
||||
f.Slider:SetPoint("BOTTOM", 0, 50)
|
||||
f.Slider:SetOrientation('HORIZONTAL')
|
||||
f.Slider:SetMinMaxValues(0.5, 2.0)
|
||||
-- the game drops a uiScale under 0.64 for its automatic scale at login
|
||||
f.Slider:SetMinMaxValues(0.64, 2.0)
|
||||
f.Slider:SetValue(UIParent:GetScale())
|
||||
|
||||
f.Slider:SetScript("OnMouseUp", function()
|
||||
local scale = round(this:GetValue(),2)
|
||||
SetCVar("uiScale", scale)
|
||||
SetCVar("useUiScale", 1)
|
||||
UIParent:SetScale(scale)
|
||||
this:SetValue(scale)
|
||||
end)
|
||||
|
||||
|
||||
+21
-139
@@ -78,7 +78,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
|
||||
-- basic frame
|
||||
local frame = CreateFrame("Frame", nil, this)
|
||||
frame:SetWidth(this.parent:GetRight()-this.parent:GetLeft()-20)
|
||||
frame:SetWidth(this.parent:GetUnscaledWidth()-20)
|
||||
frame:SetHeight(22)
|
||||
frame:SetPoint("TOPLEFT", this, "TOPLEFT", 5, (this.objectCount*-23)-5)
|
||||
|
||||
@@ -130,7 +130,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
category = {}
|
||||
category[config] = tostring(GetCVar(config))
|
||||
ufunc = function()
|
||||
SetCVar(this:GetParent().config, this:GetParent().category[config])
|
||||
SetCVar(config, category[config])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -881,14 +881,6 @@ pfUI:RegisterModule("gui", function ()
|
||||
"35:35",
|
||||
"40:40",
|
||||
},
|
||||
["scaling"] = {
|
||||
"0:" .. T["Off"],
|
||||
"4:" .. T["Huge (PixelPerfect)"],
|
||||
"5:" .. T["Large"],
|
||||
"6:" .. T["Medium"],
|
||||
"7:" .. T["Small"],
|
||||
"8:" .. T["Tiny (PixelPerfect)"],
|
||||
},
|
||||
["abbrevnum"] = {
|
||||
"0:" .. T["Full Numbers (4250)"],
|
||||
"1:" .. T["Abbreviate 2 Decimals (4.25k)"],
|
||||
@@ -1508,7 +1500,24 @@ pfUI:RegisterModule("gui", function ()
|
||||
|
||||
CreateConfig(nil, T["Settings"], nil, nil, "header")
|
||||
CreateConfig(nil, T["Language"], C.global, "language", "dropdown", pfUI.gui.dropdowns.languages)
|
||||
CreateConfig(U["pixelperfect"], T["Enable UI-Scale"], C.global, "pixelperfect", "dropdown", pfUI.gui.dropdowns.scaling)
|
||||
-- the game's own scale options, as in its Video Options; the list keeps
|
||||
-- a value set there that is none of these steps
|
||||
CreateConfig(nil, T["Use UI-Scale"], "CVAR", "useUiScale", "checkbox")
|
||||
CreateConfig(nil, T["UI-Scale"], "CVAR", "uiScale", "dropdown", function()
|
||||
local current = GetCVar("uiScale")
|
||||
local values, found = {}, nil
|
||||
for _, scale in ipairs({ .64, .65, .7, .75, .8, .85, .9, .95, 1, 1.1, 1.2, 1.3, 1.4, 1.5 }) do
|
||||
if math.abs(scale - tonumber(current)) < .001 then
|
||||
found = true
|
||||
table.insert(values, current .. ":" .. scale)
|
||||
else
|
||||
table.insert(values, tostring(scale))
|
||||
end
|
||||
end
|
||||
if not found then table.insert(values, 1, current .. ":" .. round(tonumber(current), 2)) end
|
||||
return values
|
||||
end)
|
||||
CreateConfig(nil, string.format(T["Pixel Perfect UI-Scale: %s (Overrides UI-Scale)"], round(PixelUtil.GetPixelToUIUnitFactor(), 2)), C.global, "pixelperfect", "checkbox")
|
||||
CreateConfig(nil, T["Use Original Game Fonts"], C.global, "font_blizzard", "checkbox")
|
||||
CreateConfig(nil, T["Enable Region Compatible Font"], C.global, "force_region", "checkbox")
|
||||
CreateConfig(nil, T["Standard Text Font"], C.global, "font_default", "dropdown", pfUI.gui.dropdowns.fonts)
|
||||
@@ -1585,8 +1594,6 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(nil, T["Frame Shadow Intensity"], C.appearance.border, "shadow_intensity", "dropdown", pfUI.gui.dropdowns.uf_debuff_indicator_size)
|
||||
CreateConfig(nil) -- spacer
|
||||
CreateConfig(nil, T["Force Blizzard Borders (|cffffaaaaExperimental|r)"], C.appearance.border, "force_blizz", "checkbox")
|
||||
CreateConfig(nil, T["Enable Pixel Perfect Borders"], C.appearance.border, "pixelperfect", "checkbox")
|
||||
CreateConfig(nil, T["Scale Border On HiDPI Displays"], C.appearance.border, "hidpi", "checkbox")
|
||||
CreateConfig(nil) -- spacer
|
||||
CreateConfig(nil, T["Global Border Size"], C.appearance.border, "default", "dropdown", pfUI.gui.dropdowns.border)
|
||||
CreateConfig(nil, T["Action Bar Border Size"], C.appearance.border, "actionbars", "dropdown", pfUI.gui.dropdowns.border)
|
||||
@@ -1646,71 +1653,6 @@ pfUI:RegisterModule("gui", function ()
|
||||
end)
|
||||
|
||||
-- Throttling Menu
|
||||
CreateGUIEntry(T["Throttling"], T["Nameplates"], function()
|
||||
local header = CreateConfig(nil, T["Nameplate Update Rate"], nil, nil, "header")
|
||||
header:GetParent().objectCount = header:GetParent().objectCount - 1
|
||||
header:SetHeight(20)
|
||||
|
||||
local castbarCustom
|
||||
|
||||
CreateConfig(function()
|
||||
if castbarCustom and castbarCustom.input then
|
||||
local isCustom = pfUI.throttle:IsCustom("nameplates_castbar")
|
||||
if not isCustom then
|
||||
castbarCustom.input:EnableMouse(false)
|
||||
castbarCustom.input:EnableKeyboard(false)
|
||||
castbarCustom.input:ClearFocus()
|
||||
castbarCustom.input:SetTextColor(.5,.5,.5,1)
|
||||
castbarCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_castbar")))
|
||||
else
|
||||
castbarCustom.input:EnableMouse(true)
|
||||
castbarCustom.input:EnableKeyboard(true)
|
||||
castbarCustom.input:SetTextColor(.2,1,.8,1)
|
||||
if _G.pfUI_throttle.nameplates_castbar_custom then
|
||||
castbarCustom.input:SetText(_G.pfUI_throttle.nameplates_castbar_custom)
|
||||
end
|
||||
end
|
||||
end
|
||||
end, T["Castbar Plates"], _G.pfUI_throttle, "nameplates_castbar", "dropdown", {
|
||||
"very_slow:" .. T["Very Slow"] .. " (2 FPS)",
|
||||
"slow:" .. T["Slow"] .. " (5 FPS)",
|
||||
"normal:" .. T["Normal"] .. " (10 FPS)",
|
||||
"fast:" .. T["Fast"] .. " (20 FPS)",
|
||||
"very_fast:" .. T["Very Fast"] .. " (30 FPS)",
|
||||
"fastest:" .. T["Fastest"] .. " (50 FPS)",
|
||||
"custom:" .. T["Custom"],
|
||||
})
|
||||
|
||||
castbarCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_castbar_custom")
|
||||
|
||||
local isCustom = pfUI.throttle:IsCustom("nameplates_castbar")
|
||||
if not isCustom then
|
||||
castbarCustom.input:EnableMouse(false)
|
||||
castbarCustom.input:EnableKeyboard(false)
|
||||
castbarCustom.input:SetTextColor(.5,.5,.5,1)
|
||||
castbarCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_castbar")))
|
||||
else
|
||||
castbarCustom.input:EnableMouse(true)
|
||||
castbarCustom.input:EnableKeyboard(true)
|
||||
castbarCustom.input:SetTextColor(.2,1,.8,1)
|
||||
if _G.pfUI_throttle.nameplates_castbar_custom then
|
||||
castbarCustom.input:SetText(_G.pfUI_throttle.nameplates_castbar_custom)
|
||||
end
|
||||
end
|
||||
|
||||
-- Spacer before reset button
|
||||
local spacer = CreateConfig(nil, " ", nil, nil, "header")
|
||||
spacer:GetParent().objectCount = spacer:GetParent().objectCount - 1
|
||||
spacer:SetHeight(10)
|
||||
|
||||
-- Reset to defaults button
|
||||
CreateConfig(nil, T["Reset to Defaults"], nil, nil, "button", function()
|
||||
pfUI.throttle:ResetToDefault("nameplates_castbar")
|
||||
_G.pfUI_throttle.nameplates_castbar_custom = "100"
|
||||
Reload()
|
||||
end, true)
|
||||
end)
|
||||
|
||||
CreateGUIEntry(T["Throttling"], T["Tooltips"], function()
|
||||
local header = CreateConfig(nil, T["Tooltip Update Rate"], nil, nil, "header")
|
||||
header:GetParent().objectCount = header:GetParent().objectCount - 1
|
||||
@@ -1846,67 +1788,6 @@ pfUI:RegisterModule("gui", function ()
|
||||
end, true)
|
||||
end)
|
||||
|
||||
CreateGUIEntry(T["Throttling"], T["Swing Timer"], function()
|
||||
local swingCustom
|
||||
|
||||
CreateConfig(function()
|
||||
if swingCustom and swingCustom.input then
|
||||
local isCustom = pfUI.throttle:IsCustom("swingtimer")
|
||||
if not isCustom then
|
||||
swingCustom.input:EnableMouse(false)
|
||||
swingCustom.input:EnableKeyboard(false)
|
||||
swingCustom.input:ClearFocus()
|
||||
swingCustom.input:SetTextColor(.5,.5,.5,1)
|
||||
swingCustom.input:SetText(tostring(pfUI.throttle:GetFps("swingtimer")))
|
||||
else
|
||||
swingCustom.input:EnableMouse(true)
|
||||
swingCustom.input:EnableKeyboard(true)
|
||||
swingCustom.input:SetTextColor(.2,1,.8,1)
|
||||
if _G.pfUI_throttle.swingtimer_custom then
|
||||
swingCustom.input:SetText(_G.pfUI_throttle.swingtimer_custom)
|
||||
end
|
||||
end
|
||||
end
|
||||
end, T["Swing Timer Update Rate"], _G.pfUI_throttle, "swingtimer", "dropdown", {
|
||||
"very_slow:" .. T["Very Slow"] .. " (2 FPS)",
|
||||
"slow:" .. T["Slow"] .. " (5 FPS)",
|
||||
"normal:" .. T["Normal"] .. " (10 FPS)",
|
||||
"fast:" .. T["Fast"] .. " (20 FPS)",
|
||||
"very_fast:" .. T["Very Fast"] .. " (30 FPS)",
|
||||
"fastest:" .. T["Fastest"] .. " (50 FPS)",
|
||||
"custom:" .. T["Custom"],
|
||||
})
|
||||
|
||||
swingCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "swingtimer_custom")
|
||||
|
||||
local isCustom = pfUI.throttle:IsCustom("swingtimer")
|
||||
if not isCustom then
|
||||
swingCustom.input:EnableMouse(false)
|
||||
swingCustom.input:EnableKeyboard(false)
|
||||
swingCustom.input:SetTextColor(.5,.5,.5,1)
|
||||
swingCustom.input:SetText(tostring(pfUI.throttle:GetFps("swingtimer")))
|
||||
else
|
||||
swingCustom.input:EnableMouse(true)
|
||||
swingCustom.input:EnableKeyboard(true)
|
||||
swingCustom.input:SetTextColor(.2,1,.8,1)
|
||||
if _G.pfUI_throttle.swingtimer_custom then
|
||||
swingCustom.input:SetText(_G.pfUI_throttle.swingtimer_custom)
|
||||
end
|
||||
end
|
||||
|
||||
-- Spacer before reset button
|
||||
local spacer = CreateConfig(nil, " ", nil, nil, "header")
|
||||
spacer:GetParent().objectCount = spacer:GetParent().objectCount - 1
|
||||
spacer:SetHeight(10)
|
||||
|
||||
-- Reset to defaults button
|
||||
CreateConfig(nil, T["Reset to Defaults"], nil, nil, "button", function()
|
||||
pfUI.throttle:ResetToDefault("swingtimer")
|
||||
_G.pfUI_throttle.swingtimer_custom = "50"
|
||||
Reload()
|
||||
end, true)
|
||||
end)
|
||||
|
||||
CreateGUIEntry(T["Unit Frames"], T["General"], function()
|
||||
CreateConfig(nil, T["Disable pfUI Unit Frames"], C.unitframes, "disable", "checkbox")
|
||||
CreateConfig(nil, T["Animate Health And Power Bars"], C.unitframes, "animatebars", "checkbox")
|
||||
@@ -2445,6 +2326,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(U["bars"], T["Out Of Mana Color"], C.bars, "oomcolor", "color")
|
||||
CreateConfig(U["bars"], T["Highlight Not Usable Spells"], C.bars, "showna", "checkbox")
|
||||
CreateConfig(U["bars"], T["Not Usable Color"], C.bars, "nacolor", "color")
|
||||
CreateConfig(U["bars"], T["Highlight Proc And Reactive Spells"], C.bars, "procglow", "checkbox")
|
||||
CreateConfig(U["bars"], T["Lock Actionbars"], "UVAR", "LOCK_ACTIONBAR", "checkbox")
|
||||
CreateConfig(U["bars"], T["Always Allow Drag Via Shift Key"], C.bars, "shiftdrag", "checkbox")
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ pfUI:RegisterModule("marktracking", function ()
|
||||
|
||||
local function ShortenName(name, row)
|
||||
if not name or name == "" then return "" end
|
||||
local barWidth = row.health:GetWidth()
|
||||
local barWidth = row.health:GetUnscaledWidth()
|
||||
if barWidth < 1 then barWidth = BAR_WIDTH end
|
||||
local available = barWidth - 4
|
||||
if rm_showpct then available = available - 32 end
|
||||
|
||||
+4
-3
@@ -111,15 +111,16 @@ pfUI:RegisterModule("minimap", function ()
|
||||
|
||||
-- Blink ten times between .9 and .1, then hide
|
||||
local highlight = this.highlight
|
||||
highlight:SetAlpha(.9)
|
||||
highlight.blink = highlight:CreateAnimationGroup()
|
||||
for i = 1, 10 do
|
||||
local down = highlight.blink:CreateAnimation("Alpha")
|
||||
down:SetChange(-.8)
|
||||
down:SetFromAlpha(.9)
|
||||
down:SetToAlpha(.1)
|
||||
down:SetDuration(.3)
|
||||
down:SetOrder(i * 2 - 1)
|
||||
local up = highlight.blink:CreateAnimation("Alpha")
|
||||
up:SetChange(.8)
|
||||
up:SetFromAlpha(.1)
|
||||
up:SetToAlpha(.9)
|
||||
up:SetDuration(.3)
|
||||
up:SetOrder(i * 2)
|
||||
end
|
||||
|
||||
@@ -3,20 +3,53 @@ pfUI:RegisterModule("mirrortimers", function ()
|
||||
local fontsize = tonumber(C.global.font_size)
|
||||
local height = fontsize * 1.3
|
||||
|
||||
-- The value of a mirror timer moves by `scale` per second: breath drains at
|
||||
-- -1, and refills faster than that. A running bar fills from a duration
|
||||
-- object that spans the whole bar at that rate, and its text binding shows
|
||||
-- the value as m:ss. A paused bar holds its value.
|
||||
local function Sync(frame)
|
||||
local value = GetMirrorTimerProgress(frame.timer) / 1000
|
||||
local max, scale = frame.maxvalue, frame.scale
|
||||
local label = frame.label:GetText() or ""
|
||||
|
||||
if frame.paused or scale == 0 then
|
||||
frame.statusbar:SetMinMaxValues(0, max)
|
||||
frame.statusbar:SetValue(value)
|
||||
frame.binding:Disable()
|
||||
frame.text:SetFormattedText("%s (%d:%02d)", label, value / 60, value % 60)
|
||||
return
|
||||
end
|
||||
|
||||
-- the shown value is the seconds of the duration times the rate
|
||||
local rate = math.abs(scale)
|
||||
local property
|
||||
if scale < 0 then
|
||||
frame.duration:SetTimeFromEnd(GetTime() + value / rate, max / rate)
|
||||
frame.statusbar:SetTimerDuration(frame.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
|
||||
property = Enum.DurationTextBindingProperty.RemainingDuration
|
||||
else
|
||||
frame.duration:SetTimeFromEnd(GetTime() + (max - value) / rate, max / rate)
|
||||
frame.statusbar:SetTimerDuration(frame.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
|
||||
property = Enum.DurationTextBindingProperty.ElapsedDuration
|
||||
end
|
||||
|
||||
local Down = Enum.NumericRuleFormatRounding.Down
|
||||
frame.clock:SetBreakpoints({
|
||||
{ threshold = 0, format = "%d:%02d", components = {
|
||||
{ div = 60 / rate, step = 1, rounding = Down },
|
||||
{ div = 1 / rate, mod = 60, step = 1, rounding = Down },
|
||||
} },
|
||||
})
|
||||
frame.binding:SetTextFormat(label .. " ({})", { { property = property, formatter = frame.clock } })
|
||||
frame.binding:Enable()
|
||||
end
|
||||
|
||||
for i = 1, MIRRORTIMER_NUMTIMERS do
|
||||
local frame = _G["MirrorTimer"..i]
|
||||
frame:GetRegions():Hide()
|
||||
frame:SetWidth(200)
|
||||
frame:SetHeight(height)
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if this.paused or not this.timer then return end
|
||||
MirrorTimerFrame_OnUpdate(this, arg1)
|
||||
|
||||
local text = this.label:GetText()
|
||||
if text and this.value then
|
||||
this.text:SetText(format("%s (%d:%02d)", text, this.value/60, this.value - math.floor(this.value/60)*60))
|
||||
end
|
||||
end)
|
||||
frame:SetScript("OnUpdate", nil)
|
||||
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint("TOP", UIParent, "TOP", 0, -120-(i-1)*(height + 10))
|
||||
@@ -38,5 +71,34 @@ pfUI:RegisterModule("mirrortimers", function ()
|
||||
frame.text = frame:CreateFontString(nil, "OVERLAY")
|
||||
frame.text:SetFont(font, fontsize, "OUTLINE")
|
||||
frame.text:SetPoint("CENTER", frame.statusbar, "CENTER", 0, 0)
|
||||
|
||||
frame.duration = C_DurationUtil.CreateDuration()
|
||||
frame.clock = C_StringUtil.CreateNumericRuleFormatter()
|
||||
frame.binding = C_DurationUtil.CreateDurationTextBinding()
|
||||
frame.binding:SetFontString(frame.text)
|
||||
frame.binding:SetDuration(frame.duration)
|
||||
|
||||
-- the stock handler has already set the pause state
|
||||
frame:HookScript("OnEvent", function()
|
||||
if event == "MIRROR_TIMER_PAUSE" and this:IsShown() and arg1 == this.timer then
|
||||
Sync(this)
|
||||
end
|
||||
end)
|
||||
|
||||
frame:HookScript("OnHide", function()
|
||||
this.binding:Disable()
|
||||
end)
|
||||
end
|
||||
|
||||
-- the stock function picks the bar and sets its timer, scale and pause state
|
||||
hooksecurefunc("MirrorTimer_Show", function(timer, _, maxvalue)
|
||||
for i = 1, MIRRORTIMER_NUMTIMERS do
|
||||
local frame = _G["MirrorTimer"..i]
|
||||
if frame:IsShown() and frame.timer == timer then
|
||||
frame.maxvalue = maxvalue / 1000
|
||||
Sync(frame)
|
||||
return
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
+77
-116
@@ -82,7 +82,6 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
local threatMemory = {} -- guid -> true once the mob targeted the player
|
||||
local raidGuidCache = {} -- guid -> name of the player's group
|
||||
local offtanks = {}
|
||||
local activeCasts = {} -- overlay -> true while its castbar sweeps
|
||||
local zooming = {} -- overlay -> true while its target zoom animates
|
||||
local auraSlots = {} -- reusable GetAuraSlots buffer for the per-plate aura scan
|
||||
local targetGuid
|
||||
@@ -100,6 +99,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
-- Config caching
|
||||
-- ============================================================================
|
||||
local cfg = {}
|
||||
local castFormatter = C_StringUtil.CreateNumericRuleFormatter()
|
||||
local debufffilter, debufflist
|
||||
|
||||
local function DebuffFilterPopulate()
|
||||
@@ -158,7 +158,13 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
cfg.showguildname = N.showguildname == "1"
|
||||
cfg.abbrevname = C.unitframes.abbrevname == "1"
|
||||
cfg.friendclassnamec = N.friendclassnamec == "1"
|
||||
cfg.castbardecimals = C.unitframes.castbardecimals == "1"
|
||||
-- the plate castbars' text bindings hold this formatter, so they pick the
|
||||
-- change up on their next tick
|
||||
if C.unitframes.castbardecimals == "1" then
|
||||
castFormatter:SetBreakpoints({ { threshold = 0, step = .1, rounding = Enum.NumericRuleFormatRounding.Down, format = "%.1f" } })
|
||||
else
|
||||
castFormatter:SetBreakpoints({ { threshold = 0, format = "%.2f" } })
|
||||
end
|
||||
cfg.fullhealth = N.fullhealth == "1"
|
||||
cfg.keeptarget = N.target == "1"
|
||||
cfg.critters = N.critters == "1"
|
||||
@@ -187,8 +193,6 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
cfg.rightclick = N.rightclick == "1"
|
||||
cfg.hlr, cfg.hlg, cfg.hlb, cfg.hla = GetStringColor(N.highlightcolor)
|
||||
cfg.br, cfg.bg, cfg.bb, cfg.ba = GetStringColor(C.appearance.border.color)
|
||||
-- sweep rate of the castbars on plates other than the target's
|
||||
cfg.throttle_castbar = pfUI.throttle:Get("nameplates_castbar")
|
||||
|
||||
local c = combatstate
|
||||
c.CASTING.r, c.CASTING.g, c.CASTING.b, c.CASTING.a = GetStringColor(N.combatcasting)
|
||||
@@ -227,22 +231,23 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
-- One-shot C_Spell poll. Builds the cast struct for a UNIT_SPELLCAST_* event
|
||||
-- (the payload carries no timing) and seeds a plate that binds while its unit
|
||||
-- is already mid-cast (its START fired before the plate existed). Picks cast
|
||||
-- vs channel itself.
|
||||
-- vs channel itself. A cast whose start was never seen has no duration.
|
||||
local function PollCastInfo(unit)
|
||||
local name, _, texture, startMs, endMs, _, _, noInterrupt, spellID = C_Spell.UnitCastingInfo(unit)
|
||||
local isChannel
|
||||
if not name then
|
||||
name, _, texture, startMs, endMs, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(unit)
|
||||
local name, _, texture, _, _, _, _, noInterrupt, spellID = C_Spell.UnitCastingInfo(unit)
|
||||
local duration, isChannel
|
||||
if name then
|
||||
duration = UnitCastingDuration(unit)
|
||||
else
|
||||
name, _, texture, _, _, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(unit)
|
||||
duration = name and UnitChannelDuration(unit)
|
||||
isChannel = true
|
||||
end
|
||||
if not name or not startMs or not endMs then return nil end
|
||||
if not duration then return nil end
|
||||
return {
|
||||
spellName = name,
|
||||
spellID = spellID,
|
||||
icon = texture,
|
||||
startTime = startMs / 1000,
|
||||
endTime = endMs / 1000,
|
||||
duration = (endMs - startMs) / 1000,
|
||||
duration = duration,
|
||||
isChannel = isChannel,
|
||||
noInterrupt = noInterrupt,
|
||||
}
|
||||
@@ -279,7 +284,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
local mobTargetGuid = UnitGUID(target)
|
||||
local hasTarget = mobTargetGuid and mobTargetGuid ~= NULL_GUID
|
||||
local info = castState[guid]
|
||||
local isCasting = info and info.endTime > GetTime()
|
||||
local isCasting = info and not info.duration:HasExpired()
|
||||
local targetingPlayer = hasTarget and UnitIsUnit(target, "player")
|
||||
|
||||
if targetingPlayer then
|
||||
@@ -487,8 +492,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
local elite = UnitClassification(unit)
|
||||
if level <= 0 then
|
||||
if plate.player then
|
||||
local _, dblevel = GetUnitInfo(plate.rawname)
|
||||
if dblevel and dblevel > 0 then level = dblevel end
|
||||
level = GetPlayerLevel(plate.rawname) or level
|
||||
else
|
||||
-- UnitLevel answers -1 exactly where the engine shows the skull: a
|
||||
-- world boss, or a hostile ten or more levels above the player
|
||||
@@ -819,8 +823,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- Animation driver: castbar sweeps and the target zoom. Shown only while
|
||||
-- something moves.
|
||||
-- Animation driver: the target zoom. Shown only while a plate zooms.
|
||||
-- ============================================================================
|
||||
local driver = CreateFrame("Frame", nil, UIParent)
|
||||
driver:Hide()
|
||||
@@ -841,98 +844,8 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
driver:Show()
|
||||
end
|
||||
|
||||
-- Skip SetText when the rounded value hasn't changed since last frame.
|
||||
-- `string.format("%.2f", ...)` allocates a fresh string every call; this
|
||||
-- gate compares cheap integers and only formats when the displayed value
|
||||
-- would actually differ. Reset via lastEndTime-changed branch below.
|
||||
local function SetCastbarText(castbar, remaining)
|
||||
local rounded
|
||||
if cfg.castbardecimals then
|
||||
rounded = floor(remaining * 10)
|
||||
if castbar.lastTextTick ~= rounded then
|
||||
castbar.lastTextTick = rounded
|
||||
castbar.text:SetText(rounded / 10)
|
||||
end
|
||||
else
|
||||
rounded = floor(remaining * 100)
|
||||
if castbar.lastTextTick ~= rounded then
|
||||
castbar.lastTextTick = rounded
|
||||
castbar.text:SetFormattedText("%.2f", remaining)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateCastbar(plate, info, now)
|
||||
local castbar = plate.castbar
|
||||
local duration = info.duration
|
||||
if castbar.lastEndTime ~= info.endTime then
|
||||
castbar.lastEndTime = info.endTime
|
||||
castbar.lastTextTick = nil
|
||||
-- Relative 0..duration range to avoid float precision loss with large
|
||||
-- absolute timestamps.
|
||||
castbar:SetMinMaxValues(0, duration)
|
||||
SetCastbarShield(castbar, castbar.icon, info.noInterrupt, info.isChannel)
|
||||
if info.icon then
|
||||
castbar.icon.tex:SetTexture(info.icon)
|
||||
castbar.icon.tex:SetTexCoord(.1,.9,.1,.9)
|
||||
end
|
||||
castbar.spell:SetText(cfg.spellname and info.spellName or "")
|
||||
end
|
||||
|
||||
local value = info.isChannel and (info.endTime - now) or (now - info.startTime)
|
||||
if value < 0 then value = 0 elseif value > duration then value = duration end
|
||||
castbar:SetValue(value)
|
||||
SetCastbarText(castbar, info.endTime - now)
|
||||
if not castbar.isShown then
|
||||
castbar.isShown = true
|
||||
castbar:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function HideCast(plate)
|
||||
activeCasts[plate] = nil
|
||||
local castbar = plate.castbar
|
||||
if castbar.isShown then
|
||||
castbar.isShown = nil
|
||||
castbar.lastEndTime = nil
|
||||
castbar:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- Start or stop the plate's castbar from the cast cache and the config.
|
||||
function UpdateCast(plate)
|
||||
local info = castState[plate.guid]
|
||||
if info and cfg.showcastbar and (plate.istarget or not cfg.targetcastbar) then
|
||||
if not activeCasts[plate] then
|
||||
activeCasts[plate] = true
|
||||
plate.casttick = 0
|
||||
driver:Show()
|
||||
end
|
||||
UpdateCastbar(plate, info, GetTime())
|
||||
else
|
||||
HideCast(plate)
|
||||
end
|
||||
end
|
||||
|
||||
driver:SetScript("OnUpdate", function()
|
||||
local now = GetTime()
|
||||
|
||||
-- The target's bar sweeps every frame; the others at the castbar rate.
|
||||
for plate in pairs(activeCasts) do
|
||||
if plate.istarget or plate.casttick + cfg.throttle_castbar <= now then
|
||||
plate.casttick = now
|
||||
local info = castState[plate.guid]
|
||||
if info and info.endTime > now then
|
||||
UpdateCastbar(plate, info, now)
|
||||
else
|
||||
-- ran out without a STOP: treat it as one
|
||||
castState[plate.guid] = nil
|
||||
HideCast(plate)
|
||||
UpdateCombatColor(plate)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for plate in pairs(zooming) do
|
||||
local f = (now - plate.zoomStart) / ZOOM_TIME
|
||||
if f >= 1 then
|
||||
@@ -941,10 +854,57 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
end
|
||||
SetZoom(plate, plate.zoomIn and f or 1 - f)
|
||||
end
|
||||
|
||||
if not next(activeCasts) and not next(zooming) then this:Hide() end
|
||||
if not next(zooming) then this:Hide() end
|
||||
end)
|
||||
|
||||
-- ============================================================================
|
||||
-- Castbars: the bar's timer and its text binding follow the cast's duration
|
||||
-- object on their own. Lua only starts, restyles and stops them.
|
||||
-- ============================================================================
|
||||
local function ShowCast(plate, info)
|
||||
local castbar = plate.castbar
|
||||
castbar:SetTimerDuration(info.duration, Enum.StatusBarInterpolation.Immediate,
|
||||
info.isChannel and Enum.StatusBarTimerDirection.RemainingTime or Enum.StatusBarTimerDirection.ElapsedTime)
|
||||
castbar.timer:SetDuration(info.duration)
|
||||
castbar.timer:Enable()
|
||||
SetCastbarShield(castbar, castbar.icon, info.noInterrupt, info.isChannel)
|
||||
if info.icon then
|
||||
castbar.icon.tex:SetTexture(info.icon)
|
||||
castbar.icon.tex:SetTexCoord(.1,.9,.1,.9)
|
||||
end
|
||||
castbar.spell:SetText(cfg.spellname and info.spellName or "")
|
||||
-- armed exactly while the bar shows
|
||||
if castbar.expiry then castbar.expiry:Cancel() end
|
||||
castbar.expiry = C_Timer.NewTimer(info.duration:GetRemainingDuration(), castbar.onExpire)
|
||||
castbar:Show()
|
||||
end
|
||||
|
||||
function HideCast(plate)
|
||||
local castbar = plate.castbar
|
||||
if not castbar.expiry then return end
|
||||
castbar.expiry:Cancel()
|
||||
castbar.expiry = nil
|
||||
castbar.timer:Disable()
|
||||
castbar:Hide()
|
||||
end
|
||||
|
||||
-- Start or stop the plate's castbar from the cast cache and the config.
|
||||
function UpdateCast(plate)
|
||||
local info = castState[plate.guid]
|
||||
if info and not info.duration:HasExpired() and cfg.showcastbar and (plate.istarget or not cfg.targetcastbar) then
|
||||
ShowCast(plate, info)
|
||||
else
|
||||
HideCast(plate)
|
||||
end
|
||||
end
|
||||
|
||||
-- The bar's cast ran out without a STOP: treat it as one.
|
||||
local function ExpireCast(plate)
|
||||
castState[plate.guid] = nil
|
||||
HideCast(plate)
|
||||
UpdateCombatColor(plate)
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- Cast events (per plate)
|
||||
-- ============================================================================
|
||||
@@ -1276,12 +1236,17 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
castbar.text = castbar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
castbar.text = castbar:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
castbar.text:SetPoint("RIGHT", castbar, "LEFT", -4, 0)
|
||||
castbar.text:SetNonSpaceWrap(false)
|
||||
castbar.text:SetTextColor(1,1,1,.5)
|
||||
|
||||
castbar.spell = castbar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
castbar.timer = C_DurationUtil.CreateDurationTextBinding()
|
||||
castbar.timer:SetFontString(castbar.text)
|
||||
castbar.timer:SetFormatter(castFormatter)
|
||||
castbar.onExpire = function() ExpireCast(nameplate) end
|
||||
|
||||
castbar.spell = castbar:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
castbar.spell:SetPoint("CENTER", castbar, "CENTER")
|
||||
castbar.spell:SetNonSpaceWrap(false)
|
||||
castbar.spell:SetTextColor(1,1,1,1)
|
||||
@@ -1372,8 +1337,6 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
nameplate.castbar:SetStatusBarTexture(cbtexture or hptexture)
|
||||
local cbr, cbg, cbb, cba = GetStringColor(C.appearance.castbar.castbarcolor)
|
||||
nameplate.castbar:SetStatusBarColor(cbr, cbg, cbb, cba)
|
||||
-- reset endTime cache so color/texture refresh takes effect on next cast
|
||||
nameplate.castbar.lastEndTime = nil
|
||||
CreateBackdrop(nameplate.castbar, default_border)
|
||||
|
||||
nameplate.castbar.text:SetFont(font, font_size, "OUTLINE")
|
||||
@@ -1652,8 +1615,6 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
local plate = info and plateByGuid[guid]
|
||||
if not plate then return end
|
||||
info.noInterrupt = event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE"
|
||||
-- UpdateCastbar only restyles on a new cast; force it
|
||||
plate.castbar.lastEndTime = nil
|
||||
UpdateCast(plate)
|
||||
end
|
||||
events.UNIT_SPELLCAST_NOT_INTERRUPTIBLE = events.UNIT_SPELLCAST_INTERRUPTIBLE
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
pfUI:RegisterModule("pixelperfect", function ()
|
||||
-- pre-calculated min values
|
||||
local statics = {
|
||||
[4] = 1.4222222222222,
|
||||
[5] = 1.1377777777778,
|
||||
[6] = 0.94814814814815,
|
||||
[7] = 0.81269841269841,
|
||||
[8] = 0.71111111111111,
|
||||
}
|
||||
|
||||
-- pixel perfect
|
||||
local function pixelperfect()
|
||||
local conf = tonumber(C.global.pixelperfect)
|
||||
if conf < 4 then
|
||||
-- restore gamesettings
|
||||
local scale = GetCVar("uiScale")
|
||||
local use = GetCVar("useUiScale")
|
||||
|
||||
if use == 1 then
|
||||
UIParent:SetScale(tonumber(use))
|
||||
else
|
||||
UIParent:SetScale(.9)
|
||||
end
|
||||
else
|
||||
local scale = conf and statics[conf] or 1
|
||||
|
||||
SetCVar("uiScale", scale)
|
||||
SetCVar("useUiScale", 1)
|
||||
|
||||
UIParent:SetScale(scale)
|
||||
end
|
||||
end
|
||||
|
||||
-- pixelperfect: native UIScale listener
|
||||
if tonumber(C.global.pixelperfect) > 0 then
|
||||
pfUI.events:RegisterFrameEventAndCallback("PLAYER_ENTERING_WORLD", pixelperfect)
|
||||
pixelperfect()
|
||||
end
|
||||
|
||||
pfUI.pixelperfect = {
|
||||
UpdateConfig = pixelperfect
|
||||
}
|
||||
end)
|
||||
@@ -112,18 +112,6 @@ pfUI:RegisterModule("player", function ()
|
||||
UpdateInfoText()
|
||||
end
|
||||
|
||||
-- Add throttle to player frame OnUpdate
|
||||
-- Throttle the unit frame's existing OnUpdate to ~20 FPS so the per-frame
|
||||
-- work stays cheap.
|
||||
if pfUI.uf.player:GetScript("OnUpdate") then
|
||||
local originalOnUpdate = pfUI.uf.player:GetScript("OnUpdate")
|
||||
pfUI.uf.player:SetScript("OnUpdate", function()
|
||||
if (this.throttleTick or 0) > GetTime() then return end
|
||||
this.throttleTick = GetTime() + 0.05
|
||||
originalOnUpdate()
|
||||
end)
|
||||
end
|
||||
|
||||
-- PLAYER_DAMAGE_DONE_MODS fires on the MOD_DAMAGE_DONE fields that
|
||||
-- GetSpellBonusDamage reads, so gear, buffs and talents all reach it.
|
||||
pfUI.events:RegisterFrameEventAndCallback("PLAYER_ENTERING_WORLD", UpdateInfoText)
|
||||
|
||||
+1
-2
@@ -264,8 +264,7 @@ pfUI:RegisterModule("raid", function ()
|
||||
if newGuid ~= oldGuid then
|
||||
-- GUID changed = different player = need full update
|
||||
tracker.frameToGuid[frame] = newGuid
|
||||
frame.update_full = true
|
||||
frame.update_aura = true -- Force aura refresh!
|
||||
pfUI.uf:QueueUpdate(frame, "full")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+6
-10
@@ -238,17 +238,11 @@ pfUI:RegisterModule("roll", function ()
|
||||
f.time.bar = CreateFrame("StatusBar", "pfLootRollFrame" .. id .. "TimeBar", f.time)
|
||||
f.time.bar:SetAllPoints(f.time)
|
||||
f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"])
|
||||
f.time.bar:SetMinMaxValues(0, 100)
|
||||
local r, g, b, a = GetStringColor(C.appearance.border.color)
|
||||
f.time.bar:SetStatusBarColor(r, g, b)
|
||||
f.time.bar:SetValue(20)
|
||||
f.time.bar:SetScript("OnUpdate", function()
|
||||
if not this:GetParent():GetParent().rollID then return end
|
||||
local left = GetLootRollTimeLeft(this:GetParent():GetParent().rollID)
|
||||
local min, max = this:GetMinMaxValues()
|
||||
if left < min or left > max then left = min end
|
||||
this:SetValue(left)
|
||||
end)
|
||||
-- drains by itself; UpdateLootRoll sets the roll's time span
|
||||
f.time.duration = C_DurationUtil.CreateDuration()
|
||||
f.time.bar:SetTimerDuration(f.time.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
|
||||
|
||||
return f
|
||||
end
|
||||
@@ -299,7 +293,9 @@ pfUI:RegisterModule("roll", function ()
|
||||
pfUI.roll.frames[id].name.text:SetTextColor(color.r, color.g, color.b, 1)
|
||||
pfUI.roll.frames[id].icon.tex:SetTexture(texture)
|
||||
pfUI.roll.frames[id].backdrop:SetBackdropBorderColor(color.r, color.g, color.b)
|
||||
pfUI.roll.frames[id].time.bar:SetMinMaxValues(0, pfUI.roll.frames[id].rollTime)
|
||||
-- both in milliseconds
|
||||
local left = GetLootRollTimeLeft(pfUI.roll.frames[id].rollID)
|
||||
pfUI.roll.frames[id].time.duration:SetTimeFromEnd(GetTime() + left / 1000, pfUI.roll.frames[id].rollTime / 1000)
|
||||
|
||||
if C.loot.raritytimer == "1" then
|
||||
pfUI.roll.frames[id].time.bar:SetStatusBarColor(color.r, color.g, color.b, .5)
|
||||
|
||||
@@ -5,7 +5,7 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
pfUI.socialmod:SetScript("OnEvent", function()
|
||||
local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS)
|
||||
name = name or cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S)
|
||||
if name and playerdb[name] and playerdb[name].cname then
|
||||
if name and playerdb[name] then
|
||||
playerdb[name].lastseen = date("%a %d-%b-%Y")
|
||||
end
|
||||
end)
|
||||
@@ -92,7 +92,6 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
local cname = ccolor:WrapTextInColorCode(name)
|
||||
if playerdb[name] then
|
||||
playerdb[name].lastseen = date("%a %d-%b-%Y")
|
||||
playerdb[name].cname = cname
|
||||
end
|
||||
|
||||
if friendName then
|
||||
@@ -106,11 +105,13 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
caption:SetVertexColor(1,1,1,.9)
|
||||
friendInfo:SetVertexColor(1,1,1,.9)
|
||||
else
|
||||
if playerdb[name] and playerdb[name].cname and playerdb[name].level and playerdb[name].lastseen then
|
||||
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname)
|
||||
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen)
|
||||
local _, class = C_PlayerCache.GetPlayerInfoByName(name)
|
||||
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), class and PFUI_CLASS_COLORS[class]:WrapTextInColorCode(name) or name.."|r")
|
||||
|
||||
local entry = playerdb[name]
|
||||
if entry and entry.level and entry.lastseen then
|
||||
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), entry.level, entry.lastseen)
|
||||
else
|
||||
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r")
|
||||
friendInfo:SetText(TEXT(UNKNOWN))
|
||||
end
|
||||
|
||||
|
||||
+230
-457
@@ -5,14 +5,16 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local ON_SWING_QUEUED = 0
|
||||
local ON_SWING_QUEUE_POPPED = 1
|
||||
|
||||
-- seconds a melee bar stays full after its swing is due, to bridge the gap
|
||||
-- until PLAYER_SWING arrives; it then hides unless auto attack goes on
|
||||
local GRACE = 0.15
|
||||
|
||||
-- the last half second of a hunter's ranged swing, when moving cancels the shot
|
||||
local DEADZONE = 0.5
|
||||
|
||||
-- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max)
|
||||
local S = {
|
||||
mhTimer = 0, mhTimerMax = 1,
|
||||
ohTimer = 0, ohTimerMax = 1,
|
||||
raTimer = 0, raTimerMax = 1,
|
||||
mhSpeed = 0, ohSpeed = 0, raSpeed = 0,
|
||||
mhActive = false, ohActive = false, raActive = false,
|
||||
lastMhMarkerX = -1, lastOhMarkerX = -1, lastRaMarkerX = -1,
|
||||
autoAttackActive = false,
|
||||
inCombat = false,
|
||||
hsQueued = false, cleaveQueued = false, maulQueued = false,
|
||||
@@ -21,7 +23,6 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
isDruid = false,
|
||||
cachedHSSlots = {}, cachedCleaveSlots = {}, cachedMaulSlots = {},
|
||||
useSpellQueueEvent = false,
|
||||
swingThrottle = 0,
|
||||
onSwingCache = {},
|
||||
outOfRange = {}, -- by Enum.PlayerSwingType
|
||||
}
|
||||
@@ -70,196 +71,111 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local rwR, rwG, rwB, rwA = ParseColor(C.unitframes.swingtimerrangedwarncolor, 0.9, 0.0, 0.0, 1)
|
||||
local orR, orG, orB = ParseColor(C.unitframes.swingtimeroutrangecolor, 0.5, 0.5, 0.5, 1)
|
||||
local isHunter = UnitClassBase("player") == "HUNTER"
|
||||
local mhDefaultR, mhDefaultG, mhDefaultB = mhR, mhG, mhB
|
||||
|
||||
-- the countdown text, cut down to tenths
|
||||
local countdown = C_StringUtil.CreateNumericRuleFormatter()
|
||||
countdown:AddBreakpoint({ threshold = 0, step = 0.1, rounding = Enum.NumericRuleFormatRounding.Down, format = "%.1f" })
|
||||
|
||||
|
||||
-- Create container frame
|
||||
-- the container keeps the public API
|
||||
pfUI.swingtimer = CreateFrame("Frame", "pfSwingTimer", UIParent)
|
||||
pfUI.swingtimer:SetFrameStrata("MEDIUM")
|
||||
pfUI.swingtimer:Hide()
|
||||
|
||||
-- Mainhand bar
|
||||
pfUI.swingtimer.mainhand = CreateFrame("StatusBar", "pfSwingTimerMainhand", UIParent)
|
||||
pfUI.swingtimer.mainhand:SetPoint("BOTTOM", UIParent, "CENTER", -189, 103)
|
||||
pfUI.swingtimer.mainhand:SetWidth(sw_width)
|
||||
pfUI.swingtimer.mainhand:SetHeight(sw_height)
|
||||
pfUI.swingtimer.mainhand:SetMinMaxValues(0, 1)
|
||||
pfUI.swingtimer.mainhand:SetValue(0)
|
||||
pfUI.swingtimer.mainhand:SetStatusBarTexture(sw_texture)
|
||||
pfUI.swingtimer.mainhand:SetStatusBarColor(mhR, mhG, mhB, mhA)
|
||||
pfUI.swingtimer.mainhand:Hide()
|
||||
-- A bar fills by itself from `swing`, the time span of the whole swing, and
|
||||
-- its text binding counts it down. The marker and its glows ride the edge
|
||||
-- of the fill. `shown` is whether the config lets the bar show at all.
|
||||
local function CreateSwingBar(name, label, shown)
|
||||
local bar = CreateFrame("StatusBar", name, UIParent)
|
||||
bar:SetWidth(sw_width)
|
||||
bar:SetHeight(sw_height)
|
||||
bar:SetStatusBarTexture(sw_texture)
|
||||
bar:Hide()
|
||||
bar.shown = shown
|
||||
|
||||
pfUI.swingtimer.mainhand.text = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.mainhand.text:SetPoint("CENTER", pfUI.swingtimer.mainhand, "CENTER", 0, 0)
|
||||
pfUI.swingtimer.mainhand.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.mainhand.text:SetTextColor(1, 1, 1, 1)
|
||||
pfUI.swingtimer.mainhand.text:SetText("")
|
||||
if not sw_showtext then pfUI.swingtimer.mainhand.text:Hide() end
|
||||
bar.swing = C_DurationUtil.CreateDuration()
|
||||
bar:SetTimerDuration(bar.swing, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
|
||||
|
||||
pfUI.swingtimer.mainhand.label = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.mainhand.label:SetPoint("RIGHT", pfUI.swingtimer.mainhand, "LEFT", -4, 0)
|
||||
pfUI.swingtimer.mainhand.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.mainhand.label:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
pfUI.swingtimer.mainhand.label:SetText(sw_showlabel and "MH" or "")
|
||||
bar.text = bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
bar.text:SetPoint("CENTER", bar, "CENTER", 0, 0)
|
||||
bar.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
bar.text:SetTextColor(1, 1, 1, 1)
|
||||
bar.text:SetShown(sw_showtext)
|
||||
|
||||
pfUI.swingtimer.mainhand.speed = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.mainhand.speed:SetPoint("LEFT", pfUI.swingtimer.mainhand, "RIGHT", 4, 0)
|
||||
pfUI.swingtimer.mainhand.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.mainhand.speed:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
pfUI.swingtimer.mainhand.speed:SetText("")
|
||||
if not sw_showspeed then pfUI.swingtimer.mainhand.speed:Hide() end
|
||||
bar.timer = C_DurationUtil.CreateDurationTextBinding()
|
||||
bar.timer:SetFontString(bar.text)
|
||||
bar.timer:SetDuration(bar.swing)
|
||||
bar.timer:SetFormatter(countdown)
|
||||
|
||||
CreateBackdrop(pfUI.swingtimer.mainhand)
|
||||
bar.label = bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
bar.label:SetPoint("RIGHT", bar, "LEFT", -4, 0)
|
||||
bar.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
bar.label:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
bar.label:SetText(sw_showlabel and label or "")
|
||||
|
||||
bar.speed = bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
bar.speed:SetPoint("LEFT", bar, "RIGHT", 4, 0)
|
||||
bar.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
bar.speed:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
bar.speed:SetShown(sw_showspeed)
|
||||
|
||||
pfUI.swingtimer.mainhand.marker = pfUI.swingtimer.mainhand:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.mainhand.marker:SetColorTexture(1, 1, 1, 1.0)
|
||||
pfUI.swingtimer.mainhand.marker:SetWidth(2)
|
||||
pfUI.swingtimer.mainhand.marker:SetHeight(sw_height)
|
||||
pfUI.swingtimer.mainhand.marker:Hide()
|
||||
CreateBackdrop(bar)
|
||||
|
||||
-- Left glow: fades from transparent to white (right edge = marker)
|
||||
pfUI.swingtimer.mainhand.markerGlowL = pfUI.swingtimer.mainhand:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.mainhand.markerGlowL:SetColorTexture(1, 1, 1, 1)
|
||||
pfUI.swingtimer.mainhand.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
|
||||
pfUI.swingtimer.mainhand.markerGlowL:SetWidth(10)
|
||||
pfUI.swingtimer.mainhand.markerGlowL:SetHeight(sw_height)
|
||||
pfUI.swingtimer.mainhand.markerGlowL:Hide()
|
||||
bar.edge = CreateFrame("Frame", nil, bar)
|
||||
bar.edge:SetAllPoints(bar)
|
||||
bar.edge:Hide()
|
||||
|
||||
-- Right glow: fades from white to transparent (left edge = marker)
|
||||
pfUI.swingtimer.mainhand.markerGlowR = pfUI.swingtimer.mainhand:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.mainhand.markerGlowR:SetColorTexture(1, 1, 1, 1)
|
||||
pfUI.swingtimer.mainhand.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
|
||||
pfUI.swingtimer.mainhand.markerGlowR:SetWidth(10)
|
||||
pfUI.swingtimer.mainhand.markerGlowR:SetHeight(sw_height)
|
||||
pfUI.swingtimer.mainhand.markerGlowR:Hide()
|
||||
bar.marker = bar.edge:CreateTexture(nil, "OVERLAY")
|
||||
bar.marker:SetColorTexture(1, 1, 1, 1.0)
|
||||
bar.marker:SetWidth(2)
|
||||
bar.marker:SetHeight(sw_height)
|
||||
bar.marker:SetPoint("CENTER", bar:GetStatusBarTexture(), "RIGHT")
|
||||
|
||||
CreateBackdropShadow(pfUI.swingtimer.mainhand)
|
||||
-- Left glow: fades from transparent to white (right edge = marker)
|
||||
bar.markerGlowL = bar.edge:CreateTexture(nil, "OVERLAY")
|
||||
bar.markerGlowL:SetColorTexture(1, 1, 1, 1)
|
||||
bar.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
|
||||
bar.markerGlowL:SetWidth(10)
|
||||
bar.markerGlowL:SetHeight(sw_height)
|
||||
bar.markerGlowL:SetPoint("RIGHT", bar.marker, "LEFT", 0, 0)
|
||||
|
||||
-- Offhand bar
|
||||
pfUI.swingtimer.offhand = CreateFrame("StatusBar", "pfSwingTimerOffhand", UIParent)
|
||||
pfUI.swingtimer.offhand:SetPoint("TOP", pfUI.swingtimer.mainhand, "BOTTOM", 0, -4)
|
||||
pfUI.swingtimer.offhand:SetWidth(sw_width)
|
||||
pfUI.swingtimer.offhand:SetHeight(sw_height)
|
||||
pfUI.swingtimer.offhand:SetMinMaxValues(0, 1)
|
||||
pfUI.swingtimer.offhand:SetValue(0)
|
||||
pfUI.swingtimer.offhand:SetStatusBarTexture(sw_texture)
|
||||
pfUI.swingtimer.offhand:SetStatusBarColor(ohR, ohG, ohB, ohA)
|
||||
pfUI.swingtimer.offhand:Hide()
|
||||
-- Right glow: fades from white to transparent (left edge = marker)
|
||||
bar.markerGlowR = bar.edge:CreateTexture(nil, "OVERLAY")
|
||||
bar.markerGlowR:SetColorTexture(1, 1, 1, 1)
|
||||
bar.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
|
||||
bar.markerGlowR:SetWidth(10)
|
||||
bar.markerGlowR:SetHeight(sw_height)
|
||||
bar.markerGlowR:SetPoint("LEFT", bar.marker, "RIGHT", 0, 0)
|
||||
|
||||
pfUI.swingtimer.offhand.text = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.offhand.text:SetPoint("CENTER", pfUI.swingtimer.offhand, "CENTER", 0, 0)
|
||||
pfUI.swingtimer.offhand.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.offhand.text:SetTextColor(1, 1, 1, 1)
|
||||
pfUI.swingtimer.offhand.text:SetText("")
|
||||
if not sw_showtext then pfUI.swingtimer.offhand.text:Hide() end
|
||||
CreateBackdropShadow(bar)
|
||||
return bar
|
||||
end
|
||||
|
||||
pfUI.swingtimer.offhand.label = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.offhand.label:SetPoint("RIGHT", pfUI.swingtimer.offhand, "LEFT", -4, 0)
|
||||
pfUI.swingtimer.offhand.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.offhand.label:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
pfUI.swingtimer.offhand.label:SetText(sw_showlabel and "OH" or "")
|
||||
local mainhand = CreateSwingBar("pfSwingTimerMainhand", "MH", true)
|
||||
mainhand:SetPoint("BOTTOM", UIParent, "CENTER", -189, 103)
|
||||
mainhand.showmarker = true
|
||||
|
||||
pfUI.swingtimer.offhand.speed = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.offhand.speed:SetPoint("LEFT", pfUI.swingtimer.offhand, "RIGHT", 4, 0)
|
||||
pfUI.swingtimer.offhand.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.offhand.speed:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
pfUI.swingtimer.offhand.speed:SetText("")
|
||||
if not sw_showspeed then pfUI.swingtimer.offhand.speed:Hide() end
|
||||
local offhand = CreateSwingBar("pfSwingTimerOffhand", "OH", sw_showoh)
|
||||
offhand:SetPoint("TOP", mainhand, "BOTTOM", 0, -4)
|
||||
offhand.showmarker = true
|
||||
|
||||
CreateBackdrop(pfUI.swingtimer.offhand)
|
||||
|
||||
|
||||
pfUI.swingtimer.offhand.marker = pfUI.swingtimer.offhand:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.offhand.marker:SetColorTexture(1, 1, 1, 1.0)
|
||||
pfUI.swingtimer.offhand.marker:SetWidth(2)
|
||||
pfUI.swingtimer.offhand.marker:SetHeight(sw_height)
|
||||
pfUI.swingtimer.offhand.marker:Hide()
|
||||
|
||||
-- Left glow: fades from transparent to white (right edge = marker)
|
||||
pfUI.swingtimer.offhand.markerGlowL = pfUI.swingtimer.offhand:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.offhand.markerGlowL:SetColorTexture(1, 1, 1, 1)
|
||||
pfUI.swingtimer.offhand.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
|
||||
pfUI.swingtimer.offhand.markerGlowL:SetWidth(10)
|
||||
pfUI.swingtimer.offhand.markerGlowL:SetHeight(sw_height)
|
||||
pfUI.swingtimer.offhand.markerGlowL:Hide()
|
||||
|
||||
-- Right glow: fades from white to transparent (left edge = marker)
|
||||
pfUI.swingtimer.offhand.markerGlowR = pfUI.swingtimer.offhand:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.offhand.markerGlowR:SetColorTexture(1, 1, 1, 1)
|
||||
pfUI.swingtimer.offhand.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
|
||||
pfUI.swingtimer.offhand.markerGlowR:SetWidth(10)
|
||||
pfUI.swingtimer.offhand.markerGlowR:SetHeight(sw_height)
|
||||
pfUI.swingtimer.offhand.markerGlowR:Hide()
|
||||
|
||||
CreateBackdropShadow(pfUI.swingtimer.offhand)
|
||||
|
||||
-- Ranged bar
|
||||
-- Hunters fill from the center: the bar drains toward the middle during
|
||||
-- the reload, then the auto shot deadzone grows back out in the warn color.
|
||||
pfUI.swingtimer.ranged = CreateFrame("StatusBar", "pfSwingTimerRanged", UIParent)
|
||||
pfUI.swingtimer.ranged:SetPoint("BOTTOM", UIParent, "CENTER", -189, 83)
|
||||
pfUI.swingtimer.ranged:SetWidth(sw_width)
|
||||
pfUI.swingtimer.ranged:SetHeight(sw_height)
|
||||
pfUI.swingtimer.ranged:SetMinMaxValues(0, 1)
|
||||
pfUI.swingtimer.ranged:SetValue(0)
|
||||
pfUI.swingtimer.ranged:SetStatusBarTexture(sw_texture)
|
||||
pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA)
|
||||
pfUI.swingtimer.ranged:SetFillStyle(isHunter and "CENTER" or "STANDARD")
|
||||
pfUI.swingtimer.ranged:Hide()
|
||||
-- Each phase is a time span of its own on `phase`.
|
||||
local ranged = CreateSwingBar("pfSwingTimerRanged", "Ra", sw_showranged)
|
||||
ranged:SetPoint("BOTTOM", UIParent, "CENTER", -189, 83)
|
||||
ranged:SetFillStyle(isHunter and "CENTER" or "STANDARD")
|
||||
ranged.showmarker = not isHunter
|
||||
if isHunter then
|
||||
ranged.phase = C_DurationUtil.CreateDuration()
|
||||
ranged.timer:SetDuration(ranged.phase)
|
||||
end
|
||||
|
||||
pfUI.swingtimer.ranged.text = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.ranged.text:SetPoint("CENTER", pfUI.swingtimer.ranged, "CENTER", 0, 0)
|
||||
pfUI.swingtimer.ranged.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.ranged.text:SetTextColor(1, 1, 1, 1)
|
||||
pfUI.swingtimer.ranged.text:SetText("")
|
||||
if not sw_showtext then pfUI.swingtimer.ranged.text:Hide() end
|
||||
pfUI.swingtimer.mainhand = mainhand
|
||||
pfUI.swingtimer.offhand = offhand
|
||||
pfUI.swingtimer.ranged = ranged
|
||||
|
||||
pfUI.swingtimer.ranged.label = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.ranged.label:SetPoint("RIGHT", pfUI.swingtimer.ranged, "LEFT", -4, 0)
|
||||
pfUI.swingtimer.ranged.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.ranged.label:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
pfUI.swingtimer.ranged.label:SetText(sw_showlabel and "Ra" or "")
|
||||
UpdateMovable(mainhand)
|
||||
UpdateMovable(ranged)
|
||||
|
||||
pfUI.swingtimer.ranged.speed = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
pfUI.swingtimer.ranged.speed:SetPoint("LEFT", pfUI.swingtimer.ranged, "RIGHT", 4, 0)
|
||||
pfUI.swingtimer.ranged.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
|
||||
pfUI.swingtimer.ranged.speed:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
pfUI.swingtimer.ranged.speed:SetText("")
|
||||
if not sw_showspeed then pfUI.swingtimer.ranged.speed:Hide() end
|
||||
|
||||
CreateBackdrop(pfUI.swingtimer.ranged)
|
||||
|
||||
|
||||
pfUI.swingtimer.ranged.marker = pfUI.swingtimer.ranged:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.ranged.marker:SetColorTexture(1, 1, 1, 1.0)
|
||||
pfUI.swingtimer.ranged.marker:SetWidth(2)
|
||||
pfUI.swingtimer.ranged.marker:SetHeight(sw_height)
|
||||
pfUI.swingtimer.ranged.marker:Hide()
|
||||
|
||||
-- Left glow: fades from transparent to white (right edge = marker)
|
||||
pfUI.swingtimer.ranged.markerGlowL = pfUI.swingtimer.ranged:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.ranged.markerGlowL:SetColorTexture(1, 1, 1, 1)
|
||||
pfUI.swingtimer.ranged.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
|
||||
pfUI.swingtimer.ranged.markerGlowL:SetWidth(10)
|
||||
pfUI.swingtimer.ranged.markerGlowL:SetHeight(sw_height)
|
||||
pfUI.swingtimer.ranged.markerGlowL:Hide()
|
||||
|
||||
-- Right glow: fades from white to transparent (left edge = marker)
|
||||
pfUI.swingtimer.ranged.markerGlowR = pfUI.swingtimer.ranged:CreateTexture(nil, "OVERLAY")
|
||||
pfUI.swingtimer.ranged.markerGlowR:SetColorTexture(1, 1, 1, 1)
|
||||
pfUI.swingtimer.ranged.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
|
||||
pfUI.swingtimer.ranged.markerGlowR:SetWidth(10)
|
||||
pfUI.swingtimer.ranged.markerGlowR:SetHeight(sw_height)
|
||||
pfUI.swingtimer.ranged.markerGlowR:Hide()
|
||||
|
||||
CreateBackdropShadow(pfUI.swingtimer.ranged)
|
||||
|
||||
UpdateMovable(pfUI.swingtimer.mainhand)
|
||||
UpdateMovable(pfUI.swingtimer.ranged)
|
||||
|
||||
-- OH weapon detection
|
||||
-- OH weapon detection: slot 17 (off hand) accepts one-hand (13) and off-hand
|
||||
-- weapons (22); main-hand-only (21) can never sit there.
|
||||
local OH_WEAPON_TYPES = { [13]=true, [22]=true }
|
||||
@@ -276,60 +192,6 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
S.raSpeed = (rs and rs > 0) and rs or 0
|
||||
end
|
||||
|
||||
-- Start* restart a countdown from a PLAYER_SWING reset. `remaining` can be
|
||||
-- shorter than the weapon speed (parry haste, ranged wind-up), so the bar
|
||||
-- length stays the full swing and the fill starts part-way along.
|
||||
local function StartMH(remaining)
|
||||
UpdateWeaponSpeeds()
|
||||
pfUI.swingtimer.mhGraceAt = nil -- cancel any pending hide
|
||||
S.mhTimerMax = math.max(S.mhSpeed, remaining)
|
||||
S.mhTimer = remaining
|
||||
S.mhActive = true
|
||||
pfUI.swingtimer.mainhand:Show()
|
||||
pfUI.swingtimer:Show()
|
||||
end
|
||||
|
||||
local function StartOH(remaining)
|
||||
UpdateWeaponSpeeds()
|
||||
-- The off-hand attack time stays set with a shield equipped
|
||||
if S.ohSpeed <= 0 then return end
|
||||
pfUI.swingtimer.ohGraceAt = nil -- cancel any pending hide
|
||||
S.ohTimerMax = math.max(S.ohSpeed, remaining)
|
||||
S.ohTimer = remaining
|
||||
S.ohActive = true
|
||||
if sw_showoh then pfUI.swingtimer.offhand:Show() end
|
||||
pfUI.swingtimer:Show()
|
||||
end
|
||||
|
||||
local function StartRanged(remaining)
|
||||
if not sw_showranged then return end
|
||||
UpdateWeaponSpeeds()
|
||||
if S.raSpeed <= 0 then return end
|
||||
S.raTimerMax = math.max(S.raSpeed, remaining)
|
||||
S.raTimer = remaining
|
||||
S.raActive = true
|
||||
|
||||
pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA)
|
||||
pfUI.swingtimer.ranged:SetValue(isHunter and 1 or 0)
|
||||
pfUI.swingtimer.ranged:Show()
|
||||
pfUI.swingtimer:Show()
|
||||
end
|
||||
|
||||
local function ResetAll()
|
||||
S.mhActive = false
|
||||
S.ohActive = false
|
||||
S.raActive = false
|
||||
S.mhTimer = 0
|
||||
S.ohTimer = 0
|
||||
S.raTimer = 0
|
||||
pfUI.swingtimer.mhGraceAt = nil
|
||||
pfUI.swingtimer.ohGraceAt = nil
|
||||
pfUI.swingtimer.mainhand:Hide()
|
||||
pfUI.swingtimer.offhand:Hide()
|
||||
pfUI.swingtimer.ranged:Hide()
|
||||
pfUI.swingtimer:Hide()
|
||||
end
|
||||
|
||||
-- HS/Cleave/Maul helpers. Canonical rank-1 spellIDs resolve to the
|
||||
-- localized spell name once, so per-spell classification is rank- and
|
||||
-- locale-independent without per-rank ID hardcoding (every rank of
|
||||
@@ -419,236 +281,139 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
return CheckQueuedAction(S.cachedHSSlots), CheckQueuedAction(S.cachedCleaveSlots)
|
||||
end
|
||||
|
||||
-- OnUpdate: countdown all timers with delta, then render
|
||||
pfUI.swingtimer:SetScript("OnUpdate", function()
|
||||
S.swingThrottle = S.swingThrottle + arg1
|
||||
local swingDelay = pfUI.throttle and pfUI.throttle:Get("swingtimer") or 0.02
|
||||
if S.swingThrottle < swingDelay then return end
|
||||
local delta = S.swingThrottle
|
||||
S.swingThrottle = 0
|
||||
|
||||
-- (out-of-combat hide handled naturally when timers expire)
|
||||
|
||||
local anyActive = false
|
||||
|
||||
-- Tick timers down. When a timer expires we give a short grace period before
|
||||
-- hiding the bar, to bridge the 1-2 frame gap until PLAYER_SWING arrives.
|
||||
-- If PLAYER_SWING arrives during the grace period it resets the timer
|
||||
-- normally and the grace timer is cleared. If not (e.g. auto-attack was
|
||||
-- turned off), the bar hides after the grace period ends.
|
||||
local GRACE = 0.15 -- seconds to wait after timer hits 0 before hiding
|
||||
|
||||
if S.mhActive then
|
||||
S.mhTimer = S.mhTimer - delta
|
||||
if S.mhTimer <= 0 then
|
||||
S.mhTimer = 0
|
||||
if not pfUI.swingtimer.mhGraceAt then
|
||||
pfUI.swingtimer.mhGraceAt = GetTime() + GRACE
|
||||
elseif GetTime() >= pfUI.swingtimer.mhGraceAt then
|
||||
pfUI.swingtimer.mhGraceAt = nil
|
||||
if not S.inCombat or not S.autoAttackActive then
|
||||
S.mhActive = false
|
||||
pfUI.swingtimer.mainhand:Hide()
|
||||
S.lastMhMarkerX = -1
|
||||
pfUI.swingtimer.mainhand.marker:Hide()
|
||||
pfUI.swingtimer.mainhand.markerGlowL:Hide()
|
||||
pfUI.swingtimer.mainhand.markerGlowR:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if S.ohActive then
|
||||
S.ohTimer = S.ohTimer - delta
|
||||
if S.ohTimer <= 0 then
|
||||
S.ohTimer = 0
|
||||
if not pfUI.swingtimer.ohGraceAt then
|
||||
pfUI.swingtimer.ohGraceAt = GetTime() + GRACE
|
||||
elseif GetTime() >= pfUI.swingtimer.ohGraceAt then
|
||||
pfUI.swingtimer.ohGraceAt = nil
|
||||
if not S.inCombat or not S.autoAttackActive then
|
||||
S.ohActive = false
|
||||
pfUI.swingtimer.offhand:Hide()
|
||||
S.lastOhMarkerX = -1
|
||||
pfUI.swingtimer.offhand.marker:Hide()
|
||||
pfUI.swingtimer.offhand.markerGlowL:Hide()
|
||||
pfUI.swingtimer.offhand.markerGlowR:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if S.raActive then
|
||||
S.raTimer = S.raTimer - delta
|
||||
if S.raTimer <= 0 then
|
||||
S.raTimer = 0
|
||||
S.raActive = false
|
||||
pfUI.swingtimer.ranged:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- HS/Cleave color
|
||||
local curR, curG, curB = mhDefaultR, mhDefaultG, mhDefaultB
|
||||
-- The bar colors follow the queued on-next-swing ability, the range state
|
||||
-- and the hunter deadzone. Runs on the events that change any of them;
|
||||
-- ACTIONBAR_UPDATE_STATE covers the IsCurrentAction reconcile.
|
||||
local function UpdateColors()
|
||||
local r, g, b = mhR, mhG, mhB
|
||||
if sw_hsqueue then
|
||||
if S.isDruid and S.useSpellQueueEvent then
|
||||
S.maulQueued, S.maulSeenCurrent =
|
||||
ReconcileQueued(S.maulQueued, S.cachedMaulSlots, S.maulSeenCurrent)
|
||||
end
|
||||
if S.maulQueued and S.isDruid then
|
||||
curR, curG, curB = 1.0, 0.55, 0.0 -- orange for druid maul queue
|
||||
r, g, b = 1.0, 0.55, 0.0 -- orange for druid maul queue
|
||||
elseif S.isWarrior then
|
||||
local hs, cl = IsHSOrCleaveQueued()
|
||||
if cl then
|
||||
curR, curG, curB = 0.2, 0.9, 0.2
|
||||
r, g, b = 0.2, 0.9, 0.2
|
||||
elseif hs then
|
||||
curR, curG, curB = 0.9, 0.9, 0.2
|
||||
r, g, b = 0.9, 0.9, 0.2
|
||||
end
|
||||
end
|
||||
end
|
||||
if S.outOfRange[MAINHAND] then r, g, b = orR, orG, orB end
|
||||
mainhand:SetStatusBarColor(r, g, b, mhA)
|
||||
|
||||
-- Render MH
|
||||
if S.outOfRange[MAINHAND] then
|
||||
curR, curG, curB = orR, orG, orB
|
||||
if S.outOfRange[OFFHAND] then
|
||||
offhand:SetStatusBarColor(orR, orG, orB, ohA)
|
||||
else
|
||||
offhand:SetStatusBarColor(ohR, ohG, ohB, ohA)
|
||||
end
|
||||
|
||||
if S.mhActive then
|
||||
local progress = 1 - (S.mhTimer / S.mhTimerMax)
|
||||
pfUI.swingtimer.mainhand:SetValue(progress)
|
||||
if S.mhTimer <= 0 then
|
||||
-- Timer expired: hide marker (no gap at right edge)
|
||||
if S.lastMhMarkerX ~= -1 then
|
||||
S.lastMhMarkerX = -1
|
||||
pfUI.swingtimer.mainhand.marker:Hide()
|
||||
pfUI.swingtimer.mainhand.markerGlowL:Hide()
|
||||
pfUI.swingtimer.mainhand.markerGlowR:Hide()
|
||||
end
|
||||
local a
|
||||
if ranged.warn then
|
||||
r, g, b, a = rwR, rwG, rwB, rwA
|
||||
else
|
||||
r, g, b, a = raR, raG, raB, raA
|
||||
end
|
||||
if S.outOfRange[RANGED] then r, g, b = orR, orG, orB end
|
||||
ranged:SetStatusBarColor(r, g, b, a)
|
||||
end
|
||||
|
||||
local function StopBar(bar)
|
||||
if bar.wake then bar.wake:Cancel() end
|
||||
bar.wake, bar.active, bar.graced = nil, nil, nil
|
||||
bar.timer:Disable()
|
||||
bar:Hide()
|
||||
end
|
||||
|
||||
-- a melee bar that is due waits for the next swing; it hides after the
|
||||
-- grace unless auto attack goes on, or on the event that stops it later
|
||||
local function MeleeGrace(bar)
|
||||
bar.graced = true
|
||||
if not S.inCombat or not S.autoAttackActive then StopBar(bar) end
|
||||
end
|
||||
|
||||
local function MeleeDue(bar)
|
||||
bar.edge:Hide()
|
||||
bar.wake = C_Timer.NewTimer(GRACE, function() MeleeGrace(bar) end)
|
||||
end
|
||||
|
||||
local function RangedDue(bar)
|
||||
StopBar(bar)
|
||||
end
|
||||
|
||||
-- the hunter's reload is over: the deadzone grows out in the warn color
|
||||
local function HunterDeadzone(bar)
|
||||
local finish = bar.swing:GetEndTime()
|
||||
bar.phase:SetTimeFromEnd(finish, DEADZONE)
|
||||
bar:SetTimerDuration(bar.phase, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
|
||||
bar.warn = true
|
||||
UpdateColors()
|
||||
bar.wake = C_Timer.NewTimer(finish - GetTime(), function() RangedDue(bar) end)
|
||||
end
|
||||
|
||||
-- Restarts a bar from a PLAYER_SWING reset. `remaining` can be shorter than
|
||||
-- the weapon speed (parry haste, ranged wind-up), so the bar length stays
|
||||
-- the full swing and the fill starts part-way along.
|
||||
local function StartBar(bar, remaining, speed, due)
|
||||
if bar.wake then bar.wake:Cancel() end
|
||||
local now = GetTime()
|
||||
bar.swing:SetTimeFromEnd(now + remaining, math.max(speed, remaining))
|
||||
bar.active, bar.graced, bar.warn = true, nil, nil
|
||||
if speed > 0 then bar.speed:SetFormattedText("%.2f", speed) end
|
||||
|
||||
if bar.phase then
|
||||
if remaining > DEADZONE then
|
||||
bar.phase:SetTimeFromEnd(now + remaining - DEADZONE, math.max(speed, remaining) - DEADZONE)
|
||||
bar:SetTimerDuration(bar.phase, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
|
||||
bar.wake = C_Timer.NewTimer(remaining - DEADZONE, function() HunterDeadzone(bar) end)
|
||||
else
|
||||
local mhMarkerX = progress * sw_width
|
||||
if mhMarkerX < 1 then mhMarkerX = 1 end
|
||||
if mhMarkerX > sw_width - 2 then mhMarkerX = sw_width - 2 end
|
||||
if mhMarkerX ~= S.lastMhMarkerX then
|
||||
S.lastMhMarkerX = mhMarkerX
|
||||
pfUI.swingtimer.mainhand.marker:SetPoint("LEFT", pfUI.swingtimer.mainhand, "LEFT", mhMarkerX - 1, 0)
|
||||
pfUI.swingtimer.mainhand.markerGlowL:SetPoint("RIGHT", pfUI.swingtimer.mainhand.marker, "LEFT", 0, 0)
|
||||
pfUI.swingtimer.mainhand.markerGlowR:SetPoint("LEFT", pfUI.swingtimer.mainhand.marker, "RIGHT", 0, 0)
|
||||
pfUI.swingtimer.mainhand.marker:Show()
|
||||
pfUI.swingtimer.mainhand.markerGlowL:Show()
|
||||
pfUI.swingtimer.mainhand.markerGlowR:Show()
|
||||
end
|
||||
HunterDeadzone(bar)
|
||||
end
|
||||
pfUI.swingtimer.mainhand:SetStatusBarColor(curR, curG, curB, mhA)
|
||||
if sw_showtext then
|
||||
pfUI.swingtimer.mainhand.text:SetFormattedText("%.1f", math.floor(S.mhTimer * 10) / 10)
|
||||
end
|
||||
if sw_showspeed and S.mhSpeed > 0 then
|
||||
pfUI.swingtimer.mainhand.speed:SetFormattedText("%.2f", S.mhSpeed)
|
||||
end
|
||||
anyActive = true
|
||||
else
|
||||
bar.wake = C_Timer.NewTimer(remaining, function() due(bar) end)
|
||||
end
|
||||
|
||||
-- Render OH
|
||||
if sw_showoh and S.ohActive then
|
||||
local progress = 1 - (S.ohTimer / S.ohTimerMax)
|
||||
pfUI.swingtimer.offhand:SetValue(progress)
|
||||
if S.outOfRange[OFFHAND] then
|
||||
pfUI.swingtimer.offhand:SetStatusBarColor(orR, orG, orB, ohA)
|
||||
else
|
||||
pfUI.swingtimer.offhand:SetStatusBarColor(ohR, ohG, ohB, ohA)
|
||||
end
|
||||
if S.ohTimer <= 0 then
|
||||
if S.lastOhMarkerX ~= -1 then
|
||||
S.lastOhMarkerX = -1
|
||||
pfUI.swingtimer.offhand.marker:Hide()
|
||||
pfUI.swingtimer.offhand.markerGlowL:Hide()
|
||||
pfUI.swingtimer.offhand.markerGlowR:Hide()
|
||||
end
|
||||
else
|
||||
local ohMarkerX = progress * sw_width
|
||||
if ohMarkerX < 1 then ohMarkerX = 1 end
|
||||
if ohMarkerX > sw_width - 2 then ohMarkerX = sw_width - 2 end
|
||||
if ohMarkerX ~= S.lastOhMarkerX then
|
||||
S.lastOhMarkerX = ohMarkerX
|
||||
pfUI.swingtimer.offhand.marker:SetPoint("LEFT", pfUI.swingtimer.offhand, "LEFT", ohMarkerX - 1, 0)
|
||||
pfUI.swingtimer.offhand.markerGlowL:SetPoint("RIGHT", pfUI.swingtimer.offhand.marker, "LEFT", 0, 0)
|
||||
pfUI.swingtimer.offhand.markerGlowR:SetPoint("LEFT", pfUI.swingtimer.offhand.marker, "RIGHT", 0, 0)
|
||||
pfUI.swingtimer.offhand.marker:Show()
|
||||
pfUI.swingtimer.offhand.markerGlowL:Show()
|
||||
pfUI.swingtimer.offhand.markerGlowR:Show()
|
||||
end
|
||||
end
|
||||
if sw_showtext then
|
||||
pfUI.swingtimer.offhand.text:SetFormattedText("%.1f", math.floor(S.ohTimer * 10) / 10)
|
||||
end
|
||||
if sw_showspeed and S.ohSpeed > 0 then
|
||||
pfUI.swingtimer.offhand.speed:SetFormattedText("%.2f", S.ohSpeed)
|
||||
end
|
||||
anyActive = true
|
||||
elseif not sw_showoh then
|
||||
pfUI.swingtimer.offhand:Hide()
|
||||
UpdateColors()
|
||||
if bar.shown then
|
||||
bar.edge:SetShown(bar.showmarker)
|
||||
if sw_showtext then bar.timer:Enable() end
|
||||
bar:Show()
|
||||
end
|
||||
end
|
||||
|
||||
-- Render Ranged
|
||||
if sw_showranged and S.raActive then
|
||||
local remaining = S.raTimer
|
||||
local r, g, b, a = raR, raG, raB, raA
|
||||
if isHunter then
|
||||
local DEADZONE = 0.5
|
||||
if remaining > DEADZONE then
|
||||
pfUI.swingtimer.ranged:SetValue((remaining - DEADZONE) / (S.raTimerMax - DEADZONE))
|
||||
else
|
||||
r, g, b, a = rwR, rwG, rwB, rwA
|
||||
pfUI.swingtimer.ranged:SetValue(1 - (remaining / DEADZONE))
|
||||
end
|
||||
if sw_showtext then
|
||||
if remaining <= 0.5 then
|
||||
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
|
||||
else
|
||||
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor((remaining - 0.5) * 10) / 10)
|
||||
end
|
||||
end
|
||||
else
|
||||
pfUI.swingtimer.ranged:SetValue(1 - (remaining / S.raTimerMax))
|
||||
if sw_showtext then
|
||||
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
|
||||
end
|
||||
end
|
||||
if S.outOfRange[RANGED] then
|
||||
r, g, b = orR, orG, orB
|
||||
end
|
||||
pfUI.swingtimer.ranged:SetStatusBarColor(r, g, b, a)
|
||||
if sw_showspeed and S.raSpeed > 0 then
|
||||
pfUI.swingtimer.ranged.speed:SetFormattedText("%.2f", S.raSpeed)
|
||||
end
|
||||
local raProgress = 1 - (S.raTimer / S.raTimerMax)
|
||||
local raMarkerX = raProgress * sw_width
|
||||
if raMarkerX < 1 then raMarkerX = 1 end
|
||||
if raMarkerX > sw_width - 2 then raMarkerX = sw_width - 2 end
|
||||
if raMarkerX ~= S.lastRaMarkerX then
|
||||
S.lastRaMarkerX = raMarkerX
|
||||
if not isHunter then
|
||||
pfUI.swingtimer.ranged.marker:SetPoint("LEFT", pfUI.swingtimer.ranged, "LEFT", raMarkerX - 1, 0)
|
||||
pfUI.swingtimer.ranged.marker:Show()
|
||||
pfUI.swingtimer.ranged.markerGlowL:SetPoint("RIGHT", pfUI.swingtimer.ranged.marker, "LEFT", 0, 0)
|
||||
pfUI.swingtimer.ranged.markerGlowR:SetPoint("LEFT", pfUI.swingtimer.ranged.marker, "RIGHT", 0, 0)
|
||||
pfUI.swingtimer.ranged.markerGlowL:Show()
|
||||
pfUI.swingtimer.ranged.markerGlowR:Show()
|
||||
else
|
||||
pfUI.swingtimer.ranged.marker:Hide()
|
||||
pfUI.swingtimer.ranged.markerGlowL:Hide()
|
||||
pfUI.swingtimer.ranged.markerGlowR:Hide()
|
||||
end
|
||||
end
|
||||
anyActive = true
|
||||
elseif not sw_showranged then
|
||||
pfUI.swingtimer.ranged:Hide()
|
||||
end
|
||||
local function StartMH(remaining)
|
||||
UpdateWeaponSpeeds()
|
||||
StartBar(mainhand, remaining, S.mhSpeed, MeleeDue)
|
||||
end
|
||||
|
||||
if not anyActive then
|
||||
if not pfUI.swingtimer.mainhand:IsShown()
|
||||
and not pfUI.swingtimer.offhand:IsShown()
|
||||
and not pfUI.swingtimer.ranged:IsShown() then
|
||||
this:Hide()
|
||||
end
|
||||
end
|
||||
end)
|
||||
local function StartOH(remaining)
|
||||
UpdateWeaponSpeeds()
|
||||
-- The off-hand attack time stays set with a shield equipped
|
||||
if S.ohSpeed <= 0 then return end
|
||||
StartBar(offhand, remaining, S.ohSpeed, MeleeDue)
|
||||
end
|
||||
|
||||
local function StartRanged(remaining)
|
||||
if not sw_showranged then return end
|
||||
UpdateWeaponSpeeds()
|
||||
if S.raSpeed <= 0 then return end
|
||||
StartBar(ranged, remaining, S.raSpeed, RangedDue)
|
||||
end
|
||||
|
||||
-- melee bars waiting past their grace hide once auto attack stops
|
||||
local function StopWaitingBars()
|
||||
if mainhand.graced then StopBar(mainhand) end
|
||||
if offhand.graced then StopBar(offhand) end
|
||||
end
|
||||
|
||||
local function ResetAll()
|
||||
StopBar(mainhand)
|
||||
StopBar(offhand)
|
||||
StopBar(ranged)
|
||||
end
|
||||
|
||||
local events = CreateFrame("Frame")
|
||||
events:RegisterEvent("PLAYER_SWING")
|
||||
@@ -658,6 +423,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
events:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
events:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
events:RegisterEvent("ACTIONBAR_UPDATE_STATE")
|
||||
events:RegisterEvent("PLAYER_DEAD")
|
||||
events:RegisterUnitEvent("UNIT_SPELLCAST_SENT", "player")
|
||||
events:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player")
|
||||
@@ -682,6 +448,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
-- arg1 = swingType, arg2 = isInRange, arg3 = checksRange; no check
|
||||
-- (no target, nothing to attack, no weapon) is not out of range
|
||||
S.outOfRange[arg1] = arg3 and not arg2
|
||||
UpdateColors()
|
||||
|
||||
elseif event == "UNIT_SPELLCAST_SENT" then
|
||||
-- arg4 = spellID: an on-next-swing ability (HS / Cleave / Maul) queued
|
||||
@@ -689,6 +456,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
if kind then
|
||||
S.useSpellQueueEvent = true
|
||||
SetQueuedKind(kind)
|
||||
UpdateColors()
|
||||
end
|
||||
|
||||
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
|
||||
@@ -696,6 +464,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
-- the queue, so drop the queued color
|
||||
if IsOnSwingSpell(arg3) then
|
||||
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
|
||||
UpdateColors()
|
||||
end
|
||||
|
||||
elseif event == "SPELL_QUEUE_EVENT" then
|
||||
@@ -707,12 +476,17 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
elseif eventCode == ON_SWING_QUEUE_POPPED then
|
||||
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
|
||||
end
|
||||
UpdateColors()
|
||||
|
||||
elseif event == "ACTIONBAR_UPDATE_STATE" then
|
||||
if sw_hsqueue then UpdateColors() end
|
||||
|
||||
elseif event == "START_AUTOATTACK" then
|
||||
S.autoAttackActive = true
|
||||
|
||||
elseif event == "STOP_AUTOATTACK" then
|
||||
S.autoAttackActive = false
|
||||
StopWaitingBars()
|
||||
|
||||
elseif event == "PLAYER_ENTERING_WORLD" then
|
||||
local class = UnitClassBase("player")
|
||||
@@ -725,14 +499,8 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
-- arg1 = changed slot; only weapon slots (main/off/ranged) affect swing speed
|
||||
if not INVSLOTS_EQUIPABLE_IN_COMBAT[arg1] then return end
|
||||
UpdateWeaponSpeeds()
|
||||
if S.ohSpeed == 0 then
|
||||
S.ohActive = false
|
||||
pfUI.swingtimer.offhand:Hide()
|
||||
end
|
||||
if S.raSpeed == 0 then
|
||||
S.raActive = false
|
||||
pfUI.swingtimer.ranged:Hide()
|
||||
end
|
||||
if S.ohSpeed == 0 then StopBar(offhand) end
|
||||
if S.raSpeed == 0 then StopBar(ranged) end
|
||||
|
||||
elseif event == "ACTIONBAR_SLOT_CHANGED" then
|
||||
RebuildQueueSlotCache()
|
||||
@@ -746,12 +514,22 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
S.hsQueued = false
|
||||
S.cleaveQueued = false
|
||||
S.maulQueued = false
|
||||
UpdateColors()
|
||||
StopWaitingBars()
|
||||
|
||||
elseif event == "PLAYER_DEAD" then
|
||||
ResetAll()
|
||||
end
|
||||
end)
|
||||
|
||||
local function Remaining(bar)
|
||||
return bar.active and bar.swing:GetRemainingDuration() or 0
|
||||
end
|
||||
|
||||
local function Progress(bar)
|
||||
return bar.active and bar.swing:GetElapsedPercent() or 0
|
||||
end
|
||||
|
||||
-- -------------------------------------------------------------------------
|
||||
-- Public API for external addons (e.g. SCRM)
|
||||
-- Only available when the swingtimer module is loaded.
|
||||
@@ -763,9 +541,9 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
-- -------------------------------------------------------------------------
|
||||
pfUI.swingtimer.api = {
|
||||
-- Remaining time in seconds until next swing
|
||||
GetMHTimer = function() return S.mhTimer end,
|
||||
GetOHTimer = function() return S.ohTimer end,
|
||||
GetRangedTimer = function() return S.raTimer end,
|
||||
GetMHTimer = function() return Remaining(mainhand) end,
|
||||
GetOHTimer = function() return Remaining(offhand) end,
|
||||
GetRangedTimer = function() return Remaining(ranged) end,
|
||||
|
||||
-- Weapon speed (full swing duration in seconds)
|
||||
GetMHSpeed = function() return S.mhSpeed end,
|
||||
@@ -773,20 +551,14 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
GetRangedSpeed = function() return S.raSpeed end,
|
||||
|
||||
-- Whether each timer is currently running
|
||||
IsMHActive = function() return S.mhActive end,
|
||||
IsOHActive = function() return S.ohActive end,
|
||||
IsRangedActive = function() return S.raActive end,
|
||||
IsMHActive = function() return mainhand.active == true end,
|
||||
IsOHActive = function() return offhand.active == true end,
|
||||
IsRangedActive = function() return ranged.active == true end,
|
||||
|
||||
-- Swing progress 0.0 (just reset) to 1.0 (about to swing)
|
||||
GetMHProgress = function()
|
||||
return S.mhActive and S.mhTimerMax > 0 and (1 - S.mhTimer / S.mhTimerMax) or 0
|
||||
end,
|
||||
GetOHProgress = function()
|
||||
return S.ohActive and S.ohTimerMax > 0 and (1 - S.ohTimer / S.ohTimerMax) or 0
|
||||
end,
|
||||
GetRangedProgress = function()
|
||||
return S.raActive and S.raTimerMax > 0 and (1 - S.raTimer / S.raTimerMax) or 0
|
||||
end,
|
||||
GetMHProgress = function() return Progress(mainhand) end,
|
||||
GetOHProgress = function() return Progress(offhand) end,
|
||||
GetRangedProgress = function() return Progress(ranged) end,
|
||||
|
||||
-- Warrior HS/Cleave queue state
|
||||
IsHSQueued = function() return S.hsQueued end,
|
||||
@@ -804,4 +576,5 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
if sw_showranged then EnableRangeCheck(RANGED) end
|
||||
|
||||
UpdateWeaponSpeeds()
|
||||
end)
|
||||
UpdateColors()
|
||||
end)
|
||||
|
||||
@@ -801,11 +801,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
HookAddon("SortBags", function()
|
||||
if C.thirdparty.sortbags.enable == "0" then return end
|
||||
|
||||
local sort = CreateFrame("Frame", nil)
|
||||
sort:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
sort:SetScript("OnEvent", function()
|
||||
this:UnregisterAllEvents()
|
||||
|
||||
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
|
||||
pfUI.thirdparty.RegisterBagSort("SortBags",
|
||||
function()
|
||||
SortBags()
|
||||
@@ -899,7 +895,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
|
||||
-- trigger the event whenever SuperMacro got an update
|
||||
hooksecurefunc("SM_UpdateActionSpell", function()
|
||||
for slot=1,120 do pfUI.bars.update[slot] = true end
|
||||
for slot=1,120 do pfUI.bars.QueueSlot(slot) end
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
@@ -142,11 +142,7 @@ pfUI:RegisterModule("thirdparty", function()
|
||||
HookAddon("MrPlow", function()
|
||||
if C.thirdparty.mrplow.enable == "0" then return end
|
||||
|
||||
local sort = CreateFrame("Frame", nil)
|
||||
sort:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
sort:SetScript("OnEvent", function()
|
||||
this:UnregisterAllEvents()
|
||||
|
||||
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
|
||||
local MrPlowL = AceLibrary and AceLibrary("AceLocale-2.2"):new("MrPlow")
|
||||
if not (MrPlowL and MrPlowL["Bank"]) then return end
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ pfUI:RegisterModule("tracking", function ()
|
||||
pfUI.tracking.pulse = pfUI.tracking.icon:CreateAnimationGroup()
|
||||
pfUI.tracking.pulse:SetLooping("BOUNCE")
|
||||
local fade = pfUI.tracking.pulse:CreateAnimation("Alpha")
|
||||
fade:SetChange(-.5)
|
||||
fade:SetFromAlpha(1)
|
||||
fade:SetToAlpha(.5)
|
||||
fade:SetDuration(.5)
|
||||
fade:SetSmoothing("IN_OUT")
|
||||
|
||||
|
||||
+1
-27
@@ -180,33 +180,7 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
CreateTargetIndicators()
|
||||
|
||||
-- Also try on PLAYER_ENTERING_WORLD in case target frame wasn't ready
|
||||
local initFrame = CreateFrame("Frame")
|
||||
initFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
initFrame:RegisterEvent("PLAYER_LOGOUT")
|
||||
initFrame:SetScript("OnEvent", function()
|
||||
-- Handle shutdown to prevent crash 132
|
||||
if event == "PLAYER_LOGOUT" then
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
-- Stop indicator OnUpdate scripts
|
||||
if pfUI.uf and pfUI.uf.target then
|
||||
if pfUI.uf.target.behindIndicator then
|
||||
pfUI.uf.target.behindIndicator:SetScript("OnUpdate", nil)
|
||||
end
|
||||
if pfUI.uf.target.distanceIndicator then
|
||||
pfUI.uf.target.distanceIndicator:SetScript("OnUpdate", nil)
|
||||
end
|
||||
-- free-frame mode polls from its own scanner frame, not the indicator
|
||||
if pfUI.uf.target.distanceScanner then
|
||||
pfUI.uf.target.distanceScanner:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
CreateTargetIndicators()
|
||||
this:UnregisterAllEvents()
|
||||
end)
|
||||
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", CreateTargetIndicators)
|
||||
|
||||
-- OS Notification Support
|
||||
if C.unitframes.unitxp_notify == "1" then
|
||||
|
||||
@@ -37,15 +37,15 @@ pfUI:RegisterModule("unusable", function ()
|
||||
|
||||
-- requirement changes (level, proficiency/skill, required spell, reputation,
|
||||
-- honor rank) don't touch the bags; queue a full rebag through pfUI.bag's
|
||||
-- coalesced delay, whose UpdateSlot resets the tint before we reapply it
|
||||
-- batched updates, whose UpdateSlot resets the tint before we reapply it
|
||||
local function Refresh()
|
||||
for _, bag in pairs(pfUI.BACKPACK) do
|
||||
pfUI.bag.delay.UpdateBag[bag] = true
|
||||
pfUI.bag:QueueBagUpdate(bag)
|
||||
end
|
||||
-- the bank frame is only created on first bank visit
|
||||
if pfUI.bag.left and pfUI.bag.left:IsShown() then
|
||||
for _, bag in pairs(pfUI.BANK) do
|
||||
pfUI.bag.delay.UpdateBag[bag] = true
|
||||
pfUI.bag:QueueBagUpdate(bag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+26
-22
@@ -69,11 +69,25 @@ pfUI:RegisterModule("xpbar", function ()
|
||||
self:SetAlpha(1)
|
||||
end
|
||||
|
||||
-- (Re)arm the auto-hide: fade the bar out after delay seconds unless it is
|
||||
-- always shown or under the cursor
|
||||
local function ScheduleFade(self, delay)
|
||||
if self.fadeTimer then self.fadeTimer:Cancel() end
|
||||
self.fadeTimer = nil
|
||||
if self.always or self.hover then return end
|
||||
self.fadeTimer = C_Timer.NewTimer(delay, function()
|
||||
self.fadeTimer = nil
|
||||
if self.hover or self:GetAlpha() == 0 then return end
|
||||
self.fade:Play()
|
||||
end)
|
||||
end
|
||||
|
||||
local function OnLeave(self)
|
||||
local self = self or this
|
||||
|
||||
self.hover = nil
|
||||
self.tick = GetTime() + 3.00
|
||||
if self.text_mouse == "1" then self.bar.text:Hide() end
|
||||
ScheduleFade(self, 3)
|
||||
GameTooltip:Hide()
|
||||
end
|
||||
|
||||
@@ -90,6 +104,9 @@ local function OnEnter(self)
|
||||
end
|
||||
|
||||
self.hover = true
|
||||
if self.fadeTimer then self.fadeTimer:Cancel() end
|
||||
self.fadeTimer = nil
|
||||
if self.text_mouse == "1" then self.bar.text:Show() end
|
||||
Reveal(self)
|
||||
|
||||
if mode == "XP" then
|
||||
@@ -187,21 +204,6 @@ local function OnEnter(self)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
local function OnUpdate(self)
|
||||
local self = self or this
|
||||
|
||||
if self.text_mouse == "1" then
|
||||
self.bar.text:SetShown(self.hover)
|
||||
end
|
||||
|
||||
if self.always then return end
|
||||
-- Hover comes from OnEnter/OnLeave, the same mouse focus that reveals the
|
||||
-- bar, so the fade can't restart under the cursor
|
||||
if self:GetAlpha() == 0 or self.hover or self.fade:IsPlaying() then return end
|
||||
if ( self.tick or 1) > GetTime() then return end
|
||||
self.fade:Play()
|
||||
end
|
||||
|
||||
local function OnEvent(self)
|
||||
local self = self or this
|
||||
|
||||
@@ -248,7 +250,7 @@ end
|
||||
if ex then text = "%s: %s%% (%s%% %s)" end
|
||||
self.bar.text:SetFormattedText(text, T["Experience"], xpperc, experc, T["Rested"])
|
||||
|
||||
self.tick = GetTime() + self.timeout
|
||||
ScheduleFade(self, self.timeout)
|
||||
if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then
|
||||
OnEnter(self)
|
||||
end
|
||||
@@ -266,7 +268,7 @@ end
|
||||
local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0
|
||||
self.bar.text:SetFormattedText(text, T["Pet Experience"], xpperc)
|
||||
|
||||
self.tick = GetTime() + self.timeout
|
||||
ScheduleFade(self, self.timeout)
|
||||
return
|
||||
elseif mode == "REP" then
|
||||
self.restedbar:Hide()
|
||||
@@ -294,7 +296,7 @@ end
|
||||
local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender)
|
||||
self.bar.text:SetFormattedText(text, name, perc, standing)
|
||||
|
||||
self.tick = GetTime() + self.timeout
|
||||
ScheduleFade(self, self.timeout)
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -302,6 +304,9 @@ end
|
||||
self.bar:SetStatusBarColor(.5,.5,.5,1)
|
||||
self.bar:SetMinMaxValues(0, 1)
|
||||
self.bar:SetValue(0)
|
||||
|
||||
-- nothing to show: hide now unless an earlier update is still on display
|
||||
if not self.fadeTimer then ScheduleFade(self, 0) end
|
||||
end
|
||||
|
||||
local function CreateBar(t)
|
||||
@@ -359,7 +364,7 @@ end
|
||||
b.bar.text:SetJustifyH("CENTER")
|
||||
b.bar.text:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
|
||||
b.bar.text:SetShown(b.text == "1")
|
||||
b.bar.text:SetShown(b.text == "1" and b.text_mouse ~= "1")
|
||||
|
||||
b.restedbar = b.restedbar or CreateFrame("StatusBar", nil, b)
|
||||
b.restedbar:SetStatusBarTexture(pfUI.media[C.panel.xp.texture])
|
||||
@@ -376,10 +381,10 @@ end
|
||||
|
||||
if not b.fade then
|
||||
b.fade = b:CreateAnimationGroup()
|
||||
b.fade:SetToFinalAlpha(true)
|
||||
local fade = b.fade:CreateAnimation("Alpha")
|
||||
fade:SetChange(-1)
|
||||
fade:SetDuration(.3)
|
||||
b.fade:SetScript("OnFinished", function() b:SetAlpha(0) end)
|
||||
end
|
||||
|
||||
b:RegisterEvent("FACTION_STANDING_CHANGED")
|
||||
@@ -392,7 +397,6 @@ end
|
||||
b:RegisterEvent("PLAYER_LEVEL_UP")
|
||||
b:RegisterEvent("UPDATE_FACTION")
|
||||
|
||||
b:SetScript("OnUpdate", OnUpdate)
|
||||
b:SetScript("OnEvent", OnEvent)
|
||||
b:SetScript("OnEnter", OnEnter)
|
||||
b:SetScript("OnLeave", OnLeave)
|
||||
|
||||
@@ -392,6 +392,10 @@ pfUI:SetScript("OnEvent", function()
|
||||
SetupConfigEnvironment()
|
||||
pfUI:UpdateFonts()
|
||||
|
||||
-- scale UIParent before the modules size their borders from it
|
||||
pfUI.api.UpdateUIScale()
|
||||
pfUI.events:RegisterFrameEventAndCallback("UI_SCALE_CHANGED", pfUI.api.UpdateUIScale)
|
||||
|
||||
-- load modules
|
||||
for _, m in pairs(this.modules) do
|
||||
if not ( pfUI_config["disabled"] and pfUI_config["disabled"][m] == "1" ) then
|
||||
@@ -457,15 +461,20 @@ message = function(msg)
|
||||
end
|
||||
print = print or message
|
||||
|
||||
error = function(msg)
|
||||
if PF_DEBUG_MODE then message(debugstack()) end
|
||||
if string.find(msg, "AddOns\\pfUI") then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555".. (msg or "nil" ))
|
||||
elseif not pfUI_config or (pfUI_config.global and pfUI_config.global.errors == "1") then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555".. (msg or "nil" ))
|
||||
-- Errors print to chat: pfUI's own always, other addons' when the errors
|
||||
-- option is on. In debug mode the stack follows, starting at the code that
|
||||
-- raised the error rather than at this handler.
|
||||
seterrorhandler(function(msg)
|
||||
msg = tostring(msg)
|
||||
if PF_DEBUG_MODE then
|
||||
local height = GetErrorCallstackHeight()
|
||||
message(height and debugstack(GetCallstackHeight() - height + 1) or debugstack(2))
|
||||
end
|
||||
end
|
||||
seterrorhandler(error)
|
||||
if string.find(msg, "AddOns\\pfUI") or not pfUI_config
|
||||
or (pfUI_config.global and pfUI_config.global.errors == "1") then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555" .. msg)
|
||||
end
|
||||
end)
|
||||
|
||||
function pfUI.SetupCVars()
|
||||
ClearTutorials()
|
||||
|
||||
@@ -5,10 +5,9 @@ pfUI:RegisterSkin("Mailbox", function ()
|
||||
-- Compatibility
|
||||
local StationeryBackgroundLeft, StationeryBackgroundRight
|
||||
do -- SendMailFrame
|
||||
local skin = CreateFrame("Frame")
|
||||
skin:SetScript("OnEvent", function()
|
||||
this:UnregisterEvent("MAIL_SHOW")
|
||||
if not this._Mail then
|
||||
local mailLoaded
|
||||
EventUtil.RegisterOnceFrameEventAndCallback("MAIL_SHOW", function()
|
||||
if not mailLoaded then
|
||||
StripTextures(SendMailPackageButton)
|
||||
SkinButton(SendMailPackageButton, nil, nil, nil, nil, true)
|
||||
|
||||
@@ -26,10 +25,9 @@ pfUI:RegisterSkin("Mailbox", function ()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
skin:RegisterEvent("MAIL_SHOW")
|
||||
|
||||
HookAddon("Mail", function()
|
||||
skin._Mail = true
|
||||
mailLoaded = true
|
||||
for i = 1, 21 do
|
||||
local button = _G["MailAttachment"..i]
|
||||
StripTextures(button)
|
||||
|
||||
Reference in New Issue
Block a user