4 Commits
Author SHA1 Message Date
Brues d02fcaa8ee We no longer use these features from SuperWoW/Nampower 2026-07-31 21:47:00 -05:00
Brues 5dd42a8d86 Drive nameplate castbars off UNIT_SPELLCAST_*
ClassicAPI now fires UNIT_SPELLCAST_* for nameplate tokens, so populate the
per-GUID castState cache from UNIT_SPELLCAST_{START,CHANNEL_START} (poll
PollCastInfo for the timing the payload omits) and clear it on
{STOP,CHANNEL_STOP} -- the remote poll fires STOP for natural end, interrupt,
and cancel alike. Replaces the nampower SPELL_{START,FAILED}_OTHER path; the
^nameplate arg1 gate ignores the target/focus/party fires of the same events,
and the NAME_PLATE_UNIT_ADDED seed still catches a plate spawning mid-cast.
Nameplate cast detection is now nampower-free.
2026-07-31 18:11:18 -05:00
Brues 1a342adfca Route target/focus castbars through remote UNIT_SPELLCAST_*
ClassicAPI now fires UNIT_SPELLCAST_* for remote tokens (target/focus/...)
via PollRemote, so drop the nampower SPELL_*_OTHER path and drive all three
bars off one event model. The gate is now token-based (arg1 == this.unitstr,
plus the arg1=="player" + UnitIsUnit case for target=self); the RunNextFrame
defer is gone since the remote poll fires after UnitCastingInfo is readable.
Cast detection is now nampower-free across every bar.

Use the event's rank payload (arg5): thread it through RefreshBar into
StampBar, which now only calls C_Spell.GetSpellSubtext as a fallback on the
retarget re-poll (PLAYER_TARGET/FOCUS_CHANGED) where no event is in hand.
2026-07-31 18:04:50 -05:00
Brues 892b48e60f Migrate player castbar to ClassicAPI UNIT_SPELLCAST_* events
Drive the player cast path off ClassicAPI's synthesized, player-only
UNIT_SPELLCAST_* events instead of the vanilla SPELLCAST_* plus nampower
SPELL_{START,GO,DELAYED}_SELF mix. START/CHANNEL_START poll UnitCastingInfo/
UnitChannelInfo and stamp; STOP/CHANNEL_STOP/FAILED/INTERRUPTED clear;
SUCCEEDED drives tradeskill craft counting; DELAYED/CHANNEL_UPDATE re-poll.
Since UNIT_SPELLCAST_START fires for chained same-spell recasts, the nampower
SELF deps and their RunNextFrame co-hook-ordering workarounds are gone.
Remote target/focus bars keep SPELL_*_OTHER (the new events are player-only).

Fix the cast-start flash: StampBar now primes the fill on the stamp frame,
and ClearBar no longer snaps the bar to full (that snap lingered in the fade
tail and flashed when the next cast stamped).

Add a red flash on a cancelled cast: new appearance.castbar.failcolor,
flashed by ClearBar only when a cast was actually in progress.

Handle pushback Quartz-style: DELAYED/CHANNEL_UPDATE re-poll just the times
and accumulate the endMs shift into this.delay for the +/- indicator (cast
+X, channel -X), rather than a full restamp.
2026-07-31 13:22:36 -05:00
103 changed files with 17522 additions and 9333 deletions
+3 -3
View File
@@ -23,12 +23,12 @@ jobs:
IFS=. read -r MAJOR MINOR PATCH <<<"$LATEST" IFS=. read -r MAJOR MINOR PATCH <<<"$LATEST"
PACKED=$((MAJOR * 10000 + MINOR * 100 + PATCH)) PACKED=$((MAJOR * 10000 + MINOR * 100 + PATCH))
echo "Pinning PFUI_CLASSIC_API_LATEST to $LATEST ($PACKED)" echo "Pinning PFUI_CLASSIC_API_LATEST to $LATEST ($PACKED)"
sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" API_Check.lua sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" pfUI.lua
grep 'local PFUI_CLASSIC_API_' API_Check.lua grep 'local PFUI_CLASSIC_API_' pfUI.lua
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Package and release to GitHub - name: Package and release to GitHub
uses: brues-code/packager@vCAPI uses: BigWigsMods/packager@v2
env: env:
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }} GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
-22
View File
@@ -1,22 +0,0 @@
{
"runtime": {
"version": "Lua 5.1"
},
"diagnostics": {
"disable": ["deprecated"],
"globals": [
"this",
"event",
"arg",
"arg1",
"arg2",
"arg3",
"arg4",
"arg5",
"arg6",
"arg7",
"arg8",
"arg9"
]
}
}
-4
View File
@@ -1,4 +0,0 @@
package-as: pfUI
ignore:
- .luarc.json
-78
View File
@@ -1,78 +0,0 @@
do
-- ClassicAPI dependency gate.
--
-- Which TOC the client opened already answers "is ClassicAPI here?". Whenever
-- the DLL is loaded it redirects the read of `pfUI\pfUI.toc` to
-- `pfUI_Turtle.toc` (Turtle clients) or `pfUI_ClassicAPI.toc` (everything
-- else). Reaching this file from the plain `pfUI.toc` therefore means
-- ClassicAPI is absent -- and that TOC deliberately loads nothing but this
-- file, so there is nothing to disable, only a notice to show.
--
-- The flavor TOCs still need a version gate of their own: the redirect has
-- existed since ClassicAPI v1.11.0, well below the API surface pfUI relies
-- on. There pfUI does load, and will throw wherever it reaches for something
-- the installed DLL doesn't have yet -- the popup names the cause so those
-- errors aren't a mystery.
local PFUI_CLASSIC_API_MIN = 11703 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
local function FormatVersion(packed)
local x = math.floor(packed / 10000)
local y = math.floor(math.mod(packed, 10000) / 100)
local z = math.mod(packed, 100)
return string.format("v%d.%d.%d", x, y, z)
end
local headline, detail
if not CLASSIC_API_VERSION then
headline = "|cff33ffccpf|cffffffffUI|r has been disabled."
detail = "The ClassicAPI DLL isn't loaded. Download the latest release from:"
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
headline = "|cff33ffccpf|cffffffffUI|r will not work correctly on ClassicAPI " .. FormatVersion(CLASSIC_API_VERSION) .. "."
detail = FormatVersion(PFUI_CLASSIC_API_MIN) .. " or newer is required -- any errors alongside this one are the APIs it is missing. Download the latest release from:"
end
if detail then
local function ShowRequiredPopup()
StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = {
text = headline .. "\n\n" .. detail,
button1 = OKAY,
hasEditBox = 1,
editBoxWidth = 280,
timeout = 0,
whileDead = 1,
hideOnEscape = 1,
preferredIndex = 3,
OnShow = function()
local editBox = getglobal(this:GetName().."EditBox")
if editBox then
editBox:SetText(PFUI_CLASSIC_API_LATEST_URL)
editBox:HighlightText()
editBox:SetFocus()
end
end,
}
StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED")
DEFAULT_CHAT_FRAME:AddMessage(
headline .. " " .. detail .. " " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.3, 0.3
)
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
loginFrame:SetScript("OnEvent", function()
loginFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
ShowRequiredPopup()
end)
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then
EventUtil.ContinueOnPlayerLogin(function()
C_Timer.After(8, function()
DEFAULT_CHAT_FRAME:AddMessage(
"|cff33ffccpf|rUI: ClassicAPI " .. FormatVersion(PFUI_CLASSIC_API_LATEST) .. " is available — " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.85, 0.3
)
end)
end)
end
end
+14 -16
View File
@@ -1,9 +1,12 @@
# pfUI - ClassicAPI Edition # pfUI - ClassicAPI Edition
[![Octo WoW](https://img.shields.io/badge/Octo%20WoW-1.18.1-brightgreen.svg)](https://octowow.st/)
[![ClassicAPI](https://img.shields.io/badge/ClassicAPI-Required-purple.svg)](https://github.com/brues-code/ClassicAPI) [![ClassicAPI](https://img.shields.io/badge/ClassicAPI-Required-purple.svg)](https://github.com/brues-code/ClassicAPI)
[![SuperWoW](https://img.shields.io/badge/SuperWoW-Required-purple.svg)](https://github.com/balakethelock/SuperWoW)
[![Nampower](https://img.shields.io/badge/Nampower-Required-purple.svg)](https://github.com/brues-code/nampower) [![Nampower](https://img.shields.io/badge/Nampower-Required-purple.svg)](https://github.com/brues-code/nampower)
[![SuperWoW](https://img.shields.io/badge/SuperWoW-Optional-yellow.svg)](https://github.com/balakethelock/SuperWoW) [![UnitXP](https://img.shields.io/badge/UnitXP__SP3-Optional-yellow.svg)](https://codeberg.org/konaka/UnitXP_SP3)
[![UnitXP](https://img.shields.io/badge/UnitXP__SP3-Optional-yellow.svg)](https://github.com/brues-code/UnitXP_SP3)
**A pfUI fork specifically optimized for ClassicAPI on [Octo WoW](https://octowow.st/) which requires Nampower and SuperWoW with optional UnitXP_SP3 DLL integration.**
This version includes significant performance improvements and DLL-enhanced features. This version includes significant performance improvements and DLL-enhanced features.
@@ -13,9 +16,9 @@ This version includes significant performance improvements and DLL-enhanced feat
4. Copy "pfUI" into Wow-Directory\Interface\AddOns 4. Copy "pfUI" into Wow-Directory\Interface\AddOns
5. Restart Wow 5. Restart Wow
## DLL Enhancements ## Optional DLL Enhancements
Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functionality: Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functionality. These DLLs are permitted on Octo WoW:
### [ClassicAPI](https://github.com/brues-code/ClassicAPI) ### [ClassicAPI](https://github.com/brues-code/ClassicAPI)
@@ -33,24 +36,20 @@ Provides:
- Faster, safer profile sharing - Engine-side serialize/compress/base64 via C_EncodingUtil - Faster, safer profile sharing - Engine-side serialize/compress/base64 via C_EncodingUtil
- C_Timer - After / NewTicker replacing hand-rolled OnUpdate throttles - C_Timer - After / NewTicker replacing hand-rolled OnUpdate throttles
- Feign death, shapeshift and quest-item detection via real API calls - Feign death, shapeshift and quest-item detection via real API calls
- Mouseover Unit Frames
- Click-casting
- Loot Roll History (/loothistory)
- New item highlighting
- Plenty other functions - Plenty other functions
### [SuperWoW](https://github.com/balakethelock/SuperWoW)
Provides:
- SetMouseoverUnit for improved targeting
### [Nampower](https://github.com/brues-code/nampower) ### [Nampower](https://github.com/brues-code/nampower)
Provides: Provides:
- Spell queue indicator - Spell queue indicator
- GCD indicator - GCD indicator
### [SuperWoW](https://github.com/balakethelock/SuperWoW) ### [UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3)
Provides:
- Tracks party/raid units on the minimap
### [UnitXP_SP3](https://github.com/brues-code/UnitXP_SP3)
Provides: Provides:
- Line of Sight detection - Line of Sight detection
@@ -77,7 +76,6 @@ Use `/pfdll` in-game to check which DLLs are detected.
/swapfocus Toggle Focus and Target-Frame /swapfocus Toggle Focus and Target-Frame
/pftest Toggle pfUI Unitframe Test Mode /pftest Toggle pfUI Unitframe Test Mode
/abp Addon Button Panel /abp Addon Button Panel
/loothistory Show Loot Roll History
## Languages ## Languages
pfUI supports and contains language specific code for the following gameclients. pfUI supports and contains language specific code for the following gameclients.
@@ -90,7 +88,7 @@ pfUI supports and contains language specific code for the following gameclients.
* Russian (ruRU) * Russian (ruRU)
## Recommended Addons ## Recommended Addons
* [pfQuest](https://github.com/brues-code/pfQuest) A simple database and quest helper * [pfQuest](https://shagu.org/pfQuest) A simple database and quest helper
* [SuperCleveRoidMacros](https://github.com/brues-code/SuperCleveRoidMacros) Supports modern macro formats * [SuperCleveRoidMacros](https://github.com/brues-code/SuperCleveRoidMacros) Supports modern macro formats
## Plugins ## Plugins
+5 -133
View File
@@ -204,32 +204,6 @@ function pfUI.api.UnitHasBuff(unit, name)
return C_UnitAuras.GetAuraDataBySpellName(unit, name, "HELPFUL") ~= nil or nil return C_UnitAuras.GetAuraDataBySpellName(unit, name, "HELPFUL") ~= nil or nil
end end
-- [ ScanAuraSlots ]
-- Fills `buf` with the slot ids of the auras on `unit` that match `filter` and
-- returns how many (buf[1..n]; entries past n are cleared), so a module can
-- keep one buffer and refill it every refresh. Read each aura with
-- C_UnitAuras.UnitAuraBySlot(unit, buf[i]) (positional, no table) or
-- GetAuraDataBySlot. One enumeration walks the aura array once, where a
-- by-index loop (UnitAura(unit, i)) re-walks it from the start for every i.
-- Uses GetAuraSlots' fill-a-table form (table as the 5th argument) instead of
-- its vararg return: Lua 5.0 builds an `arg` table for every vararg call, so
-- collecting the returns in a Lua helper would allocate once per scan.
-- unit [string] unit token
-- filter [string] aura filter ("HELPFUL", "HARMFUL|PLAYER", ...)
-- buf [table] reusable buffer, filled in place
-- max [number] optional cap on the slot count (nil = all)
-- return: [number] count of slot ids written to buf
function pfUI.api.ScanAuraSlots(unit, filter, buf, max)
local _, n = C_UnitAuras.GetAuraSlots(unit, filter, max, nil, buf)
return n
end
-- [ IsPlayerGuid ]
-- Returns whether a GUID or unit token refers to the local player.
-- guid [string] A unit GUID (or unitID) to test.
-- return: [bool] true if it is the player otherwise "false"
pfUI.api.IsPlayerGuid = _G.IsPlayerGuid
-- [ GetUnbuffedRoster ] -- [ GetUnbuffedRoster ]
-- Returns a comma-joined, colored list of group members missing the named aura. -- Returns a comma-joined, colored list of group members missing the named aura.
-- name [string] the localized aura name to check for -- name [string] the localized aura name to check for
@@ -262,37 +236,6 @@ function pfUI.api.GetUnitColor(unitstr)
return classColor:GenerateHexColorMarkup(), classColor:GetRGB() return classColor:GenerateHexColorMarkup(), classColor:GetRGB()
end end
-- [ GetRaceIcon ]
-- Builds an inline race icon texture from the shared races atlas.
-- 'raceKey' [string] englishRace key (e.g. "NightElf")
-- 'sex' [int] unit sex (3 = female)
-- return: [string] inline texture escape, or an empty string
function pfUI.api.GetRaceIcon(raceKey, sex)
if not raceKey then return "" end
local gender = sex == 3 and "FEMALE" or "MALE"
local coords = RACE_ICON_TCOORDS[strupper(raceKey) .. "_" .. gender]
if not coords then return "" end
return string.format(
"|TInterface\\Glues\\CharacterCreate\\UI-CharacterCreate-Races:0:0:0:0:512:512:%d:%d:%d:%d|t",
coords[1] * 512, coords[2] * 512, coords[3] * 512, coords[4] * 512)
end
-- inline faction emblems (cropped from the target-frame PvP banners)
local FACTION_ICON = {
Alliance = "|TInterface\\TargetingFrame\\UI-PVP-Alliance:0:0:0:0:64:64:5:37:3:35|t",
Horde = "|TInterface\\TargetingFrame\\UI-PVP-Horde:0:0:0:0:64:64:5:37:3:35|t",
}
-- [ GetFactionIcon ]
-- Resolves an inline faction emblem from the englishRace key stored in L["race"].
-- 'raceKey' [string] englishRace key (e.g. "Orc")
-- return: [string] inline texture escape, or an empty string
function pfUI.api.GetFactionIcon(raceKey)
local info = raceKey and L["race"][raceKey]
if not info then return "" end
return FACTION_ICON[info.faction] or ""
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.
@@ -454,33 +397,14 @@ end
-- Returns an itemLink for the given itemname -- Returns an itemLink for the given itemname
-- 'name' [string] name of the item -- 'name' [string] name of the item
-- returns: [string] entire itemLink for the given item -- returns: [string] entire itemLink for the given item
local itemLinkCache = {}
local itemLinkMisses = {}
local ITEMLINK_MAX_SCANS = 2
function pfUI.api.GetItemLinkByName(name) function pfUI.api.GetItemLinkByName(name)
-- GetInboxItem() hands us a nil name for attachment-less mail for itemID = 1, 25818 do
if not name then return end
-- cache successful resolutions so repeated lookups (e.g. per inbox click) are free
if itemLinkCache[name] then return itemLinkCache[name] end
-- Failures have to be counted, not just retried. An unresolvable name walks
-- the entire id range and finds nothing -- a visible hitch on every tooltip
-- hover. Allow a couple of attempts (rendering the tooltip caches the item,
-- so the next hover usually resolves), then stop scanning for that name.
local misses = itemLinkMisses[name] or 0
if misses >= ITEMLINK_MAX_SCANS then return end
-- Octo/Turtle custom items live well past the 25818 vanilla ceiling
for itemID = 1, 61000 do
local itemName = C_Item.GetItemNameByID(itemID) local itemName = C_Item.GetItemNameByID(itemID)
if itemName and itemName == name then if itemName and itemName == name then
local _, itemLink = C_Item.GetItemInfo(itemID) local _, itemLink = C_Item.GetItemInfo(itemID)
itemLinkCache[name] = itemLink
return itemLink return itemLink
end end
end end
itemLinkMisses[name] = misses + 1
end end
-- [ FindItem ] -- [ FindItem ]
@@ -722,10 +646,6 @@ end
function pfUI.api.CreateGoldString(money) function pfUI.api.CreateGoldString(money)
if type(money) ~= "number" then return "-" end if type(money) ~= "number" then return "-" end
if _G.GetCoinTextureString then
return "|cffffffff" .. _G.GetCoinTextureString(money) .. "|r"
end
local gold = floor(money/ 100 / 100) local gold = floor(money/ 100 / 100)
local silver = floor(mod((money/100),100)) local silver = floor(mod((money/100),100))
local copper = floor(mod(money,100)) local copper = floor(mod(money,100))
@@ -1325,32 +1245,6 @@ 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
@@ -1535,18 +1429,6 @@ 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)
@@ -1559,20 +1441,11 @@ 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
@@ -1588,13 +1461,13 @@ function pfUI.api.EnableAutohide(frame, timeout, combat)
if this.activeTo == "keep" then return end if this.activeTo == "keep" then return end
if this:IsMouseOver(10, -10, -10, 10) then if MouseIsOver(this, 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 and not this.fade:IsPlaying() then if this.activeTo < GetTime() and this.parent:GetAlpha() > 0 then
this.fade:Play() local fps = (60 / math.max(GetFramerate(), 1))
this.parent:SetAlpha(this.parent:GetAlpha() - 0.05*fps)
end end
else else
this.activeTo = GetTime() + timeout this.activeTo = GetTime() + timeout
@@ -1611,7 +1484,6 @@ 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
+4 -19
View File
@@ -156,8 +156,6 @@ 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")
@@ -228,6 +226,7 @@ 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")
@@ -325,9 +324,6 @@ 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")
@@ -520,16 +516,10 @@ 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")
@@ -564,6 +554,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", unit, "glowaggro", "1") pfUI:UpdateConfig("unitframes", unit, "glowaggro", "1")
pfUI:UpdateConfig("unitframes", unit, "glowcombat", "1") pfUI:UpdateConfig("unitframes", unit, "glowcombat", "1")
pfUI:UpdateConfig("unitframes", unit, "showtooltip", "1")
pfUI:UpdateConfig("unitframes", unit, "healthcolor", "1") pfUI:UpdateConfig("unitframes", unit, "healthcolor", "1")
pfUI:UpdateConfig("unitframes", unit, "powercolor", "1") pfUI:UpdateConfig("unitframes", unit, "powercolor", "1")
pfUI:UpdateConfig("unitframes", unit, "levelcolor", "1") pfUI:UpdateConfig("unitframes", unit, "levelcolor", "1")
@@ -655,6 +646,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("bars", nil, "animation", "zoomfade") pfUI:UpdateConfig("bars", nil, "animation", "zoomfade")
pfUI:UpdateConfig("bars", nil, "animmode", "keypress") pfUI:UpdateConfig("bars", nil, "animmode", "keypress")
pfUI:UpdateConfig("bars", nil, "animalways", "0") pfUI:UpdateConfig("bars", nil, "animalways", "0")
pfUI:UpdateConfig("bars", nil, "macroscan", "1")
pfUI:UpdateConfig("bars", nil, "reagents", "1") pfUI:UpdateConfig("bars", nil, "reagents", "1")
pfUI:UpdateConfig("bars", nil, "hunterbar", "0") pfUI:UpdateConfig("bars", nil, "hunterbar", "0")
pfUI:UpdateConfig("bars", nil, "pagemasteralt", "0") pfUI:UpdateConfig("bars", nil, "pagemasteralt", "0")
@@ -793,10 +785,6 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("tooltip", nil, "extguild", "1") pfUI:UpdateConfig("tooltip", nil, "extguild", "1")
pfUI:UpdateConfig("tooltip", nil, "itemid", "0") pfUI:UpdateConfig("tooltip", nil, "itemid", "0")
pfUI:UpdateConfig("tooltip", nil, "movespeed", "0") pfUI:UpdateConfig("tooltip", nil, "movespeed", "0")
pfUI:UpdateConfig("tooltip", nil, "aurasource", "0")
pfUI:UpdateConfig("tooltip", nil, "spellid", "0")
pfUI:UpdateConfig("tooltip", nil, "unitid", "0")
pfUI:UpdateConfig("tooltip", nil, "showbuffs", "0")
pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8") pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8")
pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0") pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0")
pfUI:UpdateConfig("tooltip", "compare", "basestats", "1") pfUI:UpdateConfig("tooltip", "compare", "basestats", "1")
@@ -826,8 +814,7 @@ 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", "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")
@@ -968,7 +955,6 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("questlog", nil, "showQuestLevels", "0") pfUI:UpdateConfig("questlog", nil, "showQuestLevels", "0")
pfUI:UpdateConfig("character", "inventory", "durability", "1") pfUI:UpdateConfig("character", "inventory", "durability", "1")
pfUI:UpdateConfig("character", "inventory", "equipflyout", "0")
pfUI:UpdateConfig("character", "reputation", "repRequired", "1") pfUI:UpdateConfig("character", "reputation", "repRequired", "1")
pfUI:UpdateConfig("thirdparty", nil, "chatbg", "1") pfUI:UpdateConfig("thirdparty", nil, "chatbg", "1")
pfUI:UpdateConfig("thirdparty", nil, "showmeter", "0") pfUI:UpdateConfig("thirdparty", nil, "showmeter", "0")
@@ -976,7 +962,6 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("thirdparty", "dpsmate", "dock", "0") pfUI:UpdateConfig("thirdparty", "dpsmate", "dock", "0")
pfUI:UpdateConfig("thirdparty", "shagudps", "skin", "0") pfUI:UpdateConfig("thirdparty", "shagudps", "skin", "0")
pfUI:UpdateConfig("thirdparty", "shagudps", "dock", "0") pfUI:UpdateConfig("thirdparty", "shagudps", "dock", "0")
pfUI:UpdateConfig("thirdparty", "greedmeter", "dock", "0")
pfUI:UpdateConfig("thirdparty", "swstats", "skin", "0") pfUI:UpdateConfig("thirdparty", "swstats", "skin", "0")
pfUI:UpdateConfig("thirdparty", "swstats", "dock", "0") pfUI:UpdateConfig("thirdparty", "swstats", "dock", "0")
pfUI:UpdateConfig("thirdparty", "ktm", "skin", "0") pfUI:UpdateConfig("thirdparty", "ktm", "skin", "0")
+13 -265
View File
@@ -170,7 +170,7 @@ do -- dropdown
end end
local function MenuOnUpdate() local function MenuOnUpdate()
if not this:IsMouseOver(100, -100, -100, 100) then if not MouseIsOver(this, 100, -100, -100, 100) then
this.button:HideMenu() this.button:HideMenu()
end end
end end
@@ -587,7 +587,7 @@ end
function pfUI.api.SetHighlight(frame, cr, cg, cb) function pfUI.api.SetHighlight(frame, cr, cg, cb)
if not frame then return end if not frame then return end
if not cr or not cg or not cb then if not cr or not cg or not cb then
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB() cr, cg, cb = GetClassColor(UnitClassBase('player'))
end end
frame.cr, frame.cg, frame.cb = cr, cg, cb, ca frame.cr, frame.cg, frame.cb = cr, cg, cb, ca
@@ -632,7 +632,7 @@ function pfUI.api.SkinButton(button, cr, cg, cb, icon, disableHighlight)
if not b then b = button end if not b then b = button end
if not b then return end if not b then return end
if not cr or not cg or not cb then if not cr or not cg or not cb then
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB() cr, cg, cb = GetClassColor(UnitClassBase('player'))
end end
pfUI.api.CreateBackdrop(b, nil, true) pfUI.api.CreateBackdrop(b, nil, true)
b:SetNormalTexture("") b:SetNormalTexture("")
@@ -661,7 +661,7 @@ function pfUI.api.SkinButton(button, cr, cg, cb, icon, disableHighlight)
end end
b.UnlockHighlight = function() b.UnlockHighlight = function()
if not MouseIsOverHitRect(b) then if not MouseIsOver(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,7 +683,6 @@ 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)
@@ -710,7 +709,7 @@ end
function pfUI.api.SkinRotateButton(button) function pfUI.api.SkinRotateButton(button)
pfUI.api.CreateBackdrop(button) pfUI.api.CreateBackdrop(button)
local cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB() local cr, cg, cb = GetClassColor(UnitClassBase('player'))
local btnW, btnH = button:GetSize() local btnW, btnH = button:GetSize()
button:SetSize(btnW - 18, btnH - 18) button:SetSize(btnW - 18, btnH - 18)
@@ -908,7 +907,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
end end
if not cr or not cg or not cb then if not cr or not cg or not cb then
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB() cr, cg, cb = GetClassColor(UnitClassBase('player'))
end end
SetHighlight(button, cr, cg, cb) SetHighlight(button, cr, cg, cb)
@@ -1161,22 +1160,18 @@ 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
if not infobox.fade:IsPlaying() then infobox.fade:Play() end infobox:SetAlpha(infobox:GetAlpha()-0.05)
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))
@@ -1219,7 +1214,6 @@ 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
@@ -1252,249 +1246,3 @@ function pfUI.api.SkinMoneyInputFrame(frame)
copperIcon:ClearAllPoints() copperIcon:ClearAllPoints()
copperIcon:SetPoint("LEFT", copper_editbox, "RIGHT", 2, 0) copperIcon:SetPoint("LEFT", copper_editbox, "RIGHT", 2, 0)
end end
-- Shared icon picker widget. Builds a name field, a "currently selected"
-- preview, and a 10x8 icon grid backed by IconDataProviderMixin with an
-- All/Spells/Items filter and a name search. Used by the macro icon picker
-- and the equipment manager's name/icon popup. The widgets attach to
-- `parent`; the caller owns the surrounding frame, its OK/Cancel buttons,
-- and the save flow. Selection is tracked by texture PATH (not index) so it
-- survives filter changes -- a spell icon you picked still saves after you
-- switch the filter to "Items" and it drops out of the visible grid.
--
-- name global-name prefix for the scroll frame and editboxes
-- parent the popup frame to attach the widgets to
-- extraType IconDataProviderExtraType.* seed (Spellbook / Equipment)
-- nameLabelText header shown above the name field
--
-- Returns a handle:
-- .editbox name EditBox (caller wires enter/escape)
-- .search search EditBox
-- .GetIcon() current selected texture path
-- .SetIcon(path) set selection (nil -> question mark)
-- .Refresh() ensure provider, redraw grid + preview
-- .SetIconAreaShown(shown) show/hide the grid, filter, search, and labels
function pfUI.api.CreateIconPicker(name, parent, extraType, nameLabelText)
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
local ICON_GRID_COLS = 10
local ICON_GRID_ROWS = 8
local ICON_BTN_SIZE = 36
local ICON_BTN_PAD = 6
local provider = nil
local selectedIconPath = QUESTION_MARK
local currentFilter = "all"
local searchText = ""
local filtered = nil -- provider indices matching the search, or nil when empty
local RefreshIconGrid, RebuildFilter
local function EnsureProvider()
if not provider then
provider = CreateAndInitFromMixin(IconDataProviderMixin, extraType)
end
end
-- Normalize a texture to an uppercase basename so matching works across
-- sources: GetMacroInfo / stored set icons return "Interface\Icons\<name>"
-- while provider paths are uppercase-prefixed for base icons and
-- mixed-case for spellbook extras.
local function IconKey(path)
if type(path) ~= "string" then return path end
return string.gsub(string.upper(path), "^.*[\\/]", "")
end
parent.nameLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
parent.nameLabel:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -20)
parent.nameLabel:SetText(nameLabelText)
parent.editbox = CreateFrame("EditBox", name.."Name", parent, "InputBoxTemplate")
parent.editbox:SetSize(280, 20)
parent.editbox:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -38)
parent.editbox:SetAutoFocus(false)
parent.editbox:SetMaxLetters(16)
CreateBackdrop(parent.editbox)
parent.selectedLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
parent.selectedLabel:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -14)
parent.selectedLabel:SetText(ICON_SELECTION_TITLE_CURRENT)
parent.selectedLabel:SetTextColor(1, 0.82, 0)
parent.selectedPreview = CreateFrame("Frame", nil, parent)
parent.selectedPreview:SetSize(42, 42)
parent.selectedPreview:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -30)
CreateBackdrop(parent.selectedPreview)
parent.selectedPreview.tex = parent.selectedPreview:CreateTexture(nil, "ARTWORK")
parent.selectedPreview.tex:SetAllPoints(parent.selectedPreview)
parent.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
parent.iconLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
parent.iconLabel:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -86)
parent.iconLabel:SetText(MACRO_POPUP_CHOOSE_ICON)
-- Icon grid + scroll
local iconScroll = CreateFrame("ScrollFrame", name.."Scroll", parent, "FauxScrollFrameTemplate")
iconScroll:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -116)
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
local scrollbar = _G[name.."ScrollScrollBar"]
scrollbar:ClearAllPoints()
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
SkinScrollbar(scrollbar)
-- Filter dropdown: All / Spells / Items
local filterDropdown = CreateFrame("Frame", name.."Filter", parent, "UIDropDownMenuTemplate")
filterDropdown:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, -80)
local function ApplyFilter(value)
currentFilter = value
UIDropDownMenu_SetSelectedValue(filterDropdown, value)
if provider then
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
else provider:SetIconTypes(nil) end
RebuildFilter()
RefreshIconGrid()
end
end
UIDropDownMenu_Initialize(filterDropdown, function()
local info
info = {}; info.text = ICON_FILTER_ALL; info.value = "all"
info.func = function() ApplyFilter("all") end
info.checked = currentFilter == "all"
UIDropDownMenu_AddButton(info)
info = {}; info.text = ICON_FILTER_SPELL; info.value = "spells"
info.func = function() ApplyFilter("spells") end
info.checked = currentFilter == "spells"
UIDropDownMenu_AddButton(info)
info = {}; info.text = ICON_FILTER_ITEM; info.value = "items"
info.func = function() ApplyFilter("items") end
info.checked = currentFilter == "items"
UIDropDownMenu_AddButton(info)
end)
UIDropDownMenu_SetWidth(120, filterDropdown)
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
SkinDropDown(filterDropdown)
local iconButtons = {}
for r = 1, ICON_GRID_ROWS do
for c = 1, ICON_GRID_COLS do
local i = (r - 1) * ICON_GRID_COLS + c
local btn = CreateFrame("Button", nil, parent)
btn:SetSize(ICON_BTN_SIZE, ICON_BTN_SIZE)
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
CreateBackdrop(btn)
btn.texture = btn:CreateTexture(nil, "ARTWORK")
btn.texture:SetAllPoints(btn)
btn.texture:SetTexCoord(.08, .92, .08, .92)
btn:SetScript("OnClick", function()
if this.iconIndex and provider then
local path = provider:GetIconByIndex(this.iconIndex)
if path then selectedIconPath = path end
RefreshIconGrid()
end
end)
btn:SetScript("OnEnter", function()
if not this.iconIndex or not provider then return end
local path = provider:GetIconByIndex(this.iconIndex)
if type(path) == "string" then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(IconKey(path), 1, 1, 1)
GameTooltip:Show()
end
end)
btn:SetScript("OnLeave", GameTooltip_Hide)
iconButtons[i] = btn
end
end
-- Rebuild the search-filtered index list (provider indices whose icon
-- basename contains the search text); nil when the search box is empty.
function RebuildFilter()
EnsureProvider()
if searchText == "" then
filtered = nil
return
end
filtered = {}
for i = 1, provider:GetNumIcons() do
local path = provider:GetIconByIndex(i)
if type(path) == "string" and string.find(IconKey(path), searchText, 1, true) then
table.insert(filtered, i)
end
end
end
function RefreshIconGrid()
EnsureProvider()
local numIcons = filtered and table.getn(filtered) or provider:GetNumIcons()
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
local offset = FauxScrollFrame_GetOffset(iconScroll)
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
local listIdx = i + offset * ICON_GRID_COLS
local btn = iconButtons[i]
if listIdx <= numIcons then
btn:Show()
local providerIdx = filtered and filtered[listIdx] or listIdx
btn.iconIndex = providerIdx
local path = provider:GetIconByIndex(providerIdx)
btn.texture:SetTexture(path)
if IconKey(path) == IconKey(selectedIconPath) then
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
else
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
end
else
btn:Hide()
btn.iconIndex = nil
end
end
parent.selectedPreview.tex:SetTexture(selectedIconPath)
end
iconScroll:SetScript("OnVerticalScroll", function()
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() RefreshIconGrid() end)
end)
-- Search box (bottom-left) filters the grid by icon name.
parent.searchLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
parent.searchLabel:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 16, 18)
parent.searchLabel:SetText(SEARCH)
parent.search = CreateFrame("EditBox", name.."Search", parent, "InputBoxTemplate")
parent.search:SetSize(180, 20)
parent.search:SetPoint("LEFT", parent.searchLabel, "RIGHT", 10, 0)
parent.search:SetAutoFocus(false)
CreateBackdrop(parent.search)
parent.search:SetScript("OnEscapePressed", function() this:ClearFocus() end)
parent.search:SetScript("OnTextChanged", function()
searchText = strupper(strtrim(this:GetText()))
RebuildFilter()
scrollbar:SetValue(0)
RefreshIconGrid()
end)
-- Release the provider when the popup closes so its icon cache GCs.
parent:HookScript("OnHide", function()
if provider then provider:Release(); provider = nil end
end)
local handle = { editbox = parent.editbox, search = parent.search }
function handle.GetIcon() return selectedIconPath end
function handle.SetIcon(path) selectedIconPath = path or QUESTION_MARK end
function handle.Refresh() RefreshIconGrid() end
function handle.SetIconAreaShown(shown)
local method = shown and "Show" or "Hide"
iconScroll[method](iconScroll)
for _, b in ipairs(iconButtons) do b[method](b) end
parent.iconLabel[method](parent.iconLabel)
parent.selectedLabel[method](parent.selectedLabel)
parent.selectedPreview[method](parent.selectedPreview)
filterDropdown[method](filterDropdown)
parent.searchLabel[method](parent.searchLabel)
parent.search[method](parent.search)
end
return handle
end
+605 -541
View File
File diff suppressed because it is too large Load Diff
+44
View File
@@ -0,0 +1,44 @@
-- load pfUI environment
setfenv(1, pfUI:GetEnvironment())
-- [[ Constants ]]--
EVENTS_MINIMAP_ZONE_UPDATE = {"PLAYER_ENTERING_WORLD", "MINIMAP_ZONE_CHANGED"}
MICRO_BUTTONS = {
'CharacterMicroButton', 'SpellbookMicroButton', 'TalentMicroButton',
'QuestLogMicroButton', 'SocialsMicroButton', 'WorldMapMicroButton',
'MainMenuMicroButton', 'HelpMicroButton',
}
NAMEPLATE_OBJECTORDER = { "border", "glow", "name", "level", "levelicon", "raidicon" }
NAMEPLATE_FRAMETYPE = "Button"
MINIMAP_TRACKING_FRAME = _G.MiniMapTrackingFrame
FRIENDS_NAME_LOCATION = "ButtonTextNameLocation"
COOLDOWN_FRAME_TYPE = "Model"
LOOT_BUTTON_FRAME_TYPE = "LootButton"
PLAYER_BUFF_START_ID = -1
ACTIONBAR_SECURE_TEMPLATE_BAR = nil
ACTIONBAR_SECURE_TEMPLATE_BUTTON = nil
UNITFRAME_SECURE_TEMPLATE = nil
--[[ Vanilla API Extensions ]]--
do -- RunMacroText
local obj = { ["GetText"] = function(self) return self.text end }
obj = setmetatable(obj, {__index = function(tab,key)
local value = function() return end
rawset(tab,key,value)
return value
end})
function RunMacroText(text)
obj.text = text
ChatEdit_ParseText(obj, 1)
end
end
+1065
View File
File diff suppressed because it is too large Load Diff
+1067
View File
File diff suppressed because it is too large Load Diff
+1060
View File
File diff suppressed because it is too large Load Diff
+1056
View File
File diff suppressed because it is too large Load Diff
+1026
View File
File diff suppressed because it is too large Load Diff
+1063
View File
File diff suppressed because it is too large Load Diff
+1080
View File
File diff suppressed because it is too large Load Diff
+2352
View File
File diff suppressed because it is too large Load Diff
-2377
View File
File diff suppressed because it is too large Load Diff
-2991
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -51,12 +51,10 @@ pfUI_translation["deDE"] = {
["Auto Sell Grey Items"] = nil, ["Auto Sell Grey Items"] = nil,
["Average Per Hour"] = nil, ["Average Per Hour"] = nil,
["Background Color"] = nil, ["Background Color"] = nil,
["Bags"] = "Taschen",
["Bags & Bank"] = nil, ["Bags & Bank"] = nil,
["Bags Border Size"] = nil, ["Bags Border Size"] = nil,
["Bagslots Per Row"] = nil, ["Bagslots Per Row"] = nil,
["Bagspace"] = nil, ["Bagspace"] = nil,
["Bank"] = "Bank",
["Banker"] = nil, ["Banker"] = nil,
["Bankslots Per Row"] = nil, ["Bankslots Per Row"] = nil,
["Bar Background"] = nil, ["Bar Background"] = nil,
@@ -200,6 +198,7 @@ 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,
@@ -291,6 +290,7 @@ pfUI_translation["deDE"] = {
["Enable Mana Ticks"] = nil, ["Enable Mana Ticks"] = nil,
["Enable Micro Bar"] = nil, ["Enable Micro Bar"] = nil,
["Enable Mouselook With Right Click"] = nil, ["Enable Mouselook With Right Click"] = nil,
["Enable Mouseover Tooltip"] = nil,
["Enable Movable Bags"] = nil, ["Enable Movable Bags"] = nil,
["Enable Offscreen Frame Positions"] = nil, ["Enable Offscreen Frame Positions"] = nil,
["Enable Overlap"] = nil, ["Enable Overlap"] = nil,
@@ -311,7 +311,6 @@ pfUI_translation["deDE"] = {
["Encode"] = nil, ["Encode"] = nil,
["Ended"] = nil, ["Ended"] = nil,
["Energy Color"] = nil, ["Energy Color"] = nil,
["Equipped"] = "Angelegt",
["Equipped Item Color"] = nil, ["Equipped Item Color"] = nil,
["Estimate Debuffs"] = nil, ["Estimate Debuffs"] = nil,
["Estimate Enemy Health Points"] = nil, ["Estimate Enemy Health Points"] = nil,
@@ -669,6 +668,7 @@ pfUI_translation["deDE"] = {
["Scale"] = nil, ["Scale"] = nil,
["Scale Border On HiDPI Displays"] = nil, ["Scale Border On HiDPI Displays"] = nil,
["Scaling"] = nil, ["Scaling"] = nil,
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = nil, ["Screen Resolution"] = nil,
["Screenshot"] = nil, ["Screenshot"] = nil,
@@ -819,7 +819,6 @@ pfUI_translation["deDE"] = {
["Top Actionbar"] = nil, ["Top Actionbar"] = nil,
["Top Left"] = nil, ["Top Left"] = nil,
["Top Right"] = nil, ["Top Right"] = nil,
["Total"] = "Gesamt",
["Total Gold"] = nil, ["Total Gold"] = nil,
["Totem Direction"] = nil, ["Totem Direction"] = nil,
["Totem Icons"] = nil, ["Totem Icons"] = nil,
@@ -872,6 +871,7 @@ 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,
+4 -16
View File
@@ -52,19 +52,15 @@ pfUI_translation["enUS"] = {
["Auto Sell Grey Items"] = nil, ["Auto Sell Grey Items"] = nil,
["Average Per Hour"] = nil, ["Average Per Hour"] = nil,
["Background Color"] = nil, ["Background Color"] = nil,
["Bags"] = nil,
["Bags & Bank"] = nil, ["Bags & Bank"] = nil,
["Bags Border Size"] = nil, ["Bags Border Size"] = nil,
["Bagslots Per Row"] = nil, ["Bagslots Per Row"] = nil,
["Bagspace"] = nil, ["Bagspace"] = nil,
["Bank"] = nil,
["Banker"] = nil, ["Banker"] = nil,
["Bankslots Per Row"] = nil, ["Bankslots Per Row"] = nil,
["Bar Background"] = nil, ["Bar Background"] = nil,
["Bar Texture"] = nil, ["Bar Texture"] = nil,
["Battleground Frames"] = nil,
["Battleground Statistics"] = nil, ["Battleground Statistics"] = nil,
["BEHIND"] = nil,
["Blacklist"] = nil, ["Blacklist"] = nil,
["Blinding Powder"] = nil, ["Blinding Powder"] = nil,
["Blue Border On Friendly Players"] = nil, ["Blue Border On Friendly Players"] = nil,
@@ -87,8 +83,6 @@ 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,
@@ -194,8 +188,6 @@ 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,
@@ -209,6 +201,7 @@ 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,
@@ -300,6 +293,7 @@ pfUI_translation["enUS"] = {
["Enable Mana Ticks"] = nil, ["Enable Mana Ticks"] = nil,
["Enable Micro Bar"] = nil, ["Enable Micro Bar"] = nil,
["Enable Mouselook With Right Click"] = nil, ["Enable Mouselook With Right Click"] = nil,
["Enable Mouseover Tooltip"] = nil,
["Enable Movable Bags"] = nil, ["Enable Movable Bags"] = nil,
["Enable Offscreen Frame Positions"] = nil, ["Enable Offscreen Frame Positions"] = nil,
["Enable Overlap"] = nil, ["Enable Overlap"] = nil,
@@ -320,7 +314,6 @@ pfUI_translation["enUS"] = {
["Encode"] = nil, ["Encode"] = nil,
["Ended"] = nil, ["Ended"] = nil,
["Energy Color"] = nil, ["Energy Color"] = nil,
["Equipped"] = nil,
["Equipped Item Color"] = nil, ["Equipped Item Color"] = nil,
["Estimate Debuffs"] = nil, ["Estimate Debuffs"] = nil,
["Estimate Enemy Health Points"] = nil, ["Estimate Enemy Health Points"] = nil,
@@ -333,7 +326,6 @@ pfUI_translation["enUS"] = {
["Export"] = nil, ["Export"] = nil,
["Face"] = nil, ["Face"] = nil,
["Fade To Custom Color"] = nil, ["Fade To Custom Color"] = nil,
["FARM MODE"] = nil,
["Fast"] = nil, ["Fast"] = nil,
["Filter Mode"] = nil, ["Filter Mode"] = nil,
["Finish"] = nil, ["Finish"] = nil,
@@ -544,7 +536,6 @@ pfUI_translation["enUS"] = {
["Next Memory Cleanup"] = nil, ["Next Memory Cleanup"] = nil,
["No"] = nil, ["No"] = nil,
["No Anchor"] = nil, ["No Anchor"] = nil,
["NO LOS"] = nil,
["None"] = nil, ["None"] = nil,
["Not a valid button!"] = nil, ["Not a valid button!"] = nil,
["No tracking spell active"] = nil, ["No tracking spell active"] = nil,
@@ -585,8 +576,6 @@ 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,
@@ -688,6 +677,7 @@ pfUI_translation["enUS"] = {
["Scale"] = nil, ["Scale"] = nil,
["Scale Border On HiDPI Displays"] = nil, ["Scale Border On HiDPI Displays"] = nil,
["Scaling"] = nil, ["Scaling"] = nil,
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = nil, ["Screen Resolution"] = nil,
["Screenshot"] = nil, ["Screenshot"] = nil,
@@ -781,8 +771,6 @@ pfUI_translation["enUS"] = {
["Small"] = nil, ["Small"] = nil,
["Some settings need to reload the UI to take effect.\nDo you want to reload now?"] = nil, ["Some settings need to reload the UI to take effect.\nDo you want to reload now?"] = nil,
["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"] = nil, ["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"] = nil,
["Sort Bags"] = nil,
["Sort Bank"] = nil,
["Sort Order"] = nil, ["Sort Order"] = nil,
["Soul Shard"] = nil, ["Soul Shard"] = nil,
["Soulshard Counter"] = nil, ["Soulshard Counter"] = nil,
@@ -847,7 +835,6 @@ pfUI_translation["enUS"] = {
["Top Actionbar"] = nil, ["Top Actionbar"] = nil,
["Top Left"] = nil, ["Top Left"] = nil,
["Top Right"] = nil, ["Top Right"] = nil,
["Total"] = nil,
["Total Gold"] = nil, ["Total Gold"] = nil,
["Totem Direction"] = nil, ["Totem Direction"] = nil,
["Totem Icons"] = nil, ["Totem Icons"] = nil,
@@ -900,6 +887,7 @@ 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,
+4 -4
View File
@@ -51,12 +51,10 @@ pfUI_translation["esES"] = {
["Auto Sell Grey Items"] = "Vender objetos grises automáticamente", ["Auto Sell Grey Items"] = "Vender objetos grises automáticamente",
["Average Per Hour"] = "Promedio por hora", ["Average Per Hour"] = "Promedio por hora",
["Background Color"] = "Color del fondo", ["Background Color"] = "Color del fondo",
["Bags"] = "Bolsas",
["Bags & Bank"] = "Bolsas y banco", ["Bags & Bank"] = "Bolsas y banco",
["Bags Border Size"] = "Tamaño del borde de las bolsas", ["Bags Border Size"] = "Tamaño del borde de las bolsas",
["Bagslots Per Row"] = "Casillas de la bolsa por fila", ["Bagslots Per Row"] = "Casillas de la bolsa por fila",
["Bagspace"] = "Huecos en las bolsas", ["Bagspace"] = "Huecos en las bolsas",
["Bank"] = "Banco",
["Banker"] = "Banquero", ["Banker"] = "Banquero",
["Bankslots Per Row"] = "Casillas del banco por fila", ["Bankslots Per Row"] = "Casillas del banco por fila",
["Bar Background"] = "Fondo de la barra", ["Bar Background"] = "Fondo de la barra",
@@ -200,6 +198,7 @@ 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",
@@ -291,6 +290,7 @@ pfUI_translation["esES"] = {
["Enable Mana Ticks"] = "Activar el indicador de pulsos del maná", ["Enable Mana Ticks"] = "Activar el indicador de pulsos del maná",
["Enable Micro Bar"] = "Activar microbarra", ["Enable Micro Bar"] = "Activar microbarra",
["Enable Mouselook With Right Click"] = "Mirar usando el puntero con clic derecho", ["Enable Mouselook With Right Click"] = "Mirar usando el puntero con clic derecho",
["Enable Mouseover Tooltip"] = "Activar la descripción emergente al pasar el puntero",
["Enable Movable Bags"] = "Permitir mover las bolsas", ["Enable Movable Bags"] = "Permitir mover las bolsas",
["Enable Offscreen Frame Positions"] = "Permitir mover los marcos fuera de la pantalla", ["Enable Offscreen Frame Positions"] = "Permitir mover los marcos fuera de la pantalla",
["Enable Overlap"] = "Permitir superposición", ["Enable Overlap"] = "Permitir superposición",
@@ -311,7 +311,6 @@ pfUI_translation["esES"] = {
["Encode"] = "Codificar", ["Encode"] = "Codificar",
["Ended"] = "Terminado", ["Ended"] = "Terminado",
["Energy Color"] = "Color de energía", ["Energy Color"] = "Color de energía",
["Equipped"] = "Equipado",
["Equipped Item Color"] = "Color del objeto equipado", ["Equipped Item Color"] = "Color del objeto equipado",
["Estimate Debuffs"] = "Estimar los perjuicios", ["Estimate Debuffs"] = "Estimar los perjuicios",
["Estimate Enemy Health Points"] = "Estimar los puntos de salud del enemigo", ["Estimate Enemy Health Points"] = "Estimar los puntos de salud del enemigo",
@@ -669,6 +668,7 @@ pfUI_translation["esES"] = {
["Scale"] = "Escala", ["Scale"] = "Escala",
["Scale Border On HiDPI Displays"] = "Escalar los bordes en las pantallas con DPI alto", ["Scale Border On HiDPI Displays"] = "Escalar los bordes en las pantallas con DPI alto",
["Scaling"] = "Escalada", ["Scaling"] = "Escalada",
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = "Intensidad de brillo en los bordes de la pantalla", ["Screen Edge Glow Intensity"] = "Intensidad de brillo en los bordes de la pantalla",
["Screen Resolution"] = "Resolución de pantalla", ["Screen Resolution"] = "Resolución de pantalla",
["Screenshot"] = "Captura de pantalla", ["Screenshot"] = "Captura de pantalla",
@@ -819,7 +819,6 @@ pfUI_translation["esES"] = {
["Top Actionbar"] = "Barra de acción superior", ["Top Actionbar"] = "Barra de acción superior",
["Top Left"] = "Superior derecha", ["Top Left"] = "Superior derecha",
["Top Right"] = "Superior izquierda", ["Top Right"] = "Superior izquierda",
["Total"] = "Total",
["Total Gold"] = "Oro Total", ["Total Gold"] = "Oro Total",
["Totem Direction"] = "Dirección del tótem", ["Totem Direction"] = "Dirección del tótem",
["Totem Icons"] = "Iconos de los tótems", ["Totem Icons"] = "Iconos de los tótems",
@@ -872,6 +871,7 @@ 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",
+4 -4
View File
@@ -51,12 +51,10 @@ pfUI_translation["frFR"] = {
["Auto Sell Grey Items"] = "Vente automatique des objets gris", ["Auto Sell Grey Items"] = "Vente automatique des objets gris",
["Average Per Hour"] = "Moyenne par heure", ["Average Per Hour"] = "Moyenne par heure",
["Background Color"] = "Couleur de l'arrière-plan", ["Background Color"] = "Couleur de l'arrière-plan",
["Bags"] = "Sacs",
["Bags & Bank"] = "Sacs et Banque", ["Bags & Bank"] = "Sacs et Banque",
["Bags Border Size"] = "Taille de la bordure des Sacs", ["Bags Border Size"] = "Taille de la bordure des Sacs",
["Bagslots Per Row"] = "Emplacements des sacs par rangée", ["Bagslots Per Row"] = "Emplacements des sacs par rangée",
["Bagspace"] = "Place libre des sacs", ["Bagspace"] = "Place libre des sacs",
["Bank"] = "Banque",
["Banker"] = "Banquier", ["Banker"] = "Banquier",
["Bankslots Per Row"] = "Emplacements de la banque par rangée", ["Bankslots Per Row"] = "Emplacements de la banque par rangée",
["Bar Background"] = "Arrière plan de la barre", ["Bar Background"] = "Arrière plan de la barre",
@@ -200,6 +198,7 @@ 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é",
@@ -291,6 +290,7 @@ pfUI_translation["frFR"] = {
["Enable Mana Ticks"] = "Activer les Ticks de mana", ["Enable Mana Ticks"] = "Activer les Ticks de mana",
["Enable Micro Bar"] = "Activer la barre des menus miniature", ["Enable Micro Bar"] = "Activer la barre des menus miniature",
["Enable Mouselook With Right Click"] = "Activer le déplacement de la caméra avec le clic droit", ["Enable Mouselook With Right Click"] = "Activer le déplacement de la caméra avec le clic droit",
["Enable Mouseover Tooltip"] = "Activer l'infobulle au passage de la souris",
["Enable Movable Bags"] = "Activer les sacs mobiles", ["Enable Movable Bags"] = "Activer les sacs mobiles",
["Enable Offscreen Frame Positions"] = "Activer le positionnement des cadres en dehors de l'écran", ["Enable Offscreen Frame Positions"] = "Activer le positionnement des cadres en dehors de l'écran",
["Enable Overlap"] = "Activer la superposition", ["Enable Overlap"] = "Activer la superposition",
@@ -311,7 +311,6 @@ pfUI_translation["frFR"] = {
["Encode"] = "Encoder", ["Encode"] = "Encoder",
["Ended"] = "Terminé", ["Ended"] = "Terminé",
["Energy Color"] = "Couleur de l'energie", ["Energy Color"] = "Couleur de l'energie",
["Equipped"] = "Équipé",
["Equipped Item Color"] = "Couleur de l'objet équipé", ["Equipped Item Color"] = "Couleur de l'objet équipé",
["Estimate Debuffs"] = "Estimer les Affaiblissements", ["Estimate Debuffs"] = "Estimer les Affaiblissements",
["Estimate Enemy Health Points"] = "Estimer les points de vie ennemis", ["Estimate Enemy Health Points"] = "Estimer les points de vie ennemis",
@@ -669,6 +668,7 @@ pfUI_translation["frFR"] = {
["Scale"] = "Échelle", ["Scale"] = "Échelle",
["Scale Border On HiDPI Displays"] = "Échelle de bordure sur les écrans HiDPI", ["Scale Border On HiDPI Displays"] = "Échelle de bordure sur les écrans HiDPI",
["Scaling"] = "Mise à l'échelle", ["Scaling"] = "Mise à l'échelle",
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = "Résolution d'écran", ["Screen Resolution"] = "Résolution d'écran",
["Screenshot"] = "Imprime écran", ["Screenshot"] = "Imprime écran",
@@ -819,7 +819,6 @@ pfUI_translation["frFR"] = {
["Top Actionbar"] = "Barre d'action supérieure", ["Top Actionbar"] = "Barre d'action supérieure",
["Top Left"] = "Haut Gauche", ["Top Left"] = "Haut Gauche",
["Top Right"] = "Haut Droit", ["Top Right"] = "Haut Droit",
["Total"] = "Total",
["Total Gold"] = "Or Total", ["Total Gold"] = "Or Total",
["Totem Direction"] = "Direction des totems", ["Totem Direction"] = "Direction des totems",
["Totem Icons"] = "Icones des totems", ["Totem Icons"] = "Icones des totems",
@@ -872,6 +871,7 @@ 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é",
+4 -4
View File
@@ -51,12 +51,10 @@ pfUI_translation["koKR"] = {
["Auto Sell Grey Items"] = "회색아이템 자동 판매", ["Auto Sell Grey Items"] = "회색아이템 자동 판매",
["Average Per Hour"] = nil, ["Average Per Hour"] = nil,
["Background Color"] = "백그라운드 색상", ["Background Color"] = "백그라운드 색상",
["Bags"] = "가방",
["Bags & Bank"] = "가방&은행", ["Bags & Bank"] = "가방&은행",
["Bags Border Size"] = "가방 테두리 크기", ["Bags Border Size"] = "가방 테두리 크기",
["Bagslots Per Row"] = nil, ["Bagslots Per Row"] = nil,
["Bagspace"] = "가방 공간", ["Bagspace"] = "가방 공간",
["Bank"] = "은행",
["Banker"] = nil, ["Banker"] = nil,
["Bankslots Per Row"] = nil, ["Bankslots Per Row"] = nil,
["Bar Background"] = nil, ["Bar Background"] = nil,
@@ -200,6 +198,7 @@ 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"] = "가리기",
@@ -291,6 +290,7 @@ pfUI_translation["koKR"] = {
["Enable Mana Ticks"] = nil, ["Enable Mana Ticks"] = nil,
["Enable Micro Bar"] = "미니바 보기(블리자드 마이크로바)", ["Enable Micro Bar"] = "미니바 보기(블리자드 마이크로바)",
["Enable Mouselook With Right Click"] = "오른쪽 클릭 Mouselook 사용", ["Enable Mouselook With Right Click"] = "오른쪽 클릭 Mouselook 사용",
["Enable Mouseover Tooltip"] = nil,
["Enable Movable Bags"] = nil, ["Enable Movable Bags"] = nil,
["Enable Offscreen Frame Positions"] = "오프스크린 프레임 위치 활성화", ["Enable Offscreen Frame Positions"] = "오프스크린 프레임 위치 활성화",
["Enable Overlap"] = "겹쳐서 표시", ["Enable Overlap"] = "겹쳐서 표시",
@@ -311,7 +311,6 @@ pfUI_translation["koKR"] = {
["Encode"] = nil, ["Encode"] = nil,
["Ended"] = nil, ["Ended"] = nil,
["Energy Color"] = nil, ["Energy Color"] = nil,
["Equipped"] = "착용 중",
["Equipped Item Color"] = nil, ["Equipped Item Color"] = nil,
["Estimate Debuffs"] = nil, ["Estimate Debuffs"] = nil,
["Estimate Enemy Health Points"] = nil, ["Estimate Enemy Health Points"] = nil,
@@ -669,6 +668,7 @@ pfUI_translation["koKR"] = {
["Scale"] = nil, ["Scale"] = nil,
["Scale Border On HiDPI Displays"] = nil, ["Scale Border On HiDPI Displays"] = nil,
["Scaling"] = nil, ["Scaling"] = nil,
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = "화면 해상도", ["Screen Resolution"] = "화면 해상도",
["Screenshot"] = nil, ["Screenshot"] = nil,
@@ -819,7 +819,6 @@ pfUI_translation["koKR"] = {
["Top Actionbar"] = nil, ["Top Actionbar"] = nil,
["Top Left"] = nil, ["Top Left"] = nil,
["Top Right"] = nil, ["Top Right"] = nil,
["Total"] = "총",
["Total Gold"] = "총 금", ["Total Gold"] = "총 금",
["Totem Direction"] = nil, ["Totem Direction"] = nil,
["Totem Icons"] = nil, ["Totem Icons"] = nil,
@@ -872,6 +871,7 @@ 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,
+4 -4
View File
@@ -51,12 +51,10 @@ pfUI_translation["ruRU"] = {
["Auto Sell Grey Items"] = "Автоматическая продажа серых предметов", ["Auto Sell Grey Items"] = "Автоматическая продажа серых предметов",
["Average Per Hour"] = "Среднее за час", ["Average Per Hour"] = "Среднее за час",
["Background Color"] = "Цвет фона", ["Background Color"] = "Цвет фона",
["Bags"] = "Сумки",
["Bags & Bank"] = "Сумки & Банк", ["Bags & Bank"] = "Сумки & Банк",
["Bags Border Size"] = "Размер границы сумок", ["Bags Border Size"] = "Размер границы сумок",
["Bagslots Per Row"] = "Количество ячеек в сумке на строку", ["Bagslots Per Row"] = "Количество ячеек в сумке на строку",
["Bagspace"] = "Место в сумках", ["Bagspace"] = "Место в сумках",
["Bank"] = "Банк",
["Banker"] = "Банкир", ["Banker"] = "Банкир",
["Bankslots Per Row"] = "Количество ячеек в банке на строку", ["Bankslots Per Row"] = "Количество ячеек в банке на строку",
["Bar Background"] = "Фон панели", ["Bar Background"] = "Фон панели",
@@ -200,6 +198,7 @@ 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"] = "Отключено",
@@ -291,6 +290,7 @@ pfUI_translation["ruRU"] = {
["Enable Mana Ticks"] = "Включить восполнение маны", ["Enable Mana Ticks"] = "Включить восполнение маны",
["Enable Micro Bar"] = "Включить панель микро меню", ["Enable Micro Bar"] = "Включить панель микро меню",
["Enable Mouselook With Right Click"] = "Включить нажатие правой кнопки мыши на индикаторе здоровья", ["Enable Mouselook With Right Click"] = "Включить нажатие правой кнопки мыши на индикаторе здоровья",
["Enable Mouseover Tooltip"] = "Включить подсказку при наведении мыши",
["Enable Movable Bags"] = "Включить подвижные сумки", ["Enable Movable Bags"] = "Включить подвижные сумки",
["Enable Offscreen Frame Positions"] = "Включить расположение окон за пределами экрана", ["Enable Offscreen Frame Positions"] = "Включить расположение окон за пределами экрана",
["Enable Overlap"] = "Включить перекрытие", ["Enable Overlap"] = "Включить перекрытие",
@@ -311,7 +311,6 @@ pfUI_translation["ruRU"] = {
["Encode"] = "Кодировать", ["Encode"] = "Кодировать",
["Ended"] = "Окончание", ["Ended"] = "Окончание",
["Energy Color"] = "Цвет энергии", ["Energy Color"] = "Цвет энергии",
["Equipped"] = "Экипировано",
["Equipped Item Color"] = "Цвет экипированных предметов", ["Equipped Item Color"] = "Цвет экипированных предметов",
["Estimate Debuffs"] = "Оценка дебафов", ["Estimate Debuffs"] = "Оценка дебафов",
["Estimate Enemy Health Points"] = "Оценка очков здоровья врага", ["Estimate Enemy Health Points"] = "Оценка очков здоровья врага",
@@ -669,6 +668,7 @@ pfUI_translation["ruRU"] = {
["Scale"] = "Масштаб", ["Scale"] = "Масштаб",
["Scale Border On HiDPI Displays"] = "Масштабировать границы на HiDPI мониторах", ["Scale Border On HiDPI Displays"] = "Масштабировать границы на HiDPI мониторах",
["Scaling"] = "Масштаб интерфейса", ["Scaling"] = "Масштаб интерфейса",
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = "Интенсивность свечения на краях экрана", ["Screen Edge Glow Intensity"] = "Интенсивность свечения на краях экрана",
["Screen Resolution"] = "Разрешение экрана", ["Screen Resolution"] = "Разрешение экрана",
["Screenshot"] = "Снимок экрана", ["Screenshot"] = "Снимок экрана",
@@ -819,7 +819,6 @@ pfUI_translation["ruRU"] = {
["Top Actionbar"] = "Верхняя", ["Top Actionbar"] = "Верхняя",
["Top Left"] = "Сверху слева", ["Top Left"] = "Сверху слева",
["Top Right"] = "Сверху справа", ["Top Right"] = "Сверху справа",
["Total"] = "Всего",
["Total Gold"] = "Всего золота", ["Total Gold"] = "Всего золота",
["Totem Direction"] = "Направление тотемов", ["Totem Direction"] = "Направление тотемов",
["Totem Icons"] = "Иконки тотемов", ["Totem Icons"] = "Иконки тотемов",
@@ -872,6 +871,7 @@ 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"] = "Победа",
+4 -4
View File
@@ -51,12 +51,10 @@ pfUI_translation["zhCN"] = {
["Auto Sell Grey Items"] = "自动贩卖灰色物品", ["Auto Sell Grey Items"] = "自动贩卖灰色物品",
["Average Per Hour"] = "平均每小时", ["Average Per Hour"] = "平均每小时",
["Background Color"] = "背景颜色", ["Background Color"] = "背景颜色",
["Bags"] = "背包",
["Bags & Bank"] = "背包和银行", ["Bags & Bank"] = "背包和银行",
["Bags Border Size"] = "背包边框大小", ["Bags Border Size"] = "背包边框大小",
["Bagslots Per Row"] = "每行包裹槽数量", ["Bagslots Per Row"] = "每行包裹槽数量",
["Bagspace"] = "背包空间", ["Bagspace"] = "背包空间",
["Bank"] = "银行",
["Banker"] = "工会银行角色", ["Banker"] = "工会银行角色",
["Bankslots Per Row"] = "每行银行槽数量", ["Bankslots Per Row"] = "每行银行槽数量",
["Bar Background"] = "动作条背景", ["Bar Background"] = "动作条背景",
@@ -200,6 +198,7 @@ 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"] = "不显示",
@@ -291,6 +290,7 @@ pfUI_translation["zhCN"] = {
["Enable Mana Ticks"] = "显示法力值刻度", ["Enable Mana Ticks"] = "显示法力值刻度",
["Enable Micro Bar"] = "显示菜单栏", ["Enable Micro Bar"] = "显示菜单栏",
["Enable Mouselook With Right Click"] = "启动右键移动镜头", ["Enable Mouselook With Right Click"] = "启动右键移动镜头",
["Enable Mouseover Tooltip"] = "启用鼠标悬停工具提示",
["Enable Movable Bags"] = "启用可移动包裹", ["Enable Movable Bags"] = "启用可移动包裹",
["Enable Offscreen Frame Positions"] = "允许本插件所有框体移出屏幕边缘", ["Enable Offscreen Frame Positions"] = "允许本插件所有框体移出屏幕边缘",
["Enable Overlap"] = "叠加显示", ["Enable Overlap"] = "叠加显示",
@@ -311,7 +311,6 @@ pfUI_translation["zhCN"] = {
["Encode"] = "编码", ["Encode"] = "编码",
["Ended"] = "结束", ["Ended"] = "结束",
["Energy Color"] = "能量值颜色", ["Energy Color"] = "能量值颜色",
["Equipped"] = "已装备",
["Equipped Item Color"] = "已装备物品颜色", ["Equipped Item Color"] = "已装备物品颜色",
["Estimate Debuffs"] = "预估Debuffs", ["Estimate Debuffs"] = "预估Debuffs",
["Estimate Enemy Health Points"] = "预估敌人的生命值", ["Estimate Enemy Health Points"] = "预估敌人的生命值",
@@ -669,6 +668,7 @@ pfUI_translation["zhCN"] = {
["Scale"] = "比例", ["Scale"] = "比例",
["Scale Border On HiDPI Displays"] = "缩放高DPI显示器上的边框", ["Scale Border On HiDPI Displays"] = "缩放高DPI显示器上的边框",
["Scaling"] = "UI缩放", ["Scaling"] = "UI缩放",
["Scan Macros For Spells"] = "扫描宏命令中的法术",
["Screen Edge Glow Intensity"] = "屏幕边缘发光强度", ["Screen Edge Glow Intensity"] = "屏幕边缘发光强度",
["Screen Resolution"] = "屏幕分辨率", ["Screen Resolution"] = "屏幕分辨率",
["Screenshot"] = "屏幕截图", ["Screenshot"] = "屏幕截图",
@@ -820,7 +820,6 @@ pfUI_translation["zhCN"] = {
["Top Actionbar"] = "上方动作条", ["Top Actionbar"] = "上方动作条",
["Top Left"] = "左上方", ["Top Left"] = "左上方",
["Top Right"] = "右上方", ["Top Right"] = "右上方",
["Total"] = "总计",
["Total Gold"] = "总黄金", ["Total Gold"] = "总黄金",
["Totem Direction"] = "图腾方向", ["Totem Direction"] = "图腾方向",
["Totem Icons"] = "图腾图标", ["Totem Icons"] = "图腾图标",
@@ -873,6 +872,7 @@ 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"] = "获胜",
+4 -4
View File
@@ -51,12 +51,10 @@ pfUI_translation["zhTW"] = {
["Auto Sell Grey Items"] = "自動販賣灰色物品", ["Auto Sell Grey Items"] = "自動販賣灰色物品",
["Average Per Hour"] = "平均每小時", ["Average Per Hour"] = "平均每小時",
["Background Color"] = "背景顏色", ["Background Color"] = "背景顏色",
["Bags"] = "背包",
["Bags & Bank"] = "背包和銀行", ["Bags & Bank"] = "背包和銀行",
["Bags Border Size"] = "背包邊框大小", ["Bags Border Size"] = "背包邊框大小",
["Bagslots Per Row"] = "每行包裹槽數量", ["Bagslots Per Row"] = "每行包裹槽數量",
["Bagspace"] = "背包空間", ["Bagspace"] = "背包空間",
["Bank"] = "銀行",
["Banker"] = "工會銀行角色", ["Banker"] = "工會銀行角色",
["Bankslots Per Row"] = "每行銀行槽數量", ["Bankslots Per Row"] = "每行銀行槽數量",
["Bar Background"] = nil, ["Bar Background"] = nil,
@@ -200,6 +198,7 @@ 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"] = "不顯示",
@@ -291,6 +290,7 @@ pfUI_translation["zhTW"] = {
["Enable Mana Ticks"] = nil, ["Enable Mana Ticks"] = nil,
["Enable Micro Bar"] = "顯示功能表列", ["Enable Micro Bar"] = "顯示功能表列",
["Enable Mouselook With Right Click"] = "啟動右鍵移動鏡頭", ["Enable Mouselook With Right Click"] = "啟動右鍵移動鏡頭",
["Enable Mouseover Tooltip"] = "啟用滑鼠懸停工具提示",
["Enable Movable Bags"] = "啟用可移動包裹", ["Enable Movable Bags"] = "啟用可移動包裹",
["Enable Offscreen Frame Positions"] = "啟用螢幕框架固定", ["Enable Offscreen Frame Positions"] = "啟用螢幕框架固定",
["Enable Overlap"] = "疊加顯示", ["Enable Overlap"] = "疊加顯示",
@@ -311,7 +311,6 @@ pfUI_translation["zhTW"] = {
["Encode"] = "編碼", ["Encode"] = "編碼",
["Ended"] = nil, ["Ended"] = nil,
["Energy Color"] = nil, ["Energy Color"] = nil,
["Equipped"] = "已裝備",
["Equipped Item Color"] = nil, ["Equipped Item Color"] = nil,
["Estimate Debuffs"] = nil, ["Estimate Debuffs"] = nil,
["Estimate Enemy Health Points"] = nil, ["Estimate Enemy Health Points"] = nil,
@@ -669,6 +668,7 @@ pfUI_translation["zhTW"] = {
["Scale"] = "比例", ["Scale"] = "比例",
["Scale Border On HiDPI Displays"] = nil, ["Scale Border On HiDPI Displays"] = nil,
["Scaling"] = nil, ["Scaling"] = nil,
["Scan Macros For Spells"] = nil,
["Screen Edge Glow Intensity"] = nil, ["Screen Edge Glow Intensity"] = nil,
["Screen Resolution"] = "螢幕解析度", ["Screen Resolution"] = "螢幕解析度",
["Screenshot"] = nil, ["Screenshot"] = nil,
@@ -819,7 +819,6 @@ pfUI_translation["zhTW"] = {
["Top Actionbar"] = nil, ["Top Actionbar"] = nil,
["Top Left"] = "左上方", ["Top Left"] = "左上方",
["Top Right"] = "右上方", ["Top Right"] = "右上方",
["Total"] = "全部",
["Total Gold"] = "總金量", ["Total Gold"] = "總金量",
["Totem Direction"] = nil, ["Totem Direction"] = nil,
["Totem Icons"] = nil, ["Totem Icons"] = nil,
@@ -872,6 +871,7 @@ 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 -1
View File
@@ -1,3 +1,3 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/"> <Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\env\tables_stock.lua"/> <Include file="..\compat\vanilla.lua"/>
</Ui> </Ui>
+2 -2
View File
@@ -70,7 +70,8 @@
<Include file="..\modules\addoncompat.lua"/> <Include file="..\modules\addoncompat.lua"/>
<Include file="..\modules\energytick.lua"/> <Include file="..\modules\energytick.lua"/>
<Include file="..\modules\totems.lua"/> <Include file="..\modules\totems.lua"/>
<Include file="..\modules\macroicons.lua"/> <Include file="..\modules\macrotweak.lua"/>
<Include file="..\modules\turtle-wow.lua"/>
<Include file="..\modules\superwow.lua"/> <Include file="..\modules\superwow.lua"/>
<Include file="..\modules\innervatecall.lua"/> <Include file="..\modules\innervatecall.lua"/>
<Include file="..\modules\nampower.lua"/> <Include file="..\modules\nampower.lua"/>
@@ -79,5 +80,4 @@
<Include file="..\modules\equipmentmanager.lua"/> <Include file="..\modules\equipmentmanager.lua"/>
<Include file="..\modules\loothistory.lua"/> <Include file="..\modules\loothistory.lua"/>
<Include file="..\modules\newitem.lua"/> <Include file="..\modules\newitem.lua"/>
<Include file="..\modules\friendnotes.lua"/>
</Ui> </Ui>
+9 -2
View File
@@ -30,12 +30,19 @@
<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"/>
<Include file="..\skins\blizzard\tabard.lua"/> <Include file="..\skins\blizzard\tabard.lua"/>
<Include file="..\skins\blizzard\itemtext.lua"/> <Include file="..\skins\blizzard\itemtext.lua"/>
<Include file="..\skins\blizzard\lft.lua"/>
<Include file="..\skins\blizzard\turtle_shop.lua"/>
<!-- Turtle WoW -->
<Include file="..\skins\blizzard\barbershop.lua"/>
<Include file="..\skins\blizzard\transmog.lua"/>
<Include file="..\skins\blizzard\custom_merchant.lua"/>
<Include file="..\skins\blizzard\arena_score.lua"/>
<Include file="..\skins\blizzard\ebc.lua"/>
<!-- Turtle WoW skins live in init\turtle.xml (pfUI_Turtle.toc only). -->
</Ui> </Ui>
-14
View File
@@ -1,14 +0,0 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<!-- Turtle WoW only: pulled in by pfUI_Turtle.toc, which ClassicAPI selects
when it detects a Turtle client. Everything in here targets frames that
only a Turtle client ever creates. -->
<Include file="..\env\tables_turtle.lua"/>
<Include file="..\skins\blizzard\lft.lua"/>
<Include file="..\skins\blizzard\turtle_shop.lua"/>
<Include file="..\skins\blizzard\barbershop.lua"/>
<Include file="..\skins\blizzard\transmog.lua"/>
<Include file="..\skins\blizzard\ebc.lua"/>
<Include file="..\modules\turtle-wow.lua"/>
</Ui>
+14 -37
View File
@@ -18,7 +18,12 @@ 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 return "01" end if classID == ItemClass.Weapon or classID == ItemClass.Armor then
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
@@ -37,40 +42,12 @@ local function SortCountSuffix(count)
return string.sub(s, -6) return string.sub(s, -6)
end end
-- Gear's slot order, keyed by the equipLoc token (INVTYPE_*). Not the local function SortKey(itemId, name, classID, subClassID, quality, count)
-- token's alphabetical order, which puts chest before head, and not its -- Zero-pad the class/subclass so the secondary grouping sorts numerically
-- INVTYPE number, which parts robe (20) from chest (5). Same order as -- (as a string, "10" would otherwise precede "2").
-- ClassicAPI's C_Container.SortBags, taken from Baganator: weapons by hand, return SortCategoryPrefix(itemId, classID, quality)
-- ranged and ammo, then armor with shields and off-hands first, head to .. string.format("%02d|%02d|", classID or 99, subClassID or 99)
-- feet, then jewelry, shirt and tabard. .. (name or "zzz") .. "|" .. SortCountSuffix(count)
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()
@@ -191,10 +168,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, _, _, _, _, _, equipLoc, _, _, classID, subClassID = C_Item.GetItemInfo(itemId) local name, _, quality, _, _, _, _, _, _, _, _, 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, equipLoc), key = SortKey(itemId, name, classID, subClassID, quality, count),
-- 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,
+1674 -25
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -10,8 +10,8 @@ local libhealth = CreateFrame("Frame")
libhealth.enabled = true libhealth.enabled = true
libhealth.reqhit = 4 libhealth.reqhit = 4
libhealth.reqdmg = 5 libhealth.reqdmg = 5
libhealth:RegisterUnitEvent("UNIT_HEALTH", "target") libhealth:RegisterEvent("UNIT_HEALTH")
libhealth:RegisterUnitEvent("UNIT_COMBAT", "target") libhealth:RegisterEvent("UNIT_COMBAT")
libhealth:RegisterEvent("PLAYER_TARGET_CHANGED") libhealth:RegisterEvent("PLAYER_TARGET_CHANGED")
libhealth:RegisterEvent("PLAYER_ENTERING_WORLD") libhealth:RegisterEvent("PLAYER_ENTERING_WORLD")
libhealth:SetScript("OnEvent", function() libhealth:SetScript("OnEvent", function()
+207 -163
View File
@@ -13,23 +13,34 @@ 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.
-- --
-- Casts are read from ClassicAPI's UNIT_SPELLCAST_* events and heal amounts -- HOT TRACKING INTEGRATION (NEW):
-- from COMBAT_LOG_EVENT_UNFILTERED. GetHotDuration() reads live HoT timers -- With Nampower enabled, HoT tracking now primarily uses libdebuff's AURA_CAST
-- from C_UnitAuras (via libdebuff) and falls back to the HealComm-fed table. -- event system for accurate server-side buff/debuff tracking with full rank
-- 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 interrupted-cast cleanup local ressGuidToName = {} -- [casterGuid] = casterName, for SPELL_FAILED_OTHER cleanup
local healGuidToName = {} -- [casterGuid] = casterName, for interrupted-cast cleanup local healGuidToName = {} -- [casterGuid] = casterName, for SPELL_FAILED_OTHER 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
@@ -113,25 +124,43 @@ libpredict:SetScript("OnEvent", function()
if origOnEvent then origOnEvent() end if origOnEvent then origOnEvent() end
end) end)
-- Own cast-time spell started (heals + rez) -- SPELL_START_SELF: own cast started (heals + rez)
local function OnOwnCastStart(spellId, castGUID) pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {}
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
-- SENT names the unit the cast went out at; self and no-target casts local pendingTarget = nil
-- carry none, so fall back to what the cast hooks recorded local pendingTargetGuid = nil
local target = sentTargets[castGUID] or spell_queue[3] local pending = pfUI.libpredict_pending_cast
local _, _, _, startMs, endMs = C_Spell.CastingInfo() if pending and pending.spellName == spellName and pending.targetGuid
local castTime = startMs and endMs - startMs or 0 and pending.time and (GetTime() - pending.time) < 1 then
-- 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, UnitGUID("player")) libpredict:Ress(player, target, casterGuid)
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
@@ -139,9 +168,6 @@ local function OnOwnCastStart(spellId, castGUID)
return return
end end
spell_queue[1] = spellName
spell_queue[2] = spellName .. (C_Spell.GetSpellSubtext(spellId) or "")
if spell_queue[1] == spellName and cache[spell_queue[2]] then if spell_queue[1] == spellName and cache[spell_queue[2]] then
local amount = cache[spell_queue[2]][1] local amount = cache[spell_queue[2]][1]
local casttime = castTime local casttime = castTime
@@ -206,17 +232,27 @@ local function OnOwnCastStart(spellId, castGUID)
return -- skip the generic Heal call below return -- skip the generic Heal call below
end end
libpredict:Heal(player, target, amount, casttime) -- If the resolved heal target is itself hostile (e.g. self-cast heal while targeting an enemy),
libpredict.sender:SendHealCommMsg("Heal/" .. (target or "") .. "/" .. amount .. "/" .. casttime .. "/") -- store the heal under the player so the player frame shows the prediction.
-- 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
-- Own cast landed: HealStop, Regrowth timer, instant HoTs, own rez timer -- SPELL_GO_SELF: own cast landed (HealStop + Regrowth timer)
local function OnOwnCastSucceeded(spellId, castGUID) pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId)
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
@@ -228,57 +264,32 @@ local function OnOwnCastSucceeded(spellId, castGUID)
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
-- Foreign cast-time spell started (heals + rez) -- SPELL_START_OTHER: foreign cast started (heals + rez)
local function OnOtherCastStart(unit, spellId, castGUID) -- Signature: fn(spellId, casterGuid, targetGuid, castTime)
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
libpredict:Ress(casterName, targetName, casterGuid) local targetName = resolveNameFromGuid(targetGuid)
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 _, _, _, startMs, endMs = C_Spell.UnitCastingInfo(unit) local targetName = resolveNameFromGuid(targetGuid)
if not startMs then return end if not targetName 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 "")
@@ -324,26 +335,77 @@ local function OnOtherCastStart(unit, spellId, castGUID)
libpredict:Heal(casterName, targetName, amount, castTime, casterGuid) libpredict:Heal(casterName, targetName, amount, castTime, casterGuid)
end end
-- Foreign cast landed: rez timer, or the predicted heal is fulfilled -- SPELL_GO_SELF: own cast landed - HoTs + own rez timer
local function OnOtherCastSucceeded(unit, spellId, castGUID) -- Signature: fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
local casterGuid = UnitGUID(unit) pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
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 = castTargets[castGUID] local targetName = resolveNameFromGuid(targetGuid)
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
-- Foreign cast interrupted: remove cancelled resses/heals -- SPELL_FAILED_OTHER: remove cancelled resses/heals
local function OnOtherCastInterrupted(unit) -- Signature: fn(casterGuid, spellId)
local casterGuid = UnitGUID(unit) pfUI.libdebuff_spell_failed_other_hooks = pfUI.libdebuff_spell_failed_other_hooks or {}
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
@@ -502,7 +564,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 UNIT_SPELLCAST_START) -- HealComm: only for other players (own resses tracked via SPELL_START_SELF hook)
local playerName = UnitName("player") local playerName = UnitName("player")
if sender ~= playerName then if sender ~= playerName then
local senderGuid local senderGuid
@@ -847,7 +909,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: UNIT_SPELLCAST_SUCCEEDED and C_UnitAuras, hook method as fallback -- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback
if effect == REJUVENATION then if effect == REJUVENATION then
local target = spell_queue[3] local target = spell_queue[3]
@@ -911,7 +973,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
spell_queue[3] = target or default spell_queue[3] = target or default
end end
-- Instant HoTs: UNIT_SPELLCAST_SUCCEEDED and C_UnitAuras, hook method as fallback -- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback
if effect == REJUVENATION then if effect == REJUVENATION then
local hotTarget = target or default local hotTarget = target or default
@@ -972,7 +1034,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: UNIT_SPELLCAST_SUCCEEDED and C_UnitAuras, hook method as fallback -- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback
if effect == REJUVENATION then if effect == REJUVENATION then
local hotTarget = spell_queue[3] local hotTarget = spell_queue[3]
@@ -1062,17 +1124,15 @@ libpredict.sender:SetScript("OnUpdate", function()
end end
end) end)
-- Own casts arrive as "player"; every other caster fires once per unit token -- Nampower events
-- it currently holds (raidN, target, nameplateN, ...), so other-unit handlers libpredict.sender:RegisterEvent("SPELL_FAILED_SELF")
-- drop the repeats by castGUID. libpredict.sender:RegisterEvent("SPELL_DELAYED_SELF")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_SENT") libpredict.sender:RegisterEvent("SPELL_HEAL_BY_SELF")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_START") libpredict.sender:RegisterEvent("SPELL_HEAL_BY_OTHER") -- populates foreignCache for other healers
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_DELAYED")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED") -- force cache updates
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_FAILED") libpredict.sender:RegisterEvent("UNIT_INVENTORY_CHANGED")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED") libpredict.sender:RegisterEvent("SKILL_LINES_CHANGED")
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()
@@ -1082,7 +1142,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 spell_queue[3] local target = s.current_cast_target or senttarget 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 ""))
@@ -1094,103 +1154,81 @@ 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
-- Real heal amounts: own heals refresh the spell cache, other group healers' libpredict.sender:SetScript("OnEvent", function()
-- heals fill foreignCache (one event per hit target for AoE heals like PoH). -- ============================================================
local function OnHeal() -- NAMPOWER PATH
-- arg4=sourceGUID, arg12=spellId, arg13=spellName, arg15=amount, arg18=critical -- ============================================================
local spellId, spellName, amount, isCrit = arg12, arg13, arg15, arg18 == 1 if event == "SPELL_HEAL_BY_SELF" then
if not spellName or not amount or amount <= 0 then return end -- arg1=targetGuid, arg2=casterGuid, arg3=spellId, arg4=amount, arg5=critical, arg6=periodic
local spellId = arg3
if IsPlayerGuid(arg4) then local amount = arg4
if spell_queue[1] == spellName then local isCrit = arg5 == 1
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
local casterName = resolveNameFromGuid(arg4) elseif event == "SPELL_HEAL_BY_OTHER" then
if not casterName then return end -- Fires once per hit target for AoE heals (e.g. PoH).
-- 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
local spellKey = spellName .. (C_Spell.GetSpellSubtext(spellId) or "") if isPeriodic or not spellId or not amount or amount <= 0 then return end
foreignCache[casterName] = foreignCache[casterName] or {} local casterName = resolveNameFromGuid(casterGuid)
local existing = foreignCache[casterName][spellKey] if not casterName or casterName == player then return end -- own heals handled by SPELL_HEAL_BY_SELF
-- 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 lastOtherCast = {} -- [event] = castGUID of the last other-unit event handled local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end
local rankStr = C_Spell.GetSpellSubtext(spellId)
local spellKey = spellName .. (rankStr or "")
libpredict.sender:SetScript("OnEvent", function() foreignCache[casterName] = foreignCache[casterName] or {}
if event == "COMBAT_LOG_EVENT_UNFILTERED" then local existing = foreignCache[casterName][spellKey]
if arg2 == "SPELL_HEAL" then OnHeal() end -- Store highest non-crit value for best prediction accuracy
return if isCrit then
end -- Estimate base from crit (vanilla crit = 150%)
local base = math.floor(amount * 2 / 3)
-- UNIT_SPELLCAST_SENT: arg1=unit, arg2=target token, arg3=castGUID, arg4=spellID if not existing or base > existing then
-- every other UNIT_SPELLCAST_*: arg1=unit, arg2=castGUID, arg3=spellID foreignCache[casterName][spellKey] = base
local unit = arg1 end
if unit == "player" then else
if event == "UNIT_SPELLCAST_SENT" then if not existing or amount > existing then
sentTargets[arg3] = arg2 ~= "" and UnitName(arg2) or nil foreignCache[casterName][spellKey] = amount
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
if UnitIsUnit(unit, "player") or lastOtherCast[event] == arg2 then return end elseif event == "SPELL_FAILED_SELF" then
lastOtherCast[event] = arg2 onCastFailed()
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 == UNKOWNBEING then return end
-- Live aura timer first (C_UnitAuras via libdebuff) -- NEW: Try libdebuff first (Nampower AURA_CAST events)
if pfUI.api.libdebuff then if pfUI.api.libdebuff and pfUI.api.libdebuff.GetBestAuraCast 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
@@ -1205,6 +1243,7 @@ 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))
@@ -1214,7 +1253,7 @@ function libpredict:GetHotDuration(unit, spell)
end end
end end
-- FALLBACK: the HealComm/cast-fed table, for HoTs C_UnitAuras has no timer for -- FALLBACK: Use old prediction system (for non-Nampower clients or no AURA_CAST data)
local start, duration, timeleft local start, duration, timeleft
local now = pfUI.uf.now or GetTime() local now = pfUI.uf.now or GetTime()
@@ -1248,11 +1287,16 @@ _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")
if pfUI.api.libdebuff then -- Check libdebuff availability
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 C_UnitAuras timers") DEFAULT_CHAT_FRAME:AddMessage(" Using AURA_CAST events for server-side tracking")
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")
+5 -6
View File
@@ -83,13 +83,12 @@ end
-- [number] Casting time of the spell in milliseconds -- [number] Casting time of the spell in milliseconds
-- [number] Minimum range from the target required to cast the spell -- [number] Minimum range from the target required to cast the spell
-- [number] Maximum range from the target at which you can cast the spell -- [number] Maximum range from the target at which you can cast the spell
-- [number] Spell's index in the book
-- [number] The type of the spellbook that the spell is in
-- [number] The numeric spell-id of the spell -- [number] The numeric spell-id of the spell
-- [number] The type of the spellbook that the spell is in
local spellinfo = {} local spellinfo = {}
function libspell.GetSpellInfo(index, bookType) function libspell.GetSpellInfo(index, bookType)
local cache = spellinfo[index] local cache = spellinfo[index]
if cache then return unpack(cache) end if cache then return cache[1], cache[2], cache[3], cache[4], cache[5], cache[6], cache[7], cache[8] end
local slot local slot
if type(index) == "string" then if type(index) == "string" then
@@ -109,10 +108,10 @@ function libspell.GetSpellInfo(index, bookType)
-- ClassicAPI's GetSpellInfo returns: name, rank, icon, cost, isFunnel, powerType, -- ClassicAPI's GetSpellInfo returns: name, rank, icon, cost, isFunnel, powerType,
-- castTime(ms), minRange, maxRange, spellID. Keep libspell's historical positional -- castTime(ms), minRange, maxRange, spellID. Keep libspell's historical positional
-- shape (castingTime at 4, ranges at 5/6, slot+bookType at 7/8). -- shape (castingTime at 4, ranges at 5/6, slot+bookType at 7/8).
local name, rank, icon, _, _, _, castingTime, minRange, maxRange, spellId = GetSpellInfo(slot, bookType) local name, rank, icon, _, _, _, castingTime, minRange, maxRange = GetSpellInfo(slot, bookType)
spellinfo[index] = SafePack(name, rank, icon, castingTime, minRange, maxRange, slot, bookType, spellId) spellinfo[index] = { name, rank, icon, castingTime, minRange, maxRange, slot, bookType }
return name, rank, icon, castingTime, minRange, maxRange, slot, bookType, spellId return name, rank, icon, castingTime, minRange, maxRange, slot, bookType
end end
-- Reset all spell caches whenever new spells are learned/unlearned -- Reset all spell caches whenever new spells are learned/unlearned
+11 -10
View File
@@ -96,12 +96,13 @@ libunitscan:SetScript("OnEvent", function()
RememberByUnit("player", name, class) RememberByUnit("player", name, class)
elseif event == "FRIENDLIST_UPDATE" then elseif event == "FRIENDLIST_UPDATE" then
local name, class, level
for i = 1, GetNumFriends() do for i = 1, GetNumFriends() do
local info = C_FriendList.GetFriendInfoByIndex(i) name, level, class = GetFriendInfo(i)
if info then class = L["class"][class] or nil
local level = info.level > 0 and info.level or nil -- friendlist updates due to friend going off-line return level 0, let's not overwrite good older values
AddData("players", info.name, info.classFilename, level) level = level > 0 and level or nil
end AddData("players", name, class, level)
end end
elseif event == "GUILD_ROSTER_UPDATE" then elseif event == "GUILD_ROSTER_UPDATE" then
@@ -136,11 +137,11 @@ libunitscan:SetScript("OnEvent", function()
end end
elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then
for i = 1, C_FriendList.GetNumWhoResults() do local name, class, level, guild, _
local info = C_FriendList.GetWhoInfo(i) for i = 1, GetNumWhoResults() do
if info then name, guild, level, _, class, _ = GetWhoInfo(i)
AddData("players", info.fullName, info.filename, info.level, nil, info.fullGuildName) class = L["class"][class] or nil
end AddData("players", name, class, level, nil, guild)
end end
elseif event == "UPDATE_MOUSEOVER_UNIT" or event == "PLAYER_TARGET_CHANGED" or event == "NAME_PLATE_UNIT_ADDED" then elseif event == "UPDATE_MOUSEOVER_UNIT" or event == "PLAYER_TARGET_CHANGED" or event == "NAME_PLATE_UNIT_ADDED" then
+184 -163
View File
@@ -81,9 +81,6 @@ 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
@@ -100,6 +97,8 @@ 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,
@@ -318,7 +317,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 MouseIsOverHitRect(self) then elseif not MouseIsOver(self) then
self.highlight:Hide() self.highlight:Hide()
end end
end end
@@ -361,8 +360,73 @@ pfUI:RegisterModule("actionbar", function ()
end end
end end
local function ButtonMacroScan(self)
if self.bar > 10 then return end
if not self.scanmacro then return end
if pfUI.bars.skip_macro then return end
-- SuperCleveRoidMacros: for macros it manages, leave spellslot/booktype unset
-- so the button's icon, cooldown, and tooltip flow through the hooked
-- GetActionTexture / GetActionCooldown / GameTooltip:SetAction and follow the
-- active conditional dynamically, instead of being frozen to the first
-- statically-scanned spell.
if CleveRoids and CleveRoids.IsManagedAction and CleveRoids.IsManagedAction(self.id) then
self.spellslot, self.booktype = nil, nil
return
end
local kind, slot = GetActionInfo(self.id)
self.spellslot, self.booktype = nil, nil
if kind == 'macro' then
local name, _, body = GetMacroInfo(slot)
if name and body then
local match
for line in gfind(body, "[^%\n]+") do
_, _, match = string.find(line, '^#showtooltip (.+)')
-- allow the user to disable the scan
if match and strfind(match, "disable") then
return
end
if not match then
-- add support to specify custom tooltips via:
-- /run --showtooltip SPELLNAME
_, _, match = string.find(line, '%-%-showtooltip (.+)')
end
if not match then
_, _, match = string.find(line, '^/cast (.+)')
end
if not match then
_, _, match = string.find(line, '^/pfcast (.+)')
end
if not match then
_, _, match = string.find(line, '^/pfmouse (.+)')
end
if not match then
_, _, match = string.find(line, 'CastSpellByName%(%"(.+)%"%)')
end
if match then
local _, _, spell, rank = string.find(match, '(.+)%((.+)%)')
spell = spell or match
self.spellslot, self.booktype = libspell.GetSpellIndex(spell, rank)
if self.spellslot and self.spellslot > 0 then return end
end
end
end
end
end
local function ButtonEnter(self) local function ButtonEnter(self)
self = self or this local self = self or this
-- indicate that dragging could get enabled -- indicate that dragging could get enabled
drag_await = true drag_await = true
@@ -380,8 +444,6 @@ pfUI:RegisterModule("actionbar", function ()
else else
GameTooltip:SetPetAction(self.id) GameTooltip:SetPetAction(self.id)
end end
elseif self.spellID then
GameTooltip:SetSpellByID(self.spellID)
elseif self.spellslot and self.booktype then elseif self.spellslot and self.booktype then
GameTooltip:SetSpell(self.spellslot, self.booktype) GameTooltip:SetSpell(self.spellslot, self.booktype)
else else
@@ -392,7 +454,7 @@ pfUI:RegisterModule("actionbar", function ()
end end
local function ButtonLeave(self) local function ButtonLeave(self)
self = self or this local self = self or this
-- no longer wait for a drag event -- no longer wait for a drag event
drag_await = nil drag_await = nil
@@ -401,33 +463,11 @@ 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)
if not self then return end if not self then return end
self = self or this local self = self or this
sid = self.id -- 1 to 120 sid = self.id -- 1 to 120
-- reset shared variables -- reset shared variables
@@ -483,9 +523,23 @@ 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))
@@ -532,7 +586,7 @@ pfUI:RegisterModule("actionbar", function ()
local sid, usable, oom, _ local sid, usable, oom, _
local function ButtonUsableUpdate(self) local function ButtonUsableUpdate(self)
self = self or this local self = self or this
sid = self.id -- 1 to 120 sid = self.id -- 1 to 120
if self.bar == 11 then if self.bar == 11 then
@@ -546,17 +600,17 @@ pfUI:RegisterModule("actionbar", function ()
-- update usable [out-of-range = 1, oom = 2, not-usable = 3, default = 0] -- update usable [out-of-range = 1, oom = 2, not-usable = 3, default = 0]
if self.outofrange and C.bars.glowrange == "1" then if self.outofrange and C.bars.glowrange == "1" then
if self.vertexstate ~= 1 then if self.vertexstate ~= 1 then
self.icon:SetVertexColor(self.rangeColor:GetRGBA()) self.icon:SetVertexColor(self.rangeColor[1], self.rangeColor[2], self.rangeColor[3], self.rangeColor[4])
self.vertexstate = 1 self.vertexstate = 1
end end
elseif oom and C.bars.showoom == "1" then elseif oom and C.bars.showoom == "1" then
if self.vertexstate ~= 2 then if self.vertexstate ~= 2 then
self.icon:SetVertexColor(self.oomColor:GetRGBA()) self.icon:SetVertexColor(self.oomColor[1], self.oomColor[2], self.oomColor[3], self.oomColor[4])
self.vertexstate = 2 self.vertexstate = 2
end end
elseif not usable and C.bars.showna == "1" then elseif not usable and C.bars.showna == "1" then
if self.vertexstate ~= 3 then if self.vertexstate ~= 3 then
self.icon:SetVertexColor(self.naColor:GetRGBA()) self.icon:SetVertexColor(self.naColor[1], self.naColor[2], self.naColor[3], self.naColor[4])
self.vertexstate = 3 self.vertexstate = 3
end end
else else
@@ -568,7 +622,7 @@ pfUI:RegisterModule("actionbar", function ()
end end
local function ButtonRangeUpdate(self) local function ButtonRangeUpdate(self)
self = self or this local self = self or this
-- update range display -- update range display
if C.bars.glowrange == "1" and self.bar ~= 11 and self.bar ~= 12 and HasAction(self.id) and ActionHasRange(self.id) and IsActionInRange(self.id) == 0 then if C.bars.glowrange == "1" and self.bar ~= 11 and self.bar ~= 12 and HasAction(self.id) and ActionHasRange(self.id) and IsActionInRange(self.id) == 0 then
@@ -637,6 +691,7 @@ pfUI:RegisterModule("actionbar", function ()
local function ButtonFullUpdate(button) local function ButtonFullUpdate(button)
if not button then return end if not button then return end
ButtonMacroScan(button)
ButtonSlotUpdate(button) ButtonSlotUpdate(button)
ButtonRangeUpdate(button) ButtonRangeUpdate(button)
ButtonUsableUpdate(button) ButtonUsableUpdate(button)
@@ -645,7 +700,7 @@ pfUI:RegisterModule("actionbar", function ()
end end
local function BarsEvent(self) local function BarsEvent(self)
self = self or this local self = self or this
-- refresh only specific slots -- refresh only specific slots
if event == "ACTIONBAR_SLOT_CHANGED" and arg1 and arg1 ~= 0 then if event == "ACTIONBAR_SLOT_CHANGED" and arg1 and arg1 ~= 0 then
@@ -705,29 +760,11 @@ 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
if button:IsVisible() then ButtonUsableUpdate(button) else button.stale = true end ButtonUsableUpdate(button)
end end
end end
@@ -735,7 +772,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
if button:IsVisible() then ButtonCooldownUpdate(button) else button.stale = true end ButtonCooldownUpdate(button)
end end
end end
@@ -743,7 +780,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
if button:IsVisible() then ButtonIsActiveUpdate(button) else button.stale = true end ButtonIsActiveUpdate(button)
end end
end end
@@ -765,40 +802,16 @@ 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.stale and button:IsVisible() then if button:IsShown() then ButtonRangeUpdate(button) end
-- catch up on updates skipped while hidden
button.stale = nil
ButtonFullUpdate(button)
elseif button:IsShown() then
ButtonRangeUpdate(button)
end
end end
end end
-- create the main event and update handler for pfUI actionbars -- create the main event and update handler for pfUI actionbars
local bars = CreateFrame("Frame", "pfActionBar", UIParent) local bars = CreateFrame("Frame", "pfActionBar", UIParent)
for event in pairs(special_events) do bars:RegisterEvent(event) end
-- The only unit events in the tables above; both concern the player alone. for event in pairs(global_events) do bars:RegisterEvent(event) end
-- A registration keeps its kind, so these have to go in unit-filtered from for event in pairs(aura_events) do bars:RegisterEvent(event) end
-- the start -- RegisterUnitEvent over a plain registration stays plain. for event in pairs(pet_events) do bars:RegisterEvent(event) end
local event_units = {
["UNIT_INVENTORY_CHANGED"] = "player",
["UNIT_PET"] = "player",
}
local function RegisterBarEvent(event)
local unit = event_units[event]
if unit then
bars:RegisterUnitEvent(event, unit)
else
bars:RegisterEvent(event)
end
end
for event in pairs(special_events) do RegisterBarEvent(event) end
for event in pairs(global_events) do RegisterBarEvent(event) end
for event in pairs(aura_events) do RegisterBarEvent(event) end
for event in pairs(pet_events) do RegisterBarEvent(event) end
-- refresh actionbar buttons on event -- refresh actionbar buttons on event
bars:SetScript("OnEvent", BarsEvent) bars:SetScript("OnEvent", BarsEvent)
@@ -861,28 +874,17 @@ 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
-- while a page modifier is held, MOD-key must run the main bar binding on -- set temporary pagemaster bindings keybinds
-- that key so the switched page fires. Overrides do this without touching if C.bars.pagemaster == "1" then
-- the saved binding set. local modifier = { "ALT", "SHIFT", "CTRL" }
local pagemods = {} local buttons = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "=", "´" }
if C.bars.pagemasteralt == "1" then table.insert(pagemods, "ALT") end local current = CURRENT_ACTIONBAR_PAGE
if C.bars.pagemasterctrl == "1" then table.insert(pagemods, "CTRL") end bars.pagemaster = bars.pagemaster or CreateFrame("Frame", "pfPageMaster", UIParent)
if C.bars.pagemastershift == "1" then table.insert(pagemods, "SHIFT") end bars.pagemaster:RegisterEvent("PLAYER_ENTERING_WORLD")
bars.pagemaster:SetScript("OnEvent", function()
if table.getn(pagemods) > 0 then for _,mod in pairs(modifier) do
local pagemaster = CreateFrame("Frame", "pfPageMaster", UIParent) for _,but in pairs(buttons) do
pagemaster:RegisterEvent("PLAYER_ENTERING_WORLD") SetBinding(mod.."-"..but)
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)
@@ -897,10 +899,6 @@ 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()
@@ -923,18 +921,6 @@ 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()
@@ -958,6 +944,20 @@ 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
@@ -992,13 +992,13 @@ pfUI:RegisterModule("actionbar", function ()
local font_offset = tonumber(C.bars.font_offset) local font_offset = tonumber(C.bars.font_offset)
local macro_size = tonumber(C.bars.macro_size) local macro_size = tonumber(C.bars.macro_size)
local macro_color = { GetStringColor(C.bars.macro_color) } local macro_color = { strsplit(",", C.bars.macro_color) }
local count_size = tonumber(C.bars.count_size) local count_size = tonumber(C.bars.count_size)
local count_color = { GetStringColor(C.bars.count_color) } local count_color = { strsplit(",", C.bars.count_color) }
local bind_size = tonumber(C.bars.bind_size) local bind_size = tonumber(C.bars.bind_size)
local bind_color = { GetStringColor(C.bars.bind_color) } local bind_color = { strsplit(",", C.bars.bind_color) }
local cd_size = tonumber(C.bars.cd_size) local cd_size = tonumber(C.bars.cd_size)
@@ -1017,7 +1017,7 @@ pfUI:RegisterModule("actionbar", function ()
local id = (bar-1)*12+button local id = (bar-1)*12+button
local exists = _G[button_name] and true or nil local exists = _G[button_name] and true or nil
local f = _G[button_name] or CreateFrame("Button", button_name, parent) local f = _G[button_name] or CreateFrame("Button", button_name, parent, ACTIONBAR_SECURE_TEMPLATE_BUTTON)
-- no button available, create a new one -- no button available, create a new one
if not exists then if not exists then
@@ -1043,7 +1043,7 @@ pfUI:RegisterModule("actionbar", function ()
f.slot = id f.slot = id
-- cooldown -- cooldown
f.cd = CreateFrame("Cooldown", f:GetName() .. "Cooldown", f, "CooldownFrameTemplate") f.cd = CreateFrame(COOLDOWN_FRAME_TYPE, 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
@@ -1149,20 +1149,25 @@ pfUI:RegisterModule("actionbar", function ()
f.count:SetJustifyH("RIGHT") f.count:SetJustifyH("RIGHT")
f.count:SetJustifyV("BOTTOM") f.count:SetJustifyV("BOTTOM")
f.scanmacro, f.spellslot, f.booktype = nil, nil, nil -- macro spell scan (disabled when macro addons are loaded)
if C.bars.macroscan == "0" or pfUI:MacroAddonsLoaded() then
f.scanmacro, f.spellslot, f.booktype = nil, nil, nil
else
f.scanmacro = true
end
-- range glow color -- range glow color
f.rangeColor = GetStringColorObject(C.bars.rangecolor) f.rangeColor = { strsplit(",", C.bars.rangecolor) }
-- out of mana color -- out of mana color
f.oomColor = GetStringColorObject(C.bars.oomcolor) f.oomColor = { strsplit(",", C.bars.oomcolor) }
-- not usable color -- not usable color
f.naColor = GetStringColorObject(C.bars.nacolor) f.naColor = { strsplit(",", C.bars.nacolor) }
-- equipped color -- equipped color
if f.equipped then if f.equipped then
f.equipped:SetTexture(GetStringColor(C.bars.eqcolor)) f.equipped:SetTexture(strsplit(",", C.bars.eqcolor))
end end
-- general appearance -- general appearance
@@ -1229,7 +1234,7 @@ pfUI:RegisterModule("actionbar", function ()
-- create frame -- create frame
local init = not bars[i] local init = not bars[i]
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent) bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent, ACTIONBAR_SECURE_TEMPLATE_BAR)
bars[i]:SetID(i) bars[i]:SetID(i)
-- autohide -- autohide
@@ -1541,7 +1546,7 @@ pfUI:RegisterModule("actionbar", function ()
-- via GetActionInfo + C_Spell.GetSpellReagents. Macro actions and -- via GetActionInfo + C_Spell.GetSpellReagents. Macro actions and
-- bag-item actions are skipped (their reagent resolution would need a -- bag-item actions are skipped (their reagent resolution would need a
-- macro-body parse / item-effect lookup that we don't bother with). -- macro-body parse / item-effect lookup that we don't bother with).
local function UpdateSlot(slot) local UpdateSlot = function(slot)
local newID = nil local newID = nil
if HasAction(slot) then if HasAction(slot) then
local kind, spellID = GetActionInfo(slot) local kind, spellID = GetActionInfo(slot)
@@ -1556,44 +1561,60 @@ pfUI:RegisterModule("actionbar", function ()
if reagent_slots[slot] ~= newID then if reagent_slots[slot] ~= newID then
reagent_slots[slot] = newID reagent_slots[slot] = newID
if newID then if newID then
reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID) reagent_counts[newID] = reagent_counts[newID] or 0
end end
updatecache[slot] = true updatecache[slot] = true
end end
end end
-- Recount every tracked reagent and flag its buttons for a refresh.
local RecountReagents = function()
for itemID in pairs(reagent_counts) do
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
end
for slot in pairs(reagent_slots) do
updatecache[slot] = true
end
end
local reagentcounter = CreateFrame("Frame", "pfReagentCounter", UIParent) local reagentcounter = CreateFrame("Frame", "pfReagentCounter", UIParent)
reagentcounter:RegisterEvent("PLAYER_ENTERING_WORLD") reagentcounter:RegisterEvent("PLAYER_ENTERING_WORLD")
reagentcounter:RegisterEvent("ACTIONBAR_SLOT_CHANGED") reagentcounter:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
reagentcounter:RegisterEvent("BAG_UPDATE_DELAYED") reagentcounter:RegisterEvent("BAG_UPDATE_DELAYED")
reagentcounter:SetScript("OnEvent", function() reagentcounter:SetScript("OnEvent", function()
if event == "ACTIONBAR_SLOT_CHANGED" then if event == "BAG_UPDATE_DELAYED" then
-- arg1 is the changed slot; 0 (or nil) means "all slots" this.event = true
if arg1 and arg1 > 0 then
UpdateSlot(arg1)
else
for slot = 1, 120 do UpdateSlot(slot) end
end
elseif event == "BAG_UPDATE_DELAYED" then
-- inventory changed: refresh the counts we already track
RecountReagents()
else else
-- PLAYER_ENTERING_WORLD: seed the full reagent map (UpdateSlot seeds this.scan = 1
-- each new reagent's count; a BAG_UPDATE_DELAYED follows during login)
for slot = 1, 120 do UpdateSlot(slot) end
end end
end) end)
-- Reagent counter update with throttle for performance optimization
reagentcounter:SetScript("OnUpdate", function()
-- Throttle entire function to 10 FPS for smooth scanning
if (this.tick_update or 0) > GetTime() then return end
this.tick_update = GetTime() + 0.1
-- scan one action slot per update
if this.scan and this.scan <= 120 then
UpdateSlot(this.scan)
this.scan = this.scan + 1
end
-- trigger reagent count updates after action scans
if this.scan and this.scan >= 120 then
this.event = true
this.scan = nil
end
-- queue events to fire only once per second
if not this.event then return end
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
-- scan for all reagent item counts
for itemID in pairs(reagent_counts) do
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
end
-- update all actionbar buttons
for slot in pairs(reagent_slots) do
updatecache[slot] = true
end
-- remove event trigger
this.event = nil
end)
function IsReagentAction(slot) function IsReagentAction(slot)
return reagent_slots[slot] and true or nil return reagent_slots[slot] and true or nil
end end
+13 -17
View File
@@ -397,26 +397,22 @@ pfUI:RegisterModule("addonbuttons", function ()
pfUI.addonbuttons:ProcessButtons() pfUI.addonbuttons:ProcessButtons()
end) end)
-- Initial setup on the next frame, once other addons' minimap buttons exist pfUI.addonbuttons:SetScript("OnUpdate", function()
RunNextFrame(function() -- check if the panel should be shown by default
-- rescan before deciding whether the panel should be shown by default if not this.initialized then
pfUI.addonbuttons:ProcessButtons() if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then
pfUI.addonbuttons:Show()
else
pfUI.addonbuttons:Hide()
end
if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then -- update all buttons
pfUI.addonbuttons:Show() pfUI.addonbuttons:ProcessButtons()
else this.initialized = true
pfUI.addonbuttons:Hide()
end end
-- apply workarounds -- throttle updates to once per 5 seconds
for k, v in pairs(pfUI.addonbuttons.overrides) do if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 5 end
_G[k] = v
end
end)
-- Rescan minimap buttons every 5 seconds while the panel is shown
C_Timer.NewTicker(5, function()
if not pfUI.addonbuttons:IsShown() then return end
-- reload/rescan minimap buttons -- reload/rescan minimap buttons
pfUI.addonbuttons:ProcessButtons() pfUI.addonbuttons:ProcessButtons()
+1
View File
@@ -9,6 +9,7 @@ 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)
-5
View File
@@ -2,11 +2,6 @@ pfUI:RegisterModule("autoshift", function ()
pfUI.autoshift = CreateFrame("Frame") pfUI.autoshift = CreateFrame("Frame")
pfUI.autoshift:RegisterEvent("UI_ERROR_MESSAGE") pfUI.autoshift:RegisterEvent("UI_ERROR_MESSAGE")
-- Empty stub kept only so Turtle WoW's pfUI-turtle addon doesn't error when
-- its obsolete moonkin autoshift module iterates pfUI.autoshift.shapeshifts.
-- This fork drives autoshift off GetShapeshiftFormID() and no longer uses it.
pfUI.autoshift.shapeshifts = {}
pfUI.autoshift.scanString = string.gsub(SPELL_FAILED_ONLY_SHAPESHIFT, "%%s", "(.+)") pfUI.autoshift.scanString = string.gsub(SPELL_FAILED_ONLY_SHAPESHIFT, "%%s", "(.+)")
pfUI.autoshift.errors = { SPELL_FAILED_NOT_MOUNTED, ERR_ATTACK_MOUNTED, ERR_TAXIPLAYERALREADYMOUNTED, pfUI.autoshift.errors = { SPELL_FAILED_NOT_MOUNTED, ERR_ATTACK_MOUNTED, ERR_TAXIPLAYERALREADYMOUNTED,
+21 -5
View File
@@ -396,13 +396,14 @@ 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("Cooldown", name, bankslot, "CooldownFrameTemplate") bankslot.cd = CreateFrame(COOLDOWN_FRAME_TYPE, name, bankslot, "CooldownFrameTemplate")
bankslot.cd:SetAllPoints(bankslot)
bankslot.cd.pfCooldownStyleAnimation = 1 bankslot.cd.pfCooldownStyleAnimation = 1
bankslot.cd.pfCooldownType = "NOGCD" bankslot.cd.pfCooldownType = "ALL"
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 = "NOGCD" bagslot.cd.pfCooldownType = "ALL"
end end
if not pfUI.bags[bag].slots[slot].frame.backdrop then if not pfUI.bags[bag].slots[slot].frame.backdrop then
@@ -971,7 +972,7 @@ pfUI:RegisterModule("bags", function ()
frame.sort.backdrop:SetBackdropBorderColor(1,1,.25,1) frame.sort.backdrop:SetBackdropBorderColor(1,1,.25,1)
frame.sort.texture:SetVertexColor(1,1,.25,1) frame.sort.texture:SetVertexColor(1,1,.25,1)
GameTooltip:SetOwner(this, "ANCHOR_RIGHT") GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(T["Sort Bags"]) GameTooltip:SetText("Sort Bags")
GameTooltip:Show() GameTooltip:Show()
end) end)
@@ -1084,6 +1085,21 @@ pfUI:RegisterModule("bags", function ()
frame.search.edit:ClearFocus() frame.search.edit:ClearFocus()
end) end)
frame.search:SetScript("OnHide", function()
frame.search.edit:SetText(T["Search"])
for bag = -2, 11 do
if pfUI.bags[bag] then
local bagsize = GetContainerNumSlots(bag)
if bag == -2 and pfUI.bag.showKeyring == true then bagsize = GetKeyRingSize() end
for slot = 1, bagsize do
if pfUI.bags[bag] and pfUI.bags[bag].slots[slot] then
pfUI.bags[bag].slots[slot].frame:SetAlpha(1)
end
end
end
end
end)
frame.search.edit:SetScript("OnMouseUp", function() frame.search.edit:SetScript("OnMouseUp", function()
if arg1 == "RightButton" then if arg1 == "RightButton" then
this:ClearFocus() this:ClearFocus()
@@ -1191,7 +1207,7 @@ pfUI:RegisterModule("bags", function ()
frame.sort.backdrop:SetBackdropBorderColor(1,1,.25,1) frame.sort.backdrop:SetBackdropBorderColor(1,1,.25,1)
frame.sort.texture:SetVertexColor(1,1,.25,1) frame.sort.texture:SetVertexColor(1,1,.25,1)
GameTooltip:SetOwner(this, "ANCHOR_RIGHT") GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(T["Sort Bank"]) GameTooltip:SetText("Sort Bank")
GameTooltip:Show() GameTooltip:Show()
end) end)
+1 -1
View File
@@ -32,7 +32,7 @@ pfUI:RegisterModule("bgscore", function ()
-- Title label -- Title label
local title = mover:CreateFontString(nil, "OVERLAY") local title = mover:CreateFontString(nil, "OVERLAY")
title:SetFont("Fonts\\FRIZQT__.TTF", 14, "OUTLINE") title:SetFont("Fonts\\FRIZQT__.TTF", 14, "OUTLINE")
title:SetText(T["Battleground Frames"]) title:SetText("Battleground Frames")
title:SetPoint("TOP", mover, "TOP", 0, -2) title:SetPoint("TOP", mover, "TOP", 0, -2)
-- BG score preview text -- BG score preview text
+17 -26
View File
@@ -7,19 +7,10 @@ pfUI:RegisterModule("buff", function ()
local br, bg, bb, ba = GetStringColor(pfUI_config.appearance.border.color) local br, bg, bb, ba = GetStringColor(pfUI_config.appearance.border.color)
-- Player aura slot ids, enumerated once per refresh pass (ScanPlayerAuraSlots)
-- and read per button by its aura index: one GetAuraSlots walk per range
-- instead of a by-index walk per button.
local helpfulSlots, harmfulSlots = {}, {}
local function ScanPlayerAuraSlots()
ScanAuraSlots("player", "HELPFUL", helpfulSlots, 32)
ScanAuraSlots("player", "HARMFUL", harmfulSlots, 16)
end
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 ~= nil and buff.gid or 0) buff.id = buff.gid - (buff.weapon ~= nill 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
@@ -32,8 +23,7 @@ pfUI:RegisterModule("buff", function ()
CreateBackdropShadow(buff) CreateBackdropShadow(buff)
end end
local slots = buff.btype == "HELPFUL" and helpfulSlots or harmfulSlots local aura = C_UnitAuras.GetAuraDataByIndex("player", buff.id, buff.btype)
local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id])
--detect weapon buffs --detect weapon buffs
if buff.btype == "HELPFUL" and ((C.buffs.separateweapons == "0" and buff.gid <= pfUI.buff.wepbuffs.count) or (pfUI.buff.wepbuffs.count > 0 and buff.weapon ~= nil)) then if buff.btype == "HELPFUL" and ((C.buffs.separateweapons == "0" and buff.gid <= pfUI.buff.wepbuffs.count) or (pfUI.buff.wepbuffs.count > 0 and buff.weapon ~= nil)) then
@@ -65,16 +55,16 @@ pfUI:RegisterModule("buff", function ()
buff.texture:SetTexture(GetInventoryItemTexture("player", 17)) buff.texture:SetTexture(GetInventoryItemTexture("player", 17))
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1)) buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1))
end end
elseif name and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then elseif aura and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then
-- Set Buff Texture and Border -- Set Buff Texture and Border
buff.mode = buff.btype buff.mode = buff.btype
buff.expirationTime = expirationTime buff.expirationTime = aura.expirationTime
buff.stackCount = count buff.stackCount = aura.applications
buff.spellId = spellId buff.spellId = aura.spellId
buff.texture:SetTexture(icon) buff.texture:SetTexture(aura.icon)
if buff.btype == "HARMFUL" then if buff.btype == "HARMFUL" then
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(dispelType) local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(aura.dispelName)
buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA()) buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA())
else else
buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba) buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba)
@@ -148,10 +138,16 @@ pfUI:RegisterModule("buff", function ()
end end
end) end)
buff:SetScript("OnLeave", GameTooltip_Hide) buff:SetScript("OnLeave", function()
GameTooltip:Hide()
end)
buff:SetScript("OnClick", function() buff:SetScript("OnClick", function()
if this.spellId then if CancelItemTempEnchantment and this.mode and this.mode == "MAINHAND" then
CancelItemTempEnchantment(1)
elseif CancelItemTempEnchantment and this.mode and this.mode == "OFFHAND" then
CancelItemTempEnchantment(2)
elseif this.spellId then
C_Spell.CancelSpellByID(this.spellId) C_Spell.CancelSpellByID(this.spellId)
end end
end) end)
@@ -164,7 +160,7 @@ pfUI:RegisterModule("buff", function ()
pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent) pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent)
pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED") pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED")
pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
pfUI.buff:RegisterUnitEvent("UNIT_MODEL_CHANGED", "player") pfUI.buff:RegisterEvent("UNIT_MODEL_CHANGED")
pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF") pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF")
pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF") pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF")
pfUI.buff:SetScript("OnEvent", function() pfUI.buff:SetScript("OnEvent", function()
@@ -176,8 +172,6 @@ pfUI:RegisterModule("buff", function ()
pfUI.buff.wepbuffs.count = 0 pfUI.buff.wepbuffs.count = 0
end end
ScanPlayerAuraSlots()
for i=1,32 do for i=1,32 do
RefreshBuffButton(pfUI.buff.buffs.buttons[i]) RefreshBuffButton(pfUI.buff.buffs.buttons[i])
end end
@@ -258,9 +252,6 @@ pfUI:RegisterModule("buff", function ()
end end
end) end)
-- CreateBuffButton refreshes each new button from the slot buffers
ScanPlayerAuraSlots()
-- Weapon Buffs -- Weapon Buffs
pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent) pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent)
pfUI.buff.wepbuffs.count = 0 pfUI.buff.wepbuffs.count = 0
+38 -26
View File
@@ -73,15 +73,12 @@ pfUI:RegisterModule("buffwatch", function ()
return anchor return anchor
end end
-- reusable GetAuraSlots buffer, filled once per RefreshBuffBarFrame local function GetBuffData(unit, id, type, selfdebuff)
local auraSlots = {} local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type
local aura = C_UnitAuras.GetAuraDataByIndex(unit, id, filter)
-- Reads one aura by the slot id GetAuraSlots returned (nil slot -> nil). if not aura then return end
local function GetBuffData(unit, slot) local remaining = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0
local name, icon, count, dispelType, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unit, slot) return remaining, aura.icon, aura.name, aura.applications
if not name then return end
local remaining = expirationTime > 0 and (expirationTime - GetTime()) or 0
return remaining, icon, name, count, dispelType
end end
local function StatusBarOnClick() local function StatusBarOnClick()
@@ -93,14 +90,12 @@ pfUI:RegisterModule("buffwatch", function ()
if val == skill then return end if val == skill then return end
end end
config.whitelist = config.whitelist .. "#" .. skill config.whitelist = config.whitelist .. "#" .. skill
fcache[tostring(config)] = nil -- invalidate so the new entry takes effect immediately
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now whitelisted."]) DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now whitelisted."])
elseif IsShiftKeyDown() then elseif IsShiftKeyDown() then
for _, val in pairs({strsplit("#", config.blacklist)}) do for _, val in pairs({strsplit("#", config.blacklist)}) do
if val == skill then return end if val == skill then return end
end end
config.blacklist = config.blacklist .. "#" .. skill config.blacklist = config.blacklist .. "#" .. skill
fcache[tostring(config)] = nil -- invalidate so the new entry takes effect immediately
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now blacklisted."]) DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now blacklisted."])
end end
elseif this.parent.unit == "player" then elseif this.parent.unit == "player" then
@@ -112,9 +107,31 @@ pfUI:RegisterModule("buffwatch", function ()
local function StatusBarOnEnter() local function StatusBarOnEnter()
GameTooltip:SetOwner(this, "NONE") GameTooltip:SetOwner(this, "NONE")
-- C_UnitAuras lists the player's own auras first, so the selfdebuff if this.unit == "player" then
-- (HARMFUL|PLAYER) list is a prefix of the HARMFUL list: same index. GameTooltip:SetUnitAura("player", this.id, this.type)
GameTooltip:SetUnitAura(this.unit, this.id, this.type) elseif this.type == "HARMFUL" then
-- selfdebuff filters the displayed list to player-cast harmful auras, but
-- SetUnitAura's index has to be into the engine's full HARMFUL list. Look
-- up the displayed aura via the PLAYER filter, then scan engine slots for
-- one whose name + sourceGUID match.
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
for gameSlot = 1, 16 do
local check = C_UnitAuras.GetDebuffDataByIndex(this.unit, gameSlot)
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
GameTooltip:SetUnitAura(this.unit, gameSlot, "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(" ")
@@ -215,15 +232,9 @@ pfUI:RegisterModule("buffwatch", function ()
local function RefreshBuffBarFrame(frame) local function RefreshBuffBarFrame(frame)
-- reinitialize all active buffs -- reinitialize all active buffs
local selfdebuff = frame.config.selfdebuff == "1" local selfdebuff = frame.config.selfdebuff == "1"
local filter = (selfdebuff and frame.type == "HARMFUL") and "HARMFUL|PLAYER" or frame.type
-- one GetAuraSlots enumeration per refresh instead of a by-index walk per
-- bar; the i-th slot is the i-th aura of the filtered list, so `i` stays
-- the index the tooltip / cancel handlers pass to the by-index API
ScanAuraSlots(frame.unit, filter, auraSlots, 32)
for i=1,32 do for i=1,32 do
local timeleft, texture, name, stacks, dtype = GetBuffData(frame.unit, auraSlots[i]) local timeleft, texture, name, stacks = GetBuffData(frame.unit, i, frame.type, selfdebuff)
timeleft = timeleft or 0 timeleft = timeleft or 0
if texture and name and name ~= "" and BuffIsVisible(frame.config, name) then if texture and name and name ~= "" and BuffIsVisible(frame.config, name) then
@@ -232,14 +243,12 @@ pfUI:RegisterModule("buffwatch", function ()
frame.buffs[i][3] = name frame.buffs[i][3] = name
frame.buffs[i][4] = texture frame.buffs[i][4] = texture
frame.buffs[i][5] = stacks frame.buffs[i][5] = stacks
frame.buffs[i][6] = dtype
else else
frame.buffs[i][1] = 0 frame.buffs[i][1] = 0
frame.buffs[i][2] = nil frame.buffs[i][2] = nil
frame.buffs[i][3] = nil frame.buffs[i][3] = nil
frame.buffs[i][4] = nil frame.buffs[i][4] = nil
frame.buffs[i][5] = 0 frame.buffs[i][5] = 0
frame.buffs[i][6] = nil
end end
end end
@@ -283,9 +292,12 @@ pfUI:RegisterModule("buffwatch", function ()
-- calculate dynamic auto color -- calculate dynamic auto color
local r, g, b local r, g, b
if frame.type == "HARMFUL" then if frame.type == "HARMFUL" then
-- official Blizzard dispel-type colors (matches unitframes/buff); r, g, b = 1, .2, .2
-- nil/unknown type falls back to the DEBUFF_TYPE_NONE colour local a = C_UnitAuras.GetDebuffDataByIndex(frame.unit, data[2])
r, g, b = C_UnitAuras.GetAuraDispelTypeColor(data[6] or ""):GetRGBA() local dtype = a and a.dispelName
if dtype and DebuffTypeColor[dtype] then
r,g,b = DebuffTypeColor[dtype].r,DebuffTypeColor[dtype].g,DebuffTypeColor[dtype].b
end
else else
r,g,b = str2rgb(data[3]) r,g,b = str2rgb(data[3])
end end
+24 -46
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
if cb:GetAlpha() > 0 and not cb.fade:IsPlaying() then cb.fade:Play() end cb.fadeout = 1
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
@@ -45,7 +45,7 @@ pfUI:RegisterModule("castbar", function ()
if not cb.tradeskillTotal or not cb.activeName or not cb.showname then return end if not cb.tradeskillTotal or not cb.activeName or not cb.showname then return end
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0) local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
if remaining > 1 then if remaining > 1 then
cb.bar.left:SetFormattedText("%s (%d)", cb.activeName, remaining) cb.bar.left:SetText(string.format("%s (%d)", cb.activeName, remaining))
else else
cb.bar.left:SetText(cb.activeName) cb.bar.left:SetText(cb.activeName)
end end
@@ -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, noInterrupt) local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank)
cb.startTime = startMs cb.startTime = startMs
cb.endTime = endMs cb.endTime = endMs
cb.isChannel = isChannel cb.isChannel = isChannel
@@ -94,13 +94,10 @@ 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
-- Shield an uninterruptible cast. Not on the player's own bar: the flag is cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
-- 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
@@ -155,10 +152,10 @@ pfUI:RegisterModule("castbar", function ()
ClearBar(cb) ClearBar(cb)
return return
end end
local name, _, tex, startMs, endMs, isTradeskill, _, noInterrupt, spellID, _, delayMs = C_Spell.UnitCastingInfo(query) local name, _, tex, startMs, endMs, isTradeskill, _, _, spellID, _, delayMs = C_Spell.UnitCastingInfo(query)
local isChan local isChan
if not name then if not name then
name, _, tex, startMs, endMs, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(query) name, _, tex, startMs, endMs, _, _, 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
@@ -172,7 +169,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, noInterrupt) StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill, rank)
else else
ClearBar(cb) ClearBar(cb)
end end
@@ -248,14 +245,8 @@ 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()
-- Fade-out after a cast ends. The bar stays at alpha 0 while idle. -- OnUpdate animates the bar fill and fades it out on completion. All
cb.fade = cb:CreateAnimationGroup() -- state transitions (cast start/stop/interrupt, channel start/stop,
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()
@@ -263,12 +254,15 @@ 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.fade:IsPlaying() then return end if this.fadeout and this:GetAlpha() > 0 then
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
@@ -328,32 +322,20 @@ pfUI:RegisterModule("castbar", function ()
-- casts only ever fire arg1=="player" -- when the bar's unit resolves to the -- casts only ever fire arg1=="player" -- when the bar's unit resolves to the
-- player (target=self). PLAYER_TARGET/FOCUS_CHANGED re-polls so a unit -- player (target=self). PLAYER_TARGET/FOCUS_CHANGED re-polls so a unit
-- already mid-cast when it becomes the target/focus still shows. -- already mid-cast when it becomes the target/focus still shows.
-- Filter to this bar's unit, plus "player" for the target/focus bars: the cb:RegisterEvent("UNIT_SPELLCAST_START")
-- player's own casts only ever fire arg1=="player", so a self-targeted cast cb:RegisterEvent("UNIT_SPELLCAST_STOP")
-- has to reach them too (nil for the player bar itself, which the filter cb:RegisterEvent("UNIT_SPELLCAST_FAILED")
-- skips). This only narrows what arrives -- the arg1/UnitIsUnit test below cb:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
-- still decides whether the bar acts on it. cb:RegisterEvent("UNIT_SPELLCAST_DELAYED")
local selfunit = unitstr ~= "player" and "player" or nil cb:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
cb:RegisterUnitEvent("UNIT_SPELLCAST_START", unitstr, selfunit) cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
cb:RegisterUnitEvent("UNIT_SPELLCAST_STOP", unitstr, selfunit) cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
cb:RegisterUnitEvent("UNIT_SPELLCAST_FAILED", unitstr, selfunit) cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
cb:RegisterUnitEvent("UNIT_SPELLCAST_INTERRUPTED", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_DELAYED", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_START", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", unitstr, selfunit)
cb:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_UPDATE", unitstr, selfunit)
if unitstr == "target" then if unitstr == "target" then
cb:RegisterEvent("PLAYER_TARGET_CHANGED") cb:RegisterEvent("PLAYER_TARGET_CHANGED")
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
@@ -409,10 +391,6 @@ 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
+86 -153
View File
@@ -63,23 +63,6 @@ pfUI:RegisterModule("chat", function ()
return pfUI_cache["chathistory"][realm][player][id] return pfUI_cache["chathistory"][realm][player][id]
end end
-- [ Chat Panel Colors ]
-- The panels normally inherit pfUI's global appearance theme; custom colors let chat
-- deviate from it. CreateBackdrop is re-run first to restore the theme, so toggling
-- custom colors back off doesn't leave the previous override behind.
local function ApplyPanelColors(panel)
if not panel then return end
CreateBackdrop(panel, default_border, nil, .8)
if C.chat.global.custombg ~= "1" then return end
local r, g, b, a = GetStringColor(C.chat.global.background)
panel.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border)
panel.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
pfUI.chat = CreateFrame("Frame",nil,UIParent) pfUI.chat = CreateFrame("Frame",nil,UIParent)
pfUI.chat.left = CreateFrame("Frame", "pfChatLeft", UIParent) pfUI.chat.left = CreateFrame("Frame", "pfChatLeft", UIParent)
@@ -93,11 +76,19 @@ pfUI:RegisterModule("chat", function ()
pfUI.chat.left:SetPoint("BOTTOMLEFT", 2*default_border,2*default_border) pfUI.chat.left:SetPoint("BOTTOMLEFT", 2*default_border,2*default_border)
pfUI.chat.left:SetScript("OnShow", function() pfUI.chat:RefreshChat() end) pfUI.chat.left:SetScript("OnShow", function() pfUI.chat:RefreshChat() end)
UpdateMovable(pfUI.chat.left) UpdateMovable(pfUI.chat.left)
ApplyPanelColors(pfUI.chat.left) CreateBackdrop(pfUI.chat.left, default_border, nil, .8)
if C.chat.global.frameshadow == "1" then if C.chat.global.frameshadow == "1" then
CreateBackdropShadow(pfUI.chat.left) CreateBackdropShadow(pfUI.chat.left)
end end
if C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
pfUI.chat.left.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
pfUI.chat.left.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
pfUI.chat.left.panelTop = CreateFrame("Frame", "leftChatPanelTop", pfUI.chat.left) pfUI.chat.left.panelTop = CreateFrame("Frame", "leftChatPanelTop", pfUI.chat.left)
pfUI.chat.left.panelTop:ClearAllPoints() pfUI.chat.left.panelTop:ClearAllPoints()
pfUI.chat.left.panelTop:SetHeight(C.global.font_size+default_border*2) pfUI.chat.left.panelTop:SetHeight(C.global.font_size+default_border*2)
@@ -260,11 +251,19 @@ pfUI:RegisterModule("chat", function ()
pfUI.chat.right:SetPoint("BOTTOMRIGHT", -2*default_border,2*default_border) pfUI.chat.right:SetPoint("BOTTOMRIGHT", -2*default_border,2*default_border)
pfUI.chat.right:SetScript("OnShow", function() pfUI.chat:RefreshChat() end) pfUI.chat.right:SetScript("OnShow", function() pfUI.chat:RefreshChat() end)
UpdateMovable(pfUI.chat.right) UpdateMovable(pfUI.chat.right)
ApplyPanelColors(pfUI.chat.right) CreateBackdrop(pfUI.chat.right, default_border, nil, .8)
if C.chat.global.frameshadow == "1" then if C.chat.global.frameshadow == "1" then
CreateBackdropShadow(pfUI.chat.right) CreateBackdropShadow(pfUI.chat.right)
end end
if C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
pfUI.chat.right.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
pfUI.chat.right.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
pfUI.chat.right.panelTop = CreateFrame("Frame", "rightChatPanelTop", pfUI.chat.right) pfUI.chat.right.panelTop = CreateFrame("Frame", "rightChatPanelTop", pfUI.chat.right)
pfUI.chat.right.panelTop:ClearAllPoints() pfUI.chat.right.panelTop:ClearAllPoints()
pfUI.chat.right.panelTop:SetHeight(C.global.font_size+default_border*2) pfUI.chat.right.panelTop:SetHeight(C.global.font_size+default_border*2)
@@ -297,68 +296,6 @@ pfUI:RegisterModule("chat", function ()
end end
end end
-- [ Chat Background Alpha ]
-- Blizzard's per-window transparency slider drives FCF_SetWindowAlpha, which only
-- touches the ChatFrame*Background textures that pfUI hides on docked frames. Mirror
-- the window's stored alpha onto the visible pfUI backdrop so the native slider
-- controls it directly.
local function ApplyChatBackgroundAlpha(panel, frame)
if not (panel and panel.backdrop and frame) then return end
local _, _, _, _, _, alpha = GetChatWindowInfo(frame:GetID())
alpha = tonumber(alpha)
if not alpha then return end
local r, g, b = panel.backdrop:GetBackdropColor()
panel.backdrop:SetBackdropColor(r, g, b, alpha)
end
function pfUI.chat:RefreshBackgroundAlpha()
-- Custom colors own the alpha. C.chat.global.background carries its own alpha, is
-- set by profiles and is shared with the meter skins, so the native slider must not
-- overwrite it -- doing so reverted the configured opacity on every refresh. The
-- slider only drives the panel when the user hasn't opted into pfUI's chat colors.
if C.chat.global.custombg == "1" then return end
-- left panel follows the currently selected docked tab
local selected = SELECTED_CHAT_FRAME
if not (selected and selected:GetParent() == pfUI.chat.left) then
selected = ChatFrame1
end
ApplyChatBackgroundAlpha(pfUI.chat.left, selected)
if C.chat.right.enable == "1" then
ApplyChatBackgroundAlpha(pfUI.chat.right, ChatFrame3)
end
end
-- live config apply, resolved by the gui as U["chat"]. Lets the color pickers
-- take effect on the spot instead of waiting for a /reload.
function pfUI.chat:UpdateConfig()
ApplyPanelColors(pfUI.chat.left)
ApplyPanelColors(pfUI.chat.right)
pfUI.chat:RefreshBackgroundAlpha()
end
function pfUI.chat:MigrateBackgroundAlpha()
-- Runs once per character. The previous SetupPositions default stored a hard 0 window
-- alpha, which now renders the pfUI backdrop fully transparent. Restore the historical
-- 0.8 look for any pfUI-managed window still sitting at 0. Only existing installs that
-- already ran chat setup carry that legacy 0; fresh installs get 0.8 from SetupPositions.
if pfUI_init.chatbgalpha then return end
if not pfUI_init["chat_position"] then return end
pfUI_init.chatbgalpha = true
local frames = { ChatFrame1, ChatFrame2 }
if C.chat.right.enable == "1" then table.insert(frames, ChatFrame3) end
for _, frame in ipairs(frames) do
local _, _, _, _, _, alpha = GetChatWindowInfo(frame:GetID())
alpha = tonumber(alpha)
if alpha and alpha <= 0 then
FCF_SetWindowAlpha(frame, 0.8)
end
end
end
function pfUI.chat:RefreshChat() function pfUI.chat:RefreshChat()
local panelheight = C.global.font_size*1.5 + default_border*2 + 2 local panelheight = C.global.font_size*1.5 + default_border*2 + 2
@@ -510,13 +447,9 @@ pfUI:RegisterModule("chat", function ()
for index, value in pairs(DOCKED_CHAT_FRAMES) do for index, value in pairs(DOCKED_CHAT_FRAMES) do
FCF_UpdateButtonSide(value) FCF_UpdateButtonSide(value)
end end
pfUI.chat:RefreshBackgroundAlpha()
end end
hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat) hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat)
hooksecurefunc("FCF_SetWindowAlpha", function() pfUI.chat:RefreshBackgroundAlpha() end)
hooksecurefunc("FCF_SelectDockFrame", function() pfUI.chat:RefreshBackgroundAlpha() end)
if C.chat.global.tabmouse == "1" then if C.chat.global.tabmouse == "1" then
pfUI.chat.mouseovertab = CreateFrame("Frame") pfUI.chat.mouseovertab = CreateFrame("Frame")
@@ -526,10 +459,10 @@ pfUI:RegisterModule("chat", function ()
if pfUI.chat.hideLock then return end if pfUI.chat.hideLock then return end
if pfUI.chat.left:IsMouseOver(10, -10, -10, 10) then if MouseIsOver(pfUI.chat.left, 10, -10, -10, 10) then
pfUI.chat.left.panelTop:Show() pfUI.chat.left.panelTop:Show()
FCF_DockUpdate() FCF_DockUpdate()
elseif pfUI.chat.right:IsMouseOver(10, -10, -10, 10) then elseif MouseIsOver(pfUI.chat.right, 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
@@ -567,7 +500,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame1, 1) FCF_SetLocked(ChatFrame1, 1)
FCF_SetWindowName(ChatFrame1, GENERAL) FCF_SetWindowName(ChatFrame1, GENERAL)
FCF_SetWindowColor(ChatFrame1, 0, 0, 0) FCF_SetWindowColor(ChatFrame1, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame1, 0.8) FCF_SetWindowAlpha(ChatFrame1, 0)
FCF_SetChatWindowFontSize(ChatFrame1, 12) FCF_SetChatWindowFontSize(ChatFrame1, 12)
ChatFrame1:SetUserPlaced(1) ChatFrame1:SetUserPlaced(1)
@@ -575,7 +508,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame2, 1) FCF_SetLocked(ChatFrame2, 1)
FCF_SetWindowName(ChatFrame2, COMBAT_LOG) FCF_SetWindowName(ChatFrame2, COMBAT_LOG)
FCF_SetWindowColor(ChatFrame2, 0, 0, 0) FCF_SetWindowColor(ChatFrame2, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame2, 0.8) FCF_SetWindowAlpha(ChatFrame2, 0)
FCF_SetChatWindowFontSize(ChatFrame2, 12) FCF_SetChatWindowFontSize(ChatFrame2, 12)
ChatFrame2:SetUserPlaced(1) ChatFrame2:SetUserPlaced(1)
@@ -585,7 +518,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame3, 1) FCF_SetLocked(ChatFrame3, 1)
FCF_SetWindowName(ChatFrame3, T["Loot & Spam"]) FCF_SetWindowName(ChatFrame3, T["Loot & Spam"])
FCF_SetWindowColor(ChatFrame3, 0, 0, 0) FCF_SetWindowColor(ChatFrame3, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame3, 0.8) FCF_SetWindowAlpha(ChatFrame3, 0)
FCF_SetChatWindowFontSize(ChatFrame3, 12) FCF_SetChatWindowFontSize(ChatFrame3, 12)
FCF_UnDockFrame(ChatFrame3) FCF_UnDockFrame(ChatFrame3)
FCF_SetTabPosition(ChatFrame3, 0) FCF_SetTabPosition(ChatFrame3, 0)
@@ -633,9 +566,6 @@ pfUI:RegisterModule("chat", function ()
end end
pfUI.chat:SetScript("OnEvent", function() pfUI.chat:SetScript("OnEvent", function()
-- restore legacy chat windows stuck at 0 alpha before anything reads it
pfUI.chat:MigrateBackgroundAlpha()
-- set the default chat -- set the default chat
FCF_SelectDockFrame(SELECTED_CHAT_FRAME) FCF_SelectDockFrame(SELECTED_CHAT_FRAME)
@@ -745,7 +675,7 @@ pfUI:RegisterModule("chat", function ()
end end
-- read and parse whisper color settings -- read and parse whisper color settings
local cr, cg, cb, ca = GetStringColor(C.chat.global.whisper) local cr, cg, cb, ca = strsplit(",", C.chat.global.whisper)
cr, cg, cb = tonumber(cr), tonumber(cg), tonumber(cb) cr, cg, cb = tonumber(cr), tonumber(cg), tonumber(cb)
local wcol = rgbhex(cr, cg, cb) local wcol = rgbhex(cr, cg, cb)
@@ -776,12 +706,22 @@ pfUI:RegisterModule("chat", function ()
_G.CHAT_WHISPER_INFORM_GET = '[W]' .. default _G.CHAT_WHISPER_INFORM_GET = '[W]' .. default
end end
local r,g,b,a = GetStringColor(C.chat.text.timecolor) local r,g,b,a = strsplit(",", C.chat.text.timecolor)
local timecolorhex = rgbhex(r,g,b,a) local timecolorhex = rgbhex(r,g,b,a)
local r,g,b = GetStringColor(C.chat.text.unknowncolor) local r,g,b = strsplit(",", 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
@@ -796,10 +736,6 @@ 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
@@ -815,85 +751,82 @@ pfUI:RegisterModule("chat", function ()
text = pfUI.chat:HandleLink(text) text = pfUI.chat:HandleLink(text)
end end
-- display class colors and player levels if already indexed -- display class colors if already indexed
local classcolor = C.chat.text.classcolor == "1" if C.chat.text.classcolor == "1" then
local playerlevel = C.chat.text.playerlevel == "1"
if classcolor or playerlevel then
-- the engine name-queries a chat sender before the event fires,
-- so the sender's GUID always resolves; other names in the text
-- fall back to the name-keyed lookups
local _, senderClass, senderRace, senderSex
if classcolor and guid and guid ~= "" then
_, senderClass, _, senderRace, senderSex = GetPlayerInfoByGUID(guid)
end
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 color, icon = "", "" local color = unknowncolorhex
local match = false
if classcolor then local _, class = C_PlayerCache.GetPlayerInfoByName(real)
local class, raceKey, sex -- local guid = GetCurrentChatGUID()
if senderClass and real == sender then -- if guid then
class, raceKey, sex = senderClass, senderRace, senderSex -- _, class = GetPlayerInfoByGUID(guid)
else -- end
_, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real) if not class then
end class = GetUnitInfo(real)
if not class then end
class = GetUnitInfo(real) if class then
end if class ~= UNKNOWN then
if class and class ~= UNKNOWN then
color = "|c" .. PFUI_CLASS_COLORS[class].colorStr color = "|c" .. PFUI_CLASS_COLORS[class].colorStr
if C.chat.text.factionicons == "1" then match = true
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
elseif C.chat.text.whosearchunknown == "1" then
C_FriendList.SendWhoQueryByName(name) -- cooldown + state managed by DLL
end end
local suffix = "" if C.chat.text.tintunknown == "1" or match then
local level = playerlevel and GetPlayerLevel(real)
if level and level > 0 then
suffix = " " .. rgbhex(GetDifficultyColor(level)) .. level .. "|r"
elseif level then
-- Show ?? for unknown levels (e.g. -1 from UnitLevel)
suffix = " |cffff0000??|r"
end
if color ~= "" or suffix ~= "" then
text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)", text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)",
left..icon..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..suffix..right.."%1") left..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1")
end
end
end
-- display player levels if available
if C.chat.text.playerlevel == "1" then
for name in gfind(text, "|Hplayer:(.-)|h") do
local real, _ = strsplit(":", name)
local level = GetPlayerLevel(real)
if level and level > 0 then
local levelcolor = rgbhex(GetDifficultyColor(level))
-- Add level after the player name, before the closing bracket
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)
text = string.gsub(text, "(|Hplayer:" .. name .. "|h.-|h|r)" .. right,
"%1 |cffff0000??|r" .. right)
end end
end end
end end
-- reduce channel name to number -- reduce channel name to number
if C.chat.text.channelnumonly == "1" and event == "CHAT_MSG_CHANNEL" then if C.chat.text.channelnumonly == "1" then
text = string.gsub(text, "%[(%d+)%..-%] ", left .. "%1" .. right .. " ", 1) local channel = string.gsub(text, ".*%[(.-)%]%s+(.*|Hplayer).+", "%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
-- pushes the whisper colour code off position 1 and the find below fails
local isWhisper = C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1
-- show timestamp in chat -- show timestamp in chat
if C.chat.text.time == "1" then if C.chat.text.time == "1" then
text = timecolorhex .. tleft .. date(C.chat.text.timeformat) .. tright .. "|r " .. text text = timecolorhex .. tleft .. date(C.chat.text.timeformat) .. tright .. "|r " .. text
end end
-- save chat history -- save chat history
if isWhisper then if C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1 then
SaveChatHistory(frame:GetID(), string.gsub(text, wcol, ""), cr, cg, cb) SaveChatHistory(frame:GetID(), string.gsub(text, wcol, ""), cr, cg, cb)
else else
SaveChatHistory(frame:GetID(), text, a1, a2, a3) SaveChatHistory(frame:GetID(), text, a1, a2, a3)
end end
if isWhisper then if C.chat.global.whispermod == "1" then
-- patch incoming whisper string to match the colors -- patch incoming whisper string to match the colors
text = string.gsub(text, "|r", "|r" .. wcol) if string.find(text, wcol, 1) == 1 then
text = string.gsub(text, "|r", "|r" .. wcol)
end
end end
frame:HookAddMessage(text, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) frame:HookAddMessage(text, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17)
+1 -1
View File
@@ -52,7 +52,7 @@ pfUI:RegisterModule("combopoints", function ()
-- combo -- combo
if class == "DRUID" or class == "ROGUE" then if class == "DRUID" or class == "ROGUE" then
local combo = CreateFrame("Frame") local combo = CreateFrame("Frame")
combo:RegisterUnitEvent("UNIT_COMBO_POINTS", "player") combo:RegisterEvent("UNIT_COMBO_POINTS")
combo:RegisterEvent("PLAYER_COMBO_POINTS") combo:RegisterEvent("PLAYER_COMBO_POINTS")
combo:RegisterEvent("PLAYER_TARGET_CHANGED") combo:RegisterEvent("PLAYER_TARGET_CHANGED")
combo:RegisterEvent("PLAYER_ENTERING_WORLD") combo:RegisterEvent("PLAYER_ENTERING_WORLD")
+28 -40
View File
@@ -1,40 +1,35 @@
pfUI:RegisterModule("cooldown", function () pfUI:RegisterModule("cooldown", function ()
-- cache values -- cache values
-- local lowcolor = {strsplit(",", C.appearance.cd.lowcolor)} local lowcolor = {strsplit(",", C.appearance.cd.lowcolor)}
-- local normalcolor = {strsplit(",", C.appearance.cd.normalcolor)} local normalcolor = {strsplit(",", C.appearance.cd.normalcolor)}
-- local minutecolor = {strsplit(",", C.appearance.cd.minutecolor)} local minutecolor = {strsplit(",", C.appearance.cd.minutecolor)}
-- local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)} local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
-- local daycolor = {strsplit(",", C.appearance.cd.daycolor)} local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
local parent, parent_name
local function pfCooldownOnUpdate() local function pfCooldownOnUpdate()
-- Throttle FIRST. One of these runs per visible cooldown text, every frame, parent = this:GetParent()
-- so anything above this gate is multiplied by the frame rate and by how if not parent then this:Hide() end
-- many cooldowns are ticking. parent_name = parent:GetName()
local now = GetTime()
if (this.tick or 0) > now then return end
this.tick = now + .1
local parent = this:GetParent() -- avoid to set cooldowns on invalid frames
if not parent then this:Hide() return end if parent_name and _G[parent_name .. "Cooldown"] then
if not _G[parent_name .. "Cooldown"]:IsShown() then
-- avoid to set cooldowns on invalid frames. The cooldown frame is stashed this:Hide()
-- at creation: resolving it as _G[parent:GetName() .. "Cooldown"] built and end
-- interned that string twice per call, and this is the hottest path in the
-- UI. The stashed reference is also the frame itself rather than a guess
-- from its parent's name, so it holds for cooldowns named anything else.
if this.cooldown and not this.cooldown:IsShown() then
this:Hide()
return
end end
-- only run every 0.1 seconds from here on
if ( this.tick or .1) > GetTime() then return else this.tick = GetTime() + .1 end
-- fix own alpha value (should be inherited, but somehow isn't always) -- fix own alpha value (should be inherited, but somehow isn't always)
if this:GetAlpha() ~= parent:GetAlpha() then if this:GetAlpha() ~= parent:GetAlpha() then
this:SetAlpha(parent:GetAlpha()) this:SetAlpha(parent:GetAlpha())
end end
if this.start < now then if this.start < GetTime() then
-- calculating remaining time as it should be -- calculating remaining time as it should be
local remaining = this.duration - (now - this.start) local remaining = this.duration - (GetTime() - this.start)
if remaining >= 0 then if remaining >= 0 then
this.text:SetText(GetColoredTimeString(remaining)) this.text:SetText(GetColoredTimeString(remaining))
else else
@@ -43,13 +38,13 @@ pfUI:RegisterModule("cooldown", function ()
else else
-- I have absolutely no idea, but it works: -- I have absolutely no idea, but it works:
-- https://github.com/Stanzilla/WoWUIBugs/issues/47 -- https://github.com/Stanzilla/WoWUIBugs/issues/47
local currentTime = time() local time = time()
local startupTime = currentTime - now local startupTime = time - GetTime()
-- just a simplification of: ((2^32) - (start * 1000)) / 1000 -- just a simplification of: ((2^32) - (start * 1000)) / 1000
local cdTime = (2 ^ 32) / 1000 - this.start local cdTime = (2 ^ 32) / 1000 - this.start
local cdStartTime = startupTime - cdTime local cdStartTime = startupTime - cdTime
local cdEndTime = cdStartTime + this.duration local cdEndTime = cdStartTime + this.duration
local remaining = cdEndTime - currentTime local remaining = cdEndTime - time
if remaining >= 0 then if remaining >= 0 then
this.text:SetText(GetColoredTimeString(remaining)) this.text:SetText(GetColoredTimeString(remaining))
@@ -60,16 +55,11 @@ pfUI:RegisterModule("cooldown", function ()
end end
local height, size local height, size
local textcount = 0
local function pfCreateCoolDown(cooldown, start, duration) local function pfCreateCoolDown(cooldown, start, duration)
textcount = textcount + 1 cooldown.pfCooldownText = CreateFrame("Frame", "pfCooldownFrame", cooldown:GetParent())
local name = cooldown.GetName and cooldown:GetName() or "pfCooldown" .. textcount
cooldown.pfCooldownText = CreateFrame("Frame", name .. "Text", cooldown:GetParent())
cooldown.pfCooldownText.cooldown = cooldown
cooldown.pfCooldownText:SetAllPoints(cooldown) cooldown.pfCooldownText:SetAllPoints(cooldown)
cooldown.pfCooldownText:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 2) cooldown.pfCooldownText:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 2)
cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString(name .. "TextString", "OVERLAY") cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString("pfCooldownFrameText", "OVERLAY")
if not cooldown.pfCooldownType then if not cooldown.pfCooldownType then
size = tonumber(C.appearance.cd.font_size_foreign) size = tonumber(C.appearance.cd.font_size_foreign)
@@ -104,10 +94,8 @@ pfUI:RegisterModule("cooldown", function ()
return return
end end
-- realign the indicator Models that stock buttons build; the model is -- realign cooldown frames
-- drawn at 36px and needs scaling to its button. A Cooldown draws its local parent = this.GetParent and this:GetParent()
-- 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)
@@ -146,7 +134,7 @@ pfUI:RegisterModule("cooldown", function ()
end end
end end
-- hook the SetTimer function that every cooldown, Cooldown frame or -- vanilla does not have a cooldown frame type, so we hook the
-- stock indicator Model, is driven through. -- regular SetTimer function that each one is calling.
hooksecurefunc("CooldownFrame_SetTimer", SetCooldown) hooksecurefunc("CooldownFrame_SetTimer", SetCooldown)
end) end)
+63 -225
View File
@@ -1,82 +1,20 @@
-- One server clock drives every power: Player::RegenerateAll fires every
-- REGEN_TIME_FULL (2s), re-arms with `+=`, and is never reset by casting. The
-- five-second rule (SetLastManaUse on any mana-costing cast) changes what a tick
-- pays, never when it lands; mp5 and the player's MOD_MANA_REGEN_INTERRUPT share
-- still come in. That share can't be computed here -- item sources are equip
-- auras absent from the buff list and m_modManaRegenInterrupt is never sent --
-- so the spark shows it instead: dim through the window until a tick lands.
--
-- The sweep free-runs on that clock and phase-locks to observed gains. A gain
-- mid-sweep (Illumination, Judgement of Wisdom, potions, a Mana Spring totem on
-- its own phase) is not the tick and never moves it.
local FIVE_SECOND_RULE = 5
-- gains farther than this from the predicted boundary are not the tick
local TICK_TOLERANCE = .25
-- arrival jitter; a tick inside this band confirms the sweep rather than
-- re-anchoring it, or the spark hitches at every wrap
local TICK_JITTER = .08
-- Player::RegenerateAll:
-- mod = GetTotalAuraModifier(SPELL_AURA_MOD_ENERGY_REGEN_TIME)
-- if mod > 0 then mod = mod * agility / 10 end
-- m_regenTimer += max(1, REGEN_TIME_FULL - mod) -- milliseconds
local REGEN_TIME_FULL = 2
local ENERGY_REGEN_TIME_AURA = 217 -- SPELL_AURA_MOD_ENERGY_REGEN_TIME
-- fixed magnitude is basePoints + baseDice (stored 11 -> 12); a die above 1 is
-- a roll the client can't know, so it counts as nothing rather than a guess
local amountCache = {}
local function auraAmount(spellID)
local amount = amountCache[spellID]
if amount then return amount end
amount = 0
local effects = C_Spell.GetSpellEffectInfo(spellID) -- nil for an id with no record
if effects then
for i = 1, 3 do
local fx = effects[i]
if fx.auraName == ENERGY_REGEN_TIME_AURA and fx.dieSides <= 1 then
amount = fx.basePoints + fx.baseDice
break
end
end
end
amountCache[spellID] = amount
return amount
end
-- A passive is in effect exactly while known (current rank only, never in the
-- buff list); anything castable or cast on us counts only while it is up.
local function getEnergyRegenTimeMod()
local sum = 0
for _, spellID in ipairs(C_SpellBook.GetPlayerSpellsByAura(ENERGY_REGEN_TIME_AURA)) do
if C_Spell.IsSpellPassive(spellID) then
sum = sum + auraAmount(spellID)
end
end
for i = 1, 32 do
local spellID = select(10, C_UnitAuras.UnitAura("player", i, "HELPFUL"))
if not spellID then break end
sum = sum + auraAmount(spellID)
end
return sum
end
-- cleared on SPELLS_CHANGED (passives) and PLAYER_AURAS_CHANGED (buffs), and
-- recomputed by the next tick that asks. Agility stays live: it's one call.
local energyRegenTimeMod
local function getAdjustedTickTimer() local function getAdjustedTickTimer()
if not energyRegenTimeMod then local adjustedEnergyTick = 2
energyRegenTimeMod = getEnergyRegenTimeMod()
end
if energyRegenTimeMod == 0 then return REGEN_TIME_FULL end
-- ms on the server, seconds here; the 1ms floor is the server's and this is a divisor -- Check rogue talents and compute energy tick timing reduction for Combat spec (1.18.0 Blade Rush Talent)
local reduction = energyRegenTimeMod * UnitStat("player", 2) / 10000 if UnitClassBase("player") == "ROGUE" then
return math.max(0.001, REGEN_TIME_FULL - reduction) local _, _, _, _, currRank = GetTalentInfo(2, 16)
local bladeRushRank = currRank or 0
if bladeRushRank > 0 then
local agility = UnitStat("player", 2) -- 2 is agility stat index
local reductionPerAgi = 0.0006 * bladeRushRank -- 0.0006 for rank 1, 0.0012 for rank 2
local totalReduction = agility * reductionPerAgi
adjustedEnergyTick = adjustedEnergyTick - totalReduction
end
end
return adjustedEnergyTick
end end
pfUI:RegisterModule("energytick", function() pfUI:RegisterModule("energytick", function()
@@ -84,60 +22,14 @@ pfUI:RegisterModule("energytick", function()
return return
end end
-- inside the module body on purpose: C is on pfUI.env, not _G
local function getBarWidth()
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
end
local rawborder, default_border = GetBorderSize("unitframes")
local 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
local function lockTick(frame)
local now, period = GetTime(), getAdjustedTickTimer()
if frame.start then
-- signed distance to the nearest predicted boundary
local err = mod(now - frame.start, period)
if err > period / 2 then err = err - period end
if math.abs(err) <= TICK_TOLERANCE then
-- correct only what lies beyond normal jitter
if err > TICK_JITTER then
frame.start = frame.start + (err - TICK_JITTER)
elseif err < -TICK_JITTER then
frame.start = frame.start + (err + TICK_JITTER)
end
frame.max, frame.rejected = period, nil
return true
end
-- two rejected gains one period apart are the real clock: relock to it
local periodic = frame.rejected and math.abs(now - frame.rejected - period) <= TICK_TOLERANCE
if not periodic then
frame.rejected = now
return false
end
end
frame.start, frame.max, frame.rejected = now, period, nil
return true
end
local energytick = CreateFrame("Frame", nil, pfUI.uf.player.power.bar) local energytick = CreateFrame("Frame", nil, pfUI.uf.player.power.bar)
energytick:SetAllPoints(pfUI.uf.player.power.bar) energytick:SetAllPoints(pfUI.uf.player.power.bar)
energytick:RegisterEvent("PLAYER_ENTERING_WORLD") energytick:RegisterEvent("PLAYER_ENTERING_WORLD")
energytick:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player") energytick:RegisterEvent("UNIT_DISPLAYPOWER")
energytick:RegisterUnitEvent("UNIT_ENERGY", "player") energytick:RegisterEvent("UNIT_ENERGY")
energytick:RegisterUnitEvent("UNIT_MANA", "player") energytick:RegisterEvent("UNIT_MANA")
energytick:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player") energytick:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF")
energytick:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", "player") energytick:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS")
energytick:RegisterEvent("SPELLS_CHANGED")
energytick:RegisterEvent("PLAYER_AURAS_CHANGED")
energytick:SetScript("OnEvent", function() energytick:SetScript("OnEvent", function()
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
@@ -150,52 +42,44 @@ pfUI:RegisterModule("energytick", function()
this:Hide() this:Hide()
end end
if event == "SPELLS_CHANGED" or event == "PLAYER_AURAS_CHANGED" then -- Filter nur eigene Energy-Gewinne von Talents/Buffs
energyRegenTimeMod = nil if event == "CHAT_MSG_SPELL_SELF_BUFF" or event == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" then
if string.find(arg1, "You gain") and string.find(arg1, "Energy from") then
this.ignoreNextGain = true
end
return return
end end
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
this.lastPower = UnitPower("player") this.lastMana = UnitPower("player")
end
-- the rule arms on the cast (Spell::TakePower: mana powerType, cost > 0),
-- not on a mana drop -- Mana Burn lowers mana without arming it
if event == "UNIT_SPELLCAST_SUCCEEDED" and arg1 == "player" then
local cost = C_Spell.GetSpellPowerCost(arg3)
cost = cost and cost[1]
if cost and cost.type == Enum.PowerType.Mana and cost.cost > 0 then
this.fsrSpell, this.fsrEnd = arg3, GetTime() + FIVE_SECOND_RULE
this.fsrGain = nil
end
return
end
-- Unit::Update won't expire the rule while the spending spell still channels
if event == "UNIT_SPELLCAST_CHANNEL_STOP" and arg1 == "player" then
if this.fsrSpell and this.fsrSpell == arg3 then
this.fsrEnd, this.fsrGain = GetTime() + FIVE_SECOND_RULE, nil
end
return
end end
if (event == "UNIT_MANA" or event == "UNIT_ENERGY") and arg1 == "player" then if (event == "UNIT_MANA" or event == "UNIT_ENERGY") and arg1 == "player" then
local power = UnitPower("player") this.currentMana = UnitPower("player")
local diff = this.lastPower and (power - this.lastPower) or 0 local diff = 0
this.lastPower = power if this.lastMana then
diff = this.currentMana - this.lastMana
end
-- only a gain can be the tick; a spend never touches the phase if this.mode == "MANA" and diff < 0 then
if diff > 0 and lockTick(this) then this.target = 5
-- a tick inside the window proves regen continues through it elseif this.mode == "MANA" and diff > 0 then
if this.fsrEnd and this.fsrEnd > GetTime() then if UnitPower("player") >= UnitPowerMax("player") then
this.fsrGain = true this.start = nil
this.spark:SetAlpha(0)
this:Hide()
elseif this.max ~= 5 and diff > (this.badtick and this.badtick * 1.2 or 5) then
this.target = 2
else
this.badtick = diff
end end
elseif this.mode == "ENERGY" and diff >= 0 then
if not this.ignoreNextGain then
this.target = getAdjustedTickTimer()
end
this.ignoreNextGain = false
end end
this.lastMana = this.currentMana
-- phase is kept while hidden; OnUpdate catches up by whole periods
if this.mode == "MANA" and power >= UnitPowerMax("player") then
this:Hide()
end
end end
end) end)
@@ -206,79 +90,37 @@ 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. The display is cosmetic and can be if this.target then
-- hidden on its own; the window itself (fsrEnd/fsrGain) keeps running this.start, this.max = GetTime(), this.target
-- either way -- it still dims the sweep spark below. this.target = nil
local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0 this.spark:SetAlpha(1)
if this.mode == "MANA" and remaining > 0 then this:Show()
if C.unitframes.player.showfsr == "1" then
if fsrOverlay then
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE)
else
this.fsrbar:SetValue(remaining)
end
this.fsrbar:Show()
else
this.fsrbar:Hide()
end
else
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
this.fsrbar:Hide()
end end
if not this.start then if not this.start then
this.spark:SetAlpha(0)
return
end
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
this.spark:SetAlpha(0)
return return
end end
this.current = GetTime() - this.start this.current = GetTime() - this.start
-- roll over by whole periods, not from now: restarting bakes frame
-- overshoot into the phase as drift the lock then has to chase
if this.current > this.max then if this.current > this.max then
this.start = this.start + this.max * math.floor(this.current / this.max) -- Don't restart tick timer if mana is full
this.max = getAdjustedTickTimer() if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
this.current = GetTime() - this.start this.start = nil
this.spark:SetAlpha(0)
return
end
this.start, this.max, this.current = GetTime(), getAdjustedTickTimer(), 0
end end
-- dim while the rule is up and nothing has ticked inside it yet local pos = (C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width)
this.spark:SetAlpha((remaining > 0 and not this.fsrGain) and .4 or 1) * (this.current / this.max)
if not C.unitframes.player.pheight then if not C.unitframes.player.pheight then
return return
end end
local pos = getBarWidth() * (this.current / this.max)
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)
if fsrOverlay then
-- classic style: a translucent shade on top of the power bar's own fill
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
energytick.fsrbar: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.spark = energytick:CreateTexture(nil, "OVERLAY") energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark") energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
energytick.spark:SetHeight(C.unitframes.player.pheight + 15) energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
@@ -289,10 +131,6 @@ 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)
+1 -20
View File
@@ -130,28 +130,9 @@ pfUI:RegisterModule("eqcompare", function ()
SetTradeTargetItem = GetTradeTargetItemLink SetTradeTargetItem = GetTradeTargetItemLink
} }
-- Guda anchors its item tooltips with ANCHOR_NONE and its own SetPoint, and
-- the tooltip module then moves every ANCHOR_NONE tooltip to its configured
-- spot. Reading the rect inside the Set* call picks the side against the
-- position the tooltip is about to leave, so place the compare a frame later.
local deferred
if C_AddOns.DoesAddOnExist("Guda") then
EventUtil.ContinueOnAddOnLoaded("Guda", function()
deferred = true
end)
end
local function makeHook(getter) local function makeHook(getter)
return function(tooltip, arg1, arg2, arg3) return function(tooltip, arg1, arg2, arg3)
local link = getter(arg1, arg2, arg3) ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
if not deferred then
return ShowCompareItem(tooltip, link)
end
RunNextFrame(function()
if tooltip:IsShown() then
ShowCompareItem(tooltip, link)
end
end)
end end
end end
+273 -116
View File
@@ -1,10 +1,11 @@
-- Equipment Manager module -- Equipment Manager module
-- Backport of the 4.3.4 GearManagerDialog UI on top of ClassicAPI's C_EquipmentSet.* API. -- Backport of the 4.3.4 GearManagerDialog UI on top of ClassicAPI's
-- C_EquipmentSet.* API. Adds a 6th tab to CharacterFrame.
pfUI:RegisterModule("equipmentmanager", function() pfUI:RegisterModule("equipmentmanager", function()
if not C_EquipmentSet or not C_EquipmentSet.CanUseEquipmentSets() then return end if not C_EquipmentSet or not C_EquipmentSet.CanUseEquipmentSets() then return end
pfUI.equipmentmanager = {} pfUI.equipmentmanager = pfUI.equipmentmanager or {}
local SET_ROW_HEIGHT = 36 local SET_ROW_HEIGHT = 36
@@ -13,7 +14,6 @@ pfUI:RegisterModule("equipmentmanager", function()
local pendingAction = nil -- "new" | "save" | "rename" — what the popups apply to local pendingAction = nil -- "new" | "save" | "rename" — what the popups apply to
local slotOverlays = {} -- [invSlotID] = ignored-overlay texture on the character slot local slotOverlays = {} -- [invSlotID] = ignored-overlay texture on the character slot
local popoutButtons = {} -- popout arrow buttons; toggled with the EM frame local popoutButtons = {} -- popout arrow buttons; toggled with the EM frame
local UpdatePopouts -- forward decl; assigned once popouts + flyout exist
-- Pending ignored-slot toggles per set: a slotID here means the -- Pending ignored-slot toggles per set: a slotID here means the
-- effective state is flipped from what's persisted. Committed on -- effective state is flipped from what's persisted. Committed on
@@ -55,7 +55,7 @@ pfUI:RegisterModule("equipmentmanager", function()
local function EquipSet(setID) local function EquipSet(setID)
if not setID then return end if not setID then return end
if C_EquipmentSet.EquipmentSetContainsLockedItems(setID) then if C_EquipmentSet.EquipmentSetContainsLockedItems(setID) then
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT, 1, .1, .1, 1) UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT or "Locked items in set", 1, .1, .1, 1)
return return
end end
ClearCursor() ClearCursor()
@@ -68,7 +68,8 @@ pfUI:RegisterModule("equipmentmanager", function()
-- ============================================================ -- ============================================================
local frame = CreateFrame("Frame", "pfEquipmentManagerFrame", CharacterFrame) local frame = CreateFrame("Frame", "pfEquipmentManagerFrame", CharacterFrame)
frame:SetSize(220, 350) frame:SetWidth(220)
frame:SetHeight(350)
frame:SetFrameStrata("HIGH") frame:SetFrameStrata("HIGH")
frame:SetScript("OnShow", function() frame:SetScript("OnShow", function()
this:ClearAllPoints() this:ClearAllPoints()
@@ -77,7 +78,7 @@ pfUI:RegisterModule("equipmentmanager", function()
else else
frame:SetPoint("TOPLEFT", CharacterFrame, "TOPRIGHT", 0, 0) frame:SetPoint("TOPLEFT", CharacterFrame, "TOPRIGHT", 0, 0)
end end
UpdatePopouts() for _, b in ipairs(popoutButtons) do b:Show() end
end) end)
CreateBackdrop(frame, nil, nil, .9) CreateBackdrop(frame, nil, nil, .9)
CreateBackdropShadow(frame) CreateBackdropShadow(frame)
@@ -85,7 +86,7 @@ pfUI:RegisterModule("equipmentmanager", function()
frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
frame.title:SetPoint("TOP", frame, "TOP", 0, -10) frame.title:SetPoint("TOP", frame, "TOP", 0, -10)
frame.title:SetText(EQUIPMENT_MANAGER) frame.title:SetText(T["Equipment Manager"] or "Equipment Manager")
local closeBtn = CreateFrame("Button", nil, frame, "UIPanelCloseButton") local closeBtn = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
closeBtn:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4) closeBtn:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4)
@@ -97,14 +98,15 @@ pfUI:RegisterModule("equipmentmanager", function()
-- ============================================================ -- ============================================================
local toggleBtn = CreateFrame("Button", "pfEqMgrToggleButton", PaperDollFrame) local toggleBtn = CreateFrame("Button", "pfEqMgrToggleButton", PaperDollFrame)
toggleBtn:SetSize(28, 28) toggleBtn:SetWidth(28)
toggleBtn:SetHeight(28)
toggleBtn:SetPoint("BOTTOM", CharacterHandsSlot, "TOP", 0, 4) toggleBtn:SetPoint("BOTTOM", CharacterHandsSlot, "TOP", 0, 4)
toggleBtn:SetNormalTexture(pfUI.path.."\\img\\UI-GearManager-Button") toggleBtn:SetNormalTexture(pfUI.path.."\\img\\UI-GearManager-Button")
toggleBtn:SetPushedTexture(pfUI.path.."\\img\\UI-GearManager-Button-Pushed") toggleBtn:SetPushedTexture(pfUI.path.."\\img\\UI-GearManager-Button-Pushed")
toggleBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD") toggleBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
toggleBtn:SetScript("OnEnter", function() toggleBtn:SetScript("OnEnter", function()
GameTooltip:SetOwner(this, "ANCHOR_RIGHT") GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(PAPERDOLL_EQUIPMENTMANAGER) GameTooltip:SetText(T["Equipment Manager"] or "Equipment Manager")
GameTooltip:Show() GameTooltip:Show()
end) end)
toggleBtn:SetScript("OnLeave", function() GameTooltip:Hide() end) toggleBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
@@ -124,13 +126,13 @@ pfUI:RegisterModule("equipmentmanager", function()
-- ============================================================ -- ============================================================
local btnEquip = CreateFrame("Button", "pfEqMgrEquip", frame, "UIPanelButtonTemplate") local btnEquip = CreateFrame("Button", "pfEqMgrEquip", frame, "UIPanelButtonTemplate")
btnEquip:SetSize(86, 22); btnEquip:SetText(EQUIPSET_EQUIP) btnEquip:SetWidth(86); btnEquip:SetHeight(22); btnEquip:SetText(T["Equip"] or "Equip")
btnEquip:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -30) btnEquip:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -30)
SkinButton(btnEquip) SkinButton(btnEquip)
btnEquip:SetScript("OnClick", function() EquipSet(selectedSetID) end) btnEquip:SetScript("OnClick", function() EquipSet(selectedSetID) end)
local btnSave = CreateFrame("Button", "pfEqMgrSave", frame, "UIPanelButtonTemplate") local btnSave = CreateFrame("Button", "pfEqMgrSave", frame, "UIPanelButtonTemplate")
btnSave:SetSize(86, 22); btnSave:SetText(SAVE) btnSave:SetWidth(86); btnSave:SetHeight(22); btnSave:SetText(T["Save"] or "Save")
btnSave:SetPoint("LEFT", btnEquip, "RIGHT", 6, 0) btnSave:SetPoint("LEFT", btnEquip, "RIGHT", 6, 0)
SkinButton(btnSave) SkinButton(btnSave)
btnSave:SetScript("OnClick", function() btnSave:SetScript("OnClick", function()
@@ -139,7 +141,7 @@ pfUI:RegisterModule("equipmentmanager", function()
if not name then return end if not name then return end
local targetID = selectedSetID local targetID = selectedSetID
StaticPopupDialogs["PFUI_EQMGR_SAVE_CONFIRM"] = { StaticPopupDialogs["PFUI_EQMGR_SAVE_CONFIRM"] = {
text = string.format(CONFIRM_SAVE_EQUIPMENT_SET, name), text = string.format(T["Would you like to save the equipment set '%s'?"] or "Would you like to save the equipment set '%s'?", name),
button1 = YES, button2 = NO, button1 = YES, button2 = NO,
OnAccept = function() OnAccept = function()
C_EquipmentSet.ClearIgnoredSlotsForSave() C_EquipmentSet.ClearIgnoredSlotsForSave()
@@ -164,7 +166,7 @@ pfUI:RegisterModule("equipmentmanager", function()
local rowMenu = CreateFrame("Frame", "pfEqMgrRowMenu", UIParent) local rowMenu = CreateFrame("Frame", "pfEqMgrRowMenu", UIParent)
rowMenu:SetFrameStrata("DIALOG") rowMenu:SetFrameStrata("DIALOG")
rowMenu:SetSize(140, 50); rowMenu:SetWidth(140); rowMenu:SetHeight(50)
rowMenu:Hide() rowMenu:Hide()
CreateBackdrop(rowMenu, nil, nil, .95) CreateBackdrop(rowMenu, nil, nil, .95)
CreateBackdropShadow(rowMenu) CreateBackdropShadow(rowMenu)
@@ -176,16 +178,16 @@ 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 MouseIsOverHitRect(this) then return end if MouseIsOver(this) then return end
if this.anchorBtn and MouseIsOverHitRect(this.anchorBtn) then return end if this.anchorBtn and MouseIsOver(this.anchorBtn) then return end
this:Hide() this:Hide()
end) end)
tinsert(UISpecialFrames, "pfEqMgrRowMenu") -- Escape closes it tinsert(UISpecialFrames, "pfEqMgrRowMenu") -- Escape closes it
rowMenu.changeBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate") rowMenu.changeBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
rowMenu.changeBtn:SetSize(130, 20); rowMenu.changeBtn:SetWidth(130); rowMenu.changeBtn:SetHeight(20)
rowMenu.changeBtn:SetPoint("TOPLEFT", rowMenu, "TOPLEFT", 5, -3) rowMenu.changeBtn:SetPoint("TOPLEFT", rowMenu, "TOPLEFT", 5, -3)
rowMenu.changeBtn:SetText(EQUIPMENT_SET_EDIT) rowMenu.changeBtn:SetText(T["Change Name/Icon"] or "Change Name/Icon")
SkinButton(rowMenu.changeBtn) SkinButton(rowMenu.changeBtn)
rowMenu.changeBtn:SetScript("OnClick", function() rowMenu.changeBtn:SetScript("OnClick", function()
rowMenu:Hide() rowMenu:Hide()
@@ -196,9 +198,9 @@ pfUI:RegisterModule("equipmentmanager", function()
end) end)
rowMenu.deleteBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate") rowMenu.deleteBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
rowMenu.deleteBtn:SetSize(130, 20); rowMenu.deleteBtn:SetWidth(130); rowMenu.deleteBtn:SetHeight(20)
rowMenu.deleteBtn:SetPoint("TOP", rowMenu.changeBtn, "BOTTOM", 0, -2) rowMenu.deleteBtn:SetPoint("TOP", rowMenu.changeBtn, "BOTTOM", 0, -2)
rowMenu.deleteBtn:SetText(DELETE) rowMenu.deleteBtn:SetText(T["Delete"] or "Delete")
SkinButton(rowMenu.deleteBtn) SkinButton(rowMenu.deleteBtn)
rowMenu.deleteBtn:SetScript("OnClick", function() rowMenu.deleteBtn:SetScript("OnClick", function()
rowMenu:Hide() rowMenu:Hide()
@@ -206,7 +208,7 @@ pfUI:RegisterModule("equipmentmanager", function()
local targetID = rowMenu.targetSetID local targetID = rowMenu.targetSetID
local name = C_EquipmentSet.GetEquipmentSetInfo(targetID) local name = C_EquipmentSet.GetEquipmentSetInfo(targetID)
StaticPopupDialogs["PFUI_EQMGR_DELETE"] = { StaticPopupDialogs["PFUI_EQMGR_DELETE"] = {
text = string.format(CONFIRM_DELETE_EQUIPMENT_SET, name or "?"), text = string.format(T["Delete equipment set '%s'?"] or "Delete equipment set '%s'?", name or "?"),
button1 = YES, button2 = NO, button1 = YES, button2 = NO,
OnAccept = function() OnAccept = function()
pendingIgnoredToggles[targetID] = nil pendingIgnoredToggles[targetID] = nil
@@ -227,8 +229,9 @@ pfUI:RegisterModule("equipmentmanager", function()
local LIST_ROW_STRIDE = SET_ROW_HEIGHT + 2 local LIST_ROW_STRIDE = SET_ROW_HEIGHT + 2
local listFrame = CreateFrame("Frame", nil, frame) local listFrame = CreateFrame("Frame", nil, frame)
listFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -58) listFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -58)
listFrame:SetWidth(180)
-- Height fits N rows + (N-1) inter-row gaps + 3px top/bottom padding. -- Height fits N rows + (N-1) inter-row gaps + 3px top/bottom padding.
listFrame:SetSize(180, LIST_VISIBLE_ROWS * LIST_ROW_STRIDE + 4) listFrame:SetHeight(LIST_VISIBLE_ROWS * LIST_ROW_STRIDE + 4)
CreateBackdrop(listFrame, nil, nil, .75) CreateBackdrop(listFrame, nil, nil, .75)
-- Mouse-wheel scroll: list of [sets..., newSetRow] is virtualized -- Mouse-wheel scroll: list of [sets..., newSetRow] is virtualized
@@ -244,13 +247,15 @@ pfUI:RegisterModule("equipmentmanager", function()
local setRows = {} local setRows = {}
local function CreateSetRow() local function CreateSetRow()
local row = CreateFrame("Button", nil, listFrame) local row = CreateFrame("Button", nil, listFrame)
row:SetSize(170, SET_ROW_HEIGHT) row:SetWidth(170)
row:SetHeight(SET_ROW_HEIGHT)
-- Position is set per-Refresh based on the row's visible slot; -- Position is set per-Refresh based on the row's visible slot;
-- start anchored to avoid uninitialized geometry before first Refresh. -- start anchored to avoid uninitialized geometry before first Refresh.
row:SetPoint("TOPLEFT", listFrame, "TOPLEFT", 5, -3) row:SetPoint("TOPLEFT", listFrame, "TOPLEFT", 5, -3)
row.icon = row:CreateTexture(nil, "ARTWORK") row.icon = row:CreateTexture(nil, "ARTWORK")
row.icon:SetSize(30, 30) row.icon:SetWidth(30)
row.icon:SetHeight(30)
row.icon:SetPoint("LEFT", row, "LEFT", 2, 0) row.icon:SetPoint("LEFT", row, "LEFT", 2, 0)
row.icon:SetTexCoord(.08, .92, .08, .92) row.icon:SetTexCoord(.08, .92, .08, .92)
@@ -265,7 +270,7 @@ pfUI:RegisterModule("equipmentmanager", function()
row.highlight:Hide() row.highlight:Hide()
row.gear = CreateFrame("Button", nil, row) row.gear = CreateFrame("Button", nil, row)
row.gear:SetSize(16, 16); row.gear:SetWidth(16); row.gear:SetHeight(16)
row.gear:SetPoint("RIGHT", row, "RIGHT", -4, 0) row.gear:SetPoint("RIGHT", row, "RIGHT", -4, 0)
row.gear.tex = row.gear:CreateTexture(nil, "ARTWORK") row.gear.tex = row.gear:CreateTexture(nil, "ARTWORK")
row.gear.tex:SetAllPoints(row.gear) row.gear.tex:SetAllPoints(row.gear)
@@ -301,37 +306,27 @@ pfUI:RegisterModule("equipmentmanager", function()
pfUI.equipmentmanager.Refresh() pfUI.equipmentmanager.Refresh()
end) end)
-- Drag a set onto an action bar. PickupEquipmentSet is newer than -- Hover handling: OnLeave fires when the cursor moves onto a child
-- the rest of C_EquipmentSet, so dragging stays inert on ClassicAPI -- (the gear button becomes the topmost mouse target). Use an
-- builds that don't ship it yet. -- OnUpdate poll so the gear stays shown while the cursor is over
if C_EquipmentSet.PickupEquipmentSet then -- the row OR the gear itself.
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
-- gear child. The gear sits inside the row's rectangle, so a single
-- MouseIsOverHitRect(row) check covers both. OnLeave on the row and the
-- gear catches every exit path, so no OnUpdate poll is needed.
local function HideRowHover()
if MouseIsOverHitRect(row) then return end
GameTooltip:Hide()
row.gear:Hide()
end
row:SetScript("OnEnter", function() row:SetScript("OnEnter", function()
if not row.setID then return end if not this.setID then return end
local name = C_EquipmentSet.GetEquipmentSetInfo(row.setID) local name = C_EquipmentSet.GetEquipmentSetInfo(this.setID)
if name then if name then
GameTooltip:SetOwner(row, "ANCHOR_RIGHT") GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetEquipmentSet(name) GameTooltip:SetEquipmentSet(name)
GameTooltip:Show() GameTooltip:Show()
end end
row.gear:Show() row.gear:Show()
this:SetScript("OnUpdate", function()
if not MouseIsOver(this) and not MouseIsOver(row.gear) then
this:SetScript("OnUpdate", nil)
GameTooltip:Hide()
row.gear:Hide()
end
end)
end) end)
row:SetScript("OnLeave", HideRowHover)
row.gear:SetScript("OnLeave", HideRowHover)
return row return row
end end
@@ -343,10 +338,10 @@ pfUI:RegisterModule("equipmentmanager", function()
-- specific set index (row[i] always shows ids[i]). -- specific set index (row[i] always shows ids[i]).
local newSetRow = CreateFrame("Button", nil, listFrame) local newSetRow = CreateFrame("Button", nil, listFrame)
newSetRow:SetSize(170, SET_ROW_HEIGHT); newSetRow:SetWidth(170); newSetRow:SetHeight(SET_ROW_HEIGHT)
newSetRow.icon = newSetRow:CreateTexture(nil, "ARTWORK") newSetRow.icon = newSetRow:CreateTexture(nil, "ARTWORK")
newSetRow.icon:SetSize(24, 24); newSetRow.icon:SetWidth(24); newSetRow.icon:SetHeight(24)
newSetRow.icon:SetPoint("LEFT", newSetRow, "LEFT", 5, 0) newSetRow.icon:SetPoint("LEFT", newSetRow, "LEFT", 5, 0)
newSetRow.icon:SetTexture(pfUI.path.."\\img\\Character-Plus") newSetRow.icon:SetTexture(pfUI.path.."\\img\\Character-Plus")
@@ -354,7 +349,7 @@ pfUI:RegisterModule("equipmentmanager", function()
newSetRow.text:SetPoint("LEFT", newSetRow.icon, "RIGHT", 8, 0) newSetRow.text:SetPoint("LEFT", newSetRow.icon, "RIGHT", 8, 0)
newSetRow.text:SetPoint("RIGHT", newSetRow, "RIGHT", -4, 0) newSetRow.text:SetPoint("RIGHT", newSetRow, "RIGHT", -4, 0)
newSetRow.text:SetJustifyH("LEFT") newSetRow.text:SetJustifyH("LEFT")
newSetRow.text:SetText(PAPERDOLL_NEWEQUIPMENTSET) newSetRow.text:SetText(T["New Set"] or "New Set")
newSetRow.text:SetTextColor(0.2, 1, 0.2) newSetRow.text:SetTextColor(0.2, 1, 0.2)
newSetRow.highlight = newSetRow:CreateTexture(nil, "BACKGROUND") newSetRow.highlight = newSetRow:CreateTexture(nil, "BACKGROUND")
@@ -367,7 +362,8 @@ pfUI:RegisterModule("equipmentmanager", function()
local function MakeButton(name, label, parent, anchor, ax, ay, width) local function MakeButton(name, label, parent, anchor, ax, ay, width)
local b = CreateFrame("Button", name, parent, "UIPanelButtonTemplate") local b = CreateFrame("Button", name, parent, "UIPanelButtonTemplate")
b:SetSize(width or 70, 22) b:SetWidth(width or 70)
b:SetHeight(22)
b:SetText(label) b:SetText(label)
b:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", ax, ay) b:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", ax, ay)
SkinButton(b) SkinButton(b)
@@ -380,7 +376,8 @@ pfUI:RegisterModule("equipmentmanager", function()
local namePopup = CreateFrame("Frame", "pfEqMgrNamePopup", UIParent) local namePopup = CreateFrame("Frame", "pfEqMgrNamePopup", UIParent)
namePopup:SetFrameStrata("DIALOG") namePopup:SetFrameStrata("DIALOG")
namePopup:SetSize(472, 484) namePopup:SetWidth(472)
namePopup:SetHeight(498)
namePopup:SetPoint("CENTER", UIParent, "CENTER") namePopup:SetPoint("CENTER", UIParent, "CENTER")
namePopup:Hide() namePopup:Hide()
CreateBackdrop(namePopup, nil, nil, .9) CreateBackdrop(namePopup, nil, nil, .9)
@@ -391,28 +388,200 @@ pfUI:RegisterModule("equipmentmanager", function()
namePopup:SetScript("OnDragStart", function() this:StartMoving() end) namePopup:SetScript("OnDragStart", function() this:StartMoving() end)
namePopup:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) namePopup:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
-- Equipment seed leads the grid with gear-relevant item icons. The widget namePopup.title = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
-- owns the name field, preview, grid, filter, and search; this module keeps namePopup.title:SetPoint("TOP", namePopup, "TOP", 0, -10)
-- the OK/Cancel buttons and the create/save/rename flow below. namePopup.title:SetText(T["Save Set"] or "Save Set")
local iconPicker = CreateIconPicker("pfEqMgrIcon", namePopup,
IconDataProviderExtraType.Equipment, GEARSETS_POPUP_TEXT)
local btnPopupOK = MakeButton("pfEqMgrPopupOK", OKAY, namePopup, namePopup, 14, -340, 80) namePopup.nameLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
namePopup.nameLabel:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -34)
namePopup.nameLabel:SetText(T["Enter Set Name (Max 16 Characters):"] or "Enter Set Name (Max 16 Characters):")
local btnPopupCancel = MakeButton("pfEqMgrPopupCancel", CANCEL, namePopup, namePopup, 0, 0, 80) namePopup.editbox = CreateFrame("EditBox", "pfEqMgrNameEdit", namePopup, "InputBoxTemplate")
namePopup.editbox:SetWidth(280)
namePopup.editbox:SetHeight(20)
namePopup.editbox:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -52)
namePopup.editbox:SetAutoFocus(false)
namePopup.editbox:SetMaxLetters(16)
CreateBackdrop(namePopup.editbox)
namePopup.selectedLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
namePopup.selectedLabel:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", -14, -28)
namePopup.selectedLabel:SetText(T["Currently Selected"] or "Currently Selected")
namePopup.selectedLabel:SetTextColor(1, 0.82, 0)
namePopup.selectedPreview = CreateFrame("Frame", nil, namePopup)
namePopup.selectedPreview:SetWidth(42)
namePopup.selectedPreview:SetHeight(42)
namePopup.selectedPreview:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", -14, -44)
CreateBackdrop(namePopup.selectedPreview)
namePopup.selectedPreview.tex = namePopup.selectedPreview:CreateTexture(nil, "ARTWORK")
namePopup.selectedPreview.tex:SetAllPoints(namePopup.selectedPreview)
namePopup.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
namePopup.iconLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
namePopup.iconLabel:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -100)
namePopup.iconLabel:SetText(T["Choose an Icon:"] or "Choose an Icon:")
-- Icon picker: 10×8 grid of buttons + scroll
local ICON_GRID_COLS = 10
local ICON_GRID_ROWS = 8
local ICON_BTN_SIZE = 36
local ICON_BTN_PAD = 6
local iconScroll = CreateFrame("ScrollFrame", "pfEqMgrIconScroll", namePopup, "FauxScrollFrameTemplate")
iconScroll:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -130)
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
-- IconDataProviderMixin owns the icon DB, dedup, and lazy load.
-- Init on first picker open; release on hide so the cache GCs.
local provider = nil
-- Selection is tracked by PATH (not index) so it survives filter
-- changes: a spell icon you picked still saves correctly even after
-- you switch the filter to "Items" and it's no longer in the visible list.
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
local selectedIconPath = QUESTION_MARK
local function EnsureProvider()
if not provider then
provider = CreateAndInitFromMixin(IconDataProviderMixin,
IconDataProviderExtraType.Equipment)
end
end
-- Anchor scrollbar to iconScroll's right edge so its position tracks
-- the icon grid rather than the popup. -16/+16 vertical insets are the
-- standard up/down arrow spacing for UIPanelScrollBarTemplate.
local scrollbar = _G["pfEqMgrIconScrollScrollBar"]
if scrollbar then
scrollbar:ClearAllPoints()
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
SkinScrollbar(scrollbar)
end
-- Filter dropdown: "All Icons" / "Spells" / "Items" (top right of icon area).
local filterDropdown = CreateFrame("Frame", "pfEqMgrIconFilter", namePopup, "UIDropDownMenuTemplate")
filterDropdown:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", 0, -94)
local currentFilter = "all"
local function ApplyFilter(value)
currentFilter = value
UIDropDownMenu_SetSelectedValue(filterDropdown, value)
if provider then
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
else provider:SetIconTypes(nil) end
-- Don't reset selection — selectedIconPath persists. If the
-- selected icon isn't in the new filter, no grid entry will be
-- highlighted but Save will still write the chosen icon.
pfUI.equipmentmanager.RefreshIconGrid()
end
end
UIDropDownMenu_Initialize(filterDropdown, function()
local info
info = {}; info.text = T["All Icons"] or "All Icons"; info.value = "all"
info.func = function() ApplyFilter("all") end
info.checked = currentFilter == "all"
UIDropDownMenu_AddButton(info)
info = {}; info.text = T["Spells"] or "Spells"; info.value = "spells"
info.func = function() ApplyFilter("spells") end
info.checked = currentFilter == "spells"
UIDropDownMenu_AddButton(info)
info = {}; info.text = T["Items"] or "Items"; info.value = "items"
info.func = function() ApplyFilter("items") end
info.checked = currentFilter == "items"
UIDropDownMenu_AddButton(info)
end)
UIDropDownMenu_SetWidth(120, filterDropdown)
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
SkinDropDown(filterDropdown)
local iconButtons = {}
for r = 1, ICON_GRID_ROWS do
for c = 1, ICON_GRID_COLS do
local i = (r - 1) * ICON_GRID_COLS + c
local btn = CreateFrame("Button", nil, namePopup)
btn:SetWidth(ICON_BTN_SIZE)
btn:SetHeight(ICON_BTN_SIZE)
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
CreateBackdrop(btn)
btn.texture = btn:CreateTexture(nil, "ARTWORK")
btn.texture:SetAllPoints(btn)
btn.texture:SetTexCoord(.08, .92, .08, .92)
btn.gridIndex = i
btn:SetScript("OnClick", function()
if this.iconIndex and provider then
local path = provider:GetIconByIndex(this.iconIndex)
if path then selectedIconPath = path end
pfUI.equipmentmanager.RefreshIconGrid()
end
end)
btn:SetScript("OnEnter", function()
if not this.iconIndex or not provider then return end
local path = provider:GetIconByIndex(this.iconIndex)
if type(path) == "string" then
local name = string.gsub(path, "^.-INTERFACE\\\\ICONS\\\\", "")
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(name)
GameTooltip:Show()
end
end)
btn:SetScript("OnLeave", GameTooltip_Hide)
iconButtons[i] = btn
end
end
function pfUI.equipmentmanager.RefreshIconGrid()
EnsureProvider()
local numIcons = provider:GetNumIcons()
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
local offset = FauxScrollFrame_GetOffset(iconScroll)
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
local listIdx = i + offset * ICON_GRID_COLS
local btn = iconButtons[i]
if listIdx <= numIcons then
btn:Show()
btn.iconIndex = listIdx
local path = provider:GetIconByIndex(listIdx)
btn.texture:SetTexture(path)
if path == selectedIconPath then
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
else
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
end
else
btn:Hide()
btn.iconIndex = nil
end
end
-- Sync the "Currently Selected" preview from the path directly so it
-- still shows the chosen icon when filtered out of the grid.
namePopup.selectedPreview.tex:SetTexture(selectedIconPath)
end
iconScroll:SetScript("OnVerticalScroll", function()
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() pfUI.equipmentmanager.RefreshIconGrid() end)
end)
namePopup:SetScript("OnHide", function()
if provider then provider:Release(); provider = nil end
end)
local btnPopupOK = MakeButton("pfEqMgrPopupOK", T["OK"] or "OK", namePopup, namePopup, 14, -340, 80)
btnPopupOK:ClearAllPoints()
btnPopupOK:SetPoint("BOTTOMLEFT", namePopup, "BOTTOMLEFT", 14, 12)
local btnPopupCancel = MakeButton("pfEqMgrPopupCancel", T["Cancel"] or "Cancel", namePopup, namePopup, 0, 0, 80)
btnPopupCancel:ClearAllPoints() btnPopupCancel:ClearAllPoints()
btnPopupCancel:SetPoint("BOTTOMRIGHT", namePopup, "BOTTOMRIGHT", -14, 12) btnPopupCancel:SetPoint("BOTTOMRIGHT", namePopup, "BOTTOMRIGHT", -14, 12)
btnPopupCancel:SetScript("OnClick", function() namePopup:Hide() end) btnPopupCancel:SetScript("OnClick", function() namePopup:Hide() end)
-- Okay | Cancel, both anchored to the bottom-right corner.
btnPopupOK:ClearAllPoints()
btnPopupOK:SetPoint("BOTTOMRIGHT", btnPopupCancel, "BOTTOMLEFT", -6, 0)
btnPopupOK:SetScript("OnClick", function() btnPopupOK:SetScript("OnClick", function()
local name = namePopup.editbox:GetText() local name = namePopup.editbox:GetText()
if not name or name == "" then return end if not name or name == "" then return end
-- Strip the prefix to match ClassicAPI's persisted short-form basenames. -- Strip the prefix to match ClassicAPI's persisted short-form basenames.
local iconForSave = string.gsub(iconPicker.GetIcon(), "INTERFACE\\ICONS\\", "") local iconForSave = string.gsub(selectedIconPath, "INTERFACE\\ICONS\\", "")
if pendingAction == "new" then if pendingAction == "new" then
C_EquipmentSet.CreateEquipmentSet(name, iconForSave) C_EquipmentSet.CreateEquipmentSet(name, iconForSave)
C_EquipmentSet.ClearIgnoredSlotsForSave() C_EquipmentSet.ClearIgnoredSlotsForSave()
@@ -441,20 +610,31 @@ pfUI:RegisterModule("equipmentmanager", function()
function OpenNamePopup(action, prefillName, prefillIcon) function OpenNamePopup(action, prefillName, prefillIcon)
pendingAction = action pendingAction = action
namePopup.editbox:SetText(prefillName or "") namePopup.editbox:SetText(prefillName or "")
EnsureProvider()
if prefillIcon then if prefillIcon then
-- Stored icons come back either as a full path or a bare basename local short = string.gsub(prefillIcon, "INTERFACE\\ICONS\\", "")
-- (see the row rendering in Refresh). Only prepend the prefix when selectedIconPath = "INTERFACE\\ICONS\\" .. strupper(short)
-- there's no path separator, so a full path isn't double-prefixed.
iconPicker.SetIcon(string.find(prefillIcon, "\\") and prefillIcon
or ("INTERFACE\\ICONS\\" .. prefillIcon))
else else
iconPicker.SetIcon(nil) selectedIconPath = QUESTION_MARK
end
if action == "rename" then
namePopup.title:SetText(T["Rename Set"] or "Rename Set")
iconScroll:Hide()
for _, b in ipairs(iconButtons) do b:Hide() end
namePopup.iconLabel:Hide()
namePopup.selectedLabel:Hide()
namePopup.selectedPreview:Hide()
filterDropdown:Hide()
else
namePopup.title:SetText(action == "new" and (T["Name Set"] or "Name Set") or (T["Save Set"] or "Save Set"))
iconScroll:Show()
namePopup.iconLabel:Show()
namePopup.selectedLabel:Show()
namePopup.selectedPreview:Show()
filterDropdown:Show()
end end
namePopup.search:SetText("")
-- Rename only changes the name, so hide the whole icon-picking area.
iconPicker.SetIconAreaShown(action ~= "rename")
namePopup:Show() namePopup:Show()
if action ~= "rename" then iconPicker.Refresh() end if action ~= "rename" then pfUI.equipmentmanager.RefreshIconGrid() end
namePopup.editbox:SetFocus() namePopup.editbox:SetFocus()
end end
@@ -463,13 +643,9 @@ pfUI:RegisterModule("equipmentmanager", function()
-- ============================================================ -- ============================================================
local flyout = CreateFrame("Frame", "pfEqMgrFlyout", UIParent) local flyout = CreateFrame("Frame", "pfEqMgrFlyout", UIParent)
-- The name/icon popup is sidecar-only, so it closes with the sidecar.
-- Popout arrows and the flyout are reconciled by UpdatePopouts: by
-- default they follow the sidecar, but with the "Always Show Equipment
-- Slot Flyouts" option they stay while the paperdoll is open.
frame:SetScript("OnHide", function() frame:SetScript("OnHide", function()
namePopup:Hide() for _, b in ipairs(popoutButtons) do b:Hide() end
UpdatePopouts() flyout:Hide()
end) end)
flyout:SetFrameStrata("DIALOG") flyout:SetFrameStrata("DIALOG")
flyout:Hide() flyout:Hide()
@@ -500,7 +676,7 @@ pfUI:RegisterModule("equipmentmanager", function()
end end
end end
ClearCursor() ClearCursor()
UIErrorsFrame:AddMessage(ERR_EQUIPMENT_MANAGER_BAGS_FULL, 1, .1, .1, 1) UIErrorsFrame:AddMessage(EQUIPMENT_MANAGER_BAGS_FULL or "Your bags are full.", 1, .1, .1, 1)
end end
local function MakeFlyoutButton(i) local function MakeFlyoutButton(i)
@@ -516,11 +692,11 @@ pfUI:RegisterModule("equipmentmanager", function()
b:SetScript("OnEnter", function() b:SetScript("OnEnter", function()
GameTooltip:SetOwner(this, "ANCHOR_RIGHT") GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
if this.specialAction == "placeInBags" then if this.specialAction == "placeInBags" then
GameTooltip:SetText(EQUIPMENT_MANAGER_PLACE_IN_BAGS, 1, 1, 1) GameTooltip:SetText(EQUIPMENT_MANAGER_PLACE_IN_BAGS or "Place in Bags", 1, 1, 1)
elseif this.specialAction == "ignore" then elseif this.specialAction == "ignore" then
GameTooltip:SetText(EQUIPMENT_MANAGER_IGNORE_SLOT, 1, 1, 1) GameTooltip:SetText(EQUIPMENT_MANAGER_IGNORE_SLOT or "Ignore this slot", 1, 1, 1)
elseif this.specialAction == "unignore" then elseif this.specialAction == "unignore" then
GameTooltip:SetText(EQUIPMENT_MANAGER_UNIGNORE_SLOT, 1, 1, 1) GameTooltip:SetText(EQUIPMENT_MANAGER_UNIGNORE_SLOT or "Stop ignoring this slot", 1, 1, 1)
elseif this.bag then elseif this.bag then
GameTooltip:SetBagItem(this.bag, this.slot) GameTooltip:SetBagItem(this.bag, this.slot)
elseif this.invSlot then elseif this.invSlot then
@@ -528,7 +704,7 @@ pfUI:RegisterModule("equipmentmanager", function()
end end
GameTooltip:Show() GameTooltip:Show()
end) end)
b:SetScript("OnLeave", GameTooltip_Hide) b:SetScript("OnLeave", function() GameTooltip:Hide() end)
b:SetScript("OnClick", function() b:SetScript("OnClick", function()
if this.specialAction == "placeInBags" then if this.specialAction == "placeInBags" then
UnequipToBags(flyout.targetInvSlot) UnequipToBags(flyout.targetInvSlot)
@@ -596,7 +772,6 @@ 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")
@@ -613,23 +788,15 @@ 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, _, q = GetContainerItemInfo(loc.bag, loc.slot) local tex, count = 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()
@@ -642,6 +809,7 @@ pfUI:RegisterModule("equipmentmanager", function()
if num == 0 then if num == 0 then
flyout:Hide() flyout:Hide()
UIErrorsFrame:AddMessage(T["No matching items in bags"] or "No matching items in bags", 1, 1, 0, 1)
return return
end end
@@ -770,26 +938,15 @@ pfUI:RegisterModule("equipmentmanager", function()
end end
end end
-- Popout arrows follow the EM sidecar by default. With the -- Tie popout visibility to the EM sidecar. They appear when the
-- "Always Show Equipment Slot Flyouts" option they follow the paperdoll -- sidecar opens, hide when it closes, and the flyout closes too.
-- instead, so gear can be swapped without opening the equipment manager. frame:HookScript("OnShow", function()
local function PopoutsActive() for _, b in ipairs(popoutButtons) do b:Show() end
if C.character.inventory.equipflyout == "1" then end)
return PaperDollFrame:IsShown() frame:HookScript("OnHide", function()
end for _, b in ipairs(popoutButtons) do b:Hide() end
return frame:IsShown() if flyout then flyout:Hide() end
end end)
function UpdatePopouts()
local show = PopoutsActive()
for _, b in ipairs(popoutButtons) do
if show then b:Show() else b:Hide() end
end
if not show then flyout:Hide() end
end
PaperDollFrame:HookScript("OnShow", UpdatePopouts)
PaperDollFrame:HookScript("OnHide", UpdatePopouts)
-- ============================================================ -- ============================================================
-- Refresh -- Refresh
+1 -1
View File
@@ -136,7 +136,7 @@ pfUI:RegisterModule("farmmode", function ()
pfUI.farmmap.button.txt = pfUI.farmmap.button:CreateFontString("pfFarmMapText", "LOW", "GameFontWhite") pfUI.farmmap.button.txt = pfUI.farmmap.button:CreateFontString("pfFarmMapText", "LOW", "GameFontWhite")
pfUI.farmmap.button.txt:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE") pfUI.farmmap.button.txt:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
pfUI.farmmap.button.txt:SetPoint("CENTER", 0, 0) pfUI.farmmap.button.txt:SetPoint("CENTER", 0, 0)
pfUI.farmmap.button.txt:SetText("|cff33ffcc " .. T["FARM MODE"]) pfUI.farmmap.button.txt:SetText("|cff33ffcc FARM MODE")
end end
CreateBackdrop(pfUI.farmmap) CreateBackdrop(pfUI.farmmap)
+14 -9
View File
@@ -127,7 +127,7 @@ pfUI:RegisterModule("firstrun", function ()
-- welcome dialog -- welcome dialog
pfUI.firstrun:AddStep("init", function() pfUI.firstrun:AddStep("init", function()
local f = CreateFirstRunPage() local f = CreateFirstRunPage()
f.text:SetFormattedText(T["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."], GetAddOnMetadata(pfUI.name, "X-Website")) f.text:SetText(string.format(T["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."], GetAddOnMetadata(pfUI.name, "X-Website")))
return f return f
end) end)
@@ -137,7 +137,8 @@ pfUI:RegisterModule("firstrun", function ()
f.text:SetText(T["A new installation of |cff33ffccpf|rUI ships with 4 prebuilt design profiles. Click below if you wish to load one of these profiles."]) f.text:SetText(T["A new installation of |cff33ffccpf|rUI ships with 4 prebuilt design profiles. Click below if you wish to load one of these profiles."])
f.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Modern:SetSize(120, 20) f.Modern:SetWidth(120)
f.Modern:SetHeight(20)
f.Modern:SetPoint("BOTTOM", -65, 100) f.Modern:SetPoint("BOTTOM", -65, 100)
f.Modern:SetTextColor(1,1,1) f.Modern:SetTextColor(1,1,1)
f.Modern:SetText("Modern") f.Modern:SetText("Modern")
@@ -150,7 +151,8 @@ pfUI:RegisterModule("firstrun", function ()
SkinButton(f.Modern) SkinButton(f.Modern)
f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Nostalgia:SetSize(120, 20) f.Nostalgia:SetWidth(120)
f.Nostalgia:SetHeight(20)
f.Nostalgia:SetPoint("BOTTOM", 65, 100) f.Nostalgia:SetPoint("BOTTOM", 65, 100)
f.Nostalgia:SetTextColor(1,1,1) f.Nostalgia:SetTextColor(1,1,1)
f.Nostalgia:SetText("Nostalgia") f.Nostalgia:SetText("Nostalgia")
@@ -163,7 +165,8 @@ pfUI:RegisterModule("firstrun", function ()
SkinButton(f.Nostalgia) SkinButton(f.Nostalgia)
f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Legacy:SetSize(120, 20) f.Legacy:SetWidth(120)
f.Legacy:SetHeight(20)
f.Legacy:SetPoint("BOTTOM", 65, 75) f.Legacy:SetPoint("BOTTOM", 65, 75)
f.Legacy:SetTextColor(1,1,1) f.Legacy:SetTextColor(1,1,1)
f.Legacy:SetText("Legacy") f.Legacy:SetText("Legacy")
@@ -176,7 +179,8 @@ pfUI:RegisterModule("firstrun", function ()
SkinButton(f.Legacy) SkinButton(f.Legacy)
f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
f.Slim:SetSize(120, 20) f.Slim:SetWidth(120)
f.Slim:SetHeight(20)
f.Slim:SetPoint("BOTTOM", -65, 75) f.Slim:SetPoint("BOTTOM", -65, 75)
f.Slim:SetTextColor(1,1,1) f.Slim:SetTextColor(1,1,1)
f.Slim:SetText("Slim") f.Slim:SetText("Slim")
@@ -193,7 +197,8 @@ pfUI:RegisterModule("firstrun", function ()
f.Slider.text:SetPoint("TOP", f.Slider, "BOTTOM", 0, 2) f.Slider.text:SetPoint("TOP", f.Slider, "BOTTOM", 0, 2)
f.Slider.text:SetText(T["Scale"]) f.Slider.text:SetText(T["Scale"])
f.Slider:SetSize(240, 20) f.Slider:SetWidth(240)
f.Slider:SetHeight(20)
f.Slider:SetPoint("BOTTOM", 0, 50) f.Slider:SetPoint("BOTTOM", 0, 50)
f.Slider:SetOrientation('HORIZONTAL') f.Slider:SetOrientation('HORIZONTAL')
f.Slider:SetMinMaxValues(0.5, 2.0) f.Slider:SetMinMaxValues(0.5, 2.0)
@@ -258,7 +263,7 @@ pfUI:RegisterModule("firstrun", function ()
SkinCheckbox(f.checkbox, 18) SkinCheckbox(f.checkbox, 18)
f.NextScript = function() f.NextScript = function()
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") return end if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") end
pfUI.chat.SetupRightChat(f.checkbox:GetChecked()) pfUI.chat.SetupRightChat(f.checkbox:GetChecked())
end end
@@ -278,7 +283,7 @@ pfUI:RegisterModule("firstrun", function ()
SkinCheckbox(f.checkbox, 18) SkinCheckbox(f.checkbox, 18)
f.NextScript = function() f.NextScript = function()
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") return end if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") end
if f.checkbox:GetChecked() then if f.checkbox:GetChecked() then
pfUI.chat.SetupPositions() pfUI.chat.SetupPositions()
end end
@@ -300,7 +305,7 @@ pfUI:RegisterModule("firstrun", function ()
SkinCheckbox(f.checkbox, 18) SkinCheckbox(f.checkbox, 18)
f.NextScript = function() f.NextScript = function()
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") return end if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") end
if f.checkbox:GetChecked() then if f.checkbox:GetChecked() then
pfUI.chat.SetupChannels() pfUI.chat.SetupChannels()
end end
-93
View File
@@ -1,93 +0,0 @@
-- Friend notes
-- Client-side notes for friends via ClassicAPI's C_FriendList note API
-- (SetFriendNotes / GetFriendInfo(.notes)). Adds an "Edit Note" entry to the
-- friend right-click menu (the "FRIEND" UnitPopup) and shows the note in a
-- tooltip when you hover a friend in the list.
pfUI:RegisterModule("friendnotes", function ()
local EDIT_TOKEN = "PFUI_FRIEND_NOTE"
-- Note editor. In 1.12 StaticPopup, OnAccept runs with `this` = the OK
-- button (the dialog is this:GetParent()); the friend name arrives as the
-- data argument passed to StaticPopup_Show.
StaticPopupDialogs["PFUI_FRIEND_NOTE_EDIT"] = {
text = SET_FRIENDNOTE_LABEL,
button1 = SAVE,
button2 = CANCEL,
hasEditBox = 1,
maxLetters = 128,
OnAccept = function(name)
C_FriendList.SetFriendNotes(name, _G[this:GetParent():GetName().."EditBox"]:GetText())
end,
EditBoxOnEnterPressed = function(name)
C_FriendList.SetFriendNotes(name, this:GetText())
this:GetParent():Hide()
end,
EditBoxOnEscapePressed = function() this:GetParent():Hide() end,
timeout = 0, whileDead = 1, hideOnEscape = 1,
}
local function OpenNoteEditor(name)
if not name or name == "" then return end
local info = C_FriendList.GetFriendInfo(name)
local dialog = StaticPopup_Show("PFUI_FRIEND_NOTE_EDIT", name, nil, name)
if not dialog then return end
local editbox = _G[dialog:GetName().."EditBox"]
editbox:SetText((info and info.notes) or "")
editbox:HighlightText()
editbox:SetFocus()
end
-- Add "Edit Note" to the friend right-click menu, just before Cancel.
UnitPopupButtons[EDIT_TOKEN] = { text = SET_NOTE, dist = 0 }
local friendMenu = UnitPopupMenus["FRIEND"]
table.insert(friendMenu, table.getn(friendMenu), EDIT_TOKEN)
UnitPopupMenus["PFUI_FRIENDNOTE"] = { EDIT_TOKEN, "CANCEL" }
local function OfflineNoteDropDown_Initialize()
UnitPopup_ShowMenu(_G[UIDROPDOWNMENU_OPEN_MENU], "PFUI_FRIENDNOTE", nil, FriendsDropDown.name)
end
local FriendsFrame_ShowDropdown_orig = FriendsFrame_ShowDropdown
_G.FriendsFrame_ShowDropdown = function(name, connected)
if connected then return FriendsFrame_ShowDropdown_orig(name, connected) end
HideDropDownMenu(1)
FriendsDropDown.initialize = OfflineNoteDropDown_Initialize
FriendsDropDown.displayMode = "MENU"
FriendsDropDown.name = name
ToggleDropDownMenu(1, nil, FriendsDropDown, "cursor")
end
-- Route our entry to the editor and close the menu; delegate the rest. A
-- bare assignment lands on pfUI.env, so set the global explicitly.
local UnitPopup_OnClick_orig = UnitPopup_OnClick
_G.UnitPopup_OnClick = function()
if this and this.value == EDIT_TOKEN then
local dropdown = _G[UIDROPDOWNMENU_INIT_MENU]
local name = dropdown and dropdown.name
CloseDropDownMenus()
OpenNoteEditor(name)
return
end
return UnitPopup_OnClick_orig()
end
-- Show the note in a tooltip while hovering a friend in the list. Friend
-- buttons carry their friend index via SetID (see FriendsFrame_Update).
local function FriendButton_OnEnter()
local index = this:GetID()
if not index or index < 1 then return end
local info = C_FriendList.GetFriendInfoByIndex(index)
if not info or not info.notes or info.notes == "" then return end
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(info.name or "")
GameTooltip:AddLine(info.notes, 1, 1, 1, 1)
GameTooltip:Show()
end
for i = 1, FRIENDS_TO_DISPLAY do
local button = _G["FriendsFrameFriendButton"..i]
if button then
button:HookScript("OnEnter", FriendButton_OnEnter)
button:HookScript("OnLeave", GameTooltip_Hide)
end
end
end)
+19 -44
View File
@@ -58,11 +58,10 @@ pfUI:RegisterModule("gui", function ()
return return
end end
local over = MouseIsOverHitRect(this) if MouseIsOver(this) and not this.over then
if over and not this.over then
this.tex:Show() this.tex:Show()
this.over = true this.over = true
elseif not over and this.over then elseif not MouseIsOver(this) and this.over then
this.tex:Hide() this.tex:Hide()
this.over = nil this.over = nil
end end
@@ -2073,6 +2072,9 @@ 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")
@@ -2238,6 +2240,7 @@ pfUI:RegisterModule("gui", function ()
-- build config entries -- build config entries
CreateConfig(U[c], T["Display Frame"] .. ": " .. t, C.unitframes[c], "visible", "checkbox") CreateConfig(U[c], T["Display Frame"] .. ": " .. t, C.unitframes[c], "visible", "checkbox")
CreateConfig(U[c], T["Enable Mouseover Tooltip"], C.unitframes[c], "showtooltip", "checkbox")
CreateConfig(U[c], T["Default Transparency"], C.unitframes[c], "alpha_visible", "dropdown", pfUI.gui.dropdowns.percent_small) CreateConfig(U[c], T["Default Transparency"], C.unitframes[c], "alpha_visible", "dropdown", pfUI.gui.dropdowns.percent_small)
CreateConfig(U[c], T["Out Of Range Transparency"], C.unitframes[c], "alpha_outrange", "dropdown", pfUI.gui.dropdowns.percent_small) CreateConfig(U[c], T["Out Of Range Transparency"], C.unitframes[c], "alpha_outrange", "dropdown", pfUI.gui.dropdowns.percent_small)
CreateConfig(U[c], T["Offline Transparency"], C.unitframes[c], "alpha_offline", "dropdown", pfUI.gui.dropdowns.percent_small) CreateConfig(U[c], T["Offline Transparency"], C.unitframes[c], "alpha_offline", "dropdown", pfUI.gui.dropdowns.percent_small)
@@ -2318,15 +2321,6 @@ 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)
@@ -2347,18 +2341,12 @@ 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
@@ -2424,7 +2412,6 @@ pfUI:RegisterModule("gui", function ()
CreateGUIEntry(T["Character"], T["Inventory"], function() CreateGUIEntry(T["Character"], T["Inventory"], function()
CreateConfig(nil, T["Show Durability"], C.character.inventory, "durability", "checkbox") CreateConfig(nil, T["Show Durability"], C.character.inventory, "durability", "checkbox")
CreateConfig(nil, T["Always Show Equipment Slot Flyouts"], C.character.inventory, "equipflyout", "checkbox")
end) end)
CreateGUIEntry(T["Character"], T["Reputation"], function() CreateGUIEntry(T["Character"], T["Reputation"], function()
@@ -2569,6 +2556,9 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["bars"], T["Button Animation"], C.bars, "animation", "dropdown", pfUI.gui.dropdowns.actionbuttonanimations) CreateConfig(U["bars"], T["Button Animation"], C.bars, "animation", "dropdown", pfUI.gui.dropdowns.actionbuttonanimations)
CreateConfig(U["bars"], T["Button Animation Trigger"], C.bars, "animmode", "dropdown", pfUI.gui.dropdowns.animationmode) CreateConfig(U["bars"], T["Button Animation Trigger"], C.bars, "animmode", "dropdown", pfUI.gui.dropdowns.animationmode)
CreateConfig(U["bars"], T["Show Animation On Hidden Bars"], C.bars, "animalways", "checkbox") CreateConfig(U["bars"], T["Show Animation On Hidden Bars"], C.bars, "animalways", "checkbox")
if not pfUI:MacroAddonsLoaded() then
CreateConfig(U["bars"], T["Scan Macros For Spells"], C.bars, "macroscan", "checkbox", nil, nil, nil, nil)
end
CreateConfig(U["bars"], T["Show Reagent Count"], C.bars, "reagents", "checkbox") CreateConfig(U["bars"], T["Show Reagent Count"], C.bars, "reagents", "checkbox")
CreateConfig(U["bars"], T["Highlight Equipped Items"], C.bars, "showequipped", "checkbox") CreateConfig(U["bars"], T["Highlight Equipped Items"], C.bars, "showequipped", "checkbox")
CreateConfig(U["bars"], T["Equipped Item Color"], C.bars, "eqcolor", "color") CreateConfig(U["bars"], T["Equipped Item Color"], C.bars, "eqcolor", "color")
@@ -2773,40 +2763,27 @@ pfUI:RegisterModule("gui", function ()
end) end)
CreateGUIEntry(T["Tooltip"], nil, function() CreateGUIEntry(T["Tooltip"], nil, function()
CreateConfig(nil, T["General"], nil, nil, "header")
CreateConfig(nil, T["Tooltip Position"], C.tooltip, "position", "dropdown", pfUI.gui.dropdowns.tooltip_position) CreateConfig(nil, T["Tooltip Position"], C.tooltip, "position", "dropdown", pfUI.gui.dropdowns.tooltip_position)
CreateConfig(nil, T["Tooltip Text Font"], C.tooltip, "font_tooltip", "dropdown", pfUI.gui.dropdowns.fonts) CreateConfig(nil, T["Tooltip Text Font"], C.tooltip, "font_tooltip", "dropdown", pfUI.gui.dropdowns.fonts)
CreateConfig(nil, T["Tooltip Text Font Size"], C.tooltip, "font_tooltip_size") CreateConfig(nil, T["Tooltip Text Font Size"], C.tooltip, "font_tooltip_size")
CreateConfig(nil, T["Cursor Tooltip Align"], C.tooltip, "cursoralign", "dropdown", pfUI.gui.dropdowns.tooltip_align) CreateConfig(nil, T["Cursor Tooltip Align"], C.tooltip, "cursoralign", "dropdown", pfUI.gui.dropdowns.tooltip_align)
CreateConfig(nil, T["Cursor Tooltip Offset"], C.tooltip, "cursoroffset") CreateConfig(nil, T["Cursor Tooltip Offset"], C.tooltip, "cursoroffset")
CreateConfig(nil, T["Enable Extended Guild Information"], C.tooltip, "extguild", "checkbox")
CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox")
CreateConfig(nil, T["Show Item IDs"], C.tooltip, "itemid", "checkbox")
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha") CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha")
CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture) CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, T["Items"], nil, nil, "header")
CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox") CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox")
CreateConfig(nil, T["Always Show Extended Vendor Values"], C.tooltip.vendor, "showalways", "checkbox") CreateConfig(nil, T["Always Show Extended Vendor Values"], C.tooltip.vendor, "showalways", "checkbox")
CreateConfig(U["questitem"], T["Show Related Quest On Questitems"], C.tooltip.questitem, "showquest", "checkbox") CreateConfig(U["questitem"], T["Show Related Quest On Questitems"], C.tooltip.questitem, "showquest", "checkbox")
CreateConfig(U["questitem"], T["Show Required Questitem Count"], C.tooltip.questitem, "showcount", "checkbox") CreateConfig(U["questitem"], T["Show Required Questitem Count"], C.tooltip.questitem, "showcount", "checkbox")
CreateConfig(nil, T["Show Item IDs"], C.tooltip, "itemid", "checkbox")
CreateConfig(nil, T["Spells"], nil, nil, "header")
CreateConfig(nil, T["Show Aura Caster"], C.tooltip, "aurasource", "checkbox")
CreateConfig(nil, T["Show Spell IDs"], C.tooltip, "spellid", "checkbox")
CreateConfig(nil, T["Units"], nil, nil, "header")
CreateConfig(nil, T["Enable Extended Guild Information"], C.tooltip, "extguild", "checkbox")
CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox")
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
CreateConfig(nil, T["Show Unit Buffs"], C.tooltip, "showbuffs", "checkbox")
CreateConfig(nil, T["Show Unit IDs"], C.tooltip, "unitid", "checkbox")
end) end)
CreateGUIEntry(T["Castbar"], T["General"], function() CreateGUIEntry(T["Castbar"], T["General"], 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)
@@ -2879,10 +2856,8 @@ 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")
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")
@@ -2894,9 +2869,9 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Only Show Chat Dock On Mouseover"], C.chat.global, "tabmouse", "checkbox") CreateConfig(nil, T["Only Show Chat Dock On Mouseover"], C.chat.global, "tabmouse", "checkbox")
CreateConfig(nil, T["Enable Chat Tab Flashing"], C.chat.global, "chatflash", "checkbox") CreateConfig(nil, T["Enable Chat Tab Flashing"], C.chat.global, "chatflash", "checkbox")
CreateConfig(nil, T["Enable Frame Shadow"], C.chat.global, "frameshadow", "checkbox") CreateConfig(nil, T["Enable Frame Shadow"], C.chat.global, "frameshadow", "checkbox")
CreateConfig(U["chat"], T["Enable Custom Colors"], C.chat.global, "custombg", "checkbox") CreateConfig(nil, T["Enable Custom Colors"], C.chat.global, "custombg", "checkbox")
CreateConfig(U["chat"], T["Chat Background Color"], C.chat.global, "background", "color") CreateConfig(nil, T["Chat Background Color"], C.chat.global, "background", "color")
CreateConfig(U["chat"], T["Chat Border Color"], C.chat.global, "border", "color") CreateConfig(nil, T["Chat Border Color"], C.chat.global, "border", "color")
CreateConfig(nil, T["Enable Custom Incoming Whispers Layout"], C.chat.global, "whispermod", "checkbox") CreateConfig(nil, T["Enable Custom Incoming Whispers Layout"], C.chat.global, "whispermod", "checkbox")
CreateConfig(nil, T["Incoming Whispers Color"], C.chat.global, "whisper", "color") CreateConfig(nil, T["Incoming Whispers Color"], C.chat.global, "whisper", "color")
CreateConfig(nil, T["Enable Sticky Chat"], C.chat.global, "sticky", "checkbox") CreateConfig(nil, T["Enable Sticky Chat"], C.chat.global, "sticky", "checkbox")
@@ -3013,7 +2988,6 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Use Chat Colors for Meters"], C.thirdparty, "chatbg", "checkbox") CreateConfig(nil, T["Use Chat Colors for Meters"], C.thirdparty, "chatbg", "checkbox")
CreateConfig(nil, "ShaguDPS (" .. T["Skin"] .. ")", C.thirdparty.shagudps, "skin", "checkbox") CreateConfig(nil, "ShaguDPS (" .. T["Skin"] .. ")", C.thirdparty.shagudps, "skin", "checkbox")
CreateConfig(nil, "ShaguDPS (" .. T["Dock"] .. ")", C.thirdparty.shagudps, "dock", "checkbox") CreateConfig(nil, "ShaguDPS (" .. T["Dock"] .. ")", C.thirdparty.shagudps, "dock", "checkbox")
CreateConfig(nil, "GreedMeter (" .. T["Dock"] .. ")", C.thirdparty.greedmeter, "dock", "checkbox")
CreateConfig(nil, "DPSMate (" .. T["Skin"] .. ")", C.thirdparty.dpsmate, "skin", "checkbox") CreateConfig(nil, "DPSMate (" .. T["Skin"] .. ")", C.thirdparty.dpsmate, "skin", "checkbox")
CreateConfig(nil, "DPSMate (" .. T["Dock"] .. ")", C.thirdparty.dpsmate, "dock", "checkbox") CreateConfig(nil, "DPSMate (" .. T["Dock"] .. ")", C.thirdparty.dpsmate, "dock", "checkbox")
CreateConfig(nil, "SWStats (" .. T["Skin"] .. ")", C.thirdparty.swstats, "skin", "checkbox") CreateConfig(nil, "SWStats (" .. T["Skin"] .. ")", C.thirdparty.swstats, "skin", "checkbox")
@@ -3042,7 +3016,8 @@ pfUI:RegisterModule("gui", function ()
CreateGUIEntry(T["Components"], T["Modules"], function() CreateGUIEntry(T["Components"], T["Modules"], function()
table.sort(pfUI.modules) table.sort(pfUI.modules)
for i,m in pairs(pfUI.modules) do for i,m in pairs(pfUI.modules) do
if m ~= "gui" then -- skip gui and macrotweak when macro addons are loaded
if m ~= "gui" and not (m == "macrotweak" and pfUI:MacroAddonsLoaded()) then
-- create disabled entry if not existing and display -- create disabled entry if not existing and display
pfUI:UpdateConfig("disabled", nil, m, "0") pfUI:UpdateConfig("disabled", nil, m, "0")
CreateConfig(nil, T["Disable Module"] .. " " .. m, C.disabled, m, "checkbox") CreateConfig(nil, T["Disable Module"] .. " " .. m, C.disabled, m, "checkbox")
+5 -21
View File
@@ -105,10 +105,6 @@ 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 ""),
@@ -161,29 +157,17 @@ 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 all existing bindings -- Remove existing binding
local keys = { GetBindingKey(binding) } local key = (GetBindingKey(binding))
for _, key in ipairs(keys) do if (key) then
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 = prefix .. (map and map[arg1] or arg1) local key = map and map[arg1] or arg1
local previous = GetBindingAction(key) if (SetBinding(prefix..key, binding)) then
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
+9 -34
View File
@@ -1,8 +1,10 @@
pfUI:RegisterModule("hunterbar", function () pfUI:RegisterModule("hunterbar", function ()
if UnitClassBase("player") ~= "HUNTER" or C.bars.hunterbar == "0" then return end local class = UnitClassBase("player")
if class ~= "HUNTER" or C.bars.hunterbar == "0" then return end
-- Wing Clip (any rank) and Arcane Shot (any rank) spell IDs. -- Wing Clip (any rank) and Arcane Shot (any rank) spell IDs.
-- C_Spell.IsSpellInRange works with any spell ID, no actionbar slot needed. -- IsSpellInRange(spellId) works with any spell ID via Nampower,
-- no actionbar slot needed.
local WINGCLIP_ID = 2974 -- melee range indicator (~5 yd) local WINGCLIP_ID = 2974 -- melee range indicator (~5 yd)
local ARCANESHOT_ID = 3044 -- ranged range indicator (~35 yd) local ARCANESHOT_ID = 3044 -- ranged range indicator (~35 yd)
@@ -10,37 +12,22 @@ pfUI:RegisterModule("hunterbar", function ()
-- Only swap BACK to melee bar when Wing Clip is actually in range. -- Only swap BACK to melee bar when Wing Clip is actually in range.
-- This prevents rapid bar-flipping in the transition zone. -- This prevents rapid bar-flipping in the transition zone.
local THROTTLE = 0.02 -- seconds between range checks (was every frame)
pfUI.hunterbar = CreateFrame("Frame", "pfHunterBar", UIParent) pfUI.hunterbar = CreateFrame("Frame", "pfHunterBar", UIParent)
-- track which page we last forced so we don't spam ChangeActionBarPage() -- track which page we last forced so we don't spam ChangeActionBarPage()
pfUI.hunterbar.lastPage = nil pfUI.hunterbar.lastPage = nil
pfUI.hunterbar.elapsed = 0
-- Idle until there's a live, attackable target. A hidden frame runs no
-- OnUpdate, so the range polling only ticks while it can matter.
pfUI.hunterbar:Hide()
pfUI.hunterbar:SetScript("OnUpdate", function() pfUI.hunterbar:SetScript("OnUpdate", function()
this.elapsed = this.elapsed + arg1 -- only act when there is a live, attackable target
if this.elapsed < THROTTLE then return end
this.elapsed = 0
-- Target died or turned unattackable without a target-change event
-- (it dropped to a corpse); stop ticking until the next target.
if not UnitExists("target") or not UnitCanAttack("player", "target") then if not UnitExists("target") or not UnitCanAttack("player", "target") then
this:Hide()
return return
end end
-- C_Spell.IsSpellInRange returns true / false / nil (rangeless). The local wingclipInRange = IsSpellInRange(WINGCLIP_ID, "target")
-- explicit == true / == false tests leave the bar unchanged on nil. local arcaneshotInRange = IsSpellInRange(ARCANESHOT_ID, "target")
local wingclipInRange = C_Spell.IsSpellInRange(WINGCLIP_ID, "target")
local arcaneshotInRange = C_Spell.IsSpellInRange(ARCANESHOT_ID, "target")
-- swap to ranged bar: out of melee range AND arcane shot (8yd) in range -- swap to ranged bar: out of melee range AND arcane shot (8yd) in range
if wingclipInRange == false and arcaneshotInRange == true then if wingclipInRange == 0 and arcaneshotInRange == 1 then
if this.lastPage ~= 2 then if this.lastPage ~= 2 then
this.lastPage = 2 this.lastPage = 2
_G.CURRENT_ACTIONBAR_PAGE = 2 _G.CURRENT_ACTIONBAR_PAGE = 2
@@ -48,7 +35,7 @@ pfUI:RegisterModule("hunterbar", function ()
end end
-- swap to melee bar: in melee range AND arcane shot (8yd) out of range -- swap to melee bar: in melee range AND arcane shot (8yd) out of range
elseif wingclipInRange == true and arcaneshotInRange == false then elseif wingclipInRange == 1 and arcaneshotInRange == 0 then
if this.lastPage ~= 1 then if this.lastPage ~= 1 then
this.lastPage = 1 this.lastPage = 1
_G.CURRENT_ACTIONBAR_PAGE = 1 _G.CURRENT_ACTIONBAR_PAGE = 1
@@ -56,16 +43,4 @@ pfUI:RegisterModule("hunterbar", function ()
end end
end end
end) end)
-- Start ticking only when we gain an attackable target.
pfUI.hunterbar:RegisterEvent("PLAYER_TARGET_CHANGED")
pfUI.hunterbar:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.hunterbar:SetScript("OnEvent", function()
if UnitExists("target") and UnitCanAttack("player", "target") then
this.elapsed = THROTTLE -- run the first check on the next frame
this:Show()
else
this:Hide()
end
end)
end) end)
+53 -33
View File
@@ -1,13 +1,25 @@
-- Innervate Callout Module -- Innervate Callout Module
-- Announces Innervate casts via raid/party/battleground chat -- Announces Innervate casts via raid/party/battleground chat
-- Listens to COMBAT_LOG_EVENT_UNFILTERED - zero polling, pure event-driven -- Registers AURA_CAST events directly - 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()
@@ -27,49 +39,57 @@ pfUI:RegisterModule("innervatecall", function ()
return nil return nil
end end
-- Set on our cast, cleared once the "ready" timer is scheduled -- Event frame - registers AURA_CAST directly (bypasses libdebuff hooks
local pending = false -- which are gated behind ownDebuffs/pendingCasts checks meant for debuffs)
-- Schedule the "ready" announcement once Innervate's own cooldown is in
-- the spell history. Until then GetSpellCooldown reports only the GCD,
-- which has no isOnGCD flag here, so tell them apart by duration.
local function ScheduleReady()
local cd = C_Spell.GetSpellCooldown(INNERVATE_SPELLID)
if not cd or cd.duration <= GCD_DURATION then return end
pending = false
C_Timer.After(cd.startTime + cd.duration - GetTime(), function()
local ch = GetAnnounceChannel()
if ch then
SendChatMessage(">> Innervate is ready <<", ch)
end
end)
end
local frame = CreateFrame("Frame") local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") -- AURA_CAST_ON_SELF fires when a buff lands ON the player
frame:RegisterEvent("SPELL_UPDATE_COOLDOWN") -- 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() frame:SetScript("OnEvent", function()
if event == "SPELL_UPDATE_COOLDOWN" then if event == "PLAYER_LOGOUT" then
if pending then ScheduleReady() end this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
return return
end end
-- Innervate is instant, so it logs SPELL_CAST_SUCCESS with its target -- AURA_CAST args: arg1=spellId, arg2=casterGuid, arg3=targetGuid
-- arg2=subevent, arg4=sourceGUID, arg9=destName, arg12=spellId local spellId = arg1
if arg2 ~= "SPELL_CAST_SUCCESS" or arg12 ~= INNERVATE_SPELLID then return end local casterGuid = arg2
local targetGuid = arg3
if spellId ~= INNERVATE_SPELLID then return end
-- Only announce our own casts -- Only announce our own casts
if not IsPlayerGuid(arg4) then return end if not IsPlayerGuid(casterGuid) then return end
local destName = arg9
-- Resolve target name from GUID
local targetName = ResolveTargetName(targetGuid) or "Unknown"
-- Determine channel -- Determine channel
local channel = GetAnnounceChannel() local channel = GetAnnounceChannel()
if not channel then return end -- solo, no announcement if not channel then return end -- solo, no announcement
SendChatMessage(">> Innervate cast on " .. (destName or "Unknown") .. " <<", channel) SendChatMessage(">> Innervate casted on " .. targetName .. " <<", channel)
pending = true -- Schedule "ready" announcement when cooldown expires
ScheduleReady() -- Use GetSpellIdCooldown for precise remaining time, fallback to 360s
local cdRemaining = 360
if GetSpellIdCooldown then
local cd = GetSpellIdCooldown(INNERVATE_SPELLID)
if cd and cd.cooldownRemainingMs and cd.cooldownRemainingMs > 0 then
cdRemaining = cd.cooldownRemainingMs / 1000
end
end
C_Timer.After(cdRemaining, function()
local ch = GetAnnounceChannel()
if ch then
SendChatMessage(">> Innervate is ready <<", ch)
end
end)
end) end)
end) end)
+4 -8
View File
@@ -26,14 +26,10 @@ pfUI:RegisterModule("itemcount", function ()
if bank < 0 then bank = 0 end if bank < 0 then bank = 0 end
frame:AddLine(" ") frame:AddLine(" ")
if bags > 0 then frame:AddDoubleLine(T["Bags"] .. ":", bags, 1, 1, 1, 1, 1, 1) end if bags > 0 then frame:AddDoubleLine("Bags:", bags, 1, 1, 1, 1, 1, 1) end
if bank > 0 then frame:AddDoubleLine(T["Bank"] .. ":", bank, 1, 1, 1, 1, 1, 1) end if bank > 0 then frame:AddDoubleLine("Bank:", bank, 1, 1, 1, 1, 1, 1) end
if equipped > 0 then frame:AddDoubleLine(T["Equipped"] .. ":", equipped, 1, 1, 1, 1, 1, 1) end if equipped > 0 then frame:AddDoubleLine("Equipped:", equipped, 1, 1, 1, 1, 1, 1) end
frame:AddDoubleLine("Total:", total, 1, 1, 1, 1, 1, 1)
local sources = (bags > 0 and 1 or 0) + (bank > 0 and 1 or 0) + (equipped > 0 and 1 or 0)
if sources > 1 then
frame:AddDoubleLine(T["Total"] .. ":", total, 1, 1, 1, 1, 1, 1)
end
frame:Show() frame:Show()
end end
+16 -6
View File
@@ -77,7 +77,9 @@ pfUI:RegisterModule("loot", function ()
if pfUI.loot.my_index or pfUI.loot.disenchanter_index or pfUI.loot.banker_index then if pfUI.loot.my_index or pfUI.loot.disenchanter_index or pfUI.loot.banker_index then
info = wipe(info) info = wipe(info)
info.text = T["Special Recipient"] info.text = T["Special Recipient"]
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB() info.textR = NORMAL_FONT_COLOR.r
info.textG = NORMAL_FONT_COLOR.g
info.textB = NORMAL_FONT_COLOR.b
info.textHeight = 12 info.textHeight = 12
info.hasArrow = 1 info.hasArrow = 1
info.notCheckable = 1 info.notCheckable = 1
@@ -156,7 +158,9 @@ pfUI:RegisterModule("loot", function ()
if level == 1 then if level == 1 then
info = wipe(info) info = wipe(info)
info.text = T["Random"] info.text = T["Random"]
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB() info.textR = NORMAL_FONT_COLOR.r
info.textG = NORMAL_FONT_COLOR.g
info.textB = NORMAL_FONT_COLOR.b
info.value = "PFRANDOM" info.value = "PFRANDOM"
info.textHeight = 12 info.textHeight = 12
info.notCheckable = 1 info.notCheckable = 1
@@ -166,7 +170,9 @@ pfUI:RegisterModule("loot", function ()
info = wipe(info) info = wipe(info)
info.text = T["Request Rolls"] info.text = T["Request Rolls"]
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB() info.textR = NORMAL_FONT_COLOR.r
info.textG = NORMAL_FONT_COLOR.g
info.textB = NORMAL_FONT_COLOR.b
info.value = "PFROLLS" info.value = "PFROLLS"
info.textHeight = 12 info.textHeight = 12
info.hasArrow = 1 info.hasArrow = 1
@@ -177,7 +183,9 @@ pfUI:RegisterModule("loot", function ()
if UIDROPDOWNMENU_MENU_VALUE == "PFROLLS" then if UIDROPDOWNMENU_MENU_VALUE == "PFROLLS" then
info = wipe(info) info = wipe(info)
info.text = T["Clear Rolls"] info.text = T["Clear Rolls"]
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB() info.textR = NORMAL_FONT_COLOR.r
info.textG = NORMAL_FONT_COLOR.g
info.textB = NORMAL_FONT_COLOR.b
info.value = "PFCLEARROLLS" info.value = "PFCLEARROLLS"
info.notCheckable = 1 info.notCheckable = 1
info.func = pfUI.loot.ClearRolls info.func = pfUI.loot.ClearRolls
@@ -185,7 +193,9 @@ pfUI:RegisterModule("loot", function ()
info = wipe(info) info = wipe(info)
info.text = T["Reroll Ties"] info.text = T["Reroll Ties"]
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB() info.textR = NORMAL_FONT_COLOR.r
info.textG = NORMAL_FONT_COLOR.g
info.textB = NORMAL_FONT_COLOR.b
info.value = "PFTIEROLL" info.value = "PFTIEROLL"
info.notCheckable = 1 info.notCheckable = 1
info.arg1 = pfUI.loot.rollers_sorted info.arg1 = pfUI.loot.rollers_sorted
@@ -511,7 +521,7 @@ pfUI:RegisterModule("loot", function ()
end end
function pfUI.loot:CreateSlot(id) function pfUI.loot:CreateSlot(id)
local frame = CreateFrame("LootButton", 'pfLootButton'..id, pfUI.loot) local frame = CreateFrame(LOOT_BUTTON_FRAME_TYPE, 'pfLootButton'..id, pfUI.loot)
frame:RegisterForClicks('LeftButtonUp', 'RightButtonUp') frame:RegisterForClicks('LeftButtonUp', 'RightButtonUp')
frame:SetPoint("LEFT", border*2, 0) frame:SetPoint("LEFT", border*2, 0)
frame:SetPoint("RIGHT", -border*2, 0) frame:SetPoint("RIGHT", -border*2, 0)
+25 -8
View File
@@ -16,6 +16,12 @@ pfUI:RegisterModule("loothistory", function ()
local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check" local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check"
local QUESTIONMARK = "Interface\\Icons\\INV_Misc_QuestionMark" local QUESTIONMARK = "Interface\\Icons\\INV_Misc_QuestionMark"
local function ClassColor(class)
local c = class and RAID_CLASS_COLORS[class]
if c then return c.r, c.g, c.b end
return 1, 1, 1
end
-- Paint an item row's icon/name/quality from a loaded Item mixin. -- Paint an item row's icon/name/quality from a loaded Item mixin.
local function RenderItemVisual(f, item) local function RenderItemVisual(f, item)
local r, g, b = 1, 1, 1 local r, g, b = 1, 1, 1
@@ -104,6 +110,7 @@ pfUI:RegisterModule("loothistory", function ()
-- Frame pools -- Frame pools
-- ========================================================================== -- ==========================================================================
local itemFrames = {} local itemFrames = {}
local usedPlayers, freePlayers = {}, {}
local FullUpdate -- forward declaration (toggle handlers call it) local FullUpdate -- forward declaration (toggle handlers call it)
@@ -203,10 +210,20 @@ pfUI:RegisterModule("loothistory", function ()
return f return f
end end
local playerPool = CreateObjectPool(CreatePlayerFrame, function(_, pf) local function RecycleAllPlayers()
pf:Hide() for i = 1, table.getn(usedPlayers) do
pf:ClearAllPoints() local pf = usedPlayers[i]
end) pf:Hide()
table.insert(freePlayers, pf)
end
usedPlayers = {}
end
local function GetPlayerFrame()
local pf = table.remove(freePlayers) or CreatePlayerFrame()
table.insert(usedPlayers, pf)
return pf
end
local function SetToggleTexture(toggle, isExpanded) local function SetToggleTexture(toggle, isExpanded)
if isExpanded then if isExpanded then
@@ -260,7 +277,7 @@ pfUI:RegisterModule("loothistory", function ()
if wroll and wroll > 0 then f.winroll:SetText(wroll) else f.winroll:SetText("") end if wroll and wroll > 0 then f.winroll:SetText(wroll) else f.winroll:SetText("") end
f.winroll:Show() f.winroll:Show()
f.winname:SetText(wname or UNKNOWN) f.winname:SetText(wname or UNKNOWN)
f.winname:SetTextColor(PFUI_CLASS_COLORS[wclass]:GetRGB()) f.winname:SetTextColor(ClassColor(wclass))
f.winname:Show() f.winname:Show()
else else
-- nobody won: everyone passed -- nobody won: everyone passed
@@ -281,7 +298,7 @@ pfUI:RegisterModule("loothistory", function ()
local function RenderPlayerFrame(pf, name, class, rollType, roll, isWinner) local function RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf.name:SetText(name or UNKNOWN) pf.name:SetText(name or UNKNOWN)
pf.name:SetTextColor(PFUI_CLASS_COLORS[class]:GetRGB()) pf.name:SetTextColor(ClassColor(class))
pf.rollicon:SetTexture(ROLL_TEX[rollType] or ROLL_TEX[ROLL_PASS]) pf.rollicon:SetTexture(ROLL_TEX[rollType] or ROLL_TEX[ROLL_PASS])
if roll and roll > 0 then pf.rolltext:SetText(roll) else pf.rolltext:SetText("") end if roll and roll > 0 then pf.rolltext:SetText(roll) else pf.rolltext:SetText("") end
@@ -298,7 +315,7 @@ pfUI:RegisterModule("loothistory", function ()
function FullUpdate() function FullUpdate()
if not pfUI.loothistory:IsShown() then return end if not pfUI.loothistory:IsShown() then return end
playerPool:ReleaseAll() RecycleAllPlayers()
local num = C_LootHistory.GetNumItems() local num = C_LootHistory.GetNumItems()
local y = -2 local y = -2
@@ -316,7 +333,7 @@ pfUI:RegisterModule("loothistory", function ()
for p = 1, f.numPlayers do for p = 1, f.numPlayers do
local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p) local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p)
if ShouldDisplayPlayer(f.isDone, roll, isMe) then if ShouldDisplayPlayer(f.isDone, roll, isMe) then
local pf = playerPool:Acquire() local pf = GetPlayerFrame()
RenderPlayerFrame(pf, name, class, rollType, roll, isWinner) RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf:ClearAllPoints() pf:ClearAllPoints()
pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y) pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y)
-128
View File
@@ -1,128 +0,0 @@
-- Macro icon picker
-- Replaces Blizzard's MacroPopupFrame (the "name + icon" dialog opened by the
-- New / Change-Icon buttons) with a pfUI-native picker driven by
-- IconDataProviderMixin, so the full spell + item + loose icon set is
-- available instead of the stock spell-only list. Saving goes through
-- C_Macro.CreateMacro / C_Macro.EditMacro, which take the icon as a texture
-- string -- so arbitrary icons (including index-less INV_* item icons) persist
-- on both the modern (Turtle/Octo) and stock-vanilla macro UIs. The surrounding
-- MacroFrame (list + body editor) is Blizzard's, already skinned elsewhere.
pfUI:RegisterModule("macroicons", function ()
HookAddonOrVariable("Blizzard_MacroUI", function()
-- ============================================================
-- Popup frame
-- ============================================================
local picker = CreateFrame("Frame", "pfMacroIconPicker", UIParent)
picker:SetFrameStrata("DIALOG")
picker:SetSize(472, 484)
-- Starts anchored to the right of the macro panel (re-anchored at open
-- once the skin's backdrop exists); dragging pins it in place after.
picker:SetPoint("BOTTOMLEFT", MacroFrame, "BOTTOMRIGHT", 8, 0)
picker:Hide()
CreateBackdrop(picker, nil, nil, .9)
CreateBackdropShadow(picker)
picker:EnableMouse(true)
picker:SetMovable(true)
picker:RegisterForDrag("LeftButton")
picker:SetScript("OnDragStart", function() this:StartMoving() end)
picker:SetScript("OnDragStop", function()
this:StopMovingOrSizing()
this.userMoved = true
end)
-- Spellbook seed leads the grid with class-relevant spell/talent icons.
local iconPicker = CreateIconPicker("pfMacroIcon", picker,
IconDataProviderExtraType.Spellbook, MACRO_POPUP_TEXT)
-- ============================================================
-- Open / save
-- ============================================================
-- Blizzard's popup disables these while it is open; mirror that so the
-- underlying frame can't be double-driven, then let MacroFrame_Update
-- restore the correct states on close.
local function SetMacroButtons(enabled)
local m = enabled and "Enable" or "Disable"
MacroNewButton[m](MacroNewButton)
MacroEditButton[m](MacroEditButton)
MacroDeleteButton[m](MacroDeleteButton)
end
local function OpenMacroPopup(mode)
if mode == "edit" and MacroFrame.selectedMacro then
picker.mode = "edit"
local name, texture = GetMacroInfo(MacroFrame.selectedMacro)
picker.editbox:SetText(name or "")
iconPicker.SetIcon(texture)
else
picker.mode = "new"
picker.editbox:SetText("")
iconPicker.SetIcon(nil)
end
picker.search:SetText("")
if not picker.userMoved then
picker:ClearAllPoints()
picker:SetPoint("BOTTOMLEFT", MacroFrame.backdrop or MacroFrame, "BOTTOMRIGHT", 8, 0)
end
SetMacroButtons(false)
picker:Show()
picker.editbox:SetFocus()
iconPicker.Refresh()
end
local function SaveMacroPopup()
local text = picker.editbox:GetText()
if text == "" then return end
local icon = iconPicker.GetIcon()
if picker.mode == "edit" and MacroFrame.selectedMacro then
C_Macro.EditMacro(MacroFrame.selectedMacro, text, icon, nil)
MacroFrame_SelectMacro(MacroFrame.selectedMacro)
else
local idx = C_Macro.CreateMacro(text, icon, nil, (MacroFrame.macroBase or 0) > 0)
if idx then MacroFrame_SelectMacro(idx) end
end
picker:Hide()
end
picker:SetScript("OnHide", function() MacroFrame_Update() end)
picker.editbox:SetScript("OnEnterPressed", SaveMacroPopup)
picker.editbox:SetScript("OnEscapePressed", function() picker:Hide() end)
-- OK / Cancel
local okay = CreateFrame("Button", "pfMacroIconPickerOkay", picker, "UIPanelButtonTemplate")
okay:SetSize(80, 22)
okay:SetText(OKAY)
okay:SetScript("OnClick", SaveMacroPopup)
SkinButton(okay)
local cancel = CreateFrame("Button", "pfMacroIconPickerCancel", picker, "UIPanelButtonTemplate")
cancel:SetSize(80, 22)
cancel:SetText(CANCEL)
cancel:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -14, 12)
cancel:SetScript("OnClick", function() picker:Hide() end)
SkinButton(cancel)
-- Okay | Cancel, both anchored to the bottom-right corner.
okay:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -6, 0)
-- ============================================================
-- Take over the New / Change-Icon buttons; retire Blizzard's popup
-- ============================================================
MacroNewButton:SetScript("OnClick", function()
MacroFrame_SaveMacro()
OpenMacroPopup("new")
end)
MacroEditButton:SetScript("OnClick", function()
MacroFrame_SaveMacro()
OpenMacroPopup("edit")
end)
-- Closing the macro window takes the picker with it.
MacroFrame:HookScript("OnHide", function() picker:Hide() end)
end)
end)
+66
View File
@@ -0,0 +1,66 @@
pfUI:RegisterModule("macrotweak", function ()
local conflictAddons = { "Supermacro", "SuperCleveRoidMacros", "UltimaMacros" }
local disabled = false
for _, addon in pairs(conflictAddons) do
local name = addon
EventUtil.ContinueOnAddOnLoaded(name, function()
if not disabled then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: " .. name .. " found, macrotweak disabled.")
end
disabled = true
end)
end
-- do not write macro calls into chat input history
if ChatFrameEditBox._AddHistoryLine then
local userinput
ChatFrameEditBox._AddHistoryLine = ChatFrameEditBox.AddHistoryLine
ChatFrameEditBox.AddHistoryLine = function(self, text)
if disabled then return ChatFrameEditBox._AddHistoryLine(self, text) end
if not userinput and text and string.find(text, "^/run(.+)") then return end
if not userinput and string.find(text, "^/script(.+)") then return end
if not userinput and string.find(text, "^/cast(.+)") then return end
ChatFrameEditBox._AddHistoryLine(self, text)
end
local OnEnter = ChatFrameEditBox:GetScript("OnEnterPressed")
ChatFrameEditBox:SetScript("OnEnterPressed", function(a1,a2,a3,a4)
userinput = true
OnEnter(a1,a2,a3,a4)
userinput = nil
end)
end
-- make sure #showtooltip inside macros won't be sent
local hookSendChatMessage = SendChatMessage
function _G.SendChatMessage(msg, ...)
if disabled then return hookSendChatMessage(msg, unpack(arg)) end
if msg and string.find(msg, "^#showtooltip ") then return end
hookSendChatMessage(msg, unpack(arg))
end
-- add /use and /equip to the macro api:
-- https://wowwiki.fandom.com/wiki/Making_a_macro
-- supported arguments:
-- /use <itemname>
-- /use <inventory slot>
-- /use <bag> <slot>
pfUI.api.RegisterSlashCommand("PFUSE", { "/equip" , "/use", "/pfequip", "/pfuse" }, function (msg)
if not msg or msg == "" then return end
local bag, slot, _
if string.find(msg, "%d+%s+%d+") then
_, _, bag, slot = string.find(msg, "(%d+)%s+(%d+)")
elseif string.find(msg, "%d+") then
_, _, slot = string.find(msg, "(%d+)")
else
bag, slot = FindItem(msg)
end
if bag and slot then
UseContainerItem(bag, slot)
elseif not bag and slot then
UseInventoryItem(slot)
end
end)
end)
+3 -9
View File
@@ -77,13 +77,7 @@ pfUI:RegisterModule("map", function ()
if point == "TOPLEFT" and relpoint == "TOPLEFT" then if point == "TOPLEFT" and relpoint == "TOPLEFT" then
offx = offx*oldscale/scale offx = offx*oldscale/scale
offy = offy*oldscale/scale offy = offy*oldscale/scale
-- Anchor to the parent (3-arg SetPoint) rather than re-using the frame WorldMapFrame:SetPoint(point, rel, relpoint, offx, offy)
-- GetPoint handed back. Re-anchoring to `rel` throws
-- "<unnamed> is dependent on this" as soon as anything else has anchored
-- itself to WorldMapFrame, which aborts the whole zoom handler. The
-- parent-relative form is also what LoadMovable/SaveMovable already
-- assume: SaveMovable stores only xpos/ypos, with no relative frame.
WorldMapFrame:SetPoint(point, offx, offy)
end end
WorldMapFrame:SetScale(scale) WorldMapFrame:SetScale(scale)
@@ -180,8 +174,8 @@ 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 WorldMapButton:IsMouseOver() then if mx and my and MouseIsOver(WorldMapButton) then
WorldMapButton.coords.text:SetFormattedText('%.1f / %.1f', mx, my) WorldMapButton.coords.text:SetText(string.format('%.1f / %.1f', mx, my))
else else
WorldMapButton.coords.text:SetText("") WorldMapButton.coords.text:SetText("")
end end
+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 WorldMapPlayer:IsMouseOver() then if MouseIsOver(WorldMapPlayer) 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 frame:IsMouseOver() then if frame:IsVisible() and MouseIsOver(frame) 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 frame:IsMouseOver() then if frame:IsVisible() and MouseIsOver(frame) then
ColorizeName(frame) ColorizeName(frame)
tooltipText = tooltipText.."\n"..frame.name tooltipText = tooltipText.."\n"..frame.name
end end
+47 -46
View File
@@ -30,14 +30,20 @@ pfUI:RegisterModule("mapreveal", function ()
pfUI.mapreveal:UpdateConfig() pfUI.mapreveal:UpdateConfig()
end) end)
local explores = {}
local explorecaches = {} local explorecaches = {}
local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } } local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } }
-- Own texture pool - separate from Blizzard's WorldMapOverlay textures -- Own texture pool - separate from Blizzard's WorldMapOverlay textures
local overlayPool = CreateTexturePool(WorldMapDetailFrame, "BORDER", nil, nil, function(_, tex) local pfOverlays = {}
tex:Hide() local pfOverlayMax = 0
tex:ClearAllPoints()
end) local function pfGetOverlay(idx)
if not pfOverlays[idx] then
pfOverlays[idx] = WorldMapDetailFrame:CreateTexture("pfReveal"..idx, "BORDER")
end
return pfOverlays[idx]
end
local exploreEnter = function() local exploreEnter = function()
WorldMapTooltip:ClearLines() WorldMapTooltip:ClearLines()
@@ -46,55 +52,31 @@ pfUI:RegisterModule("mapreveal", function ()
WorldMapTooltip:AddLine(this.name, 1, 1, 1) WorldMapTooltip:AddLine(this.name, 1, 1, 1)
WorldMapTooltip:Show() WorldMapTooltip:Show()
if not explorecaches[this.area] then return end if not explorecaches[this.name] then return end
if C.appearance.worldmap.mapreveal == "0" then return end if C.appearance.worldmap.mapreveal == "0" then return end
for texture in pairs(explorecaches[this.area]) do for texture in pairs(explorecaches[this.name]) do
texture:SetVertexColor(1,1,1,1) texture:SetVertexColor(1,1,1,1)
end end
end end
local exploreLeave = function() local exploreLeave = function()
WorldMapTooltip:Hide() WorldMapTooltip:Hide()
if not explorecaches[this.area] then return end if not explorecaches[this.name] then return end
if C.appearance.worldmap.mapreveal == "0" then return end if C.appearance.worldmap.mapreveal == "0" then return end
local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color) local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color)
for texture in pairs(explorecaches[this.area]) do for texture in pairs(explorecaches[this.name]) do
texture:SetVertexColor(r,g,b,a) texture:SetVertexColor(r,g,b,a)
end end
end end
-- Magnifying-glass icons for unexplored overlays. Everything constant lives
-- in the creator; the update only anchors and labels what it acquires -- and
-- it only acquires the ones it is going to show, where the old table grew an
-- icon for every overlay in the zone and hid most of them again.
local function CreateExplore()
local explore = CreateFrame("Frame", nil, WorldMapDetailFrame)
explore:SetSize(16, 16)
explore:SetScript("OnEnter", exploreEnter)
explore:SetScript("OnLeave", exploreLeave)
explore:EnableMouse(true)
explore:SetFrameLevel(255)
explore.tex = explore:CreateTexture(nil, "OVERLAY")
explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
explore.tex:SetBlendMode("ADD")
explore.tex:SetTexCoord(.08, .92, .08, .92)
explore.tex:SetAllPoints()
return explore
end
local explorePool = CreateObjectPool(CreateExplore, function(_, explore)
explore:Hide()
explore:ClearAllPoints()
end)
local function pfWorldMapFrame_Update() local function pfWorldMapFrame_Update()
-- clear stale caches -- clear stale caches
for k in pairs(explorecaches) do explorecaches[k] = nil end for k in pairs(explorecaches) do explorecaches[k] = nil end
-- hide all our textures from last frame -- hide all our textures from last frame
overlayPool:ReleaseAll() for i = 1, pfOverlayMax do
pfOverlays[i]:Hide()
end
local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color) local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color)
local mapFileName = GetMapInfo() local mapFileName = GetMapInfo()
@@ -112,13 +94,14 @@ pfUI:RegisterModule("mapreveal", function ()
local zoneKnown = alreadyknown[mapFileName] local zoneKnown = alreadyknown[mapFileName]
-- hide explore icons -- hide explore icons
explorePool:ReleaseAll() for _, frame in pairs(explores) do frame:Hide() end
-- ClassicAPI: full overlay list for the viewed zone (explored + unexplored), -- ClassicAPI: full overlay list for the viewed zone (explored + unexplored),
-- read straight from WorldMapOverlay.dbc. Replaces the hand-measured pfMapOverlayData. -- read straight from WorldMapOverlay.dbc. Replaces the hand-measured pfMapOverlayData.
local zoneData = C_Map.GetMapOverlays() or {} local zoneData = C_Map.GetMapOverlays() or {}
local textureCount = 0
for _, overlay in ipairs(zoneData) do for i, overlay in ipairs(zoneData) do
local name = overlay.textureName -- bare, e.g. "DRYGULCHRAVINE" local name = overlay.textureName -- bare, e.g. "DRYGULCHRAVINE"
local textureName = overlay.texturePath -- full engine path (for SetTexture) local textureName = overlay.texturePath -- full engine path (for SetTexture)
local textureWidth = overlay.textureWidth local textureWidth = overlay.textureWidth
@@ -126,15 +109,29 @@ pfUI:RegisterModule("mapreveal", function ()
local offsetX = overlay.offsetX local offsetX = overlay.offsetX
local offsetY = overlay.offsetY local offsetY = overlay.offsetY
-- explore magnifying glass icon. `alreadyknown` stores the FULL paths -- explore magnifying glass icon
-- GetMapOverlayInfo returns, so compare with the full path, not the bare explores[i] = explores[i] or CreateFrame("Frame", nil, WorldMapDetailFrame)
-- name. local explore = explores[i]
explore:SetWidth(16)
explore:SetHeight(16)
explore:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", offsetX + textureWidth/2, -offsetY - textureHeight/2)
explore:SetScript("OnEnter", exploreEnter)
explore:SetScript("OnLeave", exploreLeave)
explore:EnableMouse(true)
explore:SetFrameLevel(255)
explore.name = mapFileName .. " (" .. name .. ")"
explore.tex = explore.tex or explore:CreateTexture("", "OVERLAY")
explore.tex:SetBlendMode("ADD")
explore.tex:SetTexCoord(.08, .92, .08, .92)
explore.tex:SetAllPoints()
-- `alreadyknown` stores the FULL paths GetMapOverlayInfo returns,
-- so compare with the full path, not the bare name.
if C.appearance.worldmap.mapexploration == "1" and not zoneKnown[string.upper(textureName)] then if C.appearance.worldmap.mapexploration == "1" and not zoneKnown[string.upper(textureName)] then
local explore = explorePool:Acquire() explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
explore:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", offsetX + textureWidth/2, -offsetY - textureHeight/2)
explore.name = mapFileName .. " (" .. name .. ")"
explore.area = name -- cache key: explorecaches is keyed by the plain area name
explore:Show() explore:Show()
else
explore:Hide()
end end
-- render overlay texture tiles on BORDER draw layer -- render overlay texture tiles on BORDER draw layer
@@ -149,9 +146,11 @@ pfUI:RegisterModule("mapreveal", function ()
-- exactly what shears quirky overlays (e.g. Icepoint's Kaneq'nuun). -- exactly what shears quirky overlays (e.g. Icepoint's Kaneq'nuun).
if C.appearance.worldmap.mapreveal == "1" then if C.appearance.worldmap.mapreveal == "1" then
for _, tile in ipairs(overlay.tiles) do for _, tile in ipairs(overlay.tiles) do
local tex = overlayPool:Acquire() textureCount = textureCount + 1
local tex = pfGetOverlay(textureCount)
tex:SetSize(tile.width, tile.height) tex:SetWidth(tile.width)
tex:SetHeight(tile.height)
tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY) tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY)
tex:ClearAllPoints() tex:ClearAllPoints()
tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY) tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY)
@@ -165,6 +164,8 @@ pfUI:RegisterModule("mapreveal", function ()
end end
end end
end end
pfOverlayMax = math.max(pfOverlayMax, textureCount)
end end
-- hook WorldMapFrame_Update -- hook WorldMapFrame_Update
+70 -85
View File
@@ -1,30 +1,57 @@
pfUI:RegisterModule("marktracking", function () pfUI:RegisterModule("marktracking", function ()
-- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower)
if not UnitExists("mark1") and not UnitExists("mark8") then
if not pcall(function() UnitExists("mark1") end) then return end
end
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
-- Parse color strings "r,g,b,a" into components
local function ParseColor(str, dr, dg, db, da)
if not str or str == "" then return dr, dg, db, da end
local _, _, r, g, b, a = string.find(str, "([%d%.]+),([%d%.]+),([%d%.]+),([%d%.]+)")
if r then
return tonumber(r) or dr, tonumber(g) or dg, tonumber(b) or db, tonumber(a) or da
end
return dr, dg, db, da
end
local markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star local markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star
local markerTokens = {} -- [i] = "markN" local markerTokens = {}
local markerIndex = {} -- ["markN"] = i, for the event handler's arg1 for i = 1, 8 do markerTokens[i] = "mark" .. i end
-- Default colors per marker
local defaultColors = {
[1] = { 1.0, 0.9, 0.0, 1 }, -- star: yellow
[2] = { 1.0, 0.5, 0.0, 1 }, -- circle: orange
[3] = { 0.8, 0.0, 0.8, 1 }, -- diamond: purple
[4] = { 0.0, 0.8, 0.0, 1 }, -- triangle: green
[5] = { 0.7, 0.7, 0.7, 1 }, -- moon: silver
[6] = { 0.0, 0.4, 0.9, 1 }, -- square: blue
[7] = { 0.9, 0.0, 0.0, 1 }, -- cross: red
[8] = { 1.0, 1.0, 1.0, 1 }, -- skull: white
}
local markerConfigKeys = { local markerConfigKeys = {
"raidmarkercolor_star", [1] = "raidmarkercolor_star",
"raidmarkercolor_circle", [2] = "raidmarkercolor_circle",
"raidmarkercolor_diamond", [3] = "raidmarkercolor_diamond",
"raidmarkercolor_triangle", [4] = "raidmarkercolor_triangle",
"raidmarkercolor_moon", [5] = "raidmarkercolor_moon",
"raidmarkercolor_square", [6] = "raidmarkercolor_square",
"raidmarkercolor_cross", [7] = "raidmarkercolor_cross",
"raidmarkercolor_skull", [8] = "raidmarkercolor_skull",
} }
local markerColors = {} local markerColors = {}
for i, markKey in ipairs(markerConfigKeys) do for i = 1, 8 do
markerTokens[i] = "mark" .. i local d = defaultColors[i]
markerIndex[markerTokens[i]] = i local r, g, b, a = ParseColor(C.unitframes[markerConfigKeys[i]], d[1], d[2], d[3], d[4])
local r, g, b, a = GetStringColor(C.unitframes[markKey]) markerColors[i] = { r, g, b, a }
markerColors[i] = { tonumber(r), tonumber(g), tonumber(b), tonumber(a) }
end end
local FALLBACK_INTERVAL = 1.0 -- safety net for units that come into range after marker was set local FALLBACK_INTERVAL = 1.0 -- safety net for units that come into range after marker was set
local elapsed = 0
local isUnlocked = false local isUnlocked = false
local ROW_HEIGHT = tonumber(C.unitframes.raidmarkerheight) or 14 local ROW_HEIGHT = tonumber(C.unitframes.raidmarkerheight) or 14
local BAR_WIDTH = tonumber(C.unitframes.raidmarkerwidth) or 80 local BAR_WIDTH = tonumber(C.unitframes.raidmarkerwidth) or 80
@@ -71,7 +98,8 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
else else
pfUI.marktracking:SetPoint("TOP", UIParent, "CENTER", 0, 0) pfUI.marktracking:SetPoint("TOP", UIParent, "CENTER", 0, 0)
end end
pfUI.marktracking:SetSize(TOTAL_ROW_WIDTH, 8 * (ROW_HEIGHT + 1) + border * 2 - 1) pfUI.marktracking:SetWidth(TOTAL_ROW_WIDTH)
pfUI.marktracking:SetHeight(8 * (ROW_HEIGHT + 1) + border * 2 - 1)
pfUI.marktracking:Hide() pfUI.marktracking:Hide()
CreateBackdrop(pfUI.marktracking) CreateBackdrop(pfUI.marktracking)
@@ -97,16 +125,19 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
local i = markerOrder[idx] local i = markerOrder[idx]
local row = CreateFrame("Button", nil, pfUI.marktracking) local row = CreateFrame("Button", nil, pfUI.marktracking)
row:SetSize(TOTAL_ROW_WIDTH, ROW_HEIGHT) row:SetWidth(TOTAL_ROW_WIDTH)
row:SetHeight(ROW_HEIGHT)
row:Hide() row:Hide()
row:RegisterForClicks("LeftButtonUp", "RightButtonUp") row:RegisterForClicks("LeftButtonUp")
row:SetAttribute("type1", "target") row:SetScript("OnClick", function()
row:SetAttribute("type2", "menu") TargetUnit(markerTokens[this.markerIndex])
end)
-- raid icon -- raid icon
row.icon = row:CreateTexture(nil, "ARTWORK") row.icon = row:CreateTexture(nil, "ARTWORK")
row.icon:SetSize(ROW_HEIGHT, ROW_HEIGHT) row.icon:SetWidth(ROW_HEIGHT)
row.icon:SetHeight(ROW_HEIGHT)
row.icon:SetPoint("LEFT", row, "LEFT", 1, 0) row.icon:SetPoint("LEFT", row, "LEFT", 1, 0)
local markTex = C.unitframes.blizzard_raidicons == "1" and "Interface\\TargetingFrame\\UI-RaidTargetingIcons" or pfUI.media["img:raidicons"] local markTex = C.unitframes.blizzard_raidicons == "1" and "Interface\\TargetingFrame\\UI-RaidTargetingIcons" or pfUI.media["img:raidicons"]
row.icon:SetTexture(markTex) row.icon:SetTexture(markTex)
@@ -114,7 +145,8 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
-- portrait (right side) -- portrait (right side)
row.portrait = row:CreateTexture(nil, "ARTWORK") row.portrait = row:CreateTexture(nil, "ARTWORK")
row.portrait:SetSize(PORTRAIT_SIZE, PORTRAIT_SIZE) row.portrait:SetWidth(PORTRAIT_SIZE)
row.portrait:SetHeight(PORTRAIT_SIZE)
row.portrait:SetPoint("RIGHT", row, "RIGHT", -1, 0) row.portrait:SetPoint("RIGHT", row, "RIGHT", -1, 0)
row.portrait:SetTexCoord(.1, .9, .1, .9) row.portrait:SetTexCoord(.1, .9, .1, .9)
if not rm_showportrait then row.portrait:Hide() end if not rm_showportrait then row.portrait:Hide() end
@@ -172,8 +204,8 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
local token = markerTokens[i] local token = markerTokens[i]
if UnitExists(token) and not UnitIsDead(token) then if UnitExists(token) and not UnitIsDead(token) then
local hp, maxhp = UnitHealth(token), UnitHealthMax(token) local hp = UnitHealth(token)
row:SetAttribute('unit', token) local maxhp = UnitHealthMax(token)
if hp and maxhp and maxhp > 0 and hp > 0 then if hp and maxhp and maxhp > 0 and hp > 0 then
local pct = hp / maxhp local pct = hp / maxhp
@@ -235,27 +267,6 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
end end
end end
-- Fast per-row refresh for a single mark's UNIT_HEALTH/UNIT_MAXHEALTH: moves
-- just the bar + hp text. If the mark's visibility flips (comes into range,
-- dies, hp crosses 0) the visible rows re-pack, so hand off to UpdateDisplay.
local function UpdateRow(i)
if isUnlocked then return end
local row = pfUI.marktracking.rows[i]
if not row then return end
local token = markerTokens[i]
local hp, maxhp = UnitHealth(token), UnitHealthMax(token)
local shouldShow = UnitExists(token) and not UnitIsDead(token)
and hp and maxhp and maxhp > 0 and hp > 0 and true or false
if shouldShow ~= (row:IsShown() and true or false) then
UpdateDisplay()
return
end
if not shouldShow then return end
local pct = hp / maxhp
row.health:SetValue(pct)
if rm_showpct then row.hptext:SetText(math.ceil(pct * 100) .. "%") end
end
-- Unlock mode: show fixed 1-row placeholder so positioning works correctly -- Unlock mode: show fixed 1-row placeholder so positioning works correctly
if pfUI.unlock then if pfUI.unlock then
local origShow = pfUI.unlock:GetScript("OnShow") local origShow = pfUI.unlock:GetScript("OnShow")
@@ -281,50 +292,24 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
-- Event-driven scanner frame -- Event-driven scanner frame
local scanner = CreateFrame("Frame") local scanner = CreateFrame("Frame")
-- Fallback poll: catches units that come into range AFTER a marker was set -- RAID_TARGET_UPDATE: fires when a raid marker is set/cleared
-- (no event fires for that case, so we need this safety net). UpdateDisplay -- PLAYER_ENTERING_WORLD: fires on login, reload, and zone transitions
-- is a full eight-row rebuild, so it exists only while grouped -- raid markers -- UNIT_HEALTH/UNIT_MAXHEALTH: fires on HP changes for real-time bar updates
-- are a group feature and there is nothing to discover alone. The group events
-- below start and cancel it, so outside a group there is no timer queued at
-- all rather than one waking every second to return early.
--
-- Deliberately NOT keyed on a mark being visible: a marker set on a unit that
-- is out of range shows no row, and that is exactly what this poll catches.
local poll
local function UpdatePoll()
local grouped = IsInGroup()
if grouped and not poll then
poll = C_Timer.NewTicker(FALLBACK_INTERVAL, UpdateDisplay)
elseif not grouped and poll then
poll:Cancel()
poll = nil
end
end
-- RAID_TARGET_UPDATE: a raid marker was set/cleared -> full refresh
-- PLAYER_ENTERING_WORLD: login/reload/zone -> full refresh
-- PARTY_MEMBERS_CHANGED/RAID_ROSTER_UPDATE: joined or left a group -> the rows
-- can change, and the fallback poll starts or stops with it
-- UNIT_HEALTH/UNIT_MAXHEALTH: filtered to the eight mark tokens, so arg1 is
-- always "markN" and we refresh just that row (UpdateRow) instead of
-- rescanning all eight.
scanner:RegisterEvent("RAID_TARGET_UPDATE") scanner:RegisterEvent("RAID_TARGET_UPDATE")
scanner:RegisterEvent("PLAYER_ENTERING_WORLD") scanner:RegisterEvent("PLAYER_ENTERING_WORLD")
scanner:RegisterEvent("PARTY_MEMBERS_CHANGED") scanner:RegisterEvent("UNIT_HEALTH")
scanner:RegisterEvent("RAID_ROSTER_UPDATE") scanner:RegisterEvent("UNIT_MAXHEALTH")
scanner:RegisterUnitEvent("UNIT_HEALTH", "mark1", "mark2", "mark3", "mark4", "mark5", "mark6", "mark7", "mark8")
scanner:RegisterUnitEvent("UNIT_MAXHEALTH", "mark1", "mark2", "mark3", "mark4", "mark5", "mark6", "mark7", "mark8")
scanner:SetScript("OnEvent", function() scanner:SetScript("OnEvent", function()
if event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then UpdateDisplay()
-- arg1 is one of the eight tokens we registered for, so this is a lookup end)
-- rather than a parse -- string.match would allocate a capture and
-- tonumber would parse it, on every health tick of every marked unit. -- Fallback poll at 1s: catches units that come into range AFTER a marker was set
local i = arg1 and markerIndex[arg1] -- (no event fires for that case, so we need this safety net)
if i then UpdateRow(i) end scanner:SetScript("OnUpdate", function()
return elapsed = elapsed + arg1
end if elapsed < FALLBACK_INTERVAL then return end
if event ~= "RAID_TARGET_UPDATE" then UpdatePoll() end elapsed = 0
UpdateDisplay() UpdateDisplay()
end) end)
end) end)
+25 -25
View File
@@ -109,40 +109,40 @@ 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)
-- Blink ten times between .9 and .1, then hide this.highlight:SetScript("OnUpdate", function()
local highlight = this.highlight if not this.count then this.count = 0 end
highlight:SetAlpha(.9) if not this.modifier then this.modifier = 1 end
highlight.blink = highlight:CreateAnimationGroup() if this.count >= 10 then this:Hide() end
for i = 1, 10 do
local down = highlight.blink:CreateAnimation("Alpha") this:SetAlpha(this:GetAlpha() + this.modifier)
down:SetChange(-.8)
down:SetDuration(.3) if this:GetAlpha() <= 0.1 then
down:SetOrder(i * 2 - 1) this.modifier = 0.05
local up = highlight.blink:CreateAnimation("Alpha") this.count = this.count + 1
up:SetChange(.8) elseif this:GetAlpha() >= 0.9 then
up:SetDuration(.3) this.modifier = -0.05
up:SetOrder(i * 2) end
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
pfUI.minimapCoordinates = CreateFrame("Frame", "pfMinimapCoord", pfUI.minimap) pfUI.minimapCoordinates = CreateFrame("Frame", "pfMinimapCoord", pfUI.minimap)
-- Update coords every 0.1 seconds pfUI.minimapCoordinates:SetScript("OnUpdate", function()
C_Timer.NewTicker(0.1, function() -- Throttle to update coords every 0.1 seconds
if ( this.tick or 0) > GetTime() then return end
this.tick = GetTime() + .1
if C.appearance.minimap.coordstext == "off" then return end if C.appearance.minimap.coordstext == "off" then return end
local coord = pfUI.minimapCoordinates this.posX, this.posY = GetPlayerMapPosition("player")
coord.posX, coord.posY = GetPlayerMapPosition("player") if this.posX ~= 0 and this.posY ~= 0 then
if coord.posX ~= 0 and coord.posY ~= 0 then this.text:SetText(string.format("%.1f, %.1f", round(this.posX * 100, 1), round(this.posY * 100, 1)))
coord.text:SetFormattedText("%.1f, %.1f", round(coord.posX * 100, 1), round(coord.posY * 100, 1))
else else
coord.text:SetText("|cffffaaaaN/A") this.text:SetText("|cffffaaaaN/A")
end end
end) end)
@@ -227,7 +227,7 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimap.pvpicon = CreateFrame("Frame", nil, pfUI.minimap) pfUI.minimap.pvpicon = CreateFrame("Frame", nil, pfUI.minimap)
pfUI.minimap.pvpicon:Hide() pfUI.minimap.pvpicon:Hide()
pfUI.minimap.pvpicon:RegisterEvent("UPDATE_FACTION") pfUI.minimap.pvpicon:RegisterEvent("UPDATE_FACTION")
pfUI.minimap.pvpicon:RegisterUnitEvent("UNIT_FACTION", "player") pfUI.minimap.pvpicon:RegisterEvent("UNIT_FACTION")
pfUI.minimap.pvpicon:SetFrameStrata("HIGH") pfUI.minimap.pvpicon:SetFrameStrata("HIGH")
pfUI.minimap.pvpicon:SetSize(16, 16) pfUI.minimap.pvpicon:SetSize(16, 16)
pfUI.minimap.pvpicon:SetAlpha(.5) pfUI.minimap.pvpicon:SetAlpha(.5)
+195 -315
View File
@@ -5,7 +5,10 @@ pfUI:RegisterModule("nameplates", function ()
-- Local function references for performance -- Local function references for performance
local GetTime = GetTime local GetTime = GetTime
local UnitName = UnitName local UnitName = UnitName
local UnitClass = UnitClass
local UnitLevel = UnitLevel
local UnitIsPlayer = UnitIsPlayer local UnitIsPlayer = UnitIsPlayer
local UnitIsDead = UnitIsDead
local UnitAffectingCombat = UnitAffectingCombat local UnitAffectingCombat = UnitAffectingCombat
local UnitIsUnit = UnitIsUnit local UnitIsUnit = UnitIsUnit
local UnitCanAssist = UnitCanAssist local UnitCanAssist = UnitCanAssist
@@ -52,7 +55,6 @@ pfUI:RegisterModule("nameplates", function ()
local savedHostileState = nil local savedHostileState = nil
local savedFriendlyState = nil local savedFriendlyState = nil
local inFriendlyZone = false local inFriendlyZone = false
local myGuild = nil
local platecount = 0 local platecount = 0
local registry = {} local registry = {}
-- Subset of registry that currently has a unit assigned (between -- Subset of registry that currently has a unit assigned (between
@@ -77,10 +79,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, _, _, noInterrupt, spellID = C_Spell.UnitCastingInfo(unit) local name, _, texture, startMs, endMs, _, _, _, spellID = C_Spell.UnitCastingInfo(unit)
local isChannel local isChannel
if not name then if not name then
name, _, texture, startMs, endMs, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(unit) name, _, texture, startMs, endMs, _, _, 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,7 +94,6 @@ 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
@@ -114,7 +115,6 @@ pfUI:RegisterModule("nameplates", function ()
-- Reusable per-plate debuff display buffer (avoid GC churn from per-call table creation) -- Reusable per-plate debuff display buffer (avoid GC churn from per-call table creation)
local debuffDisplayBuf = {} -- [i] = { effect, texture, stacks, dtype, duration, timeleft } local debuffDisplayBuf = {} -- [i] = { effect, texture, stacks, dtype, duration, timeleft }
for i = 1, 16 do debuffDisplayBuf[i] = {} end for i = 1, 16 do debuffDisplayBuf[i] = {} end
local auraSlots = {} -- reusable GetAuraSlots buffer for the per-plate aura scan
local threatMemory = {} -- guid -> true if mob had player targeted local threatMemory = {} -- guid -> true if mob had player targeted
-- local debuffSeen = {} -- reusable table for debuff tracking (avoid GC churn) -- local debuffSeen = {} -- reusable table for debuff tracking (avoid GC churn)
@@ -160,24 +160,6 @@ pfUI:RegisterModule("nameplates", function ()
cfg.debuffanim = tonumber(C.nameplates.debuffanim) or 0 cfg.debuffanim = tonumber(C.nameplates.debuffanim) or 0
cfg.debufftext = tonumber(C.nameplates.debufftext) or 1 cfg.debufftext = tonumber(C.nameplates.debufftext) or 1
-- Throttle delays, resolved once instead of per plate per tick.
-- libthrottle:Get walks the saved-variable table, a defaults fallback and a
-- preset table, and can build a "<category>_custom" key -- the per-plate
-- OnUpdate was calling it one or two times for every visible plate, a
-- hundred times a second, just to decide it had nothing to do.
--
-- cfg.throttle_min is the floor across all four. No plate can ever be due
-- sooner than that, so the update can bail on it before working out which
-- category it actually belongs to.
cfg.throttle_target = pfUI.throttle:Get("nameplates_target")
cfg.throttle_mass = pfUI.throttle:Get("nameplates_mass")
cfg.throttle_normal = pfUI.throttle:Get("nameplates")
cfg.throttle_castbar = pfUI.throttle:Get("nameplates_castbar")
cfg.throttle_min = cfg.throttle_target
if cfg.throttle_mass < cfg.throttle_min then cfg.throttle_min = cfg.throttle_mass end
if cfg.throttle_normal < cfg.throttle_min then cfg.throttle_min = cfg.throttle_normal end
if cfg.throttle_castbar < cfg.throttle_min then cfg.throttle_min = cfg.throttle_castbar end
-- Rebuild offtanks lookup table -- Rebuild offtanks lookup table
offtanks = {} offtanks = {}
for k, v in pairs({strsplit("#", C.nameplates.combatofftanks)}) do for k, v in pairs({strsplit("#", C.nameplates.combatofftanks)}) do
@@ -280,6 +262,10 @@ 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
@@ -296,40 +282,15 @@ pfUI:RegisterModule("nameplates", function ()
end end
end end
-- Numeric CreatureType.dbc id for the plate's unit (8 = Critter, 11 = Totem). local function TotemPlate(name)
-- It's fixed per unit, so cache it and reset on plate reuse. nil while the GUID if C.nameplates.totemicons == "1" then
-- can't resolve yet (incl. the no-SuperWoW case, where cachedGuid is empty). for totem, icon in pairs(L["totems"]) do
-- Locale-proof and covers custom units for free -- no name tables needed. if string.find(name, totem) then return icon end
local function CreatureType(plate) end
local guid = plate.cachedGuid
if not guid or guid == "" then return nil end
if not plate.creatureType then
plate.creatureType = UnitCreatureTypeID(guid) -- nil retries next call
end end
return plate.creatureType
end end
-- Totem icon: UnitCreatedBySpell returns the totem-drop spell, whose icon IS local function HidePlate(unittype, name, fullhp, target)
-- the totem's icon. It's a broadcast descriptor field the client has for every
-- summoned unit in range, so it resolves immediately for passive and active
-- totems alike -- no self-aura read or attack-cast capture needed.
--
-- Re-read the spell every call rather than caching the icon outright: a shaman
-- can swap the totem in place (same unit, new drop spell) with no plate re-add
-- to invalidate a cache, so key the cached texture on the spell id and refresh
-- it only when the spell changes.
local function TotemPlate(plate)
if C.nameplates.totemicons ~= "1" then return nil end
if CreatureType(plate) ~= 11 then return nil end
local spellId = UnitCreatedBySpell(plate.cachedGuid)
if spellId ~= plate.totemSpell then
plate.totemSpell = spellId
plate.totemIcon = spellId and C_Spell.GetSpellTexture(spellId) or nil
end
return plate.totemIcon
end
local function HidePlate(unittype, fullhp, target, plate)
-- keep some plates always visible according to config -- keep some plates always visible according to config
if C.nameplates.fullhealth == "1" and not fullhp then return nil end if C.nameplates.fullhealth == "1" and not fullhp then return nil end
if C.nameplates.target == "1" and target then return nil end if C.nameplates.target == "1" and target then return nil end
@@ -345,10 +306,14 @@ pfUI:RegisterModule("nameplates", function ()
return true return true
elseif C.nameplates.friendlyplayer == "1" and unittype == "FRIENDLY_PLAYER" then elseif C.nameplates.friendlyplayer == "1" and unittype == "FRIENDLY_PLAYER" then
return true return true
elseif C.nameplates.critters == "1" and CreatureType(plate) == 8 then elseif C.nameplates.critters == "1" and unittype == "NEUTRAL_NPC" then
return true for i, critter in pairs(L["critters"]) do
elseif C.nameplates.totems == "1" and CreatureType(plate) == 11 then if string.lower(name) == string.lower(critter) then return true end
return true end
elseif C.nameplates.totems == "1" then
for totem in pairs(L["totems"]) do
if string.find(name, totem) then return true end
end
end end
-- nothing to hide -- nothing to hide
@@ -377,23 +342,16 @@ pfUI:RegisterModule("nameplates", function ()
end end
-- UnitSelectionType: 0 hostile, 1 unfriendly, 2 neutral, 3 friendly (or a local function GetUnitType(red, green, blue)
-- PvP-flagged player), 4 unflagged player. Type 3 is ambiguous, so callers if red > .9 and green < .2 and blue < .2 then
-- still promote *_NPC to *_PLAYER from their own player check. return "ENEMY_NPC"
local selectionTypes = { elseif red > .9 and green > .9 and blue < .2 then
[0] = "ENEMY_NPC", return "NEUTRAL_NPC"
[1] = "ENEMY_NPC", elseif red < .2 and green < .2 and blue > 0.9 then
[2] = "NEUTRAL_NPC", return "FRIENDLY_PLAYER"
[3] = "FRIENDLY_NPC", elseif red < .2 and green > .9 and blue < .2 then
[4] = "FRIENDLY_PLAYER", return "FRIENDLY_NPC"
} 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
@@ -440,8 +398,22 @@ 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)
plate.debuffs[index].cd = CreateFrame("Cooldown", plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index], "CooldownFrameTemplate") -- PERF: Use lightweight fake cooldown frame when animation disabled
plate.debuffs[index].cd:SetFrameLevel(6) -- The Model-based CooldownFrameTemplate causes major lag with many nameplates
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(COOLDOWN_FRAME_TYPE, 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
@@ -495,7 +467,7 @@ local nameplates = CreateFrame("Frame", "pfNameplates", UIParent)
nameplates:RegisterEvent("PLAYER_ENTERING_WORLD") nameplates:RegisterEvent("PLAYER_ENTERING_WORLD")
nameplates:RegisterEvent("PLAYER_TARGET_CHANGED") nameplates:RegisterEvent("PLAYER_TARGET_CHANGED")
nameplates:RegisterEvent("PLAYER_LOGOUT") nameplates:RegisterEvent("PLAYER_LOGOUT")
nameplates:RegisterUnitEvent("UNIT_COMBO_POINTS", "player") nameplates:RegisterEvent("UNIT_COMBO_POINTS")
nameplates:RegisterEvent("PLAYER_COMBO_POINTS") nameplates:RegisterEvent("PLAYER_COMBO_POINTS")
nameplates:RegisterEvent("ZONE_CHANGED_NEW_AREA") nameplates:RegisterEvent("ZONE_CHANGED_NEW_AREA")
nameplates:RegisterEvent("RAID_ROSTER_UPDATE") nameplates:RegisterEvent("RAID_ROSTER_UPDATE")
@@ -503,15 +475,13 @@ nameplates:RegisterEvent("PARTY_MEMBERS_CHANGED")
nameplates:RegisterEvent("NAME_PLATE_CREATED") nameplates:RegisterEvent("NAME_PLATE_CREATED")
nameplates:RegisterEvent("NAME_PLATE_UNIT_ADDED") nameplates:RegisterEvent("NAME_PLATE_UNIT_ADDED")
nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED") nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
nameplates:RegisterEvent("UNIT_AURA")
nameplates:RegisterEvent("UNIT_FLAGS")
nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT") nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
-- UNIT_AURA / UNIT_FLAGS / UNIT_SPELLCAST_* are registered per plate, on the nameplates:RegisterEvent("UNIT_SPELLCAST_START")
-- plate's own frame, against its own token -- see OnCreate and nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
-- NAME_PLATE_UNIT_ADDED. nameplates:RegisterEvent("UNIT_SPELLCAST_STOP")
nameplates:RegisterEvent("PLAYER_GUILD_UPDATE") nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
-- 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
@@ -522,27 +492,14 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
if nameplates.mouselook then if nameplates.mouselook then
nameplates.mouselook:SetScript("OnUpdate", nil) nameplates.mouselook:SetScript("OnUpdate", nil)
end end
-- The plates hold their own unit subscriptions now, so silencing this
-- frame alone would leave them dispatching through logout -- exactly what
-- this branch exists to prevent.
for plate in pairs(registry) do
if plate.nameplate then
plate.nameplate:UnregisterAllEvents()
plate.nameplate:SetScript("OnEvent", nil)
end
end
return return
elseif event == "PLAYER_GUILD_UPDATE" and arg1 == 'player' then
myGuild = GetGuildInfo("player")
elseif event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then elseif event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
CacheConfig() CacheConfig()
this:SetGameVariables() this:SetGameVariables()
RebuildRaidGuidCache() RebuildRaidGuidCache()
frameState.targetGuid = UnitGUID("target") frameState.targetGuid = UnitGUID("target")
myGuild = GetGuildInfo("player")
end end
-- Handle friendly zone nameplate disable feature -- Handle friendly zone nameplate disable feature
@@ -606,19 +563,6 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
local guid = UnitGUID(arg1) local guid = UnitGUID(arg1)
plate.nameplate.cachedGuid = guid plate.nameplate.cachedGuid = guid
plate.nameplate.unit = arg1 plate.nameplate.unit = arg1
-- Point this plate's own subscriptions at the token it just took. On a
-- recycled frame these replace the previous unit rather than stacking:
-- RegisterUnitEvent on an already-filtered registration swaps the units.
plate.nameplate:RegisterUnitEvent("UNIT_AURA", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_FLAGS", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_START", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_START", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_STOP", arg1)
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", arg1)
plate.nameplate.creatureType = nil -- recompute for the new unit
plate.nameplate.totemIcon = nil
plate.nameplate.totemSpell = nil
if guid then if guid then
plateByGuid[guid] = plate.nameplate plateByGuid[guid] = plate.nameplate
-- Seed: the unit may already be mid-cast (its UNIT_SPELLCAST_START -- Seed: the unit may already be mid-cast (its UNIT_SPELLCAST_START
@@ -646,9 +590,14 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
if plate and plate.nameplate and plate.nameplate.cachedGuid == guid then if plate and plate.nameplate and plate.nameplate.cachedGuid == guid then
plate.nameplate.cachedGuid = nil plate.nameplate.cachedGuid = nil
plate.nameplate.unit = nil plate.nameplate.unit = nil
-- Drop the subscriptions with the token: this slot is now free and the end
-- next plate to take it would otherwise feed this frame its events. end
plate.nameplate:UnregisterAllEvents()
elseif event == "UNIT_FLAGS" then
if arg1 and strfind(arg1, "^nameplate") then
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if plate and plate.nameplate then
plate.nameplate.eventcache = true
end end
end end
@@ -663,6 +612,48 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
if pn then pn.eventcache = true end if pn then pn.eventcache = true end
end end
elseif event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
-- ClassicAPI fires UNIT_SPELLCAST_* per unit token, including the caster's
-- "nameplateN". The payload has no timing, so poll it (PollCastInfo picks
-- cast vs channel) and cache -- only for a unit we have a plate for, so
-- the table stays bounded to on-screen casters.
if arg1 and strfind(arg1, "^nameplate") then
local guid = UnitGUID(arg1)
local plate = guid and plateByGuid[guid]
if plate then
castState[guid] = PollCastInfo(arg1)
if castState[guid] then
plate.castUpdate = true -- bypass the throttle so the bar shows now
end
end
end
elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" then
-- Cast/channel ended (natural, interrupted, or cancelled -- the poll fires
-- STOP for all three). Clear the cached cast and refresh its plate.
if arg1 and strfind(arg1, "^nameplate") then
local guid = UnitGUID(arg1)
if guid and castState[guid] then
castState[guid] = nil
local plate = plateByGuid[guid]
if plate then plate.castUpdate = true end
end
end
elseif event == "UNIT_AURA" then
-- ClassicAPI: fires with arg1 == "nameplateN" when a unit's aura set
-- changes (add/remove/modify). Flag the matching plate so OnUpdate does a
-- fresh C_UnitAuras read next tick instead of waiting on the 0.5s
-- throttle -- covers expirations, dispels, refreshes, and stack changes
-- in one event. Guard on the token prefix (UNIT_AURA also fires for
-- target/party/raid).
if arg1 and strfind(arg1, "^nameplate") then
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if plate and plate.nameplate then
plate.nameplate.auraUpdate = true
end
end
elseif event == "PLAYER_TARGET_CHANGED" then elseif event == "PLAYER_TARGET_CHANGED" then
frameState.targetGuid = UnitGUID('target') frameState.targetGuid = UnitGUID('target')
-- Flag the target's plate for update -- Flag the target's plate for update
@@ -673,17 +664,6 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
-- 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")
@@ -755,47 +735,11 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
nameplate.cache = {} nameplate.cache = {}
nameplate.original = {} nameplate.original = {}
-- Each plate watches its own unit. With RegisterUnitEvent the token IS the
-- subscription, so there is no central listener sifting every unit event in
-- the world for a "^nameplate" prefix and then resolving the plate back out
-- of arg1 -- the event arrives only at the plate it concerns, and `this` is
-- already that plate. NAME_PLATE_UNIT_ADDED points the registration at the
-- new token; _REMOVED drops it, which matters because freed slots are
-- reused and a stale token would feed this frame another unit's events.
nameplate:SetScript("OnEvent", function()
if event == "UNIT_AURA" then
-- a fresh C_UnitAuras read next tick rather than waiting out the 0.5s
-- throttle -- covers expiry, dispels, refreshes and stack changes
this.auraUpdate = true
elseif event == "UNIT_FLAGS" then
this.eventcache = true
elseif event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
-- the payload carries no timing, so poll it (PollCastInfo picks cast
-- vs channel)
local guid = this.cachedGuid
if guid then
castState[guid] = PollCastInfo(this.unit)
if castState[guid] then
this.castUpdate = true -- bypass the throttle so the bar shows now
end
end
elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" then
-- ended: natural, interrupted or cancelled -- the poll fires STOP for all
local guid = this.cachedGuid
if guid and castState[guid] then
castState[guid] = nil
this.castUpdate = true
end
end
end)
-- create shortcuts for all known elements and disable them -- create shortcuts for all known elements and disable them
nameplate.original.healthbar, nameplate.original.castbar = parent:GetChildren() nameplate.original.healthbar, nameplate.original.castbar = parent:GetChildren()
DisableObject(nameplate.original.healthbar) DisableObject(nameplate.original.healthbar)
DisableObject(nameplate.original.castbar) DisableObject(nameplate.original.castbar)
local NAMEPLATE_OBJECTORDER = { "border", "glow", "name", "level", "levelicon", "raidicon" }
for i, object in pairs({parent:GetRegions()}) do for i, object in pairs({parent:GetRegions()}) do
if NAMEPLATE_OBJECTORDER[i] and NAMEPLATE_OBJECTORDER[i] == "raidicon" then if NAMEPLATE_OBJECTORDER[i] and NAMEPLATE_OBJECTORDER[i] == "raidicon" then
nameplate[NAMEPLATE_OBJECTORDER[i]] = object nameplate[NAMEPLATE_OBJECTORDER[i]] = object
@@ -964,7 +908,6 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
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()
@@ -1005,7 +948,7 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
-- Use the same castbar texture and color as the unit frame castbar (castbar.lua) -- Use the same castbar texture and color as the unit frame castbar (castbar.lua)
local cbtexture = pfUI.media[C.appearance.castbar.texture] local cbtexture = pfUI.media[C.appearance.castbar.texture]
nameplate.castbar:SetStatusBarTexture(cbtexture or hptexture) nameplate.castbar:SetStatusBarTexture(cbtexture or hptexture)
local cbr, cbg, cbb, cba = GetStringColor(C.appearance.castbar.castbarcolor) local cbr, cbg, cbb, cba = strsplit(",", C.appearance.castbar.castbarcolor)
nameplate.castbar:SetStatusBarColor(cbr, cbg, cbb, cba) nameplate.castbar:SetStatusBarColor(cbr, cbg, cbb, cba)
-- reset endTime cache so color/texture refresh takes effect on next cast -- reset endTime cache so color/texture refresh takes effect on next cast
nameplate.castbar.lastEndTime = nil nameplate.castbar.lastEndTime = nil
@@ -1055,29 +998,13 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
local mouseover = plate.cachedGuid and plate.cachedGuid == frameState.mouseoverGuid or nil local mouseover = plate.cachedGuid and plate.cachedGuid == frameState.mouseoverGuid or nil
local unitstr = target and "target" or mouseover and "mouseover" or plate.cachedGuid or nil local unitstr = target and "target" or mouseover and "mouseover" or plate.cachedGuid or nil
-- target event sometimes fires too quickly, where nameplate identifiers are not
-- yet updated. So while being inside this event, we cannot trust the unitstr.
if event == "PLAYER_TARGET_CHANGED" then unitstr = nil end
-- remove unitstr when it doesn't resolve to this plate's unit (stale istarget,
-- stale mouseover guid or a despawned unit). Must run before the cache fills
-- below -- an unrelated unitstr would poison cache.player/minion with the
-- *other* unit's answer, and the nil-gate would then never recompute it.
if unitstr and UnitName(unitstr) ~= name then unitstr = nil end
-- resolve player vs npc from plate's own unit so libunitscan can't return -- resolve player vs npc from plate's own unit so libunitscan can't return
-- a player record for an NPC sharing the same name (e.g. Chromie). Stored -- a player record for an NPC sharing the same name (e.g. Chromie). Stored
-- as true/false/nil so it doubles as the GetUnitInfo hint. -- as true/false/nil so it doubles as the GetUnitInfo hint.
if plate.cache.player == nil and unitstr then if plate.cache.player == nil and unitstr then
plate.cache.player = UnitIsPlayer(unitstr) and true or false plate.cache.player = UnitIsPlayer(unitstr) and true or false
end end
if plate.cache.minion == nil and unitstr then local class, ulevel, elite, player, guild = GetUnitInfo(name, true, plate.cache.player)
plate.cache.minion = UnitIsMinion(unitstr) and true or false
end
local class, ulevel, elite, player, guild
if not plate.cache.minion then
class, ulevel, elite, player, guild = GetUnitInfo(name, true, plate.cache.player)
end
if plate.cache.player ~= nil then player = plate.cache.player or nil end if plate.cache.player ~= nil then player = plate.cache.player or nil end
-- Use database level ONLY if current level is ?? (fixes ?? after reload, but doesn't override visible levels) -- Use database level ONLY if current level is ?? (fixes ?? after reload, but doesn't override visible levels)
@@ -1087,7 +1014,8 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
levelFromDB = true levelFromDB = true
end end
local unittype = GetUnitType(plate) local red, green, blue = plate.original.healthbar:GetStatusBarColor()
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
@@ -1096,11 +1024,18 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
if player and unittype == "ENEMY_NPC" then unittype = "ENEMY_PLAYER" end if player and unittype == "ENEMY_NPC" then unittype = "ENEMY_PLAYER" end
if player and unittype == "FRIENDLY_NPC" then unittype = "FRIENDLY_PLAYER" end if player and unittype == "FRIENDLY_NPC" then unittype = "FRIENDLY_PLAYER" end
elite = plate.original.levelicon:IsShown() and not player and "boss" or elite elite = plate.original.levelicon:IsShown() and not player and "boss" or elite
if not class and not plate.cache.minion then plate.wait_for_scan = true end if not class then plate.wait_for_scan = true end
-- skip data updates on invisible frames -- skip data updates on invisible frames
if not visible then return end if not visible then return end
-- target event sometimes fires too quickly, where nameplate identifiers are not
-- yet updated. So while being inside this event, we cannot trust the unitstr.
if event == "PLAYER_TARGET_CHANGED" then unitstr = nil end
-- remove unitstr on unit name mismatch
if unitstr and UnitName(unitstr) ~= name then unitstr = nil end
-- always make sure to keep plate visible -- always make sure to keep plate visible
plate:Show() plate:Show()
@@ -1131,11 +1066,11 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
end end
-- hide frames according to the configuration -- hide frames according to the configuration
local TotemIcon = TotemPlate(plate) local TotemIcon = TotemPlate(name)
if TotemIcon then if TotemIcon then
-- icon resolved from the totem-drop spell (already a full path) -- create totem icon
plate.totem.icon:SetTexture(TotemIcon) plate.totem.icon:SetTexture("Interface\\Icons\\" .. TotemIcon)
plate.glow:Hide() plate.glow:Hide()
plate.level:Hide() plate.level:Hide()
@@ -1143,7 +1078,7 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
plate.health:Hide() plate.health:Hide()
plate.guild:Hide() plate.guild:Hide()
plate.totem:Show() plate.totem:Show()
elseif HidePlate(unittype, (hpmax-hp == hpmin), target, plate) then elseif HidePlate(unittype, name, (hpmax-hp == hpmin), target) then
plate.level:SetPoint("RIGHT", plate.name, "LEFT", -3, 0) plate.level:SetPoint("RIGHT", plate.name, "LEFT", -3, 0)
plate.name:SetParent(plate) plate.name:SetParent(plate)
plate.guild:SetPoint("BOTTOM", plate.name, "BOTTOM", -2, -(font_size + 2)) plate.guild:SetPoint("BOTTOM", plate.name, "BOTTOM", -2, -(font_size + 2))
@@ -1175,27 +1110,18 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
if plate.cache.level ~= level or plate.cache.elite ~= elite then if plate.cache.level ~= level or plate.cache.elite ~= elite then
plate.cache.level = level plate.cache.level = level
plate.cache.elite = elite plate.cache.elite = elite
plate.level:SetFormattedText("%s%s", level, (elitestrings[elite] or "")) plate.level:SetText(string.format("%s%s", level, (elitestrings[elite] or "")))
end end
-- Set level color from GetDifficultyColor when using DB level. -- Set level color from GetDifficultyColor when using DB level
-- No brightening here: adding a flat offset to all three channels
-- desaturates every tier toward white and collapses the boundaries
-- (orange reads as yellow, red reads as orange). Use Blizzard's values.
-- Clearing cache.levelcolor forces the sync block in OnUpdate to
-- re-colour once the ?? resolves and it takes ownership again.
if levelFromDB and type(level) == "number" then if levelFromDB and type(level) == "number" then
local color = GetDifficultyColor(level) local color = GetDifficultyColor(level)
plate.level:SetTextColor(color.r, color.g, color.b, 1) plate.level:SetTextColor(color.r + 0.3, color.g + 0.3, color.b + 0.3, 1)
plate.cache.levelcolor = nil
end end
-- remember who owns the level colour so the two writers cannot fight
plate.cache.levelfromdb = levelFromDB or nil
if guild and C.nameplates.showguildname == "1" then if guild and C.nameplates.showguildname == "1" then
plate.guild:SetText(guild) plate.guild:SetText(guild)
if guild == myGuild then if UnitIsInMyGuild(plate.unit) then
plate.guild:SetTextColor(0, 0.9, 0, 1) plate.guild:SetTextColor(0, 0.9, 0, 1)
else else
plate.guild:SetTextColor(0.8, 0.8, 0.8, 1) plate.guild:SetTextColor(0.8, 0.8, 0.8, 1)
@@ -1236,21 +1162,21 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
local hasdata = ( rhp and rhpmax ) or estimated or hpmax > 100 or (round(hpmax/100*hp) ~= hp) local hasdata = ( rhp and rhpmax ) or estimated or hpmax > 100 or (round(hpmax/100*hp) ~= hp)
if setting == "curperc" and hasdata and rhp then if setting == "curperc" and hasdata and rhp then
plate.health.text:SetFormattedText("%s | %s%%", Abbreviate(rhp), ceil(hp/hpmax*100)) plate.health.text:SetText(string.format("%s | %s%%", Abbreviate(rhp), ceil(hp/hpmax*100)))
elseif setting == "cur" and hasdata and rhp then elseif setting == "cur" and hasdata and rhp then
plate.health.text:SetFormattedText("%s", Abbreviate(rhp)) plate.health.text:SetText(string.format("%s", Abbreviate(rhp)))
elseif setting == "curmax" and hasdata and rhp then elseif setting == "curmax" and hasdata and rhp then
plate.health.text:SetFormattedText("%s - %s", Abbreviate(rhp), Abbreviate(rhpmax)) plate.health.text:SetText(string.format("%s - %s", Abbreviate(rhp), Abbreviate(rhpmax)))
elseif setting == "curmaxs" and hasdata and rhp then elseif setting == "curmaxs" and hasdata and rhp then
plate.health.text:SetFormattedText("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax)) plate.health.text:SetText(string.format("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax)))
elseif setting == "curmaxperc" and hasdata and rhp then elseif setting == "curmaxperc" and hasdata and rhp then
plate.health.text:SetFormattedText("%s - %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100)) plate.health.text:SetText(string.format("%s - %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100)))
elseif setting == "curmaxpercs" and hasdata and rhp then elseif setting == "curmaxpercs" and hasdata and rhp then
plate.health.text:SetFormattedText("%s / %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100)) plate.health.text:SetText(string.format("%s / %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100)))
elseif setting == "deficit" and rhp then elseif setting == "deficit" and rhp then
plate.health.text:SetFormattedText("-%s" .. (hasdata and "" or "%%"), Abbreviate(rhpmax - rhp)) plate.health.text:SetText(string.format("-%s" .. (hasdata and "" or "%%"), Abbreviate(rhpmax - rhp)))
else -- "percent" as fallback else -- "percent" as fallback
plate.health.text:SetFormattedText("%s%%", ceil(hp/hpmax*100)) plate.health.text:SetText(string.format("%s%%", ceil(hp/hpmax*100)))
end end
else else
plate.health.text:SetText() plate.health.text:SetText()
@@ -1259,11 +1185,12 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
local r, g, b, a = unpack(unitcolors[unittype]) local r, g, b, a = unpack(unitcolors[unittype])
if class and ( if class then
(unittype == "ENEMY_PLAYER" and C.nameplates["enemyclassc"] == "1") or if unittype == "ENEMY_PLAYER" and C.nameplates["enemyclassc"] == "1" then
(unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassc"] == "1") r, g, b, a = PFUI_CLASS_COLORS[class]:GetRGBA()
) then elseif unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassc"] == "1" then
r, g, b, a = PFUI_CLASS_COLORS[class]:GetRGBA() r, g, b, a = PFUI_CLASS_COLORS[class]:GetRGBA()
end
end end
if unitstr and UnitIsTapped(unitstr) and not UnitIsTappedByPlayer(unitstr) then if unitstr and UnitIsTapped(unitstr) and not UnitIsTappedByPlayer(unitstr) then
@@ -1284,43 +1211,15 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
plate.cache.r, plate.cache.g, plate.cache.b = r, g, b plate.cache.r, plate.cache.g, plate.cache.b = r, g, b
end end
-- Friendly player names take this colour when friendclassnamec is on. if r + g + b ~= plate.cache.namecolor and unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassnamec"] == "1" and class and PFUI_CLASS_COLORS[class] then
-- Ownership is recorded so the OnUpdate sync below stands down rather than plate.name:SetTextColor(r, g, b, a)
-- racing us: both writers used to share cache.namecolor despite storing plate.cache.namecolor = r + g + b
-- unrelated quantities (this colour vs Blizzard's name FontString), so
-- either could suppress the other -- and since nameplate.cache survives
-- pool reuse, a recycled plate could keep the previous unit's name colour.
local ownname = unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassnamec"] == "1"
and class and true or nil
if plate.cache.ownname ~= ownname then
plate.cache.ownname = ownname
-- ownership flipped: whichever writer is now in charge must re-assert
plate.cache.namecolor = nil
plate.cache.ownnamecolor = nil
end
-- read the class colour directly rather than reusing the bar's r,g,b: the
-- bar only carries a class colour when friendclassc happens to be on, and
-- it also picks up the tapped-grey and barcombatstate overrides, neither of
-- which belongs on the name.
if ownname then
local cr, cg, cb, ca = PFUI_CLASS_COLORS[class]:GetRGBA()
if cr + cg + cb ~= plate.cache.ownnamecolor then
plate.cache.ownnamecolor = cr + cg + cb
plate.name:SetTextColor(cr, cg, cb, ca)
end
end end
-- update combopoints
for i=1, 5 do plate.combopoints[i]:Hide() end
if target and C.nameplates.cpdisplay == "1" then if target and C.nameplates.cpdisplay == "1" then
local cp = GetComboPoints("target") for i=1, GetComboPoints("target") do plate.combopoints[i]:Show() end
if plate.cpShown ~= cp then
for i=1, 5 do plate.combopoints[i]:SetShown(i <= cp) end
plate.cpShown = cp
end
elseif plate.cpShown then
for i=1, 5 do plate.combopoints[i]:Hide() end
plate.cpShown = nil
end end
-- update debuffs -- update debuffs
@@ -1337,22 +1236,19 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
for i = 1, 16 do debuffDisplayBuf[i].effect = nil end for i = 1, 16 do debuffDisplayBuf[i].effect = nil end
if unitstr then if unitstr then
local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL" local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL"
local auras = C_UnitAuras.GetUnitAuras(unitstr, filter)
local now = GetTime() local now = GetTime()
-- one GetAuraSlots enumeration, then positional by-slot reads straight for _, aura in ipairs(auras) do
-- into the reusable buffer: the per-plate scan allocates nothing (no if debuffCount >= 16 then break end
-- per-aura table, no result array) and walks the aura array once
local n = ScanAuraSlots(unitstr, filter, auraSlots, 16)
for i = 1, n do
local aname, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
if not aname then break end
debuffCount = debuffCount + 1 debuffCount = debuffCount + 1
local timeleft = (aura.expirationTime and aura.expirationTime > 0) and (aura.expirationTime - now) or nil
local b = debuffDisplayBuf[debuffCount] local b = debuffDisplayBuf[debuffCount]
b.effect = aname b.effect = aura.name
b.texture = icon b.texture = aura.icon
b.stacks = count b.stacks = aura.applications
b.dtype = dispelType b.dtype = aura.dispelName
b.duration = duration b.duration = aura.duration
b.timeleft = (expirationTime and expirationTime > 0) and (expirationTime - now) or nil b.timeleft = timeleft
end end
end end
for i = 1, 16 do for i = 1, 16 do
@@ -1432,16 +1328,6 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
-- cachedGuid is maintained by NAME_PLATE_UNIT_ADDED / _REMOVED events. -- cachedGuid is maintained by NAME_PLATE_UNIT_ADDED / _REMOVED events.
-- Cheap gate first. The central loop calls this for every visible plate ~100
-- times a second, and classifying the plate below costs two C calls, a cast
-- lookup and a throttle resolution -- all of it wasted on a plate that is
-- throttled to 10fps. cfg.throttle_min is the floor across every category,
-- so nothing that would have updated can be turned away here; the real
-- category-specific throttle is still applied after the classification.
-- Event flags bypass both gates, as before.
local hasEventUpdate = nameplate.eventcache or nameplate.auraUpdate or nameplate.castUpdate or nameplate.targetUpdate or nameplate.comboUpdate
if not hasEventUpdate and (nameplate.lasttick or 0) + cfg.throttle_min > now then return end
-- PERF: Intelligent throttling based on target/castbar status and plate count -- PERF: Intelligent throttling based on target/castbar status and plate count
-- Use GUID comparison as primary target detection: instant, immune to alpha transitions, -- Use GUID comparison as primary target detection: instant, immune to alpha transitions,
-- and immediately correct on de-target (unlike istarget which updates one tick later) -- and immediately correct on de-target (unlike istarget which updates one tick later)
@@ -1468,24 +1354,25 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
end end
end end
-- Resolved in CacheConfig, so these are table reads rather than a walk
-- through the saved variables and preset tables.
local throttle local throttle
if target then if target then
throttle = cfg.throttle_target throttle = pfUI.throttle:Get("nameplates_target")
elseif visiblePlateCount > 20 then elseif visiblePlateCount > 20 then
throttle = cfg.throttle_mass throttle = pfUI.throttle:Get("nameplates_mass")
else else
throttle = cfg.throttle_normal throttle = pfUI.throttle:Get("nameplates")
end end
-- Non-target plates with active castbar use the castbar throttle -- Non-target plates with active castbar use the castbar throttle
if isCastingNonTarget and cfg.throttle_castbar < throttle then if isCastingNonTarget then
throttle = cfg.throttle_castbar local cbThrottle = pfUI.throttle:Get("nameplates_castbar")
if cbThrottle < throttle then throttle = cbThrottle end
end end
-- The category-specific gate. hasEventUpdate was read above, before the -- Check for pending event updates (these bypass throttle for immediate response)
-- classification, and still bypasses the throttle. local hasEventUpdate = nameplate.eventcache or nameplate.auraUpdate or nameplate.castUpdate or nameplate.targetUpdate or nameplate.comboUpdate
-- Event updates bypass throttle
if not hasEventUpdate and (nameplate.lasttick or 0) + throttle > now then return end if not hasEventUpdate and (nameplate.lasttick or 0) + throttle > now then return end
nameplate.lasttick = now nameplate.lasttick = now
@@ -1584,46 +1471,38 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
-- otherwise an NPC sharing a name with a known player flips wait_for_scan -- otherwise an NPC sharing a name with a known player flips wait_for_scan
-- off here, then OnDataChanged re-sets it, every frame until the mob scan -- off here, then OnDataChanged re-sets it, every frame until the mob scan
-- lands. -- lands.
if nameplate.wait_for_scan and not nameplate.cache.minion and GetUnitInfo(name, true, nameplate.cache.player) then if nameplate.wait_for_scan and GetUnitInfo(name, true, nameplate.cache.player) then
nameplate.wait_for_scan = nil nameplate.wait_for_scan = nil
update = true update = true
end end
-- trigger update when name color changed (includes combat state check). -- trigger update when name color changed (includes combat state check)
-- Skipped once OnDataChanged owns the name colour (class-coloured friendly local r, g, b = original.name:GetTextColor()
-- players), so the two writers cannot overwrite each other. local inCombatWithPlayer = cfg.namefightcolor and UnitAffectingCombat(nameplate.unit) and UnitAffectingCombat("player")
if not nameplate.cache.ownname then
local r, g, b = original.name:GetTextColor() if r + g + b ~= nameplate.cache.namecolor or (cfg.namefightcolor and nameplate.cache.inCombat ~= inCombatWithPlayer) then
local inCombatWithPlayer = cfg.namefightcolor and UnitAffectingCombat(nameplate.unit) and UnitAffectingCombat("player") nameplate.cache.namecolor = r + g + b
nameplate.cache.inCombat = inCombatWithPlayer
if r + g + b ~= nameplate.cache.namecolor or (cfg.namefightcolor and nameplate.cache.inCombat ~= inCombatWithPlayer) then if cfg.namefightcolor then
nameplate.cache.namecolor = r + g + b if (r > .9 and g < .2 and b < .2) or inCombatWithPlayer then
nameplate.cache.inCombat = inCombatWithPlayer nameplate.name:SetTextColor(1,0.4,0.2,1)
if cfg.namefightcolor then
if (r > .9 and g < .2 and b < .2) or inCombatWithPlayer then
nameplate.name:SetTextColor(1,0.4,0.2,1)
else
nameplate.name:SetTextColor(r,g,b,1)
end
else else
nameplate.name:SetTextColor(1,1,1,1) nameplate.name:SetTextColor(r,g,b,1)
end end
update = true else
nameplate.name:SetTextColor(1,1,1,1)
end end
update = true
end end
-- trigger update when level color changed. Skipped while the level came -- trigger update when level color changed
-- from the database (?? plates), where OnDataChanged owns the colour -- local r, g, b = original.level:GetTextColor()
-- otherwise both writers race and cache.levelcolor goes stale, which r, g, b = r + .3, g + .3, b + .3
-- leaves a recycled plate wearing the previous unit's colour. if r + g + b ~= nameplate.cache.levelcolor then
if not nameplate.cache.levelfromdb then nameplate.cache.levelcolor = r + g + b
local r, g, b = original.level:GetTextColor() nameplate.level:SetTextColor(r,g,b,1)
if r + g + b ~= nameplate.cache.levelcolor then update = true
nameplate.cache.levelcolor = r + g + b
nameplate.level:SetTextColor(r,g,b,1)
update = true
end
end end
-- use timer based updates -- use timer based updates
@@ -1687,9 +1566,10 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
-- engine framerate, decoupled from central loop). Only update non-target castbars here. -- engine framerate, decoupled from central loop). Only update non-target castbars here.
local isTargetPlate = target or nameplate.istarget or (nameplate.health and nameplate.health.zoomed) local isTargetPlate = target or nameplate.istarget or (nameplate.health and nameplate.health.zoomed)
if cfg.showcastbar and not cfg.targetcastbar and not isTargetPlate then if cfg.showcastbar and not cfg.targetcastbar and not isTargetPlate then
local cbThrottle = cfg.throttle_castbar local cbThrottle = pfUI.throttle:Get("nameplates_castbar")
if visiblePlateCount > 20 and cfg.throttle_mass > cbThrottle then if visiblePlateCount > 20 then
cbThrottle = cfg.throttle_mass local massThrottle = pfUI.throttle:Get("nameplates_mass")
if massThrottle > cbThrottle then cbThrottle = massThrottle end
end end
if (nameplate.castbar_tick or 0) + cbThrottle <= now then if (nameplate.castbar_tick or 0) + cbThrottle <= now then
nameplate.castbar_tick = now nameplate.castbar_tick = now
@@ -1732,7 +1612,7 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
rounded = floor(remaining * 100) rounded = floor(remaining * 100)
if castbar.lastTextTick ~= rounded then if castbar.lastTextTick ~= rounded then
castbar.lastTextTick = rounded castbar.lastTextTick = rounded
castbar.text:SetFormattedText("%.2f", remaining) castbar.text:SetText(string.format("%.2f", remaining))
end end
end end
end end
@@ -1756,7 +1636,7 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
-- 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)
SetCastbarShield(nameplate.castbar, nameplate.castbar.icon, castInfo.noInterrupt, isChannel) nameplate.castbar:SetStatusBarColor(GetStringColor(C.appearance.castbar[(isChannel and "channelcolor" or "castbarcolor")]))
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)
+60 -44
View File
@@ -37,12 +37,18 @@ pfUI:RegisterModule("panel", function()
return return
end end
if arg1 == "LeftButton" then if arg1 == "LeftButton" then
widget.timerFrame:SetShown(not widget.timerFrame:IsShown()) if widget.timerFrame:IsShown() then
widget.timerFrame:Hide()
else
widget.timerFrame:Show()
end
elseif arg1 == "RightButton" then elseif arg1 == "RightButton" then
widget.timerFrame.Snapshot = GetTime() widget.timerFrame.Snapshot = GetTime()
end end
end end
C_Timer.NewTicker(1, function() widget:SetScript("OnUpdate",function()
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
local secondsenabled = C.panel.seconds == "1" local secondsenabled = C.panel.seconds == "1"
local fmt local fmt
if C.global.twentyfour == "0" then if C.global.twentyfour == "0" then
@@ -56,7 +62,8 @@ pfUI:RegisterModule("panel", function()
widget.timerFrame = CreateFrame("Frame", "pfUITimer", UIParent) widget.timerFrame = CreateFrame("Frame", "pfUITimer", UIParent)
widget.timerFrame:Hide() widget.timerFrame:Hide()
widget.timerFrame:SetSize(120, 35) widget.timerFrame:SetWidth(120)
widget.timerFrame:SetHeight(35)
widget.timerFrame:SetPoint("TOP", 0, -100) widget.timerFrame:SetPoint("TOP", 0, -100)
UpdateMovable(widget.timerFrame) UpdateMovable(widget.timerFrame)
@@ -96,9 +103,11 @@ pfUI:RegisterModule("panel", function()
pfUI.panel:OutputPanel("combat", T["Combat"] .. ": " .. NOT_APPLICABLE) pfUI.panel:OutputPanel("combat", T["Combat"] .. ": " .. NOT_APPLICABLE)
end end
end) end)
C_Timer.NewTicker(1, function() widget.combat:SetScript("OnUpdate", function()
if widget.combat.combat then if not this.tick then this.tick = GetTime() end
pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - widget.combat.combat))) if GetTime() <= this.tick + 1 then return else this.tick = GetTime() end
if this.combat then
pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - this.combat)))
end end
end) end)
end end
@@ -122,7 +131,6 @@ 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(" ")
@@ -134,7 +142,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" .. screenWidth .. 'x' .. screenHeight) GameTooltip:AddDoubleLine(T["Screen Resolution"], "|cffffffff" .. GetCVar("gxResolution"))
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
@@ -145,7 +153,9 @@ pfUI:RegisterModule("panel", function()
pfUI.addons:Show() pfUI.addons:Show()
end end
end end
C_Timer.NewTicker(1, function() widget:SetScript("OnUpdate",function()
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
fps = floor(GetFramerate()) fps = floor(GetFramerate())
_, _, lag = GetNetStats() _, _, lag = GetNetStats()
@@ -297,18 +307,18 @@ pfUI:RegisterModule("panel", function()
local playerzone = GetRealZoneText() local playerzone = GetRealZoneText()
for friendIndex=1, all do for friendIndex=1, all do
local info = C_FriendList.GetFriendInfoByIndex(friendIndex) local friend_name, friend_level, friend_class, friend_area, friend_connected = GetFriendInfo(friendIndex)
if info and info.connected and info.classFilename and info.level then if friend_connected and friend_class and friend_level then
if not init then if not init then
GameTooltip_SetDefaultAnchor(GameTooltip, this) GameTooltip_SetDefaultAnchor(GameTooltip, this)
GameTooltip:ClearLines() GameTooltip:ClearLines()
GameTooltip:AddLine("|cff555555" .. T["Friends Online"]) GameTooltip:AddLine("|cff555555" .. T["Friends Online"])
init = true init = true
end end
local ccolor = PFUI_CLASS_COLORS[info.classFilename] local ccolor = PFUI_CLASS_COLORS[L["class"][friend_class]] or { 1, 1, 1 }
local lcolor = GetDifficultyColor(tonumber(info.level)) or { 1, 1, 1 } local lcolor = GetDifficultyColor(tonumber(friend_level)) or { 1, 1, 1 }
local zcolor = info.area == playerzone and "|cff33ffcc" or "|cffcccccc" local zcolor = friend_area == playerzone and "|cff33ffcc" or "|cffcccccc"
GameTooltip:AddDoubleLine(ccolor:WrapTextInColorCode(info.name) .. rgbhex(lcolor) .. " [" .. info.level .. "]", zcolor .. info.area) GameTooltip:AddDoubleLine(rgbhex(ccolor) .. friend_name .. rgbhex(lcolor) .. " [" .. friend_level .. "]", zcolor .. friend_area)
end end
end end
@@ -316,7 +326,15 @@ pfUI:RegisterModule("panel", function()
end end
widget.Click = function() ToggleFriendsFrame(1) end widget.Click = function() ToggleFriendsFrame(1) end
widget:SetScript("OnEvent", function() widget:SetScript("OnEvent", function()
local online = C_FriendList.GetNumOnlineFriends() local online = 0
local all = GetNumFriends()
for friendIndex=1, all do
local friend_name, friend_level, friend_class, friend_area, friend_connected = GetFriendInfo(friendIndex)
if ( friend_connected ) then
online = online + 1
end
end
pfUI.panel:OutputPanel("friends", FRIENDS .. ": " .. online, widget.Tooltip, widget.Click) pfUI.panel:OutputPanel("friends", FRIENDS .. ": " .. online, widget.Tooltip, widget.Click)
end) end)
end end
@@ -328,7 +346,7 @@ pfUI:RegisterModule("panel", function()
widget:RegisterEvent("PLAYER_GUILD_UPDATE") widget:RegisterEvent("PLAYER_GUILD_UPDATE")
widget.Tooltip = function() widget.Tooltip = function()
-- skip without guild -- skip without guild
if not IsInGuild() then return end if not GetGuildInfo("player") then return end
local raidparty = {} local raidparty = {}
for i=1,4 do -- detect people in group for i=1,4 do -- detect people in group
@@ -380,7 +398,7 @@ pfUI:RegisterModule("panel", function()
end end
widget.Click = function() ToggleFriendsFrame(3) end widget.Click = function() ToggleFriendsFrame(3) end
widget:SetScript("OnEvent", function() widget:SetScript("OnEvent", function()
if IsInGuild() then if GetGuildInfo("player") then
local count = 0 local count = 0
for i = 1, GetNumGuildMembers() do for i = 1, GetNumGuildMembers() do
local _, _, _, _, _, _, _, _, online = GetGuildRosterInfo(i) local _, _, _, _, _, _, _, _, online = GetGuildRosterInfo(i)
@@ -393,8 +411,9 @@ pfUI:RegisterModule("panel", function()
end end
end) end)
C_Timer.NewTicker(60, function() widget:SetScript("OnUpdate",function()
if IsInGuild() then GuildRoster() end if ( this.tick or 60) > GetTime() then return else this.tick = GetTime() + 60 end
if GetGuildInfo("player") then GuildRoster() end
end) end)
end end
@@ -419,13 +438,8 @@ pfUI:RegisterModule("panel", function()
local repPercent = floor(cur / max * 100) local repPercent = floor(cur / max * 100)
if repPercent < 100 then if repPercent < 100 then
local _, _, _, hex = GetColorGradient(repPercent/100) local _, _, _, hex = GetColorGradient(repPercent/100)
local link = GetInventoryItemLink("player", id)
local texture = GetInventoryItemTexture("player", id)
if texture then
link = CreateSimpleTextureMarkup(texture, 24) .. " " .. link
end
itemLines[table.getn(itemLines)+1] = { itemLines[table.getn(itemLines)+1] = {
link, GetInventoryItemLink("player", id),
string.format("%s%s%%|r", hex, repPercent) string.format("%s%s%%|r", hex, repPercent)
} }
end end
@@ -434,7 +448,8 @@ pfUI:RegisterModule("panel", function()
if totalRep > 0 then if totalRep > 0 then
GameTooltip:ClearLines() GameTooltip:ClearLines()
GameTooltip_SetDefaultAnchor(GameTooltip, this) GameTooltip_SetDefaultAnchor(GameTooltip, this)
GameTooltip:AddLine(REPAIR_COST.." " .. CreateGoldString(totalRep), 0.3333, 0.3333, 0.3333) GameTooltip:SetText("|cff555555"..(string.gsub(REPAIR_COST,":","")).."|r")
SetTooltipMoney(GameTooltip, totalRep)
for _,line in ipairs(itemLines) do for _,line in ipairs(itemLines) do
GameTooltip:AddDoubleLine(line[1],line[2]) GameTooltip:AddDoubleLine(line[1],line[2])
end end
@@ -458,7 +473,7 @@ pfUI:RegisterModule("panel", function()
do -- Zone do -- Zone
local widget = CreateFrame("Frame", "pfPanelWidgetZone", UIParent) local widget = CreateFrame("Frame", "pfPanelWidgetZone", UIParent)
for _,event in pairs({"PLAYER_ENTERING_WORLD", "MINIMAP_ZONE_CHANGED"}) do for _,event in pairs(EVENTS_MINIMAP_ZONE_UPDATE) do
widget:RegisterEvent(event) widget:RegisterEvent(event)
end end
widget.Tooltip = function() widget.Tooltip = function()
@@ -477,7 +492,11 @@ pfUI:RegisterModule("panel", function()
GameTooltip:Show() GameTooltip:Show()
end end
widget.Click = function() widget.Click = function()
WorldMapFrame:SetShown(not WorldMapFrame:IsShown()) if WorldMapFrame:IsShown() then
WorldMapFrame:Hide()
else
WorldMapFrame:Show()
end
end end
widget:SetScript("OnEvent", function() widget:SetScript("OnEvent", function()
pfUI.panel:OutputPanel("zone", GetMinimapZoneText(), widget.Tooltip, widget.Click) pfUI.panel:OutputPanel("zone", GetMinimapZoneText(), widget.Tooltip, widget.Click)
@@ -487,7 +506,7 @@ pfUI:RegisterModule("panel", function()
do -- Ammo do -- Ammo
local widget = CreateFrame("Frame", "pfPanelWidgetAmmo", UIParent) local widget = CreateFrame("Frame", "pfPanelWidgetAmmo", UIParent)
widget:RegisterEvent("PLAYER_ENTERING_WORLD") widget:RegisterEvent("PLAYER_ENTERING_WORLD")
widget:RegisterUnitEvent("UNIT_INVENTORY_CHANGED", "player") widget:RegisterEvent("UNIT_INVENTORY_CHANGED")
widget:RegisterEvent("BAG_UPDATE_DELAYED") widget:RegisterEvent("BAG_UPDATE_DELAYED")
widget.Tooltip = function() widget.Tooltip = function()
if GetInventoryItemQuality("player", 0) then if GetInventoryItemQuality("player", 0) then
@@ -626,7 +645,8 @@ pfUI:RegisterModule("panel", function()
local frame = CreateFrame("Button", nil, parent) local frame = CreateFrame("Button", nil, parent)
frame:SetFrameLevel(0) frame:SetFrameLevel(0)
frame:ClearAllPoints() frame:ClearAllPoints()
frame:SetSize(width, parent:GetHeight()) frame:SetWidth(width)
frame:SetHeight(parent:GetHeight())
frame:SetPoint(location, 0, 0) frame:SetPoint(location, 0, 0)
frame.text = frame:CreateFontString("Status", "LOW", "GameFontNormal") frame.text = frame:CreateFontString("Status", "LOW", "GameFontNormal")
frame.text:ClearAllPoints() frame.text:ClearAllPoints()
@@ -667,7 +687,8 @@ pfUI:RegisterModule("panel", function()
local imgstring = "img:" .. leftright local imgstring = "img:" .. leftright
parent.texture:SetTexture(pfUI.media[imgstring]) parent.texture:SetTexture(pfUI.media[imgstring])
parent.texture:SetPoint("CENTER", 0, 0) parent.texture:SetPoint("CENTER", 0, 0)
parent.texture:SetSize(8, 8) parent.texture:SetWidth(8)
parent.texture:SetHeight(8)
parent.texture:SetVertexColor(.25,.25,.25,1) parent.texture:SetVertexColor(.25,.25,.25,1)
return parent.texture return parent.texture
end end
@@ -689,7 +710,7 @@ pfUI:RegisterModule("panel", function()
UpdateMovable(pfUI.panel.left) UpdateMovable(pfUI.panel.left)
pfUI.panel.left.hide:SetScript("OnClick", function() pfUI.panel.left.hide:SetScript("OnClick", function()
pfUI.chat.left:SetShown(not pfUI.chat.left:IsShown()) if pfUI.chat.left:IsShown() then pfUI.chat.left:Hide() else pfUI.chat.left:Show() end
end) end)
if not pfUI.chat then pfUI.panel.left.hide:Hide() end if not pfUI.chat then pfUI.panel.left.hide:Hide() end
@@ -726,7 +747,7 @@ pfUI:RegisterModule("panel", function()
UpdateMovable(pfUI.panel.right) UpdateMovable(pfUI.panel.right)
pfUI.panel.right.hide:SetScript("OnClick", function() pfUI.panel.right.hide:SetScript("OnClick", function()
pfUI.chat.right:SetShown(not pfUI.chat.right:IsShown()) if pfUI.chat.right:IsShown() then pfUI.chat.right:Hide() else pfUI.chat.right:Show() end
end) end)
if not pfUI.chat then pfUI.panel.right.hide:Hide() end if not pfUI.chat then pfUI.panel.right.hide:Hide() end
@@ -775,18 +796,13 @@ pfUI:RegisterModule("panel", function()
pfUI.panel.microbutton = CreateFrame("Frame", "pfPanelMicroButton", UIParent) pfUI.panel.microbutton = CreateFrame("Frame", "pfPanelMicroButton", UIParent)
pfUI.panel.microbutton:SetPoint("TOP", pfUI.panel.minimap, "BOTTOM", 0, -2*default_border) pfUI.panel.microbutton:SetPoint("TOP", pfUI.panel.minimap, "BOTTOM", 0, -2*default_border)
UpdateMovable(pfUI.panel.microbutton) UpdateMovable(pfUI.panel.microbutton)
pfUI.panel.microbutton:SetSize(145, 23) pfUI.panel.microbutton:SetHeight(23)
pfUI.panel.microbutton:SetWidth(145)
pfUI.panel.microbutton:SetFrameStrata("MEDIUM") pfUI.panel.microbutton:SetFrameStrata("MEDIUM")
local microButtons = { for i=1,table.getn(MICRO_BUTTONS) do
'CharacterMicroButton', 'SpellbookMicroButton', 'TalentMicroButton', local anchor = _G[MICRO_BUTTONS[i-1]] or pfUI.panel.microbutton
'QuestLogMicroButton', 'SocialsMicroButton', 'WorldMapMicroButton', local button = _G[MICRO_BUTTONS[i]]
'MainMenuMicroButton', 'HelpMicroButton',
}
for i=1,table.getn(microButtons) do
local anchor = _G[microButtons[i-1]] or pfUI.panel.microbutton
local button = _G[microButtons[i]]
button:ClearAllPoints() button:ClearAllPoints()
button:SetParent(pfUI.panel.microbutton) button:SetParent(pfUI.panel.microbutton)
if i == 1 then if i == 1 then
+30 -28
View File
@@ -39,20 +39,17 @@ pfUI:RegisterModule("player", function ()
playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER" playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER"
-- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset -- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset
local cfgColorToHexTable = setmetatable({}, { local function cfgColorToHex(colorStr)
__index = function (t, colorStr) if not colorStr or colorStr == "" then return nil end
if not colorStr or colorStr == "" then return nil end local r, g, b = strsplit(",", colorStr)
local r, g, b = GetStringColor(colorStr) r, g, b = tonumber(r), tonumber(g), tonumber(b)
if not r or not g or not b then return nil end if not r or not g or not b then return nil end
local hex = C_ColorUtil.GenerateTextColorCode({ r=r, g = g, b=b}) return string.format("%02X%02X%02X", r * 255, g * 255, b * 255)
rawset(t, colorStr, hex) end
return hex
end
})
-- SP school colors indexed by GetSpellBonusDamage's 1-based school order -- SP school colors indexed by GetSpellBonusDamage's 1-based school order
-- (1=phys, 2=holy, 3=fire, 4=nature, 5=frost, 6=shadow, 7=arcane) -- (1=phys, 2=holy, 3=fire, 4=nature, 5=frost, 6=shadow, 7=arcane)
local spColors = { "FFFFFFFF", "FFFFFF80", "FFFF8000", "FF4DFF4D", "FF80FFFF", "FF9482C9", "FFFFFFFF" } local spColors = { "FFFFFF", "FFFF80", "FF8000", "4DFF4D", "80FFFF", "9482C9", "FFFFFF" }
-- Default SP school per class used as tiebreaker when multiple schools are equal -- Default SP school per class used as tiebreaker when multiple schools are equal
local spDefaultSchool = { local spDefaultSchool = {
@@ -71,37 +68,42 @@ pfUI:RegisterModule("player", function ()
-- from UNIT_MOD_CAST_SPEED). Talent/spell-specific cast-time reductions -- from UNIT_MOD_CAST_SPEED). Talent/spell-specific cast-time reductions
-- show up in the actual cast bar via C_Spell.UnitCastingInfo; folding them -- show up in the actual cast bar via C_Spell.UnitCastingInfo; folding them
-- in here too was mixing two different concepts into one number. -- in here too was mixing two different concepts into one number.
local isSpellCaster = playerFrame.isSpellCaster local showHaste = cfg.display_haste == "1"
local showHaste = isSpellCaster and cfg.display_haste == "1" local showSP = cfg.display_spellpower == "1"
local showSP = isSpellCaster and cfg.display_spellpower == "1"
if not showHaste and not showSP then local isSpellCaster = playerFrame.isSpellCaster
if (not showHaste or not isSpellCaster) and not showSP then
playerFrame.infoTopCenterText:SetText("") playerFrame.infoTopCenterText:SetText("")
return return
end end
local parts = {} local haste = UnitSpellHaste("player")
local text = ""
if showHaste then if showHaste and isSpellCaster and haste then
local hex = cfgColorToHexTable[cfg.display_haste_color] or "FFFFFFFF" local hasteHex = cfgColorToHex(cfg.display_haste_color) or "FFFFFF"
table.insert(parts, string.format("|c%s%.1f%%|r", hex, UnitSpellHaste("player"))) text = string.format("|cff%s%.1f%%|r", hasteHex, haste)
end end
if showSP then if showSP and isSpellCaster then
local school = spDefaultSchool[myclass] or 2 local defSchool = spDefaultSchool[myclass] or 2
local maxSP = GetSpellBonusDamage(school) or 0 local maxSP = GetSpellBonusDamage(defSchool) or 0
for i = 2, 7 do -- skip physical (1) local maxColor = spColors[defSchool]
for i = 2, 7 do -- skip physical (1); default school seeds the tiebreak
local v = GetSpellBonusDamage(i) or 0 local v = GetSpellBonusDamage(i) or 0
if v > maxSP then maxSP, school = v, i end if v > maxSP then
maxSP = v
maxColor = spColors[i]
end
end end
if maxSP > 0 then if maxSP > 0 then
local hex = (cfg.display_sp_color_override == "1" and cfgColorToHexTable[cfg.display_sp_color]) or spColors[school] local spHex = (cfg.display_sp_color_override == "1" and cfgColorToHex(cfg.display_sp_color)) or maxColor
table.insert(parts, string.format("|c%s+%d SP|r", hex, maxSP)) if text ~= "" then text = text .. " " end
text = text .. string.format("|cff%s+%d SP|r", spHex, maxSP)
end end
end end
playerFrame.infoTopCenterText:SetText(table.concat(parts, " ")) playerFrame.infoTopCenterText:SetText(text)
end end
-- Keep a reference to the generic UF UpdateConfig so we can chain it -- Keep a reference to the generic UF UpdateConfig so we can chain it
+3 -27
View File
@@ -4,9 +4,7 @@ pfUI:RegisterModule("raid", function ()
-- tell RaidFrame.lua pfUI replaces party frames -- tell RaidFrame.lua pfUI replaces party frames
HookAddonOrVariable("Blizzard_RaidUI", function() HookAddonOrVariable("Blizzard_RaidUI", function()
-- must reach _G: RaidFrame.lua reads this global, and pfUI.env has GROUP_REPLACE_PARTY = "1"
-- __index but no __newindex, so a bare assignment stays in the sandbox
_G.GROUP_REPLACE_PARTY = "1"
end) end)
pfUI.uf.raid = CreateFrame("Frame", "pfRaidUpdater", UIParent) pfUI.uf.raid = CreateFrame("Frame", "pfRaidUpdater", UIParent)
@@ -125,7 +123,6 @@ pfUI:RegisterModule("raid", function ()
local grid = self.petgrid local grid = self.petgrid
local function place(pet, cell, id) local function place(pet, cell, id)
pet.label = "raidpet"
pet.id = id pet.id = id
local r, g = SlotToCoord(cell, grid.fill, grid.x, grid.y) local r, g = SlotToCoord(cell, grid.fill, grid.x, grid.y)
pet:ClearAllPoints() pet:ClearAllPoints()
@@ -141,22 +138,6 @@ pfUI:RegisterModule("raid", function ()
return return
end end
-- Party shown as a raid grid: mirror slots 1..5 (player + party members)
-- into fixed cells. UpdateVisibility maps each slot to pet / partypet<N>.
if not IsInRaid() and IsInGroup() and C.unitframes.raidforgroup == "1" then
for id = 1, maxraid do
if self.pets[id] then
if id <= 5 then
place(self.pets[id], id, id)
else
self.pets[id].id = 0
self.pets[id]:Hide()
end
end
end
return
end
if C.unitframes.raidpet.collapse == "1" then if C.unitframes.raidpet.collapse == "1" then
-- Pack the pets that exist into the leading cells, no gaps. -- Pack the pets that exist into the leading cells, no gaps.
local k = 0 local k = 0
@@ -230,13 +211,8 @@ pfUI:RegisterModule("raid", function ()
this.tick = GetTime() + 1.0 this.tick = GetTime() + 1.0
this.pendingUpdate = nil this.pendingUpdate = nil
-- Without a raid there is nothing to sort, but a party shown as a raid -- don't proceed without raid
-- grid still needs its pet frames placed and mapped to the party pets. if not IsInRaid() then return end
if not IsInRaid() then
this:LayoutPets()
this:Hide()
return
end
-- clear all existing frames -- clear all existing frames
for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end
+3 -8
View File
@@ -7,12 +7,8 @@ pfUI:RegisterModule("roll", function ()
local LOOT_ROLL_NEED = string.gsub(LOOT_ROLL_NEED, "%%s|Hitem:%%d:%%d:%%d:%%d|h%[%%s%]|h%%s", "%%s") local LOOT_ROLL_NEED = string.gsub(LOOT_ROLL_NEED, "%%s|Hitem:%%d:%%d:%%d:%%d|h%[%%s%]|h%%s", "%%s")
local LOOT_ROLL_PASSED = string.gsub(LOOT_ROLL_PASSED, "%%s|Hitem:%%d:%%d:%%d:%%d|h%[%%s%]|h%%s", "%%s") local LOOT_ROLL_PASSED = string.gsub(LOOT_ROLL_PASSED, "%%s|Hitem:%%d:%%d:%%d:%%d|h%[%%s%]|h%%s", "%%s")
-- detect the "everyone passed" subject exactly as the loot scanner will capture -- try to detect the everyone string
-- it: feed a dummy item into LOOT_ROLL_ALL_PASSED and run the same LOOT_ROLL_PASSED local _, _, everyone, _ = strfind(LOOT_ROLL_ALL_PASSED, LOOT_ROLL_PASSED)
-- match. (The old strfind had no captures, so `everyone` was always nil and
-- "Everyone has passed on: X" got counted as a fake roller.)
local everyoneSample = string.gsub(LOOT_ROLL_ALL_PASSED, "%%s", "x")
local everyone = cmatch(everyoneSample, LOOT_ROLL_PASSED)
pfUI.roll.blacklist = { YOU, everyone } pfUI.roll.blacklist = { YOU, everyone }
pfUI.roll.cache = {} pfUI.roll.cache = {}
@@ -47,7 +43,6 @@ pfUI:RegisterModule("roll", function ()
local _, _, itemLink = string.find(hyperlink, "(item:%d+:%d+:%d+:%d+)") local _, _, itemLink = string.find(hyperlink, "(item:%d+:%d+:%d+:%d+)")
local itemName = C_Item.GetItemInfo(itemLink) local itemName = C_Item.GetItemInfo(itemLink)
if not itemName then return end -- uncached item: avoid cache[nil] "table index is nil"
-- delete obsolete tables -- delete obsolete tables
if pfUI.roll.cache[itemName] and pfUI.roll.cache[itemName]["TIMESTAMP"] < GetTime() - 60 then if pfUI.roll.cache[itemName] and pfUI.roll.cache[itemName]["TIMESTAMP"] < GetTime() - 60 then
@@ -239,7 +234,7 @@ pfUI:RegisterModule("roll", function ()
f.time.bar:SetAllPoints(f.time) f.time.bar:SetAllPoints(f.time)
f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"]) f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"])
f.time.bar:SetMinMaxValues(0, 100) f.time.bar:SetMinMaxValues(0, 100)
local r, g, b, a = GetStringColor(C.appearance.border.color) local r, g, b, a = strsplit(",", C.appearance.border.color)
f.time.bar:SetStatusBarColor(r, g, b) f.time.bar:SetStatusBarColor(r, g, b)
f.time.bar:SetValue(20) f.time.bar:SetValue(20)
f.time.bar:SetScript("OnUpdate", function() f.time.bar:SetScript("OnUpdate", function()
+4 -4
View File
@@ -119,7 +119,7 @@ pfUI:RegisterModule("screenshot", function ()
end end
function pfUI.screenshot:CHAT_MSG_SYSTEM() function pfUI.screenshot:CHAT_MSG_SYSTEM()
local standing, rep = string.match(arg1, FACTION_STANDING_CHANGEDregex) local _,_, standing, rep = string.find(arg1, FACTION_STANDING_CHANGEDregex)
if standing and rep then if standing and rep then
local dt = date("%a, %b %d, %Y %X") local dt = date("%a, %b %d, %Y %X")
local loc = string.format("%s - %s",GetRealZoneText(),GetSubZoneText()) local loc = string.format("%s - %s",GetRealZoneText(),GetSubZoneText())
@@ -150,13 +150,13 @@ pfUI:RegisterModule("screenshot", function ()
end end
function pfUI.screenshot:CHAT_MSG_LOOT() function pfUI.screenshot:CHAT_MSG_LOOT()
local item, amount = string.match(arg1, LOOT_ITEM_SELF_MULTIPLEregex) local _,_, item, amount = string.find(arg1, LOOT_ITEM_SELF_MULTIPLEregex)
if amount then -- ignore stacks if amount then -- ignore stacks
return return
else else
item = string.match(arg1, LOOT_ITEM_SELFregex) _,_, item = string.find(arg1, LOOT_ITEM_SELFregex)
if item then if item then
local itemColor, itemString, itemName = string.match(item, "^(|c%x+)|H(.+)|h(%[.+%])") local _, _, itemColor, itemString, itemName = string.find(item, "^(|c%x+)|H(.+)|h(%[.+%])")
local quality = color2quality[itemColor] local quality = color2quality[itemColor]
if quality and quality >= tonumber(C.screenshot.loot) then if quality and quality >= tonumber(C.screenshot.loot) then
local dt = date("%a, %b %d, %Y %X") local dt = date("%a, %b %d, %Y %X")
+36 -1
View File
@@ -1,4 +1,39 @@
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
@@ -44,4 +79,4 @@ pfUI:RegisterModule("skin", function ()
else else
UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE") UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
end end
end) end)
+76 -74
View File
@@ -4,7 +4,7 @@ pfUI:RegisterModule("socialmod", function ()
pfUI.socialmod:RegisterEvent("CHAT_MSG_SYSTEM") pfUI.socialmod:RegisterEvent("CHAT_MSG_SYSTEM")
pfUI.socialmod:SetScript("OnEvent", function() pfUI.socialmod:SetScript("OnEvent", function()
local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS) local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS)
name = name or cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S) name = cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S)
if name and playerdb[name] and playerdb[name].cname then if name and playerdb[name] and playerdb[name].cname then
playerdb[name].lastseen = date("%a %d-%b-%Y") playerdb[name].lastseen = date("%a %d-%b-%Y")
end end
@@ -77,19 +77,20 @@ pfUI:RegisterModule("socialmod", function ()
local off = FauxScrollFrame_GetOffset(FriendsFrameFriendsScrollFrame) local off = FauxScrollFrame_GetOffset(FriendsFrameFriendsScrollFrame)
for i=1, FRIENDS_TO_DISPLAY do for i=1, FRIENDS_TO_DISPLAY do
local info = C_FriendList.GetFriendInfoByIndex(off + i) local name, level, class, zone, connected, status = GetFriendInfo(off + i)
if not info or not info.name or info.name == _G.UNKNOWN then break end if not name or name == _G.UNKNOWN then break end
local name = info.name
local friendName = _G["FriendsFrameFriendButton"..i.."ButtonTextName"] local friendName = _G["FriendsFrameFriendButton"..i.."ButtonTextName"]
local friendLoc = _G["FriendsFrameFriendButton"..i.."ButtonTextNameLocation"] local friendLoc = _G["FriendsFrameFriendButton"..i..FRIENDS_NAME_LOCATION]
local friendInfo = _G["FriendsFrameFriendButton"..i.."ButtonTextInfo"] local friendInfo = _G["FriendsFrameFriendButton"..i.."ButtonTextInfo"]
local caption = friendName or friendLoc local caption = friendName or friendLoc
if info.connected then if connected then
local ccolor = PFUI_CLASS_COLORS[info.classFilename] if not class or class == _G.UNKNOWN then break end
local status = info.afk and CHAT_FLAG_AFK or info.dnd and CHAT_FLAG_DND or "" local ccolor = PFUI_CLASS_COLORS[L["class"][class]] or { 1, 1, 1 }
local zone = ( info.area == playerzone and "|cffffffff" or "|cffcccccc" ) .. info.area .. "|r" local lcolor = GetDifficultyColor(tonumber(level)) or { 1, 1, 1 }
local cname = ccolor:WrapTextInColorCode(name)
zone = ( zone == playerzone and "|cffffffff" or "|cffcccccc" ) .. zone .. "|r"
local cname = rgbhex(ccolor) .. name .. "|r"
if playerdb[name] then if playerdb[name] then
playerdb[name].lastseen = date("%a %d-%b-%Y") playerdb[name].lastseen = date("%a %d-%b-%Y")
playerdb[name].cname = cname playerdb[name].cname = cname
@@ -97,20 +98,20 @@ pfUI:RegisterModule("socialmod", function ()
if friendName then if friendName then
friendName:SetText(cname) friendName:SetText(cname)
friendLoc:SetFormattedText(TEXT(FRIENDS_LIST_TEMPLATE), zone, status) friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), zone, status))
else else
friendLoc:SetFormattedText(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status) friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status))
end end
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), info.level, info.className) friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), level, class))
caption:SetVertexColor(1,1,1,.9) caption:SetVertexColor(1,1,1,.9)
friendInfo:SetVertexColor(1,1,1,.9) friendInfo:SetVertexColor(1,1,1,.9)
else else
if playerdb[name] and playerdb[name].cname and playerdb[name].level and playerdb[name].lastseen then if playerdb[name] and playerdb[name].cname and playerdb[name].level and playerdb[name].lastseen then
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname) caption:SetText(format(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname))
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen) friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen))
else else
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r") caption:SetText(format(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r"))
friendInfo:SetText(TEXT(UNKNOWN)) friendInfo:SetText(TEXT(UNKNOWN))
end end
@@ -123,73 +124,74 @@ pfUI:RegisterModule("socialmod", function ()
do -- add colors to who list do -- add colors to who list
hooksecurefunc("WhoList_Update", function() hooksecurefunc("WhoList_Update", function()
local num, max = C_FriendList.GetNumWhoResults() local num, max = GetNumWhoResults()
local off = FauxScrollFrame_GetOffset(WhoListScrollFrame) local off = FauxScrollFrame_GetOffset(WhoListScrollFrame)
local playerzone = GetRealZoneText() local playerzone = GetRealZoneText()
local playerrace = UnitRace("player") local playerrace = UnitRace("player")
local playerguild = GetGuildInfo("player") local playerguild = GetGuildInfo("player")
if num + 1 >= MAX_WHOS_FROM_SERVER then
WhoFrameTotals:SetFormattedText("|cffffffff" .. GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num) .. " |cffaaaaaa" .. WHO_FRAME_SHOWN_TEMPLATE, max, MAX_WHOS_FROM_SERVER)
else
WhoFrameTotals:SetFormattedText("|cffffffff" .. GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num) .. " |cffaaaaaa" .. WHO_FRAME_SHOWN_TEMPLATE, num, num)
end
for i=1, WHOS_TO_DISPLAY do for i=1, WHOS_TO_DISPLAY do
local info = C_FriendList.GetWhoInfo(off + i) local name, guild, level, race, class, zone = GetWhoInfo(off + i)
if info then local displayedText = ""
-- filename is the class token, so no L["class"] reversal is needed
local class = info.filename
_G["WhoFrameButton"..i.."Name"]:SetTextColor(NORMAL_FONT_COLOR:GetRGB()) if num + 1 >= MAX_WHOS_FROM_SERVER then
displayedText = format(WHO_FRAME_SHOWN_TEMPLATE, MAX_WHOS_FROM_SERVER)
if (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 1) then WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), max).." |cffaaaaaa"..displayedText)
if (info.area == playerzone) then else
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1) displayedText = format(WHO_FRAME_SHOWN_TEMPLATE, num)
else WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), num).." |cffaaaaaa"..displayedText)
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
end
elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 2) then
if (info.fullGuildName == playerguild) then
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
else
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
end
elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 3) then
if (info.raceStr == playerrace) then
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
else
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
end
end
if class then
local classicon = _G["WhoFrameButton"..i].classicon
local coords = CLASS_ICON_TCOORDS[class]
local color = PFUI_CLASS_COLORS[class]
-- do we have classicons? (skin enabled?)
if classicon then
_G["WhoFrameButton"..i.."Class"]:SetTextColor(0,0,0,0)
_G["WhoFrameButton"..i.."Name"]:SetTextColor(color.r,color.g,color.b,1)
if coords then
classicon:Show()
classicon:SetTexCoord(unpack(coords))
else
classicon:Hide()
end
else
_G["WhoFrameButton"..i.."Class"]:SetTextColor(color.r,color.g,color.b,1)
end
end
local color = GetDifficultyColor(info.level)
_G["WhoFrameButton"..i.."Level"]:SetTextColor(color.r, color.g, color.b)
end end
class = L["class"][class]
_G["WhoFrameButton"..i.."Name"]:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
if (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 1) then
if (zone == playerzone) then
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
else
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
end
elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 2) then
if (guild == playerguild) then
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
else
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
end
elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 3) then
if (race == playerrace) then
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
else
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
end
end
if class then
local classicon = _G["WhoFrameButton"..i].classicon
local coords = CLASS_ICON_TCOORDS[class]
local color = PFUI_CLASS_COLORS[class]
-- do we have classicons? (skin enabled?)
if classicon then
_G["WhoFrameButton"..i.."Class"]:SetTextColor(0,0,0,0)
_G["WhoFrameButton"..i.."Name"]:SetTextColor(color.r,color.g,color.b,1)
if coords then
classicon:Show()
classicon:SetTexCoord(unpack(coords))
else
classicon:Hide()
end
else
_G["WhoFrameButton"..i.."Class"]:SetTextColor(color.r,color.g,color.b,1)
end
end
local color = GetDifficultyColor(level)
_G["WhoFrameButton"..i.."Level"]:SetTextColor(color.r, color.g, color.b)
end end
end) end)
end end
+1 -4
View File
@@ -75,10 +75,7 @@ pfUI:RegisterModule("superwow", function ()
DEFAULT_CHAT_FRAME:AddMessage("-> https://github.com/balakethelock/SuperWoW/releases/") DEFAULT_CHAT_FRAME:AddMessage("-> https://github.com/balakethelock/SuperWoW/releases/")
end end
-- compare numerically: an exact string match silently drops this on any if SUPERWOW_VERSION == "1.5" then
-- SuperWoW release past 1.5
local swVersion = tonumber(SUPERWOW_VERSION)
if swVersion and swVersion >= 1.5 then
QueueFunction(function() QueueFunction(function()
local pfCombatText_AddMessage = _G.CombatText_AddMessage local pfCombatText_AddMessage = _G.CombatText_AddMessage
_G.CombatText_AddMessage = function(message, a, b, c, d, e, f) _G.CombatText_AddMessage = function(message, a, b, c, d, e, f)
+288 -72
View File
@@ -1,10 +1,19 @@
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
-- Spell.dbc bits used to mirror server-side swing-reset rules.
local FLAG_AUTOATTACK = tonumber("0x08", 16) -- SPELL_INTERRUPT_FLAG_AUTOATTACK
local ATTR_KEEP_SWINGS = tonumber("0x20000", 16) -- SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS
local ATTR_ON_NEXT_SWING = tonumber("0x04", 16) -- SPELL_ATTR_ON_NEXT_SWING
-- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max) -- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max)
local S = { local S = {
mhTimer = 0, mhTimerMax = 1, mhTimer = 0, mhTimerMax = 1,
@@ -15,6 +24,8 @@ 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,
@@ -25,11 +36,19 @@ pfUI:RegisterModule("swingtimer", function ()
onSwingCache = {}, onSwingCache = {},
} }
-- On-next-swing spells replace the next auto-attack swing. -- 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
-- ATTR_ON_NEXT_SWING: spell replaces 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)
if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end
local result = C_Spell.IsNextMeleeSpell(spellId) local attr = GetSpellRecField(spellId, "attributes") or 0
local result = bit.band(attr, ATTR_ON_NEXT_SWING) ~= 0
S.onSwingCache[spellId] = result S.onSwingCache[spellId] = result
return result return result
end end
@@ -190,19 +209,36 @@ pfUI:RegisterModule("swingtimer", function ()
CreateBackdropShadow(pfUI.swingtimer.offhand) CreateBackdropShadow(pfUI.swingtimer.offhand)
-- Ranged bar -- Ranged bar
-- Hunters fill from the center: the bar drains toward the middle during pfUI.swingtimer.ranged = CreateFrame("Frame", "pfSwingTimerRanged", UIParent)
-- 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")
@@ -254,9 +290,7 @@ pfUI:RegisterModule("swingtimer", function ()
UpdateMovable(pfUI.swingtimer.ranged) UpdateMovable(pfUI.swingtimer.ranged)
-- OH weapon detection -- OH weapon detection
-- OH weapon detection: slot 17 (off hand) accepts one-hand (13) and off-hand local OH_WEAPON_TYPES = { [13]=true, [21]=true }
-- weapons (22); main-hand-only (21) can never sit there.
local OH_WEAPON_TYPES = { [13]=true, [22]=true }
local function HasOffhandWeapon() local function HasOffhandWeapon()
local l = GetInventoryItemLink("player", 17) local l = GetInventoryItemLink("player", 17)
if not l then return false end if not l then return false end
@@ -274,41 +308,67 @@ pfUI:RegisterModule("swingtimer", function ()
S.raSpeed = (rs and rs > 0) and rs or 0 S.raSpeed = (rs and rs > 0) and rs or 0
end end
-- Start* restart a countdown from a PLAYER_SWING reset. `remaining` can be -- Reset MH countdown to full speed (server confirmed swing)
-- shorter than the weapon speed (parry haste, ranged wind-up), so the bar local function ResetMH()
-- length stays the full swing and the fill starts part-way along. if S.mhFrozenAt then
local function StartMH(remaining) S.mhFrozenAt = nil
end
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
pfUI.swingtimer.mhGraceAt = nil -- cancel any pending hide pfUI.swingtimer.mhGraceAt = nil -- cancel any pending hide
S.mhTimerMax = math.max(S.mhSpeed, remaining) S.mhTimerMax = S.mhSpeed
S.mhTimer = remaining S.mhTimer = S.mhSpeed
S.mhActive = true S.mhActive = true
pfUI.swingtimer.mainhand:Show() pfUI.swingtimer.mainhand:Show()
pfUI.swingtimer:Show() pfUI.swingtimer:Show()
end end
local function StartOH(remaining) -- Reset OH countdown to full speed
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 = math.max(S.ohSpeed, remaining) S.ohTimerMax = S.ohSpeed
S.ohTimer = remaining S.ohTimer = S.ohSpeed
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
local function StartRanged(remaining) -- Reset ranged countdown. replaceMH=true (Hunter Auto Shot, Throw) stops
-- 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
S.raTimerMax = math.max(S.raSpeed, remaining) if replaceMH then
S.raTimer = remaining S.mhActive = false
pfUI.swingtimer.mainhand:Hide()
end
S.raTimerMax = S.raSpeed
S.raTimer = S.raSpeed
S.raActive = true S.raActive = true
pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA) if isHunter then
pfUI.swingtimer.ranged:SetValue(isHunter and 1 or 0) pfUI.swingtimer.ranged.left:ClearAllPoints()
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
@@ -430,17 +490,23 @@ 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 PLAYER_SWING arrives. -- hiding the bar, to bridge the 1-2 frame gap until AUTO_ATTACK_SELF arrives.
-- If PLAYER_SWING arrives during the grace period it resets the timer -- If AUTO_ATTACK_SELF 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
S.mhTimer = S.mhTimer - delta -- Don't tick while frozen (swing-delay spell like Slam is being cast)
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 not pfUI.swingtimer.mhGraceAt then if S.mhFrozenAt 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
@@ -530,10 +596,10 @@ pfUI:RegisterModule("swingtimer", function ()
end end
pfUI.swingtimer.mainhand:SetStatusBarColor(curR, curG, curB, mhA) pfUI.swingtimer.mainhand:SetStatusBarColor(curR, curG, curB, mhA)
if sw_showtext then if sw_showtext then
pfUI.swingtimer.mainhand.text:SetFormattedText("%.1f", math.floor(S.mhTimer * 10) / 10) pfUI.swingtimer.mainhand.text:SetText(string.format("%.1f", math.floor(S.mhTimer * 10) / 10))
end end
if sw_showspeed and S.mhSpeed > 0 then if sw_showspeed and S.mhSpeed > 0 then
pfUI.swingtimer.mainhand.speed:SetFormattedText("%.2f", S.mhSpeed) pfUI.swingtimer.mainhand.speed:SetText(string.format("%.2f", S.mhSpeed))
end end
anyActive = true anyActive = true
end end
@@ -564,10 +630,10 @@ pfUI:RegisterModule("swingtimer", function ()
end end
end end
if sw_showtext then if sw_showtext then
pfUI.swingtimer.offhand.text:SetFormattedText("%.1f", math.floor(S.ohTimer * 10) / 10) pfUI.swingtimer.offhand.text:SetText(string.format("%.1f", math.floor(S.ohTimer * 10) / 10))
end end
if sw_showspeed and S.ohSpeed > 0 then if sw_showspeed and S.ohSpeed > 0 then
pfUI.swingtimer.offhand.speed:SetFormattedText("%.2f", S.ohSpeed) pfUI.swingtimer.offhand.speed:SetText(string.format("%.2f", S.ohSpeed))
end end
anyActive = true anyActive = true
elseif not sw_showoh then elseif not sw_showoh then
@@ -579,28 +645,53 @@ 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
pfUI.swingtimer.ranged:SetStatusBarColor(raR, raG, raB, raA) local elapsed = S.raTimerMax - remaining
pfUI.swingtimer.ranged:SetValue((remaining - DEADZONE) / (S.raTimerMax - DEADZONE)) local phase1dur = 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
pfUI.swingtimer.ranged:SetStatusBarColor(rwR, rwG, rwB, rwA) local p = 1 - (remaining / DEADZONE)
pfUI.swingtimer.ranged:SetValue(1 - (remaining / DEADZONE)) local w = sw_width * p
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
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10) pfUI.swingtimer.ranged.text:SetText(string.format("%.1f", math.floor(remaining * 10) / 10))
else else
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor((remaining - 0.5) * 10) / 10) pfUI.swingtimer.ranged.text:SetText(string.format("%.1f", math.floor((remaining - 0.5) * 10) / 10))
end end
end end
else else
pfUI.swingtimer.ranged:SetValue(1 - (remaining / S.raTimerMax)) local progress = 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:SetText(string.format("%.1f", math.floor(remaining * 10) / 10))
end end
end end
if sw_showspeed and S.raSpeed > 0 then if sw_showspeed and S.raSpeed > 0 then
pfUI.swingtimer.ranged.speed:SetFormattedText("%.2f", S.raSpeed) pfUI.swingtimer.ranged.speed:SetText(string.format("%.2f", S.raSpeed))
end end
local raProgress = 1 - (S.raTimer / S.raTimerMax) local raProgress = 1 - (S.raTimer / S.raTimerMax)
local raMarkerX = raProgress * sw_width local raMarkerX = raProgress * sw_width
@@ -635,46 +726,169 @@ 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. Detect dynamically via the
-- absent AUTOATTACK interrupt flag (8); spells with that bit reset on
-- SPELL_GO_SELF so freezing isn't necessary. Subsumes the old hardcoded
-- swingDelaySpells list (no list maintenance for new Slam-style spells).
if S.mhActive then
local iflags = GetSpellRecField(arg1, "interruptFlags") or 0
if bit.band(iflags, FLAG_AUTOATTACK) == 0 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
-- Mirror the server rule for "does this spell reset the auto-attack
-- swing" (Spell::IsMeleeAttackResetSpell in Turtle's core):
-- InterruptFlags has SPELL_INTERRUPT_FLAG_AUTOATTACK (0x08)
-- AND AttributesEx2 lacks NOT_RESET_AUTO_ACTIONS (0x20000).
-- If neither path resets and we're holding a frozen-swing-during-cast
-- (mhFrozenAt set by SPELL_START_SELF for non-AUTOATTACK spells), this
-- is a Slam-style cast — push the timer forward by the cast duration
-- so the bar resumes from where it paused.
local iflags = GetSpellRecField(spellId, "interruptFlags") or 0
if bit.band(iflags, FLAG_AUTOATTACK) ~= 0
and bit.band(GetSpellRecField(spellId, "attributesEx2") or 0, ATTR_KEEP_SWINGS) == 0 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("PLAYER_SWING") events:RegisterEvent("AUTO_ATTACK_SELF")
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:RegisterEvent("PLAYER_DEAD") events:RegisterEvent("UNIT_DIED")
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 == "PLAYER_SWING" then if event == "AUTO_ATTACK_SELF" then
-- ClassicAPI tracks the server's swing clock (white hits, on-next-swing local hitInfo = arg4 or 0
-- abilities, swing-reset casts, parry haste, extra attacks, weapon local isOffhand = bit.band(hitInfo, HITINFO_LEFTSWING) ~= 0
-- swaps) and hands over the seconds left until the next swing. local noAction = bit.band(hitInfo, HITINFO_NOACTION) ~= 0
if arg2 == Enum.PlayerSwingType.MainHand then -- NOACTION means server did not advance swing clock (dodge/parry/miss).
StartMH(arg1) -- Only skip if the timer is already running - if it's not active yet
elseif arg2 == Enum.PlayerSwingType.OffHand then -- (first swing ever), we still want to start it so the bar appears.
StartOH(arg1) if noAction then
elseif arg2 == Enum.PlayerSwingType.Ranged then if isOffhand and S.ohActive then return end
StartRanged(arg1) if not isOffhand and S.mhActive then return end
end end
elseif event == "UNIT_SPELLCAST_SENT" then -- Extra attack detection: if timer still has >20% remaining for that hand,
-- arg4 = spellID: an on-next-swing ability (HS / Cleave / Maul) queued -- the server did NOT reset the swing clock -> this is an extra attack, skip.
local kind = ClassifyOnSwingSpell(arg4) -- Use 20% here (SP_SwingTimer's ShouldResetTimer threshold).
if kind then -- Exception: if timer is already at 0 (expired), always accept.
S.useSpellQueueEvent = true if isOffhand then
SetQueuedKind(kind) local pct = S.ohActive and (S.ohTimer / S.ohTimerMax) or 0
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 == "UNIT_SPELLCAST_SUCCEEDED" then elseif event == "AUTO_ATTACK_OTHER" then
-- arg3 = spellID: the on-next-swing ability resolved, which consumes -- Parry haste: enemy attacked the player and player parried
-- the queue, so drop the queued color local targetGuid = arg2
if IsOnSwingSpell(arg3) then if not targetGuid or not IsPlayerGuid(targetGuid) then return end
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false local victimState = arg5 or 0
-- 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
@@ -726,8 +940,10 @@ pfUI:RegisterModule("swingtimer", function ()
S.cleaveQueued = false S.cleaveQueued = false
S.maulQueued = false S.maulQueued = false
elseif event == "PLAYER_DEAD" then elseif event == "UNIT_DIED" then
ResetAll() if IsPlayerGuid(arg1) then
ResetAll()
end
end end
end) end)
+18 -6
View File
@@ -100,10 +100,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
CreateBackdropShadow(KLHTM_Frame) CreateBackdropShadow(KLHTM_Frame)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background) local r, g, b, a = strsplit(",", C.chat.global.background)
KLHTM_Frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) KLHTM_Frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border) local r, g, b, a = strsplit(",", C.chat.global.border)
KLHTM_Frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) KLHTM_Frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end end
@@ -199,10 +199,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
CreateBackdropShadow(TWTMain) CreateBackdropShadow(TWTMain)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background) local r, g, b, a = strsplit(",", C.chat.global.background)
TWTMain.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) TWTMain.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border) local r, g, b, a = strsplit(",", C.chat.global.border)
TWTMain.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) TWTMain.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end end
@@ -328,10 +328,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
CreateBackdropShadow(SW_BarFrame1) CreateBackdropShadow(SW_BarFrame1)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background) local r, g, b, a = strsplit(",", C.chat.global.background)
SW_BarFrame1.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) SW_BarFrame1.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border) local r, g, b, a = strsplit(",", C.chat.global.border)
SW_BarFrame1.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) SW_BarFrame1.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end end
@@ -866,6 +866,9 @@ 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)
@@ -1194,7 +1197,16 @@ 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)
+4 -101
View File
@@ -244,10 +244,10 @@ pfUI:RegisterModule("thirdparty", function()
end end
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background) local r, g, b, a = strsplit(",", C.chat.global.background)
window.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) window.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border) local r, g, b, a = strsplit(",", C.chat.global.border)
window.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) window.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end end
@@ -277,103 +277,6 @@ pfUI:RegisterModule("thirdparty", function()
end end
end) end)
-- GreedMeter Damage Meter
-- Vanilla: https://github.com/iGreed/GreedMeter
-- GreedMeter builds its windows lazily and supports several of them, so the
-- frames are resolved at call time. The primary window takes the "damage"
-- dock slot; a second window (GreedMeter.UI.frames[2]) takes the "threat"
-- slot so the two tile the panel side-by-side. Docking triggers a
-- LayoutBars/RefreshFrame so the bars refit the new width.
HookAddonOrVariable("GreedMeter", function()
local UI = GreedMeter.UI
local function frameAt(i)
return UI and UI.frames and UI.frames[i]
end
local function primary()
return (UI and UI.mainFrame) or getglobal("GreedMeterFrame1")
end
local function relayout(frame)
if not UI then return end
if UI.LayoutBars then UI:LayoutBars(frame) end
if UI.RefreshFrame then UI:RefreshFrame(frame) end
end
-- Builds a docktable for a window resolved through getframe(). left picks
-- the left half in split mode; the primary window uses the right half.
local function BuildDock(name, getframe, left)
return { "greedmeter", "GreedMeter", name,
function() -- single: fill the panel
local frame = getframe()
if not frame then return end
frame:ClearAllPoints()
frame:SetAllPoints(pfUI.chat.right)
frame:SetWidth(pfUI.chat.right:GetWidth())
relayout(frame)
end,
function() -- dual: take one half
local frame = getframe()
if not frame then return end
frame:ClearAllPoints()
if left then
frame:SetPoint("TOPLEFT", pfUI.chat.right, "TOPLEFT", 0, 0)
frame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOM", 0, 0)
else
frame:SetPoint("TOPLEFT", pfUI.chat.right, "TOP", 0, 0)
frame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, 0)
end
frame:SetWidth(pfUI.chat.right:GetWidth() / 2)
relayout(frame)
end,
function() -- show
local frame = getframe()
if frame then frame:Show() relayout(frame) end
end,
function() -- hide
local frame = getframe()
if frame then frame:Hide() end
end
}
end
local threatdock = BuildDock("GreedMeterFrame2", function() return frameAt(2) end, true)
pfUI.thirdparty.meters:RegisterMeter("damage", BuildDock("GreedMeterFrame1", primary, false))
-- pfUI's dock decides fill-vs-split by whether the threat slot is filled,
-- not by counting windows, so reconcile that slot whenever GreedMeter adds
-- or removes one. Only the first two windows fit; any beyond that float.
local function SyncSecond()
if not pfUI.thirdparty.meters.damage then return end -- dock disabled
local has2 = frameAt(2) ~= nil
if has2 and not pfUI.thirdparty.meters.threat then
pfUI.thirdparty.meters.threat = threatdock
elseif not has2 and pfUI.thirdparty.meters.threat == threatdock then
pfUI.thirdparty.meters.threat = nil
end
pfUI.thirdparty.meters:Resize()
-- match the second window's visibility to the panel (the primary tracks it)
local f1, f2 = primary(), frameAt(2)
if f2 then
if f1 and f1:IsShown() then f2:Show() relayout(f2) else f2:Hide() end
end
end
-- AddFrame and the login-time layout restore both go through
-- CreateMeterFrame; RemoveFrame handles closing a window.
local origCreate = UI.CreateMeterFrame
UI.CreateMeterFrame = function(self, isPrimary, copyFrom)
local frame = origCreate(self, isPrimary, copyFrom)
SyncSecond()
return frame
end
local origRemove = UI.RemoveFrame
UI.RemoveFrame = function(self, frame)
origRemove(self, frame)
SyncSecond()
end
end)
-- DPSMate Damage Meter -- DPSMate Damage Meter
-- Vanilla: https://github.com/Geigerkind/DPSMate -- Vanilla: https://github.com/Geigerkind/DPSMate
-- TBC: https://github.com/Geigerkind/DPSMateTBC -- TBC: https://github.com/Geigerkind/DPSMateTBC
@@ -447,10 +350,10 @@ pfUI:RegisterModule("thirdparty", function()
CreateBackdropShadow(frame) CreateBackdropShadow(frame)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background) local r, g, b, a = strsplit(",", C.chat.global.background)
frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border) local r, g, b, a = strsplit(",", C.chat.global.border)
frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end end
end end
+3 -131
View File
@@ -166,9 +166,9 @@ pfUI:RegisterModule("tooltip", function ()
end end
if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then
pfUI.tooltipStatusBar.HP:SetFormattedText("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax)) pfUI.tooltipStatusBar.HP:SetText(string.format("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax)))
elseif hpmax > 0 then elseif hpmax > 0 then
pfUI.tooltipStatusBar.HP:SetFormattedText("%s%%", ceil(hp/hpmax*100)) pfUI.tooltipStatusBar.HP:SetText(string.format("%s%%", ceil(hp/hpmax*100)))
else else
pfUI.tooltipStatusBar.HP:SetText("") pfUI.tooltipStatusBar.HP:SetText("")
end end
@@ -185,106 +185,14 @@ pfUI:RegisterModule("tooltip", function ()
GameTooltipStatusBar.SetStatusBarColor_orig = GameTooltipStatusBar.SetStatusBarColor GameTooltipStatusBar.SetStatusBarColor_orig = GameTooltipStatusBar.SetStatusBarColor
GameTooltipStatusBar.SetStatusBarColor = function() return end GameTooltipStatusBar.SetStatusBarColor = function() return end
-- A row of buff icons anchored above the tooltip for mouseover units.
-- Icons come from a ClassicAPI object pool: ReleaseAll hides the whole
-- set each refresh, then we Acquire and re-anchor left-to-right.
local BUFF_SIZE, BUFF_SPACING, BUFF_MAX = 20, 2, 32
local auraSlots = {} -- reusable GetAuraSlots buffer for UpdateBuffs
pfUI.tooltip.buffs = CreateFrame("Frame", "pfTooltipBuffs", GameTooltip)
pfUI.tooltip.buffs:SetPoint("BOTTOMLEFT", GameTooltipStatusBar, "TOPLEFT", 0, default_border + 2)
pfUI.tooltip.buffs:SetHeight(BUFF_SIZE)
local function CreateBuffIcon()
local icon = CreateFrame("Frame", nil, pfUI.tooltip.buffs)
icon:SetSize(BUFF_SIZE, BUFF_SIZE)
icon.texture = icon:CreateTexture(nil, "BACKGROUND")
icon.texture:SetTexCoord(.07, .93, .07, .93)
icon.texture:SetAllPoints(icon)
CreateBackdrop(icon)
icon.stacks = icon:CreateFontString(nil, "OVERLAY", "GameFontNormal")
icon.stacks:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 1, 0)
icon.stacks:SetFont(C.tooltip.font_tooltip, C.tooltip.font_tooltip_size, "OUTLINE")
icon.stacks:SetTextColor(1, 1, 1, 1)
icon.timer = icon:CreateFontString(nil, "OVERLAY", "GameFontNormal")
icon.timer:SetPoint("CENTER", icon, "CENTER", 0, 0)
icon.timer:SetFont(C.tooltip.font_tooltip, C.tooltip.font_tooltip_size, "OUTLINE")
icon.timer:SetTextColor(1, 1, 1, 1)
return icon
end
pfUI.tooltip.buffpool = CreateObjectPool(CreateBuffIcon, function(_, icon)
icon:Hide()
icon:ClearAllPoints()
end)
-- Tick the remaining-time text on the visible icons. OnUpdate only fires
-- while the row is shown, so it stops as soon as the tooltip hides.
pfUI.tooltip.buffs:SetScript("OnUpdate", function()
local now = GetTime()
if (this.tick or 0) > now then return end
this.tick = now + 0.1
for icon in pfUI.tooltip.buffpool:EnumerateActive() do
local timeleft = icon.expirationTime and icon.expirationTime > 0 and (icon.expirationTime - now) or 0
icon.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
end
end)
function pfUI.tooltip:UpdateBuffs(unit)
pfUI.tooltip.buffpool:ReleaseAll()
if C.tooltip.showbuffs ~= "1" or not unit or unit == "none" then
pfUI.tooltip.buffs:Hide()
return
end
local prev, count = nil, 0
-- one GetAuraSlots enumeration + positional by-slot reads: no per-aura
-- table and no per-index re-walk of the aura array
local n = ScanAuraSlots(unit, "HELPFUL", auraSlots, BUFF_MAX)
for i = 1, n do
local name, texture, applications, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unit, auraSlots[i])
if not name then break end
count = count + 1
local icon = pfUI.tooltip.buffpool:Acquire()
icon.texture:SetTexture(texture)
icon.stacks:SetText(applications and applications > 1 and applications or "")
icon.expirationTime = expirationTime
local timeleft = expirationTime and expirationTime > 0 and (expirationTime - GetTime()) or 0
icon.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
if prev then
icon:SetPoint("LEFT", prev, "RIGHT", BUFF_SPACING, 0)
else
icon:SetPoint("BOTTOMLEFT", pfUI.tooltip.buffs, "BOTTOMLEFT", 0, 0)
end
icon:Show()
prev = icon
end
if count > 0 then
pfUI.tooltip.buffs:SetWidth(count * BUFF_SIZE + (count - 1) * BUFF_SPACING)
pfUI.tooltip.buffs:Show()
else
pfUI.tooltip.buffs:Hide()
end
end
function pfUI.tooltip:Update() function pfUI.tooltip:Update()
local unit = pfUI.tooltip:GetUnit() local unit = pfUI.tooltip:GetUnit()
pfUI.tooltip:UpdateBuffs(unit)
if unit == "none" then if unit == "none" then
-- process item tooltips
if C.tooltip.itemid == "1" and GameTooltip:HasItem() then if C.tooltip.itemid == "1" and GameTooltip:HasItem() then
local _, _, itemID = GameTooltip:GetItem() local _, _, itemID = GameTooltip:GetItem()
GameTooltip:AddLine(T["ItemID"] .. ": " .. itemID, .25,.5,1) GameTooltip:AddLine(T["ItemID"] .. ": " .. itemID, .25,.5,1)
GameTooltip:Show() GameTooltip:Show()
elseif C.tooltip.spellid == "1" and GameTooltip:HasSpell() then
local _, _, spellID = GameTooltip:GetSpell()
GameTooltip:AddLine(T["SpellID"] .. ": " .. spellID, .25,.5,1)
GameTooltip:Show()
end end
return return
@@ -351,13 +259,6 @@ pfUI:RegisterModule("tooltip", function ()
end end
end end
if C.tooltip.unitid == "1" and not UnitIsPlayer(unit) then
local npcID = C_CreatureInfo.GetCreatureID(UnitGUID(unit))
if npcID then
GameTooltip:AddLine(T["UnitID"] .. ": " .. npcID, .25,.5,1)
end
end
if hp and hpm then if hp and hpm then
if hp >= 1000 then hp = round(hp / 1000, 1) .. "k" end if hp >= 1000 then hp = round(hp / 1000, 1) .. "k" end
if hpm >= 1000 then hpm = round(hpm / 1000, 1) .. "k" end if hpm >= 1000 then hpm = round(hpm / 1000, 1) .. "k" end
@@ -365,33 +266,4 @@ pfUI:RegisterModule("tooltip", function ()
end end
GameTooltip:Show() GameTooltip:Show()
end end
if C.tooltip.aurasource == "1" or C.tooltip.spellid == "1" then
hooksecurefunc(GameTooltip, "SetUnitAura", function(self, ...)
local aura = C_UnitAuras.GetAuraDataByIndex(unpack(arg))
if not aura then return end
if C.tooltip.aurasource == "1" then
local caster = aura.sourceUnit and UnitName(aura.sourceUnit)
if not caster and aura.sourceGUID then
caster = UnitNameFromGUID(aura.sourceGUID)
end
if caster and caster ~= "" then
local classToken
if aura.sourceUnit and UnitIsPlayer(aura.sourceUnit) then
classToken = UnitClassBase(aura.sourceUnit)
elseif aura.sourceGUID then
classToken = select(2, GetPlayerInfoByGUID(aura.sourceGUID))
end
if classToken then
caster = PFUI_CLASS_COLORS[classToken]:WrapTextInColorCode(caster)
end
self:AddLine(T["Cast by"] .. ": " .. caster, .25,.5,1)
end
end
if C.tooltip.spellid == "1" then
self:AddLine(T["SpellID"] .. ": " .. aura.spellId, .25,.5,1)
end
self:Show()
end)
end
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("Cooldown", "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate") self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "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"
+110 -36
View File
@@ -1,17 +1,58 @@
pfUI:RegisterModule("tracking", function () pfUI:RegisterModule("tracking", function ()
_G.MiniMapTrackingFrame:UnregisterAllEvents() MINIMAP_TRACKING_FRAME:UnregisterAllEvents()
_G.MiniMapTrackingFrame:Hide() MINIMAP_TRACKING_FRAME:Hide()
local function HasEntries(tbl)
for _ in pairs(tbl) do
return true
end
return nil
end
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
local size = tonumber(C.appearance.minimap.tracking_size) local size = tonumber(C.appearance.minimap.tracking_size)
local pulse = C.appearance.minimap.tracking_pulse == "1" local pulse = C.appearance.minimap.tracking_pulse == "1"
-- Tracking spells come from ClassicAPI's native GetNumTrackingTypes / -- Tracking spells identified by SpellID + expected icon path.
-- GetTrackingInfo (see the DLL's docs/API.md "Tracking"). The DLL -- SpellID is the primary identifier (stable, locale-independent).
-- enumerates them straight from the spellbook by tracking-aura effect, -- Icon path is used as secondary confirmation when scanning the spellbook.
-- so there is no spell table to maintain here and server-custom trackers local knownTrackingSpells = {
-- (e.g. Turtle's Find Trees) are picked up automatically. any = {
{ id = 2481, icon = "Racial_Dwarf_FindTreasure" }, -- Find Treasure
{ id = 2580, icon = "Spell_Nature_Earthquake" }, -- Find Minerals
{ id = 2383, icon = "INV_Misc_Flower_02" }, -- Find Herbs (Rank 1)
{ id = 8387, icon = "INV_Misc_Flower_02" }, -- Find Herbs (Rank 2)
{ id = 52917, icon = "INV_TradeSkillItem_03" }, -- Find Trees (TurtleWow)
},
HUNTER = {
{ id = 1494, icon = "Ability_Tracking" }, -- Track Beasts
{ id = 19883, icon = "Spell_Holy_PrayerOfHealing" }, -- Track Humanoids
{ id = 19884, icon = "Spell_Shadow_DarkSummoning" }, -- Track Undead
{ id = 19885, icon = "Ability_Stealth" }, -- Track Hidden
{ id = 19880, icon = "Spell_Frost_SummonWaterElemental" }, -- Track Elementals
{ id = 19878, icon = "Spell_Shadow_SummonFelHunter" }, -- Track Demons
{ id = 19882, icon = "Ability_Racial_Avatar" }, -- Track Giants
{ id = 19879, icon = "INV_Misc_Head_Dragon_01" }, -- Track Dragonkin
},
PALADIN = {
{ id = 5502, icon = "Spell_Holy_SenseUndead" }, -- Sense Undead
},
WARLOCK = {
{ id = 5500, icon = "Spell_Shadow_Metamorphosis" }, -- Sense Demons
},
DRUID = {
{ id = 5225, icon = "Ability_Tracking" }, -- Track Humanoids (Cat Form only)
},
}
-- Build a flat lookup: spellId -> entry, for fast spellbook matching
local spellIdLookup = {}
for _, entries in pairs(knownTrackingSpells) do
for _, entry in ipairs(entries) do
spellIdLookup[entry.id] = entry
end
end
local state = { local state = {
texture = nil, texture = nil,
@@ -34,14 +75,6 @@ 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")
@@ -56,6 +89,19 @@ 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
@@ -82,24 +128,50 @@ pfUI:RegisterModule("tracking", function ()
end) end)
function pfUI.tracking:RefreshSpells() function pfUI.tracking:RefreshSpells()
state.spells = {} local playerClass = UnitClassBase("player")
if not GetNumTrackingTypes then return end local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(playerClass)
-- Druid Track Humanoids (5225) only casts in Cat Form. Hide it out of -- Build set of valid SpellIDs for this class
-- form so the menu never offers a tracker that would fail to cast. local validIds = {}
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(UnitClassBase("player")) for _, entry in ipairs(knownTrackingSpells.any) do
validIds[entry.id] = true
end
if knownTrackingSpells[playerClass] then
for _, entry in ipairs(knownTrackingSpells[playerClass]) do
validIds[entry.id] = true
end
end
for i = 1, GetNumTrackingTypes() do -- Druids only get Track Humanoids in Cat Form
local name, texture, _, _, spellId = GetTrackingInfo(i) if playerClass == "DRUID" and not isCatForm then
local castable = not (spellId == 5225 and not isCatForm) validIds[5225] = nil
if name and texture and castable end
and not pfUI.tracking.invalidSpells[name] then
state.spells[i] = { -- Scan spellbook: match by icon path, confirm SpellID is valid for this class
index = i, -- tracking index, passed to SetTracking() for tabIndex = 1, GetNumSpellTabs() do
name = name, local _, _, offset, numSpells = GetSpellTabInfo(tabIndex)
texture = texture, for spellIndex = offset + 1, offset + numSpells do
spellId = spellId, local spellTexture = GetSpellTexture(spellIndex, BOOKTYPE_SPELL)
} local spellName = GetSpellName(spellIndex, BOOKTYPE_SPELL)
if pfUI.tracking.invalidSpells[spellName] then
spellTexture = nil
end
if spellTexture then
local lowerTexture = string.lower(spellTexture)
for spellId, entry in pairs(spellIdLookup) do
if validIds[spellId] and not state.spells[spellId]
and strfind(lowerTexture, string.lower(entry.icon)) then
state.spells[spellId] = {
index = spellIndex,
name = spellName,
texture = spellTexture,
spellId = spellId,
}
end
end
end
end end
end end
end end
@@ -108,18 +180,20 @@ 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:Stop() pfUI.tracking.pulse = nil
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 HasEntries(state.spells) then
pfUI.tracking.pulse = true
pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
if not pfUI.tracking.pulse:IsPlaying() then pfUI.tracking.pulse:Play() end pfUI.tracking.icon:SetVertexColor(1,1,1,1)
pfUI.tracking:Show() pfUI.tracking:Show()
else else
pfUI.tracking.pulse:Stop() pfUI.tracking.pulse = nil
pfUI.tracking:Hide() pfUI.tracking:Hide()
end end
end end
@@ -143,7 +217,7 @@ pfUI:RegisterModule("tracking", function ()
checked = spell.texture == state.texture, checked = spell.texture == state.texture,
arg1 = spell, arg1 = spell,
func = function (arg1) func = function (arg1)
SetTracking(arg1.index) CastSpell(arg1.index, BOOKTYPE_SPELL)
CloseDropDownMenus() CloseDropDownMenus()
end end
}) })
+2962 -22
View File
File diff suppressed because it is too large Load Diff
+5 -14
View File
@@ -28,7 +28,7 @@ pfUI:RegisterModule("unitxp", function ()
behindFrame.text:SetFont(pfUI.font_default, fontSize, "OUTLINE") behindFrame.text:SetFont(pfUI.font_default, fontSize, "OUTLINE")
behindFrame.text:SetPoint("RIGHT", behindFrame, "RIGHT", -1, 0) behindFrame.text:SetPoint("RIGHT", behindFrame, "RIGHT", -1, 0)
behindFrame.text:SetTextColor(0.3, 1, 0.3, 1) behindFrame.text:SetTextColor(0.3, 1, 0.3, 1)
behindFrame.text:SetText(T["BEHIND"]) behindFrame.text:SetText("BEHIND")
behindFrame.text:Hide() behindFrame.text:Hide()
local lastCheck = 0 local lastCheck = 0
@@ -62,7 +62,7 @@ pfUI:RegisterModule("unitxp", function ()
losFrame.text:SetFont(pfUI.font_default, fontSize, "OUTLINE") losFrame.text:SetFont(pfUI.font_default, fontSize, "OUTLINE")
losFrame.text:SetPoint("RIGHT", losFrame, "RIGHT", -1, -slot) losFrame.text:SetPoint("RIGHT", losFrame, "RIGHT", -1, -slot)
losFrame.text:SetTextColor(1, 0.3, 0.3, 1) losFrame.text:SetTextColor(1, 0.3, 0.3, 1)
losFrame.text:SetText(T["NO LOS"]) losFrame.text:SetText("NO LOS")
losFrame.text:Hide() losFrame.text:Hide()
local lastCheck = 0 local lastCheck = 0
@@ -134,7 +134,7 @@ pfUI:RegisterModule("unitxp", function ()
end end
this.text:SetTextColor(r, g, b, 1) this.text:SetTextColor(r, g, b, 1)
this.text:SetFormattedText("%.1f%s", distance, suffix) this.text:SetText(string.format("%.1f%s", distance, suffix))
this.text:Show() this.text:Show()
end) end)
@@ -169,9 +169,6 @@ pfUI:RegisterModule("unitxp", function ()
local throttle = 0 local throttle = 0
local scanner = CreateFrame("Frame") local scanner = CreateFrame("Frame")
-- expose the poller frame so PLAYER_LOGOUT can stop its UnitXP OnUpdate
-- (it lives on this separate frame, not on distanceIndicator) -> crash 132
pfUI.uf.target.distanceScanner = scanner
scanner:SetScript("OnUpdate", function() scanner:SetScript("OnUpdate", function()
throttle = throttle + arg1 throttle = throttle + arg1
if throttle < 0.05 then return end if throttle < 0.05 then return end
@@ -206,7 +203,7 @@ pfUI:RegisterModule("unitxp", function ()
end end
f.text:SetTextColor(r, g, b, 1) f.text:SetTextColor(r, g, b, 1)
f.text:SetFormattedText("%.1f%s", distance, suffix) f.text:SetText(string.format("%.1f%s", distance, suffix))
end) end)
end end
pfUI.uf.target.distanceIndicator = pfRangeDisplay pfUI.uf.target.distanceIndicator = pfRangeDisplay
@@ -239,10 +236,6 @@ pfUI:RegisterModule("unitxp", function ()
if pfUI.uf.target.distanceIndicator then if pfUI.uf.target.distanceIndicator then
pfUI.uf.target.distanceIndicator:SetScript("OnUpdate", nil) pfUI.uf.target.distanceIndicator:SetScript("OnUpdate", nil)
end end
-- free-frame mode polls from its own scanner frame, not the indicator
if pfUI.uf.target.distanceScanner then
pfUI.uf.target.distanceScanner:SetScript("OnUpdate", nil)
end
end end
return return
end end
@@ -275,9 +268,7 @@ pfUI:RegisterModule("unitxp", function ()
-- Also notify on BG queue pop -- Also notify on BG queue pop
local origBattlefieldPortShow = BattlefieldFrame_Show local origBattlefieldPortShow = BattlefieldFrame_Show
if origBattlefieldPortShow then if origBattlefieldPortShow then
-- pfUI.env has __index but no __newindex, so a bare global assignment BattlefieldFrame_Show = function()
-- inside a RegisterModule closure never leaves the sandbox
_G.BattlefieldFrame_Show = function()
pcall(UnitXP, "notify", "taskbarIcon") pcall(UnitXP, "notify", "taskbarIcon")
pcall(UnitXP, "notify", "systemSound") pcall(UnitXP, "notify", "systemSound")
return origBattlefieldPortShow() return origBattlefieldPortShow()
+12 -11
View File
@@ -130,6 +130,15 @@ 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)
@@ -139,8 +148,9 @@ 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.fade:Stop() frame.drag.text:SetAlpha(1)
frame.drag.fade:Play()
frame.drag:SetScript("OnUpdate", DraggerOnUpdate)
end end
local function SavePosition(frame) local function SavePosition(frame)
@@ -445,15 +455,6 @@ 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,25 +34,4 @@ 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)
+15 -35
View File
@@ -63,16 +63,9 @@ 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
@@ -89,8 +82,7 @@ local function OnEnter(self)
mode = "REP" mode = "REP"
end end
self.hover = true self:SetAlpha(1)
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()
@@ -191,15 +183,13 @@ 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(self.hover) self.bar.text:SetShown(MouseIsOver(self))
end end
if self.always then return end if self.always then return end
-- Hover comes from OnEnter/OnLeave, the same mouse focus that reveals the if self:GetAlpha() == 0 or MouseIsOver(self) then return end
-- bar, so the fade can't restart under the cursor if ( self.tick or 1) > GetTime() then return else self.tick = GetTime() + .01 end
if self:GetAlpha() == 0 or self.hover or self.fade:IsPlaying() then return end self:SetAlpha(self:GetAlpha() - .05)
if ( self.tick or 1) > GetTime() then return end
self.fade:Play()
end end
local function OnEvent(self) local function OnEvent(self)
@@ -220,7 +210,7 @@ end
end end
if self.always then if self.always then
Reveal(self) self:SetAlpha(1)
self:Show() self:Show()
end end
@@ -230,7 +220,7 @@ end
if mode == "XP" then if mode == "XP" then
self.enabled = true self.enabled = true
Reveal(self) self:SetAlpha(1)
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 +236,7 @@ end
local xpperc = round(xp / xpmax * 100) local xpperc = round(xp / xpmax * 100)
local experc = ex and round(ex / xpmax * 100) or 0 local experc = ex and round(ex / xpmax * 100) or 0
if ex then text = "%s: %s%% (%s%% %s)" end if ex then text = "%s: %s%% (%s%% %s)" end
self.bar.text:SetFormattedText(text, T["Experience"], xpperc, experc, T["Rested"]) self.bar.text:SetText(string.format(text, T["Experience"], xpperc, experc, T["Rested"]))
self.tick = GetTime() + self.timeout self.tick = GetTime() + self.timeout
if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then
@@ -256,7 +246,7 @@ end
elseif mode == "PETXP" then elseif mode == "PETXP" then
self.restedbar:Hide() self.restedbar:Hide()
self.enabled = true self.enabled = true
Reveal(self) self:SetAlpha(1)
local currXP, nextXP = GetPetExperience() local currXP, nextXP = GetPetExperience()
self.bar:SetMinMaxValues(min(0, currXP), nextXP) self.bar:SetMinMaxValues(min(0, currXP), nextXP)
@@ -264,7 +254,7 @@ end
local text = "%s: %s%%" local text = "%s: %s%%"
local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0 local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0
self.bar.text:SetFormattedText(text, T["Pet Experience"], xpperc) self.bar.text:SetText(string.format(text, T["Pet Experience"], xpperc))
self.tick = GetTime() + self.timeout self.tick = GetTime() + self.timeout
return return
@@ -274,7 +264,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
Reveal(self) self:SetAlpha(1)
barMax = barMax - barMin barMax = barMax - barMin
barValue = barValue - barMin barValue = barValue - barMin
@@ -292,7 +282,7 @@ end
local text = "%s: %s%% (%s)" local text = "%s: %s%% (%s)"
local perc = round(barValue / barMax * 100) local perc = round(barValue / barMax * 100)
local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender) local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender)
self.bar.text:SetFormattedText(text, name, perc, standing) self.bar.text:SetText(string.format(text, name, perc, standing))
self.tick = GetTime() + self.timeout self.tick = GetTime() + self.timeout
return return
@@ -352,7 +342,6 @@ 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)
@@ -369,23 +358,14 @@ 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:RegisterEvent("UNIT_PET")
b:RegisterUnitEvent("UNIT_LEVEL", "player") b:RegisterEvent("UNIT_LEVEL")
b:RegisterUnitEvent("UNIT_PET_EXPERIENCE", "player", "pet") b:RegisterEvent("UNIT_PET_EXPERIENCE")
b:RegisterEvent("PLAYER_ENTERING_WORLD") b:RegisterEvent("PLAYER_ENTERING_WORLD")
b:RegisterEvent("UPDATE_EXHAUSTION") b:RegisterEvent("UPDATE_EXHAUSTION")
b:RegisterEvent("PLAYER_XP_UPDATE") b:RegisterEvent("PLAYER_XP_UPDATE")
+104 -62
View File
@@ -3,8 +3,6 @@ function SlashCmdList.RELOAD(msg, editbox)
ReloadUI() ReloadUI()
end end
local addonName = ...
SLASH_PFUI1 = '/pfui' SLASH_PFUI1 = '/pfui'
function SlashCmdList.PFUI(msg, editbox) function SlashCmdList.PFUI(msg, editbox)
pfUI.gui:SetShown(not pfUI.gui:IsShown()) pfUI.gui:SetShown(not pfUI.gui:IsShown())
@@ -15,19 +13,87 @@ function SlashCmdList.GM(msg, editbox)
ToggleHelpFrame(1) ToggleHelpFrame(1)
end end
local pfUI = CreateFrame("Frame", addonName, UIParent) pfUI = CreateFrame("Frame", nil, UIParent)
pfUI:RegisterEvent("ADDON_LOADED") pfUI:RegisterEvent("ADDON_LOADED")
-- setup bootvar -- setup bootvar
pfUI.bootup = true pfUI.bootup = true
pfUI_playerDB = pfUI_playerDB or {} do
pfUI_config = pfUI_config or {} -- ClassicAPI dependency check.
pfUI_init = pfUI_init or {} -- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
pfUI_profiles = pfUI_profiles or {} -- API surface that ClassicAPI polyfills, so presence is required.
pfUI_addon_profiles = pfUI_addon_profiles or {} local PFUI_CLASSIC_API_MIN = 10802 -- (X*10000 + Y*100 + Z)
pfUI_cache = pfUI_cache or {} local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
pfUI_throttle = pfUI_throttle or {} local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
local function FormatVersion(packed)
local x = math.floor(packed / 10000)
local y = math.floor(math.mod(packed, 10000) / 100)
local z = math.mod(packed, 100)
return string.format("v%d.%d.%d", x, y, z)
end
if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN)
pfUI.disabled = true
local detail
if not CLASSIC_API_VERSION then
detail = "The ClassicAPI DLL isn't loaded. The |cff33ffcc!!!ClassicAPI|r addon ships bundled with it -- delete your |cff33ffcc!!!ClassicAPI|r folder and install the latest release from:"
else
detail = "ClassicAPI " .. minVersion .. " or newer is required. Delete your |cff33ffcc!!!ClassicAPI|r folder and reinstall the latest release from:"
end
local function ShowRequiredPopup()
StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = {
text = "|cff33ffccpf|cffffffffUI|r has been disabled.\n\n" .. detail,
button1 = OKAY,
hasEditBox = 1,
editBoxWidth = 280,
timeout = 0,
whileDead = 1,
hideOnEscape = 1,
preferredIndex = 3,
OnShow = function()
local editBox = getglobal(this:GetName().."EditBox")
if editBox then
editBox:SetText(PFUI_CLASSIC_API_LATEST_URL)
editBox:HighlightText()
editBox:SetFocus()
end
end,
}
StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED")
DEFAULT_CHAT_FRAME:AddMessage(
"|cff33ffccpf|cffffffffUI|r disabled: " .. detail .. " " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.3, 0.3
)
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
loginFrame:SetScript("OnEvent", function()
loginFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
ShowRequiredPopup()
end)
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then
EventUtil.ContinueOnPlayerLogin(function()
C_Timer.After(8, function()
DEFAULT_CHAT_FRAME:AddMessage(
"|cff33ffccpf|rUI: ClassicAPI " .. FormatVersion(PFUI_CLASSIC_API_LATEST) .. " is available — " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.85, 0.3
)
end)
end)
end
end
-- initialize saved variables
pfUI_playerDB = {}
pfUI_config = {}
pfUI_init = {}
pfUI_profiles = {}
pfUI_addon_profiles = {}
pfUI_cache = {}
pfUI_throttle = {}
-- localization -- localization
pfUI_locale = {} pfUI_locale = {}
@@ -44,32 +110,35 @@ pfUI.movables = {}
pfUI.version = {} pfUI.version = {}
pfUI.env = {} pfUI.env = {}
-- Capability flag: this fork's pfActionBar buttons correctly handle the modern if not pfUI.disabled then
-- HookScript widget method, so ClassicAPI's AddOnCompat shim must NOT shadow pfUI.events = Mixin({}, CallbackRegistryMixin)
-- HookScript during actionbar load (older forks branch on `if button.HookScript` pfUI.events:OnLoad()
-- and take a modern path they were never written for). This describes behavior, pfUI.events:SetUndefinedEventsAllowed(true)
-- not identity: a fork that keeps the HookScript-correct actionbar code keeps end
-- the flag; one that lacks it should drop the flag and get the safe fallback.
pfUI.handlesHookScript = true
pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events:OnLoad()
pfUI.events:SetUndefinedEventsAllowed(true)
-- check if macro addons are loaded (disables macrotweak/macroscan) -- check if macro addons are loaded (disables macrotweak/macroscan)
function pfUI:MacroAddonsLoaded() function pfUI:MacroAddonsLoaded()
return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros") return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros")
end end
pfUI.name = addonName -- detect current addon path
pfUI.path = "Interface\\AddOns\\" .. addonName local tocs = { "", "-master", "-tbc", "-wotlk" }
for _, name in pairs(tocs) do
local current = string.format("pfUI%s", name)
local title = C_AddOns.GetAddOnName(current)
if title then
pfUI.name = current
pfUI.path = "Interface\\AddOns\\" .. current
break
end
end
-- handle/convert media dir paths -- handle/convert media dir paths
pfUI.media = setmetatable({}, { __index = function(tab,key) pfUI.media = setmetatable({}, { __index = function(tab,key)
local value = tostring(key) local value = tostring(key)
if value:find("img:") then if strfind(value, "img:") then
value = string.gsub(value, "img:", pfUI.path .. "\\img\\") value = string.gsub(value, "img:", pfUI.path .. "\\img\\")
elseif value:find("font:") then elseif strfind(value, "font:") then
value = string.gsub(value, "font:", pfUI.path .. "\\fonts\\") value = string.gsub(value, "font:", pfUI.path .. "\\fonts\\")
else else
value = string.gsub(value, "Interface\\AddOns\\pfUI\\", pfUI.path .. "\\") value = string.gsub(value, "Interface\\AddOns\\pfUI\\", pfUI.path .. "\\")
@@ -78,7 +147,9 @@ pfUI.media = setmetatable({}, { __index = function(tab,key)
return value return value
end}) end})
pfUI.client = INTERFACE_VERSION -- cache client version
local _, _, _, client = GetBuildInfo()
pfUI.client = client or 11200
-- setup pfUI namespace -- setup pfUI namespace
setmetatable(pfUI.env, {__index = getfenv(0)}) setmetatable(pfUI.env, {__index = getfenv(0)})
@@ -221,21 +292,6 @@ function pfUI:GetEnvironment()
pfUI.env.C = pfUI_config pfUI.env.C = pfUI_config
pfUI.env.pfUI_throttle = _G.pfUI_throttle pfUI.env.pfUI_throttle = _G.pfUI_throttle
pfUI.env.L = (pfUI_locale[GetLocale()] or pfUI_locale["enUS"]) pfUI.env.L = (pfUI_locale[GetLocale()] or pfUI_locale["enUS"])
pfUI.env.L["class"] = pfUI.env.L["class"] or tInvert(LOCALIZED_CLASS_NAMES_MALE)
if not pfUI.env.L["race"] then
pfUI.env.L["race"] = {}
local i = 1
local raceInfo = C_CreatureInfo.GetRaceInfo(i)
while raceInfo ~= nil do
pfUI.env.L["race"][raceInfo.clientFileString] = {
raceName = raceInfo.raceName,
raceID = raceInfo.raceID,
faction = C_CreatureInfo.GetFactionInfo(i).groupTag,
}
i = i + 1
raceInfo = C_CreatureInfo.GetRaceInfo(i)
end
end
return pfUI.env return pfUI.env
end end
@@ -312,13 +368,14 @@ function pfUI:CheckNewModules()
end end
local function BackwardsCompatRegister(func, arg3) local function BackwardsCompatRegister(func, arg3)
if arg3 and type(func) == "string" and type(arg3) == "function" and func:find("vanilla") then if arg3 and type(func) == "string" and type(arg3) == "function" and string.find(func, "vanilla") then
return arg3 return arg3
end end
return func return func
end end
function pfUI:RegisterModule(name, func, arg3) function pfUI:RegisterModule(name, func, arg3)
if pfUI.disabled then return end
if pfUI.module[name] then return end if pfUI.module[name] then return end
func = BackwardsCompatRegister(func, arg3) func = BackwardsCompatRegister(func, arg3)
pfUI.module[name] = func pfUI.module[name] = func
@@ -329,6 +386,7 @@ function pfUI:RegisterModule(name, func, arg3)
end end
function pfUI:RegisterSkin(name, func, arg3) function pfUI:RegisterSkin(name, func, arg3)
if pfUI.disabled then return end
if pfUI.skin[name] then return end if pfUI.skin[name] then return end
func = BackwardsCompatRegister(func, arg3) func = BackwardsCompatRegister(func, arg3)
pfUI.skin[name] = func pfUI.skin[name] = func
@@ -349,6 +407,7 @@ function pfUI:LoadSkin(s)
end end
pfUI:SetScript("OnEvent", function() pfUI:SetScript("OnEvent", function()
if pfUI.disabled then return end
-- make sure to initialize and set our fonts -- make sure to initialize and set our fonts
-- each time an addon got loaded but only -- each time an addon got loaded but only
@@ -362,11 +421,11 @@ pfUI:SetScript("OnEvent", function()
-- "@project-version@" until release tooling substitutes it — mark those -- "@project-version@" until release tooling substitutes it — mark those
-- explicitly as "dev" instead of pretending they're a real numbered build. -- explicitly as "dev" instead of pretending they're a real numbered build.
local raw = tostring(GetAddOnMetadata(pfUI.name, "Version")) local raw = tostring(GetAddOnMetadata(pfUI.name, "Version"))
if raw:find("@") then if strfind(raw, "@") then
pfUI.version.major, pfUI.version.minor, pfUI.version.fix = 0, 0, 0 pfUI.version.major, pfUI.version.minor, pfUI.version.fix = 0, 0, 0
pfUI.version.string = "dev" pfUI.version.string = "dev"
else else
local major, minor, fix = pfUI.api.strsplit(".", string.gsub(raw, "^[vV]", "")) local major, minor, fix = pfUI.api.strsplit(".", raw)
pfUI.version.major = tonumber(major) or 0 pfUI.version.major = tonumber(major) or 0
pfUI.version.minor = tonumber(minor) or 0 pfUI.version.minor = tonumber(minor) or 0
pfUI.version.fix = tonumber(fix) or 0 pfUI.version.fix = tonumber(fix) or 0
@@ -495,21 +554,4 @@ function pfUI.SetupCVars()
COMBAT_TEXT_SHOW_HONOR_GAINED = "1" COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
end end
UIParentLoadAddOn("Blizzard_CombatText") UIParentLoadAddOn("Blizzard_CombatText")
end end
do -- RunMacroText
local obj = setmetatable({ ["GetText"] = function(self) return self.text end }, {
__index = function(tab,key)
local value = function() return end
rawset(tab,key,value)
return value
end
})
function RunMacroText(text)
obj.text = text
ChatEdit_ParseText(obj, 1)
end
end
_G.PLAYER_BUFF_START_ID = -1
+8 -5
View File
@@ -6,11 +6,14 @@
## Version: @project-version@ ## Version: @project-version@
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle ## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB ## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
## Dependencies: !!!ClassicAPI
## X-Website: https://github.com/brues-code/pfUI ## X-Website: https://github.com/brues-code/pfUI
# ClassicAPI-less fallback. ClassicAPI redirects this read to pfUI_Turtle.toc pfUI.lua
# (Turtle clients) or pfUI_ClassicAPI.toc (everything else), so the client only
# ever reaches THIS file when the DLL is missing. Load the notice and nothing
# else.
API_Check.lua init\env.xml
init\compat.xml
init\api.xml
init\libs.xml
init\skins.xml
init\modules.xml
-23
View File
@@ -1,23 +0,0 @@
## Interface: 11200
## Title: |cff33ffccpf|cffffffffUI
## Author: Shagu - modified by me0wg4ming & brues
## Notes: A complete user interface replacement.
## Notes-ruRU: Полная замена пользовательского интерфейса.
## Version: @project-version@
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
## X-Website: https://github.com/brues-code/pfUI
# Selected by ClassicAPI on every non-Turtle client. Turtle clients get
# pfUI_Turtle.toc instead, which trades init\stock.xml for init\turtle.xml.
API_Check.lua
pfUI.lua
init\env.xml
init\stock.xml
init\api.xml
init\libs.xml
init\skins.xml
init\modules.xml
-24
View File
@@ -1,24 +0,0 @@
## Interface: 11200
## Title: |cff33ffccpf|cffffffffUI
## Author: Shagu - modified by me0wg4ming & brues
## Notes: A complete user interface replacement.
## Notes-ruRU: Полная замена пользовательского интерфейса.
## Version: @project-version@
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
## X-Website: https://github.com/brues-code/pfUI
# Selected by ClassicAPI on Turtle WoW, where it wins over pfUI_ClassicAPI.toc.
# Same list with init\stock.xml swapped for init\turtle.xml: the modules, skins
# and vendor prices that only mean anything on a Turtle client.
API_Check.lua
pfUI.lua
init\env.xml
init\api.xml
init\libs.xml
init\skins.xml
init\modules.xml
init\turtle.xml
+3 -2
View File
@@ -295,8 +295,9 @@ pfUI:RegisterSkin("Character", function ()
if faction and faction.reaction and faction.reaction < 8 then if faction and faction.reaction and faction.reaction < 8 then
local repLeft = faction.nextReactionThreshold - faction.currentStanding local repLeft = faction.nextReactionThreshold - faction.currentStanding
if repLeft > 1 then if repLeft > 1 then
standing:SetFormattedText("%s (%d)", standing:GetText(), repLeft) local text = standing:GetText() .. string.format(" (%d)", repLeft)
standing:GetParent().standingText = standing:GetText() standing:SetText(text)
standing:GetParent().standingText = text
end end
end end
end end
+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, 72) FriendsFrame.backdrop:SetPoint("BOTTOMRIGHT", -32, 74)
FriendsFrame:SetHitRectInsets(8,32,10,72) FriendsFrame:SetHitRectInsets(8,32,10,74)
EnableMovable("FriendsFrame") EnableMovable("FriendsFrame")
SkinCloseButton(FriendsFrameCloseButton, FriendsFrame.backdrop, -6, -6) SkinCloseButton(FriendsFrameCloseButton, FriendsFrame.backdrop, -6, -6)
+15 -11
View File
@@ -19,20 +19,24 @@ 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)
-- skin every button on the menu, including ones other addons add later local buttons = {
local function SkinMenuButtons() GameMenuButtonOptions,
for _, child in pairs({GameMenuFrame:GetChildren()}) do GameMenuButtonSoundOptions,
if child:GetFrameType() == "Button" and not child._pfMenuSkinned then GameMenuButtonUIOptions,
SkinButton(child) GameMenuButtonKeybindings,
child._pfMenuSkinned = true GameMenuButtonMacros,
end GameMenuButtonRatings,
end GameMenuButtonLogout,
end GameMenuButtonQuit,
GameMenuButtonContinue,
}
SkinMenuButtons() for _, button in pairs(buttons) do
GameMenuFrame:HookScript("OnShow", SkinMenuButtons) if button then SkinButton(button) end
end
end) end)
+1 -1
View File
@@ -125,7 +125,7 @@ pfUI:RegisterSkin("Inspect", function ()
local guild, title = GetGuildInfo(InspectFrame.unit) local guild, title = GetGuildInfo(InspectFrame.unit)
if guild then if guild then
InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1) InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1)
InspectGuildText:SetFormattedText(TEXT(GUILD_TITLE_TEMPLATE), title, guild) InspectGuildText:SetText(format(TEXT(GUILD_TITLE_TEMPLATE), title, guild))
InspectGuildText:Show() InspectGuildText:Show()
else else
InspectGuildText:SetText("") InspectGuildText:SetText("")
+24 -26
View File
@@ -60,35 +60,33 @@ pfUI:RegisterSkin("Macro", function ()
MacroFrameScrollFrame:ClearAllPoints() MacroFrameScrollFrame:ClearAllPoints()
MacroFrameScrollFrame:SetPoint("TOPLEFT", MacroFrameSelectedMacroBackground, "BOTTOMLEFT", 11, -13) MacroFrameScrollFrame:SetPoint("TOPLEFT", MacroFrameSelectedMacroBackground, "BOTTOMLEFT", 11, -13)
-- The macroicons module replaces MacroPopupFrame with its own picker.
-- Only skin the stock popup when that module is disabled.
if pfUI_config["disabled"] and pfUI_config["disabled"]["macroicons"] == "1" then
StripTextures(MacroPopupFrame)
CreateBackdrop(MacroPopupFrame, nil, nil, .75)
MacroPopupFrame:SetFrameStrata("DIALOG")
MacroPopupFrame:ClearAllPoints()
MacroPopupFrame:SetPoint("TOPLEFT", MacroFrame.backdrop, "TOPRIGHT", 2*border, 0)
StripTextures(MacroPopupScrollFrame)
SkinScrollbar(MacroPopupScrollFrameScrollBar)
MacroPopupEditBox:DisableDrawLayer("BACKGROUND") StripTextures(MacroPopupFrame)
CreateBackdrop(MacroPopupEditBox, nil, true) CreateBackdrop(MacroPopupFrame, nil, nil, .75)
MacroPopupEditBox:SetScript("OnEscapePressed", function() MacroPopupFrame:SetFrameStrata("DIALOG")
MacroPopupFrame:Hide() MacroPopupFrame:ClearAllPoints()
MacroFrame_Update() MacroPopupFrame:SetPoint("TOPLEFT", MacroFrame.backdrop, "TOPRIGHT", 2*border, 0)
end)
for i=1, NUM_MACRO_ICONS_SHOWN do StripTextures(MacroPopupScrollFrame)
local button = _G["MacroPopupButton"..i] SkinScrollbar(MacroPopupScrollFrameScrollBar)
local icon = _G["MacroPopupButton"..i..'Icon']
StripTextures(button) MacroPopupEditBox:DisableDrawLayer("BACKGROUND")
SkinButton(button, nil, nil, nil, icon) CreateBackdrop(MacroPopupEditBox, nil, true)
end MacroPopupEditBox:SetScript("OnEscapePressed", function()
SkinButton(MacroPopupCancelButton) MacroPopupFrame:Hide()
SkinButton(MacroPopupOkayButton) MacroFrame_Update()
MacroPopupOkayButton:ClearAllPoints() end)
MacroPopupOkayButton:SetPoint("RIGHT", MacroPopupCancelButton, "LEFT", -2*bpad, 0)
for i=1, NUM_MACRO_ICONS_SHOWN do
local button = _G["MacroPopupButton"..i]
local icon = _G["MacroPopupButton"..i..'Icon']
StripTextures(button)
SkinButton(button, nil, nil, nil, icon)
end end
SkinButton(MacroPopupCancelButton)
SkinButton(MacroPopupOkayButton)
MacroPopupOkayButton:ClearAllPoints()
MacroPopupOkayButton:SetPoint("RIGHT", MacroPopupCancelButton, "LEFT", -2*bpad, 0)
end) end)
end) end)
-49
View File
@@ -1,49 +0,0 @@
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)

Some files were not shown because too many files have changed in this diff Show More