38 Commits
Author SHA1 Message Date
Brues 964c316cdc Replace Nampower combat events with ClassicAPI events
Read casts from UNIT_SPELLCAST_* and heal amounts from
COMBAT_LOG_EVENT_UNFILTERED in libpredict, swingtimer and the prowl
page switch. Drop libdebuff's AURA_CAST/DEBUFF_* bookkeeping and hook
tables, which nothing read: its aura readers already use C_UnitAuras.
Drop the unitframe BUFF_ADDED_OTHER start-time tracker, since
C_UnitAuras stamps an expiration for auras seen being applied.
2026-10-23 21:27:28 -05:00
Brues b73ed88346 Split chat faction and race icons into separate options 2026-10-23 19:33:08 -05:00
Brues b374a0914a Color equipment flyout borders by item quality 2026-10-23 17:27:37 -05:00
Brues aaddeb120a Drive innervate callout from the combat log
Read Innervate casts from COMBAT_LOG_EVENT_UNFILTERED instead of
Nampower's AURA_CAST events, taking the target name from the event.
Schedule the ready callout once Innervate's own cooldown appears on
SPELL_UPDATE_COOLDOWN; at cast time GetSpellCooldown reports only the GCD.
2026-10-23 17:03:23 -05:00
Brues 1ad78d43e9 nil 2026-10-23 16:21:05 -05:00
Brues dde4a5aa2d Merge chat class color and player level passes into one loop 2026-10-23 12:51:04 -05:00
Brues e7e1382693 Port unitframe and actionbar performance fixes from Duciel/pfUI@45f8ca6
Unitframes: only set the unit attribute when it changes, poll group frame indicators every 3s since events already cover them, and limit full refreshes on eventless frames to unit changes or once a second.

Actionbar: handle UNIT_INVENTORY_CHANGED with a light count/cooldown/usable pass instead of a full update of every button, and skip hidden buttons in the cached passes, catching them up once they become visible.
2026-10-23 12:39:07 -05:00
Brues 6aaf4f2047 Read chat sender and channel from GetCurrentChatEvent
Pull the CHAT_MSG payload once per line instead of decoding it from the
rendered text. The sender match uses arg2 rather than the name resolved
from the GUID, and channel shortening only runs on CHAT_MSG_CHANNEL with
a single anchored replacement in place of the backtracking pattern.
2026-10-23 03:12:42 -05:00
Brues 233d0dafc0 one more bump 2026-10-23 03:09:07 -05:00
Brues 95e832835e Color chat senders by GUID and drop the who-search option
The engine name-queries a chat sender before the CHAT_MSG event fires,
so GetPlayerInfoByGUID(GetCurrentChatGUID()) resolves every player
sender, including strangers in world channels. Other names in the text
still fall back to C_PlayerCache.GetPlayerInfoByName and libunitscan.

This makes the silent /who lookup for unknown classes obsolete; remove
it along with its FriendsFrame_OnEvent wrapper, config default, GUI
entry and translations.
2026-10-23 02:40:08 -05:00
Brues cae7c2d68a Guard unusable bag refresh against the bank frame not existing yet 2026-10-23 02:39:42 -05:00
Brues 8abde7beba Classify nameplate units with UnitSelectionType instead of bar color 2026-10-23 02:06:43 -05:00
Brues fbb0c4c254 Refresh unusable bag tint when item requirements change 2026-10-23 02:00:27 -05:00
Brues 42e950639f Rotate bar textures on vertical XP, rep and nameplate health bars
Vertical StatusBars stretched the bar texture's shading along the wrong axis. ClassicAPI's SetRotatesTexture turns it with the bar.
2026-10-22 23:56:51 -05:00
Brues e70c8025c2 Draw the ranged swing timer as a native center-fill StatusBar
The ranged bar faked its fills with three textures resized and re-texcoorded every frame. A StatusBar with ClassicAPI's SetFillStyle covers all of it: CENTER for hunters (reload drains to the middle, the deadzone grows back out in the warn color), STANDARD otherwise.
2026-10-22 23:43:44 -05:00
Brues 8f1322d71c Skin ClassicAPI's Interface Options frame and its option controls
SkinCollapseButton's icon no longer takes the mouse, so clicks reach the button under it.
2026-10-22 23:34:08 -05:00
Brues 935b854073 Remove dead buffdetect setting 2026-10-22 23:25:59 -05:00
Brues df16988d3f Flow unit frame auras: debuffs first on enemies, own size, spacing
Unit frame aura icons are placed by a layout pass after each aura
refresh instead of a fixed grid set at setup. Rows are as tall as
their largest icon, and the debuff block no longer uses the debuff
size to step past buff rows. (Closes #25)

- When buffs and debuffs share a corner, debuffs come first on
  neutral and hostile units.
- New Own Buff Size / Own Debuff Size (-1 = normal size) enlarge the
  player's auras, which C_UnitAuras already lists first.
- New Buff/Debuff X-Spacing and Y-Spacing set the gaps between icons
  and rows; the default of 1 keeps the old look.
- The selfdebuff tooltip remap is gone from unit frames and buffwatch:
  own auras lead the list, so the HARMFUL|PLAYER index is the HARMFUL
  index.
2026-10-22 23:24:15 -05:00
Brues 627ab8b437 Use ClassicAPI's native Cooldown frame for cooldown sweeps
ClassicAPI 1.17.1 draws the Cooldown sweep natively, as 4.3.4's
CGCooldown, so pfUI's cooldowns no longer need the indicator Model.

- Action buttons, bank slots, totems, unit frame auras and icons, and
  nameplate debuffs are Cooldown frames with CooldownFrameTemplate.
- The plain-Frame fallbacks for disabled animations are gone; the hook
  already hides the sweep with alpha 0.
- The 36px scale-and-inset realign only runs on stock indicator Models.
2026-10-22 22:47:18 -05:00
Brues c36184081a Bump min ClassicAPI to 1.17.1 2026-10-22 22:19:35 -05:00
Brues d881d73246 Use region:IsMouseOver instead of the MouseIsOver global 2026-10-22 21:37:50 -05:00
Brues b9f473c049 Test hover against the hit rect, not the bare frame rect
CreateBackdrop widens a frame's hit rect by the border, so mouse focus
and OnEnter/OnLeave cover a band that plain MouseIsOver reports as
outside. Add MouseIsOverHitRect, which passes the frame's hit rect
insets as MouseIsOver offsets, and use it at the unpadded call sites.
The XP bar's hover text follows the same OnEnter/OnLeave flag as its
fade.
2026-10-22 21:36:27 -05:00
Brues 9cb21b982b Use ClassicAPI animation groups for fades and pulses
Replace hand-rolled OnUpdate alpha loops with engine animation groups:
unit frame aggro/combat glow, tracking icon pulse, new-mail blink,
castbar fade-out, autohide, XP/rep bar fade, info box timeout and the
unlock-mode scale label. Timing no longer depends on framerate, and the
always-running glow driver and tracking OnUpdate are gone.

The XP bar tracks hover through OnEnter/OnLeave instead of MouseIsOver,
so a fade can't restart under the cursor.
2026-10-22 21:28:34 -05:00
Brues 65ad47e842 Bump min ClassicAPI to 1.17 2026-10-22 21:10:39 -05:00
Brues 50aba54289 castbar, nameplates: update the shield on mid-cast interruptible changes 2026-10-22 21:09:17 -05:00
Brues d58060b188 hoverbind: Escape clears every key bound to the button 2026-10-22 21:00:01 -05:00
Brues 340bd9be9e pagemaster: page modifiers via override bindings; hoverbind: warn on conflicts
The old pagemaster wipe block checked a setting that was migrated away and
never ran, so saved MOD-key bindings beat the paged action. Override every
key bound to ACTIONBUTTON1-12 for each enabled page modifier instead; the
saved binding set stays untouched.

Hoverbind now reports when a bind takes a key from another action, and when
an override masks the new bind.
2026-10-22 20:59:30 -05:00
Thorsten42andBrues 287a4ed46c Fix panel alignment: center-panel overlap and Quest Log/Friends/Spellbook heights (#63)
* Only move the center panel when it overlaps the left panel

The panel aligner cached the left panel's rightmost child once and then
re-anchored the center panel to that edge on every change. Children that
were hidden later (such as Spellbook skill-line tabs) kept pushing the
center panel right, and panels that already cleared the left panel were
pulled back to the left.

Recalculate the rightmost visible child while both panels are shown and
only shift the center panel when its backdrop really overlaps.

* Align Quest Log, Friends and Spellbook panel heights

Most skinned panels draw their backdrop 430px tall, 10px below the frame
top (insets 10/-10 and -30/72). Three panels did not match:

- Quest Log: frame was 440px and anchored at y -104, so it stood 10px
  taller than a panel opened beside it. It is now 430px at y -114, with
  the list/detail panes and visible rows (22) reduced to fit.
- Friends: bottom inset was 74 instead of 72.
- Spellbook: top-left inset was 12/-12 instead of 10/-10.

The Quest Log is a doublewide UI panel. Opening another panel next to
it made UpdateUIPanelPositions re-anchor it to the default (0, -104),
causing a visible jump. Its UIPanelWindows entry now carries the same
x/y offsets as the OnShow anchor.

* Let the panel manager place UI panels

Remove the OnUpdate panel aligner. UpdateUIPanelPositions already lays
out left/center/right panels, and with the overlap-only rule the
aligner no longer changed the spacing in normal cases.

The Quest Log's UIPanelWindows offsets put it at 10, -114 on every
layout pass, so the QuestLog_OnShow re-anchor is redundant.

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-10-22 20:48:41 -05:00
Seraphic8x2244 341ba95561 Fix addon button panel startup visibility (#61)
* fix(addonbuttons): rescan before startup visibility check

* Fix missing percentage symbols in unitframe labels
2026-10-22 20:40:00 -05:00
Brues 1600ec333b game_menu: skin every menu button dynamically
Scan GameMenuFrame's children on load and on each show instead of a
hardcoded list, so buttons added by other addons (ClassicAPI AddOn
Options, MoveAnything, MCP, SuperMacro, HardcoreDeath, Turtle shop) get
skinned without per-addon calls.
2026-10-22 18:41:47 -05:00
Brues 471c5713c5 use GetPhysicalScreenSize to get Screen Resolution 2026-10-19 20:20:03 -05:00
Brues 5d38322b2e stable: skin every stabled pet slot the client creates 2026-10-19 01:33:09 -05:00
Brues 40dd9dd6db equipmentmanager: drag a set onto the action bars via PickupEquipmentSet 2026-09-27 00:13:27 -05:00
Brues c1eba4d295 Bump CAPI min to 1.15.10 2026-09-25 10:58:07 -05:00
Brues cbc531531c swingtimer: drive the timers from ClassicAPI's PLAYER_SWING
Replaces the Lua reconstruction of the swing clock (AUTO_ATTACK_SELF with
an extra-attack guess, parry-haste maths, Slam freeze, DBC-based reset
checks on every SPELL_GO including triggered spells). PLAYER_SWING tracks
the server's clock from packets, so triggered casts, extra attacks, parry
haste, weapon swaps and the ranged wind-up all match the server.

Refs #58
2026-09-25 10:56:01 -05:00
Brues b57fc52f9a libbagsort: group gear by equip slot
Gear was split into four quality tiers and ordered by class, subclass
and name inside each, so a helm could sit between two pairs of boots.
Gear is now one category, ordered by class, then equip slot, then
subclass, then best quality first, then name.

The slot ranks come from a table keyed by the equipLoc token, not the
token's alphabetical order (which puts chest before head) or its INVTYPE
number (which parts robe from chest). The order matches ClassicAPI's
C_Container.SortBags and comes from Baganator.
2026-09-25 10:24:09 -05:00
Brues 8268af915a energytick: let the five-second rule display as a separate bar or the old shade
The shading bar sat on top of the power bar's own fill and shrank from the
right as the window drained, which reads as part of the mana bar rather than
a countdown over it -- the same problem nameplate castbars solve by living
below the health bar instead of on it.

Show Five Second Rule Style now picks between that: a real StatusBar below
the power bar, backdrop and all, or the original translucent overlay for
anyone who prefers it. The style is read once at module load like the other
structural toggles here, not swapped live -- a /reload picks up a change.
Height and color are only meaningful for the separate-bar style, so the
GUI's Five Second Rule Height and the new appearance.castbar.fsrcolor picker
apply to it alone; the overlay is unchanged from before.

The sweep spark's own dim/solid signal (fsrEnd/fsrGain) is untouched by any
of this -- it keeps dimming through an unconfirmed window regardless of
which display, or none, is shown.

Energy/Mana Ticks -- including both five-second-rule settings -- moves from
Unit Frames > General to Unit Frames > Player, right after Powerbar: they
are C.unitframes.player-only, so General was never the right tab, and
Powerbar is the setting group they actually belong to.
2026-09-22 21:19:12 -05:00
Brues 1108746949 castbar: mark a cast that none of your interrupts can stop
ClassicAPI carries notInterruptible on C_Spell.UnitCastingInfo (arg 8) and
UnitChannelInfo (arg 7); both call sites already stepped over it positionally.
The target, focus and nameplate bars now paint it.

There is no shield art to hang on the icon -- this client ships three
CastingBar textures (Border, Flash, Spark), and Blizzard's shield arrived in
3.x -- so the state is carried by color instead: the bar and the icon's
border both take a new appearance.castbar.shieldcolor, which keeps it legible
on a bar configured without an icon. One api helper paints both so the two
modules cannot drift apart on it.

Dusty rose (.8,.45,.45) rather than a steel grey. Grey is the retail
convention, but pfUI's casting color is already a near-grey pale blue, so the
two separated on brightness alone and washed out against the bar texture. The
rose sits above failcolor in luminance and well below it in saturation, and
the two are further told apart by motion: a failed cast flashes the bar full
and fades, a shielded one fills normally and tints the icon border, which
failcolor never touches.

Not on the player's own bar. The flag is measured against the reader's own
interrupts and silences, so on your own cast it answers a question nobody
asked.

Stamped once at cast start, with the rest of the cast data, rather than
re-polled. ClassicAPI evaluates lazily from the caster's live auras but does
not yet fire the 3.3.5 UNIT_SPELLCAST_INTERRUPTIBLE / _NOT_INTERRUPTIBLE
change events, and polling per frame would undo the event-driven rework these
bars just got -- so a caster who bubbles mid-cast keeps the bar it started
with until the next one.

Two limits worth knowing when it stays dark. The value is relative to your own
kit -- with no interrupt and no silence known it is never reported, whatever
the cast -- and a creature's built-in interrupt immunity lives in
creature_template, which 1.12's SMSG_SPELL_START does not carry, so a boss
with that immunity reads interruptible while your kick still fails on it.
2026-09-13 15:19:42 -05:00
51 changed files with 1058 additions and 2808 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ do
-- on. There pfUI does load, and will throw wherever it reaches for something -- on. There pfUI does load, and will throw wherever it reaches for something
-- the installed DLL doesn't have yet -- the popup names the cause so those -- the installed DLL doesn't have yet -- the popup names the cause so those
-- errors aren't a mystery. -- errors aren't a mystery.
local PFUI_CLASSIC_API_MIN = 11500 -- (X*10000 + Y*100 + Z) local PFUI_CLASSIC_API_MIN = 11703 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI" local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest" local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
+52 -12
View File
@@ -293,14 +293,6 @@ function pfUI.api.GetFactionIcon(raceKey)
return FACTION_ICON[info.faction] or "" return FACTION_ICON[info.faction] or ""
end end
-- [ GetPlayerRaceIcons ]
-- Returns the faction and race emblems of the player character.
-- return: [string] both inline textures, or an empty string
function pfUI.api.GetPlayerRaceIcons()
local _, raceKey = UnitRace("player")
return pfUI.api.GetFactionIcon(raceKey) .. pfUI.api.GetRaceIcon(raceKey, UnitSex("player"))
end
-- [ strvertical ] -- [ strvertical ]
-- Creates vertical text using linebreaks. Multibyte char friendly. -- Creates vertical text using linebreaks. Multibyte char friendly.
-- 'str' [string] String to columnize. -- 'str' [string] String to columnize.
@@ -1333,6 +1325,32 @@ function pfUI.api.CreateBackdropShadow(f)
f.backdrop_shadow:SetBackdropBorderColor(0, 0, 0, tonumber(pfUI_config.appearance.border.shadow_intensity)) f.backdrop_shadow:SetBackdropBorderColor(0, 0, 0, tonumber(pfUI_config.appearance.border.shadow_intensity))
end 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.
-- 'bar' [frame] the castbar statusbar
-- 'icon' [frame] the icon frame, or nil for a bar without one
-- 'shielded' [bool] whether the cast is uninterruptible
-- 'isChannel' [bool] selects the unshielded color
function pfUI.api.SetCastbarShield(bar, icon, shielded, isChannel)
if shielded then
bar:SetStatusBarColor(pfUI.api.GetStringColor(C.appearance.castbar.shieldcolor))
else
bar:SetStatusBarColor(pfUI.api.GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
end
local iconborder = icon and icon.backdrop
if not iconborder then return end
if shielded then
iconborder:SetBackdropBorderColor(pfUI.api.GetStringColor(C.appearance.castbar.shieldcolor))
else
iconborder:SetBackdropBorderColor(pfUI.api.GetStringColor(C.appearance.border.color))
end
end
-- [ Bar Layout Options ] -- -- [ Bar Layout Options ] --
-- 'barsize' size of bar in number of buttons -- 'barsize' size of bar in number of buttons
-- returns: array of options as strings for pfUI.gui.bar -- returns: array of options as strings for pfUI.gui.bar
@@ -1517,6 +1535,18 @@ function pfUI.api.BarButtonAnchor(button,basename,buttonindex,barsize,formfactor
return button._anchor return button._anchor
end end
-- [ MouseIsOverHitRect ] --
-- Like frame:IsMouseOver(), but tests the frame's hit rect, the area the
-- engine uses for mouse focus and OnEnter/OnLeave. CreateBackdrop widens
-- that rect by the border, so plain IsMouseOver misses a band around it.
-- 'frame' the frame to test
-- return true when the cursor is inside the frame's hit rect
function pfUI.api.MouseIsOverHitRect(frame)
if not frame.GetHitRectInsets then return frame:IsMouseOver() end
local left, right, top, bottom = frame:GetHitRectInsets()
return frame:IsMouseOver(-top, bottom, left, -right)
end
-- [ Enable Autohide ] -- -- [ Enable Autohide ] --
-- 'frame' the frame that should be hidden -- 'frame' the frame that should be hidden
function pfUI.api.EnableAutohide(frame, timeout, combat) function pfUI.api.EnableAutohide(frame, timeout, combat)
@@ -1529,11 +1559,20 @@ function pfUI.api.EnableAutohide(frame, timeout, combat)
frame.hover.parent = frame frame.hover.parent = frame
frame.hover:Show() frame.hover:Show()
if not frame.hover.fade then
frame.hover.fade = frame:CreateAnimationGroup()
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 if combat then
frame.hover:RegisterEvent("PLAYER_REGEN_ENABLED") frame.hover:RegisterEvent("PLAYER_REGEN_ENABLED")
frame.hover:RegisterEvent("PLAYER_REGEN_DISABLED") frame.hover:RegisterEvent("PLAYER_REGEN_DISABLED")
frame.hover:SetScript("OnEvent", function() frame.hover:SetScript("OnEvent", function()
if event == "PLAYER_REGEN_DISABLED" then if event == "PLAYER_REGEN_DISABLED" then
this.fade:Stop()
this.parent:SetAlpha(1) this.parent:SetAlpha(1)
this.activeTo = "keep" this.activeTo = "keep"
elseif event == "PLAYER_REGEN_ENABLED" then elseif event == "PLAYER_REGEN_ENABLED" then
@@ -1549,13 +1588,13 @@ function pfUI.api.EnableAutohide(frame, timeout, combat)
if this.activeTo == "keep" then return end if this.activeTo == "keep" then return end
if MouseIsOver(this, 10, -10, -10, 10) then if this:IsMouseOver(10, -10, -10, 10) then
this.activeTo = GetTime() + timeout this.activeTo = GetTime() + timeout
this.fade:Stop()
this.parent:SetAlpha(1) this.parent:SetAlpha(1)
elseif this.activeTo then elseif this.activeTo then
if this.activeTo < GetTime() and this.parent:GetAlpha() > 0 then if this.activeTo < GetTime() and this.parent:GetAlpha() > 0 and not this.fade:IsPlaying() then
local fps = (60 / math.max(GetFramerate(), 1)) this.fade:Play()
this.parent:SetAlpha(this.parent:GetAlpha() - 0.05*fps)
end end
else else
this.activeTo = GetTime() + timeout this.activeTo = GetTime() + timeout
@@ -1572,6 +1611,7 @@ function pfUI.api.DisableAutohide(frame)
frame.hover:SetScript("OnEvent", nil) frame.hover:SetScript("OnEvent", nil)
frame.hover:SetScript("OnUpdate", nil) frame.hover:SetScript("OnUpdate", nil)
frame.hover:Hide() frame.hover:Hide()
frame.hover.fade:Stop()
frame:SetAlpha(1) frame:SetAlpha(1)
end end
+13 -3
View File
@@ -156,6 +156,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "castbar", "castbarcolor", ".7,.7,.9,.8") pfUI:UpdateConfig("appearance", "castbar", "castbarcolor", ".7,.7,.9,.8")
pfUI:UpdateConfig("appearance", "castbar", "channelcolor", ".9,.9,.7,.8") pfUI:UpdateConfig("appearance", "castbar", "channelcolor", ".9,.9,.7,.8")
pfUI:UpdateConfig("appearance", "castbar", "failcolor", "1,.2,.2,.8") pfUI:UpdateConfig("appearance", "castbar", "failcolor", "1,.2,.2,.8")
pfUI:UpdateConfig("appearance", "castbar", "shieldcolor", ".8,.45,.45,.8")
pfUI:UpdateConfig("appearance", "castbar", "fsrcolor", ".9,.6,.2,.8")
pfUI:UpdateConfig("appearance", "castbar", "texture", "Interface\\AddOns\\pfUI\\img\\bar") pfUI:UpdateConfig("appearance", "castbar", "texture", "Interface\\AddOns\\pfUI\\img\\bar")
pfUI:UpdateConfig("appearance", "infight", "screen", "0") pfUI:UpdateConfig("appearance", "infight", "screen", "0")
pfUI:UpdateConfig("appearance", "infight", "aggro", "0") pfUI:UpdateConfig("appearance", "infight", "aggro", "0")
@@ -226,7 +228,6 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", nil, "rangecheck", "0") pfUI:UpdateConfig("unitframes", nil, "rangecheck", "0")
pfUI:UpdateConfig("unitframes", nil, "rangecheck_mode", "vanilla") pfUI:UpdateConfig("unitframes", nil, "rangecheck_mode", "vanilla")
pfUI:UpdateConfig("unitframes", nil, "rangecheck_distance", "40") pfUI:UpdateConfig("unitframes", nil, "rangecheck_distance", "40")
pfUI:UpdateConfig("unitframes", nil, "buffdetect", "0")
pfUI:UpdateConfig("unitframes", nil, "druidmanabar", "1") pfUI:UpdateConfig("unitframes", nil, "druidmanabar", "1")
pfUI:UpdateConfig("unitframes", nil, "druidmanaheight", "10") pfUI:UpdateConfig("unitframes", nil, "druidmanaheight", "10")
pfUI:UpdateConfig("unitframes", nil, "druidmanawidth", "-1") pfUI:UpdateConfig("unitframes", nil, "druidmanawidth", "-1")
@@ -324,6 +325,9 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", "player", "showRest", "0") pfUI:UpdateConfig("unitframes", "player", "showRest", "0")
pfUI:UpdateConfig("unitframes", "player", "energy", "1") pfUI:UpdateConfig("unitframes", "player", "energy", "1")
pfUI:UpdateConfig("unitframes", "player", "manatick", "0") pfUI:UpdateConfig("unitframes", "player", "manatick", "0")
pfUI:UpdateConfig("unitframes", "player", "showfsr", "1")
pfUI:UpdateConfig("unitframes", "player", "fsrheight", "4")
pfUI:UpdateConfig("unitframes", "player", "fsrstyle", "0")
pfUI:UpdateConfig("unitframes", "player", "display_haste", "0") pfUI:UpdateConfig("unitframes", "player", "display_haste", "0")
pfUI:UpdateConfig("unitframes", "player", "display_haste_color", "1,1,1,1") pfUI:UpdateConfig("unitframes", "player", "display_haste_color", "1,1,1,1")
pfUI:UpdateConfig("unitframes", "player", "display_spellpower", "1") pfUI:UpdateConfig("unitframes", "player", "display_spellpower", "1")
@@ -516,10 +520,16 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", unit, "buffsize", "20") pfUI:UpdateConfig("unitframes", unit, "buffsize", "20")
pfUI:UpdateConfig("unitframes", unit, "bufflimit", "32") pfUI:UpdateConfig("unitframes", unit, "bufflimit", "32")
pfUI:UpdateConfig("unitframes", unit, "buffperrow", "8") pfUI:UpdateConfig("unitframes", unit, "buffperrow", "8")
pfUI:UpdateConfig("unitframes", unit, "ownbuffsize", "-1")
pfUI:UpdateConfig("unitframes", unit, "buffspacingx", "1")
pfUI:UpdateConfig("unitframes", unit, "buffspacingy", "1")
pfUI:UpdateConfig("unitframes", unit, "debuffs", "TOPLEFT") pfUI:UpdateConfig("unitframes", unit, "debuffs", "TOPLEFT")
pfUI:UpdateConfig("unitframes", unit, "debuffsize", "20") pfUI:UpdateConfig("unitframes", unit, "debuffsize", "20")
pfUI:UpdateConfig("unitframes", unit, "debufflimit", "32") pfUI:UpdateConfig("unitframes", unit, "debufflimit", "32")
pfUI:UpdateConfig("unitframes", unit, "debuffperrow", "8") pfUI:UpdateConfig("unitframes", unit, "debuffperrow", "8")
pfUI:UpdateConfig("unitframes", unit, "owndebuffsize", "-1")
pfUI:UpdateConfig("unitframes", unit, "debuffspacingx", "1")
pfUI:UpdateConfig("unitframes", unit, "debuffspacingy", "1")
pfUI:UpdateConfig("unitframes", unit, "invert_healthbar", "0") pfUI:UpdateConfig("unitframes", unit, "invert_healthbar", "0")
pfUI:UpdateConfig("unitframes", unit, "verticalbar", "0") pfUI:UpdateConfig("unitframes", unit, "verticalbar", "0")
pfUI:UpdateConfig("unitframes", unit, "buff_indicator", "0") pfUI:UpdateConfig("unitframes", unit, "buff_indicator", "0")
@@ -816,8 +826,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("chat", "text", "playerlinks", "1") pfUI:UpdateConfig("chat", "text", "playerlinks", "1")
pfUI:UpdateConfig("chat", "text", "detecturl", "1") pfUI:UpdateConfig("chat", "text", "detecturl", "1")
pfUI:UpdateConfig("chat", "text", "classcolor", "1") pfUI:UpdateConfig("chat", "text", "classcolor", "1")
pfUI:UpdateConfig("chat", "text", "playericons", "0") pfUI:UpdateConfig("chat", "text", "factionicons", "0")
pfUI:UpdateConfig("chat", "text", "whosearchunknown", "0") pfUI:UpdateConfig("chat", "text", "raceicons", "0")
pfUI:UpdateConfig("chat", "text", "playerlevel", "0") pfUI:UpdateConfig("chat", "text", "playerlevel", "0")
pfUI:UpdateConfig("chat", "left", "width", "380") pfUI:UpdateConfig("chat", "left", "width", "380")
pfUI:UpdateConfig("chat", "left", "height", "180") pfUI:UpdateConfig("chat", "left", "height", "180")
+15 -9
View File
@@ -170,7 +170,7 @@ do -- dropdown
end end
local function MenuOnUpdate() local function MenuOnUpdate()
if not MouseIsOver(this, 100, -100, -100, 100) then if not this:IsMouseOver(100, -100, -100, 100) then
this.button:HideMenu() this.button:HideMenu()
end end
end end
@@ -661,7 +661,7 @@ function pfUI.api.SkinButton(button, cr, cg, cb, icon, disableHighlight)
end end
b.UnlockHighlight = function() b.UnlockHighlight = function()
if not MouseIsOver(b) then if not MouseIsOverHitRect(b) then
b:SetBackdropBorderColor(GetStringColor(pfUI_config.appearance.border.color)) b:SetBackdropBorderColor(GetStringColor(pfUI_config.appearance.border.color))
end end
b.locked = false b.locked = false
@@ -683,6 +683,7 @@ function pfUI.api.SkinCollapseButton(button, all)
b.icon:SetSize(size, size) b.icon:SetSize(size, size)
b.icon:SetPoint("LEFT", 2, 2) b.icon:SetPoint("LEFT", 2, 2)
CreateBackdrop(b.icon) CreateBackdrop(b.icon)
b.icon:EnableMouse(false)
b.icon.text = b.icon:CreateFontString(nil, "OVERLAY") b.icon.text = b.icon:CreateFontString(nil, "OVERLAY")
b.icon.text:SetFontObject(GameFontWhite) b.icon.text:SetFontObject(GameFontWhite)
b.icon.text:SetPoint("CENTER", -1, 0) b.icon.text:SetPoint("CENTER", -1, 0)
@@ -1160,18 +1161,22 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
infobox = CreateFrame("Button", "pfInfoBox", UIParent) infobox = CreateFrame("Button", "pfInfoBox", UIParent)
infobox:Hide() infobox:Hide()
infobox.fade = infobox:CreateAnimationGroup()
local fade = infobox.fade:CreateAnimation("Alpha")
fade:SetChange(-1)
fade:SetDuration(.3)
infobox.fade:SetScript("OnFinished", function()
infobox:Hide()
infobox:SetAlpha(1)
end)
infobox:SetScript("OnUpdate", function() infobox:SetScript("OnUpdate", function()
local time = infobox.lastshow + infobox.duration - GetTime() local time = infobox.lastshow + infobox.duration - GetTime()
infobox.timeout:SetValue(time) infobox.timeout:SetValue(time)
if GetTime() > infobox.lastshow + infobox.duration then if GetTime() > infobox.lastshow + infobox.duration then
infobox:SetAlpha(infobox:GetAlpha()-0.05) if not infobox.fade:IsPlaying() then infobox.fade:Play() end
elseif MouseIsOverHitRect(this) then
if infobox:GetAlpha() <= 0.1 then
infobox:Hide()
infobox:SetAlpha(1)
end
elseif MouseIsOver(this) then
this:SetAlpha(max(0.4, this:GetAlpha() - .1)) this:SetAlpha(max(0.4, this:GetAlpha() - .1))
else else
this:SetAlpha(min(1, this:GetAlpha() + .1)) this:SetAlpha(min(1, this:GetAlpha() + .1))
@@ -1214,6 +1219,7 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
infobox:SetParent(parent) infobox:SetParent(parent)
infobox:SetFrameStrata("FULLSCREEN_DIALOG") infobox:SetFrameStrata("FULLSCREEN_DIALOG")
infobox.fade:Stop()
infobox:Show() infobox:Show()
end end
+140 -233
View File
@@ -10,10 +10,64 @@ pfUI.uf.frames = {}
-- Scans run back to back and consume the buffer before the next refill. -- Scans run back to back and consume the buffer before the next refill.
local auraSlots = {} local auraSlots = {}
-- Separate buffer for the tooltip handlers. They run from OnEnter, which can -- Aura block corner -> { icon anchor point, x direction, y direction, below frame }
-- fire while RefreshUnit is showing/hiding icons under the cursor, so they must local auraCorners = {
-- not share the refresh buffer. TOPLEFT = { "BOTTOMLEFT", 1, 1 },
local tooltipSlots = {} BOTTOMLEFT = { "TOPLEFT", 1, -1, true },
TOPRIGHT = { "BOTTOMRIGHT", -1, 1 },
BOTTOMRIGHT = { "TOPRIGHT", -1, -1, true },
}
-- 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.
local function LayoutAuraBlock(unit, icons, offset)
local corner = auraCorners[icons.pos]
local anchor = unit.config.portraitheight ~= "-1" and unit.hp or unit
if anchor == unit.hp and corner[4] then anchor = unit.power end
offset = offset or icons.gapy
local x, rowsize, col = 0, 0, 0
local i = 1
while icons[i] and icons[i]:IsShown() do
if col == icons.perrow then
offset = offset + rowsize + icons.gapy
x, rowsize, col = 0, 0, 0
end
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)
x = x + size + icons.gapx
if size > rowsize then rowsize = size end
col = col + 1
i = i + 1
end
if col > 0 then offset = offset + rowsize + icons.gapy end
return offset
end
-- Buffs and debuffs sharing a corner stack in one column: buffs first on
-- friendly units, debuffs first on neutral and hostile ones.
local function LayoutAuras(unit, unitstr)
local first, second = unit.buffs, unit.debuffs
if not UnitIsFriend("player", unitstr) then first, second = second, first end
if not first then first, second = second, nil end
if not first then return end
local offset = LayoutAuraBlock(unit, first)
if second then
LayoutAuraBlock(unit, second, second.pos == first.pos and offset or nil)
end
end
-- ============================================================================ -- ============================================================================
-- GUID-based Roster Tracking for Smart Updates -- GUID-based Roster Tracking for Smart Updates
@@ -78,10 +132,6 @@ local glow2 = {
insets = {left = 0, right = 0, top = 0, bottom = 0}, insets = {left = 0, right = 0, top = 0, bottom = 0},
} }
local function DoNothing()
return
end
local function BuffOnEnter() local function BuffOnEnter()
local parent = this:GetParent() local parent = this:GetParent()
if not parent.label then return end if not parent.label then return end
@@ -119,32 +169,8 @@ local function DebuffOnEnter()
local unitstr = this:GetParent().label .. this:GetParent().id local unitstr = this:GetParent().label .. this:GetParent().id
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT") GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
if this:GetParent().label ~= "player" then -- C_UnitAuras lists the player's own auras first, so the selfdebuff
local parent = this:GetParent() -- (HARMFUL|PLAYER) list is a prefix of the HARMFUL list: same index.
-- selfdebuff filters the displayed list to player-cast harmful auras, but
-- SetUnitAura's index has to be into the unfiltered HARMFUL list. Look up
-- the displayed aura via the PLAYER filter, then find its position in the
-- unfiltered list by name + sourceGUID.
--
-- The unfiltered list is NOT capped at 16: once a unit's 16 debuff slots
-- are full the server parks further debuffs in buff slots, and C_UnitAuras
-- reports those as harmful too. So enumerate however many the unit has.
if parent.config and parent.config.selfdebuff == "1" then
local ownAura = C_UnitAuras.GetAuraDataByIndex(unitstr, this.id, "HARMFUL|PLAYER")
if ownAura then
local n = ScanAuraSlots(unitstr, "HARMFUL", tooltipSlots)
for i = 1, n do
local check = C_UnitAuras.GetAuraDataBySlot(unitstr, tooltipSlots[i])
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
GameTooltip:SetUnitAura(unitstr, i, "HARMFUL")
return
end
end
end
end
end
GameTooltip:SetUnitAura(unitstr, this.id, "HARMFUL") GameTooltip:SetUnitAura(unitstr, this.id, "HARMFUL")
end end
@@ -219,27 +245,6 @@ function pfUI.api.UnitHasAggro(unit)
return data.state return data.state
end end
pfUI.uf.glow = CreateFrame("Frame", nil, UIParent)
pfUI.uf.glow:SetScript("OnUpdate", function()
local fpsmod = GetFramerate() / 30
if not this.val or this.val >= .8 then
this.mod = -0.01 / fpsmod
elseif this.val <= .4 then
this.mod = 0.01 / fpsmod
end
this.val = this.val + this.mod
end)
pfUI.uf.glow.mod = 0
pfUI.uf.glow.val = 0.6
function pfUI.uf.glow.UpdateGlowAnimation()
local val = pfUI.uf.glow.val or 0.6
if val < 0.4 then val = 0.4 end
if val > 0.8 then val = 0.8 end
this:SetAlpha(val)
end
function pfUI.uf:UpdateVisibility() function pfUI.uf:UpdateVisibility()
local self = self or this local self = self or this
@@ -313,10 +318,16 @@ function pfUI.uf:UpdateVisibility()
self._label, self._id = nil, nil self._label, self._id = nil, nil
end end
local unitstr = ("%s%s"):format(self.label or "", self.id or "") -- this runs for every frame five times a second, so only touch the
self.unitstr = unitstr ~= "" and unitstr or nil -- attribute when the unit actually changed
self:SetAttribute("unit", self.unitstr) local unitstr = (self.label or "") .. (self.id or "")
local visibility = ("[target=%s,exists] show; hide"):format(unitstr) local unit = unitstr ~= "" and unitstr or nil
if unit ~= self.unitstr then
self.unitstr = unit
self:SetAttribute("unit", unit)
end
-- anything but "show"/"hide" means "show if the unit exists"
local visibility = "exists"
-- Group frames are redundant when the group is already shown as a raid grid: -- Group frames are redundant when the group is already shown as a raid grid:
-- either an actual raid, or a party promoted to the raid grid via -- either an actual raid, or a party promoted to the raid grid via
@@ -425,6 +436,8 @@ function pfUI.uf:UpdateConfig()
local C = pfUI_config local C = pfUI_config
local rawborder, default_border = GetBorderSize("unitframes") local rawborder, default_border = GetBorderSize("unitframes")
local spacing = f.config.pspace * GetPerfectPixel() local spacing = f.config.pspace * GetPerfectPixel()
-- aura icon backdrops sit outside the icon, so gaps clear both borders
local aura_border = (C.appearance.border.force_blizz == "1" and 1 or 2) * default_border
local cooldown_text = tonumber(f.config.cooldown_text) local cooldown_text = tonumber(f.config.cooldown_text)
local cooldown_anim = tonumber(f.config.cooldown_anim) local cooldown_anim = tonumber(f.config.cooldown_anim)
@@ -451,7 +464,6 @@ function pfUI.uf:UpdateConfig()
f.glow:SetBackdrop(glow2) f.glow:SetBackdrop(glow2)
f.glow:SetPoint("TOPLEFT", f, "TOPLEFT", -6 - default_border,6 + default_border) f.glow:SetPoint("TOPLEFT", f, "TOPLEFT", -6 - default_border,6 + default_border)
f.glow:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 6 + default_border,-6 - default_border) f.glow:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 6 + default_border,-6 - default_border)
f.glow:SetScript("OnUpdate", pfUI.uf.glow.UpdateGlowAnimation)
f.glow:Hide() f.glow:Hide()
f.combat:SetSize(tonumber(f.config.squaresize), tonumber(f.config.squaresize)) f.combat:SetSize(tonumber(f.config.squaresize), tonumber(f.config.squaresize))
@@ -757,13 +769,17 @@ function pfUI.uf:UpdateConfig()
f.buffs = nil f.buffs = nil
else else
f.buffs = f.buffs or {} f.buffs = f.buffs or {}
f.buffs.pos = f.config.buffs
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.gapx = aura_border + tonumber(f.config.buffspacingx)
f.buffs.gapy = aura_border + tonumber(f.config.buffspacingy)
f.buffs.own = 0
for i=1, 32 do for i=1, 32 do
if i > tonumber(f.config.bufflimit) then break end if i > tonumber(f.config.bufflimit) then break end
local perrow = f.config.buffperrow
local row = floor((i-1) / perrow)
f.buffs[i] = f.buffs[i] or CreateFrame("Button", "pfUI" .. f.fname .. "Buff" .. i, f) f.buffs[i] = f.buffs[i] or CreateFrame("Button", "pfUI" .. f.fname .. "Buff" .. i, f)
f.buffs[i].texture = f.buffs[i].texture or f.buffs[i]:CreateTexture() f.buffs[i].texture = f.buffs[i].texture or f.buffs[i]:CreateTexture()
f.buffs[i].texture:SetTexCoord(.08, .92, .08, .92) f.buffs[i].texture:SetTexCoord(.08, .92, .08, .92)
@@ -778,57 +794,8 @@ function pfUI.uf:UpdateConfig()
f.buffs[i]:SetFrameLevel(12) f.buffs[i]:SetFrameLevel(12)
f.buffs[i]:RegisterForClicks("RightButtonUp") f.buffs[i]:RegisterForClicks("RightButtonUp")
f.buffs[i]:ClearAllPoints()
local invert_h, invert_v, af f.buffs[i].cd = f.buffs[i].cd or CreateFrame("Cooldown", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate")
if f.config.buffs == "TOPLEFT" then
invert_h = 1
invert_v = 1
af = "BOTTOMLEFT"
elseif f.config.buffs == "BOTTOMLEFT" then
invert_h = -1
invert_v = 1
af = "TOPLEFT"
elseif f.config.buffs == "TOPRIGHT" then
invert_h = 1
invert_v = -1
af = "BOTTOMRIGHT"
elseif f.config.buffs == "BOTTOMRIGHT" then
invert_h = -1
invert_v = -1
af = "TOPRIGHT"
end
local anchor = f.config.portraitheight ~= "-1" and f.hp or f
if anchor == f.hp and (f.config.buffs == "BOTTOMLEFT" or f.config.buffs == "BOTTOMRIGHT") then
anchor = f.power
end
local multiply = C.appearance.border.force_blizz == "1" and 1 or 2
f.buffs[i]:SetPoint(af, anchor, f.config.buffs,
invert_v * (i-1-row*perrow)*(multiply*default_border + f.config.buffsize + 1),
invert_h * (row*(multiply*default_border + f.config.buffsize + 1) + (multiply*default_border + 1)))
f.buffs[i]:SetSize(f.config.buffsize, f.config.buffsize)
-- Create CD frame if it doesn't exist
if not f.buffs[i].cd then
if cooldown_anim == 1 then
-- Animation enabled: Use Model frame with CooldownFrameTemplate
f.buffs[i].cd = CreateFrame("Model", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate")
else
-- Animation disabled: Use regular Frame with dummy functions
f.buffs[i].cd = CreateFrame("Frame", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i])
f.buffs[i].cd.AdvanceTime = DoNothing
f.buffs[i].cd.SetSequence = DoNothing
f.buffs[i].cd.SetSequenceTime = DoNothing
end
end
-- Always update CD properties (in case size changed)
local cdScale = f.config.buffsize / 32
f.buffs[i].cd:ClearAllPoints()
f.buffs[i].cd:SetScale(cdScale)
f.buffs[i].cd:SetAllPoints(f.buffs[i])
f.buffs[i].cd:SetFrameLevel(14) f.buffs[i].cd:SetFrameLevel(14)
f.buffs[i].cd.pfCooldownType = "ALL" f.buffs[i].cd.pfCooldownType = "ALL"
f.buffs[i].cd.pfCooldownStyleText = cooldown_text f.buffs[i].cd.pfCooldownStyleText = cooldown_text
@@ -861,6 +828,13 @@ function pfUI.uf:UpdateConfig()
f.debuffs = nil f.debuffs = nil
else else
f.debuffs = f.debuffs or {} f.debuffs = f.debuffs or {}
f.debuffs.pos = f.config.debuffs
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.gapx = aura_border + tonumber(f.config.debuffspacingx)
f.debuffs.gapy = aura_border + tonumber(f.config.debuffspacingy)
f.debuffs.own = 0
for i=1, 32 do for i=1, 32 do
if i > tonumber(f.config.debufflimit) then break end if i > tonumber(f.config.debufflimit) then break end
@@ -879,29 +853,9 @@ function pfUI.uf:UpdateConfig()
f.debuffs[i]:SetFrameLevel(12) f.debuffs[i]:SetFrameLevel(12)
f.debuffs[i]:RegisterForClicks("RightButtonUp") f.debuffs[i]:RegisterForClicks("RightButtonUp")
f.debuffs[i]:ClearAllPoints()
f.debuffs[i]:SetSize(f.config.debuffsize, f.config.debuffsize)
f.debuffs[i]:SetNormalTexture(nil) f.debuffs[i]:SetNormalTexture(nil)
-- Create CD frame if it doesn't exist f.debuffs[i].cd = f.debuffs[i].cd or CreateFrame("Cooldown", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate")
if not f.debuffs[i].cd then
if cooldown_anim == 1 then
-- Animation enabled: Use Model frame with CooldownFrameTemplate
f.debuffs[i].cd = CreateFrame("Model", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate")
else
-- Animation disabled: Use regular Frame with dummy functions
f.debuffs[i].cd = CreateFrame("Frame", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i])
f.debuffs[i].cd.AdvanceTime = DoNothing
f.debuffs[i].cd.SetSequence = DoNothing
f.debuffs[i].cd.SetSequenceTime = DoNothing
end
end
-- Always update CD properties (in case size changed)
local cdScale = f.config.debuffsize / 32
f.debuffs[i].cd:ClearAllPoints()
f.debuffs[i].cd:SetScale(cdScale)
f.debuffs[i].cd:SetAllPoints(f.debuffs[i])
f.debuffs[i].cd:SetFrameLevel(14) f.debuffs[i].cd:SetFrameLevel(14)
f.debuffs[i].cd.pfCooldownType = "ALL" f.debuffs[i].cd.pfCooldownType = "ALL"
f.debuffs[i].cd.pfCooldownStyleText = cooldown_text f.debuffs[i].cd.pfCooldownStyleText = cooldown_text
@@ -943,32 +897,6 @@ function pfUI.uf.OnShow()
pfUI.uf:RefreshUnit(this, "base") pfUI.uf:RefreshUnit(this, "base")
end end
-- GUID-keyed aura start-time tracker, fed by Nampower's BUFF_ADDED_OTHER /
-- BUFF_REMOVED_OTHER. Non-player units have aura.expirationTime == 0 (server
-- doesn't broadcast time), so we reconstruct the swirl from "when we saw it
-- applied" + aura.duration (Spell.dbc base). Only auras we actually witnessed
-- being cast get tracked — pre-existing auras (already on a unit when you
-- target them) intentionally show no timer rather than a fabricated one.
pfUI.uf.aura_starts = {} -- {[guid] = {[spellId] = startTime}}
local auraTracker = CreateFrame("Frame", "pfUF_AuraTracker", UIParent)
auraTracker:RegisterEvent("BUFF_ADDED_OTHER")
auraTracker:RegisterEvent("BUFF_REMOVED_OTHER")
auraTracker:SetScript("OnEvent", function()
-- Nampower arg layout: arg1=guid, arg3=spellId. State code 0=added, 1=removed,
-- 2=modified; we treat any added/modified as "reset start = now" and let the
-- REMOVED event clear the entry.
if not arg1 or not arg3 or arg3 == 0 then return end
if event == "BUFF_REMOVED_OTHER" then
if pfUI.uf.aura_starts[arg1] then
pfUI.uf.aura_starts[arg1][arg3] = nil
end
else
pfUI.uf.aura_starts[arg1] = pfUI.uf.aura_starts[arg1] or {}
pfUI.uf.aura_starts[arg1][arg3] = GetTime()
end
end)
function pfUI.uf.OnEvent() function pfUI.uf.OnEvent()
-- Handle shutdown to prevent crash 132 -- Handle shutdown to prevent crash 132
if event == "PLAYER_LOGOUT" then if event == "PLAYER_LOGOUT" then
@@ -1140,7 +1068,18 @@ function pfUI.uf.OnUpdate()
end end
else else
pfUI.uf:RefreshUnitState(this) pfUI.uf:RefreshUnitState(this)
pfUI.uf:RefreshIndicators(this) -- group frames get their leader/loot/raid mark icons refreshed by
-- events through update_indicators, so polling them is only a safety
-- net; frames whose unit changes without events keep polling per tick
local label = this.label
if label == "raid" or label == "party" or label == "raidpet" or label == "partypet" then
if (this.lastIndicatorPoll or 0) < now then
this.lastIndicatorPoll = now + 3
pfUI.uf:RefreshIndicators(this)
end
else
pfUI.uf:RefreshIndicators(this)
end
end end
if this.config and this.config.glowaggro == "1" and pfUI.api.UnitHasAggro(unitstr) > 0 then if this.config and this.config.glowaggro == "1" and pfUI.api.UnitHasAggro(unitstr) > 0 then
@@ -1153,9 +1092,18 @@ function pfUI.uf.OnUpdate()
this.glow:Hide() this.glow:Hide()
end end
-- update everything on eventless frames (targettarget, etc) -- update eventless frames (targettarget, etc). A 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 if this.tick then
pfUI.uf:RefreshUnit(this, "all") 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 end
@@ -1482,7 +1430,18 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
end end
f.glow = CreateFrame("Frame", nil, f) f.glow = CreateFrame("Frame", nil, f)
f.combat = CreateFrame("Frame", nil, f.hp.bar) -- 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:SetDuration(1.3)
fade:SetSmoothing("IN_OUT")
f.glow.pulse:Play()
f.combat =CreateFrame("Frame", nil, f.hp.bar)
f.combat.tex = f.combat:CreateTexture(nil, "OVERLAY") f.combat.tex = f.combat:CreateTexture(nil, "OVERLAY")
f.combat.tex:SetAllPoints() f.combat.tex:SetAllPoints()
@@ -1767,15 +1726,6 @@ function pfUI.uf:RefreshUnit(unit, component)
else else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
elseif duration > 0 then
local guid = UnitGUID(unitstr)
local guidStarts = guid and pfUI.uf.aura_starts[guid]
local start = guidStarts and guidStarts[spellId]
if start then
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end
else else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
@@ -1783,47 +1733,18 @@ function pfUI.uf:RefreshUnit(unit, component)
unit.buffs[i]:Hide() unit.buffs[i]:Hide()
end end
end end
-- own buffs lead the list; count them only when they're drawn larger
if unit.buffs.ownsize ~= unit.buffs.size then
unit.buffs.own = ScanAuraSlots(unitstr, "HELPFUL|PLAYER", auraSlots, unit.config.bufflimit)
end
end end
-- debuffs -- debuffs
if unit.debuffs and ( component == "all" or component == "aura" ) then if unit.debuffs and ( component == "all" or component == "aura" ) then
local texture, stacks, dtype local texture, stacks, dtype
local perrow = unit.config.debuffperrow
local bperrow = unit.config.buffperrow
local selfdebuff = unit.config.selfdebuff local selfdebuff = unit.config.selfdebuff
local invert_h, invert_v, af
if unit.config.debuffs == "TOPLEFT" then
invert_h = 1
invert_v = 1
af = "BOTTOMLEFT"
elseif unit.config.debuffs == "BOTTOMLEFT" then
invert_h = -1
invert_v = 1
af = "TOPLEFT"
elseif unit.config.debuffs == "TOPRIGHT" then
invert_h = 1
invert_v = -1
af = "BOTTOMRIGHT"
elseif unit.config.debuffs == "BOTTOMRIGHT" then
invert_h = -1
invert_v = -1
af = "TOPRIGHT"
end
local buffrow, reposition = 0, ( component == "all" and true or nil )
if unit.config.buffs == unit.config.debuffs then
if unit.buffs[0*bperrow+1] and unit.buffs[0*bperrow+1]:IsShown() then buffrow = buffrow + 1 end
if unit.buffs[1*bperrow+1] and unit.buffs[1*bperrow+1]:IsShown() then buffrow = buffrow + 1 end
if unit.buffs[2*bperrow+1] and unit.buffs[2*bperrow+1]:IsShown() then buffrow = buffrow + 1 end
if unit.buffs[3*bperrow+1] and unit.buffs[3*bperrow+1]:IsShown() then buffrow = buffrow + 1 end
end
if buffrow ~= unit.lastbuffrow then
unit.lastbuffrow = buffrow
reposition = true
end
-- selfdebuff narrows to player-cast harmful auras via the PLAYER filter. -- selfdebuff narrows to player-cast harmful auras via the PLAYER filter.
-- Player-frame debuffs aren't gated on it (it'd hide most party-applied -- Player-frame debuffs aren't gated on it (it'd hide most party-applied
-- effects on you). One GetAuraSlots enumeration per refresh; the i-th slot -- effects on you). One GetAuraSlots enumeration per refresh; the i-th slot
@@ -1834,19 +1755,6 @@ function pfUI.uf:RefreshUnit(unit, component)
for i=1, unit.config.debufflimit do for i=1, unit.config.debufflimit do
if not unit.debuffs[i] then break end if not unit.debuffs[i] then break end
local row = floor((i-1) / unit.config.debuffperrow)
if reposition then
local anchor = unit.config.portraitheight ~= "-1" and unit.hp or unit
if anchor == unit.hp and (unit.config.debuffs == "BOTTOMLEFT" or unit.config.debuffs == "BOTTOMRIGHT") then
anchor = unit.power
end
local multiply = C.appearance.border.force_blizz == "1" and 1 or 2
unit.debuffs[i]:SetPoint(af, anchor, unit.config.debuffs,
invert_v * (i-1-row*perrow)*(multiply*default_border + unit.config.debuffsize + 1),
invert_h * ((row+buffrow)*(multiply*default_border + unit.config.debuffsize + 1) + (multiply*default_border + 1)))
end
-- positional read by slot id allocates nothing -- positional read by slot id allocates nothing
local name, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i]) local name, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
if name then if name then
@@ -1891,6 +1799,15 @@ function pfUI.uf:RefreshUnit(unit, component)
unit.debuffs[i]:Hide() unit.debuffs[i]:Hide()
end end
end end
-- own debuffs lead the list; count them only when they're drawn larger
if unit.debuffs.ownsize ~= unit.debuffs.size then
unit.debuffs.own = ScanAuraSlots(unitstr, "HARMFUL|PLAYER", auraSlots, unit.config.debufflimit)
end
end
if component == "all" or component == "aura" then
LayoutAuras(unit, unitstr)
end end
-- indicators -- indicators
@@ -2329,17 +2246,7 @@ function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
frame.icon[pos].stacks:SetJustifyH("RIGHT") frame.icon[pos].stacks:SetJustifyH("RIGHT")
frame.icon[pos].stacks:SetJustifyV("BOTTOM") frame.icon[pos].stacks:SetJustifyV("BOTTOM")
-- Check if parent frame has cooldown animation enabled frame.icon[pos].cd = CreateFrame("Cooldown", nil, frame.icon[pos], "CooldownFrameTemplate")
local parent_cooldown_anim = frame.config and tonumber(frame.config.cooldown_anim) or 1
if parent_cooldown_anim == 1 then
frame.icon[pos].cd = CreateFrame("Model", nil, frame.icon[pos])
else
frame.icon[pos].cd = CreateFrame("Frame", nil, frame.icon[pos])
frame.icon[pos].cd.AdvanceTime = DoNothing
frame.icon[pos].cd.SetSequence = DoNothing
frame.icon[pos].cd.SetSequenceTime = DoNothing
end
frame.icon[pos].cd.pfCooldownStyleAnimation = 0 frame.icon[pos].cd.pfCooldownStyleAnimation = 0
frame.icon[pos].cd.pfCooldownType = "ALL" frame.icon[pos].cd.pfCooldownType = "ALL"
frame.icon[pos].cd:SetFrameLevel(48) frame.icon[pos].cd:SetFrameLevel(48)
@@ -2647,7 +2554,7 @@ function pfUI.uf:GetStatusValue(unit, pos)
elseif config == "healthmax" then elseif config == "healthmax" then
return unit:GetColor("health") .. pfUI.api.Abbreviate(rhpmax) return unit:GetColor("health") .. pfUI.api.Abbreviate(rhpmax)
elseif config == "healthperc" then elseif config == "healthperc" then
return unit:GetColor("health") .. ceil(hp / hpmax * 100) return unit:GetColor("health") .. ceil(hp / hpmax * 100) .. "%"
elseif config == "healthmiss" then elseif config == "healthmiss" then
local health = ceil(rhp - rhpmax) local health = ceil(rhp - rhpmax)
if UnitIsDead(unitstr) then if UnitIsDead(unitstr) then
@@ -2700,7 +2607,7 @@ function pfUI.uf:GetStatusValue(unit, pos)
return unit:GetColor("power") .. pfUI.api.Abbreviate(mpmax) return unit:GetColor("power") .. pfUI.api.Abbreviate(mpmax)
elseif config == "powerperc" then elseif config == "powerperc" then
local perc = UnitPowerMax(unitstr) > 0 and ceil(mp / mpmax * 100) or 0 local perc = UnitPowerMax(unitstr) > 0 and ceil(mp / mpmax * 100) or 0
return unit:GetColor("power") .. perc return unit:GetColor("power") .. perc .. "%"
elseif config == "powermiss" then elseif config == "powermiss" then
local power = ceil(mp - mpmax) local power = ceil(mp - mpmax)
if power == 0 then if power == 0 then
-2
View File
@@ -200,7 +200,6 @@ pfUI_translation["deDE"] = {
["Description Font"] = nil, ["Description Font"] = nil,
["Description Font Size"] = nil, ["Description Font Size"] = nil,
["Detailed Random Roll Announcement"] = nil, ["Detailed Random Roll Announcement"] = nil,
["Detect Enemy Buffs"] = nil,
["Disable"] = nil, ["Disable"] = nil,
["Disable Blizzard Castbar"] = nil, ["Disable Blizzard Castbar"] = nil,
["Disabled"] = nil, ["Disabled"] = nil,
@@ -873,7 +872,6 @@ pfUI_translation["deDE"] = {
["Website"] = nil, ["Website"] = nil,
["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = nil, ["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = nil,
["Whitelist"] = nil, ["Whitelist"] = nil,
["Who Search Unknown Classes (|cffffaaaaExperimental|r)"] = nil,
["Width"] = nil, ["Width"] = nil,
["Wobble Zoom"] = nil, ["Wobble Zoom"] = nil,
["Won"] = nil, ["Won"] = nil,
+6 -2
View File
@@ -87,6 +87,8 @@ pfUI_translation["enUS"] = {
["Buffs"] = nil, ["Buffs"] = nil,
["Buff Size"] = nil, ["Buff Size"] = nil,
["Buff Spacing"] = nil, ["Buff Spacing"] = nil,
["Buff X-Spacing"] = nil,
["Buff Y-Spacing"] = nil,
["Buffs Per Row"] = nil, ["Buffs Per Row"] = nil,
["Button"] = nil, ["Button"] = nil,
["Button already exists in pfMinimapButtons frame"] = nil, ["Button already exists in pfMinimapButtons frame"] = nil,
@@ -192,6 +194,8 @@ pfUI_translation["enUS"] = {
["Debuff Position"] = nil, ["Debuff Position"] = nil,
["Debuffs"] = nil, ["Debuffs"] = nil,
["Debuff Size"] = nil, ["Debuff Size"] = nil,
["Debuff X-Spacing"] = nil,
["Debuff Y-Spacing"] = nil,
["Debuffs Per Row"] = nil, ["Debuffs Per Row"] = nil,
["Decode"] = nil, ["Decode"] = nil,
["Default"] = nil, ["Default"] = nil,
@@ -205,7 +209,6 @@ pfUI_translation["enUS"] = {
["Description Font"] = nil, ["Description Font"] = nil,
["Description Font Size"] = nil, ["Description Font Size"] = nil,
["Detailed Random Roll Announcement"] = nil, ["Detailed Random Roll Announcement"] = nil,
["Detect Enemy Buffs"] = nil,
["Disable"] = nil, ["Disable"] = nil,
["Disable Blizzard Castbar"] = nil, ["Disable Blizzard Castbar"] = nil,
["Disabled"] = nil, ["Disabled"] = nil,
@@ -582,6 +585,8 @@ pfUI_translation["enUS"] = {
["Overwrite If Unit Is Attacking Others"] = nil, ["Overwrite If Unit Is Attacking Others"] = nil,
["Overwrite If Unit Is Attacking You"] = nil, ["Overwrite If Unit Is Attacking You"] = nil,
["Overwrite If Unit Is Casting"] = nil, ["Overwrite If Unit Is Casting"] = nil,
["Own Buff Size"] = nil,
["Own Debuff Size"] = nil,
["Owner Name"] = nil, ["Owner Name"] = nil,
["Pageable"] = nil, ["Pageable"] = nil,
["Paging Actionbar"] = nil, ["Paging Actionbar"] = nil,
@@ -895,7 +900,6 @@ pfUI_translation["enUS"] = {
["Website"] = nil, ["Website"] = nil,
["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = nil, ["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = nil,
["Whitelist"] = nil, ["Whitelist"] = nil,
["Who Search Unknown Classes (|cffffaaaaExperimental|r)"] = nil,
["Width"] = nil, ["Width"] = nil,
["Wobble Zoom"] = nil, ["Wobble Zoom"] = nil,
["Won"] = nil, ["Won"] = nil,
-2
View File
@@ -200,7 +200,6 @@ pfUI_translation["esES"] = {
["Description Font"] = "Fuente de descripción", ["Description Font"] = "Fuente de descripción",
["Description Font Size"] = "Tamaño de la fuente de descripción", ["Description Font Size"] = "Tamaño de la fuente de descripción",
["Detailed Random Roll Announcement"] = "Anuncio detallado para tirar los dados aleatoriamente", ["Detailed Random Roll Announcement"] = "Anuncio detallado para tirar los dados aleatoriamente",
["Detect Enemy Buffs"] = nil,
["Disable"] = "Desactivar", ["Disable"] = "Desactivar",
["Disable Blizzard Castbar"] = "Desactivar la barra de lanzamiento de Blizzard", ["Disable Blizzard Castbar"] = "Desactivar la barra de lanzamiento de Blizzard",
["Disabled"] = "Desactivado", ["Disabled"] = "Desactivado",
@@ -873,7 +872,6 @@ pfUI_translation["esES"] = {
["Website"] = "Sitio de web", ["Website"] = "Sitio de web",
["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "¡Bienvenido a |cff33ffccpf|cffffffffUI|r!\n\nSoy el asistente de instalación que te guiará con la configuración básica. Si prefieres, puedes hacer clic el botón \"Predeterminado\" para cargar la configuración por defecto. Si quieres abrir este diálogo de nuevo, ve a la configuración y haz clic en el botón \"Reiniciar Primera vez\".\n\nVisita |cff33ffcc%s|r para descargar la última versión.", ["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "¡Bienvenido a |cff33ffccpf|cffffffffUI|r!\n\nSoy el asistente de instalación que te guiará con la configuración básica. Si prefieres, puedes hacer clic el botón \"Predeterminado\" para cargar la configuración por defecto. Si quieres abrir este diálogo de nuevo, ve a la configuración y haz clic en el botón \"Reiniciar Primera vez\".\n\nVisita |cff33ffcc%s|r para descargar la última versión.",
["Whitelist"] = "Lista blanca", ["Whitelist"] = "Lista blanca",
["Who Search Unknown Classes (|cffffaaaaExperimental|r)"] = nil,
["Width"] = "Ancho", ["Width"] = "Ancho",
["Wobble Zoom"] = "Acerca tambalea", ["Wobble Zoom"] = "Acerca tambalea",
["Won"] = "Ganado", ["Won"] = "Ganado",
-2
View File
@@ -200,7 +200,6 @@ pfUI_translation["frFR"] = {
["Description Font"] = "Description de la Police", ["Description Font"] = "Description de la Police",
["Description Font Size"] = "Description de la taille de la Police", ["Description Font Size"] = "Description de la taille de la Police",
["Detailed Random Roll Announcement"] = "Annonce détaillée des Rolls aléatoires", ["Detailed Random Roll Announcement"] = "Annonce détaillée des Rolls aléatoires",
["Detect Enemy Buffs"] = nil,
["Disable"] = "Désactiver", ["Disable"] = "Désactiver",
["Disable Blizzard Castbar"] = "Désactiver la barre d'incantation d'origine", ["Disable Blizzard Castbar"] = "Désactiver la barre d'incantation d'origine",
["Disabled"] = "Désactivé", ["Disabled"] = "Désactivé",
@@ -873,7 +872,6 @@ pfUI_translation["frFR"] = {
["Website"] = "Site web", ["Website"] = "Site web",
["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "Bienvenue sur l'assistant d'installation de l'interface |cff33ffccpf|cffffffffUI|r!\n\nNous allons effectuer quelques configurations de base. Si vous n'avez pas la patience, cliquer sur le bouton \"Paramètres par défaut\". Si vous souhaitez relancer cette fenêtre de dialogue, allez dans les options et cliquez sur le bouton\"Réinitialiser l'assistant d'installation\" .\n\nAllez sur |cff33ffcc%s|r pour télécharger la dernière version.", ["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "Bienvenue sur l'assistant d'installation de l'interface |cff33ffccpf|cffffffffUI|r!\n\nNous allons effectuer quelques configurations de base. Si vous n'avez pas la patience, cliquer sur le bouton \"Paramètres par défaut\". Si vous souhaitez relancer cette fenêtre de dialogue, allez dans les options et cliquez sur le bouton\"Réinitialiser l'assistant d'installation\" .\n\nAllez sur |cff33ffcc%s|r pour télécharger la dernière version.",
["Whitelist"] = "List blanche", ["Whitelist"] = "List blanche",
["Who Search Unknown Classes (|cffffaaaaExperimental|r)"] = nil,
["Width"] = "Largeur", ["Width"] = "Largeur",
["Wobble Zoom"] = "Zoom oscillant", ["Wobble Zoom"] = "Zoom oscillant",
["Won"] = "A gagné", ["Won"] = "A gagné",
-2
View File
@@ -200,7 +200,6 @@ pfUI_translation["koKR"] = {
["Description Font"] = nil, ["Description Font"] = nil,
["Description Font Size"] = nil, ["Description Font Size"] = nil,
["Detailed Random Roll Announcement"] = nil, ["Detailed Random Roll Announcement"] = nil,
["Detect Enemy Buffs"] = nil,
["Disable"] = "가리기", ["Disable"] = "가리기",
["Disable Blizzard Castbar"] = "블리자드 캐스트바 사용 중지", ["Disable Blizzard Castbar"] = "블리자드 캐스트바 사용 중지",
["Disabled"] = "가리기", ["Disabled"] = "가리기",
@@ -873,7 +872,6 @@ pfUI_translation["koKR"] = {
["Website"] = nil, ["Website"] = nil,
["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "|cff33ffccpf|cffffffffUI|r에 |cff33ffcc오신걸 환영합니다.|r \n\n저는 몇 가지 기본 구성을 안내 할 첫 번째 실행 위자드입니다. 모든 단계를 건너 뛰고 싶으시면 \"기본값 사용\" 버튼을 누르십시오. 이 대화 상자를 다시 실행하려면 추후 설정으로 이동하여 \"첫단계로 재설정\" 버튼을 누르십시오.\n\n최신버전 다운로드 사이트: |cff33ffcc%s|r", ["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "|cff33ffccpf|cffffffffUI|r에 |cff33ffcc오신걸 환영합니다.|r \n\n저는 몇 가지 기본 구성을 안내 할 첫 번째 실행 위자드입니다. 모든 단계를 건너 뛰고 싶으시면 \"기본값 사용\" 버튼을 누르십시오. 이 대화 상자를 다시 실행하려면 추후 설정으로 이동하여 \"첫단계로 재설정\" 버튼을 누르십시오.\n\n최신버전 다운로드 사이트: |cff33ffcc%s|r",
["Whitelist"] = nil, ["Whitelist"] = nil,
["Who Search Unknown Classes (|cffffaaaaExperimental|r)"] = nil,
["Width"] = nil, ["Width"] = nil,
["Wobble Zoom"] = nil, ["Wobble Zoom"] = nil,
["Won"] = nil, ["Won"] = nil,
-2
View File
@@ -200,7 +200,6 @@ pfUI_translation["ruRU"] = {
["Description Font"] = "Шрифт описания", ["Description Font"] = "Шрифт описания",
["Description Font Size"] = "Размер шрифта описания", ["Description Font Size"] = "Размер шрифта описания",
["Detailed Random Roll Announcement"] = "Подробное объявление о случайном броске", ["Detailed Random Roll Announcement"] = "Подробное объявление о случайном броске",
["Detect Enemy Buffs"] = nil,
["Disable"] = "Отключить", ["Disable"] = "Отключить",
["Disable Blizzard Castbar"] = "Отключить панель применения Blizzard", ["Disable Blizzard Castbar"] = "Отключить панель применения Blizzard",
["Disabled"] = "Отключено", ["Disabled"] = "Отключено",
@@ -873,7 +872,6 @@ pfUI_translation["ruRU"] = {
["Website"] = "Веб-сайт", ["Website"] = "Веб-сайт",
["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "Добро пожаловать в |cff33ffccpf|cffffffffUI|r!\n\nЯ мастер первого запуска, который проведет вас через некоторую базовую конфигурацию. Если вы ленитесь, не стесняйтесь нажать кнопку \"По умолчанию\". Если вы хотите снова запустить этот диалог, перейдите к настройкам и нажмите кнопку \"Первый запуск\".\n\nПосетите |cff33ffcc%s|r, чтобы проверить последнюю версию.", ["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "Добро пожаловать в |cff33ffccpf|cffffffffUI|r!\n\nЯ мастер первого запуска, который проведет вас через некоторую базовую конфигурацию. Если вы ленитесь, не стесняйтесь нажать кнопку \"По умолчанию\". Если вы хотите снова запустить этот диалог, перейдите к настройкам и нажмите кнопку \"Первый запуск\".\n\nПосетите |cff33ffcc%s|r, чтобы проверить последнюю версию.",
["Whitelist"] = "\"Белый\" список", ["Whitelist"] = "\"Белый\" список",
["Who Search Unknown Classes (|cffffaaaaExperimental|r)"] = nil,
["Width"] = "Ширина", ["Width"] = "Ширина",
["Wobble Zoom"] = "Подпрыгивание", ["Wobble Zoom"] = "Подпрыгивание",
["Won"] = "Победа", ["Won"] = "Победа",
-2
View File
@@ -200,7 +200,6 @@ pfUI_translation["zhCN"] = {
["Description Font"] = "描述字体", ["Description Font"] = "描述字体",
["Description Font Size"] = "描述字体大小", ["Description Font Size"] = "描述字体大小",
["Detailed Random Roll Announcement"] = "通告详细随机Roll点", ["Detailed Random Roll Announcement"] = "通告详细随机Roll点",
["Detect Enemy Buffs"] = "探测敌方Buffs",
["Disable"] = "禁用", ["Disable"] = "禁用",
["Disable Blizzard Castbar"] = "隐藏暴雪施法条", ["Disable Blizzard Castbar"] = "隐藏暴雪施法条",
["Disabled"] = "不显示", ["Disabled"] = "不显示",
@@ -874,7 +873,6 @@ pfUI_translation["zhCN"] = {
["Website"] = "网站", ["Website"] = "网站",
["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "欢迎使用 |cff33ffccpf|cffffffffUI|r! \n\n这是第一次运行向导,将引导您完成一些基本配置。 如果你想跳过所有步骤\"使用默认\"按钮。 如果您再次运行此对话框,请转到设置并点击 \"Reset Firstrun\" 按钮。\n\n访问 |cff33ffcc%s|r 检查最新版本。", ["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "欢迎使用 |cff33ffccpf|cffffffffUI|r! \n\n这是第一次运行向导,将引导您完成一些基本配置。 如果你想跳过所有步骤\"使用默认\"按钮。 如果您再次运行此对话框,请转到设置并点击 \"Reset Firstrun\" 按钮。\n\n访问 |cff33ffcc%s|r 检查最新版本。",
["Whitelist"] = "白名单", ["Whitelist"] = "白名单",
["Who Search Unknown Classes (|cffffaaaaExperimental|r)"] = nil,
["Width"] = "宽度", ["Width"] = "宽度",
["Wobble Zoom"] = "摆动变焦", ["Wobble Zoom"] = "摆动变焦",
["Won"] = "获胜", ["Won"] = "获胜",
-2
View File
@@ -200,7 +200,6 @@ pfUI_translation["zhTW"] = {
["Description Font"] = nil, ["Description Font"] = nil,
["Description Font Size"] = nil, ["Description Font Size"] = nil,
["Detailed Random Roll Announcement"] = "通告詳細隨機Roll點", ["Detailed Random Roll Announcement"] = "通告詳細隨機Roll點",
["Detect Enemy Buffs"] = nil,
["Disable"] = "禁用", ["Disable"] = "禁用",
["Disable Blizzard Castbar"] = "隱藏暴雪施法條", ["Disable Blizzard Castbar"] = "隱藏暴雪施法條",
["Disabled"] = "不顯示", ["Disabled"] = "不顯示",
@@ -873,7 +872,6 @@ pfUI_translation["zhTW"] = {
["Website"] = nil, ["Website"] = nil,
["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "歡迎使用 |cff33ffccpf|cffffffffUI|r! \n\n這是第一次運行嚮導,將引導您完成一些基本配置。 如果你想跳過所有步驟\"使用預設\"按鈕。 如果您再次運行此對話方塊,請轉到設置並點擊 \"Reset Firstrun\" 按鈕。\n\n訪問 |cff33ffcc%s|r 檢查最新版本。", ["Welcome to |cff33ffccpf|cffffffffUI|r!\n\nI'm the first run wizard that will guide you through some basic configuration. If you're lazy, feel free to hit the \"Defaults\" button. If you wish to run this dialog again, go to the settings and hit the \"Reset Firstrun\" button.\n\nVisit |cff33ffcc%s|r to check for the latest version."] = "歡迎使用 |cff33ffccpf|cffffffffUI|r! \n\n這是第一次運行嚮導,將引導您完成一些基本配置。 如果你想跳過所有步驟\"使用預設\"按鈕。 如果您再次運行此對話方塊,請轉到設置並點擊 \"Reset Firstrun\" 按鈕。\n\n訪問 |cff33ffcc%s|r 檢查最新版本。",
["Whitelist"] = "白名單", ["Whitelist"] = "白名單",
["Who Search Unknown Classes (|cffffaaaaExperimental|r)"] = nil,
["Width"] = nil, ["Width"] = nil,
["Wobble Zoom"] = nil, ["Wobble Zoom"] = nil,
["Won"] = nil, ["Won"] = nil,
+1
View File
@@ -30,6 +30,7 @@
<Include file="..\skins\blizzard\options-sound.lua"/> <Include file="..\skins\blizzard\options-sound.lua"/>
<Include file="..\skins\blizzard\options-video.lua"/> <Include file="..\skins\blizzard\options-video.lua"/>
<Include file="..\skins\blizzard\options-new.lua"/> <Include file="..\skins\blizzard\options-new.lua"/>
<Include file="..\skins\blizzard\options-interface.lua"/>
<Include file="..\skins\blizzard\petition.lua"/> <Include file="..\skins\blizzard\petition.lua"/>
<Include file="..\skins\blizzard\taxi.lua"/> <Include file="..\skins\blizzard\taxi.lua"/>
<Include file="..\skins\blizzard\tooltips.lua"/> <Include file="..\skins\blizzard\tooltips.lua"/>
+37 -14
View File
@@ -18,12 +18,7 @@ local ItemQuality = Enum.ItemQuality
local function SortCategoryPrefix(itemId, classID, quality) local function SortCategoryPrefix(itemId, classID, quality)
if itemId == HEARTHSTONE_ITEM_ID then return "00" end if itemId == HEARTHSTONE_ITEM_ID then return "00" end
if quality == ItemQuality.Poor then return "13" end -- gray always last if quality == ItemQuality.Poor then return "13" end -- gray always last
if classID == ItemClass.Weapon or classID == ItemClass.Armor then if classID == ItemClass.Weapon or classID == ItemClass.Armor then return "01" end
if quality and quality >= ItemQuality.Epic then return "01" end -- Epic+ gear
if quality == ItemQuality.Rare then return "02" end -- Rare gear
if quality == ItemQuality.Uncommon then return "03" end -- Uncommon gear
return "04" -- Common/poor gear
end
if classID == ItemClass.Consumable then return "05" end if classID == ItemClass.Consumable then return "05" end
if classID == ItemClass.Reagent then return "06" end if classID == ItemClass.Reagent then return "06" end
if classID == ItemClass.Tradegoods then return "07" end if classID == ItemClass.Tradegoods then return "07" end
@@ -42,12 +37,40 @@ local function SortCountSuffix(count)
return string.sub(s, -6) return string.sub(s, -6)
end end
local function SortKey(itemId, name, classID, subClassID, quality, count) -- Gear's slot order, keyed by the equipLoc token (INVTYPE_*). Not the
-- Zero-pad the class/subclass so the secondary grouping sorts numerically -- token's alphabetical order, which puts chest before head, and not its
-- (as a string, "10" would otherwise precede "2"). -- INVTYPE number, which parts robe (20) from chest (5). Same order as
return SortCategoryPrefix(itemId, classID, quality) -- ClassicAPI's C_Container.SortBags, taken from Baganator: weapons by hand,
.. string.format("%02d|%02d|", classID or 99, subClassID or 99) -- ranged and ammo, then armor with shields and off-hands first, head to
.. (name or "zzz") .. "|" .. SortCountSuffix(count) -- feet, then jewelry, shirt and tabard.
local SLOT_ORDER = {
"INVTYPE_2HWEAPON", "INVTYPE_WEAPON", "INVTYPE_WEAPONMAINHAND",
"INVTYPE_SHIELD", "INVTYPE_HOLDABLE", "INVTYPE_RANGEDRIGHT",
"INVTYPE_WEAPONOFFHAND", "INVTYPE_RANGED", "INVTYPE_THROWN",
"INVTYPE_AMMO", "INVTYPE_QUIVER", "INVTYPE_RELIC",
"INVTYPE_HEAD", "INVTYPE_SHOULDER", "INVTYPE_CLOAK", "INVTYPE_CHEST",
"INVTYPE_ROBE", "INVTYPE_WRIST", "INVTYPE_HAND", "INVTYPE_WAIST",
"INVTYPE_LEGS", "INVTYPE_FEET",
"INVTYPE_NECK", "INVTYPE_FINGER", "INVTYPE_TRINKET", "INVTYPE_BODY",
"INVTYPE_TABARD", "INVTYPE_BAG",
}
local SLOT_RANK = {}
for i, token in ipairs(SLOT_ORDER) do SLOT_RANK[token] = i end
local function SortKey(itemId, name, classID, subClassID, quality, count, equipLoc)
-- Zero-pad the numeric fields so they sort numerically as strings ("10"
-- would otherwise precede "2").
local key = SortCategoryPrefix(itemId, classID, quality)
.. string.format("%02d|", classID or 99)
if classID == ItemClass.Weapon or classID == ItemClass.Armor then
-- Gear: slot, then subtype, then best quality first, so all helms sit
-- together, then all shoulders.
key = key .. string.format("%02d|%02d|%d|",
SLOT_RANK[equipLoc] or 99, subClassID or 99, 9 - (quality or 0))
else
key = key .. string.format("%02d|", subClassID or 99)
end
return key .. (name or "zzz") .. "|" .. SortCountSuffix(count)
end end
local function ClearSortData() local function ClearSortData()
@@ -168,10 +191,10 @@ local function BuildSortGrid()
-- sit at positions 12/13. We categorize on those numeric class IDs -- sit at positions 12/13. We categorize on those numeric class IDs
-- rather than the localized itemType/itemSubType strings. (pfUI's -- rather than the localized itemType/itemSubType strings. (pfUI's
-- shimmed global GetItemInfo is only 10 fields and lacks them.) -- shimmed global GetItemInfo is only 10 fields and lacks them.)
local name, _, quality, _, _, _, _, _, _, _, _, classID, subClassID = C_Item.GetItemInfo(itemId) local name, _, quality, _, _, _, _, _, equipLoc, _, _, classID, subClassID = C_Item.GetItemInfo(itemId)
local _, count = GetContainerItemInfo(bag, slot) local _, count = GetContainerItemInfo(bag, slot)
local item = { local item = {
key = SortKey(itemId, name, classID, subClassID, quality, count), key = SortKey(itemId, name, classID, subClassID, quality, count, equipLoc),
-- vanilla items carry at most one family bit, so equality -- vanilla items carry at most one family bit, so equality
-- against a bag family suffices (no bit.band needed). -- against a bag family suffices (no bit.band needed).
family = C_Item.GetItemFamily(itemId) or 0, family = C_Item.GetItemFamily(itemId) or 0,
+8 -1616
View File
File diff suppressed because it is too large Load Diff
+159 -206
View File
@@ -13,34 +13,23 @@ setfenv(1, pfUI:GetEnvironment())
-- including resurrections. It has an option to disable the sending of those -- including resurrections. It has an option to disable the sending of those
-- messages in case HealComm is already active. -- messages in case HealComm is already active.
-- --
-- HOT TRACKING INTEGRATION (NEW): -- Casts are read from ClassicAPI's UNIT_SPELLCAST_* events and heal amounts
-- With Nampower enabled, HoT tracking now primarily uses libdebuff's AURA_CAST -- from COMBAT_LOG_EVENT_UNFILTERED. GetHotDuration() reads live HoT timers
-- event system for accurate server-side buff/debuff tracking with full rank -- from C_UnitAuras (via libdebuff) and falls back to the HealComm-fed table.
-- protection. GetHotDuration() first checks libdebuff, then falls back to the
-- legacy prediction system for backwards compatibility with non-Nampower clients.
-- This provides:
-- - Accurate duration from server (no prediction needed)
-- - Automatic rank protection (lower ranks won't overwrite higher ranks)
-- - Support for multiple casters of same HoT on one target
-- - Zero event overhead (libdebuff already tracks all auras)
-- return instantly when another libpredict is already active -- return instantly when another libpredict is already active
if pfUI.api.libpredict then return end if pfUI.api.libpredict then return end
-- Check if libdebuff integration is available
local libdebuff_available = (pfUI.api.libdebuff and pfUI.api.libdebuff.GetBestAuraCast) and true or false
-- Check if Nampower is available for SPELL_FAILED events
local senttarget
local heals, ress, events, hots = {}, {}, {}, {} local heals, ress, events, hots = {}, {}, {}, {}
local spell_queue = { "DUMMY", "DUMMYRank 9", "TARGET" } local spell_queue = { "DUMMY", "DUMMYRank 9", "TARGET" }
local player = UnitName("player") local player = UnitName("player")
local cache, gear_string = {}, "" local cache, gear_string = {}, ""
local foreignCache = {} -- [casterName][spellKey] = amount, in-memory cache for other healers local foreignCache = {} -- [casterName][spellKey] = amount, in-memory cache for other healers
local rejuvDuration, renewDuration = 12, 15 --default durations local rejuvDuration, renewDuration = 12, 15 --default durations
local ressGuidToName = {} -- [casterGuid] = casterName, for SPELL_FAILED_OTHER cleanup local ressGuidToName = {} -- [casterGuid] = casterName, for interrupted-cast cleanup
local healGuidToName = {} -- [casterGuid] = casterName, for SPELL_FAILED_OTHER cleanup local healGuidToName = {} -- [casterGuid] = casterName, for interrupted-cast cleanup
local sentTargets = {} -- [castGUID] = target name of an own cast, from UNIT_SPELLCAST_SENT
local castTargets = {} -- [castGUID] = target name of a foreign cast, from UNIT_SPELLCAST_START
local ress_timers = {} -- [target][sender] = expiry_timestamp (60s rez window) local ress_timers = {} -- [target][sender] = expiry_timestamp (60s rez window)
local RESS_TIMEOUT = 60 -- Vanilla: rez offer expires after 60s local RESS_TIMEOUT = 60 -- Vanilla: rez offer expires after 60s
@@ -124,43 +113,25 @@ libpredict:SetScript("OnEvent", function()
if origOnEvent then origOnEvent() end if origOnEvent then origOnEvent() end
end) end)
-- SPELL_START_SELF: own cast started (heals + rez) -- Own cast-time spell started (heals + rez)
pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {} local function OnOwnCastStart(spellId, castGUID)
pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
local spellName = C_Spell.GetSpellName(spellId) local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
local pendingTarget = nil -- SENT names the unit the cast went out at; self and no-target casts
local pendingTargetGuid = nil -- carry none, so fall back to what the cast hooks recorded
local pending = pfUI.libpredict_pending_cast local target = sentTargets[castGUID] or spell_queue[3]
if pending and pending.spellName == spellName and pending.targetGuid local _, _, _, startMs, endMs = C_Spell.CastingInfo()
and pending.time and (GetTime() - pending.time) < 1 then local castTime = startMs and endMs - startMs or 0
-- If SPELL_CAST_EVENT target differs from SPELL_START_SELF target,
-- the client redirected the cast (e.g. offline target → selfcast).
-- Discard pending and trust the actual targetGuid from SPELL_START_SELF.
if pending.targetGuid == targetGuid then
local name = UnitName(pending.targetGuid)
if name and name ~= UNKNOWNOBJECT and name ~= UKNOWNBEING then
pendingTarget = name
pendingTargetGuid = pending.targetGuid
end
end
pending.spellId = nil
pending.spellName = nil
pending.targetGuid = nil
pending.time = nil
end
local target = pendingTarget or resolveNameFromGuid(targetGuid) or senttarget or spell_queue[3]
-- resolved GUID: prefer pendingTargetGuid (mouseover/click-to-cast), fall back to targetGuid
local resolvedTargetGuid = pendingTargetGuid or targetGuid
libpredict.sender.castGUID = castGUID
libpredict.sender.delayMs = 0
libpredict.sender.current_cast = spellName libpredict.sender.current_cast = spellName
libpredict.sender.current_cast_target = target libpredict.sender.current_cast_target = target
if L["resurrections"][spellName] then if L["resurrections"][spellName] then
if target then if target then
libpredict:Ress(player, target, casterGuid) libpredict:Ress(player, target, UnitGUID("player"))
libpredict.sender:SendHealCommMsg("Resurrection/" .. target .. "/start/") libpredict.sender:SendHealCommMsg("Resurrection/" .. target .. "/start/")
libpredict.sender:SendResCommMsg("RES " .. target) libpredict.sender:SendResCommMsg("RES " .. target)
libpredict.sender.resurrecting = true libpredict.sender.resurrecting = true
@@ -235,27 +206,17 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu
return -- skip the generic Heal call below return -- skip the generic Heal call below
end end
-- If the resolved heal target is itself hostile (e.g. self-cast heal while targeting an enemy), libpredict:Heal(player, target, amount, casttime)
-- store the heal under the player so the player frame shows the prediction. libpredict.sender:SendHealCommMsg("Heal/" .. (target or "") .. "/" .. amount .. "/" .. casttime .. "/")
-- Use resolvedTargetGuid (prefers pendingTargetGuid from mouseover/click-to-cast) so we
-- don't wrongly fall back to player when healing a friend via mouseover with a foe in target.
local healTarget = target
local unitTargetGuid = UnitGUID and UnitGUID("target")
local healTargetIsHostile = resolvedTargetGuid and unitTargetGuid
and resolvedTargetGuid == unitTargetGuid
and UnitExists("target")
and (UnitCanAttack("player", "target") or not UnitIsFriend("player", "target"))
if healTargetIsHostile then
healTarget = player
end
libpredict:Heal(player, healTarget, amount, casttime)
libpredict.sender:SendHealCommMsg("Heal/" .. (healTarget or "") .. "/" .. amount .. "/" .. casttime .. "/")
libpredict.sender.healing = true libpredict.sender.healing = true
end end
end end
-- SPELL_GO_SELF: own cast landed (HealStop + Regrowth timer) -- Own cast landed: HealStop, Regrowth timer, instant HoTs, own rez timer
pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId) local function OnOwnCastSucceeded(spellId, castGUID)
local target = sentTargets[castGUID] or spell_queue[3]
sentTargets[castGUID] = nil
libpredict:HealStop(player) libpredict:HealStop(player)
local spellName = C_Spell.GetSpellName(spellId) local spellName = C_Spell.GetSpellName(spellId)
if spellName == REGROWTH then if spellName == REGROWTH then
@@ -267,32 +228,57 @@ pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId)
end end
libpredict.sender.regrowth_timer = now + 0.1 libpredict.sender.regrowth_timer = now + 0.1
end end
libpredict.sender.castGUID = nil
libpredict.sender.current_cast = nil libpredict.sender.current_cast = nil
libpredict.sender.current_cast_target = 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.
local hotType
if spellName == REJUVENATION then hotType = "Reju"
elseif spellName == RENEW then hotType = "Renew"
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
libpredict:Hot(player, target, hotType, duration, nil, "UNIT_SPELLCAST_SUCCEEDED", rank)
libpredict.sender:SendHealCommMsg(hotType .. "/" .. target .. "/" .. duration .. "/" .. rank .. "/")
end
if isRezSpell(spellId) then
libpredict:RessSetTimer(player, target)
end
end end
-- SPELL_START_OTHER: foreign cast started (heals + rez) -- Foreign cast-time spell started (heals + rez)
-- Signature: fn(spellId, casterGuid, targetGuid, castTime) local function OnOtherCastStart(unit, spellId, castGUID)
pfUI.libdebuff_spell_start_other_hooks = pfUI.libdebuff_spell_start_other_hooks or {}
pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
local spellName = C_Spell.GetSpellName(spellId) local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
local casterGuid = UnitGUID(unit)
local casterName = resolveNameFromGuid(casterGuid) local casterName = resolveNameFromGuid(casterGuid)
if not casterName then return end if not casterName then return end
local targetName = UnitSpellTargetName(unit)
if not targetName then return end
castTargets[castGUID] = targetName
-- Resurrection cast -- Resurrection cast
if L["resurrections"][spellName] then if L["resurrections"][spellName] then
local targetName = resolveNameFromGuid(targetGuid) libpredict:Ress(casterName, targetName, casterGuid)
if targetName then
libpredict:Ress(casterName, targetName, casterGuid)
end
return return
end end
-- Heal cast: look up cached amount from a previous cast by this healer -- Heal cast: look up cached amount from a previous cast by this healer
local targetName = resolveNameFromGuid(targetGuid) local _, _, _, startMs, endMs = C_Spell.UnitCastingInfo(unit)
if not targetName then return end if not startMs then return end
local castTime = endMs - startMs
local rankStr = C_Spell.GetSpellSubtext(spellId) or "" local rankStr = C_Spell.GetSpellSubtext(spellId) or ""
local spellKey = spellName .. (rankStr or "") local spellKey = spellName .. (rankStr or "")
@@ -338,77 +324,26 @@ pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterG
libpredict:Heal(casterName, targetName, amount, castTime, casterGuid) libpredict:Heal(casterName, targetName, amount, castTime, casterGuid)
end end
-- SPELL_GO_SELF: own cast landed - HoTs + own rez timer -- Foreign cast landed: rez timer, or the predicted heal is fulfilled
-- Signature: fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7) local function OnOtherCastSucceeded(unit, spellId, castGUID)
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {} local casterGuid = UnitGUID(unit)
pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4, a5, a6, a7)
-- Instant HoTs — classify by name (rank-independent) instead of a
-- hardcoded per-rank ID table.
local spellName = C_Spell.GetSpellName(spellId)
local hotType
if spellName == REJUVENATION then hotType = "Reju"
elseif spellName == RENEW then hotType = "Renew"
end
if hotType then
local targetGuid = a4
local targetName = resolveNameFromGuid(targetGuid)
if targetName then
local duration
if hotType == "Reju" then duration = rejuvDuration or 12
elseif hotType == "Renew" then duration = renewDuration or 15
end
local rank = 0
local rankSub = C_Spell.GetSpellSubtext(spellId)
if rankSub and rankSub ~= "" then
rank = tonumber((string.gsub(rankSub, "Rank ", ""))) or 0
end
local playerName = UnitName("player")
libpredict:Hot(playerName, targetName, hotType, duration, nil, "SPELL_GO_SELF", rank)
local rankStr = tostring(rank)
if libpredict.sender and libpredict.sender.SendHealCommMsg then
libpredict.sender:SendHealCommMsg(hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/")
elseif IsInRaid() then
SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "RAID")
elseif IsInGroup() then
SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "PARTY")
end
end
end
-- Own rez landed: set timer
if isRezSpell(spellId) then
local targetGuid = a4
local targetName = resolveNameFromGuid(targetGuid)
local playerName = UnitName("player")
if playerName and targetName then
libpredict:RessSetTimer(playerName, targetName)
end
end
end
-- SPELL_GO_OTHER: foreign cast landed - rez timer + HealStop
-- Signature: fn(spellId, casterGuid, targetGuid)
pfUI.libdebuff_spell_go_other_hooks = pfUI.libdebuff_spell_go_other_hooks or {}
pfUI.libdebuff_spell_go_other_hooks["libpredict"] = function(spellId, casterGuid, targetGuid)
local casterName = resolveNameFromGuid(casterGuid) local casterName = resolveNameFromGuid(casterGuid)
if not casterName then return end if not casterName then return end
if isRezSpell(spellId) then if isRezSpell(spellId) then
local targetName = resolveNameFromGuid(targetGuid) local targetName = castTargets[castGUID]
if targetName then if targetName then
libpredict:RessSetTimer(casterName, targetName) libpredict:RessSetTimer(casterName, targetName)
end end
return return
end end
-- Heal landed → prediction fulfilled, clean up
libpredict:HealStop(casterName) libpredict:HealStop(casterName)
end end
-- SPELL_FAILED_OTHER: remove cancelled resses/heals -- Foreign cast interrupted: remove cancelled resses/heals
-- Signature: fn(casterGuid, spellId) local function OnOtherCastInterrupted(unit)
pfUI.libdebuff_spell_failed_other_hooks = pfUI.libdebuff_spell_failed_other_hooks or {} local casterGuid = UnitGUID(unit)
pfUI.libdebuff_spell_failed_other_hooks["libpredict"] = function(casterGuid, spellId)
if not casterGuid then return end if not casterGuid then return end
local ressName = ressGuidToName[casterGuid] local ressName = ressGuidToName[casterGuid]
if ressName then if ressName then
@@ -567,7 +502,7 @@ function libpredict:ParseChatMessage(sender, msg, comm)
libpredict:Heal(sender, target, heal, time) libpredict:Heal(sender, target, heal, time)
end end
elseif msgtype == "Ress" then elseif msgtype == "Ress" then
-- HealComm: only for other players (own resses tracked via SPELL_START_SELF hook) -- HealComm: only for other players (own resses tracked via UNIT_SPELLCAST_START)
local playerName = UnitName("player") local playerName = UnitName("player")
if sender ~= playerName then if sender ~= playerName then
local senderGuid local senderGuid
@@ -912,7 +847,7 @@ hooksecurefunc("CastSpell", function(id, bookType)
rankNum = tonumber((string.gsub(rank, "Rank ", ""))) or nil rankNum = tonumber((string.gsub(rank, "Rank ", ""))) or nil
end end
-- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback -- Instant HoTs: UNIT_SPELLCAST_SUCCEEDED and C_UnitAuras, hook method as fallback
if effect == REJUVENATION then if effect == REJUVENATION then
local target = spell_queue[3] local target = spell_queue[3]
@@ -976,7 +911,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
spell_queue[3] = target or default spell_queue[3] = target or default
end end
-- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback -- Instant HoTs: UNIT_SPELLCAST_SUCCEEDED and C_UnitAuras, hook method as fallback
if effect == REJUVENATION then if effect == REJUVENATION then
local hotTarget = target or default local hotTarget = target or default
@@ -1037,7 +972,7 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
rankNum = tonumber((string.gsub(rank, "Rank ", ""))) or nil rankNum = tonumber((string.gsub(rank, "Rank ", ""))) or nil
end end
-- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback -- Instant HoTs: UNIT_SPELLCAST_SUCCEEDED and C_UnitAuras, hook method as fallback
if effect == REJUVENATION then if effect == REJUVENATION then
local hotTarget = spell_queue[3] local hotTarget = spell_queue[3]
@@ -1127,15 +1062,17 @@ libpredict.sender:SetScript("OnUpdate", function()
end end
end) end)
-- Nampower events -- Own casts arrive as "player"; every other caster fires once per unit token
libpredict.sender:RegisterEvent("SPELL_FAILED_SELF") -- it currently holds (raidN, target, nameplateN, ...), so other-unit handlers
libpredict.sender:RegisterEvent("SPELL_DELAYED_SELF") -- drop the repeats by castGUID.
libpredict.sender:RegisterEvent("SPELL_HEAL_BY_SELF") libpredict.sender:RegisterEvent("UNIT_SPELLCAST_SENT")
libpredict.sender:RegisterEvent("SPELL_HEAL_BY_OTHER") -- populates foreignCache for other healers libpredict.sender:RegisterEvent("UNIT_SPELLCAST_START")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_DELAYED")
-- force cache updates libpredict.sender:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
libpredict.sender:RegisterUnitEvent("UNIT_INVENTORY_CHANGED", "player") libpredict.sender:RegisterEvent("UNIT_SPELLCAST_FAILED")
libpredict.sender:RegisterEvent("SKILL_LINES_CHANGED") libpredict.sender:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_STOP")
libpredict.sender:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
-- Shared cleanup helper for failed/interrupted casts -- Shared cleanup helper for failed/interrupted casts
local function onCastFailed() local function onCastFailed()
@@ -1145,7 +1082,7 @@ local function onCastFailed()
s:SendHealCommMsg("Healstop") s:SendHealCommMsg("Healstop")
s.healing = nil s.healing = nil
elseif s.resurrecting then elseif s.resurrecting then
local target = s.current_cast_target or senttarget or spell_queue[3] local target = s.current_cast_target or spell_queue[3]
libpredict:RessStop(UnitName("player")) libpredict:RessStop(UnitName("player"))
s:SendHealCommMsg("Resurrection/stop/") s:SendHealCommMsg("Resurrection/stop/")
s:SendResCommMsg("RESNO " .. (target or "")) s:SendResCommMsg("RESNO " .. (target or ""))
@@ -1157,81 +1094,103 @@ local function onCastFailed()
s.regrowth_target_next = nil s.regrowth_target_next = nil
s.regrowth_start_next = nil s.regrowth_start_next = nil
end end
s.castGUID = nil
s.current_cast = nil s.current_cast = nil
s.current_cast_target = nil s.current_cast_target = nil
end end
libpredict.sender:SetScript("OnEvent", function() -- Real heal amounts: own heals refresh the spell cache, other group healers'
-- ============================================================ -- heals fill foreignCache (one event per hit target for AoE heals like PoH).
-- NAMPOWER PATH local function OnHeal()
-- ============================================================ -- arg4=sourceGUID, arg12=spellId, arg13=spellName, arg15=amount, arg18=critical
if event == "SPELL_HEAL_BY_SELF" then local spellId, spellName, amount, isCrit = arg12, arg13, arg15, arg18 == 1
-- arg1=targetGuid, arg2=casterGuid, arg3=spellId, arg4=amount, arg5=critical, arg6=periodic if not spellName or not amount or amount <= 0 then return end
local spellId = arg3
local amount = arg4 if IsPlayerGuid(arg4) then
local isCrit = arg5 == 1 if spell_queue[1] == spellName then
local isPeriodic = arg6 == 1
local spellName = C_Spell.GetSpellName(spellId)
if spellName and spell_queue[1] == spellName then
UpdateCache(spell_queue[2], amount, isCrit) UpdateCache(spell_queue[2], amount, isCrit)
end end
return
end
elseif event == "SPELL_HEAL_BY_OTHER" then local casterName = resolveNameFromGuid(arg4)
-- Fires once per hit target for AoE heals (e.g. PoH). if not casterName then return end
-- Use this to build a per-caster cache of real heal amounts.
-- arg1=targetGuid, arg2=casterGuid, arg3=spellId, arg4=amount, arg5=critical, arg6=periodic
local casterGuid = arg2
local spellId = arg3
local amount = arg4
local isCrit = arg5 == 1
local isPeriodic = arg6 == 1
if isPeriodic or not spellId or not amount or amount <= 0 then return end local spellKey = spellName .. (C_Spell.GetSpellSubtext(spellId) or "")
local casterName = resolveNameFromGuid(casterGuid) foreignCache[casterName] = foreignCache[casterName] or {}
if not casterName or casterName == player then return end -- own heals handled by SPELL_HEAL_BY_SELF local existing = foreignCache[casterName][spellKey]
-- Store highest non-crit value for best prediction accuracy
if isCrit then
-- Estimate base from crit (vanilla crit = 150%)
local base = math.floor(amount * 2 / 3)
if not existing or base > existing then
foreignCache[casterName][spellKey] = base
end
else
if not existing or amount > existing then
foreignCache[casterName][spellKey] = amount
end
end
end
local spellName = C_Spell.GetSpellName(spellId) local lastOtherCast = {} -- [event] = castGUID of the last other-unit event handled
if not spellName then return end
local rankStr = C_Spell.GetSpellSubtext(spellId)
local spellKey = spellName .. (rankStr or "")
foreignCache[casterName] = foreignCache[casterName] or {} libpredict.sender:SetScript("OnEvent", function()
local existing = foreignCache[casterName][spellKey] if event == "COMBAT_LOG_EVENT_UNFILTERED" then
-- Store highest non-crit value for best prediction accuracy if arg2 == "SPELL_HEAL" then OnHeal() end
if isCrit then return
-- Estimate base from crit (vanilla crit = 150%) end
local base = math.floor(amount * 2 / 3)
if not existing or base > existing then -- UNIT_SPELLCAST_SENT: arg1=unit, arg2=target token, arg3=castGUID, arg4=spellID
foreignCache[casterName][spellKey] = base -- every other UNIT_SPELLCAST_*: arg1=unit, arg2=castGUID, arg3=spellID
end local unit = arg1
else if unit == "player" then
if not existing or amount > existing then if event == "UNIT_SPELLCAST_SENT" then
foreignCache[casterName][spellKey] = amount sentTargets[arg3] = arg2 ~= "" and UnitName(arg2) or nil
elseif event == "UNIT_SPELLCAST_START" then
OnOwnCastStart(arg3, arg2)
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
OnOwnCastSucceeded(arg3, arg2)
elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
sentTargets[arg2] = nil
-- a refused cast (cooldown, range) fails under its own castGUID and
-- must not cancel the heal that is still casting
if arg2 == this.castGUID then onCastFailed() end
elseif event == "UNIT_SPELLCAST_DELAYED" then
-- delayTimeMs is the pushback so far; HealComm wants each step
local _, _, _, _, _, _, _, _, _, _, delayMs = C_Spell.CastingInfo()
if this.healing and delayMs and delayMs > this.delayMs then
local step = delayMs - this.delayMs
this.delayMs = delayMs
libpredict:HealDelay(player, step)
this:SendHealCommMsg("Healdelay/" .. step .. "/")
end end
end end
return
end
elseif event == "SPELL_FAILED_SELF" then if UnitIsUnit(unit, "player") or lastOtherCast[event] == arg2 then return end
onCastFailed() lastOtherCast[event] = arg2
elseif event == "SPELL_DELAYED_SELF" then
-- arg1=casterGuid, arg2=delayMs
if libpredict.sender.healing then
libpredict:HealDelay(player, arg2)
libpredict.sender:SendHealCommMsg("Healdelay/" .. arg2 .. "/")
end
if event == "UNIT_SPELLCAST_START" then
OnOtherCastStart(unit, arg3, arg2)
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
OnOtherCastSucceeded(unit, arg3, arg2)
elseif event == "UNIT_SPELLCAST_INTERRUPTED" then
OnOtherCastInterrupted(unit)
elseif event == "UNIT_SPELLCAST_STOP" then
castTargets[arg2] = nil
end end
end) end)
function libpredict:GetHotDuration(unit, spell) function libpredict:GetHotDuration(unit, spell)
if unit == UNKNOWNOBJECT or unit == UKNOWNBEING then return end if unit == UNKNOWNOBJECT or unit == UKNOWNBEING then return end
-- NEW: Try libdebuff first (Nampower AURA_CAST events) -- Live aura timer first (C_UnitAuras via libdebuff)
if pfUI.api.libdebuff and pfUI.api.libdebuff.GetBestAuraCast then if pfUI.api.libdebuff then
local guid = UnitGUID(unit) local guid = UnitGUID(unit)
if guid then if guid then
-- Get the best (highest rank) aura cast for this spell
local spellName = spell local spellName = spell
-- Map short spell codes to full names -- Map short spell codes to full names
@@ -1246,7 +1205,6 @@ function libpredict:GetHotDuration(unit, spell)
local start, duration, timeleft, rank, casterGuid = pfUI.api.libdebuff:GetBestAuraCast(guid, spellName) local start, duration, timeleft, rank, casterGuid = pfUI.api.libdebuff:GetBestAuraCast(guid, spellName)
if start and duration and timeleft then if start and duration and timeleft then
-- SUCCESS: libdebuff has accurate server-side data!
if libpredict.debug then if libpredict.debug then
DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[GetHotDuration]|r %s on %s via libdebuff: dur=%.1fs timeleft=%.1fs rank=%d", DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[GetHotDuration]|r %s on %s via libdebuff: dur=%.1fs timeleft=%.1fs rank=%d",
spell, unit, duration, timeleft, rank or 0)) spell, unit, duration, timeleft, rank or 0))
@@ -1256,7 +1214,7 @@ function libpredict:GetHotDuration(unit, spell)
end end
end end
-- FALLBACK: Use old prediction system (for non-Nampower clients or no AURA_CAST data) -- FALLBACK: the HealComm/cast-fed table, for HoTs C_UnitAuras has no timer for
local start, duration, timeleft local start, duration, timeleft
local now = pfUI.uf.now or GetTime() local now = pfUI.uf.now or GetTime()
@@ -1290,16 +1248,11 @@ _G.SlashCmdList.HOTDEBUG = function()
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[HoT Tracking Debug]|r") DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[HoT Tracking Debug]|r")
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff========================================|r") DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff========================================|r")
-- Check libdebuff availability if pfUI.api.libdebuff then
local libdebuff_now = (pfUI.api.libdebuff and pfUI.api.libdebuff.GetBestAuraCast) and true or false
if libdebuff_now then
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[PRIMARY]|r libdebuff integration: ACTIVE") DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[PRIMARY]|r libdebuff integration: ACTIVE")
DEFAULT_CHAT_FRAME:AddMessage(" Using AURA_CAST events for server-side tracking") DEFAULT_CHAT_FRAME:AddMessage(" Using C_UnitAuras timers")
DEFAULT_CHAT_FRAME:AddMessage(" Rank protection: ENABLED")
else else
DEFAULT_CHAT_FRAME:AddMessage("|cffff9900[PRIMARY]|r libdebuff integration: NOT AVAILABLE") DEFAULT_CHAT_FRAME:AddMessage("|cffff9900[PRIMARY]|r libdebuff integration: NOT AVAILABLE")
DEFAULT_CHAT_FRAME:AddMessage(" Reason: Nampower not enabled or libdebuff outdated")
end end
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[FALLBACK]|r Legacy prediction system: ACTIVE") DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[FALLBACK]|r Legacy prediction system: ACTIVE")
+95 -49
View File
@@ -81,6 +81,9 @@ pfUI:RegisterModule("actionbar", function ()
["ACTIONBAR_UPDATE_COOLDOWN"] = true, ["ACTIONBAR_UPDATE_COOLDOWN"] = true,
["ACTIONBAR_UPDATE_USABLE"] = true, ["ACTIONBAR_UPDATE_USABLE"] = true,
["ACTIONBAR_UPDATE_STATE"] = true, ["ACTIONBAR_UPDATE_STATE"] = true,
-- fires on every ammo shot / consumable use, but only counts, equipped
-- state, cooldowns and usability can change from it
["UNIT_INVENTORY_CHANGED"] = true,
} }
-- events that are shared across all buttons -- events that are shared across all buttons
@@ -97,8 +100,6 @@ pfUI:RegisterModule("actionbar", function ()
["TRADE_SKILL_CLOSE"] = true, ["TRADE_SKILL_CLOSE"] = true,
["PLAYER_ENTER_COMBAT"] = true, ["PLAYER_ENTER_COMBAT"] = true,
["PLAYER_LEAVE_COMBAT"] = true, ["PLAYER_LEAVE_COMBAT"] = true,
-- cooldown updates
["UNIT_INVENTORY_CHANGED"] = true,
-- auto repeat action -- auto repeat action
["START_AUTOREPEAT_SPELL"] = true, ["START_AUTOREPEAT_SPELL"] = true,
["STOP_AUTOREPEAT_SPELL"] = true, ["STOP_AUTOREPEAT_SPELL"] = true,
@@ -317,7 +318,7 @@ pfUI:RegisterModule("actionbar", function ()
-- handle button highlight -- handle button highlight
if keystate == "down" then if keystate == "down" then
self.highlight:Show() self.highlight:Show()
elseif not MouseIsOver(self) then elseif not MouseIsOverHitRect(self) then
self.highlight:Hide() self.highlight:Hide()
end end
end end
@@ -400,6 +401,28 @@ pfUI:RegisterModule("actionbar", function ()
GameTooltip:Hide() GameTooltip:Hide()
end end
-- item counts and equipped border, the parts of a slot an inventory change touches
local function ButtonInventoryUpdate(self)
if self.bar == 11 or self.bar == 12 then return end
local sid = self.id
-- update consumables
if IsConsumableAction(sid) then
self.count:SetText(GetActionCount(sid))
elseif IsReagentAction and IsReagentAction(sid) then
self.count:SetText(GetReagentCount(sid))
else
self.count:SetText(nil)
end
-- equipped item
if IsEquippedAction(sid) and C.bars.showequipped == "1" then
self.equipped:Show()
else
self.equipped:Hide()
end
end
local start, duration, enable, castable, autocast, token local start, duration, enable, castable, autocast, token
local grid, sid, id, bar, active, texture, _ local grid, sid, id, bar, active, texture, _
local function ButtonSlotUpdate(self) local function ButtonSlotUpdate(self)
@@ -460,23 +483,9 @@ pfUI:RegisterModule("actionbar", function ()
self.active:Hide() self.active:Hide()
end end
ButtonInventoryUpdate(self)
if self.bar ~= 11 and self.bar ~= 12 then if self.bar ~= 11 and self.bar ~= 12 then
-- update consumables
if IsConsumableAction(sid) then
self.count:SetText(GetActionCount(sid))
elseif IsReagentAction and IsReagentAction(sid) then
self.count:SetText(GetReagentCount(sid))
else
self.count:SetText(nil)
end
-- equipped item
if IsEquippedAction(sid) and C.bars.showequipped == "1" then
self.equipped:Show()
else
self.equipped:Hide()
end
-- update macro text -- update macro text
if C.bars["bar"..self.bar] and C.bars["bar"..self.bar].showmacro == "1" then if C.bars["bar"..self.bar] and C.bars["bar"..self.bar].showmacro == "1" then
self.macro:SetText(GetActionText(sid)) self.macro:SetText(GetActionText(sid))
@@ -696,11 +705,29 @@ pfUI:RegisterModule("actionbar", function ()
end end
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 -- run cached usable usable actions
if eventcache["ACTIONBAR_UPDATE_USABLE"] then if eventcache["ACTIONBAR_UPDATE_USABLE"] then
eventcache["ACTIONBAR_UPDATE_USABLE"] = nil eventcache["ACTIONBAR_UPDATE_USABLE"] = nil
for id, button in pairs(buttoncache) do for id, button in pairs(buttoncache) do
ButtonUsableUpdate(button) if button:IsVisible() then ButtonUsableUpdate(button) else button.stale = true end
end end
end end
@@ -708,7 +735,7 @@ pfUI:RegisterModule("actionbar", function ()
if eventcache["ACTIONBAR_UPDATE_COOLDOWN"] then if eventcache["ACTIONBAR_UPDATE_COOLDOWN"] then
eventcache["ACTIONBAR_UPDATE_COOLDOWN"] = nil eventcache["ACTIONBAR_UPDATE_COOLDOWN"] = nil
for id, button in pairs(buttoncache) do for id, button in pairs(buttoncache) do
ButtonCooldownUpdate(button) if button:IsVisible() then ButtonCooldownUpdate(button) else button.stale = true end
end end
end end
@@ -716,7 +743,7 @@ pfUI:RegisterModule("actionbar", function ()
if eventcache["ACTIONBAR_UPDATE_STATE"] then if eventcache["ACTIONBAR_UPDATE_STATE"] then
eventcache["ACTIONBAR_UPDATE_STATE"] = nil eventcache["ACTIONBAR_UPDATE_STATE"] = nil
for id, button in pairs(buttoncache) do for id, button in pairs(buttoncache) do
ButtonIsActiveUpdate(button) if button:IsVisible() then ButtonIsActiveUpdate(button) else button.stale = true end
end end
end end
@@ -738,7 +765,13 @@ pfUI:RegisterModule("actionbar", function ()
if ( this.tick or .2) > GetTime() then return else this.tick = GetTime() + .2 end if ( this.tick or .2) > GetTime() then return else this.tick = GetTime() + .2 end
for id, button in pairs(buttoncache) do for id, button in pairs(buttoncache) do
if button:IsShown() then ButtonRangeUpdate(button) end if button.stale and button:IsVisible() then
-- catch up on updates skipped while hidden
button.stale = nil
ButtonFullUpdate(button)
elseif button:IsShown() then
ButtonRangeUpdate(button)
end
end end
end end
@@ -828,17 +861,28 @@ pfUI:RegisterModule("actionbar", function ()
do do
local formpage, shift, ctrl, alt, default = 8, 6, 5, 3, 1 local formpage, shift, ctrl, alt, default = 8, 6, 5, 3, 1
-- set temporary pagemaster bindings keybinds -- while a page modifier is held, MOD-key must run the main bar binding on
if C.bars.pagemaster == "1" then -- that key so the switched page fires. Overrides do this without touching
local modifier = { "ALT", "SHIFT", "CTRL" } -- the saved binding set.
local buttons = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "=", "´" } local pagemods = {}
local current = CURRENT_ACTIONBAR_PAGE if C.bars.pagemasteralt == "1" then table.insert(pagemods, "ALT") end
bars.pagemaster = bars.pagemaster or CreateFrame("Frame", "pfPageMaster", UIParent) if C.bars.pagemasterctrl == "1" then table.insert(pagemods, "CTRL") end
bars.pagemaster:RegisterEvent("PLAYER_ENTERING_WORLD") if C.bars.pagemastershift == "1" then table.insert(pagemods, "SHIFT") end
bars.pagemaster:SetScript("OnEvent", function()
for _,mod in pairs(modifier) do if table.getn(pagemods) > 0 then
for _,but in pairs(buttons) do local pagemaster = CreateFrame("Frame", "pfPageMaster", UIParent)
SetBinding(mod.."-"..but) pagemaster:RegisterEvent("PLAYER_ENTERING_WORLD")
pagemaster:RegisterEvent("UPDATE_BINDINGS")
pagemaster:SetScript("OnEvent", function()
ClearOverrideBindings(this)
for i = 1, 12 do
for _, key in ipairs({ GetBindingKey("ACTIONBUTTON"..i) }) do
-- skip keys that already carry a modifier, e.g. "SHIFT-Q"
if not key:find("^%u+%-.") then
for _, mod in ipairs(pagemods) do
SetOverrideBinding(this, false, mod.."-"..key, "ACTIONBUTTON"..i)
end
end
end end
end end
end) end)
@@ -853,6 +897,10 @@ pfUI:RegisterModule("actionbar", function ()
pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM") pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
pageswitch:RegisterEvent("PLAYER_AURAS_CHANGED") pageswitch:RegisterEvent("PLAYER_AURAS_CHANGED")
pageswitch:RegisterEvent("PLAYER_LOGOUT") pageswitch:RegisterEvent("PLAYER_LOGOUT")
local PROWL_IDS = { [5215] = true, [6783] = true, [9913] = true }
if class == "DRUID" then
pageswitch:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player")
end
pageswitch:SetScript("OnEvent", function() pageswitch:SetScript("OnEvent", function()
if event == "PLAYER_LOGOUT" then if event == "PLAYER_LOGOUT" then
this:UnregisterAllEvents() this:UnregisterAllEvents()
@@ -875,6 +923,18 @@ pfUI:RegisterModule("actionbar", function ()
return return
end end
-- Eager prowl detection on the cast itself — zero-latency vs.
-- PLAYER_AURAS_CHANGED, which can land a frame or two behind it.
-- arg3 = spellID
if event == "UNIT_SPELLCAST_SUCCEEDED" then
if PROWL_IDS[arg3] then
inCatForm = true
prowlActive = true
formpaging = true
end
return
end
if event == "UPDATE_SHAPESHIFT_FORM" then if event == "UPDATE_SHAPESHIFT_FORM" then
-- Authoritative form-change signal — recompute and short-circuit. -- Authoritative form-change signal — recompute and short-circuit.
inCatForm = HasCatForm() inCatForm = HasCatForm()
@@ -898,20 +958,6 @@ pfUI:RegisterModule("actionbar", function ()
end end
end) end)
-- Eager prowl detection via Nampower SPELL_GO_SELF — zero-latency vs.
-- PLAYER_AURAS_CHANGED, which can land a frame or two behind the cast.
-- Form changes are no longer hooked here; UPDATE_SHAPESHIFT_FORM covers
-- those without needing a per-spellID watchlist.
local PROWL_IDS = { [5215] = true, [6783] = true, [9913] = true }
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
pfUI.libdebuff_spell_go_hooks["actionbar_prowl"] = function(spellId)
if class ~= "DRUID" then return end
if PROWL_IDS[spellId] then
inCatForm = true
prowlActive = true
formpaging = true
end
end
pageswitch:SetScript("OnUpdate", function() pageswitch:SetScript("OnUpdate", function()
-- switch actionbar page depending on meta key that is pressed -- switch actionbar page depending on meta key that is pressed
if C.bars.pagemastershift == "1" and IsShiftKeyDown() then if C.bars.pagemastershift == "1" and IsShiftKeyDown() then
@@ -997,7 +1043,7 @@ pfUI:RegisterModule("actionbar", function ()
f.slot = id f.slot = id
-- cooldown -- cooldown
f.cd = CreateFrame("Model", f:GetName() .. "Cooldown", f, "CooldownFrameTemplate") f.cd = CreateFrame("Cooldown", f:GetName() .. "Cooldown", f, "CooldownFrameTemplate")
f.cd.pfCooldownStyleAnimation = 1 f.cd.pfCooldownStyleAnimation = 1
f.cd.pfCooldownType = "NOGCD" f.cd.pfCooldownType = "NOGCD"
f.cd.pfCooldownSize = cd_size f.cd.pfCooldownSize = cd_size
+4 -3
View File
@@ -399,15 +399,16 @@ pfUI:RegisterModule("addonbuttons", function ()
-- Initial setup on the next frame, once other addons' minimap buttons exist -- Initial setup on the next frame, once other addons' minimap buttons exist
RunNextFrame(function() RunNextFrame(function()
-- check if the panel should be shown by default -- rescan before deciding whether the panel should be shown by default
pfUI.addonbuttons:ProcessButtons()
if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then
pfUI.addonbuttons:Show() pfUI.addonbuttons:Show()
else else
pfUI.addonbuttons:Hide() pfUI.addonbuttons:Hide()
end end
-- update all buttons and apply workarounds -- apply workarounds
pfUI.addonbuttons:ProcessButtons()
for k, v in pairs(pfUI.addonbuttons.overrides) do for k, v in pairs(pfUI.addonbuttons.overrides) do
_G[k] = v _G[k] = v
end end
-1
View File
@@ -9,7 +9,6 @@ pfUI:RegisterModule("addons", function ()
pfUI.addons:Show() pfUI.addons:Show()
HideUIPanel(GameMenuFrame) HideUIPanel(GameMenuFrame)
end) end)
SkinButton(pfUIAddonButton)
local point, relativeTo, relativePoint, xOffset, yOffset = GameMenuButtonOptions:GetPoint() local point, relativeTo, relativePoint, xOffset, yOffset = GameMenuButtonOptions:GetPoint()
GameMenuButtonOptions:SetPoint(point, relativeTo, relativePoint, xOffset, yOffset - 22) GameMenuButtonOptions:SetPoint(point, relativeTo, relativePoint, xOffset, yOffset - 22)
+3 -4
View File
@@ -396,14 +396,13 @@ pfUI:RegisterModule("bags", function ()
if tpl == "BankItemButtonGenericTemplate" then if tpl == "BankItemButtonGenericTemplate" then
local bankslot = pfUI.bags[bag].slots[slot].frame local bankslot = pfUI.bags[bag].slots[slot].frame
local name = "pfBag" .. bag .. "item" .. slot .. "Cooldown" local name = "pfBag" .. bag .. "item" .. slot .. "Cooldown"
bankslot.cd = CreateFrame("Model", name, bankslot, "CooldownFrameTemplate") bankslot.cd = CreateFrame("Cooldown", name, bankslot, "CooldownFrameTemplate")
bankslot.cd:SetAllPoints(bankslot)
bankslot.cd.pfCooldownStyleAnimation = 1 bankslot.cd.pfCooldownStyleAnimation = 1
bankslot.cd.pfCooldownType = "ALL" bankslot.cd.pfCooldownType = "NOGCD"
else else
local bagslot = pfUI.bags[bag].slots[slot].frame local bagslot = pfUI.bags[bag].slots[slot].frame
bagslot.cd = _G[bagslot:GetName().."Cooldown"] bagslot.cd = _G[bagslot:GetName().."Cooldown"]
bagslot.cd.pfCooldownType = "ALL" bagslot.cd.pfCooldownType = "NOGCD"
end end
if not pfUI.bags[bag].slots[slot].frame.backdrop then if not pfUI.bags[bag].slots[slot].frame.backdrop then
+1 -1
View File
@@ -19,7 +19,7 @@ pfUI:RegisterModule("buff", function ()
local function RefreshBuffButton(buff) local function RefreshBuffButton(buff)
if buff.btype == "HELPFUL" then if buff.btype == "HELPFUL" then
if C.buffs.separateweapons == "1" then if C.buffs.separateweapons == "1" then
buff.id = buff.gid - (buff.weapon ~= nill and buff.gid or 0) buff.id = buff.gid - (buff.weapon ~= nil and buff.gid or 0)
else else
buff.id = buff.gid - pfUI.buff.wepbuffs.count buff.id = buff.gid - pfUI.buff.wepbuffs.count
end end
+3 -34
View File
@@ -76,10 +76,6 @@ pfUI:RegisterModule("buffwatch", function ()
-- reusable GetAuraSlots buffer, filled once per RefreshBuffBarFrame -- reusable GetAuraSlots buffer, filled once per RefreshBuffBarFrame
local auraSlots = {} local auraSlots = {}
-- Separate buffer for the tooltip handler: OnEnter can fire while a refresh
-- is showing/hiding bars under the cursor, so it must not share the above.
local tooltipSlots = {}
-- Reads one aura by the slot id GetAuraSlots returned (nil slot -> nil). -- Reads one aura by the slot id GetAuraSlots returned (nil slot -> nil).
local function GetBuffData(unit, slot) local function GetBuffData(unit, slot)
local name, icon, count, dispelType, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unit, slot) local name, icon, count, dispelType, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unit, slot)
@@ -116,36 +112,9 @@ pfUI:RegisterModule("buffwatch", function ()
local function StatusBarOnEnter() local function StatusBarOnEnter()
GameTooltip:SetOwner(this, "NONE") GameTooltip:SetOwner(this, "NONE")
if this.unit == "player" then -- C_UnitAuras lists the player's own auras first, so the selfdebuff
GameTooltip:SetUnitAura("player", this.id, this.type) -- (HARMFUL|PLAYER) list is a prefix of the HARMFUL list: same index.
elseif this.type == "HARMFUL" then GameTooltip:SetUnitAura(this.unit, this.id, this.type)
-- selfdebuff filters the displayed list to player-cast harmful auras, but
-- SetUnitAura's index has to be into the unfiltered HARMFUL list. Look up
-- the displayed aura via the PLAYER filter, then find its position in the
-- unfiltered list by name + sourceGUID.
--
-- The unfiltered list is NOT capped at 16: once a unit's 16 debuff slots
-- are full the server parks further debuffs in buff slots, and
-- C_UnitAuras reports those as harmful too. So enumerate what it has.
local config = this.parent and this.parent.config
if config and config.selfdebuff == "1" then
local ownAura = C_UnitAuras.GetAuraDataByIndex(this.unit, this.id, "HARMFUL|PLAYER")
if ownAura then
local n = ScanAuraSlots(this.unit, "HARMFUL", tooltipSlots)
for i = 1, n do
local check = C_UnitAuras.GetAuraDataBySlot(this.unit, tooltipSlots[i])
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
GameTooltip:SetUnitAura(this.unit, i, "HARMFUL")
break
end
end
end
else
GameTooltip:SetUnitAura(this.unit, this.id, "HARMFUL")
end
elseif this.type == "HELPFUL" then
GameTooltip:SetUnitAura(this.unit, this.id, "HELPFUL")
end
if IsShiftKeyDown() then if IsShiftKeyDown() then
GameTooltip:AddLine(" ") GameTooltip:AddLine(" ")
+30 -14
View File
@@ -36,7 +36,7 @@ pfUI:RegisterModule("castbar", function ()
cb.bar:SetValue(1) cb.bar:SetValue(1)
end end
if cb.bar.spark then cb.bar.spark:Hide() end if cb.bar.spark then cb.bar.spark:Hide() end
cb.fadeout = 1 if cb:GetAlpha() > 0 and not cb.fade:IsPlaying() then cb.fade:Play() end
end end
-- "Spellname (N)" label for an active tradeskill merge — N is the number -- "Spellname (N)" label for an active tradeskill merge — N is the number
@@ -86,7 +86,7 @@ pfUI:RegisterModule("castbar", function ()
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate -- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
-- then animates the fill from this state without touching C_Spell. -- then animates the fill from this state without touching C_Spell.
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank) local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank, noInterrupt)
cb.startTime = startMs cb.startTime = startMs
cb.endTime = endMs cb.endTime = endMs
cb.isChannel = isChannel cb.isChannel = isChannel
@@ -94,10 +94,13 @@ pfUI:RegisterModule("castbar", function ()
cb.activeName = name cb.activeName = name
cb.isTradeskill = isTradeskill cb.isTradeskill = isTradeskill
cb.delay = (delayMs or 0) / 1000 cb.delay = (delayMs or 0) / 1000
cb.fade:Stop()
cb:SetAlpha(1) cb:SetAlpha(1)
cb.fadeout = nil
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"])) -- 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
-- cast it answers a question nobody asked.
SetCastbarShield(cb.bar, cb.icon, noInterrupt and cb.unitstr ~= "player", isChannel)
-- Rank: prefer the value the UNIT_SPELLCAST_* event delivered (arg5, passed -- Rank: prefer the value the UNIT_SPELLCAST_* event delivered (arg5, passed
-- through by RefreshBar). Only the retarget re-poll has no event in hand, so -- through by RefreshBar). Only the retarget re-poll has no event in hand, so
@@ -152,10 +155,10 @@ pfUI:RegisterModule("castbar", function ()
ClearBar(cb) ClearBar(cb)
return return
end end
local name, _, tex, startMs, endMs, isTradeskill, _, _, spellID, _, delayMs = C_Spell.UnitCastingInfo(query) local name, _, tex, startMs, endMs, isTradeskill, _, noInterrupt, spellID, _, delayMs = C_Spell.UnitCastingInfo(query)
local isChan local isChan
if not name then if not name then
name, _, tex, startMs, endMs, _, _, spellID = C_Spell.UnitChannelInfo(query) name, _, tex, startMs, endMs, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(query)
isChan = true isChan = true
end end
-- Synthetic fallback for abilities the engine treats as instant-cast but -- Synthetic fallback for abilities the engine treats as instant-cast but
@@ -169,7 +172,7 @@ pfUI:RegisterModule("castbar", function ()
end end
end end
if name and startMs and endMs then if name and startMs and endMs then
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill, rank) StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill, rank, noInterrupt)
else else
ClearBar(cb) ClearBar(cb)
end end
@@ -245,8 +248,14 @@ pfUI:RegisterModule("castbar", function ()
cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0) cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0)
cb.bar.spark:Hide() cb.bar.spark:Hide()
-- OnUpdate animates the bar fill and fades it out on completion. All -- Fade-out after a cast ends. The bar stays at alpha 0 while idle.
-- state transitions (cast start/stop/interrupt, channel start/stop, 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 -- pushback) come from the event handler below — we never poll C_Spell
-- here. -- here.
cb:SetScript("OnUpdate", function() cb:SetScript("OnUpdate", function()
@@ -254,15 +263,12 @@ pfUI:RegisterModule("castbar", function ()
this.tick = GetTime() + 0.020 -- ~50 FPS this.tick = GetTime() + 0.020 -- ~50 FPS
if this.drag and this.drag:IsShown() then if this.drag and this.drag:IsShown() then
this.fade:Stop()
this:SetAlpha(1) this:SetAlpha(1)
return return
end end
if this.fadeout and this:GetAlpha() > 0 then if this.fade:IsPlaying() then return end
this:SetAlpha(this:GetAlpha() - 0.05)
if this:GetAlpha() <= 0 then this.fadeout = nil end
return
end
if not this.endTime then if not this.endTime then
if this:GetAlpha() ~= 0 then this:SetAlpha(0) end if this:GetAlpha() ~= 0 then this:SetAlpha(0) end
@@ -342,6 +348,12 @@ pfUI:RegisterModule("castbar", function ()
elseif unitstr == "focus" then elseif unitstr == "focus" then
cb:RegisterEvent("PLAYER_FOCUS_CHANGED") cb:RegisterEvent("PLAYER_FOCUS_CHANGED")
end end
-- The shield can flip mid-cast (the caster gains or loses an aura that
-- blocks interrupts). These only fire for target and focus.
if unitstr == "target" or unitstr == "focus" then
cb:RegisterUnitEvent("UNIT_SPELLCAST_INTERRUPTIBLE", unitstr)
cb:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", unitstr)
end
cb:SetScript("OnEvent", function() cb:SetScript("OnEvent", function()
local unit = this.unitstr local unit = this.unitstr
@@ -397,6 +409,10 @@ pfUI:RegisterModule("castbar", function ()
if not this.tradeskillTotal then ClearBar(this) end if not this.tradeskillTotal then ClearBar(this) end
elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
ClearBar(this, true) ClearBar(this, true)
elseif event == "UNIT_SPELLCAST_INTERRUPTIBLE" or event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE" then
if this.endTime 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 elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then
-- Pushback: a cast delayed later or a channel shortened. Following -- Pushback: a cast delayed later or a channel shortened. Following
-- Quartz, re-poll just the times and accumulate the shift into a running -- Quartz, re-poll just the times and accumulate the shift into a running
+52 -61
View File
@@ -526,10 +526,10 @@ pfUI:RegisterModule("chat", function ()
if pfUI.chat.hideLock then return end if pfUI.chat.hideLock then return end
if MouseIsOver(pfUI.chat.left, 10, -10, -10, 10) then if pfUI.chat.left:IsMouseOver(10, -10, -10, 10) then
pfUI.chat.left.panelTop:Show() pfUI.chat.left.panelTop:Show()
FCF_DockUpdate() FCF_DockUpdate()
elseif MouseIsOver(pfUI.chat.right, 10, -10, -10, 10) then elseif pfUI.chat.right:IsMouseOver(10, -10, -10, 10) then
-- disable while dock is active -- disable while dock is active
if pfUI.chat.right:GetAlpha() == 0 then return end if pfUI.chat.right:GetAlpha() == 0 then return end
@@ -782,16 +782,6 @@ pfUI:RegisterModule("chat", function ()
local r,g,b = GetStringColor(C.chat.text.unknowncolor) local r,g,b = GetStringColor(C.chat.text.unknowncolor)
local unknowncolorhex = rgbhex(r,g,b) local unknowncolorhex = rgbhex(r,g,b)
-- Suppress FriendsFrame's WHO_LIST_UPDATE handling for our DLL-issued
-- name lookups so they don't pop the friends panel. User-issued /who
-- still works because IsWhoQueryPending only reports true for queries
-- issued via C_FriendList.SendWhoQueryByName.
local original_FriendsFrame_OnEvent = FriendsFrame_OnEvent
_G.FriendsFrame_OnEvent = function()
if _G.event == "WHO_LIST_UPDATE" and C_FriendList.IsWhoQueryPending() then return end
return original_FriendsFrame_OnEvent()
end
local function GetPlayerLevel(name) local function GetPlayerLevel(name)
if not pfUI_playerDB then return nil end if not pfUI_playerDB then return nil end
if not pfUI_playerDB[name] then return nil end if not pfUI_playerDB[name] then return nil end
@@ -806,6 +796,10 @@ pfUI:RegisterModule("chat", function ()
return frame:HookAddMessage(text, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) return frame:HookAddMessage(text, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17)
end end
-- 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()
-- Remove prat CLINKs -- 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-]-:[%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:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- vanilla
@@ -821,67 +815,64 @@ pfUI:RegisterModule("chat", function ()
text = pfUI.chat:HandleLink(text) text = pfUI.chat:HandleLink(text)
end end
-- display class colors if already indexed -- display class colors and player levels if already indexed
if C.chat.text.classcolor == "1" then local classcolor = C.chat.text.classcolor == "1"
for name in gfind(text, "|Hplayer:(.-)|h") do local playerlevel = C.chat.text.playerlevel == "1"
local real, _ = strsplit(":", name) if classcolor or playerlevel then
local color = unknowncolorhex -- the engine name-queries a chat sender before the event fires,
local match = false -- so the sender's GUID always resolves; other names in the text
local _, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real) -- fall back to the name-keyed lookups
-- local guid = GetCurrentChatGUID() local _, senderClass, senderRace, senderSex
-- if guid then if classcolor and guid and guid ~= "" then
-- _, class = GetPlayerInfoByGUID(guid) _, senderClass, _, senderRace, senderSex = GetPlayerInfoByGUID(guid)
-- end
if not class then
class = GetUnitInfo(real)
end
if class then
if class ~= UNKNOWN then
color = "|c" .. PFUI_CLASS_COLORS[class].colorStr
match = true
end
elseif C.chat.text.whosearchunknown == "1" then
C_FriendList.SendWhoQueryByName(name) -- cooldown + state managed by DLL
end
if C.chat.text.tintunknown == "1" or match then
local icon = ""
if match and C.chat.text.playericons == "1" then
icon = GetFactionIcon(raceKey) .. GetRaceIcon(raceKey, sex)
end
text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)",
left..icon..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1")
end
end end
end
-- display player levels if available
if C.chat.text.playerlevel == "1" then
for name in gfind(text, "|Hplayer:(.-)|h") do for name in gfind(text, "|Hplayer:(.-)|h") do
local real, _ = strsplit(":", name) local real, _ = strsplit(":", name)
local level = GetPlayerLevel(real) 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 if level and level > 0 then
local levelcolor = rgbhex(GetDifficultyColor(level)) suffix = " " .. rgbhex(GetDifficultyColor(level)) .. level .. "|r"
-- Add level after the player name, before the closing bracket elseif level then
text = string.gsub(text, "(|Hplayer:" .. name .. "|h.-|h|r)" .. right,
"%1 " .. levelcolor .. level .. "|r" .. right)
elseif level and level <= 0 then
-- Show ?? for unknown levels (e.g. -1 from UnitLevel) -- Show ?? for unknown levels (e.g. -1 from UnitLevel)
text = string.gsub(text, "(|Hplayer:" .. name .. "|h.-|h|r)" .. right, suffix = " |cffff0000??|r"
"%1 |cffff0000??|r" .. right) 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 end
end end
end end
-- reduce channel name to number -- reduce channel name to number
if C.chat.text.channelnumonly == "1" then if C.chat.text.channelnumonly == "1" and event == "CHAT_MSG_CHANNEL" then
local channel = string.gsub(text, ".*%[(.-)%]%s+(.*|Hplayer).+", "%1") text = string.gsub(text, "%[(%d+)%..-%] ", left .. "%1" .. right .. " ", 1)
if string.find(channel, "%d+%. ") then
channel = string.gsub(channel, "(%d+)%..*", "channel%1")
channel = string.gsub(channel, "channel", "")
text = string.gsub(text, "%[%d+%..-%]%s+(.*|Hplayer)", left .. channel .. right .. " %1")
end
end end
-- detect the whisper prefix BEFORE prepending a timestamp; the timestamp -- detect the whisper prefix BEFORE prepending a timestamp; the timestamp
+6 -4
View File
@@ -104,8 +104,10 @@ pfUI:RegisterModule("cooldown", function ()
return return
end end
-- realign cooldown frames -- realign the indicator Models that stock buttons build; the model is
local parent = this.GetParent and this:GetParent() -- drawn at 36px and needs scaling to its button. A Cooldown draws its
-- sweep to its own rect.
local parent = this.GetParent and not this:IsObjectType("Cooldown") and this:GetParent()
if parent and parent:GetWidth() / 36 > 0 then if parent and parent:GetWidth() / 36 > 0 then
this:SetScale(parent:GetWidth() / 36) this:SetScale(parent:GetWidth() / 36)
this:SetPoint("TOPLEFT", parent, "TOPLEFT", -1, 1) this:SetPoint("TOPLEFT", parent, "TOPLEFT", -1, 1)
@@ -144,7 +146,7 @@ pfUI:RegisterModule("cooldown", function ()
end end
end end
-- vanilla does not have a cooldown frame type, so we hook the -- hook the SetTimer function that every cooldown, Cooldown frame or
-- regular SetTimer function that each one is calling. -- stock indicator Model, is driven through.
hooksecurefunc("CooldownFrame_SetTimer", SetCooldown) hooksecurefunc("CooldownFrame_SetTimer", SetCooldown)
end) end)
+44 -7
View File
@@ -89,6 +89,13 @@ pfUI:RegisterModule("energytick", function()
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
end end
local rawborder, default_border = GetBorderSize("unitframes")
local cbtexture = pfUI.media[C.appearance.castbar.texture]
-- style is read once: switching it in the GUI needs a /reload, same as the
-- other structural (not cosmetic) toggles in this file
local fsrOverlay = C.unitframes.player.fsrstyle == "1"
-- was this gain the regen tick? if so, re-anchor the sweep on it -- was this gain the regen tick? if so, re-anchor the sweep on it
local function lockTick(frame) local function lockTick(frame)
local now, period = GetTime(), getAdjustedTickTimer() local now, period = GetTime(), getAdjustedTickTimer()
@@ -199,11 +206,21 @@ pfUI:RegisterModule("energytick", function()
end end
this.tick = GetTime() + 0.020 -- ~50 FPS this.tick = GetTime() + 0.020 -- ~50 FPS
-- five-second rule drains to nothing -- 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 local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0
if this.mode == "MANA" and remaining > 0 then if this.mode == "MANA" and remaining > 0 then
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE) if C.unitframes.player.showfsr == "1" then
this.fsrbar:Show() 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 else
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
this.fsrbar:Hide() this.fsrbar:Hide()
@@ -240,10 +257,26 @@ pfUI:RegisterModule("energytick", function()
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0) this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
end) end)
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK") if fsrOverlay then
energytick.fsrbar:SetTexture(1, 1, 1, .15) -- classic style: a translucent shade on top of the power bar's own fill
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0) energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0) energytick.fsrbar:SetTexture(1, 1, 1, .15)
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0)
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0)
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)
energytick.fsrbar:SetStatusBarColor(GetStringColor(C.appearance.castbar.fsrcolor))
CreateBackdrop(energytick.fsrbar, default_border)
CreateBackdropShadow(energytick.fsrbar)
end
energytick.fsrbar:Hide() energytick.fsrbar:Hide()
energytick.spark = energytick:CreateTexture(nil, "OVERLAY") energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
@@ -256,6 +289,10 @@ pfUI:RegisterModule("energytick", function()
function pfUI.uf.player.UpdateConfig() function pfUI.uf.player.UpdateConfig()
energytick.spark:SetHeight(C.unitframes.player.pheight + 15) energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
energytick.spark:SetWidth(C.unitframes.player.pheight + 5) energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
if not fsrOverlay then
energytick.fsrbar:SetHeight(tonumber(C.unitframes.player.fsrheight) or 4)
energytick.fsrbar:SetStatusBarColor(GetStringColor(C.appearance.castbar.fsrcolor))
end
hookUpdateConfig(pfUI.uf.player) hookUpdateConfig(pfUI.uf.player)
end end
end) end)
+25 -6
View File
@@ -176,8 +176,8 @@ pfUI:RegisterModule("equipmentmanager", function()
rowMenu:RegisterEvent("GLOBAL_MOUSE_DOWN") rowMenu:RegisterEvent("GLOBAL_MOUSE_DOWN")
rowMenu:SetScript("OnEvent", function() rowMenu:SetScript("OnEvent", function()
if not this:IsShown() then return end if not this:IsShown() then return end
if MouseIsOver(this) then return end if MouseIsOverHitRect(this) then return end
if this.anchorBtn and MouseIsOver(this.anchorBtn) then return end if this.anchorBtn and MouseIsOverHitRect(this.anchorBtn) then return end
this:Hide() this:Hide()
end) end)
tinsert(UISpecialFrames, "pfEqMgrRowMenu") -- Escape closes it tinsert(UISpecialFrames, "pfEqMgrRowMenu") -- Escape closes it
@@ -301,12 +301,22 @@ pfUI:RegisterModule("equipmentmanager", function()
pfUI.equipmentmanager.Refresh() pfUI.equipmentmanager.Refresh()
end) end)
-- Drag a set onto an action bar. PickupEquipmentSet is newer than
-- the rest of C_EquipmentSet, so dragging stays inert on ClassicAPI
-- builds that don't ship it yet.
if C_EquipmentSet.PickupEquipmentSet then
row:RegisterForDrag("LeftButton")
row:SetScript("OnDragStart", function()
if row.setID then C_EquipmentSet.PickupEquipmentSet(row.setID) end
end)
end
-- Show the gear and tooltip while the cursor is over the row or its -- Show the gear and tooltip while the cursor is over the row or its
-- gear child. The gear sits inside the row's rectangle, so a single -- gear child. The gear sits inside the row's rectangle, so a single
-- MouseIsOver(row) check covers both. OnLeave on the row and the gear -- MouseIsOverHitRect(row) check covers both. OnLeave on the row and the
-- catches every exit path, so no OnUpdate poll is needed. -- gear catches every exit path, so no OnUpdate poll is needed.
local function HideRowHover() local function HideRowHover()
if MouseIsOver(row) then return end if MouseIsOverHitRect(row) then return end
GameTooltip:Hide() GameTooltip:Hide()
row.gear:Hide() row.gear:Hide()
end end
@@ -586,6 +596,7 @@ pfUI:RegisterModule("equipmentmanager", function()
if i <= num then if i <= num then
local location = ordered[i] local location = ordered[i]
b.specialAction = nil; b.bag = nil; b.slot = nil; b.invSlot = nil b.specialAction = nil; b.bag = nil; b.slot = nil; b.invSlot = nil
local quality
if location == PLACEINBAGS_LOCATION then if location == PLACEINBAGS_LOCATION then
b.specialAction = "placeInBags" b.specialAction = "placeInBags"
b.texture:SetTexture(pfUI.path.."\\img\\UI-GearManager-ItemIntoBag") b.texture:SetTexture(pfUI.path.."\\img\\UI-GearManager-ItemIntoBag")
@@ -602,15 +613,23 @@ pfUI:RegisterModule("equipmentmanager", function()
local loc = EquipmentManager_GetLocationData(location) local loc = EquipmentManager_GetLocationData(location)
if loc.isBags then if loc.isBags then
b.bag = loc.bag; b.slot = loc.slot b.bag = loc.bag; b.slot = loc.slot
local tex, count = GetContainerItemInfo(loc.bag, loc.slot) local tex, count, _, q = GetContainerItemInfo(loc.bag, loc.slot)
quality = q
b.texture:SetTexture(tex) b.texture:SetTexture(tex)
if count and count > 1 then b.count:SetText(count) else b.count:SetText("") end if count and count > 1 then b.count:SetText(count) else b.count:SetText("") end
else -- isPlayer (equipped in some other slot) else -- isPlayer (equipped in some other slot)
b.invSlot = loc.slot b.invSlot = loc.slot
b.texture:SetTexture(GetInventoryItemTexture("player", loc.slot)) b.texture:SetTexture(GetInventoryItemTexture("player", loc.slot))
b.count:SetText("") b.count:SetText("")
quality = GetInventoryItemQuality("player", loc.slot)
end end
end end
-- Quality border, matching the character slots
if quality and quality > 0 then
b.backdrop:SetBackdropBorderColor(GetItemQualityColor(quality))
else
b.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
end
local col = math.mod(i - 1, FLYOUT_COLS) local col = math.mod(i - 1, FLYOUT_COLS)
local row = math.floor((i - 1) / FLYOUT_COLS) local row = math.floor((i - 1) / FLYOUT_COLS)
b:ClearAllPoints() b:ClearAllPoints()
+23 -7
View File
@@ -58,10 +58,11 @@ pfUI:RegisterModule("gui", function ()
return return
end end
if MouseIsOver(this) and not this.over then local over = MouseIsOverHitRect(this)
if over and not this.over then
this.tex:Show() this.tex:Show()
this.over = true this.over = true
elseif not MouseIsOver(this) and this.over then elseif not over and this.over then
this.tex:Hide() this.tex:Hide()
this.over = nil this.over = nil
end end
@@ -2072,9 +2073,6 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Combopoint Width"], C.unitframes, "combowidth") CreateConfig(nil, T["Combopoint Width"], C.unitframes, "combowidth")
CreateConfig(nil, T["Combopoint Height"], C.unitframes, "comboheight") CreateConfig(nil, T["Combopoint Height"], C.unitframes, "comboheight")
CreateConfig(nil, T["Show Resting"], C.unitframes.player, "showRest", "checkbox") CreateConfig(nil, T["Show Resting"], C.unitframes.player, "showRest", "checkbox")
CreateConfig(nil, T["Enable Energy Ticks"], C.unitframes.player, "energy", "checkbox")
CreateConfig(nil, T["Enable Mana Ticks"], C.unitframes.player, "manatick", "checkbox")
CreateConfig(nil, T["Detect Enemy Buffs"], C.unitframes, "buffdetect", "checkbox", nil, nil, nil, nil)
CreateConfig(nil, T["Raid Mark Icon Style"], C.unitframes, "blizzard_raidicons", "dropdown", function() return {"1:Original Blizzard", "0:pfUI Design"} end) CreateConfig(nil, T["Raid Mark Icon Style"], C.unitframes, "blizzard_raidicons", "dropdown", function() return {"1:Original Blizzard", "0:pfUI Design"} end)
CreateConfig(nil, T["Swing Timer"], nil, nil, "header") CreateConfig(nil, T["Swing Timer"], nil, nil, "header")
@@ -2320,6 +2318,15 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U[c], T["Right Text Y Offset"], C.unitframes[c], "txtpowerrightoffy") CreateConfig(U[c], T["Right Text Y Offset"], C.unitframes[c], "txtpowerrightoffy")
CreateConfig(U[c], T["Power Bar Anchor"], C.unitframes[c], "panchor", "dropdown", pfUI.gui.dropdowns.uf_powerbar_position) CreateConfig(U[c], T["Power Bar Anchor"], C.unitframes[c], "panchor", "dropdown", pfUI.gui.dropdowns.uf_powerbar_position)
if c == "player" then
CreateConfig(nil, T["Energy/Mana Ticks"], nil, nil, "header")
CreateConfig(nil, T["Enable Energy Ticks"], C.unitframes[c], "energy", "checkbox")
CreateConfig(nil, T["Enable Mana Ticks"], C.unitframes[c], "manatick", "checkbox")
CreateConfig(nil, T["Show Five Second Rule"], C.unitframes[c], "showfsr", "checkbox")
CreateConfig(nil, T["Five Second Rule Style"], C.unitframes[c], "fsrstyle", "dropdown", function() return {"0:Separate Bar", "1:Overlay"} end)
CreateConfig(nil, T["Five Second Rule Height"], C.unitframes[c], "fsrheight")
end
CreateConfig(U[c], T["Combat Text"], nil, nil, "header") CreateConfig(U[c], T["Combat Text"], nil, nil, "header")
CreateConfig(U[c], T["Show Combat Text"], C.unitframes[c], "hitindicator", "checkbox") CreateConfig(U[c], T["Show Combat Text"], C.unitframes[c], "hitindicator", "checkbox")
CreateConfig(U[c], T["Combat Text Font"], C.unitframes[c], "hitindicatorfont", "dropdown", pfUI.gui.dropdowns.fonts) CreateConfig(U[c], T["Combat Text Font"], C.unitframes[c], "hitindicatorfont", "dropdown", pfUI.gui.dropdowns.fonts)
@@ -2340,12 +2347,18 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U[c], T["Buff Size"], C.unitframes[c], "buffsize") CreateConfig(U[c], T["Buff Size"], C.unitframes[c], "buffsize")
CreateConfig(U[c], T["Buff Limit"], C.unitframes[c], "bufflimit") 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["Buffs Per Row"], C.unitframes[c], "buffperrow")
CreateConfig(U[c], T["Own Buff Size"], C.unitframes[c], "ownbuffsize")
CreateConfig(U[c], T["Buff X-Spacing"], C.unitframes[c], "buffspacingx")
CreateConfig(U[c], T["Buff Y-Spacing"], C.unitframes[c], "buffspacingy")
CreateConfig(U[c], T["Debuffs"], nil, nil, "header") CreateConfig(U[c], T["Debuffs"], nil, nil, "header")
CreateConfig(U[c], T["Debuff Position"], C.unitframes[c], "debuffs", "dropdown", pfUI.gui.dropdowns.uf_buff_position) CreateConfig(U[c], T["Debuff Position"], C.unitframes[c], "debuffs", "dropdown", pfUI.gui.dropdowns.uf_buff_position)
CreateConfig(U[c], T["Debuff Size"], C.unitframes[c], "debuffsize") CreateConfig(U[c], T["Debuff Size"], C.unitframes[c], "debuffsize")
CreateConfig(U[c], T["Debuff Limit"], C.unitframes[c], "debufflimit") 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["Debuffs Per Row"], C.unitframes[c], "debuffperrow")
CreateConfig(U[c], T["Own Debuff Size"], C.unitframes[c], "owndebuffsize")
CreateConfig(U[c], T["Debuff X-Spacing"], C.unitframes[c], "debuffspacingx")
CreateConfig(U[c], T["Debuff Y-Spacing"], C.unitframes[c], "debuffspacingy")
if c ~= "player" then if c ~= "player" then
end end
@@ -2792,6 +2805,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Use Unit Fonts"], C.castbar, "use_unitfonts", "checkbox") CreateConfig(nil, T["Use Unit Fonts"], C.castbar, "use_unitfonts", "checkbox")
CreateConfig(nil, T["Casting Color"], C.appearance.castbar, "castbarcolor", "color") CreateConfig(nil, T["Casting Color"], C.appearance.castbar, "castbarcolor", "color")
CreateConfig(nil, T["Channeling Color"], C.appearance.castbar, "channelcolor", "color") CreateConfig(nil, T["Channeling Color"], C.appearance.castbar, "channelcolor", "color")
CreateConfig(nil, T["Uninterruptible Color"], C.appearance.castbar, "shieldcolor", "color")
CreateConfig(nil, T["Five Second Rule Color"], C.appearance.castbar, "fsrcolor", "color")
CreateConfig(nil, T["Castbar Texture"], C.appearance.castbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture) CreateConfig(nil, T["Castbar Texture"], C.appearance.castbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, T["Disable Blizzard Castbar"], C.castbar.player, "hide_blizz", "checkbox") CreateConfig(nil, T["Disable Blizzard Castbar"], C.castbar.player, "hide_blizz", "checkbox")
end) end)
@@ -2864,9 +2879,10 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Generate Playerlinks"], C.chat.text, "playerlinks", "checkbox") CreateConfig(nil, T["Generate Playerlinks"], C.chat.text, "playerlinks", "checkbox")
CreateConfig(nil, T["Enable URL Detection"], C.chat.text, "detecturl", "checkbox") CreateConfig(nil, T["Enable URL Detection"], C.chat.text, "detecturl", "checkbox")
CreateConfig(nil, T["Enable Class Colors"], C.chat.text, "classcolor", "checkbox") CreateConfig(nil, T["Enable Class Colors"], C.chat.text, "classcolor", "checkbox")
CreateConfig(nil, T["Show Faction & Race Icons"] .. " " .. GetPlayerRaceIcons(), C.chat.text, "playericons", "checkbox") local _, playerRace = UnitRace("player")
CreateConfig(nil, T["Show Faction Icons"] .. " " .. GetFactionIcon(playerRace), C.chat.text, "factionicons", "checkbox")
CreateConfig(nil, T["Show Race Icons"] .. " " .. GetRaceIcon(playerRace, UnitSex("player")), C.chat.text, "raceicons", "checkbox")
CreateConfig(nil, T["Enable Player Levels"], C.chat.text, "playerlevel", "checkbox") CreateConfig(nil, T["Enable Player Levels"], C.chat.text, "playerlevel", "checkbox")
CreateConfig(nil, T["Who Search Unknown Classes (|cffffaaaaExperimental|r)"], C.chat.text, "whosearchunknown", "checkbox")
CreateConfig(nil, T["Colorize Unknown Classes"], C.chat.text, "tintunknown", "checkbox") CreateConfig(nil, T["Colorize Unknown Classes"], C.chat.text, "tintunknown", "checkbox")
CreateConfig(nil, T["Unknown Class Color"], C.chat.text, "unknowncolor", "color") CreateConfig(nil, T["Unknown Class Color"], C.chat.text, "unknowncolor", "color")
CreateConfig(nil, T["Left Chat Width"], C.chat.left, "width") CreateConfig(nil, T["Left Chat Width"], C.chat.left, "width")
+21 -5
View File
@@ -105,6 +105,10 @@ pfUI:RegisterModule("hoverbind", function ()
end end
end end
local function BindingName(command)
return _G["BINDING_NAME_" .. command] or command
end
function pfUI.hoverbind:GetPrefix() function pfUI.hoverbind:GetPrefix()
return string.format("%s%s%s", return string.format("%s%s%s",
(IsAltKeyDown() and modifiers.ALT or ""), (IsAltKeyDown() and modifiers.ALT or ""),
@@ -157,17 +161,29 @@ pfUI:RegisterModule("hoverbind", function ()
if arg1 == "ESCAPE" and not binding then pfUI.hoverbind:Hide() return end if arg1 == "ESCAPE" and not binding then pfUI.hoverbind:Hide() return end
if binding then if binding then
if arg1 == "ESCAPE" then if arg1 == "ESCAPE" then
-- Remove existing binding -- Remove all existing bindings
local key = (GetBindingKey(binding)) local keys = { GetBindingKey(binding) }
if (key) then for _, key in ipairs(keys) do
SetBinding(key) SetBinding(key)
end
if keys[1] then
SaveBindings(GetCurrentBindingSet()) SaveBindings(GetCurrentBindingSet())
end end
else else
-- Create new binding -- Create new binding
local key = map and map[arg1] or arg1 local key = prefix .. (map and map[arg1] or arg1)
if (SetBinding(prefix..key, binding)) then local previous = GetBindingAction(key)
if (SetBinding(key, binding)) then
SaveBindings(GetCurrentBindingSet()) SaveBindings(GetCurrentBindingSet())
if previous and previous ~= "" and previous ~= binding then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. key .. "|r " .. T["was unbound from"] .. " " .. BindingName(previous))
end
local active = GetBindingAction(key, true)
if active ~= binding then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. key .. "|r " .. T["is currently overridden by"] .. " " .. BindingName(active))
end
end end
end end
end end
+38 -58
View File
@@ -1,25 +1,13 @@
-- Innervate Callout Module -- Innervate Callout Module
-- Announces Innervate casts via raid/party/battleground chat -- Announces Innervate casts via raid/party/battleground chat
-- Registers AURA_CAST events directly - zero polling, pure event-driven -- Listens to COMBAT_LOG_EVENT_UNFILTERED - zero polling, pure event-driven
pfUI:RegisterModule("innervatecall", function () pfUI:RegisterModule("innervatecall", function ()
-- Requires Nampower for AURA_CAST events
if not GetNampowerVersion then return end
-- Only load for druids -- Only load for druids
if UnitClassBase("player") ~= "DRUID" then return end if UnitClassBase("player") ~= "DRUID" then return end
local INNERVATE_SPELLID = 29166 local INNERVATE_SPELLID = 29166
local GCD_DURATION = 1.5
-- GUID → name resolution for the target. UnitTokenFromGUID walks the
-- engine's known unit tokens (player, party, raid, target, ...) and
-- returns the first one currently bound to the GUID, so we don't have
-- to manually iterate party/raid here.
local function ResolveTargetName(targetGuid)
if not targetGuid then return nil end
local token = UnitTokenFromGUID(targetGuid)
return token and UnitName(token) or nil
end
-- Determine chat channel based on group context -- Determine chat channel based on group context
local function GetAnnounceChannel() local function GetAnnounceChannel()
@@ -39,57 +27,49 @@ pfUI:RegisterModule("innervatecall", function ()
return nil return nil
end end
-- Event frame - registers AURA_CAST directly (bypasses libdebuff hooks -- Set on our cast, cleared once the "ready" timer is scheduled
-- which are gated behind ownDebuffs/pendingCasts checks meant for debuffs) local pending = false
local frame = CreateFrame("Frame")
-- AURA_CAST_ON_SELF fires when a buff lands ON the player
-- AURA_CAST_ON_OTHER fires when a buff lands on someone else
-- Both needed: self-innervate = ON_SELF, innervate on others = ON_OTHER
-- casterGuid check prevents announcing other druids' innervates
frame:RegisterEvent("AURA_CAST_ON_SELF")
frame:RegisterEvent("AURA_CAST_ON_OTHER")
frame:RegisterEvent("PLAYER_LOGOUT")
frame:SetScript("OnEvent", function()
if event == "PLAYER_LOGOUT" then
this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
return
end
-- AURA_CAST args: arg1=spellId, arg2=casterGuid, arg3=targetGuid -- Schedule the "ready" announcement once Innervate's own cooldown is in
local spellId = arg1 -- the spell history. Until then GetSpellCooldown reports only the GCD,
local casterGuid = arg2 -- which has no isOnGCD flag here, so tell them apart by duration.
local targetGuid = arg3 local function ScheduleReady()
if spellId ~= INNERVATE_SPELLID then return end
-- Only announce our own casts
if not IsPlayerGuid(casterGuid) then return end
-- Resolve target name from GUID
local targetName = ResolveTargetName(targetGuid) or "Unknown"
-- Determine channel
local channel = GetAnnounceChannel()
if not channel then return end -- solo, no announcement
SendChatMessage(">> Innervate casted on " .. targetName .. " <<", channel)
-- Schedule "ready" announcement when the cooldown expires. Read the
-- precise remaining time from C_Spell.GetSpellCooldown (startTime and
-- duration are seconds from the GetTime epoch); fall back to 360s.
local cdRemaining = 360
local cd = C_Spell.GetSpellCooldown(INNERVATE_SPELLID) local cd = C_Spell.GetSpellCooldown(INNERVATE_SPELLID)
if cd and cd.duration and cd.duration > 0 then if not cd or cd.duration <= GCD_DURATION then return end
local remaining = cd.startTime + cd.duration - GetTime()
if remaining > 0 then cdRemaining = remaining end
end
C_Timer.After(cdRemaining, function() pending = false
C_Timer.After(cd.startTime + cd.duration - GetTime(), function()
local ch = GetAnnounceChannel() local ch = GetAnnounceChannel()
if ch then if ch then
SendChatMessage(">> Innervate is ready <<", ch) SendChatMessage(">> Innervate is ready <<", ch)
end end
end) end)
end
local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
frame:RegisterEvent("SPELL_UPDATE_COOLDOWN")
frame:SetScript("OnEvent", function()
if event == "SPELL_UPDATE_COOLDOWN" then
if pending then ScheduleReady() end
return
end
-- Innervate is instant, so it logs SPELL_CAST_SUCCESS with its target
-- arg2=subevent, arg4=sourceGUID, arg9=destName, arg12=spellId
if arg2 ~= "SPELL_CAST_SUCCESS" or arg12 ~= INNERVATE_SPELLID then return end
-- Only announce our own casts
if not IsPlayerGuid(arg4) then return end
local destName = arg9
-- Determine channel
local channel = GetAnnounceChannel()
if not channel then return end -- solo, no announcement
SendChatMessage(">> Innervate cast on " .. (destName or "Unknown") .. " <<", channel)
pending = true
ScheduleReady()
end) end)
end) end)
+1 -1
View File
@@ -180,7 +180,7 @@ pfUI:RegisterModule("map", function ()
my = (( my + height / 2 ) - ( y / scale )) / height * 100 my = (( my + height / 2 ) - ( y / scale )) / height * 100
end end
if mx and my and MouseIsOver(WorldMapButton) then if mx and my and WorldMapButton:IsMouseOver() then
WorldMapButton.coords.text:SetFormattedText('%.1f / %.1f', mx, my) WorldMapButton.coords.text:SetFormattedText('%.1f / %.1f', mx, my)
else else
WorldMapButton.coords.text:SetText("") WorldMapButton.coords.text:SetText("")
+3 -3
View File
@@ -95,7 +95,7 @@ pfUI:RegisterModule("mapcolors", function ()
local tooltipText = "" local tooltipText = ""
if unit_button_name == 'WorldMap' then if unit_button_name == 'WorldMap' then
-- check player -- check player
if MouseIsOver(WorldMapPlayer) then if WorldMapPlayer:IsMouseOver() then
ColorizeName(WorldMapPlayer) ColorizeName(WorldMapPlayer)
tooltipText = WorldMapPlayer.name tooltipText = WorldMapPlayer.name
end end
@@ -104,7 +104,7 @@ pfUI:RegisterModule("mapcolors", function ()
-- check party -- check party
for i=1, MAX_PARTY_MEMBERS do for i=1, MAX_PARTY_MEMBERS do
local frame = _G[unit_button_name.."Party"..i] local frame = _G[unit_button_name.."Party"..i]
if frame:IsVisible() and MouseIsOver(frame) then if frame:IsVisible() and frame:IsMouseOver() then
ColorizeName(frame) ColorizeName(frame)
tooltipText = tooltipText.."\n"..frame.name tooltipText = tooltipText.."\n"..frame.name
end end
@@ -113,7 +113,7 @@ pfUI:RegisterModule("mapcolors", function ()
--check Raid --check Raid
for i=1, MAX_RAID_MEMBERS do for i=1, MAX_RAID_MEMBERS do
local frame = _G[unit_button_name.."Raid"..i] local frame = _G[unit_button_name.."Raid"..i]
if frame:IsVisible() and MouseIsOver(frame) then if frame:IsVisible() and frame:IsMouseOver() then
ColorizeName(frame) ColorizeName(frame)
tooltipText = tooltipText.."\n"..frame.name tooltipText = tooltipText.."\n"..frame.name
end end
+17 -15
View File
@@ -109,24 +109,26 @@ pfUI:RegisterModule("minimap", function ()
this.highlight.tex:SetPoint("BOTTOMRIGHT", MiniMapMailIcon, "BOTTOMRIGHT", 2, -2) this.highlight.tex:SetPoint("BOTTOMRIGHT", MiniMapMailIcon, "BOTTOMRIGHT", 2, -2)
this.highlight.tex:SetVertexColor(1,.5,.5) this.highlight.tex:SetVertexColor(1,.5,.5)
this.highlight:SetScript("OnUpdate", function() -- Blink ten times between .9 and .1, then hide
if not this.count then this.count = 0 end local highlight = this.highlight
if not this.modifier then this.modifier = 1 end highlight:SetAlpha(.9)
if this.count >= 10 then this:Hide() end highlight.blink = highlight:CreateAnimationGroup()
for i = 1, 10 do
this:SetAlpha(this:GetAlpha() + this.modifier) local down = highlight.blink:CreateAnimation("Alpha")
down:SetChange(-.8)
if this:GetAlpha() <= 0.1 then down:SetDuration(.3)
this.modifier = 0.05 down:SetOrder(i * 2 - 1)
this.count = this.count + 1 local up = highlight.blink:CreateAnimation("Alpha")
elseif this:GetAlpha() >= 0.9 then up:SetChange(.8)
this.modifier = -0.05 up:SetDuration(.3)
end up:SetOrder(i * 2)
end) end
highlight.blink:SetScript("OnFinished", function() highlight:Hide() end)
end end
this.highlight.count = 0
this.highlight:Show() this.highlight:Show()
this.highlight.blink:Stop()
this.highlight.blink:Play()
end) end)
-- Create coordinates text frame with location configurable -- Create coordinates text frame with location configurable
+40 -35
View File
@@ -77,10 +77,10 @@ pfUI:RegisterModule("nameplates", function ()
-- vs channel itself. Never called per frame. -- vs channel itself. Never called per frame.
local function PollCastInfo(unit) local function PollCastInfo(unit)
if not unit then return nil end if not unit then return nil end
local name, _, texture, startMs, endMs, _, _, _, spellID = C_Spell.UnitCastingInfo(unit) local name, _, texture, startMs, endMs, _, _, noInterrupt, spellID = C_Spell.UnitCastingInfo(unit)
local isChannel local isChannel
if not name then if not name then
name, _, texture, startMs, endMs, _, _, spellID = C_Spell.UnitChannelInfo(unit) name, _, texture, startMs, endMs, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(unit)
isChannel = true isChannel = true
end end
if not name or not startMs or not endMs then return nil end if not name or not startMs or not endMs then return nil end
@@ -92,6 +92,7 @@ pfUI:RegisterModule("nameplates", function ()
endTime = endMs / 1000, endTime = endMs / 1000,
duration = (endMs - startMs) / 1000, duration = (endMs - startMs) / 1000,
isChannel = isChannel, isChannel = isChannel,
noInterrupt = noInterrupt,
} }
end end
@@ -279,10 +280,6 @@ pfUI:RegisterModule("nameplates", function ()
return color return color
end end
local function DoNothing()
return
end
local function DisableObject(object) local function DisableObject(object)
if not object then return end if not object then return end
if not object.GetObjectType then return end if not object.GetObjectType then return end
@@ -380,16 +377,23 @@ pfUI:RegisterModule("nameplates", function ()
end end
local function GetUnitType(red, green, blue) -- UnitSelectionType: 0 hostile, 1 unfriendly, 2 neutral, 3 friendly (or a
if red > .9 and green < .2 and blue < .2 then -- PvP-flagged player), 4 unflagged player. Type 3 is ambiguous, so callers
return "ENEMY_NPC" -- still promote *_NPC to *_PLAYER from their own player check.
elseif red > .9 and green > .9 and blue < .2 then local selectionTypes = {
return "NEUTRAL_NPC" [0] = "ENEMY_NPC",
elseif red < .2 and green < .2 and blue > 0.9 then [1] = "ENEMY_NPC",
return "FRIENDLY_PLAYER" [2] = "NEUTRAL_NPC",
elseif red < .2 and green > .9 and blue < .2 then [3] = "FRIENDLY_NPC",
return "FRIENDLY_NPC" [4] = "FRIENDLY_PLAYER",
end }
local function GetUnitType(plate)
-- an unresolved token reads as the player's own type (4), so never ask
-- without a GUID
local guid = plate.cachedGuid
if not guid or guid == "" then return "ENEMY_NPC" end
return selectionTypes[UnitSelectionType(guid)]
end end
local filter, list, cache local filter, list, cache
@@ -436,22 +440,8 @@ pfUI:RegisterModule("nameplates", function ()
plate.debuffs[index].stacks:SetJustifyV("BOTTOM") plate.debuffs[index].stacks:SetJustifyV("BOTTOM")
plate.debuffs[index].stacks:SetTextColor(1,1,0) plate.debuffs[index].stacks:SetTextColor(1,1,0)
-- PERF: Use lightweight fake cooldown frame when animation disabled plate.debuffs[index].cd = CreateFrame("Cooldown", plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index], "CooldownFrameTemplate")
-- The Model-based CooldownFrameTemplate causes major lag with many nameplates plate.debuffs[index].cd:SetFrameLevel(6)
if cfg.debuffanim ~= 1 then
plate.debuffs[index].cd = CreateFrame("Frame", plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index])
plate.debuffs[index].cd:SetAllPoints(plate.debuffs[index])
plate.debuffs[index].cd:SetFrameLevel(6)
plate.debuffs[index].cd:SetScript("OnUpdate", CooldownFrame_OnUpdateModel)
plate.debuffs[index].cd.AdvanceTime = DoNothing
plate.debuffs[index].cd.SetSequence = DoNothing
plate.debuffs[index].cd.SetSequenceTime = DoNothing
else
-- Use CooldownFrameTemplate for animation
plate.debuffs[index].cd = CreateFrame("Model", plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index], "CooldownFrameTemplate")
plate.debuffs[index].cd:SetAllPoints(plate.debuffs[index])
plate.debuffs[index].cd:SetFrameLevel(6)
end
-- Set initial config flags (will be cached per-cooldown later) -- Set initial config flags (will be cached per-cooldown later)
plate.debuffs[index].cd.pfCooldownStyleAnimation = cfg.debuffanim plate.debuffs[index].cd.pfCooldownStyleAnimation = cfg.debuffanim
@@ -518,6 +508,10 @@ nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
-- plate's own frame, against its own token -- see OnCreate and -- plate's own frame, against its own token -- see OnCreate and
-- NAME_PLATE_UNIT_ADDED. -- NAME_PLATE_UNIT_ADDED.
nameplates:RegisterEvent("PLAYER_GUILD_UPDATE") nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
-- The interruptible flips only fire for target and focus, not nameplate
-- tokens. castState is keyed by GUID, so they still reach those units' plates.
nameplates:RegisterUnitEvent("UNIT_SPELLCAST_INTERRUPTIBLE", "target", "focus")
nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focus")
nameplates:SetScript("OnEvent", function() nameplates:SetScript("OnEvent", function()
-- Stop event handling during logout to prevent crash 132 -- Stop event handling during logout to prevent crash 132
@@ -679,6 +673,17 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
-- Also propagate to all plates for alpha/strata updates -- Also propagate to all plates for alpha/strata updates
this.eventcache = true this.eventcache = true
elseif event == "UNIT_SPELLCAST_INTERRUPTIBLE" or event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE" then
local guid = UnitGUID(arg1)
local info = guid and castState[guid]
local plate = info and plateByGuid[guid]
if plate and plate.castbar then
info.noInterrupt = event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE"
-- UpdateCastbar only restyles on a new cast; force it
plate.castbar.lastEndTime = nil
plate.castUpdate = true
end
elseif event == "PLAYER_COMBO_POINTS" or event == "UNIT_COMBO_POINTS" then elseif event == "PLAYER_COMBO_POINTS" or event == "UNIT_COMBO_POINTS" then
-- Only flag the target's plate for combo point update -- Only flag the target's plate for combo point update
local plate = C_NamePlate.GetNamePlateForUnit("target") local plate = C_NamePlate.GetNamePlateForUnit("target")
@@ -959,6 +964,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
nameplate.name:SetJustifyH(nameTextPos) nameplate.name:SetJustifyH(nameTextPos)
nameplate.health:SetOrientation(orientation) nameplate.health:SetOrientation(orientation)
nameplate.health:SetRotatesTexture(orientation == "VERTICAL")
-- Bar anchors to the plate directly so the name's JustifyH (configurable -- Bar anchors to the plate directly so the name's JustifyH (configurable
-- below) can shift left/right without dragging the bar with it. -- below) can shift left/right without dragging the bar with it.
nameplate.health:ClearAllPoints() nameplate.health:ClearAllPoints()
@@ -1081,8 +1087,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
levelFromDB = true levelFromDB = true
end end
local red, green, blue = plate.original.healthbar:GetStatusBarColor() local unittype = GetUnitType(plate)
local unittype = GetUnitType(red, green, blue) or "ENEMY_NPC"
local font_size = C.nameplates.use_unitfonts == "1" and C.global.font_unit_size or C.global.font_size local font_size = C.nameplates.use_unitfonts == "1" and C.global.font_unit_size or C.global.font_size
-- ignore players with npc names if plate level is lower than player level -- ignore players with npc names if plate level is lower than player level
@@ -1751,7 +1756,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
-- Relative 0..duration range to avoid float precision loss with large -- Relative 0..duration range to avoid float precision loss with large
-- absolute timestamps. -- absolute timestamps.
nameplate.castbar:SetMinMaxValues(0, duration) nameplate.castbar:SetMinMaxValues(0, duration)
nameplate.castbar:SetStatusBarColor(GetStringColor(C.appearance.castbar[(isChannel and "channelcolor" or "castbarcolor")])) SetCastbarShield(nameplate.castbar, nameplate.castbar.icon, castInfo.noInterrupt, isChannel)
if castInfo.icon then if castInfo.icon then
nameplate.castbar.icon.tex:SetTexture(castInfo.icon) nameplate.castbar.icon.tex:SetTexture(castInfo.icon)
nameplate.castbar.icon.tex:SetTexCoord(.1,.9,.1,.9) nameplate.castbar.icon.tex:SetTexCoord(.1,.9,.1,.9)
+2 -1
View File
@@ -122,6 +122,7 @@ pfUI:RegisterModule("panel", function()
local gcmb = gckb and gckb > 0 and round((gckb or 0)/1000, 2) .. " MB" or UNAVAILABLE local gcmb = gckb and gckb > 0 and round((gckb or 0)/1000, 2) .. " MB" or UNAVAILABLE
local nin, nout, nping = GetNetStats() local nin, nout, nping = GetNetStats()
local screenWidth, screenHeight = GetPhysicalScreenSize()
GameTooltip:AddDoubleLine(T["Active Addons"], "|cffffffff" .. active .. "|cff555555 / |cffffffff" .. GetNumAddOns()) GameTooltip:AddDoubleLine(T["Active Addons"], "|cffffffff" .. active .. "|cff555555 / |cffffffff" .. GetNumAddOns())
GameTooltip:AddLine(" ") GameTooltip:AddLine(" ")
@@ -133,7 +134,7 @@ pfUI:RegisterModule("panel", function()
GameTooltip:AddDoubleLine(T["Network Latency"], "|cffffffff" .. nping .. "ms") GameTooltip:AddDoubleLine(T["Network Latency"], "|cffffffff" .. nping .. "ms")
GameTooltip:AddLine(" ") GameTooltip:AddLine(" ")
GameTooltip:AddDoubleLine(T["Graphic Renderer"], "|cffffffff" .. GetCVar("gxApi")) GameTooltip:AddDoubleLine(T["Graphic Renderer"], "|cffffffff" .. GetCVar("gxApi"))
GameTooltip:AddDoubleLine(T["Screen Resolution"], "|cffffffff" .. GetCVar("gxResolution")) GameTooltip:AddDoubleLine(T["Screen Resolution"], "|cffffffff" .. screenWidth .. 'x' .. screenHeight)
GameTooltip:AddDoubleLine(T["UI-Scale"], "|cffffffff" .. round(UIParent:GetEffectiveScale(),2)) GameTooltip:AddDoubleLine(T["UI-Scale"], "|cffffffff" .. round(UIParent:GetEffectiveScale(),2))
GameTooltip:Show() GameTooltip:Show()
end end
+1 -36
View File
@@ -1,39 +1,4 @@
pfUI:RegisterModule("skin", function () pfUI:RegisterModule("skin", function ()
-- align UIParent panels
pfUI.panelalign = CreateFrame("Frame", "pfUIParentPanelAlign", UIParent)
pfUI.panelalign:SetScript("OnUpdate", function()
-- throttle to 5 updates per second instead of every frame
if (this.tick or 0.2) > GetTime() then return else this.tick = GetTime() + 0.2 end
local left = UIParent.left
local center = UIParent.center
local rbpos, ropos
-- detect outer frame backdrops
if left and not left.rightObj then
left.rightObj = left.backdrop or left
for _, frame in pairs({left:GetChildren()}) do
rbpos = frame.backdrop and frame.backdrop.GetRight and frame.backdrop:GetRight()
ropos = left.rightObj and left.rightObj.GetRight and left.rightObj:GetRight()
if rbpos and ropos and rbpos > ropos then
left.rightObj = frame.backdrop
end
end
end
-- reset anchors
if not left or not left:IsShown() or not center or not center:IsShown() then
UIParent.pfLeftAligned = nil
return
elseif center and center:IsShown() and left and left:IsShown() then
local width = left.rightObj:GetRight()
if width and width ~= UIParent.pfLeftAligned then
center:SetPoint("TOPLEFT", "UIParent", "TOPLEFT", width + 10, -104)
UIParent.pfLeftAligned = width
end
end
end)
-- durability frame -- durability frame
pfUI.durability = CreateFrame("Frame", "pfDurability", UIParent) pfUI.durability = CreateFrame("Frame", "pfDurability", UIParent)
if pfUI.minimap then if pfUI.minimap then
@@ -79,4 +44,4 @@ pfUI:RegisterModule("skin", function ()
else else
UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE") UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
end end
end) end)
+59 -266
View File
@@ -1,10 +1,6 @@
pfUI:RegisterModule("swingtimer", function () pfUI:RegisterModule("swingtimer", function ()
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
-- HitInfo flags (EVENTS.md)
local HITINFO_LEFTSWING = 4 -- 4: Off-hand attack
local HITINFO_NOACTION = 65536 -- 65536: server did not advance the swing clock
-- SPELL_QUEUE_EVENT codes (EVENTS.md) -- SPELL_QUEUE_EVENT codes (EVENTS.md)
local ON_SWING_QUEUED = 0 local ON_SWING_QUEUED = 0
local ON_SWING_QUEUE_POPPED = 1 local ON_SWING_QUEUE_POPPED = 1
@@ -19,8 +15,6 @@ pfUI:RegisterModule("swingtimer", function ()
lastMhMarkerX = -1, lastOhMarkerX = -1, lastRaMarkerX = -1, lastMhMarkerX = -1, lastOhMarkerX = -1, lastRaMarkerX = -1,
autoAttackActive = false, autoAttackActive = false,
inCombat = false, inCombat = false,
pendingCastSpellId = nil,
mhFrozenAt = nil,
hsQueued = false, cleaveQueued = false, maulQueued = false, hsQueued = false, cleaveQueued = false, maulQueued = false,
hsSeenCurrent = false, cleaveSeenCurrent = false, maulSeenCurrent = false, hsSeenCurrent = false, cleaveSeenCurrent = false, maulSeenCurrent = false,
isWarrior = false, isWarrior = false,
@@ -31,13 +25,6 @@ pfUI:RegisterModule("swingtimer", function ()
onSwingCache = {}, onSwingCache = {},
} }
-- Wand "Shoot" runs on the ranged bar but is INDEPENDENT of the melee
-- swing clock — casters melee-weave between mainhand swings and wand
-- fires, both timers tick concurrently. Every other ranged-auto-attack
-- (Hunter Auto Shot, any future auto-repeat ranged spell) replaces MH.
local WAND_SHOOT_SPELLID = 5019
local THROW_SPELLID = 2764 -- one-shot ranged, not auto-repeat
-- On-next-swing spells replace the next auto-attack swing. -- On-next-swing spells replace the next auto-attack swing.
-- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically. -- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically.
local function IsOnSwingSpell(spellId) local function IsOnSwingSpell(spellId)
@@ -203,36 +190,19 @@ pfUI:RegisterModule("swingtimer", function ()
CreateBackdropShadow(pfUI.swingtimer.offhand) CreateBackdropShadow(pfUI.swingtimer.offhand)
-- Ranged bar -- Ranged bar
pfUI.swingtimer.ranged = CreateFrame("Frame", "pfSwingTimerRanged", UIParent) -- 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:SetPoint("BOTTOM", UIParent, "CENTER", -189, 83)
pfUI.swingtimer.ranged:SetWidth(sw_width) pfUI.swingtimer.ranged:SetWidth(sw_width)
pfUI.swingtimer.ranged:SetHeight(sw_height) 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() pfUI.swingtimer.ranged:Hide()
pfUI.swingtimer.ranged.left = pfUI.swingtimer.ranged:CreateTexture(nil, "ARTWORK")
pfUI.swingtimer.ranged.left:SetTexture(sw_texture)
pfUI.swingtimer.ranged.left:SetPoint("RIGHT", pfUI.swingtimer.ranged, "CENTER", 0, 0)
pfUI.swingtimer.ranged.left:SetHeight(sw_height)
pfUI.swingtimer.ranged.left:SetWidth(sw_width / 2)
pfUI.swingtimer.ranged.left:SetTexCoord(0, 0.5, 0, 1)
pfUI.swingtimer.ranged.left:SetVertexColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged.right = pfUI.swingtimer.ranged:CreateTexture(nil, "ARTWORK")
pfUI.swingtimer.ranged.right:SetTexture(sw_texture)
pfUI.swingtimer.ranged.right:SetPoint("LEFT", pfUI.swingtimer.ranged, "CENTER", 0, 0)
pfUI.swingtimer.ranged.right:SetHeight(sw_height)
pfUI.swingtimer.ranged.right:SetWidth(sw_width / 2)
pfUI.swingtimer.ranged.right:SetTexCoord(0.5, 1, 0, 1)
pfUI.swingtimer.ranged.right:SetVertexColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged.warn = pfUI.swingtimer.ranged:CreateTexture(nil, "ARTWORK")
pfUI.swingtimer.ranged.warn:SetTexture(sw_texture)
pfUI.swingtimer.ranged.warn:SetPoint("CENTER", pfUI.swingtimer.ranged, "CENTER", 0, 0)
pfUI.swingtimer.ranged.warn:SetHeight(sw_height)
pfUI.swingtimer.ranged.warn:SetWidth(1)
pfUI.swingtimer.ranged.warn:SetVertexColor(rwR, rwG, rwB, rwA)
pfUI.swingtimer.ranged.warn:Hide()
pfUI.swingtimer.ranged.text = pfUI.swingtimer.ranged:CreateFontString("Status", "DIALOG", "GameFontNormal") 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:SetPoint("CENTER", pfUI.swingtimer.ranged, "CENTER", 0, 0)
pfUI.swingtimer.ranged.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE") pfUI.swingtimer.ranged.text:SetFont(pfUI.font_default, sw_fontsize, "OUTLINE")
@@ -304,67 +274,41 @@ pfUI:RegisterModule("swingtimer", function ()
S.raSpeed = (rs and rs > 0) and rs or 0 S.raSpeed = (rs and rs > 0) and rs or 0
end end
-- Reset MH countdown to full speed (server confirmed swing) -- Start* restart a countdown from a PLAYER_SWING reset. `remaining` can be
local function ResetMH() -- shorter than the weapon speed (parry haste, ranged wind-up), so the bar
if S.mhFrozenAt then -- length stays the full swing and the fill starts part-way along.
S.mhFrozenAt = nil local function StartMH(remaining)
end
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
pfUI.swingtimer.mhGraceAt = nil -- cancel any pending hide pfUI.swingtimer.mhGraceAt = nil -- cancel any pending hide
S.mhTimerMax = S.mhSpeed S.mhTimerMax = math.max(S.mhSpeed, remaining)
S.mhTimer = S.mhSpeed S.mhTimer = remaining
S.mhActive = true S.mhActive = true
pfUI.swingtimer.mainhand:Show() pfUI.swingtimer.mainhand:Show()
pfUI.swingtimer:Show() pfUI.swingtimer:Show()
end end
-- Reset OH countdown to full speed local function StartOH(remaining)
local function ResetOH()
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
-- The off-hand attack time stays set with a shield equipped
if S.ohSpeed <= 0 then return end if S.ohSpeed <= 0 then return end
pfUI.swingtimer.ohGraceAt = nil -- cancel any pending hide pfUI.swingtimer.ohGraceAt = nil -- cancel any pending hide
S.ohTimerMax = S.ohSpeed S.ohTimerMax = math.max(S.ohSpeed, remaining)
S.ohTimer = S.ohSpeed S.ohTimer = remaining
S.ohActive = true S.ohActive = true
if sw_showoh then pfUI.swingtimer.offhand:Show() end if sw_showoh then pfUI.swingtimer.offhand:Show() end
pfUI.swingtimer:Show() pfUI.swingtimer:Show()
end end
-- Reset ranged countdown. replaceMH=true (Hunter Auto Shot, Throw) stops local function StartRanged(remaining)
-- the melee swing clock while ranged ticks; replaceMH=false (wand Shoot)
-- leaves it running for melee weaving.
local function ResetRanged(replaceMH)
if not sw_showranged then return end if not sw_showranged then return end
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
if S.raSpeed <= 0 then return end if S.raSpeed <= 0 then return end
if replaceMH then S.raTimerMax = math.max(S.raSpeed, remaining)
S.mhActive = false S.raTimer = remaining
pfUI.swingtimer.mainhand:Hide()
end
S.raTimerMax = S.raSpeed
S.raTimer = S.raSpeed
S.raActive = true S.raActive = true
if isHunter then pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged.left:ClearAllPoints() pfUI.swingtimer.ranged:SetValue(isHunter and 1 or 0)
pfUI.swingtimer.ranged.left:SetPoint("RIGHT", pfUI.swingtimer.ranged, "CENTER", 0, 0)
pfUI.swingtimer.ranged.left:SetWidth(sw_width / 2)
pfUI.swingtimer.ranged.left:SetTexCoord(0, 0.5, 0, 1)
pfUI.swingtimer.ranged.right:SetWidth(sw_width / 2)
pfUI.swingtimer.ranged.right:SetTexCoord(0.5, 1, 0, 1)
else
pfUI.swingtimer.ranged.left:ClearAllPoints()
pfUI.swingtimer.ranged.left:SetPoint("TOPLEFT", pfUI.swingtimer.ranged, "TOPLEFT", 0, 0)
pfUI.swingtimer.ranged.left:SetWidth(0.1)
pfUI.swingtimer.ranged.left:Hide()
pfUI.swingtimer.ranged.left:SetTexCoord(0, 0, 0, 1)
pfUI.swingtimer.ranged.right:SetWidth(0.1)
pfUI.swingtimer.ranged.right:Hide()
end
pfUI.swingtimer.ranged.left:SetVertexColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged.right:SetVertexColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged.warn:SetWidth(1)
pfUI.swingtimer.ranged.warn:Hide()
pfUI.swingtimer.ranged:Show() pfUI.swingtimer.ranged:Show()
pfUI.swingtimer:Show() pfUI.swingtimer:Show()
end end
@@ -486,23 +430,17 @@ pfUI:RegisterModule("swingtimer", function ()
local anyActive = false local anyActive = false
-- Tick timers down. When a timer expires we give a short grace period before -- 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 AUTO_ATTACK_SELF arrives. -- hiding the bar, to bridge the 1-2 frame gap until PLAYER_SWING arrives.
-- If AUTO_ATTACK_SELF arrives during the grace period it resets the timer -- 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 -- normally and the grace timer is cleared. If not (e.g. auto-attack was
-- turned off), the bar hides after the grace period ends. -- turned off), the bar hides after the grace period ends.
local GRACE = 0.15 -- seconds to wait after timer hits 0 before hiding local GRACE = 0.15 -- seconds to wait after timer hits 0 before hiding
if S.mhActive then if S.mhActive then
-- Don't tick while frozen (swing-delay spell like Slam is being cast) S.mhTimer = S.mhTimer - delta
if not S.mhFrozenAt then
S.mhTimer = S.mhTimer - delta
end
if S.mhTimer <= 0 then if S.mhTimer <= 0 then
S.mhTimer = 0 S.mhTimer = 0
if S.mhFrozenAt then if not pfUI.swingtimer.mhGraceAt then
-- Swing-delay spell cast in progress: bar holds at 0, skip grace/hide.
-- SPELL_GO handler will unfreeze and add cast duration to timer.
elseif not pfUI.swingtimer.mhGraceAt then
pfUI.swingtimer.mhGraceAt = GetTime() + GRACE pfUI.swingtimer.mhGraceAt = GetTime() + GRACE
elseif GetTime() >= pfUI.swingtimer.mhGraceAt then elseif GetTime() >= pfUI.swingtimer.mhGraceAt then
pfUI.swingtimer.mhGraceAt = nil pfUI.swingtimer.mhGraceAt = nil
@@ -641,30 +579,12 @@ pfUI:RegisterModule("swingtimer", function ()
local remaining = S.raTimer local remaining = S.raTimer
if isHunter then if isHunter then
local DEADZONE = 0.5 local DEADZONE = 0.5
local halfW = sw_width / 2
if remaining > DEADZONE then if remaining > DEADZONE then
local elapsed = S.raTimerMax - remaining pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA)
local phase1dur = S.raTimerMax - DEADZONE pfUI.swingtimer.ranged:SetValue((remaining - DEADZONE) / (S.raTimerMax - DEADZONE))
local p = elapsed / phase1dur
local w = halfW * (1 - p)
if w < 1 then w = 1 end
pfUI.swingtimer.ranged.left:Show()
pfUI.swingtimer.ranged.left:SetWidth(w)
pfUI.swingtimer.ranged.left:SetTexCoord(0, (1 - p) * 0.5, 0, 1)
pfUI.swingtimer.ranged.left:SetVertexColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged.right:Show()
pfUI.swingtimer.ranged.right:SetWidth(w)
pfUI.swingtimer.ranged.right:SetTexCoord(1 - (1 - p) * 0.5, 1, 0, 1)
pfUI.swingtimer.ranged.right:SetVertexColor(raR, raG, raB, raA)
pfUI.swingtimer.ranged.warn:Hide()
else else
local p = 1 - (remaining / DEADZONE) pfUI.swingtimer.ranged:SetStatusBarColor(rwR, rwG, rwB, rwA)
local w = sw_width * p pfUI.swingtimer.ranged:SetValue(1 - (remaining / DEADZONE))
if w < 1 then w = 1 end
pfUI.swingtimer.ranged.left:Hide()
pfUI.swingtimer.ranged.right:Hide()
pfUI.swingtimer.ranged.warn:SetWidth(w)
pfUI.swingtimer.ranged.warn:Show()
end end
if sw_showtext then if sw_showtext then
if remaining <= 0.5 then if remaining <= 0.5 then
@@ -674,14 +594,7 @@ pfUI:RegisterModule("swingtimer", function ()
end end
end end
else else
local progress = 1 - (remaining / S.raTimerMax) pfUI.swingtimer.ranged:SetValue(1 - (remaining / S.raTimerMax))
local w = sw_width * progress
if w < 1 then w = 1 end
pfUI.swingtimer.ranged.left:Show()
pfUI.swingtimer.ranged.left:SetWidth(w)
pfUI.swingtimer.ranged.left:SetTexCoord(0, progress, 0, 1)
pfUI.swingtimer.ranged.right:Hide()
pfUI.swingtimer.ranged.warn:Hide()
if sw_showtext then if sw_showtext then
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10) pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
end end
@@ -722,164 +635,46 @@ pfUI:RegisterModule("swingtimer", function ()
end end
end) end)
-- SPELL_START_SELF: fires only for cast-time spells, never instants
local spellStartFrame = CreateFrame("Frame")
spellStartFrame:RegisterEvent("SPELL_START_SELF")
spellStartFrame:SetScript("OnEvent", function()
if not (arg1 and arg1 > 0) then return end
S.pendingCastSpellId = arg1
-- Freeze the swing timer for cast-time spells that DON'T reset auto-
-- attack on completion (Slam, Hammer of Wrath on Turtle, etc.) — those
-- let the swing resume from where it paused. C_Spell.ResetsMeleeSwing
-- mirrors the server rule; spells that reset don't need freezing (they
-- reset on SPELL_GO_SELF). Subsumes the old hardcoded swingDelaySpells
-- list (no list maintenance for new Slam-style spells).
if S.mhActive then
if not C_Spell.ResetsMeleeSwing(arg1) then
S.mhFrozenAt = GetTime()
end
end
end)
-- Cast cancel/fail/interrupt: unfreeze swing timer if a delay spell was interrupted
local spellFailFrame = CreateFrame("Frame")
spellFailFrame:RegisterEvent("SPELLCAST_FAILED")
spellFailFrame:RegisterEvent("SPELLCAST_INTERRUPTED")
spellFailFrame:SetScript("OnEvent", function()
if S.mhFrozenAt then
S.mhFrozenAt = nil
end
S.pendingCastSpellId = nil
end)
-- SPELL_GO hook via libdebuff
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
pfUI.libdebuff_spell_go_hooks["swingtimer"] = function(spellId)
-- C_Spell.IsRangedAutoAttackSpell catches both Auto Shot (75) and
-- wand Shoot (5019) via Spell.dbc's AUTO_REPEAT attribute (covers
-- any future auto-repeat ranged spell automatically). Wand is the
-- one independent of the MH swing — everything else replaces it.
-- Throw isn't auto-repeat (single-shot) so it's handled explicitly.
if C_Spell.IsRangedAutoAttackSpell(spellId) then
ResetRanged(spellId ~= WAND_SHOOT_SPELLID)
return
elseif spellId == THROW_SPELLID then
ResetRanged(true)
return
elseif IsOnSwingSpell(spellId) then
-- On-next-swing ability (HS / Cleave / Maul / Raptor Strike / etc.)
-- — the swing fires as the spell consumes it. Drop the queued color.
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
ResetMH()
else
-- C_Spell.ResetsMeleeSwing mirrors the server rule (Turtle's
-- Spell::IsMeleeAttackResetSpell): InterruptFlags has AUTOATTACK and
-- AttributesEx2 lacks NOT_RESET_AUTO_ACTIONS. When the spell resets the
-- swing, snap the timers to full. Otherwise (elseif) a frozen-swing-
-- during-cast is a Slam-style cast — push the timer forward by the cast
-- duration so the bar resumes from where it paused.
if C_Spell.ResetsMeleeSwing(spellId) then
if S.mhActive and S.mhSpeed > 0 then
UpdateWeaponSpeeds()
S.mhTimerMax = S.mhSpeed
S.mhTimer = S.mhSpeed
end
if S.ohActive and S.ohSpeed > 0 then
S.ohTimerMax = S.ohSpeed
S.ohTimer = S.ohSpeed
end
elseif S.mhFrozenAt then
local castDuration = GetTime() - S.mhFrozenAt
S.mhTimer = S.mhTimer + castDuration
S.mhTimerMax = S.mhTimerMax + castDuration
S.mhFrozenAt = nil
end
end
S.pendingCastSpellId = nil
end
-- SPELL_CAST_EVENT hook: HS/Cleave/Maul queue tracking
pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {}
pfUI.libdebuff_spell_cast_hooks["swingtimer"] = function(success, spellId)
local kind = ClassifyOnSwingSpell(spellId)
if not kind then return end
S.useSpellQueueEvent = true
SetQueuedKind(kind)
end
local events = CreateFrame("Frame") local events = CreateFrame("Frame")
events:RegisterEvent("AUTO_ATTACK_SELF") events:RegisterEvent("PLAYER_SWING")
events:RegisterEvent("AUTO_ATTACK_OTHER")
events:RegisterEvent("PLAYER_ENTERING_WORLD") events:RegisterEvent("PLAYER_ENTERING_WORLD")
events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- ClassicAPI: per-slot, equipment-only events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- ClassicAPI: per-slot, equipment-only
events:RegisterEvent("PLAYER_REGEN_DISABLED") events:RegisterEvent("PLAYER_REGEN_DISABLED")
events:RegisterEvent("PLAYER_REGEN_ENABLED") events:RegisterEvent("PLAYER_REGEN_ENABLED")
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED") events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
events:RegisterUnitEvent("UNIT_DIED", UnitGUID("player")) events:RegisterEvent("PLAYER_DEAD")
events:RegisterUnitEvent("UNIT_SPELLCAST_SENT", "player")
events:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player")
events:RegisterEvent("SPELL_QUEUE_EVENT") events:RegisterEvent("SPELL_QUEUE_EVENT")
events:RegisterEvent("START_AUTOATTACK") events:RegisterEvent("START_AUTOATTACK")
events:RegisterEvent("STOP_AUTOATTACK") events:RegisterEvent("STOP_AUTOATTACK")
events:SetScript("OnEvent", function() events:SetScript("OnEvent", function()
if event == "AUTO_ATTACK_SELF" then if event == "PLAYER_SWING" then
local hitInfo = arg4 or 0 -- ClassicAPI tracks the server's swing clock (white hits, on-next-swing
local isOffhand = bit.band(hitInfo, HITINFO_LEFTSWING) ~= 0 -- abilities, swing-reset casts, parry haste, extra attacks, weapon
local noAction = bit.band(hitInfo, HITINFO_NOACTION) ~= 0 -- swaps) and hands over the seconds left until the next swing.
-- NOACTION means server did not advance swing clock (dodge/parry/miss). if arg2 == Enum.PlayerSwingType.MainHand then
-- Only skip if the timer is already running - if it's not active yet StartMH(arg1)
-- (first swing ever), we still want to start it so the bar appears. elseif arg2 == Enum.PlayerSwingType.OffHand then
if noAction then StartOH(arg1)
if isOffhand and S.ohActive then return end elseif arg2 == Enum.PlayerSwingType.Ranged then
if not isOffhand and S.mhActive then return end StartRanged(arg1)
end end
-- Extra attack detection: if timer still has >20% remaining for that hand, elseif event == "UNIT_SPELLCAST_SENT" then
-- the server did NOT reset the swing clock -> this is an extra attack, skip. -- arg4 = spellID: an on-next-swing ability (HS / Cleave / Maul) queued
-- Use 20% here (SP_SwingTimer's ShouldResetTimer threshold). local kind = ClassifyOnSwingSpell(arg4)
-- Exception: if timer is already at 0 (expired), always accept. if kind then
if isOffhand then S.useSpellQueueEvent = true
local pct = S.ohActive and (S.ohTimer / S.ohTimerMax) or 0 SetQueuedKind(kind)
if S.ohActive and S.ohTimer > 0 and pct > 0.20 then
return
end
ResetOH()
else
local pct = S.mhActive and (S.mhTimer / S.mhTimerMax) or 0
if S.mhActive and S.mhTimer > 0 and pct > 0.20 then
return
end
ResetMH()
end end
elseif event == "AUTO_ATTACK_OTHER" then elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
-- Parry haste: enemy attacked the player and player parried -- arg3 = spellID: the on-next-swing ability resolved, which consumes
local targetGuid = arg2 -- the queue, so drop the queued color
if not targetGuid or not IsPlayerGuid(targetGuid) then return end if IsOnSwingSpell(arg3) then
local victimState = arg5 or 0 S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
-- VICTIMSTATE_PARRY = 3
-- Vanilla: parry reduces the NEXT swing timer by 40% of weapon speed,
-- minimum 20% of weapon speed remaining (SP_SwingTimer approach)
if victimState == 3 then
-- Apply to whichever swing comes next (smallest % remaining = closest to firing)
if S.ohActive and S.ohSpeed > 0 and (S.ohTimer / S.ohTimerMax) < (S.mhTimer / S.mhTimerMax) then
local minimum = S.ohSpeed * 0.20
if S.ohTimer > minimum then
local reduct = S.ohSpeed * 0.40
local before = S.ohTimer
S.ohTimer = S.ohTimer - reduct
if S.ohTimer < minimum then S.ohTimer = minimum end
end
elseif S.mhActive and S.mhSpeed > 0 then
local minimum = S.mhSpeed * 0.20
if S.mhTimer > minimum then
local reduct = S.mhSpeed * 0.40
local before = S.mhTimer
S.mhTimer = S.mhTimer - reduct
if S.mhTimer < minimum then S.mhTimer = minimum end
end
else
end
end end
elseif event == "SPELL_QUEUE_EVENT" then elseif event == "SPELL_QUEUE_EVENT" then
@@ -931,10 +726,8 @@ pfUI:RegisterModule("swingtimer", function ()
S.cleaveQueued = false S.cleaveQueued = false
S.maulQueued = false S.maulQueued = false
elseif event == "UNIT_DIED" then elseif event == "PLAYER_DEAD" then
if IsPlayerGuid(arg1) then ResetAll()
ResetAll()
end
end end
end) end)
-12
View File
@@ -866,9 +866,6 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
if C.thirdparty.supermacro.enable == "0" then return end if C.thirdparty.supermacro.enable == "0" then return end
if not pfUI.bars then return end if not pfUI.bars then return end
-- skin game menu button
SkinButton(GameMenuButtonSuperMacro)
-- hook events to include SuperMacro refreshs -- hook events to include SuperMacro refreshs
local ButtonFullUpdate = pfUI.bars.ButtonFullUpdate local ButtonFullUpdate = pfUI.bars.ButtonFullUpdate
pfUI.bars.ButtonFullUpdate = function(button) pfUI.bars.ButtonFullUpdate = function(button)
@@ -1197,16 +1194,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
HookAddonOrVariable("LunaUnitFrames", EnableHealComm) HookAddonOrVariable("LunaUnitFrames", EnableHealComm)
HookAddonOrVariable("NotGrid", EnableHealComm) HookAddonOrVariable("NotGrid", EnableHealComm)
HookAddonOrVariable("MoveAnything", function()
SkinButton(GameMenuButtonMoveAnything)
end)
HookAddonOrVariable("MCP", function()
SkinButton(GameMenuButtonAddOns)
end)
HookAddonOrVariable("HardcoreDeath", function() HookAddonOrVariable("HardcoreDeath", function()
SkinButton(GameMenuButtonHardcoreDeathLogGUI)
GameMenuFrame:SetHeight(GameMenuFrame:GetHeight() + 20) GameMenuFrame:SetHeight(GameMenuFrame:GetHeight() + 20)
end) 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 = 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:SetSize(self.iconsize - 3, self.iconsize - 3)
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0) self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
self.bar[i].cd = self.bar[i].cd or CreateFrame("Model", "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate") self.bar[i].cd = self.bar[i].cd or CreateFrame("Cooldown", "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
self.bar[i].cd.pfCooldownStyleAnimation = 1 self.bar[i].cd.pfCooldownStyleAnimation = 1
self.bar[i].cd.pfCooldownType = "ALL" self.bar[i].cd.pfCooldownType = "ALL"
+11 -18
View File
@@ -34,6 +34,14 @@ pfUI:RegisterModule("tracking", function ()
pfUI.tracking.icon:SetTexCoord(.08, .92, .08, .92) pfUI.tracking.icon:SetTexCoord(.08, .92, .08, .92)
pfUI.tracking.icon:SetAllPoints(pfUI.tracking) pfUI.tracking.icon:SetAllPoints(pfUI.tracking)
-- Pulses the icon while no tracking spell is active
pfUI.tracking.pulse = pfUI.tracking.icon:CreateAnimationGroup()
pfUI.tracking.pulse:SetLooping("BOUNCE")
local fade = pfUI.tracking.pulse:CreateAnimation("Alpha")
fade:SetChange(-.5)
fade:SetDuration(.5)
fade:SetSmoothing("IN_OUT")
pfUI.tracking.menu = CreateFrame("Frame", "pfUIDropDownMenuTracking", nil, "UIDropDownMenuTemplate") pfUI.tracking.menu = CreateFrame("Frame", "pfUIDropDownMenuTracking", nil, "UIDropDownMenuTemplate")
pfUI.tracking:RegisterEvent("PLAYER_ENTERING_WORLD") pfUI.tracking:RegisterEvent("PLAYER_ENTERING_WORLD")
@@ -48,19 +56,6 @@ pfUI:RegisterModule("tracking", function ()
this:RefreshMenu() this:RefreshMenu()
end) end)
pfUI.tracking:SetScript("OnUpdate", function()
if this.pulse then
local _,_,_,alpha = this.icon:GetVertexColor()
local fpsmod = GetFramerate() / 30
if not alpha or alpha >= 0.9 then
this.modifier = -0.03 / fpsmod
elseif alpha <= .5 then
this.modifier = 0.03 / fpsmod
end
this.icon:SetVertexColor(1,1,1,alpha + this.modifier)
end
end)
pfUI.tracking:RegisterForClicks("LeftButtonUp", "RightButtonUp") pfUI.tracking:RegisterForClicks("LeftButtonUp", "RightButtonUp")
pfUI.tracking:SetScript("OnClick", function() pfUI.tracking:SetScript("OnClick", function()
if arg1 == "RightButton" then if arg1 == "RightButton" then
@@ -113,20 +108,18 @@ pfUI:RegisterModule("tracking", function ()
local texture = GetTrackingTexture() local texture = GetTrackingTexture()
if texture and texture ~= state.texture then if texture and texture ~= state.texture then
state.texture = texture state.texture = texture
pfUI.tracking.pulse = nil pfUI.tracking.pulse:Stop()
pfUI.tracking.icon:SetTexture(texture) pfUI.tracking.icon:SetTexture(texture)
pfUI.tracking.icon:SetVertexColor(1,1,1,1)
pfUI.tracking:Show() pfUI.tracking:Show()
elseif not texture then elseif not texture then
state.texture = nil state.texture = nil
if pulse and next(state.spells) ~= nil then if pulse and next(state.spells) ~= nil then
pfUI.tracking.pulse = true
pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
pfUI.tracking.icon:SetVertexColor(1,1,1,1) if not pfUI.tracking.pulse:IsPlaying() then pfUI.tracking.pulse:Play() end
pfUI.tracking:Show() pfUI.tracking:Show()
else else
pfUI.tracking.pulse = nil pfUI.tracking.pulse:Stop()
pfUI.tracking:Hide() pfUI.tracking:Hide()
end end
end end
+2 -4
View File
@@ -98,11 +98,9 @@ pfUI:RegisterModule("turtle-wow", function ()
GameMenuButtonOptions:ClearAllPoints() GameMenuButtonOptions:ClearAllPoints()
GameMenuButtonOptions:SetPoint("TOP", GameMenuButtonShop, "BOTTOM", 0, -16) GameMenuButtonOptions:SetPoint("TOP", GameMenuButtonShop, "BOTTOM", 0, -16)
-- apply pfUI skin to the new shop button -- match the shop button's text to the other skinned menu buttons
if pfUI.skin["Game Menu"] and pfUI_config["disabled"]["skin_Game Menu"] ~= "1" then if pfUI.skin["Game Menu"] and pfUI_config["disabled"]["skin_Game Menu"] ~= "1" then
local font = GameMenuButtonShop:GetFontString() GameMenuButtonShop:GetFontString():SetTextColor(1,1,1,1)
font:SetTextColor(1,1,1,1)
SkinButton(GameMenuButtonShop)
end end
end end
+11 -12
View File
@@ -130,15 +130,6 @@ pfUI:RegisterModule("unlock", function ()
return frames return frames
end end
local function DraggerOnUpdate()
this.text:SetAlpha(this.text:GetAlpha() -0.05)
if this.text:GetAlpha() < 0.1 then
this.text:SetText(strsub(this:GetParent():GetName(),3))
this.text:SetAlpha(1)
this:SetScript("OnUpdate", function() return end)
end
end
local function SaveScale(frame, scale) local function SaveScale(frame, scale)
frame:SetScale(scale) frame:SetScale(scale)
@@ -148,9 +139,8 @@ pfUI:RegisterModule("unlock", function ()
C.position[frame:GetName()]["scale"] = scale C.position[frame:GetName()]["scale"] = scale
frame.drag.text:SetText(T["Scale"] .. ": " .. scale) frame.drag.text:SetText(T["Scale"] .. ": " .. scale)
frame.drag.text:SetAlpha(1) frame.drag.fade:Stop()
frame.drag.fade:Play()
frame.drag:SetScript("OnUpdate", DraggerOnUpdate)
end end
local function SavePosition(frame) local function SavePosition(frame)
@@ -455,6 +445,15 @@ pfUI:RegisterModule("unlock", function ()
d.text:SetPoint("CENTER", 0, 0) d.text:SetPoint("CENTER", 0, 0)
d.text:SetFontObject(GameFontWhite) d.text:SetFontObject(GameFontWhite)
-- Fades out the scale readout, then puts the frame name back
d.fade = d.text:CreateAnimationGroup()
local fade = d.fade:CreateAnimation("Alpha")
fade:SetChange(-1)
fade:SetDuration(.3)
d.fade:SetScript("OnFinished", function()
d.text:SetText(strsub(f:GetName(),3))
end)
d.frame = f d.frame = f
d.fname = fname d.fname = fname
d.label = label d.label = label
+21
View File
@@ -34,4 +34,25 @@ pfUI:RegisterModule("unusable", function ()
hooksecurefunc("BankFrameItemButton_UpdateLock", function() hooksecurefunc("BankFrameItemButton_UpdateLock", function()
pfUI.unusable:UpdateSlot(-1, this:GetID()) pfUI.unusable:UpdateSlot(-1, this:GetID())
end) end)
-- 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
local refresh = CreateFrame("Frame")
refresh:RegisterEvent("PLAYER_LEVEL_UP")
refresh:RegisterEvent("SKILL_LINES_CHANGED")
refresh:RegisterEvent("SPELLS_CHANGED")
refresh:RegisterEvent("FACTION_STANDING_CHANGED")
refresh:RegisterEvent("PLAYER_PVP_RANK_CHANGED")
refresh:SetScript("OnEvent", function()
for _, bag in pairs(pfUI.BACKPACK) do
pfUI.bag.delay.UpdateBag[bag] = true
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
end
end
end)
end) end)
+29 -9
View File
@@ -63,9 +63,16 @@ pfUI:RegisterModule("xpbar", function ()
end end
end) end)
-- Cancel a running fade-out and show the bar at full alpha
local function Reveal(self)
self.fade:Stop()
self:SetAlpha(1)
end
local function OnLeave(self) local function OnLeave(self)
local self = self or this local self = self or this
self.hover = nil
self.tick = GetTime() + 3.00 self.tick = GetTime() + 3.00
GameTooltip:Hide() GameTooltip:Hide()
end end
@@ -82,7 +89,8 @@ local function OnEnter(self)
mode = "REP" mode = "REP"
end end
self:SetAlpha(1) self.hover = true
Reveal(self)
if mode == "XP" then if mode == "XP" then
local xp, xpmax, exh = UnitXP("player"), UnitXPMax("player"), GetXPExhaustion() local xp, xpmax, exh = UnitXP("player"), UnitXPMax("player"), GetXPExhaustion()
@@ -183,13 +191,15 @@ end
local self = self or this local self = self or this
if self.text_mouse == "1" then if self.text_mouse == "1" then
self.bar.text:SetShown(MouseIsOver(self)) self.bar.text:SetShown(self.hover)
end end
if self.always then return end if self.always then return end
if self:GetAlpha() == 0 or MouseIsOver(self) then return end -- Hover comes from OnEnter/OnLeave, the same mouse focus that reveals the
if ( self.tick or 1) > GetTime() then return else self.tick = GetTime() + .01 end -- bar, so the fade can't restart under the cursor
self:SetAlpha(self:GetAlpha() - .05) 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 end
local function OnEvent(self) local function OnEvent(self)
@@ -210,7 +220,7 @@ end
end end
if self.always then if self.always then
self:SetAlpha(1) Reveal(self)
self:Show() self:Show()
end end
@@ -220,7 +230,7 @@ end
if mode == "XP" then if mode == "XP" then
self.enabled = true self.enabled = true
self:SetAlpha(1) Reveal(self)
self.bar:SetMinMaxValues(0, UnitXPMax("player")) self.bar:SetMinMaxValues(0, UnitXPMax("player"))
self.bar:SetValue(UnitXP("player")) self.bar:SetValue(UnitXP("player"))
if GetXPExhaustion() then if GetXPExhaustion() then
@@ -246,7 +256,7 @@ end
elseif mode == "PETXP" then elseif mode == "PETXP" then
self.restedbar:Hide() self.restedbar:Hide()
self.enabled = true self.enabled = true
self:SetAlpha(1) Reveal(self)
local currXP, nextXP = GetPetExperience() local currXP, nextXP = GetPetExperience()
self.bar:SetMinMaxValues(min(0, currXP), nextXP) self.bar:SetMinMaxValues(min(0, currXP), nextXP)
@@ -264,7 +274,7 @@ end
local name, standingID, barMin, barMax, barValue = GetRepDisplay(self.factionID) local name, standingID, barMin, barMax, barValue = GetRepDisplay(self.factionID)
if name then if name then
self.enabled = true self.enabled = true
self:SetAlpha(1) Reveal(self)
barMax = barMax - barMin barMax = barMax - barMin
barValue = barValue - barMin barValue = barValue - barMin
@@ -342,6 +352,7 @@ end
local cr, cg, cb, ca = pfUI.api.GetStringColor(b.xp_color) local cr, cg, cb, ca = pfUI.api.GetStringColor(b.xp_color)
b.bar:SetStatusBarColor(cr,cg,cb,ca) b.bar:SetStatusBarColor(cr,cg,cb,ca)
b.bar:SetOrientation(b.mode) b.bar:SetOrientation(b.mode)
b.bar:SetRotatesTexture(b.mode == "VERTICAL")
b.bar.text = b.bar.text or b.bar:CreateFontString() b.bar.text = b.bar.text or b.bar:CreateFontString()
b.bar.text:SetPoint("CENTER", b, "CENTER", 0, b.text_off_y) b.bar.text:SetPoint("CENTER", b, "CENTER", 0, b.text_off_y)
@@ -358,10 +369,19 @@ end
local cr, cg, cb, ca = pfUI.api.GetStringColor(b.rest_color) local cr, cg, cb, ca = pfUI.api.GetStringColor(b.rest_color)
b.restedbar:SetStatusBarColor(cr,cg,cb,ca) b.restedbar:SetStatusBarColor(cr,cg,cb,ca)
b.restedbar:SetOrientation(b.mode) b.restedbar:SetOrientation(b.mode)
b.restedbar:SetRotatesTexture(b.mode == "VERTICAL")
-- auto hide -- auto hide
b:EnableMouse(true) b:EnableMouse(true)
if not b.fade then
b.fade = b:CreateAnimationGroup()
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") b:RegisterEvent("FACTION_STANDING_CHANGED")
b:RegisterUnitEvent("UNIT_PET", "player") b:RegisterUnitEvent("UNIT_PET", "player")
b:RegisterUnitEvent("UNIT_LEVEL", "player") b:RegisterUnitEvent("UNIT_LEVEL", "player")
+2 -2
View File
@@ -8,8 +8,8 @@ pfUI:RegisterSkin("Friends", function ()
CreateBackdropShadow(FriendsFrame) CreateBackdropShadow(FriendsFrame)
FriendsFrame.backdrop:SetPoint("TOPLEFT", 8, -10) FriendsFrame.backdrop:SetPoint("TOPLEFT", 8, -10)
FriendsFrame.backdrop:SetPoint("BOTTOMRIGHT", -32, 74) FriendsFrame.backdrop:SetPoint("BOTTOMRIGHT", -32, 72)
FriendsFrame:SetHitRectInsets(8,32,10,74) FriendsFrame:SetHitRectInsets(8,32,10,72)
EnableMovable("FriendsFrame") EnableMovable("FriendsFrame")
SkinCloseButton(FriendsFrameCloseButton, FriendsFrame.backdrop, -6, -6) SkinCloseButton(FriendsFrameCloseButton, FriendsFrame.backdrop, -6, -6)
+11 -15
View File
@@ -19,24 +19,20 @@ pfUI:RegisterSkin("Game Menu", function ()
pfUI.gui:Show() pfUI.gui:Show()
HideUIPanel(GameMenuFrame) HideUIPanel(GameMenuFrame)
end) end)
SkinButton(pfUIButton)
local point, relativeTo, relativePoint, xOffset, yOffset = GameMenuButtonOptions:GetPoint() local point, relativeTo, relativePoint, xOffset, yOffset = GameMenuButtonOptions:GetPoint()
GameMenuButtonOptions:SetPoint(point, relativeTo, relativePoint, xOffset, yOffset - 22) GameMenuButtonOptions:SetPoint(point, relativeTo, relativePoint, xOffset, yOffset - 22)
local buttons = { -- skin every button on the menu, including ones other addons add later
GameMenuButtonOptions, local function SkinMenuButtons()
GameMenuButtonSoundOptions, for _, child in pairs({GameMenuFrame:GetChildren()}) do
GameMenuButtonUIOptions, if child:GetFrameType() == "Button" and not child._pfMenuSkinned then
GameMenuButtonKeybindings, SkinButton(child)
GameMenuButtonMacros, child._pfMenuSkinned = true
GameMenuButtonRatings, end
GameMenuButtonLogout, end
GameMenuButtonQuit,
GameMenuButtonContinue,
}
for _, button in pairs(buttons) do
if button then SkinButton(button) end
end end
SkinMenuButtons()
GameMenuFrame:HookScript("OnShow", SkinMenuButtons)
end) end)
+49
View File
@@ -0,0 +1,49 @@
pfUI:RegisterSkin("Options - Interface", function ()
local rawborder, border = GetBorderSize()
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
local cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase("player")]:GetRGB()
StripTextures(InterfaceOptionsFrame)
CreateBackdrop(InterfaceOptionsFrame, nil, nil, .75)
CreateBackdropShadow(InterfaceOptionsFrame)
local title = GetNoNameObject(InterfaceOptionsFrame, "FontString", "ARTWORK", ADDON_OPTIONS)
title:ClearAllPoints()
title:SetPoint("TOP", InterfaceOptionsFrame, "TOP", 0, -10)
CreateBackdrop(InterfaceOptionsFrameAddOns, nil, true, .75)
CreateBackdrop(InterfaceOptionsFramePanelContainer, nil, true, .75)
SkinScrollbar(InterfaceOptionsFrameAddOnsListScrollBar)
for _, button in ipairs(InterfaceOptionsFrameAddOns.buttons) do
button:GetHighlightTexture():SetTexture(cr, cg, cb, .25)
-- the list update swaps plus/minus textures on the toggle by path
local toggle = _G[button:GetName() .. "Toggle"]
SkinCollapseButton(toggle)
toggle:SetPushedTexture("")
toggle:SetHighlightTexture("")
toggle.SetPushedTexture = function() end
end
-- ClassicAPI's own pages (Options\Panel.lua) number their controls
local i = 1
while _G["ClassicAPIOptionsControl" .. i] do
local control = _G["ClassicAPIOptionsControl" .. i]
local ctype = control:GetFrameType()
if ctype == "CheckButton" then
SkinCheckbox(control)
elseif ctype == "Slider" then
SkinSlider(control)
else
SkinDropDown(control, nil, nil, nil, true)
end
i = i + 1
end
SkinButton(InterfaceOptionsFrameDefaults)
SkinButton(InterfaceOptionsFrameCancel)
SkinButton(InterfaceOptionsFrameOkay)
InterfaceOptionsFrameOkay:ClearAllPoints()
InterfaceOptionsFrameOkay:SetPoint("RIGHT", InterfaceOptionsFrameCancel, "LEFT", -2*bpad, 0)
end)
+9 -9
View File
@@ -2,19 +2,19 @@ pfUI:RegisterSkin("Quest Log", function ()
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel() local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
_G.QUESTS_DISPLAYED = 23 _G.QUESTS_DISPLAYED = 22
_G.MAX_WATCHABLE_QUESTS = 20 -- TODO _G.MAX_WATCHABLE_QUESTS = 20 -- TODO
do -- quest log frame do -- quest log frame
QuestLogQuestCount:ClearAllPoints() QuestLogQuestCount:ClearAllPoints()
QuestLogQuestCount:SetPoint("TOPRIGHT", -10, -30) QuestLogQuestCount:SetPoint("TOPRIGHT", -10, -30)
hooksecurefunc("QuestLog_OnShow", function() -- let the panel manager anchor this doublewide panel at 10, -114
QuestLogFrame:ClearAllPoints() UIPanelWindows["QuestLogFrame"].xoffset = 10
QuestLogFrame:SetPoint("TOPLEFT", 10, -104) UIPanelWindows["QuestLogFrame"].yoffset = -10
end)
QuestLogFrame:SetSize(676, 440) -- match the 430px height and top edge of the other skinned panels
QuestLogFrame:SetSize(676, 430)
QuestLogFrame:DisableDrawLayer("BACKGROUND") QuestLogFrame:DisableDrawLayer("BACKGROUND")
StripTextures(QuestLogFrame, true) StripTextures(QuestLogFrame, true)
@@ -144,7 +144,7 @@ pfUI:RegisterSkin("Quest Log", function ()
end end
QuestLogListScrollFrame:SetPoint("TOPLEFT", 10, -54) QuestLogListScrollFrame:SetPoint("TOPLEFT", 10, -54)
QuestLogListScrollFrame:SetHeight(350) QuestLogListScrollFrame:SetHeight(340)
hooksecurefunc("QuestLog_Update", function() hooksecurefunc("QuestLog_Update", function()
local numEntries = GetNumQuestLogEntries() local numEntries = GetNumQuestLogEntries()
@@ -181,8 +181,8 @@ pfUI:RegisterSkin("Quest Log", function ()
QuestLogDetailScrollFrame:ClearAllPoints() QuestLogDetailScrollFrame:ClearAllPoints()
QuestLogDetailScrollFrame:SetPoint("TOPLEFT", QuestLogListScrollFrame, "TOPRIGHT", 35, 0) QuestLogDetailScrollFrame:SetPoint("TOPLEFT", QuestLogListScrollFrame, "TOPRIGHT", 35, 0)
QuestLogDetailScrollFrame:SetHeight(376) QuestLogDetailScrollFrame:SetHeight(366)
QuestLogDetailScrollChildFrame:SetHeight(376) QuestLogDetailScrollChildFrame:SetHeight(366)
local bg = QuestLogDetailScrollFrame:CreateTexture(nil, "BACKGROUND") local bg = QuestLogDetailScrollFrame:CreateTexture(nil, "BACKGROUND")
bg:SetAllPoints() bg:SetAllPoints()
+2 -2
View File
@@ -6,9 +6,9 @@ pfUI:RegisterSkin("Spellbook", function ()
CreateBackdrop(SpellBookFrame, nil, nil, .75) CreateBackdrop(SpellBookFrame, nil, nil, .75)
CreateBackdropShadow(SpellBookFrame) CreateBackdropShadow(SpellBookFrame)
SpellBookFrame.backdrop:SetPoint("TOPLEFT", 12, -12) SpellBookFrame.backdrop:SetPoint("TOPLEFT", 10, -10)
SpellBookFrame.backdrop:SetPoint("BOTTOMRIGHT", -30, 72) SpellBookFrame.backdrop:SetPoint("BOTTOMRIGHT", -30, 72)
SpellBookFrame:SetHitRectInsets(12,30,12,72) SpellBookFrame:SetHitRectInsets(10,30,10,72)
EnableMovable(SpellBookFrame) EnableMovable(SpellBookFrame)
SpellBookTitleText:ClearAllPoints() SpellBookTitleText:ClearAllPoints()
+10 -3
View File
@@ -22,9 +22,16 @@ pfUI:RegisterSkin("Pet Stable Master", function ()
PetStablePetInfo:ClearAllPoints() PetStablePetInfo:ClearAllPoints()
PetStablePetInfo:SetPoint("TOPLEFT", PetStableModel, "TOPLEFT", 10, 0) PetStablePetInfo:SetPoint("TOPLEFT", PetStableModel, "TOPLEFT", 10, 0)
local buttons = {"PetStableCurrentPet", "PetStableStabledPet1", "PetStableStabledPet2"} -- Servers differ in stable size (Turtle WoW has 4 slots), so collect
for _,v in pairs(buttons) do -- every PetStableStabledPetN the client created.
local button = _G[v] local buttons = { PetStableCurrentPet }
local i = 1
while _G["PetStableStabledPet"..i] do
table.insert(buttons, _G["PetStableStabledPet"..i])
i = i + 1
end
for _, button in pairs(buttons) do
StripTextures(button) StripTextures(button)
SkinButton(button, nil, nil, nil, _G[button:GetName().."IconTexture"]) SkinButton(button, nil, nil, nil, _G[button:GetName().."IconTexture"])