Compare commits

...
20 Commits
Author SHA1 Message Date
Brues ee9bd9e31a libunitscan: cache levels only, classes come from C_PlayerCache 2026-11-03 01:17:04 -06:00
Brues df7c13651b Action bars: glow proc and reactive spells from ClassicAPI's overlay events
A button on the action bars pulses a gold border while the spell in its
slot glows: Overpower, Revenge, Execute, Riposte, Counterattack, Mongoose
Bite and Hammer of Wrath while usable, and Shadow Bolt under Shadow
Trance. The slot update resolves the spell, through GetMacroSpell for a
macro, and reads IsSpellOverlayed, so glows lit before a /reload show.
SPELL_ACTIVATION_OVERLAY_GLOW_SHOW/HIDE then update the buttons with that
spell ID; every rank fires its own event.

"Highlight Proc And Reactive Spells" under Action Bars turns it off.
The screen alerts are left out: their art isn't in the 1.12 client.
2026-11-03 01:07:03 -06:00
Brues 646174fc60 Animations: absolute alpha and setToFinalAlpha, combat feedback text fades on an animation group
Combat feedback text fades in, holds and fades out on an animation group
on the font string instead of an OnUpdate helper frame. Autohide and the
XP/rep bars keep the faded-out alpha with SetToFinalAlpha instead of an
OnFinished that sets it. Pulses, the mail blink, the holiday dusk and the
candle flicker give their alphas with SetFromAlpha / SetToAlpha.
2026-11-03 01:07:03 -06:00
Brues 1c1be19711 Castbars: stripe the fill of uninterruptible casts with a tiled texture
The shield draws img/stripes on OVERLAY, anchored to the fill with
horizontal and vertical tiling so the stripes keep their size as the bar
fills. Castbar text moves from the invalid DIALOG layer (read as ARTWORK)
to OVERLAY so it draws above the stripes.
2026-11-03 01:07:03 -06:00
Brues eb68642d21 Error handler: own function with the error-site stack, stop replacing the global error 2026-11-03 01:07:03 -06:00
Brues d2088c455f Unit frames: drain update flags on a queued flush, heal prediction from libpredict callbacks, drop the OnUpdate 2026-11-03 01:07:03 -06:00
Brues 9e2a89de62 Bags: batch event-queued updates on a timer, drop the OnUpdate 2026-11-03 01:07:02 -06:00
Brues 1a6314aa60 XP/rep bars: auto-hide rides a timer, drop the OnUpdate 2026-11-03 01:07:02 -06:00
Brues 70bfc1c443 One-shot event handlers: use EventUtil.RegisterOnceFrameEventAndCallback instead of self-unregistering frames 2026-11-03 01:07:02 -06:00
Brues d143e91f81 Mirror timers: bars fill from duration objects, drop the OnUpdate
A breath, fatigue or feign death bar fills from a duration object that
spans the bar at the timer's rate, and a text binding writes the m:ss
countdown. MirrorTimer_Show and MIRROR_TIMER_PAUSE resync it from
GetMirrorTimerProgress; a paused bar holds its value.
2026-11-03 01:07:02 -06:00
Brues 14ae659f2a Swing timer: bars fill from duration objects, drop the OnUpdate
Each bar fills itself from a duration object of the swing, a text
binding counts it down, and the marker with its glows rides the fill's
edge. One C_Timer per bar ends the swing, runs the melee grace and
switches a hunter's reload to the deadzone. Colors update on the events
that change them, ACTIONBAR_UPDATE_STATE standing in for the per-tick
IsCurrentAction reconcile; a bar waiting past its grace hides on
STOP_AUTOATTACK or leaving combat. The three bar builders become one.

The Swing Timer throttle setting only paced the OnUpdate, so it goes.
2026-11-03 01:07:02 -06:00
Brues 9187155ea6 Cast bar: the tradeskill spark rides a timer-driven fill, drop the OnUpdate
The per-craft spark of a merged tradeskill chain sits on the edge of an
invisible status bar that fills from cb.craft (ClassicAPI v1.19.1), so
nothing moves it by hand each frame.
2026-11-03 01:07:02 -06:00
Brues 465e87d7d2 Energy tick: the spark rides a timer-driven fill, drop the OnUpdate
The tick frame is a status bar whose invisible fill sweeps each period
from a duration object, with the spark anchored to the fill's edge
(ClassicAPI v1.19.1). A timer at each period's end rolls the sweep over
by whole periods, paused while hidden and caught up as it shows. The
five-second rule ends on its own timer, and the spark's alpha and the
rule bar update on the events that change them. Full mana now hides the
frame on every event, not just on a power update.
2026-11-03 01:07:02 -06:00
Brues dd4ebda0a9 Unit frames: anchor the heal prediction to the health fill's edge
ClassicAPI v1.19.1 moves a status bar texture's rect with the fill, so
the prediction starts at GetStatusBarTexture() and is only as wide as the
incoming heal. It follows the health bar while it animates instead of
sitting at the final value.
2026-11-03 01:07:02 -06:00
Brues 1abeafcec2 Action bars: range and usable state from ClassicAPI events, drop the range ticker
Shown buttons on bars 1-10 enable C_ActionBar.EnableActionRangeCheck for
their slot and take ACTION_RANGE_CHECK_UPDATE; hiding a bar or paging
releases the old slot. ACTION_USABLE_CHANGED refreshes only the changed
slots; ACTIONBAR_UPDATE_USABLE is left to the stance buttons. Requires
ClassicAPI v1.19.1.
2026-11-03 01:07:02 -06:00
Brues cde16e7fbe Action bars: route events to narrow passes, drop the OnUpdate
The pfActionBar frame ran an OnUpdate every frame and fully rebuilt all 140
buttons on events that only touch one part of them.

- Events go where the stock 1.12 bars send them: page and bonus-bar changes
  to the main bar's pager only, UPDATE_BINDINGS to the keybind text only,
  combat, auto repeat and tradeskill events to the active-border pass, and
  aura/form changes to the stance buttons that exist. Full rebuilds are left
  for entering the world, slot changes and the drag grid.
- Keybind text is read once per binding change from a fixed per-button
  command, no longer formatted and gsub'd on every slot update.
- Updates flush on the next frame through RunNextFrame instead of a 40 Hz
  OnUpdate; range is polled on a C_Timer ticker only while glowrange is on.
- Passes check visibility once per bar and walk only its shown buttons; a bar
  refreshes its buttons from OnShow, replacing the polled stale catch-up.
  Range no longer checks the buttons of disabled bars.
- Cooldowns skip CooldownFrame_SetTimer when start/duration/enable are
  unchanged; the active border only touches widgets when its state changes,
  and the "statechange" animation now follows every state change.
- PET_BAR_UPDATE only shows or hides the pet bar instead of re-running its
  whole layout; unlock mode is hooked instead of polled.
- Stance and pet buttons no longer inherit the previous button's oom tint.

pfUI.bars.update is replaced by pfUI.bars.QueueSlot(slot).
2026-11-03 01:07:02 -06:00
Brues a376afb2d7 UI scale: size borders from the scale the game applies, mirror its options
GetPerfectPixel divided by the uiScale option, or by 1 while useUiScale was
off, though the game's automatic scale is never under 0.9, so default
borders came out about a tenth of a pixel thin. The game scales UIParent
only after add-ons and saved variables load, so the pixel now comes from
GetPhysicalScreenSize and the scale the game is about to apply, worked out
from the same options and rules as OnDisplaySizeChanged. UI_SCALE_CHANGED
offers a reload once when the pixel size changes.

The preset dropdown and the pixelperfect module go: its presets were pixel
perfect only at 1080p and its restore path never ran. The settings show the
game's own Use UI-Scale and UI-Scale options, plus a Pixel Perfect UI-Scale
checkbox (one UI unit per screen pixel, kept through window resizes) that
overrides them. The pixel perfect border and HiDPI options go too; border
sizes now count real pixels. CVAR settings rows write their option through
their own config instead of this:GetParent(), which a dropdown's menu
button is not. The first-run slider stops at 0.64, below which the game
drops uiScale for its automatic scale.
2026-11-03 01:07:02 -06:00
Brues c8cc7bde79 Frame sizes: measure anchor-sized regions with the unscaled getters
GetWidth/GetHeight return an anchor-sized region's size times its effective
scale, so at any UI scale but 1 they disagreed with the sizes and offsets
built from them. The craft spark, raid-marker name shortening, unit frame
indicators, dropdown widening and the cooldown font's first size now read
GetUnscaledWidth/GetUnscaledHeight. The combat log and the settings rows
drop their GetRight()-GetLeft() workarounds for the same bug.
2026-11-03 01:07:02 -06:00
Brues d7e732981b Timer bars: buffwatch, loot roll, infobox and five-second rule drain from duration objects
Each bar hands its fill to StatusBar:SetTimerDuration instead of a per-frame
SetValue. Buffwatch's time text moves to a duration text binding holding the
cooldown formatter, now exposed as GetTimeFormatter (replacing
GetColoredTimeString, whose only caller this was). The five-second rule's
overlay style becomes a color-filled StatusBar too, so both styles share one
path; energytick's OnUpdate keeps only the tick spark.
2026-11-03 01:07:02 -06:00
Brues 2909679d62 Cast bars: fill and count down from duration objects, drop the OnUpdate sweeps
Player/target/focus bars and nameplate castbars hand their fill to
StatusBar:SetTimerDuration and their timer text to a duration text binding.
Pushback re-reads UnitCastingDuration/UnitChannelDuration; a C_Timer clears a
bar whose STOP never arrives. The nameplate castbar throttle is obsolete and
goes with its settings page.
2026-11-03 01:07:02 -06:00
44 changed files with 1433 additions and 1810 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ do
-- on. There pfUI does load, and will throw wherever it reaches for something -- 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 -- the installed DLL doesn't have yet -- the popup names the cause so those
-- errors aren't a mystery. -- 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_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI" local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest" local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
+1 -1
View File
@@ -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_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_UnitAuras - Replaces over 3k lines of hand-rolled aura tracking
- C_Spell - Replaces thousands of hardcoded spell names for each locale and powers castbar - 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) - 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) - 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 - Instant Bag Sorting - C_Container.MoveItem / SwapItems instead of cursor pickup/drop
+64 -27
View File
@@ -992,28 +992,53 @@ function pfUI.api.GetBorderSize(pref)
end end
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 ] -- [ GetPerfectPixel ]
-- Returns a number that equals a real pixel on regular scaled frames. -- Returns the size of one physical pixel in UIParent units.
-- Respects the current UI-scale and calculates a real pixel based on
-- the screen resolution and the 768px sized drawlayer.
function pfUI.api.GetPerfectPixel() function pfUI.api.GetPerfectPixel()
if pfUI.pixel then return pfUI.pixel end if pfUI.pixel then return pfUI.pixel end
if pfUI_config.appearance.border.pixelperfect == "1" then pfUI.pixel = PixelUtil.GetPixelToUIUnitFactor() / GetUIScale()
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.backdrop = { pfUI.backdrop = {
bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0, bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0,
@@ -1158,8 +1183,11 @@ end
-- [ Set Castbar Shield ] -- [ Set Castbar Shield ]
-- Colors a castbar for a cast that none of the player's own interrupts or -- 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, -- 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 -- so the state still reads on a bar configured without an icon. The fill also
-- restores the normal cast/channel color and the configured border. -- 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 -- 'bar' [frame] the castbar statusbar
-- 'icon' [frame] the icon frame, or nil for a bar without one -- 'icon' [frame] the icon frame, or nil for a bar without one
-- 'shielded' [bool] whether the cast is uninterruptible -- '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"])) bar:SetStatusBarColor(pfUI.api.GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
end 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 local iconborder = icon and icon.backdrop
if not iconborder then return end if not iconborder then return end
@@ -1364,10 +1404,10 @@ function pfUI.api.EnableAutohide(frame, timeout, combat)
if not frame.hover.fade then if not frame.hover.fade then
frame.hover.fade = frame:CreateAnimationGroup() frame.hover.fade = frame:CreateAnimationGroup()
frame.hover.fade:SetToFinalAlpha(true)
local fade = frame.hover.fade:CreateAnimation("Alpha") local fade = frame.hover.fade:CreateAnimation("Alpha")
fade:SetChange(-1) fade:SetChange(-1)
fade:SetDuration(1) fade:SetDuration(1)
frame.hover.fade:SetScript("OnFinished", function() frame:SetAlpha(0) end)
end end
if combat then if combat then
@@ -1469,13 +1509,10 @@ end
-- return object -- return object
function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2) function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
-- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren() -- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren()
-- below, and the traceback stops here — useless, since all callers share this -- below, where the message would name this line, shared by every caller;
-- line. pfUI shadows Lua's `error` (dropping the level arg and not throwing), -- level 2 names the offending skin instead.
-- 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.
if type(frame) ~= "table" or not frame.GetRegions then if type(frame) ~= "table" or not frame.GetRegions then
error("GetNoNameObject: invalid parent frame\n" .. debugstack(2, 3, 0)) error("GetNoNameObject: invalid parent frame", 2)
return
end end
local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1") local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
+1 -2
View File
@@ -129,9 +129,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "border", "color", "0.2,0.2,0.2,1") pfUI:UpdateConfig("appearance", "border", "color", "0.2,0.2,0.2,1")
pfUI:UpdateConfig("appearance", "border", "shadow", "0") pfUI:UpdateConfig("appearance", "border", "shadow", "0")
pfUI:UpdateConfig("appearance", "border", "shadow_intensity", ".35") pfUI:UpdateConfig("appearance", "border", "shadow_intensity", ".35")
pfUI:UpdateConfig("appearance", "border", "pixelperfect", "1")
pfUI:UpdateConfig("appearance", "border", "force_blizz", "0") pfUI:UpdateConfig("appearance", "border", "force_blizz", "0")
pfUI:UpdateConfig("appearance", "border", "hidpi", "1")
pfUI:UpdateConfig("appearance", "border", "default", "3") pfUI:UpdateConfig("appearance", "border", "default", "3")
pfUI:UpdateConfig("appearance", "border", "nameplates", "-1") pfUI:UpdateConfig("appearance", "border", "nameplates", "-1")
pfUI:UpdateConfig("appearance", "border", "actionbars", "-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, "oomcolor", ".2,.2,1,1")
pfUI:UpdateConfig("bars", nil, "showna", "1") pfUI:UpdateConfig("bars", nil, "showna", "1")
pfUI:UpdateConfig("bars", nil, "nacolor", ".3,.3,.3,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, "showequipped", "1")
pfUI:UpdateConfig("bars", nil, "eqcolor", ".2,.8,.2,.2") pfUI:UpdateConfig("bars", nil, "eqcolor", ".2,.8,.2,.2")
pfUI:UpdateConfig("bars", nil, "shiftdrag", "1") pfUI:UpdateConfig("bars", nil, "shiftdrag", "1")
+8 -7
View File
@@ -85,7 +85,7 @@ local function SetupCooldown(cooldown)
cooldown:SetReverse(kind == "aura") cooldown:SetReverse(kind == "aura")
cooldown:SetMinimumCountdownDuration(kind == "spell" and tonumber(C.cooldown.threshold) * 1000 or 0) cooldown:SetMinimumCountdownDuration(kind == "spell" and tonumber(C.cooldown.threshold) * 1000 or 0)
cooldown:SetCountdownFormatter(GetFormatter()) cooldown:SetCountdownFormatter(GetFormatter())
UpdateFont(cooldown, cooldown:GetParent():GetHeight()) UpdateFont(cooldown, cooldown:GetParent():GetUnscaledHeight())
end end
-- applies the "foreign" timers to another addon's cooldown. They only take -- 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:SetMinimumCountdownDuration(tonumber(C.cooldown.threshold) * 1000)
cooldown:SetCountdownFormatter(GetFormatter()) cooldown:SetCountdownFormatter(GetFormatter())
UpdateFont(cooldown, (cooldown:GetParent() or cooldown):GetHeight()) UpdateFont(cooldown, (cooldown:GetParent() or cooldown):GetUnscaledHeight())
end end
-- runs as a pfUI cooldown's timer starts: a timer with a total brings back -- 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 return start, dur
end end
-- [ GetColoredTimeString ] -- -- [ GetTimeFormatter ] --
-- 'remaining' [number] the time in seconds that should be converted -- The formatter cooldown numbers use, for other timers to show time the same
-- return [string] the time as cooldown numbers show it -- way. Hold it rather than a copy: a settings change updates it in place.
function pfUI.api.GetColoredTimeString(remaining) -- return [formatter] a NumericRuleFormatter
return GetFormatter():FormatNumber(remaining) function pfUI.api.GetTimeFormatter()
return GetFormatter()
end end
+5 -10
View File
@@ -601,7 +601,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
button:HookScript("OnClick", function() button:HookScript("OnClick", function()
local DropDownListWidth = DropDownList1:GetWidth() local DropDownListWidth = DropDownList1:GetWidth()
local DropDownFrameWidth = frame.backdrop:GetWidth() local DropDownFrameWidth = frame.backdrop:GetUnscaledWidth()
if DropDownListWidth < DropDownFrameWidth then if DropDownListWidth < DropDownFrameWidth then
local diff = DropDownFrameWidth - DropDownListWidth local diff = DropDownFrameWidth - DropDownListWidth
DropDownList1:SetWidth(DropDownList1:GetWidth() + diff) DropDownList1:SetWidth(DropDownList1:GetWidth() + diff)
@@ -834,10 +834,7 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
end) end)
infobox:SetScript("OnUpdate", function() infobox:SetScript("OnUpdate", function()
local time = infobox.lastshow + infobox.duration - GetTime() if infobox.duration:HasExpired() then
infobox.timeout:SetValue(time)
if GetTime() > infobox.lastshow + infobox.duration then
if not infobox.fade:IsPlaying() then infobox.fade:Play() end if not infobox.fade:IsPlaying() then infobox.fade:Play() end
elseif MouseIsOverHitRect(this) then elseif MouseIsOverHitRect(this) then
this:SetAlpha(max(0.4, this:GetAlpha() - .1)) 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 = CreateFrame("StatusBar", nil, infobox)
infobox.timeout:SetStatusBarTexture(pfUI.media["img:bar"]) infobox.timeout:SetStatusBarTexture(pfUI.media["img:bar"])
infobox.timeout:SetStatusBarColor(.3,1,.8,1) 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:ClearAllPoints()
infobox.text:SetAllPoints(infobox) infobox.text:SetAllPoints(infobox)
@@ -872,11 +871,7 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
end end
infobox.text:SetText(text) infobox.text:SetText(text)
infobox.timeout:SetMinMaxValues(0, time) infobox.duration:SetTimeFromStart(GetTime(), time)
infobox.timeout:SetValue(time)
infobox.duration = time
infobox.lastshow = GetTime()
infobox:SetSize(infobox.text:GetStringWidth() + 50, height) infobox:SetSize(infobox.text:GetStringWidth() + 50, height)
infobox:SetParent(parent) infobox:SetParent(parent)
+168 -183
View File
@@ -362,7 +362,7 @@ pfUI.events:RegisterCallback("aggro:changed", function(_, old, new)
if frame.aggro then if frame.aggro then
local guid = UnitGUID(frame.label .. frame.id) local guid = UnitGUID(frame.label .. frame.id)
if guid and (guid == old or guid == new) then if guid and (guid == old or guid == new) then
frame.update_combat = true pfUI.uf:QueueUpdate(frame, "combat")
end end
end end
end end
@@ -498,10 +498,18 @@ function pfUI.uf:UpdateVisibility()
self.chain = visibility ~= "hide" and unitstr:match("^(.+)target$") or nil self.chain = visibility ~= "hide" and unitstr:match("^(.+)target$") or nil
if self.chain then if self.chain then
local guid = UnitName(unitstr) and UnitGUID(unitstr) 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.unitguid = guid
self:RegisterUnitEvents(guid and CHAIN_TOKENS[unitstr] and unitstr or guid) self:RegisterUnitEvents(guid and CHAIN_TOKENS[unitstr] and unitstr or guid)
else 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) self:RegisterUnitEvents(visibility ~= "hide" and unitstr or nil)
end end
self:RegisterChainEvents(self.chain) self:RegisterChainEvents(self.chain)
@@ -773,7 +781,6 @@ function pfUI.uf:UpdateConfig()
if f.config.hitindicator == "1" then if f.config.hitindicator == "1" then
f.feedbackText:SetFont(pfUI.media[f.config.hitindicatorfont], f.config.hitindicatorsize, "OUTLINE") f.feedbackText:SetFont(pfUI.media[f.config.hitindicatorfont], f.config.hitindicatorsize, "OUTLINE")
f.feedbackFontHeight = f.config.hitindicatorsize f.feedbackFontHeight = f.config.hitindicatorsize
f.feedbackStartTime = GetTime()
if f.config.portrait == "bar" or f.config.portrait == "off" then if f.config.portrait == "bar" or f.config.portrait == "off" then
f.feedbackText:SetParent(f.texts) f.feedbackText:SetParent(f.texts)
f.feedbackText:ClearAllPoints() f.feedbackText:ClearAllPoints()
@@ -785,6 +792,7 @@ function pfUI.uf:UpdateConfig()
end end
f.combatfeedback = true f.combatfeedback = true
else else
f.feedback:Stop()
f.feedbackText:Hide() f.feedbackText:Hide()
f.combatfeedback = nil f.combatfeedback = nil
end end
@@ -853,12 +861,16 @@ function pfUI.uf:UpdateConfig()
f.incHeal.texture:SetVertexColor(cr, cg, cb, ca) f.incHeal.texture:SetVertexColor(cr, cg, cb, ca)
f.incHeal:Hide() f.incHeal:Hide()
if f.config.verticalbar == "0" then -- 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() f.incHeal:ClearAllPoints()
f.incHeal:SetPoint("TOPLEFT", f.hp.bar, "TOPLEFT", 0, 0) 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 else
f.incHeal:ClearAllPoints() f.incHeal:SetPoint("BOTTOMLEFT", f.hp.bar:GetStatusBarTexture(), "TOPLEFT")
f.incHeal:SetPoint("BOTTOM", f.hp.bar, "BOTTOM", 0, 0)
end end
f.ressIcon:SetFrameLevel(16) f.ressIcon:SetFrameLevel(16)
@@ -1031,6 +1043,8 @@ end
function pfUI.uf.OnShow() function pfUI.uf.OnShow()
RegisterShownEvents(this) RegisterShownEvents(this)
pfUI.uf:RefreshUnit(this, "all") pfUI.uf:RefreshUnit(this, "all")
-- flags left from while the frame was off screen, and the portrait's model
pfUI.uf:QueueUpdate(this)
end end
function pfUI.uf.OnHide() function pfUI.uf.OnHide()
@@ -1047,7 +1061,6 @@ function pfUI.uf.OnEvent()
if event == "PLAYER_LOGOUT" then if event == "PLAYER_LOGOUT" then
this:UnregisterAllEvents() this:UnregisterAllEvents()
this:SetScript("OnEvent", nil) this:SetScript("OnEvent", nil)
this:SetScript("OnUpdate", nil)
-- the visibility timer keeps ticking, so leaving this frame on its list -- the visibility timer keeps ticking, so leaving this frame on its list
-- would have UpdateVisibility re-register the unit events we just -- would have UpdateVisibility re-register the unit events we just
-- dropped -- straight back into the crash 132 this branch prevents -- dropped -- straight back into the crash 132 this branch prevents
@@ -1063,13 +1076,13 @@ function pfUI.uf.OnEvent()
event == "RAID_ROSTER_UPDATE" or event == "RAID_ROSTER_UPDATE" or
event == "PLAYER_UPDATE_RESTING" event == "PLAYER_UPDATE_RESTING"
then then
this.update_indicators = true pfUI.uf:QueueUpdate(this, "indicators")
end end
-- abort on broken unitframes (e.g focus) -- abort on broken unitframes (e.g focus)
if not this.label then return end 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 -- show it from here
if VISIBILITY_EVENTS[event] then if VISIBILITY_EVENTS[event] then
this:UpdateVisibility() this:UpdateVisibility()
@@ -1077,11 +1090,11 @@ function pfUI.uf.OnEvent()
-- update regular frames -- update regular frames
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
this.update_full = true pfUI.uf:QueueUpdate(this, "full")
-- Clear GUID tracking on zone change for full rebuild -- Clear GUID tracking on zone change for full rebuild
if pfUI.uf.ClearGuidTracking then pfUI.uf.ClearGuidTracking() end if pfUI.uf.ClearGuidTracking then pfUI.uf.ClearGuidTracking() end
elseif this.label == "target" and event == "PLAYER_TARGET_CHANGED" then 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 -- immediately update raid icon to avoid delay on target swap
if this.raidIcon and this.config and this.config.raidicon == "1" then if this.raidIcon and this.config and this.config.raidicon == "1" then
local raidIcon = UnitName("target") and GetRaidTargetIndex("target") local raidIcon = UnitName("target") and GetRaidTargetIndex("target")
@@ -1100,26 +1113,26 @@ function pfUI.uf.OnEvent()
local oldGuid = pfUI.uf.guidTracker.frameToGuid[this] local oldGuid = pfUI.uf.guidTracker.frameToGuid[this]
if newGuid ~= oldGuid then if newGuid ~= oldGuid then
pfUI.uf.guidTracker.frameToGuid[this] = newGuid pfUI.uf.guidTracker.frameToGuid[this] = newGuid
this.update_full = true pfUI.uf:QueueUpdate(this, "full")
end end
else else
this.update_full = true pfUI.uf:QueueUpdate(this, "full")
end end
elseif ( this.label == "raid" or this.label == "party" ) and event == "RAID_ROSTER_UPDATE" then 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 -- after frame IDs are reassigned. We don't set update_full here anymore
-- for raid frames to avoid the freeze. -- for raid frames to avoid the freeze.
if this.label == "party" then if this.label == "party" then
-- Party frames still need the old logic (no smart tracking yet) -- Party frames still need the old logic (no smart tracking yet)
this.update_full = true pfUI.uf:QueueUpdate(this, "full")
end end
-- Raid frames: update_full is set by raid.lua GUID tracker -- Raid frames: update_full is set by raid.lua GUID tracker
elseif this.label == "pet" and event == "UNIT_PET" then 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 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 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 elseif event == "UNIT_IN_RANGE_UPDATE" then
-- registered for the frame's unit alone, and fired in the filter's lower -- registered for the frame's unit alone, and fired in the filter's lower
-- case spelling, which a GUID's compare below would miss -- 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. -- GUID, so filtering on one of the two never wakes the frame twice.
elseif arg1 and arg1 == this.eventunit then elseif arg1 and arg1 == this.eventunit then
if event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" 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 elseif event == "UNIT_AURA" then
this.update_aura = true pfUI.uf:QueueUpdate(this, "aura")
elseif event == "UNIT_AURA_DELTA" then elseif event == "UNIT_AURA_DELTA" then
-- an aura that only refreshed or restacked leaves every aura in its -- an aura that only refreshed or restacked leaves every aura in its
-- slot, so the icons need their timers and stacks, not a rebuild -- slot, so the icons need their timers and stacks, not a rebuild
if arg2.isFullUpdate or arg2.addedAuraInstanceIDs or arg2.removedAuraInstanceIDs then if arg2.isFullUpdate or arg2.addedAuraInstanceIDs or arg2.removedAuraInstanceIDs then
this.update_aura = true pfUI.uf:QueueUpdate(this, "aura")
else else
this.update_auratime = true pfUI.uf:QueueUpdate(this, "auratime")
end end
elseif event == "UNIT_POWER_UPDATE" or event == "UNIT_MAXPOWER" then elseif event == "UNIT_POWER_UPDATE" or event == "UNIT_MAXPOWER" then
-- every power of the unit reports, a cat druid's mana regen too; redraw -- 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 -- for the one the bar shows, and for mana under a druid mana bar
local _, shown = UnitPowerType(arg1) local _, shown = UnitPowerType(arg1)
if arg2 == shown or (arg2 == "MANA" and this.druidmana) then if arg2 == shown or (arg2 == "MANA" and this.druidmana) then
this.update_base = true pfUI.uf:QueueUpdate(this, "base")
end end
elseif event == "UNIT_FACTION" then elseif event == "UNIT_FACTION" then
-- the PvP flag, the reaction colour and the tapped grey -- the PvP flag, the reaction colour and the tapped grey
this.update_indicators = true pfUI.uf:QueueUpdate(this, "indicators")
this.update_base = true pfUI.uf:QueueUpdate(this, "base")
elseif event == "UNIT_FLAGS" then elseif event == "UNIT_FLAGS" then
this.update_combat = true pfUI.uf:QueueUpdate(this, "combat")
elseif event == "UNIT_DISTANCE_CHECK_UPDATE" then elseif event == "UNIT_DISTANCE_CHECK_UPDATE" then
-- the portrait trades the question mark for the model, the frame fades -- 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 -- by sight, and an attacker that left with the member is let go
this.update_portrait = true pfUI.uf:QueueUpdate(this, "portrait")
this.update_combat = true pfUI.uf:QueueUpdate(this, "combat")
elseif event == "UNIT_CONNECTION" then elseif event == "UNIT_CONNECTION" then
this.update_full = true pfUI.uf:QueueUpdate(this, "full")
elseif event == "UNIT_COMBAT" then elseif event == "UNIT_COMBAT" then
-- every hit starts the fade over
this.feedback:Stop()
CombatFeedback_OnCombatEvent(arg2, arg3, arg4, arg5) CombatFeedback_OnCombatEvent(arg2, arg3, arg4, arg5)
this.feedback:Play()
elseif event == "INCOMING_RESURRECT_CHANGED" then elseif event == "INCOMING_RESURRECT_CHANGED" then
this.update_indicators = true pfUI.uf:QueueUpdate(this, "indicators")
else else
this.update_base = true pfUI.uf:QueueUpdate(this, "base")
end end
end end
end end
-- Local reference for performance -- Drain the update flags the events set, then hand a 3D portrait its unit.
local _GetTime = GetTime -- 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 if frame.update_full then
pfUI.uf.now = 0 -- process full updates, indicators and combat state included
pfUI.uf:RefreshUnit(frame, "all")
-- ============================================================================ -- clear update caches
-- OnUpdate - draining the event-set update flags, heal prediction, and the frame.update_full = nil
-- two polls no event replaces (a UnitXP range threshold, the pet frames). frame.update_base = nil
-- ============================================================================ frame.update_aura = nil
function pfUI.uf.OnUpdate() frame.update_auratime = nil
local now = _GetTime() frame.update_portrait = nil
pfUI.uf.now = now frame.update_indicators = nil
frame.update_combat = nil
-- 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 else
pfUI.uf:RefreshUnit(this, "base") if frame.update_indicators then
pfUI.uf:RefreshIndicators(frame)
frame.update_indicators = nil
end end
if frame.update_combat then
pfUI.uf:RefreshCombat(frame)
frame.update_combat = nil
end end
-- process individual events; a rebuild re-times the icons as well
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 frame.update_portrait then
pfUI.uf:RefreshUnit(frame, "portrait")
frame.update_portrait = nil
frame.update_base = true
end
if frame.update_base then
pfUI.uf:RefreshUnit(frame, "base")
frame.update_base = nil
end end
end end
-- Heal Prediction (throttled to 0.1s for responsiveness) -- a model takes its unit only once it is on screen, so it waits for this
if libpredict and this.incHeal then local model = frame.portrait and frame.portrait.model
if not this.lastHealTick then this.lastHealTick = now end if model and model.update then
if this.lastHealTick < now then model.lastUnit = UnitName(model.update)
this.lastHealTick = now + 0.1 model:SetUnit(model.update)
model:SetCamera(0)
model.update = nil
end
end
local unit = this.label .. this.id -- 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 heal = libpredict:UnitGetIncomingHeals(unit)
local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit) local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
if heal - health - maxHealth ~= this.predictstate then if heal - health - maxHealth == frame.predictstate then return end
local overhealperc = tonumber(this.config.overhealperc) frame.predictstate = heal - health - maxHealth
this.predictstate = heal - health - maxHealth
local overhealperc = tonumber(frame.config.overhealperc)
if heal > 0 and (health < maxHealth or overhealperc > 0 ) then if heal > 0 and (health < maxHealth or overhealperc > 0 ) then
local width = this.config.width local width = frame.config.width
local height = this.config.height local height = frame.config.height
if this.config.verticalbar == "0" then if frame.config.verticalbar == "0" then
local healthWidth = width * (health / maxHealth) local healthWidth = width * (health / maxHealth)
local incWidth = width * heal / maxHealth local incWidth = width * heal / maxHealth
if healthWidth + incWidth > width * (1+(overhealperc/100)) then if healthWidth + incWidth > width * (1+(overhealperc/100)) then
incWidth = width * (1+overhealperc/100) - healthWidth incWidth = width * (1+overhealperc/100) - healthWidth
end end
frame.incHeal:SetWidth(incWidth)
if this.config.invert_healthbar == "1" then
this.incHeal:SetWidth(incWidth)
else
this.incHeal:SetWidth(incWidth + healthWidth)
end
else else
local healthHeight = height * (health / maxHealth) local healthHeight = height * (health / maxHealth)
local incHeight = height * heal / maxHealth local incHeight = height * heal / maxHealth
if healthHeight + incHeight > height * (1+(overhealperc/100)) then if healthHeight + incHeight > height * (1+(overhealperc/100)) then
incHeight = height * (1+overhealperc/100) - healthHeight incHeight = height * (1+overhealperc/100) - healthHeight
end end
frame.incHeal:SetHeight(incHeight)
end
if this.config.invert_healthbar == "1" then frame.incHeal:Show()
this.incHeal:SetHeight(incHeight)
else else
this.incHeal:SetHeight(incHeight + healthHeight) frame.incHeal:Hide()
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
-- process full updates, indicators and combat state included
pfUI.uf:RefreshUnit(this, "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
else
if this.update_indicators then
pfUI.uf:RefreshIndicators(this)
this.update_indicators = nil
end
if this.update_combat then
pfUI.uf:RefreshCombat(this)
this.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
end
if this.update_portrait then
pfUI.uf:RefreshUnit(this, "portrait")
this.update_portrait = nil
this.update_base = true
end
if this.update_base then
pfUI.uf:RefreshUnit(this, "base")
this.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
end end
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 -- 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: -- routes through its "UNIT_XXX Events" branch. These are not registered here:
-- UpdateVisibility owns them, because it owns the frame's unit (below). -- 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("OnShow", pfUI.uf.OnShow)
f:SetScript("OnHide", pfUI.uf.OnHide) f:SetScript("OnHide", pfUI.uf.OnHide)
f:SetScript("OnEvent", pfUI.uf.OnEvent) f:SetScript("OnEvent", pfUI.uf.OnEvent)
f:SetScript("OnUpdate", pfUI.uf.OnUpdate)
visibilityframes[f] = true visibilityframes[f] = true
end end
@@ -1606,11 +1574,11 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
f.glow = CreateFrame("Frame", nil, f) f.glow = CreateFrame("Frame", nil, f)
-- Pulse between .8 and .4 for good. The group only advances while the glow -- Pulse between .8 and .4 for good. The group only advances while the glow
-- is shown, so a hidden glow costs nothing. -- is shown, so a hidden glow costs nothing.
f.glow:SetAlpha(.8)
f.glow.pulse = f.glow:CreateAnimationGroup() f.glow.pulse = f.glow:CreateAnimationGroup()
f.glow.pulse:SetLooping("BOUNCE") f.glow.pulse:SetLooping("BOUNCE")
local fade = f.glow.pulse:CreateAnimation("Alpha") local fade = f.glow.pulse:CreateAnimation("Alpha")
fade:SetChange(-.4) fade:SetFromAlpha(.8)
fade:SetToAlpha(.4)
fade:SetDuration(1.3) fade:SetDuration(1.3)
fade:SetSmoothing("IN_OUT") fade:SetSmoothing("IN_OUT")
f.glow.pulse:Play() 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.portrait.model.next = CreateFrame("PlayerModel", nil, nil)
f.feedbackText = f:CreateFontString("pfHitIndicator" .. f.label .. f.id, "OVERLAY", "NumberFontNormalHuge") 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 if f.label == "raid" and mod(f.id, 5) == 1 then
local group = math.ceil(f.id/5) local group = math.ceil(f.id/5)
f.group = f.group or f.hp.bar:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall") f.group = f.group or f.hp.bar:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall")
@@ -2042,7 +2025,7 @@ function pfUI.uf:RefreshUnit(unit, component)
local size local size
if disptype == "4" or disptype == "3" then 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 if size ~= indicator.size or disptype ~= indicator.disp or indipos ~= indicator.ipos then
indicator:ClearAllPoints() indicator:ClearAllPoints()
indicator:SetPoint(indipos, 0, 0) indicator:SetPoint(indipos, 0, 0)
@@ -2257,6 +2240,7 @@ function pfUI.uf:RefreshUnit(unit, component)
unit.portrait.model.update = unitstr unit.portrait.model.update = unitstr
end end
end end
if unit.portrait.model.update then pfUI.uf:QueueUpdate(unit) end
end end
end end
@@ -2397,6 +2381,7 @@ function pfUI.uf:RefreshUnit(unit, component)
if unit.druidmana then pfUI.uf:UpdateDruidMana(unit) end if unit.druidmana then pfUI.uf:UpdateDruidMana(unit) end
pfUI.uf:RefreshIncomingHeal(unit)
pfUI.uf:RefreshUnitState(unit) pfUI.uf:RefreshUnitState(unit)
end end
end end
@@ -2447,7 +2432,7 @@ function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
if not frame.hp then return end if not frame.hp then return end
local frame = frame.hp.bar 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) frame.icon = frame.icon or CreateFrame("Frame", nil, frame)
+1 -4
View File
@@ -935,14 +935,11 @@ pfUI_profiles["Adapta"].new_module_positions = pfUI_profiles["Modern"].new_modul
-- overwrite core profiles in userdata -- overwrite core profiles in userdata
local profile_loader = CreateFrame("Frame") EventUtil.RegisterOnceFrameEventAndCallback("VARIABLES_LOADED", function()
profile_loader:RegisterEvent("VARIABLES_LOADED")
profile_loader:SetScript("OnEvent", function()
pfUI_profiles["Modern"] = modern pfUI_profiles["Modern"] = modern
pfUI_profiles["Nostalgia"] = nostalgia pfUI_profiles["Nostalgia"] = nostalgia
pfUI_profiles["Legacy"] = legacy pfUI_profiles["Legacy"] = legacy
pfUI_profiles["Adapta"] = adapta pfUI_profiles["Adapta"] = adapta
pfUI_profiles["Light"] = light pfUI_profiles["Light"] = light
pfUI_profiles["Slim"] = slim pfUI_profiles["Slim"] = slim
this:UnregisterAllEvents()
end) end)
-1
View File
@@ -907,7 +907,6 @@ pfUI_translation["deDE"] = {
["Chat Tab"] = "Chat Tab", ["Chat Tab"] = "Chat Tab",
["Libpredict"] = "Libpredict", ["Libpredict"] = "Libpredict",
["Panel Alignment"] = "Panel Ausrichtung", ["Panel Alignment"] = "Panel Ausrichtung",
["Nameplate Update Rate"] = "Namensplaketten Aktualisierungsrate",
["Target/Casting Plates"] = "Ziel/Cast Plaketten", ["Target/Casting Plates"] = "Ziel/Cast Plaketten",
["Tooltip Update Rate"] = "Tooltip Aktualisierungsrate", ["Tooltip Update Rate"] = "Tooltip Aktualisierungsrate",
["Cursor Follow"] = "Cursor Folgen", ["Cursor Follow"] = "Cursor Folgen",
+1 -1
View File
@@ -433,6 +433,7 @@ pfUI_translation["enUS"] = {
["Highlight Out Of Mana Spells"] = nil, ["Highlight Out Of Mana Spells"] = nil,
["Highlight New Items"] = nil, ["Highlight New Items"] = nil,
["Highlight Out Of Range Spells"] = nil, ["Highlight Out Of Range Spells"] = nil,
["Highlight Proc And Reactive Spells"] = nil,
["Highlight Settings That Require Reload"] = nil, ["Highlight Settings That Require Reload"] = nil,
["Highlight Unusable Items"] = nil, ["Highlight Unusable Items"] = nil,
["Hold [Shift] to use item."] = nil, ["Hold [Shift] to use item."] = nil,
@@ -939,7 +940,6 @@ pfUI_translation["enUS"] = {
["Chat Tab"] = nil, ["Chat Tab"] = nil,
["Libpredict"] = nil, ["Libpredict"] = nil,
["Panel Alignment"] = nil, ["Panel Alignment"] = nil,
["Nameplate Update Rate"] = nil,
["Target/Casting Plates"] = nil, ["Target/Casting Plates"] = nil,
["Tooltip Update Rate"] = nil, ["Tooltip Update Rate"] = nil,
["Cursor Follow"] = nil, ["Cursor Follow"] = nil,
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

-1
View File
@@ -54,7 +54,6 @@
<Include file="..\modules\gm.lua"/> <Include file="..\modules\gm.lua"/>
<Include file="..\modules\mirrortimers.lua"/> <Include file="..\modules\mirrortimers.lua"/>
<Include file="..\modules\feigndeath.lua"/> <Include file="..\modules\feigndeath.lua"/>
<Include file="..\modules\pixelperfect.lua"/>
<Include file="..\modules\hdgraphic.lua"/> <Include file="..\modules\hdgraphic.lua"/>
<Include file="..\modules\share.lua"/> <Include file="..\modules\share.lua"/>
<Include file="..\modules\tracking.lua"/> <Include file="..\modules\tracking.lua"/>
+29 -15
View File
@@ -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 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 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. -- Localized spell names resolved once from canonical rank-1 spellIDs.
-- Every rank shares the same name, so per-rank comparisons elsewhere can -- Every rank shares the same name, so per-rank comparisons elsewhere can
-- be done against these constants without per-locale or per-rank tables. -- be done against these constants without per-locale or per-rank tables.
@@ -309,7 +321,7 @@ end
local function RefreshResFrames(guid) local function RefreshResFrames(guid)
for _, frame in pairs(pfUI.uf.frames) do for _, frame in pairs(pfUI.uf.frames) do
if frame.unitstr and UnitGUID(frame.unitstr) == guid then if frame.unitstr and UnitGUID(frame.unitstr) == guid then
frame.update_indicators = true pfUI.uf:QueueUpdate(frame, "indicators")
end end
end end
end end
@@ -439,7 +451,7 @@ function libpredict:ParseChatMessage(sender, msg)
end end
elseif msgtype == "Hot" then elseif msgtype == "Hot" then
-- Duplicate check: ignore same sender+target+spell within DUPLICATE_WINDOW -- 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 local key = sender .. target .. heal
if recentHots[key] and (now - recentHots[key]) < DUPLICATE_WINDOW then if recentHots[key] and (now - recentHots[key]) < DUPLICATE_WINDOW then
if libpredict.debug then if libpredict.debug then
@@ -484,11 +496,11 @@ function libpredict:Heal(sender, target, amount, duration, senderGuid)
return return
end end
local now = pfUI.uf.now or GetTime() local timeout = duration/1000 + GetTime()
local timeout = duration/1000 + now
heals[target] = heals[target] or {} heals[target] = heals[target] or {}
heals[target][sender] = { amount, timeout } heals[target][sender] = { amount, timeout }
if senderGuid then healGuidToName[senderGuid] = sender end if senderGuid then healGuidToName[senderGuid] = sender end
HealsChanged(target, timeout)
end end
-- Debug flag -- Debug flag
@@ -512,7 +524,7 @@ function libpredict:Hot(sender, target, spell, duration, startTime, source, rank
local existingRank = tonumber(existing.rank) or 0 local existingRank = tonumber(existing.rank) or 0
local newRank = tonumber(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 local existingTimeleft = (existing.start + existing.duration) - now
-- If existing HoT is still active and has higher rank, don't overwrite -- 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
end end
local now = pfUI.uf.now or GetTime() local now = GetTime()
hots[target][spell].duration = duration hots[target][spell].duration = duration
hots[target][spell].start = startTime or now hots[target][spell].start = startTime or now
hots[target][spell].rank = rank -- Store rank for protection 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 if pfUI and pfUI.uf and pfUI.uf.frames then
for _, frame in pairs(pfUI.uf.frames) do for _, frame in pairs(pfUI.uf.frames) do
if frame.namecache == target then if frame.namecache == target then
frame.update_aura = true pfUI.uf:QueueUpdate(frame, "aura")
if libpredict.debug then if libpredict.debug then
DEFAULT_CHAT_FRAME:AddMessage(" |cff00ff00-> Frame update triggered for " .. (frame:GetName() or "?") .. "|r") DEFAULT_CHAT_FRAME:AddMessage(" |cff00ff00-> Frame update triggered for " .. (frame:GetName() or "?") .. "|r")
end end
@@ -558,6 +570,7 @@ function libpredict:HealStop(sender)
for tsender in pairs(heals[ttarget]) do for tsender in pairs(heals[ttarget]) do
if sender == tsender then if sender == tsender then
heals[ttarget][tsender] = nil heals[ttarget][tsender] = nil
HealsChanged(ttarget)
end end
end end
end end
@@ -573,6 +586,7 @@ function libpredict:HealDelay(sender, delay)
for tsender, amount in pairs(heals[target]) do for tsender, amount in pairs(heals[target]) do
if sender == tsender then if sender == tsender then
amount[2] = amount[2] + delay amount[2] = amount[2] + delay
HealsChanged(target, amount[2])
end end
end end
end end
@@ -588,7 +602,7 @@ function libpredict:UnitGetIncomingHeals(unit)
if not heals[name] then if not heals[name] then
return sumheal return sumheal
else else
local now = pfUI.uf.now or GetTime() local now = GetTime()
for sender, amount in pairs(heals[name]) do for sender, amount in pairs(heals[name]) do
if amount[2] <= now then if amount[2] <= now then
heals[name][sender] = nil heals[name][sender] = nil
@@ -720,7 +734,7 @@ hooksecurefunc("CastSpell", function(id, bookType)
if effect == REJUVENATION then if effect == REJUVENATION then
local target = spell_queue[3] local target = spell_queue[3]
local now = pfUI.uf.now or GetTime() local now = GetTime()
local key = "Reju" .. target local key = "Reju" .. target
-- Cooldown-Check -- Cooldown-Check
@@ -737,7 +751,7 @@ hooksecurefunc("CastSpell", function(id, bookType)
libpredict.sender:SendHealCommMsg("Reju/"..target.."/"..rejuvDuration.."/"..rankStr.."/") libpredict.sender:SendHealCommMsg("Reju/"..target.."/"..rejuvDuration.."/"..rankStr.."/")
elseif effect == RENEW then elseif effect == RENEW then
local target = spell_queue[3] local target = spell_queue[3]
local now = pfUI.uf.now or GetTime() local now = GetTime()
local key = "Renew" .. target local key = "Renew" .. target
-- Cooldown-Check -- Cooldown-Check
@@ -783,7 +797,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
if effect == REJUVENATION then if effect == REJUVENATION then
local hotTarget = target or default local hotTarget = target or default
local now = pfUI.uf.now or GetTime() local now = GetTime()
local key = "Reju" .. hotTarget local key = "Reju" .. hotTarget
-- Cooldown-Check -- Cooldown-Check
@@ -800,7 +814,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/") libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
elseif effect == RENEW then elseif effect == RENEW then
local hotTarget = target or default local hotTarget = target or default
local now = pfUI.uf.now or GetTime() local now = GetTime()
local key = "Renew" .. hotTarget local key = "Renew" .. hotTarget
-- Cooldown-Check -- Cooldown-Check
@@ -843,7 +857,7 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
if effect == REJUVENATION then if effect == REJUVENATION then
local hotTarget = spell_queue[3] local hotTarget = spell_queue[3]
local now = pfUI.uf.now or GetTime() local now = GetTime()
local key = "Reju" .. hotTarget local key = "Reju" .. hotTarget
-- Cooldown-Check -- Cooldown-Check
@@ -860,7 +874,7 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/") libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
elseif effect == RENEW then elseif effect == RENEW then
local hotTarget = spell_queue[3] local hotTarget = spell_queue[3]
local now = pfUI.uf.now or GetTime() local now = GetTime()
local key = "Renew" .. hotTarget local key = "Renew" .. hotTarget
-- Cooldown-Check -- 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 -- FALLBACK: the HealComm/cast-fed table, for HoTs C_UnitAuras has no timer for
local start, duration, timeleft local start, duration, timeleft
local now = pfUI.uf.now or GetTime() local now = GetTime()
local unitName = UnitName(unit) local unitName = UnitName(unit)
local unitdata = hots[unitName] local unitdata = hots[unitName]
+1 -11
View File
@@ -31,10 +31,8 @@ libthrottle.presetNames = {
-- Default throttle categories -- Default throttle categories
libthrottle.defaults = { libthrottle.defaults = {
nameplates_castbar = "custom",
tooltip_cursor = "fastest", tooltip_cursor = "fastest",
chat_tab = "custom", chat_tab = "custom",
swingtimer = "custom",
} }
-- Convert FPS to throttle delay in seconds -- Convert FPS to throttle delay in seconds
@@ -141,8 +139,7 @@ function libthrottle:ResetAllToDefaults()
end end
-- Initialize - set defaults if config doesn't exist -- Initialize - set defaults if config doesn't exist
libthrottle:SetScript("OnEvent", function() EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
if event == "PLAYER_ENTERING_WORLD" then
if not _G.pfUI_throttle then if not _G.pfUI_throttle then
_G.pfUI_throttle = {} _G.pfUI_throttle = {}
end end
@@ -155,16 +152,9 @@ libthrottle:SetScript("OnEvent", function()
end end
-- Set defaults for custom fields if missing -- 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.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.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) end)
libthrottle:RegisterEvent("PLAYER_ENTERING_WORLD")
-- Export to pfUI namespace for easier access -- Export to pfUI namespace for easier access
pfUI.throttle = libthrottle pfUI.throttle = libthrottle
+45 -66
View File
@@ -2,20 +2,17 @@
setfenv(1, pfUI:GetEnvironment()) setfenv(1, pfUI:GetEnvironment())
--[[ libunitscan ]]-- --[[ libunitscan ]]--
-- A pfUI library that remembers the class, level and guild of every player it -- A pfUI library that remembers the level of every player it sees: in the
-- sees: in the friends, guild, raid and party rosters, in /who results, and as -- friends, guild, raid and party rosters, in /who results, and as target,
-- target, mouseover or nameplate. The data is persisted within pfUI_playerDB, -- mouseover or nameplate. The levels are persisted within pfUI_playerDB, for
-- for the places that only have a player's name, such as chat links and -- the places that only have a player's name, such as chat links and offline
-- offline friends. Anything with a unit reads the unit itself instead. -- friends. Anything with a unit reads the unit itself instead. A player's
-- class comes from C_PlayerCache.GetPlayerInfoByName, not from here.
-- --
-- External functions: -- External functions:
-- GetUnitInfo(name) -- GetPlayerLevel(name)
-- Returns information of the given player name. Returns nil if the player -- Returns the last seen level of the given player name, or nil if it was
-- was never seen. -- never seen.
--
-- class[String] - The class of the player
-- level[Number] - The level of the player
-- guild[String] - The guild name of the player
-- --
-- return instantly when another libunitscan is already active -- return instantly when another libunitscan is already active
@@ -23,33 +20,33 @@ if pfUI.api.libunitscan then return end
local players = {} local players = {}
-- Feed (guid, name, classToken) into ClassicAPI's persistent name -- Feed a unit into ClassicAPI's persistent name cache. Roster-style packets
-- cache when the unit's GUID is resolvable. Roster-style packets -- (party / raid / inspect on glance) carry class inline, so the engine often
-- (party / raid / inspect on glance) carry class inline, so the -- doesn't issue a separate name query and the DLL's NameCache hook never
-- engine often doesn't issue a separate name query and the DLL's -- fires for them — this is where we close that gap.
-- NameCache hook never fires for them — this is where we close that gap. local function RememberByUnit(unit)
local function RememberByUnit(unit, name, class)
if not (name and class) then return end
local guid = UnitGUID(unit) local guid = UnitGUID(unit)
if not guid then return end 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 end
function GetUnitInfo(name) function GetPlayerLevel(name)
local ret = players[name] local entry = players[name]
if ret then return entry and entry.level
return ret.class, ret.level, ret.guild 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
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) local libunitscan = CreateFrame("Frame", "pfUnitScan", UIParent)
libunitscan:RegisterEvent("PLAYER_ENTERING_WORLD") libunitscan:RegisterEvent("PLAYER_ENTERING_WORLD")
libunitscan:RegisterEvent("FRIENDLIST_UPDATE") libunitscan:RegisterEvent("FRIENDLIST_UPDATE")
@@ -68,58 +65,45 @@ libunitscan:SetScript("OnEvent", function()
players = pfUI_playerDB players = pfUI_playerDB
-- update own character details -- update own character details
local name = UnitName("player") AddLevel(UnitName("player"), UnitLevel("player"))
local class = UnitClassBase("player") RememberByUnit("player")
local level = UnitLevel("player")
local guild = GetGuildInfo("player")
AddData(name, class, level, guild)
RememberByUnit("player", name, class)
elseif event == "FRIENDLIST_UPDATE" then elseif event == "FRIENDLIST_UPDATE" then
for i = 1, GetNumFriends() do for i = 1, GetNumFriends() do
local info = C_FriendList.GetFriendInfoByIndex(i) local info = C_FriendList.GetFriendInfoByIndex(i)
if info then if info then
local level = info.level > 0 and info.level or nil AddLevel(info.name, info.level)
AddData(info.name, info.classFilename, level)
end end
end end
elseif event == "GUILD_ROSTER_UPDATE" then elseif event == "GUILD_ROSTER_UPDATE" then
local guild = GetGuildInfo("player") local name, level, _
local name, class, level, _
for i = 1, GetNumGuildMembers() do for i = 1, GetNumGuildMembers() do
name, _, _, level, class = GetGuildRosterInfo(i) name, _, _, level = GetGuildRosterInfo(i)
class = L["class"][class] or nil AddLevel(name, level)
AddData(name, class, level, guild)
end end
elseif event == "RAID_ROSTER_UPDATE" then elseif event == "RAID_ROSTER_UPDATE" then
local name, class, level, unit, _ local name, level, _
for i = 1, GetNumRaidMembers() do for i = 1, GetNumRaidMembers() do
unit = "raid" .. i name, _, _, level = GetRaidRosterInfo(i)
name, _, _, level, class = GetRaidRosterInfo(i) AddLevel(name, level)
class = L["class"][class] or nil RememberByUnit("raid" .. i)
AddData(name, class, level)
RememberByUnit(unit, name, class)
end end
elseif event == "PARTY_MEMBERS_CHANGED" then elseif event == "PARTY_MEMBERS_CHANGED" then
local name, class, level, unit, guild local unit
for i = 1, GetNumPartyMembers() do for i = 1, GetNumPartyMembers() do
unit = "party" .. i unit = "party" .. i
class = UnitClassBase(unit) AddLevel(UnitName(unit), UnitLevel(unit))
name = UnitName(unit) RememberByUnit(unit)
level = UnitLevel(unit)
guild = GetGuildInfo(unit)
AddData(name, class, level, guild)
RememberByUnit(unit, name, class)
end end
elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then
for i = 1, C_FriendList.GetNumWhoResults() do for i = 1, C_FriendList.GetNumWhoResults() do
local info = C_FriendList.GetWhoInfo(i) local info = C_FriendList.GetWhoInfo(i)
if info then if info then
AddData(info.fullName, info.filename, info.level, info.fullGuildName) AddLevel(info.fullName, info.level)
end end
end end
@@ -128,13 +112,8 @@ libunitscan:SetScript("OnEvent", function()
or event == "NAME_PLATE_UNIT_ADDED" and arg1 or event == "NAME_PLATE_UNIT_ADDED" and arg1
or "mouseover" or "mouseover"
if UnitIsPlayer(scan) then if UnitIsPlayer(scan) then
local name = UnitName(scan) AddLevel(UnitName(scan), UnitLevel(scan))
local class = UnitClassBase(scan) RememberByUnit(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)
end end
end end
end) end)
+321 -238
View File
@@ -9,7 +9,7 @@ pfUI:RegisterModule("actionbar", function ()
local rawborder, border = GetBorderSize("actionbars") local rawborder, border = GetBorderSize("actionbars")
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel() 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 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 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 if f.Hide then f:Hide() end
end end
-- also abbreviate mouse buttons -- the abbreviated text of the key bound to a command, mouse buttons included
local OrigGetBindingText = GetBindingText local mouse_abbrevs = {
local function GetBindingText(msg, mod, abbrev) [_G.KEY_BUTTON3] = "MB3",
local txt = OrigGetBindingText(msg, mod, abbrev) [_G.KEY_BUTTON4] = "MB4",
if abbrev then [_G.KEY_BUTTON5] = "MB5",
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON3")], "MB3") [_G.KEY_MOUSEWHEELDOWN] = "MWD",
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON4")], "MB4") [_G.KEY_MOUSEWHEELUP] = "MWU",
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") 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 end
return txt return text
end end
kill(MainMenuBar) kill(MainMenuBar)
@@ -83,33 +86,34 @@ pfUI:RegisterModule("actionbar", function ()
_G.SHOW_MULTI_ACTIONBAR_3 = nil _G.SHOW_MULTI_ACTIONBAR_3 = nil
_G.SHOW_MULTI_ACTIONBAR_4 = nil _G.SHOW_MULTI_ACTIONBAR_4 = nil
-- events that provide special updater functions -- events that refresh one part of every visible button -> [event] = pass.
local special_events = { -- Page changes only touch the main bar, whose pager handles them.
["ACTIONBAR_UPDATE_COOLDOWN"] = true, local pass_events = {
["ACTIONBAR_UPDATE_USABLE"] = true, ["ACTIONBAR_UPDATE_COOLDOWN"] = "cooldown",
["ACTIONBAR_UPDATE_STATE"] = true, -- 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 -- fires on every ammo shot / consumable use, but only counts, equipped
-- state, cooldowns and usability can change from it -- 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 = { local global_events = {
-- slot/button updates
["PLAYER_ENTERING_WORLD"] = true, ["PLAYER_ENTERING_WORLD"] = true,
["ACTIONBAR_SLOT_CHANGED"] = 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 -- 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") self.equipped:SetShown(IsEquippedAction(sid) and C.bars.showequipped == "1")
end end
local start, duration, enable, castable, autocast, token -- the keybind text, which an empty hidden button hides as well. The binding
local grid, sid, id, bar, active, texture, _ -- 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) local function ButtonSlotUpdate(self)
if not self then return end
self = self or this
sid = self.id -- 1 to 120 sid = self.id -- 1 to 120
-- reset shared variables -- reset shared variables
castable, autocast, token = nil, nil, nil castable, autocast, token = nil, nil, nil
-- set the own ID for compatibility to some vanilla addons -- 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 grid = self.bar == 12 and showgrid_pet or showgrid
if self.bar == 11 then if self.bar == 11 then
-- stance button -- stance button
bar = self.bar texture, _, active = GetShapeshiftFormInfo(sid)
id = sid
texture, _, active = GetShapeshiftFormInfo(id)
elseif self.bar == 12 then elseif self.bar == 12 then
-- pet button -- pet button
bar = self.bar _, _, texture, token, active, castable, autocast = GetPetActionInfo(sid)
id = sid
_, _, texture, token, active, castable, autocast = GetPetActionInfo(id)
texture = token and _G[texture] or texture texture = token and _G[texture] or texture
else else
active = IsCurrentAction(sid) or IsAutoRepeatAction(sid) active = IsCurrentAction(sid) or IsAutoRepeatAction(sid)
texture = GetActionTexture(sid) texture = GetActionTexture(sid)
bar = GetActiveBar()
id = self.bar == 1 and self.slot or sid-((self.bar)-1)*12
end end
if not self.showempty and self.backdrop and not texture and grid == 0 then 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 self.hide = nil
end 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) ButtonInventoryUpdate(self)
if self.bar ~= 11 and self.bar ~= 12 then if self.bar ~= 11 and self.bar ~= 12 then
@@ -457,6 +496,8 @@ pfUI:RegisterModule("actionbar", function ()
else else
self.macro:SetText("") self.macro:SetText("")
end end
ButtonProcUpdate(self)
end end
-- icon -- icon
@@ -481,14 +522,8 @@ pfUI:RegisterModule("actionbar", function ()
self.autocastable:SetShown(castable and C.bars.showcastable == "1") self.autocastable:SetShown(castable and C.bars.showcastable == "1")
end end
-- keybinds ButtonActiveUpdate(self, active, true)
if not self.hide and self.bar == 1 and self.bar ~= 11 and self.bar ~= 12 then ButtonKeybindUpdate(self)
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
end end
local sid, usable, oom, _ local sid, usable, oom, _
@@ -498,8 +533,9 @@ pfUI:RegisterModule("actionbar", function ()
if self.bar == 11 then if self.bar == 11 then
_, _, _, usable = GetShapeshiftFormInfo(sid) _, _, _, usable = GetShapeshiftFormInfo(sid)
oom = nil
elseif self.bar == 12 then elseif self.bar == 12 then
usable = true usable, oom = true, nil
else else
usable, oom = IsUsableAction(sid) usable, oom = IsUsableAction(sid)
end end
@@ -528,25 +564,22 @@ pfUI:RegisterModule("actionbar", function ()
end end
end end
local function ButtonRangeUpdate(self) -- Keeps the range check of the slot a shown action button displays
self = self or this -- 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 button.rangeslot then C_ActionBar.EnableActionRangeCheck(button.rangeslot, false) end
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 slot then C_ActionBar.EnableActionRangeCheck(slot, true) end
if not self.outofrange then button.rangeslot = slot
self.outofrange = true button.outofrange = nil
ButtonUsableUpdate(self)
end
elseif self.outofrange then
self.outofrange = nil
ButtonUsableUpdate(self)
end
end end
local start, duration, enable local start, duration, enable
local function ButtonCooldownUpdate(button) local function ButtonCooldownUpdate(button)
if not button then return end
if button.bar == 11 then if button.bar == 11 then
start, duration, enable = GetShapeshiftFormCooldown(button.id) start, duration, enable = GetShapeshiftFormCooldown(button.id)
elseif button.bar == 12 then elseif button.bar == 12 then
@@ -556,183 +589,205 @@ pfUI:RegisterModule("actionbar", function ()
end end
-- Nil-protect: GetActionCooldown can return nil during macro parsing/indexing -- Nil-protect: GetActionCooldown can return nil during macro parsing/indexing
if start and duration then if not start or not duration then return end
CooldownFrame_SetTimer(button.cd, start, duration, enable or 1) enable = enable or 1
end
-- 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 end
local _, active local _, active
local function ButtonIsActiveUpdate(button) local function ButtonIsActiveUpdate(button)
if not button then return end
if button.bar == 11 then if button.bar == 11 then
_, _, active, _ = GetShapeshiftFormInfo(button.id) _, _, active = GetShapeshiftFormInfo(button.id)
elseif button.bar == 12 then elseif button.bar == 12 then
_, _, _, _, active, _, _ = GetPetActionInfo(button.id) _, _, _, _, active = GetPetActionInfo(button.id)
else else
active = IsCurrentAction(button.id) or IsAutoRepeatAction(button.id) active = IsCurrentAction(button.id) or IsAutoRepeatAction(button.id)
end end
-- active border ButtonActiveUpdate(button, active)
if active then
if not button.border_active then
button.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
button.border_active = true
end end
button.active:Show() -- everything of a button; the slot update covers its active state
else
if button.border_active then
button.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
button.border_active = nil
end
button.active:Hide()
end
end
local function ButtonFullUpdate(button) local function ButtonFullUpdate(button)
if not button then return end if not button then return end
ButtonSlotUpdate(button) ButtonSlotUpdate(button)
ButtonRangeUpdate(button)
ButtonUsableUpdate(button) ButtonUsableUpdate(button)
ButtonCooldownUpdate(button) ButtonCooldownUpdate(button)
ButtonIsActiveUpdate(button)
end end
local function BarsEvent(self) -- the pfActionBar frame handles the events of every button; its array part
self = self or this -- 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 -- refresh only specific slots
if event == "ACTIONBAR_SLOT_CHANGED" and arg1 and arg1 ~= 0 then if event == "ACTIONBAR_SLOT_CHANGED" and arg1 and arg1 ~= 0 then
updatecache[arg1] = true QueueSlot(arg1)
return return
end end
-- run special refresh functions on next update -- refresh one part of every visible button
if special_events[event] then if pass_events[event] then
eventcache[event] = true QueuePass(pass_events[event])
return return
end end
-- handle aura events -- auras and forms change the stance buttons
if aura_events[event] then if aura_events[event] then
for j=1,12 do for j = 1, bars[11].count do
if self[11] and self[11][j] then QueueSlot(bars[11][j].slot)
updatecache[self[11][j].slot] = true
end
end end
return return
end end
-- handle pet events -- handle pet events
if pet_events[event] then if pet_events[event] then
for j=1,12 do for j = 1, bars[12].count do
if self[12] and self[12][j] then QueueSlot(bars[12][j].slot)
updatecache[self[12][j].slot] = true
end
end end
return return
end end
-- handle global events -- refresh every button and its keybind
for id in pairs(buttoncache) do if global_events[event] then
updatecache[id] = true QueuePass("bindings")
end for slot in pairs(buttoncache) do
end QueueSlot(slot)
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 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)
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. -- 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 -- A registration keeps its kind, so these have to go in unit-filtered from
-- the start -- RegisterUnitEvent over a plain registration stays plain. -- the start -- RegisterUnitEvent over a plain registration stays plain.
@@ -750,17 +805,18 @@ pfUI:RegisterModule("actionbar", function ()
end end
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(global_events) do RegisterBarEvent(event) end
for event in pairs(aura_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 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 -- refresh actionbar buttons on event
bars:SetScript("OnEvent", BarsEvent) bars:SetScript("OnEvent", BarsEvent)
-- update actionbar buttons
bars:SetScript("OnUpdate", BarsUpdate)
-- enable bar paging via secure functions -- enable bar paging via secure functions
local function EnablePaging(bar) local function EnablePaging(bar)
if not bar.pager then if not bar.pager then
@@ -776,9 +832,8 @@ pfUI:RegisterModule("actionbar", function ()
local active = GetActiveBar() local active = GetActiveBar()
for i=1,12 do for i=1,12 do
local id = i + (active-1)*12 bar[i].id = i + (active-1)*12
bar[i].id = id QueueSlot(i)
updatecache[i] = true
end end
end) end)
end end
@@ -1006,6 +1061,9 @@ pfUI:RegisterModule("actionbar", function ()
-- set a static slot -- set a static slot
f.slot = id 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 -- cooldown
f.cd = CreateCooldown(f, "spell", C.bars, "timers", f:GetName() .. "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:SetAllPoints()
f.active:Hide() 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 -- add to buttoncache
buttoncache[id] = f buttoncache[id] = f
end end
@@ -1168,6 +1243,7 @@ pfUI:RegisterModule("actionbar", function ()
-- don't process empty bars -- don't process empty bars
if buttons == 0 then if buttons == 0 then
bars[i].count = 0
bars[i]:Hide() bars[i]:Hide()
return return
end end
@@ -1186,6 +1262,11 @@ pfUI:RegisterModule("actionbar", function ()
local init = not bars[i] local init = not bars[i]
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent) bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent)
bars[i]:SetID(i) 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 -- autohide
if autohide == "1" then if autohide == "1" then
@@ -1201,18 +1282,12 @@ pfUI:RegisterModule("actionbar", function ()
-- only show when pet actions exists -- only show when pet actions exists
bars[i]:SetShown(PetHasActionBar() or pfUI.unlock and pfUI.unlock:IsShown()) 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 if init then
bars[i]:RegisterEvent("PET_BAR_UPDATE") bars[i]:RegisterEvent("PET_BAR_UPDATE")
bars[i]:SetScript("OnEvent", function() bars[i]:SetScript("OnEvent", function()
-- hide obsolete buttons this:SetShown(PetHasActionBar() or pfUI.unlock and pfUI.unlock:IsShown())
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)
end) end)
end end
@@ -1270,7 +1345,7 @@ pfUI:RegisterModule("actionbar", function ()
end end
-- refresh button -- refresh button
updatecache[bars[i][j].slot] = true QueueSlot(bars[i][j].slot)
end end
for j=buttons+1,12 do for j=buttons+1,12 do
@@ -1303,8 +1378,9 @@ pfUI:RegisterModule("actionbar", function ()
elseif i == 12 then -- pet elseif i == 12 then -- pet
bars[i]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border) bars[i]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
if init then
-- make stance bar dodge by default -- make stance bar dodge by default
bars[i]:SetScript("OnShow", function() bars[i]:HookScript("OnShow", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints() bars[11]:ClearAllPoints()
@@ -1322,6 +1398,7 @@ pfUI:RegisterModule("actionbar", function ()
UpdateMovable(bars[11], true) UpdateMovable(bars[11], true)
end end
end) end)
end
else -- others else -- others
bars[i]:SetPoint("TOP", 0, -i*50) bars[i]:SetPoint("TOP", 0, -i*50)
end end
@@ -1382,7 +1459,7 @@ pfUI:RegisterModule("actionbar", function ()
-- create actionbars -- create actionbars
pfUI.bars = bars pfUI.bars = bars
pfUI.bars.update = updatecache pfUI.bars.QueueSlot = QueueSlot
pfUI.bars.buttons = buttoncache pfUI.bars.buttons = buttoncache
pfUI.bars.ButtonFullUpdate = ButtonFullUpdate pfUI.bars.ButtonFullUpdate = ButtonFullUpdate
pfUI.bars.ButtonEnter = ButtonEnter pfUI.bars.ButtonEnter = ButtonEnter
@@ -1392,13 +1469,13 @@ pfUI:RegisterModule("actionbar", function ()
if not typ then if not typ then
showgrid = state showgrid = state
for id in pairs(buttoncache) do for slot in pairs(buttoncache) do
updatecache[id] = true QueueSlot(slot)
end end
elseif typ == "PET" then elseif typ == "PET" then
showgrid_pet = state showgrid_pet = state
for slot=133,142 do for slot=133,142 do
updatecache[slot] = true QueueSlot(slot)
end end
end end
end end
@@ -1411,6 +1488,12 @@ pfUI:RegisterModule("actionbar", function ()
pfUI.bars:UpdateConfig() 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) -- Localize custom keybinds for additional actionbars (see Bindings.xml)
local names = { local names = {
["PAGING"] = T["Paging Actionbar"], ["PAGING"] = T["Paging Actionbar"],
@@ -1492,7 +1575,7 @@ pfUI:RegisterModule("actionbar", function ()
if newID then if newID then
reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID) reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID)
end end
updatecache[slot] = true QueueSlot(slot)
end end
end end
@@ -1502,7 +1585,7 @@ pfUI:RegisterModule("actionbar", function ()
reagent_counts[itemID] = C_Item.GetItemCount(itemID) reagent_counts[itemID] = C_Item.GetItemCount(itemID)
end end
for slot in pairs(reagent_slots) do for slot in pairs(reagent_slots) do
updatecache[slot] = true QueueSlot(slot)
end end
end end
+30 -16
View File
@@ -89,42 +89,56 @@ pfUI:RegisterModule("bags", function ()
pfUI.bag.delay = { UpdateBag = {} } pfUI.bag.delay = { UpdateBag = {} }
pfUI.bag:SetScript("OnUpdate", function() -- run the updates queued by events, batched into one pass per 0.1s
-- update delayed ones every 0.1s local function FlushDelayed()
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end local delay = pfUI.bag.delay
delay.pending = nil
if this.delay.RefreshSpells then if delay.RefreshSpells then
this.delay.RefreshSpells = nil delay.RefreshSpells = nil
pfUI.bag:RefreshSpells() pfUI.bag:RefreshSpells()
end end
if this.delay.CheckFullUpdate then if delay.CheckFullUpdate then
this.delay.CheckFullUpdate = nil delay.CheckFullUpdate = nil
pfUI.bag:CheckFullUpdate() pfUI.bag:CheckFullUpdate()
end end
if this.delay.UpdateCooldowns then if delay.UpdateCooldowns then
this.delay.UpdateCooldowns = nil delay.UpdateCooldowns = nil
pfUI.bag:UpdateCooldowns() pfUI.bag:UpdateCooldowns()
end end
if this.delay.UpdateItemLock then if delay.UpdateItemLock then
this.delay.UpdateItemLock = nil delay.UpdateItemLock = nil
pfUI.bag:UpdateItemLock() pfUI.bag:UpdateItemLock()
end end
if this.delay.UpdateSearch then if delay.UpdateSearch then
this.delay.UpdateSearch = nil delay.UpdateSearch = nil
pfUI.bag:UpdateSearch() pfUI.bag:UpdateSearch()
end end
for bag in pairs(this.delay.UpdateBag) do for bag in pairs(delay.UpdateBag) do
this.delay.UpdateBag[bag] = nil delay.UpdateBag[bag] = nil
pfUI.bag:UpdateBag(bag) pfUI.bag:UpdateBag(bag)
end 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() pfUI.bag:SetScript("OnEvent", function()
ScheduleFlush()
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
pfUI.bag:CreateBags() pfUI.bag:CreateBags()
pfUI.bag.right:Hide() pfUI.bag.right:Hide()
+24 -15
View File
@@ -130,15 +130,14 @@ pfUI:RegisterModule("buffwatch", function ()
GameTooltip:Hide() GameTooltip:Hide()
end end
local function StatusBarOnUpdate() -- an enabled text binding writes every frame, shown or not
local remaining = this.endtime - GetTime() local function StatusBarOnShow()
this.bar:SetValue(remaining > 0 and remaining or 0) this.timer:Enable()
this.parent:RefreshPosition()
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
this.time:SetText(remaining > 0 and GetColoredTimeString(remaining) or "")
end end
local function StatusBarRefreshParent() local function StatusBarOnHide()
this.timer:Disable()
this.parent:RefreshPosition() this.parent:RefreshPosition()
end end
@@ -183,6 +182,16 @@ pfUI:RegisterModule("buffwatch", function ()
frame.time:SetTextColor(1,1,1,1) frame.time:SetTextColor(1,1,1,1)
frame.time:SetJustifyH("RIGHT") 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 = frame:CreateTexture(nil, "OVERLAY")
frame.icon:SetSize(height, height) frame.icon:SetSize(height, height)
frame.icon:SetPoint("LEFT", frame, "LEFT", 0, 0) frame.icon:SetPoint("LEFT", frame, "LEFT", 0, 0)
@@ -195,9 +204,8 @@ pfUI:RegisterModule("buffwatch", function ()
frame.stacks:SetJustifyV("CENTER") frame.stacks:SetJustifyV("CENTER")
frame.parent = parent frame.parent = parent
frame:SetScript("OnUpdate", StatusBarOnUpdate) frame:SetScript("OnShow", StatusBarOnShow)
frame:SetScript("OnShow", StatusBarRefreshParent) frame:SetScript("OnHide", StatusBarOnHide)
frame:SetScript("OnHide", StatusBarRefreshParent)
frame:RegisterForClicks("LeftButtonUp", "RightButtonUp") frame:RegisterForClicks("LeftButtonUp", "RightButtonUp")
frame:SetScript("OnClick", StatusBarOnClick) frame:SetScript("OnClick", StatusBarOnClick)
@@ -261,7 +269,6 @@ pfUI:RegisterModule("buffwatch", function ()
frame.bars[bar].id = data[2] frame.bars[bar].id = data[2]
frame.bars[bar].unit = frame.unit frame.bars[bar].unit = frame.unit
frame.bars[bar].type = frame.type 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 -- update max duration the cached remaining values is less than
-- the real one, indicates a buff renewal -- the real one, indicates a buff renewal
@@ -313,10 +320,12 @@ pfUI:RegisterModule("buffwatch", function ()
frame.bars[bar].icon:SetTexture(data[4]) frame.bars[bar].icon:SetTexture(data[4])
end end
-- cache maxduration -- the bar spans the longest time left seen for this aura; one without
if frame.bars[bar].cacheMaxDuration ~= frame.durations[uuid][2] then -- a time left (threshold -1) shows an empty bar and no time
frame.bars[bar].cacheMaxDuration = frame.durations[uuid][2] if data[1] > 0 then
frame.bars[bar].bar:SetMinMaxValues(0, frame.durations[uuid][2]) frame.bars[bar].duration:SetTimeFromEnd(GetTime() + data[1], frame.durations[uuid][2])
else
frame.bars[bar].duration:Reset()
end end
-- set stacks -- set stacks
+120 -159
View File
@@ -5,37 +5,68 @@ pfUI:RegisterModule("castbar", function ()
local rawborder, default_border = GetBorderSize("unitframes") local rawborder, default_border = GetBorderSize("unitframes")
local cbtexture = pfUI.media[C.appearance.castbar.texture] local cbtexture = pfUI.media[C.appearance.castbar.texture]
-- Helper function for castbar timer formatting -- Castbar timer text: 1 decimal rounded half up (matches Blizzard spellbook
local function FormatCastbarTime(value) -- display), or 2 decimals (default)
local formatter = C_StringUtil.CreateNumericRuleFormatter()
if C.unitframes.castbardecimals == "1" then if C.unitframes.castbardecimals == "1" then
-- 1 decimal, round half up (matches Blizzard spellbook display) formatter:AddBreakpoint({ threshold = 0, step = .1, format = "%.1f" })
return string.format("%.1f", floor(value * 10 + 0.5) / 10)
else else
-- 2 decimals (default) formatter:AddBreakpoint({ threshold = 0, format = "%.2f" })
return string.format("%.2f", value)
end
end end
-- Clear cast state on the bar and start the fade-out. On a normal end the bar -- "cur / max": a cast counts up, a channel counts down
-- is left at its current fill (a completed cast is already ~full; nothing local castText = {
-- snaps to full, which used to flash for a frame when the next cast stamped { property = Enum.DurationTextBindingProperty.ElapsedDuration, formatter = formatter },
-- the bar). When `failed` is set for a cast that was actually in progress, the { property = Enum.DurationTextBindingProperty.TotalDuration, formatter = formatter },
-- bar flashes full red before fading — the cancelled-cast indicator. }
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 function ClearBar(cb, failed)
local wasActive = cb.endTime ~= nil if cb.expiry then cb.expiry:Cancel() end
cb.startTime, cb.endTime, cb.isChannel = nil, nil, nil 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.activeName, cb.spellID = nil, nil
cb.isTradeskill = nil cb.isTradeskill = nil
cb.tradeskillTotal, cb.tradeskillCompleted, cb.tradeskillSpellID = nil, nil, nil cb.tradeskillTotal, cb.tradeskillCompleted, cb.tradeskillSpellID = nil, nil, nil
cb.tradeskillSingleMs, cb.currentCraftStart = nil, nil cb.tradeskillSingle = nil
cb.lastMax = nil
cb.delay = 0 cb.delay = 0
if failed and wasActive then if cb.craftbar then cb.craftbar:Hide() end
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:IsShown() and not cb.fade:IsPlaying() then cb.fade:Play() end if cb:IsShown() and not cb.fade:IsPlaying() then cb.fade:Play() end
end end
@@ -55,40 +86,35 @@ pfUI:RegisterModule("castbar", function ()
-- fills continuously across the chain instead of resetting per craft. -- fills continuously across the chain instead of resetting per craft.
-- Also arms the per-craft spark — a thin marker that crosses the bar once -- 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). -- 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.tradeskillTotal = count
cb.tradeskillCompleted = 0 cb.tradeskillCompleted = 0
cb.tradeskillSpellID = cb.spellID cb.tradeskillSpellID = cb.spellID
local single = endMs - startMs cb.tradeskillSingle = single
cb.tradeskillSingleMs = single cb.craft:SetTimeFromStart(start, single)
cb.currentCraftStart = startMs cb.duration:SetTimeFromStart(start, single * count)
local mergedEnd = startMs + single * count ArmExpiry(cb)
cb.endTime = mergedEnd cb.craftbar:Show()
local duration = (mergedEnd - startMs) / 1000
cb.bar:SetMinMaxValues(0, duration)
cb.lastMax = duration
if cb.bar.spark then cb.bar.spark:Show() end
UpdateTradeskillLabel(cb) UpdateTradeskillLabel(cb)
end end
-- Mark the start of craft 2..N within an active merge (called from the -- 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 -- UNIT_SPELLCAST_START handler) and re-fit the bar's end to the crafts left.
-- edge of the bar.
local function StartTradeskillCraft(cb) local function StartTradeskillCraft(cb)
cb.currentCraftStart = GetTime() * 1000 local now = GetTime()
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0) local remaining = cb.tradeskillTotal - cb.tradeskillCompleted
cb.endTime = cb.currentCraftStart + cb.tradeskillSingleMs * remaining cb.craft:SetTimeFromStart(now, cb.tradeskillSingle)
local duration = (cb.endTime - cb.startTime) / 1000 cb.duration:SetTimeSpan(cb.duration:GetStartTime(), now + cb.tradeskillSingle * remaining)
cb.bar:SetMinMaxValues(0, duration) ArmExpiry(cb)
cb.lastMax = duration
UpdateTradeskillLabel(cb) UpdateTradeskillLabel(cb)
end end
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate -- Stamp the bar with cast data and render text/icon/lag once. From here the
-- then animates the fill from this state without touching C_Spell. -- 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) local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank, noInterrupt)
cb.startTime = startMs cb.duration:SetTimeSpan(startMs / 1000, endMs / 1000)
cb.endTime = endMs cb.active = true
cb.isChannel = isChannel cb.isChannel = isChannel
cb.spellID = spellID cb.spellID = spellID
cb.activeName = name cb.activeName = name
@@ -124,27 +150,18 @@ pfUI:RegisterModule("castbar", function ()
cb.icon:Hide() cb.icon:Hide()
end end
local duration = (endMs - startMs) / 1000
if cb.showlag then if cb.showlag then
local _, _, lag = GetNetStats() 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() cb.bar.lag:Show()
else else
cb.bar.lag:Hide() cb.bar.lag:Hide()
end end
cb.bar:SetMinMaxValues(0, duration) cb.bar:SetTimerDuration(cb.duration, Enum.StatusBarInterpolation.Immediate,
cb.lastMax = duration isChannel and Enum.StatusBarTimerDirection.RemainingTime or Enum.StatusBarTimerDirection.ElapsedTime)
UpdateTimerText(cb)
-- Prime the fill on this frame. StampBar otherwise leaves the previous ArmExpiry(cb)
-- 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
end end
-- One-shot poll: read C_Spell for the bar's unit, stamp or clear. Called -- 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:SetFrameLevel(8)
cb.unitstr = unitstr 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 -- icon
cb.icon = CreateFrame("Frame", nil, cb) cb.icon = CreateFrame("Frame", nil, cb)
@@ -210,7 +231,7 @@ pfUI:RegisterModule("castbar", function ()
CreateBackdropShadow(cb.bar) CreateBackdropShadow(cb.bar)
-- text left -- 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:ClearAllPoints()
cb.bar.left:SetPoint("TOPLEFT", cb.bar, "TOPLEFT", 3 + C.castbar[unitstr].txtleftoffx, C.castbar[unitstr].txtleftoffy) 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) 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") cb.bar.left:SetJustifyH(C.castbar[unitstr].namealign or "LEFT")
-- text right -- 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:ClearAllPoints()
cb.bar.right:SetPoint("TOPLEFT", cb.bar, "TOPLEFT", 3 + C.castbar[unitstr].txtrightoffx, C.castbar[unitstr].txtrightoffy) 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) 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:SetFont(font, font_size, "OUTLINE")
cb.bar.right:SetJustifyH(C.castbar[unitstr].timealign or "RIGHT") 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 = cb.bar:CreateTexture(nil, "OVERLAY")
cb.bar.lag:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0) cb.bar.lag:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0)
cb.bar.lag:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", 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 -- 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 -- 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 -- moves 5x faster than the main fill). It rides the edge of an invisible
-- while a merge is active; hidden otherwise. -- fill that follows cb.craft, shown while a merge is active, and snaps
cb.bar.spark = cb.bar:CreateTexture(nil, "OVERLAY") -- back to the left each time StartTradeskillCraft restarts the craft.
cb.bar.spark:SetColorTexture(1, 1, 1, 0.8) cb.craftbar = CreateFrame("StatusBar", nil, cb.bar)
cb.bar.spark:SetWidth(2) cb.craftbar:SetAllPoints(cb.bar)
cb.bar.spark:SetPoint("TOP", cb.bar, "TOPLEFT", 0, 0) cb.craftbar:SetColorFill(1, 1, 1, 0)
cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0) cb.craftbar:SetTimerDuration(cb.craft, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
cb.bar.spark:Hide() cb.craftbar:Hide()
-- Fade-out after a cast ends. The bar is hidden while idle, so its OnUpdate cb.craftbar.spark = cb.craftbar:CreateTexture(nil, "OVERLAY")
-- only runs during a cast; unlock mode keeps it shown for dragging. cb.craftbar.spark:SetColorTexture(1, 1, 1, 0.8)
local function unlocked() return cb.drag and cb.drag:IsShown() end 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() cb.fade = cb:CreateAnimationGroup()
local fade = cb.fade:CreateAnimation("Alpha") local fade = cb.fade:CreateAnimation("Alpha")
fade:SetChange(-1) fade:SetChange(-1)
fade:SetDuration(.4) fade:SetDuration(.4)
cb.fade:SetScript("OnFinished", function() cb.fade:SetScript("OnFinished", function()
if not unlocked() then cb:Hide() end if not (cb.drag and cb.drag:IsShown()) 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
end) end)
-- Cast lifecycle, entirely on ClassicAPI's UNIT_SPELLCAST_* events. They -- Cast lifecycle, entirely on ClassicAPI's UNIT_SPELLCAST_* events. They
@@ -383,7 +349,7 @@ pfUI:RegisterModule("castbar", function ()
RefreshBar(this, arg5) RefreshBar(this, arg5)
if this.isTradeskill and (this.pendingTradeskillCount or 0) > 1 if this.isTradeskill and (this.pendingTradeskillCount or 0) > 1
and C.castbar.player.mergetradeskill == "1" then and C.castbar.player.mergetradeskill == "1" then
EnterTradeskillMerge(this, this.startTime, this.endTime, this.pendingTradeskillCount) EnterTradeskillMerge(this, this.pendingTradeskillCount)
end end
this.pendingTradeskillCount = nil this.pendingTradeskillCount = nil
end end
@@ -392,7 +358,7 @@ pfUI:RegisterModule("castbar", function ()
-- count and clear when the chain is done. A no-op for normal casts, -- count and clear when the chain is done. A no-op for normal casts,
-- which are cleared by UNIT_SPELLCAST_STOP. -- which are cleared by UNIT_SPELLCAST_STOP.
if this.tradeskillTotal and arg3 == this.tradeskillSpellID then 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 if this.tradeskillCompleted >= this.tradeskillTotal then
ClearBar(this) ClearBar(this)
else else
@@ -412,35 +378,30 @@ pfUI:RegisterModule("castbar", function ()
elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
ClearBar(this, true) ClearBar(this, true)
elseif event == "UNIT_SPELLCAST_INTERRUPTIBLE" or event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE" then 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) SetCastbarShield(this.bar, this.icon, event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE", this.isChannel)
end end
elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then
-- Pushback: a cast delayed later or a channel shortened. Following -- 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 -- this.delay for the +/- indicator, rather than a full restamp (which
-- would reset that total and re-render icon/text). ClassicAPI moves -- would reset that total and re-render icon/text). Pushback moves the
-- endMs (SpellDelayed_h bumps g_cast.endMs; the MSG_CHANNEL_UPDATE -- end time while the start stays put, so we diff the end. delay is a
-- co-hook rewrites g_channel.endMs) while startMs stays put, so we diff -- positive magnitude; UpdateTimerText signs it "+" for casts and "-"
-- endMs. delay is a positive magnitude; OnUpdate signs it "+" for casts -- for channels. Skipped during a tradeskill merge.
-- and "-" for channels. Skipped during a tradeskill merge. if this.active and not this.tradeskillTotal then
if this.endTime and not this.tradeskillTotal then local duration
local startMs, endMs
if this.isChannel then if this.isChannel then
local _, _, _, s, e = C_Spell.UnitChannelInfo(unit) duration = UnitChannelDuration(unit)
startMs, endMs = s, e
else else
local _, _, _, s, e = C_Spell.UnitCastingInfo(unit) duration = UnitCastingDuration(unit)
startMs, endMs = s, e
end end
if startMs and endMs then if duration then
local shift = this.isChannel and (this.endTime - endMs) or (endMs - this.endTime) local shift = duration:GetEndTime() - this.duration:GetEndTime()
this.delay = (this.delay or 0) + shift / 1000 this.delay = this.delay + (this.isChannel and -shift or shift)
this.startTime = startMs this.duration:Assign(duration)
this.endTime = endMs UpdateTimerText(this)
local newDuration = (endMs - startMs) / 1000 ArmExpiry(this)
this.bar:SetMinMaxValues(0, newDuration)
this.lastMax = newDuration
end end
end end
end end
+2 -14
View File
@@ -701,12 +701,6 @@ pfUI:RegisterModule("chat", function ()
local r,g,b = GetStringColor(C.chat.text.unknowncolor) local r,g,b = GetStringColor(C.chat.text.unknowncolor)
local unknowncolorhex = rgbhex(r,g,b) 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 -- the sender of the line AddMessage is reading, from its chat event
local lineSender, lineSenderClass, lineSenderRace, lineSenderSex local lineSender, lineSenderClass, lineSenderRace, lineSenderSex
@@ -727,10 +721,7 @@ pfUI:RegisterModule("chat", function ()
else else
_, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real) _, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real)
end end
if not class then if class then
class = GetUnitInfo(real)
end
if class and class ~= UNKNOWN then
color = "|c" .. PFUI_CLASS_COLORS[class].colorStr color = "|c" .. PFUI_CLASS_COLORS[class].colorStr
if C.chat.text.factionicons == "1" then if C.chat.text.factionicons == "1" then
icon = GetFactionIcon(raceKey) icon = GetFactionIcon(raceKey)
@@ -745,11 +736,8 @@ pfUI:RegisterModule("chat", function ()
local suffix = "" local suffix = ""
local level = C.chat.text.playerlevel == "1" and GetPlayerLevel(real) 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" suffix = " " .. rgbhex(GetDifficultyColor(level)) .. level .. "|r"
elseif level then
-- Show ?? for unknown levels (e.g. -1 from UnitLevel)
suffix = " |cffff0000??|r"
end end
if color == "" and suffix == "" then return link end if color == "" and suffix == "" then return link end
+1 -5
View File
@@ -1,7 +1,5 @@
pfUI:RegisterModule("chatcopy", function () pfUI:RegisterModule("chatcopy", function ()
local f = CreateFrame("Frame") EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:SetScript("OnEvent", function()
if not pfUI.chat then return end if not pfUI.chat then return end
local button = CreateFrame("Button", "pfChatCopyButton", pfUI.chat.left.panelTop) local button = CreateFrame("Button", "pfChatCopyButton", pfUI.chat.left.panelTop)
@@ -99,7 +97,5 @@ pfUI:RegisterModule("chatcopy", function ()
frame.scroll = scroll frame.scroll = scroll
end end
this:UnregisterAllEvents()
end) end)
end) end)
+1 -7
View File
@@ -2509,13 +2509,7 @@ pfUI:RegisterModule("combatlog", function ()
local buttonName, button, textWidth; local buttonName, button, textWidth;
local buttonIndex = 1; local buttonIndex = 1;
-- subtract the width of the dropdown button -- subtract the width of the dropdown button
local clogleft, clogright = COMBATLOG:GetRight(), COMBATLOG:GetLeft(); local maxWidth = COMBATLOG:GetUnscaledWidth() - 31;
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 additionalFilterButton = CombatLogQuickButtonFrame.AdditionalFilterButton; local additionalFilterButton = CombatLogQuickButtonFrame.AdditionalFilterButton;
+12 -9
View File
@@ -30,15 +30,15 @@ pfUI:RegisterModule("easteregg", function ()
scene.dtext:SetPoint("TOP", 0, -430) scene.dtext:SetPoint("TOP", 0, -430)
scene.dtext:SetText(info and info .. "\n\n" .. exit or exit) 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 -- Fade the night in. The scene keeps an alpha of 1, which comes back
-- alpha of 1, which comes back when the fade ends. -- when the fade ends.
scene.dusk = scene:CreateAnimationGroup() scene.dusk = scene:CreateAnimationGroup()
local fade = scene.dusk:CreateAnimation("Alpha") local fade = scene.dusk:CreateAnimation("Alpha")
fade:SetChange(-1) fade:SetToAlpha(1)
fade:SetDuration(.8) fade:SetDuration(.8)
scene:SetScript("OnShow", function() scene:SetScript("OnShow", function()
this.dusk:Play(true) this.dusk:Play()
end) end)
-- start over on the next AFK -- start over on the next AFK
@@ -159,7 +159,7 @@ pfUI:RegisterModule("easteregg", function ()
halloween.text:SetTextColor(1, .5, 0) halloween.text:SetTextColor(1, .5, 0)
-- Candlelight glows in from the screen edges. It flickers in uneven -- 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. -- never jumps.
local candle = CreateFrame("Frame", nil, halloween) local candle = CreateFrame("Frame", nil, halloween)
candle:SetAllPoints() candle:SetAllPoints()
@@ -169,16 +169,19 @@ pfUI:RegisterModule("easteregg", function ()
candle.flicker = candle:CreateAnimationGroup() candle.flicker = candle:CreateAnimationGroup()
candle.flicker:SetLooping("REPEAT") candle.flicker:SetLooping("REPEAT")
local steps = { local steps = {
{ .18, -.25 }, { .09, .1 }, { .22, -.2 }, { .14, .3 }, { .3, -.1 }, { .18, .75 }, { .09, .85 }, { .22, .65 }, { .14, .95 }, { .3, .85 },
{ .11, -.15 }, { .2, .2 }, { .16, -.3 }, { .26, .25 }, { .12, .15 }, { .11, .7 }, { .2, .9 }, { .16, .6 }, { .26, .85 }, { .12, 1 },
} }
local from = 1
for order, step in ipairs(steps) do for order, step in ipairs(steps) do
local duration, change = unpack(step) local duration, alpha = unpack(step)
local fade = candle.flicker:CreateAnimation("Alpha") local fade = candle.flicker:CreateAnimation("Alpha")
fade:SetOrder(order) fade:SetOrder(order)
fade:SetDuration(duration) fade:SetDuration(duration)
fade:SetChange(change) fade:SetFromAlpha(from)
fade:SetToAlpha(alpha)
fade:SetSmoothing("IN_OUT") fade:SetSmoothing("IN_OUT")
from = alpha
end end
-- Once the night has fallen, undead rise along the bottom one after -- Once the night has fallen, undead rise along the bottom one after
+79 -84
View File
@@ -84,11 +84,6 @@ pfUI:RegisterModule("energytick", function()
return return
end 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 rawborder, default_border = GetBorderSize("unitframes")
local cbtexture = pfUI.media[C.appearance.castbar.texture] local cbtexture = pfUI.media[C.appearance.castbar.texture]
@@ -128,8 +123,13 @@ pfUI:RegisterModule("energytick", function()
return true return true
end 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: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:RegisterEvent("PLAYER_ENTERING_WORLD")
energytick:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player") energytick:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player")
energytick:RegisterUnitEvent("UNIT_POWER_UPDATE", "player") energytick:RegisterUnitEvent("UNIT_POWER_UPDATE", "player")
@@ -138,21 +138,61 @@ pfUI:RegisterModule("energytick", function()
energytick:RegisterEvent("SPELLS_CHANGED") energytick:RegisterEvent("SPELLS_CHANGED")
energytick:RegisterEvent("PLAYER_AURAS_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() energytick:SetScript("OnEvent", function()
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
this.mode = "MANA" this.mode = "MANA"
this:Show()
elseif UnitPowerType("player") == Enum.PowerType.Energy and C.unitframes.player.energy == "1" then elseif UnitPowerType("player") == Enum.PowerType.Energy and C.unitframes.player.energy == "1" then
this.mode = "ENERGY" this.mode = "ENERGY"
this:Show()
else else
this.mode = nil this.mode = nil
this:Hide()
end 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 if event == "SPELLS_CHANGED" or event == "PLAYER_AURAS_CHANGED" then
energyRegenTimeMod = nil energyRegenTimeMod = nil
return
end end
-- a shift swaps the power the gains are measured on -- a shift swaps the power the gains are measured on
@@ -166,18 +206,17 @@ pfUI:RegisterModule("energytick", function()
local cost = C_Spell.GetSpellPowerCost(arg3) local cost = C_Spell.GetSpellPowerCost(arg3)
cost = cost and cost[1] cost = cost and cost[1]
if cost and cost.type == Enum.PowerType.Mana and cost.cost > 0 then if cost and cost.type == Enum.PowerType.Mana and cost.cost > 0 then
this.fsrSpell, this.fsrEnd = arg3, GetTime() + FIVE_SECOND_RULE this.fsrSpell, this.fsrGain = arg3, nil
this.fsrGain = nil ArmRule()
end end
return
end end
-- Unit::Update won't expire the rule while the spending spell still channels -- Unit::Update won't expire the rule while the spending spell still channels
if event == "UNIT_SPELLCAST_CHANNEL_STOP" and arg1 == "player" then if event == "UNIT_SPELLCAST_CHANNEL_STOP" and arg1 == "player" then
if this.fsrSpell and this.fsrSpell == arg3 then if this.fsrSpell and this.fsrSpell == arg3 then
this.fsrEnd, this.fsrGain = GetTime() + FIVE_SECOND_RULE, nil this.fsrGain = nil
ArmRule()
end end
return
end end
-- only the shown power: a cat druid's mana regen is not an energy gain -- 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 -- only a gain can be the tick; a spend never touches the phase
if diff > 0 and lockTick(this) then if diff > 0 and lockTick(this) then
-- a tick inside the window proves regen continues through it -- 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 this.fsrGain = true
end end
Sweep()
end
end end
-- phase is kept while hidden; OnUpdate catches up by whole periods UpdateRule()
if this.mode == "MANA" and power >= UnitPowerMax("player") then
this:Hide()
end
end
end) end)
energytick:SetScript("OnUpdate", function() energytick.fsr = C_DurationUtil.CreateDuration()
-- 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)
if fsrOverlay then if fsrOverlay then
-- classic style: a translucent shade on top of the power bar's own fill -- classic style: a translucent shade on top of the power bar's own fill,
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK") -- under the spark
energytick.fsrbar:SetColorTexture(1, 1, 1, .15) energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player.power.bar)
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0) energytick.fsrbar:SetAllPoints(energytick)
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0) energytick.fsrbar:SetColorFill(1, 1, 1, .15)
energytick:SetFrameLevel(energytick.fsrbar:GetFrameLevel() + 1)
else else
-- a real bar below the power bar, not an overlay on top of it -- the same -- 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 -- treatment nameplate castbars get relative to the health bar, so the
-- window can't be mistaken for missing mana -- window can't be mistaken for missing mana
energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player) energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player)
energytick.fsrbar:SetStatusBarTexture(cbtexture) 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("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:SetPoint("TOPRIGHT", pfUI.uf.player.power.bar, "BOTTOMRIGHT", 0, -default_border * 2)
energytick.fsrbar:SetHeight(tonumber(C.unitframes.player.fsrheight) or 4) energytick.fsrbar:SetHeight(tonumber(C.unitframes.player.fsrheight) or 4)
@@ -279,13 +260,27 @@ pfUI:RegisterModule("energytick", function()
CreateBackdrop(energytick.fsrbar, default_border) CreateBackdrop(energytick.fsrbar, default_border)
CreateBackdropShadow(energytick.fsrbar) CreateBackdropShadow(energytick.fsrbar)
end end
energytick.fsrbar:SetTimerDuration(energytick.fsr, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
energytick.fsrbar:Hide() 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 = energytick:CreateTexture(nil, "OVERLAY")
energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark") energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
energytick.spark:SetHeight(C.unitframes.player.pheight + 15) energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
energytick.spark:SetWidth(C.unitframes.player.pheight + 5) energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
energytick.spark:SetBlendMode("ADD") energytick.spark:SetBlendMode("ADD")
energytick.spark:SetPoint("CENTER", energytick:GetStatusBarTexture(), "RIGHT")
energytick.spark:SetAlpha(0)
local hookUpdateConfig = pfUI.uf.player.UpdateConfig local hookUpdateConfig = pfUI.uf.player.UpdateConfig
function pfUI.uf.player.UpdateConfig() function pfUI.uf.player.UpdateConfig()
+13 -26
View File
@@ -136,17 +136,19 @@ pfUI:RegisterModule("firstrun", function ()
local f = CreateFirstRunPage() 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."]) 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 = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Modern:SetSize(120, 20) f.Modern:SetSize(120, 20)
f.Modern:SetPoint("BOTTOM", -65, 100) f.Modern:SetPoint("BOTTOM", -65, 100)
f.Modern:SetTextColor(1,1,1) f.Modern:SetTextColor(1,1,1)
f.Modern:SetText("Modern") f.Modern:SetText("Modern")
f.Modern:SetScript("OnClick", function() f.Modern:SetScript("OnClick", function() LoadProfile("Modern") end)
_G["pfUI_config"] = CopyTable(pfUI_profiles["Modern"])
pfUI_init.selected_profile = "Modern"
pfUI:LoadConfig()
ReloadUI()
end)
SkinButton(f.Modern) SkinButton(f.Modern)
f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
@@ -154,12 +156,7 @@ pfUI:RegisterModule("firstrun", function ()
f.Nostalgia:SetPoint("BOTTOM", 65, 100) f.Nostalgia:SetPoint("BOTTOM", 65, 100)
f.Nostalgia:SetTextColor(1,1,1) f.Nostalgia:SetTextColor(1,1,1)
f.Nostalgia:SetText("Nostalgia") f.Nostalgia:SetText("Nostalgia")
f.Nostalgia:SetScript("OnClick", function() f.Nostalgia:SetScript("OnClick", function() LoadProfile("Nostalgia") end)
_G["pfUI_config"] = CopyTable(pfUI_profiles["Nostalgia"])
pfUI_init.selected_profile = "Nostalgia"
pfUI:LoadConfig()
ReloadUI()
end)
SkinButton(f.Nostalgia) SkinButton(f.Nostalgia)
f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
@@ -167,12 +164,7 @@ pfUI:RegisterModule("firstrun", function ()
f.Legacy:SetPoint("BOTTOM", 65, 75) f.Legacy:SetPoint("BOTTOM", 65, 75)
f.Legacy:SetTextColor(1,1,1) f.Legacy:SetTextColor(1,1,1)
f.Legacy:SetText("Legacy") f.Legacy:SetText("Legacy")
f.Legacy:SetScript("OnClick", function() f.Legacy:SetScript("OnClick", function() LoadProfile("Legacy") end)
_G["pfUI_config"] = CopyTable(pfUI_profiles["Legacy"])
pfUI_init.selected_profile = "Legacy"
pfUI:LoadConfig()
ReloadUI()
end)
SkinButton(f.Legacy) SkinButton(f.Legacy)
f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
@@ -180,12 +172,7 @@ pfUI:RegisterModule("firstrun", function ()
f.Slim:SetPoint("BOTTOM", -65, 75) f.Slim:SetPoint("BOTTOM", -65, 75)
f.Slim:SetTextColor(1,1,1) f.Slim:SetTextColor(1,1,1)
f.Slim:SetText("Slim") f.Slim:SetText("Slim")
f.Slim:SetScript("OnClick", function() f.Slim:SetScript("OnClick", function() LoadProfile("Slim") end)
_G["pfUI_config"] = CopyTable(pfUI_profiles["Slim"])
pfUI_init.selected_profile = "Slim"
pfUI:LoadConfig()
ReloadUI()
end)
SkinButton(f.Slim) SkinButton(f.Slim)
f.Slider = CreateFrame("Slider", "pfFirstRunWizardScaleSlider", f, "OptionsSliderTemplate") f.Slider = CreateFrame("Slider", "pfFirstRunWizardScaleSlider", f, "OptionsSliderTemplate")
@@ -196,14 +183,14 @@ pfUI:RegisterModule("firstrun", function ()
f.Slider:SetSize(240, 20) f.Slider:SetSize(240, 20)
f.Slider:SetPoint("BOTTOM", 0, 50) f.Slider:SetPoint("BOTTOM", 0, 50)
f.Slider:SetOrientation('HORIZONTAL') 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:SetValue(UIParent:GetScale())
f.Slider:SetScript("OnMouseUp", function() f.Slider:SetScript("OnMouseUp", function()
local scale = round(this:GetValue(),2) local scale = round(this:GetValue(),2)
SetCVar("uiScale", scale) SetCVar("uiScale", scale)
SetCVar("useUiScale", 1) SetCVar("useUiScale", 1)
UIParent:SetScale(scale)
this:SetValue(scale) this:SetValue(scale)
end) end)
+21 -139
View File
@@ -78,7 +78,7 @@ pfUI:RegisterModule("gui", function ()
-- basic frame -- basic frame
local frame = CreateFrame("Frame", nil, this) 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:SetHeight(22)
frame:SetPoint("TOPLEFT", this, "TOPLEFT", 5, (this.objectCount*-23)-5) frame:SetPoint("TOPLEFT", this, "TOPLEFT", 5, (this.objectCount*-23)-5)
@@ -130,7 +130,7 @@ pfUI:RegisterModule("gui", function ()
category = {} category = {}
category[config] = tostring(GetCVar(config)) category[config] = tostring(GetCVar(config))
ufunc = function() ufunc = function()
SetCVar(this:GetParent().config, this:GetParent().category[config]) SetCVar(config, category[config])
end end
end end
@@ -881,14 +881,6 @@ pfUI:RegisterModule("gui", function ()
"35:35", "35:35",
"40:40", "40:40",
}, },
["scaling"] = {
"0:" .. T["Off"],
"4:" .. T["Huge (PixelPerfect)"],
"5:" .. T["Large"],
"6:" .. T["Medium"],
"7:" .. T["Small"],
"8:" .. T["Tiny (PixelPerfect)"],
},
["abbrevnum"] = { ["abbrevnum"] = {
"0:" .. T["Full Numbers (4250)"], "0:" .. T["Full Numbers (4250)"],
"1:" .. T["Abbreviate 2 Decimals (4.25k)"], "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["Settings"], nil, nil, "header")
CreateConfig(nil, T["Language"], C.global, "language", "dropdown", pfUI.gui.dropdowns.languages) 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["Use Original Game Fonts"], C.global, "font_blizzard", "checkbox")
CreateConfig(nil, T["Enable Region Compatible Font"], C.global, "force_region", "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) 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, T["Frame Shadow Intensity"], C.appearance.border, "shadow_intensity", "dropdown", pfUI.gui.dropdowns.uf_debuff_indicator_size)
CreateConfig(nil) -- spacer CreateConfig(nil) -- spacer
CreateConfig(nil, T["Force Blizzard Borders (|cffffaaaaExperimental|r)"], C.appearance.border, "force_blizz", "checkbox") 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) -- spacer
CreateConfig(nil, T["Global Border Size"], C.appearance.border, "default", "dropdown", pfUI.gui.dropdowns.border) 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) 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) end)
-- Throttling Menu -- 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() CreateGUIEntry(T["Throttling"], T["Tooltips"], function()
local header = CreateConfig(nil, T["Tooltip Update Rate"], nil, nil, "header") local header = CreateConfig(nil, T["Tooltip Update Rate"], nil, nil, "header")
header:GetParent().objectCount = header:GetParent().objectCount - 1 header:GetParent().objectCount = header:GetParent().objectCount - 1
@@ -1846,67 +1788,6 @@ pfUI:RegisterModule("gui", function ()
end, true) end, true)
end) 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() CreateGUIEntry(T["Unit Frames"], T["General"], function()
CreateConfig(nil, T["Disable pfUI Unit Frames"], C.unitframes, "disable", "checkbox") CreateConfig(nil, T["Disable pfUI Unit Frames"], C.unitframes, "disable", "checkbox")
CreateConfig(nil, T["Animate Health And Power Bars"], C.unitframes, "animatebars", "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["Out Of Mana Color"], C.bars, "oomcolor", "color")
CreateConfig(U["bars"], T["Highlight Not Usable Spells"], C.bars, "showna", "checkbox") 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["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["Lock Actionbars"], "UVAR", "LOCK_ACTIONBAR", "checkbox")
CreateConfig(U["bars"], T["Always Allow Drag Via Shift Key"], C.bars, "shiftdrag", "checkbox") CreateConfig(U["bars"], T["Always Allow Drag Via Shift Key"], C.bars, "shiftdrag", "checkbox")
+1 -1
View File
@@ -41,7 +41,7 @@ pfUI:RegisterModule("marktracking", function ()
local function ShortenName(name, row) local function ShortenName(name, row)
if not name or name == "" then return "" end 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 if barWidth < 1 then barWidth = BAR_WIDTH end
local available = barWidth - 4 local available = barWidth - 4
if rm_showpct then available = available - 32 end if rm_showpct then available = available - 32 end
+4 -3
View File
@@ -111,15 +111,16 @@ pfUI:RegisterModule("minimap", function ()
-- Blink ten times between .9 and .1, then hide -- Blink ten times between .9 and .1, then hide
local highlight = this.highlight local highlight = this.highlight
highlight:SetAlpha(.9)
highlight.blink = highlight:CreateAnimationGroup() highlight.blink = highlight:CreateAnimationGroup()
for i = 1, 10 do for i = 1, 10 do
local down = highlight.blink:CreateAnimation("Alpha") local down = highlight.blink:CreateAnimation("Alpha")
down:SetChange(-.8) down:SetFromAlpha(.9)
down:SetToAlpha(.1)
down:SetDuration(.3) down:SetDuration(.3)
down:SetOrder(i * 2 - 1) down:SetOrder(i * 2 - 1)
local up = highlight.blink:CreateAnimation("Alpha") local up = highlight.blink:CreateAnimation("Alpha")
up:SetChange(.8) up:SetFromAlpha(.1)
up:SetToAlpha(.9)
up:SetDuration(.3) up:SetDuration(.3)
up:SetOrder(i * 2) up:SetOrder(i * 2)
end end
+71 -9
View File
@@ -3,20 +3,53 @@ pfUI:RegisterModule("mirrortimers", function ()
local fontsize = tonumber(C.global.font_size) local fontsize = tonumber(C.global.font_size)
local height = fontsize * 1.3 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 for i = 1, MIRRORTIMER_NUMTIMERS do
local frame = _G["MirrorTimer"..i] local frame = _G["MirrorTimer"..i]
frame:GetRegions():Hide() frame:GetRegions():Hide()
frame:SetWidth(200) frame:SetWidth(200)
frame:SetHeight(height) frame:SetHeight(height)
frame:SetScript("OnUpdate", function() frame:SetScript("OnUpdate", nil)
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:ClearAllPoints() frame:ClearAllPoints()
frame:SetPoint("TOP", UIParent, "TOP", 0, -120-(i-1)*(height + 10)) 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 = frame:CreateFontString(nil, "OVERLAY")
frame.text:SetFont(font, fontsize, "OUTLINE") frame.text:SetFont(font, fontsize, "OUTLINE")
frame.text:SetPoint("CENTER", frame.statusbar, "CENTER", 0, 0) 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
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) end)
+77 -116
View File
@@ -82,7 +82,6 @@ pfUI:RegisterModule("nameplates", function ()
local threatMemory = {} -- guid -> true once the mob targeted the player local threatMemory = {} -- guid -> true once the mob targeted the player
local raidGuidCache = {} -- guid -> name of the player's group local raidGuidCache = {} -- guid -> name of the player's group
local offtanks = {} local offtanks = {}
local activeCasts = {} -- overlay -> true while its castbar sweeps
local zooming = {} -- overlay -> true while its target zoom animates local zooming = {} -- overlay -> true while its target zoom animates
local auraSlots = {} -- reusable GetAuraSlots buffer for the per-plate aura scan local auraSlots = {} -- reusable GetAuraSlots buffer for the per-plate aura scan
local targetGuid local targetGuid
@@ -100,6 +99,7 @@ pfUI:RegisterModule("nameplates", function ()
-- Config caching -- Config caching
-- ============================================================================ -- ============================================================================
local cfg = {} local cfg = {}
local castFormatter = C_StringUtil.CreateNumericRuleFormatter()
local debufffilter, debufflist local debufffilter, debufflist
local function DebuffFilterPopulate() local function DebuffFilterPopulate()
@@ -158,7 +158,13 @@ pfUI:RegisterModule("nameplates", function ()
cfg.showguildname = N.showguildname == "1" cfg.showguildname = N.showguildname == "1"
cfg.abbrevname = C.unitframes.abbrevname == "1" cfg.abbrevname = C.unitframes.abbrevname == "1"
cfg.friendclassnamec = N.friendclassnamec == "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.fullhealth = N.fullhealth == "1"
cfg.keeptarget = N.target == "1" cfg.keeptarget = N.target == "1"
cfg.critters = N.critters == "1" cfg.critters = N.critters == "1"
@@ -187,8 +193,6 @@ pfUI:RegisterModule("nameplates", function ()
cfg.rightclick = N.rightclick == "1" cfg.rightclick = N.rightclick == "1"
cfg.hlr, cfg.hlg, cfg.hlb, cfg.hla = GetStringColor(N.highlightcolor) cfg.hlr, cfg.hlg, cfg.hlb, cfg.hla = GetStringColor(N.highlightcolor)
cfg.br, cfg.bg, cfg.bb, cfg.ba = GetStringColor(C.appearance.border.color) 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 local c = combatstate
c.CASTING.r, c.CASTING.g, c.CASTING.b, c.CASTING.a = GetStringColor(N.combatcasting) 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 -- 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 -- (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 -- 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 function PollCastInfo(unit)
local name, _, texture, startMs, endMs, _, _, noInterrupt, spellID = C_Spell.UnitCastingInfo(unit) local name, _, texture, _, _, _, _, noInterrupt, spellID = C_Spell.UnitCastingInfo(unit)
local isChannel local duration, isChannel
if not name then if name then
name, _, texture, startMs, endMs, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(unit) duration = UnitCastingDuration(unit)
else
name, _, texture, _, _, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(unit)
duration = name and UnitChannelDuration(unit)
isChannel = true isChannel = true
end end
if not name or not startMs or not endMs then return nil end if not duration then return nil end
return { return {
spellName = name, spellName = name,
spellID = spellID, spellID = spellID,
icon = texture, icon = texture,
startTime = startMs / 1000, duration = duration,
endTime = endMs / 1000,
duration = (endMs - startMs) / 1000,
isChannel = isChannel, isChannel = isChannel,
noInterrupt = noInterrupt, noInterrupt = noInterrupt,
} }
@@ -279,7 +284,7 @@ pfUI:RegisterModule("nameplates", function ()
local mobTargetGuid = UnitGUID(target) local mobTargetGuid = UnitGUID(target)
local hasTarget = mobTargetGuid and mobTargetGuid ~= NULL_GUID local hasTarget = mobTargetGuid and mobTargetGuid ~= NULL_GUID
local info = castState[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") local targetingPlayer = hasTarget and UnitIsUnit(target, "player")
if targetingPlayer then if targetingPlayer then
@@ -487,8 +492,7 @@ pfUI:RegisterModule("nameplates", function ()
local elite = UnitClassification(unit) local elite = UnitClassification(unit)
if level <= 0 then if level <= 0 then
if plate.player then if plate.player then
local _, dblevel = GetUnitInfo(plate.rawname) level = GetPlayerLevel(plate.rawname) or level
if dblevel and dblevel > 0 then level = dblevel end
else else
-- UnitLevel answers -1 exactly where the engine shows the skull: a -- UnitLevel answers -1 exactly where the engine shows the skull: a
-- world boss, or a hostile ten or more levels above the player -- world boss, or a hostile ten or more levels above the player
@@ -819,8 +823,7 @@ pfUI:RegisterModule("nameplates", function ()
end end
-- ============================================================================ -- ============================================================================
-- Animation driver: castbar sweeps and the target zoom. Shown only while -- Animation driver: the target zoom. Shown only while a plate zooms.
-- something moves.
-- ============================================================================ -- ============================================================================
local driver = CreateFrame("Frame", nil, UIParent) local driver = CreateFrame("Frame", nil, UIParent)
driver:Hide() driver:Hide()
@@ -841,98 +844,8 @@ pfUI:RegisterModule("nameplates", function ()
driver:Show() driver:Show()
end 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() driver:SetScript("OnUpdate", function()
local now = GetTime() 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 for plate in pairs(zooming) do
local f = (now - plate.zoomStart) / ZOOM_TIME local f = (now - plate.zoomStart) / ZOOM_TIME
if f >= 1 then if f >= 1 then
@@ -941,10 +854,57 @@ pfUI:RegisterModule("nameplates", function ()
end end
SetZoom(plate, plate.zoomIn and f or 1 - f) SetZoom(plate, plate.zoomIn and f or 1 - f)
end end
if not next(zooming) then this:Hide() end
if not next(activeCasts) and not next(zooming) then this:Hide() end
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) -- Cast events (per plate)
-- ============================================================================ -- ============================================================================
@@ -1276,12 +1236,17 @@ pfUI:RegisterModule("nameplates", function ()
end end
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:SetPoint("RIGHT", castbar, "LEFT", -4, 0)
castbar.text:SetNonSpaceWrap(false) castbar.text:SetNonSpaceWrap(false)
castbar.text:SetTextColor(1,1,1,.5) 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:SetPoint("CENTER", castbar, "CENTER")
castbar.spell:SetNonSpaceWrap(false) castbar.spell:SetNonSpaceWrap(false)
castbar.spell:SetTextColor(1,1,1,1) castbar.spell:SetTextColor(1,1,1,1)
@@ -1372,8 +1337,6 @@ pfUI:RegisterModule("nameplates", function ()
nameplate.castbar:SetStatusBarTexture(cbtexture or hptexture) nameplate.castbar:SetStatusBarTexture(cbtexture or hptexture)
local cbr, cbg, cbb, cba = GetStringColor(C.appearance.castbar.castbarcolor) local cbr, cbg, cbb, cba = GetStringColor(C.appearance.castbar.castbarcolor)
nameplate.castbar:SetStatusBarColor(cbr, cbg, cbb, cba) 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) CreateBackdrop(nameplate.castbar, default_border)
nameplate.castbar.text:SetFont(font, font_size, "OUTLINE") nameplate.castbar.text:SetFont(font, font_size, "OUTLINE")
@@ -1652,8 +1615,6 @@ pfUI:RegisterModule("nameplates", function ()
local plate = info and plateByGuid[guid] local plate = info and plateByGuid[guid]
if not plate then return end if not plate then return end
info.noInterrupt = event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE" info.noInterrupt = event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE"
-- UpdateCastbar only restyles on a new cast; force it
plate.castbar.lastEndTime = nil
UpdateCast(plate) UpdateCast(plate)
end end
events.UNIT_SPELLCAST_NOT_INTERRUPTIBLE = events.UNIT_SPELLCAST_INTERRUPTIBLE events.UNIT_SPELLCAST_NOT_INTERRUPTIBLE = events.UNIT_SPELLCAST_INTERRUPTIBLE
-43
View File
@@ -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)
-12
View File
@@ -112,18 +112,6 @@ pfUI:RegisterModule("player", function ()
UpdateInfoText() UpdateInfoText()
end 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 -- PLAYER_DAMAGE_DONE_MODS fires on the MOD_DAMAGE_DONE fields that
-- GetSpellBonusDamage reads, so gear, buffs and talents all reach it. -- GetSpellBonusDamage reads, so gear, buffs and talents all reach it.
pfUI.events:RegisterFrameEventAndCallback("PLAYER_ENTERING_WORLD", UpdateInfoText) pfUI.events:RegisterFrameEventAndCallback("PLAYER_ENTERING_WORLD", UpdateInfoText)
+1 -2
View File
@@ -264,8 +264,7 @@ pfUI:RegisterModule("raid", function ()
if newGuid ~= oldGuid then if newGuid ~= oldGuid then
-- GUID changed = different player = need full update -- GUID changed = different player = need full update
tracker.frameToGuid[frame] = newGuid tracker.frameToGuid[frame] = newGuid
frame.update_full = true pfUI.uf:QueueUpdate(frame, "full")
frame.update_aura = true -- Force aura refresh!
end end
end end
end end
+6 -10
View File
@@ -238,17 +238,11 @@ pfUI:RegisterModule("roll", function ()
f.time.bar = CreateFrame("StatusBar", "pfLootRollFrame" .. id .. "TimeBar", f.time) f.time.bar = CreateFrame("StatusBar", "pfLootRollFrame" .. id .. "TimeBar", f.time)
f.time.bar:SetAllPoints(f.time) f.time.bar:SetAllPoints(f.time)
f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"]) f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"])
f.time.bar:SetMinMaxValues(0, 100)
local r, g, b, a = GetStringColor(C.appearance.border.color) local r, g, b, a = GetStringColor(C.appearance.border.color)
f.time.bar:SetStatusBarColor(r, g, b) f.time.bar:SetStatusBarColor(r, g, b)
f.time.bar:SetValue(20) -- drains by itself; UpdateLootRoll sets the roll's time span
f.time.bar:SetScript("OnUpdate", function() f.time.duration = C_DurationUtil.CreateDuration()
if not this:GetParent():GetParent().rollID then return end f.time.bar:SetTimerDuration(f.time.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
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)
return f return f
end 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].name.text:SetTextColor(color.r, color.g, color.b, 1)
pfUI.roll.frames[id].icon.tex:SetTexture(texture) pfUI.roll.frames[id].icon.tex:SetTexture(texture)
pfUI.roll.frames[id].backdrop:SetBackdropBorderColor(color.r, color.g, color.b) 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 if C.loot.raritytimer == "1" then
pfUI.roll.frames[id].time.bar:SetStatusBarColor(color.r, color.g, color.b, .5) pfUI.roll.frames[id].time.bar:SetStatusBarColor(color.r, color.g, color.b, .5)
+7 -6
View File
@@ -5,7 +5,7 @@ pfUI:RegisterModule("socialmod", function ()
pfUI.socialmod:SetScript("OnEvent", function() pfUI.socialmod:SetScript("OnEvent", function()
local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS) local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS)
name = name or cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S) 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") playerdb[name].lastseen = date("%a %d-%b-%Y")
end end
end) end)
@@ -92,7 +92,6 @@ pfUI:RegisterModule("socialmod", function ()
local cname = ccolor:WrapTextInColorCode(name) local cname = ccolor:WrapTextInColorCode(name)
if playerdb[name] then if playerdb[name] then
playerdb[name].lastseen = date("%a %d-%b-%Y") playerdb[name].lastseen = date("%a %d-%b-%Y")
playerdb[name].cname = cname
end end
if friendName then if friendName then
@@ -106,11 +105,13 @@ pfUI:RegisterModule("socialmod", function ()
caption:SetVertexColor(1,1,1,.9) caption:SetVertexColor(1,1,1,.9)
friendInfo:SetVertexColor(1,1,1,.9) friendInfo:SetVertexColor(1,1,1,.9)
else else
if playerdb[name] and playerdb[name].cname and playerdb[name].level and playerdb[name].lastseen then local _, class = C_PlayerCache.GetPlayerInfoByName(name)
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname) caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), class and PFUI_CLASS_COLORS[class]:WrapTextInColorCode(name) or name.."|r")
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen)
local entry = playerdb[name]
if entry and entry.level and entry.lastseen then
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), entry.level, entry.lastseen)
else else
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r")
friendInfo:SetText(TEXT(UNKNOWN)) friendInfo:SetText(TEXT(UNKNOWN))
end end
+227 -454
View File
@@ -5,14 +5,16 @@ pfUI:RegisterModule("swingtimer", function ()
local ON_SWING_QUEUED = 0 local ON_SWING_QUEUED = 0
local ON_SWING_QUEUE_POPPED = 1 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) -- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max)
local S = { local S = {
mhTimer = 0, mhTimerMax = 1,
ohTimer = 0, ohTimerMax = 1,
raTimer = 0, raTimerMax = 1,
mhSpeed = 0, ohSpeed = 0, raSpeed = 0, mhSpeed = 0, ohSpeed = 0, raSpeed = 0,
mhActive = false, ohActive = false, raActive = false,
lastMhMarkerX = -1, lastOhMarkerX = -1, lastRaMarkerX = -1,
autoAttackActive = false, autoAttackActive = false,
inCombat = false, inCombat = false,
hsQueued = false, cleaveQueued = false, maulQueued = false, hsQueued = false, cleaveQueued = false, maulQueued = false,
@@ -21,7 +23,6 @@ pfUI:RegisterModule("swingtimer", function ()
isDruid = false, isDruid = false,
cachedHSSlots = {}, cachedCleaveSlots = {}, cachedMaulSlots = {}, cachedHSSlots = {}, cachedCleaveSlots = {}, cachedMaulSlots = {},
useSpellQueueEvent = false, useSpellQueueEvent = false,
swingThrottle = 0,
onSwingCache = {}, onSwingCache = {},
outOfRange = {}, -- by Enum.PlayerSwingType 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 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 orR, orG, orB = ParseColor(C.unitframes.swingtimeroutrangecolor, 0.5, 0.5, 0.5, 1)
local isHunter = UnitClassBase("player") == "HUNTER" 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" })
-- the container keeps the public API
-- Create container frame
pfUI.swingtimer = CreateFrame("Frame", "pfSwingTimer", UIParent) pfUI.swingtimer = CreateFrame("Frame", "pfSwingTimer", UIParent)
pfUI.swingtimer:SetFrameStrata("MEDIUM")
pfUI.swingtimer:Hide() pfUI.swingtimer:Hide()
-- Mainhand bar -- A bar fills by itself from `swing`, the time span of the whole swing, and
pfUI.swingtimer.mainhand = CreateFrame("StatusBar", "pfSwingTimerMainhand", UIParent) -- its text binding counts it down. The marker and its glows ride the edge
pfUI.swingtimer.mainhand:SetPoint("BOTTOM", UIParent, "CENTER", -189, 103) -- of the fill. `shown` is whether the config lets the bar show at all.
pfUI.swingtimer.mainhand:SetWidth(sw_width) local function CreateSwingBar(name, label, shown)
pfUI.swingtimer.mainhand:SetHeight(sw_height) local bar = CreateFrame("StatusBar", name, UIParent)
pfUI.swingtimer.mainhand:SetMinMaxValues(0, 1) bar:SetWidth(sw_width)
pfUI.swingtimer.mainhand:SetValue(0) bar:SetHeight(sw_height)
pfUI.swingtimer.mainhand:SetStatusBarTexture(sw_texture) bar:SetStatusBarTexture(sw_texture)
pfUI.swingtimer.mainhand:SetStatusBarColor(mhR, mhG, mhB, mhA) bar:Hide()
pfUI.swingtimer.mainhand:Hide() bar.shown = shown
pfUI.swingtimer.mainhand.text = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal") bar.swing = C_DurationUtil.CreateDuration()
pfUI.swingtimer.mainhand.text:SetPoint("CENTER", pfUI.swingtimer.mainhand, "CENTER", 0, 0) bar:SetTimerDuration(bar.swing, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
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
pfUI.swingtimer.mainhand.label = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal") bar.text = bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.mainhand.label:SetPoint("RIGHT", pfUI.swingtimer.mainhand, "LEFT", -4, 0) bar.text:SetPoint("CENTER", bar, "CENTER", 0, 0)
pfUI.swingtimer.mainhand.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE") bar.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.mainhand.label:SetTextColor(0.8, 0.8, 0.8, 1) bar.text:SetTextColor(1, 1, 1, 1)
pfUI.swingtimer.mainhand.label:SetText(sw_showlabel and "MH" or "") bar.text:SetShown(sw_showtext)
pfUI.swingtimer.mainhand.speed = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal") bar.timer = C_DurationUtil.CreateDurationTextBinding()
pfUI.swingtimer.mainhand.speed:SetPoint("LEFT", pfUI.swingtimer.mainhand, "RIGHT", 4, 0) bar.timer:SetFontString(bar.text)
pfUI.swingtimer.mainhand.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE") bar.timer:SetDuration(bar.swing)
pfUI.swingtimer.mainhand.speed:SetTextColor(0.8, 0.8, 0.8, 1) bar.timer:SetFormatter(countdown)
pfUI.swingtimer.mainhand.speed:SetText("")
if not sw_showspeed then pfUI.swingtimer.mainhand.speed:Hide() end
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") CreateBackdrop(bar)
pfUI.swingtimer.mainhand.marker:SetColorTexture(1, 1, 1, 1.0)
pfUI.swingtimer.mainhand.marker:SetWidth(2) bar.edge = CreateFrame("Frame", nil, bar)
pfUI.swingtimer.mainhand.marker:SetHeight(sw_height) bar.edge:SetAllPoints(bar)
pfUI.swingtimer.mainhand.marker:Hide() bar.edge: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")
-- Left glow: fades from transparent to white (right edge = marker) -- Left glow: fades from transparent to white (right edge = marker)
pfUI.swingtimer.mainhand.markerGlowL = pfUI.swingtimer.mainhand:CreateTexture(nil, "OVERLAY") bar.markerGlowL = bar.edge:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.mainhand.markerGlowL:SetColorTexture(1, 1, 1, 1) bar.markerGlowL:SetColorTexture(1, 1, 1, 1)
pfUI.swingtimer.mainhand.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35) bar.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
pfUI.swingtimer.mainhand.markerGlowL:SetWidth(10) bar.markerGlowL:SetWidth(10)
pfUI.swingtimer.mainhand.markerGlowL:SetHeight(sw_height) bar.markerGlowL:SetHeight(sw_height)
pfUI.swingtimer.mainhand.markerGlowL:Hide() bar.markerGlowL:SetPoint("RIGHT", bar.marker, "LEFT", 0, 0)
-- Right glow: fades from white to transparent (left edge = marker) -- Right glow: fades from white to transparent (left edge = marker)
pfUI.swingtimer.mainhand.markerGlowR = pfUI.swingtimer.mainhand:CreateTexture(nil, "OVERLAY") bar.markerGlowR = bar.edge:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.mainhand.markerGlowR:SetColorTexture(1, 1, 1, 1) bar.markerGlowR:SetColorTexture(1, 1, 1, 1)
pfUI.swingtimer.mainhand.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0) bar.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
pfUI.swingtimer.mainhand.markerGlowR:SetWidth(10) bar.markerGlowR:SetWidth(10)
pfUI.swingtimer.mainhand.markerGlowR:SetHeight(sw_height) bar.markerGlowR:SetHeight(sw_height)
pfUI.swingtimer.mainhand.markerGlowR:Hide() bar.markerGlowR:SetPoint("LEFT", bar.marker, "RIGHT", 0, 0)
CreateBackdropShadow(pfUI.swingtimer.mainhand) CreateBackdropShadow(bar)
return bar
end
-- Offhand bar local mainhand = CreateSwingBar("pfSwingTimerMainhand", "MH", true)
pfUI.swingtimer.offhand = CreateFrame("StatusBar", "pfSwingTimerOffhand", UIParent) mainhand:SetPoint("BOTTOM", UIParent, "CENTER", -189, 103)
pfUI.swingtimer.offhand:SetPoint("TOP", pfUI.swingtimer.mainhand, "BOTTOM", 0, -4) mainhand.showmarker = true
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()
pfUI.swingtimer.offhand.text = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal") local offhand = CreateSwingBar("pfSwingTimerOffhand", "OH", sw_showoh)
pfUI.swingtimer.offhand.text:SetPoint("CENTER", pfUI.swingtimer.offhand, "CENTER", 0, 0) offhand:SetPoint("TOP", mainhand, "BOTTOM", 0, -4)
pfUI.swingtimer.offhand.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE") offhand.showmarker = true
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
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 "")
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
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 -- 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. -- the reload, then the auto shot deadzone grows back out in the warn color.
pfUI.swingtimer.ranged = CreateFrame("StatusBar", "pfSwingTimerRanged", UIParent) -- Each phase is a time span of its own on `phase`.
pfUI.swingtimer.ranged:SetPoint("BOTTOM", UIParent, "CENTER", -189, 83) local ranged = CreateSwingBar("pfSwingTimerRanged", "Ra", sw_showranged)
pfUI.swingtimer.ranged:SetWidth(sw_width) ranged:SetPoint("BOTTOM", UIParent, "CENTER", -189, 83)
pfUI.swingtimer.ranged:SetHeight(sw_height) ranged:SetFillStyle(isHunter and "CENTER" or "STANDARD")
pfUI.swingtimer.ranged:SetMinMaxValues(0, 1) ranged.showmarker = not isHunter
pfUI.swingtimer.ranged:SetValue(0) if isHunter then
pfUI.swingtimer.ranged:SetStatusBarTexture(sw_texture) ranged.phase = C_DurationUtil.CreateDuration()
pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA) ranged.timer:SetDuration(ranged.phase)
pfUI.swingtimer.ranged:SetFillStyle(isHunter and "CENTER" or "STANDARD") end
pfUI.swingtimer.ranged:Hide()
pfUI.swingtimer.ranged.text = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal") pfUI.swingtimer.mainhand = mainhand
pfUI.swingtimer.ranged.text:SetPoint("CENTER", pfUI.swingtimer.ranged, "CENTER", 0, 0) pfUI.swingtimer.offhand = offhand
pfUI.swingtimer.ranged.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE") pfUI.swingtimer.ranged = ranged
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.ranged.label = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal") UpdateMovable(mainhand)
pfUI.swingtimer.ranged.label:SetPoint("RIGHT", pfUI.swingtimer.ranged, "LEFT", -4, 0) UpdateMovable(ranged)
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 "")
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 -- OH weapon detection: slot 17 (off hand) accepts one-hand (13) and off-hand
-- weapons (22); main-hand-only (21) can never sit there. -- weapons (22); main-hand-only (21) can never sit there.
local OH_WEAPON_TYPES = { [13]=true, [22]=true } 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 S.raSpeed = (rs and rs > 0) and rs or 0
end 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 -- HS/Cleave/Maul helpers. Canonical rank-1 spellIDs resolve to the
-- localized spell name once, so per-spell classification is rank- and -- localized spell name once, so per-spell classification is rank- and
-- locale-independent without per-rank ID hardcoding (every rank of -- 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) return CheckQueuedAction(S.cachedHSSlots), CheckQueuedAction(S.cachedCleaveSlots)
end end
-- OnUpdate: countdown all timers with delta, then render -- The bar colors follow the queued on-next-swing ability, the range state
pfUI.swingtimer:SetScript("OnUpdate", function() -- and the hunter deadzone. Runs on the events that change any of them;
S.swingThrottle = S.swingThrottle + arg1 -- ACTIONBAR_UPDATE_STATE covers the IsCurrentAction reconcile.
local swingDelay = pfUI.throttle and pfUI.throttle:Get("swingtimer") or 0.02 local function UpdateColors()
if S.swingThrottle < swingDelay then return end local r, g, b = mhR, mhG, mhB
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
if sw_hsqueue then if sw_hsqueue then
if S.isDruid and S.useSpellQueueEvent then if S.isDruid and S.useSpellQueueEvent then
S.maulQueued, S.maulSeenCurrent = S.maulQueued, S.maulSeenCurrent =
ReconcileQueued(S.maulQueued, S.cachedMaulSlots, S.maulSeenCurrent) ReconcileQueued(S.maulQueued, S.cachedMaulSlots, S.maulSeenCurrent)
end end
if S.maulQueued and S.isDruid then 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 elseif S.isWarrior then
local hs, cl = IsHSOrCleaveQueued() local hs, cl = IsHSOrCleaveQueued()
if cl then if cl then
curR, curG, curB = 0.2, 0.9, 0.2 r, g, b = 0.2, 0.9, 0.2
elseif hs then elseif hs then
curR, curG, curB = 0.9, 0.9, 0.2 r, g, b = 0.9, 0.9, 0.2
end end
end 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
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
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
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
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 if S.outOfRange[OFFHAND] then
pfUI.swingtimer.offhand:SetStatusBarColor(orR, orG, orB, ohA) offhand:SetStatusBarColor(orR, orG, orB, ohA)
else else
pfUI.swingtimer.offhand:SetStatusBarColor(ohR, ohG, ohB, ohA) 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()
end end
-- Render Ranged local a
if sw_showranged and S.raActive then if ranged.warn 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 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 else
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor((remaining - 0.5) * 10) / 10) r, g, b, a = raR, raG, raB, raA
end end
end if S.outOfRange[RANGED] then r, g, b = orR, orG, orB end
else ranged:SetStatusBarColor(r, g, b, a)
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 end
if not anyActive then local function StopBar(bar)
if not pfUI.swingtimer.mainhand:IsShown() if bar.wake then bar.wake:Cancel() end
and not pfUI.swingtimer.offhand:IsShown() bar.wake, bar.active, bar.graced = nil, nil, nil
and not pfUI.swingtimer.ranged:IsShown() then bar.timer:Disable()
this:Hide() 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
HunterDeadzone(bar)
end
else
bar.wake = C_Timer.NewTimer(remaining, function() due(bar) end)
end
UpdateColors()
if bar.shown then
bar.edge:SetShown(bar.showmarker)
if sw_showtext then bar.timer:Enable() end
bar:Show()
end end
end end
end)
local function StartMH(remaining)
UpdateWeaponSpeeds()
StartBar(mainhand, remaining, S.mhSpeed, MeleeDue)
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") local events = CreateFrame("Frame")
events:RegisterEvent("PLAYER_SWING") events:RegisterEvent("PLAYER_SWING")
@@ -658,6 +423,7 @@ pfUI:RegisterModule("swingtimer", function ()
events:RegisterEvent("PLAYER_REGEN_DISABLED") events:RegisterEvent("PLAYER_REGEN_DISABLED")
events:RegisterEvent("PLAYER_REGEN_ENABLED") events:RegisterEvent("PLAYER_REGEN_ENABLED")
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED") events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
events:RegisterEvent("ACTIONBAR_UPDATE_STATE")
events:RegisterEvent("PLAYER_DEAD") events:RegisterEvent("PLAYER_DEAD")
events:RegisterUnitEvent("UNIT_SPELLCAST_SENT", "player") events:RegisterUnitEvent("UNIT_SPELLCAST_SENT", "player")
events:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player") events:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player")
@@ -682,6 +448,7 @@ pfUI:RegisterModule("swingtimer", function ()
-- arg1 = swingType, arg2 = isInRange, arg3 = checksRange; no check -- arg1 = swingType, arg2 = isInRange, arg3 = checksRange; no check
-- (no target, nothing to attack, no weapon) is not out of range -- (no target, nothing to attack, no weapon) is not out of range
S.outOfRange[arg1] = arg3 and not arg2 S.outOfRange[arg1] = arg3 and not arg2
UpdateColors()
elseif event == "UNIT_SPELLCAST_SENT" then elseif event == "UNIT_SPELLCAST_SENT" then
-- arg4 = spellID: an on-next-swing ability (HS / Cleave / Maul) queued -- arg4 = spellID: an on-next-swing ability (HS / Cleave / Maul) queued
@@ -689,6 +456,7 @@ pfUI:RegisterModule("swingtimer", function ()
if kind then if kind then
S.useSpellQueueEvent = true S.useSpellQueueEvent = true
SetQueuedKind(kind) SetQueuedKind(kind)
UpdateColors()
end end
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
@@ -696,6 +464,7 @@ pfUI:RegisterModule("swingtimer", function ()
-- the queue, so drop the queued color -- the queue, so drop the queued color
if IsOnSwingSpell(arg3) then if IsOnSwingSpell(arg3) then
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
UpdateColors()
end end
elseif event == "SPELL_QUEUE_EVENT" then elseif event == "SPELL_QUEUE_EVENT" then
@@ -707,12 +476,17 @@ pfUI:RegisterModule("swingtimer", function ()
elseif eventCode == ON_SWING_QUEUE_POPPED then elseif eventCode == ON_SWING_QUEUE_POPPED then
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
end end
UpdateColors()
elseif event == "ACTIONBAR_UPDATE_STATE" then
if sw_hsqueue then UpdateColors() end
elseif event == "START_AUTOATTACK" then elseif event == "START_AUTOATTACK" then
S.autoAttackActive = true S.autoAttackActive = true
elseif event == "STOP_AUTOATTACK" then elseif event == "STOP_AUTOATTACK" then
S.autoAttackActive = false S.autoAttackActive = false
StopWaitingBars()
elseif event == "PLAYER_ENTERING_WORLD" then elseif event == "PLAYER_ENTERING_WORLD" then
local class = UnitClassBase("player") local class = UnitClassBase("player")
@@ -725,14 +499,8 @@ pfUI:RegisterModule("swingtimer", function ()
-- arg1 = changed slot; only weapon slots (main/off/ranged) affect swing speed -- arg1 = changed slot; only weapon slots (main/off/ranged) affect swing speed
if not INVSLOTS_EQUIPABLE_IN_COMBAT[arg1] then return end if not INVSLOTS_EQUIPABLE_IN_COMBAT[arg1] then return end
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
if S.ohSpeed == 0 then if S.ohSpeed == 0 then StopBar(offhand) end
S.ohActive = false if S.raSpeed == 0 then StopBar(ranged) end
pfUI.swingtimer.offhand:Hide()
end
if S.raSpeed == 0 then
S.raActive = false
pfUI.swingtimer.ranged:Hide()
end
elseif event == "ACTIONBAR_SLOT_CHANGED" then elseif event == "ACTIONBAR_SLOT_CHANGED" then
RebuildQueueSlotCache() RebuildQueueSlotCache()
@@ -746,12 +514,22 @@ pfUI:RegisterModule("swingtimer", function ()
S.hsQueued = false S.hsQueued = false
S.cleaveQueued = false S.cleaveQueued = false
S.maulQueued = false S.maulQueued = false
UpdateColors()
StopWaitingBars()
elseif event == "PLAYER_DEAD" then elseif event == "PLAYER_DEAD" then
ResetAll() ResetAll()
end end
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) -- Public API for external addons (e.g. SCRM)
-- Only available when the swingtimer module is loaded. -- Only available when the swingtimer module is loaded.
@@ -763,9 +541,9 @@ pfUI:RegisterModule("swingtimer", function ()
-- ------------------------------------------------------------------------- -- -------------------------------------------------------------------------
pfUI.swingtimer.api = { pfUI.swingtimer.api = {
-- Remaining time in seconds until next swing -- Remaining time in seconds until next swing
GetMHTimer = function() return S.mhTimer end, GetMHTimer = function() return Remaining(mainhand) end,
GetOHTimer = function() return S.ohTimer end, GetOHTimer = function() return Remaining(offhand) end,
GetRangedTimer = function() return S.raTimer end, GetRangedTimer = function() return Remaining(ranged) end,
-- Weapon speed (full swing duration in seconds) -- Weapon speed (full swing duration in seconds)
GetMHSpeed = function() return S.mhSpeed end, GetMHSpeed = function() return S.mhSpeed end,
@@ -773,20 +551,14 @@ pfUI:RegisterModule("swingtimer", function ()
GetRangedSpeed = function() return S.raSpeed end, GetRangedSpeed = function() return S.raSpeed end,
-- Whether each timer is currently running -- Whether each timer is currently running
IsMHActive = function() return S.mhActive end, IsMHActive = function() return mainhand.active == true end,
IsOHActive = function() return S.ohActive end, IsOHActive = function() return offhand.active == true end,
IsRangedActive = function() return S.raActive end, IsRangedActive = function() return ranged.active == true end,
-- Swing progress 0.0 (just reset) to 1.0 (about to swing) -- Swing progress 0.0 (just reset) to 1.0 (about to swing)
GetMHProgress = function() GetMHProgress = function() return Progress(mainhand) end,
return S.mhActive and S.mhTimerMax > 0 and (1 - S.mhTimer / S.mhTimerMax) or 0 GetOHProgress = function() return Progress(offhand) end,
end, GetRangedProgress = function() return Progress(ranged) 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,
-- Warrior HS/Cleave queue state -- Warrior HS/Cleave queue state
IsHSQueued = function() return S.hsQueued end, IsHSQueued = function() return S.hsQueued end,
@@ -804,4 +576,5 @@ pfUI:RegisterModule("swingtimer", function ()
if sw_showranged then EnableRangeCheck(RANGED) end if sw_showranged then EnableRangeCheck(RANGED) end
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
UpdateColors()
end) end)
+2 -6
View File
@@ -801,11 +801,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
HookAddon("SortBags", function() HookAddon("SortBags", function()
if C.thirdparty.sortbags.enable == "0" then return end if C.thirdparty.sortbags.enable == "0" then return end
local sort = CreateFrame("Frame", nil) EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
sort:RegisterEvent("PLAYER_ENTERING_WORLD")
sort:SetScript("OnEvent", function()
this:UnregisterAllEvents()
pfUI.thirdparty.RegisterBagSort("SortBags", pfUI.thirdparty.RegisterBagSort("SortBags",
function() function()
SortBags() SortBags()
@@ -899,7 +895,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
-- trigger the event whenever SuperMacro got an update -- trigger the event whenever SuperMacro got an update
hooksecurefunc("SM_UpdateActionSpell", function() 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)
end) end)
+1 -5
View File
@@ -142,11 +142,7 @@ pfUI:RegisterModule("thirdparty", function()
HookAddon("MrPlow", function() HookAddon("MrPlow", function()
if C.thirdparty.mrplow.enable == "0" then return end if C.thirdparty.mrplow.enable == "0" then return end
local sort = CreateFrame("Frame", nil) EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
sort:RegisterEvent("PLAYER_ENTERING_WORLD")
sort:SetScript("OnEvent", function()
this:UnregisterAllEvents()
local MrPlowL = AceLibrary and AceLibrary("AceLocale-2.2"):new("MrPlow") local MrPlowL = AceLibrary and AceLibrary("AceLocale-2.2"):new("MrPlow")
if not (MrPlowL and MrPlowL["Bank"]) then return end if not (MrPlowL and MrPlowL["Bank"]) then return end
+2 -1
View File
@@ -38,7 +38,8 @@ pfUI:RegisterModule("tracking", function ()
pfUI.tracking.pulse = pfUI.tracking.icon:CreateAnimationGroup() pfUI.tracking.pulse = pfUI.tracking.icon:CreateAnimationGroup()
pfUI.tracking.pulse:SetLooping("BOUNCE") pfUI.tracking.pulse:SetLooping("BOUNCE")
local fade = pfUI.tracking.pulse:CreateAnimation("Alpha") local fade = pfUI.tracking.pulse:CreateAnimation("Alpha")
fade:SetChange(-.5) fade:SetFromAlpha(1)
fade:SetToAlpha(.5)
fade:SetDuration(.5) fade:SetDuration(.5)
fade:SetSmoothing("IN_OUT") fade:SetSmoothing("IN_OUT")
+1 -27
View File
@@ -180,33 +180,7 @@ pfUI:RegisterModule("unitxp", function ()
CreateTargetIndicators() CreateTargetIndicators()
-- Also try on PLAYER_ENTERING_WORLD in case target frame wasn't ready -- Also try on PLAYER_ENTERING_WORLD in case target frame wasn't ready
local initFrame = CreateFrame("Frame") EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", CreateTargetIndicators)
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)
-- OS Notification Support -- OS Notification Support
if C.unitframes.unitxp_notify == "1" then if C.unitframes.unitxp_notify == "1" then
+3 -3
View File
@@ -37,15 +37,15 @@ pfUI:RegisterModule("unusable", function ()
-- requirement changes (level, proficiency/skill, required spell, reputation, -- requirement changes (level, proficiency/skill, required spell, reputation,
-- honor rank) don't touch the bags; queue a full rebag through pfUI.bag's -- 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() local function Refresh()
for _, bag in pairs(pfUI.BACKPACK) do for _, bag in pairs(pfUI.BACKPACK) do
pfUI.bag.delay.UpdateBag[bag] = true pfUI.bag:QueueBagUpdate(bag)
end end
-- the bank frame is only created on first bank visit -- the bank frame is only created on first bank visit
if pfUI.bag.left and pfUI.bag.left:IsShown() then if pfUI.bag.left and pfUI.bag.left:IsShown() then
for _, bag in pairs(pfUI.BANK) do for _, bag in pairs(pfUI.BANK) do
pfUI.bag.delay.UpdateBag[bag] = true pfUI.bag:QueueBagUpdate(bag)
end end
end end
end end
+26 -22
View File
@@ -69,11 +69,25 @@ pfUI:RegisterModule("xpbar", function ()
self:SetAlpha(1) self:SetAlpha(1)
end 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 function OnLeave(self)
local self = self or this local self = self or this
self.hover = nil self.hover = nil
self.tick = GetTime() + 3.00 if self.text_mouse == "1" then self.bar.text:Hide() end
ScheduleFade(self, 3)
GameTooltip:Hide() GameTooltip:Hide()
end end
@@ -90,6 +104,9 @@ local function OnEnter(self)
end end
self.hover = true 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) Reveal(self)
if mode == "XP" then if mode == "XP" then
@@ -187,21 +204,6 @@ local function OnEnter(self)
GameTooltip:Show() GameTooltip:Show()
end 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 function OnEvent(self)
local self = self or this local self = self or this
@@ -248,7 +250,7 @@ end
if ex then text = "%s: %s%% (%s%% %s)" end if ex then text = "%s: %s%% (%s%% %s)" end
self.bar.text:SetFormattedText(text, T["Experience"], xpperc, experc, T["Rested"]) 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 if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then
OnEnter(self) OnEnter(self)
end end
@@ -266,7 +268,7 @@ end
local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0 local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0
self.bar.text:SetFormattedText(text, T["Pet Experience"], xpperc) self.bar.text:SetFormattedText(text, T["Pet Experience"], xpperc)
self.tick = GetTime() + self.timeout ScheduleFade(self, self.timeout)
return return
elseif mode == "REP" then elseif mode == "REP" then
self.restedbar:Hide() self.restedbar:Hide()
@@ -294,7 +296,7 @@ end
local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender) local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender)
self.bar.text:SetFormattedText(text, name, perc, standing) self.bar.text:SetFormattedText(text, name, perc, standing)
self.tick = GetTime() + self.timeout ScheduleFade(self, self.timeout)
return return
end end
end end
@@ -302,6 +304,9 @@ end
self.bar:SetStatusBarColor(.5,.5,.5,1) self.bar:SetStatusBarColor(.5,.5,.5,1)
self.bar:SetMinMaxValues(0, 1) self.bar:SetMinMaxValues(0, 1)
self.bar:SetValue(0) 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 end
local function CreateBar(t) local function CreateBar(t)
@@ -359,7 +364,7 @@ end
b.bar.text:SetJustifyH("CENTER") b.bar.text:SetJustifyH("CENTER")
b.bar.text:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE") 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 = b.restedbar or CreateFrame("StatusBar", nil, b)
b.restedbar:SetStatusBarTexture(pfUI.media[C.panel.xp.texture]) b.restedbar:SetStatusBarTexture(pfUI.media[C.panel.xp.texture])
@@ -376,10 +381,10 @@ end
if not b.fade then if not b.fade then
b.fade = b:CreateAnimationGroup() b.fade = b:CreateAnimationGroup()
b.fade:SetToFinalAlpha(true)
local fade = b.fade:CreateAnimation("Alpha") local fade = b.fade:CreateAnimation("Alpha")
fade:SetChange(-1) fade:SetChange(-1)
fade:SetDuration(.3) fade:SetDuration(.3)
b.fade:SetScript("OnFinished", function() b:SetAlpha(0) end)
end end
b:RegisterEvent("FACTION_STANDING_CHANGED") b:RegisterEvent("FACTION_STANDING_CHANGED")
@@ -392,7 +397,6 @@ end
b:RegisterEvent("PLAYER_LEVEL_UP") b:RegisterEvent("PLAYER_LEVEL_UP")
b:RegisterEvent("UPDATE_FACTION") b:RegisterEvent("UPDATE_FACTION")
b:SetScript("OnUpdate", OnUpdate)
b:SetScript("OnEvent", OnEvent) b:SetScript("OnEvent", OnEvent)
b:SetScript("OnEnter", OnEnter) b:SetScript("OnEnter", OnEnter)
b:SetScript("OnLeave", OnLeave) b:SetScript("OnLeave", OnLeave)
+17 -8
View File
@@ -392,6 +392,10 @@ pfUI:SetScript("OnEvent", function()
SetupConfigEnvironment() SetupConfigEnvironment()
pfUI:UpdateFonts() 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 -- load modules
for _, m in pairs(this.modules) do for _, m in pairs(this.modules) do
if not ( pfUI_config["disabled"] and pfUI_config["disabled"][m] == "1" ) then if not ( pfUI_config["disabled"] and pfUI_config["disabled"][m] == "1" ) then
@@ -457,15 +461,20 @@ message = function(msg)
end end
print = print or message print = print or message
error = function(msg) -- Errors print to chat: pfUI's own always, other addons' when the errors
if PF_DEBUG_MODE then message(debugstack()) end -- option is on. In debug mode the stack follows, starting at the code that
if string.find(msg, "AddOns\\pfUI") then -- raised the error rather than at this handler.
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555".. (msg or "nil" )) seterrorhandler(function(msg)
elseif not pfUI_config or (pfUI_config.global and pfUI_config.global.errors == "1") then msg = tostring(msg)
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555".. (msg or "nil" )) if PF_DEBUG_MODE then
local height = GetErrorCallstackHeight()
message(height and debugstack(GetCallstackHeight() - height + 1) or debugstack(2))
end end
end if string.find(msg, "AddOns\\pfUI") or not pfUI_config
seterrorhandler(error) or (pfUI_config.global and pfUI_config.global.errors == "1") then
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555" .. msg)
end
end)
function pfUI.SetupCVars() function pfUI.SetupCVars()
ClearTutorials() ClearTutorials()
+4 -6
View File
@@ -5,10 +5,9 @@ pfUI:RegisterSkin("Mailbox", function ()
-- Compatibility -- Compatibility
local StationeryBackgroundLeft, StationeryBackgroundRight local StationeryBackgroundLeft, StationeryBackgroundRight
do -- SendMailFrame do -- SendMailFrame
local skin = CreateFrame("Frame") local mailLoaded
skin:SetScript("OnEvent", function() EventUtil.RegisterOnceFrameEventAndCallback("MAIL_SHOW", function()
this:UnregisterEvent("MAIL_SHOW") if not mailLoaded then
if not this._Mail then
StripTextures(SendMailPackageButton) StripTextures(SendMailPackageButton)
SkinButton(SendMailPackageButton, nil, nil, nil, nil, true) SkinButton(SendMailPackageButton, nil, nil, nil, nil, true)
@@ -26,10 +25,9 @@ pfUI:RegisterSkin("Mailbox", function ()
end) end)
end end
end) end)
skin:RegisterEvent("MAIL_SHOW")
HookAddon("Mail", function() HookAddon("Mail", function()
skin._Mail = true mailLoaded = true
for i = 1, 21 do for i = 1, 21 do
local button = _G["MailAttachment"..i] local button = _G["MailAttachment"..i]
StripTextures(button) StripTextures(button)