Compare commits

...
41 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
Brues f1c1bfd0c6 Options: skin ClassicAPI's key binding buttons and page scroll bars
ClassicAPI's option pages now have key binding rows. Their two buttons are
numbered ClassicAPIOptionsControlN like the other controls, but they are
Buttons: strip the UIPanelButtonTemplate2 slices (BACKGROUND layer) and
skin them as buttons.

The control loop's last branch skinned anything unknown as a dropdown,
and SkinDropDown errors without the dropdown's Button, which stopped the
loop for every later control. It now runs only for a control that has
that Button; a type without a branch stays unskinned.

ClassicAPI's pages scroll with retail's MinimalScrollBar, named
ClassicAPIOptionsScrollBarN. SkinScrollbar expects the old named
UIPanelScrollBar parts, so these get their own pass: pfUI arrow buttons,
a backdrop track between them and a flat thumb, as SkinScrollbar draws.
The bar's steppers and thumb set their atlas again on every state
change, so their art is hidden rather than cleared.
2026-11-03 00:56:37 -06:00
Brues 2a6bddd756 DPSMate dock: find DPSMate by its globals and its first window by settings
The hook gated on an addon folder named exactly DPSMate and a first
window named DPSMate, so repacks under other folder names and renamed
windows never docked (#77). It now waits on login, checks DPSMate's
globals, resolves the first window from DPSMateSettings on every call,
and re-docks after DPSMate rebuilds its frames.
2026-11-02 23:14:27 -06:00
Brues 893524174c Equipment manager: close the slot flyout with the character frame
PopoutsActive checked IsShown, which stays true on the paperdoll and the
sidecar when only CharacterFrame hides. Check IsVisible instead.
2026-11-02 20:08:32 -06:00
Brues 001e09c1b7 Pick cooldown timers per feature with one dropdown
Each feature's settings page now picks its own timers - None, Numbers,
Sweep, or Numbers And Sweep - in place of the "Show Numbers On", "Show
Sweep On" and "Reverse Sweep On" lists on Settings -> Cooldown. Unit
frames get theirs per frame again ("Buff And Debuff Timers" and
"Indicator Timers"), so party and pet can drop their numbers while the
target keeps them. The other dropdowns sit on Action Bars, Bags & Bank,
Buff/Debuff Icons, Totem Icons, Tooltip and Nameplates.

Settings -> Cooldown keeps the shared font, size, threshold, tenths and
colors, plus the timers of Spellbook And Character, Loss Of Control and
Other Addons, which have no page of their own.

CreateCooldown(parent, kind, config, key, name) reads its mode from
config[key]. The kind says only what it times: "spell" hides numbers on
the global cooldown, "aura" drains its sweep, "timer" fills it. The
reverse option is gone; the direction follows the kind.

The migration carries every old setting into its timers: the
C.cooldown kind tables, appearance.cd's foreign and Blizzard toggles,
the buff frame's sweep, the nameplate debuff options, and each unit
frame's timer text, sweep and "Show Time Left" from before the kinds,
so frames that had their own settings keep them. Reverse options are
dropped.
2026-11-02 16:07:41 -06:00
Brues 494f176f18 Nameplates: stay in the world stratum, raise the target by level
The overlay inherits the engine plate's world stratum, below every UI
stratum. Moving it to BACKGROUND/LOW put it level-for-level against the
chat panels, so plates drew over the chat. The target now rises above
the other plates by raising its engine frame and re-parenting the
overlay, which carries the subtree's levels along. Debuff icons take
their level from the health bar so ones grown on a raised plate stay
above it.
2026-11-02 01:36:06 -06:00
Brues 8f1b3dc39b Cooldowns: size the numbers from OnSizeChanged, not timer starts
Buff buttons start their timers before they are sized, so their numbers took
the height-0 font and kept it until a timer restarted.
2026-11-02 01:18:59 -06:00
Brues 23d25c4791 Focus target: follow its token, not its GUID
The focus target frame filtered its unit events on the unit's GUID, which
only arrive while nampower fires its raw-GUID events (NP_EnableUnitEventsGuid
on, NP_EnableUnitEventsGuidFiltering off) and SuperWoW watches every unit.
Without them the bars froze until the focus switched targets (#76).

ClassicAPI fires the focus target's unit events as "focustarget" and the
focus's UNIT_TARGET as "focus" whatever nampower does, so the frame follows
those tokens. The swap check keeps its own GUID cache, since eventunit is
no longer a GUID for that frame.
2026-11-02 01:06:17 -06:00
Brues ebb4b056c3 Nameplates: drive every plate from unit events, drop the per-tick loop
The module polled: a central OnUpdate at 100 Hz over every visible plate,
a full OnDataChanged refresh of each plate every 0.5 s, and per-tick reads
of the engine's name/level regions, frame size, mouse state and
SpellIsTargeting. ClassicAPI feeds every unit event to the nameplateN
tokens, so none of that is needed.

Each bound plate now registers its unit events on its own frame against
its token (UNIT_HEALTH/MAXHEALTH, UNIT_AURA_DELTA, UNIT_FLAGS,
UNIT_DYNAMIC_FLAGS, UNIT_FACTION, UNIT_LEVEL, UNIT_CLASSIFICATION_CHANGED,
UNIT_NAME_UPDATE, UNIT_TARGET, the casts) and each handler updates only
what that event can change; the module-level events (target, combat,
level-up, roster, guild, spell targeting) fan out to the plates they
concern. Health comes from UnitHealth on the token instead of the
engine bar's OnValueChanged hook; name, level and difficulty colour from
UnitName/UnitLevel/GetDifficultyColor instead of the blanked engine
regions (UnitLevel's -1 is exactly the engine's skull case, so "??B"
survives). The engine resets the plate's size at every bind, so the
engine frame's size and click routing are re-applied from its OnShow
instead of being checked every tick; the overlay steps aside for
ground-target spells on CURRENT_SPELL_CAST_CHANGED.

The only OnUpdate left is a driver frame, shown while a castbar sweeps or
the target zoom animates and hidden when both sets are empty; the zoom
is time-based now rather than per-tick multiplication. The
nameplates/nameplates_target/nameplates_mass throttle categories no
longer mean anything and are gone from libthrottle and the GUI;
nameplates_castbar stays as the sweep rate of non-target castbars.

Mouseover tracking is gone (the token already names the unit), and
libhealth's "known" return now decides whether absolute health is shown,
so a unit whose health is only known as a percentage no longer prints
"57 | 57%".
2026-11-01 21:40:50 -06:00
Brues b17f5bcd3d Unit frames: only the player's pet wakes them with UNIT_HAPPINESS
Shown frames registered UNIT_HAPPINESS unfiltered, so every hunter pet in view woke them. It is now a unit event for "pet".
2026-11-01 21:08:57 -06:00
Brues e60f9eb410 libpredict: drop the always-running cleanup OnUpdate
The events table it pruned was never read; expired heals already drop out of UnitGetIncomingHeals by their own timeout. Resurrection offers are now checked by a ticker that runs only while an offer is open.
2026-11-01 20:57:55 -06:00
Brues 66171d5354 libpredict: stop the sender's per-frame work
The Regrowth HoT now goes out on a one-shot timer instead of an always-running OnUpdate, and other units' spellcast events are filtered to the group's tokens, the only casters libpredict can resolve; solo, no one else's casts arrive.
2026-11-01 20:54:40 -06:00
Brues 8b239a5532 Castbars: hide idle bars instead of fading them to alpha 0
An idle bar stayed shown at alpha 0, so its OnUpdate ran every frame. The fade-out now ends in Hide, a cast shows the bar, and unlock mode keeps it shown for dragging.
2026-11-01 20:44:24 -06:00
Brues b6d570df97 Unit frames: hidden frames only hear the events that can show them
Every frame registered the same global events whether shown or not. A frame now keeps only the visibility events for the unit it was created for, and registers the display-only events while shown; OnShow does a full refresh to catch up on what it missed.
2026-11-01 20:44:24 -06:00
Brues 698dedcb7a Chat: cut the per-line work of the AddMessage hook
- Save the chat history from the frames when leaving the world, through
  GetMessageInfo, instead of on every line. Lines written back from the
  history carry a mark, so they are saved as they are. History is no longer
  recorded while it is off, keeps 30 lines instead of 29, and whispers are
  dimmed in the whisper colour (gsub's match count was taken as red).
- Skip the CLINK and URL passes on lines that cannot hold them.
- Colour player links in one gsub with a callback instead of a gsub per name.
- Read a chat line that several frames show only once, by its line ID.
- Build the timestamp once per second.
- Lay the tabs out on hover only when their bar comes back, not 10 times a
  second.
- Drop ChatFrame_OnUpdate: it only blinks the flash of the bottom button
  pfUI hides. The lines fade in the engine's update.
2026-11-01 19:59:53 -06:00
AbyssandBrues e609809fff Raid unitframes: add option to reverse group order (#75)
* Add raidreverse default to unitframes config

Defaults to off, so existing layouts are unchanged.

* Raid frames: add option to reverse group order

Read the new raidreverse setting and mirror the column index when placing raid frames, so group 1 sits on the far right and the following groups grow to the left.

* Add "Reverse Group Order" checkbox to raid settings

Placed under "Raid Fill Direction" in the raid frame options.
/pfui > Settings > Unit Frames > Raid > Layout

* Raid frames: rename reverse option to "Grow Right To Left"

The option mirrors columns in both fill directions, so name it for what
it does. Mirror across the columns maxraid can fill instead of the whole
layout, so smaller raids stay next to the anchor. Lay the raid grid out
through SlotToCoord like the pet grid, move the default next to the other
raid layout keys, and add the label to the locale tables.

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-11-01 18:02:31 -06:00
Brues 3651341697 Scroll every chat copy box to the newest line when it opens
The caret at the end scrolled only the box with the focus, and each
visible chat frame's box takes the focus as it opens, so only the last
one shown reached the bottom. Even that one stopped short: 1.12's
OnCursorChanged has no input context, so ScrollCursorIntoView lets the
last line clip.

Each box now measures its scroll box and scrolls it to the end on the
next frame, after the edit box has laid the new text out.
2026-11-01 17:22:06 -06:00
Antoine ChedinandBrues 77be27094a Add Offsets to unit frame buff/debuff (#74)
* Add anchor offset for unitframe buff or defuff

* Fix default values

* Stack a shared-corner aura block past the other block's Y offset, read the offsets without a table per icon

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-11-01 16:39:48 -06:00
Brues 3e50a0b34f Turn on countdownForCooldowns when migrating the old cooldown settings
pfUI drew its own cooldown numbers before; the native countdown shows
them only with the countdownForCooldowns cvar on, and only the first-run
setup set it. Upgraded installs showed no numbers at all, so the
cooldown settings migration turns the cvar on as it removes
appearance.cd.
2026-11-01 00:23:39 -05:00
Brues 3faeafdfc5 Migrate the old cooldown settings into C.cooldown
The shared appearance.cd values (font, size, threshold, tenths, colors)
move to the cooldown table. "Enable Durations On Foreign Frames" and
"Hide Foreign Cooldown Animations" become the Other Addons numbers and
sweep; the spellbook and character slots take that sweep, and the
foreign numbers when "Enable Durations On Blizzard Frames" was off.

Each module's old options move to its kind: the bars, bags and totems
reverse, the buffs sweep and reverse, and the nameplate debuff text,
sweep and reverse, with "Enable Debuff Timers" off turning off both
numbers and sweep. Unit frames had one set per frame and now share one:
the target frame's timer options and the raid frames' "Show Time Left"
stand for the rest. The per-frame keys, appearance.cd and bars.cd_size
are removed.

The migration is keyed on the old keys rather than the version, so it
also runs on dev installs, and runs once.
2026-10-31 23:52:28 -05:00
Brues b2cffcb3c2 Load each module and skin through securecallfunction so one error no longer stops the rest from loading 2026-10-31 21:17:42 -05:00
Brues 5476a28982 Skin menu dividers: CreateMenuDivider draws a 1px border-colored line; use it in the radar menu 2026-10-31 21:04:11 -05:00
60 changed files with 3287 additions and 3785 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ do
-- on. There pfUI does load, and will throw wherever it reaches for something
-- the installed DLL doesn't have yet -- the popup names the cause so those
-- errors aren't a mystery.
local PFUI_CLASSIC_API_MIN = 11813 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_MIN = 11901 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
+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_UnitAuras - Replaces over 3k lines of hand-rolled aura tracking
- C_Spell - Replaces thousands of hardcoded spell names for each locale and powers castbar
- Real cast bars - Actual cast/channel timing, spellID and interrupt state for any unit (C_Spell.UnitCastingInfo)
- Real cast bars - Actual cast/channel timing, spellID and interrupt state for any unit (C_Spell.UnitCastingInfo), filled and counted down engine-side (UnitCastingDuration, StatusBar:SetTimerDuration, duration text bindings) with no Lua OnUpdate
- Focus - A genuine `focus` unit plus `/focus` and `/clearfocus` (FocusUnit / ClearFocus)
- C_EquipmentSet - Full Equipment Manager with GUID-tracked gear sets (tells apart duplicate/enchanted items)
- Instant Bag Sorting - C_Container.MoveItem / SwapItems instead of cursor pickup/drop
+64 -27
View File
@@ -992,28 +992,53 @@ function pfUI.api.GetBorderSize(pref)
end
end
-- The scale UIParent ends up with: one UI unit per screen pixel when pixel
-- perfect, else the game's own. The game applies its scale only after the
-- add-ons and their saved variables are loaded (CGGameUI::Initialize ends in
-- OnDisplaySizeChanged), so at load time it is worked out from the same
-- options and rules: uiScale while useUiScale is on and it is at least 0.64,
-- else 768 over the window height kept within 0.9 and 1. Windows narrower
-- than 4:3 cap either at 3/4 of their aspect.
local function GetUIScale()
if pfUI_config.global.pixelperfect == "1" then return PixelUtil.GetPixelToUIUnitFactor() end
local width, height = GetPhysicalScreenSize()
local aspect = GetCVar("widescreen") == "1" and width / height or 4/3
local function fit(scale)
return aspect < 4/3 and math.min(scale, aspect * .75) or scale
end
if GetCVar("useUiScale") == "1" then
local scale = fit(tonumber(GetCVar("uiScale")))
if scale >= .64 then return scale end
end
return math.max(fit(height > 768 and 768 / height or 1), .9)
end
-- [ UpdateUIScale ]
-- Gives UIParent the pixel perfect scale when enabled, else leaves the game's
-- own. The engine puts its own scale back on every window resize and scale
-- option change, so this runs again on each UI_SCALE_CHANGED. Borders keep
-- the pixel size they were built with, so a change of it offers a reload once.
function pfUI.api.UpdateUIScale()
if pfUI_config.global.pixelperfect == "1" then
UIParent:SetScale(PixelUtil.GetPixelToUIUnitFactor())
end
if not pfUI.pixel or pfUI.scaleprompt then return end
if math.abs(PixelUtil.GetPixelToUIUnitFactor() / UIParent:GetScale() - pfUI.pixel) < .0001 then return end
pfUI.scaleprompt = true
CreateQuestionDialog(T["The UI-Scale has changed. Reload the UI to redraw the borders?"], ReloadUI)
end
-- [ GetPerfectPixel ]
-- Returns a number that equals a real pixel on regular scaled frames.
-- Respects the current UI-scale and calculates a real pixel based on
-- the screen resolution and the 768px sized drawlayer.
-- Returns the size of one physical pixel in UIParent units.
function pfUI.api.GetPerfectPixel()
if pfUI.pixel then return pfUI.pixel end
if pfUI_config.appearance.border.pixelperfect == "1" then
local scale = GetCVar("useUiScale") == "1" and GetCVar("uiScale") or "1"
local resolution = GetCVar("gxResolution")
local _, _, screenwidth, screenheight = strfind(resolution, "(.+)x(.+)")
pfUI.pixel = 768 / screenheight / scale
pfUI.pixel = pfUI.pixel > 1 and 1 or pfUI.pixel
-- autodetect and zoom for HiDPI displays
if pfUI_config.appearance.border.hidpi == "1" then
pfUI.pixel = pfUI.pixel < .5 and pfUI.pixel * 2 or pfUI.pixel
end
else
pfUI.pixel = .7
end
pfUI.pixel = PixelUtil.GetPixelToUIUnitFactor() / GetUIScale()
pfUI.backdrop = {
bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0,
@@ -1158,8 +1183,11 @@ end
-- [ Set Castbar Shield ]
-- Colors a castbar for a cast that none of the player's own interrupts or
-- silences can stop. The bar and the icon's border both take the shield color,
-- so the state still reads on a bar configured without an icon. Not shielded
-- restores the normal cast/channel color and the configured border.
-- so the state still reads on a bar configured without an icon. The fill also
-- gets diagonal stripes, tiled at the image's own size so they keep their
-- spacing as the fill grows or shrinks. They sit on OVERLAY, under the bar's
-- text on the same layer. Not shielded restores the normal cast/channel color
-- and the configured border.
-- 'bar' [frame] the castbar statusbar
-- 'icon' [frame] the icon frame, or nil for a bar without one
-- 'shielded' [bool] whether the cast is uninterruptible
@@ -1171,6 +1199,18 @@ function pfUI.api.SetCastbarShield(bar, icon, shielded, isChannel)
bar:SetStatusBarColor(pfUI.api.GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
end
if shielded and not bar.shieldstripes then
bar.shieldstripes = bar:CreateTexture(nil, "OVERLAY")
bar.shieldstripes:SetTexture(pfUI.media["img:stripes"])
bar.shieldstripes:SetHorizTile(true)
bar.shieldstripes:SetVertTile(true)
bar.shieldstripes:SetVertexColor(1, 1, 1, .3)
bar.shieldstripes:SetAllPoints(bar:GetStatusBarTexture())
end
if bar.shieldstripes then
bar.shieldstripes:SetShown(shielded)
end
local iconborder = icon and icon.backdrop
if not iconborder then return end
@@ -1364,10 +1404,10 @@ function pfUI.api.EnableAutohide(frame, timeout, combat)
if not frame.hover.fade then
frame.hover.fade = frame:CreateAnimationGroup()
frame.hover.fade:SetToFinalAlpha(true)
local fade = frame.hover.fade:CreateAnimation("Alpha")
fade:SetChange(-1)
fade:SetDuration(1)
frame.hover.fade:SetScript("OnFinished", function() frame:SetAlpha(0) end)
end
if combat then
@@ -1469,13 +1509,10 @@ end
-- return object
function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
-- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren()
-- below, and the traceback stops here — useless, since all callers share this
-- line. pfUI shadows Lua's `error` (dropping the level arg and not throwing),
-- so fold the caller frame in via debugstack to name the offending skin, then
-- bail so we don't fall through and crash on frame:GetRegions() anyway.
-- below, where the message would name this line, shared by every caller;
-- level 2 names the offending skin instead.
if type(frame) ~= "table" or not frame.GetRegions then
error("GetNoNameObject: invalid parent frame\n" .. debugstack(2, 3, 0))
return
error("GetNoNameObject: invalid parent frame", 2)
end
local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
+112 -35
View File
@@ -71,6 +71,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("buffs", nil, "debuffrowsize", "16")
pfUI:UpdateConfig("buffs", nil, "textinside", "0")
pfUI:UpdateConfig("buffs", nil, "fontsize", "-1")
pfUI:UpdateConfig("buffs", nil, "timers", "both")
pfUI:UpdateConfig("buffbar", "pbuff", "enable", "0")
pfUI:UpdateConfig("buffbar", "pbuff", "use_unitfonts", "0")
@@ -128,9 +129,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "border", "color", "0.2,0.2,0.2,1")
pfUI:UpdateConfig("appearance", "border", "shadow", "0")
pfUI:UpdateConfig("appearance", "border", "shadow_intensity", ".35")
pfUI:UpdateConfig("appearance", "border", "pixelperfect", "1")
pfUI:UpdateConfig("appearance", "border", "force_blizz", "0")
pfUI:UpdateConfig("appearance", "border", "hidpi", "1")
pfUI:UpdateConfig("appearance", "border", "default", "3")
pfUI:UpdateConfig("appearance", "border", "nameplates", "-1")
pfUI:UpdateConfig("appearance", "border", "actionbars", "-1")
@@ -149,38 +148,9 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("cooldown", nil, "minutecolor", ".2,1,1,1")
pfUI:UpdateConfig("cooldown", nil, "hourcolor", ".2,.5,1,1")
pfUI:UpdateConfig("cooldown", nil, "daycolor", ".2,.2,1,1")
pfUI:UpdateConfig("cooldown", "actionbar", "text", "1")
pfUI:UpdateConfig("cooldown", "actionbar", "animation", "1")
pfUI:UpdateConfig("cooldown", "actionbar", "reverse", "0")
pfUI:UpdateConfig("cooldown", "bags", "text", "1")
pfUI:UpdateConfig("cooldown", "bags", "animation", "1")
pfUI:UpdateConfig("cooldown", "bags", "reverse", "0")
pfUI:UpdateConfig("cooldown", "blizzard", "text", "1")
pfUI:UpdateConfig("cooldown", "blizzard", "animation", "1")
pfUI:UpdateConfig("cooldown", "blizzard", "reverse", "0")
pfUI:UpdateConfig("cooldown", "unitframe", "text", "1")
pfUI:UpdateConfig("cooldown", "unitframe", "animation", "0")
pfUI:UpdateConfig("cooldown", "unitframe", "reverse", "1")
pfUI:UpdateConfig("cooldown", "indicator", "text", "1")
pfUI:UpdateConfig("cooldown", "indicator", "animation", "0")
pfUI:UpdateConfig("cooldown", "indicator", "reverse", "0")
pfUI:UpdateConfig("cooldown", "nameplate", "text", "1")
pfUI:UpdateConfig("cooldown", "nameplate", "animation", "0")
pfUI:UpdateConfig("cooldown", "nameplate", "reverse", "1")
pfUI:UpdateConfig("cooldown", "buff", "text", "1")
pfUI:UpdateConfig("cooldown", "buff", "animation", "1")
pfUI:UpdateConfig("cooldown", "buff", "reverse", "1")
pfUI:UpdateConfig("cooldown", "tooltip", "text", "1")
pfUI:UpdateConfig("cooldown", "tooltip", "animation", "0")
pfUI:UpdateConfig("cooldown", "tooltip", "reverse", "0")
pfUI:UpdateConfig("cooldown", "totem", "text", "1")
pfUI:UpdateConfig("cooldown", "totem", "animation", "1")
pfUI:UpdateConfig("cooldown", "totem", "reverse", "1")
pfUI:UpdateConfig("cooldown", "lossofcontrol", "text", "1")
pfUI:UpdateConfig("cooldown", "lossofcontrol", "animation", "1")
pfUI:UpdateConfig("cooldown", "lossofcontrol", "reverse", "0")
pfUI:UpdateConfig("cooldown", "foreign", "text", "0")
pfUI:UpdateConfig("cooldown", "foreign", "animation", "1")
pfUI:UpdateConfig("cooldown", nil, "blizzard_timers", "both")
pfUI:UpdateConfig("cooldown", nil, "lossofcontrol_timers", "both")
pfUI:UpdateConfig("cooldown", nil, "foreign_timers", "sweep")
pfUI:UpdateConfig("appearance", "castbar", "castbarcolor", ".7,.7,.9,.8")
pfUI:UpdateConfig("appearance", "castbar", "channelcolor", ".9,.9,.7,.8")
@@ -207,6 +177,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "bags", "autoSortOnOpen", "0")
pfUI:UpdateConfig("appearance", "bags", "sortreverse", "0")
pfUI:UpdateConfig("appearance", "bags", "sortprioreverse", "0")
pfUI:UpdateConfig("appearance", "bags", "timers", "both")
pfUI:UpdateConfig("appearance", "minimap", "size", "140")
pfUI:UpdateConfig("appearance", "minimap", "arrowscale", "1")
pfUI:UpdateConfig("appearance", "minimap", "zonetext", "off")
@@ -460,6 +431,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", "raid", "raidlayout", "8x5")
pfUI:UpdateConfig("unitframes", "raid", "raidpadding", "3")
pfUI:UpdateConfig("unitframes", "raid", "raidfill", "VERTICAL")
pfUI:UpdateConfig("unitframes", "raid", "raidgrowleft", "0")
pfUI:UpdateConfig("unitframes", "raid", "collapse", "0")
pfUI:UpdateConfig("unitframes", "raid", "raidgrouplabel", "0")
pfUI:UpdateConfig("unitframes", "raid", "grouplabelxoff", "0")
@@ -550,6 +522,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", unit, "bufflimit", "32")
pfUI:UpdateConfig("unitframes", unit, "buffperrow", "8")
pfUI:UpdateConfig("unitframes", unit, "ownbuffsize", "-1")
pfUI:UpdateConfig("unitframes", unit, "buffoffx", "0")
pfUI:UpdateConfig("unitframes", unit, "buffoffy", "0")
pfUI:UpdateConfig("unitframes", unit, "buffspacingx", "1")
pfUI:UpdateConfig("unitframes", unit, "buffspacingy", "1")
pfUI:UpdateConfig("unitframes", unit, "debuffs", "TOPLEFT")
@@ -557,8 +531,11 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", unit, "debufflimit", "32")
pfUI:UpdateConfig("unitframes", unit, "debuffperrow", "8")
pfUI:UpdateConfig("unitframes", unit, "owndebuffsize", "-1")
pfUI:UpdateConfig("unitframes", unit, "debuffoffx", "0")
pfUI:UpdateConfig("unitframes", unit, "debuffoffy", "0")
pfUI:UpdateConfig("unitframes", unit, "debuffspacingx", "1")
pfUI:UpdateConfig("unitframes", unit, "debuffspacingy", "1")
pfUI:UpdateConfig("unitframes", unit, "aura_timers", "text")
pfUI:UpdateConfig("unitframes", unit, "invert_healthbar", "0")
pfUI:UpdateConfig("unitframes", unit, "verticalbar", "0")
pfUI:UpdateConfig("unitframes", unit, "buff_indicator", "0")
@@ -579,6 +556,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", unit, "indicator_size", "10")
pfUI:UpdateConfig("unitframes", unit, "indicator_spacing","1")
pfUI:UpdateConfig("unitframes", unit, "indicator_pos", "TOPLEFT")
pfUI:UpdateConfig("unitframes", unit, "indicator_timers", "text")
pfUI:UpdateConfig("unitframes", unit, "clickcast", "0")
pfUI:UpdateConfig("unitframes", unit, "faderange", "0")
@@ -697,6 +675,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("bars", nil, "oomcolor", ".2,.2,1,1")
pfUI:UpdateConfig("bars", nil, "showna", "1")
pfUI:UpdateConfig("bars", nil, "nacolor", ".3,.3,.3,1")
pfUI:UpdateConfig("bars", nil, "procglow", "1")
pfUI:UpdateConfig("bars", nil, "showequipped", "1")
pfUI:UpdateConfig("bars", nil, "eqcolor", ".2,.8,.2,.2")
pfUI:UpdateConfig("bars", nil, "shiftdrag", "1")
@@ -709,6 +688,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("bars", nil, "count_color", ".2,1,.8,1")
pfUI:UpdateConfig("bars", nil, "bind_size", "8")
pfUI:UpdateConfig("bars", nil, "bind_color", "1,1,0,1")
pfUI:UpdateConfig("bars", nil, "timers", "both")
pfUI:UpdateConfig("bars", "gryphons", "texture", "None")
pfUI:UpdateConfig("bars", "gryphons", "color", ".6,.6,.6,1")
@@ -722,6 +702,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("totems", nil, "iconsize", "26")
pfUI:UpdateConfig("totems", nil, "spacing", "3")
pfUI:UpdateConfig("totems", nil, "showbg", "0")
pfUI:UpdateConfig("totems", nil, "timers", "both")
pfUI:UpdateConfig("panel", nil, "use_unitfonts", "0")
pfUI:UpdateConfig("panel", nil, "hide_leftchat", "0")
@@ -834,6 +815,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("tooltip", "statusbar", "texture", "Interface\\AddOns\\pfUI\\img\\bar")
pfUI:UpdateConfig("tooltip", nil, "font_tooltip", "Interface\\AddOns\\pfUI\\fonts\\Myriad-Pro.ttf")
pfUI:UpdateConfig("tooltip", nil, "font_tooltip_size", "12")
pfUI:UpdateConfig("tooltip", nil, "aura_timers", "text")
-- Throttle Settings
@@ -958,6 +940,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("nameplates", nil, "glowcolor", "1,1,1,1")
pfUI:UpdateConfig("nameplates", nil, "targetzoom", "0")
pfUI:UpdateConfig("nameplates", nil, "targetzoomval", ".40")
pfUI:UpdateConfig("nameplates", nil, "debuff_timers", "text")
pfUI:UpdateConfig("nameplates", nil, "notargalpha", ".75")
pfUI:UpdateConfig("nameplates", nil, "healthtexture", "Interface\\AddOns\\pfUI\\img\\bar")
pfUI:UpdateConfig("nameplates", "name", "fontstyle", "OUTLINE")
@@ -1431,6 +1414,100 @@ function pfUI:MigrateConfig()
pfUI_config.unitframes.always2dportrait = nil
end
-- migrate the old cooldown settings into each feature's timers setting;
-- keyed on the old settings rather than the version, like the portrait
-- style. Reverse options are dropped: the direction follows what a
-- cooldown times.
local cooldown = pfUI_config.cooldown
-- 'mode' with its numbers and sweep replaced by 'text' and 'sweep' ("1" or
-- "0"), each kept when nil
local function timers(mode, text, sweep)
local numbers = mode == "text" or mode == "both"
local swipe = mode == "sweep" or mode == "both"
if text then numbers = text == "1" end
if sweep then swipe = sweep == "1" end
if numbers and swipe then return "both" end
return numbers and "text" or swipe and "sweep" or "off"
end
-- every unit frame's settings
local function units(func)
for _, unit in pairs(pfUI_config.unitframes) do
if type(unit) == "table" and unit.aura_timers then func(unit) end
end
end
-- the shared look and the foreign and Blizzard toggles of appearance.cd
local cd = pfUI_config.appearance.cd
if cd then
for _, entry in pairs({ "font", "font_size", "dynamicsize", "threshold", "milliseconds",
"lowcolor", "normalcolor", "minutecolor", "hourcolor", "daycolor" }) do
cooldown[entry] = cd[entry] or cooldown[entry]
end
local sweep = cd.hideanim and (cd.hideanim == "1" and "0" or "1")
cooldown.foreign_timers = timers(cooldown.foreign_timers, cd.foreign, sweep)
-- spellbook and character slots took the foreign sweep, and with the
-- Blizzard toggle off the foreign numbers too
cooldown.blizzard_timers = timers(cooldown.blizzard_timers, cd.blizzard == "0" and (cd.foreign or "0") or nil, sweep)
-- pfUI drew its own numbers before; the native countdown shows them
-- only with this cvar on, which only the first-run setup sets
SetCVar("countdownForCooldowns", "1")
pfUI_config.appearance.cd = nil
end
-- the options of each module
local buffs, nameplates = pfUI_config.buffs, pfUI_config.nameplates
buffs.timers = timers(buffs.timers, nil, buffs.cooldown_anim)
nameplates.debuff_timers = timers(nameplates.debuff_timers, nameplates.debufftext, nameplates.debuffanim)
if nameplates.debufftimers == "0" then nameplates.debuff_timers = "off" end
units(function(unit)
unit.aura_timers = timers(unit.aura_timers, unit.cooldown_text, unit.cooldown_anim)
unit.indicator_timers = timers(unit.indicator_timers, unit.indicator_time)
unit.cooldown_text, unit.cooldown_anim, unit.cooldown_reverse, unit.indicator_time = nil, nil, nil, nil
end)
pfUI_config.bars.cooldown_reverse = nil
pfUI_config.appearance.bags.cooldown_reverse = nil
pfUI_config.totems.cooldown_reverse = nil
buffs.cooldown_anim, buffs.cooldown_reverse = nil, nil
nameplates.debufftext, nameplates.debuffanim, nameplates.debreverse, nameplates.debufftimers = nil, nil, nil, nil
-- the cooldown kinds of Settings -> Cooldown, each with its numbers and
-- sweep, into the setting that replaced it
local kinds = {
actionbar = { pfUI_config.bars, "timers" },
bags = { pfUI_config.appearance.bags, "timers" },
blizzard = { cooldown, "blizzard_timers" },
nameplate = { nameplates, "debuff_timers" },
buff = { buffs, "timers" },
tooltip = { pfUI_config.tooltip, "aura_timers" },
totem = { pfUI_config.totems, "timers" },
lossofcontrol = { cooldown, "lossofcontrol_timers" },
foreign = { cooldown, "foreign_timers" },
unitframe = "aura_timers",
indicator = "indicator_timers",
}
for kind, target in pairs(kinds) do
local old = cooldown[kind]
if type(old) == "table" then
if type(target) == "string" then
units(function(unit) unit[target] = timers(unit[target], old.text, old.animation) end)
else
target[1][target[2]] = timers(target[1][target[2]], old.text, old.animation)
end
cooldown[kind] = nil
end
end
pfUI_config.bars.cd_size = nil
-- Remove "Show only own debuffs" from unitframes and nameplates
-- (feature was removed; only Target Debuff Bar in buffwatch keeps it)
if pfUI_config.nameplates then
@@ -1459,4 +1536,4 @@ function pfUI:MigrateConfig()
else
pfUI_config.version = pfUI.version.string
end
end
end
+58 -47
View File
@@ -1,16 +1,20 @@
-- load pfUI environment
setfenv(1, pfUI:GetEnvironment())
-- Every pfUI cooldown is a Cooldown frame from CreateCooldown, made for one
-- kind of timer. Settings -> Cooldown (C.cooldown) sets for each kind its
-- countdown numbers, its sweep and the sweep's direction, and shares one font
-- and one formatter between them all. The numbers are the frame's own
-- countdown (ClassicAPI): the engine ticks them, gated by the
-- countdownForCooldowns cvar. Other addons' Cooldown frames take the
-- "foreign" settings as their timers start.
-- Every pfUI cooldown is a Cooldown frame from CreateCooldown. The settings
-- of the feature it belongs to pick its timers: none, numbers, sweep or both.
-- Settings -> Cooldown (C.cooldown) sets one font and one formatter for them
-- all. The numbers are the frame's own countdown (ClassicAPI): the engine
-- ticks them, gated by the countdownForCooldowns cvar. Other addons' Cooldown
-- frames take the "foreign" timers as their timers start.
-- kinds that time spells or items: no numbers for the global cooldown
local spell = { actionbar = true, bags = true, blizzard = true, foreign = true }
local function ShowNumbers(mode)
return mode == "text" or mode == "both"
end
local function ShowSweep(mode)
return mode == "sweep" or mode == "both"
end
-- every cooldown CreateCooldown made, for UpdateCooldowns
local cooldowns = {}
@@ -58,60 +62,60 @@ function pfUI.api.GetCooldownFontSize(height)
return size
end
-- sizes the numbers to the cooldown's parent; the size follows its height,
-- so a timer start calls this again whenever that height changed
local function UpdateFont(cooldown)
local height = (cooldown:GetParent() or cooldown):GetHeight()
cooldown.pfCountdownHeight = height
-- sizes the numbers to the height of the frame the cooldown covers
local function UpdateFont(cooldown, height)
cooldown:GetCountdownFontString():SetFont(pfUI.media[C.cooldown.font], pfUI.api.GetCooldownFontSize(height), "OUTLINE")
end
-- applies the settings of its kind to one of pfUI's cooldowns
local function SetupCooldown(cooldown)
local kind = cooldown.pfCooldownKind
local config = C.cooldown[kind]
local sweep = config.animation == "1" and not cooldown.pfTimeLeftOnly
cooldown:SetHideCountdownNumbers(config.text ~= "1")
cooldown:SetDrawSwipe(sweep)
cooldown:SetDrawBling(sweep)
cooldown:SetReverse(config.reverse == "1")
cooldown:SetMinimumCountdownDuration(spell[kind] and tonumber(C.cooldown.threshold) * 1000 or 0)
cooldown:SetCountdownFormatter(GetFormatter())
UpdateFont(cooldown)
-- a pfUI cooldown covers its parent, so it resizes with it; a hidden one is
-- laid out, and this runs, as it shows
local function OnSizeChanged(cooldown, width, height)
UpdateFont(cooldown, height)
end
-- applies the "foreign" settings to another addon's cooldown. They only take
-- applies its timers setting to one of pfUI's cooldowns
local function SetupCooldown(cooldown)
local kind = cooldown.pfCooldownKind
local mode = cooldown.pfCooldownConfig[cooldown.pfCooldownKey]
local sweep = ShowSweep(mode) and not cooldown.pfTimeLeftOnly
cooldown:SetHideCountdownNumbers(not ShowNumbers(mode))
cooldown:SetDrawSwipe(sweep)
cooldown:SetDrawBling(sweep)
cooldown:SetReverse(kind == "aura")
cooldown:SetMinimumCountdownDuration(kind == "spell" and tonumber(C.cooldown.threshold) * 1000 or 0)
cooldown:SetCountdownFormatter(GetFormatter())
UpdateFont(cooldown, cooldown:GetParent():GetUnscaledHeight())
end
-- applies the "foreign" timers to another addon's cooldown. They only take
-- the numbers or the sweep away, which the addon may have set up itself, so
-- turning one back on needs a reload.
local function SetupForeign(cooldown)
local config = C.cooldown.foreign
local mode = C.cooldown.foreign_timers
if config.animation ~= "1" then
if not ShowSweep(mode) then
cooldown:SetDrawSwipe(false)
cooldown:SetDrawBling(false)
end
-- omnicc's flag for cooldowns that want no numbers
if config.text ~= "1" or cooldown.noCooldownCount then
if not ShowNumbers(mode) or cooldown.noCooldownCount then
cooldown:SetHideCountdownNumbers(true)
return
end
cooldown:SetMinimumCountdownDuration(tonumber(C.cooldown.threshold) * 1000)
cooldown:SetCountdownFormatter(GetFormatter())
UpdateFont(cooldown)
UpdateFont(cooldown, (cooldown:GetParent() or cooldown):GetUnscaledHeight())
end
-- runs as a pfUI cooldown's timer starts: a timer with a total brings back
-- the sweep a time-left-only one took away, and the font follows a changed
-- height
-- the sweep a time-left-only one took away
local function StartTimer(cooldown, timeleftOnly)
if cooldown.pfTimeLeftOnly ~= timeleftOnly then
cooldown.pfTimeLeftOnly = timeleftOnly
SetupCooldown(cooldown)
elseif cooldown:GetParent():GetHeight() ~= cooldown.pfCountdownHeight then
UpdateFont(cooldown)
end
end
@@ -129,17 +133,23 @@ hooksecurefunc("CooldownFrame_SetTimer", function(cooldown)
end)
-- [ CreateCooldown ] --
-- Creates one of pfUI's cooldowns: a Cooldown frame over all of 'parent'
-- with the settings of its kind. Start it with CooldownFrame_SetTimer.
-- Creates one of pfUI's cooldowns: a Cooldown frame over all of 'parent'.
-- Start it with CooldownFrame_SetTimer.
-- 'parent' [frame] the frame the cooldown covers
-- 'kind' [string] what it times, a group of C.cooldown: actionbar,
-- bags, blizzard, unitframe, indicator, nameplate,
-- buff, tooltip, totem or lossofcontrol
-- 'kind' [string] what it times: "spell" (spells and items: the sweep
-- fills, no numbers for the global cooldown), "aura"
-- (the sweep drains) or "timer" (the sweep fills)
-- 'config' [table] the settings that hold its timers
-- 'key' [string] the timers setting in 'config': "off", "text",
-- "sweep" or "both"
-- 'name' [string] optional frame name
-- return [frame] the Cooldown frame
function pfUI.api.CreateCooldown(parent, kind, name)
function pfUI.api.CreateCooldown(parent, kind, config, key, name)
local cooldown = CreateFrame("Cooldown", name, parent, "CooldownFrameTemplate")
cooldown.pfCooldownKind = kind
cooldown.pfCooldownConfig = config
cooldown.pfCooldownKey = key
cooldown:SetScript("OnSizeChanged", OnSizeChanged)
table.insert(cooldowns, cooldown)
SetupCooldown(cooldown)
return cooldown
@@ -186,9 +196,10 @@ function pfUI.api.GetAuraTimer(duration, expirationTime)
return start, dur
end
-- [ GetColoredTimeString ] --
-- 'remaining' [number] the time in seconds that should be converted
-- return [string] the time as cooldown numbers show it
function pfUI.api.GetColoredTimeString(remaining)
return GetFormatter():FormatNumber(remaining)
-- [ GetTimeFormatter ] --
-- The formatter cooldown numbers use, for other timers to show time the same
-- way. Hold it rather than a copy: a settings change updates it in place.
-- return [formatter] a NumericRuleFormatter
function pfUI.api.GetTimeFormatter()
return GetFormatter()
end
+24 -10
View File
@@ -86,6 +86,25 @@ do
end
end
-- [ CreateMenuDivider ]
-- Adds a divider to a pfUIMenuStyleMixin menu: a 1px line in the border
-- color in place of the stock divider art. The style itself cannot do this,
-- as menu entries draw their own art.
-- 'description' [table] the menu description to add the divider to
-- returns: [table] the divider's element description
do
local function SkinDivider(frame)
frame.divider:SetHeight(1)
frame.divider:SetColorTexture(GetStringColor(pfUI_config.appearance.border.color))
end
function pfUI.api.CreateMenuDivider(description)
local divider = description:CreateDivider()
divider:AddInitializer(SkinDivider)
return divider
end
end
-- [ pfUIDropdownTemplate ]
-- Mixin of the template in ui-widgets.xml: a dropdown that shows its selection
-- as text, with pfUI's arrow box at the right.
@@ -582,7 +601,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
button:HookScript("OnClick", function()
local DropDownListWidth = DropDownList1:GetWidth()
local DropDownFrameWidth = frame.backdrop:GetWidth()
local DropDownFrameWidth = frame.backdrop:GetUnscaledWidth()
if DropDownListWidth < DropDownFrameWidth then
local diff = DropDownFrameWidth - DropDownListWidth
DropDownList1:SetWidth(DropDownList1:GetWidth() + diff)
@@ -815,10 +834,7 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
end)
infobox:SetScript("OnUpdate", function()
local time = infobox.lastshow + infobox.duration - GetTime()
infobox.timeout:SetValue(time)
if GetTime() > infobox.lastshow + infobox.duration then
if infobox.duration:HasExpired() then
if not infobox.fade:IsPlaying() then infobox.fade:Play() end
elseif MouseIsOverHitRect(this) then
this:SetAlpha(max(0.4, this:GetAlpha() - .1))
@@ -838,6 +854,8 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
infobox.timeout = CreateFrame("StatusBar", nil, infobox)
infobox.timeout:SetStatusBarTexture(pfUI.media["img:bar"])
infobox.timeout:SetStatusBarColor(.3,1,.8,1)
infobox.duration = C_DurationUtil.CreateDuration()
infobox.timeout:SetTimerDuration(infobox.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
infobox:ClearAllPoints()
infobox.text:SetAllPoints(infobox)
@@ -853,11 +871,7 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
end
infobox.text:SetText(text)
infobox.timeout:SetMinMaxValues(0, time)
infobox.timeout:SetValue(time)
infobox.duration = time
infobox.lastshow = GetTime()
infobox.duration:SetTimeFromStart(GetTime(), time)
infobox:SetSize(infobox.text:GetStringWidth() + 50, height)
infobox:SetParent(parent)
+266 -227
View File
@@ -19,11 +19,11 @@ local auraCorners = {
}
-- Flows the shown icons of an aura block outward from the frame edge, starting
-- `offset` away from it (its own row gap when nil): `perrow` icons per row,
-- each row as tall as its largest icon. The first `icons.own` icons are the
-- player's (C_UnitAuras lists them first) and use `icons.ownsize`. Returns the
-- offset just past the block's last row, where a block sharing the same corner
-- continues.
-- `offset` away from it (its own row gap when nil) and shifted on screen by
-- `offx`/`offy`: `perrow` icons per row, each row as tall as its largest icon.
-- The first `icons.own` icons are the player's (C_UnitAuras lists them first)
-- and use `icons.ownsize`. Returns the offset just past the block's last
-- shifted row, where a block sharing the same corner continues.
local function LayoutAuraBlock(unit, icons, offset)
local corner = auraCorners[icons.pos]
local anchor = unit.config.portraitheight ~= "-1" and unit.hp or unit
@@ -42,7 +42,7 @@ local function LayoutAuraBlock(unit, icons, offset)
local size = i <= icons.own and icons.ownsize or icons.size
icons[i]:SetSize(size, size)
icons[i]:ClearAllPoints()
icons[i]:SetPoint(corner[1], anchor, icons.pos, corner[2] * x, corner[3] * offset)
icons[i]:SetPoint(corner[1], anchor, icons.pos, icons.offx + corner[2] * x, icons.offy + corner[3] * offset)
x = x + size + icons.gapx
if size > rowsize then rowsize = size end
@@ -50,7 +50,7 @@ local function LayoutAuraBlock(unit, icons, offset)
i = i + 1
end
if col > 0 then offset = offset + rowsize + icons.gapy end
if col > 0 then offset = offset + rowsize + icons.gapy + corner[3] * icons.offy end
return offset
end
@@ -248,6 +248,48 @@ local VISIBILITY_EVENTS = {
UNIT_TARGET = true, -- a link of a chained frame's unit retargeted
}
-- The units of a chain that unit events name by token, whatever nampower does
-- with its GUID events: the focus, and the focus's target, whose unit events
-- ClassicAPI fires as "focustarget" from the unit's own fields.
local CHAIN_TOKENS = { focus = true, focustarget = true }
-- The events a frame hears hidden or not: the visibility events that concern
-- the unit it was created for (raid.lua remaps a raid frame's label, so this is
-- decided once, at creation), and the logout guard. UNIT_TARGET belongs to
-- RegisterChainEvents, and focus.lua drives the focus frames itself.
local function GetIdleEvents(label, fname)
local events = { PLAYER_ENTERING_WORLD = true, PLAYER_LOGOUT = true }
if label:find("^party") or label:find("^raid")
or fname == "Group0" or fname == "PartyPet0" or fname == "Party0Target" then
events.PARTY_MEMBERS_CHANGED = true
events.RAID_ROSTER_UPDATE = true
end
if label:find("^target") then events.PLAYER_TARGET_CHANGED = true end
if label:find("pet") then events.UNIT_PET = true end
return events
end
-- The events that only redraw what a frame shows. A hidden frame drops them,
-- and OnShow's full refresh catches up on whatever it missed.
local SHOWN_EVENTS = {
"PLAYER_AURAS_CHANGED", -- label=player && frame=buff
"PLAYER_EQUIPMENT_CHANGED", -- label=player && frame=buff (ClassicAPI: weapon-enchant buffs)
"PARTY_MEMBERS_CHANGED", -- frame=leaderIcon
"PARTY_LEADER_CHANGED", -- frame=leaderIcon
"RAID_ROSTER_UPDATE", -- frame=raidIcon
"PLAYER_UPDATE_RESTING", -- frame=restIcon
"PARTY_LOOT_METHOD_CHANGED", -- frame=lootIcon
"RAID_TARGET_UPDATE", -- frame=raidIcon
}
local function RegisterShownEvents(frame)
for i = 1, table.getn(SHOWN_EVENTS) do
frame:RegisterEvent(SHOWN_EVENTS[i])
end
-- label=pet; the player's own pet only, not every pet in view
frame:RegisterUnitEvent("UNIT_HAPPINESS", "pet")
end
-- ============================================================================
-- GetUnitStats - health + power for a unit token.
-- Returns: hp, maxHp, power, maxPower, powerType
@@ -320,7 +362,7 @@ pfUI.events:RegisterCallback("aggro:changed", function(_, old, new)
if frame.aggro then
local guid = UnitGUID(frame.label .. frame.id)
if guid and (guid == old or guid == new) then
frame.update_combat = true
pfUI.uf:QueueUpdate(frame, "combat")
end
end
end
@@ -445,7 +487,9 @@ function pfUI.uf:UpdateVisibility()
--
-- A chained unit -- another unit's target, "targettarget" or "party1target"
-- -- has no token that unit events name, so the frame follows it by GUID,
-- which every unit event also fires with. It follows the unit only while
-- which nampower fires every unit event with too, unless its GUID events
-- are off or filtered. The focus's target is named (CHAIN_TOKENS), so that
-- frame follows its token instead. It follows the unit only while
-- the client has it: a link's target can be out of sight, and its coming
-- back is then a new unit too. The unit changes with no event of its own
-- whenever a link of the chain retargets; UNIT_TARGET on every link reports
@@ -454,9 +498,18 @@ function pfUI.uf:UpdateVisibility()
self.chain = visibility ~= "hide" and unitstr:match("^(.+)target$") or nil
if self.chain then
local guid = UnitName(unitstr) and UnitGUID(unitstr)
if guid ~= self.eventunit then self.update_full = true end
self:RegisterUnitEvents(guid)
if guid ~= self.unitguid then pfUI.uf:QueueUpdate(self, "full") end
self.unitguid = guid
self:RegisterUnitEvents(guid and CHAIN_TOKENS[unitstr] and unitstr or guid)
else
-- A pet frame's token gets the pet's unit events, but a new pet behind it
-- (summoned, or remapped by raid.lua) comes with none of its own; the
-- visibility timer's GUID compare turns it into the full refresh.
if self.tick then
local guid = UnitGUID(unitstr)
if guid ~= self.unitguid then pfUI.uf:QueueUpdate(self, "full") end
self.unitguid = guid
end
self:RegisterUnitEvents(visibility ~= "hide" and unitstr or nil)
end
self:RegisterChainEvents(self.chain)
@@ -728,7 +781,6 @@ function pfUI.uf:UpdateConfig()
if f.config.hitindicator == "1" then
f.feedbackText:SetFont(pfUI.media[f.config.hitindicatorfont], f.config.hitindicatorsize, "OUTLINE")
f.feedbackFontHeight = f.config.hitindicatorsize
f.feedbackStartTime = GetTime()
if f.config.portrait == "bar" or f.config.portrait == "off" then
f.feedbackText:SetParent(f.texts)
f.feedbackText:ClearAllPoints()
@@ -740,6 +792,7 @@ function pfUI.uf:UpdateConfig()
end
f.combatfeedback = true
else
f.feedback:Stop()
f.feedbackText:Hide()
f.combatfeedback = nil
end
@@ -808,12 +861,16 @@ function pfUI.uf:UpdateConfig()
f.incHeal.texture:SetVertexColor(cr, cg, cb, ca)
f.incHeal:Hide()
if f.config.verticalbar == "0" then
f.incHeal:ClearAllPoints()
f.incHeal:SetPoint("TOPLEFT", f.hp.bar, "TOPLEFT", 0, 0)
-- the bar texture covers only the fill, so a prediction anchored to it
-- starts at the edge of the health and follows it as the bar animates.
-- An inverted bar fills with the missing health, so it starts at the bar.
f.incHeal:ClearAllPoints()
if f.config.invert_healthbar == "1" then
f.incHeal:SetPoint(f.config.verticalbar == "0" and "TOPLEFT" or "BOTTOM", f.hp.bar)
elseif f.config.verticalbar == "0" then
f.incHeal:SetPoint("TOPLEFT", f.hp.bar:GetStatusBarTexture(), "TOPRIGHT")
else
f.incHeal:ClearAllPoints()
f.incHeal:SetPoint("BOTTOM", f.hp.bar, "BOTTOM", 0, 0)
f.incHeal:SetPoint("BOTTOMLEFT", f.hp.bar:GetStatusBarTexture(), "TOPLEFT")
end
f.ressIcon:SetFrameLevel(16)
@@ -877,6 +934,8 @@ function pfUI.uf:UpdateConfig()
f.buffs.size = tonumber(f.config.buffsize)
f.buffs.ownsize = f.config.ownbuffsize == "-1" and f.buffs.size or tonumber(f.config.ownbuffsize)
f.buffs.perrow = tonumber(f.config.buffperrow)
f.buffs.offx = tonumber(f.config.buffoffx)
f.buffs.offy = tonumber(f.config.buffoffy)
f.buffs.gapx = aura_border + tonumber(f.config.buffspacingx)
f.buffs.gapy = aura_border + tonumber(f.config.buffspacingy)
f.buffs.own = 0
@@ -899,7 +958,7 @@ function pfUI.uf:UpdateConfig()
f.buffs[i]:SetFrameLevel(12)
f.buffs[i]:RegisterForClicks("RightButtonUp")
f.buffs[i].cd = f.buffs[i].cd or CreateCooldown(f.buffs[i], "unitframe", f.buffs[i]:GetName() .. "Cooldown")
f.buffs[i].cd = f.buffs[i].cd or CreateCooldown(f.buffs[i], "aura", f.config, "aura_timers", f.buffs[i]:GetName() .. "Cooldown")
f.buffs[i].cd:SetFrameLevel(14)
f.buffs[i].id = i
@@ -927,6 +986,8 @@ function pfUI.uf:UpdateConfig()
f.debuffs.size = tonumber(f.config.debuffsize)
f.debuffs.ownsize = f.config.owndebuffsize == "-1" and f.debuffs.size or tonumber(f.config.owndebuffsize)
f.debuffs.perrow = tonumber(f.config.debuffperrow)
f.debuffs.offx = tonumber(f.config.debuffoffx)
f.debuffs.offy = tonumber(f.config.debuffoffy)
f.debuffs.gapx = aura_border + tonumber(f.config.debuffspacingx)
f.debuffs.gapy = aura_border + tonumber(f.config.debuffspacingy)
f.debuffs.own = 0
@@ -950,7 +1011,7 @@ function pfUI.uf:UpdateConfig()
f.debuffs[i]:RegisterForClicks("RightButtonUp")
f.debuffs[i]:SetNormalTexture(nil)
f.debuffs[i].cd = f.debuffs[i].cd or CreateCooldown(f.debuffs[i], "unitframe", f.debuffs[i]:GetName() .. "Cooldown")
f.debuffs[i].cd = f.debuffs[i].cd or CreateCooldown(f.debuffs[i], "aura", f.config, "aura_timers", f.debuffs[i]:GetName() .. "Cooldown")
f.debuffs[i].cd:SetFrameLevel(14)
f.debuffs[i].id = i
@@ -980,8 +1041,19 @@ function pfUI.uf:UpdateConfig()
end
function pfUI.uf.OnShow()
pfUI.uf:RefreshUnit(this, "portrait")
pfUI.uf:RefreshUnit(this, "base")
RegisterShownEvents(this)
pfUI.uf:RefreshUnit(this, "all")
-- flags left from while the frame was off screen, and the portrait's model
pfUI.uf:QueueUpdate(this)
end
function pfUI.uf.OnHide()
for i = 1, table.getn(SHOWN_EVENTS) do
if not this.idleevents[SHOWN_EVENTS[i]] then
this:UnregisterEvent(SHOWN_EVENTS[i])
end
end
this:UnregisterEvent("UNIT_HAPPINESS")
end
function pfUI.uf.OnEvent()
@@ -989,7 +1061,6 @@ function pfUI.uf.OnEvent()
if event == "PLAYER_LOGOUT" then
this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
this:SetScript("OnUpdate", nil)
-- the visibility timer keeps ticking, so leaving this frame on its list
-- would have UpdateVisibility re-register the unit events we just
-- dropped -- straight back into the crash 132 this branch prevents
@@ -1005,13 +1076,13 @@ function pfUI.uf.OnEvent()
event == "RAID_ROSTER_UPDATE" or
event == "PLAYER_UPDATE_RESTING"
then
this.update_indicators = true
pfUI.uf:QueueUpdate(this, "indicators")
end
-- abort on broken unitframes (e.g focus)
if not this.label then return end
-- a hidden frame runs no OnUpdate, so its unit coming into existence has to
-- a hidden frame does not refresh, so its unit coming into existence has to
-- show it from here
if VISIBILITY_EVENTS[event] then
this:UpdateVisibility()
@@ -1019,11 +1090,11 @@ function pfUI.uf.OnEvent()
-- update regular frames
if event == "PLAYER_ENTERING_WORLD" then
this.update_full = true
pfUI.uf:QueueUpdate(this, "full")
-- Clear GUID tracking on zone change for full rebuild
if pfUI.uf.ClearGuidTracking then pfUI.uf.ClearGuidTracking() end
elseif this.label == "target" and event == "PLAYER_TARGET_CHANGED" then
this.update_full = true
pfUI.uf:QueueUpdate(this, "full")
-- immediately update raid icon to avoid delay on target swap
if this.raidIcon and this.config and this.config.raidicon == "1" then
local raidIcon = UnitName("target") and GetRaidTargetIndex("target")
@@ -1042,26 +1113,26 @@ function pfUI.uf.OnEvent()
local oldGuid = pfUI.uf.guidTracker.frameToGuid[this]
if newGuid ~= oldGuid then
pfUI.uf.guidTracker.frameToGuid[this] = newGuid
this.update_full = true
pfUI.uf:QueueUpdate(this, "full")
end
else
this.update_full = true
pfUI.uf:QueueUpdate(this, "full")
end
elseif ( this.label == "raid" or this.label == "party" ) and event == "RAID_ROSTER_UPDATE" then
-- Note: Smart GUID-based updates are handled in raid.lua OnUpdate
-- Note: Smart GUID-based updates are handled in raid.lua UpdateRoster
-- after frame IDs are reassigned. We don't set update_full here anymore
-- for raid frames to avoid the freeze.
if this.label == "party" then
-- Party frames still need the old logic (no smart tracking yet)
this.update_full = true
pfUI.uf:QueueUpdate(this, "full")
end
-- Raid frames: update_full is set by raid.lua GUID tracker
elseif this.label == "pet" and event == "UNIT_PET" then
this.update_full = true
pfUI.uf:QueueUpdate(this, "full")
elseif this.label == "player" and (event == "PLAYER_AURAS_CHANGED" or event == "PLAYER_EQUIPMENT_CHANGED") then
this.update_aura = true
pfUI.uf:QueueUpdate(this, "aura")
elseif this.label == "pet" and event == "UNIT_HAPPINESS" then
this.update_full = true
pfUI.uf:QueueUpdate(this, "full")
elseif event == "UNIT_IN_RANGE_UPDATE" then
-- registered for the frame's unit alone, and fired in the filter's lower
-- case spelling, which a GUID's compare below would miss
@@ -1073,224 +1144,180 @@ function pfUI.uf.OnEvent()
-- GUID, so filtering on one of the two never wakes the frame twice.
elseif arg1 and arg1 == this.eventunit then
if event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" then
this.update_portrait = true
pfUI.uf:QueueUpdate(this, "portrait")
elseif event == "UNIT_AURA" then
this.update_aura = true
pfUI.uf:QueueUpdate(this, "aura")
elseif event == "UNIT_AURA_DELTA" then
-- an aura that only refreshed or restacked leaves every aura in its
-- slot, so the icons need their timers and stacks, not a rebuild
if arg2.isFullUpdate or arg2.addedAuraInstanceIDs or arg2.removedAuraInstanceIDs then
this.update_aura = true
pfUI.uf:QueueUpdate(this, "aura")
else
this.update_auratime = true
pfUI.uf:QueueUpdate(this, "auratime")
end
elseif event == "UNIT_POWER_UPDATE" or event == "UNIT_MAXPOWER" then
-- every power of the unit reports, a cat druid's mana regen too; redraw
-- for the one the bar shows, and for mana under a druid mana bar
local _, shown = UnitPowerType(arg1)
if arg2 == shown or (arg2 == "MANA" and this.druidmana) then
this.update_base = true
pfUI.uf:QueueUpdate(this, "base")
end
elseif event == "UNIT_FACTION" then
-- the PvP flag, the reaction colour and the tapped grey
this.update_indicators = true
this.update_base = true
pfUI.uf:QueueUpdate(this, "indicators")
pfUI.uf:QueueUpdate(this, "base")
elseif event == "UNIT_FLAGS" then
this.update_combat = true
pfUI.uf:QueueUpdate(this, "combat")
elseif event == "UNIT_DISTANCE_CHECK_UPDATE" then
-- the portrait trades the question mark for the model, the frame fades
-- by sight, and an attacker that left with the member is let go
this.update_portrait = true
this.update_combat = true
pfUI.uf:QueueUpdate(this, "portrait")
pfUI.uf:QueueUpdate(this, "combat")
elseif event == "UNIT_CONNECTION" then
this.update_full = true
pfUI.uf:QueueUpdate(this, "full")
elseif event == "UNIT_COMBAT" then
-- every hit starts the fade over
this.feedback:Stop()
CombatFeedback_OnCombatEvent(arg2, arg3, arg4, arg5)
this.feedback:Play()
elseif event == "INCOMING_RESURRECT_CHANGED" then
this.update_indicators = true
pfUI.uf:QueueUpdate(this, "indicators")
else
this.update_base = true
pfUI.uf:QueueUpdate(this, "base")
end
end
end
-- Local reference for performance
local _GetTime = GetTime
-- Drain the update flags the events set, then hand a 3D portrait its unit.
-- A frame that is not on screen keeps its flags; OnShow flushes them.
local function Flush(frame)
frame.queued = nil
if not frame:IsVisible() then return end
-- Global cached time for libpredict and other functions
pfUI.uf.now = 0
-- ============================================================================
-- OnUpdate - draining the event-set update flags, heal prediction, and the
-- two polls no event replaces (a UnitXP range threshold, the pet frames).
-- ============================================================================
function pfUI.uf.OnUpdate()
local now = _GetTime()
pfUI.uf.now = now
-- update combat feedback (no throttle - needs immediate feedback)
if this.feedbackText then CombatFeedback_OnUpdate(arg1) end
-- Throttle raid/party frames for performance
if this.label == "raid" or this.label == "party" then
if (this.throttleTick or 0) > now then
return
end
this.throttleTick = now + 0.1 -- Default: 10 FPS
end
if this.label then
-- what no event reports: a UnitXP range threshold, and the pet frames
-- (created with a tick)
if this.rangepoll or this.tick then
-- Validate tick value - it should be an interval (e.g. 0.5), not a timestamp
local tickInterval = this.tick
if tickInterval and tickInterval > 10 then
-- tick is a timestamp, not an interval - ignore it
tickInterval = nil
end
-- Reset lastTick if it's invalid (much larger than now, e.g. from corrupted state)
if this.lastTick and this.lastTick > now + 10 then
this.lastTick = nil
end
if not this.lastTick then this.lastTick = now + (tickInterval or .5) end
if this.lastTick < now then
local unitstr = this.label .. this.id
this.lastTick = now + (tickInterval or .5)
if this.rangepoll then pfUI.uf:RefreshUnitState(this) end
-- A pet's full refresh rescans every aura and the portrait, so only
-- do it when the unit behind the frame changes or once a second;
-- other ticks refresh the bars only.
if this.tick then
local guid = UnitGUID(unitstr)
if guid ~= this.tickGuid or (this.tickFull or 0) < now then
this.tickGuid = guid
this.tickFull = now + 1
pfUI.uf:RefreshUnit(this, "all")
else
pfUI.uf:RefreshUnit(this, "base")
end
end
end
end
-- Heal Prediction (throttled to 0.1s for responsiveness)
if libpredict and this.incHeal then
if not this.lastHealTick then this.lastHealTick = now end
if this.lastHealTick < now then
this.lastHealTick = now + 0.1
local unit = this.label .. this.id
local heal = libpredict:UnitGetIncomingHeals(unit)
local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
if heal - health - maxHealth ~= this.predictstate then
local overhealperc = tonumber(this.config.overhealperc)
this.predictstate = heal - health - maxHealth
if heal > 0 and (health < maxHealth or overhealperc > 0 ) then
local width = this.config.width
local height = this.config.height
if this.config.verticalbar == "0" then
local healthWidth = width * (health / maxHealth)
local incWidth = width * heal / maxHealth
if healthWidth + incWidth > width * (1+(overhealperc/100)) then
incWidth = width * (1+overhealperc/100) - healthWidth
end
if this.config.invert_healthbar == "1" then
this.incHeal:SetWidth(incWidth)
else
this.incHeal:SetWidth(incWidth + healthWidth)
end
else
local healthHeight = height * (health / maxHealth)
local incHeight = height * heal / maxHealth
if healthHeight + incHeight > height * (1+(overhealperc/100)) then
incHeight = height * (1+overhealperc/100) - healthHeight
end
if this.config.invert_healthbar == "1" then
this.incHeal:SetHeight(incHeight)
else
this.incHeal:SetHeight(incHeight + healthHeight)
end
end
this.incHeal:Show()
else
this.incHeal:Hide()
end
end
end
end
end
-- ============================================================================
-- EVENT-BASED UPDATES (Health, Mana, Auras, etc.)
-- ============================================================================
-- process all queued unit events
if this.update_full then
if frame.update_full then
-- process full updates, indicators and combat state included
pfUI.uf:RefreshUnit(this, "all")
pfUI.uf:RefreshUnit(frame, "all")
-- clear update caches
this.update_full = nil
this.update_base = nil
this.update_aura = nil
this.update_auratime = nil
this.update_portrait = nil
this.update_indicators = nil
this.update_combat = nil
frame.update_full = nil
frame.update_base = nil
frame.update_aura = nil
frame.update_auratime = nil
frame.update_portrait = nil
frame.update_indicators = nil
frame.update_combat = nil
else
if this.update_indicators then
pfUI.uf:RefreshIndicators(this)
this.update_indicators = nil
if frame.update_indicators then
pfUI.uf:RefreshIndicators(frame)
frame.update_indicators = nil
end
if this.update_combat then
pfUI.uf:RefreshCombat(this)
this.update_combat = nil
if frame.update_combat then
pfUI.uf:RefreshCombat(frame)
frame.update_combat = nil
end
-- process individual events; a rebuild re-times the icons as well
if this.update_aura then
pfUI.uf:RefreshUnit(this, "aura")
this.update_aura = nil
this.update_auratime = nil
this.update_base = true
elseif this.update_auratime then
pfUI.uf:RefreshUnit(this, "auratime")
this.update_auratime = nil
if frame.update_aura then
pfUI.uf:RefreshUnit(frame, "aura")
frame.update_aura = nil
frame.update_auratime = nil
frame.update_base = true
elseif frame.update_auratime then
pfUI.uf:RefreshUnit(frame, "auratime")
frame.update_auratime = nil
end
if this.update_portrait then
pfUI.uf:RefreshUnit(this, "portrait")
this.update_portrait = nil
this.update_base = true
if frame.update_portrait then
pfUI.uf:RefreshUnit(frame, "portrait")
frame.update_portrait = nil
frame.update_base = true
end
if this.update_base then
pfUI.uf:RefreshUnit(this, "base")
this.update_base = nil
if frame.update_base then
pfUI.uf:RefreshUnit(frame, "base")
frame.update_base = nil
end
end
-- update portrait on first visible frame
if this.portrait and this.portrait.model and this.portrait.model.update then
this.portrait.model.lastUnit = UnitName(this.portrait.model.update)
this.portrait.model:SetUnit(this.portrait.model.update)
this.portrait.model:SetCamera(0)
this.portrait.model.update = nil
-- a model takes its unit only once it is on screen, so it waits for this
local model = frame.portrait and frame.portrait.model
if model and model.update then
model.lastUnit = UnitName(model.update)
model:SetUnit(model.update)
model:SetCamera(0)
model.update = nil
end
end
-- Flag `kind` ("full", "base", "aura", "auratime", "portrait", "indicators",
-- "combat", or nil for none) for the frame's next flush, and schedule that
-- flush: the next frame, or within 0.1s for the raid and party frames, which
-- batch their bursts.
function pfUI.uf:QueueUpdate(frame, kind)
if kind then frame["update_" .. kind] = true end
if frame.queued then return end
frame.queued = true
if frame.label == "raid" or frame.label == "party" then
C_Timer.After(.1, function() Flush(frame) end)
else
RunNextFrame(function() Flush(frame) end)
end
end
-- Size the incoming-heal overlay from libpredict's heals and the unit's
-- health. Runs with the bars (RefreshUnit "base") and when libpredict reports
-- the heals on the unit changed.
function pfUI.uf:RefreshIncomingHeal(frame)
local unit = frame.label .. frame.id
local heal = libpredict:UnitGetIncomingHeals(unit)
local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
if heal - health - maxHealth == frame.predictstate then return end
frame.predictstate = heal - health - maxHealth
local overhealperc = tonumber(frame.config.overhealperc)
if heal > 0 and (health < maxHealth or overhealperc > 0 ) then
local width = frame.config.width
local height = frame.config.height
if frame.config.verticalbar == "0" then
local healthWidth = width * (health / maxHealth)
local incWidth = width * heal / maxHealth
if healthWidth + incWidth > width * (1+(overhealperc/100)) then
incWidth = width * (1+overhealperc/100) - healthWidth
end
frame.incHeal:SetWidth(incWidth)
else
local healthHeight = height * (health / maxHealth)
local incHeight = height * heal / maxHealth
if healthHeight + incHeight > height * (1+(overhealperc/100)) then
incHeight = height * (1+overhealperc/100) - healthHeight
end
frame.incHeal:SetHeight(incHeight)
end
frame.incHeal:Show()
else
frame.incHeal:Hide()
end
end
pfUI.events:RegisterCallback("libpredict:heals", function(_, target)
for frame in pairs(visibilityframes) do
if frame.namecache == target and frame:IsVisible() then
pfUI.uf:RefreshIncomingHeal(frame)
end
end
end, "unitframes")
-- A UnitXP range threshold has no event, so the frames fading by it poll.
C_Timer.NewTicker(.5, function()
for frame in pairs(visibilityframes) do
if frame.rangepoll and frame:IsVisible() then pfUI.uf:RefreshUnitState(frame) end
end
end)
-- The unit events whose arg1 is the frame's OWN unit -- everything the OnEvent
-- routes through its "UNIT_XXX Events" branch. These are not registered here:
-- UpdateVisibility owns them, because it owns the frame's unit (below).
@@ -1309,8 +1336,8 @@ local UNIT_EVENTS = {
-- Auras and power. ClassicAPI's UNIT_AURA_DELTA also reports refreshes and new
-- durations and says what changed; its generic power events name the power, so
-- a frame skips the ones it does not draw. Both fire for tokens only, so a
-- chained unit, followed by GUID, keeps 1.12's events instead. A group
-- a frame skips the ones it does not draw. Both fire for tokens only, and not
-- as "focustarget", so a chained unit keeps 1.12's events instead. A group
-- member's connection and sight come with them, and a chained unit has
-- neither event.
local TOKEN_EVENTS = {
@@ -1375,16 +1402,18 @@ end
-- unit, whose `owner` is the unit it is the target of -- for
-- "targettargettarget" the links are "targettarget" and "target" -- or drop it
-- when `owner` is nil. By GUID, since the inner links are no token that events
-- name. Cheap to call repeatedly: it re-registers only when a link changed.
-- name, save the ones in CHAIN_TOKENS. Cheap to call repeatedly: it
-- re-registers only when a link changed.
local chainlinks = {}
function pfUI.uf:RegisterChainEvents(owner)
local n, key = 0, nil
while owner do
local guid = UnitGUID(owner)
if guid then
local link = CHAIN_TOKENS[owner] and owner or guid
n = n + 1
chainlinks[n] = guid
key = key and key .. guid or guid
chainlinks[n] = link
key = key and key .. link or link
end
owner = owner:match("^(.+)target$")
end
@@ -1403,19 +1432,10 @@ end
function pfUI.uf:EnableEvents()
local f = self
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("PLAYER_LOGOUT")
f:RegisterEvent("PLAYER_AURAS_CHANGED") -- label=player && frame=buff
f:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- label=player && frame=buff (ClassicAPI: weapon-enchant buffs)
f:RegisterEvent("PARTY_MEMBERS_CHANGED") -- label=party, frame=leaderIcon
f:RegisterEvent("PARTY_LEADER_CHANGED") -- frame=leaderIcon
f:RegisterEvent("RAID_ROSTER_UPDATE") -- label=raidIcon
f:RegisterEvent("PLAYER_UPDATE_RESTING") -- label=restIcon
f:RegisterEvent("PLAYER_TARGET_CHANGED") -- label=target
f:RegisterEvent("PARTY_LOOT_METHOD_CHANGED") -- frame=lootIcon
f:RegisterEvent("RAID_TARGET_UPDATE") -- frame=raidIcon
f:RegisterEvent("UNIT_PET")
f:RegisterEvent("UNIT_HAPPINESS")
for event in pairs(f.idleevents) do
f:RegisterEvent(event)
end
if f:IsShown() then RegisterShownEvents(f) end
f:RegisterForClicks('LeftButtonUp', 'RightButtonUp',
'MiddleButtonUp', 'Button4Up', 'Button5Up')
@@ -1481,8 +1501,8 @@ function pfUI.uf:EnableScripts()
end
f:SetScript("OnShow", pfUI.uf.OnShow)
f:SetScript("OnHide", pfUI.uf.OnHide)
f:SetScript("OnEvent", pfUI.uf.OnEvent)
f:SetScript("OnUpdate", pfUI.uf.OnUpdate)
visibilityframes[f] = true
end
@@ -1528,6 +1548,7 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
f.id = id
f.config = config or pfUI_config.unitframes.fallback
f.tick = tick
f.idleevents = GetIdleEvents(f.label, fname)
CreateBackdropShadow(f)
@@ -1553,11 +1574,11 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
f.glow = CreateFrame("Frame", nil, f)
-- Pulse between .8 and .4 for good. The group only advances while the glow
-- is shown, so a hidden glow costs nothing.
f.glow:SetAlpha(.8)
f.glow.pulse = f.glow:CreateAnimationGroup()
f.glow.pulse:SetLooping("BOUNCE")
local fade = f.glow.pulse:CreateAnimation("Alpha")
fade:SetChange(-.4)
fade:SetFromAlpha(.8)
fade:SetToAlpha(.4)
fade:SetDuration(1.3)
fade:SetSmoothing("IN_OUT")
f.glow.pulse:Play()
@@ -1616,6 +1637,21 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
f.portrait.model.next = CreateFrame("PlayerModel", nil, nil)
f.feedbackText = f:CreateFontString("pfHitIndicator" .. f.label .. f.id, "OVERLAY", "NumberFontNormalHuge")
-- Fade the combat feedback text in, hold it, and fade it out -- the timing
-- of CombatFeedback_OnUpdate -- then hide it.
f.feedback = f.feedbackText:CreateAnimationGroup()
local fadein = f.feedback:CreateAnimation("Alpha")
fadein:SetOrder(1)
fadein:SetDuration(COMBATFEEDBACK_FADEINTIME)
fadein:SetToAlpha(1)
local fadeout = f.feedback:CreateAnimation("Alpha")
fadeout:SetOrder(2)
fadeout:SetStartDelay(COMBATFEEDBACK_HOLDTIME)
fadeout:SetDuration(COMBATFEEDBACK_FADEOUTTIME)
fadeout:SetFromAlpha(1)
fadeout:SetToAlpha(0)
f.feedback:SetScript("OnFinished", function() f.feedbackText:Hide() end)
if f.label == "raid" and mod(f.id, 5) == 1 then
local group = math.ceil(f.id/5)
f.group = f.group or f.hp.bar:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall")
@@ -1989,7 +2025,7 @@ function pfUI.uf:RefreshUnit(unit, component)
local size
if disptype == "4" or disptype == "3" then
size = unit.hp.bar:GetHeight() * tonumber(unit.config.debuff_ind_size)
size = unit.hp.bar:GetUnscaledHeight() * tonumber(unit.config.debuff_ind_size)
if size ~= indicator.size or disptype ~= indicator.disp or indipos ~= indicator.ipos then
indicator:ClearAllPoints()
indicator:SetPoint(indipos, 0, 0)
@@ -2204,6 +2240,7 @@ function pfUI.uf:RefreshUnit(unit, component)
unit.portrait.model.update = unitstr
end
end
if unit.portrait.model.update then pfUI.uf:QueueUpdate(unit) end
end
end
@@ -2344,6 +2381,7 @@ function pfUI.uf:RefreshUnit(unit, component)
if unit.druidmana then pfUI.uf:UpdateDruidMana(unit) end
pfUI.uf:RefreshIncomingHeal(unit)
pfUI.uf:RefreshUnitState(unit)
end
end
@@ -2390,10 +2428,11 @@ function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
local position = frame.config.indicator_pos or "TOPLEFT"
local iconsize = tonumber(frame.config.indicator_size)
local spacing = tonumber(frame.config.indicator_spacing)
local config = frame.config
if not frame.hp then return end
local frame = frame.hp.bar
if pos > 6 or pos > ceil(frame:GetWidth() / iconsize) then return end
if pos > 6 or pos > ceil(frame:GetUnscaledWidth() / iconsize) then return end
frame.icon = frame.icon or CreateFrame("Frame", nil, frame)
@@ -2408,7 +2447,7 @@ function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
frame.icon[pos].stacks:SetJustifyH("RIGHT")
frame.icon[pos].stacks:SetJustifyV("BOTTOM")
frame.icon[pos].cd = CreateCooldown(frame.icon[pos], "indicator")
frame.icon[pos].cd = CreateCooldown(frame.icon[pos], "aura", config, "indicator_timers")
frame.icon[pos].cd:SetFrameLevel(48)
end
+1 -4
View File
@@ -935,14 +935,11 @@ pfUI_profiles["Adapta"].new_module_positions = pfUI_profiles["Modern"].new_modul
-- overwrite core profiles in userdata
local profile_loader = CreateFrame("Frame")
profile_loader:RegisterEvent("VARIABLES_LOADED")
profile_loader:SetScript("OnEvent", function()
EventUtil.RegisterOnceFrameEventAndCallback("VARIABLES_LOADED", function()
pfUI_profiles["Modern"] = modern
pfUI_profiles["Nostalgia"] = nostalgia
pfUI_profiles["Legacy"] = legacy
pfUI_profiles["Adapta"] = adapta
pfUI_profiles["Light"] = light
pfUI_profiles["Slim"] = slim
this:UnregisterAllEvents()
end)
+11 -13
View File
@@ -8,7 +8,6 @@ pfUI_translation["deDE"] = {
["Actionbar"] = nil,
["Action Bar"] = nil,
["Action Bar Border Size"] = nil,
["Action Bars"] = nil,
["Active Addons"] = nil,
["Add button to the frame"] = nil,
["Added button"] = nil,
@@ -77,11 +76,11 @@ pfUI_translation["deDE"] = {
["Bottom"] = nil,
["Bottom Left"] = nil,
["Bottom Right"] = nil,
["Buff And Debuff Timers"] = nil,
["Buffbar Height"] = nil,
["Buffbar Width"] = nil,
["Buff/Debuff Icons"] = nil,
["Buff/Debuff Indicators"] = nil,
["Buff Indicators"] = nil,
["Buff Limit"] = nil,
["Buff Position"] = nil,
["Buffs"] = nil,
@@ -106,6 +105,15 @@ pfUI_translation["deDE"] = {
["Center Text"] = nil,
["Center Text X Offset"] = nil,
["Center Text Y Offset"] = nil,
["Cooldown Timers"] = nil,
["Debuff Timers"] = nil,
["Duration Timers"] = nil,
["Indicator Timers"] = nil,
["Numbers"] = nil,
["Numbers And Sweep"] = nil,
["Sweep"] = nil,
["Timers"] = nil,
["Unit Buff Timers"] = nil,
["|cff33ffccBlizzard: \"Interface Options\"|r\n\nDo you want me to set up the recommended Blizzard UI settings? This will enable settings that can be found in the Interface section of your client. Options like Buff Durations, Instant Quest Text, Auto Selfcast and others will be set."] = nil,
["|cff33ffccChat: \"Channels\"|r\n\nDo you want me to setup the chat channels of your chatframes? This would set important or personal messages to the left chat and world channels and lootinformation to the right chat."] = nil,
["|cff33ffccChat: \"Layout\"|r\n\nDo you want me to adjust the layout of your chatframes? This would make sure, that every window is placed on its dedicated position."] = nil,
@@ -359,6 +367,7 @@ pfUI_translation["deDE"] = {
["Group Options"] = nil,
["Group-Pet"] = nil,
["Group-Target"] = nil,
["Grow Right To Left"] = nil,
["Gryphon"] = nil,
["Gryphons"] = nil,
["Guild Online"] = nil,
@@ -515,7 +524,6 @@ pfUI_translation["deDE"] = {
["Name | Health Missing"] = nil,
["Name (Linebreak) -Health Missing"] = nil,
["Nameplate Border Size"] = nil,
["Nameplate Debuffs"] = nil,
["Nameplates"] = nil,
["Nameplate Width"] = nil,
["Name (Short)"] = nil,
@@ -583,7 +591,6 @@ pfUI_translation["deDE"] = {
["Pixel"] = nil,
["Player"] = nil,
["Player Buff Bar"] = nil,
["Player Buffs"] = nil,
["Player Castbar"] = nil,
["Player Debuff Bar"] = nil,
["Player SP/Haste Display"] = nil,
@@ -650,7 +657,6 @@ pfUI_translation["deDE"] = {
["Resting"] = nil,
["Reveal Unexplored Areas"] = nil,
["Switch to current zone"] = "Zur aktuellen Zone wechseln",
["Reverse Sweep On"] = nil,
["Right"] = nil,
["Right Actionbar"] = nil,
["Right Anchor"] = nil,
@@ -732,7 +738,6 @@ pfUI_translation["deDE"] = {
["Show Loot Icon"] = nil,
["Show Macro Text"] = nil,
["Show Meters By Default"] = nil,
["Show Numbers On"] = nil,
["Show On Friendly Units"] = nil,
["Show On Hostile Units"] = nil,
["Show Procs Indicators"] = nil,
@@ -750,7 +755,6 @@ pfUI_translation["deDE"] = {
["Show Spell Icon"] = nil,
["Show Spell Name"] = nil,
["Show Stacks"] = nil,
["Show Sweep On"] = nil,
["Show Tenths Below 5 Seconds"] = nil,
["Show Text"] = nil,
["Show Timer"] = nil,
@@ -818,7 +822,6 @@ pfUI_translation["deDE"] = {
["Tiny (PixelPerfect)"] = nil,
["Toggle as Tank"] = nil,
["Tooltip"] = nil,
["Tooltip Auras"] = nil,
["Tooltip Position"] = nil,
["Tooltip Text Font"] = nil,
["Tooltip Text Font Size"] = nil,
@@ -830,13 +833,11 @@ pfUI_translation["deDE"] = {
["Total Gold"] = nil,
["Totem Direction"] = nil,
["Totem Icons"] = nil,
["Totems"] = nil,
["Tracked Reputation"] = nil,
["Tracking Icon Size"] = nil,
["Trigger Actions On Key Down"] = nil,
["UI-Scale"] = nil,
["Unbuffed"] = nil,
["Unit Frame Auras"] = nil,
["Unit Frame Border Size"] = nil,
["Unit Frame Layout"] = nil,
["Unit Frames"] = nil,
@@ -906,10 +907,7 @@ pfUI_translation["deDE"] = {
["Chat Tab"] = "Chat Tab",
["Libpredict"] = "Libpredict",
["Panel Alignment"] = "Panel Ausrichtung",
["Nameplate Update Rate"] = "Namensplaketten Aktualisierungsrate",
["Target/Casting Plates"] = "Ziel/Cast Plaketten",
["Normal Plates"] = "Normale Plaketten",
["Mass Pulls (20+ Plates)"] = "Große Pulls (20+ Plaketten)",
["Tooltip Update Rate"] = "Tooltip Aktualisierungsrate",
["Cursor Follow"] = "Cursor Folgen",
["Health/Status Bar"] = "Leben/Status Leiste",
+16 -13
View File
@@ -8,7 +8,6 @@ pfUI_translation["enUS"] = {
["Actionbar"] = nil,
["Action Bar"] = nil,
["Action Bar Border Size"] = nil,
["Action Bars"] = nil,
["Active Addons"] = nil,
["Add button to the frame"] = nil,
["Added button"] = nil,
@@ -80,16 +79,18 @@ pfUI_translation["enUS"] = {
["Bottom"] = nil,
["Bottom Left"] = nil,
["Bottom Right"] = nil,
["Buff And Debuff Timers"] = nil,
["Buffbar Height"] = nil,
["Buffbar Width"] = nil,
["Buff/Debuff Icons"] = nil,
["Buff/Debuff Indicators"] = nil,
["Buff Indicators"] = nil,
["Buff Limit"] = nil,
["Buff Position"] = nil,
["Buffs"] = nil,
["Buff Size"] = nil,
["Buff Spacing"] = nil,
["Buff X-Offset"] = nil,
["Buff Y-Offset"] = nil,
["Buff X-Spacing"] = nil,
["Buff Y-Spacing"] = nil,
["Buffs Per Row"] = nil,
@@ -111,6 +112,15 @@ pfUI_translation["enUS"] = {
["Center Text"] = nil,
["Center Text X Offset"] = nil,
["Center Text Y Offset"] = nil,
["Cooldown Timers"] = nil,
["Debuff Timers"] = nil,
["Duration Timers"] = nil,
["Indicator Timers"] = nil,
["Numbers"] = nil,
["Numbers And Sweep"] = nil,
["Sweep"] = nil,
["Timers"] = nil,
["Unit Buff Timers"] = nil,
["|cff33ffccBlizzard: \"Interface Options\"|r\n\nDo you want me to set up the recommended Blizzard UI settings? This will enable settings that can be found in the Interface section of your client. Options like Buff Durations, Instant Quest Text, Auto Selfcast and others will be set."] = nil,
["|cff33ffccChat: \"Channels\"|r\n\nDo you want me to setup the chat channels of your chatframes? This would set important or personal messages to the left chat and world channels and lootinformation to the right chat."] = nil,
["|cff33ffccChat: \"Layout\"|r\n\nDo you want me to adjust the layout of your chatframes? This would make sure, that every window is placed on its dedicated position."] = nil,
@@ -196,6 +206,8 @@ pfUI_translation["enUS"] = {
["Debuff Position"] = nil,
["Debuffs"] = nil,
["Debuff Size"] = nil,
["Debuff X-Offset"] = nil,
["Debuff Y-Offset"] = nil,
["Debuff X-Spacing"] = nil,
["Debuff Y-Spacing"] = nil,
["Debuffs Per Row"] = nil,
@@ -369,6 +381,7 @@ pfUI_translation["enUS"] = {
["Group Options"] = nil,
["Group-Pet"] = nil,
["Group-Target"] = nil,
["Grow Right To Left"] = nil,
["Gryphon"] = nil,
["Gryphons"] = nil,
["Guild Online"] = nil,
@@ -420,6 +433,7 @@ pfUI_translation["enUS"] = {
["Highlight Out Of Mana Spells"] = nil,
["Highlight New Items"] = nil,
["Highlight Out Of Range Spells"] = nil,
["Highlight Proc And Reactive Spells"] = nil,
["Highlight Settings That Require Reload"] = nil,
["Highlight Unusable Items"] = nil,
["Hold [Shift] to use item."] = nil,
@@ -528,7 +542,6 @@ pfUI_translation["enUS"] = {
["Name | Health Missing"] = nil,
["Name (Linebreak) -Health Missing"] = nil,
["Nameplate Border Size"] = nil,
["Nameplate Debuffs"] = nil,
["Nameplates"] = nil,
["Nameplate Width"] = nil,
["Name (Short)"] = nil,
@@ -601,7 +614,6 @@ pfUI_translation["enUS"] = {
["Pixel"] = nil,
["Player"] = nil,
["Player Buff Bar"] = nil,
["Player Buffs"] = nil,
["Player Castbar"] = nil,
["Player Debuff Bar"] = nil,
["Player SP/Haste Display"] = nil,
@@ -669,7 +681,6 @@ pfUI_translation["enUS"] = {
["Reveal Unexplored Areas"] = nil,
["Switch to current zone"] = nil,
["Retrieving item information..."] = nil,
["Reverse Sweep On"] = nil,
["Right"] = nil,
["Right Actionbar"] = nil,
["Right Anchor"] = nil,
@@ -751,7 +762,6 @@ pfUI_translation["enUS"] = {
["Show Loot Icon"] = nil,
["Show Macro Text"] = nil,
["Show Meters By Default"] = nil,
["Show Numbers On"] = nil,
["Show On Friendly Units"] = nil,
["Show On Hostile Units"] = nil,
["Show Procs Indicators"] = nil,
@@ -769,7 +779,6 @@ pfUI_translation["enUS"] = {
["Show Spell Icon"] = nil,
["Show Spell Name"] = nil,
["Show Stacks"] = nil,
["Show Sweep On"] = nil,
["Show Tenths Below 5 Seconds"] = nil,
["Show Text"] = nil,
["Show Timer"] = nil,
@@ -846,7 +855,6 @@ pfUI_translation["enUS"] = {
["Tiny (PixelPerfect)"] = nil,
["Toggle as Tank"] = nil,
["Tooltip"] = nil,
["Tooltip Auras"] = nil,
["Tooltip Position"] = nil,
["Tooltip Text Font"] = nil,
["Tooltip Text Font Size"] = nil,
@@ -858,13 +866,11 @@ pfUI_translation["enUS"] = {
["Total Gold"] = nil,
["Totem Direction"] = nil,
["Totem Icons"] = nil,
["Totems"] = nil,
["Tracked Reputation"] = nil,
["Tracking Icon Size"] = nil,
["Trigger Actions On Key Down"] = nil,
["UI-Scale"] = nil,
["Unbuffed"] = nil,
["Unit Frame Auras"] = nil,
["Unit Frame Border Size"] = nil,
["Unit Frame Layout"] = nil,
["Unit Frames"] = nil,
@@ -934,10 +940,7 @@ pfUI_translation["enUS"] = {
["Chat Tab"] = nil,
["Libpredict"] = nil,
["Panel Alignment"] = nil,
["Nameplate Update Rate"] = nil,
["Target/Casting Plates"] = nil,
["Normal Plates"] = nil,
["Mass Pulls (20+ Plates)"] = nil,
["Tooltip Update Rate"] = nil,
["Cursor Follow"] = nil,
["Health/Status Bar"] = nil,
+11 -10
View File
@@ -8,7 +8,6 @@ pfUI_translation["esES"] = {
["Actionbar"] = "Barra de acción",
["Action Bar"] = "Barra de acción",
["Action Bar Border Size"] = "Tamaño del borde de la barra de acción",
["Action Bars"] = nil,
["Active Addons"] = "Accesorios activos",
["Add button to the frame"] = "Añadir botón al marco",
["Added button"] = "Botón añadido",
@@ -77,11 +76,11 @@ pfUI_translation["esES"] = {
["Bottom"] = "Inferior",
["Bottom Left"] = "Inferior izquierda",
["Bottom Right"] = "Inferior derecha",
["Buff And Debuff Timers"] = nil,
["Buffbar Height"] = "Altura de la barra de beneficios",
["Buffbar Width"] = "Ancho de la barra de beneficios",
["Buff/Debuff Icons"] = "Iconos de los beneficios/perjuicios",
["Buff/Debuff Indicators"] = "Indicadores de beneficios/perjuicios",
["Buff Indicators"] = nil,
["Buff Limit"] = "Límite de beneficios",
["Buff Position"] = "Posición de los beneficios",
["Buffs"] = "Beneficios",
@@ -106,6 +105,15 @@ pfUI_translation["esES"] = {
["Center Text"] = "Centrar texto",
["Center Text X Offset"] = nil,
["Center Text Y Offset"] = nil,
["Cooldown Timers"] = nil,
["Debuff Timers"] = nil,
["Duration Timers"] = nil,
["Indicator Timers"] = nil,
["Numbers"] = nil,
["Numbers And Sweep"] = nil,
["Sweep"] = nil,
["Timers"] = nil,
["Unit Buff Timers"] = nil,
["|cff33ffccBlizzard: \"Interface Options\"|r\n\nDo you want me to set up the recommended Blizzard UI settings? This will enable settings that can be found in the Interface section of your client. Options like Buff Durations, Instant Quest Text, Auto Selfcast and others will be set."] = "|cff33ffccBlizzard: \"Opciones de la interfaz\"|r\n\n¿Prefieres usar la configuración IU recomendada de Blizzard? Se activarán algunas de las opciones que se encuentran en la sección Interfaz de tu cliente. Opciones como duración de buffs, texto de misión instantáneo, autolanzarse hechizos y otros serán activadas.",
["|cff33ffccChat: \"Channels\"|r\n\nDo you want me to setup the chat channels of your chatframes? This would set important or personal messages to the left chat and world channels and lootinformation to the right chat."] = "|cff33ffccChat: \"Canales\"|r\n\n¿Quieres que configure los canales de chat para tu marco de chat? Los mensajes importantes o personales aparecerán en la ventana de chat izquierda y los canales del mundo e información del botín en la ventana de chat derecha.",
["|cff33ffccChat: \"Layout\"|r\n\nDo you want me to adjust the layout of your chatframes? This would make sure, that every window is placed on its dedicated position."] = "|cff33ffccChat: \"Disposición\"|r\n\n¿Quieres que ajuste la disposición de los marcos de chat? De esta manera cada ventana estará en su posición correcta.",
@@ -359,6 +367,7 @@ pfUI_translation["esES"] = {
["Group Options"] = "Opciones de grupo",
["Group-Pet"] = "Mascota-Grupo",
["Group-Target"] = "Objetivo-Grupo",
["Grow Right To Left"] = nil,
["Gryphon"] = nil,
["Gryphons"] = nil,
["Guild Online"] = "Conectados en hermandad",
@@ -515,7 +524,6 @@ pfUI_translation["esES"] = {
["Name | Health Missing"] = "Nombre | Falta de salud",
["Name (Linebreak) -Health Missing"] = nil,
["Nameplate Border Size"] = "Tamaño del borde de las placas de nombre",
["Nameplate Debuffs"] = nil,
["Nameplates"] = "Placas identificativas",
["Nameplate Width"] = "Ancho de las placas identificativas",
["Name (Short)"] = "Nombre (corto)",
@@ -583,7 +591,6 @@ pfUI_translation["esES"] = {
["Pixel"] = "Píxel",
["Player"] = "Jugador",
["Player Buff Bar"] = "Barra de beneficios del jugador",
["Player Buffs"] = nil,
["Player Castbar"] = "Barra de lanzamiento del jugador",
["Player Debuff Bar"] = "Barra de perjuicios del jugador",
["Player SP/Haste Display"] = nil,
@@ -650,7 +657,6 @@ pfUI_translation["esES"] = {
["Resting"] = "Descansando",
["Reveal Unexplored Areas"] = "Mostrar áreas no exploradas",
["Switch to current zone"] = "Ir a la zona actual",
["Reverse Sweep On"] = nil,
["Right"] = "Derecho",
["Right Actionbar"] = "Barra de acción derecha",
["Right Anchor"] = nil,
@@ -732,7 +738,6 @@ pfUI_translation["esES"] = {
["Show Loot Icon"] = "Mostrar icono de botín",
["Show Macro Text"] = "Mostrar texto de macro",
["Show Meters By Default"] = "Mostrar medidores por defecto",
["Show Numbers On"] = nil,
["Show On Friendly Units"] = "Mostrar en unidades amistosas",
["Show On Hostile Units"] = "Mostrar en unidades enemigas",
["Show Procs Indicators"] = "Mostrar indicadores de beneficios temporales activados",
@@ -750,7 +755,6 @@ pfUI_translation["esES"] = {
["Show Spell Icon"] = "Mostrar icono de hechizo",
["Show Spell Name"] = nil,
["Show Stacks"] = "Mostrar pilas",
["Show Sweep On"] = nil,
["Show Tenths Below 5 Seconds"] = nil,
["Show Text"] = nil,
["Show Timer"] = nil,
@@ -818,7 +822,6 @@ pfUI_translation["esES"] = {
["Tiny (PixelPerfect)"] = "Minúsculo (píxel perfecta)",
["Toggle as Tank"] = "Alternar si eres el tanque",
["Tooltip"] = "Descripción emergente",
["Tooltip Auras"] = nil,
["Tooltip Position"] = "Posición de la descripción emergente",
["Tooltip Text Font"] = nil,
["Tooltip Text Font Size"] = nil,
@@ -830,13 +833,11 @@ pfUI_translation["esES"] = {
["Total Gold"] = "Oro Total",
["Totem Direction"] = "Dirección del tótem",
["Totem Icons"] = "Iconos de los tótems",
["Totems"] = nil,
["Tracked Reputation"] = "Reputación rastreada",
["Tracking Icon Size"] = "Tamaño del icono de rastreo",
["Trigger Actions On Key Down"] = "Disparar acciones por presionar la tecla abajo",
["UI-Scale"] = "Escala de IU",
["Unbuffed"] = "Sin beneficios",
["Unit Frame Auras"] = nil,
["Unit Frame Border Size"] = "Tamaño del borde del marco de unidad",
["Unit Frame Layout"] = "Disposición del Marco de unidad",
["Unit Frames"] = "Marcos de unidad",
+11 -10
View File
@@ -8,7 +8,6 @@ pfUI_translation["frFR"] = {
["Actionbar"] = "Barre d'action",
["Action Bar"] = "Barre d'action",
["Action Bar Border Size"] = "Taille de la bordure de la barre d'action",
["Action Bars"] = nil,
["Active Addons"] = "Addons Actif",
["Add button to the frame"] = "Ajoute un bouton au cadre",
["Added button"] = "Bouton ajouté",
@@ -77,11 +76,11 @@ pfUI_translation["frFR"] = {
["Bottom"] = "Bas",
["Bottom Left"] = "Bas Gauche",
["Bottom Right"] = "Bas Droit",
["Buff And Debuff Timers"] = nil,
["Buffbar Height"] = "Hauteur de la barre des Améliorations",
["Buffbar Width"] = "Largeur de la barre des Améliorations",
["Buff/Debuff Icons"] = "Icones de Améliorations/Affaiblissements",
["Buff/Debuff Indicators"] = nil,
["Buff Indicators"] = nil,
["Buff Limit"] = "Limite des améliorations",
["Buff Position"] = "Position des améliorations",
["Buffs"] = "Buffs",
@@ -106,6 +105,15 @@ pfUI_translation["frFR"] = {
["Center Text"] = "Centrer le texte",
["Center Text X Offset"] = nil,
["Center Text Y Offset"] = nil,
["Cooldown Timers"] = nil,
["Debuff Timers"] = nil,
["Duration Timers"] = nil,
["Indicator Timers"] = nil,
["Numbers"] = nil,
["Numbers And Sweep"] = nil,
["Sweep"] = nil,
["Timers"] = nil,
["Unit Buff Timers"] = nil,
["|cff33ffccBlizzard: \"Interface Options\"|r\n\nDo you want me to set up the recommended Blizzard UI settings? This will enable settings that can be found in the Interface section of your client. Options like Buff Durations, Instant Quest Text, Auto Selfcast and others will be set."] = "|cff33ffccBlizzard: \"Options d'Interface\"|r\n\nVoulez-vous que le logiciel vous paramètre les options d'interface recommandées ? Si oui, cela va choisir les options d'interface par défaut de votre client. Les options comme la durée des améliorations, le texte instantané des quêtes, lancer sur soi par défaut et d'autres vont être activées.",
["|cff33ffccChat: \"Channels\"|r\n\nDo you want me to setup the chat channels of your chatframes? This would set important or personal messages to the left chat and world channels and lootinformation to the right chat."] = "|cff33ffccChat: \"Canaux\"|r\n\nVoulez-vous que le logiciel vous paramètre les canaux de discussion dans votre fenêtre de chat ? En faisant ceci, les messages importants et personnels seront sur le chat de gauche, alors que les textes de butin ou autre informations diverses seront affichés sur le chat de droite.",
["|cff33ffccChat: \"Layout\"|r\n\nDo you want me to adjust the layout of your chatframes? This would make sure, that every window is placed on its dedicated position."] = "|cff33ffccChat: \"Position\"|r\n\nVoulez-vous que le logiciel vous paramètre la position de la fenêtre de discussion ? Ceci permet d'optimiser et de placer correctement les fenêtres.",
@@ -359,6 +367,7 @@ pfUI_translation["frFR"] = {
["Group Options"] = "Options de groupe",
["Group-Pet"] = "Groupe des familiers",
["Group-Target"] = "Groupe des cibles",
["Grow Right To Left"] = nil,
["Gryphon"] = nil,
["Gryphons"] = nil,
["Guild Online"] = "Membres de guilde connectés",
@@ -515,7 +524,6 @@ pfUI_translation["frFR"] = {
["Name | Health Missing"] = "Nom | santé manquante",
["Name (Linebreak) -Health Missing"] = nil,
["Nameplate Border Size"] = "Taille de la bordure des barres de vie flottantes",
["Nameplate Debuffs"] = nil,
["Nameplates"] = "Barres de vie flottantes",
["Nameplate Width"] = "Largeur des barres de vie flottantes",
["Name (Short)"] = "Nom (Court)",
@@ -583,7 +591,6 @@ pfUI_translation["frFR"] = {
["Pixel"] = "Pixel",
["Player"] = "Joueur",
["Player Buff Bar"] = "Barre des Améliorations du Joueur",
["Player Buffs"] = nil,
["Player Castbar"] = "Barre d'incantation du Joueur",
["Player Debuff Bar"] = "Barre des Affaiblissements du Joueur",
["Player SP/Haste Display"] = nil,
@@ -650,7 +657,6 @@ pfUI_translation["frFR"] = {
["Resting"] = "Au Repos",
["Reveal Unexplored Areas"] = "Révèle les zones non explorées",
["Switch to current zone"] = "Aller à la zone actuelle",
["Reverse Sweep On"] = nil,
["Right"] = "Droit",
["Right Actionbar"] = "Barre d'action droite",
["Right Anchor"] = nil,
@@ -732,7 +738,6 @@ pfUI_translation["frFR"] = {
["Show Loot Icon"] = "Afficher l'icône du butin",
["Show Macro Text"] = "Afficher le texte des macros",
["Show Meters By Default"] = "Afficher les compteurs par défaut",
["Show Numbers On"] = nil,
["Show On Friendly Units"] = "Afficher sur les unités amicales",
["Show On Hostile Units"] = "Afficher sur les unités hostiles",
["Show Procs Indicators"] = "Afficher les indicateurs de Procs",
@@ -750,7 +755,6 @@ pfUI_translation["frFR"] = {
["Show Spell Icon"] = "Afficher l'icone des sorts",
["Show Spell Name"] = nil,
["Show Stacks"] = "Afficher les empilements",
["Show Sweep On"] = nil,
["Show Tenths Below 5 Seconds"] = nil,
["Show Text"] = nil,
["Show Timer"] = nil,
@@ -818,7 +822,6 @@ pfUI_translation["frFR"] = {
["Tiny (PixelPerfect)"] = "Minuscule (PixelParfait)",
["Toggle as Tank"] = "Basculer en tant que tank",
["Tooltip"] = "Infobulle",
["Tooltip Auras"] = nil,
["Tooltip Position"] = "Position de l'infobulle",
["Tooltip Text Font"] = nil,
["Tooltip Text Font Size"] = nil,
@@ -830,13 +833,11 @@ pfUI_translation["frFR"] = {
["Total Gold"] = "Or Total",
["Totem Direction"] = "Direction des totems",
["Totem Icons"] = "Icones des totems",
["Totems"] = nil,
["Tracked Reputation"] = nil,
["Tracking Icon Size"] = "Taille de l'icône de suivi",
["Trigger Actions On Key Down"] = "Déclencher des actions sur la touche enfoncée",
["UI-Scale"] = "Echelle de l'interface",
["Unbuffed"] = "Sans Améliorations",
["Unit Frame Auras"] = nil,
["Unit Frame Border Size"] = "Taille de la bordure des cadres d'unité",
["Unit Frame Layout"] = "Position des cadres d'unité",
["Unit Frames"] = "Cadres d'unités",
+11 -10
View File
@@ -8,7 +8,6 @@ pfUI_translation["koKR"] = {
["Actionbar"] = "액션바",
["Action Bar"] = nil,
["Action Bar Border Size"] = "액션바 테두리 크기",
["Action Bars"] = nil,
["Active Addons"] = "애드온 활성화",
["Add button to the frame"] = nil,
["Added button"] = nil,
@@ -77,11 +76,11 @@ pfUI_translation["koKR"] = {
["Bottom"] = "아래",
["Bottom Left"] = nil,
["Bottom Right"] = nil,
["Buff And Debuff Timers"] = nil,
["Buffbar Height"] = nil,
["Buffbar Width"] = nil,
["Buff/Debuff Icons"] = nil,
["Buff/Debuff Indicators"] = nil,
["Buff Indicators"] = nil,
["Buff Limit"] = "버프 한도",
["Buff Position"] = "버프 위치",
["Buffs"] = nil,
@@ -106,6 +105,15 @@ pfUI_translation["koKR"] = {
["Center Text"] = "가운데 텍스트",
["Center Text X Offset"] = nil,
["Center Text Y Offset"] = nil,
["Cooldown Timers"] = nil,
["Debuff Timers"] = nil,
["Duration Timers"] = nil,
["Indicator Timers"] = nil,
["Numbers"] = nil,
["Numbers And Sweep"] = nil,
["Sweep"] = nil,
["Timers"] = nil,
["Unit Buff Timers"] = nil,
["|cff33ffccBlizzard: \"Interface Options\"|r\n\nDo you want me to set up the recommended Blizzard UI settings? This will enable settings that can be found in the Interface section of your client. Options like Buff Durations, Instant Quest Text, Auto Selfcast and others will be set."] = "|cff33ffcc블리자드: \"인터페이스 옵션\"|r\n\n내가 권장하는 블리자드 UI 설정을 설치하길 원하십니까? 이렇게하면 클라이언트의 인터페이스 섹션에서 찾을 수 있는 설정이 활성화됩니다. 버프 기간, 인스턴트 퀘스트 텍스트, 자동 셀프 캐스트 및 기타 옵션이 설정됩니다.",
["|cff33ffccChat: \"Channels\"|r\n\nDo you want me to setup the chat channels of your chatframes? This would set important or personal messages to the left chat and world channels and lootinformation to the right chat."] = "|cff33ffcc채팅: \"채널\"|r\n\n채팅 프레임의 채팅 채널을 설정하길 원하십니까? 그렇다면 왼쪽하단 채팅창에 중요하거나 개인적인 메시지가 표시됩니다. 그리고 오른쪽하단 채팅창에는 월드채널,룻팅정보가 표시됩니다.",
["|cff33ffccChat: \"Layout\"|r\n\nDo you want me to adjust the layout of your chatframes? This would make sure, that every window is placed on its dedicated position."] = "|cff33ffcc채팅: \"레이아웃\"|r\n\n채팅 프레임의 레이아웃을 조정하길 원하십니까? 이렇게하면 모든 창을 전용 위치에 배치 할 수 있습니다.",
@@ -359,6 +367,7 @@ pfUI_translation["koKR"] = {
["Group Options"] = nil,
["Group-Pet"] = "파티-팻",
["Group-Target"] = "파티-타켓",
["Grow Right To Left"] = nil,
["Gryphon"] = nil,
["Gryphons"] = nil,
["Guild Online"] = "접속중인 길드원",
@@ -515,7 +524,6 @@ pfUI_translation["koKR"] = {
["Name | Health Missing"] = nil,
["Name (Linebreak) -Health Missing"] = nil,
["Nameplate Border Size"] = nil,
["Nameplate Debuffs"] = nil,
["Nameplates"] = "상바",
["Nameplate Width"] = "상바 넓이",
["Name (Short)"] = nil,
@@ -583,7 +591,6 @@ pfUI_translation["koKR"] = {
["Pixel"] = nil,
["Player"] = "플레이어",
["Player Buff Bar"] = nil,
["Player Buffs"] = nil,
["Player Castbar"] = nil,
["Player Debuff Bar"] = nil,
["Player SP/Haste Display"] = nil,
@@ -650,7 +657,6 @@ pfUI_translation["koKR"] = {
["Resting"] = "휴식",
["Reveal Unexplored Areas"] = nil,
["Switch to current zone"] = nil,
["Reverse Sweep On"] = nil,
["Right"] = "오른쪽",
["Right Actionbar"] = nil,
["Right Anchor"] = nil,
@@ -732,7 +738,6 @@ pfUI_translation["koKR"] = {
["Show Loot Icon"] = nil,
["Show Macro Text"] = "매크로 텍스트 표시",
["Show Meters By Default"] = nil,
["Show Numbers On"] = nil,
["Show On Friendly Units"] = nil,
["Show On Hostile Units"] = nil,
["Show Procs Indicators"] = nil,
@@ -750,7 +755,6 @@ pfUI_translation["koKR"] = {
["Show Spell Icon"] = nil,
["Show Spell Name"] = nil,
["Show Stacks"] = nil,
["Show Sweep On"] = nil,
["Show Tenths Below 5 Seconds"] = nil,
["Show Text"] = nil,
["Show Timer"] = nil,
@@ -818,7 +822,6 @@ pfUI_translation["koKR"] = {
["Tiny (PixelPerfect)"] = nil,
["Toggle as Tank"] = nil,
["Tooltip"] = "툴팁",
["Tooltip Auras"] = nil,
["Tooltip Position"] = "툴팁위치",
["Tooltip Text Font"] = nil,
["Tooltip Text Font Size"] = nil,
@@ -830,13 +833,11 @@ pfUI_translation["koKR"] = {
["Total Gold"] = "총 금",
["Totem Direction"] = nil,
["Totem Icons"] = nil,
["Totems"] = nil,
["Tracked Reputation"] = nil,
["Tracking Icon Size"] = nil,
["Trigger Actions On Key Down"] = nil,
["UI-Scale"] = "UI-크기",
["Unbuffed"] = nil,
["Unit Frame Auras"] = nil,
["Unit Frame Border Size"] = "유닛프레임 테두리 크기",
["Unit Frame Layout"] = "유닛프레임 레이아웃",
["Unit Frames"] = "유닛프레임",
+11 -10
View File
@@ -8,7 +8,6 @@ pfUI_translation["ruRU"] = {
["Actionbar"] = "Панель действий",
["Action Bar"] = "Панель действий",
["Action Bar Border Size"] = "Размер границы панели действий",
["Action Bars"] = nil,
["Active Addons"] = "Активные аддоны",
["Add button to the frame"] = "Добавить кнопку в окно",
["Added button"] = "Добавлена кнопка",
@@ -77,11 +76,11 @@ pfUI_translation["ruRU"] = {
["Bottom"] = "Снизу",
["Bottom Left"] = "Снизу слева",
["Bottom Right"] = "Снизу справа",
["Buff And Debuff Timers"] = nil,
["Buffbar Height"] = "Высота панели баффов",
["Buffbar Width"] = "Ширина панели баффов",
["Buff/Debuff Icons"] = "Иконки баффов/дебаффов",
["Buff/Debuff Indicators"] = "Индикаторы баффов/дебаффов",
["Buff Indicators"] = nil,
["Buff Limit"] = "Предел баффов",
["Buff Position"] = "Расположение баффов",
["Buffs"] = "Баффы",
@@ -106,6 +105,15 @@ pfUI_translation["ruRU"] = {
["Center Text"] = "Текст по центру",
["Center Text X Offset"] = nil,
["Center Text Y Offset"] = nil,
["Cooldown Timers"] = nil,
["Debuff Timers"] = nil,
["Duration Timers"] = nil,
["Indicator Timers"] = nil,
["Numbers"] = nil,
["Numbers And Sweep"] = nil,
["Sweep"] = nil,
["Timers"] = nil,
["Unit Buff Timers"] = nil,
["|cff33ffccBlizzard: \"Interface Options\"|r\n\nDo you want me to set up the recommended Blizzard UI settings? This will enable settings that can be found in the Interface section of your client. Options like Buff Durations, Instant Quest Text, Auto Selfcast and others will be set."] = "|cff33ffccBlizzard: \"Настройки интерфейса\"|r\n\nВы хотите, чтобы я установил рекомендуемые настройки пользовательского интерфейса Blizzard? Это позволит настроить параметры, которые можно найти в разделе \"Интерфейс\" вашего клиента. Будут установлены такие параметры, как Срок действия эффектов, Мгновенное отображение текста, Автовыбор себя и другие.",
["|cff33ffccChat: \"Channels\"|r\n\nDo you want me to setup the chat channels of your chatframes? This would set important or personal messages to the left chat and world channels and lootinformation to the right chat."] = "|cff33ffccЧат: \"Каналы\"|r\n\nВы хотите, чтобы я настраивал каналы для вашего чата? Это установило бы важные или личные сообщения в левый чат, а мировые каналы и информацию о добыче в правый чат.",
["|cff33ffccChat: \"Layout\"|r\n\nDo you want me to adjust the layout of your chatframes? This would make sure, that every window is placed on its dedicated position."] = "|cff33ffccЧат: \"Макет\"|r\n\nВы хотите, чтобы я скорректировал макет вашего чата? Это обеспечило бы уверенность, что каждое окно будет помещено в его выделенное положение.",
@@ -359,6 +367,7 @@ pfUI_translation["ruRU"] = {
["Group Options"] = "Настройки группы",
["Group-Pet"] = "Группа-питомец",
["Group-Target"] = "Группа-цель",
["Grow Right To Left"] = nil,
["Gryphon"] = nil,
["Gryphons"] = nil,
["Guild Online"] = "Члены гильдии в сети",
@@ -515,7 +524,6 @@ pfUI_translation["ruRU"] = {
["Name | Health Missing"] = "Имя | Отсутствующее здоровье",
["Name (Linebreak) -Health Missing"] = "Имя (разрыв линии) -Отсутствует здоровье",
["Nameplate Border Size"] = "Размер границы индикатора здоровья",
["Nameplate Debuffs"] = nil,
["Nameplates"] = "Индикаторы здоровья",
["Nameplate Width"] = "Ширина индикатора здоровья",
["Name (Short)"] = "Короткое имя",
@@ -583,7 +591,6 @@ pfUI_translation["ruRU"] = {
["Pixel"] = "пиксель",
["Player"] = "Игрок",
["Player Buff Bar"] = "Панель баффов игрока",
["Player Buffs"] = nil,
["Player Castbar"] = "Панель применения игрока",
["Player Debuff Bar"] = "Панель дебаффов игрока",
["Player SP/Haste Display"] = nil,
@@ -650,7 +657,6 @@ pfUI_translation["ruRU"] = {
["Resting"] = "Отдыхающий",
["Reveal Unexplored Areas"] = "Показывать неисследованные области",
["Switch to current zone"] = "Перейти к текущей зоне",
["Reverse Sweep On"] = nil,
["Right"] = "Справа",
["Right Actionbar"] = "Правая",
["Right Anchor"] = nil,
@@ -732,7 +738,6 @@ pfUI_translation["ruRU"] = {
["Show Loot Icon"] = "Показать иконку добычи",
["Show Macro Text"] = "Показать текст макросов",
["Show Meters By Default"] = "Показать счетчики по умолчанию",
["Show Numbers On"] = nil,
["Show On Friendly Units"] = "Показать на дружественных юнитах",
["Show On Hostile Units"] = "Показать на вражеских юнитах",
["Show Procs Indicators"] = "Показать проки (Procs)",
@@ -750,7 +755,6 @@ pfUI_translation["ruRU"] = {
["Show Spell Icon"] = "Показать иконку заклинания",
["Show Spell Name"] = "Показать название заклинания",
["Show Stacks"] = "Показать стаки",
["Show Sweep On"] = nil,
["Show Tenths Below 5 Seconds"] = nil,
["Show Text"] = nil,
["Show Timer"] = "Показать таймер",
@@ -818,7 +822,6 @@ pfUI_translation["ruRU"] = {
["Tiny (PixelPerfect)"] = "Крошечный (идеальный пиксель)",
["Toggle as Tank"] = "Переключить как танк",
["Tooltip"] = "Подсказка",
["Tooltip Auras"] = nil,
["Tooltip Position"] = "Расположение подсказки",
["Tooltip Text Font"] = nil,
["Tooltip Text Font Size"] = nil,
@@ -830,13 +833,11 @@ pfUI_translation["ruRU"] = {
["Total Gold"] = "Всего золота",
["Totem Direction"] = "Направление тотемов",
["Totem Icons"] = "Иконки тотемов",
["Totems"] = nil,
["Tracked Reputation"] = "Отслеживаемая репутация",
["Tracking Icon Size"] = "Размер иконки отслеживания",
["Trigger Actions On Key Down"] = "Активация действий при нажатии клавиши (а не ее отпускании)",
["UI-Scale"] = "Масштаб пользовательского интерфейса",
["Unbuffed"] = "Без баффа",
["Unit Frame Auras"] = nil,
["Unit Frame Border Size"] = "Размер границы окна юнита",
["Unit Frame Layout"] = "Макет окна юнита",
["Unit Frames"] = "Окна юнита",
+11 -10
View File
@@ -8,7 +8,6 @@ pfUI_translation["zhCN"] = {
["Actionbar"] = "动作条",
["Action Bar"] = "动作条",
["Action Bar Border Size"] = "动作条边框大小",
["Action Bars"] = nil,
["Active Addons"] = "激活的插件",
["Add button to the frame"] = "添加按钮到框架",
["Added button"] = "已添加按钮",
@@ -77,11 +76,11 @@ pfUI_translation["zhCN"] = {
["Bottom"] = "底部",
["Bottom Left"] = "左下方",
["Bottom Right"] = "右下方",
["Buff And Debuff Timers"] = nil,
["Buffbar Height"] = "Buff条高度",
["Buffbar Width"] = "Buff条宽度",
["Buff/Debuff Icons"] = "Buff/Debuff图标",
["Buff/Debuff Indicators"] = "Buff/Debuff指示器",
["Buff Indicators"] = nil,
["Buff Limit"] = "Buff总数量",
["Buff Position"] = "Buff显示的位置",
["Buffs"] = "Buff(增益)",
@@ -106,6 +105,15 @@ pfUI_translation["zhCN"] = {
["Center Text"] = "中间文字",
["Center Text X Offset"] = "中心文本水平偏移量",
["Center Text Y Offset"] = "中心文本垂直偏移量",
["Cooldown Timers"] = nil,
["Debuff Timers"] = nil,
["Duration Timers"] = nil,
["Indicator Timers"] = nil,
["Numbers"] = nil,
["Numbers And Sweep"] = nil,
["Sweep"] = nil,
["Timers"] = nil,
["Unit Buff Timers"] = nil,
["|cff33ffccBlizzard: \"Interface Options\"|r\n\nDo you want me to set up the recommended Blizzard UI settings? This will enable settings that can be found in the Interface section of your client. Options like Buff Durations, Instant Quest Text, Auto Selfcast and others will be set."] = "|cff33ffcc暴雪: \"界面选项\"|r\n\n你要我安装暴雪UI设置建议? 启用此设置可以在客户端的接口部分找到。 像buff持续时间选项,即时任务文本,自动施法和其他功能将被设置。",
["|cff33ffccChat: \"Channels\"|r\n\nDo you want me to setup the chat channels of your chatframes? This would set important or personal messages to the left chat and world channels and lootinformation to the right chat."] = "|cff33ffcc聊天框架: \"聊天频道\"|r\n\n你想建立聊天框架中新的聊天频道? 这将设置重要或个人信息到左侧聊天。 和世界频道和信息到正确的聊天窗口。",
["|cff33ffccChat: \"Layout\"|r\n\nDo you want me to adjust the layout of your chatframes? This would make sure, that every window is placed on its dedicated position."] = "|cff33ffcc聊天框架: \"聊天窗口布局\"|r\n\n你要我调整聊天框架的布局吗? 这将确保每个窗口都放在其专用位置上。",
@@ -359,6 +367,7 @@ pfUI_translation["zhCN"] = {
["Group Options"] = "队伍选项",
["Group-Pet"] = "小队宠物",
["Group-Target"] = "小队目标",
["Grow Right To Left"] = nil,
["Gryphon"] = "狮鹫",
["Gryphons"] = "狮鹫(多个)",
["Guild Online"] = "公会在线",
@@ -515,7 +524,6 @@ pfUI_translation["zhCN"] = {
["Name | Health Missing"] = "名字 | 失去的生命值",
["Name (Linebreak) -Health Missing"] = "名字 (换行符) -失去的生命值",
["Nameplate Border Size"] = "姓名板边框尺寸",
["Nameplate Debuffs"] = nil,
["Nameplates"] = "姓名板",
["Nameplate Width"] = "姓名板宽度",
["Name (Short)"] = "名字 (短)",
@@ -583,7 +591,6 @@ pfUI_translation["zhCN"] = {
["Pixel"] = "像素",
["Player"] = "玩家",
["Player Buff Bar"] = "玩家Buffs条",
["Player Buffs"] = nil,
["Player Castbar"] = "玩家施法条",
["Player Debuff Bar"] = "玩家Debuffs条",
["Player SP/Haste Display"] = nil,
@@ -650,7 +657,6 @@ pfUI_translation["zhCN"] = {
["Resting"] = "休息中",
["Reveal Unexplored Areas"] = "展示未勘探的地区",
["Switch to current zone"] = nil,
["Reverse Sweep On"] = nil,
["Right"] = "右边",
["Right Actionbar"] = "右侧动作条",
["Right Anchor"] = "右锚点",
@@ -733,7 +739,6 @@ pfUI_translation["zhCN"] = {
["Show Loot Icon"] = "显示拾取权图标",
["Show Macro Text"] = "显示宏的名字",
["Show Meters By Default"] = "由默认显示其他接口表",
["Show Numbers On"] = nil,
["Show On Friendly Units"] = "显示友好单位姓名板",
["Show On Hostile Units"] = "显示敌对单位姓名板",
["Show Procs Indicators"] = "显示触发效果计时器",
@@ -751,7 +756,6 @@ pfUI_translation["zhCN"] = {
["Show Spell Icon"] = "显示技能图标",
["Show Spell Name"] = "显示技能名称",
["Show Stacks"] = "显示堆叠",
["Show Sweep On"] = nil,
["Show Tenths Below 5 Seconds"] = nil,
["Show Text"] = "显示文本",
["Show Timer"] = "显示计时器",
@@ -819,7 +823,6 @@ pfUI_translation["zhCN"] = {
["Tiny (PixelPerfect)"] = "极小(像素完美)",
["Toggle as Tank"] = "切换成为坦克",
["Tooltip"] = "信息提示框",
["Tooltip Auras"] = nil,
["Tooltip Position"] = "信息提示框位置",
["Tooltip Text Font"] = "鼠标提示框字体",
["Tooltip Text Font Size"] ="鼠标提示框字体大小",
@@ -831,13 +834,11 @@ pfUI_translation["zhCN"] = {
["Total Gold"] = "总黄金",
["Totem Direction"] = "图腾方向",
["Totem Icons"] = "图腾图标",
["Totems"] = nil,
["Tracked Reputation"] = "被追踪的声望",
["Tracking Icon Size"] = "追踪图标大小",
["Trigger Actions On Key Down"] = "按下即施法",
["UI-Scale"] = "UI缩放比例",
["Unbuffed"] = "该Buff缺失者",
["Unit Frame Auras"] = nil,
["Unit Frame Border Size"] = "头像边框大小",
["Unit Frame Layout"] = "头像框架样式",
["Unit Frames"] = "头像框架",
+11 -10
View File
@@ -8,7 +8,6 @@ pfUI_translation["zhTW"] = {
["Actionbar"] = "動作條",
["Action Bar"] = nil,
["Action Bar Border Size"] = "動作條邊框大小",
["Action Bars"] = nil,
["Active Addons"] = "啟動的外掛程式",
["Add button to the frame"] = nil,
["Added button"] = nil,
@@ -77,11 +76,11 @@ pfUI_translation["zhTW"] = {
["Bottom"] = "底部",
["Bottom Left"] = "左下方",
["Bottom Right"] = "右下方",
["Buff And Debuff Timers"] = nil,
["Buffbar Height"] = "Buff高度",
["Buffbar Width"] = "Buff寬度",
["Buff/Debuff Icons"] = nil,
["Buff/Debuff Indicators"] = nil,
["Buff Indicators"] = nil,
["Buff Limit"] = "Buff總數量",
["Buff Position"] = "Buff顯示的位置",
["Buffs"] = "Buff(增益)",
@@ -106,6 +105,15 @@ pfUI_translation["zhTW"] = {
["Center Text"] = "中間文字",
["Center Text X Offset"] = nil,
["Center Text Y Offset"] = nil,
["Cooldown Timers"] = nil,
["Debuff Timers"] = nil,
["Duration Timers"] = nil,
["Indicator Timers"] = nil,
["Numbers"] = nil,
["Numbers And Sweep"] = nil,
["Sweep"] = nil,
["Timers"] = nil,
["Unit Buff Timers"] = nil,
["|cff33ffccBlizzard: \"Interface Options\"|r\n\nDo you want me to set up the recommended Blizzard UI settings? This will enable settings that can be found in the Interface section of your client. Options like Buff Durations, Instant Quest Text, Auto Selfcast and others will be set."] = "|cff33ffcc暴雪: \"介面選項\"|r\n\n你要我安裝暴雪UI設置建議? 啟用此設置可以在用戶端的介面部分找到。 像buff持續時間選項,即時任務文本,自動施法和其他功能將被設置。",
["|cff33ffccChat: \"Channels\"|r\n\nDo you want me to setup the chat channels of your chatframes? This would set important or personal messages to the left chat and world channels and lootinformation to the right chat."] = "|cff33ffcc聊天框架: \"聊天頻道\"|r\n\n你想建立聊天框架中新的聊天頻道? 這將設置重要或個人資訊到左側聊天。 和世界頻道和資訊到正確的聊天視窗。",
["|cff33ffccChat: \"Layout\"|r\n\nDo you want me to adjust the layout of your chatframes? This would make sure, that every window is placed on its dedicated position."] = "|cff33ffcc聊天框架: \"聊天視窗佈局\"|r\n\n你要我調整聊天框架的佈局嗎? 這將確保每個視窗都放在其專用位置上。",
@@ -359,6 +367,7 @@ pfUI_translation["zhTW"] = {
["Group Options"] = nil,
["Group-Pet"] = "小隊寵物",
["Group-Target"] = "小隊目標",
["Grow Right To Left"] = nil,
["Gryphon"] = nil,
["Gryphons"] = nil,
["Guild Online"] = "公會線上",
@@ -515,7 +524,6 @@ pfUI_translation["zhTW"] = {
["Name | Health Missing"] = nil,
["Name (Linebreak) -Health Missing"] = nil,
["Nameplate Border Size"] = nil,
["Nameplate Debuffs"] = nil,
["Nameplates"] = "姓名板",
["Nameplate Width"] = "姓名板高度",
["Name (Short)"] = "名字 (短)",
@@ -583,7 +591,6 @@ pfUI_translation["zhTW"] = {
["Pixel"] = nil,
["Player"] = "玩家",
["Player Buff Bar"] = nil,
["Player Buffs"] = nil,
["Player Castbar"] = nil,
["Player Debuff Bar"] = nil,
["Player SP/Haste Display"] = nil,
@@ -650,7 +657,6 @@ pfUI_translation["zhTW"] = {
["Resting"] = "休息中",
["Reveal Unexplored Areas"] = "展示未勘探的地區",
["Switch to current zone"] = nil,
["Reverse Sweep On"] = nil,
["Right"] = "右邊",
["Right Actionbar"] = nil,
["Right Anchor"] = nil,
@@ -732,7 +738,6 @@ pfUI_translation["zhTW"] = {
["Show Loot Icon"] = nil,
["Show Macro Text"] = "顯示巨集的名字",
["Show Meters By Default"] = "由默認顯示其他介面表",
["Show Numbers On"] = nil,
["Show On Friendly Units"] = nil,
["Show On Hostile Units"] = nil,
["Show Procs Indicators"] = nil,
@@ -750,7 +755,6 @@ pfUI_translation["zhTW"] = {
["Show Spell Icon"] = nil,
["Show Spell Name"] = nil,
["Show Stacks"] = nil,
["Show Sweep On"] = nil,
["Show Tenths Below 5 Seconds"] = nil,
["Show Text"] = nil,
["Show Timer"] = nil,
@@ -818,7 +822,6 @@ pfUI_translation["zhTW"] = {
["Tiny (PixelPerfect)"] = "極小(像素完美)",
["Toggle as Tank"] = "切換成為坦克",
["Tooltip"] = "滑鼠提示框",
["Tooltip Auras"] = nil,
["Tooltip Position"] = "滑鼠提示框位置",
["Tooltip Text Font"] = nil,
["Tooltip Text Font Size"] = nil,
@@ -830,13 +833,11 @@ pfUI_translation["zhTW"] = {
["Total Gold"] = "總金量",
["Totem Direction"] = nil,
["Totem Icons"] = nil,
["Totems"] = nil,
["Tracked Reputation"] = nil,
["Tracking Icon Size"] = "追蹤圖示大小",
["Trigger Actions On Key Down"] = nil,
["UI-Scale"] = "UI縮放比例",
["Unbuffed"] = "該Buff缺失者",
["Unit Frame Auras"] = nil,
["Unit Frame Border Size"] = "頭像邊框大小",
["Unit Frame Layout"] = "頭像框架樣式",
["Unit Frames"] = "頭像框架",
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\mirrortimers.lua"/>
<Include file="..\modules\feigndeath.lua"/>
<Include file="..\modules\pixelperfect.lua"/>
<Include file="..\modules\hdgraphic.lua"/>
<Include file="..\modules\share.lua"/>
<Include file="..\modules\tracking.lua"/>
+83 -101
View File
@@ -25,7 +25,7 @@ setfenv(1, pfUI:GetEnvironment())
-- return instantly when another libpredict is already active
if pfUI.api.libpredict then return end
local heals, events, hots = {}, {}, {}
local heals, hots = {}, {}
local spell_queue = { "DUMMY", "DUMMYRank 9", "TARGET" }
local player = UnitName("player")
local cache, gear_string = {}, ""
@@ -36,6 +36,18 @@ local sentTargets = {} -- [castGUID] = target name of an own cast, from UNIT_
local resOffers = {} -- [guid] = expiry of a landed resurrection the target has yet to accept
local RES_OFFER_TIMEOUT = 60 -- the target's resurrect popup declines itself after 60s
-- Tell the listeners of "libpredict:heals" that the heals on `target` (a
-- name) changed, and again at `expiry`, when a heal that is never stopped
-- drops out of UnitGetIncomingHeals.
local function HealsChanged(target, expiry)
pfUI.events:TriggerEvent("libpredict:heals", target)
if expiry then
C_Timer.After(expiry - GetTime(), function()
pfUI.events:TriggerEvent("libpredict:heals", target)
end)
end
end
-- Localized spell names resolved once from canonical rank-1 spellIDs.
-- Every rank shares the same name, so per-rank comparisons elsewhere can
-- be done against these constants without per-locale or per-rank tables.
@@ -134,19 +146,6 @@ local function OnOwnCastStart(spellId, castGUID)
local amount = cache[spell_queue[2]][1]
local casttime = castTime
if spellName == REGROWTH then
local fullSpell = spell_queue[2]
local _, _, rankStr = fullSpell and string.find(fullSpell, "Rank (%d+)")
local rankNum = rankStr and tonumber(rankStr) or nil
if libpredict.sender.regrowth_timer then
libpredict.sender.regrowth_target_next = target
libpredict.sender.regrowth_rank_next = rankNum
else
libpredict.sender.regrowth_target = target
libpredict.sender.regrowth_rank = rankNum
end
end
if spellName == PRAYER_OF_HEALING then
-- target is already correctly set from spell_queue[3]:
-- selfcast (ALT) = player, otherwise = current target
@@ -207,34 +206,35 @@ local function OnOwnCastSucceeded(spellId, castGUID)
libpredict:HealStop(player)
local spellName = C_Spell.GetSpellName(spellId)
if spellName == REGROWTH then
local now = pfUI.uf.now or GetTime()
if libpredict.sender.regrowth_timer then
libpredict.sender.regrowth_start_next = now
else
libpredict.sender.regrowth_start = now
end
libpredict.sender.regrowth_timer = now + 0.1
end
libpredict.sender.castGUID = nil
libpredict.sender.current_cast = nil
libpredict.sender.current_cast_target = nil
if not target then return end
-- Instant HoTs — classify by name (rank-independent) instead of a
-- hardcoded per-rank ID table.
-- HoTs — classify by name (rank-independent) instead of a hardcoded
-- per-rank ID table.
local hotType
if spellName == REJUVENATION then hotType = "Reju"
elseif spellName == RENEW then hotType = "Renew"
elseif spellName == REGROWTH then hotType = "Regr"
end
if hotType then
local duration = hotType == "Reju" and rejuvDuration or renewDuration
local rank = 0
local rankSub = C_Spell.GetSpellSubtext(spellId)
if rankSub and rankSub ~= "" then
rank = tonumber((string.gsub(rankSub, "Rank ", ""))) or 0
end
if hotType == "Regr" then
-- Regrowth's HoT follows its direct heal a moment later
local start = GetTime()
C_Timer.After(0.1, function()
libpredict:Hot(player, target, hotType, 20, start, "UNIT_SPELLCAST_SUCCEEDED", rank)
libpredict.sender:SendHealCommMsg(hotType .. "/" .. target .. "/20/" .. rank .. "/")
end)
return
end
local duration = hotType == "Reju" and rejuvDuration or renewDuration
libpredict:Hot(player, target, hotType, duration, nil, "UNIT_SPELLCAST_SUCCEEDED", rank)
libpredict.sender:SendHealCommMsg(hotType .. "/" .. target .. "/" .. duration .. "/" .. rank .. "/")
end
@@ -321,25 +321,16 @@ end
local function RefreshResFrames(guid)
for _, frame in pairs(pfUI.uf.frames) do
if frame.unitstr and UnitGUID(frame.unitstr) == guid then
frame.update_indicators = true
pfUI.uf:QueueUpdate(frame, "indicators")
end
end
end
libpredict:SetScript("OnUpdate", function()
-- throttle cleanup - no need to check every frame
local now = pfUI.uf.now or GetTime()
if (this.tick or 0) > now then return end
this.tick = now + pfUI.throttle:Get("libpredict") -- Default: Normal (10 FPS)
-- update on timeout events
for timestamp, targets in pairs(events) do
if now >= timestamp then
events[timestamp] = nil
end
end
-- drop resurrection offers the target accepted or let run out
-- Drop resurrection offers the target accepted or let run out. Nothing reports
-- either, so a ticker checks while any offer is open.
local resTicker
local function CheckResOffers()
local now = GetTime()
for guid, expiry in pairs(resOffers) do
local unit = UnitTokenFromGUID(guid)
if now >= expiry or not (unit and UnitIsDeadOrGhost(unit)) then
@@ -347,7 +338,11 @@ libpredict:SetScript("OnUpdate", function()
RefreshResFrames(guid)
end
end
end)
if not next(resOffers) then
resTicker:Cancel()
resTicker = nil
end
end
function libpredict:ParseComm(sender, msg)
local msgtype, target, heal, time, rank
@@ -456,7 +451,7 @@ function libpredict:ParseChatMessage(sender, msg)
end
elseif msgtype == "Hot" then
-- Duplicate check: ignore same sender+target+spell within DUPLICATE_WINDOW
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local key = sender .. target .. heal
if recentHots[key] and (now - recentHots[key]) < DUPLICATE_WINDOW then
if libpredict.debug then
@@ -496,22 +491,16 @@ function libpredict:ParseChatMessage(sender, msg)
end
end
function libpredict:AddEvent(time, target)
events[time] = events[time] or {}
table.insert(events[time], target)
end
function libpredict:Heal(sender, target, amount, duration, senderGuid)
if not sender or not target or not amount or not duration then
return
end
local now = pfUI.uf.now or GetTime()
local timeout = duration/1000 + now
local timeout = duration/1000 + GetTime()
heals[target] = heals[target] or {}
heals[target][sender] = { amount, timeout }
if senderGuid then healGuidToName[senderGuid] = sender end
libpredict:AddEvent(timeout, target)
HealsChanged(target, timeout)
end
-- Debug flag
@@ -535,7 +524,7 @@ function libpredict:Hot(sender, target, spell, duration, startTime, source, rank
local existingRank = tonumber(existing.rank) or 0
local newRank = tonumber(rank) or 0
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local existingTimeleft = (existing.start + existing.duration) - now
-- If existing HoT is still active and has higher rank, don't overwrite
@@ -548,7 +537,7 @@ function libpredict:Hot(sender, target, spell, duration, startTime, source, rank
end
end
local now = pfUI.uf.now or GetTime()
local now = GetTime()
hots[target][spell].duration = duration
hots[target][spell].start = startTime or now
hots[target][spell].rank = rank -- Store rank for protection
@@ -567,7 +556,7 @@ function libpredict:Hot(sender, target, spell, duration, startTime, source, rank
if pfUI and pfUI.uf and pfUI.uf.frames then
for _, frame in pairs(pfUI.uf.frames) do
if frame.namecache == target then
frame.update_aura = true
pfUI.uf:QueueUpdate(frame, "aura")
if libpredict.debug then
DEFAULT_CHAT_FRAME:AddMessage(" |cff00ff00-> Frame update triggered for " .. (frame:GetName() or "?") .. "|r")
end
@@ -581,6 +570,7 @@ function libpredict:HealStop(sender)
for tsender in pairs(heals[ttarget]) do
if sender == tsender then
heals[ttarget][tsender] = nil
HealsChanged(ttarget)
end
end
end
@@ -596,7 +586,7 @@ function libpredict:HealDelay(sender, delay)
for tsender, amount in pairs(heals[target]) do
if sender == tsender then
amount[2] = amount[2] + delay
libpredict:AddEvent(amount[2], target)
HealsChanged(target, amount[2])
end
end
end
@@ -612,7 +602,7 @@ function libpredict:UnitGetIncomingHeals(unit)
if not heals[name] then
return sumheal
else
local now = pfUI.uf.now or GetTime()
local now = GetTime()
for sender, amount in pairs(heals[name]) do
if amount[2] <= now then
heals[name][sender] = nil
@@ -744,7 +734,7 @@ hooksecurefunc("CastSpell", function(id, bookType)
if effect == REJUVENATION then
local target = spell_queue[3]
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local key = "Reju" .. target
-- Cooldown-Check
@@ -761,7 +751,7 @@ hooksecurefunc("CastSpell", function(id, bookType)
libpredict.sender:SendHealCommMsg("Reju/"..target.."/"..rejuvDuration.."/"..rankStr.."/")
elseif effect == RENEW then
local target = spell_queue[3]
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local key = "Renew" .. target
-- Cooldown-Check
@@ -807,7 +797,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
if effect == REJUVENATION then
local hotTarget = target or default
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local key = "Reju" .. hotTarget
-- Cooldown-Check
@@ -824,7 +814,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
elseif effect == RENEW then
local hotTarget = target or default
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local key = "Renew" .. hotTarget
-- Cooldown-Check
@@ -867,7 +857,7 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
if effect == REJUVENATION then
local hotTarget = spell_queue[3]
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local key = "Reju" .. hotTarget
-- Cooldown-Check
@@ -884,7 +874,7 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
elseif effect == RENEW then
local hotTarget = spell_queue[3]
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local key = "Renew" .. hotTarget
-- Cooldown-Check
@@ -932,39 +922,33 @@ libpredict.sender.SendResCommMsg = function(self, msg)
-- BG groups are handled by RAID channel
end
libpredict.sender:SetScript("OnUpdate", function()
-- trigger delayed regrowth timers
local now = pfUI.uf.now or GetTime()
if this.regrowth_timer and now > this.regrowth_timer then
local target = this.regrowth_target or player
local duration = 20
local startTime = this.regrowth_start
local rank = this.regrowth_rank
libpredict:Hot(player, target, "Regr", duration, startTime, "OnUpdate", rank)
local rankStr = rank and tostring(rank) or "0"
libpredict.sender:SendHealCommMsg("Regr/"..target.."/"..duration.."/"..rankStr.."/")
-- Apply next queued Regrowth if available
this.regrowth_target = this.regrowth_target_next
this.regrowth_start = this.regrowth_start_next
this.regrowth_rank = this.regrowth_rank_next
this.regrowth_target_next = nil
this.regrowth_start_next = nil
this.regrowth_rank_next = nil
this.regrowth_timer = nil
-- Own casts arrive as "player". Other casters only matter as group members
-- (the only names resolveNameFromGuid knows), so their events are filtered to
-- the group's tokens, and solo nobody else's casts arrive at all. A member in
-- a raid also fires as raidN for its own casts; the handler drops those by
-- UnitIsUnit, and other repeats by castGUID.
local GROUP_CAST_EVENTS = {
"UNIT_SPELLCAST_START", "UNIT_SPELLCAST_SUCCEEDED", "UNIT_SPELLCAST_INTERRUPTED",
}
local castunits = {}
local function RegisterCastEvents()
local n, token = 0, IsInRaid() and "raid" or "party"
castunits[1] = "player"
for i = 1, IsInRaid() and GetNumRaidMembers() or GetNumPartyMembers() do
castunits[i + 1] = token .. i
n = i
end
end)
-- Own casts arrive as "player"; every other caster fires once per unit token
-- it currently holds (raidN, target, nameplateN, ...), so other-unit handlers
-- drop the repeats by castGUID.
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_SENT")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_START")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_DELAYED")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_FAILED")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
for i = table.getn(castunits), n + 2, -1 do castunits[i] = nil end
for i = 1, table.getn(GROUP_CAST_EVENTS) do
libpredict.sender:RegisterUnitEvent(GROUP_CAST_EVENTS[i], unpack(castunits))
end
end
RegisterCastEvents()
libpredict.sender:RegisterUnitEvent("UNIT_SPELLCAST_SENT", "player")
libpredict.sender:RegisterUnitEvent("UNIT_SPELLCAST_DELAYED", "player")
libpredict.sender:RegisterUnitEvent("UNIT_SPELLCAST_FAILED", "player")
libpredict.sender:RegisterEvent("PARTY_MEMBERS_CHANGED")
libpredict.sender:RegisterEvent("RAID_ROSTER_UPDATE")
libpredict.sender:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
-- Shared cleanup helper for failed/interrupted casts
@@ -980,12 +964,6 @@ local function onCastFailed()
s:SendResCommMsg("RESNO " .. (target or ""))
s.resurrecting = nil
end
if s.current_cast == REGROWTH then
s.regrowth_timer = nil
s.regrowth_start = nil
s.regrowth_target_next = nil
s.regrowth_start_next = nil
end
s.castGUID = nil
s.current_cast = nil
s.current_cast_target = nil
@@ -1032,6 +1010,7 @@ local function OnResurrect()
-- arg8=destGUID
resOffers[arg8] = GetTime() + RES_OFFER_TIMEOUT
RefreshResFrames(arg8)
resTicker = resTicker or C_Timer.NewTicker(.1, CheckResOffers)
end
local lastOtherCast = {} -- [event] = castGUID of the last other-unit event handled
@@ -1041,6 +1020,9 @@ libpredict.sender:SetScript("OnEvent", function()
if arg2 == "SPELL_HEAL" then OnHeal()
elseif arg2 == "SPELL_RESURRECT" then OnResurrect() end
return
elseif event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE" then
RegisterCastEvents()
return
end
-- UNIT_SPELLCAST_SENT: arg1=unit, arg2=target token, arg3=castGUID, arg4=spellID
@@ -1115,7 +1097,7 @@ function libpredict:GetHotDuration(unit, spell)
-- FALLBACK: the HealComm/cast-fed table, for HoTs C_UnitAuras has no timer for
local start, duration, timeleft
local now = pfUI.uf.now or GetTime()
local now = GetTime()
local unitName = UnitName(unit)
local unitdata = hots[unitName]
+1 -17
View File
@@ -31,13 +31,8 @@ libthrottle.presetNames = {
-- Default throttle categories
libthrottle.defaults = {
nameplates = "custom",
nameplates_target = "custom",
nameplates_castbar = "custom",
nameplates_mass = "custom",
tooltip_cursor = "fastest",
chat_tab = "custom",
swingtimer = "custom",
}
-- Convert FPS to throttle delay in seconds
@@ -144,8 +139,7 @@ function libthrottle:ResetAllToDefaults()
end
-- Initialize - set defaults if config doesn't exist
libthrottle:SetScript("OnEvent", function()
if event == "PLAYER_ENTERING_WORLD" then
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
if not _G.pfUI_throttle then
_G.pfUI_throttle = {}
end
@@ -158,19 +152,9 @@ libthrottle:SetScript("OnEvent", function()
end
-- Set defaults for custom fields if missing
if not _G.pfUI_throttle.nameplates_target_custom then _G.pfUI_throttle.nameplates_target_custom = "50" end
if not _G.pfUI_throttle.nameplates_custom then _G.pfUI_throttle.nameplates_custom = "10" end
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "100" end
if not _G.pfUI_throttle.nameplates_mass_custom then _G.pfUI_throttle.nameplates_mass_custom = "7" end
if not _G.pfUI_throttle.tooltip_cursor_custom then _G.pfUI_throttle.tooltip_cursor_custom = "10" end
if not _G.pfUI_throttle.chat_tab_custom then _G.pfUI_throttle.chat_tab_custom = "10" end
if not _G.pfUI_throttle.swingtimer_custom then _G.pfUI_throttle.swingtimer_custom = "50" end
this:UnregisterEvent("PLAYER_ENTERING_WORLD")
end
end)
libthrottle:RegisterEvent("PLAYER_ENTERING_WORLD")
-- Export to pfUI namespace for easier access
pfUI.throttle = libthrottle
+45 -66
View File
@@ -2,20 +2,17 @@
setfenv(1, pfUI:GetEnvironment())
--[[ libunitscan ]]--
-- A pfUI library that remembers the class, level and guild of every player it
-- sees: in the friends, guild, raid and party rosters, in /who results, and as
-- target, mouseover or nameplate. The data is persisted within pfUI_playerDB,
-- for the places that only have a player's name, such as chat links and
-- offline friends. Anything with a unit reads the unit itself instead.
-- A pfUI library that remembers the level of every player it sees: in the
-- friends, guild, raid and party rosters, in /who results, and as target,
-- mouseover or nameplate. The levels are persisted within pfUI_playerDB, for
-- the places that only have a player's name, such as chat links and offline
-- friends. Anything with a unit reads the unit itself instead. A player's
-- class comes from C_PlayerCache.GetPlayerInfoByName, not from here.
--
-- External functions:
-- GetUnitInfo(name)
-- Returns information of the given player name. Returns nil if the player
-- was never seen.
--
-- class[String] - The class of the player
-- level[Number] - The level of the player
-- guild[String] - The guild name of the player
-- GetPlayerLevel(name)
-- Returns the last seen level of the given player name, or nil if it was
-- never seen.
--
-- return instantly when another libunitscan is already active
@@ -23,33 +20,33 @@ if pfUI.api.libunitscan then return end
local players = {}
-- Feed (guid, name, classToken) into ClassicAPI's persistent name
-- cache when the unit's GUID is resolvable. Roster-style packets
-- (party / raid / inspect on glance) carry class inline, so the
-- engine often doesn't issue a separate name query and the DLL's
-- NameCache hook never fires for them — this is where we close that gap.
local function RememberByUnit(unit, name, class)
if not (name and class) then return end
-- Feed a unit into ClassicAPI's persistent name cache. Roster-style packets
-- (party / raid / inspect on glance) carry class inline, so the engine often
-- doesn't issue a separate name query and the DLL's NameCache hook never
-- fires for them — this is where we close that gap.
local function RememberByUnit(unit)
local guid = UnitGUID(unit)
if not guid then return end
C_PlayerCache.RememberPlayer(guid, name, class, UnitRace(unit), UnitSex(unit))
local _, race = UnitRace(unit)
C_PlayerCache.RememberPlayer(guid, UnitName(unit), UnitClassBase(unit), race, UnitSex(unit) - 2)
end
function GetUnitInfo(name)
local ret = players[name]
if ret then
return ret.class, ret.level, ret.guild
function GetPlayerLevel(name)
local entry = players[name]
return entry and entry.level
end
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
local function AddLevel(name, level)
if not name or not level or level <= 0 then return end
local entry = players[name]
if entry then
entry.level = level
else
players[name] = { level = level }
end
end
local function AddData(name, class, level, guild)
if not name then return end
players[name] = players[name] or {}
players[name].class = class or players[name].class
players[name].level = level or players[name].level
players[name].guild = guild or players[name].guild
end
local libunitscan = CreateFrame("Frame", "pfUnitScan", UIParent)
libunitscan:RegisterEvent("PLAYER_ENTERING_WORLD")
libunitscan:RegisterEvent("FRIENDLIST_UPDATE")
@@ -68,58 +65,45 @@ libunitscan:SetScript("OnEvent", function()
players = pfUI_playerDB
-- update own character details
local name = UnitName("player")
local class = UnitClassBase("player")
local level = UnitLevel("player")
local guild = GetGuildInfo("player")
AddData(name, class, level, guild)
RememberByUnit("player", name, class)
AddLevel(UnitName("player"), UnitLevel("player"))
RememberByUnit("player")
elseif event == "FRIENDLIST_UPDATE" then
for i = 1, GetNumFriends() do
local info = C_FriendList.GetFriendInfoByIndex(i)
if info then
local level = info.level > 0 and info.level or nil
AddData(info.name, info.classFilename, level)
AddLevel(info.name, info.level)
end
end
elseif event == "GUILD_ROSTER_UPDATE" then
local guild = GetGuildInfo("player")
local name, class, level, _
local name, level, _
for i = 1, GetNumGuildMembers() do
name, _, _, level, class = GetGuildRosterInfo(i)
class = L["class"][class] or nil
AddData(name, class, level, guild)
name, _, _, level = GetGuildRosterInfo(i)
AddLevel(name, level)
end
elseif event == "RAID_ROSTER_UPDATE" then
local name, class, level, unit, _
local name, level, _
for i = 1, GetNumRaidMembers() do
unit = "raid" .. i
name, _, _, level, class = GetRaidRosterInfo(i)
class = L["class"][class] or nil
AddData(name, class, level)
RememberByUnit(unit, name, class)
name, _, _, level = GetRaidRosterInfo(i)
AddLevel(name, level)
RememberByUnit("raid" .. i)
end
elseif event == "PARTY_MEMBERS_CHANGED" then
local name, class, level, unit, guild
local unit
for i = 1, GetNumPartyMembers() do
unit = "party" .. i
class = UnitClassBase(unit)
name = UnitName(unit)
level = UnitLevel(unit)
guild = GetGuildInfo(unit)
AddData(name, class, level, guild)
RememberByUnit(unit, name, class)
AddLevel(UnitName(unit), UnitLevel(unit))
RememberByUnit(unit)
end
elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then
for i = 1, C_FriendList.GetNumWhoResults() do
local info = C_FriendList.GetWhoInfo(i)
if info then
AddData(info.fullName, info.filename, info.level, info.fullGuildName)
AddLevel(info.fullName, info.level)
end
end
@@ -128,13 +112,8 @@ libunitscan:SetScript("OnEvent", function()
or event == "NAME_PLATE_UNIT_ADDED" and arg1
or "mouseover"
if UnitIsPlayer(scan) then
local name = UnitName(scan)
local class = UnitClassBase(scan)
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
local level = UnitLevel(scan)
level = level > 0 and level or nil
AddData(name, class, level, GetGuildInfo(scan))
RememberByUnit(scan, name, class)
AddLevel(UnitName(scan), UnitLevel(scan))
RememberByUnit(scan)
end
end
end)
+339 -256
View File
@@ -9,7 +9,7 @@ pfUI:RegisterModule("actionbar", function ()
local rawborder, border = GetBorderSize("actionbars")
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
local eventcache = { } -- contains a list of events that shall be processed later -> [event] = true
local passcache = { } -- contains the passes over the visible buttons that shall run later -> [pass] = true
local updatecache = { } -- contains a list of buttons slots that shall be refreshed later -> [slot] = true
local buttoncache = { } -- contains a list of all buttons ever created -> [slot] = frame
@@ -54,18 +54,21 @@ pfUI:RegisterModule("actionbar", function ()
if f.Hide then f:Hide() end
end
-- also abbreviate mouse buttons
local OrigGetBindingText = GetBindingText
local function GetBindingText(msg, mod, abbrev)
local txt = OrigGetBindingText(msg, mod, abbrev)
if abbrev then
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON3")], "MB3")
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON4")], "MB4")
txt = string.gsub(txt, _G[string.format("%s%s", mod, "BUTTON5")], "MB5")
txt = string.gsub(txt, _G[string.format("%s%s", mod, "MOUSEWHEELDOWN")], "MWD")
txt = string.gsub(txt, _G[string.format("%s%s", mod, "MOUSEWHEELUP")], "MWU")
-- the abbreviated text of the key bound to a command, mouse buttons included
local mouse_abbrevs = {
[_G.KEY_BUTTON3] = "MB3",
[_G.KEY_BUTTON4] = "MB4",
[_G.KEY_BUTTON5] = "MB5",
[_G.KEY_MOUSEWHEELDOWN] = "MWD",
[_G.KEY_MOUSEWHEELUP] = "MWU",
}
local function GetKeybindText(command)
local text = GetBindingText(GetBindingKey(command), "KEY_", 1)
for name, abbrev in pairs(mouse_abbrevs) do
text = text:gsub(name, abbrev)
end
return txt
return text
end
kill(MainMenuBar)
@@ -83,33 +86,34 @@ pfUI:RegisterModule("actionbar", function ()
_G.SHOW_MULTI_ACTIONBAR_3 = nil
_G.SHOW_MULTI_ACTIONBAR_4 = nil
-- events that provide special updater functions
local special_events = {
["ACTIONBAR_UPDATE_COOLDOWN"] = true,
["ACTIONBAR_UPDATE_USABLE"] = true,
["ACTIONBAR_UPDATE_STATE"] = true,
-- events that refresh one part of every visible button -> [event] = pass.
-- Page changes only touch the main bar, whose pager handles them.
local pass_events = {
["ACTIONBAR_UPDATE_COOLDOWN"] = "cooldown",
-- action slots take ACTION_USABLE_CHANGED; the stance buttons still
-- need a pass on this one
["ACTIONBAR_UPDATE_USABLE"] = "stance",
["ACTIONBAR_UPDATE_STATE"] = "state",
-- the active border of attack, auto repeat and tradeskill actions
["PLAYER_ENTER_COMBAT"] = "state",
["PLAYER_LEAVE_COMBAT"] = "state",
["START_AUTOREPEAT_SPELL"] = "state",
["STOP_AUTOREPEAT_SPELL"] = "state",
["CRAFT_SHOW"] = "state",
["CRAFT_CLOSE"] = "state",
["TRADE_SKILL_SHOW"] = "state",
["TRADE_SKILL_CLOSE"] = "state",
-- fires on every ammo shot / consumable use, but only counts, equipped
-- state, cooldowns and usability can change from it
["UNIT_INVENTORY_CHANGED"] = true,
["UNIT_INVENTORY_CHANGED"] = "inventory",
-- the keybind text of every button, shown or not
["UPDATE_BINDINGS"] = "bindings",
}
-- events that are shared across all buttons
-- events that refresh every button
local global_events = {
-- slot/button updates
["PLAYER_ENTERING_WORLD"] = true,
["ACTIONBAR_SLOT_CHANGED"] = true,
["UPDATE_BINDINGS"] = true,
["ACTIONBAR_PAGE_CHANGED"] = true,
["UPDATE_BONUS_ACTIONBAR"] = true,
["CRAFT_SHOW"] = true,
["CRAFT_CLOSE"] = true,
["TRADE_SKILL_SHOW"] = true,
["TRADE_SKILL_CLOSE"] = true,
["PLAYER_ENTER_COMBAT"] = true,
["PLAYER_LEAVE_COMBAT"] = true,
-- auto repeat action
["START_AUTOREPEAT_SPELL"] = true,
["STOP_AUTOREPEAT_SPELL"] = true,
}
-- events that are used for the aura/shapeshift bar
@@ -394,37 +398,85 @@ pfUI:RegisterModule("actionbar", function ()
self.equipped:SetShown(IsEquippedAction(sid) and C.bars.showequipped == "1")
end
local start, duration, enable, castable, autocast, token
local grid, sid, id, bar, active, texture, _
-- the keybind text, which an empty hidden button hides as well. The binding
-- itself is only read on UPDATE_BINDINGS (the "bindings" pass).
local function ButtonKeybindUpdate(self)
self.keybind:SetText(not self.hide and self.binding or "")
end
-- the active border of current, attack and auto repeat actions, which the
-- "statechange" animation follows. A full update applies it unconditionally.
local function ButtonActiveUpdate(self, active, force)
if active then
if not self.isactive and C.bars.animmode == "statechange" then
PlayButtonAnimation(self)
end
if force or not self.isactive then
self.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
self.active:Show()
self.isactive = true
end
elseif force or self.isactive then
self.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
self.active:Hide()
self.isactive = nil
end
end
-- the pulsing border of a spell whose proc glow is lit
local function ButtonProcShow(self, glow)
if glow then
if not self.proc:IsShown() then
self.proc:Show()
self.proc.pulse:Play()
end
elseif self.proc:IsShown() then
self.proc.pulse:Stop()
self.proc:Hide()
end
end
-- the spell an action slot casts, directly or through a macro, and its
-- proc glow. The glow events name that spell.
local function ButtonProcUpdate(self)
local kind, id, spellid, _
if C.bars.procglow == "1" then
kind, id = GetActionInfo(self.id)
if kind == "spell" then
spellid = id
elseif kind == "macro" then
_, _, spellid = GetMacroSpell(id)
end
end
self.spellid = spellid
ButtonProcShow(self, spellid and IsSpellOverlayed(spellid))
end
local castable, autocast, token
local grid, sid, active, texture, _
local function ButtonSlotUpdate(self)
if not self then return end
self = self or this
sid = self.id -- 1 to 120
-- reset shared variables
castable, autocast, token = nil, nil, nil
-- set the own ID for compatibility to some vanilla addons
self:SetID(self.id)
self:SetID(sid)
grid = self.bar == 12 and showgrid_pet or showgrid
if self.bar == 11 then
-- stance button
bar = self.bar
id = sid
texture, _, active = GetShapeshiftFormInfo(id)
texture, _, active = GetShapeshiftFormInfo(sid)
elseif self.bar == 12 then
-- pet button
bar = self.bar
id = sid
_, _, texture, token, active, castable, autocast = GetPetActionInfo(id)
_, _, texture, token, active, castable, autocast = GetPetActionInfo(sid)
texture = token and _G[texture] or texture
else
active = IsCurrentAction(sid) or IsAutoRepeatAction(sid)
texture = GetActionTexture(sid)
bar = GetActiveBar()
id = self.bar == 1 and self.slot or sid-((self.bar)-1)*12
end
if not self.showempty and self.backdrop and not texture and grid == 0 then
@@ -435,19 +487,6 @@ pfUI:RegisterModule("actionbar", function ()
self.hide = nil
end
-- active border
if active then
if C.bars.animmode == "statechange" and not self.active:IsShown() then
PlayButtonAnimation(self)
end
self.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
self.active:Show()
else
self.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
self.active:Hide()
end
ButtonInventoryUpdate(self)
if self.bar ~= 11 and self.bar ~= 12 then
@@ -457,6 +496,8 @@ pfUI:RegisterModule("actionbar", function ()
else
self.macro:SetText("")
end
ButtonProcUpdate(self)
end
-- icon
@@ -481,14 +522,8 @@ pfUI:RegisterModule("actionbar", function ()
self.autocastable:SetShown(castable and C.bars.showcastable == "1")
end
-- keybinds
if not self.hide and self.bar == 1 and self.bar ~= 11 and self.bar ~= 12 then
self.keybind:SetText(GetBindingText(GetBindingKey("ACTIONBUTTON"..id), "KEY_", 1))
elseif not self.hide and buttontypes[self.bar] then
self.keybind:SetText(GetBindingText(GetBindingKey(buttontypes[self.bar]..id), "KEY_", 1))
else
self.keybind:SetText("")
end
ButtonActiveUpdate(self, active, true)
ButtonKeybindUpdate(self)
end
local sid, usable, oom, _
@@ -498,8 +533,9 @@ pfUI:RegisterModule("actionbar", function ()
if self.bar == 11 then
_, _, _, usable = GetShapeshiftFormInfo(sid)
oom = nil
elseif self.bar == 12 then
usable = true
usable, oom = true, nil
else
usable, oom = IsUsableAction(sid)
end
@@ -528,25 +564,22 @@ pfUI:RegisterModule("actionbar", function ()
end
end
local function ButtonRangeUpdate(self)
self = self or this
-- Keeps the range check of the slot a shown action button displays
-- enabled, so ACTION_RANGE_CHECK_UPDATE reports it. Each button holds its
-- own enable, as the client counts them per slot. An enable fires the
-- event with the current state, so a new slot starts in range until then.
local function ButtonRangeWatch(button)
local slot = C.bars.glowrange == "1" and button.bar <= 10 and button:IsVisible() and button.id or nil
if slot == button.rangeslot then return end
-- update range display
if C.bars.glowrange == "1" and self.bar ~= 11 and self.bar ~= 12 and HasAction(self.id) and ActionHasRange(self.id) and IsActionInRange(self.id) == 0 then
if not self.outofrange then
self.outofrange = true
ButtonUsableUpdate(self)
end
elseif self.outofrange then
self.outofrange = nil
ButtonUsableUpdate(self)
end
if button.rangeslot then C_ActionBar.EnableActionRangeCheck(button.rangeslot, false) end
if slot then C_ActionBar.EnableActionRangeCheck(slot, true) end
button.rangeslot = slot
button.outofrange = nil
end
local start, duration, enable
local function ButtonCooldownUpdate(button)
if not button then return end
if button.bar == 11 then
start, duration, enable = GetShapeshiftFormCooldown(button.id)
elseif button.bar == 12 then
@@ -556,183 +589,205 @@ pfUI:RegisterModule("actionbar", function ()
end
-- Nil-protect: GetActionCooldown can return nil during macro parsing/indexing
if start and duration then
CooldownFrame_SetTimer(button.cd, start, duration, enable or 1)
end
if not start or not duration then return end
enable = enable or 1
-- most buttons keep their cooldown through a cooldown event
if start == button.cdstart and duration == button.cdduration and enable == button.cdenable then return end
button.cdstart, button.cdduration, button.cdenable = start, duration, enable
CooldownFrame_SetTimer(button.cd, start, duration, enable)
end
local _, active
local function ButtonIsActiveUpdate(button)
if not button then return end
if button.bar == 11 then
_, _, active, _ = GetShapeshiftFormInfo(button.id)
_, _, active = GetShapeshiftFormInfo(button.id)
elseif button.bar == 12 then
_, _, _, _, active, _, _ = GetPetActionInfo(button.id)
_, _, _, _, active = GetPetActionInfo(button.id)
else
active = IsCurrentAction(button.id) or IsAutoRepeatAction(button.id)
end
-- active border
if active then
if not button.border_active then
button.backdrop:SetBackdropBorderColor(cr,cg,cb,1)
button.border_active = true
end
button.active:Show()
else
if button.border_active then
button.backdrop:SetBackdropBorderColor(er,eg,eb,ea)
button.border_active = nil
end
button.active:Hide()
end
ButtonActiveUpdate(button, active)
end
-- everything of a button; the slot update covers its active state
local function ButtonFullUpdate(button)
if not button then return end
ButtonSlotUpdate(button)
ButtonRangeUpdate(button)
ButtonUsableUpdate(button)
ButtonCooldownUpdate(button)
ButtonIsActiveUpdate(button)
end
local function BarsEvent(self)
self = self or this
-- the pfActionBar frame handles the events of every button; its array part
-- holds the bar frames, bars[1] to bars[12]
local bars = CreateFrame("Frame", "pfActionBar", UIParent)
-- Updates wait for the next frame, so the events of one frame share one
-- pass. Passes and full updates skip hidden bars, which catch up as they
-- show (BarShowHide). The shown buttons of a bar are its first `count` ones.
local scheduled
-- calls func on the buttons that show a slot: its own button and the main
-- bar button paged to it
local function ForSlotButtons(slot, func, a1)
local button = buttoncache[slot]
if button then func(button, a1) end
local page = bars[1][1].id - 1
local paged = slot > page and slot <= page + 12 and bars[1][slot - page]
if paged and paged ~= button then func(paged, a1) end
end
-- the range watch follows hidden buttons too, so a hidden bar lets go of
-- its slots
local function SlotButtonUpdate(button)
ButtonRangeWatch(button)
if button:IsVisible() then pfUI.bars.ButtonFullUpdate(button) end
end
local function Flush()
scheduled = nil
local inventory, cooldown, state = passcache.inventory, passcache.cooldown, passcache.state
local stance, bindings = passcache.stance, passcache.bindings
table.wipe(passcache)
if inventory or cooldown or state then
for i = 1, 12 do
local bar = bars[i]
if bar:IsVisible() then
for j = 1, bar.count do
local button = bar[j]
if inventory then ButtonInventoryUpdate(button) end
if inventory or cooldown then ButtonCooldownUpdate(button) end
if inventory then ButtonUsableUpdate(button) end
if state then ButtonIsActiveUpdate(button) end
end
end
end
end
if stance and not inventory and bars[11]:IsVisible() then
for j = 1, bars[11].count do
ButtonUsableUpdate(bars[11][j])
end
end
-- the keybinds of hidden buttons as well, as they don't catch up on them
if bindings then
for _, button in pairs(buttoncache) do
button.binding = GetKeybindText(button.bindcommand)
ButtonKeybindUpdate(button)
end
end
for slot in pairs(updatecache) do
updatecache[slot] = nil
ForSlotButtons(slot, SlotButtonUpdate)
end
end
local function Schedule()
if scheduled then return end
scheduled = true
RunNextFrame(Flush)
end
-- queues a full update of the button in a slot
local function QueueSlot(slot)
updatecache[slot] = true
Schedule()
end
-- queues a pass over the visible buttons
local function QueuePass(pass)
passcache[pass] = true
Schedule()
end
-- passes skip hidden bars, so a bar refreshes its buttons as it shows;
-- as it hides, its buttons let go of their range checks
local function BarShowHide()
local offset = (this:GetID() - 1) * 12
for j = 1, this.count do
QueueSlot(offset + j)
end
end
local function ButtonRangeEvent(button, outofrange)
if button.rangeslot ~= arg1 or button.outofrange == outofrange then return end
button.outofrange = outofrange
ButtonUsableUpdate(button)
end
local function ButtonUsableEvent(button)
if button:IsVisible() then ButtonUsableUpdate(button) end
end
local function BarsEvent()
-- slot, isInRange, checksRange of a slot a button watches
if event == "ACTION_RANGE_CHECK_UPDATE" then
ForSlotButtons(arg1, ButtonRangeEvent, arg3 and not arg2 or nil)
return
end
-- the slots whose usable state changed
if event == "ACTION_USABLE_CHANGED" then
for _, change in ipairs(arg1) do
ForSlotButtons(change.slot, ButtonUsableEvent)
end
return
end
-- the spell whose glow started or ended; each rank has its own event
if event == "SPELL_ACTIVATION_OVERLAY_GLOW_SHOW" or event == "SPELL_ACTIVATION_OVERLAY_GLOW_HIDE" then
local glow = IsSpellOverlayed(arg1)
for _, button in pairs(buttoncache) do
if button.spellid == arg1 then ButtonProcShow(button, glow) end
end
return
end
-- refresh only specific slots
if event == "ACTIONBAR_SLOT_CHANGED" and arg1 and arg1 ~= 0 then
updatecache[arg1] = true
QueueSlot(arg1)
return
end
-- run special refresh functions on next update
if special_events[event] then
eventcache[event] = true
-- refresh one part of every visible button
if pass_events[event] then
QueuePass(pass_events[event])
return
end
-- handle aura events
-- auras and forms change the stance buttons
if aura_events[event] then
for j=1,12 do
if self[11] and self[11][j] then
updatecache[self[11][j].slot] = true
end
for j = 1, bars[11].count do
QueueSlot(bars[11][j].slot)
end
return
end
-- handle pet events
if pet_events[event] then
for j=1,12 do
if self[12] and self[12][j] then
updatecache[self[12][j].slot] = true
end
for j = 1, bars[12].count do
QueueSlot(bars[12][j].slot)
end
return
end
-- handle global events
for id in pairs(buttoncache) do
updatecache[id] = true
end
end
local self, button, unlock
-- Main update loop with throttle for performance optimization
local function BarsUpdate(self)
self = self or this
-- Throttle for performance
if (this.tick_main or 0) > GetTime() then return end
this.tick_main = GetTime() + 0.025
if pfUI.unlock then
-- update all bars when entering unlock
if pfUI.unlock:IsShown() ~= unlock then
pfUI.bars:UpdateConfig()
unlock = pfUI.unlock:IsShown()
end
end
-- The cached passes below only touch visible buttons. Hidden ones (other
-- pages, hidden bars) are flagged stale and get a full update once the
-- range pass sees them visible again.
-- run cached inventory changes (ammo, reagents, consumables)
if eventcache["UNIT_INVENTORY_CHANGED"] then
eventcache["UNIT_INVENTORY_CHANGED"] = nil
for id, button in pairs(buttoncache) do
if button:IsVisible() then
ButtonInventoryUpdate(button)
ButtonCooldownUpdate(button)
ButtonUsableUpdate(button)
else
button.stale = true
end
end
end
-- run cached usable usable actions
if eventcache["ACTIONBAR_UPDATE_USABLE"] then
eventcache["ACTIONBAR_UPDATE_USABLE"] = nil
for id, button in pairs(buttoncache) do
if button:IsVisible() then ButtonUsableUpdate(button) else button.stale = true end
end
end
-- run cached cooldown events
if eventcache["ACTIONBAR_UPDATE_COOLDOWN"] then
eventcache["ACTIONBAR_UPDATE_COOLDOWN"] = nil
for id, button in pairs(buttoncache) do
if button:IsVisible() then ButtonCooldownUpdate(button) else button.stale = true end
end
end
-- run cached action state events
if eventcache["ACTIONBAR_UPDATE_STATE"] then
eventcache["ACTIONBAR_UPDATE_STATE"] = nil
for id, button in pairs(buttoncache) do
if button:IsVisible() then ButtonIsActiveUpdate(button) else button.stale = true end
end
end
for id in pairs(updatecache) do
-- run updates based on slot
pfUI.bars.ButtonFullUpdate(buttoncache[id])
-- run updates on paging actionbar if required
for i=1,12 do
if pfUI.bars[1][i].id == id then
pfUI.bars.ButtonFullUpdate(pfUI.bars[1][i])
end
end
-- clear update cache
updatecache[id] = nil
end
if ( this.tick or .2) > GetTime() then return else this.tick = GetTime() + .2 end
for id, button in pairs(buttoncache) do
if button.stale and button:IsVisible() then
-- catch up on updates skipped while hidden
button.stale = nil
ButtonFullUpdate(button)
elseif button:IsShown() then
ButtonRangeUpdate(button)
-- refresh every button and its keybind
if global_events[event] then
QueuePass("bindings")
for slot in pairs(buttoncache) do
QueueSlot(slot)
end
end
end
-- create the main event and update handler for pfUI actionbars
local bars = CreateFrame("Frame", "pfActionBar", UIParent)
-- The only unit events in the tables above; both concern the player alone.
-- A registration keeps its kind, so these have to go in unit-filtered from
-- the start -- RegisterUnitEvent over a plain registration stays plain.
@@ -750,17 +805,18 @@ pfUI:RegisterModule("actionbar", function ()
end
end
for event in pairs(special_events) do RegisterBarEvent(event) end
for event in pairs(pass_events) do RegisterBarEvent(event) end
for event in pairs(global_events) do RegisterBarEvent(event) end
for event in pairs(aura_events) do RegisterBarEvent(event) end
for event in pairs(pet_events) do RegisterBarEvent(event) end
bars:RegisterEvent("ACTION_RANGE_CHECK_UPDATE")
bars:RegisterEvent("ACTION_USABLE_CHANGED")
bars:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_SHOW")
bars:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_HIDE")
-- refresh actionbar buttons on event
bars:SetScript("OnEvent", BarsEvent)
-- update actionbar buttons
bars:SetScript("OnUpdate", BarsUpdate)
-- enable bar paging via secure functions
local function EnablePaging(bar)
if not bar.pager then
@@ -776,9 +832,8 @@ pfUI:RegisterModule("actionbar", function ()
local active = GetActiveBar()
for i=1,12 do
local id = i + (active-1)*12
bar[i].id = id
updatecache[i] = true
bar[i].id = i + (active-1)*12
QueueSlot(i)
end
end)
end
@@ -1006,8 +1061,11 @@ pfUI:RegisterModule("actionbar", function ()
-- set a static slot
f.slot = id
-- the binding command of the button, fixed on the paged main bar too
f.bindcommand = (bar == 1 and "ACTIONBUTTON" or buttontypes[bar]) .. button
-- cooldown
f.cd = CreateCooldown(f, "actionbar", f:GetName() .. "Cooldown")
f.cd = CreateCooldown(f, "spell", C.bars, "timers", f:GetName() .. "Cooldown")
-- icon
f.icon = f:CreateTexture(button_name .. "Icon", "BACKGROUND")
@@ -1065,6 +1123,23 @@ pfUI:RegisterModule("actionbar", function ()
f.active:SetAllPoints()
f.active:Hide()
-- proc glow
if bar ~= 11 and bar ~= 12 then
f.proc = CreateFrame("Frame", nil, f)
f.proc:SetBackdrop(backdrop_highlight)
f.proc:SetBackdropBorderColor(1,.8,.2,1)
f.proc:SetAllPoints()
f.proc:Hide()
f.proc.pulse = f.proc:CreateAnimationGroup()
f.proc.pulse:SetLooping("BOUNCE")
local fade = f.proc.pulse:CreateAnimation("Alpha")
fade:SetFromAlpha(1)
fade:SetToAlpha(.3)
fade:SetDuration(.5)
fade:SetSmoothing("IN_OUT")
end
-- add to buttoncache
buttoncache[id] = f
end
@@ -1168,6 +1243,7 @@ pfUI:RegisterModule("actionbar", function ()
-- don't process empty bars
if buttons == 0 then
bars[i].count = 0
bars[i]:Hide()
return
end
@@ -1186,6 +1262,11 @@ pfUI:RegisterModule("actionbar", function ()
local init = not bars[i]
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent)
bars[i]:SetID(i)
bars[i].count = buttons
if init then
bars[i]:SetScript("OnShow", BarShowHide)
if i <= 10 then bars[i]:SetScript("OnHide", BarShowHide) end
end
-- autohide
if autohide == "1" then
@@ -1201,18 +1282,12 @@ pfUI:RegisterModule("actionbar", function ()
-- only show when pet actions exists
bars[i]:SetShown(PetHasActionBar() or pfUI.unlock and pfUI.unlock:IsShown())
-- show/hide petbar on petbar updates
-- show/hide petbar on petbar updates; the pet events of the
-- pfActionBar frame refresh its buttons
if init then
bars[i]:RegisterEvent("PET_BAR_UPDATE")
bars[i]:SetScript("OnEvent", function()
-- hide obsolete buttons
for i=1, NUM_PET_ACTION_SLOTS do
if not PetHasActionBar() and bars[12][i] then
bars[12][i]:Hide()
end
end
-- refresh layout
CreateActionBar(12)
this:SetShown(PetHasActionBar() or pfUI.unlock and pfUI.unlock:IsShown())
end)
end
@@ -1270,7 +1345,7 @@ pfUI:RegisterModule("actionbar", function ()
end
-- refresh button
updatecache[bars[i][j].slot] = true
QueueSlot(bars[i][j].slot)
end
for j=buttons+1,12 do
@@ -1303,25 +1378,27 @@ pfUI:RegisterModule("actionbar", function ()
elseif i == 12 then -- pet
bars[i]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
-- make stance bar dodge by default
bars[i]:SetScript("OnShow", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints()
bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border)
UpdateMovable(bars[11], true)
end
end)
if init then
-- make stance bar dodge by default
bars[i]:HookScript("OnShow", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints()
bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border)
UpdateMovable(bars[11], true)
end
end)
-- restore old stance bar position
bars[i]:SetScript("OnHide", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints()
bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
UpdateMovable(bars[11], true)
end
end)
-- restore old stance bar position
bars[i]:SetScript("OnHide", function()
if pfUI.unlock and pfUI.unlock:IsShown() then return end
if bars[11] and bars[11]:IsShown() then
bars[11]:ClearAllPoints()
bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
UpdateMovable(bars[11], true)
end
end)
end
else -- others
bars[i]:SetPoint("TOP", 0, -i*50)
end
@@ -1382,7 +1459,7 @@ pfUI:RegisterModule("actionbar", function ()
-- create actionbars
pfUI.bars = bars
pfUI.bars.update = updatecache
pfUI.bars.QueueSlot = QueueSlot
pfUI.bars.buttons = buttoncache
pfUI.bars.ButtonFullUpdate = ButtonFullUpdate
pfUI.bars.ButtonEnter = ButtonEnter
@@ -1392,13 +1469,13 @@ pfUI:RegisterModule("actionbar", function ()
if not typ then
showgrid = state
for id in pairs(buttoncache) do
updatecache[id] = true
for slot in pairs(buttoncache) do
QueueSlot(slot)
end
elseif typ == "PET" then
showgrid_pet = state
for slot=133,142 do
updatecache[slot] = true
QueueSlot(slot)
end
end
end
@@ -1411,6 +1488,12 @@ pfUI:RegisterModule("actionbar", function ()
pfUI.bars:UpdateConfig()
-- update all bars when entering or leaving unlock mode
if pfUI.unlock then
pfUI.unlock:HookScript("OnShow", function() pfUI.bars:UpdateConfig() end)
pfUI.unlock:HookScript("OnHide", function() pfUI.bars:UpdateConfig() end)
end
-- Localize custom keybinds for additional actionbars (see Bindings.xml)
local names = {
["PAGING"] = T["Paging Actionbar"],
@@ -1492,7 +1575,7 @@ pfUI:RegisterModule("actionbar", function ()
if newID then
reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID)
end
updatecache[slot] = true
QueueSlot(slot)
end
end
@@ -1502,7 +1585,7 @@ pfUI:RegisterModule("actionbar", function ()
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
end
for slot in pairs(reagent_slots) do
updatecache[slot] = true
QueueSlot(slot)
end
end
+31 -17
View File
@@ -89,42 +89,56 @@ pfUI:RegisterModule("bags", function ()
pfUI.bag.delay = { UpdateBag = {} }
pfUI.bag:SetScript("OnUpdate", function()
-- update delayed ones every 0.1s
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
-- run the updates queued by events, batched into one pass per 0.1s
local function FlushDelayed()
local delay = pfUI.bag.delay
delay.pending = nil
if this.delay.RefreshSpells then
this.delay.RefreshSpells = nil
if delay.RefreshSpells then
delay.RefreshSpells = nil
pfUI.bag:RefreshSpells()
end
if this.delay.CheckFullUpdate then
this.delay.CheckFullUpdate = nil
if delay.CheckFullUpdate then
delay.CheckFullUpdate = nil
pfUI.bag:CheckFullUpdate()
end
if this.delay.UpdateCooldowns then
this.delay.UpdateCooldowns = nil
if delay.UpdateCooldowns then
delay.UpdateCooldowns = nil
pfUI.bag:UpdateCooldowns()
end
if this.delay.UpdateItemLock then
this.delay.UpdateItemLock = nil
if delay.UpdateItemLock then
delay.UpdateItemLock = nil
pfUI.bag:UpdateItemLock()
end
if this.delay.UpdateSearch then
this.delay.UpdateSearch = nil
if delay.UpdateSearch then
delay.UpdateSearch = nil
pfUI.bag:UpdateSearch()
end
for bag in pairs(this.delay.UpdateBag) do
this.delay.UpdateBag[bag] = nil
for bag in pairs(delay.UpdateBag) do
delay.UpdateBag[bag] = nil
pfUI.bag:UpdateBag(bag)
end
end)
end
local function ScheduleFlush()
if pfUI.bag.delay.pending then return end
pfUI.bag.delay.pending = true
C_Timer.After(.1, FlushDelayed)
end
function pfUI.bag:QueueBagUpdate(bag)
self.delay.UpdateBag[bag] = true
ScheduleFlush()
end
pfUI.bag:SetScript("OnEvent", function()
ScheduleFlush()
if event == "PLAYER_ENTERING_WORLD" then
pfUI.bag:CreateBags()
pfUI.bag.right:Hide()
@@ -415,7 +429,7 @@ pfUI:RegisterModule("bags", function ()
-- reaches it after that
local slotframe = pfUI.bags[bag].slots[slot].frame
local cdname = slotframe:GetName() .. "Cooldown"
slotframe.cd = CreateCooldown(slotframe, "bags", cdname)
slotframe.cd = CreateCooldown(slotframe, "spell", C.appearance.bags, "timers", cdname)
_G[cdname] = slotframe.cd
if not pfUI.bags[bag].slots[slot].frame.backdrop then
+1 -1
View File
@@ -152,7 +152,7 @@ pfUI:RegisterModule("buff", function ()
-- the sweep and the duration numbers (the template covers the button and
-- starts hidden); RefreshBuffButton starts its timer
buff.cd = CreateCooldown(buff, "buff", buttonName .. "Cooldown")
buff.cd = CreateCooldown(buff, "aura", C.buffs, "timers", buttonName .. "Cooldown")
buff.cd:SetFrameLevel(buff:GetFrameLevel() + 1)
buff.overlay:SetFrameLevel(buff:GetFrameLevel() + 2)
+24 -15
View File
@@ -130,15 +130,14 @@ pfUI:RegisterModule("buffwatch", function ()
GameTooltip:Hide()
end
local function StatusBarOnUpdate()
local remaining = this.endtime - GetTime()
this.bar:SetValue(remaining > 0 and remaining or 0)
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
this.time:SetText(remaining > 0 and GetColoredTimeString(remaining) or "")
-- an enabled text binding writes every frame, shown or not
local function StatusBarOnShow()
this.timer:Enable()
this.parent:RefreshPosition()
end
local function StatusBarRefreshParent()
local function StatusBarOnHide()
this.timer:Disable()
this.parent:RefreshPosition()
end
@@ -183,6 +182,16 @@ pfUI:RegisterModule("buffwatch", function ()
frame.time:SetTextColor(1,1,1,1)
frame.time:SetJustifyH("RIGHT")
-- the bar drains and the time counts down from this, engine-side
frame.duration = C_DurationUtil.CreateDuration()
frame.bar:SetTimerDuration(frame.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
frame.timer = C_DurationUtil.CreateDurationTextBinding()
frame.timer:SetFontString(frame.time)
frame.timer:SetFormatter(GetTimeFormatter())
frame.timer:SetDuration(frame.duration)
frame.timer:SetExpiredText("")
frame.timer:SetZeroDurationText("")
frame.icon = frame:CreateTexture(nil, "OVERLAY")
frame.icon:SetSize(height, height)
frame.icon:SetPoint("LEFT", frame, "LEFT", 0, 0)
@@ -195,9 +204,8 @@ pfUI:RegisterModule("buffwatch", function ()
frame.stacks:SetJustifyV("CENTER")
frame.parent = parent
frame:SetScript("OnUpdate", StatusBarOnUpdate)
frame:SetScript("OnShow", StatusBarRefreshParent)
frame:SetScript("OnHide", StatusBarRefreshParent)
frame:SetScript("OnShow", StatusBarOnShow)
frame:SetScript("OnHide", StatusBarOnHide)
frame:RegisterForClicks("LeftButtonUp", "RightButtonUp")
frame:SetScript("OnClick", StatusBarOnClick)
@@ -261,7 +269,6 @@ pfUI:RegisterModule("buffwatch", function ()
frame.bars[bar].id = data[2]
frame.bars[bar].unit = frame.unit
frame.bars[bar].type = frame.type
frame.bars[bar].endtime = GetTime() + ( data[1] > 0 and data[1] or -1 )
-- update max duration the cached remaining values is less than
-- the real one, indicates a buff renewal
@@ -313,10 +320,12 @@ pfUI:RegisterModule("buffwatch", function ()
frame.bars[bar].icon:SetTexture(data[4])
end
-- cache maxduration
if frame.bars[bar].cacheMaxDuration ~= frame.durations[uuid][2] then
frame.bars[bar].cacheMaxDuration = frame.durations[uuid][2]
frame.bars[bar].bar:SetMinMaxValues(0, frame.durations[uuid][2])
-- the bar spans the longest time left seen for this aura; one without
-- a time left (threshold -1) shows an empty bar and no time
if data[1] > 0 then
frame.bars[bar].duration:SetTimeFromEnd(GetTime() + data[1], frame.durations[uuid][2])
else
frame.bars[bar].duration:Reset()
end
-- set stacks
+126 -162
View File
@@ -5,38 +5,69 @@ pfUI:RegisterModule("castbar", function ()
local rawborder, default_border = GetBorderSize("unitframes")
local cbtexture = pfUI.media[C.appearance.castbar.texture]
-- Helper function for castbar timer formatting
local function FormatCastbarTime(value)
if C.unitframes.castbardecimals == "1" then
-- 1 decimal, round half up (matches Blizzard spellbook display)
return string.format("%.1f", floor(value * 10 + 0.5) / 10)
else
-- 2 decimals (default)
return string.format("%.2f", value)
end
-- Castbar timer text: 1 decimal rounded half up (matches Blizzard spellbook
-- display), or 2 decimals (default)
local formatter = C_StringUtil.CreateNumericRuleFormatter()
if C.unitframes.castbardecimals == "1" then
formatter:AddBreakpoint({ threshold = 0, step = .1, format = "%.1f" })
else
formatter:AddBreakpoint({ threshold = 0, format = "%.2f" })
end
-- Clear cast state on the bar and start the fade-out. On a normal end the bar
-- is left at its current fill (a completed cast is already ~full; nothing
-- snaps to full, which used to flash for a frame when the next cast stamped
-- the bar). When `failed` is set for a cast that was actually in progress, the
-- bar flashes full red before fading — the cancelled-cast indicator.
-- "cur / max": a cast counts up, a channel counts down
local castText = {
{ property = Enum.DurationTextBindingProperty.ElapsedDuration, formatter = formatter },
{ property = Enum.DurationTextBindingProperty.TotalDuration, formatter = formatter },
}
local channelText = {
{ property = Enum.DurationTextBindingProperty.RemainingDuration, formatter = formatter },
{ property = Enum.DurationTextBindingProperty.TotalDuration, formatter = formatter },
}
-- The bar's fill and timer text both follow cb.duration on their own; only
-- the pushback total in front of the text needs a refresh.
local function UpdateTimerText(cb)
if not cb.showtimer then return end
local prefix = ""
if cb.delay > 0 then
prefix = "|cffffaaaa" .. (cb.isChannel and "-" or "+") .. formatter:FormatNumber(cb.delay) .. " |r "
end
cb.timer:SetTextFormat(prefix .. "{} / {}", cb.isChannel and channelText or castText)
cb.timer:Enable()
end
-- STOP doesn't arrive for every cast (a synthetic cast has none, a remote
-- unit's can be missed out of range), so the bar also clears at its end time.
local function ArmExpiry(cb)
if cb.expiry then cb.expiry:Cancel() end
cb.expiry = C_Timer.NewTimer(cb.duration:GetRemainingDuration(), cb.onExpire)
end
-- Clear cast state on the bar and start the fade-out. The fill freezes where
-- the cast stopped (SetMinMaxValues drops the timer). When `failed` is set for
-- a cast that was actually in progress, the bar flashes full red before
-- fading — the cancelled-cast indicator.
local function ClearBar(cb, failed)
local wasActive = cb.endTime ~= nil
cb.startTime, cb.endTime, cb.isChannel = nil, nil, nil
if cb.expiry then cb.expiry:Cancel() end
cb.expiry = nil
cb.timer:Disable()
if cb.active then
cb.bar:SetMinMaxValues(0, 1)
if failed then
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar.failcolor))
cb.bar:SetValue(1)
else
cb.bar:SetValue(cb.isChannel and cb.duration:GetRemainingPercent() or cb.duration:GetElapsedPercent())
end
end
cb.active, cb.isChannel = nil, nil
cb.activeName, cb.spellID = nil, nil
cb.isTradeskill = nil
cb.tradeskillTotal, cb.tradeskillCompleted, cb.tradeskillSpellID = nil, nil, nil
cb.tradeskillSingleMs, cb.currentCraftStart = nil, nil
cb.lastMax = nil
cb.tradeskillSingle = nil
cb.delay = 0
if failed and wasActive then
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar.failcolor))
cb.bar:SetMinMaxValues(0, 1)
cb.bar:SetValue(1)
end
if cb.bar.spark then cb.bar.spark:Hide() end
if cb:GetAlpha() > 0 and not cb.fade:IsPlaying() then cb.fade:Play() end
if cb.craftbar then cb.craftbar:Hide() end
if cb:IsShown() and not cb.fade:IsPlaying() then cb.fade:Play() end
end
-- "Spellname (N)" label for an active tradeskill merge — N is the number
@@ -55,47 +86,42 @@ pfUI:RegisterModule("castbar", function ()
-- fills continuously across the chain instead of resetting per craft.
-- Also arms the per-craft spark — a thin marker that crosses the bar once
-- per craft (so it moves N× faster than the main fill on an N-stack).
local function EnterTradeskillMerge(cb, startMs, endMs, count)
local function EnterTradeskillMerge(cb, count)
local start, single = cb.duration:GetStartTime(), cb.duration:GetTotalDuration()
cb.tradeskillTotal = count
cb.tradeskillCompleted = 0
cb.tradeskillSpellID = cb.spellID
local single = endMs - startMs
cb.tradeskillSingleMs = single
cb.currentCraftStart = startMs
local mergedEnd = startMs + single * count
cb.endTime = mergedEnd
local duration = (mergedEnd - startMs) / 1000
cb.bar:SetMinMaxValues(0, duration)
cb.lastMax = duration
if cb.bar.spark then cb.bar.spark:Show() end
cb.tradeskillSingle = single
cb.craft:SetTimeFromStart(start, single)
cb.duration:SetTimeFromStart(start, single * count)
ArmExpiry(cb)
cb.craftbar:Show()
UpdateTradeskillLabel(cb)
end
-- Mark the start of craft 2..N within an active merge (called from the
-- UNIT_SPELLCAST_START handler). Resets the per-craft spark to the left
-- edge of the bar.
-- UNIT_SPELLCAST_START handler) and re-fit the bar's end to the crafts left.
local function StartTradeskillCraft(cb)
cb.currentCraftStart = GetTime() * 1000
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
cb.endTime = cb.currentCraftStart + cb.tradeskillSingleMs * remaining
local duration = (cb.endTime - cb.startTime) / 1000
cb.bar:SetMinMaxValues(0, duration)
cb.lastMax = duration
local now = GetTime()
local remaining = cb.tradeskillTotal - cb.tradeskillCompleted
cb.craft:SetTimeFromStart(now, cb.tradeskillSingle)
cb.duration:SetTimeSpan(cb.duration:GetStartTime(), now + cb.tradeskillSingle * remaining)
ArmExpiry(cb)
UpdateTradeskillLabel(cb)
end
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
-- then animates the fill from this state without touching C_Spell.
-- Stamp the bar with cast data and render text/icon/lag once. From here the
-- bar's timer and text binding follow cb.duration without any Lua.
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank, noInterrupt)
cb.startTime = startMs
cb.endTime = endMs
cb.duration:SetTimeSpan(startMs / 1000, endMs / 1000)
cb.active = true
cb.isChannel = isChannel
cb.spellID = spellID
cb.activeName = name
cb.isTradeskill = isTradeskill
cb.delay = (delayMs or 0) / 1000
cb.fade:Stop()
cb:SetAlpha(1)
cb:Show()
-- Shield an uninterruptible cast. Not on the player's own bar: the flag is
-- measured against the reader's own interrupts and silences, so on your own
@@ -124,27 +150,18 @@ pfUI:RegisterModule("castbar", function ()
cb.icon:Hide()
end
local duration = (endMs - startMs) / 1000
if cb.showlag then
local _, _, lag = GetNetStats()
cb.bar.lag:SetWidth(math.min(cb:GetWidth(), cb:GetWidth() / duration * (lag/1000)))
cb.bar.lag:SetWidth(math.min(cb:GetWidth(), cb:GetWidth() / cb.duration:GetTotalDuration() * (lag/1000)))
cb.bar.lag:Show()
else
cb.bar.lag:Hide()
end
cb.bar:SetMinMaxValues(0, duration)
cb.lastMax = duration
-- Prime the fill on this frame. StampBar otherwise leaves the previous
-- value in place (ClearBar, run on the prior cast's STOP, leaves it full),
-- so the bar would flash full for the frame between here and the next
-- OnUpdate tick. Reset the throttle too so the timer text updates promptly.
local nowSec = GetTime()
local cur = isChannel and (endMs / 1000 - nowSec) or (nowSec - startMs / 1000)
if cur < 0 then cur = 0 elseif cur > duration then cur = duration end
cb.bar:SetValue(cur)
cb.tick = 0
cb.bar:SetTimerDuration(cb.duration, Enum.StatusBarInterpolation.Immediate,
isChannel and Enum.StatusBarTimerDirection.RemainingTime or Enum.StatusBarTimerDirection.ElapsedTime)
UpdateTimerText(cb)
ArmExpiry(cb)
end
-- One-shot poll: read C_Spell for the bar's unit, stamp or clear. Called
@@ -186,6 +203,10 @@ pfUI:RegisterModule("castbar", function ()
cb:SetFrameLevel(8)
cb.unitstr = unitstr
cb.delay = 0
cb.duration = C_DurationUtil.CreateDuration()
cb.craft = C_DurationUtil.CreateDuration() -- the current craft of a tradeskill merge
cb.onExpire = function() ClearBar(cb) end
-- icon
cb.icon = CreateFrame("Frame", nil, cb)
@@ -210,7 +231,7 @@ pfUI:RegisterModule("castbar", function ()
CreateBackdropShadow(cb.bar)
-- text left
cb.bar.left = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
cb.bar.left = cb.bar:CreateFontString("Status", "OVERLAY", "GameFontNormal")
cb.bar.left:ClearAllPoints()
cb.bar.left:SetPoint("TOPLEFT", cb.bar, "TOPLEFT", 3 + C.castbar[unitstr].txtleftoffx, C.castbar[unitstr].txtleftoffy)
cb.bar.left:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", -3 + C.castbar[unitstr].txtleftoffx, C.castbar[unitstr].txtleftoffy)
@@ -221,7 +242,7 @@ pfUI:RegisterModule("castbar", function ()
cb.bar.left:SetJustifyH(C.castbar[unitstr].namealign or "LEFT")
-- text right
cb.bar.right = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
cb.bar.right = cb.bar:CreateFontString("Status", "OVERLAY", "GameFontNormal")
cb.bar.right:ClearAllPoints()
cb.bar.right:SetPoint("TOPLEFT", cb.bar, "TOPLEFT", 3 + C.castbar[unitstr].txtrightoffx, C.castbar[unitstr].txtrightoffy)
cb.bar.right:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", -3 + C.castbar[unitstr].txtrightoffx, C.castbar[unitstr].txtrightoffy)
@@ -231,6 +252,10 @@ pfUI:RegisterModule("castbar", function ()
cb.bar.right:SetFont(font, font_size, "OUTLINE")
cb.bar.right:SetJustifyH(C.castbar[unitstr].timealign or "RIGHT")
cb.timer = C_DurationUtil.CreateDurationTextBinding()
cb.timer:SetFontString(cb.bar.right)
cb.timer:SetDuration(cb.duration)
cb.bar.lag = cb.bar:CreateTexture(nil, "OVERLAY")
cb.bar.lag:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0)
cb.bar.lag:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", 0, 0)
@@ -238,85 +263,29 @@ pfUI:RegisterModule("castbar", function ()
-- Per-craft progress spark for tradeskill merge — a thin vertical line
-- that crosses the bar once per craft in the chain (so on a 5-stack it
-- moves 5x faster than the main fill). Position is updated by OnUpdate
-- while a merge is active; hidden otherwise.
cb.bar.spark = cb.bar:CreateTexture(nil, "OVERLAY")
cb.bar.spark:SetColorTexture(1, 1, 1, 0.8)
cb.bar.spark:SetWidth(2)
cb.bar.spark:SetPoint("TOP", cb.bar, "TOPLEFT", 0, 0)
cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0)
cb.bar.spark:Hide()
-- moves 5x faster than the main fill). It rides the edge of an invisible
-- fill that follows cb.craft, shown while a merge is active, and snaps
-- back to the left each time StartTradeskillCraft restarts the craft.
cb.craftbar = CreateFrame("StatusBar", nil, cb.bar)
cb.craftbar:SetAllPoints(cb.bar)
cb.craftbar:SetColorFill(1, 1, 1, 0)
cb.craftbar:SetTimerDuration(cb.craft, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
cb.craftbar:Hide()
-- Fade-out after a cast ends. The bar stays at alpha 0 while idle.
cb.craftbar.spark = cb.craftbar:CreateTexture(nil, "OVERLAY")
cb.craftbar.spark:SetColorTexture(1, 1, 1, 0.8)
cb.craftbar.spark:SetWidth(2)
cb.craftbar.spark:SetPoint("TOPRIGHT", cb.craftbar:GetStatusBarTexture(), "TOPRIGHT")
cb.craftbar.spark:SetPoint("BOTTOMRIGHT", cb.craftbar:GetStatusBarTexture(), "BOTTOMRIGHT")
-- Fade-out after a cast ends. The bar is hidden while idle; unlock mode
-- keeps it shown for dragging.
cb.fade = cb:CreateAnimationGroup()
local fade = cb.fade:CreateAnimation("Alpha")
fade:SetChange(-1)
fade:SetDuration(.4)
cb.fade:SetScript("OnFinished", function() cb:SetAlpha(0) 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 this.drag and this.drag:IsShown() then
this.fade:Stop()
this:SetAlpha(1)
return
end
if this.fade:IsPlaying() then return end
if not this.endTime then
if this:GetAlpha() ~= 0 then this:SetAlpha(0) end
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
cb.fade:SetScript("OnFinished", function()
if not (cb.drag and cb.drag:IsShown()) then cb:Hide() end
end)
-- Cast lifecycle, entirely on ClassicAPI's UNIT_SPELLCAST_* events. They
@@ -380,7 +349,7 @@ pfUI:RegisterModule("castbar", function ()
RefreshBar(this, arg5)
if this.isTradeskill and (this.pendingTradeskillCount or 0) > 1
and C.castbar.player.mergetradeskill == "1" then
EnterTradeskillMerge(this, this.startTime, this.endTime, this.pendingTradeskillCount)
EnterTradeskillMerge(this, this.pendingTradeskillCount)
end
this.pendingTradeskillCount = nil
end
@@ -389,7 +358,7 @@ pfUI:RegisterModule("castbar", function ()
-- count and clear when the chain is done. A no-op for normal casts,
-- which are cleared by UNIT_SPELLCAST_STOP.
if this.tradeskillTotal and arg3 == this.tradeskillSpellID then
this.tradeskillCompleted = (this.tradeskillCompleted or 0) + 1
this.tradeskillCompleted = this.tradeskillCompleted + 1
if this.tradeskillCompleted >= this.tradeskillTotal then
ClearBar(this)
else
@@ -409,41 +378,36 @@ pfUI:RegisterModule("castbar", function ()
elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
ClearBar(this, true)
elseif event == "UNIT_SPELLCAST_INTERRUPTIBLE" or event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE" then
if this.endTime then
if this.active then
SetCastbarShield(this.bar, this.icon, event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE", this.isChannel)
end
elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then
-- Pushback: a cast delayed later or a channel shortened. Following
-- Quartz, re-poll just the times and accumulate the shift into a running
-- Quartz, re-read just the times and accumulate the shift into a running
-- this.delay for the +/- indicator, rather than a full restamp (which
-- would reset that total and re-render icon/text). ClassicAPI moves
-- endMs (SpellDelayed_h bumps g_cast.endMs; the MSG_CHANNEL_UPDATE
-- co-hook rewrites g_channel.endMs) while startMs stays put, so we diff
-- endMs. delay is a positive magnitude; OnUpdate signs it "+" for casts
-- and "-" for channels. Skipped during a tradeskill merge.
if this.endTime and not this.tradeskillTotal then
local startMs, endMs
-- would reset that total and re-render icon/text). Pushback moves the
-- end time while the start stays put, so we diff the end. delay is a
-- positive magnitude; UpdateTimerText signs it "+" for casts and "-"
-- for channels. Skipped during a tradeskill merge.
if this.active and not this.tradeskillTotal then
local duration
if this.isChannel then
local _, _, _, s, e = C_Spell.UnitChannelInfo(unit)
startMs, endMs = s, e
duration = UnitChannelDuration(unit)
else
local _, _, _, s, e = C_Spell.UnitCastingInfo(unit)
startMs, endMs = s, e
duration = UnitCastingDuration(unit)
end
if startMs and endMs then
local shift = this.isChannel and (this.endTime - endMs) or (endMs - this.endTime)
this.delay = (this.delay or 0) + shift / 1000
this.startTime = startMs
this.endTime = endMs
local newDuration = (endMs - startMs) / 1000
this.bar:SetMinMaxValues(0, newDuration)
this.lastMax = newDuration
if duration then
local shift = duration:GetEndTime() - this.duration:GetEndTime()
this.delay = this.delay + (this.isChannel and -shift or shift)
this.duration:Assign(duration)
UpdateTimerText(this)
ArmExpiry(this)
end
end
end
end)
cb:SetAlpha(0)
cb:Hide()
return cb
end
+142 -113
View File
@@ -30,39 +30,6 @@ pfUI:RegisterModule("chat", function ()
end
end)
local realm = GetRealmName()
local player = UnitName("player")
local history
local function SaveChatHistory(id, msg, r, g, b)
-- create cache tables if not existing
pfUI_cache = pfUI_cache or {}
pfUI_cache["chathistory"] = pfUI_cache["chathistory"] or {}
pfUI_cache["chathistory"][realm] = pfUI_cache["chathistory"][realm] or {}
pfUI_cache["chathistory"][realm][player] = pfUI_cache["chathistory"][realm][player] or {}
pfUI_cache["chathistory"][realm][player][id] = pfUI_cache["chathistory"][realm][player][id] or {}
if r and g and b then
local color = rgbhex(r*.5+.2, g*.5+.2, b*.5+.2)
msg = string.gsub(msg, "^", color)
msg = string.gsub(msg, "|r", "|r" .. color)
end
history = pfUI_cache["chathistory"][realm][player][id]
table.insert(history, 1, msg)
if history[30] then table.remove(history, 30) end
end
local function GetChatHistory(id)
-- create cache tables if not existing
pfUI_cache = pfUI_cache or {}
pfUI_cache["chathistory"] = pfUI_cache["chathistory"] or {}
pfUI_cache["chathistory"][realm] = pfUI_cache["chathistory"][realm] or {}
pfUI_cache["chathistory"][realm][player] = pfUI_cache["chathistory"][realm][player] or {}
pfUI_cache["chathistory"][realm][player][id] = pfUI_cache["chathistory"][realm][player][id] or {}
return pfUI_cache["chathistory"][realm][player][id]
end
-- [ Chat Panel Colors ]
-- The panels normally inherit pfUI's global appearance theme; custom colors let chat
-- deviate from it. CreateBackdrop is re-run first to restore the theme, so toggling
@@ -456,15 +423,20 @@ pfUI:RegisterModule("chat", function ()
if pfUI.chat.hideLock then return end
-- the dock lays the tabs out again only when their bar comes back
if pfUI.chat.left:IsMouseOver(10, -10, -10, 10) then
pfUI.chat.left.panelTop:Show()
FCF_DockUpdate()
if not pfUI.chat.left.panelTop:IsShown() then
pfUI.chat.left.panelTop:Show()
FCF_DockUpdate()
end
elseif pfUI.chat.right:IsMouseOver(10, -10, -10, 10) then
-- disable while dock is active
if pfUI.chat.right:GetAlpha() == 0 then return end
pfUI.chat.right.panelTop:Show()
FCF_DockUpdate()
if not pfUI.chat.right.panelTop:IsShown() then
pfUI.chat.right.panelTop:Show()
FCF_DockUpdate()
end
else
pfUI.chat.left.panelTop:Hide()
pfUI.chat.right.panelTop:Hide()
@@ -587,6 +559,9 @@ pfUI:RegisterModule("chat", function ()
end
for i=1, NUM_CHAT_WINDOWS do
-- ChatFrame_OnUpdate only blinks the flash of the bottom button hidden
-- below; the lines fade in the engine's own update
_G["ChatFrame" .. i]:SetScript("OnUpdate", nil)
_G["ChatFrame" .. i .. "UpButton"]:Hide()
_G["ChatFrame" .. i .. "UpButton"].Show = function() return end
_G["ChatFrame" .. i .. "DownButton"]:Hide()
@@ -726,12 +701,56 @@ pfUI:RegisterModule("chat", function ()
local r,g,b = GetStringColor(C.chat.text.unknowncolor)
local unknowncolorhex = rgbhex(r,g,b)
local function GetPlayerLevel(name)
if not pfUI_playerDB then return nil end
if not pfUI_playerDB[name] then return nil end
return pfUI_playerDB[name].level
-- the sender of the line AddMessage is reading, from its chat event
local lineSender, lineSenderClass, lineSenderRace, lineSenderSex
-- [ FormatPlayerLink ]
-- The gsub callback for a player link: the name in its class colour, with
-- the level and icons the settings ask for. Returns the link unchanged when
-- there is nothing to add, as a Lua 5.0 callback that returns nil deletes
-- the match.
local function FormatPlayerLink(link, name, shown)
local real = strsplit(":", name)
if shown ~= real then return link end
local color, icon = "", ""
if C.chat.text.classcolor == "1" then
local _, class, raceKey, sex
if lineSenderClass and real == lineSender then
class, raceKey, sex = lineSenderClass, lineSenderRace, lineSenderSex
else
_, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real)
end
if class then
color = "|c" .. PFUI_CLASS_COLORS[class].colorStr
if C.chat.text.factionicons == "1" then
icon = GetFactionIcon(raceKey)
end
if C.chat.text.raceicons == "1" then
icon = icon .. GetRaceIcon(raceKey, sex)
end
elseif C.chat.text.tintunknown == "1" then
color = unknowncolorhex
end
end
local suffix = ""
local level = C.chat.text.playerlevel == "1" and GetPlayerLevel(real)
if level then
suffix = " " .. rgbhex(GetDifficultyColor(level)) .. level .. "|r"
end
if color == "" and suffix == "" then return link end
return left .. icon .. color .. "|Hplayer:" .. name .. "|h" .. color .. real .. "|h|r" .. suffix .. right
end
-- the timestamp of the current second
local stampTime, stamp
-- the last chat line read and what it became. A line shown in several
-- frames is read once: nothing in the result depends on the frame.
local lastLineID, lastText, lastResult
-- Passes every argument after the text on: GetMessageInfo returns them all,
-- and a hook that drops some loses them for every frame after it.
local function AddMessage(frame, text, ...)
@@ -744,76 +763,40 @@ pfUI:RegisterModule("chat", function ()
-- payload of the CHAT_MSG event printing this line; nil for lines
-- that don't come from one (system text, addon output, history)
local event, _, sender, _, _, _, _, _, _, _, _, _, guid = GetCurrentChatEvent()
local event, _, sender, _, _, _, _, _, _, _, _, lineID, guid = GetCurrentChatEvent()
-- Remove prat CLINKs
text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- tbc
text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- vanilla
-- text emotes all have line 0
if lineID and lineID ~= 0 and lineID == lastLineID and text == lastText then
return frame:HookAddMessage(lastResult, ...)
end
local input = text
-- Remove chatter CLINKs
text = gsub(text, "{CLINK:item:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:enchant:(%x+):([%d-]-):([^}]-)}", "|c%1|Henchant:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:spell:(%x+):([%d-]-):([^}]-)}", "|c%1|Hspell:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:quest:(%x+):([%d-]-):([%d-]-):([^}]-)}", "|c%1|Hquest:%2:%3|h[%4]|h|r")
-- Remove prat and chatter CLINKs
if text:find("{CLINK:", 1, true) then
text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- tbc
text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- vanilla
text = gsub(text, "{CLINK:item:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:enchant:(%x+):([%d-]-):([^}]-)}", "|c%1|Henchant:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:spell:(%x+):([%d-]-):([^}]-)}", "|c%1|Hspell:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:quest:(%x+):([%d-]-):([%d-]-):([^}]-)}", "|c%1|Hquest:%2:%3|h[%4]|h|r")
end
-- detect urls
if C.chat.text.detecturl == "1" then
-- detect urls; every pattern needs a dot between name characters or a "://"
if C.chat.text.detecturl == "1" and (text:find("[%w_-]%.[%w_-]") or text:find("://", 1, true)) then
text = pfUI.chat:HandleLink(text)
end
-- display class colors and player levels if already indexed
local classcolor = C.chat.text.classcolor == "1"
local playerlevel = C.chat.text.playerlevel == "1"
if classcolor or playerlevel then
if classcolor or C.chat.text.playerlevel == "1" then
-- the engine name-queries a chat sender before the event fires,
-- so the sender's GUID always resolves; other names in the text
-- fall back to the name-keyed lookups
local _, senderClass, senderRace, senderSex
lineSender, lineSenderClass, lineSenderRace, lineSenderSex = sender, nil, nil, nil
if classcolor and guid and guid ~= "" then
_, senderClass, _, senderRace, senderSex = GetPlayerInfoByGUID(guid)
end
for name in gfind(text, "|Hplayer:(.-)|h") do
local real, _ = strsplit(":", name)
local color, icon = "", ""
if classcolor then
local class, raceKey, sex
if senderClass and real == sender then
class, raceKey, sex = senderClass, senderRace, senderSex
else
_, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real)
end
if not class then
class = GetUnitInfo(real)
end
if class and class ~= UNKNOWN then
color = "|c" .. PFUI_CLASS_COLORS[class].colorStr
if C.chat.text.factionicons == "1" then
icon = GetFactionIcon(raceKey)
end
if C.chat.text.raceicons == "1" then
icon = icon .. GetRaceIcon(raceKey, sex)
end
elseif C.chat.text.tintunknown == "1" then
color = unknowncolorhex
end
end
local suffix = ""
local level = playerlevel and GetPlayerLevel(real)
if level and level > 0 then
suffix = " " .. rgbhex(GetDifficultyColor(level)) .. level .. "|r"
elseif level then
-- Show ?? for unknown levels (e.g. -1 from UnitLevel)
suffix = " |cffff0000??|r"
end
if color ~= "" or suffix ~= "" then
text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)",
left..icon..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..suffix..right.."%1")
end
_, lineSenderClass, _, lineSenderRace, lineSenderSex = GetPlayerInfoByGUID(guid)
end
text = text:gsub("(|Hplayer:([^|]*)|h%[([^%]]*)%]|h)", FormatPlayerLink)
end
-- reduce channel name to number
@@ -823,9 +806,7 @@ pfUI:RegisterModule("chat", function ()
-- detect the whisper prefix BEFORE prepending a timestamp; the timestamp
-- pushes the whisper colour code off position 1 and the find below fails
local isWhisper = C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1
if isWhisper then
if C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1 then
-- patch incoming whisper string to match the colors, before the
-- timestamp so it keeps the form StripTimestamp looks for
text = string.gsub(text, "|r", "|r" .. wcol)
@@ -833,19 +814,66 @@ pfUI:RegisterModule("chat", function ()
-- show timestamp in chat
if C.chat.text.time == "1" then
text = timestampStart .. date(C.chat.text.timeformat) .. timestampEnd .. text
end
-- save chat history
if isWhisper then
SaveChatHistory(frame:GetID(), string.gsub(text, wcol, ""), cr, cg, cb)
else
SaveChatHistory(frame:GetID(), text, ...)
local now = time()
if now ~= stampTime then
stampTime, stamp = now, timestampStart .. date(C.chat.text.timeformat) .. timestampEnd
end
text = stamp .. text
end
lastLineID, lastText, lastResult = lineID, input, text
frame:HookAddMessage(text, ...)
end
-- [ Chat History ]
-- The newest lines of each chat frame, kept per character and written back
-- at login, newest first and dimmed. They are read from the frames when
-- leaving the world, which logout and reload both do before the saved
-- variables are written.
local HISTORY_LINES = 30
local function GetChatHistory()
local realm, player = GetRealmName(), UnitName("player")
pfUI_cache["chathistory"] = pfUI_cache["chathistory"] or {}
pfUI_cache["chathistory"][realm] = pfUI_cache["chathistory"][realm] or {}
pfUI_cache["chathistory"][realm][player] = pfUI_cache["chathistory"][realm][player] or {}
return pfUI_cache["chathistory"][realm][player]
end
-- a line dimmed in its colour, which also comes back after every |r in it
local function DimHistoryLine(text, r, g, b)
local color = rgbhex(r*.5+.2, g*.5+.2, b*.5+.2)
return color .. text:gsub("|r", "|r" .. color)
end
local function SaveChatHistory()
local history = GetChatHistory()
for i=1, NUM_CHAT_WINDOWS do
local frame = _G["ChatFrame"..i]
if not frame.pfCombatLog then
local lines = {}
local num = frame:GetNumMessages()
for index = num, math.max(1, num - HISTORY_LINES + 1), -1 do
local text, r, g, b, _, restored = frame:GetMessageInfo(index)
-- lines written back from the history are dimmed already
if not restored then
-- an incoming whisper is in the whisper colour, not its own
if C.chat.global.whispermod == "1" and pfUI.chat.StripTimestamp(text):sub(1, #wcol) == wcol then
text, r, g, b = text:gsub(wcol, ""), cr, cg, cb
end
text = DimHistoryLine(text, r, g, b)
end
table.insert(lines, text)
end
history[i] = lines
end
end
end
if C.chat.text.history == "1" then
pfUI.events:RegisterFrameEventAndCallback("PLAYER_LEAVING_WORLD", SaveChatHistory)
end
for i=1,NUM_CHAT_WINDOWS do
if C.chat.global.maxlines ~= "128" then
_G["ChatFrame"..i]:SetMaxLines(tonumber(C.chat.global.maxlines) or 128)
@@ -853,13 +881,14 @@ pfUI:RegisterModule("chat", function ()
if not _G["ChatFrame"..i].HookAddMessage then
if C.chat.text.history == "1" then
-- write history to chat, newest first, above the lines already there
for _, msg in ipairs(GetChatHistory(i)) do
_G["ChatFrame"..i]:BackFillMessage(msg, .7,.7,.7)
-- write history to chat, newest first, above the lines already there;
-- after the colour come the id slot and the mark SaveChatHistory reads
for _, msg in ipairs(GetChatHistory()[i] or {}) do
_G["ChatFrame"..i]:BackFillMessage(msg, .7,.7,.7, nil, true)
end
end
-- add chat parse and history hooks
-- add chat parse hooks
_G["ChatFrame"..i].HookAddMessage = _G["ChatFrame"..i].AddMessage
_G["ChatFrame"..i].AddMessage = AddMessage
end
+9 -7
View File
@@ -1,7 +1,5 @@
pfUI:RegisterModule("chatcopy", function ()
local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:SetScript("OnEvent", function()
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
if not pfUI.chat then return end
local button = CreateFrame("Button", "pfChatCopyButton", pfUI.chat.left.panelTop)
@@ -85,15 +83,19 @@ pfUI:RegisterModule("chatcopy", function ()
scroll:SetFont(frame:GetFont())
scroll:SetText(text)
-- the cursor at the end scrolls the box to the newest line
scroll:SetFocus()
scroll:SetCursorPosition(#text)
-- the edit box lays the text out in this frame's update, so scroll to
-- the newest line after it; the caret only scrolls the focused box
RunNextFrame(function()
local scrollBox = scroll:GetScrollBox()
scrollBox:FullUpdate(ScrollBoxConstants.UpdateImmediately)
scrollBox:ScrollToEnd(ScrollBoxConstants.NoScrollInterpolation)
end)
end)
frame.scroll = scroll
end
this:UnregisterAllEvents()
end)
end)
+1 -7
View File
@@ -2509,13 +2509,7 @@ pfUI:RegisterModule("combatlog", function ()
local buttonName, button, textWidth;
local buttonIndex = 1;
-- subtract the width of the dropdown button
local clogleft, clogright = COMBATLOG:GetRight(), COMBATLOG:GetLeft();
local maxWidth;
if ( clogleft and clogright ) then
maxWidth = (COMBATLOG:GetRight()-COMBATLOG:GetLeft())-31; --Hacky hacky because GetWidth goes crazy when it is docked
else
maxWidth = COMBATLOG:GetWidth() - 31;
end
local maxWidth = COMBATLOG:GetUnscaledWidth() - 31;
local additionalFilterButton = CombatLogQuickButtonFrame.AdditionalFilterButton;
+12 -9
View File
@@ -30,15 +30,15 @@ pfUI:RegisterModule("easteregg", function ()
scene.dtext:SetPoint("TOP", 0, -430)
scene.dtext:SetText(info and info .. "\n\n" .. exit or exit)
-- Fade the night in by playing a fade-out backwards. The scene keeps an
-- alpha of 1, which comes back when the fade ends.
-- Fade the night in. The scene keeps an alpha of 1, which comes back
-- when the fade ends.
scene.dusk = scene:CreateAnimationGroup()
local fade = scene.dusk:CreateAnimation("Alpha")
fade:SetChange(-1)
fade:SetToAlpha(1)
fade:SetDuration(.8)
scene:SetScript("OnShow", function()
this.dusk:Play(true)
this.dusk:Play()
end)
-- start over on the next AFK
@@ -159,7 +159,7 @@ pfUI:RegisterModule("easteregg", function ()
halloween.text:SetTextColor(1, .5, 0)
-- Candlelight glows in from the screen edges. It flickers in uneven
-- steps of { duration, change } that end where they began, so the loop
-- steps of { duration, alpha } that end where they began, so the loop
-- never jumps.
local candle = CreateFrame("Frame", nil, halloween)
candle:SetAllPoints()
@@ -169,16 +169,19 @@ pfUI:RegisterModule("easteregg", function ()
candle.flicker = candle:CreateAnimationGroup()
candle.flicker:SetLooping("REPEAT")
local steps = {
{ .18, -.25 }, { .09, .1 }, { .22, -.2 }, { .14, .3 }, { .3, -.1 },
{ .11, -.15 }, { .2, .2 }, { .16, -.3 }, { .26, .25 }, { .12, .15 },
{ .18, .75 }, { .09, .85 }, { .22, .65 }, { .14, .95 }, { .3, .85 },
{ .11, .7 }, { .2, .9 }, { .16, .6 }, { .26, .85 }, { .12, 1 },
}
local from = 1
for order, step in ipairs(steps) do
local duration, change = unpack(step)
local duration, alpha = unpack(step)
local fade = candle.flicker:CreateAnimation("Alpha")
fade:SetOrder(order)
fade:SetDuration(duration)
fade:SetChange(change)
fade:SetFromAlpha(from)
fade:SetToAlpha(alpha)
fade:SetSmoothing("IN_OUT")
from = alpha
end
-- Once the night has fallen, undead rise along the bottom one after
+79 -84
View File
@@ -84,11 +84,6 @@ pfUI:RegisterModule("energytick", function()
return
end
-- inside the module body on purpose: C is on pfUI.env, not _G
local function getBarWidth()
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
end
local rawborder, default_border = GetBorderSize("unitframes")
local cbtexture = pfUI.media[C.appearance.castbar.texture]
@@ -128,8 +123,13 @@ pfUI:RegisterModule("energytick", function()
return true
end
local energytick = CreateFrame("Frame", nil, pfUI.uf.player.power.bar)
-- the bar sweeps its fill over one tick period from a duration object; the
-- fill is invisible and carries the spark on its edge
local energytick = CreateFrame("StatusBar", nil, pfUI.uf.player.power.bar)
energytick:SetAllPoints(pfUI.uf.player.power.bar)
energytick:SetColorFill(1, 1, 1, 0)
energytick.sweep = C_DurationUtil.CreateDuration()
energytick:SetTimerDuration(energytick.sweep, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
energytick:RegisterEvent("PLAYER_ENTERING_WORLD")
energytick:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player")
energytick:RegisterUnitEvent("UNIT_POWER_UPDATE", "player")
@@ -138,21 +138,61 @@ pfUI:RegisterModule("energytick", function()
energytick:RegisterEvent("SPELLS_CHANGED")
energytick:RegisterEvent("PLAYER_AURAS_CHANGED")
-- the spark hides until a tick is seen, and dims while the rule is up and
-- nothing has ticked inside it yet
local function UpdateRule()
local fsrUp = energytick.mode == "MANA" and energytick.fsr:IsActive()
energytick.fsrbar:SetShown(fsrUp and energytick:IsShown() and C.unitframes.player.showfsr == "1")
energytick.spark:SetAlpha(not energytick.start and 0 or (fsrUp and not energytick.fsrGain) and .4 or 1)
end
-- the window closed without being re-armed
local function RuleEnd()
energytick.fsrSpell, energytick.fsrGain = nil, nil
energytick.fsr:Reset()
UpdateRule()
end
local function ArmRule()
energytick.fsr:SetTimeFromStart(GetTime(), FIVE_SECOND_RULE)
if energytick.ruletimer then energytick.ruletimer:Cancel() end
energytick.ruletimer = C_Timer.NewTimer(FIVE_SECOND_RULE, RuleEnd)
end
-- sweeps the period that holds now and wakes at its end. Rolls over by
-- whole periods, not from now: restarting bakes timer overshoot into the
-- phase as drift the lock then has to chase. Runs only while shown; a
-- hidden bar keeps its phase and catches up as it shows.
local Sweep
local function Wrap() Sweep() end
function Sweep()
if energytick.wrap then energytick.wrap:Cancel() end
if not energytick.start or not energytick:IsShown() then return end
local now = GetTime()
while now - energytick.start >= energytick.max do
energytick.start = energytick.start + energytick.max * math.floor((now - energytick.start) / energytick.max)
energytick.max = getAdjustedTickTimer()
end
energytick.sweep:SetTimeFromStart(energytick.start, energytick.max)
energytick.wrap = C_Timer.NewTimer(energytick.start + energytick.max - now, Wrap)
end
energytick:SetScript("OnEvent", function()
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
this.mode = "MANA"
this:Show()
elseif UnitPowerType("player") == Enum.PowerType.Energy and C.unitframes.player.energy == "1" then
this.mode = "ENERGY"
this:Show()
else
this.mode = nil
this:Hide()
end
-- mana regen stops showing at full mana
this:SetShown(this.mode and not (this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player")))
if event == "SPELLS_CHANGED" or event == "PLAYER_AURAS_CHANGED" then
energyRegenTimeMod = nil
return
end
-- a shift swaps the power the gains are measured on
@@ -166,18 +206,17 @@ pfUI:RegisterModule("energytick", function()
local cost = C_Spell.GetSpellPowerCost(arg3)
cost = cost and cost[1]
if cost and cost.type == Enum.PowerType.Mana and cost.cost > 0 then
this.fsrSpell, this.fsrEnd = arg3, GetTime() + FIVE_SECOND_RULE
this.fsrGain = nil
this.fsrSpell, this.fsrGain = arg3, nil
ArmRule()
end
return
end
-- Unit::Update won't expire the rule while the spending spell still channels
if event == "UNIT_SPELLCAST_CHANNEL_STOP" and arg1 == "player" then
if this.fsrSpell and this.fsrSpell == arg3 then
this.fsrEnd, this.fsrGain = GetTime() + FIVE_SECOND_RULE, nil
this.fsrGain = nil
ArmRule()
end
return
end
-- only the shown power: a cat druid's mana regen is not an energy gain
@@ -189,89 +228,31 @@ pfUI:RegisterModule("energytick", function()
-- only a gain can be the tick; a spend never touches the phase
if diff > 0 and lockTick(this) then
-- a tick inside the window proves regen continues through it
if this.fsrEnd and this.fsrEnd > GetTime() then
if this.fsr:IsActive() then
this.fsrGain = true
end
end
-- phase is kept while hidden; OnUpdate catches up by whole periods
if this.mode == "MANA" and power >= UnitPowerMax("player") then
this:Hide()
Sweep()
end
end
UpdateRule()
end)
energytick:SetScript("OnUpdate", function()
-- Throttle for performance
if (this.tick or 0) > GetTime() then
return
end
this.tick = GetTime() + 0.020 -- ~50 FPS
-- five-second rule drains to nothing. The display is cosmetic and can be
-- hidden on its own; the window itself (fsrEnd/fsrGain) keeps running
-- either way -- it still dims the sweep spark below.
local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0
if this.mode == "MANA" and remaining > 0 then
if C.unitframes.player.showfsr == "1" then
if fsrOverlay then
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE)
else
this.fsrbar:SetValue(remaining)
end
this.fsrbar:Show()
else
this.fsrbar:Hide()
end
else
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
this.fsrbar:Hide()
end
if not this.start then
this.spark:SetAlpha(0)
return
end
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
this.spark:SetAlpha(0)
return
end
this.current = GetTime() - this.start
-- roll over by whole periods, not from now: restarting bakes frame
-- overshoot into the phase as drift the lock then has to chase
if this.current > this.max then
this.start = this.start + this.max * math.floor(this.current / this.max)
this.max = getAdjustedTickTimer()
this.current = GetTime() - this.start
end
-- dim while the rule is up and nothing has ticked inside it yet
this.spark:SetAlpha((remaining > 0 and not this.fsrGain) and .4 or 1)
if not C.unitframes.player.pheight then
return
end
local pos = getBarWidth() * (this.current / this.max)
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
end)
energytick.fsr = C_DurationUtil.CreateDuration()
if fsrOverlay then
-- classic style: a translucent shade on top of the power bar's own fill
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
energytick.fsrbar:SetColorTexture(1, 1, 1, .15)
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0)
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0)
-- classic style: a translucent shade on top of the power bar's own fill,
-- under the spark
energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player.power.bar)
energytick.fsrbar:SetAllPoints(energytick)
energytick.fsrbar:SetColorFill(1, 1, 1, .15)
energytick:SetFrameLevel(energytick.fsrbar:GetFrameLevel() + 1)
else
-- a real bar below the power bar, not an overlay on top of it -- the same
-- treatment nameplate castbars get relative to the health bar, so the
-- window can't be mistaken for missing mana
energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player)
energytick.fsrbar:SetStatusBarTexture(cbtexture)
energytick.fsrbar:SetMinMaxValues(0, FIVE_SECOND_RULE)
energytick.fsrbar:SetPoint("TOPLEFT", pfUI.uf.player.power.bar, "BOTTOMLEFT", 0, -default_border * 2)
energytick.fsrbar:SetPoint("TOPRIGHT", pfUI.uf.player.power.bar, "BOTTOMRIGHT", 0, -default_border * 2)
energytick.fsrbar:SetHeight(tonumber(C.unitframes.player.fsrheight) or 4)
@@ -279,13 +260,27 @@ pfUI:RegisterModule("energytick", function()
CreateBackdrop(energytick.fsrbar, default_border)
CreateBackdropShadow(energytick.fsrbar)
end
energytick.fsrbar:SetTimerDuration(energytick.fsr, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
energytick.fsrbar:Hide()
-- the bar isn't parented to energytick, so it follows it by hand
energytick:SetScript("OnShow", function()
Sweep()
UpdateRule()
end)
energytick:SetScript("OnHide", function()
Sweep()
this.fsrbar:Hide()
end)
energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
energytick.spark:SetBlendMode("ADD")
energytick.spark:SetPoint("CENTER", energytick:GetStatusBarTexture(), "RIGHT")
energytick.spark:SetAlpha(0)
local hookUpdateConfig = pfUI.uf.player.UpdateConfig
function pfUI.uf.player.UpdateConfig()
+4 -2
View File
@@ -768,11 +768,13 @@ pfUI:RegisterModule("equipmentmanager", function()
-- Popout arrows follow the EM sidecar by default. With the
-- "Always Show Equipment Slot Flyouts" option they follow the paperdoll
-- instead, so gear can be swapped without opening the equipment manager.
-- IsVisible, not IsShown: closing CharacterFrame fires OnHide on both
-- without clearing their own shown flags.
local function PopoutsActive()
if C.character.inventory.equipflyout == "1" then
return PaperDollFrame:IsShown()
return PaperDollFrame:IsVisible()
end
return frame:IsShown()
return frame:IsVisible()
end
function UpdatePopouts()
+13 -26
View File
@@ -136,17 +136,19 @@ pfUI:RegisterModule("firstrun", function ()
local f = CreateFirstRunPage()
f.text:SetText(T["A new installation of |cff33ffccpf|rUI ships with 4 prebuilt design profiles. Click below if you wish to load one of these profiles."])
local function LoadProfile(name)
_G["pfUI_config"] = CopyTable(pfUI_profiles[name])
pfUI_init.selected_profile = name
pfUI:LoadConfig()
ReloadUI()
end
f.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Modern:SetSize(120, 20)
f.Modern:SetPoint("BOTTOM", -65, 100)
f.Modern:SetTextColor(1,1,1)
f.Modern:SetText("Modern")
f.Modern:SetScript("OnClick", function()
_G["pfUI_config"] = CopyTable(pfUI_profiles["Modern"])
pfUI_init.selected_profile = "Modern"
pfUI:LoadConfig()
ReloadUI()
end)
f.Modern:SetScript("OnClick", function() LoadProfile("Modern") end)
SkinButton(f.Modern)
f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
@@ -154,12 +156,7 @@ pfUI:RegisterModule("firstrun", function ()
f.Nostalgia:SetPoint("BOTTOM", 65, 100)
f.Nostalgia:SetTextColor(1,1,1)
f.Nostalgia:SetText("Nostalgia")
f.Nostalgia:SetScript("OnClick", function()
_G["pfUI_config"] = CopyTable(pfUI_profiles["Nostalgia"])
pfUI_init.selected_profile = "Nostalgia"
pfUI:LoadConfig()
ReloadUI()
end)
f.Nostalgia:SetScript("OnClick", function() LoadProfile("Nostalgia") end)
SkinButton(f.Nostalgia)
f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
@@ -167,12 +164,7 @@ pfUI:RegisterModule("firstrun", function ()
f.Legacy:SetPoint("BOTTOM", 65, 75)
f.Legacy:SetTextColor(1,1,1)
f.Legacy:SetText("Legacy")
f.Legacy:SetScript("OnClick", function()
_G["pfUI_config"] = CopyTable(pfUI_profiles["Legacy"])
pfUI_init.selected_profile = "Legacy"
pfUI:LoadConfig()
ReloadUI()
end)
f.Legacy:SetScript("OnClick", function() LoadProfile("Legacy") end)
SkinButton(f.Legacy)
f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
@@ -180,12 +172,7 @@ pfUI:RegisterModule("firstrun", function ()
f.Slim:SetPoint("BOTTOM", -65, 75)
f.Slim:SetTextColor(1,1,1)
f.Slim:SetText("Slim")
f.Slim:SetScript("OnClick", function()
_G["pfUI_config"] = CopyTable(pfUI_profiles["Slim"])
pfUI_init.selected_profile = "Slim"
pfUI:LoadConfig()
ReloadUI()
end)
f.Slim:SetScript("OnClick", function() LoadProfile("Slim") end)
SkinButton(f.Slim)
f.Slider = CreateFrame("Slider", "pfFirstRunWizardScaleSlider", f, "OptionsSliderTemplate")
@@ -196,14 +183,14 @@ pfUI:RegisterModule("firstrun", function ()
f.Slider:SetSize(240, 20)
f.Slider:SetPoint("BOTTOM", 0, 50)
f.Slider:SetOrientation('HORIZONTAL')
f.Slider:SetMinMaxValues(0.5, 2.0)
-- the game drops a uiScale under 0.64 for its automatic scale at login
f.Slider:SetMinMaxValues(0.64, 2.0)
f.Slider:SetValue(UIParent:GetScale())
f.Slider:SetScript("OnMouseUp", function()
local scale = round(this:GetValue(),2)
SetCVar("uiScale", scale)
SetCVar("useUiScale", 1)
UIParent:SetScale(scale)
this:SetValue(scale)
end)
+4 -3
View File
@@ -17,9 +17,10 @@ pfUI:RegisterModule("focus", function ()
-- PLAYER_FOCUS_CHANGED drives immediate refresh on focus assign / clear.
-- Between events, ClassicAPI fires UNIT_* (health/mana/aura/...) with
-- arg1 == "focus", so the bars update event-driven like target.
-- focustarget is a chained unit like targettarget: UNIT_TARGET follows the
-- focus retargeting, and a new focus only needs its visibility re-checked,
-- which queues the full refresh when its target is a different unit.
-- focustarget is a chained unit like targettarget, but ClassicAPI fires its
-- UNIT_* as "focustarget", and UNIT_TARGET on "focus" follows the focus
-- retargeting. A new focus only needs its visibility re-checked, which
-- queues the full refresh when its target is a different unit.
pfUI.events:RegisterFrameEventAndCallback("PLAYER_FOCUS_CHANGED", function()
pfUI.uf:RefreshUnit(pfUI.uf.focus, "all")
pfUI.uf.focustarget:UpdateVisibility()
+49 -323
View File
@@ -78,7 +78,7 @@ pfUI:RegisterModule("gui", function ()
-- basic frame
local frame = CreateFrame("Frame", nil, this)
frame:SetWidth(this.parent:GetRight()-this.parent:GetLeft()-20)
frame:SetWidth(this.parent:GetUnscaledWidth()-20)
frame:SetHeight(22)
frame:SetPoint("TOPLEFT", this, "TOPLEFT", 5, (this.objectCount*-23)-5)
@@ -130,7 +130,7 @@ pfUI:RegisterModule("gui", function ()
category = {}
category[config] = tostring(GetCVar(config))
ufunc = function()
SetCVar(this:GetParent().config, this:GetParent().category[config])
SetCVar(config, category[config])
end
end
@@ -881,14 +881,6 @@ pfUI:RegisterModule("gui", function ()
"35:35",
"40:40",
},
["scaling"] = {
"0:" .. T["Off"],
"4:" .. T["Huge (PixelPerfect)"],
"5:" .. T["Large"],
"6:" .. T["Medium"],
"7:" .. T["Small"],
"8:" .. T["Tiny (PixelPerfect)"],
},
["abbrevnum"] = {
"0:" .. T["Full Numbers (4250)"],
"1:" .. T["Abbreviate 2 Decimals (4.25k)"],
@@ -981,6 +973,12 @@ pfUI:RegisterModule("gui", function ()
"BOTTOMRIGHT:" .. T["Bottom Right"],
"off:" .. T["Disabled"]
},
["cooldown_timers"] = {
"off:" .. T["None"],
"text:" .. T["Numbers"],
"sweep:" .. T["Sweep"],
"both:" .. T["Numbers And Sweep"]
},
["uf_debuff_indicator"] = {
"0:" .. T["Disabled"],
"1:" .. T["Legacy"],
@@ -1502,7 +1500,24 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Settings"], nil, nil, "header")
CreateConfig(nil, T["Language"], C.global, "language", "dropdown", pfUI.gui.dropdowns.languages)
CreateConfig(U["pixelperfect"], T["Enable UI-Scale"], C.global, "pixelperfect", "dropdown", pfUI.gui.dropdowns.scaling)
-- the game's own scale options, as in its Video Options; the list keeps
-- a value set there that is none of these steps
CreateConfig(nil, T["Use UI-Scale"], "CVAR", "useUiScale", "checkbox")
CreateConfig(nil, T["UI-Scale"], "CVAR", "uiScale", "dropdown", function()
local current = GetCVar("uiScale")
local values, found = {}, nil
for _, scale in ipairs({ .64, .65, .7, .75, .8, .85, .9, .95, 1, 1.1, 1.2, 1.3, 1.4, 1.5 }) do
if math.abs(scale - tonumber(current)) < .001 then
found = true
table.insert(values, current .. ":" .. scale)
else
table.insert(values, tostring(scale))
end
end
if not found then table.insert(values, 1, current .. ":" .. round(tonumber(current), 2)) end
return values
end)
CreateConfig(nil, string.format(T["Pixel Perfect UI-Scale: %s (Overrides UI-Scale)"], round(PixelUtil.GetPixelToUIUnitFactor(), 2)), C.global, "pixelperfect", "checkbox")
CreateConfig(nil, T["Use Original Game Fonts"], C.global, "font_blizzard", "checkbox")
CreateConfig(nil, T["Enable Region Compatible Font"], C.global, "force_region", "checkbox")
CreateConfig(nil, T["Standard Text Font"], C.global, "font_default", "dropdown", pfUI.gui.dropdowns.fonts)
@@ -1579,8 +1594,6 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Frame Shadow Intensity"], C.appearance.border, "shadow_intensity", "dropdown", pfUI.gui.dropdowns.uf_debuff_indicator_size)
CreateConfig(nil) -- spacer
CreateConfig(nil, T["Force Blizzard Borders (|cffffaaaaExperimental|r)"], C.appearance.border, "force_blizz", "checkbox")
CreateConfig(nil, T["Enable Pixel Perfect Borders"], C.appearance.border, "pixelperfect", "checkbox")
CreateConfig(nil, T["Scale Border On HiDPI Displays"], C.appearance.border, "hidpi", "checkbox")
CreateConfig(nil) -- spacer
CreateConfig(nil, T["Global Border Size"], C.appearance.border, "default", "dropdown", pfUI.gui.dropdowns.border)
CreateConfig(nil, T["Action Bar Border Size"], C.appearance.border, "actionbars", "dropdown", pfUI.gui.dropdowns.border)
@@ -1597,23 +1610,6 @@ pfUI:RegisterModule("gui", function ()
end)
CreateGUIEntry(T["Settings"], T["Cooldown"], function()
-- every kind of cooldown CreateCooldown makes, as C.cooldown groups.
-- Other addons' cooldowns only lose numbers or sweep (see
-- api/cooldown.lua), so giving them back takes a reload
local kinds = {
{ "actionbar", T["Action Bars"] },
{ "bags", T["Bags"] },
{ "blizzard", T["Spellbook And Character"] },
{ "unitframe", T["Unit Frame Auras"] },
{ "indicator", T["Buff Indicators"] },
{ "nameplate", T["Nameplate Debuffs"] },
{ "buff", T["Player Buffs"] },
{ "tooltip", T["Tooltip Auras"] },
{ "totem", T["Totems"] },
{ "lossofcontrol", T["Loss Of Control"] },
{ "foreign", T["Other Addons"] },
}
CreateConfig(nil, COUNTDOWN_FOR_COOLDOWNS_TEXT, "CVAR", "countdownForCooldowns", "checkbox")
CreateConfig(UpdateCooldowns, T["Font"], C.cooldown, "font", "dropdown", pfUI.gui.dropdowns.fonts)
CreateConfig(nil, T["Font Size"], C.cooldown, "font_size")
@@ -1626,22 +1622,14 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(UpdateCooldowns, T["Cooldown Color (Hours)"], C.cooldown, "hourcolor", "color")
CreateConfig(UpdateCooldowns, T["Cooldown Color (Days)"], C.cooldown, "daycolor", "color")
CreateConfig(nil, T["Show Numbers On"], nil, nil, "header")
for _, kind in ipairs(kinds) do
CreateConfig(kind[1] ~= "foreign" and UpdateCooldowns or nil, kind[2], C.cooldown[kind[1]], "text", "checkbox")
end
CreateConfig(nil, T["Show Sweep On"], nil, nil, "header")
for _, kind in ipairs(kinds) do
CreateConfig(kind[1] ~= "foreign" and UpdateCooldowns or nil, kind[2], C.cooldown[kind[1]], "animation", "checkbox")
end
CreateConfig(nil, T["Reverse Sweep On"], nil, nil, "header")
for _, kind in ipairs(kinds) do
if kind[1] ~= "foreign" then
CreateConfig(UpdateCooldowns, kind[2], C.cooldown[kind[1]], "reverse", "checkbox")
end
end
-- the timers of cooldowns no other page covers. Other addons' cooldowns
-- only lose numbers or sweep (see api/cooldown.lua), so giving them
-- back takes a reload
local timers = pfUI.gui.dropdowns.cooldown_timers
CreateConfig(nil, T["Timers"], nil, nil, "header")
CreateConfig(UpdateCooldowns, T["Spellbook And Character"], C.cooldown, "blizzard_timers", "dropdown", timers)
CreateConfig(UpdateCooldowns, T["Loss Of Control"], C.cooldown, "lossofcontrol_timers", "dropdown", timers)
CreateConfig(nil, T["Other Addons"], C.cooldown, "foreign_timers", "dropdown", timers)
end)
CreateGUIEntry(T["Settings"], T["Screenshot"], function()
@@ -1665,221 +1653,6 @@ pfUI:RegisterModule("gui", function ()
end)
-- Throttling Menu
CreateGUIEntry(T["Throttling"], T["Nameplates"], function()
local header = CreateConfig(nil, T["Nameplate Update Rate"], nil, nil, "header")
header:GetParent().objectCount = header:GetParent().objectCount - 1
header:SetHeight(20)
local targetCustom -- declare first so callback can use it
CreateConfig(function()
-- Callback when dropdown changes - update custom field immediately
if targetCustom and targetCustom.input then
local isCustom = pfUI.throttle:IsCustom("nameplates_target")
if not isCustom then
-- Preset selected - show FPS from preset, make readonly
targetCustom.input:EnableMouse(false)
targetCustom.input:EnableKeyboard(false)
targetCustom.input:ClearFocus()
targetCustom.input:SetTextColor(.5,.5,.5,1)
targetCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_target")))
else
-- Custom selected - make editable
targetCustom.input:EnableMouse(true)
targetCustom.input:EnableKeyboard(true)
targetCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_target_custom then
targetCustom.input:SetText(_G.pfUI_throttle.nameplates_target_custom)
end
end
end
end, T["Target Plates"], _G.pfUI_throttle, "nameplates_target", "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"],
})
-- Now create custom field AFTER dropdown
targetCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_target_custom")
-- Set initial state
local isCustom = pfUI.throttle:IsCustom("nameplates_target")
if not isCustom then
targetCustom.input:EnableMouse(false)
targetCustom.input:EnableKeyboard(false)
targetCustom.input:SetTextColor(.5,.5,.5,1)
targetCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_target")))
else
targetCustom.input:EnableMouse(true)
targetCustom.input:EnableKeyboard(true)
targetCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_target_custom then
targetCustom.input:SetText(_G.pfUI_throttle.nameplates_target_custom)
end
end
-- Spacer after custom field
local spacer1 = CreateConfig(nil, " ", nil, nil, "header")
spacer1:GetParent().objectCount = spacer1:GetParent().objectCount - 1
spacer1:SetHeight(5)
local normalCustom
CreateConfig(function()
if normalCustom and normalCustom.input then
local isCustom = pfUI.throttle:IsCustom("nameplates")
if not isCustom then
normalCustom.input:EnableMouse(false)
normalCustom.input:EnableKeyboard(false)
normalCustom.input:ClearFocus()
normalCustom.input:SetTextColor(.5,.5,.5,1)
normalCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates")))
else
normalCustom.input:EnableMouse(true)
normalCustom.input:EnableKeyboard(true)
normalCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_custom then
normalCustom.input:SetText(_G.pfUI_throttle.nameplates_custom)
end
end
end
end, T["Normal Plates"], _G.pfUI_throttle, "nameplates", "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"],
})
normalCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_custom")
local isCustom2 = pfUI.throttle:IsCustom("nameplates")
if not isCustom2 then
normalCustom.input:EnableMouse(false)
normalCustom.input:EnableKeyboard(false)
normalCustom.input:SetTextColor(.5,.5,.5,1)
normalCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates")))
else
normalCustom.input:EnableMouse(true)
normalCustom.input:EnableKeyboard(true)
normalCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_custom then
normalCustom.input:SetText(_G.pfUI_throttle.nameplates_custom)
end
end
-- Spacer after custom field
local spacer2 = CreateConfig(nil, " ", nil, nil, "header")
spacer2:GetParent().objectCount = spacer2:GetParent().objectCount - 1
spacer2:SetHeight(5)
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 spacer3 = CreateConfig(nil, " ", nil, nil, "header")
spacer3:GetParent().objectCount = spacer3:GetParent().objectCount - 1
spacer3:SetHeight(5)
local massCustom
CreateConfig(function()
if massCustom and massCustom.input then
local isCustom = pfUI.throttle:IsCustom("nameplates_mass")
if not isCustom then
massCustom.input:EnableMouse(false)
massCustom.input:EnableKeyboard(false)
massCustom.input:ClearFocus()
massCustom.input:SetTextColor(.5,.5,.5,1)
massCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_mass")))
else
massCustom.input:EnableMouse(true)
massCustom.input:EnableKeyboard(true)
massCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_mass_custom then
massCustom.input:SetText(_G.pfUI_throttle.nameplates_mass_custom)
end
end
end
end, T["Mass Pulls (20+ Plates)"], _G.pfUI_throttle, "nameplates_mass", "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"],
})
massCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_mass_custom")
local isCustom3 = pfUI.throttle:IsCustom("nameplates_mass")
if not isCustom3 then
massCustom.input:EnableMouse(false)
massCustom.input:EnableKeyboard(false)
massCustom.input:SetTextColor(.5,.5,.5,1)
massCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_mass")))
else
massCustom.input:EnableMouse(true)
massCustom.input:EnableKeyboard(true)
massCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_mass_custom then
massCustom.input:SetText(_G.pfUI_throttle.nameplates_mass_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_target")
pfUI.throttle:ResetToDefault("nameplates")
pfUI.throttle:ResetToDefault("nameplates_mass")
-- Also reset custom fields to their default FPS values
_G.pfUI_throttle.nameplates_target_custom = "50"
_G.pfUI_throttle.nameplates_custom = "10"
_G.pfUI_throttle.nameplates_mass_custom = "7"
Reload()
end, true)
end)
CreateGUIEntry(T["Throttling"], T["Tooltips"], function()
local header = CreateConfig(nil, T["Tooltip Update Rate"], nil, nil, "header")
header:GetParent().objectCount = header:GetParent().objectCount - 1
@@ -2015,67 +1788,6 @@ pfUI:RegisterModule("gui", function ()
end, true)
end)
CreateGUIEntry(T["Throttling"], T["Swing Timer"], function()
local swingCustom
CreateConfig(function()
if swingCustom and swingCustom.input then
local isCustom = pfUI.throttle:IsCustom("swingtimer")
if not isCustom then
swingCustom.input:EnableMouse(false)
swingCustom.input:EnableKeyboard(false)
swingCustom.input:ClearFocus()
swingCustom.input:SetTextColor(.5,.5,.5,1)
swingCustom.input:SetText(tostring(pfUI.throttle:GetFps("swingtimer")))
else
swingCustom.input:EnableMouse(true)
swingCustom.input:EnableKeyboard(true)
swingCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.swingtimer_custom then
swingCustom.input:SetText(_G.pfUI_throttle.swingtimer_custom)
end
end
end
end, T["Swing Timer Update Rate"], _G.pfUI_throttle, "swingtimer", "dropdown", {
"very_slow:" .. T["Very Slow"] .. " (2 FPS)",
"slow:" .. T["Slow"] .. " (5 FPS)",
"normal:" .. T["Normal"] .. " (10 FPS)",
"fast:" .. T["Fast"] .. " (20 FPS)",
"very_fast:" .. T["Very Fast"] .. " (30 FPS)",
"fastest:" .. T["Fastest"] .. " (50 FPS)",
"custom:" .. T["Custom"],
})
swingCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "swingtimer_custom")
local isCustom = pfUI.throttle:IsCustom("swingtimer")
if not isCustom then
swingCustom.input:EnableMouse(false)
swingCustom.input:EnableKeyboard(false)
swingCustom.input:SetTextColor(.5,.5,.5,1)
swingCustom.input:SetText(tostring(pfUI.throttle:GetFps("swingtimer")))
else
swingCustom.input:EnableMouse(true)
swingCustom.input:EnableKeyboard(true)
swingCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.swingtimer_custom then
swingCustom.input:SetText(_G.pfUI_throttle.swingtimer_custom)
end
end
-- Spacer before reset button
local spacer = CreateConfig(nil, " ", nil, nil, "header")
spacer:GetParent().objectCount = spacer:GetParent().objectCount - 1
spacer:SetHeight(10)
-- Reset to defaults button
CreateConfig(nil, T["Reset to Defaults"], nil, nil, "button", function()
pfUI.throttle:ResetToDefault("swingtimer")
_G.pfUI_throttle.swingtimer_custom = "50"
Reload()
end, true)
end)
CreateGUIEntry(T["Unit Frames"], T["General"], function()
CreateConfig(nil, T["Disable pfUI Unit Frames"], C.unitframes, "disable", "checkbox")
CreateConfig(nil, T["Animate Health And Power Bars"], C.unitframes, "animatebars", "checkbox")
@@ -2294,6 +2006,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["raid"], T["Raid Padding"], C.unitframes[c], "raidpadding")
CreateConfig(U["raid"], T["Raid Layout"], C.unitframes[c], "raidlayout", "dropdown", pfUI.gui.dropdowns.uf_raidlayout)
CreateConfig(U["raid"], T["Raid Fill Direction"], C.unitframes[c], "raidfill", "dropdown", pfUI.gui.dropdowns.orientation)
CreateConfig(U["raid"], T["Grow Right To Left"], C.unitframes[c], "raidgrowleft", "checkbox")
CreateConfig(U["raid"], T["Collapse Empty Slots"], C.unitframes[c], "collapse", "checkbox")
elseif c == "raidpet" then
CreateConfig(U[c], T["Layout"], nil, nil, "header")
@@ -2355,11 +2068,14 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U[c], T["Automatic Class Text Color"], C.unitframes[c], "classcolor", "checkbox")
CreateConfig(U[c], T["Buffs"], nil, nil, "header")
CreateConfig(UpdateCooldowns, T["Buff And Debuff Timers"], C.unitframes[c], "aura_timers", "dropdown", pfUI.gui.dropdowns.cooldown_timers)
CreateConfig(U[c], T["Buff Position"], C.unitframes[c], "buffs", "dropdown", pfUI.gui.dropdowns.uf_buff_position)
CreateConfig(U[c], T["Buff Size"], C.unitframes[c], "buffsize")
CreateConfig(U[c], T["Buff Limit"], C.unitframes[c], "bufflimit")
CreateConfig(U[c], T["Buffs Per Row"], C.unitframes[c], "buffperrow")
CreateConfig(U[c], T["Own Buff Size"], C.unitframes[c], "ownbuffsize")
CreateConfig(U[c], T["Buff X-Offset"], C.unitframes[c], "buffoffx")
CreateConfig(U[c], T["Buff Y-Offset"], C.unitframes[c], "buffoffy")
CreateConfig(U[c], T["Buff X-Spacing"], C.unitframes[c], "buffspacingx")
CreateConfig(U[c], T["Buff Y-Spacing"], C.unitframes[c], "buffspacingy")
@@ -2369,6 +2085,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U[c], T["Debuff Limit"], C.unitframes[c], "debufflimit")
CreateConfig(U[c], T["Debuffs Per Row"], C.unitframes[c], "debuffperrow")
CreateConfig(U[c], T["Own Debuff Size"], C.unitframes[c], "owndebuffsize")
CreateConfig(U[c], T["Debuff X-Offset"], C.unitframes[c], "debuffoffx")
CreateConfig(U[c], T["Debuff Y-Offset"], C.unitframes[c], "debuffoffy")
CreateConfig(U[c], T["Debuff X-Spacing"], C.unitframes[c], "debuffspacingx")
CreateConfig(U[c], T["Debuff Y-Spacing"], C.unitframes[c], "debuffspacingy")
@@ -2384,6 +2102,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U[c], T["Buff/Debuff Indicators"], nil, nil, "header")
CreateConfig(U[c], T["Enable Indicators"], C.unitframes[c], "buff_indicator", "checkbox")
CreateConfig(U[c], T["Show Stacks"], C.unitframes[c], "indicator_stacks", "checkbox")
CreateConfig(UpdateCooldowns, T["Indicator Timers"], C.unitframes[c], "indicator_timers", "dropdown", pfUI.gui.dropdowns.cooldown_timers)
CreateConfig(U[c], T["Indicator Size"], C.unitframes[c], "indicator_size")
CreateConfig(U[c], T["Indicator Spacing"], C.unitframes[c], "indicator_spacing", "dropdown", pfUI.gui.dropdowns.spacing)
CreateConfig(U[c], T["Indicator Position"], C.unitframes[c], "indicator_pos", "dropdown", pfUI.gui.dropdowns.positions)
@@ -2470,6 +2189,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Bagslots Per Row"], C.appearance.bags, "bagrowlength")
CreateConfig(nil, T["Bankslots Per Row"], C.appearance.bags, "bankrowlength")
CreateConfig(nil, T["Item Slot Size"], C.appearance.bags, "icon_size")
CreateConfig(UpdateCooldowns, T["Cooldown Timers"], C.appearance.bags, "timers", "dropdown", pfUI.gui.dropdowns.cooldown_timers)
CreateConfig(nil, T["Auto Sell Grey Items"], C.global, "autosell", "checkbox")
CreateConfig(nil, T["Auto Repair Items"], C.global, "autorepair", "checkbox")
CreateConfig(nil, T["Auto Sort When Opening Bags"], C.appearance.bags, "autoSortOnOpen", "checkbox")
@@ -2518,6 +2238,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["buff"], T["Number Of Weapon Buffs Per Row"], C.buffs, "wepbuffrowsize")
CreateConfig(U["buff"], T["Number Of Buffs Per Row"], C.buffs, "buffrowsize")
CreateConfig(U["buff"], T["Number Of Debuffs Per Row"], C.buffs, "debuffrowsize")
CreateConfig(UpdateCooldowns, T["Duration Timers"], C.buffs, "timers", "dropdown", pfUI.gui.dropdowns.cooldown_timers)
CreateConfig(U["buff"], T["Show Duration Inside Buff"], C.buffs, "textinside", "checkbox")
CreateConfig(U["buff"], T["Stack Count Font Size"], C.buffs, "fontsize")
end)
@@ -2527,6 +2248,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["totems"], T["Icon Size"], C.totems, "iconsize")
CreateConfig(U["totems"], T["Spacing"], C.totems, "spacing", "dropdown", pfUI.gui.dropdowns.spacing)
CreateConfig(U["totems"], T["Show Background"], C.totems, "showbg", "checkbox")
CreateConfig(UpdateCooldowns, T["Duration Timers"], C.totems, "timers", "dropdown", pfUI.gui.dropdowns.cooldown_timers)
end)
CreateGUIEntry(T["Buffs"], T["Player Buff Bar"], function()
@@ -2593,6 +2315,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["bars"], T["Self Cast: Right Click"], C.bars, "rightself", "checkbox")
CreateConfig(U["bars"], T["Button Animation"], C.bars, "animation", "dropdown", pfUI.gui.dropdowns.actionbuttonanimations)
CreateConfig(U["bars"], T["Button Animation Trigger"], C.bars, "animmode", "dropdown", pfUI.gui.dropdowns.animationmode)
CreateConfig(UpdateCooldowns, T["Cooldown Timers"], C.bars, "timers", "dropdown", pfUI.gui.dropdowns.cooldown_timers)
CreateConfig(U["bars"], T["Show Animation On Hidden Bars"], C.bars, "animalways", "checkbox")
CreateConfig(U["bars"], T["Show Reagent Count"], C.bars, "reagents", "checkbox")
CreateConfig(U["bars"], T["Highlight Equipped Items"], C.bars, "showequipped", "checkbox")
@@ -2603,6 +2326,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["bars"], T["Out Of Mana Color"], C.bars, "oomcolor", "color")
CreateConfig(U["bars"], T["Highlight Not Usable Spells"], C.bars, "showna", "checkbox")
CreateConfig(U["bars"], T["Not Usable Color"], C.bars, "nacolor", "color")
CreateConfig(U["bars"], T["Highlight Proc And Reactive Spells"], C.bars, "procglow", "checkbox")
CreateConfig(U["bars"], T["Lock Actionbars"], "UVAR", "LOCK_ACTIONBAR", "checkbox")
CreateConfig(U["bars"], T["Always Allow Drag Via Shift Key"], C.bars, "shiftdrag", "checkbox")
@@ -2822,6 +2546,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox")
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
CreateConfig(nil, T["Show Unit Buffs"], C.tooltip, "showbuffs", "checkbox")
CreateConfig(UpdateCooldowns, T["Unit Buff Timers"], C.tooltip, "aura_timers", "dropdown", pfUI.gui.dropdowns.cooldown_timers)
CreateConfig(nil, T["Show Unit IDs"], C.tooltip, "unitid", "checkbox")
end)
@@ -2985,6 +2710,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["nameplates"], T["Debuff Icon Offset"], C.nameplates, "debuffoffset")
CreateConfig(U["nameplates"], T["Debuff Icon Size"], C.nameplates, "debuffsize")
CreateConfig(U["nameplates"], T["Show Debuff Stacks"], C.nameplates.debuffs, "showstacks", "checkbox")
CreateConfig(UpdateCooldowns, T["Debuff Timers"], C.nameplates, "debuff_timers", "dropdown", pfUI.gui.dropdowns.cooldown_timers)
CreateConfig(U["nameplates"], T["Filter Mode"], C.nameplates.debuffs, "filter", "dropdown", pfUI.gui.dropdowns.buffbarfilter)
CreateConfig(U["nameplates"], T["Blacklist"], C.nameplates.debuffs, "blacklist", "list")
@@ -3084,4 +2810,4 @@ pfUI:RegisterModule("gui", function ()
end
end)
end
end)
end)
+1 -1
View File
@@ -34,7 +34,7 @@ pfUI:RegisterModule("lossofcontrol", function ()
frame.icon.tex = frame.icon:CreateTexture(nil, "ARTWORK")
HandleIcon(frame.icon, frame.icon.tex)
frame.cd = CreateCooldown(frame.icon, "lossofcontrol")
frame.cd = CreateCooldown(frame.icon, "timer", C.cooldown, "lossofcontrol_timers")
frame.cd:SetAllPoints(frame.icon.tex)
frame.label = frame:CreateFontString(nil, "OVERLAY")
+1 -1
View File
@@ -41,7 +41,7 @@ pfUI:RegisterModule("marktracking", function ()
local function ShortenName(name, row)
if not name or name == "" then return "" end
local barWidth = row.health:GetWidth()
local barWidth = row.health:GetUnscaledWidth()
if barWidth < 1 then barWidth = BAR_WIDTH end
local available = barWidth - 4
if rm_showpct then available = available - 32 end
+4 -3
View File
@@ -111,15 +111,16 @@ pfUI:RegisterModule("minimap", function ()
-- Blink ten times between .9 and .1, then hide
local highlight = this.highlight
highlight:SetAlpha(.9)
highlight.blink = highlight:CreateAnimationGroup()
for i = 1, 10 do
local down = highlight.blink:CreateAnimation("Alpha")
down:SetChange(-.8)
down:SetFromAlpha(.9)
down:SetToAlpha(.1)
down:SetDuration(.3)
down:SetOrder(i * 2 - 1)
local up = highlight.blink:CreateAnimation("Alpha")
up:SetChange(.8)
up:SetFromAlpha(.1)
up:SetToAlpha(.9)
up:SetDuration(.3)
up:SetOrder(i * 2)
end
+71 -9
View File
@@ -3,20 +3,53 @@ pfUI:RegisterModule("mirrortimers", function ()
local fontsize = tonumber(C.global.font_size)
local height = fontsize * 1.3
-- The value of a mirror timer moves by `scale` per second: breath drains at
-- -1, and refills faster than that. A running bar fills from a duration
-- object that spans the whole bar at that rate, and its text binding shows
-- the value as m:ss. A paused bar holds its value.
local function Sync(frame)
local value = GetMirrorTimerProgress(frame.timer) / 1000
local max, scale = frame.maxvalue, frame.scale
local label = frame.label:GetText() or ""
if frame.paused or scale == 0 then
frame.statusbar:SetMinMaxValues(0, max)
frame.statusbar:SetValue(value)
frame.binding:Disable()
frame.text:SetFormattedText("%s (%d:%02d)", label, value / 60, value % 60)
return
end
-- the shown value is the seconds of the duration times the rate
local rate = math.abs(scale)
local property
if scale < 0 then
frame.duration:SetTimeFromEnd(GetTime() + value / rate, max / rate)
frame.statusbar:SetTimerDuration(frame.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
property = Enum.DurationTextBindingProperty.RemainingDuration
else
frame.duration:SetTimeFromEnd(GetTime() + (max - value) / rate, max / rate)
frame.statusbar:SetTimerDuration(frame.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
property = Enum.DurationTextBindingProperty.ElapsedDuration
end
local Down = Enum.NumericRuleFormatRounding.Down
frame.clock:SetBreakpoints({
{ threshold = 0, format = "%d:%02d", components = {
{ div = 60 / rate, step = 1, rounding = Down },
{ div = 1 / rate, mod = 60, step = 1, rounding = Down },
} },
})
frame.binding:SetTextFormat(label .. " ({})", { { property = property, formatter = frame.clock } })
frame.binding:Enable()
end
for i = 1, MIRRORTIMER_NUMTIMERS do
local frame = _G["MirrorTimer"..i]
frame:GetRegions():Hide()
frame:SetWidth(200)
frame:SetHeight(height)
frame:SetScript("OnUpdate", function()
if this.paused or not this.timer then return end
MirrorTimerFrame_OnUpdate(this, arg1)
local text = this.label:GetText()
if text and this.value then
this.text:SetText(format("%s (%d:%02d)", text, this.value/60, this.value - math.floor(this.value/60)*60))
end
end)
frame:SetScript("OnUpdate", nil)
frame:ClearAllPoints()
frame:SetPoint("TOP", UIParent, "TOP", 0, -120-(i-1)*(height + 10))
@@ -38,5 +71,34 @@ pfUI:RegisterModule("mirrortimers", function ()
frame.text = frame:CreateFontString(nil, "OVERLAY")
frame.text:SetFont(font, fontsize, "OUTLINE")
frame.text:SetPoint("CENTER", frame.statusbar, "CENTER", 0, 0)
frame.duration = C_DurationUtil.CreateDuration()
frame.clock = C_StringUtil.CreateNumericRuleFormatter()
frame.binding = C_DurationUtil.CreateDurationTextBinding()
frame.binding:SetFontString(frame.text)
frame.binding:SetDuration(frame.duration)
-- the stock handler has already set the pause state
frame:HookScript("OnEvent", function()
if event == "MIRROR_TIMER_PAUSE" and this:IsShown() and arg1 == this.timer then
Sync(this)
end
end)
frame:HookScript("OnHide", function()
this.binding:Disable()
end)
end
-- the stock function picks the bar and sets its timer, scale and pause state
hooksecurefunc("MirrorTimer_Show", function(timer, _, maxvalue)
for i = 1, MIRRORTIMER_NUMTIMERS do
local frame = _G["MirrorTimer"..i]
if frame:IsShown() and frame.timer == timer then
frame.maxvalue = maxvalue / 1000
Sync(frame)
return
end
end
end)
end)
+1215 -1458
View File
File diff suppressed because it is too large Load Diff
-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()
end
-- Add throttle to player frame OnUpdate
-- Throttle the unit frame's existing OnUpdate to ~20 FPS so the per-frame
-- work stays cheap.
if pfUI.uf.player:GetScript("OnUpdate") then
local originalOnUpdate = pfUI.uf.player:GetScript("OnUpdate")
pfUI.uf.player:SetScript("OnUpdate", function()
if (this.throttleTick or 0) > GetTime() then return end
this.throttleTick = GetTime() + 0.05
originalOnUpdate()
end)
end
-- PLAYER_DAMAGE_DONE_MODS fires on the MOD_DAMAGE_DONE fields that
-- GetSpellBonusDamage reads, so gear, buffs and talents all reach it.
pfUI.events:RegisterFrameEventAndCallback("PLAYER_ENTERING_WORLD", UpdateInfoText)
+1 -1
View File
@@ -85,7 +85,7 @@ pfUI:RegisterModule("radar", function ()
for _, yards in ipairs(RANGES) do
rootDescription:CreateRadio(yards .. " yd", IsRange, SetRange, yards)
end
rootDescription:CreateDivider()
CreateMenuDivider(rootDescription)
rootDescription:CreateButton(HIDE, function() radar:Hide() end)
end
+17 -24
View File
@@ -30,8 +30,8 @@ pfUI:RegisterModule("raid", function ()
-- flat pool of pet frames, laid out by LayoutPets (mirror or collapsed)
pfUI.uf.raid.pets = {}
-- 1-based grid slot -> (row, col) for the current fill direction, matching
-- the raid grid's own fill loops.
-- 1-based grid slot -> (col, row) for the current fill direction, shared by
-- the raid and pet grids.
local function SlotToCoord(slot, fill, x, y)
slot = slot - 1
if fill == "VERTICAL" then
@@ -75,12 +75,12 @@ pfUI:RegisterModule("raid", function ()
end
end
local i = 1
local width = pfUI.uf.raid[1]:GetWidth()+2*default_border
local height = pfUI.uf.raid[1]:GetHeight()+2*default_border
local layout = pfUI.uf.raid[1].config.raidlayout
local padding = tonumber(pfUI.uf.raid[1].config.raidpadding)*GetPerfectPixel()
local fill = pfUI.uf.raid[1].config.raidfill
local growleft = pfUI.uf.raid[1].config.raidgrowleft == "1"
local _, _, x, y = string.find(layout,"(.+)x(.+)")
x, y = tonumber(x), tonumber(y)
@@ -95,24 +95,18 @@ pfUI:RegisterModule("raid", function ()
}
end
if fill == "VERTICAL" then
for r=1, x do for g=1, y do
if pfUI.uf.raid[i] then
pfUI.uf.raid[i]:ClearAllPoints()
pfUI.uf.raid[i]:SetPoint("BOTTOMLEFT", (r-1)*(padding+width), (g-1)*(padding+height))
UpdateMovable(pfUI.uf.raid[i], true)
end
i = i + 1
end end
else
for g=1, y do for r=1, x do
if pfUI.uf.raid[i] then
pfUI.uf.raid[i]:ClearAllPoints()
pfUI.uf.raid[i]:SetPoint("BOTTOMLEFT", (r-1)*(padding+width), (g-1)*(padding+height))
UpdateMovable(pfUI.uf.raid[i], true)
end
i = i + 1
end end
-- right to left mirrors across the columns maxraid can fill, so a smaller
-- raid stays next to the anchor instead of the far edge of the layout
local cols = min(x, fill == "VERTICAL" and ceil(maxraid / y) or maxraid)
for i=1, x*y do
if pfUI.uf.raid[i] then
local r, g = SlotToCoord(i, fill, x, y)
if growleft then r = cols - r + 1 end
pfUI.uf.raid[i]:ClearAllPoints()
pfUI.uf.raid[i]:SetPoint("BOTTOMLEFT", (r-1)*(padding+width), (g-1)*(padding+height))
UpdateMovable(pfUI.uf.raid[i], true)
end
end
self:LayoutPets()
@@ -270,8 +264,7 @@ pfUI:RegisterModule("raid", function ()
if newGuid ~= oldGuid then
-- GUID changed = different player = need full update
tracker.frameToGuid[frame] = newGuid
frame.update_full = true
frame.update_aura = true -- Force aura refresh!
pfUI.uf:QueueUpdate(frame, "full")
end
end
end
@@ -303,4 +296,4 @@ pfUI:RegisterModule("raid", function ()
pfUI.uf.raid:QueueUpdate(0)
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:SetAllPoints(f.time)
f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"])
f.time.bar:SetMinMaxValues(0, 100)
local r, g, b, a = GetStringColor(C.appearance.border.color)
f.time.bar:SetStatusBarColor(r, g, b)
f.time.bar:SetValue(20)
f.time.bar:SetScript("OnUpdate", function()
if not this:GetParent():GetParent().rollID then return end
local left = GetLootRollTimeLeft(this:GetParent():GetParent().rollID)
local min, max = this:GetMinMaxValues()
if left < min or left > max then left = min end
this:SetValue(left)
end)
-- drains by itself; UpdateLootRoll sets the roll's time span
f.time.duration = C_DurationUtil.CreateDuration()
f.time.bar:SetTimerDuration(f.time.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
return f
end
@@ -299,7 +293,9 @@ pfUI:RegisterModule("roll", function ()
pfUI.roll.frames[id].name.text:SetTextColor(color.r, color.g, color.b, 1)
pfUI.roll.frames[id].icon.tex:SetTexture(texture)
pfUI.roll.frames[id].backdrop:SetBackdropBorderColor(color.r, color.g, color.b)
pfUI.roll.frames[id].time.bar:SetMinMaxValues(0, pfUI.roll.frames[id].rollTime)
-- both in milliseconds
local left = GetLootRollTimeLeft(pfUI.roll.frames[id].rollID)
pfUI.roll.frames[id].time.duration:SetTimeFromEnd(GetTime() + left / 1000, pfUI.roll.frames[id].rollTime / 1000)
if C.loot.raritytimer == "1" then
pfUI.roll.frames[id].time.bar:SetStatusBarColor(color.r, color.g, color.b, .5)
+7 -6
View File
@@ -5,7 +5,7 @@ pfUI:RegisterModule("socialmod", function ()
pfUI.socialmod:SetScript("OnEvent", function()
local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS)
name = name or cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S)
if name and playerdb[name] and playerdb[name].cname then
if name and playerdb[name] then
playerdb[name].lastseen = date("%a %d-%b-%Y")
end
end)
@@ -92,7 +92,6 @@ pfUI:RegisterModule("socialmod", function ()
local cname = ccolor:WrapTextInColorCode(name)
if playerdb[name] then
playerdb[name].lastseen = date("%a %d-%b-%Y")
playerdb[name].cname = cname
end
if friendName then
@@ -106,11 +105,13 @@ pfUI:RegisterModule("socialmod", function ()
caption:SetVertexColor(1,1,1,.9)
friendInfo:SetVertexColor(1,1,1,.9)
else
if playerdb[name] and playerdb[name].cname and playerdb[name].level and playerdb[name].lastseen then
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname)
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen)
local _, class = C_PlayerCache.GetPlayerInfoByName(name)
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), class and PFUI_CLASS_COLORS[class]:WrapTextInColorCode(name) or name.."|r")
local entry = playerdb[name]
if entry and entry.level and entry.lastseen then
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), entry.level, entry.lastseen)
else
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r")
friendInfo:SetText(TEXT(UNKNOWN))
end
+230 -457
View File
@@ -5,14 +5,16 @@ pfUI:RegisterModule("swingtimer", function ()
local ON_SWING_QUEUED = 0
local ON_SWING_QUEUE_POPPED = 1
-- seconds a melee bar stays full after its swing is due, to bridge the gap
-- until PLAYER_SWING arrives; it then hides unless auto attack goes on
local GRACE = 0.15
-- the last half second of a hunter's ranged swing, when moving cancels the shot
local DEADZONE = 0.5
-- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max)
local S = {
mhTimer = 0, mhTimerMax = 1,
ohTimer = 0, ohTimerMax = 1,
raTimer = 0, raTimerMax = 1,
mhSpeed = 0, ohSpeed = 0, raSpeed = 0,
mhActive = false, ohActive = false, raActive = false,
lastMhMarkerX = -1, lastOhMarkerX = -1, lastRaMarkerX = -1,
autoAttackActive = false,
inCombat = false,
hsQueued = false, cleaveQueued = false, maulQueued = false,
@@ -21,7 +23,6 @@ pfUI:RegisterModule("swingtimer", function ()
isDruid = false,
cachedHSSlots = {}, cachedCleaveSlots = {}, cachedMaulSlots = {},
useSpellQueueEvent = false,
swingThrottle = 0,
onSwingCache = {},
outOfRange = {}, -- by Enum.PlayerSwingType
}
@@ -70,196 +71,111 @@ pfUI:RegisterModule("swingtimer", function ()
local rwR, rwG, rwB, rwA = ParseColor(C.unitframes.swingtimerrangedwarncolor, 0.9, 0.0, 0.0, 1)
local orR, orG, orB = ParseColor(C.unitframes.swingtimeroutrangecolor, 0.5, 0.5, 0.5, 1)
local isHunter = UnitClassBase("player") == "HUNTER"
local mhDefaultR, mhDefaultG, mhDefaultB = mhR, mhG, mhB
-- the countdown text, cut down to tenths
local countdown = C_StringUtil.CreateNumericRuleFormatter()
countdown:AddBreakpoint({ threshold = 0, step = 0.1, rounding = Enum.NumericRuleFormatRounding.Down, format = "%.1f" })
-- Create container frame
-- the container keeps the public API
pfUI.swingtimer = CreateFrame("Frame", "pfSwingTimer", UIParent)
pfUI.swingtimer:SetFrameStrata("MEDIUM")
pfUI.swingtimer:Hide()
-- Mainhand bar
pfUI.swingtimer.mainhand = CreateFrame("StatusBar", "pfSwingTimerMainhand", UIParent)
pfUI.swingtimer.mainhand:SetPoint("BOTTOM", UIParent, "CENTER", -189, 103)
pfUI.swingtimer.mainhand:SetWidth(sw_width)
pfUI.swingtimer.mainhand:SetHeight(sw_height)
pfUI.swingtimer.mainhand:SetMinMaxValues(0, 1)
pfUI.swingtimer.mainhand:SetValue(0)
pfUI.swingtimer.mainhand:SetStatusBarTexture(sw_texture)
pfUI.swingtimer.mainhand:SetStatusBarColor(mhR, mhG, mhB, mhA)
pfUI.swingtimer.mainhand:Hide()
-- A bar fills by itself from `swing`, the time span of the whole swing, and
-- its text binding counts it down. The marker and its glows ride the edge
-- of the fill. `shown` is whether the config lets the bar show at all.
local function CreateSwingBar(name, label, shown)
local bar = CreateFrame("StatusBar", name, UIParent)
bar:SetWidth(sw_width)
bar:SetHeight(sw_height)
bar:SetStatusBarTexture(sw_texture)
bar:Hide()
bar.shown = shown
pfUI.swingtimer.mainhand.text = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.mainhand.text:SetPoint("CENTER", pfUI.swingtimer.mainhand, "CENTER", 0, 0)
pfUI.swingtimer.mainhand.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.mainhand.text:SetTextColor(1, 1, 1, 1)
pfUI.swingtimer.mainhand.text:SetText("")
if not sw_showtext then pfUI.swingtimer.mainhand.text:Hide() end
bar.swing = C_DurationUtil.CreateDuration()
bar:SetTimerDuration(bar.swing, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
pfUI.swingtimer.mainhand.label = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.mainhand.label:SetPoint("RIGHT", pfUI.swingtimer.mainhand, "LEFT", -4, 0)
pfUI.swingtimer.mainhand.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.mainhand.label:SetTextColor(0.8, 0.8, 0.8, 1)
pfUI.swingtimer.mainhand.label:SetText(sw_showlabel and "MH" or "")
bar.text = bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
bar.text:SetPoint("CENTER", bar, "CENTER", 0, 0)
bar.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
bar.text:SetTextColor(1, 1, 1, 1)
bar.text:SetShown(sw_showtext)
pfUI.swingtimer.mainhand.speed = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.mainhand.speed:SetPoint("LEFT", pfUI.swingtimer.mainhand, "RIGHT", 4, 0)
pfUI.swingtimer.mainhand.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.mainhand.speed:SetTextColor(0.8, 0.8, 0.8, 1)
pfUI.swingtimer.mainhand.speed:SetText("")
if not sw_showspeed then pfUI.swingtimer.mainhand.speed:Hide() end
bar.timer = C_DurationUtil.CreateDurationTextBinding()
bar.timer:SetFontString(bar.text)
bar.timer:SetDuration(bar.swing)
bar.timer:SetFormatter(countdown)
CreateBackdrop(pfUI.swingtimer.mainhand)
bar.label = bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
bar.label:SetPoint("RIGHT", bar, "LEFT", -4, 0)
bar.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
bar.label:SetTextColor(0.8, 0.8, 0.8, 1)
bar.label:SetText(sw_showlabel and label or "")
bar.speed = bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
bar.speed:SetPoint("LEFT", bar, "RIGHT", 4, 0)
bar.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
bar.speed:SetTextColor(0.8, 0.8, 0.8, 1)
bar.speed:SetShown(sw_showspeed)
pfUI.swingtimer.mainhand.marker = pfUI.swingtimer.mainhand:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.mainhand.marker:SetColorTexture(1, 1, 1, 1.0)
pfUI.swingtimer.mainhand.marker:SetWidth(2)
pfUI.swingtimer.mainhand.marker:SetHeight(sw_height)
pfUI.swingtimer.mainhand.marker:Hide()
CreateBackdrop(bar)
-- Left glow: fades from transparent to white (right edge = marker)
pfUI.swingtimer.mainhand.markerGlowL = pfUI.swingtimer.mainhand:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.mainhand.markerGlowL:SetColorTexture(1, 1, 1, 1)
pfUI.swingtimer.mainhand.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
pfUI.swingtimer.mainhand.markerGlowL:SetWidth(10)
pfUI.swingtimer.mainhand.markerGlowL:SetHeight(sw_height)
pfUI.swingtimer.mainhand.markerGlowL:Hide()
bar.edge = CreateFrame("Frame", nil, bar)
bar.edge:SetAllPoints(bar)
bar.edge:Hide()
-- Right glow: fades from white to transparent (left edge = marker)
pfUI.swingtimer.mainhand.markerGlowR = pfUI.swingtimer.mainhand:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.mainhand.markerGlowR:SetColorTexture(1, 1, 1, 1)
pfUI.swingtimer.mainhand.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
pfUI.swingtimer.mainhand.markerGlowR:SetWidth(10)
pfUI.swingtimer.mainhand.markerGlowR:SetHeight(sw_height)
pfUI.swingtimer.mainhand.markerGlowR:Hide()
bar.marker = bar.edge:CreateTexture(nil, "OVERLAY")
bar.marker:SetColorTexture(1, 1, 1, 1.0)
bar.marker:SetWidth(2)
bar.marker:SetHeight(sw_height)
bar.marker:SetPoint("CENTER", bar:GetStatusBarTexture(), "RIGHT")
CreateBackdropShadow(pfUI.swingtimer.mainhand)
-- Left glow: fades from transparent to white (right edge = marker)
bar.markerGlowL = bar.edge:CreateTexture(nil, "OVERLAY")
bar.markerGlowL:SetColorTexture(1, 1, 1, 1)
bar.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
bar.markerGlowL:SetWidth(10)
bar.markerGlowL:SetHeight(sw_height)
bar.markerGlowL:SetPoint("RIGHT", bar.marker, "LEFT", 0, 0)
-- Offhand bar
pfUI.swingtimer.offhand = CreateFrame("StatusBar", "pfSwingTimerOffhand", UIParent)
pfUI.swingtimer.offhand:SetPoint("TOP", pfUI.swingtimer.mainhand, "BOTTOM", 0, -4)
pfUI.swingtimer.offhand:SetWidth(sw_width)
pfUI.swingtimer.offhand:SetHeight(sw_height)
pfUI.swingtimer.offhand:SetMinMaxValues(0, 1)
pfUI.swingtimer.offhand:SetValue(0)
pfUI.swingtimer.offhand:SetStatusBarTexture(sw_texture)
pfUI.swingtimer.offhand:SetStatusBarColor(ohR, ohG, ohB, ohA)
pfUI.swingtimer.offhand:Hide()
-- Right glow: fades from white to transparent (left edge = marker)
bar.markerGlowR = bar.edge:CreateTexture(nil, "OVERLAY")
bar.markerGlowR:SetColorTexture(1, 1, 1, 1)
bar.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
bar.markerGlowR:SetWidth(10)
bar.markerGlowR:SetHeight(sw_height)
bar.markerGlowR:SetPoint("LEFT", bar.marker, "RIGHT", 0, 0)
pfUI.swingtimer.offhand.text = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.offhand.text:SetPoint("CENTER", pfUI.swingtimer.offhand, "CENTER", 0, 0)
pfUI.swingtimer.offhand.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.offhand.text:SetTextColor(1, 1, 1, 1)
pfUI.swingtimer.offhand.text:SetText("")
if not sw_showtext then pfUI.swingtimer.offhand.text:Hide() end
CreateBackdropShadow(bar)
return bar
end
pfUI.swingtimer.offhand.label = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.offhand.label:SetPoint("RIGHT", pfUI.swingtimer.offhand, "LEFT", -4, 0)
pfUI.swingtimer.offhand.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.offhand.label:SetTextColor(0.8, 0.8, 0.8, 1)
pfUI.swingtimer.offhand.label:SetText(sw_showlabel and "OH" or "")
local mainhand = CreateSwingBar("pfSwingTimerMainhand", "MH", true)
mainhand:SetPoint("BOTTOM", UIParent, "CENTER", -189, 103)
mainhand.showmarker = true
pfUI.swingtimer.offhand.speed = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.offhand.speed:SetPoint("LEFT", pfUI.swingtimer.offhand, "RIGHT", 4, 0)
pfUI.swingtimer.offhand.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.offhand.speed:SetTextColor(0.8, 0.8, 0.8, 1)
pfUI.swingtimer.offhand.speed:SetText("")
if not sw_showspeed then pfUI.swingtimer.offhand.speed:Hide() end
local offhand = CreateSwingBar("pfSwingTimerOffhand", "OH", sw_showoh)
offhand:SetPoint("TOP", mainhand, "BOTTOM", 0, -4)
offhand.showmarker = true
CreateBackdrop(pfUI.swingtimer.offhand)
pfUI.swingtimer.offhand.marker = pfUI.swingtimer.offhand:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.offhand.marker:SetColorTexture(1, 1, 1, 1.0)
pfUI.swingtimer.offhand.marker:SetWidth(2)
pfUI.swingtimer.offhand.marker:SetHeight(sw_height)
pfUI.swingtimer.offhand.marker:Hide()
-- Left glow: fades from transparent to white (right edge = marker)
pfUI.swingtimer.offhand.markerGlowL = pfUI.swingtimer.offhand:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.offhand.markerGlowL:SetColorTexture(1, 1, 1, 1)
pfUI.swingtimer.offhand.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
pfUI.swingtimer.offhand.markerGlowL:SetWidth(10)
pfUI.swingtimer.offhand.markerGlowL:SetHeight(sw_height)
pfUI.swingtimer.offhand.markerGlowL:Hide()
-- Right glow: fades from white to transparent (left edge = marker)
pfUI.swingtimer.offhand.markerGlowR = pfUI.swingtimer.offhand:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.offhand.markerGlowR:SetColorTexture(1, 1, 1, 1)
pfUI.swingtimer.offhand.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
pfUI.swingtimer.offhand.markerGlowR:SetWidth(10)
pfUI.swingtimer.offhand.markerGlowR:SetHeight(sw_height)
pfUI.swingtimer.offhand.markerGlowR:Hide()
CreateBackdropShadow(pfUI.swingtimer.offhand)
-- Ranged bar
-- Hunters fill from the center: the bar drains toward the middle during
-- the reload, then the auto shot deadzone grows back out in the warn color.
pfUI.swingtimer.ranged = CreateFrame("StatusBar", "pfSwingTimerRanged", UIParent)
pfUI.swingtimer.ranged:SetPoint("BOTTOM", UIParent, "CENTER", -189, 83)
pfUI.swingtimer.ranged:SetWidth(sw_width)
pfUI.swingtimer.ranged:SetHeight(sw_height)
pfUI.swingtimer.ranged:SetMinMaxValues(0, 1)
pfUI.swingtimer.ranged:SetValue(0)
pfUI.swingtimer.ranged:SetStatusBarTexture(sw_texture)
pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged:SetFillStyle(isHunter and "CENTER" or "STANDARD")
pfUI.swingtimer.ranged:Hide()
-- Each phase is a time span of its own on `phase`.
local ranged = CreateSwingBar("pfSwingTimerRanged", "Ra", sw_showranged)
ranged:SetPoint("BOTTOM", UIParent, "CENTER", -189, 83)
ranged:SetFillStyle(isHunter and "CENTER" or "STANDARD")
ranged.showmarker = not isHunter
if isHunter then
ranged.phase = C_DurationUtil.CreateDuration()
ranged.timer:SetDuration(ranged.phase)
end
pfUI.swingtimer.ranged.text = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.ranged.text:SetPoint("CENTER", pfUI.swingtimer.ranged, "CENTER", 0, 0)
pfUI.swingtimer.ranged.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.ranged.text:SetTextColor(1, 1, 1, 1)
pfUI.swingtimer.ranged.text:SetText("")
if not sw_showtext then pfUI.swingtimer.ranged.text:Hide() end
pfUI.swingtimer.mainhand = mainhand
pfUI.swingtimer.offhand = offhand
pfUI.swingtimer.ranged = ranged
pfUI.swingtimer.ranged.label = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.ranged.label:SetPoint("RIGHT", pfUI.swingtimer.ranged, "LEFT", -4, 0)
pfUI.swingtimer.ranged.label:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.ranged.label:SetTextColor(0.8, 0.8, 0.8, 1)
pfUI.swingtimer.ranged.label:SetText(sw_showlabel and "Ra" or "")
UpdateMovable(mainhand)
UpdateMovable(ranged)
pfUI.swingtimer.ranged.speed = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal")
pfUI.swingtimer.ranged.speed:SetPoint("LEFT", pfUI.swingtimer.ranged, "RIGHT", 4, 0)
pfUI.swingtimer.ranged.speed:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
pfUI.swingtimer.ranged.speed:SetTextColor(0.8, 0.8, 0.8, 1)
pfUI.swingtimer.ranged.speed:SetText("")
if not sw_showspeed then pfUI.swingtimer.ranged.speed:Hide() end
CreateBackdrop(pfUI.swingtimer.ranged)
pfUI.swingtimer.ranged.marker = pfUI.swingtimer.ranged:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.ranged.marker:SetColorTexture(1, 1, 1, 1.0)
pfUI.swingtimer.ranged.marker:SetWidth(2)
pfUI.swingtimer.ranged.marker:SetHeight(sw_height)
pfUI.swingtimer.ranged.marker:Hide()
-- Left glow: fades from transparent to white (right edge = marker)
pfUI.swingtimer.ranged.markerGlowL = pfUI.swingtimer.ranged:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.ranged.markerGlowL:SetColorTexture(1, 1, 1, 1)
pfUI.swingtimer.ranged.markerGlowL:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.35)
pfUI.swingtimer.ranged.markerGlowL:SetWidth(10)
pfUI.swingtimer.ranged.markerGlowL:SetHeight(sw_height)
pfUI.swingtimer.ranged.markerGlowL:Hide()
-- Right glow: fades from white to transparent (left edge = marker)
pfUI.swingtimer.ranged.markerGlowR = pfUI.swingtimer.ranged:CreateTexture(nil, "OVERLAY")
pfUI.swingtimer.ranged.markerGlowR:SetColorTexture(1, 1, 1, 1)
pfUI.swingtimer.ranged.markerGlowR:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0.35, 1, 1, 1, 0)
pfUI.swingtimer.ranged.markerGlowR:SetWidth(10)
pfUI.swingtimer.ranged.markerGlowR:SetHeight(sw_height)
pfUI.swingtimer.ranged.markerGlowR:Hide()
CreateBackdropShadow(pfUI.swingtimer.ranged)
UpdateMovable(pfUI.swingtimer.mainhand)
UpdateMovable(pfUI.swingtimer.ranged)
-- OH weapon detection
-- OH weapon detection: slot 17 (off hand) accepts one-hand (13) and off-hand
-- weapons (22); main-hand-only (21) can never sit there.
local OH_WEAPON_TYPES = { [13]=true, [22]=true }
@@ -276,60 +192,6 @@ pfUI:RegisterModule("swingtimer", function ()
S.raSpeed = (rs and rs > 0) and rs or 0
end
-- Start* restart a countdown from a PLAYER_SWING reset. `remaining` can be
-- shorter than the weapon speed (parry haste, ranged wind-up), so the bar
-- length stays the full swing and the fill starts part-way along.
local function StartMH(remaining)
UpdateWeaponSpeeds()
pfUI.swingtimer.mhGraceAt = nil -- cancel any pending hide
S.mhTimerMax = math.max(S.mhSpeed, remaining)
S.mhTimer = remaining
S.mhActive = true
pfUI.swingtimer.mainhand:Show()
pfUI.swingtimer:Show()
end
local function StartOH(remaining)
UpdateWeaponSpeeds()
-- The off-hand attack time stays set with a shield equipped
if S.ohSpeed <= 0 then return end
pfUI.swingtimer.ohGraceAt = nil -- cancel any pending hide
S.ohTimerMax = math.max(S.ohSpeed, remaining)
S.ohTimer = remaining
S.ohActive = true
if sw_showoh then pfUI.swingtimer.offhand:Show() end
pfUI.swingtimer:Show()
end
local function StartRanged(remaining)
if not sw_showranged then return end
UpdateWeaponSpeeds()
if S.raSpeed <= 0 then return end
S.raTimerMax = math.max(S.raSpeed, remaining)
S.raTimer = remaining
S.raActive = true
pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged:SetValue(isHunter and 1 or 0)
pfUI.swingtimer.ranged:Show()
pfUI.swingtimer:Show()
end
local function ResetAll()
S.mhActive = false
S.ohActive = false
S.raActive = false
S.mhTimer = 0
S.ohTimer = 0
S.raTimer = 0
pfUI.swingtimer.mhGraceAt = nil
pfUI.swingtimer.ohGraceAt = nil
pfUI.swingtimer.mainhand:Hide()
pfUI.swingtimer.offhand:Hide()
pfUI.swingtimer.ranged:Hide()
pfUI.swingtimer:Hide()
end
-- HS/Cleave/Maul helpers. Canonical rank-1 spellIDs resolve to the
-- localized spell name once, so per-spell classification is rank- and
-- locale-independent without per-rank ID hardcoding (every rank of
@@ -419,236 +281,139 @@ pfUI:RegisterModule("swingtimer", function ()
return CheckQueuedAction(S.cachedHSSlots), CheckQueuedAction(S.cachedCleaveSlots)
end
-- OnUpdate: countdown all timers with delta, then render
pfUI.swingtimer:SetScript("OnUpdate", function()
S.swingThrottle = S.swingThrottle + arg1
local swingDelay = pfUI.throttle and pfUI.throttle:Get("swingtimer") or 0.02
if S.swingThrottle < swingDelay then return end
local delta = S.swingThrottle
S.swingThrottle = 0
-- (out-of-combat hide handled naturally when timers expire)
local anyActive = false
-- Tick timers down. When a timer expires we give a short grace period before
-- hiding the bar, to bridge the 1-2 frame gap until PLAYER_SWING arrives.
-- If PLAYER_SWING arrives during the grace period it resets the timer
-- normally and the grace timer is cleared. If not (e.g. auto-attack was
-- turned off), the bar hides after the grace period ends.
local GRACE = 0.15 -- seconds to wait after timer hits 0 before hiding
if S.mhActive then
S.mhTimer = S.mhTimer - delta
if S.mhTimer <= 0 then
S.mhTimer = 0
if not pfUI.swingtimer.mhGraceAt then
pfUI.swingtimer.mhGraceAt = GetTime() + GRACE
elseif GetTime() >= pfUI.swingtimer.mhGraceAt then
pfUI.swingtimer.mhGraceAt = nil
if not S.inCombat or not S.autoAttackActive then
S.mhActive = false
pfUI.swingtimer.mainhand:Hide()
S.lastMhMarkerX = -1
pfUI.swingtimer.mainhand.marker:Hide()
pfUI.swingtimer.mainhand.markerGlowL:Hide()
pfUI.swingtimer.mainhand.markerGlowR:Hide()
end
end
end
end
if S.ohActive then
S.ohTimer = S.ohTimer - delta
if S.ohTimer <= 0 then
S.ohTimer = 0
if not pfUI.swingtimer.ohGraceAt then
pfUI.swingtimer.ohGraceAt = GetTime() + GRACE
elseif GetTime() >= pfUI.swingtimer.ohGraceAt then
pfUI.swingtimer.ohGraceAt = nil
if not S.inCombat or not S.autoAttackActive then
S.ohActive = false
pfUI.swingtimer.offhand:Hide()
S.lastOhMarkerX = -1
pfUI.swingtimer.offhand.marker:Hide()
pfUI.swingtimer.offhand.markerGlowL:Hide()
pfUI.swingtimer.offhand.markerGlowR:Hide()
end
end
end
end
if S.raActive then
S.raTimer = S.raTimer - delta
if S.raTimer <= 0 then
S.raTimer = 0
S.raActive = false
pfUI.swingtimer.ranged:Hide()
end
end
-- HS/Cleave color
local curR, curG, curB = mhDefaultR, mhDefaultG, mhDefaultB
-- The bar colors follow the queued on-next-swing ability, the range state
-- and the hunter deadzone. Runs on the events that change any of them;
-- ACTIONBAR_UPDATE_STATE covers the IsCurrentAction reconcile.
local function UpdateColors()
local r, g, b = mhR, mhG, mhB
if sw_hsqueue then
if S.isDruid and S.useSpellQueueEvent then
S.maulQueued, S.maulSeenCurrent =
ReconcileQueued(S.maulQueued, S.cachedMaulSlots, S.maulSeenCurrent)
end
if S.maulQueued and S.isDruid then
curR, curG, curB = 1.0, 0.55, 0.0 -- orange for druid maul queue
r, g, b = 1.0, 0.55, 0.0 -- orange for druid maul queue
elseif S.isWarrior then
local hs, cl = IsHSOrCleaveQueued()
if cl then
curR, curG, curB = 0.2, 0.9, 0.2
r, g, b = 0.2, 0.9, 0.2
elseif hs then
curR, curG, curB = 0.9, 0.9, 0.2
r, g, b = 0.9, 0.9, 0.2
end
end
end
if S.outOfRange[MAINHAND] then r, g, b = orR, orG, orB end
mainhand:SetStatusBarColor(r, g, b, mhA)
-- Render MH
if S.outOfRange[MAINHAND] then
curR, curG, curB = orR, orG, orB
if S.outOfRange[OFFHAND] then
offhand:SetStatusBarColor(orR, orG, orB, ohA)
else
offhand:SetStatusBarColor(ohR, ohG, ohB, ohA)
end
if S.mhActive then
local progress = 1 - (S.mhTimer / S.mhTimerMax)
pfUI.swingtimer.mainhand:SetValue(progress)
if S.mhTimer <= 0 then
-- Timer expired: hide marker (no gap at right edge)
if S.lastMhMarkerX ~= -1 then
S.lastMhMarkerX = -1
pfUI.swingtimer.mainhand.marker:Hide()
pfUI.swingtimer.mainhand.markerGlowL:Hide()
pfUI.swingtimer.mainhand.markerGlowR:Hide()
end
local a
if ranged.warn then
r, g, b, a = rwR, rwG, rwB, rwA
else
r, g, b, a = raR, raG, raB, raA
end
if S.outOfRange[RANGED] then r, g, b = orR, orG, orB end
ranged:SetStatusBarColor(r, g, b, a)
end
local function StopBar(bar)
if bar.wake then bar.wake:Cancel() end
bar.wake, bar.active, bar.graced = nil, nil, nil
bar.timer:Disable()
bar:Hide()
end
-- a melee bar that is due waits for the next swing; it hides after the
-- grace unless auto attack goes on, or on the event that stops it later
local function MeleeGrace(bar)
bar.graced = true
if not S.inCombat or not S.autoAttackActive then StopBar(bar) end
end
local function MeleeDue(bar)
bar.edge:Hide()
bar.wake = C_Timer.NewTimer(GRACE, function() MeleeGrace(bar) end)
end
local function RangedDue(bar)
StopBar(bar)
end
-- the hunter's reload is over: the deadzone grows out in the warn color
local function HunterDeadzone(bar)
local finish = bar.swing:GetEndTime()
bar.phase:SetTimeFromEnd(finish, DEADZONE)
bar:SetTimerDuration(bar.phase, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
bar.warn = true
UpdateColors()
bar.wake = C_Timer.NewTimer(finish - GetTime(), function() RangedDue(bar) end)
end
-- Restarts a bar from a PLAYER_SWING reset. `remaining` can be shorter than
-- the weapon speed (parry haste, ranged wind-up), so the bar length stays
-- the full swing and the fill starts part-way along.
local function StartBar(bar, remaining, speed, due)
if bar.wake then bar.wake:Cancel() end
local now = GetTime()
bar.swing:SetTimeFromEnd(now + remaining, math.max(speed, remaining))
bar.active, bar.graced, bar.warn = true, nil, nil
if speed > 0 then bar.speed:SetFormattedText("%.2f", speed) end
if bar.phase then
if remaining > DEADZONE then
bar.phase:SetTimeFromEnd(now + remaining - DEADZONE, math.max(speed, remaining) - DEADZONE)
bar:SetTimerDuration(bar.phase, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
bar.wake = C_Timer.NewTimer(remaining - DEADZONE, function() HunterDeadzone(bar) end)
else
local mhMarkerX = progress * sw_width
if mhMarkerX < 1 then mhMarkerX = 1 end
if mhMarkerX > sw_width - 2 then mhMarkerX = sw_width - 2 end
if mhMarkerX ~= S.lastMhMarkerX then
S.lastMhMarkerX = mhMarkerX
pfUI.swingtimer.mainhand.marker:SetPoint("LEFT", pfUI.swingtimer.mainhand, "LEFT", mhMarkerX - 1, 0)
pfUI.swingtimer.mainhand.markerGlowL:SetPoint("RIGHT", pfUI.swingtimer.mainhand.marker, "LEFT", 0, 0)
pfUI.swingtimer.mainhand.markerGlowR:SetPoint("LEFT", pfUI.swingtimer.mainhand.marker, "RIGHT", 0, 0)
pfUI.swingtimer.mainhand.marker:Show()
pfUI.swingtimer.mainhand.markerGlowL:Show()
pfUI.swingtimer.mainhand.markerGlowR:Show()
end
HunterDeadzone(bar)
end
pfUI.swingtimer.mainhand:SetStatusBarColor(curR, curG, curB, mhA)
if sw_showtext then
pfUI.swingtimer.mainhand.text:SetFormattedText("%.1f", math.floor(S.mhTimer * 10) / 10)
end
if sw_showspeed and S.mhSpeed > 0 then
pfUI.swingtimer.mainhand.speed:SetFormattedText("%.2f", S.mhSpeed)
end
anyActive = true
else
bar.wake = C_Timer.NewTimer(remaining, function() due(bar) end)
end
-- Render OH
if sw_showoh and S.ohActive then
local progress = 1 - (S.ohTimer / S.ohTimerMax)
pfUI.swingtimer.offhand:SetValue(progress)
if S.outOfRange[OFFHAND] then
pfUI.swingtimer.offhand:SetStatusBarColor(orR, orG, orB, ohA)
else
pfUI.swingtimer.offhand:SetStatusBarColor(ohR, ohG, ohB, ohA)
end
if S.ohTimer <= 0 then
if S.lastOhMarkerX ~= -1 then
S.lastOhMarkerX = -1
pfUI.swingtimer.offhand.marker:Hide()
pfUI.swingtimer.offhand.markerGlowL:Hide()
pfUI.swingtimer.offhand.markerGlowR:Hide()
end
else
local ohMarkerX = progress * sw_width
if ohMarkerX < 1 then ohMarkerX = 1 end
if ohMarkerX > sw_width - 2 then ohMarkerX = sw_width - 2 end
if ohMarkerX ~= S.lastOhMarkerX then
S.lastOhMarkerX = ohMarkerX
pfUI.swingtimer.offhand.marker:SetPoint("LEFT", pfUI.swingtimer.offhand, "LEFT", ohMarkerX - 1, 0)
pfUI.swingtimer.offhand.markerGlowL:SetPoint("RIGHT", pfUI.swingtimer.offhand.marker, "LEFT", 0, 0)
pfUI.swingtimer.offhand.markerGlowR:SetPoint("LEFT", pfUI.swingtimer.offhand.marker, "RIGHT", 0, 0)
pfUI.swingtimer.offhand.marker:Show()
pfUI.swingtimer.offhand.markerGlowL:Show()
pfUI.swingtimer.offhand.markerGlowR:Show()
end
end
if sw_showtext then
pfUI.swingtimer.offhand.text:SetFormattedText("%.1f", math.floor(S.ohTimer * 10) / 10)
end
if sw_showspeed and S.ohSpeed > 0 then
pfUI.swingtimer.offhand.speed:SetFormattedText("%.2f", S.ohSpeed)
end
anyActive = true
elseif not sw_showoh then
pfUI.swingtimer.offhand:Hide()
UpdateColors()
if bar.shown then
bar.edge:SetShown(bar.showmarker)
if sw_showtext then bar.timer:Enable() end
bar:Show()
end
end
-- Render Ranged
if sw_showranged and S.raActive then
local remaining = S.raTimer
local r, g, b, a = raR, raG, raB, raA
if isHunter then
local DEADZONE = 0.5
if remaining > DEADZONE then
pfUI.swingtimer.ranged:SetValue((remaining - DEADZONE) / (S.raTimerMax - DEADZONE))
else
r, g, b, a = rwR, rwG, rwB, rwA
pfUI.swingtimer.ranged:SetValue(1 - (remaining / DEADZONE))
end
if sw_showtext then
if remaining <= 0.5 then
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
else
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor((remaining - 0.5) * 10) / 10)
end
end
else
pfUI.swingtimer.ranged:SetValue(1 - (remaining / S.raTimerMax))
if sw_showtext then
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
end
end
if S.outOfRange[RANGED] then
r, g, b = orR, orG, orB
end
pfUI.swingtimer.ranged:SetStatusBarColor(r, g, b, a)
if sw_showspeed and S.raSpeed > 0 then
pfUI.swingtimer.ranged.speed:SetFormattedText("%.2f", S.raSpeed)
end
local raProgress = 1 - (S.raTimer / S.raTimerMax)
local raMarkerX = raProgress * sw_width
if raMarkerX < 1 then raMarkerX = 1 end
if raMarkerX > sw_width - 2 then raMarkerX = sw_width - 2 end
if raMarkerX ~= S.lastRaMarkerX then
S.lastRaMarkerX = raMarkerX
if not isHunter then
pfUI.swingtimer.ranged.marker:SetPoint("LEFT", pfUI.swingtimer.ranged, "LEFT", raMarkerX - 1, 0)
pfUI.swingtimer.ranged.marker:Show()
pfUI.swingtimer.ranged.markerGlowL:SetPoint("RIGHT", pfUI.swingtimer.ranged.marker, "LEFT", 0, 0)
pfUI.swingtimer.ranged.markerGlowR:SetPoint("LEFT", pfUI.swingtimer.ranged.marker, "RIGHT", 0, 0)
pfUI.swingtimer.ranged.markerGlowL:Show()
pfUI.swingtimer.ranged.markerGlowR:Show()
else
pfUI.swingtimer.ranged.marker:Hide()
pfUI.swingtimer.ranged.markerGlowL:Hide()
pfUI.swingtimer.ranged.markerGlowR:Hide()
end
end
anyActive = true
elseif not sw_showranged then
pfUI.swingtimer.ranged:Hide()
end
local function StartMH(remaining)
UpdateWeaponSpeeds()
StartBar(mainhand, remaining, S.mhSpeed, MeleeDue)
end
if not anyActive then
if not pfUI.swingtimer.mainhand:IsShown()
and not pfUI.swingtimer.offhand:IsShown()
and not pfUI.swingtimer.ranged:IsShown() then
this:Hide()
end
end
end)
local function StartOH(remaining)
UpdateWeaponSpeeds()
-- The off-hand attack time stays set with a shield equipped
if S.ohSpeed <= 0 then return end
StartBar(offhand, remaining, S.ohSpeed, MeleeDue)
end
local function StartRanged(remaining)
if not sw_showranged then return end
UpdateWeaponSpeeds()
if S.raSpeed <= 0 then return end
StartBar(ranged, remaining, S.raSpeed, RangedDue)
end
-- melee bars waiting past their grace hide once auto attack stops
local function StopWaitingBars()
if mainhand.graced then StopBar(mainhand) end
if offhand.graced then StopBar(offhand) end
end
local function ResetAll()
StopBar(mainhand)
StopBar(offhand)
StopBar(ranged)
end
local events = CreateFrame("Frame")
events:RegisterEvent("PLAYER_SWING")
@@ -658,6 +423,7 @@ pfUI:RegisterModule("swingtimer", function ()
events:RegisterEvent("PLAYER_REGEN_DISABLED")
events:RegisterEvent("PLAYER_REGEN_ENABLED")
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
events:RegisterEvent("ACTIONBAR_UPDATE_STATE")
events:RegisterEvent("PLAYER_DEAD")
events:RegisterUnitEvent("UNIT_SPELLCAST_SENT", "player")
events:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player")
@@ -682,6 +448,7 @@ pfUI:RegisterModule("swingtimer", function ()
-- arg1 = swingType, arg2 = isInRange, arg3 = checksRange; no check
-- (no target, nothing to attack, no weapon) is not out of range
S.outOfRange[arg1] = arg3 and not arg2
UpdateColors()
elseif event == "UNIT_SPELLCAST_SENT" then
-- arg4 = spellID: an on-next-swing ability (HS / Cleave / Maul) queued
@@ -689,6 +456,7 @@ pfUI:RegisterModule("swingtimer", function ()
if kind then
S.useSpellQueueEvent = true
SetQueuedKind(kind)
UpdateColors()
end
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
@@ -696,6 +464,7 @@ pfUI:RegisterModule("swingtimer", function ()
-- the queue, so drop the queued color
if IsOnSwingSpell(arg3) then
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
UpdateColors()
end
elseif event == "SPELL_QUEUE_EVENT" then
@@ -707,12 +476,17 @@ pfUI:RegisterModule("swingtimer", function ()
elseif eventCode == ON_SWING_QUEUE_POPPED then
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
end
UpdateColors()
elseif event == "ACTIONBAR_UPDATE_STATE" then
if sw_hsqueue then UpdateColors() end
elseif event == "START_AUTOATTACK" then
S.autoAttackActive = true
elseif event == "STOP_AUTOATTACK" then
S.autoAttackActive = false
StopWaitingBars()
elseif event == "PLAYER_ENTERING_WORLD" then
local class = UnitClassBase("player")
@@ -725,14 +499,8 @@ pfUI:RegisterModule("swingtimer", function ()
-- arg1 = changed slot; only weapon slots (main/off/ranged) affect swing speed
if not INVSLOTS_EQUIPABLE_IN_COMBAT[arg1] then return end
UpdateWeaponSpeeds()
if S.ohSpeed == 0 then
S.ohActive = false
pfUI.swingtimer.offhand:Hide()
end
if S.raSpeed == 0 then
S.raActive = false
pfUI.swingtimer.ranged:Hide()
end
if S.ohSpeed == 0 then StopBar(offhand) end
if S.raSpeed == 0 then StopBar(ranged) end
elseif event == "ACTIONBAR_SLOT_CHANGED" then
RebuildQueueSlotCache()
@@ -746,12 +514,22 @@ pfUI:RegisterModule("swingtimer", function ()
S.hsQueued = false
S.cleaveQueued = false
S.maulQueued = false
UpdateColors()
StopWaitingBars()
elseif event == "PLAYER_DEAD" then
ResetAll()
end
end)
local function Remaining(bar)
return bar.active and bar.swing:GetRemainingDuration() or 0
end
local function Progress(bar)
return bar.active and bar.swing:GetElapsedPercent() or 0
end
-- -------------------------------------------------------------------------
-- Public API for external addons (e.g. SCRM)
-- Only available when the swingtimer module is loaded.
@@ -763,9 +541,9 @@ pfUI:RegisterModule("swingtimer", function ()
-- -------------------------------------------------------------------------
pfUI.swingtimer.api = {
-- Remaining time in seconds until next swing
GetMHTimer = function() return S.mhTimer end,
GetOHTimer = function() return S.ohTimer end,
GetRangedTimer = function() return S.raTimer end,
GetMHTimer = function() return Remaining(mainhand) end,
GetOHTimer = function() return Remaining(offhand) end,
GetRangedTimer = function() return Remaining(ranged) end,
-- Weapon speed (full swing duration in seconds)
GetMHSpeed = function() return S.mhSpeed end,
@@ -773,20 +551,14 @@ pfUI:RegisterModule("swingtimer", function ()
GetRangedSpeed = function() return S.raSpeed end,
-- Whether each timer is currently running
IsMHActive = function() return S.mhActive end,
IsOHActive = function() return S.ohActive end,
IsRangedActive = function() return S.raActive end,
IsMHActive = function() return mainhand.active == true end,
IsOHActive = function() return offhand.active == true end,
IsRangedActive = function() return ranged.active == true end,
-- Swing progress 0.0 (just reset) to 1.0 (about to swing)
GetMHProgress = function()
return S.mhActive and S.mhTimerMax > 0 and (1 - S.mhTimer / S.mhTimerMax) or 0
end,
GetOHProgress = function()
return S.ohActive and S.ohTimerMax > 0 and (1 - S.ohTimer / S.ohTimerMax) or 0
end,
GetRangedProgress = function()
return S.raActive and S.raTimerMax > 0 and (1 - S.raTimer / S.raTimerMax) or 0
end,
GetMHProgress = function() return Progress(mainhand) end,
GetOHProgress = function() return Progress(offhand) end,
GetRangedProgress = function() return Progress(ranged) end,
-- Warrior HS/Cleave queue state
IsHSQueued = function() return S.hsQueued end,
@@ -804,4 +576,5 @@ pfUI:RegisterModule("swingtimer", function ()
if sw_showranged then EnableRangeCheck(RANGED) end
UpdateWeaponSpeeds()
end)
UpdateColors()
end)
+2 -6
View File
@@ -801,11 +801,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
HookAddon("SortBags", function()
if C.thirdparty.sortbags.enable == "0" then return end
local sort = CreateFrame("Frame", nil)
sort:RegisterEvent("PLAYER_ENTERING_WORLD")
sort:SetScript("OnEvent", function()
this:UnregisterAllEvents()
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
pfUI.thirdparty.RegisterBagSort("SortBags",
function()
SortBags()
@@ -899,7 +895,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
-- trigger the event whenever SuperMacro got an update
hooksecurefunc("SM_UpdateActionSpell", function()
for slot=1,120 do pfUI.bars.update[slot] = true end
for slot=1,120 do pfUI.bars.QueueSlot(slot) end
end)
end)
+45 -32
View File
@@ -142,11 +142,7 @@ pfUI:RegisterModule("thirdparty", function()
HookAddon("MrPlow", function()
if C.thirdparty.mrplow.enable == "0" then return end
local sort = CreateFrame("Frame", nil)
sort:RegisterEvent("PLAYER_ENTERING_WORLD")
sort:SetScript("OnEvent", function()
this:UnregisterAllEvents()
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", function()
local MrPlowL = AceLibrary and AceLibrary("AceLocale-2.2"):new("MrPlow")
if not (MrPlowL and MrPlowL["Bank"]) then return end
@@ -373,46 +369,63 @@ pfUI:RegisterModule("thirdparty", function()
-- DPSMate Damage Meter
-- Vanilla: https://github.com/Geigerkind/DPSMate
-- TBC: https://github.com/Geigerkind/DPSMateTBC
HookAddon("DPSMate", function()
-- DPSMate builds its windows on VARIABLES_LOADED, each named after its
-- settings entry, so DPSMate_DPSMate exists only while the first window
-- keeps the default name "DPSMate"
if not DPSMate_DPSMate then return end
-- Repacks ship DPSMate under other folder names, so this waits on login and
-- checks DPSMate's globals instead of HookAddon's folder lookup.
EventUtil.ContinueOnPlayerLogin(function()
if not DPSMate or not DPSMateSettings then return end
local docktable = { "dpsmate", "DPSMate", "DPSMate_DPSMate",
-- DPSMate names each window "DPSMate_<name>" after its settings entry and
-- the user can rename it at any time, so the first window is resolved on
-- every call
local function window()
local first = DPSMateSettings.windows and DPSMateSettings.windows[1]
return first and _G["DPSMate_"..first.name]
end
local function place(anchor, width)
local frame = window()
if not frame then return end
local name = frame:GetName()
frame:ClearAllPoints()
frame:SetPoint("TOPLEFT", pfUI.chat.right, anchor, 0, 0)
frame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, 0)
frame:SetWidth(width)
_G[name.."_ScrollFrame"]:ClearAllPoints()
_G[name.."_ScrollFrame"]:SetWidth(width)
_G[name.."_ScrollFrame"]:SetPoint("TOPLEFT", _G[name.."_Head"], "BOTTOMLEFT", 0, 0)
_G[name.."_ScrollFrame"]:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, pfUI.panel.right:GetHeight())
_G[name.."_ScrollFrame_Child"]:SetWidth(width)
_G[name.."_Resize"]:Hide()
end
local docktable = { "dpsmate", "DPSMate", "DPSMate",
function() -- single
DPSMate_DPSMate:ClearAllPoints()
DPSMate_DPSMate:SetAllPoints(pfUI.chat.right)
DPSMate_DPSMate:SetWidth(pfUI.chat.right:GetWidth())
DPSMate_DPSMate_ScrollFrame:ClearAllPoints()
DPSMate_DPSMate_ScrollFrame:SetWidth(pfUI.chat.right:GetWidth())
DPSMate_DPSMate_ScrollFrame:SetPoint("TOPLEFT", DPSMate_DPSMate_Head, "BOTTOMLEFT", 0, 0)
DPSMate_DPSMate_ScrollFrame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, pfUI.panel.right:GetHeight())
DPSMate_DPSMate_ScrollFrame_Child:SetWidth(pfUI.chat.right:GetWidth())
DPSMate_DPSMate_Resize:Hide()
place("TOPLEFT", pfUI.chat.right:GetWidth())
end,
function() -- dual
DPSMate_DPSMate:ClearAllPoints()
DPSMate_DPSMate:SetPoint("TOPLEFT", pfUI.chat.right, "TOP", 0, 0)
DPSMate_DPSMate:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, 0)
DPSMate_DPSMate:SetWidth(pfUI.chat.right:GetWidth() / 2)
DPSMate_DPSMate_ScrollFrame:ClearAllPoints()
DPSMate_DPSMate_ScrollFrame:SetWidth(pfUI.chat.right:GetWidth() / 2)
DPSMate_DPSMate_ScrollFrame:SetPoint("TOPLEFT", DPSMate_DPSMate_Head, "BOTTOMLEFT", 0, 0)
DPSMate_DPSMate_ScrollFrame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, pfUI.panel.right:GetHeight())
DPSMate_DPSMate_ScrollFrame_Child:SetWidth(pfUI.chat.right:GetWidth() / 2)
DPSMate_DPSMate_Resize:Hide()
place("TOP", pfUI.chat.right:GetWidth() / 2)
end,
function() -- show
DPSMate_DPSMate:Show()
local frame = window()
if frame then frame:Show() end
end,
function() -- hide
DPSMate_DPSMate:Hide()
local frame = window()
if frame then frame:Hide() end
end
}
pfUI.thirdparty.meters:RegisterMeter("damage", docktable)
-- DPSMate rebuilds its windows at their saved positions whenever its
-- settings change, so put the docked one back afterwards
hooksecurefunc(DPSMate, "InitializeFrames", function()
if pfUI.thirdparty.meters.damage == docktable then
pfUI.thirdparty.meters:Resize()
end
end)
if C.thirdparty.dpsmate.skin == "1" then
if DPSMateSettings then
-- set DPSMate appearance to match pfUI
+1 -1
View File
@@ -216,7 +216,7 @@ pfUI:RegisterModule("tooltip", function ()
icon.stacks:SetFont(C.tooltip.font_tooltip, C.tooltip.font_tooltip_size, "OUTLINE")
icon.stacks:SetTextColor(1, 1, 1, 1)
icon.cd = CreateCooldown(icon, "tooltip")
icon.cd = CreateCooldown(icon, "aura", C.tooltip, "aura_timers")
return icon
end
+1 -1
View File
@@ -113,7 +113,7 @@ pfUI:RegisterModule("totems", function ()
self.bar[i].cdbg = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i])
self.bar[i].cdbg:SetSize(self.iconsize - 3, self.iconsize - 3)
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
self.bar[i].cd = self.bar[i].cd or CreateCooldown(self.bar[i].cdbg, "totem", "pfTotemsBar"..i.."Cooldown")
self.bar[i].cd = self.bar[i].cd or CreateCooldown(self.bar[i].cdbg, "aura", C.totems, "timers", "pfTotemsBar"..i.."Cooldown")
self.bar[i]:RegisterForClicks("LeftButtonUp", "RightButtonUp")
self.bar[i]:SetScript("OnClick", self.OnClick)
+2 -1
View File
@@ -38,7 +38,8 @@ pfUI:RegisterModule("tracking", function ()
pfUI.tracking.pulse = pfUI.tracking.icon:CreateAnimationGroup()
pfUI.tracking.pulse:SetLooping("BOUNCE")
local fade = pfUI.tracking.pulse:CreateAnimation("Alpha")
fade:SetChange(-.5)
fade:SetFromAlpha(1)
fade:SetToAlpha(.5)
fade:SetDuration(.5)
fade:SetSmoothing("IN_OUT")
+1 -27
View File
@@ -180,33 +180,7 @@ pfUI:RegisterModule("unitxp", function ()
CreateTargetIndicators()
-- Also try on PLAYER_ENTERING_WORLD in case target frame wasn't ready
local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
initFrame:RegisterEvent("PLAYER_LOGOUT")
initFrame:SetScript("OnEvent", function()
-- Handle shutdown to prevent crash 132
if event == "PLAYER_LOGOUT" then
this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
-- Stop indicator OnUpdate scripts
if pfUI.uf and pfUI.uf.target then
if pfUI.uf.target.behindIndicator then
pfUI.uf.target.behindIndicator:SetScript("OnUpdate", nil)
end
if pfUI.uf.target.distanceIndicator then
pfUI.uf.target.distanceIndicator:SetScript("OnUpdate", nil)
end
-- free-frame mode polls from its own scanner frame, not the indicator
if pfUI.uf.target.distanceScanner then
pfUI.uf.target.distanceScanner:SetScript("OnUpdate", nil)
end
end
return
end
CreateTargetIndicators()
this:UnregisterAllEvents()
end)
EventUtil.RegisterOnceFrameEventAndCallback("PLAYER_ENTERING_WORLD", CreateTargetIndicators)
-- OS Notification Support
if C.unitframes.unitxp_notify == "1" then
+3 -3
View File
@@ -37,15 +37,15 @@ pfUI:RegisterModule("unusable", function ()
-- requirement changes (level, proficiency/skill, required spell, reputation,
-- honor rank) don't touch the bags; queue a full rebag through pfUI.bag's
-- coalesced delay, whose UpdateSlot resets the tint before we reapply it
-- batched updates, whose UpdateSlot resets the tint before we reapply it
local function Refresh()
for _, bag in pairs(pfUI.BACKPACK) do
pfUI.bag.delay.UpdateBag[bag] = true
pfUI.bag:QueueBagUpdate(bag)
end
-- the bank frame is only created on first bank visit
if pfUI.bag.left and pfUI.bag.left:IsShown() then
for _, bag in pairs(pfUI.BANK) do
pfUI.bag.delay.UpdateBag[bag] = true
pfUI.bag:QueueBagUpdate(bag)
end
end
end
+26 -22
View File
@@ -69,11 +69,25 @@ pfUI:RegisterModule("xpbar", function ()
self:SetAlpha(1)
end
-- (Re)arm the auto-hide: fade the bar out after delay seconds unless it is
-- always shown or under the cursor
local function ScheduleFade(self, delay)
if self.fadeTimer then self.fadeTimer:Cancel() end
self.fadeTimer = nil
if self.always or self.hover then return end
self.fadeTimer = C_Timer.NewTimer(delay, function()
self.fadeTimer = nil
if self.hover or self:GetAlpha() == 0 then return end
self.fade:Play()
end)
end
local function OnLeave(self)
local self = self or this
self.hover = nil
self.tick = GetTime() + 3.00
if self.text_mouse == "1" then self.bar.text:Hide() end
ScheduleFade(self, 3)
GameTooltip:Hide()
end
@@ -90,6 +104,9 @@ local function OnEnter(self)
end
self.hover = true
if self.fadeTimer then self.fadeTimer:Cancel() end
self.fadeTimer = nil
if self.text_mouse == "1" then self.bar.text:Show() end
Reveal(self)
if mode == "XP" then
@@ -187,21 +204,6 @@ local function OnEnter(self)
GameTooltip:Show()
end
local function OnUpdate(self)
local self = self or this
if self.text_mouse == "1" then
self.bar.text:SetShown(self.hover)
end
if self.always then return end
-- Hover comes from OnEnter/OnLeave, the same mouse focus that reveals the
-- bar, so the fade can't restart under the cursor
if self:GetAlpha() == 0 or self.hover or self.fade:IsPlaying() then return end
if ( self.tick or 1) > GetTime() then return end
self.fade:Play()
end
local function OnEvent(self)
local self = self or this
@@ -248,7 +250,7 @@ end
if ex then text = "%s: %s%% (%s%% %s)" end
self.bar.text:SetFormattedText(text, T["Experience"], xpperc, experc, T["Rested"])
self.tick = GetTime() + self.timeout
ScheduleFade(self, self.timeout)
if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then
OnEnter(self)
end
@@ -266,7 +268,7 @@ end
local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0
self.bar.text:SetFormattedText(text, T["Pet Experience"], xpperc)
self.tick = GetTime() + self.timeout
ScheduleFade(self, self.timeout)
return
elseif mode == "REP" then
self.restedbar:Hide()
@@ -294,7 +296,7 @@ end
local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender)
self.bar.text:SetFormattedText(text, name, perc, standing)
self.tick = GetTime() + self.timeout
ScheduleFade(self, self.timeout)
return
end
end
@@ -302,6 +304,9 @@ end
self.bar:SetStatusBarColor(.5,.5,.5,1)
self.bar:SetMinMaxValues(0, 1)
self.bar:SetValue(0)
-- nothing to show: hide now unless an earlier update is still on display
if not self.fadeTimer then ScheduleFade(self, 0) end
end
local function CreateBar(t)
@@ -359,7 +364,7 @@ end
b.bar.text:SetJustifyH("CENTER")
b.bar.text:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
b.bar.text:SetShown(b.text == "1")
b.bar.text:SetShown(b.text == "1" and b.text_mouse ~= "1")
b.restedbar = b.restedbar or CreateFrame("StatusBar", nil, b)
b.restedbar:SetStatusBarTexture(pfUI.media[C.panel.xp.texture])
@@ -376,10 +381,10 @@ end
if not b.fade then
b.fade = b:CreateAnimationGroup()
b.fade:SetToFinalAlpha(true)
local fade = b.fade:CreateAnimation("Alpha")
fade:SetChange(-1)
fade:SetDuration(.3)
b.fade:SetScript("OnFinished", function() b:SetAlpha(0) end)
end
b:RegisterEvent("FACTION_STANDING_CHANGED")
@@ -392,7 +397,6 @@ end
b:RegisterEvent("PLAYER_LEVEL_UP")
b:RegisterEvent("UPDATE_FACTION")
b:SetScript("OnUpdate", OnUpdate)
b:SetScript("OnEvent", OnEvent)
b:SetScript("OnEnter", OnEnter)
b:SetScript("OnLeave", OnLeave)
+21 -10
View File
@@ -345,14 +345,16 @@ function pfUI:RegisterSkin(name, func, arg3)
end
end
-- an error in one module or skin goes to the error handler without
-- stopping the ones that load after it
function pfUI:LoadModule(m)
setfenv(pfUI.module[m], pfUI:GetEnvironment())
pfUI.module[m]()
securecallfunction(pfUI.module[m])
end
function pfUI:LoadSkin(s)
setfenv(pfUI.skin[s], pfUI:GetEnvironment())
pfUI.skin[s]()
securecallfunction(pfUI.skin[s])
end
pfUI:SetScript("OnEvent", function()
@@ -390,6 +392,10 @@ pfUI:SetScript("OnEvent", function()
SetupConfigEnvironment()
pfUI:UpdateFonts()
-- scale UIParent before the modules size their borders from it
pfUI.api.UpdateUIScale()
pfUI.events:RegisterFrameEventAndCallback("UI_SCALE_CHANGED", pfUI.api.UpdateUIScale)
-- load modules
for _, m in pairs(this.modules) do
if not ( pfUI_config["disabled"] and pfUI_config["disabled"][m] == "1" ) then
@@ -455,15 +461,20 @@ message = function(msg)
end
print = print or message
error = function(msg)
if PF_DEBUG_MODE then message(debugstack()) end
if string.find(msg, "AddOns\\pfUI") then
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555".. (msg or "nil" ))
elseif not pfUI_config or (pfUI_config.global and pfUI_config.global.errors == "1") then
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555".. (msg or "nil" ))
-- Errors print to chat: pfUI's own always, other addons' when the errors
-- option is on. In debug mode the stack follows, starting at the code that
-- raised the error rather than at this handler.
seterrorhandler(function(msg)
msg = tostring(msg)
if PF_DEBUG_MODE then
local height = GetErrorCallstackHeight()
message(height and debugstack(GetCallstackHeight() - height + 1) or debugstack(2))
end
end
seterrorhandler(error)
if string.find(msg, "AddOns\\pfUI") or not pfUI_config
or (pfUI_config.global and pfUI_config.global.errors == "1") then
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555" .. msg)
end
end)
function pfUI.SetupCVars()
ClearTutorials()
+1 -1
View File
@@ -199,7 +199,7 @@ pfUI:RegisterSkin("Character", function ()
-- name only while that global is unset, so the global is rebound by
-- hand; the Model starts hidden and nothing reaches it after that
local cdname = "Character"..slotName.."Cooldown"
_G[cdname] = CreateCooldown(frame, "blizzard", cdname)
_G[cdname] = CreateCooldown(frame, "spell", C.cooldown, "blizzard_timers", cdname)
CreateFontString(frame, "scoreText", "OVERLAY", 12)
frame.scoreText:SetPoint("TOPRIGHT", 0, 0)
+4 -6
View File
@@ -5,10 +5,9 @@ pfUI:RegisterSkin("Mailbox", function ()
-- Compatibility
local StationeryBackgroundLeft, StationeryBackgroundRight
do -- SendMailFrame
local skin = CreateFrame("Frame")
skin:SetScript("OnEvent", function()
this:UnregisterEvent("MAIL_SHOW")
if not this._Mail then
local mailLoaded
EventUtil.RegisterOnceFrameEventAndCallback("MAIL_SHOW", function()
if not mailLoaded then
StripTextures(SendMailPackageButton)
SkinButton(SendMailPackageButton, nil, nil, nil, nil, true)
@@ -26,10 +25,9 @@ pfUI:RegisterSkin("Mailbox", function ()
end)
end
end)
skin:RegisterEvent("MAIL_SHOW")
HookAddon("Mail", function()
skin._Mail = true
mailLoaded = true
for i = 1, 21 do
local button = _G["MailAttachment"..i]
StripTextures(button)
+43 -1
View File
@@ -38,9 +38,51 @@ pfUI:RegisterSkin("Options - Interface", function ()
elseif ctype == "EditBox" then
StripTextures(control, nil, "BACKGROUND")
CreateBackdrop(control, nil, true)
else
elseif ctype == "Button" then
-- key binding buttons: UIPanelButtonTemplate2's slices are BACKGROUND
StripTextures(control, nil, "BACKGROUND")
SkinButton(control)
elseif _G[control:GetName() .. "Button"] then
SkinDropDown(control, nil, nil, nil, true)
end
-- a control type without a branch stays unskinned; erroring here would
-- stop the loop and leave every later control unskinned too
i = i + 1
end
-- the pages scroll with retail's MinimalScrollBar, not the old named
-- UIPanelScrollBar, so SkinScrollbar does not fit. Its steppers and thumb set
-- their atlas again on every state change, so their art is hidden, not cleared
i = 1
while _G["ClassicAPIOptionsScrollBar" .. i] do
local bar = _G["ClassicAPIOptionsScrollBar" .. i]
local thumb = bar.Track.Thumb
StripTextures(bar)
bar.Background:Hide()
for _, stepper in ipairs({ bar.Back, bar.Forward }) do
stepper.Texture:Hide()
stepper.Overlay:SetAlpha(0)
end
SkinArrowButton(bar.Back, "up")
SkinArrowButton(bar.Forward, "down")
thumb.Begin:Hide()
thumb.Middle:Hide()
thumb.End:Hide()
thumb.thumbTexture:Hide()
bar.bg = CreateFrame("Frame", nil, bar)
bar.bg:SetPoint("TOPLEFT", bar.Back, "BOTTOMLEFT", 0, -3)
bar.bg:SetPoint("BOTTOMRIGHT", bar.Forward, "TOPRIGHT", 0, 3)
CreateBackdrop(bar.bg, nil, true)
bar.thumb = bar.bg:CreateTexture(nil, "ARTWORK")
bar.thumb:SetColorTexture(.8, .8, .8, .8)
bar.thumb:SetPoint("TOP", thumb, "TOP", 0, -4)
bar.thumb:SetPoint("BOTTOM", thumb, "BOTTOM", 0, 4)
bar.thumb:SetPoint("LEFT", bar.bg, "LEFT", 1, 0)
bar.thumb:SetPoint("RIGHT", bar.bg, "RIGHT", -1, 0)
i = i + 1
end
+1 -1
View File
@@ -39,7 +39,7 @@ pfUI:RegisterSkin("Spellbook", function ()
-- the global is rebound by hand; the Model starts hidden and nothing
-- reaches it after that
local cdname = "SpellButton"..i.."Cooldown"
_G[cdname] = CreateCooldown(button, "blizzard", cdname)
_G[cdname] = CreateCooldown(button, "spell", C.cooldown, "blizzard_timers", cdname)
_G["SpellButton"..i.."SubSpellName"]:SetTextColor(1, 1, 1)
_G["SpellButton"..i.."AutoCastable"]:SetTexture("Interface\\Buttons\\UI-AutoCastableOverlay")