mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-22 07:36:56 +00:00
Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38b4100353 | |||
| f497c71523 | |||
| 8e109a585a | |||
| 83f89be14a | |||
| e59158764c | |||
| 863d99647d | |||
| 06b3ae8d22 | |||
| 879d7afe83 | |||
| 4eab494a01 | |||
| bb8ce8fd63 | |||
| 75c4657ab9 | |||
| 5815b9e81e | |||
| 60f953178f | |||
| 1dc10964a2 | |||
| 0fc08e77c0 | |||
| 7f3795b45b | |||
| 8eee766652 | |||
| 2b5288a688 | |||
| daba4e202e | |||
| 98c7751416 | |||
| 8796fab6d8 | |||
| 7ca0e40d8b | |||
| 2f93b246a1 | |||
| c647698f8f | |||
| 48026008b6 | |||
| c1d96bae58 | |||
| 1d44c5ec6f | |||
| 8f88dfd852 | |||
| a57214aeb0 | |||
| 9bde843e33 | |||
| ad13e6ace3 | |||
| 82c74c2db5 | |||
| a3cfd711fe | |||
| a7ad1f073f | |||
| fe5b3b135b | |||
| 9c29f0c7d4 | |||
| 685ecb4a3e | |||
| 8cdaf1fc00 | |||
| bb39c9d94e | |||
| 18059ffbb7 | |||
| d75e280238 | |||
| 9eb34b8f0a | |||
| f8dde49f09 | |||
| 74fbbb6154 | |||
| af6893c10c | |||
| be6b2ea036 | |||
| afdb6c3862 | |||
| b230a45b92 | |||
| ef1fa47539 | |||
| ab0ad7dcf9 | |||
| c82813df5b | |||
| 6e1f8095a2 | |||
| 9cabdf2974 | |||
| b6931359b0 | |||
| e017dbf515 | |||
| 69d778d6d6 | |||
| 75a25cf614 | |||
| 9daf6165fd | |||
| 06768c985a | |||
| 881a17e508 | |||
| 4e461d766e | |||
| 2dfaa65168 | |||
| 97ef039d3a | |||
| 25ef02705c | |||
| 48d1da2015 | |||
| ff29e9d3c2 | |||
| f0f1c80e24 | |||
| 5b4cb2bc9f | |||
| 5a3a962c90 | |||
| cd08db75d2 | |||
| 6365f0cdb1 | |||
| faa801e27d | |||
| 421a103f22 | |||
| ecb28fb194 | |||
| 94268b1162 |
@@ -23,12 +23,12 @@ jobs:
|
||||
IFS=. read -r MAJOR MINOR PATCH <<<"$LATEST"
|
||||
PACKED=$((MAJOR * 10000 + MINOR * 100 + PATCH))
|
||||
echo "Pinning PFUI_CLASSIC_API_LATEST to $LATEST ($PACKED)"
|
||||
sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" pfUI.lua
|
||||
grep 'local PFUI_CLASSIC_API_' pfUI.lua
|
||||
sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" API_Check.lua
|
||||
grep 'local PFUI_CLASSIC_API_' API_Check.lua
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Package and release to GitHub
|
||||
uses: BigWigsMods/packager@v2
|
||||
uses: brues-code/packager@vCAPI
|
||||
env:
|
||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"runtime": {
|
||||
"version": "Lua 5.1"
|
||||
},
|
||||
"diagnostics": {
|
||||
"disable": ["deprecated"],
|
||||
"globals": [
|
||||
"this",
|
||||
"event",
|
||||
"arg",
|
||||
"arg1",
|
||||
"arg2",
|
||||
"arg3",
|
||||
"arg4",
|
||||
"arg5",
|
||||
"arg6",
|
||||
"arg7",
|
||||
"arg8",
|
||||
"arg9"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
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 = 11500 -- (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
|
||||
@@ -1,13 +1,10 @@
|
||||
# pfUI - ClassicAPI Edition
|
||||
|
||||
[](https://octowow.st/)
|
||||
[](https://github.com/brues-code/ClassicAPI)
|
||||
[](https://github.com/brues-code/nampower)
|
||||
[](https://github.com/balakethelock/SuperWoW)
|
||||
[](https://github.com/brues-code/UnitXP_SP3)
|
||||
|
||||
**A pfUI fork specifically optimized for ClassicAPI on [Octo WoW](https://octowow.st/)**
|
||||
|
||||
This version includes significant performance improvements and DLL-enhanced features.
|
||||
|
||||
## Installation
|
||||
@@ -18,7 +15,7 @@ This version includes significant performance improvements and DLL-enhanced feat
|
||||
|
||||
## DLL Enhancements
|
||||
|
||||
Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functionality. These DLLs are permitted on Octo WoW:
|
||||
Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functionality:
|
||||
|
||||
### [ClassicAPI](https://github.com/brues-code/ClassicAPI)
|
||||
|
||||
|
||||
+89
-1
@@ -204,6 +204,32 @@ function pfUI.api.UnitHasBuff(unit, name)
|
||||
return C_UnitAuras.GetAuraDataBySpellName(unit, name, "HELPFUL") ~= nil or nil
|
||||
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 ]
|
||||
-- Returns a comma-joined, colored list of group members missing the named aura.
|
||||
-- name [string] the localized aura name to check for
|
||||
@@ -236,6 +262,45 @@ function pfUI.api.GetUnitColor(unitstr)
|
||||
return classColor:GenerateHexColorMarkup(), classColor:GetRGB()
|
||||
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
|
||||
|
||||
-- [ GetPlayerRaceIcons ]
|
||||
-- Returns the faction and race emblems of the player character.
|
||||
-- return: [string] both inline textures, or an empty string
|
||||
function pfUI.api.GetPlayerRaceIcons()
|
||||
local _, raceKey = UnitRace("player")
|
||||
return pfUI.api.GetFactionIcon(raceKey) .. pfUI.api.GetRaceIcon(raceKey, UnitSex("player"))
|
||||
end
|
||||
|
||||
-- [ strvertical ]
|
||||
-- Creates vertical text using linebreaks. Multibyte char friendly.
|
||||
-- 'str' [string] String to columnize.
|
||||
@@ -397,14 +462,33 @@ end
|
||||
-- Returns an itemLink for the given itemname
|
||||
-- 'name' [string] name of the item
|
||||
-- returns: [string] entire itemLink for the given item
|
||||
local itemLinkCache = {}
|
||||
local itemLinkMisses = {}
|
||||
local ITEMLINK_MAX_SCANS = 2
|
||||
function pfUI.api.GetItemLinkByName(name)
|
||||
for itemID = 1, 25818 do
|
||||
-- GetInboxItem() hands us a nil name for attachment-less mail
|
||||
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)
|
||||
if itemName and itemName == name then
|
||||
local _, itemLink = C_Item.GetItemInfo(itemID)
|
||||
itemLinkCache[name] = itemLink
|
||||
return itemLink
|
||||
end
|
||||
end
|
||||
|
||||
itemLinkMisses[name] = misses + 1
|
||||
end
|
||||
|
||||
-- [ FindItem ]
|
||||
@@ -646,6 +730,10 @@ end
|
||||
function pfUI.api.CreateGoldString(money)
|
||||
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 silver = floor(mod((money/100),100))
|
||||
local copper = floor(mod(money,100))
|
||||
|
||||
+6
-2
@@ -554,7 +554,6 @@ function pfUI:LoadConfig()
|
||||
|
||||
pfUI:UpdateConfig("unitframes", unit, "glowaggro", "1")
|
||||
pfUI:UpdateConfig("unitframes", unit, "glowcombat", "1")
|
||||
pfUI:UpdateConfig("unitframes", unit, "showtooltip", "1")
|
||||
pfUI:UpdateConfig("unitframes", unit, "healthcolor", "1")
|
||||
pfUI:UpdateConfig("unitframes", unit, "powercolor", "1")
|
||||
pfUI:UpdateConfig("unitframes", unit, "levelcolor", "1")
|
||||
@@ -646,7 +645,6 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("bars", nil, "animation", "zoomfade")
|
||||
pfUI:UpdateConfig("bars", nil, "animmode", "keypress")
|
||||
pfUI:UpdateConfig("bars", nil, "animalways", "0")
|
||||
pfUI:UpdateConfig("bars", nil, "macroscan", "1")
|
||||
pfUI:UpdateConfig("bars", nil, "reagents", "1")
|
||||
pfUI:UpdateConfig("bars", nil, "hunterbar", "0")
|
||||
pfUI:UpdateConfig("bars", nil, "pagemasteralt", "0")
|
||||
@@ -786,6 +784,9 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("tooltip", nil, "itemid", "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, "alwaysperc", "0")
|
||||
pfUI:UpdateConfig("tooltip", "compare", "basestats", "1")
|
||||
@@ -815,6 +816,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("chat", "text", "playerlinks", "1")
|
||||
pfUI:UpdateConfig("chat", "text", "detecturl", "1")
|
||||
pfUI:UpdateConfig("chat", "text", "classcolor", "1")
|
||||
pfUI:UpdateConfig("chat", "text", "playericons", "0")
|
||||
pfUI:UpdateConfig("chat", "text", "whosearchunknown", "0")
|
||||
pfUI:UpdateConfig("chat", "text", "playerlevel", "0")
|
||||
pfUI:UpdateConfig("chat", "left", "width", "380")
|
||||
@@ -956,6 +958,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("questlog", nil, "showQuestLevels", "0")
|
||||
|
||||
pfUI:UpdateConfig("character", "inventory", "durability", "1")
|
||||
pfUI:UpdateConfig("character", "inventory", "equipflyout", "0")
|
||||
pfUI:UpdateConfig("character", "reputation", "repRequired", "1")
|
||||
pfUI:UpdateConfig("thirdparty", nil, "chatbg", "1")
|
||||
pfUI:UpdateConfig("thirdparty", nil, "showmeter", "0")
|
||||
@@ -963,6 +966,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("thirdparty", "dpsmate", "dock", "0")
|
||||
pfUI:UpdateConfig("thirdparty", "shagudps", "skin", "0")
|
||||
pfUI:UpdateConfig("thirdparty", "shagudps", "dock", "0")
|
||||
pfUI:UpdateConfig("thirdparty", "greedmeter", "dock", "0")
|
||||
pfUI:UpdateConfig("thirdparty", "swstats", "skin", "0")
|
||||
pfUI:UpdateConfig("thirdparty", "swstats", "dock", "0")
|
||||
pfUI:UpdateConfig("thirdparty", "ktm", "skin", "0")
|
||||
|
||||
+4
-4
@@ -587,7 +587,7 @@ end
|
||||
function pfUI.api.SetHighlight(frame, cr, cg, cb)
|
||||
if not frame then return end
|
||||
if not cr or not cg or not cb then
|
||||
cr, cg, cb = GetClassColor(UnitClassBase('player'))
|
||||
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB()
|
||||
end
|
||||
|
||||
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 return end
|
||||
if not cr or not cg or not cb then
|
||||
cr, cg, cb = GetClassColor(UnitClassBase('player'))
|
||||
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB()
|
||||
end
|
||||
pfUI.api.CreateBackdrop(b, nil, true)
|
||||
b:SetNormalTexture("")
|
||||
@@ -709,7 +709,7 @@ end
|
||||
function pfUI.api.SkinRotateButton(button)
|
||||
pfUI.api.CreateBackdrop(button)
|
||||
|
||||
local cr, cg, cb = GetClassColor(UnitClassBase('player'))
|
||||
local cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB()
|
||||
|
||||
local btnW, btnH = button:GetSize()
|
||||
button:SetSize(btnW - 18, btnH - 18)
|
||||
@@ -907,7 +907,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
|
||||
end
|
||||
|
||||
if not cr or not cg or not cb then
|
||||
cr, cg, cb = GetClassColor(UnitClassBase('player'))
|
||||
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB()
|
||||
end
|
||||
|
||||
SetHighlight(button, cr, cg, cb)
|
||||
|
||||
+347
-248
@@ -4,6 +4,17 @@ setfenv(1, pfUI:GetEnvironment())
|
||||
pfUI.uf = CreateFrame("Frame", nil, UIParent)
|
||||
pfUI.uf.frames = {}
|
||||
|
||||
-- Reusable buffer for C_UnitAuras.GetAuraSlots slot ids (filled by
|
||||
-- ScanAuraSlots, api.lua). Each aura scan in RefreshUnit fills it once, then
|
||||
-- reads every aura by slot id: one array walk per scan instead of one per index.
|
||||
-- Scans run back to back and consume the buffer before the next refill.
|
||||
local auraSlots = {}
|
||||
|
||||
-- Separate buffer for the tooltip handlers. They run from OnEnter, which can
|
||||
-- fire while RefreshUnit is showing/hiding icons under the cursor, so they must
|
||||
-- not share the refresh buffer.
|
||||
local tooltipSlots = {}
|
||||
|
||||
-- ============================================================================
|
||||
-- GUID-based Roster Tracking for Smart Updates
|
||||
-- Only updates frames where the unit actually changed, not ALL 40 frames
|
||||
@@ -24,14 +35,38 @@ pfUI.api.RegisterSlashCommand("PFTEST", { "/pftest", "/pfuftest" }, function()
|
||||
if pfUI.uf.raid and pfUI.uf.raid.LayoutPets then pfUI.uf.raid:LayoutPets() end
|
||||
end, true)
|
||||
|
||||
-- HoT buff indicators that need name verification because their icons are
|
||||
-- reused by other spells. Maps icon (lowercased) → expected aura name +
|
||||
-- libpredict key for the prediction integration.
|
||||
local HOT_INDICATORS = {
|
||||
[strlower(C_Spell.GetSpellTexture(774))] = { name = strlower(C_Spell.GetSpellName(774)), predict = "Reju" },
|
||||
[strlower(C_Spell.GetSpellTexture(139))] = { name = strlower(C_Spell.GetSpellName(139)), predict = "Renew" },
|
||||
[strlower(C_Spell.GetSpellTexture(8936))] = { name = strlower(C_Spell.GetSpellName(8936)), predict = "Regr" },
|
||||
}
|
||||
-- Buff indicators are identified by a spell id, resolved once into the aura's
|
||||
-- localized name plus its icon. A match needs both to agree.
|
||||
--
|
||||
-- Icon alone is ambiguous: Spell.dbc reuses icons across unrelated spells, so
|
||||
-- an icon-only filter lights the indicator for the wrong buff (Blessing of
|
||||
-- Sanctuary shares its icon with Lightning Shield and Shadowguard, Blessing of
|
||||
-- Kings with Mage Armor and Commanding Shout, Totemic Power with the Blessed
|
||||
-- Sunfruit food buff). Name alone is ambiguous too -- creature and item auras
|
||||
-- reuse player spell names ("Renew", "Rejuvenation", "Fire Resistance").
|
||||
--
|
||||
-- Every rank of a spell carries the same name and icon, so one id per buff
|
||||
-- covers the whole rank ladder. Ids missing from this client resolve to nil and
|
||||
-- drop out of the list. 'predict' names the libpredict key of a HoT.
|
||||
--
|
||||
-- Both fields are stored raw, and RefreshUnit compares them raw. The aura's
|
||||
-- name and icon and these come out of the same DBC records byte for byte --
|
||||
-- Spell.dbc's localized name, SpellIcon.dbc's path -- so case folding either
|
||||
-- side would only burn a string per aura per scan. Feed this ids, never
|
||||
-- hand-written names or icon paths, or that equality quietly stops holding.
|
||||
local indicator_cache = {}
|
||||
local function AddIndicator(indicators, spellId, predict)
|
||||
local record = indicator_cache[spellId]
|
||||
if record == nil then
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
local icon = name and C_Spell.GetSpellTexture(spellId)
|
||||
-- cache misses as false, so an absent spell is only looked up once
|
||||
record = icon and { name = name, icon = icon, predict = predict } or false
|
||||
indicator_cache[spellId] = record
|
||||
end
|
||||
|
||||
if record then table.insert(indicators, record) end
|
||||
end
|
||||
|
||||
local glow = {
|
||||
edgeFile = pfUI.media["img:glow"], edgeSize = 8,
|
||||
@@ -88,16 +123,21 @@ local function DebuffOnEnter()
|
||||
local parent = this:GetParent()
|
||||
|
||||
-- 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.
|
||||
-- SetUnitAura's index has to be into the unfiltered HARMFUL list. Look up
|
||||
-- the displayed aura via the PLAYER filter, then find its position in the
|
||||
-- unfiltered list by name + sourceGUID.
|
||||
--
|
||||
-- The unfiltered list is NOT capped at 16: once a unit's 16 debuff slots
|
||||
-- are full the server parks further debuffs in buff slots, and C_UnitAuras
|
||||
-- reports those as harmful too. So enumerate however many the unit has.
|
||||
if parent.config and parent.config.selfdebuff == "1" then
|
||||
local ownAura = C_UnitAuras.GetAuraDataByIndex(unitstr, this.id, "HARMFUL|PLAYER")
|
||||
if ownAura then
|
||||
for gameSlot = 1, 16 do
|
||||
local check = C_UnitAuras.GetDebuffDataByIndex(unitstr, gameSlot)
|
||||
local n = ScanAuraSlots(unitstr, "HARMFUL", tooltipSlots)
|
||||
for i = 1, n do
|
||||
local check = C_UnitAuras.GetAuraDataBySlot(unitstr, tooltipSlots[i])
|
||||
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
|
||||
GameTooltip:SetUnitAura(unitstr, gameSlot, "HARMFUL")
|
||||
GameTooltip:SetUnitAura(unitstr, i, "HARMFUL")
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -140,32 +180,43 @@ function pfUI.api.GetUnitStats(unitstr)
|
||||
end
|
||||
|
||||
local aggrodata = { }
|
||||
local aggroScan = nil -- static { u, t, tt } triples, built once so the hot scan allocates nothing
|
||||
function pfUI.api.UnitHasAggro(unit)
|
||||
-- Only cache positive results to allow instant detection when aggro changes
|
||||
if aggrodata[unit] and aggrodata[unit].state > 0 and GetTime() < aggrodata[unit].check + 1 then
|
||||
return aggrodata[unit].state
|
||||
local now = GetTime()
|
||||
local data = aggrodata[unit]
|
||||
-- Cache positive results 1s (so aggro clears fast) AND negative results 0.3s
|
||||
-- (so we don't rescan the whole unit table on every call while nothing has aggro).
|
||||
if data and now < data.check + (data.state > 0 and 1 or 0.3) then
|
||||
return data.state
|
||||
end
|
||||
|
||||
aggrodata[unit] = aggrodata[unit] or { }
|
||||
aggrodata[unit].check = GetTime()
|
||||
aggrodata[unit].state = 0
|
||||
if not data then data = { }; aggrodata[unit] = data end
|
||||
data.check = now
|
||||
data.state = 0
|
||||
|
||||
if UnitExists(unit) and UnitIsFriend(unit, "player") then
|
||||
for u in pairs(pfValidUnits) do
|
||||
local t = u .. "target"
|
||||
local tt = t .. "target"
|
||||
|
||||
if UnitExists(t) and UnitIsUnit(t, unit) and UnitCanAttack(u, unit) then
|
||||
aggrodata[unit].state = aggrodata[unit].state + 1
|
||||
-- pfValidUnits never changes after load, so precompute the "<u>target" /
|
||||
-- "<u>targettarget" token strings once instead of concatenating per call.
|
||||
if not aggroScan then
|
||||
aggroScan = {}
|
||||
for u in pairs(pfValidUnits) do
|
||||
local t = u .. "target"
|
||||
table.insert(aggroScan, { u = u, t = t, tt = t .. "target" })
|
||||
end
|
||||
end
|
||||
|
||||
if UnitExists(tt) and UnitIsUnit(tt, unit) and UnitCanAttack(t, unit) then
|
||||
aggrodata[unit].state = aggrodata[unit].state + 1
|
||||
for i = 1, table.getn(aggroScan) do
|
||||
local s = aggroScan[i]
|
||||
if UnitExists(s.t) and UnitIsUnit(s.t, unit) and UnitCanAttack(s.u, unit) then
|
||||
data.state = data.state + 1
|
||||
end
|
||||
if UnitExists(s.tt) and UnitIsUnit(s.tt, unit) and UnitCanAttack(s.t, unit) then
|
||||
data.state = data.state + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return aggrodata[unit].state
|
||||
return data.state
|
||||
end
|
||||
|
||||
pfUI.uf.glow = CreateFrame("Frame", nil, UIParent)
|
||||
@@ -194,8 +245,13 @@ function pfUI.uf:UpdateVisibility()
|
||||
|
||||
-- cache result of strsub to avoid repeating calls
|
||||
if not self.cache_raid then
|
||||
if strsub(self:GetName(),0,6) == "pfRaid" then
|
||||
self.cache_raid = tonumber(strsub(self:GetName(),7,8)) or 0
|
||||
local name = self:GetName()
|
||||
if strsub(name,0,9) == "pfRaidPet" then
|
||||
-- pet grid slot (pfRaidPet1..40); used to mirror party pets below
|
||||
self.cache_raid = 0
|
||||
self.cache_raidpet = tonumber(strsub(name,10)) or 0
|
||||
elseif strsub(name,0,6) == "pfRaid" then
|
||||
self.cache_raid = tonumber(strsub(name,7,8)) or 0
|
||||
else
|
||||
self.cache_raid = 0
|
||||
end
|
||||
@@ -227,6 +283,26 @@ function pfUI.uf:UpdateVisibility()
|
||||
end
|
||||
end
|
||||
|
||||
-- show raidpet frames as party pets when a party is shown as a raid grid
|
||||
if self.cache_raidpet then
|
||||
if not IsInRaid() and IsInGroup() and C.unitframes.raidforgroup == "1" then
|
||||
local id = self.cache_raidpet
|
||||
if id == 1 then
|
||||
-- grid slot 1 mirrors the player, so its pet is the player's pet
|
||||
self.id = ""
|
||||
self.label = "pet"
|
||||
elseif id <= 5 then
|
||||
self.id = id - 1
|
||||
self.label = "partypet"
|
||||
end
|
||||
|
||||
-- reset to regular raidpet unitstrings after leaving party mode
|
||||
elseif self.label == "pet" or self.label == "partypet" then
|
||||
self.id = self.cache_raidpet
|
||||
self.label = "raidpet"
|
||||
end
|
||||
end
|
||||
|
||||
-- display every unit as player while pfUI.uf.showall is set
|
||||
if pfUI.uf.showall then
|
||||
self._label = self._label or self.label
|
||||
@@ -237,9 +313,10 @@ function pfUI.uf:UpdateVisibility()
|
||||
self._label, self._id = nil, nil
|
||||
end
|
||||
|
||||
local unitstr = string.format("%s%s", self.label or "", self.id or "")
|
||||
self:SetAttribute("unit", unitstr ~= "" and unitstr or nil)
|
||||
local visibility = string.format("[target=%s,exists] show; hide", unitstr)
|
||||
local unitstr = ("%s%s"):format(self.label or "", self.id or "")
|
||||
self.unitstr = unitstr ~= "" and unitstr or nil
|
||||
self:SetAttribute("unit", self.unitstr)
|
||||
local visibility = ("[target=%s,exists] show; hide"):format(unitstr)
|
||||
|
||||
-- Group frames are redundant when the group is already shown as a raid grid:
|
||||
-- either an actual raid, or a party promoted to the raid grid via
|
||||
@@ -255,9 +332,10 @@ function pfUI.uf:UpdateVisibility()
|
||||
-- frame shall not be visible
|
||||
visibility = "hide"
|
||||
self.visible = nil
|
||||
elseif hide_group and self.cache_raid == 0 and self.label and strsub(self.label,0,5) == "party" then
|
||||
-- hide group while shown as a raid grid and option is set (raid frames
|
||||
-- carry label "party" under raidforgroup, so exclude them by cache_raid)
|
||||
elseif hide_group and self.cache_raid == 0 and not self.cache_raidpet and self.label and strsub(self.label,0,5) == "party" then
|
||||
-- hide group while shown as a raid grid and option is set (raid player
|
||||
-- frames carry label "party" and raid pet frames carry label "partypet"
|
||||
-- under raidforgroup, so exclude them by cache_raid / cache_raidpet)
|
||||
visibility = "hide"
|
||||
self.visible = nil
|
||||
elseif ( self.fname == "Group0" or self.fname == "PartyPet0" or self.fname == "Party0Target" )
|
||||
@@ -267,6 +345,15 @@ function pfUI.uf:UpdateVisibility()
|
||||
self.visible = nil
|
||||
end
|
||||
|
||||
-- This is the single place a frame's unit is ever assigned, so it is also the
|
||||
-- place its subscriptions follow it to: the engine then delivers only this
|
||||
-- unit's events, and OnEvent compares against the cached string instead of
|
||||
-- rebuilding label..id -- and calling UnitGUID -- for every unit event fired
|
||||
-- by anything, anywhere. A frame that is not in use drops them entirely; the
|
||||
-- roster events that would bring it back are registered plainly, and
|
||||
-- visibilityscan re-runs this every 0.2s regardless, so it recovers on its own.
|
||||
self:RegisterUnitEvents(visibility ~= "hide" and self.unitstr or nil)
|
||||
|
||||
-- vanilla visibility
|
||||
if self.unitname then
|
||||
self:Show()
|
||||
@@ -547,11 +634,14 @@ function pfUI.uf:UpdateConfig()
|
||||
f.feedbackText:ClearAllPoints()
|
||||
f.feedbackText:SetPoint("CENTER", f.portrait, "CENTER")
|
||||
end
|
||||
f:RegisterEvent("UNIT_COMBAT")
|
||||
f.combatfeedback = true
|
||||
else
|
||||
f.feedbackText:Hide()
|
||||
f:UnregisterEvent("UNIT_COMBAT")
|
||||
f.combatfeedback = nil
|
||||
end
|
||||
-- RegisterUnitEvents owns UNIT_COMBAT; clearing the cached unit makes the
|
||||
-- next UpdateVisibility re-run it against the new combatfeedback state.
|
||||
f.eventunit = nil
|
||||
|
||||
f.hpLeftText:SetFontObject(GameFontWhite)
|
||||
f.hpLeftText:SetFont(fontname, fontsize, fontstyle)
|
||||
@@ -724,7 +814,7 @@ function pfUI.uf:UpdateConfig()
|
||||
if not f.buffs[i].cd then
|
||||
if cooldown_anim == 1 then
|
||||
-- Animation enabled: Use Model frame with CooldownFrameTemplate
|
||||
f.buffs[i].cd = CreateFrame(COOLDOWN_FRAME_TYPE, f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate")
|
||||
f.buffs[i].cd = CreateFrame("Model", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate")
|
||||
else
|
||||
-- Animation disabled: Use regular Frame with dummy functions
|
||||
f.buffs[i].cd = CreateFrame("Frame", f.buffs[i]:GetName() .. "Cooldown", f.buffs[i])
|
||||
@@ -797,7 +887,7 @@ function pfUI.uf:UpdateConfig()
|
||||
if not f.debuffs[i].cd then
|
||||
if cooldown_anim == 1 then
|
||||
-- Animation enabled: Use Model frame with CooldownFrameTemplate
|
||||
f.debuffs[i].cd = CreateFrame(COOLDOWN_FRAME_TYPE, f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate")
|
||||
f.debuffs[i].cd = CreateFrame("Model", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate")
|
||||
else
|
||||
-- Animation disabled: Use regular Frame with dummy functions
|
||||
f.debuffs[i].cd = CreateFrame("Frame", f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i])
|
||||
@@ -841,6 +931,9 @@ function pfUI.uf:UpdateConfig()
|
||||
f:UpdateFrameSize()
|
||||
else
|
||||
f:UnregisterAllEvents()
|
||||
-- that dropped the unit filters along with the registrations, so the cache
|
||||
-- has to go too or the next UpdateVisibility believes they are still set
|
||||
f.eventunit = nil
|
||||
f:Hide()
|
||||
end
|
||||
end
|
||||
@@ -882,6 +975,10 @@ function pfUI.uf.OnEvent()
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
this:SetScript("OnUpdate", nil)
|
||||
-- visibilityscan is a separate frame and keeps ticking, so leaving this one
|
||||
-- on its list would have UpdateVisibility re-register the unit events we
|
||||
-- just dropped -- straight back into the crash 132 this branch prevents
|
||||
visibilityscan.frames[this] = nil
|
||||
return
|
||||
end
|
||||
|
||||
@@ -948,8 +1045,12 @@ function pfUI.uf.OnEvent()
|
||||
this.update_aura = true
|
||||
elseif this.label == "pet" and event == "UNIT_HAPPINESS" then
|
||||
this.update_full = true
|
||||
-- UNIT_XXX Events
|
||||
elseif arg1 and (arg1 == this.label .. this.id or (UnitGUID and arg1 == UnitGUID(this.label .. this.id))) then
|
||||
-- UNIT_XXX Events. RegisterUnitEvents means arg1 can only be this frame's own
|
||||
-- unit; the compare is kept for the case the filter sits out, which is when
|
||||
-- arg1 is not a string. The old GUID alternative is gone: these events fire
|
||||
-- once per token that resolves to the unit AND once with the raw GUID, so the
|
||||
-- token form always arrives and the GUID form was only ever a duplicate wake.
|
||||
elseif arg1 and arg1 == this.unitstr then
|
||||
if event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" then
|
||||
this.update_portrait = true
|
||||
elseif event == "UNIT_AURA" then
|
||||
@@ -1174,25 +1275,56 @@ function pfUI.uf.OnUpdate()
|
||||
end
|
||||
end
|
||||
|
||||
-- The unit events whose arg1 is the frame's OWN unit -- everything the OnEvent
|
||||
-- routes through its "UNIT_XXX Events" branch. These are not registered here:
|
||||
-- UpdateVisibility owns them, because it owns the frame's unit (below).
|
||||
--
|
||||
-- UNIT_PET and UNIT_HAPPINESS are deliberately absent. Their branches key on
|
||||
-- the frame's label, and UNIT_PET's arg1 is the pet's OWNER ("player" for a
|
||||
-- "pet" frame), so filtering them by the frame's own unit would drop them.
|
||||
local UNIT_EVENTS = {
|
||||
"UNIT_DISPLAYPOWER",
|
||||
"UNIT_HEALTH", "UNIT_MAXHEALTH",
|
||||
"UNIT_MANA", "UNIT_MAXMANA",
|
||||
"UNIT_RAGE", "UNIT_MAXRAGE",
|
||||
"UNIT_ENERGY", "UNIT_MAXENERGY",
|
||||
"UNIT_FOCUS",
|
||||
"UNIT_PORTRAIT_UPDATE", "UNIT_MODEL_CHANGED",
|
||||
"UNIT_FACTION",
|
||||
"UNIT_AURA", -- frame=buff, frame=debuff
|
||||
}
|
||||
|
||||
-- Point this frame's unit-event subscriptions at `unitstr`, or drop them when
|
||||
-- the frame has no unit. Cheap to call repeatedly: it no-ops unless the unit
|
||||
-- actually changed, which matters because visibilityscan runs UpdateVisibility
|
||||
-- for every frame five times a second.
|
||||
function pfUI.uf:RegisterUnitEvents(unitstr)
|
||||
if self.eventunit == unitstr then return end
|
||||
self.eventunit = unitstr
|
||||
|
||||
for i = 1, table.getn(UNIT_EVENTS) do
|
||||
if unitstr then
|
||||
self:RegisterUnitEvent(UNIT_EVENTS[i], unitstr)
|
||||
else
|
||||
self:UnregisterEvent(UNIT_EVENTS[i])
|
||||
end
|
||||
end
|
||||
|
||||
-- UNIT_COMBAT rides along only while the frame draws combat feedback text.
|
||||
-- UpdateConfig clears eventunit when it toggles that, so the next
|
||||
-- UpdateVisibility re-runs this.
|
||||
if unitstr and self.combatfeedback then
|
||||
self:RegisterUnitEvent("UNIT_COMBAT", unitstr)
|
||||
else
|
||||
self:UnregisterEvent("UNIT_COMBAT")
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.uf:EnableEvents()
|
||||
local f = self
|
||||
|
||||
f:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
f:RegisterEvent("PLAYER_LOGOUT")
|
||||
f:RegisterEvent("UNIT_DISPLAYPOWER")
|
||||
f:RegisterEvent("UNIT_HEALTH")
|
||||
f:RegisterEvent("UNIT_MAXHEALTH")
|
||||
f:RegisterEvent("UNIT_MANA")
|
||||
f:RegisterEvent("UNIT_MAXMANA")
|
||||
f:RegisterEvent("UNIT_RAGE")
|
||||
f:RegisterEvent("UNIT_MAXRAGE")
|
||||
f:RegisterEvent("UNIT_ENERGY")
|
||||
f:RegisterEvent("UNIT_MAXENERGY")
|
||||
f:RegisterEvent("UNIT_FOCUS")
|
||||
f:RegisterEvent("UNIT_PORTRAIT_UPDATE")
|
||||
f:RegisterEvent("UNIT_MODEL_CHANGED")
|
||||
f:RegisterEvent("UNIT_FACTION")
|
||||
f:RegisterEvent("UNIT_AURA") -- frame=buff, frame=debuff
|
||||
f:RegisterEvent("PLAYER_AURAS_CHANGED") -- label=player && frame=buff
|
||||
f:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- label=player && frame=buff (ClassicAPI: weapon-enchant buffs)
|
||||
f:RegisterEvent("PARTY_MEMBERS_CHANGED") -- label=party, frame=leaderIcon
|
||||
@@ -1310,6 +1442,7 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
|
||||
f.UpdateConfig = pfUI.uf.UpdateConfig
|
||||
f.EnableScripts = pfUI.uf.EnableScripts
|
||||
f.EnableEvents = pfUI.uf.EnableEvents
|
||||
f.RegisterUnitEvents = pfUI.uf.RegisterUnitEvents
|
||||
f.EnableClickCast = pfUI.uf.EnableClickCast
|
||||
f.GetColor = pfUI.uf.GetColor
|
||||
|
||||
@@ -1409,7 +1542,7 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
|
||||
f.group:SetFont(pfUI.font_unit, 8, "OUTLINE")
|
||||
f.group:SetTextColor(1,1,1,.8)
|
||||
f.group:SetHeight(16)
|
||||
f.group:SetText("Group " .. group)
|
||||
f.group:SetText(T["Group"] .. " " .. group)
|
||||
f.group:Hide()
|
||||
end
|
||||
|
||||
@@ -1426,6 +1559,9 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
|
||||
f:UpdateFrameSize()
|
||||
else
|
||||
f:UnregisterAllEvents()
|
||||
-- that dropped the unit filters along with the registrations, so the cache
|
||||
-- has to go too or the next UpdateVisibility believes they are still set
|
||||
f.eventunit = nil
|
||||
f:Hide()
|
||||
end
|
||||
|
||||
@@ -1598,42 +1734,45 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
|
||||
-- buffs
|
||||
if unit.buffs and ( component == "all" or component == "aura" ) then
|
||||
-- one GetAuraSlots enumeration per refresh, then a positional read per
|
||||
-- slot id: allocates nothing and never re-walks the aura array per icon
|
||||
ScanAuraSlots(unitstr, "HELPFUL", auraSlots, unit.config.bufflimit)
|
||||
for i=1, unit.config.bufflimit do
|
||||
if not unit.buffs[i] then break end
|
||||
|
||||
local aura = C_UnitAuras.GetBuffDataByIndex(unitstr, i)
|
||||
local name, icon, count, _, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
|
||||
if aura then
|
||||
unit.buffs[i].texture:SetTexture(aura.icon)
|
||||
if name then
|
||||
unit.buffs[i].texture:SetTexture(icon)
|
||||
unit.buffs[i]:Show()
|
||||
|
||||
if aura.applications > 1 then
|
||||
unit.buffs[i].stacks:SetText(aura.applications)
|
||||
if count > 1 then
|
||||
unit.buffs[i].stacks:SetText(count)
|
||||
else
|
||||
unit.buffs[i].stacks:SetText("")
|
||||
end
|
||||
|
||||
if aura.expirationTime > 0 then
|
||||
if expirationTime > 0 then
|
||||
-- pfUI's cooldown text falls into a 2^32 wraparound branch when start > GetTime(),
|
||||
-- which happens for talent-extended buffs where the real duration exceeds aura.duration
|
||||
-- (the Spell.dbc base). Anchor start to now in that case to keep the remaining math sane.
|
||||
-- which happens for talent-extended buffs where the real duration exceeds the
|
||||
-- Spell.dbc base. Anchor start to now in that case to keep the remaining math sane.
|
||||
local now = GetTime()
|
||||
local start = aura.expirationTime - aura.duration
|
||||
local duration = aura.duration
|
||||
if start > now or duration <= 0 then
|
||||
start, duration = now, aura.expirationTime - now
|
||||
local start = expirationTime - duration
|
||||
local dur = duration
|
||||
if start > now or dur <= 0 then
|
||||
start, dur = now, expirationTime - now
|
||||
end
|
||||
if duration > 0 then
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
|
||||
if dur > 0 then
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, start, dur, 1)
|
||||
else
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
|
||||
end
|
||||
elseif aura.duration > 0 then
|
||||
elseif duration > 0 then
|
||||
local guid = UnitGUID(unitstr)
|
||||
local guidStarts = guid and pfUI.uf.aura_starts[guid]
|
||||
local start = guidStarts and guidStarts[aura.spellId]
|
||||
local start = guidStarts and guidStarts[spellId]
|
||||
if start then
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, start, aura.duration, 1)
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
|
||||
else
|
||||
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
|
||||
end
|
||||
@@ -1685,6 +1824,13 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
reposition = true
|
||||
end
|
||||
|
||||
-- selfdebuff narrows to player-cast harmful auras via the PLAYER filter.
|
||||
-- Player-frame debuffs aren't gated on it (it'd hide most party-applied
|
||||
-- effects on you). One GetAuraSlots enumeration per refresh; the i-th slot
|
||||
-- is the i-th aura of the filtered list, so `i` stays the tooltip index.
|
||||
local filter = (unit.label ~= "player" and selfdebuff == "1") and "HARMFUL|PLAYER" or "HARMFUL"
|
||||
ScanAuraSlots(unitstr, filter, auraSlots, unit.config.debufflimit)
|
||||
|
||||
for i=1, unit.config.debufflimit do
|
||||
if not unit.debuffs[i] then break end
|
||||
|
||||
@@ -1701,13 +1847,10 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
invert_h * ((row+buffrow)*(multiply*default_border + unit.config.debuffsize + 1) + (multiply*default_border + 1)))
|
||||
end
|
||||
|
||||
-- selfdebuff narrows to player-cast harmful auras via the PLAYER filter.
|
||||
-- Player-frame debuffs aren't gated on it (it'd hide most party-applied
|
||||
-- effects on you).
|
||||
local filter = (unit.label ~= "player" and selfdebuff == "1") and "HARMFUL|PLAYER" or "HARMFUL"
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, filter)
|
||||
if aura then
|
||||
texture, stacks, dtype = aura.icon, aura.applications, aura.dispelName
|
||||
-- positional read by slot id allocates nothing
|
||||
local name, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if name then
|
||||
texture, stacks, dtype = icon, count, dispelType
|
||||
else
|
||||
texture, stacks, dtype = nil, 0, nil
|
||||
end
|
||||
@@ -1720,18 +1863,18 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
if texture then
|
||||
unit.debuffs[i]:Show()
|
||||
|
||||
if aura and aura.expirationTime > 0 then
|
||||
if expirationTime > 0 then
|
||||
-- Cap start to now so talent-extended debuffs (expirationTime past
|
||||
-- the dbc base duration) don't push start into the future and trip
|
||||
-- CooldownFrame_SetTimer's 2^32-wraparound branch.
|
||||
local now = GetTime()
|
||||
local start = aura.expirationTime - aura.duration
|
||||
local duration = aura.duration
|
||||
if start > now or duration <= 0 then
|
||||
start, duration = now, aura.expirationTime - now
|
||||
local start = expirationTime - duration
|
||||
local dur = duration
|
||||
if start > now or dur <= 0 then
|
||||
start, dur = now, expirationTime - now
|
||||
end
|
||||
if duration > 0 then
|
||||
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, duration, 1)
|
||||
if dur > 0 then
|
||||
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, dur, 1)
|
||||
else
|
||||
CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0)
|
||||
end
|
||||
@@ -1789,6 +1932,18 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
end
|
||||
end
|
||||
|
||||
-- Scan the 16 harmful slots once into a reusable set, instead of
|
||||
-- re-scanning all 16 for every dispellable type below. Reused across
|
||||
-- frames (populated and consumed within this single RefreshUnit call).
|
||||
local present = pfUI.uf.dispelPresent or {}
|
||||
pfUI.uf.dispelPresent = present
|
||||
for k in pairs(present) do present[k] = nil end
|
||||
local n = ScanAuraSlots(unitstr, "HARMFUL", auraSlots)
|
||||
for i=1,n do
|
||||
local name, _, _, dispelType = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if name and dispelType and dispelType ~= "" then present[dispelType] = true end
|
||||
end
|
||||
|
||||
for _, debuff in pairs(unit.dispellable) do
|
||||
indicator[debuff] = indicator[debuff] or CreateFrame("Frame", nil, indicator)
|
||||
indicator[debuff]:SetParent(indicator)
|
||||
@@ -1828,15 +1983,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
indicator[debuff].disp = indicator.disp
|
||||
end
|
||||
|
||||
indicator[debuff].visible = nil
|
||||
|
||||
for i=1,16 do
|
||||
local a = C_UnitAuras.GetDebuffDataByIndex(unitstr, i)
|
||||
local dtype = a and a.dispelName
|
||||
if dtype == debuff then
|
||||
indicator[debuff].visible = true
|
||||
end
|
||||
end
|
||||
indicator[debuff].visible = present[debuff]
|
||||
|
||||
if indicator[debuff].visible then
|
||||
indicator[debuff]:Show()
|
||||
@@ -1875,7 +2022,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
if not unit.indicator_custom and unit.config.buff_indicator == "1" then
|
||||
unit.indicator_custom = {}
|
||||
for k, v in pairs({strsplit("#", unit.config.custom_indicator)}) do
|
||||
unit.indicator_custom[k] = string.lower(v)
|
||||
unit.indicator_custom[k] = v:lower()
|
||||
end
|
||||
elseif not unit.indicator_custom then
|
||||
unit.indicator_custom = {}
|
||||
@@ -1883,21 +2030,19 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
|
||||
local pos = 1
|
||||
if table.getn(unit.indicators) > 0 then
|
||||
for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unitstr, "HELPFUL")) do
|
||||
local texLower = string.lower(aura.icon)
|
||||
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
|
||||
local n = ScanAuraSlots(unitstr, "HELPFUL", auraSlots)
|
||||
for i=1,n do
|
||||
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if not name then break end
|
||||
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
|
||||
|
||||
for _, filter in pairs(unit.indicators) do
|
||||
if filter == texLower then
|
||||
local hot = HOT_INDICATORS[texLower]
|
||||
if hot and string.lower(aura.name) ~= hot.name then
|
||||
break -- texture matches but name disambiguates (e.g. shared icon)
|
||||
end
|
||||
if hot then
|
||||
local start, duration, prediction = libpredict:GetHotDuration(unitstr, hot.predict)
|
||||
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft or prediction, aura.applications, tonumber(start), tonumber(duration))
|
||||
if filter.icon == icon and filter.name == name then
|
||||
if filter.predict then
|
||||
local start, duration, prediction = libpredict:GetHotDuration(unitstr, filter.predict)
|
||||
pfUI.uf:AddIcon(unit, pos, icon, timeleft or prediction, count, tonumber(start), tonumber(duration))
|
||||
else
|
||||
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
|
||||
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
|
||||
end
|
||||
pos = pos + 1
|
||||
break
|
||||
@@ -1907,12 +2052,15 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
end
|
||||
|
||||
if table.getn(unit.indicator_custom) > 0 then
|
||||
for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unitstr, "HELPFUL")) do
|
||||
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
|
||||
local lowerName = string.lower(aura.name)
|
||||
local n = ScanAuraSlots(unitstr, "HELPFUL", auraSlots)
|
||||
for i=1,n do
|
||||
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if not name then break end
|
||||
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
|
||||
local lowerName = name:lower()
|
||||
for _, filter in pairs(unit.indicator_custom) do
|
||||
if filter == lowerName then
|
||||
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
|
||||
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
|
||||
pos = pos + 1
|
||||
break
|
||||
end
|
||||
@@ -1920,13 +2068,14 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
end
|
||||
|
||||
local debuffFilter = unit.config.selfdebuff == "1" and "HARMFUL|PLAYER" or "HARMFUL"
|
||||
for i=1,16 do -- scan for custom debuffs
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, debuffFilter)
|
||||
if aura then
|
||||
local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
|
||||
n = ScanAuraSlots(unitstr, debuffFilter, auraSlots)
|
||||
for i=1,n do -- scan for custom debuffs
|
||||
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
if name then
|
||||
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
|
||||
for _, filter in pairs(unit.indicator_custom) do
|
||||
if filter == string.lower(aura.name) then
|
||||
pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
|
||||
if filter == name:lower() then
|
||||
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count)
|
||||
pos = pos + 1
|
||||
break
|
||||
end
|
||||
@@ -2134,14 +2283,17 @@ function pfUI.uf:EnableClickCast()
|
||||
local action = pfUI_config.unitframes["clickcast"..bconf..mconf]
|
||||
if action and action ~= "" then
|
||||
local prefix = modifier ~= "" and (modifier .. "-") or ""
|
||||
local low = string.lower(action)
|
||||
local low = action:lower()
|
||||
if low == "menu" then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "menu")
|
||||
elseif low == "target" then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "target")
|
||||
elseif low == "focus" then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "focus")
|
||||
elseif string.find(action, "^/") then
|
||||
elseif low:find("^macro:") then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "macro")
|
||||
self:SetAttribute(prefix .. "macro" .. bid, action:sub(7):gsub("^%s+", ""))
|
||||
elseif action:find("^/") then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "macro")
|
||||
self:SetAttribute(prefix .. "macrotext" .. bid, action)
|
||||
else
|
||||
@@ -2180,7 +2332,7 @@ function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
|
||||
-- Check if parent frame has cooldown animation enabled
|
||||
local parent_cooldown_anim = frame.config and tonumber(frame.config.cooldown_anim) or 1
|
||||
if parent_cooldown_anim == 1 then
|
||||
frame.icon[pos].cd = CreateFrame(COOLDOWN_FRAME_TYPE, nil, frame.icon[pos])
|
||||
frame.icon[pos].cd = CreateFrame("Model", nil, frame.icon[pos])
|
||||
else
|
||||
frame.icon[pos].cd = CreateFrame("Frame", nil, frame.icon[pos])
|
||||
frame.icon[pos].cd.AdvanceTime = DoNothing
|
||||
@@ -2279,175 +2431,122 @@ function pfUI.uf:SetupBuffIndicators(config)
|
||||
|
||||
if config.show_buffs == "1" then -- buffs
|
||||
if myclass == "DRUID" then
|
||||
-- Mark of the Wild
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_regeneration")
|
||||
-- Gift of the Wild
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_giftofthewild")
|
||||
-- Thorns
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_thorns")
|
||||
AddIndicator(indicators, 1126) -- Mark of the Wild
|
||||
AddIndicator(indicators, 21849) -- Gift of the Wild
|
||||
AddIndicator(indicators, 467) -- Thorns
|
||||
end
|
||||
|
||||
if myclass == "PRIEST" then
|
||||
-- Prayer Of Fortitude"
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_wordfortitude")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_prayeroffortitude")
|
||||
-- Prayer of Spirit
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_divinespirit")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_prayerofspirit")
|
||||
-- Shadow Protection
|
||||
table.insert(indicators, "interface\\icons\\spell_shadow_antishadow")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_prayerofshadowprotection")
|
||||
-- Fear Ward
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_excorcism")
|
||||
AddIndicator(indicators, 1243) -- Power Word: Fortitude
|
||||
AddIndicator(indicators, 21562) -- Prayer of Fortitude
|
||||
AddIndicator(indicators, 6386) -- Divine Spirit
|
||||
AddIndicator(indicators, 27681) -- Prayer of Spirit
|
||||
AddIndicator(indicators, 976) -- Shadow Protection
|
||||
AddIndicator(indicators, 27683) -- Prayer of Shadow Protection
|
||||
AddIndicator(indicators, 6346) -- Fear Ward
|
||||
end
|
||||
|
||||
if myclass == "PALADIN" then
|
||||
-- Blessing of Salvation
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofsalvation")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_sealofsalvation")
|
||||
-- Blessing of Wisdom
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_sealofwisdom")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofwisdom")
|
||||
-- Blessing of Sanctuary
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_lightningshield")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofsanctuary")
|
||||
-- Blessing of Kings
|
||||
table.insert(indicators, "interface\\icons\\spell_magic_magearmor")
|
||||
table.insert(indicators, "interface\\icons\\spell_magic_greaterblessingofkings")
|
||||
-- Blessing of Might
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_fistofjustice")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofkings")
|
||||
-- Blessing of Light
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_prayerofhealing02")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingoflight")
|
||||
-- Blessing of Sacrifice
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_sealofsacrifice")
|
||||
-- Blessing of Freedom
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_sealofvalor")
|
||||
-- Blessing of Protection
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_sealofprotection")
|
||||
AddIndicator(indicators, 1038) -- Blessing of Salvation
|
||||
AddIndicator(indicators, 25895) -- Greater Blessing of Salvation
|
||||
AddIndicator(indicators, 19742) -- Blessing of Wisdom
|
||||
AddIndicator(indicators, 25894) -- Greater Blessing of Wisdom
|
||||
AddIndicator(indicators, 20204) -- Blessing of Sanctuary
|
||||
AddIndicator(indicators, 25899) -- Greater Blessing of Sanctuary
|
||||
AddIndicator(indicators, 20217) -- Blessing of Kings
|
||||
AddIndicator(indicators, 25898) -- Greater Blessing of Kings
|
||||
AddIndicator(indicators, 19740) -- Blessing of Might
|
||||
AddIndicator(indicators, 25782) -- Greater Blessing of Might
|
||||
AddIndicator(indicators, 19977) -- Blessing of Light
|
||||
AddIndicator(indicators, 25890) -- Greater Blessing of Light
|
||||
AddIndicator(indicators, 6940) -- Hand of Sacrifice
|
||||
AddIndicator(indicators, 45801) -- Greater Blessing of Sacrifice
|
||||
AddIndicator(indicators, 1044) -- Hand of Freedom
|
||||
AddIndicator(indicators, 1022) -- Hand of Protection
|
||||
end
|
||||
|
||||
if myclass == "WARLOCK" then
|
||||
-- Fire Shield
|
||||
table.insert(indicators, "interface\\icons\\spell_fire_firearmor")
|
||||
-- Blood Pact
|
||||
table.insert(indicators, "interface\\icons\\spell_shadow_bloodboil")
|
||||
-- Soulstone
|
||||
table.insert(indicators, "interface\\icons\\spell_shadow_soulgem")
|
||||
-- Unending Breath
|
||||
table.insert(indicators, "interface\\icons\\spell_shadow_demonbreath")
|
||||
-- Detect Greater Invisibility or Detect Invisibility
|
||||
table.insert(indicators, "interface\\icons\\spell_shadow_detectinvisibility")
|
||||
-- Detect Lesser Invisibility
|
||||
table.insert(indicators, "interface\\icons\\spell_shadow_detectlesserinvisibility")
|
||||
-- Paranoia
|
||||
table.insert(indicators, "interface\\icons\\Spell_Shadow_AuraOfDarkness")
|
||||
AddIndicator(indicators, 1167) -- Fire Shield
|
||||
AddIndicator(indicators, 6307) -- Blood Pact
|
||||
AddIndicator(indicators, 20707) -- Soulstone Resurrection
|
||||
AddIndicator(indicators, 5697) -- Unending Breath
|
||||
AddIndicator(indicators, 2970) -- Detect Invisibility
|
||||
AddIndicator(indicators, 11743) -- Detect Greater Invisibility
|
||||
AddIndicator(indicators, 132) -- Detect Lesser Invisibility
|
||||
AddIndicator(indicators, 19480) -- Paranoia
|
||||
end
|
||||
|
||||
if myclass == "WARRIOR" then
|
||||
-- Battle Shout
|
||||
table.insert(indicators, "interface\\icons\\ability_warrior_battleshout")
|
||||
-- Commanding Shout (TBC)
|
||||
table.insert(indicators, "interface\\icons\\ability_warrior_rallyingcry")
|
||||
AddIndicator(indicators, 5242) -- Battle Shout
|
||||
AddIndicator(indicators, 45580) -- Commanding Shout
|
||||
end
|
||||
|
||||
if myclass == "MAGE" then
|
||||
-- Arcane Intellect
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_magicalsentry")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_arcaneintellect")
|
||||
-- Dampen Magic
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_abolishmagic")
|
||||
-- Amplify Magic
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_flashheal")
|
||||
AddIndicator(indicators, 1459) -- Arcane Intellect
|
||||
AddIndicator(indicators, 23028) -- Arcane Brilliance
|
||||
AddIndicator(indicators, 604) -- Dampen Magic
|
||||
AddIndicator(indicators, 1008) -- Amplify Magic
|
||||
end
|
||||
|
||||
if myclass == "HUNTER" then
|
||||
-- Aspect of the Wild
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_protectionformnature")
|
||||
|
||||
-- Aspect of the Pack
|
||||
table.insert(indicators, "interface\\icons\\ability_mount_whitetiger")
|
||||
|
||||
-- Misdirection (TBC)
|
||||
table.insert(indicators, "interface\\icons\\ability_hunter_misdirection")
|
||||
AddIndicator(indicators, 20043) -- Aspect of the Wild
|
||||
AddIndicator(indicators, 13159) -- Aspect of the Pack
|
||||
end
|
||||
|
||||
if myclass == "SHAMAN" then
|
||||
-- Earth Shield (TBC)
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_skinofearth")
|
||||
AddIndicator(indicators, 45525) -- Earth Shield
|
||||
end
|
||||
end
|
||||
|
||||
if config.show_procs == "1" then -- procs
|
||||
if myclass == "SHAMAN" or config.all_procs == "1" then
|
||||
-- Ancestral Fortitude
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_undyingstrength")
|
||||
-- Healing Way
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_healingway")
|
||||
-- Totemic Power (known issue: one conflicts with Blessed Sunfruit buff)
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_spiritualguidence")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_devotion")
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_holynova")
|
||||
table.insert(indicators, "interface\\icons\\spell_magic_magearmor")
|
||||
AddIndicator(indicators, 16177) -- Ancestral Fortitude
|
||||
AddIndicator(indicators, 29202) -- Healing Way
|
||||
-- Totemic Power is four auras, one per totem school, each with its own icon
|
||||
AddIndicator(indicators, 28824)
|
||||
AddIndicator(indicators, 28825)
|
||||
AddIndicator(indicators, 28826)
|
||||
AddIndicator(indicators, 28827)
|
||||
end
|
||||
|
||||
if myclass == "PRIEST" or config.all_procs == "1" then
|
||||
-- Inspiration
|
||||
table.insert(indicators, "interface\\icons\\inv_shield_06")
|
||||
AddIndicator(indicators, 14893) -- Inspiration
|
||||
end
|
||||
end
|
||||
|
||||
if config.show_hots == "1" then -- hots
|
||||
if myclass == "PRIEST" or config.all_hots == "1" then
|
||||
-- Renew
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_renew")
|
||||
-- Power Word: Shield
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_powerwordshield")
|
||||
-- Prayer of Mending (TBC)
|
||||
table.insert(indicators, "interface\\icons\\spell_holy_prayerofmendingtga")
|
||||
AddIndicator(indicators, 139, "Renew") -- Renew
|
||||
AddIndicator(indicators, 17) -- Power Word: Shield
|
||||
end
|
||||
|
||||
if myclass == "DRUID" or config.all_hots == "1" then
|
||||
-- Regrowth
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_resistnature")
|
||||
-- Rejuvenation
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_rejuvenation")
|
||||
-- Lifebloom
|
||||
table.insert(indicators, "interface\\icons\\inv_misc_herb_felblossom")
|
||||
AddIndicator(indicators, 8936, "Regr") -- Regrowth
|
||||
AddIndicator(indicators, 774, "Reju") -- Rejuvenation
|
||||
end
|
||||
end
|
||||
|
||||
if config.show_totems == "1" and myclass == "SHAMAN" then -- totems
|
||||
-- Strength of Earth Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_earthbindtotem")
|
||||
-- Stoneskin Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_stoneskintotem")
|
||||
-- Mana Spring Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_manaregentotem")
|
||||
-- Mana Tide Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_frost_summonwaterelemental")
|
||||
-- Healing Spring Totem
|
||||
table.insert(indicators, "interface\\icons\\inv_spear_04")
|
||||
-- Tranquil Air Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_brilliance")
|
||||
-- Grace of Air Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_invisibilitytotem")
|
||||
-- Grounding Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_groundingtotem")
|
||||
-- Nature Resistance Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_nature_natureresistancetotem")
|
||||
-- Fire Resistance Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_fireresistancetotem_01")
|
||||
-- Frost Resistance Totem
|
||||
table.insert(indicators, "interface\\icons\\spell_frostresistancetotem_01")
|
||||
-- the aura each totem applies, not the cast that drops it: they share an
|
||||
-- icon but the totem's own name carries a " Totem" suffix the aura lacks
|
||||
AddIndicator(indicators, 8076) -- Strength of Earth
|
||||
AddIndicator(indicators, 8072) -- Stoneskin
|
||||
AddIndicator(indicators, 5677) -- Mana Spring
|
||||
AddIndicator(indicators, 16191) -- Mana Tide
|
||||
AddIndicator(indicators, 5672) -- Healing Stream
|
||||
AddIndicator(indicators, 25909) -- Tranquil Air
|
||||
AddIndicator(indicators, 8836) -- Grace of Air
|
||||
AddIndicator(indicators, 8177) -- Grounding Totem
|
||||
AddIndicator(indicators, 10596) -- Nature Resistance
|
||||
AddIndicator(indicators, 8185) -- Fire Resistance
|
||||
AddIndicator(indicators, 8182) -- Frost Resistance
|
||||
end
|
||||
|
||||
return indicators
|
||||
end
|
||||
|
||||
local function abbrevname(t)
|
||||
return string.sub(t,1,1)..". "
|
||||
return t:sub(1,1)..". "
|
||||
end
|
||||
|
||||
function pfUI.uf:GetNameString(unitstr)
|
||||
@@ -2457,12 +2556,12 @@ function pfUI.uf:GetNameString(unitstr)
|
||||
|
||||
-- first try to only abbreviate the first word
|
||||
if abbrev and name and strlen(name) > size then
|
||||
name = string.gsub(name, "^(%S+) ", abbrevname)
|
||||
name = name:gsub("^(%S+) ", abbrevname)
|
||||
end
|
||||
|
||||
-- abbreviate all if it still doesn't fit
|
||||
if abbrev and name and strlen(name) > size then
|
||||
name = string.gsub(name, "(%S+) ", abbrevname)
|
||||
name = name:gsub("(%S+) ", abbrevname)
|
||||
end
|
||||
|
||||
return name
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
-- 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
|
||||
|
||||
--[[ 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
|
||||
Vendored
-1065
File diff suppressed because it is too large
Load Diff
Vendored
-1067
File diff suppressed because it is too large
Load Diff
Vendored
-1060
File diff suppressed because it is too large
Load Diff
Vendored
-1056
File diff suppressed because it is too large
Load Diff
Vendored
-1026
File diff suppressed because it is too large
Load Diff
Vendored
-1063
File diff suppressed because it is too large
Load Diff
Vendored
-1080
File diff suppressed because it is too large
Load Diff
Vendored
-2352
File diff suppressed because it is too large
Load Diff
Vendored
+2377
File diff suppressed because it is too large
Load Diff
Vendored
+2991
File diff suppressed because it is too large
Load Diff
Vendored
+4
-2
@@ -51,10 +51,12 @@ pfUI_translation["deDE"] = {
|
||||
["Auto Sell Grey Items"] = nil,
|
||||
["Average Per Hour"] = nil,
|
||||
["Background Color"] = nil,
|
||||
["Bags"] = "Taschen",
|
||||
["Bags & Bank"] = nil,
|
||||
["Bags Border Size"] = nil,
|
||||
["Bagslots Per Row"] = nil,
|
||||
["Bagspace"] = nil,
|
||||
["Bank"] = "Bank",
|
||||
["Banker"] = nil,
|
||||
["Bankslots Per Row"] = nil,
|
||||
["Bar Background"] = nil,
|
||||
@@ -290,7 +292,6 @@ pfUI_translation["deDE"] = {
|
||||
["Enable Mana Ticks"] = nil,
|
||||
["Enable Micro Bar"] = nil,
|
||||
["Enable Mouselook With Right Click"] = nil,
|
||||
["Enable Mouseover Tooltip"] = nil,
|
||||
["Enable Movable Bags"] = nil,
|
||||
["Enable Offscreen Frame Positions"] = nil,
|
||||
["Enable Overlap"] = nil,
|
||||
@@ -311,6 +312,7 @@ pfUI_translation["deDE"] = {
|
||||
["Encode"] = nil,
|
||||
["Ended"] = nil,
|
||||
["Energy Color"] = nil,
|
||||
["Equipped"] = "Angelegt",
|
||||
["Equipped Item Color"] = nil,
|
||||
["Estimate Debuffs"] = nil,
|
||||
["Estimate Enemy Health Points"] = nil,
|
||||
@@ -668,7 +670,6 @@ pfUI_translation["deDE"] = {
|
||||
["Scale"] = nil,
|
||||
["Scale Border On HiDPI Displays"] = nil,
|
||||
["Scaling"] = nil,
|
||||
["Scan Macros For Spells"] = nil,
|
||||
["Screen Edge Glow Intensity"] = nil,
|
||||
["Screen Resolution"] = nil,
|
||||
["Screenshot"] = nil,
|
||||
@@ -819,6 +820,7 @@ pfUI_translation["deDE"] = {
|
||||
["Top Actionbar"] = nil,
|
||||
["Top Left"] = nil,
|
||||
["Top Right"] = nil,
|
||||
["Total"] = "Gesamt",
|
||||
["Total Gold"] = nil,
|
||||
["Totem Direction"] = nil,
|
||||
["Totem Icons"] = nil,
|
||||
|
||||
Vendored
+10
-2
@@ -52,15 +52,19 @@ pfUI_translation["enUS"] = {
|
||||
["Auto Sell Grey Items"] = nil,
|
||||
["Average Per Hour"] = nil,
|
||||
["Background Color"] = nil,
|
||||
["Bags"] = nil,
|
||||
["Bags & Bank"] = nil,
|
||||
["Bags Border Size"] = nil,
|
||||
["Bagslots Per Row"] = nil,
|
||||
["Bagspace"] = nil,
|
||||
["Bank"] = nil,
|
||||
["Banker"] = nil,
|
||||
["Bankslots Per Row"] = nil,
|
||||
["Bar Background"] = nil,
|
||||
["Bar Texture"] = nil,
|
||||
["Battleground Frames"] = nil,
|
||||
["Battleground Statistics"] = nil,
|
||||
["BEHIND"] = nil,
|
||||
["Blacklist"] = nil,
|
||||
["Blinding Powder"] = nil,
|
||||
["Blue Border On Friendly Players"] = nil,
|
||||
@@ -293,7 +297,6 @@ pfUI_translation["enUS"] = {
|
||||
["Enable Mana Ticks"] = nil,
|
||||
["Enable Micro Bar"] = nil,
|
||||
["Enable Mouselook With Right Click"] = nil,
|
||||
["Enable Mouseover Tooltip"] = nil,
|
||||
["Enable Movable Bags"] = nil,
|
||||
["Enable Offscreen Frame Positions"] = nil,
|
||||
["Enable Overlap"] = nil,
|
||||
@@ -314,6 +317,7 @@ pfUI_translation["enUS"] = {
|
||||
["Encode"] = nil,
|
||||
["Ended"] = nil,
|
||||
["Energy Color"] = nil,
|
||||
["Equipped"] = nil,
|
||||
["Equipped Item Color"] = nil,
|
||||
["Estimate Debuffs"] = nil,
|
||||
["Estimate Enemy Health Points"] = nil,
|
||||
@@ -326,6 +330,7 @@ pfUI_translation["enUS"] = {
|
||||
["Export"] = nil,
|
||||
["Face"] = nil,
|
||||
["Fade To Custom Color"] = nil,
|
||||
["FARM MODE"] = nil,
|
||||
["Fast"] = nil,
|
||||
["Filter Mode"] = nil,
|
||||
["Finish"] = nil,
|
||||
@@ -536,6 +541,7 @@ pfUI_translation["enUS"] = {
|
||||
["Next Memory Cleanup"] = nil,
|
||||
["No"] = nil,
|
||||
["No Anchor"] = nil,
|
||||
["NO LOS"] = nil,
|
||||
["None"] = nil,
|
||||
["Not a valid button!"] = nil,
|
||||
["No tracking spell active"] = nil,
|
||||
@@ -677,7 +683,6 @@ pfUI_translation["enUS"] = {
|
||||
["Scale"] = nil,
|
||||
["Scale Border On HiDPI Displays"] = nil,
|
||||
["Scaling"] = nil,
|
||||
["Scan Macros For Spells"] = nil,
|
||||
["Screen Edge Glow Intensity"] = nil,
|
||||
["Screen Resolution"] = nil,
|
||||
["Screenshot"] = nil,
|
||||
@@ -771,6 +776,8 @@ pfUI_translation["enUS"] = {
|
||||
["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 reloadUI now?"] = nil,
|
||||
["Sort Bags"] = nil,
|
||||
["Sort Bank"] = nil,
|
||||
["Sort Order"] = nil,
|
||||
["Soul Shard"] = nil,
|
||||
["Soulshard Counter"] = nil,
|
||||
@@ -835,6 +842,7 @@ pfUI_translation["enUS"] = {
|
||||
["Top Actionbar"] = nil,
|
||||
["Top Left"] = nil,
|
||||
["Top Right"] = nil,
|
||||
["Total"] = nil,
|
||||
["Total Gold"] = nil,
|
||||
["Totem Direction"] = nil,
|
||||
["Totem Icons"] = nil,
|
||||
|
||||
Vendored
+4
-2
@@ -51,10 +51,12 @@ pfUI_translation["esES"] = {
|
||||
["Auto Sell Grey Items"] = "Vender objetos grises automáticamente",
|
||||
["Average Per Hour"] = "Promedio por hora",
|
||||
["Background Color"] = "Color del fondo",
|
||||
["Bags"] = "Bolsas",
|
||||
["Bags & Bank"] = "Bolsas y banco",
|
||||
["Bags Border Size"] = "Tamaño del borde de las bolsas",
|
||||
["Bagslots Per Row"] = "Casillas de la bolsa por fila",
|
||||
["Bagspace"] = "Huecos en las bolsas",
|
||||
["Bank"] = "Banco",
|
||||
["Banker"] = "Banquero",
|
||||
["Bankslots Per Row"] = "Casillas del banco por fila",
|
||||
["Bar Background"] = "Fondo de la barra",
|
||||
@@ -290,7 +292,6 @@ pfUI_translation["esES"] = {
|
||||
["Enable Mana Ticks"] = "Activar el indicador de pulsos del maná",
|
||||
["Enable Micro Bar"] = "Activar microbarra",
|
||||
["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 Offscreen Frame Positions"] = "Permitir mover los marcos fuera de la pantalla",
|
||||
["Enable Overlap"] = "Permitir superposición",
|
||||
@@ -311,6 +312,7 @@ pfUI_translation["esES"] = {
|
||||
["Encode"] = "Codificar",
|
||||
["Ended"] = "Terminado",
|
||||
["Energy Color"] = "Color de energía",
|
||||
["Equipped"] = "Equipado",
|
||||
["Equipped Item Color"] = "Color del objeto equipado",
|
||||
["Estimate Debuffs"] = "Estimar los perjuicios",
|
||||
["Estimate Enemy Health Points"] = "Estimar los puntos de salud del enemigo",
|
||||
@@ -668,7 +670,6 @@ pfUI_translation["esES"] = {
|
||||
["Scale"] = "Escala",
|
||||
["Scale Border On HiDPI Displays"] = "Escalar los bordes en las pantallas con DPI alto",
|
||||
["Scaling"] = "Escalada",
|
||||
["Scan Macros For Spells"] = nil,
|
||||
["Screen Edge Glow Intensity"] = "Intensidad de brillo en los bordes de la pantalla",
|
||||
["Screen Resolution"] = "Resolución de pantalla",
|
||||
["Screenshot"] = "Captura de pantalla",
|
||||
@@ -819,6 +820,7 @@ pfUI_translation["esES"] = {
|
||||
["Top Actionbar"] = "Barra de acción superior",
|
||||
["Top Left"] = "Superior derecha",
|
||||
["Top Right"] = "Superior izquierda",
|
||||
["Total"] = "Total",
|
||||
["Total Gold"] = "Oro Total",
|
||||
["Totem Direction"] = "Dirección del tótem",
|
||||
["Totem Icons"] = "Iconos de los tótems",
|
||||
|
||||
Vendored
+4
-2
@@ -51,10 +51,12 @@ pfUI_translation["frFR"] = {
|
||||
["Auto Sell Grey Items"] = "Vente automatique des objets gris",
|
||||
["Average Per Hour"] = "Moyenne par heure",
|
||||
["Background Color"] = "Couleur de l'arrière-plan",
|
||||
["Bags"] = "Sacs",
|
||||
["Bags & Bank"] = "Sacs et Banque",
|
||||
["Bags Border Size"] = "Taille de la bordure des Sacs",
|
||||
["Bagslots Per Row"] = "Emplacements des sacs par rangée",
|
||||
["Bagspace"] = "Place libre des sacs",
|
||||
["Bank"] = "Banque",
|
||||
["Banker"] = "Banquier",
|
||||
["Bankslots Per Row"] = "Emplacements de la banque par rangée",
|
||||
["Bar Background"] = "Arrière plan de la barre",
|
||||
@@ -290,7 +292,6 @@ pfUI_translation["frFR"] = {
|
||||
["Enable Mana Ticks"] = "Activer les Ticks de mana",
|
||||
["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 Mouseover Tooltip"] = "Activer l'infobulle au passage de la souris",
|
||||
["Enable Movable Bags"] = "Activer les sacs mobiles",
|
||||
["Enable Offscreen Frame Positions"] = "Activer le positionnement des cadres en dehors de l'écran",
|
||||
["Enable Overlap"] = "Activer la superposition",
|
||||
@@ -311,6 +312,7 @@ pfUI_translation["frFR"] = {
|
||||
["Encode"] = "Encoder",
|
||||
["Ended"] = "Terminé",
|
||||
["Energy Color"] = "Couleur de l'energie",
|
||||
["Equipped"] = "Équipé",
|
||||
["Equipped Item Color"] = "Couleur de l'objet équipé",
|
||||
["Estimate Debuffs"] = "Estimer les Affaiblissements",
|
||||
["Estimate Enemy Health Points"] = "Estimer les points de vie ennemis",
|
||||
@@ -668,7 +670,6 @@ pfUI_translation["frFR"] = {
|
||||
["Scale"] = "Échelle",
|
||||
["Scale Border On HiDPI Displays"] = "Échelle de bordure sur les écrans HiDPI",
|
||||
["Scaling"] = "Mise à l'échelle",
|
||||
["Scan Macros For Spells"] = nil,
|
||||
["Screen Edge Glow Intensity"] = nil,
|
||||
["Screen Resolution"] = "Résolution d'écran",
|
||||
["Screenshot"] = "Imprime écran",
|
||||
@@ -819,6 +820,7 @@ pfUI_translation["frFR"] = {
|
||||
["Top Actionbar"] = "Barre d'action supérieure",
|
||||
["Top Left"] = "Haut Gauche",
|
||||
["Top Right"] = "Haut Droit",
|
||||
["Total"] = "Total",
|
||||
["Total Gold"] = "Or Total",
|
||||
["Totem Direction"] = "Direction des totems",
|
||||
["Totem Icons"] = "Icones des totems",
|
||||
|
||||
Vendored
+4
-2
@@ -51,10 +51,12 @@ pfUI_translation["koKR"] = {
|
||||
["Auto Sell Grey Items"] = "회색아이템 자동 판매",
|
||||
["Average Per Hour"] = nil,
|
||||
["Background Color"] = "백그라운드 색상",
|
||||
["Bags"] = "가방",
|
||||
["Bags & Bank"] = "가방&은행",
|
||||
["Bags Border Size"] = "가방 테두리 크기",
|
||||
["Bagslots Per Row"] = nil,
|
||||
["Bagspace"] = "가방 공간",
|
||||
["Bank"] = "은행",
|
||||
["Banker"] = nil,
|
||||
["Bankslots Per Row"] = nil,
|
||||
["Bar Background"] = nil,
|
||||
@@ -290,7 +292,6 @@ pfUI_translation["koKR"] = {
|
||||
["Enable Mana Ticks"] = nil,
|
||||
["Enable Micro Bar"] = "미니바 보기(블리자드 마이크로바)",
|
||||
["Enable Mouselook With Right Click"] = "오른쪽 클릭 Mouselook 사용",
|
||||
["Enable Mouseover Tooltip"] = nil,
|
||||
["Enable Movable Bags"] = nil,
|
||||
["Enable Offscreen Frame Positions"] = "오프스크린 프레임 위치 활성화",
|
||||
["Enable Overlap"] = "겹쳐서 표시",
|
||||
@@ -311,6 +312,7 @@ pfUI_translation["koKR"] = {
|
||||
["Encode"] = nil,
|
||||
["Ended"] = nil,
|
||||
["Energy Color"] = nil,
|
||||
["Equipped"] = "착용 중",
|
||||
["Equipped Item Color"] = nil,
|
||||
["Estimate Debuffs"] = nil,
|
||||
["Estimate Enemy Health Points"] = nil,
|
||||
@@ -668,7 +670,6 @@ pfUI_translation["koKR"] = {
|
||||
["Scale"] = nil,
|
||||
["Scale Border On HiDPI Displays"] = nil,
|
||||
["Scaling"] = nil,
|
||||
["Scan Macros For Spells"] = nil,
|
||||
["Screen Edge Glow Intensity"] = nil,
|
||||
["Screen Resolution"] = "화면 해상도",
|
||||
["Screenshot"] = nil,
|
||||
@@ -819,6 +820,7 @@ pfUI_translation["koKR"] = {
|
||||
["Top Actionbar"] = nil,
|
||||
["Top Left"] = nil,
|
||||
["Top Right"] = nil,
|
||||
["Total"] = "총",
|
||||
["Total Gold"] = "총 금",
|
||||
["Totem Direction"] = nil,
|
||||
["Totem Icons"] = nil,
|
||||
|
||||
Vendored
+4
-2
@@ -51,10 +51,12 @@ pfUI_translation["ruRU"] = {
|
||||
["Auto Sell Grey Items"] = "Автоматическая продажа серых предметов",
|
||||
["Average Per Hour"] = "Среднее за час",
|
||||
["Background Color"] = "Цвет фона",
|
||||
["Bags"] = "Сумки",
|
||||
["Bags & Bank"] = "Сумки & Банк",
|
||||
["Bags Border Size"] = "Размер границы сумок",
|
||||
["Bagslots Per Row"] = "Количество ячеек в сумке на строку",
|
||||
["Bagspace"] = "Место в сумках",
|
||||
["Bank"] = "Банк",
|
||||
["Banker"] = "Банкир",
|
||||
["Bankslots Per Row"] = "Количество ячеек в банке на строку",
|
||||
["Bar Background"] = "Фон панели",
|
||||
@@ -290,7 +292,6 @@ pfUI_translation["ruRU"] = {
|
||||
["Enable Mana Ticks"] = "Включить восполнение маны",
|
||||
["Enable Micro Bar"] = "Включить панель микро меню",
|
||||
["Enable Mouselook With Right Click"] = "Включить нажатие правой кнопки мыши на индикаторе здоровья",
|
||||
["Enable Mouseover Tooltip"] = "Включить подсказку при наведении мыши",
|
||||
["Enable Movable Bags"] = "Включить подвижные сумки",
|
||||
["Enable Offscreen Frame Positions"] = "Включить расположение окон за пределами экрана",
|
||||
["Enable Overlap"] = "Включить перекрытие",
|
||||
@@ -311,6 +312,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Encode"] = "Кодировать",
|
||||
["Ended"] = "Окончание",
|
||||
["Energy Color"] = "Цвет энергии",
|
||||
["Equipped"] = "Экипировано",
|
||||
["Equipped Item Color"] = "Цвет экипированных предметов",
|
||||
["Estimate Debuffs"] = "Оценка дебафов",
|
||||
["Estimate Enemy Health Points"] = "Оценка очков здоровья врага",
|
||||
@@ -668,7 +670,6 @@ pfUI_translation["ruRU"] = {
|
||||
["Scale"] = "Масштаб",
|
||||
["Scale Border On HiDPI Displays"] = "Масштабировать границы на HiDPI мониторах",
|
||||
["Scaling"] = "Масштаб интерфейса",
|
||||
["Scan Macros For Spells"] = nil,
|
||||
["Screen Edge Glow Intensity"] = "Интенсивность свечения на краях экрана",
|
||||
["Screen Resolution"] = "Разрешение экрана",
|
||||
["Screenshot"] = "Снимок экрана",
|
||||
@@ -819,6 +820,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Top Actionbar"] = "Верхняя",
|
||||
["Top Left"] = "Сверху слева",
|
||||
["Top Right"] = "Сверху справа",
|
||||
["Total"] = "Всего",
|
||||
["Total Gold"] = "Всего золота",
|
||||
["Totem Direction"] = "Направление тотемов",
|
||||
["Totem Icons"] = "Иконки тотемов",
|
||||
|
||||
Vendored
+4
-2
@@ -51,10 +51,12 @@ pfUI_translation["zhCN"] = {
|
||||
["Auto Sell Grey Items"] = "自动贩卖灰色物品",
|
||||
["Average Per Hour"] = "平均每小时",
|
||||
["Background Color"] = "背景颜色",
|
||||
["Bags"] = "背包",
|
||||
["Bags & Bank"] = "背包和银行",
|
||||
["Bags Border Size"] = "背包边框大小",
|
||||
["Bagslots Per Row"] = "每行包裹槽数量",
|
||||
["Bagspace"] = "背包空间",
|
||||
["Bank"] = "银行",
|
||||
["Banker"] = "工会银行角色",
|
||||
["Bankslots Per Row"] = "每行银行槽数量",
|
||||
["Bar Background"] = "动作条背景",
|
||||
@@ -290,7 +292,6 @@ pfUI_translation["zhCN"] = {
|
||||
["Enable Mana Ticks"] = "显示法力值刻度",
|
||||
["Enable Micro Bar"] = "显示菜单栏",
|
||||
["Enable Mouselook With Right Click"] = "启动右键移动镜头",
|
||||
["Enable Mouseover Tooltip"] = "启用鼠标悬停工具提示",
|
||||
["Enable Movable Bags"] = "启用可移动包裹",
|
||||
["Enable Offscreen Frame Positions"] = "允许本插件所有框体移出屏幕边缘",
|
||||
["Enable Overlap"] = "叠加显示",
|
||||
@@ -311,6 +312,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Encode"] = "编码",
|
||||
["Ended"] = "结束",
|
||||
["Energy Color"] = "能量值颜色",
|
||||
["Equipped"] = "已装备",
|
||||
["Equipped Item Color"] = "已装备物品颜色",
|
||||
["Estimate Debuffs"] = "预估Debuffs",
|
||||
["Estimate Enemy Health Points"] = "预估敌人的生命值",
|
||||
@@ -668,7 +670,6 @@ pfUI_translation["zhCN"] = {
|
||||
["Scale"] = "比例",
|
||||
["Scale Border On HiDPI Displays"] = "缩放高DPI显示器上的边框",
|
||||
["Scaling"] = "UI缩放",
|
||||
["Scan Macros For Spells"] = "扫描宏命令中的法术",
|
||||
["Screen Edge Glow Intensity"] = "屏幕边缘发光强度",
|
||||
["Screen Resolution"] = "屏幕分辨率",
|
||||
["Screenshot"] = "屏幕截图",
|
||||
@@ -820,6 +821,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Top Actionbar"] = "上方动作条",
|
||||
["Top Left"] = "左上方",
|
||||
["Top Right"] = "右上方",
|
||||
["Total"] = "总计",
|
||||
["Total Gold"] = "总黄金",
|
||||
["Totem Direction"] = "图腾方向",
|
||||
["Totem Icons"] = "图腾图标",
|
||||
|
||||
Vendored
+4
-2
@@ -51,10 +51,12 @@ pfUI_translation["zhTW"] = {
|
||||
["Auto Sell Grey Items"] = "自動販賣灰色物品",
|
||||
["Average Per Hour"] = "平均每小時",
|
||||
["Background Color"] = "背景顏色",
|
||||
["Bags"] = "背包",
|
||||
["Bags & Bank"] = "背包和銀行",
|
||||
["Bags Border Size"] = "背包邊框大小",
|
||||
["Bagslots Per Row"] = "每行包裹槽數量",
|
||||
["Bagspace"] = "背包空間",
|
||||
["Bank"] = "銀行",
|
||||
["Banker"] = "工會銀行角色",
|
||||
["Bankslots Per Row"] = "每行銀行槽數量",
|
||||
["Bar Background"] = nil,
|
||||
@@ -290,7 +292,6 @@ pfUI_translation["zhTW"] = {
|
||||
["Enable Mana Ticks"] = nil,
|
||||
["Enable Micro Bar"] = "顯示功能表列",
|
||||
["Enable Mouselook With Right Click"] = "啟動右鍵移動鏡頭",
|
||||
["Enable Mouseover Tooltip"] = "啟用滑鼠懸停工具提示",
|
||||
["Enable Movable Bags"] = "啟用可移動包裹",
|
||||
["Enable Offscreen Frame Positions"] = "啟用螢幕框架固定",
|
||||
["Enable Overlap"] = "疊加顯示",
|
||||
@@ -311,6 +312,7 @@ pfUI_translation["zhTW"] = {
|
||||
["Encode"] = "編碼",
|
||||
["Ended"] = nil,
|
||||
["Energy Color"] = nil,
|
||||
["Equipped"] = "已裝備",
|
||||
["Equipped Item Color"] = nil,
|
||||
["Estimate Debuffs"] = nil,
|
||||
["Estimate Enemy Health Points"] = nil,
|
||||
@@ -668,7 +670,6 @@ pfUI_translation["zhTW"] = {
|
||||
["Scale"] = "比例",
|
||||
["Scale Border On HiDPI Displays"] = nil,
|
||||
["Scaling"] = nil,
|
||||
["Scan Macros For Spells"] = nil,
|
||||
["Screen Edge Glow Intensity"] = nil,
|
||||
["Screen Resolution"] = "螢幕解析度",
|
||||
["Screenshot"] = nil,
|
||||
@@ -819,6 +820,7 @@ pfUI_translation["zhTW"] = {
|
||||
["Top Actionbar"] = nil,
|
||||
["Top Left"] = "左上方",
|
||||
["Top Right"] = "右上方",
|
||||
["Total"] = "全部",
|
||||
["Total Gold"] = "總金量",
|
||||
["Totem Direction"] = nil,
|
||||
["Totem Icons"] = nil,
|
||||
|
||||
+1
-2
@@ -70,9 +70,7 @@
|
||||
<Include file="..\modules\addoncompat.lua"/>
|
||||
<Include file="..\modules\energytick.lua"/>
|
||||
<Include file="..\modules\totems.lua"/>
|
||||
<Include file="..\modules\macrotweak.lua"/>
|
||||
<Include file="..\modules\macroicons.lua"/>
|
||||
<Include file="..\modules\turtle-wow.lua"/>
|
||||
<Include file="..\modules\superwow.lua"/>
|
||||
<Include file="..\modules\innervatecall.lua"/>
|
||||
<Include file="..\modules\nampower.lua"/>
|
||||
@@ -81,4 +79,5 @@
|
||||
<Include file="..\modules\equipmentmanager.lua"/>
|
||||
<Include file="..\modules\loothistory.lua"/>
|
||||
<Include file="..\modules\newitem.lua"/>
|
||||
<Include file="..\modules\friendnotes.lua"/>
|
||||
</Ui>
|
||||
+1
-9
@@ -35,14 +35,6 @@
|
||||
<Include file="..\skins\blizzard\tooltips.lua"/>
|
||||
<Include file="..\skins\blizzard\tabard.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>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<Include file="..\compat\vanilla.lua"/>
|
||||
<Include file="..\env\tables_stock.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,14 @@
|
||||
<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>
|
||||
+26
-67
@@ -376,11 +376,9 @@ local function GetDebuffSlotMap(guid)
|
||||
local texture = libdebuff:GetSpellIcon(spellId)
|
||||
local stacks = (auraApps and auraApps[auraSlot] or 0) + 1
|
||||
local dtype = nil
|
||||
if GetSpellRecField then
|
||||
local dispelId = GetSpellRecField(spellId, "dispel")
|
||||
if dispelId and dispelId > 0 then
|
||||
dtype = dispelTypeMap[dispelId]
|
||||
end
|
||||
local dispelId = C_Spell.GetSpellDispelType(spellId)
|
||||
if dispelId and dispelId > 0 then
|
||||
dtype = dispelTypeMap[dispelId]
|
||||
end
|
||||
map[displaySlot] = {
|
||||
auraSlot = auraSlot,
|
||||
@@ -593,37 +591,13 @@ end
|
||||
-- DURATION FUNCTIONS
|
||||
-- ============================================================================
|
||||
|
||||
-- Report the live, talent-accurate duration of a matching aura on the player.
|
||||
-- ClassicAPI's C_UnitAuras carries the real (mod-folded) duration straight from
|
||||
-- the engine, so there's no static duration table to maintain. Returns 0 when
|
||||
-- the player has no such aura (e.g. a debuff that only ever lands on enemies).
|
||||
function libdebuff:GetDuration(effect, rank)
|
||||
if L["debuffs"][effect] then
|
||||
local rank = rank and tonumber((string.gsub(rank, RANK, ""))) or 0
|
||||
local rank = L["debuffs"][effect][rank] and rank or libdebuff:GetMaxRank(effect)
|
||||
local duration = L["debuffs"][effect][rank]
|
||||
|
||||
if effect == L["dyndebuffs"]["Demoralizing Shout"] then
|
||||
local _,_,_,_,count = GetTalentInfo(2,1)
|
||||
if count and count > 0 then duration = duration + ( duration / 100 * (count*10)) end
|
||||
elseif effect == L["dyndebuffs"]["Shadow Word: Pain"] then
|
||||
local _,_,_,_,count = GetTalentInfo(3,4)
|
||||
if count and count > 0 then duration = duration + count * 3 end
|
||||
elseif effect == L["dyndebuffs"]["Frostbolt"] then
|
||||
local _,_,_,_,count = GetTalentInfo(3,7)
|
||||
if count and count > 0 then duration = duration + count end
|
||||
elseif effect == L["dyndebuffs"]["Gouge"] then
|
||||
local _,_,_,_,count = GetTalentInfo(3,3)
|
||||
if count and count > 0 then duration = duration + (count*.5) end
|
||||
end
|
||||
return duration
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function libdebuff:GetMaxRank(effect)
|
||||
local max = 0
|
||||
for id in pairs(L["debuffs"][effect]) do
|
||||
if id > max then max = id end
|
||||
end
|
||||
return max
|
||||
local aura = C_UnitAuras.GetAuraDataBySpellName("player", effect)
|
||||
return aura and aura.duration or 0
|
||||
end
|
||||
|
||||
function libdebuff:UpdateDuration(unit, unitlevel, effect, duration)
|
||||
@@ -649,7 +623,6 @@ libdebuff.objects = {}
|
||||
|
||||
function libdebuff:AddPending(unit, unitlevel, effect, duration, caster, rank)
|
||||
if not unit or duration <= 0 then return end
|
||||
if not L["debuffs"][effect] then return end
|
||||
if libdebuff.pending[3] then return end
|
||||
|
||||
libdebuff.pending[1] = unit
|
||||
@@ -728,39 +701,25 @@ end
|
||||
-- API: GetBestAuraCast (for libpredict HoT tracking)
|
||||
-- ============================================================================
|
||||
|
||||
-- Report the active aura for spellName on `guid`, read straight from
|
||||
-- C_UnitAuras. expirationTime is the true, caster-modified remaining time, so
|
||||
-- no cast-event bookkeeping is needed -- and a unit only ever holds one instance
|
||||
-- of a given spell, so this is inherently the "best" one.
|
||||
-- Returns (startTime, duration, timeleft, rank, casterGuid) or nil.
|
||||
function libdebuff:GetBestAuraCast(guid, spellName)
|
||||
if not guid or not spellName then return nil end
|
||||
|
||||
-- Check ownDebuffs first (for our casts)
|
||||
if ownDebuffs[guid] and ownDebuffs[guid][spellName] then
|
||||
local data = ownDebuffs[guid][spellName]
|
||||
local timeleft = (data.startTime + data.duration) - GetTime()
|
||||
if timeleft > 0 then
|
||||
return data.startTime, data.duration, timeleft, data.rank, GetPlayerGuid()
|
||||
end
|
||||
end
|
||||
|
||||
-- Check allAuraCasts (for any caster)
|
||||
if allAuraCasts[guid] and allAuraCasts[guid][spellName] then
|
||||
local bestData = nil
|
||||
local bestCaster = nil
|
||||
local bestTimeleft = 0
|
||||
|
||||
for casterGuid, data in pairs(allAuraCasts[guid][spellName]) do
|
||||
local timeleft = (data.startTime + data.duration) - GetTime()
|
||||
if timeleft > bestTimeleft then
|
||||
bestTimeleft = timeleft
|
||||
bestData = data
|
||||
bestCaster = casterGuid
|
||||
end
|
||||
end
|
||||
|
||||
if bestData and bestTimeleft > 0 then
|
||||
return bestData.startTime, bestData.duration, bestTimeleft, bestData.rank, bestCaster
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
local aura = C_UnitAuras.GetAuraDataBySpellName(guid, spellName)
|
||||
if not aura or not aura.expirationTime or aura.expirationTime == 0 then return nil end
|
||||
|
||||
local timeleft = aura.expirationTime - GetTime()
|
||||
if timeleft <= 0 then return nil end
|
||||
|
||||
local duration = (aura.duration and aura.duration > 0) and aura.duration or timeleft
|
||||
local startTime = aura.expirationTime - duration
|
||||
local rank = aura.spellId and tonumber((string.gsub(C_Spell.GetSpellSubtext(aura.spellId) or "", RANK, ""))) or nil
|
||||
|
||||
return startTime, duration, timeleft, rank, aura.sourceGUID
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ local libhealth = CreateFrame("Frame")
|
||||
libhealth.enabled = true
|
||||
libhealth.reqhit = 4
|
||||
libhealth.reqdmg = 5
|
||||
libhealth:RegisterEvent("UNIT_HEALTH")
|
||||
libhealth:RegisterEvent("UNIT_COMBAT")
|
||||
libhealth:RegisterUnitEvent("UNIT_HEALTH", "target")
|
||||
libhealth:RegisterUnitEvent("UNIT_COMBAT", "target")
|
||||
libhealth:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
libhealth:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
libhealth:SetScript("OnEvent", function()
|
||||
|
||||
+5
-2
@@ -168,6 +168,9 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu
|
||||
return
|
||||
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
|
||||
local amount = cache[spell_queue[2]][1]
|
||||
local casttime = castTime
|
||||
@@ -1131,7 +1134,7 @@ libpredict.sender:RegisterEvent("SPELL_HEAL_BY_SELF")
|
||||
libpredict.sender:RegisterEvent("SPELL_HEAL_BY_OTHER") -- populates foreignCache for other healers
|
||||
|
||||
-- force cache updates
|
||||
libpredict.sender:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||||
libpredict.sender:RegisterUnitEvent("UNIT_INVENTORY_CHANGED", "player")
|
||||
libpredict.sender:RegisterEvent("SKILL_LINES_CHANGED")
|
||||
|
||||
-- Shared cleanup helper for failed/interrupted casts
|
||||
@@ -1222,7 +1225,7 @@ libpredict.sender:SetScript("OnEvent", function()
|
||||
end)
|
||||
|
||||
function libpredict:GetHotDuration(unit, spell)
|
||||
if unit == UNKNOWNOBJECT or unit == UNKOWNBEING then return end
|
||||
if unit == UNKNOWNOBJECT or unit == UKNOWNBEING then return end
|
||||
|
||||
-- NEW: Try libdebuff first (Nampower AURA_CAST events)
|
||||
if pfUI.api.libdebuff and pfUI.api.libdebuff.GetBestAuraCast then
|
||||
|
||||
+6
-5
@@ -83,12 +83,13 @@ end
|
||||
-- [number] Casting time of the spell in milliseconds
|
||||
-- [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] The numeric spell-id of 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
|
||||
local spellinfo = {}
|
||||
function libspell.GetSpellInfo(index, bookType)
|
||||
local cache = spellinfo[index]
|
||||
if cache then return cache[1], cache[2], cache[3], cache[4], cache[5], cache[6], cache[7], cache[8] end
|
||||
if cache then return unpack(cache) end
|
||||
|
||||
local slot
|
||||
if type(index) == "string" then
|
||||
@@ -108,10 +109,10 @@ function libspell.GetSpellInfo(index, bookType)
|
||||
-- ClassicAPI's GetSpellInfo returns: name, rank, icon, cost, isFunnel, powerType,
|
||||
-- castTime(ms), minRange, maxRange, spellID. Keep libspell's historical positional
|
||||
-- shape (castingTime at 4, ranges at 5/6, slot+bookType at 7/8).
|
||||
local name, rank, icon, _, _, _, castingTime, minRange, maxRange = GetSpellInfo(slot, bookType)
|
||||
local name, rank, icon, _, _, _, castingTime, minRange, maxRange, spellId = GetSpellInfo(slot, bookType)
|
||||
|
||||
spellinfo[index] = { name, rank, icon, castingTime, minRange, maxRange, slot, bookType }
|
||||
return name, rank, icon, castingTime, minRange, maxRange, slot, bookType
|
||||
spellinfo[index] = SafePack(name, rank, icon, castingTime, minRange, maxRange, slot, bookType, spellId)
|
||||
return name, rank, icon, castingTime, minRange, maxRange, slot, bookType, spellId
|
||||
end
|
||||
|
||||
-- Reset all spell caches whenever new spells are learned/unlearned
|
||||
|
||||
+10
-11
@@ -96,13 +96,12 @@ libunitscan:SetScript("OnEvent", function()
|
||||
RememberByUnit("player", name, class)
|
||||
|
||||
elseif event == "FRIENDLIST_UPDATE" then
|
||||
local name, class, level
|
||||
for i = 1, GetNumFriends() do
|
||||
name, level, class = GetFriendInfo(i)
|
||||
class = L["class"][class] or nil
|
||||
-- friendlist updates due to friend going off-line return level 0, let's not overwrite good older values
|
||||
level = level > 0 and level or nil
|
||||
AddData("players", name, class, level)
|
||||
local info = C_FriendList.GetFriendInfoByIndex(i)
|
||||
if info then
|
||||
local level = info.level > 0 and info.level or nil
|
||||
AddData("players", info.name, info.classFilename, level)
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == "GUILD_ROSTER_UPDATE" then
|
||||
@@ -137,11 +136,11 @@ libunitscan:SetScript("OnEvent", function()
|
||||
end
|
||||
|
||||
elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then
|
||||
local name, class, level, guild, _
|
||||
for i = 1, GetNumWhoResults() do
|
||||
name, guild, level, _, class, _ = GetWhoInfo(i)
|
||||
class = L["class"][class] or nil
|
||||
AddData("players", name, class, level, nil, guild)
|
||||
for i = 1, C_FriendList.GetNumWhoResults() do
|
||||
local info = C_FriendList.GetWhoInfo(i)
|
||||
if info then
|
||||
AddData("players", info.fullName, info.filename, info.level, nil, info.fullGuildName)
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == "UPDATE_MOUSEOVER_UNIT" or event == "PLAYER_TARGET_CHANGED" or event == "NAME_PLATE_UNIT_ADDED" then
|
||||
|
||||
+34
-85
@@ -360,73 +360,8 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
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 self = self or this
|
||||
self = self or this
|
||||
|
||||
-- indicate that dragging could get enabled
|
||||
drag_await = true
|
||||
@@ -444,6 +379,8 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
else
|
||||
GameTooltip:SetPetAction(self.id)
|
||||
end
|
||||
elseif self.spellID then
|
||||
GameTooltip:SetSpellByID(self.spellID)
|
||||
elseif self.spellslot and self.booktype then
|
||||
GameTooltip:SetSpell(self.spellslot, self.booktype)
|
||||
else
|
||||
@@ -454,7 +391,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
local function ButtonLeave(self)
|
||||
local self = self or this
|
||||
self = self or this
|
||||
|
||||
-- no longer wait for a drag event
|
||||
drag_await = nil
|
||||
@@ -467,7 +404,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
local grid, sid, id, bar, active, texture, _
|
||||
local function ButtonSlotUpdate(self)
|
||||
if not self then return end
|
||||
local self = self or this
|
||||
self = self or this
|
||||
sid = self.id -- 1 to 120
|
||||
|
||||
-- reset shared variables
|
||||
@@ -586,7 +523,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
local sid, usable, oom, _
|
||||
local function ButtonUsableUpdate(self)
|
||||
local self = self or this
|
||||
self = self or this
|
||||
sid = self.id -- 1 to 120
|
||||
|
||||
if self.bar == 11 then
|
||||
@@ -622,7 +559,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
local function ButtonRangeUpdate(self)
|
||||
local self = self or this
|
||||
self = self or this
|
||||
|
||||
-- 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
|
||||
@@ -691,7 +628,6 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
local function ButtonFullUpdate(button)
|
||||
if not button then return end
|
||||
|
||||
ButtonMacroScan(button)
|
||||
ButtonSlotUpdate(button)
|
||||
ButtonRangeUpdate(button)
|
||||
ButtonUsableUpdate(button)
|
||||
@@ -700,7 +636,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
local function BarsEvent(self)
|
||||
local self = self or this
|
||||
self = self or this
|
||||
|
||||
-- refresh only specific slots
|
||||
if event == "ACTIONBAR_SLOT_CHANGED" and arg1 and arg1 ~= 0 then
|
||||
@@ -808,10 +744,28 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- create the main event and update handler for pfUI actionbars
|
||||
local bars = CreateFrame("Frame", "pfActionBar", UIParent)
|
||||
for event in pairs(special_events) do bars:RegisterEvent(event) end
|
||||
for event in pairs(global_events) do bars:RegisterEvent(event) end
|
||||
for event in pairs(aura_events) do bars:RegisterEvent(event) end
|
||||
for event in pairs(pet_events) do bars:RegisterEvent(event) end
|
||||
|
||||
-- The only unit events in the tables above; both concern the player alone.
|
||||
-- A registration keeps its kind, so these have to go in unit-filtered from
|
||||
-- the start -- RegisterUnitEvent over a plain registration stays plain.
|
||||
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
|
||||
bars:SetScript("OnEvent", BarsEvent)
|
||||
@@ -1017,7 +971,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
local id = (bar-1)*12+button
|
||||
local exists = _G[button_name] and true or nil
|
||||
local f = _G[button_name] or CreateFrame("Button", button_name, parent, ACTIONBAR_SECURE_TEMPLATE_BUTTON)
|
||||
local f = _G[button_name] or CreateFrame("Button", button_name, parent)
|
||||
|
||||
-- no button available, create a new one
|
||||
if not exists then
|
||||
@@ -1043,7 +997,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
f.slot = id
|
||||
|
||||
-- cooldown
|
||||
f.cd = CreateFrame(COOLDOWN_FRAME_TYPE, f:GetName() .. "Cooldown", f, "CooldownFrameTemplate")
|
||||
f.cd = CreateFrame("Model", f:GetName() .. "Cooldown", f, "CooldownFrameTemplate")
|
||||
f.cd.pfCooldownStyleAnimation = 1
|
||||
f.cd.pfCooldownType = "NOGCD"
|
||||
f.cd.pfCooldownSize = cd_size
|
||||
@@ -1149,12 +1103,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
f.count:SetJustifyH("RIGHT")
|
||||
f.count:SetJustifyV("BOTTOM")
|
||||
|
||||
-- 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
|
||||
f.scanmacro, f.spellslot, f.booktype = nil, nil, nil
|
||||
|
||||
-- range glow color
|
||||
f.rangeColor = GetStringColorObject(C.bars.rangecolor)
|
||||
@@ -1234,7 +1183,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- create frame
|
||||
local init = not bars[i]
|
||||
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent, ACTIONBAR_SECURE_TEMPLATE_BAR)
|
||||
bars[i] = bars[i] or CreateFrame("Frame", "pfActionBar" .. barnames[i], UIParent)
|
||||
bars[i]:SetID(i)
|
||||
|
||||
-- autohide
|
||||
|
||||
@@ -2,6 +2,11 @@ pfUI:RegisterModule("autoshift", function ()
|
||||
pfUI.autoshift = CreateFrame("Frame")
|
||||
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.errors = { SPELL_FAILED_NOT_MOUNTED, ERR_ATTACK_MOUNTED, ERR_TAXIPLAYERALREADYMOUNTED,
|
||||
|
||||
+3
-18
@@ -396,7 +396,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
if tpl == "BankItemButtonGenericTemplate" then
|
||||
local bankslot = pfUI.bags[bag].slots[slot].frame
|
||||
local name = "pfBag" .. bag .. "item" .. slot .. "Cooldown"
|
||||
bankslot.cd = CreateFrame(COOLDOWN_FRAME_TYPE, name, bankslot, "CooldownFrameTemplate")
|
||||
bankslot.cd = CreateFrame("Model", name, bankslot, "CooldownFrameTemplate")
|
||||
bankslot.cd:SetAllPoints(bankslot)
|
||||
bankslot.cd.pfCooldownStyleAnimation = 1
|
||||
bankslot.cd.pfCooldownType = "ALL"
|
||||
@@ -972,7 +972,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.sort.backdrop:SetBackdropBorderColor(1,1,.25,1)
|
||||
frame.sort.texture:SetVertexColor(1,1,.25,1)
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Sort Bags")
|
||||
GameTooltip:SetText(T["Sort Bags"])
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
|
||||
@@ -1085,21 +1085,6 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.search.edit:ClearFocus()
|
||||
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()
|
||||
if arg1 == "RightButton" then
|
||||
this:ClearFocus()
|
||||
@@ -1207,7 +1192,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.sort.backdrop:SetBackdropBorderColor(1,1,.25,1)
|
||||
frame.sort.texture:SetVertexColor(1,1,.25,1)
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Sort Bank")
|
||||
GameTooltip:SetText(T["Sort Bank"])
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ pfUI:RegisterModule("bgscore", function ()
|
||||
-- Title label
|
||||
local title = mover:CreateFontString(nil, "OVERLAY")
|
||||
title:SetFont("Fonts\\FRIZQT__.TTF", 14, "OUTLINE")
|
||||
title:SetText("Battleground Frames")
|
||||
title:SetText(T["Battleground Frames"])
|
||||
title:SetPoint("TOP", mover, "TOP", 0, -2)
|
||||
|
||||
-- BG score preview text
|
||||
|
||||
+23
-8
@@ -7,6 +7,15 @@ pfUI:RegisterModule("buff", function ()
|
||||
|
||||
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)
|
||||
if buff.btype == "HELPFUL" then
|
||||
if C.buffs.separateweapons == "1" then
|
||||
@@ -23,7 +32,8 @@ pfUI:RegisterModule("buff", function ()
|
||||
CreateBackdropShadow(buff)
|
||||
end
|
||||
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex("player", buff.id, buff.btype)
|
||||
local slots = buff.btype == "HELPFUL" and helpfulSlots or harmfulSlots
|
||||
local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id])
|
||||
|
||||
--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
|
||||
@@ -55,16 +65,16 @@ pfUI:RegisterModule("buff", function ()
|
||||
buff.texture:SetTexture(GetInventoryItemTexture("player", 17))
|
||||
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1))
|
||||
end
|
||||
elseif aura and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then
|
||||
elseif name and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then
|
||||
-- Set Buff Texture and Border
|
||||
buff.mode = buff.btype
|
||||
buff.expirationTime = aura.expirationTime
|
||||
buff.stackCount = aura.applications
|
||||
buff.spellId = aura.spellId
|
||||
buff.texture:SetTexture(aura.icon)
|
||||
buff.expirationTime = expirationTime
|
||||
buff.stackCount = count
|
||||
buff.spellId = spellId
|
||||
buff.texture:SetTexture(icon)
|
||||
|
||||
if buff.btype == "HARMFUL" then
|
||||
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(aura.dispelName)
|
||||
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(dispelType)
|
||||
buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA())
|
||||
else
|
||||
buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba)
|
||||
@@ -154,7 +164,7 @@ pfUI:RegisterModule("buff", function ()
|
||||
pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent)
|
||||
pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED")
|
||||
pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
||||
pfUI.buff:RegisterEvent("UNIT_MODEL_CHANGED")
|
||||
pfUI.buff:RegisterUnitEvent("UNIT_MODEL_CHANGED", "player")
|
||||
pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF")
|
||||
pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF")
|
||||
pfUI.buff:SetScript("OnEvent", function()
|
||||
@@ -166,6 +176,8 @@ pfUI:RegisterModule("buff", function ()
|
||||
pfUI.buff.wepbuffs.count = 0
|
||||
end
|
||||
|
||||
ScanPlayerAuraSlots()
|
||||
|
||||
for i=1,32 do
|
||||
RefreshBuffButton(pfUI.buff.buffs.buttons[i])
|
||||
end
|
||||
@@ -246,6 +258,9 @@ pfUI:RegisterModule("buff", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
-- CreateBuffButton refreshes each new button from the slot buffers
|
||||
ScanPlayerAuraSlots()
|
||||
|
||||
-- Weapon Buffs
|
||||
pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent)
|
||||
pfUI.buff.wepbuffs.count = 0
|
||||
|
||||
+38
-19
@@ -73,12 +73,19 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
return anchor
|
||||
end
|
||||
|
||||
local function GetBuffData(unit, id, type, selfdebuff)
|
||||
local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex(unit, id, filter)
|
||||
if not aura then return end
|
||||
local remaining = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0
|
||||
return remaining, aura.icon, aura.name, aura.applications
|
||||
-- reusable GetAuraSlots buffer, filled once per RefreshBuffBarFrame
|
||||
local auraSlots = {}
|
||||
|
||||
-- Separate buffer for the tooltip handler: OnEnter can fire while a refresh
|
||||
-- is showing/hiding bars under the cursor, so it must not share the above.
|
||||
local tooltipSlots = {}
|
||||
|
||||
-- Reads one aura by the slot id GetAuraSlots returned (nil slot -> nil).
|
||||
local function GetBuffData(unit, slot)
|
||||
local name, icon, count, dispelType, _, expirationTime = C_UnitAuras.UnitAuraBySlot(unit, slot)
|
||||
if not name then return end
|
||||
local remaining = expirationTime > 0 and (expirationTime - GetTime()) or 0
|
||||
return remaining, icon, name, count, dispelType
|
||||
end
|
||||
|
||||
local function StatusBarOnClick()
|
||||
@@ -90,12 +97,14 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
if val == skill then return end
|
||||
end
|
||||
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."])
|
||||
elseif IsShiftKeyDown() then
|
||||
for _, val in pairs({strsplit("#", config.blacklist)}) do
|
||||
if val == skill then return end
|
||||
end
|
||||
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."])
|
||||
end
|
||||
elseif this.parent.unit == "player" then
|
||||
@@ -111,17 +120,22 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
GameTooltip:SetUnitAura("player", 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.
|
||||
-- SetUnitAura's index has to be into the unfiltered HARMFUL list. Look up
|
||||
-- the displayed aura via the PLAYER filter, then find its position in the
|
||||
-- unfiltered list by name + sourceGUID.
|
||||
--
|
||||
-- The unfiltered list is NOT capped at 16: once a unit's 16 debuff slots
|
||||
-- are full the server parks further debuffs in buff slots, and
|
||||
-- C_UnitAuras reports those as harmful too. So enumerate what it has.
|
||||
local config = this.parent and this.parent.config
|
||||
if config and config.selfdebuff == "1" then
|
||||
local ownAura = C_UnitAuras.GetAuraDataByIndex(this.unit, this.id, "HARMFUL|PLAYER")
|
||||
if ownAura then
|
||||
for gameSlot = 1, 16 do
|
||||
local check = C_UnitAuras.GetDebuffDataByIndex(this.unit, gameSlot)
|
||||
local n = ScanAuraSlots(this.unit, "HARMFUL", tooltipSlots)
|
||||
for i = 1, n do
|
||||
local check = C_UnitAuras.GetAuraDataBySlot(this.unit, tooltipSlots[i])
|
||||
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
|
||||
GameTooltip:SetUnitAura(this.unit, gameSlot, "HARMFUL")
|
||||
GameTooltip:SetUnitAura(this.unit, i, "HARMFUL")
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -232,9 +246,15 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
local function RefreshBuffBarFrame(frame)
|
||||
-- reinitialize all active buffs
|
||||
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
|
||||
local timeleft, texture, name, stacks = GetBuffData(frame.unit, i, frame.type, selfdebuff)
|
||||
local timeleft, texture, name, stacks, dtype = GetBuffData(frame.unit, auraSlots[i])
|
||||
timeleft = timeleft or 0
|
||||
|
||||
if texture and name and name ~= "" and BuffIsVisible(frame.config, name) then
|
||||
@@ -243,12 +263,14 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
frame.buffs[i][3] = name
|
||||
frame.buffs[i][4] = texture
|
||||
frame.buffs[i][5] = stacks
|
||||
frame.buffs[i][6] = dtype
|
||||
else
|
||||
frame.buffs[i][1] = 0
|
||||
frame.buffs[i][2] = nil
|
||||
frame.buffs[i][3] = nil
|
||||
frame.buffs[i][4] = nil
|
||||
frame.buffs[i][5] = 0
|
||||
frame.buffs[i][6] = nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -292,12 +314,9 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
-- calculate dynamic auto color
|
||||
local r, g, b
|
||||
if frame.type == "HARMFUL" then
|
||||
r, g, b = 1, .2, .2
|
||||
local a = C_UnitAuras.GetDebuffDataByIndex(frame.unit, data[2])
|
||||
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
|
||||
-- official Blizzard dispel-type colors (matches unitframes/buff);
|
||||
-- nil/unknown type falls back to the DEBUFF_TYPE_NONE colour
|
||||
r, g, b = C_UnitAuras.GetAuraDispelTypeColor(data[6] or ""):GetRGBA()
|
||||
else
|
||||
r,g,b = str2rgb(data[3])
|
||||
end
|
||||
|
||||
+16
-10
@@ -45,7 +45,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
if not cb.tradeskillTotal or not cb.activeName or not cb.showname then return end
|
||||
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
|
||||
if remaining > 1 then
|
||||
cb.bar.left:SetText(string.format("%s (%d)", cb.activeName, remaining))
|
||||
cb.bar.left:SetFormattedText("%s (%d)", cb.activeName, remaining)
|
||||
else
|
||||
cb.bar.left:SetText(cb.activeName)
|
||||
end
|
||||
@@ -322,15 +322,21 @@ pfUI:RegisterModule("castbar", function ()
|
||||
-- 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
|
||||
-- already mid-cast when it becomes the target/focus still shows.
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_START")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_STOP")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_FAILED")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_DELAYED")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
|
||||
-- Filter to this bar's unit, plus "player" for the target/focus bars: the
|
||||
-- player's own casts only ever fire arg1=="player", so a self-targeted cast
|
||||
-- has to reach them too (nil for the player bar itself, which the filter
|
||||
-- skips). This only narrows what arrives -- the arg1/UnitIsUnit test below
|
||||
-- still decides whether the bar acts on it.
|
||||
local selfunit = unitstr ~= "player" and "player" or nil
|
||||
cb:RegisterUnitEvent("UNIT_SPELLCAST_START", unitstr, selfunit)
|
||||
cb:RegisterUnitEvent("UNIT_SPELLCAST_STOP", unitstr, selfunit)
|
||||
cb:RegisterUnitEvent("UNIT_SPELLCAST_FAILED", unitstr, selfunit)
|
||||
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
|
||||
cb:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
elseif unitstr == "focus" then
|
||||
|
||||
+104
-28
@@ -63,6 +63,23 @@ pfUI:RegisterModule("chat", function ()
|
||||
return pfUI_cache["chathistory"][realm][player][id]
|
||||
end
|
||||
|
||||
-- [ Chat Panel Colors ]
|
||||
-- The panels normally inherit pfUI's global appearance theme; custom colors let chat
|
||||
-- deviate from it. CreateBackdrop is re-run first to restore the theme, so toggling
|
||||
-- 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.left = CreateFrame("Frame", "pfChatLeft", UIParent)
|
||||
@@ -76,19 +93,11 @@ pfUI:RegisterModule("chat", function ()
|
||||
pfUI.chat.left:SetPoint("BOTTOMLEFT", 2*default_border,2*default_border)
|
||||
pfUI.chat.left:SetScript("OnShow", function() pfUI.chat:RefreshChat() end)
|
||||
UpdateMovable(pfUI.chat.left)
|
||||
CreateBackdrop(pfUI.chat.left, default_border, nil, .8)
|
||||
ApplyPanelColors(pfUI.chat.left)
|
||||
if C.chat.global.frameshadow == "1" then
|
||||
CreateBackdropShadow(pfUI.chat.left)
|
||||
end
|
||||
|
||||
if C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
pfUI.chat.left.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = GetStringColor(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:ClearAllPoints()
|
||||
pfUI.chat.left.panelTop:SetHeight(C.global.font_size+default_border*2)
|
||||
@@ -251,19 +260,11 @@ pfUI:RegisterModule("chat", function ()
|
||||
pfUI.chat.right:SetPoint("BOTTOMRIGHT", -2*default_border,2*default_border)
|
||||
pfUI.chat.right:SetScript("OnShow", function() pfUI.chat:RefreshChat() end)
|
||||
UpdateMovable(pfUI.chat.right)
|
||||
CreateBackdrop(pfUI.chat.right, default_border, nil, .8)
|
||||
ApplyPanelColors(pfUI.chat.right)
|
||||
if C.chat.global.frameshadow == "1" then
|
||||
CreateBackdropShadow(pfUI.chat.right)
|
||||
end
|
||||
|
||||
if C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
pfUI.chat.right.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = GetStringColor(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:ClearAllPoints()
|
||||
pfUI.chat.right.panelTop:SetHeight(C.global.font_size+default_border*2)
|
||||
@@ -296,6 +297,68 @@ pfUI:RegisterModule("chat", function ()
|
||||
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()
|
||||
local panelheight = C.global.font_size*1.5 + default_border*2 + 2
|
||||
|
||||
@@ -447,9 +510,13 @@ pfUI:RegisterModule("chat", function ()
|
||||
for index, value in pairs(DOCKED_CHAT_FRAMES) do
|
||||
FCF_UpdateButtonSide(value)
|
||||
end
|
||||
|
||||
pfUI.chat:RefreshBackgroundAlpha()
|
||||
end
|
||||
|
||||
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
|
||||
pfUI.chat.mouseovertab = CreateFrame("Frame")
|
||||
@@ -500,7 +567,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
FCF_SetLocked(ChatFrame1, 1)
|
||||
FCF_SetWindowName(ChatFrame1, GENERAL)
|
||||
FCF_SetWindowColor(ChatFrame1, 0, 0, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame1, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame1, 0.8)
|
||||
FCF_SetChatWindowFontSize(ChatFrame1, 12)
|
||||
ChatFrame1:SetUserPlaced(1)
|
||||
|
||||
@@ -508,7 +575,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
FCF_SetLocked(ChatFrame2, 1)
|
||||
FCF_SetWindowName(ChatFrame2, COMBAT_LOG)
|
||||
FCF_SetWindowColor(ChatFrame2, 0, 0, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame2, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame2, 0.8)
|
||||
FCF_SetChatWindowFontSize(ChatFrame2, 12)
|
||||
ChatFrame2:SetUserPlaced(1)
|
||||
|
||||
@@ -518,7 +585,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
FCF_SetLocked(ChatFrame3, 1)
|
||||
FCF_SetWindowName(ChatFrame3, T["Loot & Spam"])
|
||||
FCF_SetWindowColor(ChatFrame3, 0, 0, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame3, 0)
|
||||
FCF_SetWindowAlpha(ChatFrame3, 0.8)
|
||||
FCF_SetChatWindowFontSize(ChatFrame3, 12)
|
||||
FCF_UnDockFrame(ChatFrame3)
|
||||
FCF_SetTabPosition(ChatFrame3, 0)
|
||||
@@ -566,6 +633,9 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
pfUI.chat:SetScript("OnEvent", function()
|
||||
-- restore legacy chat windows stuck at 0 alpha before anything reads it
|
||||
pfUI.chat:MigrateBackgroundAlpha()
|
||||
|
||||
-- set the default chat
|
||||
FCF_SelectDockFrame(SELECTED_CHAT_FRAME)
|
||||
|
||||
@@ -757,7 +827,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
local real, _ = strsplit(":", name)
|
||||
local color = unknowncolorhex
|
||||
local match = false
|
||||
local _, class = C_PlayerCache.GetPlayerInfoByName(real)
|
||||
local _, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real)
|
||||
-- local guid = GetCurrentChatGUID()
|
||||
-- if guid then
|
||||
-- _, class = GetPlayerInfoByGUID(guid)
|
||||
@@ -775,8 +845,12 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
if C.chat.text.tintunknown == "1" or match then
|
||||
local icon = ""
|
||||
if match and C.chat.text.playericons == "1" then
|
||||
icon = GetFactionIcon(raceKey) .. GetRaceIcon(raceKey, sex)
|
||||
end
|
||||
text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)",
|
||||
left..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1")
|
||||
left..icon..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..right.."%1")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -810,23 +884,25 @@ pfUI:RegisterModule("chat", function ()
|
||||
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
|
||||
if C.chat.text.time == "1" then
|
||||
text = timecolorhex .. tleft .. date(C.chat.text.timeformat) .. tright .. "|r " .. text
|
||||
end
|
||||
|
||||
-- save chat history
|
||||
if C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1 then
|
||||
if isWhisper then
|
||||
SaveChatHistory(frame:GetID(), string.gsub(text, wcol, ""), cr, cg, cb)
|
||||
else
|
||||
SaveChatHistory(frame:GetID(), text, a1, a2, a3)
|
||||
end
|
||||
|
||||
if C.chat.global.whispermod == "1" then
|
||||
if isWhisper then
|
||||
-- patch incoming whisper string to match the colors
|
||||
if string.find(text, wcol, 1) == 1 then
|
||||
text = string.gsub(text, "|r", "|r" .. wcol)
|
||||
end
|
||||
text = string.gsub(text, "|r", "|r" .. wcol)
|
||||
end
|
||||
|
||||
frame:HookAddMessage(text, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17)
|
||||
|
||||
@@ -52,7 +52,7 @@ pfUI:RegisterModule("combopoints", function ()
|
||||
-- combo
|
||||
if class == "DRUID" or class == "ROGUE" then
|
||||
local combo = CreateFrame("Frame")
|
||||
combo:RegisterEvent("UNIT_COMBO_POINTS")
|
||||
combo:RegisterUnitEvent("UNIT_COMBO_POINTS", "player")
|
||||
combo:RegisterEvent("PLAYER_COMBO_POINTS")
|
||||
combo:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
combo:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
|
||||
+29
-19
@@ -6,30 +6,35 @@ pfUI:RegisterModule("cooldown", function ()
|
||||
-- local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
|
||||
-- local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
|
||||
|
||||
local parent, parent_name
|
||||
local function pfCooldownOnUpdate()
|
||||
parent = this:GetParent()
|
||||
if not parent then this:Hide() end
|
||||
parent_name = parent:GetName()
|
||||
-- Throttle FIRST. One of these runs per visible cooldown text, every frame,
|
||||
-- so anything above this gate is multiplied by the frame rate and by how
|
||||
-- many cooldowns are ticking.
|
||||
local now = GetTime()
|
||||
if (this.tick or 0) > now then return end
|
||||
this.tick = now + .1
|
||||
|
||||
-- avoid to set cooldowns on invalid frames
|
||||
if parent_name and _G[parent_name .. "Cooldown"] then
|
||||
if not _G[parent_name .. "Cooldown"]:IsShown() then
|
||||
this:Hide()
|
||||
end
|
||||
local parent = this:GetParent()
|
||||
if not parent then this:Hide() return end
|
||||
|
||||
-- avoid to set cooldowns on invalid frames. The cooldown frame is stashed
|
||||
-- at creation: resolving it as _G[parent:GetName() .. "Cooldown"] built and
|
||||
-- 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
|
||||
|
||||
-- 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)
|
||||
if this:GetAlpha() ~= parent:GetAlpha() then
|
||||
this:SetAlpha(parent:GetAlpha())
|
||||
end
|
||||
|
||||
if this.start < GetTime() then
|
||||
if this.start < now then
|
||||
-- calculating remaining time as it should be
|
||||
local remaining = this.duration - (GetTime() - this.start)
|
||||
local remaining = this.duration - (now - this.start)
|
||||
if remaining >= 0 then
|
||||
this.text:SetText(GetColoredTimeString(remaining))
|
||||
else
|
||||
@@ -38,13 +43,13 @@ pfUI:RegisterModule("cooldown", function ()
|
||||
else
|
||||
-- I have absolutely no idea, but it works:
|
||||
-- https://github.com/Stanzilla/WoWUIBugs/issues/47
|
||||
local time = time()
|
||||
local startupTime = time - GetTime()
|
||||
local currentTime = time()
|
||||
local startupTime = currentTime - now
|
||||
-- just a simplification of: ((2^32) - (start * 1000)) / 1000
|
||||
local cdTime = (2 ^ 32) / 1000 - this.start
|
||||
local cdStartTime = startupTime - cdTime
|
||||
local cdEndTime = cdStartTime + this.duration
|
||||
local remaining = cdEndTime - time
|
||||
local remaining = cdEndTime - currentTime
|
||||
|
||||
if remaining >= 0 then
|
||||
this.text:SetText(GetColoredTimeString(remaining))
|
||||
@@ -55,11 +60,16 @@ pfUI:RegisterModule("cooldown", function ()
|
||||
end
|
||||
|
||||
local height, size
|
||||
local textcount = 0
|
||||
local function pfCreateCoolDown(cooldown, start, duration)
|
||||
cooldown.pfCooldownText = CreateFrame("Frame", "pfCooldownFrame", cooldown:GetParent())
|
||||
textcount = textcount + 1
|
||||
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:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 2)
|
||||
cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString("pfCooldownFrameText", "OVERLAY")
|
||||
cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString(name .. "TextString", "OVERLAY")
|
||||
|
||||
if not cooldown.pfCooldownType then
|
||||
size = tonumber(C.appearance.cd.font_size_foreign)
|
||||
|
||||
+185
-60
@@ -1,20 +1,82 @@
|
||||
local function getAdjustedTickTimer()
|
||||
local adjustedEnergyTick = 2
|
||||
-- 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.
|
||||
|
||||
-- Check rogue talents and compute energy tick timing reduction for Combat spec (1.18.0 Blade Rush Talent)
|
||||
if UnitClassBase("player") == "ROGUE" then
|
||||
local _, _, _, _, currRank = GetTalentInfo(2, 16)
|
||||
local bladeRushRank = currRank or 0
|
||||
local FIVE_SECOND_RULE = 5
|
||||
|
||||
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
|
||||
-- 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
|
||||
|
||||
return adjustedEnergyTick
|
||||
-- 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()
|
||||
if not energyRegenTimeMod then
|
||||
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
|
||||
local reduction = energyRegenTimeMod * UnitStat("player", 2) / 10000
|
||||
return math.max(0.001, REGEN_TIME_FULL - reduction)
|
||||
end
|
||||
|
||||
pfUI:RegisterModule("energytick", function()
|
||||
@@ -22,14 +84,53 @@ pfUI:RegisterModule("energytick", function()
|
||||
return
|
||||
end
|
||||
|
||||
-- inside the module body on purpose: C is on pfUI.env, not _G
|
||||
local function getBarWidth()
|
||||
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
|
||||
end
|
||||
|
||||
-- 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)
|
||||
energytick:SetAllPoints(pfUI.uf.player.power.bar)
|
||||
energytick:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
energytick:RegisterEvent("UNIT_DISPLAYPOWER")
|
||||
energytick:RegisterEvent("UNIT_ENERGY")
|
||||
energytick:RegisterEvent("UNIT_MANA")
|
||||
energytick:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF")
|
||||
energytick:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS")
|
||||
energytick:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player")
|
||||
energytick:RegisterUnitEvent("UNIT_ENERGY", "player")
|
||||
energytick:RegisterUnitEvent("UNIT_MANA", "player")
|
||||
energytick:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player")
|
||||
energytick:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", "player")
|
||||
energytick:RegisterEvent("SPELLS_CHANGED")
|
||||
energytick:RegisterEvent("PLAYER_AURAS_CHANGED")
|
||||
|
||||
energytick:SetScript("OnEvent", function()
|
||||
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
|
||||
@@ -42,44 +143,52 @@ pfUI:RegisterModule("energytick", function()
|
||||
this:Hide()
|
||||
end
|
||||
|
||||
-- Filter nur eigene Energy-Gewinne von Talents/Buffs
|
||||
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
|
||||
if event == "SPELLS_CHANGED" or event == "PLAYER_AURAS_CHANGED" then
|
||||
energyRegenTimeMod = nil
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
this.lastMana = UnitPower("player")
|
||||
this.lastPower = 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
|
||||
|
||||
if (event == "UNIT_MANA" or event == "UNIT_ENERGY") and arg1 == "player" then
|
||||
this.currentMana = UnitPower("player")
|
||||
local diff = 0
|
||||
if this.lastMana then
|
||||
diff = this.currentMana - this.lastMana
|
||||
local power = UnitPower("player")
|
||||
local diff = this.lastPower and (power - this.lastPower) or 0
|
||||
this.lastPower = power
|
||||
|
||||
-- only a gain can be the tick; a spend never touches the phase
|
||||
if diff > 0 and lockTick(this) then
|
||||
-- a tick inside the window proves regen continues through it
|
||||
if this.fsrEnd and this.fsrEnd > GetTime() then
|
||||
this.fsrGain = true
|
||||
end
|
||||
end
|
||||
|
||||
if this.mode == "MANA" and diff < 0 then
|
||||
this.target = 5
|
||||
elseif this.mode == "MANA" and diff > 0 then
|
||||
if UnitPower("player") >= UnitPowerMax("player") then
|
||||
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
|
||||
elseif this.mode == "ENERGY" and diff >= 0 then
|
||||
if not this.ignoreNextGain then
|
||||
this.target = getAdjustedTickTimer()
|
||||
end
|
||||
this.ignoreNextGain = false
|
||||
-- phase is kept while hidden; OnUpdate catches up by whole periods
|
||||
if this.mode == "MANA" and power >= UnitPowerMax("player") then
|
||||
this:Hide()
|
||||
end
|
||||
this.lastMana = this.currentMana
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -90,37 +199,53 @@ pfUI:RegisterModule("energytick", function()
|
||||
end
|
||||
this.tick = GetTime() + 0.020 -- ~50 FPS
|
||||
|
||||
if this.target then
|
||||
this.start, this.max = GetTime(), this.target
|
||||
this.target = nil
|
||||
this.spark:SetAlpha(1)
|
||||
this:Show()
|
||||
-- five-second rule drains to nothing
|
||||
local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0
|
||||
if this.mode == "MANA" and remaining > 0 then
|
||||
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE)
|
||||
this.fsrbar:Show()
|
||||
else
|
||||
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
|
||||
this.fsrbar:Hide()
|
||||
end
|
||||
|
||||
if not this.start then
|
||||
this.spark:SetAlpha(0)
|
||||
return
|
||||
end
|
||||
|
||||
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
|
||||
this.spark:SetAlpha(0)
|
||||
return
|
||||
end
|
||||
|
||||
this.current = GetTime() - this.start
|
||||
|
||||
-- roll over by whole periods, not from now: restarting bakes frame
|
||||
-- overshoot into the phase as drift the lock then has to chase
|
||||
if this.current > this.max then
|
||||
-- Don't restart tick timer if mana is full
|
||||
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
|
||||
this.start = nil
|
||||
this.spark:SetAlpha(0)
|
||||
return
|
||||
end
|
||||
this.start, this.max, this.current = GetTime(), getAdjustedTickTimer(), 0
|
||||
this.start = this.start + this.max * math.floor(this.current / this.max)
|
||||
this.max = getAdjustedTickTimer()
|
||||
this.current = GetTime() - this.start
|
||||
end
|
||||
|
||||
local pos = (C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width)
|
||||
* (this.current / this.max)
|
||||
-- dim while the rule is up and nothing has ticked inside it yet
|
||||
this.spark:SetAlpha((remaining > 0 and not this.fsrGain) and .4 or 1)
|
||||
|
||||
if not C.unitframes.player.pheight then
|
||||
return
|
||||
end
|
||||
|
||||
local pos = getBarWidth() * (this.current / this.max)
|
||||
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
|
||||
end)
|
||||
|
||||
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
|
||||
energytick.fsrbar:SetTexture(1, 1, 1, .15)
|
||||
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0)
|
||||
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0)
|
||||
energytick.fsrbar:Hide()
|
||||
|
||||
energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
|
||||
energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
|
||||
energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
|
||||
@@ -133,4 +258,4 @@ pfUI:RegisterModule("energytick", function()
|
||||
energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
|
||||
hookUpdateConfig(pfUI.uf.player)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
+20
-1
@@ -130,9 +130,28 @@ pfUI:RegisterModule("eqcompare", function ()
|
||||
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)
|
||||
return function(tooltip, arg1, arg2, arg3)
|
||||
ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
|
||||
local link = 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
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local pendingAction = nil -- "new" | "save" | "rename" — what the popups apply to
|
||||
local slotOverlays = {} -- [invSlotID] = ignored-overlay texture on the character slot
|
||||
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
|
||||
-- effective state is flipped from what's persisted. Committed on
|
||||
@@ -76,7 +77,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
else
|
||||
frame:SetPoint("TOPLEFT", CharacterFrame, "TOPRIGHT", 0, 0)
|
||||
end
|
||||
for _, b in ipairs(popoutButtons) do b:Show() end
|
||||
UpdatePopouts()
|
||||
end)
|
||||
CreateBackdrop(frame, nil, nil, .9)
|
||||
CreateBackdropShadow(frame)
|
||||
@@ -452,13 +453,13 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local flyout = CreateFrame("Frame", "pfEqMgrFlyout", UIParent)
|
||||
-- Everything tied to the EM sidecar closes with it: the popout arrows,
|
||||
-- the per-slot flyout, and the name/icon popup. (OnShow re-shows the
|
||||
-- popout arrows.)
|
||||
-- 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()
|
||||
for _, b in ipairs(popoutButtons) do b:Hide() end
|
||||
flyout:Hide()
|
||||
namePopup:Hide()
|
||||
UpdatePopouts()
|
||||
end)
|
||||
flyout:SetFrameStrata("DIALOG")
|
||||
flyout:Hide()
|
||||
@@ -750,6 +751,27 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end
|
||||
end
|
||||
|
||||
-- Popout arrows follow the EM sidecar by default. With the
|
||||
-- "Always Show Equipment Slot Flyouts" option they follow the paperdoll
|
||||
-- instead, so gear can be swapped without opening the equipment manager.
|
||||
local function PopoutsActive()
|
||||
if C.character.inventory.equipflyout == "1" then
|
||||
return PaperDollFrame:IsShown()
|
||||
end
|
||||
return frame:IsShown()
|
||||
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
|
||||
-- ============================================================
|
||||
|
||||
@@ -136,7 +136,7 @@ pfUI:RegisterModule("farmmode", function ()
|
||||
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:SetPoint("CENTER", 0, 0)
|
||||
pfUI.farmmap.button.txt:SetText("|cff33ffcc FARM MODE")
|
||||
pfUI.farmmap.button.txt:SetText("|cff33ffcc " .. T["FARM MODE"])
|
||||
end
|
||||
|
||||
CreateBackdrop(pfUI.farmmap)
|
||||
|
||||
+9
-14
@@ -127,7 +127,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
-- welcome dialog
|
||||
pfUI.firstrun:AddStep("init", function()
|
||||
local f = CreateFirstRunPage()
|
||||
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")))
|
||||
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"))
|
||||
return f
|
||||
end)
|
||||
|
||||
@@ -137,8 +137,7 @@ 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.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Modern:SetWidth(120)
|
||||
f.Modern:SetHeight(20)
|
||||
f.Modern:SetSize(120, 20)
|
||||
f.Modern:SetPoint("BOTTOM", -65, 100)
|
||||
f.Modern:SetTextColor(1,1,1)
|
||||
f.Modern:SetText("Modern")
|
||||
@@ -151,8 +150,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinButton(f.Modern)
|
||||
|
||||
f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Nostalgia:SetWidth(120)
|
||||
f.Nostalgia:SetHeight(20)
|
||||
f.Nostalgia:SetSize(120, 20)
|
||||
f.Nostalgia:SetPoint("BOTTOM", 65, 100)
|
||||
f.Nostalgia:SetTextColor(1,1,1)
|
||||
f.Nostalgia:SetText("Nostalgia")
|
||||
@@ -165,8 +163,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinButton(f.Nostalgia)
|
||||
|
||||
f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Legacy:SetWidth(120)
|
||||
f.Legacy:SetHeight(20)
|
||||
f.Legacy:SetSize(120, 20)
|
||||
f.Legacy:SetPoint("BOTTOM", 65, 75)
|
||||
f.Legacy:SetTextColor(1,1,1)
|
||||
f.Legacy:SetText("Legacy")
|
||||
@@ -179,8 +176,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinButton(f.Legacy)
|
||||
|
||||
f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
|
||||
f.Slim:SetWidth(120)
|
||||
f.Slim:SetHeight(20)
|
||||
f.Slim:SetSize(120, 20)
|
||||
f.Slim:SetPoint("BOTTOM", -65, 75)
|
||||
f.Slim:SetTextColor(1,1,1)
|
||||
f.Slim:SetText("Slim")
|
||||
@@ -197,8 +193,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
f.Slider.text:SetPoint("TOP", f.Slider, "BOTTOM", 0, 2)
|
||||
f.Slider.text:SetText(T["Scale"])
|
||||
|
||||
f.Slider:SetWidth(240)
|
||||
f.Slider:SetHeight(20)
|
||||
f.Slider:SetSize(240, 20)
|
||||
f.Slider:SetPoint("BOTTOM", 0, 50)
|
||||
f.Slider:SetOrientation('HORIZONTAL')
|
||||
f.Slider:SetMinMaxValues(0.5, 2.0)
|
||||
@@ -263,7 +258,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinCheckbox(f.checkbox, 18)
|
||||
|
||||
f.NextScript = function()
|
||||
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") end
|
||||
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") return end
|
||||
pfUI.chat.SetupRightChat(f.checkbox:GetChecked())
|
||||
end
|
||||
|
||||
@@ -283,7 +278,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinCheckbox(f.checkbox, 18)
|
||||
|
||||
f.NextScript = function()
|
||||
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") end
|
||||
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") return end
|
||||
if f.checkbox:GetChecked() then
|
||||
pfUI.chat.SetupPositions()
|
||||
end
|
||||
@@ -305,7 +300,7 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
SkinCheckbox(f.checkbox, 18)
|
||||
|
||||
f.NextScript = function()
|
||||
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") end
|
||||
if not pfUI.chat then message("Couldn't apply settings. Chat module is disabled.") return end
|
||||
if f.checkbox:GetChecked() then
|
||||
pfUI.chat.SetupChannels()
|
||||
end
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
-- 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)
|
||||
+22
-14
@@ -2240,7 +2240,6 @@ pfUI:RegisterModule("gui", function ()
|
||||
|
||||
-- build config entries
|
||||
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["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)
|
||||
@@ -2412,6 +2411,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
|
||||
CreateGUIEntry(T["Character"], T["Inventory"], function()
|
||||
CreateConfig(nil, T["Show Durability"], C.character.inventory, "durability", "checkbox")
|
||||
CreateConfig(nil, T["Always Show Equipment Slot Flyouts"], C.character.inventory, "equipflyout", "checkbox")
|
||||
end)
|
||||
|
||||
CreateGUIEntry(T["Character"], T["Reputation"], function()
|
||||
@@ -2556,9 +2556,6 @@ pfUI:RegisterModule("gui", function ()
|
||||
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["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["Highlight Equipped Items"], C.bars, "showequipped", "checkbox")
|
||||
CreateConfig(U["bars"], T["Equipped Item Color"], C.bars, "eqcolor", "color")
|
||||
@@ -2763,22 +2760,32 @@ pfUI:RegisterModule("gui", function ()
|
||||
end)
|
||||
|
||||
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 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["Cursor Tooltip Align"], C.tooltip, "cursoralign", "dropdown", pfUI.gui.dropdowns.tooltip_align)
|
||||
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["Show Aura Caster"], C.tooltip, "aurasource", "checkbox")
|
||||
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["Items"], nil, nil, "header")
|
||||
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(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(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)
|
||||
|
||||
CreateGUIEntry(T["Castbar"], T["General"], function()
|
||||
@@ -2857,6 +2864,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
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 Class Colors"], C.chat.text, "classcolor", "checkbox")
|
||||
CreateConfig(nil, T["Show Faction & Race Icons"] .. " " .. GetPlayerRaceIcons(), C.chat.text, "playericons", "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")
|
||||
@@ -2870,9 +2878,9 @@ pfUI:RegisterModule("gui", function ()
|
||||
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 Frame Shadow"], C.chat.global, "frameshadow", "checkbox")
|
||||
CreateConfig(nil, T["Enable Custom Colors"], C.chat.global, "custombg", "checkbox")
|
||||
CreateConfig(nil, T["Chat Background Color"], C.chat.global, "background", "color")
|
||||
CreateConfig(nil, T["Chat Border Color"], C.chat.global, "border", "color")
|
||||
CreateConfig(U["chat"], T["Enable Custom Colors"], C.chat.global, "custombg", "checkbox")
|
||||
CreateConfig(U["chat"], T["Chat Background Color"], C.chat.global, "background", "color")
|
||||
CreateConfig(U["chat"], 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["Incoming Whispers Color"], C.chat.global, "whisper", "color")
|
||||
CreateConfig(nil, T["Enable Sticky Chat"], C.chat.global, "sticky", "checkbox")
|
||||
@@ -2989,6 +2997,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
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["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["Dock"] .. ")", C.thirdparty.dpsmate, "dock", "checkbox")
|
||||
CreateConfig(nil, "SWStats (" .. T["Skin"] .. ")", C.thirdparty.swstats, "skin", "checkbox")
|
||||
@@ -3017,8 +3026,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateGUIEntry(T["Components"], T["Modules"], function()
|
||||
table.sort(pfUI.modules)
|
||||
for i,m in pairs(pfUI.modules) do
|
||||
-- skip gui and macrotweak when macro addons are loaded
|
||||
if m ~= "gui" and not (m == "macrotweak" and pfUI:MacroAddonsLoaded()) then
|
||||
if m ~= "gui" then
|
||||
-- create disabled entry if not existing and display
|
||||
pfUI:UpdateConfig("disabled", nil, m, "0")
|
||||
CreateConfig(nil, T["Disable Module"] .. " " .. m, C.disabled, m, "checkbox")
|
||||
|
||||
+34
-9
@@ -1,10 +1,8 @@
|
||||
pfUI:RegisterModule("hunterbar", function ()
|
||||
local class = UnitClassBase("player")
|
||||
if class ~= "HUNTER" or C.bars.hunterbar == "0" then return end
|
||||
if UnitClassBase("player") ~= "HUNTER" or C.bars.hunterbar == "0" then return end
|
||||
|
||||
-- Wing Clip (any rank) and Arcane Shot (any rank) spell IDs.
|
||||
-- IsSpellInRange(spellId) works with any spell ID via Nampower,
|
||||
-- no actionbar slot needed.
|
||||
-- C_Spell.IsSpellInRange works with any spell ID, no actionbar slot needed.
|
||||
local WINGCLIP_ID = 2974 -- melee range indicator (~5 yd)
|
||||
local ARCANESHOT_ID = 3044 -- ranged range indicator (~35 yd)
|
||||
|
||||
@@ -12,22 +10,37 @@ pfUI:RegisterModule("hunterbar", function ()
|
||||
-- Only swap BACK to melee bar when Wing Clip is actually in range.
|
||||
-- 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)
|
||||
|
||||
-- track which page we last forced so we don't spam ChangeActionBarPage()
|
||||
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()
|
||||
-- only act when there is a live, attackable target
|
||||
this.elapsed = this.elapsed + arg1
|
||||
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
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local wingclipInRange = IsSpellInRange(WINGCLIP_ID, "target")
|
||||
local arcaneshotInRange = IsSpellInRange(ARCANESHOT_ID, "target")
|
||||
-- C_Spell.IsSpellInRange returns true / false / nil (rangeless). The
|
||||
-- explicit == true / == false tests leave the bar unchanged on nil.
|
||||
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
|
||||
if wingclipInRange == 0 and arcaneshotInRange == 1 then
|
||||
if wingclipInRange == false and arcaneshotInRange == true then
|
||||
if this.lastPage ~= 2 then
|
||||
this.lastPage = 2
|
||||
_G.CURRENT_ACTIONBAR_PAGE = 2
|
||||
@@ -35,7 +48,7 @@ pfUI:RegisterModule("hunterbar", function ()
|
||||
end
|
||||
|
||||
-- swap to melee bar: in melee range AND arcane shot (8yd) out of range
|
||||
elseif wingclipInRange == 1 and arcaneshotInRange == 0 then
|
||||
elseif wingclipInRange == true and arcaneshotInRange == false then
|
||||
if this.lastPage ~= 1 then
|
||||
this.lastPage = 1
|
||||
_G.CURRENT_ACTIONBAR_PAGE = 1
|
||||
@@ -43,4 +56,16 @@ pfUI:RegisterModule("hunterbar", function ()
|
||||
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)
|
||||
@@ -75,14 +75,14 @@ pfUI:RegisterModule("innervatecall", function ()
|
||||
|
||||
SendChatMessage(">> Innervate casted on " .. targetName .. " <<", channel)
|
||||
|
||||
-- Schedule "ready" announcement when cooldown expires
|
||||
-- Use GetSpellIdCooldown for precise remaining time, fallback to 360s
|
||||
-- Schedule "ready" announcement when the cooldown expires. Read the
|
||||
-- precise remaining time from C_Spell.GetSpellCooldown (startTime and
|
||||
-- duration are seconds from the GetTime epoch); fall back to 360s.
|
||||
local cdRemaining = 360
|
||||
if GetSpellIdCooldown then
|
||||
local cd = GetSpellIdCooldown(INNERVATE_SPELLID)
|
||||
if cd and cd.cooldownRemainingMs and cd.cooldownRemainingMs > 0 then
|
||||
cdRemaining = cd.cooldownRemainingMs / 1000
|
||||
end
|
||||
local cd = C_Spell.GetSpellCooldown(INNERVATE_SPELLID)
|
||||
if cd and cd.duration and cd.duration > 0 then
|
||||
local remaining = cd.startTime + cd.duration - GetTime()
|
||||
if remaining > 0 then cdRemaining = remaining end
|
||||
end
|
||||
|
||||
C_Timer.After(cdRemaining, function()
|
||||
|
||||
@@ -26,10 +26,14 @@ pfUI:RegisterModule("itemcount", function ()
|
||||
if bank < 0 then bank = 0 end
|
||||
|
||||
frame:AddLine(" ")
|
||||
if bags > 0 then frame:AddDoubleLine("Bags:", bags, 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("Equipped:", equipped, 1, 1, 1, 1, 1, 1) end
|
||||
frame:AddDoubleLine("Total:", total, 1, 1, 1, 1, 1, 1)
|
||||
if bags > 0 then frame:AddDoubleLine(T["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 equipped > 0 then frame:AddDoubleLine(T["Equipped"] .. ":", equipped, 1, 1, 1, 1, 1, 1) end
|
||||
|
||||
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()
|
||||
end
|
||||
|
||||
|
||||
+6
-16
@@ -77,9 +77,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
if pfUI.loot.my_index or pfUI.loot.disenchanter_index or pfUI.loot.banker_index then
|
||||
info = wipe(info)
|
||||
info.text = T["Special Recipient"]
|
||||
info.textR = NORMAL_FONT_COLOR.r
|
||||
info.textG = NORMAL_FONT_COLOR.g
|
||||
info.textB = NORMAL_FONT_COLOR.b
|
||||
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB()
|
||||
info.textHeight = 12
|
||||
info.hasArrow = 1
|
||||
info.notCheckable = 1
|
||||
@@ -158,9 +156,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
if level == 1 then
|
||||
info = wipe(info)
|
||||
info.text = T["Random"]
|
||||
info.textR = NORMAL_FONT_COLOR.r
|
||||
info.textG = NORMAL_FONT_COLOR.g
|
||||
info.textB = NORMAL_FONT_COLOR.b
|
||||
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB()
|
||||
info.value = "PFRANDOM"
|
||||
info.textHeight = 12
|
||||
info.notCheckable = 1
|
||||
@@ -170,9 +166,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
|
||||
info = wipe(info)
|
||||
info.text = T["Request Rolls"]
|
||||
info.textR = NORMAL_FONT_COLOR.r
|
||||
info.textG = NORMAL_FONT_COLOR.g
|
||||
info.textB = NORMAL_FONT_COLOR.b
|
||||
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB()
|
||||
info.value = "PFROLLS"
|
||||
info.textHeight = 12
|
||||
info.hasArrow = 1
|
||||
@@ -183,9 +177,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
if UIDROPDOWNMENU_MENU_VALUE == "PFROLLS" then
|
||||
info = wipe(info)
|
||||
info.text = T["Clear Rolls"]
|
||||
info.textR = NORMAL_FONT_COLOR.r
|
||||
info.textG = NORMAL_FONT_COLOR.g
|
||||
info.textB = NORMAL_FONT_COLOR.b
|
||||
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB()
|
||||
info.value = "PFCLEARROLLS"
|
||||
info.notCheckable = 1
|
||||
info.func = pfUI.loot.ClearRolls
|
||||
@@ -193,9 +185,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
|
||||
info = wipe(info)
|
||||
info.text = T["Reroll Ties"]
|
||||
info.textR = NORMAL_FONT_COLOR.r
|
||||
info.textG = NORMAL_FONT_COLOR.g
|
||||
info.textB = NORMAL_FONT_COLOR.b
|
||||
info.textR, info.textG, info.textB = NORMAL_FONT_COLOR:GetRGB()
|
||||
info.value = "PFTIEROLL"
|
||||
info.notCheckable = 1
|
||||
info.arg1 = pfUI.loot.rollers_sorted
|
||||
@@ -521,7 +511,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
end
|
||||
|
||||
function pfUI.loot:CreateSlot(id)
|
||||
local frame = CreateFrame(LOOT_BUTTON_FRAME_TYPE, 'pfLootButton'..id, pfUI.loot)
|
||||
local frame = CreateFrame("LootButton", 'pfLootButton'..id, pfUI.loot)
|
||||
frame:RegisterForClicks('LeftButtonUp', 'RightButtonUp')
|
||||
frame:SetPoint("LEFT", border*2, 0)
|
||||
frame:SetPoint("RIGHT", -border*2, 0)
|
||||
|
||||
+8
-25
@@ -16,12 +16,6 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check"
|
||||
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.
|
||||
local function RenderItemVisual(f, item)
|
||||
local r, g, b = 1, 1, 1
|
||||
@@ -110,7 +104,6 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
-- Frame pools
|
||||
-- ==========================================================================
|
||||
local itemFrames = {}
|
||||
local usedPlayers, freePlayers = {}, {}
|
||||
|
||||
local FullUpdate -- forward declaration (toggle handlers call it)
|
||||
|
||||
@@ -210,20 +203,10 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
return f
|
||||
end
|
||||
|
||||
local function RecycleAllPlayers()
|
||||
for i = 1, table.getn(usedPlayers) do
|
||||
local pf = usedPlayers[i]
|
||||
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 playerPool = CreateObjectPool(CreatePlayerFrame, function(_, pf)
|
||||
pf:Hide()
|
||||
pf:ClearAllPoints()
|
||||
end)
|
||||
|
||||
local function SetToggleTexture(toggle, isExpanded)
|
||||
if isExpanded then
|
||||
@@ -277,7 +260,7 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
if wroll and wroll > 0 then f.winroll:SetText(wroll) else f.winroll:SetText("") end
|
||||
f.winroll:Show()
|
||||
f.winname:SetText(wname or UNKNOWN)
|
||||
f.winname:SetTextColor(ClassColor(wclass))
|
||||
f.winname:SetTextColor(PFUI_CLASS_COLORS[wclass]:GetRGB())
|
||||
f.winname:Show()
|
||||
else
|
||||
-- nobody won: everyone passed
|
||||
@@ -298,7 +281,7 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
|
||||
local function RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
|
||||
pf.name:SetText(name or UNKNOWN)
|
||||
pf.name:SetTextColor(ClassColor(class))
|
||||
pf.name:SetTextColor(PFUI_CLASS_COLORS[class]:GetRGB())
|
||||
|
||||
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
|
||||
@@ -315,7 +298,7 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
|
||||
function FullUpdate()
|
||||
if not pfUI.loothistory:IsShown() then return end
|
||||
RecycleAllPlayers()
|
||||
playerPool:ReleaseAll()
|
||||
|
||||
local num = C_LootHistory.GetNumItems()
|
||||
local y = -2
|
||||
@@ -333,7 +316,7 @@ pfUI:RegisterModule("loothistory", function ()
|
||||
for p = 1, f.numPlayers do
|
||||
local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p)
|
||||
if ShouldDisplayPlayer(f.isDone, roll, isMe) then
|
||||
local pf = GetPlayerFrame()
|
||||
local pf = playerPool:Acquire()
|
||||
RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
|
||||
pf:ClearAllPoints()
|
||||
pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y)
|
||||
|
||||
@@ -72,7 +72,7 @@ pfUI:RegisterModule("macroicons", function ()
|
||||
end
|
||||
|
||||
local function SaveMacroPopup()
|
||||
local text = strtrim(picker.editbox:GetText())
|
||||
local text = picker.editbox:GetText()
|
||||
if text == "" then return end
|
||||
|
||||
local icon = iconPicker.GetIcon()
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
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)
|
||||
+8
-2
@@ -77,7 +77,13 @@ pfUI:RegisterModule("map", function ()
|
||||
if point == "TOPLEFT" and relpoint == "TOPLEFT" then
|
||||
offx = offx*oldscale/scale
|
||||
offy = offy*oldscale/scale
|
||||
WorldMapFrame:SetPoint(point, rel, relpoint, offx, offy)
|
||||
-- Anchor to the parent (3-arg SetPoint) rather than re-using the frame
|
||||
-- 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
|
||||
|
||||
WorldMapFrame:SetScale(scale)
|
||||
@@ -175,7 +181,7 @@ pfUI:RegisterModule("map", function ()
|
||||
end
|
||||
|
||||
if mx and my and MouseIsOver(WorldMapButton) then
|
||||
WorldMapButton.coords.text:SetText(string.format('%.1f / %.1f', mx, my))
|
||||
WorldMapButton.coords.text:SetFormattedText('%.1f / %.1f', mx, my)
|
||||
else
|
||||
WorldMapButton.coords.text:SetText("")
|
||||
end
|
||||
|
||||
+46
-47
@@ -30,20 +30,14 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
pfUI.mapreveal:UpdateConfig()
|
||||
end)
|
||||
|
||||
local explores = {}
|
||||
local explorecaches = {}
|
||||
local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } }
|
||||
|
||||
-- Own texture pool - separate from Blizzard's WorldMapOverlay textures
|
||||
local pfOverlays = {}
|
||||
local pfOverlayMax = 0
|
||||
|
||||
local function pfGetOverlay(idx)
|
||||
if not pfOverlays[idx] then
|
||||
pfOverlays[idx] = WorldMapDetailFrame:CreateTexture("pfReveal"..idx, "BORDER")
|
||||
end
|
||||
return pfOverlays[idx]
|
||||
end
|
||||
local overlayPool = CreateTexturePool(WorldMapDetailFrame, "BORDER", nil, nil, function(_, tex)
|
||||
tex:Hide()
|
||||
tex:ClearAllPoints()
|
||||
end)
|
||||
|
||||
local exploreEnter = function()
|
||||
WorldMapTooltip:ClearLines()
|
||||
@@ -52,31 +46,55 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
WorldMapTooltip:AddLine(this.name, 1, 1, 1)
|
||||
WorldMapTooltip:Show()
|
||||
|
||||
if not explorecaches[this.name] then return end
|
||||
if not explorecaches[this.area] then return end
|
||||
if C.appearance.worldmap.mapreveal == "0" then return end
|
||||
for texture in pairs(explorecaches[this.name]) do
|
||||
for texture in pairs(explorecaches[this.area]) do
|
||||
texture:SetVertexColor(1,1,1,1)
|
||||
end
|
||||
end
|
||||
|
||||
local exploreLeave = function()
|
||||
WorldMapTooltip:Hide()
|
||||
if not explorecaches[this.name] then return end
|
||||
if not explorecaches[this.area] then return end
|
||||
if C.appearance.worldmap.mapreveal == "0" then return end
|
||||
local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color)
|
||||
for texture in pairs(explorecaches[this.name]) do
|
||||
for texture in pairs(explorecaches[this.area]) do
|
||||
texture:SetVertexColor(r,g,b,a)
|
||||
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()
|
||||
-- clear stale caches
|
||||
for k in pairs(explorecaches) do explorecaches[k] = nil end
|
||||
|
||||
-- hide all our textures from last frame
|
||||
for i = 1, pfOverlayMax do
|
||||
pfOverlays[i]:Hide()
|
||||
end
|
||||
overlayPool:ReleaseAll()
|
||||
|
||||
local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color)
|
||||
local mapFileName = GetMapInfo()
|
||||
@@ -94,14 +112,13 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
local zoneKnown = alreadyknown[mapFileName]
|
||||
|
||||
-- hide explore icons
|
||||
for _, frame in pairs(explores) do frame:Hide() end
|
||||
explorePool:ReleaseAll()
|
||||
|
||||
-- ClassicAPI: full overlay list for the viewed zone (explored + unexplored),
|
||||
-- read straight from WorldMapOverlay.dbc. Replaces the hand-measured pfMapOverlayData.
|
||||
local zoneData = C_Map.GetMapOverlays() or {}
|
||||
local textureCount = 0
|
||||
|
||||
for i, overlay in ipairs(zoneData) do
|
||||
for _, overlay in ipairs(zoneData) do
|
||||
local name = overlay.textureName -- bare, e.g. "DRYGULCHRAVINE"
|
||||
local textureName = overlay.texturePath -- full engine path (for SetTexture)
|
||||
local textureWidth = overlay.textureWidth
|
||||
@@ -109,29 +126,15 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
local offsetX = overlay.offsetX
|
||||
local offsetY = overlay.offsetY
|
||||
|
||||
-- explore magnifying glass icon
|
||||
explores[i] = explores[i] or CreateFrame("Frame", nil, WorldMapDetailFrame)
|
||||
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.
|
||||
-- explore magnifying glass icon. `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
|
||||
explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
|
||||
local explore = explorePool:Acquire()
|
||||
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()
|
||||
else
|
||||
explore:Hide()
|
||||
end
|
||||
|
||||
-- render overlay texture tiles on BORDER draw layer
|
||||
@@ -146,11 +149,9 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
-- exactly what shears quirky overlays (e.g. Icepoint's Kaneq'nuun).
|
||||
if C.appearance.worldmap.mapreveal == "1" then
|
||||
for _, tile in ipairs(overlay.tiles) do
|
||||
textureCount = textureCount + 1
|
||||
local tex = pfGetOverlay(textureCount)
|
||||
local tex = overlayPool:Acquire()
|
||||
|
||||
tex:SetWidth(tile.width)
|
||||
tex:SetHeight(tile.height)
|
||||
tex:SetSize(tile.width, tile.height)
|
||||
tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY)
|
||||
tex:ClearAllPoints()
|
||||
tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY)
|
||||
@@ -164,8 +165,6 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pfOverlayMax = math.max(pfOverlayMax, textureCount)
|
||||
end
|
||||
|
||||
-- hook WorldMapFrame_Update
|
||||
|
||||
+38
-18
@@ -1,12 +1,9 @@
|
||||
pfUI:RegisterModule("marktracking", function ()
|
||||
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 markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star
|
||||
local markerTokens = {}
|
||||
local markerTokens = {} -- [i] = "markN"
|
||||
local markerIndex = {} -- ["markN"] = i, for the event handler's arg1
|
||||
|
||||
local markerConfigKeys = {
|
||||
"raidmarkercolor_star",
|
||||
@@ -22,6 +19,7 @@ pfUI:RegisterModule("marktracking", function ()
|
||||
local markerColors = {}
|
||||
for i, markKey in ipairs(markerConfigKeys) do
|
||||
markerTokens[i] = "mark" .. i
|
||||
markerIndex[markerTokens[i]] = i
|
||||
local r, g, b, a = GetStringColor(C.unitframes[markKey])
|
||||
markerColors[i] = { tonumber(r), tonumber(g), tonumber(b), tonumber(a) }
|
||||
end
|
||||
@@ -73,8 +71,7 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
else
|
||||
pfUI.marktracking:SetPoint("TOP", UIParent, "CENTER", 0, 0)
|
||||
end
|
||||
pfUI.marktracking:SetWidth(TOTAL_ROW_WIDTH)
|
||||
pfUI.marktracking:SetHeight(8 * (ROW_HEIGHT + 1) + border * 2 - 1)
|
||||
pfUI.marktracking:SetSize(TOTAL_ROW_WIDTH, 8 * (ROW_HEIGHT + 1) + border * 2 - 1)
|
||||
pfUI.marktracking:Hide()
|
||||
|
||||
CreateBackdrop(pfUI.marktracking)
|
||||
@@ -284,27 +281,50 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
-- Event-driven scanner frame
|
||||
local scanner = CreateFrame("Frame")
|
||||
|
||||
-- Fallback poll: catches units that come into range AFTER a marker was set
|
||||
-- (no event fires for that case, so we need this safety net). UpdateDisplay
|
||||
-- is a full eight-row rebuild, so it exists only while grouped -- raid markers
|
||||
-- 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
|
||||
-- UNIT_HEALTH/UNIT_MAXHEALTH: ClassicAPI fires these per token; with the mark
|
||||
-- tokens observed they arrive as arg1 == "markN", so we refresh just that
|
||||
-- one row (UpdateRow) instead of rescanning all eight.
|
||||
-- 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("PLAYER_ENTERING_WORLD")
|
||||
scanner:RegisterEvent("UNIT_HEALTH")
|
||||
scanner:RegisterEvent("UNIT_MAXHEALTH")
|
||||
scanner:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
||||
scanner:RegisterEvent("RAID_ROSTER_UPDATE")
|
||||
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()
|
||||
if event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
|
||||
-- arg1 is the token; "markN" -> N, nil for any non-mark token.
|
||||
local i = arg1 and tonumber(string.match(arg1, "^mark(%d)"))
|
||||
-- arg1 is one of the eight tokens we registered for, so this is a lookup
|
||||
-- rather than a parse -- string.match would allocate a capture and
|
||||
-- tonumber would parse it, on every health tick of every marked unit.
|
||||
local i = arg1 and markerIndex[arg1]
|
||||
if i then UpdateRow(i) end
|
||||
return
|
||||
end
|
||||
if event ~= "RAID_TARGET_UPDATE" then UpdatePoll() end
|
||||
UpdateDisplay()
|
||||
end)
|
||||
|
||||
-- Fallback poll: catches units that come into range AFTER a marker was set
|
||||
-- (no event fires for that case, so we need this safety net)
|
||||
C_Timer.NewTicker(FALLBACK_INTERVAL, UpdateDisplay)
|
||||
end)
|
||||
+2
-2
@@ -138,7 +138,7 @@ pfUI:RegisterModule("minimap", function ()
|
||||
local coord = pfUI.minimapCoordinates
|
||||
coord.posX, coord.posY = GetPlayerMapPosition("player")
|
||||
if coord.posX ~= 0 and coord.posY ~= 0 then
|
||||
coord.text:SetText(string.format("%.1f, %.1f", round(coord.posX * 100, 1), round(coord.posY * 100, 1)))
|
||||
coord.text:SetFormattedText("%.1f, %.1f", round(coord.posX * 100, 1), round(coord.posY * 100, 1))
|
||||
else
|
||||
coord.text:SetText("|cffffaaaaN/A")
|
||||
end
|
||||
@@ -225,7 +225,7 @@ pfUI:RegisterModule("minimap", function ()
|
||||
pfUI.minimap.pvpicon = CreateFrame("Frame", nil, pfUI.minimap)
|
||||
pfUI.minimap.pvpicon:Hide()
|
||||
pfUI.minimap.pvpicon:RegisterEvent("UPDATE_FACTION")
|
||||
pfUI.minimap.pvpicon:RegisterEvent("UNIT_FACTION")
|
||||
pfUI.minimap.pvpicon:RegisterUnitEvent("UNIT_FACTION", "player")
|
||||
pfUI.minimap.pvpicon:SetFrameStrata("HIGH")
|
||||
pfUI.minimap.pvpicon:SetSize(16, 16)
|
||||
pfUI.minimap.pvpicon:SetAlpha(.5)
|
||||
|
||||
+267
-158
@@ -5,10 +5,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
-- Local function references for performance
|
||||
local GetTime = GetTime
|
||||
local UnitName = UnitName
|
||||
local UnitClass = UnitClass
|
||||
local UnitLevel = UnitLevel
|
||||
local UnitIsPlayer = UnitIsPlayer
|
||||
local UnitIsDead = UnitIsDead
|
||||
local UnitAffectingCombat = UnitAffectingCombat
|
||||
local UnitIsUnit = UnitIsUnit
|
||||
local UnitCanAssist = UnitCanAssist
|
||||
@@ -116,6 +113,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
-- Reusable per-plate debuff display buffer (avoid GC churn from per-call table creation)
|
||||
local debuffDisplayBuf = {} -- [i] = { effect, texture, stacks, dtype, duration, timeleft }
|
||||
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 debuffSeen = {} -- reusable table for debuff tracking (avoid GC churn)
|
||||
|
||||
@@ -161,6 +159,24 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
cfg.debuffanim = tonumber(C.nameplates.debuffanim) or 0
|
||||
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
|
||||
offtanks = {}
|
||||
for k, v in pairs({strsplit("#", C.nameplates.combatofftanks)}) do
|
||||
@@ -283,15 +299,40 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
end
|
||||
end
|
||||
|
||||
local function TotemPlate(name)
|
||||
if C.nameplates.totemicons == "1" then
|
||||
for totem, icon in pairs(L["totems"]) do
|
||||
if string.find(name, totem) then return icon end
|
||||
end
|
||||
-- Numeric CreatureType.dbc id for the plate's unit (8 = Critter, 11 = Totem).
|
||||
-- It's fixed per unit, so cache it and reset on plate reuse. nil while the GUID
|
||||
-- can't resolve yet (incl. the no-SuperWoW case, where cachedGuid is empty).
|
||||
-- Locale-proof and covers custom units for free -- no name tables needed.
|
||||
local function CreatureType(plate)
|
||||
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
|
||||
return plate.creatureType
|
||||
end
|
||||
|
||||
local function HidePlate(unittype, name, fullhp, target)
|
||||
-- Totem icon: UnitCreatedBySpell returns the totem-drop spell, whose icon IS
|
||||
-- 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
|
||||
if C.nameplates.fullhealth == "1" and not fullhp then return nil end
|
||||
if C.nameplates.target == "1" and target then return nil end
|
||||
@@ -307,14 +348,10 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
return true
|
||||
elseif C.nameplates.friendlyplayer == "1" and unittype == "FRIENDLY_PLAYER" then
|
||||
return true
|
||||
elseif C.nameplates.critters == "1" and unittype == "NEUTRAL_NPC" then
|
||||
for i, critter in pairs(L["critters"]) do
|
||||
if string.lower(name) == string.lower(critter) then return true end
|
||||
end
|
||||
elseif C.nameplates.totems == "1" then
|
||||
for totem in pairs(L["totems"]) do
|
||||
if string.find(name, totem) then return true end
|
||||
end
|
||||
elseif C.nameplates.critters == "1" and CreatureType(plate) == 8 then
|
||||
return true
|
||||
elseif C.nameplates.totems == "1" and CreatureType(plate) == 11 then
|
||||
return true
|
||||
end
|
||||
|
||||
-- nothing to hide
|
||||
@@ -411,7 +448,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
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 = CreateFrame("Model", plate.platename.."Debuff"..index.."Cooldown", plate.debuffs[index], "CooldownFrameTemplate")
|
||||
plate.debuffs[index].cd:SetAllPoints(plate.debuffs[index])
|
||||
plate.debuffs[index].cd:SetFrameLevel(6)
|
||||
end
|
||||
@@ -468,7 +505,7 @@ local nameplates = CreateFrame("Frame", "pfNameplates", UIParent)
|
||||
nameplates:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
nameplates:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
nameplates:RegisterEvent("PLAYER_LOGOUT")
|
||||
nameplates:RegisterEvent("UNIT_COMBO_POINTS")
|
||||
nameplates:RegisterUnitEvent("UNIT_COMBO_POINTS", "player")
|
||||
nameplates:RegisterEvent("PLAYER_COMBO_POINTS")
|
||||
nameplates:RegisterEvent("ZONE_CHANGED_NEW_AREA")
|
||||
nameplates:RegisterEvent("RAID_ROSTER_UPDATE")
|
||||
@@ -476,13 +513,10 @@ nameplates:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
||||
nameplates:RegisterEvent("NAME_PLATE_CREATED")
|
||||
nameplates:RegisterEvent("NAME_PLATE_UNIT_ADDED")
|
||||
nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
|
||||
nameplates:RegisterEvent("UNIT_AURA")
|
||||
nameplates:RegisterEvent("UNIT_FLAGS")
|
||||
nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_START")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_STOP")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
|
||||
-- UNIT_AURA / UNIT_FLAGS / UNIT_SPELLCAST_* are registered per plate, on the
|
||||
-- plate's own frame, against its own token -- see OnCreate and
|
||||
-- NAME_PLATE_UNIT_ADDED.
|
||||
nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
|
||||
nameplates:SetScript("OnEvent", function()
|
||||
@@ -494,6 +528,15 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
if nameplates.mouselook then
|
||||
nameplates.mouselook:SetScript("OnUpdate", nil)
|
||||
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
|
||||
|
||||
elseif event == "PLAYER_GUILD_UPDATE" and arg1 == 'player' then
|
||||
@@ -569,6 +612,19 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
local guid = UnitGUID(arg1)
|
||||
plate.nameplate.cachedGuid = guid
|
||||
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
|
||||
plateByGuid[guid] = plate.nameplate
|
||||
-- Seed: the unit may already be mid-cast (its UNIT_SPELLCAST_START
|
||||
@@ -596,14 +652,9 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
if plate and plate.nameplate and plate.nameplate.cachedGuid == guid then
|
||||
plate.nameplate.cachedGuid = nil
|
||||
plate.nameplate.unit = nil
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
-- Drop the subscriptions with the token: this slot is now free and the
|
||||
-- next plate to take it would otherwise feed this frame its events.
|
||||
plate.nameplate:UnregisterAllEvents()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -618,48 +669,6 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
if pn then pn.eventcache = true 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
|
||||
frameState.targetGuid = UnitGUID('target')
|
||||
-- Flag the target's plate for update
|
||||
@@ -741,11 +750,47 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
nameplate.cache = {}
|
||||
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
|
||||
nameplate.original.healthbar, nameplate.original.castbar = parent:GetChildren()
|
||||
DisableObject(nameplate.original.healthbar)
|
||||
DisableObject(nameplate.original.castbar)
|
||||
|
||||
local NAMEPLATE_OBJECTORDER = { "border", "glow", "name", "level", "levelicon", "raidicon" }
|
||||
|
||||
for i, object in pairs({parent:GetRegions()}) do
|
||||
if NAMEPLATE_OBJECTORDER[i] and NAMEPLATE_OBJECTORDER[i] == "raidicon" then
|
||||
nameplate[NAMEPLATE_OBJECTORDER[i]] = object
|
||||
@@ -1004,13 +1049,29 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
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
|
||||
|
||||
-- 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
|
||||
-- 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.
|
||||
if plate.cache.player == nil and unitstr then
|
||||
plate.cache.player = UnitIsPlayer(unitstr) and true or false
|
||||
end
|
||||
local class, ulevel, elite, player, guild = GetUnitInfo(name, true, plate.cache.player)
|
||||
if plate.cache.minion == nil and unitstr then
|
||||
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
|
||||
|
||||
-- Use database level ONLY if current level is ?? (fixes ?? after reload, but doesn't override visible levels)
|
||||
@@ -1030,18 +1091,11 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
if player and unittype == "ENEMY_NPC" then unittype = "ENEMY_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
|
||||
if not class then plate.wait_for_scan = true end
|
||||
if not class and not plate.cache.minion then plate.wait_for_scan = true end
|
||||
|
||||
-- skip data updates on invisible frames
|
||||
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
|
||||
plate:Show()
|
||||
|
||||
@@ -1072,11 +1126,11 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
end
|
||||
|
||||
-- hide frames according to the configuration
|
||||
local TotemIcon = TotemPlate(name)
|
||||
local TotemIcon = TotemPlate(plate)
|
||||
|
||||
if TotemIcon then
|
||||
-- create totem icon
|
||||
plate.totem.icon:SetTexture("Interface\\Icons\\" .. TotemIcon)
|
||||
-- icon resolved from the totem-drop spell (already a full path)
|
||||
plate.totem.icon:SetTexture(TotemIcon)
|
||||
|
||||
plate.glow:Hide()
|
||||
plate.level:Hide()
|
||||
@@ -1084,7 +1138,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
plate.health:Hide()
|
||||
plate.guild:Hide()
|
||||
plate.totem:Show()
|
||||
elseif HidePlate(unittype, name, (hpmax-hp == hpmin), target) then
|
||||
elseif HidePlate(unittype, (hpmax-hp == hpmin), target, plate) then
|
||||
plate.level:SetPoint("RIGHT", plate.name, "LEFT", -3, 0)
|
||||
plate.name:SetParent(plate)
|
||||
plate.guild:SetPoint("BOTTOM", plate.name, "BOTTOM", -2, -(font_size + 2))
|
||||
@@ -1116,15 +1170,24 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
if plate.cache.level ~= level or plate.cache.elite ~= elite then
|
||||
plate.cache.level = level
|
||||
plate.cache.elite = elite
|
||||
plate.level:SetText(string.format("%s%s", level, (elitestrings[elite] or "")))
|
||||
plate.level:SetFormattedText("%s%s", level, (elitestrings[elite] or ""))
|
||||
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
|
||||
local color = GetDifficultyColor(level)
|
||||
plate.level:SetTextColor(color.r + 0.3, color.g + 0.3, color.b + 0.3, 1)
|
||||
plate.level:SetTextColor(color.r, color.g, color.b, 1)
|
||||
plate.cache.levelcolor = nil
|
||||
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
|
||||
plate.guild:SetText(guild)
|
||||
if guild == myGuild then
|
||||
@@ -1168,21 +1231,21 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
local hasdata = ( rhp and rhpmax ) or estimated or hpmax > 100 or (round(hpmax/100*hp) ~= hp)
|
||||
|
||||
if setting == "curperc" and hasdata and rhp then
|
||||
plate.health.text:SetText(string.format("%s | %s%%", Abbreviate(rhp), ceil(hp/hpmax*100)))
|
||||
plate.health.text:SetFormattedText("%s | %s%%", Abbreviate(rhp), ceil(hp/hpmax*100))
|
||||
elseif setting == "cur" and hasdata and rhp then
|
||||
plate.health.text:SetText(string.format("%s", Abbreviate(rhp)))
|
||||
plate.health.text:SetFormattedText("%s", Abbreviate(rhp))
|
||||
elseif setting == "curmax" and hasdata and rhp then
|
||||
plate.health.text:SetText(string.format("%s - %s", Abbreviate(rhp), Abbreviate(rhpmax)))
|
||||
plate.health.text:SetFormattedText("%s - %s", Abbreviate(rhp), Abbreviate(rhpmax))
|
||||
elseif setting == "curmaxs" and hasdata and rhp then
|
||||
plate.health.text:SetText(string.format("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax)))
|
||||
plate.health.text:SetFormattedText("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax))
|
||||
elseif setting == "curmaxperc" and hasdata and rhp then
|
||||
plate.health.text:SetText(string.format("%s - %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100)))
|
||||
plate.health.text:SetFormattedText("%s - %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100))
|
||||
elseif setting == "curmaxpercs" and hasdata and rhp then
|
||||
plate.health.text:SetText(string.format("%s / %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100)))
|
||||
plate.health.text:SetFormattedText("%s / %s | %s%%", Abbreviate(rhp), Abbreviate(rhpmax), ceil(hp/hpmax*100))
|
||||
elseif setting == "deficit" and rhp then
|
||||
plate.health.text:SetText(string.format("-%s" .. (hasdata and "" or "%%"), Abbreviate(rhpmax - rhp)))
|
||||
plate.health.text:SetFormattedText("-%s" .. (hasdata and "" or "%%"), Abbreviate(rhpmax - rhp))
|
||||
else -- "percent" as fallback
|
||||
plate.health.text:SetText(string.format("%s%%", ceil(hp/hpmax*100)))
|
||||
plate.health.text:SetFormattedText("%s%%", ceil(hp/hpmax*100))
|
||||
end
|
||||
else
|
||||
plate.health.text:SetText()
|
||||
@@ -1191,12 +1254,11 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
|
||||
local r, g, b, a = unpack(unitcolors[unittype])
|
||||
|
||||
if class then
|
||||
if unittype == "ENEMY_PLAYER" and C.nameplates["enemyclassc"] == "1" then
|
||||
r, g, b, a = PFUI_CLASS_COLORS[class]:GetRGBA()
|
||||
elseif unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassc"] == "1" then
|
||||
r, g, b, a = PFUI_CLASS_COLORS[class]:GetRGBA()
|
||||
end
|
||||
if class and (
|
||||
(unittype == "ENEMY_PLAYER" and C.nameplates["enemyclassc"] == "1") or
|
||||
(unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassc"] == "1")
|
||||
) then
|
||||
r, g, b, a = PFUI_CLASS_COLORS[class]:GetRGBA()
|
||||
end
|
||||
|
||||
if unitstr and UnitIsTapped(unitstr) and not UnitIsTappedByPlayer(unitstr) then
|
||||
@@ -1217,15 +1279,43 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
plate.cache.r, plate.cache.g, plate.cache.b = r, g, b
|
||||
end
|
||||
|
||||
if r + g + b ~= plate.cache.namecolor and unittype == "FRIENDLY_PLAYER" and C.nameplates["friendclassnamec"] == "1" and class and PFUI_CLASS_COLORS[class] then
|
||||
plate.name:SetTextColor(r, g, b, a)
|
||||
plate.cache.namecolor = r + g + b
|
||||
-- Friendly player names take this colour when friendclassnamec is on.
|
||||
-- Ownership is recorded so the OnUpdate sync below stands down rather than
|
||||
-- racing us: both writers used to share cache.namecolor despite storing
|
||||
-- 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
|
||||
|
||||
-- update combopoints
|
||||
for i=1, 5 do plate.combopoints[i]:Hide() end
|
||||
if target and C.nameplates.cpdisplay == "1" then
|
||||
for i=1, GetComboPoints("target") do plate.combopoints[i]:Show() end
|
||||
local cp = GetComboPoints("target")
|
||||
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
|
||||
|
||||
-- update debuffs
|
||||
@@ -1242,19 +1332,22 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
for i = 1, 16 do debuffDisplayBuf[i].effect = nil end
|
||||
if unitstr then
|
||||
local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL"
|
||||
local auras = C_UnitAuras.GetUnitAuras(unitstr, filter)
|
||||
local now = GetTime()
|
||||
for _, aura in ipairs(auras) do
|
||||
if debuffCount >= 16 then break end
|
||||
-- one GetAuraSlots enumeration, then positional by-slot reads straight
|
||||
-- into the reusable buffer: the per-plate scan allocates nothing (no
|
||||
-- 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
|
||||
local timeleft = (aura.expirationTime and aura.expirationTime > 0) and (aura.expirationTime - now) or nil
|
||||
local b = debuffDisplayBuf[debuffCount]
|
||||
b.effect = aura.name
|
||||
b.texture = aura.icon
|
||||
b.stacks = aura.applications
|
||||
b.dtype = aura.dispelName
|
||||
b.duration = aura.duration
|
||||
b.timeleft = timeleft
|
||||
b.effect = aname
|
||||
b.texture = icon
|
||||
b.stacks = count
|
||||
b.dtype = dispelType
|
||||
b.duration = duration
|
||||
b.timeleft = (expirationTime and expirationTime > 0) and (expirationTime - now) or nil
|
||||
end
|
||||
end
|
||||
for i = 1, 16 do
|
||||
@@ -1334,6 +1427,16 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
|
||||
-- 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
|
||||
-- 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)
|
||||
@@ -1360,25 +1463,24 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
end
|
||||
end
|
||||
|
||||
-- Resolved in CacheConfig, so these are table reads rather than a walk
|
||||
-- through the saved variables and preset tables.
|
||||
local throttle
|
||||
if target then
|
||||
throttle = pfUI.throttle:Get("nameplates_target")
|
||||
throttle = cfg.throttle_target
|
||||
elseif visiblePlateCount > 20 then
|
||||
throttle = pfUI.throttle:Get("nameplates_mass")
|
||||
throttle = cfg.throttle_mass
|
||||
else
|
||||
throttle = pfUI.throttle:Get("nameplates")
|
||||
throttle = cfg.throttle_normal
|
||||
end
|
||||
|
||||
-- Non-target plates with active castbar use the castbar throttle
|
||||
if isCastingNonTarget then
|
||||
local cbThrottle = pfUI.throttle:Get("nameplates_castbar")
|
||||
if cbThrottle < throttle then throttle = cbThrottle end
|
||||
if isCastingNonTarget and cfg.throttle_castbar < throttle then
|
||||
throttle = cfg.throttle_castbar
|
||||
end
|
||||
|
||||
-- Check for pending event updates (these bypass throttle for immediate response)
|
||||
local hasEventUpdate = nameplate.eventcache or nameplate.auraUpdate or nameplate.castUpdate or nameplate.targetUpdate or nameplate.comboUpdate
|
||||
|
||||
-- Event updates bypass throttle
|
||||
-- The category-specific gate. hasEventUpdate was read above, before the
|
||||
-- classification, and still bypasses the throttle.
|
||||
if not hasEventUpdate and (nameplate.lasttick or 0) + throttle > now then return end
|
||||
nameplate.lasttick = now
|
||||
|
||||
@@ -1477,38 +1579,46 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
-- 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
|
||||
-- lands.
|
||||
if nameplate.wait_for_scan and GetUnitInfo(name, true, nameplate.cache.player) then
|
||||
if nameplate.wait_for_scan and not nameplate.cache.minion and GetUnitInfo(name, true, nameplate.cache.player) then
|
||||
nameplate.wait_for_scan = nil
|
||||
update = true
|
||||
end
|
||||
|
||||
-- trigger update when name color changed (includes combat state check)
|
||||
local r, g, b = original.name:GetTextColor()
|
||||
local inCombatWithPlayer = cfg.namefightcolor and UnitAffectingCombat(nameplate.unit) and UnitAffectingCombat("player")
|
||||
|
||||
if r + g + b ~= nameplate.cache.namecolor or (cfg.namefightcolor and nameplate.cache.inCombat ~= inCombatWithPlayer) then
|
||||
nameplate.cache.namecolor = r + g + b
|
||||
nameplate.cache.inCombat = inCombatWithPlayer
|
||||
-- trigger update when name color changed (includes combat state check).
|
||||
-- Skipped once OnDataChanged owns the name colour (class-coloured friendly
|
||||
-- players), so the two writers cannot overwrite each other.
|
||||
if not nameplate.cache.ownname then
|
||||
local r, g, b = original.name:GetTextColor()
|
||||
local inCombatWithPlayer = cfg.namefightcolor and UnitAffectingCombat(nameplate.unit) and UnitAffectingCombat("player")
|
||||
|
||||
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)
|
||||
if r + g + b ~= nameplate.cache.namecolor or (cfg.namefightcolor and nameplate.cache.inCombat ~= inCombatWithPlayer) then
|
||||
nameplate.cache.namecolor = r + g + b
|
||||
nameplate.cache.inCombat = inCombatWithPlayer
|
||||
|
||||
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
|
||||
nameplate.name:SetTextColor(r,g,b,1)
|
||||
nameplate.name:SetTextColor(1,1,1,1)
|
||||
end
|
||||
else
|
||||
nameplate.name:SetTextColor(1,1,1,1)
|
||||
update = true
|
||||
end
|
||||
update = true
|
||||
end
|
||||
|
||||
-- trigger update when level color changed
|
||||
local r, g, b = original.level:GetTextColor()
|
||||
r, g, b = r + .3, g + .3, b + .3
|
||||
if r + g + b ~= nameplate.cache.levelcolor then
|
||||
nameplate.cache.levelcolor = r + g + b
|
||||
nameplate.level:SetTextColor(r,g,b,1)
|
||||
update = true
|
||||
-- trigger update when level color changed. Skipped while the level came
|
||||
-- from the database (?? plates), where OnDataChanged owns the colour --
|
||||
-- otherwise both writers race and cache.levelcolor goes stale, which
|
||||
-- leaves a recycled plate wearing the previous unit's colour.
|
||||
if not nameplate.cache.levelfromdb then
|
||||
local r, g, b = original.level:GetTextColor()
|
||||
if r + g + b ~= nameplate.cache.levelcolor then
|
||||
nameplate.cache.levelcolor = r + g + b
|
||||
nameplate.level:SetTextColor(r,g,b,1)
|
||||
update = true
|
||||
end
|
||||
end
|
||||
|
||||
-- use timer based updates
|
||||
@@ -1572,10 +1682,9 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
-- 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)
|
||||
if cfg.showcastbar and not cfg.targetcastbar and not isTargetPlate then
|
||||
local cbThrottle = pfUI.throttle:Get("nameplates_castbar")
|
||||
if visiblePlateCount > 20 then
|
||||
local massThrottle = pfUI.throttle:Get("nameplates_mass")
|
||||
if massThrottle > cbThrottle then cbThrottle = massThrottle end
|
||||
local cbThrottle = cfg.throttle_castbar
|
||||
if visiblePlateCount > 20 and cfg.throttle_mass > cbThrottle then
|
||||
cbThrottle = cfg.throttle_mass
|
||||
end
|
||||
if (nameplate.castbar_tick or 0) + cbThrottle <= now then
|
||||
nameplate.castbar_tick = now
|
||||
@@ -1618,7 +1727,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
rounded = floor(remaining * 100)
|
||||
if castbar.lastTextTick ~= rounded then
|
||||
castbar.lastTextTick = rounded
|
||||
castbar.text:SetText(string.format("%.2f", remaining))
|
||||
castbar.text:SetFormattedText("%.2f", remaining)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+25
-23
@@ -296,18 +296,18 @@ pfUI:RegisterModule("panel", function()
|
||||
local playerzone = GetRealZoneText()
|
||||
|
||||
for friendIndex=1, all do
|
||||
local friend_name, friend_level, friend_class, friend_area, friend_connected = GetFriendInfo(friendIndex)
|
||||
if friend_connected and friend_class and friend_level then
|
||||
local info = C_FriendList.GetFriendInfoByIndex(friendIndex)
|
||||
if info and info.connected and info.classFilename and info.level then
|
||||
if not init then
|
||||
GameTooltip_SetDefaultAnchor(GameTooltip, this)
|
||||
GameTooltip:ClearLines()
|
||||
GameTooltip:AddLine("|cff555555" .. T["Friends Online"])
|
||||
init = true
|
||||
end
|
||||
local ccolor = PFUI_CLASS_COLORS[L["class"][friend_class]] or { 1, 1, 1 }
|
||||
local lcolor = GetDifficultyColor(tonumber(friend_level)) or { 1, 1, 1 }
|
||||
local zcolor = friend_area == playerzone and "|cff33ffcc" or "|cffcccccc"
|
||||
GameTooltip:AddDoubleLine(rgbhex(ccolor) .. friend_name .. rgbhex(lcolor) .. " [" .. friend_level .. "]", zcolor .. friend_area)
|
||||
local ccolor = PFUI_CLASS_COLORS[info.classFilename]
|
||||
local lcolor = GetDifficultyColor(tonumber(info.level)) or { 1, 1, 1 }
|
||||
local zcolor = info.area == playerzone and "|cff33ffcc" or "|cffcccccc"
|
||||
GameTooltip:AddDoubleLine(ccolor:WrapTextInColorCode(info.name) .. rgbhex(lcolor) .. " [" .. info.level .. "]", zcolor .. info.area)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -315,15 +315,7 @@ pfUI:RegisterModule("panel", function()
|
||||
end
|
||||
widget.Click = function() ToggleFriendsFrame(1) end
|
||||
widget:SetScript("OnEvent", function()
|
||||
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
|
||||
|
||||
local online = C_FriendList.GetNumOnlineFriends()
|
||||
pfUI.panel:OutputPanel("friends", FRIENDS .. ": " .. online, widget.Tooltip, widget.Click)
|
||||
end)
|
||||
end
|
||||
@@ -426,8 +418,13 @@ pfUI:RegisterModule("panel", function()
|
||||
local repPercent = floor(cur / max * 100)
|
||||
if repPercent < 100 then
|
||||
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] = {
|
||||
GetInventoryItemLink("player", id),
|
||||
link,
|
||||
string.format("%s%s%%|r", hex, repPercent)
|
||||
}
|
||||
end
|
||||
@@ -436,8 +433,7 @@ pfUI:RegisterModule("panel", function()
|
||||
if totalRep > 0 then
|
||||
GameTooltip:ClearLines()
|
||||
GameTooltip_SetDefaultAnchor(GameTooltip, this)
|
||||
GameTooltip:SetText("|cff555555"..(string.gsub(REPAIR_COST,":","")).."|r")
|
||||
SetTooltipMoney(GameTooltip, totalRep)
|
||||
GameTooltip:AddLine(REPAIR_COST.." " .. CreateGoldString(totalRep), 0.3333, 0.3333, 0.3333)
|
||||
for _,line in ipairs(itemLines) do
|
||||
GameTooltip:AddDoubleLine(line[1],line[2])
|
||||
end
|
||||
@@ -461,7 +457,7 @@ pfUI:RegisterModule("panel", function()
|
||||
|
||||
do -- Zone
|
||||
local widget = CreateFrame("Frame", "pfPanelWidgetZone", UIParent)
|
||||
for _,event in pairs(EVENTS_MINIMAP_ZONE_UPDATE) do
|
||||
for _,event in pairs({"PLAYER_ENTERING_WORLD", "MINIMAP_ZONE_CHANGED"}) do
|
||||
widget:RegisterEvent(event)
|
||||
end
|
||||
widget.Tooltip = function()
|
||||
@@ -490,7 +486,7 @@ pfUI:RegisterModule("panel", function()
|
||||
do -- Ammo
|
||||
local widget = CreateFrame("Frame", "pfPanelWidgetAmmo", UIParent)
|
||||
widget:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
widget:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||||
widget:RegisterUnitEvent("UNIT_INVENTORY_CHANGED", "player")
|
||||
widget:RegisterEvent("BAG_UPDATE_DELAYED")
|
||||
widget.Tooltip = function()
|
||||
if GetInventoryItemQuality("player", 0) then
|
||||
@@ -781,9 +777,15 @@ pfUI:RegisterModule("panel", function()
|
||||
pfUI.panel.microbutton:SetSize(145, 23)
|
||||
pfUI.panel.microbutton:SetFrameStrata("MEDIUM")
|
||||
|
||||
for i=1,table.getn(MICRO_BUTTONS) do
|
||||
local anchor = _G[MICRO_BUTTONS[i-1]] or pfUI.panel.microbutton
|
||||
local button = _G[MICRO_BUTTONS[i]]
|
||||
local microButtons = {
|
||||
'CharacterMicroButton', 'SpellbookMicroButton', 'TalentMicroButton',
|
||||
'QuestLogMicroButton', 'SocialsMicroButton', 'WorldMapMicroButton',
|
||||
'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:SetParent(pfUI.panel.microbutton)
|
||||
if i == 1 then
|
||||
|
||||
+27
-3
@@ -4,7 +4,9 @@ pfUI:RegisterModule("raid", function ()
|
||||
|
||||
-- tell RaidFrame.lua pfUI replaces party frames
|
||||
HookAddonOrVariable("Blizzard_RaidUI", function()
|
||||
GROUP_REPLACE_PARTY = "1"
|
||||
-- must reach _G: RaidFrame.lua reads this global, and pfUI.env has
|
||||
-- __index but no __newindex, so a bare assignment stays in the sandbox
|
||||
_G.GROUP_REPLACE_PARTY = "1"
|
||||
end)
|
||||
|
||||
pfUI.uf.raid = CreateFrame("Frame", "pfRaidUpdater", UIParent)
|
||||
@@ -123,6 +125,7 @@ pfUI:RegisterModule("raid", function ()
|
||||
local grid = self.petgrid
|
||||
|
||||
local function place(pet, cell, id)
|
||||
pet.label = "raidpet"
|
||||
pet.id = id
|
||||
local r, g = SlotToCoord(cell, grid.fill, grid.x, grid.y)
|
||||
pet:ClearAllPoints()
|
||||
@@ -138,6 +141,22 @@ pfUI:RegisterModule("raid", function ()
|
||||
return
|
||||
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
|
||||
-- Pack the pets that exist into the leading cells, no gaps.
|
||||
local k = 0
|
||||
@@ -211,8 +230,13 @@ pfUI:RegisterModule("raid", function ()
|
||||
this.tick = GetTime() + 1.0
|
||||
this.pendingUpdate = nil
|
||||
|
||||
-- don't proceed without raid
|
||||
if not IsInRaid() then return end
|
||||
-- Without a raid there is nothing to sort, but a party shown as a raid
|
||||
-- grid still needs its pet frames placed and mapped to the party pets.
|
||||
if not IsInRaid() then
|
||||
this:LayoutPets()
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- clear all existing frames
|
||||
for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end
|
||||
|
||||
+7
-2
@@ -7,8 +7,12 @@ 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_PASSED = string.gsub(LOOT_ROLL_PASSED, "%%s|Hitem:%%d:%%d:%%d:%%d|h%[%%s%]|h%%s", "%%s")
|
||||
|
||||
-- try to detect the everyone string
|
||||
local _, _, everyone, _ = strfind(LOOT_ROLL_ALL_PASSED, LOOT_ROLL_PASSED)
|
||||
-- detect the "everyone passed" subject exactly as the loot scanner will capture
|
||||
-- it: feed a dummy item into LOOT_ROLL_ALL_PASSED and run the same 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.cache = {}
|
||||
@@ -43,6 +47,7 @@ pfUI:RegisterModule("roll", function ()
|
||||
|
||||
local _, _, itemLink = string.find(hyperlink, "(item:%d+:%d+:%d+:%d+)")
|
||||
local itemName = C_Item.GetItemInfo(itemLink)
|
||||
if not itemName then return end -- uncached item: avoid cache[nil] "table index is nil"
|
||||
|
||||
-- delete obsolete tables
|
||||
if pfUI.roll.cache[itemName] and pfUI.roll.cache[itemName]["TIMESTAMP"] < GetTime() - 60 then
|
||||
|
||||
@@ -119,7 +119,7 @@ pfUI:RegisterModule("screenshot", function ()
|
||||
end
|
||||
|
||||
function pfUI.screenshot:CHAT_MSG_SYSTEM()
|
||||
local _,_, standing, rep = string.find(arg1, FACTION_STANDING_CHANGEDregex)
|
||||
local standing, rep = string.match(arg1, FACTION_STANDING_CHANGEDregex)
|
||||
if standing and rep then
|
||||
local dt = date("%a, %b %d, %Y %X")
|
||||
local loc = string.format("%s - %s",GetRealZoneText(),GetSubZoneText())
|
||||
@@ -150,13 +150,13 @@ pfUI:RegisterModule("screenshot", function ()
|
||||
end
|
||||
|
||||
function pfUI.screenshot:CHAT_MSG_LOOT()
|
||||
local _,_, item, amount = string.find(arg1, LOOT_ITEM_SELF_MULTIPLEregex)
|
||||
local item, amount = string.match(arg1, LOOT_ITEM_SELF_MULTIPLEregex)
|
||||
if amount then -- ignore stacks
|
||||
return
|
||||
else
|
||||
_,_, item = string.find(arg1, LOOT_ITEM_SELFregex)
|
||||
item = string.match(arg1, LOOT_ITEM_SELFregex)
|
||||
if item then
|
||||
local _, _, itemColor, itemString, itemName = string.find(item, "^(|c%x+)|H(.+)|h(%[.+%])")
|
||||
local itemColor, itemString, itemName = string.match(item, "^(|c%x+)|H(.+)|h(%[.+%])")
|
||||
local quality = color2quality[itemColor]
|
||||
if quality and quality >= tonumber(C.screenshot.loot) then
|
||||
local dt = date("%a, %b %d, %Y %X")
|
||||
|
||||
+71
-73
@@ -4,7 +4,7 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
pfUI.socialmod:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
pfUI.socialmod:SetScript("OnEvent", function()
|
||||
local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS)
|
||||
name = cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S)
|
||||
name = name or cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S)
|
||||
if name and playerdb[name] and playerdb[name].cname then
|
||||
playerdb[name].lastseen = date("%a %d-%b-%Y")
|
||||
end
|
||||
@@ -77,20 +77,19 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
local off = FauxScrollFrame_GetOffset(FriendsFrameFriendsScrollFrame)
|
||||
|
||||
for i=1, FRIENDS_TO_DISPLAY do
|
||||
local name, level, class, zone, connected, status = GetFriendInfo(off + i)
|
||||
if not name or name == _G.UNKNOWN then break end
|
||||
local info = C_FriendList.GetFriendInfoByIndex(off + i)
|
||||
if not info or not info.name or info.name == _G.UNKNOWN then break end
|
||||
local name = info.name
|
||||
local friendName = _G["FriendsFrameFriendButton"..i.."ButtonTextName"]
|
||||
local friendLoc = _G["FriendsFrameFriendButton"..i..FRIENDS_NAME_LOCATION]
|
||||
local friendLoc = _G["FriendsFrameFriendButton"..i.."ButtonTextNameLocation"]
|
||||
local friendInfo = _G["FriendsFrameFriendButton"..i.."ButtonTextInfo"]
|
||||
local caption = friendName or friendLoc
|
||||
|
||||
if connected then
|
||||
if not class or class == _G.UNKNOWN then break end
|
||||
local ccolor = PFUI_CLASS_COLORS[L["class"][class]] or { 1, 1, 1 }
|
||||
local lcolor = GetDifficultyColor(tonumber(level)) or { 1, 1, 1 }
|
||||
|
||||
zone = ( zone == playerzone and "|cffffffff" or "|cffcccccc" ) .. zone .. "|r"
|
||||
local cname = rgbhex(ccolor) .. name .. "|r"
|
||||
if info.connected then
|
||||
local ccolor = PFUI_CLASS_COLORS[info.classFilename]
|
||||
local status = info.afk and CHAT_FLAG_AFK or info.dnd and CHAT_FLAG_DND or ""
|
||||
local zone = ( info.area == playerzone and "|cffffffff" or "|cffcccccc" ) .. info.area .. "|r"
|
||||
local cname = ccolor:WrapTextInColorCode(name)
|
||||
if playerdb[name] then
|
||||
playerdb[name].lastseen = date("%a %d-%b-%Y")
|
||||
playerdb[name].cname = cname
|
||||
@@ -98,20 +97,20 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
|
||||
if friendName then
|
||||
friendName:SetText(cname)
|
||||
friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), zone, status))
|
||||
friendLoc:SetFormattedText(TEXT(FRIENDS_LIST_TEMPLATE), zone, status)
|
||||
else
|
||||
friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status))
|
||||
friendLoc:SetFormattedText(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status)
|
||||
end
|
||||
|
||||
friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), level, class))
|
||||
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), info.level, info.className)
|
||||
caption:SetVertexColor(1,1,1,.9)
|
||||
friendInfo:SetVertexColor(1,1,1,.9)
|
||||
else
|
||||
if playerdb[name] and playerdb[name].cname and playerdb[name].level and playerdb[name].lastseen then
|
||||
caption:SetText(format(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname))
|
||||
friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen))
|
||||
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), playerdb[name].cname)
|
||||
friendInfo:SetFormattedText(TEXT(FRIENDS_LEVEL_TEMPLATE), playerdb[name].level, playerdb[name].lastseen)
|
||||
else
|
||||
caption:SetText(format(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r"))
|
||||
caption:SetFormattedText(TEXT(FRIENDS_LIST_OFFLINE_TEMPLATE), name.."|r")
|
||||
friendInfo:SetText(TEXT(UNKNOWN))
|
||||
end
|
||||
|
||||
@@ -124,74 +123,73 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
|
||||
do -- add colors to who list
|
||||
hooksecurefunc("WhoList_Update", function()
|
||||
local num, max = GetNumWhoResults()
|
||||
local num, max = C_FriendList.GetNumWhoResults()
|
||||
local off = FauxScrollFrame_GetOffset(WhoListScrollFrame)
|
||||
|
||||
local playerzone = GetRealZoneText()
|
||||
local playerrace = UnitRace("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
|
||||
local name, guild, level, race, class, zone = GetWhoInfo(off + i)
|
||||
local displayedText = ""
|
||||
local info = C_FriendList.GetWhoInfo(off + i)
|
||||
if info then
|
||||
-- filename is the class token, so no L["class"] reversal is needed
|
||||
local class = info.filename
|
||||
|
||||
if num + 1 >= MAX_WHOS_FROM_SERVER then
|
||||
displayedText = format(WHO_FRAME_SHOWN_TEMPLATE, MAX_WHOS_FROM_SERVER)
|
||||
WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), max).." |cffaaaaaa"..displayedText)
|
||||
else
|
||||
displayedText = format(WHO_FRAME_SHOWN_TEMPLATE, num)
|
||||
WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), num).." |cffaaaaaa"..displayedText)
|
||||
end
|
||||
_G["WhoFrameButton"..i.."Name"]:SetTextColor(NORMAL_FONT_COLOR:GetRGB())
|
||||
|
||||
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))
|
||||
if (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 1) then
|
||||
if (info.area == playerzone) then
|
||||
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
|
||||
else
|
||||
classicon:Hide()
|
||||
_G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
|
||||
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)
|
||||
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
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -75,7 +75,10 @@ pfUI:RegisterModule("superwow", function ()
|
||||
DEFAULT_CHAT_FRAME:AddMessage("-> https://github.com/balakethelock/SuperWoW/releases/")
|
||||
end
|
||||
|
||||
if SUPERWOW_VERSION == "1.5" then
|
||||
-- compare numerically: an exact string match silently drops this on any
|
||||
-- SuperWoW release past 1.5
|
||||
local swVersion = tonumber(SUPERWOW_VERSION)
|
||||
if swVersion and swVersion >= 1.5 then
|
||||
QueueFunction(function()
|
||||
local pfCombatText_AddMessage = _G.CombatText_AddMessage
|
||||
_G.CombatText_AddMessage = function(message, a, b, c, d, e, f)
|
||||
|
||||
+26
-35
@@ -9,11 +9,6 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local ON_SWING_QUEUED = 0
|
||||
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)
|
||||
local S = {
|
||||
mhTimer = 0, mhTimerMax = 1,
|
||||
@@ -43,12 +38,11 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
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.
|
||||
-- On-next-swing spells replace the next auto-attack swing.
|
||||
-- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically.
|
||||
local function IsOnSwingSpell(spellId)
|
||||
if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end
|
||||
local attr = GetSpellRecField(spellId, "attributes") or 0
|
||||
local result = bit.band(attr, ATTR_ON_NEXT_SWING) ~= 0
|
||||
local result = C_Spell.IsNextMeleeSpell(spellId)
|
||||
S.onSwingCache[spellId] = result
|
||||
return result
|
||||
end
|
||||
@@ -290,7 +284,9 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
UpdateMovable(pfUI.swingtimer.ranged)
|
||||
|
||||
-- OH weapon detection
|
||||
local OH_WEAPON_TYPES = { [13]=true, [21]=true }
|
||||
-- OH weapon detection: slot 17 (off hand) accepts one-hand (13) and off-hand
|
||||
-- weapons (22); main-hand-only (21) can never sit there.
|
||||
local OH_WEAPON_TYPES = { [13]=true, [22]=true }
|
||||
local function HasOffhandWeapon()
|
||||
local l = GetInventoryItemLink("player", 17)
|
||||
if not l then return false end
|
||||
@@ -596,10 +592,10 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
end
|
||||
pfUI.swingtimer.mainhand:SetStatusBarColor(curR, curG, curB, mhA)
|
||||
if sw_showtext then
|
||||
pfUI.swingtimer.mainhand.text:SetText(string.format("%.1f", math.floor(S.mhTimer * 10) / 10))
|
||||
pfUI.swingtimer.mainhand.text:SetFormattedText("%.1f", math.floor(S.mhTimer * 10) / 10)
|
||||
end
|
||||
if sw_showspeed and S.mhSpeed > 0 then
|
||||
pfUI.swingtimer.mainhand.speed:SetText(string.format("%.2f", S.mhSpeed))
|
||||
pfUI.swingtimer.mainhand.speed:SetFormattedText("%.2f", S.mhSpeed)
|
||||
end
|
||||
anyActive = true
|
||||
end
|
||||
@@ -630,10 +626,10 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
end
|
||||
end
|
||||
if sw_showtext then
|
||||
pfUI.swingtimer.offhand.text:SetText(string.format("%.1f", math.floor(S.ohTimer * 10) / 10))
|
||||
pfUI.swingtimer.offhand.text:SetFormattedText("%.1f", math.floor(S.ohTimer * 10) / 10)
|
||||
end
|
||||
if sw_showspeed and S.ohSpeed > 0 then
|
||||
pfUI.swingtimer.offhand.speed:SetText(string.format("%.2f", S.ohSpeed))
|
||||
pfUI.swingtimer.offhand.speed:SetFormattedText("%.2f", S.ohSpeed)
|
||||
end
|
||||
anyActive = true
|
||||
elseif not sw_showoh then
|
||||
@@ -672,9 +668,9 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
end
|
||||
if sw_showtext then
|
||||
if remaining <= 0.5 then
|
||||
pfUI.swingtimer.ranged.text:SetText(string.format("%.1f", math.floor(remaining * 10) / 10))
|
||||
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
|
||||
else
|
||||
pfUI.swingtimer.ranged.text:SetText(string.format("%.1f", math.floor((remaining - 0.5) * 10) / 10))
|
||||
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor((remaining - 0.5) * 10) / 10)
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -687,11 +683,11 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
pfUI.swingtimer.ranged.right:Hide()
|
||||
pfUI.swingtimer.ranged.warn:Hide()
|
||||
if sw_showtext then
|
||||
pfUI.swingtimer.ranged.text:SetText(string.format("%.1f", math.floor(remaining * 10) / 10))
|
||||
pfUI.swingtimer.ranged.text:SetFormattedText("%.1f", math.floor(remaining * 10) / 10)
|
||||
end
|
||||
end
|
||||
if sw_showspeed and S.raSpeed > 0 then
|
||||
pfUI.swingtimer.ranged.speed:SetText(string.format("%.2f", S.raSpeed))
|
||||
pfUI.swingtimer.ranged.speed:SetFormattedText("%.2f", S.raSpeed)
|
||||
end
|
||||
local raProgress = 1 - (S.raTimer / S.raTimerMax)
|
||||
local raMarkerX = raProgress * sw_width
|
||||
@@ -734,13 +730,12 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
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).
|
||||
-- let the swing resume from where it paused. C_Spell.ResetsMeleeSwing
|
||||
-- mirrors the server rule; spells that reset don't need freezing (they
|
||||
-- reset on SPELL_GO_SELF). Subsumes the old hardcoded swingDelaySpells
|
||||
-- list (no list maintenance for new Slam-style spells).
|
||||
if S.mhActive then
|
||||
local iflags = GetSpellRecField(arg1, "interruptFlags") or 0
|
||||
if bit.band(iflags, FLAG_AUTOATTACK) == 0 then
|
||||
if not C_Spell.ResetsMeleeSwing(arg1) then
|
||||
S.mhFrozenAt = GetTime()
|
||||
end
|
||||
end
|
||||
@@ -777,17 +772,13 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
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
|
||||
-- C_Spell.ResetsMeleeSwing mirrors the server rule (Turtle's
|
||||
-- Spell::IsMeleeAttackResetSpell): InterruptFlags has AUTOATTACK and
|
||||
-- AttributesEx2 lacks NOT_RESET_AUTO_ACTIONS. When the spell resets the
|
||||
-- swing, snap the timers to full. Otherwise (elseif) a frozen-swing-
|
||||
-- during-cast is a Slam-style cast — push the timer forward by the cast
|
||||
-- duration so the bar resumes from where it paused.
|
||||
if C_Spell.ResetsMeleeSwing(spellId) then
|
||||
if S.mhActive and S.mhSpeed > 0 then
|
||||
UpdateWeaponSpeeds()
|
||||
S.mhTimerMax = S.mhSpeed
|
||||
@@ -825,7 +816,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
events:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
events:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
events:RegisterEvent("UNIT_DIED")
|
||||
events:RegisterUnitEvent("UNIT_DIED", UnitGUID("player"))
|
||||
events:RegisterEvent("SPELL_QUEUE_EVENT")
|
||||
events:RegisterEvent("START_AUTOATTACK")
|
||||
events:RegisterEvent("STOP_AUTOATTACK")
|
||||
|
||||
@@ -277,6 +277,103 @@ pfUI:RegisterModule("thirdparty", function()
|
||||
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
|
||||
-- Vanilla: https://github.com/Geigerkind/DPSMate
|
||||
-- TBC: https://github.com/Geigerkind/DPSMateTBC
|
||||
|
||||
+123
-9
@@ -166,9 +166,9 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
end
|
||||
|
||||
if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then
|
||||
pfUI.tooltipStatusBar.HP:SetText(string.format("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax)))
|
||||
pfUI.tooltipStatusBar.HP:SetFormattedText("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax))
|
||||
elseif hpmax > 0 then
|
||||
pfUI.tooltipStatusBar.HP:SetText(string.format("%s%%", ceil(hp/hpmax*100)))
|
||||
pfUI.tooltipStatusBar.HP:SetFormattedText("%s%%", ceil(hp/hpmax*100))
|
||||
else
|
||||
pfUI.tooltipStatusBar.HP:SetText("")
|
||||
end
|
||||
@@ -185,14 +185,106 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
GameTooltipStatusBar.SetStatusBarColor_orig = GameTooltipStatusBar.SetStatusBarColor
|
||||
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()
|
||||
local unit = pfUI.tooltip:GetUnit()
|
||||
pfUI.tooltip:UpdateBuffs(unit)
|
||||
if unit == "none" then
|
||||
-- process item tooltips
|
||||
if C.tooltip.itemid == "1" and GameTooltip:HasItem() then
|
||||
local _, _, itemID = GameTooltip:GetItem()
|
||||
GameTooltip:AddLine(T["ItemID"] .. ": " .. itemID, .25,.5,1)
|
||||
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
|
||||
|
||||
return
|
||||
@@ -259,6 +351,13 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
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 >= 1000 then hp = round(hp / 1000, 1) .. "k" end
|
||||
if hpm >= 1000 then hpm = round(hpm / 1000, 1) .. "k" end
|
||||
@@ -267,16 +366,31 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
if C.tooltip.aurasource == "1" then
|
||||
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
|
||||
local caster = aura.sourceUnit and UnitName(aura.sourceUnit)
|
||||
if not caster and aura.sourceGUID then
|
||||
caster = UnitNameFromGUID(aura.sourceGUID)
|
||||
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
|
||||
if not caster or caster == "" then return end
|
||||
self:AddLine(T["Cast by"] .. ": " .. caster, .7, .7, 1)
|
||||
self:Show()
|
||||
end)
|
||||
end
|
||||
|
||||
+1
-1
@@ -113,7 +113,7 @@ pfUI:RegisterModule("totems", function ()
|
||||
self.bar[i].cdbg = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i])
|
||||
self.bar[i].cdbg:SetSize(self.iconsize - 3, self.iconsize - 3)
|
||||
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
|
||||
self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
|
||||
self.bar[i].cd = self.bar[i].cd or CreateFrame("Model", "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
|
||||
self.bar[i].cd.pfCooldownStyleAnimation = 1
|
||||
self.bar[i].cd.pfCooldownType = "ALL"
|
||||
|
||||
|
||||
+25
-92
@@ -1,58 +1,17 @@
|
||||
pfUI:RegisterModule("tracking", function ()
|
||||
|
||||
MINIMAP_TRACKING_FRAME:UnregisterAllEvents()
|
||||
MINIMAP_TRACKING_FRAME:Hide()
|
||||
|
||||
local function HasEntries(tbl)
|
||||
for _ in pairs(tbl) do
|
||||
return true
|
||||
end
|
||||
return nil
|
||||
end
|
||||
_G.MiniMapTrackingFrame:UnregisterAllEvents()
|
||||
_G.MiniMapTrackingFrame:Hide()
|
||||
|
||||
local rawborder, border = GetBorderSize()
|
||||
local size = tonumber(C.appearance.minimap.tracking_size)
|
||||
local pulse = C.appearance.minimap.tracking_pulse == "1"
|
||||
|
||||
-- Tracking spells identified by SpellID + expected icon path.
|
||||
-- SpellID is the primary identifier (stable, locale-independent).
|
||||
-- Icon path is used as secondary confirmation when scanning the spellbook.
|
||||
local knownTrackingSpells = {
|
||||
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
|
||||
-- Tracking spells come from ClassicAPI's native GetNumTrackingTypes /
|
||||
-- GetTrackingInfo (see the DLL's docs/API.md "Tracking"). The DLL
|
||||
-- enumerates them straight from the spellbook by tracking-aura effect,
|
||||
-- so there is no spell table to maintain here and server-custom trackers
|
||||
-- (e.g. Turtle's Find Trees) are picked up automatically.
|
||||
|
||||
local state = {
|
||||
texture = nil,
|
||||
@@ -128,50 +87,24 @@ pfUI:RegisterModule("tracking", function ()
|
||||
end)
|
||||
|
||||
function pfUI.tracking:RefreshSpells()
|
||||
local playerClass = UnitClassBase("player")
|
||||
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(playerClass)
|
||||
state.spells = {}
|
||||
if not GetNumTrackingTypes then return end
|
||||
|
||||
-- Build set of valid SpellIDs for this class
|
||||
local validIds = {}
|
||||
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
|
||||
-- Druid Track Humanoids (5225) only casts in Cat Form. Hide it out of
|
||||
-- form so the menu never offers a tracker that would fail to cast.
|
||||
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(UnitClassBase("player"))
|
||||
|
||||
-- Druids only get Track Humanoids in Cat Form
|
||||
if playerClass == "DRUID" and not isCatForm then
|
||||
validIds[5225] = nil
|
||||
end
|
||||
|
||||
-- Scan spellbook: match by icon path, confirm SpellID is valid for this class
|
||||
for tabIndex = 1, GetNumSpellTabs() do
|
||||
local _, _, offset, numSpells = GetSpellTabInfo(tabIndex)
|
||||
for spellIndex = offset + 1, offset + numSpells do
|
||||
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
|
||||
for i = 1, GetNumTrackingTypes() do
|
||||
local name, texture, _, _, spellId = GetTrackingInfo(i)
|
||||
local castable = not (spellId == 5225 and not isCatForm)
|
||||
if name and texture and castable
|
||||
and not pfUI.tracking.invalidSpells[name] then
|
||||
state.spells[i] = {
|
||||
index = i, -- tracking index, passed to SetTracking()
|
||||
name = name,
|
||||
texture = texture,
|
||||
spellId = spellId,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -187,7 +120,7 @@ pfUI:RegisterModule("tracking", function ()
|
||||
elseif not texture then
|
||||
state.texture = nil
|
||||
|
||||
if pulse and HasEntries(state.spells) then
|
||||
if pulse and next(state.spells) ~= nil then
|
||||
pfUI.tracking.pulse = true
|
||||
pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
pfUI.tracking.icon:SetVertexColor(1,1,1,1)
|
||||
@@ -217,7 +150,7 @@ pfUI:RegisterModule("tracking", function ()
|
||||
checked = spell.texture == state.texture,
|
||||
arg1 = spell,
|
||||
func = function (arg1)
|
||||
CastSpell(arg1.index, BOOKTYPE_SPELL)
|
||||
SetTracking(arg1.index)
|
||||
CloseDropDownMenus()
|
||||
end
|
||||
})
|
||||
|
||||
+1
-2958
File diff suppressed because it is too large
Load Diff
+14
-5
@@ -28,7 +28,7 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
behindFrame.text:SetFont(pfUI.font_default, fontSize, "OUTLINE")
|
||||
behindFrame.text:SetPoint("RIGHT", behindFrame, "RIGHT", -1, 0)
|
||||
behindFrame.text:SetTextColor(0.3, 1, 0.3, 1)
|
||||
behindFrame.text:SetText("BEHIND")
|
||||
behindFrame.text:SetText(T["BEHIND"])
|
||||
behindFrame.text:Hide()
|
||||
|
||||
local lastCheck = 0
|
||||
@@ -62,7 +62,7 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
losFrame.text:SetFont(pfUI.font_default, fontSize, "OUTLINE")
|
||||
losFrame.text:SetPoint("RIGHT", losFrame, "RIGHT", -1, -slot)
|
||||
losFrame.text:SetTextColor(1, 0.3, 0.3, 1)
|
||||
losFrame.text:SetText("NO LOS")
|
||||
losFrame.text:SetText(T["NO LOS"])
|
||||
losFrame.text:Hide()
|
||||
|
||||
local lastCheck = 0
|
||||
@@ -134,7 +134,7 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
end
|
||||
|
||||
this.text:SetTextColor(r, g, b, 1)
|
||||
this.text:SetText(string.format("%.1f%s", distance, suffix))
|
||||
this.text:SetFormattedText("%.1f%s", distance, suffix)
|
||||
this.text:Show()
|
||||
end)
|
||||
|
||||
@@ -169,6 +169,9 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
|
||||
local throttle = 0
|
||||
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()
|
||||
throttle = throttle + arg1
|
||||
if throttle < 0.05 then return end
|
||||
@@ -203,7 +206,7 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
end
|
||||
|
||||
f.text:SetTextColor(r, g, b, 1)
|
||||
f.text:SetText(string.format("%.1f%s", distance, suffix))
|
||||
f.text:SetFormattedText("%.1f%s", distance, suffix)
|
||||
end)
|
||||
end
|
||||
pfUI.uf.target.distanceIndicator = pfRangeDisplay
|
||||
@@ -236,6 +239,10 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
if pfUI.uf.target.distanceIndicator then
|
||||
pfUI.uf.target.distanceIndicator:SetScript("OnUpdate", nil)
|
||||
end
|
||||
-- free-frame mode polls from its own scanner frame, not the indicator
|
||||
if pfUI.uf.target.distanceScanner then
|
||||
pfUI.uf.target.distanceScanner:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -268,7 +275,9 @@ pfUI:RegisterModule("unitxp", function ()
|
||||
-- Also notify on BG queue pop
|
||||
local origBattlefieldPortShow = BattlefieldFrame_Show
|
||||
if origBattlefieldPortShow then
|
||||
BattlefieldFrame_Show = function()
|
||||
-- pfUI.env has __index but no __newindex, so a bare global assignment
|
||||
-- inside a RegisterModule closure never leaves the sandbox
|
||||
_G.BattlefieldFrame_Show = function()
|
||||
pcall(UnitXP, "notify", "taskbarIcon")
|
||||
pcall(UnitXP, "notify", "systemSound")
|
||||
return origBattlefieldPortShow()
|
||||
|
||||
+6
-6
@@ -236,7 +236,7 @@ end
|
||||
local xpperc = round(xp / xpmax * 100)
|
||||
local experc = ex and round(ex / xpmax * 100) or 0
|
||||
if ex then text = "%s: %s%% (%s%% %s)" end
|
||||
self.bar.text:SetText(string.format(text, T["Experience"], xpperc, experc, T["Rested"]))
|
||||
self.bar.text:SetFormattedText(text, T["Experience"], xpperc, experc, T["Rested"])
|
||||
|
||||
self.tick = GetTime() + self.timeout
|
||||
if event == "UPDATE_EXHAUSTION" and GameTooltip:IsOwned(self) then
|
||||
@@ -254,7 +254,7 @@ end
|
||||
|
||||
local text = "%s: %s%%"
|
||||
local xpperc = nextXP and nextXP ~= 0 and round(currXP / nextXP * 100) or 0
|
||||
self.bar.text:SetText(string.format(text, T["Pet Experience"], xpperc))
|
||||
self.bar.text:SetFormattedText(text, T["Pet Experience"], xpperc)
|
||||
|
||||
self.tick = GetTime() + self.timeout
|
||||
return
|
||||
@@ -282,7 +282,7 @@ end
|
||||
local text = "%s: %s%% (%s)"
|
||||
local perc = round(barValue / barMax * 100)
|
||||
local standing = GetText("FACTION_STANDING_LABEL"..standingID, gender)
|
||||
self.bar.text:SetText(string.format(text, name, perc, standing))
|
||||
self.bar.text:SetFormattedText(text, name, perc, standing)
|
||||
|
||||
self.tick = GetTime() + self.timeout
|
||||
return
|
||||
@@ -363,9 +363,9 @@ end
|
||||
b:EnableMouse(true)
|
||||
|
||||
b:RegisterEvent("FACTION_STANDING_CHANGED")
|
||||
b:RegisterEvent("UNIT_PET")
|
||||
b:RegisterEvent("UNIT_LEVEL")
|
||||
b:RegisterEvent("UNIT_PET_EXPERIENCE")
|
||||
b:RegisterUnitEvent("UNIT_PET", "player")
|
||||
b:RegisterUnitEvent("UNIT_LEVEL", "player")
|
||||
b:RegisterUnitEvent("UNIT_PET_EXPERIENCE", "player", "pet")
|
||||
b:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
b:RegisterEvent("UPDATE_EXHAUSTION")
|
||||
b:RegisterEvent("PLAYER_XP_UPDATE")
|
||||
|
||||
@@ -3,6 +3,8 @@ function SlashCmdList.RELOAD(msg, editbox)
|
||||
ReloadUI()
|
||||
end
|
||||
|
||||
local addonName = ...
|
||||
|
||||
SLASH_PFUI1 = '/pfui'
|
||||
function SlashCmdList.PFUI(msg, editbox)
|
||||
pfUI.gui:SetShown(not pfUI.gui:IsShown())
|
||||
@@ -13,87 +15,19 @@ function SlashCmdList.GM(msg, editbox)
|
||||
ToggleHelpFrame(1)
|
||||
end
|
||||
|
||||
pfUI = CreateFrame("Frame", nil, UIParent)
|
||||
local pfUI = CreateFrame("Frame", addonName, UIParent)
|
||||
pfUI:RegisterEvent("ADDON_LOADED")
|
||||
|
||||
-- setup bootvar
|
||||
pfUI.bootup = true
|
||||
|
||||
do
|
||||
-- ClassicAPI dependency check.
|
||||
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
|
||||
-- API surface that ClassicAPI polyfills, so presence is required.
|
||||
local PFUI_CLASSIC_API_MIN = 10903 -- (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
|
||||
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 = {}
|
||||
pfUI_playerDB = pfUI_playerDB or {}
|
||||
pfUI_config = pfUI_config or {}
|
||||
pfUI_init = pfUI_init or {}
|
||||
pfUI_profiles = pfUI_profiles or {}
|
||||
pfUI_addon_profiles = pfUI_addon_profiles or {}
|
||||
pfUI_cache = pfUI_cache or {}
|
||||
pfUI_throttle = pfUI_throttle or {}
|
||||
|
||||
-- localization
|
||||
pfUI_locale = {}
|
||||
@@ -110,35 +44,32 @@ pfUI.movables = {}
|
||||
pfUI.version = {}
|
||||
pfUI.env = {}
|
||||
|
||||
if not pfUI.disabled then
|
||||
pfUI.events = Mixin({}, CallbackRegistryMixin)
|
||||
pfUI.events:OnLoad()
|
||||
pfUI.events:SetUndefinedEventsAllowed(true)
|
||||
end
|
||||
-- Capability flag: this fork's pfActionBar buttons correctly handle the modern
|
||||
-- HookScript widget method, so ClassicAPI's AddOnCompat shim must NOT shadow
|
||||
-- HookScript during actionbar load (older forks branch on `if button.HookScript`
|
||||
-- and take a modern path they were never written for). This describes behavior,
|
||||
-- not identity: a fork that keeps the HookScript-correct actionbar code keeps
|
||||
-- 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)
|
||||
function pfUI:MacroAddonsLoaded()
|
||||
return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros")
|
||||
end
|
||||
|
||||
-- detect current addon path
|
||||
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
|
||||
pfUI.name = addonName
|
||||
pfUI.path = "Interface\\AddOns\\" .. addonName
|
||||
|
||||
-- handle/convert media dir paths
|
||||
pfUI.media = setmetatable({}, { __index = function(tab,key)
|
||||
local value = tostring(key)
|
||||
if strfind(value, "img:") then
|
||||
if value:find("img:") then
|
||||
value = string.gsub(value, "img:", pfUI.path .. "\\img\\")
|
||||
elseif strfind(value, "font:") then
|
||||
elseif value:find("font:") then
|
||||
value = string.gsub(value, "font:", pfUI.path .. "\\fonts\\")
|
||||
else
|
||||
value = string.gsub(value, "Interface\\AddOns\\pfUI\\", pfUI.path .. "\\")
|
||||
@@ -147,9 +78,7 @@ pfUI.media = setmetatable({}, { __index = function(tab,key)
|
||||
return value
|
||||
end})
|
||||
|
||||
-- cache client version
|
||||
local _, _, _, client = GetBuildInfo()
|
||||
pfUI.client = client or 11200
|
||||
pfUI.client = INTERFACE_VERSION
|
||||
|
||||
-- setup pfUI namespace
|
||||
setmetatable(pfUI.env, {__index = getfenv(0)})
|
||||
@@ -292,6 +221,21 @@ function pfUI:GetEnvironment()
|
||||
pfUI.env.C = pfUI_config
|
||||
pfUI.env.pfUI_throttle = _G.pfUI_throttle
|
||||
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
|
||||
end
|
||||
@@ -368,14 +312,13 @@ function pfUI:CheckNewModules()
|
||||
end
|
||||
|
||||
local function BackwardsCompatRegister(func, arg3)
|
||||
if arg3 and type(func) == "string" and type(arg3) == "function" and string.find(func, "vanilla") then
|
||||
if arg3 and type(func) == "string" and type(arg3) == "function" and func:find("vanilla") then
|
||||
return arg3
|
||||
end
|
||||
return func
|
||||
end
|
||||
|
||||
function pfUI:RegisterModule(name, func, arg3)
|
||||
if pfUI.disabled then return end
|
||||
if pfUI.module[name] then return end
|
||||
func = BackwardsCompatRegister(func, arg3)
|
||||
pfUI.module[name] = func
|
||||
@@ -386,7 +329,6 @@ function pfUI:RegisterModule(name, func, arg3)
|
||||
end
|
||||
|
||||
function pfUI:RegisterSkin(name, func, arg3)
|
||||
if pfUI.disabled then return end
|
||||
if pfUI.skin[name] then return end
|
||||
func = BackwardsCompatRegister(func, arg3)
|
||||
pfUI.skin[name] = func
|
||||
@@ -407,7 +349,6 @@ function pfUI:LoadSkin(s)
|
||||
end
|
||||
|
||||
pfUI:SetScript("OnEvent", function()
|
||||
if pfUI.disabled then return end
|
||||
|
||||
-- make sure to initialize and set our fonts
|
||||
-- each time an addon got loaded but only
|
||||
@@ -421,11 +362,11 @@ pfUI:SetScript("OnEvent", function()
|
||||
-- "@project-version@" until release tooling substitutes it — mark those
|
||||
-- explicitly as "dev" instead of pretending they're a real numbered build.
|
||||
local raw = tostring(GetAddOnMetadata(pfUI.name, "Version"))
|
||||
if strfind(raw, "@") then
|
||||
if raw:find("@") then
|
||||
pfUI.version.major, pfUI.version.minor, pfUI.version.fix = 0, 0, 0
|
||||
pfUI.version.string = "dev"
|
||||
else
|
||||
local major, minor, fix = pfUI.api.strsplit(".", raw)
|
||||
local major, minor, fix = pfUI.api.strsplit(".", string.gsub(raw, "^[vV]", ""))
|
||||
pfUI.version.major = tonumber(major) or 0
|
||||
pfUI.version.minor = tonumber(minor) or 0
|
||||
pfUI.version.fix = tonumber(fix) or 0
|
||||
@@ -554,4 +495,21 @@ function pfUI.SetupCVars()
|
||||
COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
|
||||
end
|
||||
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
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
## Version: @project-version@
|
||||
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
|
||||
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
|
||||
## Dependencies: !!!ClassicAPI
|
||||
## X-Website: https://github.com/brues-code/pfUI
|
||||
|
||||
pfUI.lua
|
||||
# ClassicAPI-less fallback. ClassicAPI redirects this read to pfUI_Turtle.toc
|
||||
# (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.
|
||||
|
||||
init\env.xml
|
||||
init\compat.xml
|
||||
init\api.xml
|
||||
init\libs.xml
|
||||
init\skins.xml
|
||||
init\modules.xml
|
||||
API_Check.lua
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
## 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
|
||||
@@ -0,0 +1,24 @@
|
||||
## 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
|
||||
@@ -295,9 +295,8 @@ pfUI:RegisterSkin("Character", function ()
|
||||
if faction and faction.reaction and faction.reaction < 8 then
|
||||
local repLeft = faction.nextReactionThreshold - faction.currentStanding
|
||||
if repLeft > 1 then
|
||||
local text = standing:GetText() .. string.format(" (%d)", repLeft)
|
||||
standing:SetText(text)
|
||||
standing:GetParent().standingText = text
|
||||
standing:SetFormattedText("%s (%d)", standing:GetText(), repLeft)
|
||||
standing:GetParent().standingText = standing:GetText()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -125,7 +125,7 @@ pfUI:RegisterSkin("Inspect", function ()
|
||||
local guild, title = GetGuildInfo(InspectFrame.unit)
|
||||
if guild then
|
||||
InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1)
|
||||
InspectGuildText:SetText(format(TEXT(GUILD_TITLE_TEMPLATE), title, guild))
|
||||
InspectGuildText:SetFormattedText(TEXT(GUILD_TITLE_TEMPLATE), title, guild)
|
||||
InspectGuildText:Show()
|
||||
else
|
||||
InspectGuildText:SetText("")
|
||||
|
||||
Reference in New Issue
Block a user