mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-22 07:36:56 +00:00
Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 1c7c91fdf7 | |||
| 7f5d18bc30 | |||
| 7efc3334ae | |||
| f322fbd6c6 | |||
| 41b77a5728 | |||
| 06fea6c889 | |||
| 940a1adae0 | |||
| d13710c5a4 | |||
| c3dd7cb601 | |||
| 9190b88974 | |||
| fe0ec82ede | |||
| 350b1d63ae | |||
| c7f42491ef | |||
| 6b458c9e27 | |||
| f338deebe7 | |||
| c994d00596 | |||
| 4be680183a | |||
| 2352f1c6ed | |||
| 305ff6c86e | |||
| a8b131898b | |||
| 1f9be33772 | |||
| a798e67b7f | |||
| 8b87e30266 | |||
| f68b14e53b | |||
| 8ab6fab04f | |||
| 6d1bfe805e | |||
| 838f7f5d25 | |||
| fad078fa87 | |||
| 48d76138ed | |||
| fe22dfc456 | |||
| 5eaab2784f | |||
| f41d5ac6d0 | |||
| af8780bb41 | |||
| ea37f41db1 | |||
| 6f36da7aa1 | |||
| 149d5dd362 | |||
| 0156d9dfec | |||
| 6b945c2c97 | |||
| 452eef3864 | |||
| 6f7a57f530 | |||
| a55460e543 | |||
| 58aaeef5f0 | |||
| 487af0c8f4 | |||
| 00292ca3b4 | |||
| cba3604906 | |||
| a31d10384b | |||
| f06af48bc2 | |||
| d73c4de695 | |||
| dd8c529463 | |||
| 47a7b4c686 | |||
| cf3f1e5440 | |||
| 182127bd67 | |||
| 3d2e6e202b | |||
| 3a062ee2ac | |||
| 5ed1d98ecc | |||
| fa1dc5637e | |||
| 8506f5ffbf | |||
| 9c529512d1 | |||
| 88d0f7bf74 | |||
| a3ff20782d | |||
| 88c2462fbd | |||
| e28a7e5606 | |||
| dfa74c5756 | |||
| 1c0c9dc019 | |||
| 801935130e | |||
| 8cdced5ec0 | |||
| 756e8840af | |||
| b2db091d54 | |||
| 5fae0bd459 | |||
| 3deddf2b08 | |||
| 650bcda001 | |||
| 931fe6c22d | |||
| bd4a40b0f2 | |||
| 31ff2773b3 | |||
| 8176b906df | |||
| 721ecce59d | |||
| 73b409fb88 | |||
| 0665764610 | |||
| 3faf06141f | |||
| 0c06401ec4 | |||
| c68e48111d | |||
| 6936fd894b | |||
| b5277ea457 | |||
| 63f0dcbf7a | |||
| 0344bff471 | |||
| 1510969105 | |||
| 6e7361c543 | |||
| 2e20a03cbd | |||
| 7389c241a4 | |||
| fb5230828c | |||
| 7aee348a70 | |||
| 2a5f480839 | |||
| 23d1ab840c | |||
| 13a08b0ea3 | |||
| a2177fbf49 | |||
| 9cd83e90ad | |||
| 9bcc11e64f | |||
| acab272ec0 | |||
| 61c2f996fa | |||
| 59ce6d9e74 | |||
| 967487e283 | |||
| ab11096b01 | |||
| 0402db380a | |||
| 68fe7e22db | |||
| a93e9f530c | |||
| 3be0585039 | |||
| ee7c729bcb | |||
| 62e0d4eb07 | |||
| a4ceb8ba53 | |||
| 80b677a6f8 | |||
| f6683b31af | |||
| b47df9272d | |||
| c0b717878d | |||
| 31c95606d0 | |||
| 3fe072c594 | |||
| 9706a74d16 | |||
| f9b0b5983a | |||
| 176e131d6b | |||
| a17229abb1 | |||
| e0cf01772c | |||
| c2d4106170 | |||
| dbfd337d42 | |||
| d834be9f0b | |||
| 82a07e1e35 | |||
| 703d7770ca | |||
| bd722b0f0f | |||
| e5c51fff48 | |||
| 6570afb70a | |||
| 0f97e8b0bd | |||
| 64c62a2a25 | |||
| 4d316ed43a | |||
| db0195f0f7 | |||
| 3d2d5f9254 | |||
| dfbeeb454a | |||
| 439d5a397a | |||
| 5524ad3b91 | |||
| 0f59a3a9b5 | |||
| ffdf376ac4 | |||
| 9bdde06150 | |||
| 8bf6672114 | |||
| 29b948e6fa | |||
| 5f61f94a24 | |||
| 65678da3d2 | |||
| 9b7aca8541 | |||
| 60525eec3a | |||
| c3829c2bd2 | |||
| 28de6fd835 | |||
| fc75e091a4 | |||
| ef478b0552 | |||
| 760d8992b6 | |||
| 62a99ac410 | |||
| deeec89955 | |||
| cca9771ef0 | |||
| 371872bb90 | |||
| 591045a606 | |||
| f657a9e7e8 | |||
| 0cbd9ecf98 | |||
| 6ca482fcaa | |||
| 8902d43f40 | |||
| 8f006c0643 | |||
| de7dacf788 | |||
| e0bcbbf2dc | |||
| 65bbea6fa9 | |||
| 822c873132 | |||
| 96fd486187 | |||
| b79132b9f4 | |||
| d62a1222df | |||
| be6ae25315 | |||
| d6951b386e | |||
| fd21b24a74 | |||
| 90ac4f9de0 | |||
| 63f9b26d91 |
+2
-2
@@ -1,2 +1,2 @@
|
||||
github: [shagu]
|
||||
ko_fi: shagu
|
||||
github: [brues]
|
||||
buy_me_a_coffee: brues
|
||||
|
||||
@@ -4,16 +4,18 @@ about: Report a bug or issue with pfUI
|
||||
title: "[Bug Description] "
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Please fill out ALL fields below. Issues without this info may be closed. -->
|
||||
|
||||
**pfUI Branch:**
|
||||
- [ ] Master
|
||||
- [ ] experiment
|
||||
**pfUI Version:**
|
||||
|
||||
**Your ClassicAPI Version:**
|
||||
|
||||
**Your Nampower Version:**
|
||||
|
||||
|
||||
**Your Nampower Version:** (e.g. latest, or specific commit/date)
|
||||
<!-- Skip if using Compat branch -->
|
||||
|
||||
**Other Addons installed:**
|
||||
<!-- List any other addons you are using, or "none" -->
|
||||
@@ -45,4 +47,3 @@ Paste error log here
|
||||
## Screenshot / Video
|
||||
<!-- Attach a screenshot or video showing the issue. This is REQUIRED. -->
|
||||
<!-- Drag & drop images here or paste a link -->
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ about: Suggest a new feature or improvement for pfUI
|
||||
title: "[Feature] "
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Feature Description
|
||||
@@ -20,4 +21,3 @@ assignees: ''
|
||||
|
||||
## Examples
|
||||
<!-- If possible, show examples from other addons or screenshots/mockups of how it could look -->
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ 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 = [0-9]*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" pfUI.lua
|
||||
sed -i "s/^\([[:space:]]*\)local PFUI_CLASSIC_API_LATEST = .*/\1local PFUI_CLASSIC_API_LATEST = $PACKED/" pfUI.lua
|
||||
grep 'local PFUI_CLASSIC_API_' pfUI.lua
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
- Hard dependency on [ClassicAPI](https://github.com/brues-code/ClassicAPI) — modern `C_*` namespaces and engine polyfills (focus, nameplates, GUIDs).
|
||||
- TBC/Wrath/expansion plumbing removed; vanilla 1.12 + Turtle WoW only.
|
||||
- Nameplate overhaul — GUID-keyed caches, per-tick allocation cuts, name-collision filtering, configurable name text position.
|
||||
- Unit auras rebuilt on `C_UnitAuras` — replaces the old tooltip-scraping aura tracker with structured engine data, so buff/debuff durations, stack counts, and source attribution are accurate without polling.
|
||||
- Equipment manager — new backport of Blizzard's gear-set UI, integrated into the character pane. Save, swap, and edit sets without a third-party addon.
|
||||
- Bag sorting — new built-in feature (previously pfUI only deferred to third-party sorters).
|
||||
- `/focus` and `/clearfocus` moved to ClassicAPI; `/focusname` retained in pfUI.
|
||||
- questitem refactored onto `C_QuestLog`.
|
||||
- `updatenotify` isolated to its own `pfUI-brues` addon-message prefix so we don't share traffic with upstream pfUI installs.
|
||||
|
||||
See full commit history at https://github.com/brues-code/pfUI/commits/master for details.
|
||||
+167
-101
@@ -28,14 +28,6 @@ function pfUI.api.HasNampower()
|
||||
return GetNampowerVersion and true or false
|
||||
end
|
||||
|
||||
local isTurtleWoW
|
||||
function pfUI.api.IsTurtleWoW()
|
||||
if isTurtleWoW == nil then
|
||||
isTurtleWoW = C_Spell.GetSpellTexture(46050) == "Interface\\Icons\\Trade_Survival"
|
||||
end
|
||||
return isTurtleWoW
|
||||
end
|
||||
|
||||
-- [ GetUnitDistance ]
|
||||
-- Returns distance to unit using best available method
|
||||
-- 'unit1' [string] first unit (default: "player")
|
||||
@@ -72,6 +64,10 @@ end
|
||||
-- Requires UnitXP_SP3
|
||||
function pfUI.api.UnitInLineOfSight(unit1, unit2)
|
||||
if not pfUI.api.HasUnitXP() then return nil end
|
||||
if not unit2 then
|
||||
unit2 = unit1
|
||||
unit1 = "player"
|
||||
end
|
||||
local success, inSight = pcall(UnitXP, "inSight", unit1, unit2)
|
||||
if success then return inSight end
|
||||
return nil
|
||||
@@ -82,6 +78,10 @@ end
|
||||
-- Requires UnitXP_SP3
|
||||
function pfUI.api.UnitIsBehind(unit1, unit2)
|
||||
if not pfUI.api.HasUnitXP() then return nil end
|
||||
if not unit2 then
|
||||
unit2 = unit1
|
||||
unit1 = "player"
|
||||
end
|
||||
local success, behind = pcall(UnitXP, "behind", unit1, unit2)
|
||||
if success then return behind end
|
||||
return nil
|
||||
@@ -92,16 +92,24 @@ gfind = string.gmatch or string.gfind
|
||||
mod = math.mod or mod
|
||||
|
||||
-- [ strsplit ]
|
||||
-- Splits a string using a delimiter.
|
||||
-- Splits a string using a delimiter. Self-contained on purpose: it does NOT
|
||||
-- delegate to the global strsplit / string.split, because third-party addons
|
||||
-- clobber those (e.g. BigWigs' SpellRequests redefines string:split to return
|
||||
-- a table, which would make this return a single table instead of r,g,b,a and
|
||||
-- break color/version parsing depending on load order). Delimiter chars are
|
||||
-- treated as a set (any one char splits), and empty fields are preserved
|
||||
-- ("a,,b" -> "a", "", "b"), matching real strsplit semantics.
|
||||
-- 'delimiter' [string] characters that will be interpreted as delimiter
|
||||
-- characters (bytes) in the string.
|
||||
-- 'subject' [string] String to split.
|
||||
-- return: [list] a list of strings.
|
||||
local format, sgsub = string.format, string.gsub
|
||||
function pfUI.api.strsplit(delimiter, subject)
|
||||
if not subject then return nil end
|
||||
local delimiter, fields = delimiter or ":", {}
|
||||
local pattern = string.format("([^%s]+)", delimiter)
|
||||
string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
|
||||
local fields = {}
|
||||
delimiter = delimiter or ":"
|
||||
local pattern = format("([^%s]+)", delimiter)
|
||||
sgsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
|
||||
return unpack(fields)
|
||||
end
|
||||
|
||||
@@ -110,11 +118,7 @@ end
|
||||
-- 'tbl' [table] the table that shall be checked
|
||||
-- return: [boolean] result of the check.
|
||||
function pfUI.api.isempty(tbl)
|
||||
if not tbl then return true end
|
||||
for k, v in pairs(tbl) do
|
||||
return false
|
||||
end
|
||||
return true
|
||||
return next(tbl or {}) == nil
|
||||
end
|
||||
|
||||
-- [ checkversion ]
|
||||
@@ -143,7 +147,6 @@ end
|
||||
-- It takes care of the rangecheck module if existing.
|
||||
-- unit [string] A unit to query (string, unitID)
|
||||
-- return: [bool] "1" if in range otherwise "nil"
|
||||
local RangeCache = {}
|
||||
function pfUI.api.UnitInRange(unit)
|
||||
if not UnitExists(unit) or not UnitIsVisible(unit) then
|
||||
return nil
|
||||
@@ -151,6 +154,11 @@ function pfUI.api.UnitInRange(unit)
|
||||
return 1
|
||||
end
|
||||
|
||||
-- master switch: with the 40y check off, a visible unit beyond interact
|
||||
-- range counts as in range (nothing fades). Invisible units already
|
||||
-- returned nil above, matching the pre-collapse behavior.
|
||||
if C.unitframes.rangecheck == "0" then return 1 end
|
||||
|
||||
-- UnitXP precise mode: skip librange entirely, use direct distance check
|
||||
if C.unitframes.rangecheck_mode == "unitxp" and _G.UnitXP then
|
||||
local threshold = tonumber(C.unitframes.rangecheck_distance) or 40
|
||||
@@ -193,10 +201,7 @@ end
|
||||
-- name [string] The localized name of the buff.
|
||||
-- return: [bool] true if unit has buff otherwise "nil"
|
||||
function pfUI.api.UnitHasBuff(unit, name)
|
||||
for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unit, "HELPFUL")) do
|
||||
if aura.name == name then return true end
|
||||
end
|
||||
return nil
|
||||
return C_UnitAuras.GetAuraDataBySpellName(unit, name, "HELPFUL") ~= nil or nil
|
||||
end
|
||||
|
||||
-- [ GetUnbuffedRoster ]
|
||||
@@ -211,7 +216,7 @@ function pfUI.api.GetUnbuffedRoster(name)
|
||||
end
|
||||
end
|
||||
|
||||
if UnitInRaid("player") then
|
||||
if IsInRaid() then
|
||||
for i=1,40 do check("raid"..i) end
|
||||
else
|
||||
check("player")
|
||||
@@ -226,7 +231,7 @@ end
|
||||
-- unit [string] the unitstring
|
||||
-- return: [table] string, r, g, b
|
||||
function pfUI.api.GetUnitColor(unitstr)
|
||||
local _, class = UnitClass(unitstr)
|
||||
local class = UnitClassBase(unitstr)
|
||||
local classColor = PFUI_CLASS_COLORS[class]
|
||||
return classColor:GenerateHexColorMarkup(), classColor:GetRGB()
|
||||
end
|
||||
@@ -277,11 +282,7 @@ end
|
||||
-- 'f' [float] the number to breakdown.
|
||||
-- returns: [int],[float] whole and fractional part.
|
||||
function pfUI.api.modf(f)
|
||||
if modf then return modf(f) end
|
||||
if f > 0 then
|
||||
return math.floor(f), mod(f,1)
|
||||
end
|
||||
return math.ceil(f), mod(f,1)
|
||||
return math.modf(f)
|
||||
end
|
||||
|
||||
-- [ GetServerEpoch ]
|
||||
@@ -376,36 +377,53 @@ end
|
||||
-- 'str' [string] input string that should be matched
|
||||
-- 'pat' [string] unformatted pattern
|
||||
-- returns: [strings] matched string in capture order
|
||||
local a, b, c, d, e
|
||||
local _, va, vb, vc, vd, ve
|
||||
local ra, rb, rc, rd, re
|
||||
function pfUI.api.cmatch(str, pat)
|
||||
-- read capture indexes
|
||||
a, b, c, d, e = GetCaptures(pat)
|
||||
_, _, va, vb, vc, vd, ve = string.find(str, pfUI.api.SanitizePattern(pat))
|
||||
-- idx* = the logical %N index of each physical capture slot (nil when the
|
||||
-- pattern has no %N$ markers); val* = the matched values in physical order.
|
||||
local idx1, idx2, idx3, idx4, idx5 = GetCaptures(pat)
|
||||
local _, _, val1, val2, val3, val4, val5 = string.find(str, pfUI.api.SanitizePattern(pat))
|
||||
|
||||
-- put entries into the proper return values
|
||||
ra = e == 1 and ve or d == 1 and vd or c == 1 and vc or b == 1 and vb or va
|
||||
rb = e == 2 and ve or d == 2 and vd or c == 2 and vc or a == 2 and va or vb
|
||||
rc = e == 3 and ve or d == 3 and vd or a == 3 and va or b == 3 and vb or vc
|
||||
rd = e == 4 and ve or a == 4 and va or c == 4 and vc or b == 4 and vb or vd
|
||||
re = a == 5 and va or d == 5 and vd or c == 5 and vc or b == 5 and vb or ve
|
||||
-- reorder the physical matches into logical (%1..%5) order
|
||||
local out1 = idx5 == 1 and val5 or idx4 == 1 and val4 or idx3 == 1 and val3 or idx2 == 1 and val2 or val1
|
||||
local out2 = idx5 == 2 and val5 or idx4 == 2 and val4 or idx3 == 2 and val3 or idx1 == 2 and val1 or val2
|
||||
local out3 = idx5 == 3 and val5 or idx4 == 3 and val4 or idx1 == 3 and val1 or idx2 == 3 and val2 or val3
|
||||
local out4 = idx5 == 4 and val5 or idx1 == 4 and val1 or idx3 == 4 and val3 or idx2 == 4 and val2 or val4
|
||||
local out5 = idx1 == 5 and val1 or idx4 == 5 and val4 or idx3 == 5 and val3 or idx2 == 5 and val2 or val5
|
||||
|
||||
return ra, rb, rc, rd, re
|
||||
return out1, out2, out3, out4, out5
|
||||
end
|
||||
|
||||
-- [ GetItemLinkByName ]
|
||||
-- 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
|
||||
local itemName, hyperLink, itemQuality = GetItemInfo(itemID)
|
||||
if (itemName and itemName == name) then
|
||||
local _, _, _, hex = GetItemQualityColor(tonumber(itemQuality))
|
||||
return hex.. "|H"..hyperLink.."|h["..itemName.."]|h|r"
|
||||
-- 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 ]
|
||||
@@ -437,13 +455,17 @@ function pfUI.api.GetBagFamily(bag)
|
||||
|
||||
local id = GetInventoryItemID("player", ContainerIDToInventoryID(bag))
|
||||
if id then
|
||||
local _, _, _, _, _, itemType, subType = GetItemInfo(id)
|
||||
local bagsubtype = L["bagtypes"][subType]
|
||||
|
||||
if bagsubtype == "DEFAULT" then return "BAG" end
|
||||
if bagsubtype == "SOULBAG" then return "SOULBAG" end
|
||||
if bagsubtype == "QUIVER" then return "QUIVER" end
|
||||
if bagsubtype == nil then return "SPECIAL" end
|
||||
-- classID 1 = Container (bags), 11 = Quiver
|
||||
-- Container subclasses: 0 = Bag (default), 1 = Soul Bag, 2+ = specialty (herb/enchanting/etc.)
|
||||
-- Quiver subclasses: 2 = Quiver (arrows), 3 = Ammo Pouch (bullets)
|
||||
local _, _, _, _, _, classID, subClassID = C_Item.GetItemInfoInstant(id)
|
||||
if classID == 1 then
|
||||
if subClassID == 0 then return "BAG" end
|
||||
if subClassID == 1 then return "SOULBAG" end
|
||||
return "SPECIAL"
|
||||
elseif classID == 11 then
|
||||
return "QUIVER"
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -488,7 +510,7 @@ end
|
||||
-- 'msg' [string] the message to send
|
||||
function pfUI.api.SendChatMessageWide(msg)
|
||||
local channel = "SAY"
|
||||
if UnitInRaid("player") then
|
||||
if IsInRaid() then
|
||||
if ( IsRaidLeader() or IsRaidOfficer() ) then
|
||||
channel = "RAID_WARNING"
|
||||
else
|
||||
@@ -555,36 +577,54 @@ end
|
||||
-- 'script' [string] the handler to hook
|
||||
-- 'func' [function] the function that should be added
|
||||
function HookScript(f, script, func)
|
||||
local prev = f:GetScript(script)
|
||||
f:SetScript(script, function(a1,a2,a3,a4,a5,a6,a7,a8,a9)
|
||||
if prev then prev(a1,a2,a3,a4,a5,a6,a7,a8,a9) end
|
||||
func(a1,a2,a3,a4,a5,a6,a7,a8,a9)
|
||||
end)
|
||||
f:HookScript(script, func)
|
||||
end
|
||||
|
||||
function hooksecurefunc(tbl, name, func)
|
||||
if type(tbl) == "string" then tbl, name, func = _G, tbl, name end
|
||||
if not tbl or type(tbl[name]) ~= "function" then return end
|
||||
return _G.hooksecurefunc(tbl, name, func)
|
||||
end
|
||||
|
||||
-- [ HookAddonOrVariable ]
|
||||
-- Sets a function to be called automatically once an addon gets loaded
|
||||
-- 'addon' [string] addon or variable name
|
||||
-- 'func' [function] function that should run
|
||||
function pfUI.api.HookAddonOrVariable(addon, func)
|
||||
local lurker = CreateFrame("Frame", nil)
|
||||
lurker.func = func
|
||||
lurker:RegisterEvent("ADDON_LOADED")
|
||||
lurker:RegisterEvent("VARIABLES_LOADED")
|
||||
lurker:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
lurker:SetScript("OnEvent",function()
|
||||
-- only run when config is available
|
||||
if event == "ADDON_LOADED" and not this.foundConfig then
|
||||
return
|
||||
elseif event == "VARIABLES_LOADED" then
|
||||
this.foundConfig = true
|
||||
do
|
||||
local lurker
|
||||
local pending = {}
|
||||
|
||||
local function ProcessPending()
|
||||
if not lurker.foundConfig then return end
|
||||
for i = table.getn(pending), 1, -1 do
|
||||
local hook = pending[i]
|
||||
if IsAddOnLoaded(hook.addon) or _G[hook.addon] then
|
||||
hook.func()
|
||||
table.remove(pending, i)
|
||||
end
|
||||
end
|
||||
if table.getn(pending) == 0 then
|
||||
lurker:UnregisterAllEvents()
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.api.HookAddonOrVariable(addon, func)
|
||||
if not lurker then
|
||||
lurker = CreateFrame("Frame", nil)
|
||||
lurker:SetScript("OnEvent", function()
|
||||
if event == "VARIABLES_LOADED" or event == "PLAYER_ENTERING_WORLD" then
|
||||
this.foundConfig = true
|
||||
end
|
||||
ProcessPending()
|
||||
end)
|
||||
end
|
||||
|
||||
if IsAddOnLoaded(addon) or _G[addon] then
|
||||
this:func()
|
||||
this:UnregisterAllEvents()
|
||||
end
|
||||
end)
|
||||
table.insert(pending, { addon = addon, func = func })
|
||||
lurker:RegisterEvent("ADDON_LOADED")
|
||||
lurker:RegisterEvent("VARIABLES_LOADED")
|
||||
lurker:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
ProcessPending()
|
||||
end
|
||||
end
|
||||
|
||||
-- [ QueueFunction ]
|
||||
@@ -708,24 +748,15 @@ function pfUI.api.CopyTable(src)
|
||||
end
|
||||
|
||||
-- [ Wipe Table ]
|
||||
-- Empties a table and returns it
|
||||
-- Empties a table and returns it.
|
||||
-- 'src' [table] the table that should be emptied.
|
||||
-- return: [table] the emptied table.
|
||||
-- Delegates to ClassicAPI's table.wipe, which also resets the Lua 5.0 getn
|
||||
-- length (luaL_setn(t,0)) so table.insert on a wiped table resumes at [1].
|
||||
-- Append to wiped arrays with table.insert -- NOT the t[table.getn(t)+1]=v
|
||||
-- idiom, which needs an unmanaged length counter and won't work here.
|
||||
function pfUI.api.wipe(src)
|
||||
-- notes: table.insert, table.remove will have undefined behavior
|
||||
-- when used on tables emptied this way because Lua removes nil
|
||||
-- entries from tables after an indeterminate time.
|
||||
-- Instead of table.insert(t,v) use t[table.getn(t)+1]=v as table.getn collapses nil entries.
|
||||
-- There are no issues with hash tables, t[k]=v where k is not a number behaves as expected.
|
||||
local mt = getmetatable(src) or {}
|
||||
if mt.__mode == nil or mt.__mode ~= "kv" then
|
||||
mt.__mode = "kv"
|
||||
src=setmetatable(src,mt)
|
||||
end
|
||||
for k in pairs(src) do
|
||||
src[k] = nil
|
||||
end
|
||||
return src
|
||||
return table.wipe(src)
|
||||
end
|
||||
|
||||
-- [ Load Movable ]
|
||||
@@ -983,16 +1014,34 @@ end
|
||||
|
||||
-- [ GetStringColor ]
|
||||
-- Queries the pfUI setting strings and extract its color codes
|
||||
-- returns r,g,b,a
|
||||
local color_cache = {}
|
||||
function pfUI.api.GetStringColor(colorstr)
|
||||
if not color_cache[colorstr] then
|
||||
local r, g, b, a = pfUI.api.strsplit(",", colorstr)
|
||||
color_cache[colorstr] = { r, g, b, a }
|
||||
-- returns r,g,b,a as strings
|
||||
local color_cache = setmetatable({}, {
|
||||
__index = function(t, k)
|
||||
local color = { pfUI.api.strsplit(",", k) }
|
||||
rawset(t, k, color)
|
||||
return color
|
||||
end
|
||||
})
|
||||
function pfUI.api.GetStringColor(colorstr)
|
||||
return unpack(color_cache[colorstr])
|
||||
end
|
||||
|
||||
-- [ GetStringColorObject ]
|
||||
-- Like GetStringColor, but returns a cached ColorMixin instead of raw values.
|
||||
-- The object is a shared per-string singleton, so treat it as read-only.
|
||||
-- returns a ColorMixin
|
||||
local color_object_cache = setmetatable({}, {
|
||||
__index = function(t, k)
|
||||
local r,g,b,a = pfUI.api.GetStringColor(k)
|
||||
local color = CreateColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
rawset(t, k, color)
|
||||
return color
|
||||
end
|
||||
})
|
||||
function pfUI.api.GetStringColorObject(colorstr)
|
||||
return color_object_cache[colorstr]
|
||||
end
|
||||
|
||||
-- [ rgbhex ]
|
||||
-- Returns color format from color info
|
||||
-- 'r' [table | number] color table or r color component
|
||||
@@ -1000,12 +1049,13 @@ end
|
||||
-- 'b' [number] optional b color component
|
||||
-- 'a' [number] optional alpha component
|
||||
-- returns color string in the form of '|caarrggbb'
|
||||
local _r, _g, _b, _a
|
||||
local rgbhex_cache = {}
|
||||
function pfUI.api.rgbhex(r, g, b, a)
|
||||
local _r, _g, _b, _a
|
||||
if type(r) == "table" then
|
||||
if r.r then
|
||||
_r, _g, _b, _a = r.r, r.g, r.b, (r.a or 1)
|
||||
elseif table.getn(r) >= 3 then
|
||||
elseif r[3] ~= nil then
|
||||
_r, _g, _b, _a = r[1], r[2], r[3], (r[4] or 1)
|
||||
end
|
||||
elseif tonumber(r) then
|
||||
@@ -1013,7 +1063,13 @@ function pfUI.api.rgbhex(r, g, b, a)
|
||||
end
|
||||
|
||||
if _r and _g and _b and _a then
|
||||
return CreateColor(_r, _g, _b, _a):GenerateHexColorMarkup()
|
||||
local key = ((Round(_r*255)*256 + Round(_g*255))*256 + Round(_b*255))*256 + Round(_a*255)
|
||||
local hex = rgbhex_cache[key]
|
||||
if not hex then
|
||||
hex = "|c" .. C_ColorUtil.GenerateTextColorCode({ r = _r, g = _g, b = _b, a = _a })
|
||||
rgbhex_cache[key] = hex
|
||||
end
|
||||
return hex
|
||||
end
|
||||
|
||||
return ""
|
||||
@@ -1566,6 +1622,16 @@ end
|
||||
-- 'arg1' [string]
|
||||
-- return object
|
||||
function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
|
||||
-- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren()
|
||||
-- below, and the traceback stops here — useless, since all callers share this
|
||||
-- line. pfUI shadows Lua's `error` (dropping the level arg and not throwing),
|
||||
-- so fold the caller frame in via debugstack to name the offending skin, then
|
||||
-- bail so we don't fall through and crash on frame:GetRegions() anyway.
|
||||
if type(frame) ~= "table" or not frame.GetRegions then
|
||||
error("GetNoNameObject: invalid parent frame\n" .. debugstack(2, 3, 0))
|
||||
return
|
||||
end
|
||||
|
||||
local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
|
||||
local arg2 = arg2 and gsub(arg2, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
|
||||
|
||||
|
||||
+61
-14
@@ -155,6 +155,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("appearance", "cd", "dynamicsize", "1")
|
||||
pfUI:UpdateConfig("appearance", "castbar", "castbarcolor", ".7,.7,.9,.8")
|
||||
pfUI:UpdateConfig("appearance", "castbar", "channelcolor", ".9,.9,.7,.8")
|
||||
pfUI:UpdateConfig("appearance", "castbar", "failcolor", "1,.2,.2,.8")
|
||||
pfUI:UpdateConfig("appearance", "castbar", "texture", "Interface\\AddOns\\pfUI\\img\\bar")
|
||||
pfUI:UpdateConfig("appearance", "infight", "screen", "0")
|
||||
pfUI:UpdateConfig("appearance", "infight", "aggro", "0")
|
||||
@@ -162,6 +163,8 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("appearance", "infight", "intensity", "16")
|
||||
pfUI:UpdateConfig("appearance", "bags", "unusable", "1")
|
||||
pfUI:UpdateConfig("appearance", "bags", "unusable_color", ".9,.2,.2,1")
|
||||
pfUI:UpdateConfig("appearance", "bags", "newitem", "1")
|
||||
pfUI:UpdateConfig("appearance", "bags", "newitem_color", "1,1,1,1")
|
||||
pfUI:UpdateConfig("appearance", "bags", "borderlimit", "1")
|
||||
pfUI:UpdateConfig("appearance", "bags", "borderonlygear", "0")
|
||||
pfUI:UpdateConfig("appearance", "bags", "fulltext", "1")
|
||||
@@ -172,6 +175,8 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("appearance", "bags", "bagrowlength", "10")
|
||||
pfUI:UpdateConfig("appearance", "bags", "bankrowlength", "10")
|
||||
pfUI:UpdateConfig("appearance", "bags", "autoSortOnOpen", "0")
|
||||
pfUI:UpdateConfig("appearance", "bags", "sortreverse", "0")
|
||||
pfUI:UpdateConfig("appearance", "bags", "sortprioreverse", "0")
|
||||
pfUI:UpdateConfig("appearance", "minimap", "size", "140")
|
||||
pfUI:UpdateConfig("appearance", "minimap", "arrowscale", "1")
|
||||
pfUI:UpdateConfig("appearance", "minimap", "zonetext", "off")
|
||||
@@ -196,6 +201,8 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("loot", nil, "rollannounce", "0")
|
||||
pfUI:UpdateConfig("loot", nil, "raritytimer", "1")
|
||||
|
||||
pfUI:UpdateConfig("loothistory", nil, "autoshow", "0")
|
||||
|
||||
pfUI:UpdateConfig("unitframes", nil, "disable", "0")
|
||||
pfUI:UpdateConfig("unitframes", nil, "pastel", "1")
|
||||
pfUI:UpdateConfig("unitframes", nil, "custom", "0")
|
||||
@@ -216,7 +223,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("unitframes", nil, "always2dportrait", "0")
|
||||
pfUI:UpdateConfig("unitframes", nil, "portraittexture", "1")
|
||||
pfUI:UpdateConfig("unitframes", nil, "layout", "default")
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangecheck", "0")
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangecheck", "1")
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangecheck_mode", "vanilla")
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangecheck_distance", "40")
|
||||
pfUI:UpdateConfig("unitframes", nil, "buffdetect", "0")
|
||||
@@ -227,8 +234,8 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("unitframes", nil, "druidmanaoffy", "0")
|
||||
pfUI:UpdateConfig("unitframes", nil, "druidmanaspace", "-3")
|
||||
pfUI:UpdateConfig("unitframes", nil, "druidmanatexture", "Interface\\AddOns\\pfUI\\img\\bar")
|
||||
pfUI:UpdateConfig("unitframes", nil, "druidmanatext", "1")
|
||||
|
||||
pfUI:UpdateConfig("unitframes", nil, "rangechecki", "4")
|
||||
pfUI:UpdateConfig("unitframes", nil, "combowidth", "6")
|
||||
pfUI:UpdateConfig("unitframes", nil, "comboheight", "6")
|
||||
pfUI:UpdateConfig("unitframes", nil, "swingtimerwidth", "200")
|
||||
@@ -379,6 +386,27 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("unitframes", "grouppet", "glowcombat", "0")
|
||||
pfUI:UpdateConfig("unitframes", "grouppet", "txthpright", "healthperc")
|
||||
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "portrait", "off")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "width", "50")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "height", "14")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "pheight", "0")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "buffs", "off")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "buffsize", "16")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "debuffs", "off")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "debuffsize", "16")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "faderange", "1")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "glowcombat", "0")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "txthpright", "healthperc")
|
||||
-- off by default; mirrors the raid grid layout when enabled
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "visible", "0")
|
||||
-- collapse: pack only pets that exist (from the roster snapshot) instead
|
||||
-- of mirroring every raid slot
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "collapse", "1")
|
||||
-- pet block has its own layout, independent of the raid grid
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "raidlayout", "8x5")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "raidpadding", "3")
|
||||
pfUI:UpdateConfig("unitframes", "raidpet", "raidfill", "VERTICAL")
|
||||
|
||||
pfUI:UpdateConfig("unitframes", "raid", "portrait", "off")
|
||||
pfUI:UpdateConfig("unitframes", "raid", "width", "50")
|
||||
pfUI:UpdateConfig("unitframes", "raid", "height", "26")
|
||||
@@ -397,6 +425,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("unitframes", "raid", "raidlayout", "8x5")
|
||||
pfUI:UpdateConfig("unitframes", "raid", "raidpadding", "3")
|
||||
pfUI:UpdateConfig("unitframes", "raid", "raidfill", "VERTICAL")
|
||||
pfUI:UpdateConfig("unitframes", "raid", "collapse", "0")
|
||||
pfUI:UpdateConfig("unitframes", "raid", "raidgrouplabel", "0")
|
||||
pfUI:UpdateConfig("unitframes", "raid", "grouplabelxoff", "0")
|
||||
pfUI:UpdateConfig("unitframes", "raid", "grouplabelyoff", "8")
|
||||
@@ -452,7 +481,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("unitframes", "ptarget", "txthpright", "none")
|
||||
pfUI:UpdateConfig("unitframes", "ptarget", "overhealperc", "10")
|
||||
|
||||
local ufs = { "player", "target", "focus", "focustarget", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback", "tttarget" }
|
||||
local ufs = { "player", "target", "focus", "focustarget", "group", "grouptarget", "grouppet", "raid", "raidpet", "ttarget", "pet", "ptarget", "fallback", "tttarget" }
|
||||
for _, unit in pairs(ufs) do
|
||||
pfUI:UpdateConfig("unitframes", unit, "selfdebuff", "0")
|
||||
pfUI:UpdateConfig("unitframes", unit, "visible", "1")
|
||||
@@ -525,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")
|
||||
@@ -624,6 +652,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("bars", nil, "pagemastershift", "0")
|
||||
pfUI:UpdateConfig("bars", nil, "pagemasterctrl", "0")
|
||||
pfUI:UpdateConfig("bars", nil, "druidstealth", "0")
|
||||
pfUI:UpdateConfig("bars", nil, "priestshadow", "0")
|
||||
pfUI:UpdateConfig("bars", nil, "showcastable", "1")
|
||||
pfUI:UpdateConfig("bars", nil, "glowrange", "1")
|
||||
pfUI:UpdateConfig("bars", nil, "rangecolor", "1,0.1,0.1,1")
|
||||
@@ -714,8 +743,11 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("castbar", "player", "txtleftoffy", "0")
|
||||
pfUI:UpdateConfig("castbar", "player", "showlag", "0")
|
||||
pfUI:UpdateConfig("castbar", "player", "showrank", "0")
|
||||
pfUI:UpdateConfig("castbar", "player", "mergetradeskill", "0")
|
||||
pfUI:UpdateConfig("castbar", "player", "txtrightoffx", "0")
|
||||
pfUI:UpdateConfig("castbar", "player", "txtrightoffy", "0")
|
||||
pfUI:UpdateConfig("castbar", "player", "namealign", "LEFT")
|
||||
pfUI:UpdateConfig("castbar", "player", "timealign", "RIGHT")
|
||||
pfUI:UpdateConfig("castbar", "target", "hide_pfui", "0")
|
||||
pfUI:UpdateConfig("castbar", "target", "width", "-1")
|
||||
pfUI:UpdateConfig("castbar", "target", "height", "-1")
|
||||
@@ -728,6 +760,8 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("castbar", "target", "showrank", "0")
|
||||
pfUI:UpdateConfig("castbar", "target", "txtrightoffx", "0")
|
||||
pfUI:UpdateConfig("castbar", "target", "txtrightoffy", "0")
|
||||
pfUI:UpdateConfig("castbar", "target", "namealign", "LEFT")
|
||||
pfUI:UpdateConfig("castbar", "target", "timealign", "RIGHT")
|
||||
pfUI:UpdateConfig("castbar", "focus", "hide_pfui", "0")
|
||||
pfUI:UpdateConfig("castbar", "focus", "width", "-1")
|
||||
pfUI:UpdateConfig("castbar", "focus", "height", "-1")
|
||||
@@ -740,6 +774,8 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("castbar", "focus", "showrank", "0")
|
||||
pfUI:UpdateConfig("castbar", "focus", "txtrightoffx", "0")
|
||||
pfUI:UpdateConfig("castbar", "focus", "txtrightoffy", "0")
|
||||
pfUI:UpdateConfig("castbar", "focus", "namealign", "LEFT")
|
||||
pfUI:UpdateConfig("castbar", "focus", "timealign", "RIGHT")
|
||||
pfUI:UpdateConfig("castbar", nil, "use_unitfonts", "0")
|
||||
|
||||
pfUI:UpdateConfig("tooltip", nil, "position", "chat")
|
||||
@@ -747,6 +783,11 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("tooltip", nil, "cursoroffset", "20")
|
||||
pfUI:UpdateConfig("tooltip", nil, "extguild", "1")
|
||||
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")
|
||||
@@ -817,7 +858,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("nameplates", nil, "selfdebuff", "0")
|
||||
pfUI:UpdateConfig("nameplates", nil, "showdebuffs_hostile", "1")
|
||||
pfUI:UpdateConfig("nameplates", nil, "showdebuffs_friendly", "0")
|
||||
pfUI:UpdateConfig("nameplates", nil, "guessdebuffs", "1")
|
||||
pfUI:UpdateConfig("nameplates", nil, "owndebuffs", "0")
|
||||
pfUI:UpdateConfig("nameplates", nil, "clickthrough", "0")
|
||||
pfUI:UpdateConfig("nameplates", nil, "rightclick", "1")
|
||||
pfUI:UpdateConfig("nameplates", nil, "clickthreshold", "0.5")
|
||||
@@ -917,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")
|
||||
@@ -924,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")
|
||||
@@ -1097,13 +1140,6 @@ function pfUI:MigrateConfig()
|
||||
end
|
||||
end
|
||||
|
||||
-- migrating rangecheck interval (> 3.2.2)
|
||||
if checkversion(3, 2, 2) then
|
||||
if tonumber(pfUI_config.unitframes.rangechecki) <= 1 then
|
||||
pfUI_config.unitframes.rangechecki = "2"
|
||||
end
|
||||
end
|
||||
|
||||
-- migrating legacy buff/debuff naming (> 3.5.0)
|
||||
if checkversion(3, 5, 0) then
|
||||
local unitframes = { "player", "target", "focus", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback" }
|
||||
@@ -1370,7 +1406,6 @@ function pfUI:MigrateConfig()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Remove "Show only own debuffs" from unitframes and nameplates
|
||||
-- (feature was removed; only Target Debuff Bar in buffwatch keeps it)
|
||||
if pfUI_config.nameplates then
|
||||
@@ -1386,5 +1421,17 @@ function pfUI:MigrateConfig()
|
||||
end
|
||||
end
|
||||
|
||||
pfUI_config.version = pfUI.version.string
|
||||
-- Stamp the version we've migrated to. Dev / git-cloned builds carry
|
||||
-- "@project-version@" in the toc, which pfUI.lua normalizes to "dev" —
|
||||
-- writing that here would have checkversion() parse it back as 0/0/0 on
|
||||
-- the next /reload and re-fire every migration block, clobbering user
|
||||
-- toggles (notably the >3.6.1 buff migration that rewrites
|
||||
-- buffs.{buffs,debuffs,weapons} from the deprecated global.hide{,w}buff
|
||||
-- knobs). Stamp a far-future sentinel instead so checkversion() can't
|
||||
-- flip true again on subsequent loads.
|
||||
if pfUI.version.string == "dev" then
|
||||
pfUI_config.version = "999.999.999"
|
||||
else
|
||||
pfUI_config.version = pfUI.version.string
|
||||
end
|
||||
end
|
||||
+275
-67
@@ -21,8 +21,7 @@ do -- statusbars
|
||||
|
||||
local handlers = {
|
||||
["DisplayValue"] = function(self, val)
|
||||
val = val > self.max and self.max or val
|
||||
val = val < self.min and self.min or val
|
||||
val = Clamp(val, self.min, self.max)
|
||||
|
||||
-- remove animation queue
|
||||
if val == self.val_ then
|
||||
@@ -38,8 +37,7 @@ do -- statusbars
|
||||
point = height / (self.max - self.min) * (val - self.min)
|
||||
|
||||
-- keep values in limits
|
||||
point = math.min(height, point)
|
||||
point = math.max(0, point)
|
||||
point = Clamp(point, 0, height)
|
||||
|
||||
-- set point to zero if value and max is zero
|
||||
if val == 0 then point = 0 end
|
||||
@@ -57,8 +55,7 @@ do -- statusbars
|
||||
point = width / (self.max - self.min) * (val - self.min)
|
||||
|
||||
-- keep values in limits
|
||||
point = math.min(width, point)
|
||||
point = math.max(0, point)
|
||||
point = Clamp(point, 0, width)
|
||||
|
||||
-- set point to zero if value and max is zero
|
||||
if val == 0 then point = 0 end
|
||||
@@ -138,15 +135,8 @@ do -- statusbars
|
||||
end
|
||||
|
||||
do -- dropdown
|
||||
local _, class = UnitClass("player")
|
||||
local color = PFUI_CLASS_COLORS[class]
|
||||
|
||||
local function ListEntryOnShow()
|
||||
if this.parent.id == this.id then
|
||||
this.icon:Show()
|
||||
else
|
||||
this.icon:Hide()
|
||||
end
|
||||
this.icon:SetShown(this.parent.id == this.id)
|
||||
end
|
||||
|
||||
local function ListEntryOnClick()
|
||||
@@ -288,8 +278,7 @@ do -- dropdown
|
||||
|
||||
frame.icon = frame:CreateTexture(nil, "OVERLAY")
|
||||
frame.icon:SetPoint("RIGHT", frame, "RIGHT", -2, 0)
|
||||
frame.icon:SetHeight(16)
|
||||
frame.icon:SetWidth(16)
|
||||
frame.icon:SetSize(16, 16)
|
||||
frame.icon:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
|
||||
|
||||
frame.text = frame:CreateFontString(nil, "OVERLAY")
|
||||
@@ -329,8 +318,7 @@ do -- dropdown
|
||||
|
||||
local button = CreateFrame("Button", nil, frame)
|
||||
button:SetPoint("RIGHT", frame, "RIGHT", -2, 0)
|
||||
button:SetWidth(16)
|
||||
button:SetHeight(16)
|
||||
button:SetSize(16, 16)
|
||||
button:SetScript("OnClick", ListButtonOnClick)
|
||||
SkinArrowButton(button, "down")
|
||||
button.icon:SetVertexColor(1,.9,.1)
|
||||
@@ -380,8 +368,7 @@ function pfUI.api.CreateTabChild(self, title, bwidth, bheight, bottom, static)
|
||||
end
|
||||
|
||||
-- set dimensions
|
||||
b:SetHeight(button_height)
|
||||
b:SetWidth(button_width)
|
||||
b:SetSize(button_width, button_height)
|
||||
b:SetID(childcount)
|
||||
|
||||
if not self.align or self.align == "LEFT" then
|
||||
@@ -516,13 +503,7 @@ function pfUI.api.CreateScrollFrame(name, parent)
|
||||
local max = f:GetVerticalScrollRange()
|
||||
local new = current - step
|
||||
|
||||
if new >= max then
|
||||
f:SetVerticalScroll(max)
|
||||
elseif new <= 0 then
|
||||
f:SetVerticalScroll(0)
|
||||
else
|
||||
f:SetVerticalScroll(new)
|
||||
end
|
||||
f:SetVerticalScroll(Clamp(new, 0, max))
|
||||
|
||||
f:UpdateScrollState()
|
||||
end
|
||||
@@ -539,8 +520,7 @@ function pfUI.api.CreateScrollChild(name, parent)
|
||||
local f = CreateFrame("Frame", name, parent)
|
||||
|
||||
-- dummy values required
|
||||
f:SetWidth(1)
|
||||
f:SetHeight(1)
|
||||
f:SetSize(1, 1)
|
||||
f:SetAllPoints(parent)
|
||||
|
||||
parent:SetScrollChild(f)
|
||||
@@ -569,7 +549,7 @@ end
|
||||
-- 'frame' [frame] the modelframe that should be used
|
||||
function pfUI.api.EnableClickRotate(frame)
|
||||
frame:EnableMouse(true)
|
||||
HookScript(frame, "OnUpdate", function()
|
||||
frame:HookScript("OnUpdate", function()
|
||||
if this.rotate then
|
||||
local x,_ = GetCursorPosition()
|
||||
if this.curx > x then
|
||||
@@ -582,14 +562,14 @@ function pfUI.api.EnableClickRotate(frame)
|
||||
end
|
||||
end)
|
||||
|
||||
HookScript(frame, "OnMouseDown", function()
|
||||
frame:HookScript("OnMouseDown", function()
|
||||
if arg1 == "LeftButton" then
|
||||
this.rotate = true
|
||||
this.curx, this.cury = GetCursorPosition()
|
||||
end
|
||||
end)
|
||||
|
||||
HookScript(frame, "OnMouseUp", function()
|
||||
frame:HookScript("OnMouseUp", function()
|
||||
this.rotate, this.curx, this.cury = nil, nil, nil
|
||||
end)
|
||||
end
|
||||
@@ -607,15 +587,13 @@ 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
|
||||
local _, class = UnitClass("player")
|
||||
cr, cg, cb = GetClassColor(class)
|
||||
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB()
|
||||
end
|
||||
|
||||
frame.cr, frame.cg, frame.cb = cr, cg, cb, ca
|
||||
frame.rr, frame.rg, frame.rb, frame.ra = GetStringColor(pfUI_config.appearance.border.color)
|
||||
|
||||
if not frame.pfEnterLeave then
|
||||
if not frame.HookScript then frame.HookScript = HookScript end
|
||||
local enter, leave = frame:GetScript("OnEnter"), frame:GetScript("OnLeave")
|
||||
|
||||
if enter then
|
||||
@@ -654,8 +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
|
||||
local _, class = UnitClass("player")
|
||||
cr, cg, cb = GetClassColor(class)
|
||||
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB()
|
||||
end
|
||||
pfUI.api.CreateBackdrop(b, nil, true)
|
||||
b:SetNormalTexture("")
|
||||
@@ -703,8 +680,7 @@ function pfUI.api.SkinCollapseButton(button, all)
|
||||
|
||||
b.icon = _G[name] or CreateFrame("Button", name, b)
|
||||
if all then size = 14 end
|
||||
b.icon:SetWidth(size)
|
||||
b.icon:SetHeight(size)
|
||||
b.icon:SetSize(size, size)
|
||||
b.icon:SetPoint("LEFT", 2, 2)
|
||||
CreateBackdrop(b.icon)
|
||||
b.icon.text = b.icon:CreateFontString(nil, "OVERLAY")
|
||||
@@ -733,12 +709,10 @@ end
|
||||
function pfUI.api.SkinRotateButton(button)
|
||||
pfUI.api.CreateBackdrop(button)
|
||||
|
||||
local _, class = UnitClass("player")
|
||||
local color = PFUI_CLASS_COLORS[class]
|
||||
local cr, cg, cb = color.r , color.g, color.b
|
||||
local cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB()
|
||||
|
||||
button:SetWidth(button:GetWidth() - 18)
|
||||
button:SetHeight(button:GetHeight() - 18)
|
||||
local btnW, btnH = button:GetSize()
|
||||
button:SetSize(btnW - 18, btnH - 18)
|
||||
|
||||
button:GetNormalTexture():SetTexCoord(0.3, 0.29, 0.3, 0.65, 0.69, 0.29, 0.69, 0.65)
|
||||
button:GetPushedTexture():SetTexCoord(0.3, 0.29, 0.3, 0.65, 0.69, 0.29, 0.69, 0.65)
|
||||
@@ -760,8 +734,7 @@ function pfUI.api.SkinCloseButton(button, parentFrame, offsetX, offsetY)
|
||||
|
||||
SkinButton(button, 1, .25, .25)
|
||||
|
||||
button:SetWidth(15)
|
||||
button:SetHeight(15)
|
||||
button:SetSize(15, 15)
|
||||
|
||||
if parentFrame then
|
||||
button:ClearAllPoints()
|
||||
@@ -788,8 +761,7 @@ function pfUI.api.SkinArrowButton(button, dir, size)
|
||||
button:SetDisabledTexture(nil)
|
||||
|
||||
if size then
|
||||
button:SetWidth(size)
|
||||
button:SetHeight(size)
|
||||
button:SetSize(size, size)
|
||||
end
|
||||
|
||||
if not button.icon then
|
||||
@@ -844,7 +816,6 @@ function pfUI.api.SkinScrollbar(frame, always)
|
||||
-- always show parent frame
|
||||
if always then
|
||||
RunOOC(function()
|
||||
if not parent.HookScript then parent.HookScript = HookScript end
|
||||
parent:HookScript("OnHide", function() this:Show() end)
|
||||
end)
|
||||
end
|
||||
@@ -896,8 +867,7 @@ function pfUI.api.SkinCheckbox(frame, size)
|
||||
frame:SetPushedTexture("")
|
||||
frame:SetHighlightTexture("")
|
||||
if size then
|
||||
frame:SetWidth(size)
|
||||
frame:SetHeight(size)
|
||||
frame:SetSize(size, size)
|
||||
end
|
||||
CreateBackdrop(frame)
|
||||
SetAllPointsOffset(frame.backdrop, frame, 4)
|
||||
@@ -925,8 +895,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
|
||||
CreateBackdrop(button)
|
||||
|
||||
button.backdrop:ClearAllPoints()
|
||||
button.backdrop:SetWidth(18)
|
||||
button.backdrop:SetHeight(18)
|
||||
button.backdrop:SetSize(18, 18)
|
||||
button.backdrop:SetPoint("RIGHT", frame.backdrop, "RIGHT", -2, 0)
|
||||
|
||||
if not button.icon then
|
||||
@@ -938,9 +907,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
|
||||
end
|
||||
|
||||
if not cr or not cg or not cb then
|
||||
local _, class = UnitClass("player")
|
||||
local color = PFUI_CLASS_COLORS[class]
|
||||
cr, cg, cb = color.r , color.g, color.b
|
||||
cr, cg, cb = PFUI_CLASS_COLORS[UnitClassBase('player')]:GetRGB()
|
||||
end
|
||||
|
||||
SetHighlight(button, cr, cg, cb)
|
||||
@@ -1110,8 +1077,7 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
|
||||
-- buttons
|
||||
question.yes = CreateFrame("Button", "pfQuestionDialogYes", question, "UIPanelButtonTemplate")
|
||||
pfUI.api.SkinButton(question.yes)
|
||||
question.yes:SetWidth(100)
|
||||
question.yes:SetHeight(22)
|
||||
question.yes:SetSize(100, 22)
|
||||
question.yes:SetText(yescap)
|
||||
question.yes:SetScript("OnClick", function()
|
||||
if yes then yes() end
|
||||
@@ -1126,8 +1092,7 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
|
||||
|
||||
question.no = CreateFrame("Button", "pfQuestionDialogNo", question, "UIPanelButtonTemplate")
|
||||
pfUI.api.SkinButton(question.no)
|
||||
question.no:SetWidth(100)
|
||||
question.no:SetHeight(22)
|
||||
question.no:SetSize(100, 22)
|
||||
question.no:SetText(nocap)
|
||||
question.no:SetScript("OnClick", function()
|
||||
if no then no() end
|
||||
@@ -1143,8 +1108,7 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
|
||||
question.close = CreateFrame("Button", "pfQuestionDialogClose", question)
|
||||
question.close:SetPoint("TOPRIGHT", -border, -border)
|
||||
pfUI.api.CreateBackdrop(question.close)
|
||||
question.close:SetHeight(10)
|
||||
question.close:SetWidth(10)
|
||||
question.close:SetSize(10, 10)
|
||||
question.close.texture = question.close:CreateTexture("pfQuestionDialogCloseTex")
|
||||
question.close.texture:SetTexture(pfUI.media["img:close"])
|
||||
question.close.texture:ClearAllPoints()
|
||||
@@ -1172,10 +1136,9 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
|
||||
local width = 200
|
||||
|
||||
-- delay the auto sizing, to make sure the font rendering happened
|
||||
question:SetScript("OnUpdate", function()
|
||||
RunNextFrame(function()
|
||||
if question.text:GetStringWidth() > width then width = question.text:GetStringWidth() end
|
||||
question:SetWidth( width + 2*padding)
|
||||
this:SetScript("OnUpdate", nil)
|
||||
question:SetWidth(width + 2*padding)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -1247,9 +1210,8 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
|
||||
infobox.duration = time
|
||||
infobox.lastshow = GetTime()
|
||||
|
||||
infobox:SetWidth(infobox.text:GetStringWidth() + 50)
|
||||
infobox:SetSize(infobox.text:GetStringWidth() + 50, height)
|
||||
infobox:SetParent(parent)
|
||||
infobox:SetHeight(height)
|
||||
|
||||
infobox:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
infobox:Show()
|
||||
@@ -1284,3 +1246,249 @@ function pfUI.api.SkinMoneyInputFrame(frame)
|
||||
copperIcon:ClearAllPoints()
|
||||
copperIcon:SetPoint("LEFT", copper_editbox, "RIGHT", 2, 0)
|
||||
end
|
||||
|
||||
-- Shared icon picker widget. Builds a name field, a "currently selected"
|
||||
-- preview, and a 10x8 icon grid backed by IconDataProviderMixin with an
|
||||
-- All/Spells/Items filter and a name search. Used by the macro icon picker
|
||||
-- and the equipment manager's name/icon popup. The widgets attach to
|
||||
-- `parent`; the caller owns the surrounding frame, its OK/Cancel buttons,
|
||||
-- and the save flow. Selection is tracked by texture PATH (not index) so it
|
||||
-- survives filter changes -- a spell icon you picked still saves after you
|
||||
-- switch the filter to "Items" and it drops out of the visible grid.
|
||||
--
|
||||
-- name global-name prefix for the scroll frame and editboxes
|
||||
-- parent the popup frame to attach the widgets to
|
||||
-- extraType IconDataProviderExtraType.* seed (Spellbook / Equipment)
|
||||
-- nameLabelText header shown above the name field
|
||||
--
|
||||
-- Returns a handle:
|
||||
-- .editbox name EditBox (caller wires enter/escape)
|
||||
-- .search search EditBox
|
||||
-- .GetIcon() current selected texture path
|
||||
-- .SetIcon(path) set selection (nil -> question mark)
|
||||
-- .Refresh() ensure provider, redraw grid + preview
|
||||
-- .SetIconAreaShown(shown) show/hide the grid, filter, search, and labels
|
||||
function pfUI.api.CreateIconPicker(name, parent, extraType, nameLabelText)
|
||||
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
|
||||
|
||||
local ICON_GRID_COLS = 10
|
||||
local ICON_GRID_ROWS = 8
|
||||
local ICON_BTN_SIZE = 36
|
||||
local ICON_BTN_PAD = 6
|
||||
|
||||
local provider = nil
|
||||
local selectedIconPath = QUESTION_MARK
|
||||
local currentFilter = "all"
|
||||
local searchText = ""
|
||||
local filtered = nil -- provider indices matching the search, or nil when empty
|
||||
local RefreshIconGrid, RebuildFilter
|
||||
|
||||
local function EnsureProvider()
|
||||
if not provider then
|
||||
provider = CreateAndInitFromMixin(IconDataProviderMixin, extraType)
|
||||
end
|
||||
end
|
||||
|
||||
-- Normalize a texture to an uppercase basename so matching works across
|
||||
-- sources: GetMacroInfo / stored set icons return "Interface\Icons\<name>"
|
||||
-- while provider paths are uppercase-prefixed for base icons and
|
||||
-- mixed-case for spellbook extras.
|
||||
local function IconKey(path)
|
||||
if type(path) ~= "string" then return path end
|
||||
return string.gsub(string.upper(path), "^.*[\\/]", "")
|
||||
end
|
||||
|
||||
parent.nameLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
parent.nameLabel:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -20)
|
||||
parent.nameLabel:SetText(nameLabelText)
|
||||
|
||||
parent.editbox = CreateFrame("EditBox", name.."Name", parent, "InputBoxTemplate")
|
||||
parent.editbox:SetSize(280, 20)
|
||||
parent.editbox:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -38)
|
||||
parent.editbox:SetAutoFocus(false)
|
||||
parent.editbox:SetMaxLetters(16)
|
||||
CreateBackdrop(parent.editbox)
|
||||
|
||||
parent.selectedLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
parent.selectedLabel:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -14)
|
||||
parent.selectedLabel:SetText(ICON_SELECTION_TITLE_CURRENT)
|
||||
parent.selectedLabel:SetTextColor(1, 0.82, 0)
|
||||
|
||||
parent.selectedPreview = CreateFrame("Frame", nil, parent)
|
||||
parent.selectedPreview:SetSize(42, 42)
|
||||
parent.selectedPreview:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -30)
|
||||
CreateBackdrop(parent.selectedPreview)
|
||||
parent.selectedPreview.tex = parent.selectedPreview:CreateTexture(nil, "ARTWORK")
|
||||
parent.selectedPreview.tex:SetAllPoints(parent.selectedPreview)
|
||||
parent.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
parent.iconLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
parent.iconLabel:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -86)
|
||||
parent.iconLabel:SetText(MACRO_POPUP_CHOOSE_ICON)
|
||||
|
||||
-- Icon grid + scroll
|
||||
local iconScroll = CreateFrame("ScrollFrame", name.."Scroll", parent, "FauxScrollFrameTemplate")
|
||||
iconScroll:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -116)
|
||||
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
||||
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
||||
|
||||
local scrollbar = _G[name.."ScrollScrollBar"]
|
||||
scrollbar:ClearAllPoints()
|
||||
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
|
||||
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
|
||||
SkinScrollbar(scrollbar)
|
||||
|
||||
-- Filter dropdown: All / Spells / Items
|
||||
local filterDropdown = CreateFrame("Frame", name.."Filter", parent, "UIDropDownMenuTemplate")
|
||||
filterDropdown:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, -80)
|
||||
local function ApplyFilter(value)
|
||||
currentFilter = value
|
||||
UIDropDownMenu_SetSelectedValue(filterDropdown, value)
|
||||
if provider then
|
||||
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
|
||||
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
|
||||
else provider:SetIconTypes(nil) end
|
||||
RebuildFilter()
|
||||
RefreshIconGrid()
|
||||
end
|
||||
end
|
||||
UIDropDownMenu_Initialize(filterDropdown, function()
|
||||
local info
|
||||
info = {}; info.text = ICON_FILTER_ALL; info.value = "all"
|
||||
info.func = function() ApplyFilter("all") end
|
||||
info.checked = currentFilter == "all"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
info = {}; info.text = ICON_FILTER_SPELL; info.value = "spells"
|
||||
info.func = function() ApplyFilter("spells") end
|
||||
info.checked = currentFilter == "spells"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
info = {}; info.text = ICON_FILTER_ITEM; info.value = "items"
|
||||
info.func = function() ApplyFilter("items") end
|
||||
info.checked = currentFilter == "items"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
end)
|
||||
UIDropDownMenu_SetWidth(120, filterDropdown)
|
||||
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
|
||||
SkinDropDown(filterDropdown)
|
||||
|
||||
local iconButtons = {}
|
||||
for r = 1, ICON_GRID_ROWS do
|
||||
for c = 1, ICON_GRID_COLS do
|
||||
local i = (r - 1) * ICON_GRID_COLS + c
|
||||
local btn = CreateFrame("Button", nil, parent)
|
||||
btn:SetSize(ICON_BTN_SIZE, ICON_BTN_SIZE)
|
||||
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
|
||||
CreateBackdrop(btn)
|
||||
btn.texture = btn:CreateTexture(nil, "ARTWORK")
|
||||
btn.texture:SetAllPoints(btn)
|
||||
btn.texture:SetTexCoord(.08, .92, .08, .92)
|
||||
btn:SetScript("OnClick", function()
|
||||
if this.iconIndex and provider then
|
||||
local path = provider:GetIconByIndex(this.iconIndex)
|
||||
if path then selectedIconPath = path end
|
||||
RefreshIconGrid()
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnEnter", function()
|
||||
if not this.iconIndex or not provider then return end
|
||||
local path = provider:GetIconByIndex(this.iconIndex)
|
||||
if type(path) == "string" then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText(IconKey(path), 1, 1, 1)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnLeave", GameTooltip_Hide)
|
||||
iconButtons[i] = btn
|
||||
end
|
||||
end
|
||||
|
||||
-- Rebuild the search-filtered index list (provider indices whose icon
|
||||
-- basename contains the search text); nil when the search box is empty.
|
||||
function RebuildFilter()
|
||||
EnsureProvider()
|
||||
if searchText == "" then
|
||||
filtered = nil
|
||||
return
|
||||
end
|
||||
filtered = {}
|
||||
for i = 1, provider:GetNumIcons() do
|
||||
local path = provider:GetIconByIndex(i)
|
||||
if type(path) == "string" and string.find(IconKey(path), searchText, 1, true) then
|
||||
table.insert(filtered, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function RefreshIconGrid()
|
||||
EnsureProvider()
|
||||
local numIcons = filtered and table.getn(filtered) or provider:GetNumIcons()
|
||||
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
|
||||
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
|
||||
local offset = FauxScrollFrame_GetOffset(iconScroll)
|
||||
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
|
||||
local listIdx = i + offset * ICON_GRID_COLS
|
||||
local btn = iconButtons[i]
|
||||
if listIdx <= numIcons then
|
||||
btn:Show()
|
||||
local providerIdx = filtered and filtered[listIdx] or listIdx
|
||||
btn.iconIndex = providerIdx
|
||||
local path = provider:GetIconByIndex(providerIdx)
|
||||
btn.texture:SetTexture(path)
|
||||
if IconKey(path) == IconKey(selectedIconPath) then
|
||||
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
|
||||
else
|
||||
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
|
||||
end
|
||||
else
|
||||
btn:Hide()
|
||||
btn.iconIndex = nil
|
||||
end
|
||||
end
|
||||
parent.selectedPreview.tex:SetTexture(selectedIconPath)
|
||||
end
|
||||
|
||||
iconScroll:SetScript("OnVerticalScroll", function()
|
||||
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() RefreshIconGrid() end)
|
||||
end)
|
||||
|
||||
-- Search box (bottom-left) filters the grid by icon name.
|
||||
parent.searchLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
parent.searchLabel:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 16, 18)
|
||||
parent.searchLabel:SetText(SEARCH)
|
||||
|
||||
parent.search = CreateFrame("EditBox", name.."Search", parent, "InputBoxTemplate")
|
||||
parent.search:SetSize(180, 20)
|
||||
parent.search:SetPoint("LEFT", parent.searchLabel, "RIGHT", 10, 0)
|
||||
parent.search:SetAutoFocus(false)
|
||||
CreateBackdrop(parent.search)
|
||||
parent.search:SetScript("OnEscapePressed", function() this:ClearFocus() end)
|
||||
parent.search:SetScript("OnTextChanged", function()
|
||||
searchText = strupper(strtrim(this:GetText()))
|
||||
RebuildFilter()
|
||||
scrollbar:SetValue(0)
|
||||
RefreshIconGrid()
|
||||
end)
|
||||
|
||||
-- Release the provider when the popup closes so its icon cache GCs.
|
||||
parent:HookScript("OnHide", function()
|
||||
if provider then provider:Release(); provider = nil end
|
||||
end)
|
||||
|
||||
local handle = { editbox = parent.editbox, search = parent.search }
|
||||
function handle.GetIcon() return selectedIconPath end
|
||||
function handle.SetIcon(path) selectedIconPath = path or QUESTION_MARK end
|
||||
function handle.Refresh() RefreshIconGrid() end
|
||||
function handle.SetIconAreaShown(shown)
|
||||
local method = shown and "Show" or "Hide"
|
||||
iconScroll[method](iconScroll)
|
||||
for _, b in ipairs(iconButtons) do b[method](b) end
|
||||
parent.iconLabel[method](parent.iconLabel)
|
||||
parent.selectedLabel[method](parent.selectedLabel)
|
||||
parent.selectedPreview[method](parent.selectedPreview)
|
||||
filterDropdown[method](filterDropdown)
|
||||
parent.searchLabel[method](parent.searchLabel)
|
||||
parent.search[method](parent.search)
|
||||
end
|
||||
return handle
|
||||
end
|
||||
|
||||
+328
-727
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,6 @@
|
||||
setfenv(1, pfUI:GetEnvironment())
|
||||
|
||||
-- [[ Constants ]]--
|
||||
CASTBAR_EVENT_CAST_DELAY = "SPELLCAST_DELAYED"
|
||||
CASTBAR_EVENT_CHANNEL_DELAY = "SPELLCAST_CHANNEL_UPDATE"
|
||||
CASTBAR_EVENT_CAST_START = "SPELLCAST_START"
|
||||
CASTBAR_EVENT_CHANNEL_START = "SPELLCAST_CHANNEL_START"
|
||||
|
||||
EVENTS_MINIMAP_ZONE_UPDATE = {"PLAYER_ENTERING_WORLD", "MINIMAP_ZONE_CHANGED"}
|
||||
|
||||
MICRO_BUTTONS = {
|
||||
@@ -30,45 +25,8 @@ PLAYER_BUFF_START_ID = -1
|
||||
|
||||
ACTIONBAR_SECURE_TEMPLATE_BAR = nil
|
||||
ACTIONBAR_SECURE_TEMPLATE_BUTTON = nil
|
||||
UNITFRAME_SECURE_TEMPLATE = nil
|
||||
|
||||
--[[ Vanilla API Extensions ]]--
|
||||
function hooksecurefunc(tbl, name, func, prepend)
|
||||
if type(tbl) == "string" then
|
||||
prepend, func, name, tbl = func, name, tbl, _G
|
||||
end
|
||||
|
||||
if not tbl or not tbl[name] then return end
|
||||
|
||||
pfUI.hooks[tostring(func)] = {}
|
||||
pfUI.hooks[tostring(func)]["old"] = tbl[name]
|
||||
pfUI.hooks[tostring(func)]["new"] = func
|
||||
|
||||
if prepend then
|
||||
pfUI.hooks[tostring(func)]["function"] = function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
|
||||
pfUI.hooks[tostring(func)]["new"](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
|
||||
return pfUI.hooks[tostring(func)]["old"](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
|
||||
end
|
||||
else
|
||||
pfUI.hooks[tostring(func)]["function"] = function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
|
||||
local ok, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16 = pcall(pfUI.hooks[tostring(func)]["old"], a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
|
||||
if not ok then return end
|
||||
pfUI.hooks[tostring(func)]["new"](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
|
||||
return r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16
|
||||
end
|
||||
end
|
||||
|
||||
tbl[name] = pfUI.hooks[tostring(func)]["function"]
|
||||
end
|
||||
|
||||
do -- GetItemInfo
|
||||
local name, link, rarity, minlevel, itype, isubtype, stack
|
||||
function GetItemInfo(item)
|
||||
if not item then return end
|
||||
name, link, rarity, minlevel, itype, isubtype, stack = _G.GetItemInfo(item)
|
||||
return name, link, rarity, nil, minlevel, itype, isubtype, stack
|
||||
end
|
||||
end
|
||||
|
||||
do -- RunMacroText
|
||||
local obj = { ["GetText"] = function(self) return self.text end }
|
||||
|
||||
Vendored
-3685
File diff suppressed because it is too large
Load Diff
Vendored
-3701
File diff suppressed because it is too large
Load Diff
Vendored
-3688
File diff suppressed because it is too large
Load Diff
Vendored
-3681
File diff suppressed because it is too large
Load Diff
Vendored
-3617
File diff suppressed because it is too large
Load Diff
Vendored
-3691
File diff suppressed because it is too large
Load Diff
Vendored
-3692
File diff suppressed because it is too large
Load Diff
Vendored
-72
@@ -1,20 +1,3 @@
|
||||
-- this table is added in later expansions
|
||||
CLASS_SORT_ORDER = CLASS_SORT_ORDER or {
|
||||
"WARRIOR",
|
||||
"DEATHKNIGHT",
|
||||
"PALADIN",
|
||||
"MONK",
|
||||
"PRIEST",
|
||||
"SHAMAN",
|
||||
"DRUID",
|
||||
"ROGUE",
|
||||
"MAGE",
|
||||
"WARLOCK",
|
||||
"HUNTER",
|
||||
"DEMONHUNTER",
|
||||
"UNKNOWN",
|
||||
}
|
||||
|
||||
CLASS_ICON_TCOORDS = CLASS_ICON_TCOORDS or {
|
||||
["WARRIOR"] = {0, 0.25, 0, 0.25},
|
||||
["MAGE"] = {0.25, 0.49609375, 0, 0.25},
|
||||
@@ -2417,58 +2400,3 @@ pfSellData = {
|
||||
[23725]=2600, [23727]=2600, [23728]=2600, [23743]=11, [24071]=393470, [24101]=400000,
|
||||
[24102]=400000, [24222]=221054, [24231]=211, [24232]=900, [24281]=1210, [24282]=18250,
|
||||
[24283]=152345, [24358]=365815 }
|
||||
pfMapOverlayData = {
|
||||
["Alterac"] = {"CHILLWINDPOINT:308:250:660:261", "CORRAHNSDAGGER:173:277:411:391", "CRUSHRIDGEHOLD:269:230:339:167:283:0", "DALARAN:277:283:39:275", "DANDREDSFOLD:273:224:282:0", "GALLOWSCORNER:183:185:415:287", "GAVINSNAZE:140:161:235:486", "GROWLESSCAVE:175:156:327:382", "LORDAMEREINTERNMENTCAMP:315:255:55:413", "MISTYSHORE:204:268:207:139", "RUINSOFALTERAC:252:249:274:201", "RAVENHOLDTMANOR:217:165:737:452", "SOFERASNAZE:241:312:469:311", "STRAHNBRAD:355:289:555:110", "THEHEADLAND:142:187:325:481", "THEUPLANDS:219:183:470:84",},
|
||||
["AlteracValley"] = {"DUNBALDAR:254:228:356:18:3299:3318", "FROSTWOLFKEEP:220:278:408:382:3299:3318", "ICEBLOODGARRISON:286:288:343:179:3299:3318",},
|
||||
["Arathi"] = {"BOULDERFISTHALL:190:214:442:371", "BOULDERGOR:222:214:244:167", "CIRCLEOFEASTBINDING:140:213:568:125", "CIRCLEOFINNERBINDING:186:164:302:321", "CIRCLEOFOUTERBINDING:149:137:429:301", "CIRCLEOFWESTBINDING:156:189:152:71", "DABYRIESFARMSTEAD:160:171:482:197", "FALDIRSCOVE:247:197:180:428", "FARWELLSTEAD:112:128:267:31", "GOSHEKFARM:208:177:539:284", "HAMMERFALL:182:235:665:128:318:0", "NORTHFOLDMANOR:202:205:204:119", "REFUGEPOINT:157:189:380:214", "RUINSOFZULRASAZ:160:160:346:29", "STROMGARDEKEEP:220:213:122:295", "THANDOLSPAN:175:198:369:424", "THORADINSWALL:173:225:103:148", "WITHERBARKVILLAGE:197:194:566:339", "WILDTUSKVILLAGE:183:102:379:145",},
|
||||
["Ashenvale"] = {"ASTRANAAR:190:173:281:259", "BOUGHSHADOW:140:190:862:157", "FALLENSKYLAKE:219:190:555:433:432:0", "FELFIREHILL:241:244:720:349", "FIRESCARSHRINE:153:161:194:332", "FORESTSONG:162:142:817:232", "IRISLAKE:185:190:401:226", "LAKEFALATHIM:115:181:138:144", "MAESTRASPOST:198:290:216:46", "MYSTRALLAKE:262:228:363:354:420:2359", "NIGHTRUN:212:244:604:263:431:0", "RAYNEWOODRETREAT:165:235:527:243", "SATYRNAAR:163:172:702:230", "THEHOWLINGVALE:196:172:471:147", "THERUINSOFSTARDUST:141:135:268:382", "THESHRINEOFAESSINA:205:179:113:268", "THEZORAMSTRAND:228:235:26:33", "THISTLEFURVILLAGE:243:184:210:164", "WARSONGLUMBERCAMP:185:146:804:319",},
|
||||
["Aszhara"] = {"BAYOFSTORMS:257:285:486:208", "BITTERREACHES:231:173:651:46", "FORLORNRIDGE:210:247:198:373", "HALDARRENCAMPMENT:191:138:83:339", "JAGGEDREEF:556:153:373:9", "LAKEMENNAR:301:184:302:437", "LEGASHENCAMPMENT:224:131:484:49", "RAVENCRESTMONUMENT:227:115:559:506", "RUINSOFELDARATH:251:268:246:228", "SHADOWSONGSHRINE:213:170:43:428", "SOUTHRIDGEBEACH:355:207:397:360", "TEMPLEOFARKKORAN:177:187:689:160", "THALASSIANBASECAMP:224:144:507:126", "THERUINEDREACHES:384:116:403:549", "THESHATTEREDSTRAND:148:199:412:202", "TIMBERMAWHOLD:209:194:258:175", "TOWEROFELDARA:107:145:824:114", "URSOLAN:219:212:327:94", "VALORMOK:201:158:93:236",},
|
||||
["Badlands"] = {"AGMONDSEND:249:256:354:396", "ANGORFORTRESS:186:241:327:101", "APOCRYPHANSREST:251:195:20:315", "CAMPBOFF:195:207:506:361", "CAMPCAGG:247:205:15:432:347:0", "CAMPKOSH:214:211:556:56", "CRYSTALLINEPINNACLE:140:155:609:337", "DUSTWINDGULCH:229:190:506:216", "HAMMERTOESDIGSITE:185:177:455:131", "KARGATH:232:245:0:154", "LETHLORRAVINE:344:395:627:173", "MIRAGEFLATS:276:229:153:387", "REDBRANDSDIGSITE:325:155:496:454", "RUINSOFCORTHAN:210:200:203:124", "SCALEBANERIDGE:205:179:751:142", "THEDUSTBOWL:253:266:166:203", "THEMAKERSTERRACE:230:172:399:16:1517:0", "VALLEYOFFANGS:215:215:358:265",},
|
||||
["Balor"] = {"BILGERATCOMPOUND:174:141:323:76", "CROAKINGPLATEAU:371:154:397:201", "GRAHANESTATE:93:116:306:392", "GULLWINGWRECKAGE:110:105:221:34", "LANGSTONORCHARD:102:129:410:346", "RUINSOFBREEZEHAVEN:163:141:450:256", "SCURRYINGTHICKET:144:158:283:161", "SIOUTPOST:175:232:597:389", "SORROWMORELAKE:174:140:303:277", "STORMBREAKERPOINT:243:197:583:212", "STORMREAVERSPIRE:151:119:491:429", "STORMWROUGHTCASTLE:202:166:458:332", "TREACHEROUSCRAGS:243:171:381:457", "VANDERFARMSTEAD:146:140:330:372", "WINDROCKCLIFFS:192:279:256:303",},
|
||||
["Barrens"] = {"AGAMAGOR:183:173:348:242", "ANCHORSEDGE:118:83:690:300", "BAELMODAN:120:123:435:482", "BLACKTHORNRIDGE:140:123:340:465", "BOULDERLODEMINE:109:105:560:0", "BRAMBLESCAR:112:152:446:304", "CAMPTAURAJO:130:113:370:355", "DREADMISTPEAK:121:93:423:68", "FARWATCHPOST:85:152:572:58", "FIELDOFGIANTS:194:137:363:410", "GROLDOMFARM:112:107:499:67", "HONORSSTAND:128:128:306:130", "LUSHWATEROASIS:160:169:372:186", "NORTHWATCHFOLD:134:107:533:310", "RAPTORGROUNDS:101:99:512:299", "RATCHET:116:118:556:190", "RAZORFENDOWNS:137:106:414:562", "RAZORFENKRAUL:116:123:345:539", "THECROSSROADS:142:140:436:127", "THEDRYHILLS:185:134:324:36", "THEFORGOTTENPOOLS:104:116:391:120", "THEMERCHANTCOAST:80:123:584:249", "THEMORSHANRAMPART:118:91:418:0", "THESLUDGEFEN:153:113:463:0", "THESTAGNANTOASIS:147:126:484:211", "THORNHILL:128:120:502:123",},
|
||||
["BlackstoneIsland"] = {"BLACKASHCOALPITS:303:474:209:178", "BLACKASHMINE:251:204:397:222", "GAZZIKSWORKSHOP:329:247:439:403", "RUSTGATELUMBERYARD:217:245:551:241", "RUSTGATERIDGE:396:226:372:360", "THEWATERHOLE:386:239:382:43", "VENTURECOSLUMS:270:192:498:129",},
|
||||
["BlastedLands"] = {"ALTAROFSTORMS:169:142:318:141", "DARKPORTAL:254:208:460:265", "DREADMAULHOLD:200:171:351:21", "DREADMAULPOST:228:183:370:204", "GARRISONARMORY:155:192:480:15", "NETHERGARDEKEEP:169:178:567:35", "RISEOFTHEDEFILER:150:131:414:131", "SERPENTSCOIL:211:155:510:148", "THETAINTEDSCAR:370:438:220:184",},
|
||||
["BurningSteppes"] = {"ALTAROFSTORMS:215:210:41:114", "BLACKROCKMOUNTAIN:249:272:178:104", "BLACKROCKPASS:259:299:597:285", "BLACKROCKSTRONGHOLD:232:258:342:120", "DRACODAR:405:309:61:262", "DREADMAULROCK:203:214:717:174", "MORGANSVIGIL:287:265:715:311", "PILLAROFASH:313:261:382:289", "RUINSOFTHAURISSAN:258:275:521:105", "TERRORWINGPATH:271:296:730:52",},
|
||||
["Darkshore"] = {"AMETHARAN:163:178:340:323", "AUBERDINE:134:179:323:191", "BASHALARAN:153:171:379:199", "CLIFFSPRINGRIVER:205:161:391:112:445:0", "GROVEOFTHEANCIENTS:180:138:314:436", "REMTRAVELSEXCAVATION:148:164:245:504", "RUINSOFMATHYSTRA:176:205:519:3", "THEMASTERSGLAIVE:146:139:346:529", "TOWEROFALTHALAXX:142:159:484:110",},
|
||||
["DeadwindPass"] = {"DEADMANSCROSSING:368:353:257:81:2938:0", "KARAZHAN:287:234:277:342", "THEVICE:254:257:433:305",},
|
||||
["Desolace"] = {"ETHELRETHOR:189:237:318:68", "GELKISVILLAGE:181:235:300:433", "KODOGRAVEYARD:253:237:396:251", "KOLKARVILLAGE:206:208:615:221", "KORMEKSHUT:156:147:562:187", "MAGRAMVILLAGE:182:272:606:373", "MANNOROCCOVEN:270:271:408:385", "NIJELSPOINT:188:246:561:0", "RANAZJARISLE:88:89:248:11", "SARGERON:274:237:632:38", "SHADOWBREAKRAVINE:190:181:698:454", "SHADOWPREYVILLAGE:215:218:175:395", "TETHRISARAN:185:137:440:0", "THUNDERAXEFORTRESS:174:207:455:108", "VALLEYOFSPEARS:234:274:217:221",},
|
||||
["DunMorogh"] = {"AMBERSTILLRANCH:105:104:582:288", "ANVILMAR:209:159:167:414:77:0", "BREWNALLVILLAGE:94:102:263:256", "CHILLBREEZEVALLEY:160:113:281:304", "COLDRIDGEPASS:134:123:300:387", "FROSTMANEHOLD:112:118:225:291", "GNOMERAGON:158:176:178:164", "GOLBOLARQUARRY:144:149:619:301", "HELMSBEDLAKE:131:153:706:284:716:0", "ICEFLOWLAKE:117:167:286:173", "IRONFORGE:291:187:414:166", "IRONFORGEAIRFIELDS:213:233:582:100", "KHARANOS:175:186:397:296:189:0", "MISTYPINEREFUGE:117:155:507:224", "NORTHERNGATEOUTPOST:115:155:766:177", "RUGFORDSMOUNTAINREST:199:114:739:406", "SHIMMERRIDGE:115:173:355:169", "SOUTHERNGATEOUTPOST:113:106:800:284", "THEGRIZZLEDDEN:178:155:321:328", "THETUNDRIDHILLS:135:115:531:328",},
|
||||
["Durotar"] = {"DRYGULCHRAVINE:139:134:438:92:410:0", "ECHOISLES:175:236:561:432", "KOLKARCRAG:131:96:426:487", "ORGRIMMAR:427:150:251:0", "RAZORHILL:188:207:445:182", "RAZORMANEGROUNDS:196:202:318:204", "SENJINVILLAGE:133:167:485:398", "SKULLROCK:118:79:469:51", "SPARKWATERPORT:122:134:522:94", "THUNDERRIDGE:156:174:341:72", "TIRAGARDEKEEP:160:148:477:300", "VALLEYOFTRIALS:186:184:370:335",},
|
||||
["Duskwood"] = {"ADDLESSTEAD:255:231:67:353", "BRIGHTWOODGROVE:191:315:520:131", "DARKSHIRE:291:259:644:173:576:0", "MANORMISTMANTLE:174:150:668:134", "RAVENHILL:166:115:117:317", "RAVENHILLCEMETARY:323:276:99:165:243:0", "THEDARKENEDBANK:886:184:105:44", "THEHUSHEDBANK:136:306:29:143", "THEROTTINGORCHARD:231:210:549:380", "THEYORGENFARMSTEAD:211:235:403:393", "TRANQUILGARDENSCEMETARY:204:196:692:363", "TWILIGHTGROVE:327:384:314:99", "VULGOLOGREMOUND:231:266:259:365",},
|
||||
["Dustwallow"] = {"ALCAZISLAND:176:179:671:29", "BACKBAYWETLANDS:384:243:248:196", "BRACKENWALLVILLAGE:264:264:239:2:507:0", "THEDENOFFLAME:246:235:264:320", "THERAMOREISLE:214:187:542:232", "THEWYRMBOG:263:229:372:387", "WITCHHILL:230:311:431:1:518:0", "WESTHAVENHOLLOW:166:149:603:428",},
|
||||
["EasternPlaguelands"] = {"BLACKWOODLAKE:217:221:451:207", "CORINSCROSSING:153:146:544:373", "CROWNGUARDTOWER:193:152:299:408", "DARROWSHIRE:195:167:316:497", "EASTWALLTOWER:164:143:600:250", "FORLORNSUMMIT:192:185:64:225", "LAKEMERELDAR:240:164:543:469", "LIGHTSHOPECHAPEL:165:238:721:304", "NORTHDALE:172:191:628:135", "NORTHPASSTOWER:228:178:465:118", "PESTILENTSCAR:189:241:416:349", "PLAGUEWOOD:346:258:178:92", "QUELLITHIENLODGE:216:136:430:44", "STRATHOLME:227:189:202:15", "TERRORDALE:175:129:86:137", "THEFUNGALVALE:202:199:277:267", "THEINFECTISSCAR:183:262:628:298", "THEMARRISSTEAD:182:194:164:367", "THENOXIOUSGLADE:211:202:730:172", "THEUNDERCROFT:171:136:178:484", "THONDRORILRIVER:210:348:12:235", "TYRSHAND:201:159:724:475", "ZULMASHAR:189:152:621:36",},
|
||||
["Elwynn"] = {"BRACKWELLPUMPKINPATCH:222:223:592:431", "CRYSTALLAKE:196:192:435:345", "EASTVALELOGGINGCAMP:237:190:713:339", "FARGODEEPMINE:238:230:247:438:63:64", "FORESTSEDGE:242:335:132:333:120:0", "GOLDSHIRE:219:199:261:280", "JERODSLANDING:206:212:437:445", "NORTHSHIREVALLEY:237:244:390:154", "RIDGEPOINTTOWER:287:207:710:445", "STONECAIRNLAKE:278:230:601:203", "STORMWIND:452:392:21:3", "TOWEROFAZORA:232:226:561:303:54:0",},
|
||||
["Felwood"] = {"BLOODVENOMFALLS:225:132:297:269", "DEADWOODVILLAGE:162:129:415:539", "EMERALDSANCTUARY:171:150:411:436", "FELPAWVILLAGE:224:138:489:0:1769:0", "IRONTREEWOODS:200:203:426:60", "JADEFIREGLEN:151:145:339:469", "JADEFIRERUN:181:159:338:35", "JAEDENAR:236:122:275:333", "MORLOSARAN:129:151:503:515", "RUINSOFCONSTELLAS:224:142:303:387", "SHATTERSCARVALE:218:186:315:130", "TALONBRANCHGLADE:145:130:554:97",},
|
||||
["Feralas"] = {"CAMPMOJACHE:146:148:694:238", "CHIMAERAROOSTVALE:106:150:785:321", "DIREMAUL:214:180:461:208", "DREAMBOUGH:141:122:459:0", "FERALSCARVALE:103:104:491:335", "FRAYFEATHERHIGHLANDS:101:163:485:391", "GORDUNNIOUTPOST:154:128:697:148", "GRIMTOTEMCOMPOUND:110:186:630:172", "ISLEOFDREAD:205:285:197:381", "LOWERWILDS:214:152:756:204", "ONEIROS:101:103:499:74", "RUINSOFISILDIEN:179:241:547:326", "RUINSOFRAVENWIND:178:149:310:0", "SARDORISLE:167:168:216:241", "THEFORGOTTENCOAST:132:316:409:258", "THETWINCOLOSSALS:278:235:323:78", "THEWRITHINGDEEP:214:205:625:305:2519:0",},
|
||||
["Gillijim"] = {"DEEPTIDESANCTUM:204:117:199:512", "DISTILLERYISLE:90:69:234:216", "FAELONSFOLLY:108:103:331:304", "GILLIJIMSTRAND:108:324:641:160", "JADEMINE:141:149:500:256", "KALKORPOINT:123:115:421:210", "KAZONISLAND:194:113:432:51", "MAULOGGPOST:137:227:512:401", "MAULOGGREFUGE:231:132:615:465", "RUINSOFZULRAZAR:215:136:369:384", "SILVERCOAST:245:184:311:386", "SILVERSANDBAR:47:128:256:398", "SOUTHSEASANDBAR:125:172:178:63", "TANGLEWOOD:135:188:576:297", "ZULRAZAR:137:168:375:282",},
|
||||
["Gilneas"] = {"BLACKTHORNSCAMP:160:163:32:171", "BROLOKMOUND:140:120:467:349", "DAWNSTONEMINE:130:144:292:155", "FREYSHEARKEEP:72:70:624:405", "GILNEASCITY:295:242:89:105", "GLAYMORESTEAD:140:127:483:129", "GREYMANESWATCH:119:158:580:311", "HOLLOWWEBCEMETARY:180:167:215:395", "HOLLOWWEBWOODS:171:118:341:452", "NORTHGATETOWER:130:137:397:172", "OLDROCKPASS:155:165:290:37", "RAVENSHIRE:194:237:472:431", "RAVENWOODKEEP:253:174:496:494", "ROSEWICKPLANTATION:134:140:331:155", "RUINSOFGREYSHIRE:192:144:230:260", "SHADEMORETAVERN:117:120:279:336", "SOUTHMIREORCHARD:202:133:302:354", "STILLWARDCHURCH:213:166:471:220", "THEDRYROCKMINE:129:127:145:262", "THEDRYROCKPIT:230:217:69:295", "THEGREYMANEWALL:131:173:412:48", "THEOVERGROWNACRE:134:154:390:269",},
|
||||
["GrimReaches"] = {"BAGGOTHSRAMPART:112:113:399:238", "BARLEYCRESTFARMSTEAD:116:88:499:222", "BRANGARSFOLLY:108:123:568:33", "DUNKITHAS:137:95:470:329", "EASTRIDGEOUTPOST:166:107:374:165", "GETHKAR:118:124:444:84", "GROLDANSEXCAVATION:125:117:567:231", "LAKEKITHAS:217:150:481:270", "RUINSOFSTOLGAZKEEP:134:146:366:53", "SALGAZMINES:150:119:535:380", "SHATTERBLADEPOST:186:130:512:126", "SLATEBEARDSFORGE:136:99:457:404", "THEGRIMHOLLOW:288:200:396:463", "THEHIGHPASS:97:141:409:315", "ZARMGETHPOINT:143:76:405:16", "ZARMGETHSTRONGHOLD:120:150:487:17",},
|
||||
["Hilsbrad"] = {"AZURELOADMINE:146:174:188:288", "DARROWHILL:173:132:426:164", "DUNGAROK:225:257:644:305", "DURNHOLDEKEEP:367:347:614:82", "EASTERNSTRAND:208:301:537:350", "HILLSBRADFIELDS:282:257:211:166", "NETHANDERSTEAD:190:215:554:249", "PURGATIONISLE:120:94:111:485", "SOUTHPOINTTOWER:271:207:11:203", "SOUTHSHORE:219:245:423:213", "TARRENMILL:195:291:524:9", "WESTERNSTRAND:272:139:215:377",},
|
||||
["Hinterlands"] = {"AERIEPEAK:240:195:20:250", "AGOLWATHA:188:180:382:173", "HIRIWATHA:201:118:188:314", "JINTHAALOR:223:277:514:338", "PLAGUEMISTRAVINE:124:203:170:158", "QUELDANILLODGE:168:181:248:194", "SERADANE:260:263:519:25", "SHADRAALOR:181:170:247:394", "SHAOLWATHA:265:190:580:247", "SKULKROCK:148:133:520:241", "THEALTAROFZUL:186:153:382:371", "THECREEPINGRUIN:168:157:417:268", "THEOVERLOOKCLIFFS:153:299:702:309", "THERASAZTRAILS:118:133:210:381", "VALORWINDLAKE:153:160:329:309",},
|
||||
["Hyjal"] = {"BARKSKINPLATEAU:207:167:390:297", "BARKSKINVILLAGE:502:293:417:346", "BLEAKHOLLOWCRATER:297:335:401:20", "CIRCLEOFPOWER:277:331:203:63", "DARKHOLLOWPASS:214:237:342:389", "NORDANAAR:139:146:778:118", "NORDRASSILGLADE:418:408:578:0", "RUINSOFTELENNAS:165:193:141:236", "THEEMERALDGATEWAY:309:244:109:353", "ZULHATHA:191:210:3:194",},
|
||||
["Icepoint"] = {"KANEQNUUN:512:430:237:129",},
|
||||
["Lapidis"] = {"BRIGHTCOAST:298:213:237:246", "CAELANSREST:147:111:517:230", "CROWNISLAND:140:95:512:33", "GORDOSHHEIGHTS:253:243:256:89", "HAZURRIGLADE:87:82:486:306", "SHANKSREEF:207:90:446:109", "THEROCK:74:72:687:149", "TOWEROFLAPIDIS:203:118:487:175", "WALLOWINGCOAST:240:184:505:326", "ZULHAZU:227:178:348:407",},
|
||||
["LochModan"] = {"GRIZZLEPAWRIDGE:262:343:326:325", "IRONBANDSEXCAVATIONSITE:321:294:491:276", "MOGROSHSTRONGHOLD:287:265:558:61", "NORTHGATEPASS:195:273:143:24:925:0", "SILVERSTREAMMINE:201:243:247:25", "STONESPLINTERVALLEY:226:255:230:363", "STONEWROUGHTDAM:260:149:354:23", "THEFARSTRIDERLODGE:162:182:741:298", "THELOCH:288:380:367:99", "THELSAMAR:243:205:225:214", "VALLEYOFKINGS:166:224:123:382",},
|
||||
["Moonglade"] = {"LAKEELUNEARA:530:484:256:101:2361:0",},
|
||||
["Moonwhisper"] = {"ANSHESRESPITE:170:120:398:81", "ANCESTRALGROUNDS:164:82:295:88", "BLACKROOTHOLD:162:137:495:465", "BLACKROOTVILLAGE:224:143:411:525", "FOULHEARTSANCTUM:157:149:496:232", "GROVEOFTHEMOON:152:146:464:347", "LUNARCLAWDEN:84:60:674:376", "MARASETHIL:96:94:640:417", "MOONHOOFRETREAT:172:144:419:167", "MOONHOOFVILLAGE:163:157:570:188", "MOONSILKHOLLOW:125:114:577:458", "MOROGAIVILLAGE:133:127:571:372", "NARVALISPOINT:162:139:503:118", "RUINSOFNENDIS:183:130:584:275", "STARSHARDCRADLE:122:109:334:121", "TYRANDAS:158:169:580:61", "VYSNAGOSASREST:163:102:420:8",},
|
||||
["Mulgore"] = {"BAELDUNDIGSITE:175:151:269:227", "BLOODHOOFVILLAGE:236:182:378:311", "PALEMANEROCK:102:183:315:317", "RAVAGEDCARAVAN:110:100:482:269", "REDCLOUDMESA:436:231:285:437:221:0", "REDROCKS:154:138:522:92", "SUNTAILPASS:129:116:533:18", "THEGOLDENPLAINS:184:217:441:91", "THEROLLINGPLAINS:231:166:536:369", "THEVENTURECOMINE:187:204:549:251", "THUNDERBLUFF:254:217:264:70", "THUNDERHORNWATERWELL:118:137:384:248", "WILDMANEWATERWELL:152:111:299:10", "WINDFURYRIDGE:182:119:405:2", "WINTERHOOFWATERWELL:144:107:470:379",},
|
||||
["Northwind"] = {"ABBEYGARDENS:250:224:512:33", "AMBERSHIRE:234:215:334:278", "AMBERWOODKEEP:243:205:187:308", "BLACKROCKBREACH:255:201:721:222", "BRISTLEWHISKERCAVERN:197:185:568:232", "CINDERFALLPASS:229:229:733:348", "CRAWFORDWINERY:160:222:513:285", "CRYSTALFALLS:320:205:627:464", "GRIMMENLAKE:218:210:550:352", "MERCHANTSHIGHROAD:353:249:256:420", "NORTHRIDGEPOINT:298:314:349:31", "NORTHWINDLOGGINGCAMP:224:185:258:171", "RUINSOFBIRKHAVEN:180:208:645:116", "SHERWOODQUARRY:293:287:702:1", "STILLHEARTPORT:174:155:116:217", "TOWEROFMAGILOU:181:223:189:169", "WITCHCOVEN:147:162:257:81",},
|
||||
["Redridge"] = {"ALTHERSMILL:222:260:407:134", "GALARDELLVALLEY:234:240:661:168:96:0", "LAKEEVERSTILL:522:260:140:249", "LAKERIDGEHIGHWAY:411:274:196:341", "LAKESHIRE:325:179:92:204", "REDRIDGECANYONS:345:232:132:79:98:0", "RENDERSCAMP:264:248:284:4:998:0", "RENDERSVALLEY:453:248:491:365", "REDWALLKEEP:162:110:830:466", "STONEWATCH:243:289:507:221:999:0", "STONEWATCHFALLS:308:200:601:325", "THREECORNERS:360:337:0:288",},
|
||||
["SearingGorge"] = {"BLACKCHARCAVE:264:224:84:371", "DUSTFIREVALLEY:446:355:428:11", "FIREWATCHRIDGE:385:413:95:39", "GRIMSILTDIGSITE:292:210:501:306", "TANNERCAMP:292:215:552:413", "THECAULDRON:414:310:257:178", "THESEAOFCINDERS:345:273:256:395",},
|
||||
["Silithus"] = {"HIVEASHI:431:276:305:34:3426:0", "HIVEREGAL:469:335:262:333:3427:0", "HIVEZORA:343:424:117:188", "SOUTHWINDVILLAGE:346:348:519:83:3077:0", "THECRYSTALVALE:285:274:121:31", "THESCARABWALL:271:145:125:523:2741:0", "TWILIGHTBASECAMP:297:238:355:206:3097:2739",},
|
||||
["Silverpine"] = {"AMBERMILL:204:210:512:279", "BERENSPERIL:206:146:508:433", "DEEPELEMMINE:127:143:484:274", "FENRISISLE:234:187:593:88:232:0", "MALDENSORCHARD:228:138:479:9:239:0", "NORTHTIDESHOLLOW:152:104:337:141", "OLSENSFARTHING:128:152:401:267", "PYREWOODVILLAGE:122:108:405:456", "SHADOWFANGKEEP:185:131:380:373", "THEDEADFIELD:141:134:419:82", "THEDECREPITFERRY:144:156:475:160", "THEGREYMANEWALL:175:185:398:465", "THESEPULCHER:193:135:360:184", "THESHININGSTRAND:225:192:475:27:227:0", "THESKITTERINGDARK:160:165:302:18",},
|
||||
["StonetalonMountains"] = {"AMANIALOR:504:252:8:5", "BAELHARDUL:357:252:512:257", "BLACKSANDOILFIELDS:504:504:8:5", "BOULDERSLIDERAVINE:90:76:586:547", "BRAMBLETHORNPASS:382:359:512:257", "BROKENCLIFFMINE:252:504:260:5", "CAMPAPARAJE:222:100:670:552", "GRIMTOTEMPOST:146:77:675:519", "MALAKAJIN:137:86:662:562", "MIRKFALLONLAKE:504:504:260:5", "POWDERTOWN:252:252:260:257", "SISHIRCANYON:252:264:512:257", "STONETALONPEAK:258:252:260:5", "SUNROCKRETREAT:504:252:260:257", "THECHARREDVALE:252:308:260:257", "THEEARTHENRING:504:252:260:257", "VENTURECOMPANYCAMP:252:504:260:5", "WEBWINDERPATH:256:324:512:256", "WINDSHEARCRAG:256:256:512:256:1277:0",},
|
||||
["Stranglethorn"] = {"BALALRUINS:83:65:245:101", "BALIAMAHRUINS:100:133:376:133", "BLOODSAILCOMPOUND:154:163:201:291", "BOOTYBAY:137:125:207:435:312:0", "CRYSTALVEINMINE:111:114:350:280", "GROMGOLBASECAMP:101:98:265:137", "JAGUEROISLE:132:107:319:498", "KALAIRUINS:86:89:303:93", "KURZENSCOMPOUND:141:139:394:5", "LAKENAZFERITI:124:120:332:60", "MISTVALEVALLEY:120:116:283:372", "MIZJAHRUINS:93:102:315:133", "MOSHOGGOGREMOUND:123:168:435:96", "NEKMANIWELLSPRING:80:102:215:365", "NESINGWARYSEXPEDITION:128:98:274:29", "REBELCAMP:164:86:289:0", "RUINSOFABORAZ:87:87:356:340", "RUINSOFJUBUWAL:100:105:312:304", "RUINSOFZULKUNDA:113:132:201:8", "RUINSOFZULMAMWE:162:122:397:213", "THEARENA:186:176:241:194", "THEVILEREEF:177:160:159:98", "VENTURECOBASECAMP:96:120:391:66", "WILDSHORE:151:175:237:431", "ZIATAJAIRUINS:127:120:364:231", "ZULGURUB:230:213:489:12", "ZUULDAIARUINS:103:105:160:47",},
|
||||
["SwampOfSorrows"] = {"FALLOWSANCTUARY:348:292:501:0", "ITHARIUSSCAVE:230:232:0:270", "MISTYREEDSTRAND:250:664:752:0:1978:0", "MISTYVALLEY:211:179:29:148", "POOLOFTEARS:280:256:576:227", "SORROWMURK:199:351:734:128", "SPLINTERSPEARJUNCTION:258:222:138:245", "STAGALBOG:328:238:561:386", "STONARD:343:303:287:244", "SORROWGUARDKEEP:220:174:0:266", "THEHARBORAGE:216:187:182:157", "THESHIFTINGMIRE:303:221:294:118",},
|
||||
["Tanaris"] = {"ABYSSALSANDS:194:159:376:207", "BROKENPILLAR:93:165:481:243", "CAVERNSOFTIME:139:132:567:262", "DUNEMAULCOMPOUND:186:127:337:298", "EASTMOONRUINS:143:130:405:355", "GADGETZAN:156:149:430:102", "LANDSENDBEACH:184:148:456:514", "LOSTRIGGERCOVE:141:173:635:230", "NOONSHADERUINS:129:191:530:43", "SANDSORROWWATCH:175:157:311:111", "SOUTHBREAKSHORE:192:151:510:304", "SOUTHMOONRUINS:185:196:324:366", "STEAMWHEEDLEPORT:136:129:599:86", "SLICKWICKOILRIG:237:181:129:436", "THEGAPINGCHASM:198:188:458:380", "THENOXIOUSLAIR:155:180:265:209", "THISTLESHRUBVALLEY:166:234:217:294", "VALLEYOFTHEWATCHERS:125:115:311:470", "WATERSPRINGFIELD:145:164:516:177", "ZALASHJISDEN:90:127:619:157", "ZULFARRAK:191:166:265:5",},
|
||||
["TelAbim"] = {"BIXXLESSTOREHOUSE:300:127:368:178", "HIGHVALERISE:191:187:468:248", "TAZZOSSHACK:229:232:453:391", "TELCOBASECAMP:184:183:342:440", "THEDERELICTCAMP:165:205:364:273", "THEJAGGEDISLES:313:256:385:16",},
|
||||
["Teldrassil"] = {"BANETHILHOLLOW:136:191:392:289", "DARNASSUS:294:252:110:251", "DOLANAAR:170:119:469:327", "GNARLPINEHOLD:230:158:316:417", "LAKEALAMETH:237:163:445:388", "POOLSOFARLITHRIEN:125:180:337:314", "RUTTHERANVILLAGE:110:91:504:548", "SHADOWGLEN:198:201:504:164", "STARBREEZEVILLAGE:153:175:588:303", "THEORACLEGLADE:150:223:279:135", "URSANHEIGHTS:229:154:243:423", "WELLSPRINGLAKE:166:245:383:98:265:0",},
|
||||
["ThalassianHighlands"] = {"ALAHTHALAS:470:276:429:38", "ANASTERIANPARK:229:219:385:256", "BRINTHILIEN:180:211:395:450", "FELSTRIDERRETREAT:201:207:165:428", "ISLEOFETERNALAUTUMN:204:167:236:117", "RUINSOFNASHALARAN:249:266:36:151", "SILVERSUNMINE:208:189:288:373", "THEFARSTRIDE:230:199:212:255", "THELASTRUNESTONE:179:203:507:379",},
|
||||
["ThousandNeedles"] = {"CAMPETHOK:289:300:7:1:2237:0", "DARKCLOUDPINNACLE:189:182:267:138", "FREEWINDPOST:193:173:366:273", "HIGHPERCH:178:174:39:164", "SPLITHOOFCRAG:193:175:400:203", "THEGREATLIFT:191:163:213:78", "THESCREECHINGCANYON:240:231:187:206", "THESHIMMERINGFLATS:312:355:612:308", "WINDBREAKCANYON:225:213:501:249",},
|
||||
["Tirisfal"] = {"AGAMANDMILLS:244:191:342:148", "BALNIRFARMSTEAD:179:153:639:335", "BRIGHTWATERLAKE:176:280:598:143", "BRILL:121:144:540:305", "BULWARK:184:169:712:374", "COLDHEARTHMANOR:136:125:479:328", "CRUSADEROUTPOST:154:121:701:293", "DEATHKNELL:210:186:236:337", "GARRENSHAUNT:148:199:507:154", "GLENSHIRE:163:161:157:393:5033:5039", "MONASTARY:175:157:762:139:161:0", "NIGHTMAREVALE:209:151:370:355", "RUINSOFLORDAERON:299:216:469:370", "SCARLETWATCHPOST:141:223:701:113", "SOLLIDENFARMSTEAD:225:138:247:259", "STEEPCLIFFPORT:121:120:5:350", "STILLWATERPOND:168:123:401:279", "THECORINTHFARMSTEAD:116:152:73:402:5041:0", "THEWHISPERINGFOREST:243:175:86:263:5035:0", "VENOMWEBVALE:199:183:776:225",},
|
||||
["UngoroCrater"] = {"FIREPLUMERIDGE:277:258:377:185", "GOLAKKAHOTSPRINGS:299:334:133:158", "IRONSTONEPLATEAU:276:281:588:69", "LAKKARITARPITS:553:255:170:13", "TERRORRUN:333:275:166:375", "THEMARSHLANDS:295:341:568:247", "THESLITHERINGSCAR:326:260:378:403",},
|
||||
["WesternPlaguelands"] = {"CAERDARROW:158:150:608:421", "DALSONSTEARS:209:138:388:271", "DARROWMERELAKE:359:261:510:348", "FELSTONEFIELD:149:119:306:315", "GAHRRONSWITHERING:165:191:527:257", "HEARTHGLEN:331:280:312:20", "NORTHRIDGELUMBERCAMP:205:168:390:170", "RUINSOFANDORHOL:275:219:265:360", "SORROWHILL:290:201:360:467", "THEBULWARK:216:172:143:300", "THEWEEPINGCAVE:148:192:572:203", "THEWRITHINGHAUNT:159:177:457:329", "THONDRORILRIVER:193:326:597:94",},
|
||||
["Westfall"] = {"ALEXSTONFARMSTEAD:275:182:220:274", "DEMONTSPLACE:166:155:226:391", "FURLBROWSPUMPKINFARM:186:191:399:24", "GOLDCOASTQUARRY:194:241:234:113", "JANGOLODEMINE:185:187:323:44", "MOONBROOK:208:174:317:349:919:0", "SALDEANSFARM:191:184:478:119", "SENTINELHILL:161:210:461:259", "THEDAGGERHILLS:235:149:349:430", "THEDEADACRE:164:213:542:267", "THEDUSTPLAINS:260:204:538:393", "THEJANSENSTEAD:143:183:498:6", "THEMOLSENFARM:199:180:342:161", "WESTFALLLIGHTHOUSE:306:161:171:482",},
|
||||
["Wetlands"] = {"ANGERFANGENCAMPMENT:196:150:364:241", "BLACKCHANNELMARSH:213:173:95:260", "BLUEGILLMARSH:195:165:111:157", "DIREFORGEHILL:233:225:515:128", "DUNMODR:173:147:419:42", "DUNAGRATH:222:190:97:335", "GRIMBATOL:317:331:623:242:1037:0", "HAWKSVIGIL:170:162:264:353", "IRONBEARDSTOMB:165:156:368:135", "MENETHILHARBOR:156:116:27:321", "MOSSHIDEFEN:215:224:539:277", "RAPTORRIDGE:159:115:640:191", "SALTSPRAYGLEN:170:214:254:58", "SUNDOWNMARSH:265:212:113:100", "THEGREENBELT:153:209:473:143", "THELGANROCK:207:165:478:383:836:0", "WHELGARSEXCAVATIONSITE:172:156:264:224",},
|
||||
["Winterspring"] = {"DARKWHISPERGORGE:247:191:452:447", "EVERLOOK:151:189:516:114", "FROSTFIREHOTSPRINGS:226:124:231:180", "FROSTSABERROCK:240:172:375:11", "FROSTWHISPERGORGE:184:144:531:383", "ICETHISTLEHILLS:116:155:617:247", "LAKEKELTHERIL:203:176:409:205", "MAZTHORIL:174:169:499:265", "OWLWINGTHICKET:149:127:600:348", "STARFALLVILLAGE:174:148:399:143", "THEHIDDENGROVE:162:174:562:33", "TIMBERMAWPOST:223:112:235:248", "WINTERFALLVILLAGE:133:120:622:163",},
|
||||
}
|
||||
|
||||
Vendored
+11
-5
@@ -27,7 +27,6 @@ pfUI_translation["deDE"] = {
|
||||
["Always Show Item Comparison"] = nil,
|
||||
["Always Show On Target Units"] = nil,
|
||||
["Always Show On Units With Missing HP"] = nil,
|
||||
["Always Show Self In Raid Frames"] = nil,
|
||||
["Always Use 2D Portraits"] = nil,
|
||||
["Ammo Counter"] = nil,
|
||||
["Anchor Bags Above Chat"] = nil,
|
||||
@@ -52,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,
|
||||
@@ -124,6 +125,7 @@ pfUI_translation["deDE"] = {
|
||||
["Click Casting"] = nil,
|
||||
["Clock"] = nil,
|
||||
["Close"] = nil,
|
||||
["Collapse Empty Slots"] = nil,
|
||||
["Color"] = nil,
|
||||
["Color Buff Stacks"] = nil,
|
||||
["Color Debuff Stacks"] = nil,
|
||||
@@ -138,7 +140,6 @@ pfUI_translation["deDE"] = {
|
||||
["Combat Timer"] = nil,
|
||||
["Combopoint Height"] = nil,
|
||||
["Combopoint Width"] = nil,
|
||||
["Compare Item Base Stats"] = nil,
|
||||
["Components"] = nil,
|
||||
["Config UI Settings"] = nil,
|
||||
["Configuration"] = nil,
|
||||
@@ -291,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,
|
||||
@@ -312,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,
|
||||
@@ -569,6 +570,7 @@ pfUI_translation["deDE"] = {
|
||||
["Overwrite If Unit Is Attacking Others"] = nil,
|
||||
["Overwrite If Unit Is Attacking You"] = nil,
|
||||
["Overwrite If Unit Is Casting"] = nil,
|
||||
["Owner Name"] = nil,
|
||||
["Pageable"] = nil,
|
||||
["Paging Actionbar"] = nil,
|
||||
["Panel"] = nil,
|
||||
@@ -616,12 +618,13 @@ pfUI_translation["deDE"] = {
|
||||
["Raid Mark X-Offset"] = nil,
|
||||
["Raid Mark Y-Offset"] = nil,
|
||||
["Raid Padding"] = nil,
|
||||
["Raid-Pet"] = nil,
|
||||
["Random"] = nil,
|
||||
["Random Roll Announcement Rarity"] = nil,
|
||||
["Random Rolling"] = nil,
|
||||
["Range Based Hunter Paging"] = nil,
|
||||
["Range Check Interval"] = nil,
|
||||
["Rank"] = nil,
|
||||
["Recast Totem"] = nil,
|
||||
["Red Border On Enemy Units"] = nil,
|
||||
["Red Name Text On Infight Units"] = nil,
|
||||
["Regional Settings"] = nil,
|
||||
@@ -703,6 +706,7 @@ pfUI_translation["deDE"] = {
|
||||
["Show Description"] = nil,
|
||||
["Show Dispel Indicators"] = nil,
|
||||
["Show Druid Mana Bar"] = nil,
|
||||
["Show Druid Mana Bar Text"] = nil,
|
||||
["Show Duration Inside Buff"] = nil,
|
||||
["Show Empty Buttons"] = nil,
|
||||
["Show FPS and Latency Colors"] = nil,
|
||||
@@ -739,6 +743,7 @@ pfUI_translation["deDE"] = {
|
||||
["Show Required Questitem Count"] = nil,
|
||||
["Show Resting"] = nil,
|
||||
["Show Self In Group Frames"] = nil,
|
||||
["Show Self In Raid Frames When Solo"] = nil,
|
||||
["Show Spell Icon"] = nil,
|
||||
["Show Spell Name"] = nil,
|
||||
["Show Stacks"] = nil,
|
||||
@@ -783,6 +788,7 @@ pfUI_translation["deDE"] = {
|
||||
["Target Castbar"] = nil,
|
||||
["Target Debuff Bar"] = nil,
|
||||
["Target Nameplate Zoom Factor"] = nil,
|
||||
["Target Totem"] = nil,
|
||||
["Target-Target"] = nil,
|
||||
["Target-Target-Target"] = nil,
|
||||
["Text"] = nil,
|
||||
@@ -815,6 +821,7 @@ pfUI_translation["deDE"] = {
|
||||
["Top Actionbar"] = nil,
|
||||
["Top Left"] = nil,
|
||||
["Top Right"] = nil,
|
||||
["Total"] = "Gesamt",
|
||||
["Total Gold"] = nil,
|
||||
["Totem Direction"] = nil,
|
||||
["Totem Icons"] = nil,
|
||||
@@ -877,7 +884,6 @@ pfUI_translation["deDE"] = {
|
||||
["XP Percentage"] = nil,
|
||||
["Yellow Border On Neutral Units"] = nil,
|
||||
["Yes"] = nil,
|
||||
["You gain (.+) Mana from Totemic Recall"] = nil,
|
||||
["You got"] = nil,
|
||||
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
|
||||
["Your items have been repaired for"] = nil,
|
||||
|
||||
Vendored
+33
-5
@@ -19,6 +19,7 @@ pfUI_translation["enUS"] = {
|
||||
["Align Chat Windows"] = nil,
|
||||
["Aligned Position"] = nil,
|
||||
["All messages will be forwarded to:"] = nil,
|
||||
["All players passed"] = nil,
|
||||
["Alt-Click Action"] = nil,
|
||||
["Always Allow Drag Via Shift Key"] = nil,
|
||||
["Always Show"] = nil,
|
||||
@@ -27,7 +28,6 @@ pfUI_translation["enUS"] = {
|
||||
["Always Show Item Comparison"] = nil,
|
||||
["Always Show On Target Units"] = nil,
|
||||
["Always Show On Units With Missing HP"] = nil,
|
||||
["Always Show Self In Raid Frames"] = nil,
|
||||
["Always Use 2D Portraits"] = nil,
|
||||
["Ammo Counter"] = nil,
|
||||
["Anchor Bags Above Chat"] = nil,
|
||||
@@ -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,
|
||||
@@ -119,11 +123,13 @@ pfUI_translation["enUS"] = {
|
||||
["Chat Bubble Transparency"] = nil,
|
||||
["Chat Default Brackets"] = nil,
|
||||
["Class"] = nil,
|
||||
["Clear"] = nil,
|
||||
["Clear Rolls"] = nil,
|
||||
["Click Action"] = nil,
|
||||
["Click Casting"] = nil,
|
||||
["Clock"] = nil,
|
||||
["Close"] = nil,
|
||||
["Collapse Empty Slots"] = nil,
|
||||
["Color"] = nil,
|
||||
["Color Buff Stacks"] = nil,
|
||||
["Color Debuff Stacks"] = nil,
|
||||
@@ -138,7 +144,6 @@ pfUI_translation["enUS"] = {
|
||||
["Combat Timer"] = nil,
|
||||
["Combopoint Height"] = nil,
|
||||
["Combopoint Width"] = nil,
|
||||
["Compare Item Base Stats"] = nil,
|
||||
["Components"] = nil,
|
||||
["Config UI Settings"] = nil,
|
||||
["Configuration"] = nil,
|
||||
@@ -195,6 +200,7 @@ pfUI_translation["enUS"] = {
|
||||
["Deficit"] = nil,
|
||||
["Delete profile"] = nil,
|
||||
["Delete / Reset"] = nil,
|
||||
["Dependencies"] = nil,
|
||||
["Descending"] = nil,
|
||||
["Description Font"] = nil,
|
||||
["Description Font Size"] = nil,
|
||||
@@ -291,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,
|
||||
@@ -312,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,
|
||||
@@ -324,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,
|
||||
@@ -409,6 +416,7 @@ pfUI_translation["enUS"] = {
|
||||
["Highlight Equipped Items"] = nil,
|
||||
["Highlight Not Usable Spells"] = nil,
|
||||
["Highlight Out Of Mana Spells"] = nil,
|
||||
["Highlight New Items"] = nil,
|
||||
["Highlight Out Of Range Spells"] = nil,
|
||||
["Highlight Settings That Require Reload"] = nil,
|
||||
["Highlight Unusable Items"] = nil,
|
||||
@@ -477,6 +485,7 @@ pfUI_translation["enUS"] = {
|
||||
["Look & Feel"] = nil,
|
||||
["Loot"] = nil,
|
||||
["Loot & Spam"] = nil,
|
||||
["Loot History"] = nil,
|
||||
["Macro Text Color"] = nil,
|
||||
["Macro Text Size"] = nil,
|
||||
["Main Actionbar"] = nil,
|
||||
@@ -499,6 +508,7 @@ pfUI_translation["enUS"] = {
|
||||
["Menu Font Size"] = nil,
|
||||
["Messages are no longer forwarded to:"] = nil,
|
||||
["Middle Mouse Button"] = nil,
|
||||
["Missing"] = nil,
|
||||
["Minimap"] = nil,
|
||||
["Minimap Panel"] = nil,
|
||||
["Minimap Size (|cffffaaaaExperimental|r)"] = nil,
|
||||
@@ -525,11 +535,13 @@ pfUI_translation["enUS"] = {
|
||||
["Network Latency"] = nil,
|
||||
["Network Up"] = nil,
|
||||
["New entry:"] = nil,
|
||||
["New Item Color"] = nil,
|
||||
["NEW TIMER"] = nil,
|
||||
["Next"] = nil,
|
||||
["Next Memory Cleanup"] = nil,
|
||||
["No"] = nil,
|
||||
["No Anchor"] = nil,
|
||||
["NO LOS"] = nil,
|
||||
["None"] = nil,
|
||||
["Not a valid button!"] = nil,
|
||||
["No tracking spell active"] = nil,
|
||||
@@ -552,6 +564,7 @@ pfUI_translation["enUS"] = {
|
||||
["Only Show Own Debuffs (|cffffaaaaExperimental|r)"] = nil,
|
||||
["Only Show Target Castbar"] = nil,
|
||||
["On State Change"] = nil,
|
||||
["Optional Dependencies"] = nil,
|
||||
["Options"] = nil,
|
||||
["Orientation"] = nil,
|
||||
["Other Panel: Minimap"] = nil,
|
||||
@@ -569,6 +582,7 @@ pfUI_translation["enUS"] = {
|
||||
["Overwrite If Unit Is Attacking Others"] = nil,
|
||||
["Overwrite If Unit Is Attacking You"] = nil,
|
||||
["Overwrite If Unit Is Casting"] = nil,
|
||||
["Owner Name"] = nil,
|
||||
["Pageable"] = nil,
|
||||
["Paging Actionbar"] = nil,
|
||||
["Panel"] = nil,
|
||||
@@ -616,12 +630,13 @@ pfUI_translation["enUS"] = {
|
||||
["Raid Mark X-Offset"] = nil,
|
||||
["Raid Mark Y-Offset"] = nil,
|
||||
["Raid Padding"] = nil,
|
||||
["Raid-Pet"] = nil,
|
||||
["Random"] = nil,
|
||||
["Random Roll Announcement Rarity"] = nil,
|
||||
["Random Rolling"] = nil,
|
||||
["Range Based Hunter Paging"] = nil,
|
||||
["Range Check Interval"] = nil,
|
||||
["Rank"] = nil,
|
||||
["Recast Totem"] = nil,
|
||||
["Red Border On Enemy Units"] = nil,
|
||||
["Red Name Text On Infight Units"] = nil,
|
||||
["Regional Settings"] = nil,
|
||||
@@ -645,6 +660,7 @@ pfUI_translation["enUS"] = {
|
||||
["Resting"] = nil,
|
||||
["Reveal Unexplored Areas"] = nil,
|
||||
["Switch to current zone"] = nil,
|
||||
["Retrieving item information..."] = nil,
|
||||
["Right"] = nil,
|
||||
["Right Actionbar"] = nil,
|
||||
["Right Anchor"] = nil,
|
||||
@@ -703,6 +719,7 @@ pfUI_translation["enUS"] = {
|
||||
["Show Description"] = nil,
|
||||
["Show Dispel Indicators"] = nil,
|
||||
["Show Druid Mana Bar"] = nil,
|
||||
["Show Druid Mana Bar Text"] = nil,
|
||||
["Show Duration Inside Buff"] = nil,
|
||||
["Show Empty Buttons"] = nil,
|
||||
["Show FPS and Latency Colors"] = nil,
|
||||
@@ -739,6 +756,7 @@ pfUI_translation["enUS"] = {
|
||||
["Show Required Questitem Count"] = nil,
|
||||
["Show Resting"] = nil,
|
||||
["Show Self In Group Frames"] = nil,
|
||||
["Show Self In Raid Frames When Solo"] = nil,
|
||||
["Show Spell Icon"] = nil,
|
||||
["Show Spell Name"] = nil,
|
||||
["Show Stacks"] = nil,
|
||||
@@ -750,12 +768,17 @@ pfUI_translation["enUS"] = {
|
||||
["Show Totems Indicators"] = nil,
|
||||
["Shrink & Return"] = nil,
|
||||
["Size"] = nil,
|
||||
["Spell Name Alignment"] = nil,
|
||||
["Spell Name X Offset"] = nil,
|
||||
["Spell Name Y Offset"] = nil,
|
||||
["Skin"] = nil,
|
||||
["Skins"] = nil,
|
||||
["Slow"] = nil,
|
||||
["Small"] = nil,
|
||||
["Some settings need to reload the UI to take effect.\nDo you want to reload now?"] = nil,
|
||||
["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"] = nil,
|
||||
["Sort Bags"] = nil,
|
||||
["Sort Bank"] = nil,
|
||||
["Sort Order"] = nil,
|
||||
["Soul Shard"] = nil,
|
||||
["Soulshard Counter"] = nil,
|
||||
@@ -777,12 +800,14 @@ pfUI_translation["enUS"] = {
|
||||
["Switch Pages On Alt Key Press"] = nil,
|
||||
["Switch Pages On Ctrl Key Press"] = nil,
|
||||
["Switch Pages On Druid Stealth"] = nil,
|
||||
["Switch Pages On Priest Shadowform"] = nil,
|
||||
["Switch Pages On Shift Key Press"] = nil,
|
||||
["Systeminfo"] = nil,
|
||||
["Target"] = nil,
|
||||
["Target Castbar"] = nil,
|
||||
["Target Debuff Bar"] = nil,
|
||||
["Target Nameplate Zoom Factor"] = nil,
|
||||
["Target Totem"] = nil,
|
||||
["Target-Target"] = nil,
|
||||
["Target-Target-Target"] = nil,
|
||||
["Text"] = nil,
|
||||
@@ -798,6 +823,9 @@ pfUI_translation["enUS"] = {
|
||||
["Threshold To Trust Health Estimation"] = nil,
|
||||
["Time"] = nil,
|
||||
["Timer"] = nil,
|
||||
["Timer Alignment"] = nil,
|
||||
["Timer X Offset"] = nil,
|
||||
["Timer Y Offset"] = nil,
|
||||
["Time Remaining"] = nil,
|
||||
["Timer In Minutes"] = nil,
|
||||
["Timestamp Brackets"] = nil,
|
||||
@@ -815,6 +843,7 @@ pfUI_translation["enUS"] = {
|
||||
["Top Actionbar"] = nil,
|
||||
["Top Left"] = nil,
|
||||
["Top Right"] = nil,
|
||||
["Total"] = nil,
|
||||
["Total Gold"] = nil,
|
||||
["Totem Direction"] = nil,
|
||||
["Totem Icons"] = nil,
|
||||
@@ -877,7 +906,6 @@ pfUI_translation["enUS"] = {
|
||||
["XP Percentage"] = nil,
|
||||
["Yellow Border On Neutral Units"] = nil,
|
||||
["Yes"] = nil,
|
||||
["You gain (.+) Mana from Totemic Recall"] = nil,
|
||||
["You got"] = nil,
|
||||
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
|
||||
["Your items have been repaired for"] = nil,
|
||||
|
||||
Vendored
+11
-5
@@ -27,7 +27,6 @@ pfUI_translation["esES"] = {
|
||||
["Always Show Item Comparison"] = "Mostrar siempre la comparativa entre objetos",
|
||||
["Always Show On Target Units"] = "Mostrar siempre en el objectivo",
|
||||
["Always Show On Units With Missing HP"] = "Mostrar siempre en unidades con falta de salud",
|
||||
["Always Show Self In Raid Frames"] = "Mostrarse siempre a sí mismo en los marcos de banda",
|
||||
["Always Use 2D Portraits"] = "Usar siempre retratos 2D",
|
||||
["Ammo Counter"] = "Contador de munición",
|
||||
["Anchor Bags Above Chat"] = nil,
|
||||
@@ -52,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",
|
||||
@@ -124,6 +125,7 @@ pfUI_translation["esES"] = {
|
||||
["Click Casting"] = "Lanzamiento al hacer click",
|
||||
["Clock"] = "Reloj",
|
||||
["Close"] = "Cerrar",
|
||||
["Collapse Empty Slots"] = nil,
|
||||
["Color"] = nil,
|
||||
["Color Buff Stacks"] = "Colorear pilas de beneficios",
|
||||
["Color Debuff Stacks"] = "Colorear pilas de perjuicios",
|
||||
@@ -138,7 +140,6 @@ pfUI_translation["esES"] = {
|
||||
["Combat Timer"] = "Temporizador de combate",
|
||||
["Combopoint Height"] = nil,
|
||||
["Combopoint Width"] = nil,
|
||||
["Compare Item Base Stats"] = "Comparar estadísticas bases de objetos",
|
||||
["Components"] = "Componentes",
|
||||
["Config UI Settings"] = nil,
|
||||
["Configuration"] = "Configuración",
|
||||
@@ -291,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",
|
||||
@@ -312,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",
|
||||
@@ -569,6 +570,7 @@ pfUI_translation["esES"] = {
|
||||
["Overwrite If Unit Is Attacking Others"] = nil,
|
||||
["Overwrite If Unit Is Attacking You"] = nil,
|
||||
["Overwrite If Unit Is Casting"] = nil,
|
||||
["Owner Name"] = nil,
|
||||
["Pageable"] = "Paginable",
|
||||
["Paging Actionbar"] = "Paginando la barra de acción",
|
||||
["Panel"] = "Panel",
|
||||
@@ -616,12 +618,13 @@ pfUI_translation["esES"] = {
|
||||
["Raid Mark X-Offset"] = nil,
|
||||
["Raid Mark Y-Offset"] = nil,
|
||||
["Raid Padding"] = "Espaciado del marco de banda",
|
||||
["Raid-Pet"] = nil,
|
||||
["Random"] = "Aleatorio",
|
||||
["Random Roll Announcement Rarity"] = "Anuncio para tirar los dados aleatoriamente",
|
||||
["Random Rolling"] = "Tirar los dados aleatoriamente",
|
||||
["Range Based Hunter Paging"] = "Paginado de alcance para cazadores",
|
||||
["Range Check Interval"] = "Intervalo de comprobación de alcance",
|
||||
["Rank"] = "Rango",
|
||||
["Recast Totem"] = nil,
|
||||
["Red Border On Enemy Units"] = "Borde rojo en unidades enemigas",
|
||||
["Red Name Text On Infight Units"] = nil,
|
||||
["Regional Settings"] = nil,
|
||||
@@ -703,6 +706,7 @@ pfUI_translation["esES"] = {
|
||||
["Show Description"] = "Mostrar descripción",
|
||||
["Show Dispel Indicators"] = "Mostrar indicadores para disipar",
|
||||
["Show Druid Mana Bar"] = nil,
|
||||
["Show Druid Mana Bar Text"] = nil,
|
||||
["Show Duration Inside Buff"] = "Mostrar la duración dentro del beneficios",
|
||||
["Show Empty Buttons"] = "Mostrar botones vacíos",
|
||||
["Show FPS and Latency Colors"] = "Mostrar FPS y colores de latencia",
|
||||
@@ -739,6 +743,7 @@ pfUI_translation["esES"] = {
|
||||
["Show Required Questitem Count"] = "Muestra el número requerido de objetos de misión",
|
||||
["Show Resting"] = "Mostrar descanso",
|
||||
["Show Self In Group Frames"] = "Mostrar a sí mismo en marcos de grupo",
|
||||
["Show Self In Raid Frames When Solo"] = nil,
|
||||
["Show Spell Icon"] = "Mostrar icono de hechizo",
|
||||
["Show Spell Name"] = nil,
|
||||
["Show Stacks"] = "Mostrar pilas",
|
||||
@@ -783,6 +788,7 @@ pfUI_translation["esES"] = {
|
||||
["Target Castbar"] = "Barra de lanzamiento del objetivo",
|
||||
["Target Debuff Bar"] = "Barra de perjuicios del objetivo",
|
||||
["Target Nameplate Zoom Factor"] = "Factor de zoom de la placa de nombre del objetivo",
|
||||
["Target Totem"] = nil,
|
||||
["Target-Target"] = "Objetivo-Objetivo",
|
||||
["Target-Target-Target"] = "Objetivo-Objetivo-Objetivo",
|
||||
["Text"] = nil,
|
||||
@@ -815,6 +821,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",
|
||||
@@ -877,7 +884,6 @@ pfUI_translation["esES"] = {
|
||||
["XP Percentage"] = "Porcentaje de exp.",
|
||||
["Yellow Border On Neutral Units"] = "Borde amarillo en unidades neutrales",
|
||||
["Yes"] = "Sí",
|
||||
["You gain (.+) Mana from Totemic Recall"] = nil,
|
||||
["You got"] = "Obtienes",
|
||||
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "Ahora su interfaz está configurada.\n\nPara la configuración avanzada, abra la configuración |cff33ffccpf|rUI por el menú de escape o escriba \"|cffffffaa/pfui|r\" en el chat.",
|
||||
["Your items have been repaired for"] = "Tus objetos se han reparado por",
|
||||
|
||||
Vendored
+11
-5
@@ -27,7 +27,6 @@ pfUI_translation["frFR"] = {
|
||||
["Always Show Item Comparison"] = "Toujours afficher la comparaison d'objet",
|
||||
["Always Show On Target Units"] = "Toujours montrer sur la cible",
|
||||
["Always Show On Units With Missing HP"] = "Toujours montrer sur les cibles avec de la vie manquante",
|
||||
["Always Show Self In Raid Frames"] = "Toujours montrer soi-même dans le cadre de raid",
|
||||
["Always Use 2D Portraits"] = "Toujours utiliser les portraits 2D",
|
||||
["Ammo Counter"] = "Compteur de munitions",
|
||||
["Anchor Bags Above Chat"] = nil,
|
||||
@@ -52,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",
|
||||
@@ -124,6 +125,7 @@ pfUI_translation["frFR"] = {
|
||||
["Click Casting"] = "Clique sur le lancement de sort",
|
||||
["Clock"] = "Horloge",
|
||||
["Close"] = "Fermer",
|
||||
["Collapse Empty Slots"] = nil,
|
||||
["Color"] = nil,
|
||||
["Color Buff Stacks"] = "Couleur des l'empilements des Améliorations",
|
||||
["Color Debuff Stacks"] = "Couleur de l'empilements de Affaiblissements",
|
||||
@@ -138,7 +140,6 @@ pfUI_translation["frFR"] = {
|
||||
["Combat Timer"] = "Chronomètre de combat",
|
||||
["Combopoint Height"] = nil,
|
||||
["Combopoint Width"] = nil,
|
||||
["Compare Item Base Stats"] = "Compare les stats de base d'un objet",
|
||||
["Components"] = "Composants",
|
||||
["Config UI Settings"] = nil,
|
||||
["Configuration"] = "Configuration",
|
||||
@@ -291,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",
|
||||
@@ -312,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",
|
||||
@@ -569,6 +570,7 @@ pfUI_translation["frFR"] = {
|
||||
["Overwrite If Unit Is Attacking Others"] = nil,
|
||||
["Overwrite If Unit Is Attacking You"] = nil,
|
||||
["Overwrite If Unit Is Casting"] = nil,
|
||||
["Owner Name"] = nil,
|
||||
["Pageable"] = "Pageable",
|
||||
["Paging Actionbar"] = "Barre d'action de pagination",
|
||||
["Panel"] = "Panneau",
|
||||
@@ -616,12 +618,13 @@ pfUI_translation["frFR"] = {
|
||||
["Raid Mark X-Offset"] = nil,
|
||||
["Raid Mark Y-Offset"] = nil,
|
||||
["Raid Padding"] = "Remplissage du Raid",
|
||||
["Raid-Pet"] = nil,
|
||||
["Random"] = "Aléatoire",
|
||||
["Random Roll Announcement Rarity"] = "Rareté des annonces des jets de dés aléatoires",
|
||||
["Random Rolling"] = "Lancer de dés aléatoires",
|
||||
["Range Based Hunter Paging"] = "Pagination de distance basée sur le chasseur",
|
||||
["Range Check Interval"] = "Intervalle de vérification de la distance",
|
||||
["Rank"] = nil,
|
||||
["Recast Totem"] = nil,
|
||||
["Red Border On Enemy Units"] = "Bordure rouge sur les unités ennemies",
|
||||
["Red Name Text On Infight Units"] = nil,
|
||||
["Regional Settings"] = nil,
|
||||
@@ -703,6 +706,7 @@ pfUI_translation["frFR"] = {
|
||||
["Show Description"] = "Afficher les descriptions",
|
||||
["Show Dispel Indicators"] = "Afficher les indicateurs de dissipation",
|
||||
["Show Druid Mana Bar"] = nil,
|
||||
["Show Druid Mana Bar Text"] = nil,
|
||||
["Show Duration Inside Buff"] = "Afficher la durée à l'intérieur des améliorations",
|
||||
["Show Empty Buttons"] = "Afficher les boutons vides",
|
||||
["Show FPS and Latency Colors"] = nil,
|
||||
@@ -739,6 +743,7 @@ pfUI_translation["frFR"] = {
|
||||
["Show Required Questitem Count"] = nil,
|
||||
["Show Resting"] = "Afficher au repos",
|
||||
["Show Self In Group Frames"] = "S'afficher dans les cadres de groupe",
|
||||
["Show Self In Raid Frames When Solo"] = nil,
|
||||
["Show Spell Icon"] = "Afficher l'icone des sorts",
|
||||
["Show Spell Name"] = nil,
|
||||
["Show Stacks"] = "Afficher les empilements",
|
||||
@@ -783,6 +788,7 @@ pfUI_translation["frFR"] = {
|
||||
["Target Castbar"] = "Barre d'incantation de la cible",
|
||||
["Target Debuff Bar"] = "Barre des affaiblissements de la cible",
|
||||
["Target Nameplate Zoom Factor"] = "Facteur de zoom du Nameplate de la cible",
|
||||
["Target Totem"] = nil,
|
||||
["Target-Target"] = "Cible de la cible",
|
||||
["Target-Target-Target"] = "Cible de la cible de la cible",
|
||||
["Text"] = nil,
|
||||
@@ -815,6 +821,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",
|
||||
@@ -877,7 +884,6 @@ pfUI_translation["frFR"] = {
|
||||
["XP Percentage"] = "Pourcentage de la barre d'expérience",
|
||||
["Yellow Border On Neutral Units"] = "Bordure jaune sur les unités neutres",
|
||||
["Yes"] = "Oui",
|
||||
["You gain (.+) Mana from Totemic Recall"] = nil,
|
||||
["You got"] = "Vous avez",
|
||||
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
|
||||
["Your items have been repaired for"] = "Vos objets ont été réparés pour",
|
||||
|
||||
Vendored
+11
-5
@@ -27,7 +27,6 @@ pfUI_translation["koKR"] = {
|
||||
["Always Show Item Comparison"] = "항상 착용 장비와 비교 표시",
|
||||
["Always Show On Target Units"] = nil,
|
||||
["Always Show On Units With Missing HP"] = nil,
|
||||
["Always Show Self In Raid Frames"] = nil,
|
||||
["Always Use 2D Portraits"] = "항상 2D초상화 사용",
|
||||
["Ammo Counter"] = "탄약 갯수",
|
||||
["Anchor Bags Above Chat"] = nil,
|
||||
@@ -52,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,
|
||||
@@ -124,6 +125,7 @@ pfUI_translation["koKR"] = {
|
||||
["Click Casting"] = nil,
|
||||
["Clock"] = "시계",
|
||||
["Close"] = "닫기",
|
||||
["Collapse Empty Slots"] = nil,
|
||||
["Color"] = nil,
|
||||
["Color Buff Stacks"] = nil,
|
||||
["Color Debuff Stacks"] = nil,
|
||||
@@ -138,7 +140,6 @@ pfUI_translation["koKR"] = {
|
||||
["Combat Timer"] = "전투 타이머",
|
||||
["Combopoint Height"] = nil,
|
||||
["Combopoint Width"] = nil,
|
||||
["Compare Item Base Stats"] = nil,
|
||||
["Components"] = nil,
|
||||
["Config UI Settings"] = nil,
|
||||
["Configuration"] = "구성",
|
||||
@@ -291,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"] = "겹쳐서 표시",
|
||||
@@ -312,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,
|
||||
@@ -569,6 +570,7 @@ pfUI_translation["koKR"] = {
|
||||
["Overwrite If Unit Is Attacking Others"] = nil,
|
||||
["Overwrite If Unit Is Attacking You"] = nil,
|
||||
["Overwrite If Unit Is Casting"] = nil,
|
||||
["Owner Name"] = nil,
|
||||
["Pageable"] = nil,
|
||||
["Paging Actionbar"] = nil,
|
||||
["Panel"] = "패널",
|
||||
@@ -616,12 +618,13 @@ pfUI_translation["koKR"] = {
|
||||
["Raid Mark X-Offset"] = nil,
|
||||
["Raid Mark Y-Offset"] = nil,
|
||||
["Raid Padding"] = nil,
|
||||
["Raid-Pet"] = nil,
|
||||
["Random"] = nil,
|
||||
["Random Roll Announcement Rarity"] = nil,
|
||||
["Random Rolling"] = nil,
|
||||
["Range Based Hunter Paging"] = nil,
|
||||
["Range Check Interval"] = nil,
|
||||
["Rank"] = nil,
|
||||
["Recast Totem"] = nil,
|
||||
["Red Border On Enemy Units"] = nil,
|
||||
["Red Name Text On Infight Units"] = nil,
|
||||
["Regional Settings"] = nil,
|
||||
@@ -703,6 +706,7 @@ pfUI_translation["koKR"] = {
|
||||
["Show Description"] = nil,
|
||||
["Show Dispel Indicators"] = nil,
|
||||
["Show Druid Mana Bar"] = nil,
|
||||
["Show Druid Mana Bar Text"] = nil,
|
||||
["Show Duration Inside Buff"] = nil,
|
||||
["Show Empty Buttons"] = nil,
|
||||
["Show FPS and Latency Colors"] = nil,
|
||||
@@ -739,6 +743,7 @@ pfUI_translation["koKR"] = {
|
||||
["Show Required Questitem Count"] = nil,
|
||||
["Show Resting"] = nil,
|
||||
["Show Self In Group Frames"] = nil,
|
||||
["Show Self In Raid Frames When Solo"] = nil,
|
||||
["Show Spell Icon"] = nil,
|
||||
["Show Spell Name"] = nil,
|
||||
["Show Stacks"] = nil,
|
||||
@@ -783,6 +788,7 @@ pfUI_translation["koKR"] = {
|
||||
["Target Castbar"] = nil,
|
||||
["Target Debuff Bar"] = nil,
|
||||
["Target Nameplate Zoom Factor"] = nil,
|
||||
["Target Totem"] = nil,
|
||||
["Target-Target"] = "대상-대상",
|
||||
["Target-Target-Target"] = nil,
|
||||
["Text"] = nil,
|
||||
@@ -815,6 +821,7 @@ pfUI_translation["koKR"] = {
|
||||
["Top Actionbar"] = nil,
|
||||
["Top Left"] = nil,
|
||||
["Top Right"] = nil,
|
||||
["Total"] = "총",
|
||||
["Total Gold"] = "총 금",
|
||||
["Totem Direction"] = nil,
|
||||
["Totem Icons"] = nil,
|
||||
@@ -877,7 +884,6 @@ pfUI_translation["koKR"] = {
|
||||
["XP Percentage"] = "경험치 퍼센트",
|
||||
["Yellow Border On Neutral Units"] = nil,
|
||||
["Yes"] = nil,
|
||||
["You gain (.+) Mana from Totemic Recall"] = nil,
|
||||
["You got"] = nil,
|
||||
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
|
||||
["Your items have been repaired for"] = nil,
|
||||
|
||||
Vendored
+11
-5
@@ -27,7 +27,6 @@ pfUI_translation["ruRU"] = {
|
||||
["Always Show Item Comparison"] = "Всегда показывать сравнение предметов",
|
||||
["Always Show On Target Units"] = "Всегда показывать над выбранной целью",
|
||||
["Always Show On Units With Missing HP"] = "Всегда показывать над целями с неполным здоровьем",
|
||||
["Always Show Self In Raid Frames"] = "Всегда показывать себя в рейде",
|
||||
["Always Use 2D Portraits"] = "Всегда использовать 2D-портреты",
|
||||
["Ammo Counter"] = "Счетчик боеприпасов",
|
||||
["Anchor Bags Above Chat"] = nil,
|
||||
@@ -52,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"] = "Фон панели",
|
||||
@@ -124,6 +125,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Click Casting"] = "Каст по нажатию",
|
||||
["Clock"] = "Часы",
|
||||
["Close"] = "Закрыть",
|
||||
["Collapse Empty Slots"] = nil,
|
||||
["Color"] = nil,
|
||||
["Color Buff Stacks"] = "Цвет стаков баффа",
|
||||
["Color Debuff Stacks"] = "Цвет стаков дебаффа",
|
||||
@@ -138,7 +140,6 @@ pfUI_translation["ruRU"] = {
|
||||
["Combat Timer"] = "Таймер боя",
|
||||
["Combopoint Height"] = nil,
|
||||
["Combopoint Width"] = nil,
|
||||
["Compare Item Base Stats"] = "Сравнивать базовые характеристики предмета",
|
||||
["Components"] = "Компоненты",
|
||||
["Config UI Settings"] = "Настройка параметров пользовательского интерфейса",
|
||||
["Configuration"] = "Конфигурация",
|
||||
@@ -291,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"] = "Включить перекрытие",
|
||||
@@ -312,6 +312,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Encode"] = "Кодировать",
|
||||
["Ended"] = "Окончание",
|
||||
["Energy Color"] = "Цвет энергии",
|
||||
["Equipped"] = "Экипировано",
|
||||
["Equipped Item Color"] = "Цвет экипированных предметов",
|
||||
["Estimate Debuffs"] = "Оценка дебафов",
|
||||
["Estimate Enemy Health Points"] = "Оценка очков здоровья врага",
|
||||
@@ -569,6 +570,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Overwrite If Unit Is Attacking Others"] = nil,
|
||||
["Overwrite If Unit Is Attacking You"] = nil,
|
||||
["Overwrite If Unit Is Casting"] = nil,
|
||||
["Owner Name"] = nil,
|
||||
["Pageable"] = "Прокрутка страниц",
|
||||
["Paging Actionbar"] = "Прокрутка панелей",
|
||||
["Panel"] = "Панель",
|
||||
@@ -616,12 +618,13 @@ pfUI_translation["ruRU"] = {
|
||||
["Raid Mark X-Offset"] = nil,
|
||||
["Raid Mark Y-Offset"] = nil,
|
||||
["Raid Padding"] = "Отступ рейда",
|
||||
["Raid-Pet"] = nil,
|
||||
["Random"] = "Случайно",
|
||||
["Random Roll Announcement Rarity"] = "Оповещение случайного броска для качества предмета",
|
||||
["Random Rolling"] = "Случайный бросок костей для",
|
||||
["Range Based Hunter Paging"] = "[|cffA9D271Охотник|r] Переключение страниц на основе диапазона",
|
||||
["Range Check Interval"] = "Интервал проверки диапазона",
|
||||
["Rank"] = "Ранг",
|
||||
["Recast Totem"] = nil,
|
||||
["Red Border On Enemy Units"] = "Красные границы вражеских юнитов",
|
||||
["Red Name Text On Infight Units"] = nil,
|
||||
["Regional Settings"] = "Региональные настройки",
|
||||
@@ -703,6 +706,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Show Description"] = "Показать описание",
|
||||
["Show Dispel Indicators"] = "Показать индикаторы рассеивания",
|
||||
["Show Druid Mana Bar"] = nil,
|
||||
["Show Druid Mana Bar Text"] = nil,
|
||||
["Show Duration Inside Buff"] = "Показать продолжительность внутри баффа",
|
||||
["Show Empty Buttons"] = "Показать пустые кнопки",
|
||||
["Show FPS and Latency Colors"] = "Показать частоту кадров и задержку в цвете",
|
||||
@@ -739,6 +743,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Show Required Questitem Count"] = "Показать необходимое количество предметов для задания",
|
||||
["Show Resting"] = "Показать иконку отдыха",
|
||||
["Show Self In Group Frames"] = "Показать себя в окне группы",
|
||||
["Show Self In Raid Frames When Solo"] = nil,
|
||||
["Show Spell Icon"] = "Показать иконку заклинания",
|
||||
["Show Spell Name"] = "Показать название заклинания",
|
||||
["Show Stacks"] = "Показать стаки",
|
||||
@@ -783,6 +788,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Target Castbar"] = "Панель применения цели",
|
||||
["Target Debuff Bar"] = "Панель дебаффов цели",
|
||||
["Target Nameplate Zoom Factor"] = "Коэффициент увеличения индикатора здоровья цели",
|
||||
["Target Totem"] = nil,
|
||||
["Target-Target"] = "Цель цели",
|
||||
["Target-Target-Target"] = "Цель цели цели",
|
||||
["Text"] = nil,
|
||||
@@ -815,6 +821,7 @@ pfUI_translation["ruRU"] = {
|
||||
["Top Actionbar"] = "Верхняя",
|
||||
["Top Left"] = "Сверху слева",
|
||||
["Top Right"] = "Сверху справа",
|
||||
["Total"] = "Всего",
|
||||
["Total Gold"] = "Всего золота",
|
||||
["Totem Direction"] = "Направление тотемов",
|
||||
["Totem Icons"] = "Иконки тотемов",
|
||||
@@ -877,7 +884,6 @@ pfUI_translation["ruRU"] = {
|
||||
["XP Percentage"] = "Процент опыта",
|
||||
["Yellow Border On Neutral Units"] = "Желтые границы на нейтральных юнитах",
|
||||
["Yes"] = "Да",
|
||||
["You gain (.+) Mana from Totemic Recall"] = nil,
|
||||
["You got"] = "Вы получили",
|
||||
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "Теперь ваш интерфейс настроен.\n\nДля расширенной настройки откройте \"Настройки |cff33ffccpf|rUI\" с помощью escape меню или введите \"|cffffffaa/pfui|r\" в чат.\n\nЖелаю хорошего путешествия!\n\n|cffaaaaaa- Shagu",
|
||||
["Your items have been repaired for"] = "Ваши предметы были отремонтированы за",
|
||||
|
||||
Vendored
+11
-5
@@ -27,7 +27,6 @@ pfUI_translation["zhCN"] = {
|
||||
["Always Show Item Comparison"] = "始终显示装备比较(或按SHIFT键)",
|
||||
["Always Show On Target Units"] = "始终在目标单位上显示",
|
||||
["Always Show On Units With Missing HP"] = "始终在未满血单位上显示",
|
||||
["Always Show Self In Raid Frames"] = "始终在团队框架中显示自己",
|
||||
["Always Use 2D Portraits"] = "始终使用2D头像",
|
||||
["Ammo Counter"] = "弹药数量",
|
||||
["Anchor Bags Above Chat"] = "将背包锚定在聊天框上方",
|
||||
@@ -52,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"] = "动作条背景",
|
||||
@@ -124,6 +125,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Click Casting"] = "点击施法",
|
||||
["Clock"] = "时间",
|
||||
["Close"] = "关闭",
|
||||
["Collapse Empty Slots"] = nil,
|
||||
["Color"] = "颜色",
|
||||
["Color Buff Stacks"] = "Buff堆叠颜色",
|
||||
["Color Debuff Stacks"] = "Debuff堆叠颜色",
|
||||
@@ -138,7 +140,6 @@ pfUI_translation["zhCN"] = {
|
||||
["Combat Timer"] = "战斗计时器",
|
||||
["Combopoint Height"] = "连击点高度",
|
||||
["Combopoint Width"] = "连击点宽度",
|
||||
["Compare Item Base Stats"] = "基于属性的装备对比",
|
||||
["Components"] = "组件",
|
||||
["Config UI Settings"] = "界面设置",
|
||||
["Configuration"] = "配置",
|
||||
@@ -291,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"] = "叠加显示",
|
||||
@@ -312,6 +312,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Encode"] = "编码",
|
||||
["Ended"] = "结束",
|
||||
["Energy Color"] = "能量值颜色",
|
||||
["Equipped"] = "已装备",
|
||||
["Equipped Item Color"] = "已装备物品颜色",
|
||||
["Estimate Debuffs"] = "预估Debuffs",
|
||||
["Estimate Enemy Health Points"] = "预估敌人的生命值",
|
||||
@@ -569,6 +570,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Overwrite If Unit Is Attacking Others"] = "如果单位正在攻击其它单位则变色",
|
||||
["Overwrite If Unit Is Attacking You"] = "如果单位正在攻击你则变色",
|
||||
["Overwrite If Unit Is Casting"] = "如果单位正在施法则变色",
|
||||
["Owner Name"] = nil,
|
||||
["Pageable"] = "可分页",
|
||||
["Paging Actionbar"] = "分页动作条",
|
||||
["Panel"] = "面板",
|
||||
@@ -616,12 +618,13 @@ pfUI_translation["zhCN"] = {
|
||||
["Raid Mark X-Offset"] = nil,
|
||||
["Raid Mark Y-Offset"] = nil,
|
||||
["Raid Padding"] = "团队填充",
|
||||
["Raid-Pet"] = nil,
|
||||
["Random"] = "给随机玩家",
|
||||
["Random Roll Announcement Rarity"] = "随机Roll点稀有度",
|
||||
["Random Rolling"] = "随机Roll点 物品:",
|
||||
["Range Based Hunter Paging"] = "启用基于范围的自动分页[|cff7fff7f猎人|r]",
|
||||
["Range Check Interval"] = "范围检查间隔",
|
||||
["Rank"] = "军衔",
|
||||
["Recast Totem"] = nil,
|
||||
["Red Border On Enemy Units"] = "显示敌方单位红色边框",
|
||||
["Red Name Text On Infight Units"] = "进战斗的单位显示红色姓名",
|
||||
["Regional Settings"] = "区域设置",
|
||||
@@ -703,6 +706,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Show Description"] = "显示描述",
|
||||
["Show Dispel Indicators"] = "显示驱散指示器",
|
||||
["Show Druid Mana Bar"] = "显示德鲁伊法力条",
|
||||
["Show Druid Mana Bar Text"] = nil,
|
||||
["Show Duration Inside Buff"] = "显示持续时间在Buff里面",
|
||||
["Show Empty Buttons"] = "显示空按钮",
|
||||
["Show FPS and Latency Colors"] = "显示帧数以及延迟颜色",
|
||||
@@ -740,6 +744,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Show Required Questitem Count"] = "显示所需的任务物品计数",
|
||||
["Show Resting"] = "显示休息图标",
|
||||
["Show Self In Group Frames"] = "在队伍框架中显示自己",
|
||||
["Show Self In Raid Frames When Solo"] = nil,
|
||||
["Show Spell Icon"] = "显示技能图标",
|
||||
["Show Spell Name"] = "显示技能名称",
|
||||
["Show Stacks"] = "显示堆叠",
|
||||
@@ -784,6 +789,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Target Castbar"] = "目标施法条",
|
||||
["Target Debuff Bar"] = "目标Debuffs条",
|
||||
["Target Nameplate Zoom Factor"] = "目标姓名板缩放系数",
|
||||
["Target Totem"] = nil,
|
||||
["Target-Target"] = "目标的目标",
|
||||
["Target-Target-Target"] = "目标的目标的目标",
|
||||
["Text"] = "文本",
|
||||
@@ -816,6 +822,7 @@ pfUI_translation["zhCN"] = {
|
||||
["Top Actionbar"] = "上方动作条",
|
||||
["Top Left"] = "左上方",
|
||||
["Top Right"] = "右上方",
|
||||
["Total"] = "总计",
|
||||
["Total Gold"] = "总黄金",
|
||||
["Totem Direction"] = "图腾方向",
|
||||
["Totem Icons"] = "图腾图标",
|
||||
@@ -878,7 +885,6 @@ pfUI_translation["zhCN"] = {
|
||||
["XP Percentage"] = "经验百分比",
|
||||
["Yellow Border On Neutral Units"] = "显示中立单位黄色边框",
|
||||
["Yes"] = "是",
|
||||
["You gain (.+) Mana from Totemic Recall"] = "你从图腾召回获得了(.+)点法力值",
|
||||
["You got"] = "你已得到",
|
||||
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "你的界面现在已经完成设置.高级设置请点击游戏菜单或者输入命令/pfui进行设置.祝您游戏愉快",
|
||||
["Your items have been repaired for"] = "你的物品已经修好了",
|
||||
|
||||
Vendored
+11
-5
@@ -27,7 +27,6 @@ pfUI_translation["zhTW"] = {
|
||||
["Always Show Item Comparison"] = "始終顯示裝備比較",
|
||||
["Always Show On Target Units"] = nil,
|
||||
["Always Show On Units With Missing HP"] = nil,
|
||||
["Always Show Self In Raid Frames"] = nil,
|
||||
["Always Use 2D Portraits"] = "始終使用2D頭像",
|
||||
["Ammo Counter"] = "彈藥數量",
|
||||
["Anchor Bags Above Chat"] = nil,
|
||||
@@ -52,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,
|
||||
@@ -124,6 +125,7 @@ pfUI_translation["zhTW"] = {
|
||||
["Click Casting"] = nil,
|
||||
["Clock"] = "時間",
|
||||
["Close"] = "關閉",
|
||||
["Collapse Empty Slots"] = nil,
|
||||
["Color"] = nil,
|
||||
["Color Buff Stacks"] = nil,
|
||||
["Color Debuff Stacks"] = nil,
|
||||
@@ -138,7 +140,6 @@ pfUI_translation["zhTW"] = {
|
||||
["Combat Timer"] = "戰鬥計時器",
|
||||
["Combopoint Height"] = nil,
|
||||
["Combopoint Width"] = nil,
|
||||
["Compare Item Base Stats"] = "基於屬性的裝備對比",
|
||||
["Components"] = "組件",
|
||||
["Config UI Settings"] = nil,
|
||||
["Configuration"] = "配置",
|
||||
@@ -291,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"] = "疊加顯示",
|
||||
@@ -312,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,
|
||||
@@ -569,6 +570,7 @@ pfUI_translation["zhTW"] = {
|
||||
["Overwrite If Unit Is Attacking Others"] = "如果单位正在攻擊其它单位則覆蓋",
|
||||
["Overwrite If Unit Is Attacking You"] = "如果单位正在攻擊你則覆蓋",
|
||||
["Overwrite If Unit Is Casting"] = "如果单位正在施法則覆蓋",
|
||||
["Owner Name"] = nil,
|
||||
["Pageable"] = nil,
|
||||
["Paging Actionbar"] = nil,
|
||||
["Panel"] = "面板",
|
||||
@@ -616,12 +618,13 @@ pfUI_translation["zhTW"] = {
|
||||
["Raid Mark X-Offset"] = nil,
|
||||
["Raid Mark Y-Offset"] = nil,
|
||||
["Raid Padding"] = nil,
|
||||
["Raid-Pet"] = nil,
|
||||
["Random"] = "給隨機玩家",
|
||||
["Random Roll Announcement Rarity"] = "隨機Roll點公示稀有度",
|
||||
["Random Rolling"] = "隨機Roll點 物品:",
|
||||
["Range Based Hunter Paging"] = nil,
|
||||
["Range Check Interval"] = "範圍檢查間隔",
|
||||
["Rank"] = nil,
|
||||
["Recast Totem"] = nil,
|
||||
["Red Border On Enemy Units"] = nil,
|
||||
["Red Name Text On Infight Units"] = nil,
|
||||
["Regional Settings"] = nil,
|
||||
@@ -703,6 +706,7 @@ pfUI_translation["zhTW"] = {
|
||||
["Show Description"] = nil,
|
||||
["Show Dispel Indicators"] = nil,
|
||||
["Show Druid Mana Bar"] = nil,
|
||||
["Show Druid Mana Bar Text"] = nil,
|
||||
["Show Duration Inside Buff"] = "顯示持續時間在Buff裏面",
|
||||
["Show Empty Buttons"] = nil,
|
||||
["Show FPS and Latency Colors"] = nil,
|
||||
@@ -739,6 +743,7 @@ pfUI_translation["zhTW"] = {
|
||||
["Show Required Questitem Count"] = nil,
|
||||
["Show Resting"] = nil,
|
||||
["Show Self In Group Frames"] = nil,
|
||||
["Show Self In Raid Frames When Solo"] = nil,
|
||||
["Show Spell Icon"] = nil,
|
||||
["Show Spell Name"] = nil,
|
||||
["Show Stacks"] = nil,
|
||||
@@ -783,6 +788,7 @@ pfUI_translation["zhTW"] = {
|
||||
["Target Castbar"] = nil,
|
||||
["Target Debuff Bar"] = nil,
|
||||
["Target Nameplate Zoom Factor"] = nil,
|
||||
["Target Totem"] = nil,
|
||||
["Target-Target"] = "目標的目標",
|
||||
["Target-Target-Target"] = nil,
|
||||
["Text"] = nil,
|
||||
@@ -815,6 +821,7 @@ pfUI_translation["zhTW"] = {
|
||||
["Top Actionbar"] = nil,
|
||||
["Top Left"] = "左上方",
|
||||
["Top Right"] = "右上方",
|
||||
["Total"] = "全部",
|
||||
["Total Gold"] = "總金量",
|
||||
["Totem Direction"] = nil,
|
||||
["Totem Icons"] = nil,
|
||||
@@ -877,7 +884,6 @@ pfUI_translation["zhTW"] = {
|
||||
["XP Percentage"] = "經驗百分比",
|
||||
["Yellow Border On Neutral Units"] = nil,
|
||||
["Yes"] = nil,
|
||||
["You gain (.+) Mana from Totemic Recall"] = nil,
|
||||
["You got"] = nil,
|
||||
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "您的UI已經設置完畢.使用遊戲菜單或在聊天窗口輸入\"|cffffffaa/pfui|r\"開啟高級設置.祝您遊戲愉快!\n\n|cffaaaaaa- Shagu",
|
||||
["Your items have been repaired for"] = "你的物品已經修好了",
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<Include file="..\libs\libtipscan.lua"/>
|
||||
<Include file="..\libs\libspell.lua"/>
|
||||
<Include file="..\libs\libcast.lua"/>
|
||||
<Include file="..\libs\libdebuff.lua"/>
|
||||
<Include file="..\libs\librange.lua"/>
|
||||
<Include file="..\libs\libunitscan.lua"/>
|
||||
<Include file="..\libs\libtooltip.lua"/>
|
||||
<Include file="..\libs\libhealth.lua"/>
|
||||
<Include file="..\libs\libtotem.lua"/>
|
||||
<Include file="..\libs\libthrottle.lua"/>
|
||||
<Include file="..\libs\libpredict.lua"/>
|
||||
<Include file="..\libs\libbagsort.lua"/>
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
<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"/>
|
||||
@@ -78,4 +79,7 @@
|
||||
<Include file="..\modules\unitxp.lua"/>
|
||||
<Include file="..\modules\bgscore.lua"/>
|
||||
<Include file="..\modules\equipmentmanager.lua"/>
|
||||
<Include file="..\modules\loothistory.lua"/>
|
||||
<Include file="..\modules\newitem.lua"/>
|
||||
<Include file="..\modules\friendnotes.lua"/>
|
||||
</Ui>
|
||||
+1
-2
@@ -1,5 +1,6 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<Include file="..\skins\blizzard\character.lua"/>
|
||||
<Include file="..\skins\blizzard\inspect.lua"/>
|
||||
<Include file="..\skins\blizzard\spellbook.lua"/>
|
||||
<Include file="..\skins\blizzard\friends.lua"/>
|
||||
<Include file="..\skins\blizzard\talents.lua"/>
|
||||
@@ -40,8 +41,6 @@
|
||||
<!-- 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"/>
|
||||
|
||||
</Ui>
|
||||
|
||||
+123
-63
@@ -11,26 +11,27 @@ pfUI.api.libbagsort = libbagsort
|
||||
libbagsort.itemGrid = {}
|
||||
libbagsort.bagList = nil
|
||||
|
||||
local HEARTHSTONE_ITEM_ID = 6948
|
||||
local ItemClass = Enum.ItemClass
|
||||
local ItemQuality = Enum.ItemQuality
|
||||
|
||||
-- Lower prefix = sorted earlier in the bag.
|
||||
local function SortCategoryPrefix(itemId, itemType, itemSubType, quality)
|
||||
if itemId == HEARTHSTONE_ITEM_ID then return "00" end
|
||||
if quality == 0 then return "13" end -- Poor (gray) always last
|
||||
if itemType == "Weapon" or itemType == "Armor" then
|
||||
if quality and quality >= 4 then return "01" end -- Epic+ gear
|
||||
if quality == 3 then return "02" end -- Rare gear
|
||||
if quality == 2 then return "03" end -- Uncommon gear
|
||||
return "04" -- Common/poor gear
|
||||
local function SortCategoryPrefix(itemId, classID, quality)
|
||||
if itemId == HEARTHSTONE_ITEM_ID then return "00" end
|
||||
if quality == ItemQuality.Poor then return "13" end -- gray always last
|
||||
if classID == ItemClass.Weapon or classID == ItemClass.Armor then
|
||||
if quality and quality >= ItemQuality.Epic then return "01" end -- Epic+ gear
|
||||
if quality == ItemQuality.Rare then return "02" end -- Rare gear
|
||||
if quality == ItemQuality.Uncommon then return "03" end -- Uncommon gear
|
||||
return "04" -- Common/poor gear
|
||||
end
|
||||
if itemType == "Consumable" then return "05" end
|
||||
if itemType == "Reagent" then return "06" end
|
||||
if itemType == "Trade Goods" then return "07" end
|
||||
if itemType == "Quest" then return "08" end
|
||||
if classID == ItemClass.Consumable then return "05" end
|
||||
if classID == ItemClass.Reagent then return "06" end
|
||||
if classID == ItemClass.Tradegoods then return "07" end
|
||||
if classID == ItemClass.Questitem then return "08" end
|
||||
-- Non-gear items without a specific type, sorted by quality
|
||||
if quality and quality >= 4 then return "09" end
|
||||
if quality == 3 then return "10" end
|
||||
if quality == 2 then return "11" end
|
||||
if quality and quality >= ItemQuality.Epic then return "09" end
|
||||
if quality == ItemQuality.Rare then return "10" end
|
||||
if quality == ItemQuality.Uncommon then return "11" end
|
||||
return "12"
|
||||
end
|
||||
|
||||
@@ -41,18 +42,31 @@ local function SortCountSuffix(count)
|
||||
return string.sub(s, -6)
|
||||
end
|
||||
|
||||
local function SortKey(itemId, name, itype, subtype, quality, count)
|
||||
return SortCategoryPrefix(itemId, itype, subtype, quality)
|
||||
.. (itype or "") .. "|" .. (subtype or "") .. "|" .. (name or "zzz") .. "|" .. SortCountSuffix(count)
|
||||
local function SortKey(itemId, name, classID, subClassID, quality, count)
|
||||
-- Zero-pad the class/subclass so the secondary grouping sorts numerically
|
||||
-- (as a string, "10" would otherwise precede "2").
|
||||
return SortCategoryPrefix(itemId, classID, quality)
|
||||
.. string.format("%02d|%02d|", classID or 99, subClassID or 99)
|
||||
.. (name or "zzz") .. "|" .. SortCountSuffix(count)
|
||||
end
|
||||
|
||||
local function ClearSortData()
|
||||
libbagsort.itemGrid = {}
|
||||
libbagsort.bagList = nil
|
||||
libbagsort.opts = nil
|
||||
libbagsort:UnregisterEvent("BAG_UPDATE_DELAYED")
|
||||
libbagsort:SetScript("OnEvent", nil)
|
||||
end
|
||||
|
||||
local function ReverseArray(t)
|
||||
local i, j = 1, table.getn(t)
|
||||
while i < j do
|
||||
t[i], t[j] = t[j], t[i]
|
||||
i = i + 1
|
||||
j = j - 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Two-pointer consolidation: sorts stacks largest-first, then merges from
|
||||
-- both ends toward the middle. n is set explicitly so table.getn / table.sort
|
||||
-- work correctly in Lua 5.0.
|
||||
@@ -112,36 +126,61 @@ local function BuildConsolidateOps(bagList)
|
||||
return ops
|
||||
end
|
||||
|
||||
-- Bag family bitmask (1 << (familyID-1)); 0 = general-purpose (holds
|
||||
-- anything). Backpack (0) and bank (-1) are always general. A specialty
|
||||
-- bag's family comes from the equipped bag item -- ClassicAPI derives it
|
||||
-- from the container subclass when the raw field is empty (Turtle leaves
|
||||
-- bags' m_bagFamily at 0), so quivers/soul/profession bags report properly.
|
||||
local function BagFamily(bag)
|
||||
if bag == 0 or bag == -1 then return 0 end
|
||||
local id = GetInventoryItemID("player", ContainerIDToInventoryID(bag))
|
||||
return id and C_Item.GetItemFamily(id) or 0
|
||||
end
|
||||
|
||||
local function BuildSortGrid()
|
||||
local bagList = libbagsort.bagList
|
||||
libbagsort.itemGrid = {}
|
||||
local normalItems = {}
|
||||
local poorItems = {}
|
||||
local bagCount = 0
|
||||
local bagSlots = {}
|
||||
|
||||
-- Destination cells, split by the family they can accept. A specialty
|
||||
-- bag's slots only take items of its own family; general slots take
|
||||
-- anything. Cells are collected in forward order (bag order, slot 1..n).
|
||||
local generalCells = {} -- { {bag=,slot=}, ... }
|
||||
local specialtyCells = {} -- family -> { {bag=,slot=}, ... }
|
||||
|
||||
for _, bag in ipairs(bagList) do
|
||||
bagCount = bagCount + 1
|
||||
local fam = BagFamily(bag)
|
||||
local numSlots = GetContainerNumSlots(bag)
|
||||
bagSlots[bagCount] = numSlots
|
||||
if numSlots > 0 then
|
||||
libbagsort.itemGrid[bag] = {}
|
||||
for slot = 1, numSlots do
|
||||
if fam == 0 then
|
||||
tinsert(generalCells, {bag=bag, slot=slot})
|
||||
else
|
||||
specialtyCells[fam] = specialtyCells[fam] or {}
|
||||
tinsert(specialtyCells[fam], {bag=bag, slot=slot})
|
||||
end
|
||||
|
||||
local itemId = C_Container.GetContainerItemID(bag, slot)
|
||||
if itemId then
|
||||
-- pfUI's compat layer shims GetItemInfo to the modern 10-field
|
||||
-- signature (inserts nil for itemLevel between quality and
|
||||
-- minlevel) — so itype/subtype sit at positions 6/7, not 5/6.
|
||||
local name, _, quality, _, _, itype, subtype = GetItemInfo(itemId)
|
||||
-- C_Item.GetItemInfo is the full 18-field tuple; classID/subClassID
|
||||
-- sit at positions 12/13. We categorize on those numeric class IDs
|
||||
-- rather than the localized itemType/itemSubType strings. (pfUI's
|
||||
-- shimmed global GetItemInfo is only 10 fields and lacks them.)
|
||||
local name, _, quality, _, _, _, _, _, _, _, _, classID, subClassID = C_Item.GetItemInfo(itemId)
|
||||
local _, count = GetContainerItemInfo(bag, slot)
|
||||
local item = {
|
||||
key = SortKey(itemId, name, itype, subtype, quality, count),
|
||||
key = SortKey(itemId, name, classID, subClassID, quality, count),
|
||||
-- vanilla items carry at most one family bit, so equality
|
||||
-- against a bag family suffices (no bit.band needed).
|
||||
family = C_Item.GetItemFamily(itemId) or 0,
|
||||
srcBag = bag,
|
||||
srcSlot = slot,
|
||||
curBag = bag,
|
||||
curSlot = slot,
|
||||
}
|
||||
if quality == 0 then
|
||||
if quality == ItemQuality.Poor then
|
||||
tinsert(poorItems, item)
|
||||
else
|
||||
tinsert(normalItems, item)
|
||||
@@ -152,46 +191,61 @@ local function BuildSortGrid()
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(normalItems, function(a, b) return a.key < b.key end)
|
||||
-- Sort poor items descending so they read in ascending order when placed
|
||||
-- back-to-front (last poor item lands on the last slot).
|
||||
table.sort(poorItems, function(a, b) return a.key > b.key end)
|
||||
local opts = libbagsort.opts or {}
|
||||
local reverse = opts.reverse
|
||||
local reversePrio = opts.reversePrio
|
||||
|
||||
-- Forward pass: assign normal items from slot 1 of bag 1 onward
|
||||
local bagIdx, destSlot = 1, 1
|
||||
while bagIdx <= bagCount and bagSlots[bagIdx] == 0 do
|
||||
bagIdx = bagIdx + 1
|
||||
if reverse then
|
||||
ReverseArray(generalCells)
|
||||
for _, cells in pairs(specialtyCells) do
|
||||
ReverseArray(cells)
|
||||
end
|
||||
end
|
||||
|
||||
if reversePrio then
|
||||
table.sort(normalItems, function(a, b) return a.key > b.key end)
|
||||
else
|
||||
table.sort(normalItems, function(a, b) return a.key < b.key end)
|
||||
end
|
||||
|
||||
if reverse then
|
||||
table.sort(poorItems, function(a, b) return a.key < b.key end)
|
||||
else
|
||||
table.sort(poorItems, function(a, b) return a.key > b.key end)
|
||||
end
|
||||
|
||||
-- Forward pass: route each normal item into the next free cell that
|
||||
-- accepts it -- a matching specialty bag first, overflowing to general.
|
||||
local genIdx = 1
|
||||
local specIdx = {} -- family -> next free index into specialtyCells[family]
|
||||
for _, item in ipairs(normalItems) do
|
||||
while bagIdx <= bagCount do
|
||||
if destSlot <= bagSlots[bagIdx] then break end
|
||||
bagIdx = bagIdx + 1
|
||||
destSlot = 1
|
||||
local cell
|
||||
local fam = item.family
|
||||
if fam ~= 0 and specialtyCells[fam] then
|
||||
local i = specIdx[fam] or 1
|
||||
if i <= table.getn(specialtyCells[fam]) then
|
||||
cell = specialtyCells[fam][i]
|
||||
specIdx[fam] = i + 1
|
||||
end
|
||||
end
|
||||
if bagIdx > bagCount then break end
|
||||
local grid = libbagsort.itemGrid[item.srcBag][item.srcSlot]
|
||||
grid.destBag = bagList[bagIdx]
|
||||
grid.destSlot = destSlot
|
||||
destSlot = destSlot + 1
|
||||
if not cell and genIdx <= table.getn(generalCells) then
|
||||
cell = generalCells[genIdx]
|
||||
genIdx = genIdx + 1
|
||||
end
|
||||
if not cell then break end
|
||||
item.destBag = cell.bag
|
||||
item.destSlot = cell.slot
|
||||
end
|
||||
|
||||
-- Reverse pass: assign poor items from the last slot of the last bag backward
|
||||
local rBagIdx = bagCount
|
||||
local rDestSlot = 0
|
||||
while rBagIdx >= 1 do
|
||||
if bagSlots[rBagIdx] > 0 then rDestSlot = bagSlots[rBagIdx]; break end
|
||||
rBagIdx = rBagIdx - 1
|
||||
end
|
||||
-- Reverse pass: poor items are general; fill remaining general cells from
|
||||
-- the back, stopping before the ones the forward pass already claimed.
|
||||
local genBack = table.getn(generalCells)
|
||||
for _, item in ipairs(poorItems) do
|
||||
while rBagIdx >= 1 and rDestSlot < 1 do
|
||||
rBagIdx = rBagIdx - 1
|
||||
rDestSlot = rBagIdx >= 1 and bagSlots[rBagIdx] or 0
|
||||
end
|
||||
if rBagIdx < 1 then break end
|
||||
local grid = libbagsort.itemGrid[item.srcBag][item.srcSlot]
|
||||
grid.destBag = bagList[rBagIdx]
|
||||
grid.destSlot = rDestSlot
|
||||
rDestSlot = rDestSlot - 1
|
||||
if genBack < genIdx then break end
|
||||
local cell = generalCells[genBack]
|
||||
genBack = genBack - 1
|
||||
item.destBag = cell.bag
|
||||
item.destSlot = cell.slot
|
||||
end
|
||||
end
|
||||
|
||||
@@ -248,9 +302,15 @@ end
|
||||
-- BAG_UPDATE_DELAYED cycle), then place items by category/name/quality.
|
||||
-- e.g. `libbagsort:Sort({0, 1, 2, 3, 4})` for the main bags;
|
||||
-- `{-1, 5, 6, 7, 8, 9, 10}` for the bank.
|
||||
function libbagsort:Sort(bagList)
|
||||
--
|
||||
-- opts (optional): { reverse = bool, reversePrio = bool }
|
||||
-- reverse - place the first-ranked item into the last slot of the last
|
||||
-- bag (junk fills from the opposite end).
|
||||
-- reversePrio - flip the category ranking (e.g. hearthstone sorts last).
|
||||
function libbagsort:Sort(bagList, opts)
|
||||
ClearSortData()
|
||||
self.bagList = bagList
|
||||
self.opts = opts
|
||||
|
||||
-- Phase 1: fire every consolidation op in a single batch.
|
||||
local ops = BuildConsolidateOps(bagList)
|
||||
|
||||
@@ -1,571 +0,0 @@
|
||||
-- load pfUI environment
|
||||
setfenv(1, pfUI:GetEnvironment())
|
||||
|
||||
--[[ libcast ]]--
|
||||
-- A pfUI library that detects and saves all ongoing castbars of players, NPCs and enemies.
|
||||
-- The library also includes spells that usually don't have a castbar like Multi-Shot and Aimed Shot.
|
||||
-- This is exclusivly used for vanilla in order to provide pfGetChannelInfo and pfGetCastInfo functions.
|
||||
--
|
||||
-- External functions:
|
||||
-- pfGetChannelInfo(unit)
|
||||
-- Returns information on the spell currently cast by the specified unit.
|
||||
-- Returns nil if no spell is being cast.
|
||||
--
|
||||
-- cast[String] - The name of the spell, or nil if no spell is being cast.
|
||||
-- nameSubtext[String] - (DUMMY) The string describing the rank of the spell, e.g. "Rank 1".
|
||||
-- text[String] - The name to be displayed.
|
||||
-- texture[String] - The texture path associated with the spell.
|
||||
-- startTime[Number] - Specifies when casting has begun, in milliseconds.
|
||||
-- endTime[Number] - Specifies when casting will end, in milliseconds.
|
||||
-- isTradeSkill[Boolean] - (DUMMY) Specifies if the cast is a tradeskill
|
||||
--
|
||||
-- pfGetCastInfo(unit)
|
||||
-- Returns information on the spell currently channeled by the specified unit.
|
||||
-- Returns nil if no spell is being channeled.
|
||||
--
|
||||
-- cast[String] - The name of the spell, or nil if no spell is being cast.
|
||||
-- nameSubtext[String] - (DUMMY) The string describing the rank of the spell, e.g. "Rank 1".
|
||||
-- text[String] - The name to be displayed.
|
||||
-- texture[String] - The texture path associated with the spell.
|
||||
-- startTime[Number] - Specifies when casting has begun, in milliseconds.
|
||||
-- endTime[Number] - Specifies when casting will end, in milliseconds.
|
||||
-- isTradeSkill[Boolean] - (DUMMY) Specifies if the cast is a tradeskill
|
||||
--
|
||||
-- Internal functions:
|
||||
-- libcast:AddAction(mob, spell, channel)
|
||||
-- Adds a spell to the database by using pfUI's spell database
|
||||
-- to obtain durations and icons
|
||||
--
|
||||
-- libcast:RemoveAction(mob, spell)
|
||||
-- Removes the castbar of a given mob, if `spell` is an interrupt.
|
||||
-- spell can be set to "INTERRUPT" to force remove an action.
|
||||
--
|
||||
|
||||
-- return instantly when another libcast is already active
|
||||
if pfUI.api.libcast then return end
|
||||
|
||||
local lastcasttex, lastrank, _
|
||||
|
||||
local libcast = CreateFrame("Frame", "pfEnemyCast")
|
||||
local player = UnitName("player")
|
||||
|
||||
pfGetChannelInfo = function(unit)
|
||||
-- convert to name if unitstring was given
|
||||
local unitName = pfValidUnits[unit] and UnitName(unit) or unit
|
||||
|
||||
-- Get GUID if Nampower is available
|
||||
local guid = nil
|
||||
|
||||
-- Check if unit itself is a GUID (starts with "0x")
|
||||
if type(unit) == "string" and string.sub(unit, 1, 2) == "0x" then
|
||||
guid = unit -- unit IS the GUID
|
||||
elseif pfValidUnits[unit] and UnitExists then
|
||||
-- unit is a token like "target" - get GUID from it
|
||||
local unitGuid = UnitGUID(unit)
|
||||
guid = unitGuid
|
||||
end
|
||||
|
||||
-- For player: ALWAYS use libcast.db because it handles channel updates correctly
|
||||
local isPlayer = unit == "player" or unitName == player
|
||||
|
||||
if isPlayer then
|
||||
local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
||||
local db = libcast.db[player]
|
||||
|
||||
if db and db.cast and db.start + db.casttime / 1000 > GetTime() then
|
||||
if not db.channel then return end
|
||||
cast = db.cast
|
||||
nameSubtext = db.rank
|
||||
text = ""
|
||||
texture = db.icon
|
||||
startTime = db.start * 1000
|
||||
endTime = startTime + db.casttime
|
||||
isTradeSkill = nil
|
||||
elseif db then
|
||||
db.cast = nil
|
||||
db.rank = nil
|
||||
db.start = nil
|
||||
db.casttime = nil
|
||||
db.icon = nil
|
||||
db.channel = nil
|
||||
end
|
||||
|
||||
return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
||||
end
|
||||
|
||||
-- For non-player units: use libdebuff GUID-based tracking or libcast.db
|
||||
-- Try GUID-based lookup first (from libdebuff's SPELL_START tracking)
|
||||
local db = nil
|
||||
if guid and pfUI.libdebuff_casts and pfUI.libdebuff_casts[guid] then
|
||||
-- Use libdebuff's cast tracking (from SPELL_START_OTHER events)
|
||||
local castData = pfUI.libdebuff_casts[guid]
|
||||
if castData.event == "START" and castData.endTime and castData.endTime > GetTime() then
|
||||
-- Convert libdebuff format to libcast format
|
||||
db = {
|
||||
cast = castData.spellName,
|
||||
rank = nil,
|
||||
start = castData.startTime,
|
||||
casttime = castData.duration * 1000, -- Convert back to ms
|
||||
icon = castData.icon,
|
||||
channel = nil -- TODO: libdebuff should distinguish channel vs cast
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Fallback to name-based lookup (CHAT_MSG castbars)
|
||||
-- Skip when GUID is available to avoid same-name mob bleed
|
||||
if not db and not guid and libcast.db[unitName] then
|
||||
db = libcast.db[unitName]
|
||||
end
|
||||
|
||||
-- Fallback to libcast.db for non-player units
|
||||
local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
||||
|
||||
if db and db.cast and db.start + db.casttime / 1000 > GetTime() then
|
||||
if not db.channel then return end
|
||||
cast = db.cast
|
||||
nameSubtext = db.rank
|
||||
text = ""
|
||||
texture = db.icon
|
||||
startTime = db.start * 1000
|
||||
endTime = startTime + db.casttime
|
||||
isTradeSkill = nil
|
||||
elseif db then
|
||||
db.cast = nil
|
||||
db.rank = nil
|
||||
db.start = nil
|
||||
db.casttime = nil
|
||||
db.icon = nil
|
||||
db.channel = nil
|
||||
end
|
||||
|
||||
return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
||||
end
|
||||
|
||||
pfGetCastInfo = function(unit)
|
||||
-- convert to name if unitstring was given
|
||||
local unitName = pfValidUnits[unit] and UnitName(unit) or unit
|
||||
|
||||
-- Get GUID if Nampower is available
|
||||
local guid = nil
|
||||
|
||||
-- Check if unit itself is a GUID (starts with "0x")
|
||||
if type(unit) == "string" and string.sub(unit, 1, 2) == "0x" then
|
||||
guid = unit -- unit IS the GUID
|
||||
elseif pfValidUnits[unit] and UnitExists then
|
||||
-- unit is a token like "target" - get GUID from it
|
||||
local unitGuid = UnitGUID(unit)
|
||||
guid = unitGuid
|
||||
end
|
||||
|
||||
-- For player: ALWAYS use libcast.db because it handles pushback correctly
|
||||
local isPlayer = unit == "player" or unitName == player
|
||||
|
||||
if isPlayer then
|
||||
local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
||||
local db = libcast.db[player]
|
||||
|
||||
if db and db.cast and db.start + db.casttime / 1000 > GetTime() then
|
||||
if db.channel then return end
|
||||
cast = db.cast
|
||||
nameSubtext = db.rank or ""
|
||||
text = ""
|
||||
texture = db.icon
|
||||
startTime = db.start * 1000
|
||||
endTime = startTime + db.casttime
|
||||
isTradeSkill = nil
|
||||
elseif db then
|
||||
db.cast = nil
|
||||
db.rank = nil
|
||||
db.start = nil
|
||||
db.casttime = nil
|
||||
db.icon = nil
|
||||
db.channel = nil
|
||||
end
|
||||
|
||||
return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
||||
end
|
||||
|
||||
-- For non-player units: use libdebuff GUID-based tracking or libcast.db
|
||||
-- Try GUID-based lookup first (from libdebuff's SPELL_START tracking)
|
||||
local db = nil
|
||||
if guid and pfUI.libdebuff_casts and pfUI.libdebuff_casts[guid] then
|
||||
-- Use libdebuff's cast tracking (from SPELL_START_OTHER events)
|
||||
local castData = pfUI.libdebuff_casts[guid]
|
||||
if castData.event == "START" and castData.endTime and castData.endTime > GetTime() then
|
||||
-- Convert libdebuff format to libcast format
|
||||
db = {
|
||||
cast = castData.spellName,
|
||||
rank = nil,
|
||||
start = castData.startTime,
|
||||
casttime = castData.duration * 1000, -- Convert back to ms
|
||||
icon = castData.icon,
|
||||
channel = nil -- TODO: libdebuff should distinguish channel vs cast
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Fallback to name-based lookup (CHAT_MSG castbars)
|
||||
-- Skip when GUID is available to avoid same-name mob bleed
|
||||
if not db and not guid and libcast.db[unitName] then
|
||||
db = libcast.db[unitName]
|
||||
end
|
||||
|
||||
-- Fallback to libcast.db for non-player units
|
||||
local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
||||
|
||||
if db and db.cast and db.start + db.casttime / 1000 > GetTime() then
|
||||
if db.channel then return end
|
||||
cast = db.cast
|
||||
nameSubtext = db.rank or ""
|
||||
text = ""
|
||||
texture = db.icon
|
||||
startTime = db.start * 1000
|
||||
endTime = startTime + db.casttime
|
||||
isTradeSkill = nil
|
||||
elseif db then
|
||||
db.cast = nil
|
||||
db.rank = nil
|
||||
db.start = nil
|
||||
db.casttime = nil
|
||||
db.icon = nil
|
||||
db.channel = nil
|
||||
end
|
||||
|
||||
return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
||||
end
|
||||
|
||||
function libcast:AddAction(mob, spell, channel)
|
||||
if not mob or not spell then return nil end
|
||||
|
||||
if L["spells"][spell] ~= nil then
|
||||
local casttime = L["spells"][spell].t
|
||||
local icon = L["spells"][spell].icon and string.format("%s%s", "Interface\\Icons\\", L["spells"][spell].icon) or nil
|
||||
|
||||
-- add cast action to the database
|
||||
if not self.db[mob] then self.db[mob] = {} end
|
||||
self.db[mob].cast = spell
|
||||
self.db[mob].rank = nil
|
||||
self.db[mob].start = GetTime()
|
||||
self.db[mob].casttime = casttime
|
||||
self.db[mob].icon = icon
|
||||
self.db[mob].channel = channel
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function libcast:RemoveAction(mob, spell)
|
||||
if self.db[mob] and ( L["interrupts"][spell] ~= nil or spell == "INTERRUPT" ) then
|
||||
|
||||
-- remove cast action to the database
|
||||
self.db[mob].cast = nil
|
||||
self.db[mob].rank = nil
|
||||
self.db[mob].start = nil
|
||||
self.db[mob].casttime = nil
|
||||
self.db[mob].icon = nil
|
||||
self.db[mob].channel = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- main data
|
||||
libcast.db = { [player] = {} }
|
||||
|
||||
-- environmental casts
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PARTY_BUFF")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE")
|
||||
libcast:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF")
|
||||
|
||||
-- player spells
|
||||
libcast:RegisterEvent("SPELLCAST_START")
|
||||
libcast:RegisterEvent("SPELLCAST_STOP")
|
||||
libcast:RegisterEvent("SPELLCAST_FAILED")
|
||||
libcast:RegisterEvent("SPELLCAST_INTERRUPTED")
|
||||
libcast:RegisterEvent("SPELLCAST_DELAYED")
|
||||
libcast:RegisterEvent("SPELLCAST_CHANNEL_START")
|
||||
libcast:RegisterEvent("SPELLCAST_CHANNEL_STOP")
|
||||
libcast:RegisterEvent("SPELLCAST_CHANNEL_UPDATE")
|
||||
|
||||
local mob, spell, icon, _
|
||||
local lastSpellId = nil -- spellId cached from SPELL_START_SELF (Nampower)
|
||||
|
||||
libcast:SetScript("OnEvent", function()
|
||||
-- Fill database with player casts
|
||||
if event == "SPELLCAST_START" then
|
||||
-- Get icon via spellId cached from SPELL_START_SELF
|
||||
icon = lastSpellId and C_Spell.GetSpellTexture(lastSpellId) or nil
|
||||
-- fallback to L["spells"] / lastcasttex if no icon resolved
|
||||
if not icon then
|
||||
icon = L["spells"][arg1] and L["spells"][arg1].icon and string.format("%s%s", "Interface\\Icons\\", L["spells"][arg1].icon) or lastcasttex
|
||||
end
|
||||
lastSpellId = nil
|
||||
|
||||
this.db[player].cast = arg1
|
||||
this.db[player].rank = lastrank
|
||||
this.db[player].start = GetTime()
|
||||
this.db[player].casttime = arg2
|
||||
this.db[player].icon = icon
|
||||
this.db[player].channel = nil
|
||||
|
||||
if not L["spells"][arg1] or not L["spells"][arg1].icon or not L["spells"][arg1].t then
|
||||
L["spells"][arg1] = L["spells"][arg1] or { }
|
||||
L["spells"][arg1].icon = L["spells"][arg1].icon or icon
|
||||
L["spells"][arg1].t = L["spells"][arg1].t or arg2
|
||||
end
|
||||
lastcasttex, lastrank = nil, nil
|
||||
elseif event == "SPELLCAST_STOP" or event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED" then
|
||||
lastSpellId = nil
|
||||
if this.db[player] and not this.db[player].channel then
|
||||
-- remove cast action to the database
|
||||
this.db[player].cast = nil
|
||||
this.db[player].rank = nil
|
||||
this.db[player].rank = nil
|
||||
this.db[player].start = nil
|
||||
this.db[player].casttime = nil
|
||||
this.db[player].icon = nil
|
||||
this.db[player].channel = nil
|
||||
else
|
||||
lastcasttex, lastrank = nil, nil
|
||||
end
|
||||
elseif event == "SPELLCAST_DELAYED" then
|
||||
if this.db[player].cast then
|
||||
-- Pushback: increase casttime instead of shifting start
|
||||
-- arg1 is the delay amount in milliseconds
|
||||
this.db[player].casttime = this.db[player].casttime + arg1
|
||||
end
|
||||
elseif event == "SPELLCAST_CHANNEL_START" then
|
||||
-- add cast action to the database
|
||||
this.db[player].cast = arg2
|
||||
this.db[player].rank = lastrank
|
||||
this.db[player].start = GetTime()
|
||||
this.db[player].casttime = arg1
|
||||
this.db[player].icon = L["spells"][arg2] and L["spells"][arg2].icon and string.format("%s%s", "Interface\\Icons\\", L["spells"][arg2].icon) or lastcasttex
|
||||
this.db[player].channel = true
|
||||
|
||||
lastcasttex, lastrank = nil, nil
|
||||
elseif event == "SPELLCAST_CHANNEL_STOP" then
|
||||
if this.db[player] and this.db[player].channel then
|
||||
-- remove cast action to the database
|
||||
this.db[player].cast = nil
|
||||
this.db[player].rank = nil
|
||||
this.db[player].start = nil
|
||||
this.db[player].casttime = nil
|
||||
this.db[player].icon = nil
|
||||
this.db[player].channel = nil
|
||||
end
|
||||
elseif event == "SPELLCAST_CHANNEL_UPDATE" then
|
||||
if this.db[player].cast then
|
||||
this.db[player].start = -this.db[player].casttime/1000 + GetTime() + arg1/1000
|
||||
end
|
||||
-- Fill database with environmental casts
|
||||
elseif arg1 then
|
||||
-- (.+) begins to cast (.+).
|
||||
mob, spell = cmatch(arg1, SPELLCASTOTHERSTART)
|
||||
if libcast:AddAction(mob, spell) then return end
|
||||
|
||||
-- (.+) begins to perform (.+).
|
||||
mob, spell = cmatch(arg1, SPELLPERFORMOTHERSTART)
|
||||
if libcast:AddAction(mob, spell) then return end
|
||||
|
||||
-- (.+) gains (.+).
|
||||
mob, spell = cmatch(arg1, AURAADDEDOTHERHELPFUL)
|
||||
if libcast:RemoveAction(mob, spell) then return end
|
||||
|
||||
-- (.+) is afflicted by (.+).
|
||||
mob, spell = cmatch(arg1, AURAADDEDOTHERHARMFUL)
|
||||
if libcast:RemoveAction(mob, spell) then return end
|
||||
|
||||
-- Your (.+) hits (.+) for (%d+).
|
||||
spell, mob = cmatch(arg1, SPELLLOGSELFOTHER)
|
||||
if libcast:RemoveAction(mob, spell) then return end
|
||||
|
||||
-- Your (.+) crits (.+) for (%d+).
|
||||
spell, mob = cmatch(arg1, SPELLLOGCRITSELFOTHER)
|
||||
if libcast:RemoveAction(mob, spell) then return end
|
||||
|
||||
-- (.+)'s (.+) %a hits (.+) for (%d+).
|
||||
_, spell, mob = cmatch(arg1, SPELLLOGOTHEROTHER)
|
||||
if libcast:RemoveAction(mob, spell) then return end
|
||||
|
||||
-- (.+)'s (.+) %a crits (.+) for (%d+).
|
||||
_, spell, mob = cmatch(arg1, SPELLLOGCRITOTHEROTHER)
|
||||
if libcast:RemoveAction(mob, spell) then return end
|
||||
|
||||
-- You interrupt (.+)'s (.+).
|
||||
mob, _ = cmatch(arg1, SPELLINTERRUPTSELFOTHER)
|
||||
if libcast:RemoveAction(mob, "INTERRUPT") then return end
|
||||
|
||||
-- (.+) interrupts (.+)'s (.+).
|
||||
_, mob, _ = cmatch(arg1, SPELLINTERRUPTOTHEROTHER)
|
||||
if libcast:RemoveAction(mob, "INTERRUPT") then return end
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Custom Casts
|
||||
Enable Castbars for spells that don't have a castbar by default
|
||||
(e.g Multi-Shot and Aimed Shot)
|
||||
]]--
|
||||
local aimedshot = L["customcast"]["AIMEDSHOT"]
|
||||
local multishot = L["customcast"]["MULTISHOT"]
|
||||
|
||||
-- Shot-timer haste lookup. Keyed by icon path because some of these buffs have
|
||||
-- spell-ID variants across server flavors but stable icons. Berserking is the
|
||||
-- variable Troll-racial calculation (more haste at lower HP); other entries are
|
||||
-- flat duration multipliers.
|
||||
local SHOT_HASTE = {
|
||||
["Interface\\Icons\\Racial_Troll_Berserk"] = "berserking",
|
||||
["Interface\\Icons\\Ability_Hunter_RunningShot"] = 1.4,
|
||||
["Interface\\Icons\\Ability_Warrior_InnerRage"] = 1.3,
|
||||
["Interface\\Icons\\Inv_Trinket_Naxxramas04"] = 1.2,
|
||||
}
|
||||
|
||||
function libcast.ApplyShotHaste(duration)
|
||||
for _, a in ipairs(C_UnitAuras.GetUnitAuras("player", "HELPFUL")) do
|
||||
local mult = SHOT_HASTE[a.icon]
|
||||
if mult == "berserking" then
|
||||
local hp = UnitHealth("player") / UnitHealthMax("player")
|
||||
local berserk = hp >= 0.40 and (1.30 - hp) / 3 or 0.3
|
||||
duration = duration / (1 + berserk)
|
||||
elseif mult then
|
||||
duration = duration / mult
|
||||
end
|
||||
end
|
||||
return duration
|
||||
end
|
||||
|
||||
libcast.customcast = {}
|
||||
libcast.customcast[strlower(aimedshot)] = function(begin, duration)
|
||||
if begin then
|
||||
local duration = libcast.ApplyShotHaste(duration or 3000)
|
||||
|
||||
local _,_, lag = GetNetStats()
|
||||
local start = GetTime() + lag/1000
|
||||
|
||||
-- add cast action to the database
|
||||
libcast.db[player].cast = aimedshot
|
||||
libcast.db[player].rank = lastrank
|
||||
libcast.db[player].start = start
|
||||
libcast.db[player].casttime = duration
|
||||
libcast.db[player].icon = "Interface\\Icons\\Inv_spear_07"
|
||||
libcast.db[player].channel = nil
|
||||
else
|
||||
-- remove cast action to the database
|
||||
libcast.db[player].cast = nil
|
||||
libcast.db[player].rank = nil
|
||||
libcast.db[player].start = nil
|
||||
libcast.db[player].casttime = nil
|
||||
libcast.db[player].icon = nil
|
||||
libcast.db[player].channel = nil
|
||||
end
|
||||
end
|
||||
|
||||
libcast.customcast[strlower(multishot)] = function(begin, duration)
|
||||
if begin then
|
||||
local duration = libcast.ApplyShotHaste(duration or 500)
|
||||
|
||||
local _,_, lag = GetNetStats()
|
||||
local start = GetTime() + lag/1000
|
||||
|
||||
-- add cast action to the database
|
||||
libcast.db[player].cast = multishot
|
||||
libcast.db[player].rank = lastrank
|
||||
libcast.db[player].start = start
|
||||
libcast.db[player].casttime = duration
|
||||
libcast.db[player].icon = "Interface\\Icons\\Ability_upgrademoonglaive"
|
||||
libcast.db[player].channel = nil
|
||||
else
|
||||
-- remove cast action to the database
|
||||
libcast.db[player].cast = nil
|
||||
libcast.db[player].rank = nil
|
||||
libcast.db[player].start = nil
|
||||
libcast.db[player].casttime = nil
|
||||
libcast.db[player].icon = nil
|
||||
libcast.db[player].channel = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function CastCustom(id, bookType, rawSpellName, rank, texture, castingTime)
|
||||
if not id or not rawSpellName then return end -- ignore if the spell is not found
|
||||
if not castingTime or castingTime == 0 then
|
||||
-- instant-cast: clear lastcasttex so next cast doesn't inherit this icon
|
||||
lastcasttex, lastrank = nil, nil
|
||||
return
|
||||
end
|
||||
|
||||
lastrank = rank
|
||||
lastcasttex = texture
|
||||
|
||||
local func = libcast.customcast[strlower(rawSpellName)]
|
||||
if not func then return end
|
||||
|
||||
if GetSpellCooldown(id, bookType) == 0 or pfGetCastInfo(player) then return end -- detect casting
|
||||
|
||||
func(true)
|
||||
end
|
||||
|
||||
hooksecurefunc("UseContainerItem", function(id, index)
|
||||
lastcasttex = GetContainerItemInfo(id, index)
|
||||
end)
|
||||
|
||||
hooksecurefunc("CastSpell", function(id, bookType)
|
||||
local cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime, _, _, cachedSpellId, cachedBookType = libspell.GetSpellInfo(id, bookType)
|
||||
|
||||
CastCustom(cachedSpellId, cachedBookType, cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime)
|
||||
end)
|
||||
|
||||
hooksecurefunc("CastSpellByName", function(spellCasted, target)
|
||||
local cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime, _, _, cachedSpellId, cachedBookType = libspell.GetSpellInfo(spellCasted)
|
||||
|
||||
CastCustom(cachedSpellId, cachedBookType, cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime)
|
||||
end)
|
||||
|
||||
hooksecurefunc("UseAction", function(slot, target, button)
|
||||
if not IsCurrentAction(slot) then return end
|
||||
|
||||
-- Resolve action slot → spellID (handles both spell and macro actions),
|
||||
-- then resolve to spellbook slot for libspell. GetMacroSpell returns the
|
||||
-- highest rank the player actually knows, so FindSpellBookSlotByID's
|
||||
-- "spell must be in spellbook" requirement is satisfied.
|
||||
local kind, id = GetActionInfo(slot)
|
||||
local spellID
|
||||
if kind == "spell" then
|
||||
spellID = id
|
||||
elseif kind == "macro" then
|
||||
local _, _, sid = GetMacroSpell(id)
|
||||
spellID = sid
|
||||
end
|
||||
if not spellID then return end
|
||||
|
||||
local sbSlot = FindSpellBookSlotByID(spellID)
|
||||
if not sbSlot then return end
|
||||
|
||||
local cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime, _, _, cachedSpellId, cachedBookType = libspell.GetSpellInfo(sbSlot, BOOKTYPE_SPELL)
|
||||
|
||||
CastCustom(cachedSpellId, cachedBookType, cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime)
|
||||
end)
|
||||
|
||||
-- Cache spellId from SPELL_START_SELF so SPELLCAST_START can use it for icon lookup
|
||||
pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {}
|
||||
pfUI.libdebuff_spell_start_self_hooks["libcast_icon"] = function(spellId)
|
||||
lastSpellId = spellId
|
||||
end
|
||||
|
||||
-- add libcast to pfUI API
|
||||
pfUI.api.libcast = libcast
|
||||
+120
-523
@@ -11,9 +11,14 @@ setfenv(1, pfUI:GetEnvironment())
|
||||
-- This eliminates ~400 lines of error-prone shift logic while maintaining full
|
||||
-- multi-caster tracking support.
|
||||
--
|
||||
-- libdebuff:UnitDebuff(unit, id)
|
||||
-- Returns debuff informations on the given effect of the specified unit.
|
||||
-- name, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
-- The internal debuff plumbing now runs on ClassicAPI's C_UnitAuras (which
|
||||
-- provides sourceUnit/sourceGUID and non-player expirationTime). The public
|
||||
-- per-aura readers (UnitDebuff, UnitOwnDebuff) survive only as thin adapters
|
||||
-- over C_UnitAuras for third-party addons (e.g. pfUI-WeakIcons) that still
|
||||
-- expect the legacy multi-return signature. The rest of libdebuff is the
|
||||
-- cast-event bookkeeping consumed by GetBestAuraCast (libpredict HoT tracking)
|
||||
-- and the libdebuff_*_hooks broadcast surface (subscribers in actionbar /
|
||||
-- swingtimer react to SPELL_GO and SPELL_FAILED).
|
||||
|
||||
-- return instantly when another libdebuff is already active
|
||||
if pfUI.api.libdebuff then return end
|
||||
@@ -24,8 +29,7 @@ if GetLocale() == "ruRU" then
|
||||
end
|
||||
|
||||
local libdebuff = CreateFrame("Frame", "pfdebuffsScanner", UIParent)
|
||||
local _, class = UnitClass("player")
|
||||
local lastspell
|
||||
local class = UnitClassBase("player")
|
||||
|
||||
-- Nampower Support
|
||||
local hasNampower = false
|
||||
@@ -40,16 +44,8 @@ if GetNampowerVersion then
|
||||
end
|
||||
end
|
||||
|
||||
-- Nampower startup check: show version info and ensure CVars are set.
|
||||
-- Runs on first OnUpdate after PLAYER_ENTERING_WORLD to give Nampower time to initialize.
|
||||
local nampowerCheckFrame = CreateFrame("Frame")
|
||||
nampowerCheckFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
nampowerCheckFrame:SetScript("OnEvent", function()
|
||||
-- Defer to next frame so Nampower is fully initialized
|
||||
this:SetScript("OnUpdate", function()
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
EventUtil.ContinueOnPlayerLogin(function()
|
||||
RunNextFrame(function()
|
||||
|
||||
if GetNampowerVersion then
|
||||
local major, minor, patch = GetNampowerVersion()
|
||||
@@ -57,8 +53,6 @@ nampowerCheckFrame:SetScript("OnEvent", function()
|
||||
local versionString = major .. "." .. minor .. "." .. patch
|
||||
|
||||
if major > 3 or (major == 3 and minor > 0) or (major == 3 and minor == 0 and patch >= 0) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Nampower v" .. versionString .. " detected - GetUnitField mode enabled!")
|
||||
|
||||
if SetCVar and GetCVar then
|
||||
local cvarsToEnable = {
|
||||
"NP_EnableSpellStartEvents",
|
||||
@@ -88,8 +82,6 @@ nampowerCheckFrame:SetScript("OnEvent", function()
|
||||
|
||||
if enabledCount > 0 then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Enabled " .. enabledCount .. " Nampower CVars")
|
||||
elseif alreadyEnabledCount == table.getn(cvarsToEnable) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r All required Nampower CVars already enabled")
|
||||
end
|
||||
if failedCount > 0 then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00[libdebuff]|r Warning: Could not check/set " .. failedCount .. " CVars")
|
||||
@@ -142,8 +134,6 @@ local iconCache = pfUI.libdebuff_icon_cache
|
||||
|
||||
-- Cast Tracking: [casterGuid] = {spellID, spellName, icon, startTime, duration, endTime}
|
||||
-- Shared with nameplates for cast-bar display
|
||||
pfUI.libdebuff_casts = pfUI.libdebuff_casts or {}
|
||||
pfUI.libdebuff_item_icons = pfUI.libdebuff_item_icons or {} -- [casterGuid] = icon (persists across SPELL_GO)
|
||||
|
||||
-- Cleveroids API: [targetGUID][spellID] = {start, duration, caster, stacks}
|
||||
pfUI.libdebuff_objects_guid = pfUI.libdebuff_objects_guid or {}
|
||||
@@ -203,10 +193,6 @@ pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {}
|
||||
pfUI.libdebuff_downrank_blocked_hooks = pfUI.libdebuff_downrank_blocked_hooks or {}
|
||||
local AURA_CAST_DEDUPE_WINDOW = 0.1 -- Ignore duplicates within 100ms
|
||||
|
||||
-- Captured combo points from SPELL_CAST_EVENT (before client consumes them)
|
||||
-- SPELL_CAST_EVENT fires BEFORE UnitAura updates, so GetComboPoints() still works
|
||||
local capturedCP = nil
|
||||
|
||||
-- Pending cast info for libpredict (heal prediction target tracking)
|
||||
-- SPELL_CAST_EVENT fires with targetGuid BEFORE SPELLCAST_START,
|
||||
-- which allows libpredict to know the correct target for queued casts.
|
||||
@@ -279,50 +265,10 @@ local debuffOverwritePairs = {
|
||||
["Demoralizing Roar"] = "Demoralizing Shout",
|
||||
}
|
||||
|
||||
-- Combopoint-based abilities: Only show timers for OUR casts
|
||||
-- Format: [spellName] = { base = N, perCP = N }
|
||||
-- Duration formula: duration = base + combopoints * perCP
|
||||
local combopointAbilities = {
|
||||
-- Druid
|
||||
["Rip"] = { base = 8, perCP = 2 },
|
||||
|
||||
-- Rogue
|
||||
["Rupture"] = { base = 6, perCP = 2 },
|
||||
["Kidney Shot"] = { base = 1, perCP = 1 },
|
||||
["Slice and Dice"] = { base = 9, perCP = 3 },
|
||||
["Expose Armor"] = { base = 30, perCP = 0 }, -- fixed 30s
|
||||
}
|
||||
|
||||
-- ============================================================================
|
||||
-- HELPER FUNCTIONS
|
||||
-- ============================================================================
|
||||
|
||||
-- Check if spell is a combo-point ability
|
||||
local function IsComboPointAbility(spellName)
|
||||
if not spellName then return false end
|
||||
return combopointAbilities[spellName] ~= nil
|
||||
end
|
||||
|
||||
-- Get combo-point spell data (base duration and per-CP bonus)
|
||||
local function GetComboPointData(spellName)
|
||||
if not spellName then return nil, nil end
|
||||
local cpData = combopointAbilities[spellName]
|
||||
if cpData then
|
||||
return cpData.base, cpData.perCP
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- Player GUID Cache
|
||||
local playerGUID = nil
|
||||
local function GetPlayerGUID()
|
||||
if not playerGUID and UnitGUID then
|
||||
local guid = UnitGUID("player")
|
||||
playerGUID = guid
|
||||
end
|
||||
return playerGUID
|
||||
end
|
||||
|
||||
-- Debug Stats
|
||||
pfUI.libdebuff_debugstats = pfUI.libdebuff_debugstats or {
|
||||
enabled = false,
|
||||
@@ -426,15 +372,13 @@ local function GetDebuffSlotMap(guid)
|
||||
local spellId = auras[auraSlot]
|
||||
if spellId and spellId > 0 then
|
||||
displaySlot = displaySlot + 1
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
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,
|
||||
@@ -462,7 +406,7 @@ local function GetSlotCaster(guid, auraSlot, spellName)
|
||||
end
|
||||
|
||||
-- Fallback: Check ownDebuffs
|
||||
local myGuid = GetPlayerGUID()
|
||||
local myGuid = GetPlayerGuid()
|
||||
if ownDebuffs[guid] and ownDebuffs[guid][spellName] then
|
||||
return myGuid, true
|
||||
end
|
||||
@@ -647,46 +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"]["Rupture"] then
|
||||
local cp = GetComboPoints() or 0
|
||||
duration = duration + cp*2
|
||||
elseif effect == L["dyndebuffs"]["Kidney Shot"] then
|
||||
local cp = GetComboPoints() or 0
|
||||
duration = duration + cp*1
|
||||
elseif effect == "Rip" or effect == L["dyndebuffs"]["Rip"] then
|
||||
local cp = GetComboPoints() or 0
|
||||
duration = 8 + cp*2
|
||||
elseif 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)
|
||||
@@ -712,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
|
||||
@@ -787,283 +697,78 @@ function libdebuff:AddEffect(unit, unitlevel, effect, duration, caster, rank)
|
||||
lastspell = libdebuff.objects[unit][unitlevel][effect]
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- MAIN API: UnitDebuff (GetUnitField-based)
|
||||
-- ============================================================================
|
||||
|
||||
local cache = {}
|
||||
|
||||
function libdebuff:UnitDebuff(unit, displaySlot)
|
||||
local unitname = UnitName(unit)
|
||||
local unitlevel = UnitLevel(unit)
|
||||
local duration, timeleft = nil, -1
|
||||
local rank = nil
|
||||
local caster = nil
|
||||
local effect = nil
|
||||
local texture = nil
|
||||
local stacks = 0
|
||||
local dtype = nil
|
||||
|
||||
-- Nampower: Use GetUnitField for ALL debuff data (no Blizzard UnitDebuff needed)
|
||||
if hasNampower and UnitGUID then
|
||||
local guid = UnitGUID(unit)
|
||||
if not guid then
|
||||
-- Safety fallback: no GUID available (should not happen with Nampower)
|
||||
local aura = C_UnitAuras.GetDebuffDataByIndex(unit, displaySlot)
|
||||
if aura then
|
||||
return aura.name, rank, aura.icon, aura.applications, aura.dispelName, duration, timeleft, caster
|
||||
end
|
||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
end
|
||||
|
||||
-- Get current slot map from GetUnitField (cached 50ms)
|
||||
local slotMap = GetDebuffSlotMap(guid)
|
||||
if not slotMap or not slotMap[displaySlot] then
|
||||
return nil
|
||||
end
|
||||
|
||||
local slotData = slotMap[displaySlot]
|
||||
effect = slotData.spellName
|
||||
texture = slotData.texture
|
||||
stacks = slotData.stacks
|
||||
dtype = slotData.dtype
|
||||
local auraSlot = slotData.auraSlot
|
||||
|
||||
-- Get caster info for this slot
|
||||
local slotCasterGuid, isOurs = GetSlotCaster(guid, auraSlot, effect)
|
||||
|
||||
if isOurs then
|
||||
-- OUR debuff - get timer from ownDebuffs
|
||||
if ownDebuffs[guid] and ownDebuffs[guid][effect] then
|
||||
local data = ownDebuffs[guid][effect]
|
||||
local remaining = (data.startTime + data.duration) - GetTime()
|
||||
if remaining > 0 then
|
||||
duration = data.duration
|
||||
timeleft = remaining
|
||||
caster = "player"
|
||||
rank = data.rank
|
||||
elseif remaining > -1 then
|
||||
-- Grace period - show 0 timeleft
|
||||
duration = data.duration
|
||||
timeleft = 0
|
||||
caster = "player"
|
||||
rank = data.rank
|
||||
end
|
||||
end
|
||||
else
|
||||
-- OTHER player's debuff - get timer from allAuraCasts
|
||||
if slotCasterGuid and allAuraCasts[guid] and allAuraCasts[guid][effect] then
|
||||
local data = allAuraCasts[guid][effect][slotCasterGuid]
|
||||
if data then
|
||||
local remaining = (data.startTime + data.duration) - GetTime()
|
||||
if remaining > 0 and data.duration > 0 then
|
||||
duration = data.duration
|
||||
timeleft = remaining
|
||||
caster = "other"
|
||||
rank = data.rank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fallback: Search all casters if specific one not found
|
||||
if not duration and allAuraCasts[guid] and allAuraCasts[guid][effect] then
|
||||
for anyCasterGuid, data in pairs(allAuraCasts[guid][effect]) do
|
||||
local remaining = (data.startTime + data.duration) - GetTime()
|
||||
if remaining > 0 and data.duration > 0 then
|
||||
duration = data.duration
|
||||
timeleft = remaining
|
||||
caster = "other"
|
||||
rank = data.rank
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- FALLBACK: Legacy (non-Nampower) system
|
||||
-- ============================================================================
|
||||
|
||||
local aura = C_UnitAuras.GetDebuffDataByIndex(unit, displaySlot)
|
||||
if aura then
|
||||
texture = aura.icon
|
||||
stacks = aura.applications
|
||||
dtype = aura.dispelName
|
||||
effect = aura.name
|
||||
end
|
||||
|
||||
|
||||
if effect and libdebuff.objects[unitname] then
|
||||
for level, effects in pairs(libdebuff.objects[unitname]) do
|
||||
if effects[effect] and effects[effect].duration then
|
||||
local timeleft = effects[effect].start and
|
||||
effects[effect].start + effects[effect].duration - GetTime()
|
||||
|
||||
if timeleft and timeleft > 0 then
|
||||
return effect, effects[effect].rank, texture, stacks, dtype,
|
||||
effects[effect].duration, timeleft, effects[effect].caster
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- API: UnitOwnDebuff (only OUR debuffs)
|
||||
-- ============================================================================
|
||||
|
||||
-- Pre-defined sort function for UnitOwnDebuff (avoids closure creation per call)
|
||||
local _ownDebuffSortFunc = function(a, b)
|
||||
if a.data.startTime == b.data.startTime then
|
||||
return a.spellName < b.spellName
|
||||
end
|
||||
return a.data.startTime < b.data.startTime
|
||||
end
|
||||
|
||||
function libdebuff:UnitOwnDebuff(unit, id)
|
||||
if hasNampower and UnitGUID then
|
||||
local guid = UnitGUID(unit)
|
||||
if guid and ownDebuffs[guid] then
|
||||
-- Build sorted list of our active debuffs
|
||||
local sortedDebuffs = {}
|
||||
local now = GetTime()
|
||||
|
||||
local toRemove = nil
|
||||
for spellName, data in pairs(ownDebuffs[guid]) do
|
||||
local timeleft = (data.startTime + data.duration) - now
|
||||
if timeleft > 0 then
|
||||
local count = table.getn(sortedDebuffs) + 1
|
||||
sortedDebuffs[count] = {
|
||||
spellName = spellName,
|
||||
data = data,
|
||||
timeleft = timeleft
|
||||
}
|
||||
elseif data.pending then
|
||||
if timeleft < -2 then
|
||||
toRemove = toRemove or {}
|
||||
toRemove[spellName] = true
|
||||
end
|
||||
else
|
||||
toRemove = toRemove or {}
|
||||
toRemove[spellName] = true
|
||||
end
|
||||
end
|
||||
if toRemove then
|
||||
for spellName in pairs(toRemove) do
|
||||
ownDebuffs[guid][spellName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort by startTime (oldest first = lowest display slot)
|
||||
-- If startTime is equal (e.g. after Carnage refresh), use spellName for stable sorting
|
||||
table.sort(sortedDebuffs, _ownDebuffSortFunc)
|
||||
|
||||
-- Return debuff at position 'id'
|
||||
if sortedDebuffs[id] then
|
||||
local entry = sortedDebuffs[id]
|
||||
local texture = entry.data.texture or "Interface\\Icons\\INV_Misc_QuestionMark"
|
||||
local displayTimeleft = entry.timeleft > 0 and entry.timeleft or 0
|
||||
|
||||
-- Get dtype from SpellRec DBC via stored spellId
|
||||
local entryDtype = nil
|
||||
if entry.data.spellId and GetSpellRecField then
|
||||
local dispelId = GetSpellRecField(entry.data.spellId, "dispel")
|
||||
if dispelId and dispelId > 0 then
|
||||
entryDtype = dispelTypeMap[dispelId]
|
||||
end
|
||||
end
|
||||
|
||||
return entry.spellName, entry.data.rank, texture, 1, entryDtype, entry.data.duration, displayTimeleft, "player"
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Fallback: Iterate through all debuffs and filter
|
||||
for k in pairs(cache) do cache[k] = nil end
|
||||
local count = 1
|
||||
for i=1,16 do
|
||||
local effect, rank, texture, stacks, dtype, duration, timeleft, caster = libdebuff:UnitDebuff(unit, i)
|
||||
if effect and not cache[effect] and caster and caster == "player" then
|
||||
cache[effect] = true
|
||||
if count == id then
|
||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
|
||||
-- ============================================================================
|
||||
-- API: GetEnhancedDebuffs (for external modules)
|
||||
-- API: UnitDebuff / UnitOwnDebuff (C_UnitAuras adapters)
|
||||
-- ============================================================================
|
||||
-- Thin readers kept for third-party addons (e.g. pfUI-WeakIcons) that still
|
||||
-- expect libdebuff's legacy multi-return signature:
|
||||
-- effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
-- ClassicAPI's C_UnitAuras already resolves source and expiration, so these
|
||||
-- just remap its AuraData onto that tuple -- no cast-tracking tables
|
||||
-- (ownDebuffs/allAuraCasts) or GetUnitField slot mapping involved.
|
||||
|
||||
function libdebuff:GetEnhancedDebuffs(targetGUID)
|
||||
if not targetGUID then return nil end
|
||||
local result = {}
|
||||
|
||||
if ownDebuffs[targetGUID] then
|
||||
local myGuid = GetPlayerGUID()
|
||||
for spellName, data in pairs(ownDebuffs[targetGUID]) do
|
||||
local timeleft = (data.startTime + data.duration) - GetTime()
|
||||
if timeleft > 0 then
|
||||
result[spellName] = result[spellName] or {}
|
||||
result[spellName][myGuid] = {
|
||||
startTime = data.startTime,
|
||||
duration = data.duration,
|
||||
texture = data.texture,
|
||||
rank = data.rank
|
||||
}
|
||||
end
|
||||
end
|
||||
local function AuraToLegacy(aura)
|
||||
if not aura then return nil end
|
||||
|
||||
local duration = aura.duration or 0
|
||||
local timeleft = -1
|
||||
-- Only report a timer for genuinely timed auras. ClassicAPI can leave a
|
||||
-- stale expirationTime on permanent (duration 0) auras, so gate on duration.
|
||||
if duration > 0 and aura.expirationTime and aura.expirationTime > 0 then
|
||||
timeleft = aura.expirationTime - GetTime()
|
||||
if timeleft < 0 then timeleft = 0 end
|
||||
end
|
||||
|
||||
return result
|
||||
|
||||
-- Aura spellId is the specific cast rank, so its subtext is the active rank.
|
||||
local rank
|
||||
local subtext = aura.spellId and C_Spell.GetSpellSubtext(aura.spellId)
|
||||
if subtext and subtext ~= "" then
|
||||
rank = tonumber((string.gsub(subtext, "Rank ", "")))
|
||||
end
|
||||
|
||||
local dtype = aura.dispelName
|
||||
if dtype == "" then dtype = nil end
|
||||
|
||||
local caster = aura.isFromPlayerOrPlayerPet and "player" or "other"
|
||||
|
||||
return aura.name, rank, aura.icon, aura.applications or 0, dtype, duration, timeleft, caster
|
||||
end
|
||||
|
||||
-- id is a 1-based harmful-aura index, matching C_UnitAuras / Blizzard's
|
||||
-- compacted debuff slots.
|
||||
function libdebuff:UnitDebuff(unit, id)
|
||||
return AuraToLegacy(C_UnitAuras.GetAuraDataByIndex(unit, id, "HARMFUL"))
|
||||
end
|
||||
|
||||
-- Player-cast harmful auras only, via the PLAYER filter -- no manual
|
||||
-- caster-GUID bookkeeping needed.
|
||||
function libdebuff:UnitOwnDebuff(unit, id)
|
||||
return AuraToLegacy(C_UnitAuras.GetAuraDataByIndex(unit, id, "HARMFUL|PLAYER"))
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
@@ -1097,7 +802,7 @@ if hasNampower then
|
||||
-- Carnage triggered! Refresh Rip & Rake
|
||||
local guid = carnageState.targetGuid
|
||||
local refreshTime = GetTime()
|
||||
local myGuid = GetPlayerGUID()
|
||||
local myGuid = GetPlayerGuid()
|
||||
|
||||
-- Refresh in ownDebuffs - only if timer still active
|
||||
if ownDebuffs[guid] then
|
||||
@@ -1144,12 +849,8 @@ if hasNampower then
|
||||
pfTarget.update_aura = true
|
||||
end
|
||||
end
|
||||
|
||||
if pfUI.nameplates and pfUI.nameplates.OnAuraUpdate then
|
||||
pfUI.nameplates:OnAuraUpdate(guid)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
carnageState = nil
|
||||
this:Hide()
|
||||
end)
|
||||
@@ -1180,32 +881,22 @@ if hasNampower then
|
||||
return
|
||||
|
||||
elseif event == "SPELLCAST_CHANNEL_STOP" then
|
||||
-- Channel interrupted by player - clear ownDebuffs for the channeled spell immediately.
|
||||
-- DEBUFF_REMOVED fires later (0.5-1s server lag), causing phantom debuff display.
|
||||
-- We look up the active channel cast and pre-clear ownDebuffs for its target.
|
||||
local myGuid = GetPlayerGUID()
|
||||
local castData = myGuid and pfUI.libdebuff_casts[myGuid]
|
||||
if castData and castData.event == "CHANNEL" and castData.spellName then
|
||||
local spellName = castData.spellName
|
||||
-- Channel interrupted by player - clear ownDebuffs for the channeled spell
|
||||
-- via C_Spell.ChannelInfo. DEBUFF_REMOVED fires later (0.5-1s server lag),
|
||||
-- causing phantom debuff display without this pre-clear.
|
||||
local spellName = C_Spell.ChannelInfo()
|
||||
if spellName then
|
||||
local targetGuid = UnitGUID and UnitGUID("target")
|
||||
if targetGuid and ownDebuffs[targetGuid] and ownDebuffs[targetGuid][spellName] then
|
||||
local data = ownDebuffs[targetGuid][spellName]
|
||||
-- Only clear if the timer is still active (not already expired naturally)
|
||||
local remaining = (data.startTime + data.duration) - GetTime()
|
||||
if remaining > 0 then
|
||||
ownDebuffs[targetGuid][spellName] = nil
|
||||
end
|
||||
end
|
||||
pfUI.libdebuff_casts[myGuid] = nil
|
||||
end
|
||||
|
||||
elseif event == "PLAYER_ENTERING_WORLD" then
|
||||
GetPlayerGUID()
|
||||
elseif event == "PLAYER_ENTERING_WORLD" or event == "PLAYER_TALENT_UPDATE" then
|
||||
UpdateCarnageRank()
|
||||
|
||||
elseif event == "PLAYER_TALENT_UPDATE" then
|
||||
UpdateCarnageRank()
|
||||
|
||||
elseif event == "UNIT_HEALTH" then
|
||||
local guid = arg1
|
||||
if guid and UnitIsDead and UnitIsDead(guid) then
|
||||
@@ -1218,48 +909,15 @@ if hasNampower then
|
||||
end
|
||||
|
||||
elseif event == "SPELL_START_SELF" or event == "SPELL_START_OTHER" then
|
||||
local itemId = arg1
|
||||
local spellId = arg2
|
||||
local casterGuid = arg3
|
||||
local spellType = arg8 or 0 -- 0=Normal, 1=Channel, 2=Autorepeating
|
||||
-- arg6=castTime, arg7=channel duration
|
||||
-- prefer arg6 if present — some spells (e.g. Volley post-rework) still send
|
||||
-- arg8=1 but now have a real cast time in arg6, so we only fall back to arg7
|
||||
-- when arg6 is nil (true channels like Blizzard)
|
||||
-- arg6=castTime (ms), arg7=channel duration (ms), arg8=spellType
|
||||
-- For channels: arg6=0 (no cast time), arg7=duration, spellType=1
|
||||
-- For normal casts: arg6=castTime, arg7=0, spellType=0
|
||||
-- Note: "not arg6" is wrong in Lua since 0 is truthy - use arg6 == 0 or nil
|
||||
-- arg6=castTime (ms), arg7=channel duration (ms). Prefer arg6 when set —
|
||||
-- some spells (e.g. post-rework Volley) flag as channel via arg8 but ship a
|
||||
-- real cast time in arg6, so we only fall back to arg7 for true channels
|
||||
-- like Blizzard where arg6 is 0/nil.
|
||||
local castTime = (arg6 and arg6 > 0) and arg6 or arg7
|
||||
local isChannel = spellType == 1 and (not arg6 or arg6 == 0)
|
||||
|
||||
if not casterGuid or not spellId then return end
|
||||
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
local icon = libdebuff:GetSpellIcon(spellId)
|
||||
|
||||
-- Use item icon for item-triggered casts
|
||||
if itemId and itemId > 0 then
|
||||
icon = C_Item.GetItemIconByID(itemId) or icon
|
||||
pfUI.libdebuff_item_icons[casterGuid] = {
|
||||
icon = icon,
|
||||
name = GetItemInfo(itemId),
|
||||
}
|
||||
else
|
||||
pfUI.libdebuff_item_icons[casterGuid] = nil
|
||||
end
|
||||
|
||||
pfUI.libdebuff_casts[casterGuid] = {
|
||||
spellID = spellId,
|
||||
itemID = itemId and itemId > 0 and itemId or nil,
|
||||
spellName = spellName,
|
||||
icon = icon,
|
||||
startTime = GetTime(),
|
||||
duration = castTime and castTime / 1000 or 0,
|
||||
endTime = castTime and (GetTime() + castTime / 1000) or nil,
|
||||
event = isChannel and "CHANNEL" or "START"
|
||||
}
|
||||
|
||||
if event == "SPELL_START_SELF" and pfUI.libdebuff_spell_start_self_hooks then
|
||||
for _, fn in pairs(pfUI.libdebuff_spell_start_self_hooks) do
|
||||
fn(spellId, casterGuid, arg4, castTime)
|
||||
@@ -1277,16 +935,7 @@ if hasNampower then
|
||||
local targetGuid = arg4
|
||||
local numHit = arg6 or 0
|
||||
local numMissed = arg7 or 0
|
||||
|
||||
-- Clear cast bar only if SPELL_GO matches the active cast
|
||||
-- (Reactive procs like Frost Armor trigger SPELL_GO but shouldn't clear the castbar)
|
||||
-- Don't clear channels on SPELL_GO - channels persist until duration expires or SPELL_FAILED
|
||||
if casterGuid and pfUI.libdebuff_casts[casterGuid] then
|
||||
if pfUI.libdebuff_casts[casterGuid].spellID == spellId and pfUI.libdebuff_casts[casterGuid].event ~= "CHANNEL" then
|
||||
pfUI.libdebuff_casts[casterGuid] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Fire registered SPELL_GO_SELF hooks BEFORE miss guard
|
||||
-- (Swingtimer needs to see ALL casts, even misses, for swing reset)
|
||||
if event == "SPELL_GO_SELF" and pfUI.libdebuff_spell_go_hooks then
|
||||
@@ -1297,15 +946,15 @@ if hasNampower then
|
||||
|
||||
if numMissed > 0 or numHit == 0 then return end
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellRankString = GetSpellRecField and GetSpellRecField(spellId, "rank")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
local spellRankString = C_Spell.GetSpellSubtext(spellId)
|
||||
|
||||
local castRank = 0
|
||||
if spellRankString and spellRankString ~= "" then
|
||||
castRank = tonumber((string.gsub(spellRankString, "Rank ", ""))) or 0
|
||||
end
|
||||
|
||||
|
||||
-- Store in pendingCasts for DEBUFF_ADDED correlation.
|
||||
-- If this cast is a downrank of an already active debuff, fire the downrank blocked hook
|
||||
-- so external addons (e.g. SuperCleveRoidMacros) don't need to re-implement this check.
|
||||
@@ -1342,7 +991,7 @@ if hasNampower then
|
||||
local selfdebuffMode = pfUI_config and pfUI_config.buffbar and
|
||||
pfUI_config.buffbar.tdebuff and pfUI_config.buffbar.tdebuff.selfdebuff == "1"
|
||||
if selfdebuffMode then
|
||||
local myGuid2 = GetPlayerGUID()
|
||||
local myGuid2 = GetPlayerGuid()
|
||||
if casterGuid == myGuid2 then
|
||||
local duration = libdebuff:GetDuration(spellName, castRank) or 0
|
||||
if duration > 0 then
|
||||
@@ -1372,7 +1021,7 @@ if hasNampower then
|
||||
end
|
||||
|
||||
-- Store rank for our casts
|
||||
local myGuid = GetPlayerGUID()
|
||||
local myGuid = GetPlayerGuid()
|
||||
if casterGuid == myGuid then
|
||||
lastCastRanks[spellName] = {
|
||||
rank = castRank,
|
||||
@@ -1410,14 +1059,6 @@ if hasNampower then
|
||||
|
||||
elseif event == "SPELL_FAILED_OTHER" then
|
||||
local casterGuid = arg1
|
||||
local spellId = arg2
|
||||
|
||||
if casterGuid and pfUI.libdebuff_casts[casterGuid] then
|
||||
-- Only clear if spellID matches to avoid clearing a cast that already moved on
|
||||
if pfUI.libdebuff_casts[casterGuid].spellID == spellId then
|
||||
pfUI.libdebuff_casts[casterGuid] = nil
|
||||
end
|
||||
end
|
||||
if pfUI.libdebuff_spell_failed_other_hooks then
|
||||
for _, fn in pairs(pfUI.libdebuff_spell_failed_other_hooks) do
|
||||
fn(casterGuid, arg2)
|
||||
@@ -1453,11 +1094,6 @@ if hasNampower then
|
||||
pfUI.libpredict_pending_cast.time = nil
|
||||
end
|
||||
|
||||
-- Only capture CPs for combo-point abilities
|
||||
if spellName and IsComboPointAbility(spellName) then
|
||||
capturedCP = GetComboPoints() or 0
|
||||
end
|
||||
|
||||
-- Fire registered SPELL_CAST_EVENT hooks
|
||||
if pfUI.libdebuff_spell_cast_hooks then
|
||||
for _, fn in pairs(pfUI.libdebuff_spell_cast_hooks) do
|
||||
@@ -1479,7 +1115,7 @@ if hasNampower then
|
||||
if not spellId then return end
|
||||
if not targetGuid or targetGuid == "" or targetGuid == "0x0000000000000000" then return end
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
-- Deduplicate: Ignore if we processed this exact cast recently (within 100ms)
|
||||
@@ -1498,44 +1134,24 @@ if hasNampower then
|
||||
|
||||
-- Rank aus spellId ermitteln
|
||||
local rankNum = 0
|
||||
local rankString = GetSpellRecField(spellId, "rank")
|
||||
local rankString = C_Spell.GetSpellSubtext(spellId)
|
||||
if rankString and rankString ~= "" then
|
||||
rankNum = tonumber((string.gsub(rankString, "Rank ", ""))) or 0
|
||||
end
|
||||
|
||||
local duration = durationMs and (durationMs / 1000) or 0
|
||||
local startTime = GetTime()
|
||||
local myGuid = GetPlayerGUID()
|
||||
local myGuid = GetPlayerGuid()
|
||||
local isOurs = (myGuid and casterGuid == myGuid)
|
||||
|
||||
if debugStats.enabled and isOurs then
|
||||
debugStats.aura_cast = debugStats.aura_cast + 1
|
||||
end
|
||||
|
||||
-- Combo-point abilities: Calculate duration based on CPs used
|
||||
if IsComboPointAbility(spellName) then
|
||||
if isOurs then
|
||||
-- OWN casts: use captured CPs from SPELL_CAST_EVENT (if available)
|
||||
local cp = capturedCP or 0
|
||||
local base, perCP = GetComboPointData(spellName)
|
||||
if base and perCP then
|
||||
duration = base + cp * perCP
|
||||
else
|
||||
-- Fallback to legacy database
|
||||
duration = libdebuff:GetDuration(spellName, rankNum)
|
||||
end
|
||||
capturedCP = nil -- consumed
|
||||
else
|
||||
-- OTHER players: CP unknown, no timer (except Expose Armor = fixed 30s)
|
||||
local base, perCP = GetComboPointData(spellName)
|
||||
if perCP and perCP == 0 and base then
|
||||
duration = base -- fixed duration (Expose Armor)
|
||||
else
|
||||
duration = 0 -- CP unknown for other players
|
||||
end
|
||||
end
|
||||
elseif duration == 0 then
|
||||
-- Non-CP managed spells: use database if AURA_CAST returned 0
|
||||
-- Duration comes from nampower's AURA_CAST event. ClassicAPI's
|
||||
-- C_UnitAuras now resolves combo-scaled durations server-side, so
|
||||
-- libdebuff only needs the database fallback when AURA_CAST reports 0.
|
||||
if duration == 0 then
|
||||
duration = libdebuff:GetDuration(spellName, rankNum) or 0
|
||||
end
|
||||
|
||||
@@ -1635,22 +1251,14 @@ if hasNampower then
|
||||
|
||||
end
|
||||
|
||||
-- Notify nameplates
|
||||
if pfUI.nameplates and pfUI.nameplates.OnAuraUpdate then
|
||||
pfUI.nameplates:OnAuraUpdate(targetGuid)
|
||||
end
|
||||
|
||||
-- Notify unitframes of debuff updates (UNIT_AURA doesn't fire on refreshes!)
|
||||
-- Check player
|
||||
if UnitGUID("player") then
|
||||
local playerGuid = UnitGUID("player")
|
||||
if playerGuid == targetGuid and pfPlayer then
|
||||
pfPlayer.update_aura = true
|
||||
end
|
||||
if IsPlayerGuid(targetGuid) and pfPlayer then
|
||||
pfPlayer.update_aura = true
|
||||
end
|
||||
|
||||
-- Check target
|
||||
if UnitGUID("target") then
|
||||
if UnitExists("target") then
|
||||
local tarUnitGUID = UnitGUID("target")
|
||||
if tarUnitGUID == targetGuid and pfTarget then
|
||||
pfTarget.update_aura = true
|
||||
@@ -1744,7 +1352,7 @@ if hasNampower then
|
||||
|
||||
-- Invalidate slot map cache for this GUID
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
if debugStats.enabled then
|
||||
@@ -1797,7 +1405,7 @@ if hasNampower then
|
||||
end
|
||||
end
|
||||
|
||||
local myGuid = GetPlayerGUID()
|
||||
local myGuid = GetPlayerGuid()
|
||||
local isOurs = (myGuid and casterGuid == myGuid)
|
||||
|
||||
-- Fallback: Check ownDebuffs timing
|
||||
@@ -1832,7 +1440,6 @@ if hasNampower then
|
||||
-- CRITICAL FIX: Update ownDebuffs here too for refresh timing!
|
||||
-- This prevents the gap between DEBUFF_REMOVED and AURA_CAST where buffwatch shows nothing
|
||||
if isOurs and casterGuid then
|
||||
local myGuid = GetPlayerGUID()
|
||||
if myGuid and casterGuid == myGuid then
|
||||
-- Check if we have timer data from allAuraCasts
|
||||
if allAuraCasts[guid] and allAuraCasts[guid][spellName] and allAuraCasts[guid][spellName][casterGuid] then
|
||||
@@ -1857,11 +1464,7 @@ if hasNampower then
|
||||
|
||||
-- Cleanup expired timers
|
||||
CleanupExpiredTimers(guid)
|
||||
|
||||
-- Notify nameplates
|
||||
if pfUI.nameplates and pfUI.nameplates.OnAuraUpdate then
|
||||
pfUI.nameplates:OnAuraUpdate(guid)
|
||||
end
|
||||
|
||||
if pfUI.libdebuff_debuff_added_other_hooks then
|
||||
for _, fn in pairs(pfUI.libdebuff_debuff_added_other_hooks) do
|
||||
fn(arg1, arg2, arg3, arg4)
|
||||
@@ -1878,9 +1481,9 @@ if hasNampower then
|
||||
local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil
|
||||
|
||||
-- Invalidate slot map cache for this GUID
|
||||
|
||||
local spellName = (GetSpellRecField and GetSpellRecField(spellId, "name")) or "?"
|
||||
|
||||
|
||||
local spellName = C_Spell.GetSpellName(spellId) or "?"
|
||||
|
||||
if debugStats.enabled then
|
||||
debugStats.debuff_removed = debugStats.debuff_removed + 1
|
||||
if IsCurrentTarget(guid) then
|
||||
@@ -1888,13 +1491,13 @@ if hasNampower then
|
||||
GetDebugTimestamp(), displaySlot, auraSlot or -1, auraSlot_0based or -1, spellName))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- If unit is dead, cleanup all
|
||||
if UnitIsDead and UnitIsDead(guid) then
|
||||
CleanupUnit(guid)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- Get auraSlot from event parameter (Nampower 2.29+)
|
||||
-- Fallback to displayToAura mapping if not available
|
||||
local foundAuraSlot = auraSlot
|
||||
@@ -1950,11 +1553,7 @@ if hasNampower then
|
||||
|
||||
-- Cleanup expired timers
|
||||
CleanupExpiredTimers(guid)
|
||||
|
||||
-- Notify nameplates
|
||||
if pfUI.nameplates and pfUI.nameplates.OnAuraUpdate then
|
||||
pfUI.nameplates:OnAuraUpdate(guid)
|
||||
end
|
||||
|
||||
if pfUI.libdebuff_debuff_removed_other_hooks then
|
||||
for _, fn in pairs(pfUI.libdebuff_debuff_removed_other_hooks) do
|
||||
fn(arg1, arg2, arg3, arg4)
|
||||
@@ -2094,5 +1693,3 @@ _G.SlashCmdList["MEMCHECK"] = function()
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00No ownSlots/allSlots (eliminated by GetUnitField approach!)|r")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff============================================================|r")
|
||||
end
|
||||
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r GetUnitField Edition loaded!")
|
||||
+65
-126
@@ -44,78 +44,13 @@ local healGuidToName = {} -- [casterGuid] = casterName, for SPELL_FAILED_OTHER c
|
||||
local ress_timers = {} -- [target][sender] = expiry_timestamp (60s rez window)
|
||||
local RESS_TIMEOUT = 60 -- Vanilla: rez offer expires after 60s
|
||||
|
||||
local PRAYER_OF_HEALING
|
||||
do -- Prayer of Healing
|
||||
local locales = {
|
||||
["deDE"] = "Gebet der Heilung",
|
||||
["enUS"] = "Prayer of Healing",
|
||||
["esES"] = "Rezo de curación",
|
||||
["frFR"] = "Prière de soins",
|
||||
["koKR"] = "치유의 기원",
|
||||
["ruRU"] = "Молитва исцеления",
|
||||
["zhCN"] = "治疗祷言",
|
||||
}
|
||||
|
||||
PRAYER_OF_HEALING = locales[GetLocale()] or locales["enUS"]
|
||||
end
|
||||
|
||||
local REJUVENATION
|
||||
do -- Rejuvenation
|
||||
local locales = {
|
||||
["deDE"] = "Verjüngung",
|
||||
["enUS"] = "Rejuvenation",
|
||||
["esES"] = "Rejuvenecimiento",
|
||||
["frFR"] = "Récupération",
|
||||
["koKR"] = "회복",
|
||||
["ruRU"] = "Омоложение",
|
||||
["zhCN"] = "回春术",
|
||||
}
|
||||
|
||||
REJUVENATION = locales[GetLocale()] or locales["enUS"]
|
||||
end
|
||||
|
||||
local RENEW
|
||||
do -- Renew
|
||||
local locales = {
|
||||
["deDE"] = "Erneuerung",
|
||||
["enUS"] = "Renew",
|
||||
["esES"] = "Renovar",
|
||||
["frFR"] = "Rénovation",
|
||||
["koKR"] = "소생",
|
||||
["ruRU"] = "Обновление",
|
||||
["zhCN"] = "恢复",
|
||||
}
|
||||
|
||||
RENEW = locales[GetLocale()] or locales["enUS"]
|
||||
end
|
||||
|
||||
local REGROWTH
|
||||
do -- Regrowth
|
||||
local locales = {
|
||||
["deDE"] = "Nachwachsen",
|
||||
["enUS"] = "Regrowth",
|
||||
["esES"] = "Recrecimiento",
|
||||
["frFR"] = "Rétablissement",
|
||||
["koKR"] = "재생",
|
||||
["ruRU"] = "Восстановление",
|
||||
["zhCN"] = "愈合",
|
||||
}
|
||||
|
||||
REGROWTH = locales[GetLocale()] or locales["enUS"]
|
||||
end
|
||||
|
||||
|
||||
-- Spell IDs for SPELL_GO_SELF callback (Nampower) - Instant HoT detection
|
||||
local SPELL_IDS = {
|
||||
-- Rejuvenation (all ranks)
|
||||
[774] = "Reju", [1058] = "Reju", [1430] = "Reju", [2090] = "Reju", [2091] = "Reju",
|
||||
[3627] = "Reju", [8910] = "Reju", [9839] = "Reju", [9840] = "Reju", [9841] = "Reju",
|
||||
[25299] = "Reju", [26981] = "Reju", [26982] = "Reju",
|
||||
-- Renew (all ranks)
|
||||
[139] = "Renew", [6074] = "Renew", [6075] = "Renew", [6076] = "Renew", [6077] = "Renew",
|
||||
[6078] = "Renew", [10927] = "Renew", [10928] = "Renew", [10929] = "Renew", [25315] = "Renew",
|
||||
[25221] = "Renew", [25222] = "Renew",
|
||||
}
|
||||
-- Localized spell names resolved once from canonical rank-1 spellIDs.
|
||||
-- Every rank shares the same name, so per-rank comparisons elsewhere can
|
||||
-- be done against these constants without per-locale or per-rank tables.
|
||||
local PRAYER_OF_HEALING = C_Spell.GetSpellName(596) -- Prayer of Healing (Rank 1)
|
||||
local REJUVENATION = C_Spell.GetSpellName(774) -- Rejuvenation (Rank 1)
|
||||
local RENEW = C_Spell.GetSpellName(139) -- Renew (Rank 1)
|
||||
local REGROWTH = C_Spell.GetSpellName(8936) -- Regrowth (Rank 1)
|
||||
|
||||
local libpredict = CreateFrame("Frame")
|
||||
libpredict:RegisterEvent("UNIT_HEALTH")
|
||||
@@ -173,7 +108,7 @@ end
|
||||
|
||||
local function isRezSpell(spellId)
|
||||
if not L["resurrections"] then return false end
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
return spellName and L["resurrections"][spellName]
|
||||
end
|
||||
|
||||
@@ -192,7 +127,7 @@ end)
|
||||
-- SPELL_START_SELF: own cast started (heals + rez)
|
||||
pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {}
|
||||
pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
local pendingTarget = nil
|
||||
@@ -255,7 +190,7 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu
|
||||
-- selfcast (ALT) = player, otherwise = current target
|
||||
-- Use this to find the correct group to heal
|
||||
local pohTarget = target or player
|
||||
if GetNumRaidMembers() > 0 then
|
||||
if IsInRaid() then
|
||||
-- Raid: find pohTarget's subgroup and heal only those members
|
||||
-- (Turtle WoW changed PoH to heal the target's group, not the caster's group)
|
||||
local targetGroup
|
||||
@@ -319,7 +254,7 @@ end
|
||||
-- SPELL_GO_SELF: own cast landed (HealStop + Regrowth timer)
|
||||
pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId)
|
||||
libpredict:HealStop(player)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName == REGROWTH then
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
if libpredict.sender.regrowth_timer then
|
||||
@@ -337,7 +272,7 @@ end
|
||||
-- Signature: fn(spellId, casterGuid, targetGuid, castTime)
|
||||
pfUI.libdebuff_spell_start_other_hooks = pfUI.libdebuff_spell_start_other_hooks or {}
|
||||
pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
local casterName = resolveNameFromGuid(casterGuid)
|
||||
@@ -356,7 +291,7 @@ pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterG
|
||||
local targetName = resolveNameFromGuid(targetGuid)
|
||||
if not targetName then return end
|
||||
|
||||
local rankStr = GetSpellRecField and GetSpellRecField(spellId, "rank") or ""
|
||||
local rankStr = C_Spell.GetSpellSubtext(spellId) or ""
|
||||
local spellKey = spellName .. (rankStr or "")
|
||||
|
||||
local amount = foreignCache[casterName] and foreignCache[casterName][spellKey]
|
||||
@@ -364,7 +299,7 @@ pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterG
|
||||
|
||||
-- Prayer of Healing: heal entire subgroup of the target
|
||||
if spellName == PRAYER_OF_HEALING then
|
||||
if GetNumRaidMembers() > 0 then
|
||||
if IsInRaid() then
|
||||
local targetGroup
|
||||
for i = 1, GetNumRaidMembers() do
|
||||
local rname, _, subgroup = GetRaidRosterInfo(i)
|
||||
@@ -404,8 +339,13 @@ end
|
||||
-- Signature: fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
|
||||
pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4, a5, a6, a7)
|
||||
-- Instant HoTs
|
||||
local hotType = SPELL_IDS[spellId]
|
||||
-- Instant HoTs — classify by name (rank-independent) instead of a
|
||||
-- hardcoded per-rank ID table.
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
local hotType
|
||||
if spellName == REJUVENATION then hotType = "Reju"
|
||||
elseif spellName == RENEW then hotType = "Renew"
|
||||
end
|
||||
if hotType then
|
||||
local targetGuid = a4
|
||||
local targetName = resolveNameFromGuid(targetGuid)
|
||||
@@ -415,20 +355,18 @@ pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4,
|
||||
elseif hotType == "Renew" then duration = renewDuration or 15
|
||||
end
|
||||
local rank = 0
|
||||
if GetSpellRecField then
|
||||
local rankStr = GetSpellRecField(spellId, "rank")
|
||||
if rankStr and rankStr ~= "" then
|
||||
rank = tonumber((string.gsub(rankStr, "Rank ", ""))) or 0
|
||||
end
|
||||
local rankSub = C_Spell.GetSpellSubtext(spellId)
|
||||
if rankSub and rankSub ~= "" then
|
||||
rank = tonumber((string.gsub(rankSub, "Rank ", ""))) or 0
|
||||
end
|
||||
local playerName = UnitName("player")
|
||||
libpredict:Hot(playerName, targetName, hotType, duration, nil, "SPELL_GO_SELF", rank)
|
||||
local rankStr = tostring(rank)
|
||||
if libpredict.sender and libpredict.sender.SendHealCommMsg then
|
||||
libpredict.sender:SendHealCommMsg(hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/")
|
||||
elseif GetNumRaidMembers() > 0 then
|
||||
elseif IsInRaid() then
|
||||
SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "RAID")
|
||||
elseif GetNumPartyMembers() > 0 then
|
||||
elseif IsInGroup() then
|
||||
SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "PARTY")
|
||||
end
|
||||
end
|
||||
@@ -546,30 +484,45 @@ function libpredict:ParseComm(sender, msg)
|
||||
rank = tonumber(rankStr)
|
||||
end
|
||||
end
|
||||
elseif select and pfGetCastInfo then
|
||||
elseif select then
|
||||
-- latest healcomm
|
||||
msgtype = tonumber(string.sub(msg, 1, 3))
|
||||
if not msgtype then return end
|
||||
|
||||
-- Resolve sender's name to a group unit token so C_Spell can query
|
||||
-- the cast. Group rosters are tiny (44 slots max) so the walk is cheap
|
||||
-- relative to the cost of receiving a HealComm message.
|
||||
local function senderUnit()
|
||||
if UnitName("player") == sender then return "player" end
|
||||
for i = 1, GetNumPartyMembers() do
|
||||
if UnitName("party"..i) == sender then return "party"..i end
|
||||
end
|
||||
for i = 1, GetNumRaidMembers() do
|
||||
if UnitName("raid"..i) == sender then return "raid"..i end
|
||||
end
|
||||
end
|
||||
|
||||
if msgtype == 0 then
|
||||
msgtype = "Heal"
|
||||
heal = tonumber(string.sub(msg, 4, 8))
|
||||
target = string.sub(msg,9, -1)
|
||||
|
||||
local starttime = select(5, pfGetCastInfo(sender))
|
||||
local endtime = select(6, pfGetCastInfo(sender))
|
||||
if not starttime or not endtime then return end
|
||||
time = endtime - starttime
|
||||
local unit = senderUnit()
|
||||
if not unit then return end
|
||||
local _, _, _, startMs, endMs = C_Spell.UnitCastingInfo(unit)
|
||||
if not startMs or not endMs then return end
|
||||
time = (endMs - startMs) / 1000
|
||||
elseif msgtype == 1 then
|
||||
msgtype = "Stop"
|
||||
elseif msgtype == 2 then
|
||||
msgtype = "Heal"
|
||||
heal = tonumber(string.sub(msg,4, 8))
|
||||
target = {strsplit(":", string.sub(msg,9, -1))}
|
||||
local starttime = select(5, pfGetCastInfo(sender))
|
||||
local endtime = select(6, pfGetCastInfo(sender))
|
||||
if not starttime or not endtime then return end
|
||||
time = endtime - starttime
|
||||
local unit = senderUnit()
|
||||
if not unit then return end
|
||||
local _, _, _, startMs, endMs = C_Spell.UnitCastingInfo(unit)
|
||||
if not startMs or not endMs then return end
|
||||
time = (endMs - startMs) / 1000
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -861,7 +814,7 @@ local hotsetbonus = libtipscan:GetScanner("hotsetbonus")
|
||||
resetcache:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
||||
resetcache:RegisterEvent("CHARACTER_POINTS_CHANGED")
|
||||
resetcache:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||||
resetcache:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
||||
resetcache:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
-- load and initialize previous caches of spell amounts
|
||||
@@ -872,10 +825,7 @@ resetcache:SetScript("OnEvent", function()
|
||||
cache = pfUI_cache["prediction"][realm][player]["heals"]
|
||||
end
|
||||
|
||||
if event == "UNIT_INVENTORY_CHANGED" or "PLAYER_ENTERING_WORLD" then
|
||||
-- skip non-player events
|
||||
if arg1 and arg1 ~= "player" then return end
|
||||
|
||||
if event == "PLAYER_EQUIPMENT_CHANGED" or event == "PLAYER_ENTERING_WORLD" then
|
||||
local gear = ""
|
||||
for id = 1, 18 do
|
||||
gear = gear .. (GetInventoryItemLink("player",id) or "")
|
||||
@@ -944,16 +894,6 @@ local INSTANT_HOT_COOLDOWN = 1.0 -- 1 Sekunde Cooldown (GCD ist 1.5s)
|
||||
-- Pending HoTs Queue - wird nach Delay verifiziert
|
||||
local pendingHots = {}
|
||||
|
||||
-- Helper: check if buff is present on unit
|
||||
local function UnitHasBuff(unit, buffName)
|
||||
for i = 1, 32 do
|
||||
local name = UnitBuff(unit, i)
|
||||
if not name then break end
|
||||
if name == buffName then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Gather Data by User Actions
|
||||
hooksecurefunc("CastSpell", function(id, bookType)
|
||||
if not libpredict.sender.enabled then return end
|
||||
@@ -1012,8 +952,6 @@ hooksecurefunc("CastSpellByName", function(effect, target)
|
||||
if not libpredict.sender.enabled then return end
|
||||
local effect, rank = libspell.GetSpellInfo(effect)
|
||||
if not effect then return end
|
||||
local mouseover = pfUI and pfUI.uf and pfUI.uf.mouseover and pfUI.uf.mouseover.unit
|
||||
mouseover = mouseover and UnitCanAssist("player", mouseover) and UnitName(mouseover)
|
||||
|
||||
local default = UnitName("target") and UnitCanAssist("player", "target") and UnitName("target") or UnitName("player")
|
||||
|
||||
@@ -1032,13 +970,13 @@ hooksecurefunc("CastSpellByName", function(effect, target)
|
||||
if not libpredict.sender.current_cast then
|
||||
spell_queue[1] = effect
|
||||
spell_queue[2] = effect.. ( rank or "" )
|
||||
spell_queue[3] = target or mouseover or default
|
||||
spell_queue[3] = target or default
|
||||
end
|
||||
|
||||
-- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback
|
||||
|
||||
if effect == REJUVENATION then
|
||||
local hotTarget = target or mouseover or default
|
||||
local hotTarget = target or default
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local key = "Reju" .. hotTarget
|
||||
|
||||
@@ -1055,7 +993,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
|
||||
local rankStr = rankNum and tostring(rankNum) or "0"
|
||||
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
|
||||
elseif effect == RENEW then
|
||||
local hotTarget = target or mouseover or default
|
||||
local hotTarget = target or default
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
local key = "Renew" .. hotTarget
|
||||
|
||||
@@ -1080,7 +1018,8 @@ hooksecurefunc("UseAction", function(slot, target, selfcast)
|
||||
local kind, id = GetActionInfo(slot)
|
||||
local effect, rank
|
||||
if kind == "spell" then
|
||||
effect, rank = GetSpellInfo(id)
|
||||
local spellInfo = C_Spell.GetSpellInfo(id)
|
||||
effect, rank = spellInfo.name, spellInfo.rank
|
||||
elseif kind == "macro" then
|
||||
effect, rank = GetMacroSpell(id)
|
||||
end
|
||||
@@ -1138,10 +1077,10 @@ libpredict.sender = CreateFrame("Frame", "pfPredictionSender", UIParent)
|
||||
libpredict.sender.enabled = true
|
||||
libpredict.sender.SendHealCommMsg = function(self, msg)
|
||||
-- Smart channel selection: Only send to relevant channel to avoid duplicates
|
||||
if GetNumRaidMembers() > 0 then
|
||||
if IsInRaid() then
|
||||
-- In raid: Only send to RAID (includes all raid members)
|
||||
SendAddonMessage("HealComm", msg, "RAID")
|
||||
elseif GetNumPartyMembers() > 0 then
|
||||
elseif IsInGroup() then
|
||||
-- In party: Only send to PARTY
|
||||
SendAddonMessage("HealComm", msg, "PARTY")
|
||||
end
|
||||
@@ -1150,10 +1089,10 @@ libpredict.sender.SendHealCommMsg = function(self, msg)
|
||||
end
|
||||
libpredict.sender.SendResCommMsg = function(self, msg)
|
||||
-- Smart channel selection: Only send to relevant channel to avoid duplicates
|
||||
if GetNumRaidMembers() > 0 then
|
||||
if IsInRaid() then
|
||||
-- In raid: Only send to RAID (includes all raid members)
|
||||
SendAddonMessage("CTRA", msg, "RAID")
|
||||
elseif GetNumPartyMembers() > 0 then
|
||||
elseif IsInGroup() then
|
||||
-- In party: Only send to PARTY
|
||||
SendAddonMessage("CTRA", msg, "PARTY")
|
||||
end
|
||||
@@ -1229,7 +1168,7 @@ libpredict.sender:SetScript("OnEvent", function()
|
||||
local amount = arg4
|
||||
local isCrit = arg5 == 1
|
||||
local isPeriodic = arg6 == 1
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName and spell_queue[1] == spellName then
|
||||
UpdateCache(spell_queue[2], amount, isCrit)
|
||||
end
|
||||
@@ -1249,9 +1188,9 @@ libpredict.sender:SetScript("OnEvent", function()
|
||||
local casterName = resolveNameFromGuid(casterGuid)
|
||||
if not casterName or casterName == player then return end -- own heals handled by SPELL_HEAL_BY_SELF
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
local rankStr = GetSpellRecField and GetSpellRecField(spellId, "rank") or ""
|
||||
local rankStr = C_Spell.GetSpellSubtext(spellId)
|
||||
local spellKey = spellName .. (rankStr or "")
|
||||
|
||||
foreignCache[casterName] = foreignCache[casterName] or {}
|
||||
@@ -1283,7 +1222,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
|
||||
|
||||
+14
-164
@@ -2,178 +2,28 @@
|
||||
setfenv(1, pfUI:GetEnvironment())
|
||||
|
||||
--[[ librange ]]--
|
||||
-- A pfUI library that detects and caches distance to units.
|
||||
-- A thin wrapper over ClassicAPI's UnitInRange: a fixed 40y healing-range
|
||||
-- check computed C-side from unit positions, valid for any unit. There is
|
||||
-- no cache or scan loop -- the check is cheap enough to run per query,
|
||||
-- which also sidesteps the staleness a cached scan hit on zone changes and
|
||||
-- roster re-indexing.
|
||||
--
|
||||
-- librange:UnitInSpellRange(unit)
|
||||
-- Returns `1` if the unit is within range, `nil` otherwise.
|
||||
--
|
||||
-- Requires SuperWoW's UnitPosition for the friendly scan path. Target
|
||||
-- range still works via IsActionInRange (vanilla-native) for any class
|
||||
-- with a known 40y healing spell on the action bar.
|
||||
|
||||
if pfUI.api.librange then return end
|
||||
|
||||
local _, class = UnitClass("player")
|
||||
local librange = CreateFrame("Frame", "pfRangecheck", UIParent)
|
||||
|
||||
-- 40y spells per class. Only consulted to find an action-bar slot for the
|
||||
-- IsActionInRange target-range path; the party/raid scan uses UnitPosition.
|
||||
local spells = {
|
||||
["PALADIN"] = {
|
||||
"Interface\\Icons\\Spell_Holy_FlashHeal",
|
||||
"Interface\\Icons\\Spell_Holy_HolyBolt",
|
||||
},
|
||||
["PRIEST"] = {
|
||||
"Interface\\Icons\\Spell_Holy_FlashHeal",
|
||||
"Interface\\Icons\\Spell_Holy_LesserHeal",
|
||||
"Interface\\Icons\\Spell_Holy_Heal",
|
||||
"Interface\\Icons\\Spell_Holy_GreaterHeal",
|
||||
"Interface\\Icons\\Spell_Holy_Renew",
|
||||
},
|
||||
["DRUID"] = {
|
||||
"Interface\\Icons\\Spell_Nature_HealingTouch",
|
||||
"Interface\\Icons\\Spell_Nature_ResistNature",
|
||||
"Interface\\Icons\\Spell_Nature_Rejuvenation",
|
||||
},
|
||||
["SHAMAN"] = {
|
||||
"Interface\\Icons\\Spell_Nature_MagicImmunity",
|
||||
"Interface\\Icons\\Spell_Nature_HealingWaveLesser",
|
||||
"Interface\\Icons\\Spell_Nature_HealingWaveGreater",
|
||||
},
|
||||
}
|
||||
|
||||
-- friendly units the scan loop iterates
|
||||
local units = {}
|
||||
table.insert(units, "pet")
|
||||
for i=1,4 do table.insert(units, "party" .. i) end
|
||||
for i=1,4 do table.insert(units, "partypet" .. i) end
|
||||
for i=1,40 do table.insert(units, "raid" .. i) end
|
||||
for i=1,40 do table.insert(units, "raidpet" .. i) end
|
||||
local numunits = table.getn(units)
|
||||
|
||||
local unitcache = {}
|
||||
local unitdata = {}
|
||||
local librange_isLoggingOut = false
|
||||
librange.id = 1
|
||||
|
||||
librange:Hide()
|
||||
librange:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
librange:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
librange:RegisterEvent("PLAYER_LOGOUT")
|
||||
librange:RegisterEvent("PLAYER_LEAVING_WORLD")
|
||||
librange:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_LOGOUT" or event == "PLAYER_LEAVING_WORLD" then
|
||||
librange_isLoggingOut = true
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
if pfUI_config.unitframes.rangecheck == "0" then
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
this.interval = tonumber(C.unitframes.rangechecki)/numunits
|
||||
|
||||
if event == "ACTIONBAR_SLOT_CHANGED" or event == "PLAYER_ENTERING_WORLD" then
|
||||
librange.slot = this:GetRangeSlot()
|
||||
if UnitPosition then this:Show() end
|
||||
end
|
||||
end)
|
||||
|
||||
librange:SetScript("OnUpdate", function()
|
||||
if librange_isLoggingOut then return end
|
||||
|
||||
if (this.tick or 1) > GetTime() then return end
|
||||
this.tick = GetTime() + this.interval
|
||||
|
||||
while not this:NeedRangeScan(units[this.id]) and this.id <= numunits do
|
||||
this.id = this.id + 1
|
||||
end
|
||||
|
||||
if this.id <= numunits then
|
||||
local unit = units[this.id]
|
||||
if not UnitIsUnit("target", unit) then
|
||||
local x1, y1, z1 = UnitPosition("player")
|
||||
local x2, y2, z2 = UnitPosition(unit)
|
||||
if x1 and x2 then
|
||||
local distance = ((x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2)^.5
|
||||
unitdata[unit] = distance < 45 and 1 or 0
|
||||
end
|
||||
end
|
||||
this.id = this.id + 1
|
||||
else
|
||||
this.id = 1
|
||||
end
|
||||
end)
|
||||
|
||||
function librange:NeedRangeScan(unit)
|
||||
if not UnitExists(unit) then return nil end
|
||||
if not UnitIsVisible(unit) then return nil end
|
||||
if CheckInteractDistance(unit, 4) then return nil end
|
||||
return true
|
||||
end
|
||||
|
||||
function librange:GetRealUnit(unit)
|
||||
if unitdata[unit] then return unit end
|
||||
|
||||
if unitcache[unit] and UnitIsUnit(unitcache[unit], unit) then
|
||||
return unitcache[unit]
|
||||
end
|
||||
|
||||
for id, realunit in pairs(units) do
|
||||
if UnitIsUnit(realunit, unit) then
|
||||
unitcache[unit] = realunit
|
||||
return realunit
|
||||
end
|
||||
end
|
||||
|
||||
return unit
|
||||
end
|
||||
|
||||
function librange:GetRangeSlot()
|
||||
if not spells[class] then return nil end
|
||||
for i=1,120 do
|
||||
-- Resolve the slot to a spellID for both spell and macro actions; the old
|
||||
-- `not GetActionText` macro-filter missed macros that cast a 40y heal but
|
||||
-- displayed a non-spell icon. C_Spell.GetSpellTexture(spellID) gives the
|
||||
-- spell's *intrinsic* icon, which is what we match against.
|
||||
local kind, id = GetActionInfo(i)
|
||||
local spellID
|
||||
if kind == "spell" then
|
||||
spellID = id
|
||||
elseif kind == "macro" then
|
||||
local _, _, sid = GetMacroSpell(id)
|
||||
spellID = sid
|
||||
end
|
||||
if spellID then
|
||||
local texture = C_Spell.GetSpellTexture(spellID)
|
||||
if texture then
|
||||
for _, check in pairs(spells[class]) do
|
||||
if check == texture then return i end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
local librange = {}
|
||||
|
||||
function librange:UnitInSpellRange(unit)
|
||||
if UnitIsUnit("target", unit) then
|
||||
if not librange.slot then return nil end
|
||||
return IsActionInRange(librange.slot) == 1 and 1 or nil
|
||||
end
|
||||
|
||||
local unit = librange:GetRealUnit(unit)
|
||||
|
||||
if unitdata[unit] and unitdata[unit] == 1 then
|
||||
return 1
|
||||
elseif not unitdata[unit] then
|
||||
return 1
|
||||
else
|
||||
return nil
|
||||
end
|
||||
-- _G-qualified: bare `UnitInRange` resolves to pfUI.api.UnitInRange inside
|
||||
-- the pfUI environment (which calls us), so this must reach ClassicAPI's
|
||||
-- global directly or it recurses.
|
||||
local inRange, checked = _G.UnitInRange(unit)
|
||||
-- position miss (e.g. a unit outside the client's sync range): we can't
|
||||
-- tell, so default to in-range -- matches the old cache's nil behavior.
|
||||
if not checked then return 1 end
|
||||
return inRange and 1 or nil
|
||||
end
|
||||
|
||||
-- add librange to pfUI API
|
||||
|
||||
+9
-6
@@ -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,17 +109,19 @@ 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
|
||||
local resetcache = CreateFrame("Frame")
|
||||
resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
||||
resetcache:SetScript("OnEvent", function()
|
||||
spellmaxrank, spellindex, spellinfo = {}, {}, {}
|
||||
table.wipe(spellmaxrank)
|
||||
table.wipe(spellindex)
|
||||
table.wipe(spellinfo)
|
||||
end)
|
||||
|
||||
-- add libspell to pfUI API
|
||||
|
||||
@@ -35,7 +35,7 @@ libthrottle.defaults = {
|
||||
nameplates_target = "custom",
|
||||
nameplates_castbar = "custom",
|
||||
nameplates_mass = "custom",
|
||||
tooltip_cursor = "custom",
|
||||
tooltip_cursor = "fastest",
|
||||
chat_tab = "custom",
|
||||
swingtimer = "custom",
|
||||
}
|
||||
@@ -160,7 +160,7 @@ libthrottle:SetScript("OnEvent", function()
|
||||
-- Set defaults for custom fields if missing
|
||||
if not _G.pfUI_throttle.nameplates_target_custom then _G.pfUI_throttle.nameplates_target_custom = "50" end
|
||||
if not _G.pfUI_throttle.nameplates_custom then _G.pfUI_throttle.nameplates_custom = "10" end
|
||||
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "50" end
|
||||
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "100" end
|
||||
if not _G.pfUI_throttle.nameplates_mass_custom then _G.pfUI_throttle.nameplates_mass_custom = "7" end
|
||||
if not _G.pfUI_throttle.tooltip_cursor_custom then _G.pfUI_throttle.tooltip_cursor_custom = "10" end
|
||||
if not _G.pfUI_throttle.chat_tab_custom then _G.pfUI_throttle.chat_tab_custom = "10" end
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
-- load pfUI environment
|
||||
setfenv(1, pfUI:GetEnvironment())
|
||||
|
||||
--[[ libtooltip ]]--
|
||||
-- A pfUI library that provides additional GameTooltip information.
|
||||
--
|
||||
-- libtooltip:GetItemID()
|
||||
-- returns the itemID of the current GameTooltip
|
||||
-- `nil` when no item is displayed
|
||||
--
|
||||
-- libtooltip:GetItemLink()
|
||||
-- returns the itemLink of the current GameTooltip
|
||||
-- `nil` when no item is displayed
|
||||
--
|
||||
-- libtooltip:GetItemCount()
|
||||
-- returns the item count (bags) of the current GameTooltip
|
||||
-- `nil` when no item is displayed
|
||||
|
||||
-- return instantly when another libtooltip is already active
|
||||
if pfUI.api.libtooltip then return end
|
||||
|
||||
local libtooltip = CreateFrame("Frame" , "pfLibTooltip", GameTooltip)
|
||||
|
||||
libtooltip:SetScript("OnShow", function()
|
||||
if this:GetParent():HasItem() then
|
||||
libtooltip.itemName, libtooltip.itemLink, libtooltip.itemID = this:GetParent():GetItem()
|
||||
end
|
||||
end)
|
||||
|
||||
libtooltip:SetScript("OnHide", function()
|
||||
this.itemID = nil
|
||||
this.itemLink = nil
|
||||
this.itemCount = nil
|
||||
this.itemName = nil
|
||||
end)
|
||||
|
||||
-- core functions
|
||||
libtooltip.GetItemID = function(self)
|
||||
if not libtooltip.itemLink then return end
|
||||
if not libtooltip.itemID then
|
||||
libtooltip.itemID = C_Item.GetItemInfoInstant(libtooltip.itemLink)
|
||||
end
|
||||
|
||||
return libtooltip.itemID
|
||||
end
|
||||
|
||||
libtooltip.GetItemLink = function(self)
|
||||
return libtooltip.itemLink
|
||||
end
|
||||
|
||||
libtooltip.GetItemCount = function(self)
|
||||
return libtooltip.itemCount
|
||||
end
|
||||
|
||||
pfUI.api.libtooltip = libtooltip
|
||||
|
||||
hooksecurefunc(GameTooltip, "SetBagItem", function(self, container, slot)
|
||||
_, libtooltip.itemCount = GetContainerItemInfo(container, slot)
|
||||
end)
|
||||
@@ -1,274 +0,0 @@
|
||||
-- load pfUI environment
|
||||
setfenv(1, pfUI:GetEnvironment())
|
||||
|
||||
--[[ libtotem ]]--
|
||||
-- A pfUI library that tries to emulate the TotemAPI that was introduced in Patch 2.4.
|
||||
-- It detects and saves all current totems of the player and returns information based
|
||||
-- on the totem slot ID. The function GetTotemInfo is supposed to work as it would
|
||||
-- on later expansions.
|
||||
--
|
||||
-- GetTotemInfo(id)
|
||||
-- Returns totem informations on the givent totem slot
|
||||
-- active, name, start, duration, icon
|
||||
|
||||
-- return instantly when another libtotem is already active
|
||||
if pfUI.api.libtotem then return end
|
||||
|
||||
MAX_TOTEMS = MAX_TOTEMS or 4
|
||||
FIRE_TOTEM_SLOT = FIRE_TOTEM_SLOT or 1
|
||||
EARTH_TOTEM_SLOT = EARTH_TOTEM_SLOT or 2
|
||||
WATER_TOTEM_SLOT = WATER_TOTEM_SLOT or 3
|
||||
AIR_TOTEM_SLOT = AIR_TOTEM_SLOT or 4
|
||||
|
||||
local _, class = UnitClass("player")
|
||||
|
||||
local libtotem
|
||||
local active = { [1] = {}, [2] = {}, [3] = {}, [4] = {} }
|
||||
|
||||
-- SpellID -> { slot, duration } mapping
|
||||
-- rank-specific durations are handled via spellId directly
|
||||
local spellids = {
|
||||
-- FIRE (slot 1)
|
||||
[1535] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R1
|
||||
[8498] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R2
|
||||
[8499] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R3
|
||||
[11314] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R4
|
||||
[11315] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R5
|
||||
[8227] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R1
|
||||
[8249] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R2
|
||||
[10526] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R3
|
||||
[16387] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R4
|
||||
[8184] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R1
|
||||
[10478] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R2
|
||||
[10479] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R3
|
||||
[8190] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R1
|
||||
[10585] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R2
|
||||
[10586] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R3
|
||||
[10587] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R4
|
||||
[3599] = { slot = FIRE_TOTEM_SLOT, duration = 30 }, -- Searing Totem R1
|
||||
[6363] = { slot = FIRE_TOTEM_SLOT, duration = 35 }, -- Searing Totem R2
|
||||
[6364] = { slot = FIRE_TOTEM_SLOT, duration = 40 }, -- Searing Totem R3
|
||||
[6365] = { slot = FIRE_TOTEM_SLOT, duration = 45 }, -- Searing Totem R4
|
||||
[10437] = { slot = FIRE_TOTEM_SLOT, duration = 50 }, -- Searing Totem R5
|
||||
[10438] = { slot = FIRE_TOTEM_SLOT, duration = 55 }, -- Searing Totem R6
|
||||
|
||||
-- EARTH (slot 2)
|
||||
[2484] = { slot = EARTH_TOTEM_SLOT, duration = 45 }, -- Earthbind Totem
|
||||
[5730] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R1
|
||||
[6390] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R2
|
||||
[6391] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R3
|
||||
[6392] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R4
|
||||
[10427] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R5
|
||||
[10428] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R6
|
||||
[8071] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R1
|
||||
[8154] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R2
|
||||
[8155] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R3
|
||||
[10406] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R4
|
||||
[10407] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R5
|
||||
[10408] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R6
|
||||
[8075] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R1
|
||||
[8160] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R2
|
||||
[8161] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R3
|
||||
[10442] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R4
|
||||
[25361] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R5
|
||||
[8143] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Tremor Totem
|
||||
|
||||
-- WATER (slot 3)
|
||||
[8170] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Disease Cleansing Totem
|
||||
[8185] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R1
|
||||
[10537] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R2
|
||||
[10538] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R3
|
||||
[5394] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R1
|
||||
[6375] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R2
|
||||
[6377] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R3
|
||||
[10462] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R4
|
||||
[10463] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R5
|
||||
[5675] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R1
|
||||
[10495] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R2
|
||||
[10496] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R3
|
||||
[10497] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R4
|
||||
[16190] = { slot = WATER_TOTEM_SLOT, duration = 12 }, -- Mana Tide Totem
|
||||
[8166] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Poison Cleansing Totem
|
||||
|
||||
-- AIR (slot 4)
|
||||
[8835] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Grace of Air Totem R1
|
||||
[10627] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Grace of Air Totem R2
|
||||
[8177] = { slot = AIR_TOTEM_SLOT, duration = 45 }, -- Grounding Totem
|
||||
[10595] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R1
|
||||
[10600] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R2
|
||||
[10601] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R3
|
||||
[25359] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Tranquil Air Totem
|
||||
[8512] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R1
|
||||
[10613] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R2
|
||||
[10614] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R3
|
||||
[15107] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R1
|
||||
[15421] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R2
|
||||
[15422] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R3
|
||||
}
|
||||
|
||||
-- icon-based fallback table (used by CastSpell/UseAction hooks that don't have spellId)
|
||||
local totems = {
|
||||
[FIRE_TOTEM_SLOT] = {
|
||||
["Spell_Fire_SealOfFire"] = {[-1] = 5},
|
||||
["Spell_Nature_GuardianWard"] = {[-1] = 120},
|
||||
["Spell_FrostResistanceTotem_01"] = {[-1] = 120},
|
||||
["Spell_Fire_SelfDestruct"] = {[-1] = 20},
|
||||
["Spell_Fire_SearingTotem"] = {[-1] = 55,[1] = 30,[2] = 35,[3] = 40,[4] = 45,[5] = 50,[6] = 55},
|
||||
},
|
||||
[EARTH_TOTEM_SLOT] = {
|
||||
["Spell_Nature_StrengthOfEarthTotem02"] = {[-1] = 45},
|
||||
["Spell_Nature_StoneClawTotem"] = {[-1] = 15},
|
||||
["Spell_Nature_StoneSkinTotem"] = {[-1] = 120},
|
||||
["Spell_Nature_EarthBindTotem"] = {[-1] = 120},
|
||||
["Spell_Nature_TremorTotem"] = {[-1] = 120},
|
||||
},
|
||||
[WATER_TOTEM_SLOT] = {
|
||||
["Spell_Nature_DiseaseCleansingTotem"] = {[-1] = 120},
|
||||
["Spell_FireResistanceTotem_01"] = {[-1] = 120},
|
||||
["INV_Spear_04"] = {[-1] = 60},
|
||||
["Spell_Nature_ManaRegenTotem"] = {[-1] = 60},
|
||||
["Spell_Frost_SummonWaterElemental"] = {[-1] = 12},
|
||||
["Spell_Nature_PoisonCleansingTotem"] = {[-1] = 120},
|
||||
},
|
||||
[AIR_TOTEM_SLOT] = {
|
||||
["Spell_Nature_InvisibilityTotem"] = {[-1] = 120},
|
||||
["Spell_Nature_GroundingTotem"] = {[-1] = 45},
|
||||
["Spell_Nature_NatureResistanceTotem"] = {[-1] = 120},
|
||||
["Spell_Nature_Brilliance"] = {[-1] = 120},
|
||||
["Spell_Nature_Windfury"] = {[-1] = 120},
|
||||
["Spell_Nature_EarthBind"] = {[-1] = 120},
|
||||
},
|
||||
}
|
||||
|
||||
GetTotemInfo = function(id)
|
||||
if not active[id] or not active[id].name then return end
|
||||
if active[id].start + active[id].duration - GetTime() < 0 then
|
||||
libtotem:Clean(id)
|
||||
return nil
|
||||
end
|
||||
return 1, active[id].name, active[id].start, active[id].duration, active[id].icon
|
||||
end
|
||||
|
||||
if class ~= "SHAMAN" then return end
|
||||
|
||||
libtotem = CreateFrame("Frame")
|
||||
libtotem:RegisterEvent("PLAYER_DEAD")
|
||||
libtotem:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_DEAD" then
|
||||
for i = 1, 4 do libtotem:Clean(i) end
|
||||
end
|
||||
end)
|
||||
|
||||
libtotem.totems = totems
|
||||
|
||||
libtotem.Clean = function(self, slot)
|
||||
active[slot].name = nil
|
||||
active[slot].start = nil
|
||||
active[slot].duration = nil
|
||||
active[slot].icon = nil
|
||||
end
|
||||
|
||||
-- Direct SpellID commit (Nampower SPELL_GO_SELF, most accurate)
|
||||
libtotem.CommitBySpellId = function(spellId, icon)
|
||||
local data = spellids[spellId]
|
||||
if not data then return false end
|
||||
local slot = data.slot
|
||||
active[slot].name = active[slot].pending_name or active[slot].name
|
||||
active[slot].duration = data.duration
|
||||
active[slot].icon = icon or active[slot].pending_icon
|
||||
active[slot].start = GetTime()
|
||||
active[slot].pending_name = nil
|
||||
active[slot].pending_icon = nil
|
||||
return true
|
||||
end
|
||||
|
||||
-- Fallback: icon-based lookup (for CastSpell/UseAction without spellId)
|
||||
libtotem.CheckAddQueue = function(self, name, rank, icon, spellId)
|
||||
-- if we have a spellId, just store the name/icon as pending for SPELL_GO
|
||||
if spellId and spellids[spellId] then
|
||||
local slot = spellids[spellId].slot
|
||||
active[slot].pending_name = name
|
||||
active[slot].pending_icon = icon
|
||||
return true
|
||||
end
|
||||
|
||||
-- icon-based fallback
|
||||
for slot = 1, 4 do
|
||||
for texture, data in pairs(totems[slot]) do
|
||||
if string.find(icon, texture, 1) then
|
||||
if rank then
|
||||
_, _, rank = string.find(rank, "%s(%d+)")
|
||||
end
|
||||
local duration
|
||||
if rank and tonumber(rank) and data[tonumber(rank)] then
|
||||
duration = data[tonumber(rank)]
|
||||
else
|
||||
duration = data[-1]
|
||||
end
|
||||
active[slot].pending_name = name
|
||||
active[slot].pending_icon = icon
|
||||
active[slot].pending_duration = duration
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- assign library to global space
|
||||
pfUI.api.libtotem = libtotem
|
||||
|
||||
-- SPELL_GO_SELF from libdebuff: commit directly by SpellID, no queue needed
|
||||
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
|
||||
pfUI.libdebuff_spell_go_hooks["libtotem"] = function(spellId)
|
||||
if not spellId then return end
|
||||
local data = spellids[spellId]
|
||||
if not data then return end
|
||||
local slot = data.slot
|
||||
-- use pending name/icon if available (set by CastSpellByName hook), else GetSpellInfo
|
||||
local name = active[slot].pending_name
|
||||
local icon = active[slot].pending_icon
|
||||
if not name and GetSpellInfo then
|
||||
name = GetSpellInfo(spellId)
|
||||
end
|
||||
active[slot].name = name
|
||||
active[slot].duration = data.duration
|
||||
active[slot].icon = icon
|
||||
active[slot].start = GetTime()
|
||||
active[slot].pending_name = nil
|
||||
active[slot].pending_icon = nil
|
||||
active[slot].pending_duration = nil
|
||||
end
|
||||
|
||||
-- Hook CastSpellByName to store pending name/icon per slot
|
||||
hooksecurefunc("CastSpellByName", function(effect, target)
|
||||
local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(effect)
|
||||
if not name then return end
|
||||
libtotem:CheckAddQueue(name, rank, icon, spellId)
|
||||
end)
|
||||
|
||||
-- Hook CastSpell to store pending name/icon per slot
|
||||
hooksecurefunc("CastSpell", function(id, bookType)
|
||||
if not id or not bookType then return end
|
||||
if bookType ~= BOOKTYPE_SPELL and bookType ~= BOOKTYPE_PET then return end
|
||||
local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(id, bookType)
|
||||
if not name then return end
|
||||
libtotem:CheckAddQueue(name, rank, icon, spellId)
|
||||
end)
|
||||
|
||||
-- Hook UseAction. GetActionInfo + GetMacroSpell give us the spellID
|
||||
-- directly for both spell-action and macro-action slots, so the
|
||||
-- tooltip-scan fallback (and the "no spellId available" caveat) goes away.
|
||||
hooksecurefunc("UseAction", function(slot, target, selfcast)
|
||||
if not IsCurrentAction(slot) then return end
|
||||
local kind, id = GetActionInfo(slot)
|
||||
local name, rank, spellID
|
||||
if kind == "spell" then
|
||||
spellID = id
|
||||
name, rank = GetSpellInfo(id)
|
||||
elseif kind == "macro" then
|
||||
name, rank, spellID = GetMacroSpell(id)
|
||||
end
|
||||
if not name then return end
|
||||
libtotem:CheckAddQueue(name, rank, GetActionTexture(slot), spellID)
|
||||
end)
|
||||
+32
-31
@@ -89,20 +89,19 @@ libunitscan:SetScript("OnEvent", function()
|
||||
|
||||
-- update own character details
|
||||
local name = UnitName("player")
|
||||
local _, class = UnitClass("player")
|
||||
local class = UnitClassBase("player")
|
||||
local level = UnitLevel("player")
|
||||
local guild = GetGuildInfo("player")
|
||||
AddData("players", name, class, level, nil, guild)
|
||||
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
|
||||
@@ -128,7 +127,7 @@ libunitscan:SetScript("OnEvent", function()
|
||||
local name, class, level, unit, _, guild
|
||||
for i = 1, GetNumPartyMembers() do
|
||||
unit = "party" .. i
|
||||
_, class = UnitClass(unit)
|
||||
class = UnitClassBase(unit)
|
||||
name = UnitName(unit)
|
||||
level = UnitLevel(unit)
|
||||
guild = GetGuildInfo(unit)
|
||||
@@ -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
|
||||
@@ -149,24 +148,26 @@ libunitscan:SetScript("OnEvent", function()
|
||||
or event == "NAME_PLATE_UNIT_ADDED" and arg1
|
||||
or "mouseover"
|
||||
local name, class, level, elite, guild, _
|
||||
if UnitIsPlayer(scan) then
|
||||
_, class = UnitClass(scan)
|
||||
level = UnitLevel(scan)
|
||||
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
|
||||
level = level > 0 and level or nil
|
||||
name = UnitName(scan)
|
||||
guild = GetGuildInfo(scan)
|
||||
AddData("players", name, class, level, nil, guild)
|
||||
RememberByUnit(scan, name, class)
|
||||
else
|
||||
_, class = UnitClass(scan)
|
||||
elite = UnitClassification(scan)
|
||||
level = UnitLevel(scan)
|
||||
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
|
||||
level = level > 0 and level or nil
|
||||
name = UnitName(scan)
|
||||
guild = UnitSubName(scan)
|
||||
AddData("mobs", name, class, level, elite, guild)
|
||||
if UnitExists(scan) then
|
||||
if UnitIsPlayer(scan) then
|
||||
class = UnitClassBase(scan)
|
||||
level = UnitLevel(scan)
|
||||
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
|
||||
level = level > 0 and level or nil
|
||||
name = UnitName(scan)
|
||||
guild = GetGuildInfo(scan)
|
||||
AddData("players", name, class, level, nil, guild)
|
||||
RememberByUnit(scan, name, class)
|
||||
else
|
||||
class = UnitClassBase(scan)
|
||||
elite = UnitClassification(scan)
|
||||
level = UnitLevel(scan)
|
||||
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
|
||||
level = level > 0 and level or nil
|
||||
name = UnitName(scan)
|
||||
guild = UnitSubName(scan)
|
||||
AddData("mobs", name, class, level, elite, guild)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
+87
-97
@@ -1,5 +1,5 @@
|
||||
pfUI:RegisterModule("actionbar", function ()
|
||||
local _, class = UnitClass("player")
|
||||
local class = UnitClassBase("player")
|
||||
local _, cr, cg, cb = GetUnitColor('player')
|
||||
local er, eg, eb, ea = GetStringColor(pfUI_config.appearance.border.color)
|
||||
|
||||
@@ -168,8 +168,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
["zoomfade"] = function()
|
||||
if this.active == 0 then
|
||||
-- init animation
|
||||
this:SetWidth(this.parent:GetWidth())
|
||||
this:SetHeight(this.parent:GetHeight())
|
||||
this:SetSize(this.parent:GetSize())
|
||||
this:SetScale(this.parent:GetScale())
|
||||
this.tex:SetTexture(this.parent.icon:GetTexture())
|
||||
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
|
||||
@@ -191,8 +190,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
["shrinkreturn"] = function()
|
||||
if this.active == 0 then
|
||||
-- init animation
|
||||
this:SetWidth(this.parent:GetWidth())
|
||||
this:SetHeight(this.parent:GetHeight())
|
||||
this:SetSize(this.parent:GetSize())
|
||||
this:SetScale(this.parent:GetScale())
|
||||
this.tex:SetTexture(this.parent.icon:GetTexture())
|
||||
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
|
||||
@@ -218,8 +216,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
["elasticzoom"] = function()
|
||||
if this.active == 0 then
|
||||
-- init animation
|
||||
this:SetWidth(this.parent:GetWidth())
|
||||
this:SetHeight(this.parent:GetHeight())
|
||||
this:SetSize(this.parent:GetSize())
|
||||
this:SetScale(this.parent:GetScale())
|
||||
this.tex:SetTexture(this.parent.icon:GetTexture())
|
||||
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
|
||||
@@ -245,8 +242,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
["wobblezoom"] = function()
|
||||
if this.active == 0 then
|
||||
-- init animation
|
||||
this:SetWidth(this.parent:GetWidth())
|
||||
this:SetHeight(this.parent:GetHeight())
|
||||
this:SetSize(this.parent:GetSize())
|
||||
this:SetScale(this.parent:GetScale())
|
||||
this.tex:SetTexture(this.parent.icon:GetTexture())
|
||||
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
|
||||
@@ -335,8 +331,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
local slfcast = C.bars.altself == "1" and IsAltKeyDown() and true or self.slfcast
|
||||
slfcast = C.bars.rightself == "1" and arg1 and arg1 == "RightButton" and true or slfcast
|
||||
self.slfcast = nil
|
||||
|
||||
if ( pfUI_config.bars.keydown == "1" and keystate == "down" and not drag_active ) or (pfUI_config.bars.keydown == "0" and keystate == "up" or drag_active ) or self.bar == 11 or mouse then
|
||||
if ( C.bars.keydown == "1" and keystate == "down" and not drag_active ) or (C.bars.keydown == "0" and keystate == "up" or drag_active ) or self.bar == 11 or mouse then
|
||||
if self.bar == 11 then
|
||||
CastShapeshiftForm(self.id)
|
||||
elseif grid == 1 then
|
||||
@@ -376,12 +371,12 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
-- 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
|
||||
self.spellslot, self.booktype, self.spellID = nil, nil, nil
|
||||
return
|
||||
end
|
||||
|
||||
local kind, slot = GetActionInfo(self.id)
|
||||
self.spellslot, self.booktype = nil, nil
|
||||
self.spellslot, self.booktype, self.spellID = nil, nil, nil
|
||||
if kind == 'macro' then
|
||||
local name, _, body = GetMacroInfo(slot)
|
||||
|
||||
@@ -419,10 +414,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
if match then
|
||||
local _, _, spell, rank = string.find(match, '(.+)%((.+)%)')
|
||||
spell = spell or match
|
||||
self.spellslot, self.booktype = libspell.GetSpellIndex(spell, rank)
|
||||
|
||||
self.spellslot, self.booktype, self.spellID = select(7, libspell.GetSpellInfo(match))
|
||||
if self.spellslot and self.spellslot > 0 then return end
|
||||
end
|
||||
end
|
||||
@@ -431,7 +423,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
local function ButtonEnter(self)
|
||||
local self = self or this
|
||||
self = self or this
|
||||
|
||||
-- indicate that dragging could get enabled
|
||||
drag_await = true
|
||||
@@ -449,6 +441,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
|
||||
@@ -459,7 +453,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
|
||||
@@ -472,7 +466,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
|
||||
@@ -591,7 +585,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
|
||||
@@ -605,17 +599,17 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
-- update usable [out-of-range = 1, oom = 2, not-usable = 3, default = 0]
|
||||
if self.outofrange and C.bars.glowrange == "1" then
|
||||
if self.vertexstate ~= 1 then
|
||||
self.icon:SetVertexColor(self.rangeColor[1], self.rangeColor[2], self.rangeColor[3], self.rangeColor[4])
|
||||
self.icon:SetVertexColor(self.rangeColor:GetRGBA())
|
||||
self.vertexstate = 1
|
||||
end
|
||||
elseif oom and C.bars.showoom == "1" then
|
||||
if self.vertexstate ~= 2 then
|
||||
self.icon:SetVertexColor(self.oomColor[1], self.oomColor[2], self.oomColor[3], self.oomColor[4])
|
||||
self.icon:SetVertexColor(self.oomColor:GetRGBA())
|
||||
self.vertexstate = 2
|
||||
end
|
||||
elseif not usable and C.bars.showna == "1" then
|
||||
if self.vertexstate ~= 3 then
|
||||
self.icon:SetVertexColor(self.naColor[1], self.naColor[2], self.naColor[3], self.naColor[4])
|
||||
self.icon:SetVertexColor(self.naColor:GetRGBA())
|
||||
self.vertexstate = 3
|
||||
end
|
||||
else
|
||||
@@ -627,7 +621,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
|
||||
@@ -705,7 +699,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
|
||||
@@ -854,16 +848,20 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
end
|
||||
|
||||
local cat, stealth
|
||||
local inCatForm = nil
|
||||
local prowlActive = nil
|
||||
|
||||
-- Cat Form ID per vanilla 1.12 SpellShapeshiftForm.dbc.
|
||||
-- Form IDs per vanilla 1.12 SpellShapeshiftForm.dbc.
|
||||
local CAT_FORM = 1
|
||||
local SHADOWFORM = 28
|
||||
local function HasCatForm()
|
||||
return GetShapeshiftFormID() == CAT_FORM and true or nil
|
||||
end
|
||||
|
||||
local function InShadowform()
|
||||
return GetShapeshiftFormID() == SHADOWFORM and true or nil
|
||||
end
|
||||
|
||||
local function FullScan()
|
||||
if class ~= "DRUID" then return nil end
|
||||
inCatForm = HasCatForm()
|
||||
@@ -873,7 +871,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- pagemaster / meta page switch
|
||||
do
|
||||
local prowl, shift, ctrl, alt, default = 8, 6, 5, 3, 1
|
||||
local formpage, shift, ctrl, alt, default = 8, 6, 5, 3, 1
|
||||
|
||||
-- set temporary pagemaster bindings keybinds
|
||||
if C.bars.pagemaster == "1" then
|
||||
@@ -891,8 +889,10 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end)
|
||||
end
|
||||
|
||||
-- setup page switch frame
|
||||
local prowling = nil
|
||||
-- setup page switch frame. `formpaging` is the shared "auto-page is
|
||||
-- active" flag; druid prowl and priest shadowform each drive it, and
|
||||
-- no character is ever both, so one flag covers both features.
|
||||
local formpaging = nil
|
||||
local pageswitch = CreateFrame("Frame", "pfActionBarPageSwitch", UIParent)
|
||||
pageswitch:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
|
||||
@@ -906,10 +906,17 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
return
|
||||
end
|
||||
|
||||
if class == "PRIEST" then
|
||||
if event == "UPDATE_SHAPESHIFT_FORM" or event == "PLAYER_ENTERING_WORLD" then
|
||||
formpaging = InShadowform()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if class ~= "DRUID" then return end
|
||||
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
prowling = FullScan()
|
||||
formpaging = FullScan()
|
||||
return
|
||||
end
|
||||
|
||||
@@ -918,7 +925,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
inCatForm = HasCatForm()
|
||||
if not inCatForm then
|
||||
prowlActive = nil
|
||||
prowling = nil
|
||||
formpaging = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -928,10 +935,10 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
if inCatForm then
|
||||
if IsStealthed() then
|
||||
prowlActive = true
|
||||
prowling = true
|
||||
formpaging = true
|
||||
else
|
||||
prowlActive = nil
|
||||
prowling = nil
|
||||
formpaging = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -947,7 +954,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
if PROWL_IDS[spellId] then
|
||||
inCatForm = true
|
||||
prowlActive = true
|
||||
prowling = true
|
||||
formpaging = true
|
||||
end
|
||||
end
|
||||
pageswitch:SetScript("OnUpdate", function()
|
||||
@@ -965,11 +972,12 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
SwitchBar(default)
|
||||
end
|
||||
|
||||
-- switch actionbar page if druid stealth is detected
|
||||
if C.bars.druidstealth == "1" then
|
||||
if prowling and _G.CURRENT_ACTIONBAR_PAGE == 1 then
|
||||
SwitchBar(prowl)
|
||||
elseif not prowling and _G.CURRENT_ACTIONBAR_PAGE == 8 then
|
||||
-- switch actionbar page while druid stealth / priest shadowform is active
|
||||
if (class == "DRUID" and C.bars.druidstealth == "1")
|
||||
or (class == "PRIEST" and C.bars.priestshadow == "1") then
|
||||
if formpaging and _G.CURRENT_ACTIONBAR_PAGE == 1 then
|
||||
SwitchBar(formpage)
|
||||
elseif not formpaging and _G.CURRENT_ACTIONBAR_PAGE == 8 then
|
||||
SwitchBar(default)
|
||||
end
|
||||
end
|
||||
@@ -983,13 +991,13 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
local font_offset = tonumber(C.bars.font_offset)
|
||||
|
||||
local macro_size = tonumber(C.bars.macro_size)
|
||||
local macro_color = { strsplit(",", C.bars.macro_color) }
|
||||
local macro_color = { GetStringColor(C.bars.macro_color) }
|
||||
|
||||
local count_size = tonumber(C.bars.count_size)
|
||||
local count_color = { strsplit(",", C.bars.count_color) }
|
||||
local count_color = { GetStringColor(C.bars.count_color) }
|
||||
|
||||
local bind_size = tonumber(C.bars.bind_size)
|
||||
local bind_color = { strsplit(",", C.bars.bind_color) }
|
||||
local bind_color = { GetStringColor(C.bars.bind_color) }
|
||||
|
||||
local cd_size = tonumber(C.bars.cd_size)
|
||||
|
||||
@@ -1013,10 +1021,8 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
-- no button available, create a new one
|
||||
if not exists then
|
||||
-- prepare the button for vanilla
|
||||
if not f.HookScript then
|
||||
f.HookScript = HookScript
|
||||
f:SetScript("OnClick", ButtonClick)
|
||||
end
|
||||
if not f.HookScript then f.HookScript = HookScript end
|
||||
f:SetScript("OnClick", ButtonClick)
|
||||
|
||||
if bar ~= 11 then
|
||||
f:RegisterForDrag("LeftButton", "RightButton")
|
||||
@@ -1150,23 +1156,22 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
-- range glow color
|
||||
f.rangeColor = { strsplit(",", C.bars.rangecolor) }
|
||||
f.rangeColor = GetStringColorObject(C.bars.rangecolor)
|
||||
|
||||
-- out of mana color
|
||||
f.oomColor = { strsplit(",", C.bars.oomcolor) }
|
||||
f.oomColor = GetStringColorObject(C.bars.oomcolor)
|
||||
|
||||
-- not usable color
|
||||
f.naColor = { strsplit(",", C.bars.nacolor) }
|
||||
f.naColor = GetStringColorObject(C.bars.nacolor)
|
||||
|
||||
-- equipped color
|
||||
if f.equipped then
|
||||
f.equipped:SetTexture(strsplit(",", C.bars.eqcolor))
|
||||
f.equipped:SetTexture(GetStringColor(C.bars.eqcolor))
|
||||
end
|
||||
|
||||
-- general appearance
|
||||
f.showempty = showempty == "1" and true or nil
|
||||
f:SetHeight(size)
|
||||
f:SetWidth(size)
|
||||
f:SetSize(size, size)
|
||||
CreateBackdrop(f, border)
|
||||
|
||||
return f
|
||||
@@ -1338,8 +1343,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- adjust actionbar size
|
||||
BarLayoutSize(bars[i], buttons, formfactor, size, border, spacing, uneven, fillmode)
|
||||
bars[i]:SetWidth(bars[i]._size[1])
|
||||
bars[i]:SetHeight(bars[i]._size[2])
|
||||
bars[i]:SetSize(bars[i]._size[1], bars[i]._size[2])
|
||||
bars[i]:ClearAllPoints()
|
||||
if i == 1 then -- main
|
||||
bars[i]:SetPoint("BOTTOM", 0, 2*border)
|
||||
@@ -1358,6 +1362,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- make stance bar dodge by default
|
||||
bars[i]:SetScript("OnShow", function()
|
||||
if pfUI.unlock and pfUI.unlock:IsShown() then return end
|
||||
if bars[11] and bars[11]:IsShown() then
|
||||
bars[11]:ClearAllPoints()
|
||||
bars[11]:SetPoint("BOTTOM", bars[12], "TOP", 0, 3*border)
|
||||
@@ -1367,6 +1372,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
|
||||
-- restore old stance bar position
|
||||
bars[i]:SetScript("OnHide", function()
|
||||
if pfUI.unlock and pfUI.unlock:IsShown() then return end
|
||||
if bars[11] and bars[11]:IsShown() then
|
||||
bars[11]:ClearAllPoints()
|
||||
bars[11]:SetPoint("BOTTOM", bars[6], "TOP", 0, 3*border)
|
||||
@@ -1539,7 +1545,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
-- via GetActionInfo + C_Spell.GetSpellReagents. Macro actions and
|
||||
-- bag-item actions are skipped (their reagent resolution would need a
|
||||
-- macro-body parse / item-effect lookup that we don't bother with).
|
||||
local UpdateSlot = function(slot)
|
||||
local function UpdateSlot(slot)
|
||||
local newID = nil
|
||||
if HasAction(slot) then
|
||||
local kind, spellID = GetActionInfo(slot)
|
||||
@@ -1554,60 +1560,44 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
if reagent_slots[slot] ~= newID then
|
||||
reagent_slots[slot] = newID
|
||||
if newID then
|
||||
reagent_counts[newID] = reagent_counts[newID] or 0
|
||||
reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID)
|
||||
end
|
||||
updatecache[slot] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Recount every tracked reagent and flag its buttons for a refresh.
|
||||
local RecountReagents = function()
|
||||
for itemID in pairs(reagent_counts) do
|
||||
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
|
||||
end
|
||||
for slot in pairs(reagent_slots) do
|
||||
updatecache[slot] = true
|
||||
end
|
||||
end
|
||||
|
||||
local reagentcounter = CreateFrame("Frame", "pfReagentCounter", UIParent)
|
||||
reagentcounter:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
reagentcounter:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
reagentcounter:RegisterEvent("BAG_UPDATE_DELAYED")
|
||||
reagentcounter:SetScript("OnEvent", function()
|
||||
if event == "BAG_UPDATE_DELAYED" then
|
||||
this.event = true
|
||||
if event == "ACTIONBAR_SLOT_CHANGED" then
|
||||
-- arg1 is the changed slot; 0 (or nil) means "all slots"
|
||||
if arg1 and arg1 > 0 then
|
||||
UpdateSlot(arg1)
|
||||
else
|
||||
for slot = 1, 120 do UpdateSlot(slot) end
|
||||
end
|
||||
elseif event == "BAG_UPDATE_DELAYED" then
|
||||
-- inventory changed: refresh the counts we already track
|
||||
RecountReagents()
|
||||
else
|
||||
this.scan = 1
|
||||
-- PLAYER_ENTERING_WORLD: seed the full reagent map (UpdateSlot seeds
|
||||
-- each new reagent's count; a BAG_UPDATE_DELAYED follows during login)
|
||||
for slot = 1, 120 do UpdateSlot(slot) end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Reagent counter update with throttle for performance optimization
|
||||
reagentcounter:SetScript("OnUpdate", function()
|
||||
-- Throttle entire function to 10 FPS for smooth scanning
|
||||
if (this.tick_update or 0) > GetTime() then return end
|
||||
this.tick_update = GetTime() + 0.1
|
||||
|
||||
-- scan one action slot per update
|
||||
if this.scan and this.scan <= 120 then
|
||||
UpdateSlot(this.scan)
|
||||
this.scan = this.scan + 1
|
||||
end
|
||||
|
||||
-- trigger reagent count updates after action scans
|
||||
if this.scan and this.scan >= 120 then
|
||||
this.event = true
|
||||
this.scan = nil
|
||||
end
|
||||
|
||||
-- queue events to fire only once per second
|
||||
if not this.event then return end
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
|
||||
|
||||
-- scan for all reagent item counts
|
||||
for itemID in pairs(reagent_counts) do
|
||||
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
|
||||
end
|
||||
|
||||
-- update all actionbar buttons
|
||||
for slot in pairs(reagent_slots) do
|
||||
updatecache[slot] = true
|
||||
end
|
||||
|
||||
-- remove event trigger
|
||||
this.event = nil
|
||||
end)
|
||||
|
||||
function IsReagentAction(slot)
|
||||
return reagent_slots[slot] and true or nil
|
||||
end
|
||||
|
||||
+27
-33
@@ -37,15 +37,10 @@ pfUI:RegisterModule("addonbuttons", function ()
|
||||
|
||||
pfUI.addonbuttons.minimapbutton = CreateFrame("Button", "pfMinimapButton", pfUI.minimap or UIParent)
|
||||
pfUI.addonbuttons.minimapbutton:SetFrameLevel(24)
|
||||
pfUI.addonbuttons.minimapbutton:SetWidth(12)
|
||||
pfUI.addonbuttons.minimapbutton:SetHeight(12)
|
||||
pfUI.addonbuttons.minimapbutton:SetSize(12, 12)
|
||||
|
||||
pfUI.addonbuttons.minimapbutton:SetScript("OnClick", function()
|
||||
if pfUI.addonbuttons:IsShown() then
|
||||
pfUI.addonbuttons:Hide()
|
||||
else
|
||||
pfUI.addonbuttons:Show()
|
||||
end
|
||||
pfUI.addonbuttons:SetShown(not pfUI.addonbuttons:IsShown())
|
||||
end)
|
||||
|
||||
pfUI.addonbuttons.buttons = {}
|
||||
@@ -163,27 +158,25 @@ pfUI:RegisterModule("addonbuttons", function ()
|
||||
pfUI.addonbuttons:SetScale(pfUI.minimap:GetScale())
|
||||
|
||||
pfUI.addonbuttons.minimapbutton:ClearAllPoints()
|
||||
local mbtnWidth, mbtnHeight = pfUI.minimap:GetSize()
|
||||
local dynamicSize = ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing)
|
||||
if C.abuttons.position == "bottom" then
|
||||
pfUI.addonbuttons:SetWidth(pfUI.minimap:GetWidth())
|
||||
pfUI.addonbuttons:SetHeight(ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing))
|
||||
pfUI.addonbuttons:SetSize(mbtnWidth, dynamicSize)
|
||||
pfUI.addonbuttons:SetPoint("TOP", pfUI.minimap, "BOTTOM", 0 , -default_border * 3)
|
||||
SkinArrowButton(pfUI.addonbuttons.minimapbutton, "down")
|
||||
pfUI.addonbuttons.minimapbutton:SetPoint("BOTTOM", pfUI.minimap, "BOTTOM", 0, 4)
|
||||
elseif C.abuttons.position == "left" then
|
||||
pfUI.addonbuttons:SetWidth(ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing))
|
||||
pfUI.addonbuttons:SetHeight(pfUI.minimap:GetHeight())
|
||||
pfUI.addonbuttons:SetSize(dynamicSize, mbtnHeight)
|
||||
pfUI.addonbuttons:SetPoint("TOPRIGHT", pfUI.minimap, "TOPLEFT", -default_border * 3, 0)
|
||||
SkinArrowButton(pfUI.addonbuttons.minimapbutton, "left")
|
||||
pfUI.addonbuttons.minimapbutton:SetPoint("LEFT", pfUI.minimap, "LEFT", 4, 0)
|
||||
elseif C.abuttons.position == "top" then
|
||||
pfUI.addonbuttons:SetWidth(pfUI.minimap:GetWidth())
|
||||
pfUI.addonbuttons:SetHeight(ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing))
|
||||
pfUI.addonbuttons:SetSize(mbtnWidth, dynamicSize)
|
||||
pfUI.addonbuttons:SetPoint("BOTTOM", pfUI.minimap, "TOP", 0 , default_border * 3)
|
||||
SkinArrowButton(pfUI.addonbuttons.minimapbutton, "up")
|
||||
pfUI.addonbuttons.minimapbutton:SetPoint("TOP", pfUI.minimap, "TOP", 0, -4)
|
||||
elseif C.abuttons.position == "right" then
|
||||
pfUI.addonbuttons:SetWidth(ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing))
|
||||
pfUI.addonbuttons:SetHeight(pfUI.minimap:GetHeight())
|
||||
pfUI.addonbuttons:SetSize(dynamicSize, mbtnHeight)
|
||||
pfUI.addonbuttons:SetPoint("TOPLEFT", pfUI.minimap, "TOPRIGHT", default_border * 3, 0)
|
||||
SkinArrowButton(pfUI.addonbuttons.minimapbutton, "right")
|
||||
pfUI.addonbuttons.minimapbutton:SetPoint("RIGHT", pfUI.minimap, "RIGHT", -4, 0)
|
||||
@@ -231,7 +224,7 @@ pfUI:RegisterModule("addonbuttons", function ()
|
||||
frame.backup.is_clamped_to_screen = frame:IsClampedToScreen()
|
||||
frame.backup.is_movable = frame:IsMovable()
|
||||
frame.backup.point = {frame:GetPoint()}
|
||||
frame.backup.size = {frame:GetHeight(), frame:GetWidth()}
|
||||
frame.backup.size = {frame:GetSize()}
|
||||
frame.backup.scale = frame:GetScale()
|
||||
if frame:HasScript("OnDragStart") then
|
||||
frame.backup.on_drag_start = frame:GetScript("OnDragStart")
|
||||
@@ -256,8 +249,7 @@ pfUI:RegisterModule("addonbuttons", function ()
|
||||
frame:SetClampedToScreen(frame.backup.is_clamped_to_screen)
|
||||
frame:SetMovable(frame.backup.is_movable)
|
||||
frame:SetScale(frame.backup.scale)
|
||||
frame:SetHeight(frame.backup.size[1])
|
||||
frame:SetWidth(frame.backup.size[2])
|
||||
frame:SetSize(frame.backup.size[2], frame.backup.size[1])
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint(frame.backup.point[1], frame.backup.point[2], frame.backup.point[3], frame.backup.point[4], frame.backup.point[5])
|
||||
if frame.backup.on_drag_start ~= nil then
|
||||
@@ -405,22 +397,25 @@ pfUI:RegisterModule("addonbuttons", function ()
|
||||
pfUI.addonbuttons:ProcessButtons()
|
||||
end)
|
||||
|
||||
pfUI.addonbuttons:SetScript("OnUpdate", function()
|
||||
-- Initial setup on the next frame, once other addons' minimap buttons exist
|
||||
RunNextFrame(function()
|
||||
-- check if the panel should be shown by default
|
||||
if not this.initialized then
|
||||
if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then
|
||||
pfUI.addonbuttons:Show()
|
||||
else
|
||||
pfUI.addonbuttons:Hide()
|
||||
end
|
||||
|
||||
-- update all buttons
|
||||
pfUI.addonbuttons:ProcessButtons()
|
||||
this.initialized = true
|
||||
if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then
|
||||
pfUI.addonbuttons:Show()
|
||||
else
|
||||
pfUI.addonbuttons:Hide()
|
||||
end
|
||||
|
||||
-- throttle updates to once per 5 seconds
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 5 end
|
||||
-- update all buttons and apply workarounds
|
||||
pfUI.addonbuttons:ProcessButtons()
|
||||
for k, v in pairs(pfUI.addonbuttons.overrides) do
|
||||
_G[k] = v
|
||||
end
|
||||
end)
|
||||
|
||||
-- Rescan minimap buttons every 5 seconds while the panel is shown
|
||||
C_Timer.NewTicker(5, function()
|
||||
if not pfUI.addonbuttons:IsShown() then return end
|
||||
|
||||
-- reload/rescan minimap buttons
|
||||
pfUI.addonbuttons:ProcessButtons()
|
||||
@@ -440,6 +435,5 @@ pfUI:RegisterModule("addonbuttons", function ()
|
||||
|
||||
pfUI.addonbuttons:UpdateConfig()
|
||||
|
||||
_G.SLASH_PFABP1, _G.SLASH_PFABP2 = "/abp", "/pfabp"
|
||||
_G.SlashCmdList.PFABP = ManualAddOrRemove
|
||||
pfUI.api.RegisterSlashCommand("PFABP", { "/abp", "/pfabp" }, ManualAddOrRemove, true)
|
||||
end)
|
||||
|
||||
+4
-14
@@ -134,19 +134,9 @@ pfUI:RegisterModule("addoncompat", function ()
|
||||
end
|
||||
|
||||
-- run the addonconflict queue when firstrun is ready
|
||||
local delay = CreateFrame("Frame")
|
||||
delay:SetScript("OnUpdate", function()
|
||||
-- throttle to to one query per .1 second
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
|
||||
|
||||
-- make sure the firstrun dialog has finished
|
||||
if pfUI.firstrun and pfUI.firstrun.steps then
|
||||
for _, step in pairs(pfUI.firstrun.steps) do
|
||||
if not pfUI_init[step.name] then return end
|
||||
end
|
||||
end
|
||||
|
||||
if pfUI.firstrun and pfUI.firstrun.completed then
|
||||
RunQueue()
|
||||
this:SetScript("OnUpdate", nil)
|
||||
end)
|
||||
else
|
||||
pfUI.events:RegisterCallback("firstrun:complete", RunQueue, "addoncompat")
|
||||
end
|
||||
end)
|
||||
|
||||
+26
-14
@@ -18,8 +18,7 @@ pfUI:RegisterModule("addons", function ()
|
||||
-- addon window
|
||||
pfUI.addons = CreateFrame("Frame", "pfAddons", UIParent)
|
||||
pfUI.addons:SetFrameStrata("DIALOG")
|
||||
pfUI.addons:SetHeight(490)
|
||||
pfUI.addons:SetWidth(380)
|
||||
pfUI.addons:SetSize(380, 490)
|
||||
pfUI.addons:SetPoint("CENTER", 0,0)
|
||||
pfUI.addons:EnableMouseWheel(1)
|
||||
pfUI.addons:SetMovable(true)
|
||||
@@ -51,8 +50,7 @@ pfUI:RegisterModule("addons", function ()
|
||||
pfUI.addons.close = CreateFrame("Button", "pfBagClose", pfUI.addons)
|
||||
pfUI.addons.close:SetPoint("TOPRIGHT", -border*2,-border*2 )
|
||||
CreateBackdrop(pfUI.addons.close)
|
||||
pfUI.addons.close:SetHeight(15)
|
||||
pfUI.addons.close:SetWidth(15)
|
||||
pfUI.addons.close:SetSize(15, 15)
|
||||
pfUI.addons.close.texture = pfUI.addons.close:CreateTexture("pfBagClose")
|
||||
pfUI.addons.close.texture:SetTexture(pfUI.media["img:close"])
|
||||
pfUI.addons.close.texture:ClearAllPoints()
|
||||
@@ -125,8 +123,7 @@ pfUI:RegisterModule("addons", function ()
|
||||
end)
|
||||
|
||||
pfUI.addons.profile:SetPoint("TOP", pfUI.addons, "TOP", 0, -30)
|
||||
pfUI.addons.profile:SetHeight(30)
|
||||
pfUI.addons.profile:SetWidth(370)
|
||||
pfUI.addons.profile:SetSize(370, 30)
|
||||
CreateBackdrop(pfUI.addons.profile, nil, true)
|
||||
|
||||
-- addon profile: title
|
||||
@@ -139,8 +136,7 @@ pfUI:RegisterModule("addons", function ()
|
||||
-- addon profile: delete
|
||||
pfUI.addons.profile.del = CreateFrame("Button", nil, pfUI.addons.profile, "UIPanelButtonTemplate")
|
||||
SkinButton(pfUI.addons.profile.del)
|
||||
pfUI.addons.profile.del:SetWidth(16)
|
||||
pfUI.addons.profile.del:SetHeight(16)
|
||||
pfUI.addons.profile.del:SetSize(16, 16)
|
||||
pfUI.addons.profile.del:SetPoint("RIGHT", -10, 0)
|
||||
pfUI.addons.profile.del:GetFontString():SetPoint("CENTER", 1, 0)
|
||||
pfUI.addons.profile.del:SetText("-")
|
||||
@@ -157,8 +153,7 @@ pfUI:RegisterModule("addons", function ()
|
||||
-- addon profile: create
|
||||
pfUI.addons.profile.add = CreateFrame("Button", nil, pfUI.addons.profile, "UIPanelButtonTemplate")
|
||||
SkinButton(pfUI.addons.profile.add)
|
||||
pfUI.addons.profile.add:SetWidth(16)
|
||||
pfUI.addons.profile.add:SetHeight(16)
|
||||
pfUI.addons.profile.add:SetSize(16, 16)
|
||||
pfUI.addons.profile.add:SetPoint("RIGHT", pfUI.addons.profile.del, "LEFT", -4, 0)
|
||||
pfUI.addons.profile.add:GetFontString():SetPoint("CENTER", 1, 0)
|
||||
pfUI.addons.profile.add:SetText("+")
|
||||
@@ -201,8 +196,7 @@ pfUI:RegisterModule("addons", function ()
|
||||
|
||||
-- addon list: scroll frame
|
||||
pfUI.addons.scroll = CreateScrollFrame("pfAddonListScroll", pfUI.addons)
|
||||
pfUI.addons.scroll:SetWidth(360)
|
||||
pfUI.addons.scroll:SetHeight(410)
|
||||
pfUI.addons.scroll:SetSize(360, 410)
|
||||
pfUI.addons.scroll:SetPoint("BOTTOM", 0, 10)
|
||||
|
||||
pfUI.addons.scroll.backdrop = CreateFrame("Frame", nil, pfUI.addons.scroll)
|
||||
@@ -217,6 +211,21 @@ pfUI:RegisterModule("addons", function ()
|
||||
pfUI.addons.list:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
pfUI.addons.list:SetHeight(GetNumAddOns() * 25 + 26)
|
||||
|
||||
local function AddDependencyLines(header, deps)
|
||||
if not deps or table.getn(deps) == 0 then return end
|
||||
GameTooltip:AddLine(" ")
|
||||
GameTooltip:AddLine(header .. ":", .2, 1, .8)
|
||||
for _, dep in ipairs(deps) do
|
||||
if IsAddOnLoaded(dep) then
|
||||
GameTooltip:AddLine(" " .. dep, .5, 1, .5)
|
||||
elseif C_AddOns.DoesAddOnExist(dep) then
|
||||
GameTooltip:AddLine(" " .. dep, 1, .82, 0)
|
||||
else
|
||||
GameTooltip:AddLine(" " .. dep .. " (" .. T["Missing"] .. ")", 1, .4, .4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function AddonOnEnter()
|
||||
this:SetBackdropBorderColor(1,1,1,.08)
|
||||
|
||||
@@ -231,6 +240,8 @@ pfUI:RegisterModule("addons", function ()
|
||||
end
|
||||
|
||||
GameTooltip:AddLine(this.anote, .75,.75,.75,1)
|
||||
AddDependencyLines(T["Dependencies"], this.adeps)
|
||||
AddDependencyLines(T["Optional Dependencies"], this.aoptdeps)
|
||||
GameTooltip:SetWidth(180)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
@@ -272,9 +283,10 @@ pfUI:RegisterModule("addons", function ()
|
||||
frame.anote = anote
|
||||
frame.aauthor = aauthor
|
||||
frame.aversion = aversion
|
||||
frame.adeps = { GetAddOnDependencies(i) } -- required (.toc Dependencies)
|
||||
frame.aoptdeps = { C_AddOns.GetAddOnOptionalDependencies(i) } -- optional (.toc OptionalDeps)
|
||||
|
||||
frame:SetWidth(340)
|
||||
frame:SetHeight(25)
|
||||
frame:SetSize(340, 25)
|
||||
frame:SetPoint("TOPLEFT", 5, i * -25 + 20)
|
||||
|
||||
frame:SetBackdrop(pfUI.backdrop_hover)
|
||||
|
||||
+6
-10
@@ -1,5 +1,4 @@
|
||||
pfUI:RegisterModule("afkcam", function ()
|
||||
local MARKED_AFK_CAPTURE = SanitizePattern(MARKED_AFK_MESSAGE)
|
||||
local social_chats = {
|
||||
"CHAT_MSG_SAY",
|
||||
"CHAT_MSG_WHISPER",
|
||||
@@ -49,8 +48,7 @@ pfUI:RegisterModule("afkcam", function ()
|
||||
local chat = CreateFrame("ScrollingMessageFrame", "pfAFKCamChat", overlay)
|
||||
chat:EnableMouse(false)
|
||||
chat:EnableMouseWheel(true)
|
||||
chat:SetHeight(150)
|
||||
chat:SetWidth(500)
|
||||
chat:SetSize(500, 150)
|
||||
chat:SetPoint("BOTTOMLEFT",overlay,"BOTTOMLEFT", 10, 10)
|
||||
chat:SetTimeVisible(1800.0)
|
||||
chat:SetMaxLines(500)
|
||||
@@ -151,9 +149,7 @@ pfUI:RegisterModule("afkcam", function ()
|
||||
delay:SetScript("OnUpdate", function()
|
||||
if ( this.tick or 0) > GetTime() then return else this.tick = GetTime() + 1 end
|
||||
|
||||
local name = UnitName("player")
|
||||
local cast = pfGetCastInfo(name)
|
||||
if not cast then cast = pfGetChannelInfo(name) end
|
||||
local cast = C_Spell.UnitCastingInfo("player") or C_Spell.UnitChannelInfo("player")
|
||||
if not this.delay then this.delay = 0 end
|
||||
|
||||
if cast then
|
||||
@@ -167,10 +163,10 @@ pfUI:RegisterModule("afkcam", function ()
|
||||
end)
|
||||
|
||||
afkcam:SetScript("OnEvent", function()
|
||||
if event == "CHAT_MSG_SYSTEM" then
|
||||
if (arg1 == _G.MARKED_AFK) or strfind(arg1, MARKED_AFK_CAPTURE) then
|
||||
if event == "PLAYER_FLAGS_CHANGED" then
|
||||
if UnitIsAFK('player') then
|
||||
delay:Show()
|
||||
elseif (arg1 == _G.CLEARED_AFK) then
|
||||
elseif delay:IsVisible() then
|
||||
delay:Hide()
|
||||
this:stop()
|
||||
end
|
||||
@@ -182,7 +178,7 @@ pfUI:RegisterModule("afkcam", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
afkcam:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
afkcam:RegisterEvent("PLAYER_FLAGS_CHANGED")
|
||||
afkcam:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
afkcam:RegisterEvent("PLAYER_LEAVING_WORLD") -- reseting cvars on PLAYER_LOGOUT crashes the client ¯\_(ツ)_/¯
|
||||
end)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -19,12 +19,7 @@ pfUI:RegisterModule("autovendor", function ()
|
||||
local startGold = GetMoney()
|
||||
C_MerchantFrame.SellAllJunkItems()
|
||||
|
||||
local reporter = CreateFrame("Frame")
|
||||
reporter.deadline = GetTime() + 0.3
|
||||
reporter:SetScript("OnUpdate", function()
|
||||
if GetTime() < this.deadline then return end
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this:Hide()
|
||||
C_Timer.After(0.3, function()
|
||||
local income = GetMoney() - startGold
|
||||
if income > 0 then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(T["Your vendor trash has been sold and you earned"] .. " " .. CreateGoldString(income))
|
||||
@@ -61,8 +56,7 @@ pfUI:RegisterModule("autovendor", function ()
|
||||
|
||||
-- Setup Autosell button
|
||||
autovendor.button = CreateFrame("Button", "pfMerchantAutoVendorButton", MerchantFrame)
|
||||
autovendor.button:SetWidth(36)
|
||||
autovendor.button:SetHeight(36)
|
||||
autovendor.button:SetSize(36, 36)
|
||||
autovendor.button.icon = autovendor.button:CreateTexture("ARTWORK")
|
||||
autovendor.button.icon:SetTexture("Interface\\Icons\\Spell_Shadow_SacrificialShield")
|
||||
autovendor.button:SetScript("OnEnter", function()
|
||||
|
||||
+33
-54
@@ -8,6 +8,13 @@ pfUI:RegisterModule("bags", function ()
|
||||
|
||||
local scanner = libtipscan:GetScanner("input_search")
|
||||
|
||||
local function BagSortOpts()
|
||||
return {
|
||||
reverse = C.appearance.bags.sortreverse == "1",
|
||||
reversePrio = C.appearance.bags.sortprioreverse == "1",
|
||||
}
|
||||
end
|
||||
|
||||
-- function to detect openable items in inventory
|
||||
local openable = { bag = nil, slot = nil, icon = nil }
|
||||
local function GetNextOpenable()
|
||||
@@ -319,8 +326,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
default_border + x*(frame.button_size+default_border*3),
|
||||
-default_border*2 - y*(frame.button_size+default_border*3) - topspace)
|
||||
|
||||
pfUI.bags[bag].slots[slot].frame:SetHeight(frame.button_size)
|
||||
pfUI.bags[bag].slots[slot].frame:SetWidth(frame.button_size)
|
||||
pfUI.bags[bag].slots[slot].frame:SetSize(frame.button_size, frame.button_size)
|
||||
|
||||
if x >= rowlength - 1 then
|
||||
y = y + 1
|
||||
@@ -337,12 +343,13 @@ pfUI:RegisterModule("bags", function ()
|
||||
local chat = pfUI.chat and ( object == "bank" and pfUI.chat.left or pfUI.chat.right) or nil
|
||||
|
||||
frame:SetScript("OnShow", function()
|
||||
frame.opened = true
|
||||
if C.appearance.bags.hidechat == "1" and chat and chat:IsVisible() then
|
||||
frame.chatWasOpen = true
|
||||
chat:Hide()
|
||||
end
|
||||
if C.appearance.bags.autoSortOnOpen == "1" then
|
||||
libbagsort:Sort({0, 1, 2, 3, 4})
|
||||
libbagsort:Sort({0, 1, 2, 3, 4}, BagSortOpts())
|
||||
end
|
||||
pfUI.bag:CreateBags(object)
|
||||
PlaySound("INTERFACESOUND_BACKPACKOPEN")
|
||||
@@ -355,6 +362,10 @@ pfUI:RegisterModule("bags", function ()
|
||||
end
|
||||
pfUI.bag:CreateBags(object)
|
||||
PlaySound("INTERFACESOUND_BACKPACKCLOSE")
|
||||
if frame.opened then
|
||||
frame.opened = nil
|
||||
pfUI.events:TriggerEvent("bag:closed", object)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -443,7 +454,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
end
|
||||
end
|
||||
|
||||
local _, _, q, _, _, _, itype = GetItemInfo(itemID)
|
||||
local _, _, q, _, _, _, itype = C_Item.GetItemInfo(itemID)
|
||||
|
||||
-- running advanced item color scan
|
||||
if C.appearance.bags.borderonlygear == "0" and texture and quality and quality < 1 then
|
||||
@@ -533,8 +544,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
local width = (frame.button_size/5*4 + default_border*2) * (max-min+1+extra)
|
||||
local height = default_border + (frame.button_size/5*4 + default_border)
|
||||
|
||||
frame.bagslots:SetWidth(width)
|
||||
frame.bagslots:SetHeight(height)
|
||||
frame.bagslots:SetSize(width, height)
|
||||
for slot=min, max do
|
||||
if not frame.bagslots.slots[slot] then
|
||||
frame.bagslots.slots[slot] = {}
|
||||
@@ -594,8 +604,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
|
||||
frame.bagslots.slots[slot].frame:ClearAllPoints()
|
||||
frame.bagslots.slots[slot].frame:SetPoint("TOPLEFT", frame.bagslots, "TOPLEFT", left, top)
|
||||
frame.bagslots.slots[slot].frame:SetHeight(frame.button_size/5*4)
|
||||
frame.bagslots.slots[slot].frame:SetWidth(frame.button_size/5*4)
|
||||
frame.bagslots.slots[slot].frame:SetSize(frame.button_size/5*4, frame.button_size/5*4)
|
||||
|
||||
CreateBackdrop(frame.bagslots.slots[slot].frame, default_border)
|
||||
frame.bagslots.slots[slot].frame:Show()
|
||||
@@ -614,8 +623,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
end
|
||||
frame.bagslots.buy:SetPoint("RIGHT", frame.bagslots, "RIGHT", -default_border, 0)
|
||||
CreateBackdrop(frame.bagslots.buy, default_border)
|
||||
frame.bagslots.buy:SetHeight(frame.button_size/5*4)
|
||||
frame.bagslots.buy:SetWidth(frame.button_size/5*4)
|
||||
frame.bagslots.buy:SetSize(frame.button_size/5*4, frame.button_size/5*4)
|
||||
frame.bagslots.buy:SetText("+")
|
||||
frame.bagslots.buy:SetTextColor(.5,.5,1,1)
|
||||
frame.bagslots.buy:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
@@ -707,8 +715,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.close = CreateFrame("Button", "pfBagClose", frame)
|
||||
frame.close:SetPoint("TOPRIGHT", -default_border*1,-default_border )
|
||||
CreateBackdrop(frame.close, default_border)
|
||||
frame.close:SetHeight(12)
|
||||
frame.close:SetWidth(12)
|
||||
frame.close:SetSize(12, 12)
|
||||
frame.close.texture = frame.close:CreateTexture("pfBagClose")
|
||||
frame.close.texture:SetTexture(pfUI.media["img:close"])
|
||||
frame.close.texture:ClearAllPoints()
|
||||
@@ -733,8 +740,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.bags = CreateFrame("Button", "pfBagSlotShow", frame)
|
||||
frame.bags:SetPoint("TOPRIGHT", frame.close, "TOPLEFT", -default_border*3, 0)
|
||||
CreateBackdrop(frame.bags, default_border)
|
||||
frame.bags:SetHeight(12)
|
||||
frame.bags:SetWidth(12)
|
||||
frame.bags:SetSize(12, 12)
|
||||
frame.bags:SetTextColor(1,1,.25,1)
|
||||
frame.bags:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
frame.bags.texture = frame.bags:CreateTexture("pfBagArrowUp")
|
||||
@@ -761,11 +767,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
end)
|
||||
|
||||
frame.bags:SetScript("OnClick", function()
|
||||
if pfUI.bag.right.bagslots:IsShown() then
|
||||
pfUI.bag.right.bagslots:Hide()
|
||||
else
|
||||
pfUI.bag.right.bagslots:Show()
|
||||
end
|
||||
pfUI.bag.right.bagslots:SetShown(not pfUI.bag.right.bagslots:IsShown())
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -774,8 +776,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.open = CreateFrame("Button", "pfBagSlotOpen", frame)
|
||||
frame.open:SetPoint("TOPRIGHT", frame.bags, "TOPLEFT", -default_border*3, 0)
|
||||
CreateBackdrop(frame.open, default_border)
|
||||
frame.open:SetHeight(12)
|
||||
frame.open:SetWidth(12)
|
||||
frame.open:SetSize(12, 12)
|
||||
frame.open:SetTextColor(1,1,.25,1)
|
||||
frame.open:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
frame.open.texture = frame.open:CreateTexture("pfBagOpenContainer")
|
||||
@@ -833,8 +834,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.disenchant = CreateFrame("Button", "pfBagSlotDisenchant", frame)
|
||||
frame.disenchant:SetPoint("TOPRIGHT", frame.open, "TOPLEFT", -default_border*3, 0)
|
||||
CreateBackdrop(frame.disenchant, default_border)
|
||||
frame.disenchant:SetHeight(12)
|
||||
frame.disenchant:SetWidth(12)
|
||||
frame.disenchant:SetSize(12, 12)
|
||||
frame.disenchant:SetTextColor(1,1,.25,1)
|
||||
frame.disenchant:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
frame.disenchant.texture = frame.disenchant:CreateTexture("pfBagDisenchant")
|
||||
@@ -876,8 +876,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.picklock = CreateFrame("Button", "pfBagSlotPicklock", frame)
|
||||
frame.picklock:SetPoint("TOPRIGHT", frame.disenchant, "TOPLEFT", -default_border*3, 0)
|
||||
CreateBackdrop(frame.picklock, default_border)
|
||||
frame.picklock:SetHeight(12)
|
||||
frame.picklock:SetWidth(12)
|
||||
frame.picklock:SetSize(12, 12)
|
||||
frame.picklock:SetTextColor(1,1,.25,1)
|
||||
frame.picklock:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
frame.picklock.texture = frame.picklock:CreateTexture("pfBagPicklock")
|
||||
@@ -919,8 +918,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.keys = CreateFrame("Button", "pfBagSlotShow", frame)
|
||||
frame.keys:SetPoint("TOPRIGHT", frame.picklock, "TOPLEFT", -default_border*3, 0)
|
||||
CreateBackdrop(frame.keys, default_border)
|
||||
frame.keys:SetHeight(12)
|
||||
frame.keys:SetWidth(12)
|
||||
frame.keys:SetSize(12, 12)
|
||||
frame.keys:SetTextColor(1,1,.25,1)
|
||||
frame.keys:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
frame.keys.texture = frame.keys:CreateTexture("pfBagArrowUp")
|
||||
@@ -962,8 +960,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.sort = CreateFrame("Button", "pfBagSort", frame)
|
||||
frame.sort:SetPoint("TOPRIGHT", frame.keys, "TOPLEFT", -default_border*3, 0)
|
||||
CreateBackdrop(frame.sort, default_border)
|
||||
frame.sort:SetHeight(12)
|
||||
frame.sort:SetWidth(12)
|
||||
frame.sort:SetSize(12, 12)
|
||||
frame.sort.texture = frame.sort:CreateTexture("pfBagSortIcon")
|
||||
frame.sort.texture:SetTexture(pfUI.media["img:sort"])
|
||||
frame.sort.texture:ClearAllPoints()
|
||||
@@ -975,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)
|
||||
|
||||
@@ -988,7 +985,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
end)
|
||||
|
||||
frame.sort:SetScript("OnClick", function()
|
||||
libbagsort:Sort({0, 1, 2, 3, 4})
|
||||
libbagsort:Sort({0, 1, 2, 3, 4}, BagSortOpts())
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -996,8 +993,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
if not frame.gold and (C.appearance.bags.movable == "1" or not pfUI.panel) then
|
||||
frame.gold = CreateFrame("Frame", "pfBagGoldString", frame)
|
||||
frame.gold:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 1)
|
||||
frame.gold:SetWidth(200)
|
||||
frame.gold:SetHeight(18)
|
||||
frame.gold:SetSize(200, 18)
|
||||
frame.gold:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
frame.gold:RegisterEvent("PLAYER_MONEY")
|
||||
frame.gold:SetScript("OnEvent", function()
|
||||
@@ -1089,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()
|
||||
@@ -1133,8 +1114,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.close = CreateFrame("Button", "pfBagClose", frame)
|
||||
frame.close:SetPoint("TOPRIGHT", -default_border*1,-default_border )
|
||||
CreateBackdrop(frame.close, default_border)
|
||||
frame.close:SetHeight(12)
|
||||
frame.close:SetWidth(12)
|
||||
frame.close:SetSize(12, 12)
|
||||
frame.close.texture = frame.close:CreateTexture("pfBagClose")
|
||||
frame.close.texture:SetTexture(pfUI.media["img:close"])
|
||||
frame.close.texture:ClearAllPoints()
|
||||
@@ -1159,8 +1139,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
frame.bags = CreateFrame("Button", "pfBagSlotShow", frame)
|
||||
frame.bags:SetPoint("TOPRIGHT", frame.close, "TOPLEFT", -default_border*3, 0)
|
||||
CreateBackdrop(frame.bags, default_border)
|
||||
frame.bags:SetHeight(12)
|
||||
frame.bags:SetWidth(12)
|
||||
frame.bags:SetSize(12, 12)
|
||||
frame.bags:SetTextColor(1,1,.25,1)
|
||||
frame.bags:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
frame.bags.texture = frame.bags:CreateTexture("pfBagArrowUp")
|
||||
@@ -1213,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)
|
||||
|
||||
@@ -1226,7 +1205,7 @@ pfUI:RegisterModule("bags", function ()
|
||||
end)
|
||||
|
||||
frame.sort:SetScript("OnClick", function()
|
||||
libbagsort:Sort({-1, 5, 6, 7, 8, 9, 10})
|
||||
libbagsort:Sort({-1, 5, 6, 7, 8, 9, 10}, BagSortOpts())
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
+3
-5
@@ -2,14 +2,12 @@ pfUI:RegisterModule("bgscore", function ()
|
||||
local bgframe = WorldStateAlwaysUpFrame
|
||||
if not bgframe then
|
||||
bgframe = CreateFrame("Frame", "WorldStateAlwaysUpFrame", UIParent)
|
||||
bgframe:SetWidth(200)
|
||||
bgframe:SetHeight(25)
|
||||
bgframe:SetSize(200, 25)
|
||||
bgframe:SetPoint("TOP", UIParent, "TOP", 0, -100)
|
||||
end
|
||||
|
||||
local mover = CreateFrame("Frame", "pfUIBGScoreMover", UIParent)
|
||||
mover:SetWidth(220)
|
||||
mover:SetHeight(30)
|
||||
mover:SetSize(220, 30)
|
||||
mover:SetPoint("TOP", UIParent, "TOP", 0, -100)
|
||||
mover:SetFrameStrata("DIALOG")
|
||||
mover:SetMovable(true)
|
||||
@@ -34,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
|
||||
|
||||
+5
-12
@@ -12,15 +12,11 @@ pfUI:RegisterModule("bubbles", function ()
|
||||
pfUI.bubbles:RegisterEvent("CHAT_MSG_MONSTER_PARTY")
|
||||
|
||||
pfUI.bubbles:SetScript("OnEvent", function()
|
||||
pfUI.bubbles:SetScript("OnUpdate", pfUI.bubbles.ScanBubbles)
|
||||
-- Bubble frames are attached to WorldFrame after the chat event fires,
|
||||
-- so wait one tick before scanning.
|
||||
RunNextFrame(function() pfUI.bubbles:ScanBubbles() end)
|
||||
end)
|
||||
|
||||
function pfUI.bubbles:IsBubble(f)
|
||||
if f:GetName() then return end
|
||||
if not f:GetRegions() then return end
|
||||
return f:GetRegions().GetTexture and f:GetRegions():GetTexture() == "Interface\\Tooltips\\ChatBubble-Background"
|
||||
end
|
||||
|
||||
function pfUI.bubbles:ProcessBubble(f)
|
||||
f.text:Hide()
|
||||
f.text:SetFont(pfUI.font_default, tonumber(C.global.font_size) * UIParent:GetScale(), "OUTLINE")
|
||||
@@ -32,9 +28,8 @@ pfUI:RegisterModule("bubbles", function ()
|
||||
end
|
||||
|
||||
function pfUI.bubbles:ScanBubbles()
|
||||
local childs = { WorldFrame:GetChildren() }
|
||||
for _, f in pairs(childs) do
|
||||
if not f.frame and pfUI.bubbles:IsBubble(f) then
|
||||
for _, f in ipairs(C_ChatBubbles.GetAllChatBubbles()) do
|
||||
if not f.frame then
|
||||
local textures = {f:GetRegions()}
|
||||
for _, object in pairs(textures) do
|
||||
if object:GetObjectType() == "Texture" then
|
||||
@@ -66,7 +61,5 @@ pfUI:RegisterModule("bubbles", function ()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
pfUI.bubbles:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end)
|
||||
|
||||
+26
-27
@@ -60,6 +60,7 @@ pfUI:RegisterModule("buff", function ()
|
||||
buff.mode = buff.btype
|
||||
buff.expirationTime = aura.expirationTime
|
||||
buff.stackCount = aura.applications
|
||||
buff.spellId = aura.spellId
|
||||
buff.texture:SetTexture(aura.icon)
|
||||
|
||||
if buff.btype == "HARMFUL" then
|
||||
@@ -137,18 +138,11 @@ pfUI:RegisterModule("buff", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
buff:SetScript("OnLeave", function()
|
||||
GameTooltip:Hide()
|
||||
end)
|
||||
buff:SetScript("OnLeave", GameTooltip_Hide)
|
||||
|
||||
buff:SetScript("OnClick", function()
|
||||
if CancelItemTempEnchantment and this.mode and this.mode == "MAINHAND" then
|
||||
CancelItemTempEnchantment(1)
|
||||
elseif CancelItemTempEnchantment and this.mode and this.mode == "OFFHAND" then
|
||||
CancelItemTempEnchantment(2)
|
||||
else
|
||||
local bid = GetPlayerBuff(PLAYER_BUFF_START_ID + this.id, this.btype)
|
||||
if bid >= 0 then CancelPlayerBuff(bid) end
|
||||
if this.spellId then
|
||||
C_Spell.CancelSpellByID(this.spellId)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -159,7 +153,7 @@ pfUI:RegisterModule("buff", function ()
|
||||
|
||||
pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent)
|
||||
pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED")
|
||||
pfUI.buff:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||||
pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
||||
pfUI.buff:RegisterEvent("UNIT_MODEL_CHANGED")
|
||||
pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF")
|
||||
pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF")
|
||||
@@ -277,28 +271,27 @@ pfUI:RegisterModule("buff", function ()
|
||||
-- config loading
|
||||
function pfUI.buff:UpdateConfigBuffButton(buff)
|
||||
local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize
|
||||
local buffSize, buffSpacing = tonumber(C.buffs.size), tonumber(C.buffs.spacing)
|
||||
local rowcount, relFrame, offsetX, offsetY
|
||||
if buff.btype == "HELPFUL" then
|
||||
if buff.weapon == 1 and C.buffs.separateweapons == "1" then
|
||||
rowcount = floor((buff.gid-1) / tonumber(C.buffs.wepbuffrowsize))
|
||||
relFrame = pfUI.buff.wepbuffs
|
||||
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.wepbuffrowsize))*(tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
|
||||
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
|
||||
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.wepbuffrowsize))*(buffSize+2*buffSpacing)
|
||||
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
||||
else
|
||||
rowcount = floor((buff.gid-1) / tonumber(C.buffs.buffrowsize))
|
||||
relFrame = pfUI.buff.buffs
|
||||
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.buffrowsize))*(tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
|
||||
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
|
||||
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.buffrowsize))*(buffSize+2*buffSpacing)
|
||||
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
||||
end
|
||||
else
|
||||
rowcount = floor((buff.gid-1) / tonumber(C.buffs.debuffrowsize))
|
||||
relFrame = pfUI.buff.debuffs
|
||||
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.debuffrowsize))*(tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
|
||||
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
|
||||
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.debuffrowsize))*(buffSize+2*buffSpacing)
|
||||
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
||||
end
|
||||
|
||||
buff:SetWidth(tonumber(C.buffs.size))
|
||||
buff:SetHeight(tonumber(C.buffs.size))
|
||||
buff:SetSize(buffSize, buffSize)
|
||||
buff:ClearAllPoints()
|
||||
buff:SetPoint("TOPRIGHT", relFrame, "TOPRIGHT",offsetX, offsetY)
|
||||
|
||||
@@ -318,20 +311,26 @@ pfUI:RegisterModule("buff", function ()
|
||||
function pfUI.buff:UpdateConfig()
|
||||
local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize
|
||||
|
||||
pfUI.buff.buffs:SetWidth(tonumber(C.buffs.buffrowsize) * (tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
|
||||
pfUI.buff.buffs:SetHeight(ceil(32/tonumber(C.buffs.buffrowsize)) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
|
||||
pfUI.buff.buffs:SetPoint("TOPRIGHT", pfUI.minimap or UIParent, "TOPLEFT", -4*tonumber(C.buffs.spacing), 0)
|
||||
local spacing = tonumber(C.buffs.spacing)
|
||||
local cell = tonumber(C.buffs.size) + 2 * spacing
|
||||
local rowextra = C.buffs.textinside == "1" and 0 or (fontsize * 1.5)
|
||||
|
||||
local function SizeBuffFrame(frame, rowsize, count)
|
||||
rowsize = tonumber(rowsize)
|
||||
frame:SetSize(rowsize * cell, ceil(count / rowsize) * (rowextra + cell))
|
||||
end
|
||||
|
||||
SizeBuffFrame(pfUI.buff.buffs, C.buffs.buffrowsize, 32)
|
||||
pfUI.buff.buffs:SetPoint("TOPRIGHT", pfUI.minimap or UIParent, "TOPLEFT", -4 * spacing, 0)
|
||||
UpdateMovable(pfUI.buff.buffs)
|
||||
|
||||
pfUI.buff.debuffs:SetWidth(tonumber(C.buffs.debuffrowsize) * (tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
|
||||
pfUI.buff.debuffs:SetHeight(ceil(16/tonumber(C.buffs.debuffrowsize)) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
|
||||
SizeBuffFrame(pfUI.buff.debuffs, C.buffs.debuffrowsize, 16)
|
||||
pfUI.buff.debuffs:SetPoint("TOPRIGHT", pfUI.buff.buffs, "BOTTOMRIGHT", 0, 0)
|
||||
UpdateMovable(pfUI.buff.debuffs)
|
||||
|
||||
if C.buffs.separateweapons == "1" then
|
||||
pfUI.buff.wepbuffs:ClearAllPoints()
|
||||
pfUI.buff.wepbuffs:SetWidth(tonumber(C.buffs.wepbuffrowsize) * (tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
|
||||
pfUI.buff.wepbuffs:SetHeight(ceil(2/tonumber(C.buffs.wepbuffrowsize)) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
|
||||
SizeBuffFrame(pfUI.buff.wepbuffs, C.buffs.wepbuffrowsize, 2)
|
||||
pfUI.buff.wepbuffs:SetPoint("TOPRIGHT", pfUI.buff.debuffs, "BOTTOMRIGHT", 0, 0)
|
||||
pfUI.buff.wepbuffs:Show()
|
||||
UpdateMovable(pfUI.buff.wepbuffs)
|
||||
|
||||
+31
-46
@@ -74,18 +74,11 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
end
|
||||
|
||||
local function GetBuffData(unit, id, type, selfdebuff)
|
||||
if unit == "player" then
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex("player", id, type)
|
||||
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
|
||||
elseif libdebuff and selfdebuff then
|
||||
local name, _, texture, stacks, _, _, timeleft = libdebuff:UnitOwnDebuff(unit, id)
|
||||
return timeleft, texture, name, stacks
|
||||
elseif libdebuff then
|
||||
local name, _, texture, stacks, _, _, timeleft = libdebuff:UnitDebuff(unit, id)
|
||||
return timeleft, texture, name, stacks
|
||||
end
|
||||
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
|
||||
end
|
||||
|
||||
local function StatusBarOnClick()
|
||||
@@ -97,17 +90,19 @@ 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
|
||||
local bid = GetPlayerBuff(PLAYER_BUFF_START_ID + this.id, this.type)
|
||||
if bid >= 0 then CancelPlayerBuff(bid) end
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex("player", this.id, this.type)
|
||||
if aura and aura.spellId then C_Spell.CancelSpellByID(aura.spellId) end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -117,15 +112,17 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
if this.unit == "player" then
|
||||
GameTooltip:SetUnitAura("player", this.id, this.type)
|
||||
elseif this.type == "HARMFUL" then
|
||||
-- For "only own debuffs" mode: find the REAL slot by matching spell name AND caster
|
||||
-- selfdebuff filters the displayed list to player-cast harmful auras, but
|
||||
-- SetUnitAura's index has to be into the engine's full HARMFUL list. Look
|
||||
-- up the displayed aura via the PLAYER filter, then scan engine slots for
|
||||
-- one whose name + sourceGUID match.
|
||||
local config = this.parent and this.parent.config
|
||||
if config and config.selfdebuff == "1" and libdebuff then
|
||||
local ownDebuffName = libdebuff:UnitOwnDebuff(this.unit, this.id)
|
||||
if ownDebuffName then
|
||||
-- Search through all game slots to find OUR debuff with matching name
|
||||
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 gameName, _, _, _, _, _, _, gameCaster = libdebuff:UnitDebuff(this.unit, gameSlot)
|
||||
if gameName == ownDebuffName and gameCaster == "player" then
|
||||
local check = C_UnitAuras.GetDebuffDataByIndex(this.unit, gameSlot)
|
||||
if check and check.name == ownAura.name and check.sourceGUID == ownAura.sourceGUID then
|
||||
GameTooltip:SetUnitAura(this.unit, gameSlot, "HARMFUL")
|
||||
break
|
||||
end
|
||||
@@ -167,8 +164,7 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
local color = parent.color
|
||||
local bordercolor = parent.bordercolor
|
||||
local textcolor = parent.textcolor
|
||||
local width = parent:GetWidth()
|
||||
local height = parent:GetHeight()
|
||||
local width, height = parent:GetSize()
|
||||
local framename = "pf" .. parent.unit .. ( parent.type == "HARMFUL" and "Debuff" or "Buff" ) .. "Bar" .. bar
|
||||
|
||||
local font = parent.config.use_unitfonts == "1" and pfUI.font_unit or pfUI.font_default
|
||||
@@ -177,8 +173,7 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
frame:EnableMouse(1)
|
||||
frame:Hide()
|
||||
frame:SetPoint("BOTTOM", 0, (bar-1)*(height+2*border+1))
|
||||
frame:SetWidth(width)
|
||||
frame:SetHeight(height)
|
||||
frame:SetSize(width, height)
|
||||
|
||||
frame.bar = CreateFrame("StatusBar", "pfBuffBar" .. bar, frame)
|
||||
frame.bar:SetPoint("TOPLEFT", frame, "TOPLEFT", height+1, 0)
|
||||
@@ -207,8 +202,7 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
frame.time:SetJustifyH("RIGHT")
|
||||
|
||||
frame.icon = frame:CreateTexture(nil, "OVERLAY")
|
||||
frame.icon:SetWidth(height)
|
||||
frame.icon:SetHeight(height)
|
||||
frame.icon:SetSize(height, height)
|
||||
frame.icon:SetPoint("LEFT", frame, "LEFT", 0, 0)
|
||||
frame.icon:SetTexCoord(.07,.93,.07,.93)
|
||||
|
||||
@@ -230,7 +224,7 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
|
||||
CreateBackdrop(frame)
|
||||
CreateBackdropShadow(frame)
|
||||
if bordercolor.r ~= "0" and bordercolor.g ~= "0" and bordercolor.b ~= "0" and bordercolor.a ~= "0" then
|
||||
if bordercolor.r ~= 0 and bordercolor.g ~= 0 and bordercolor.b ~= 0 and bordercolor.a ~= 0 then
|
||||
frame.backdrop:SetBackdropBorderColor(bordercolor.r,bordercolor.g,bordercolor.b,1)
|
||||
end
|
||||
|
||||
@@ -427,18 +421,15 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
if pfUI.uf.player and C.buffbar.pbuff.enable == "1" then
|
||||
pfUI.uf.player.buffbar = CreateBuffBarFrame("Player", "HELPFUL")
|
||||
local config = C.buffbar.pbuff
|
||||
local r, g, b, a = strsplit(",", config.color)
|
||||
local br, bg, bb, ba = strsplit(",", config.bordercolor)
|
||||
local tr, tg, tb, ta = strsplit(",", config.textcolor)
|
||||
|
||||
pfUI.uf.player.buffbar:SetWidth(config.width == "-1" and pfUI.uf.player:GetWidth() or config.width)
|
||||
pfUI.uf.player.buffbar:SetHeight(config.height)
|
||||
pfUI.uf.player.buffbar.threshold = tonumber(config.threshold)
|
||||
pfUI.uf.player.buffbar.config = config
|
||||
pfUI.uf.player.buffbar.buffcmp = config.sort == "asc" and asc or desc
|
||||
pfUI.uf.player.buffbar.color = { r = r, g = g, b = b, a = a }
|
||||
pfUI.uf.player.buffbar.bordercolor = { r = br, g = bg, b = bb, a = ba }
|
||||
pfUI.uf.player.buffbar.textcolor = { r = tr, g = tg, b = tb, a = ta }
|
||||
pfUI.uf.player.buffbar.color = GetStringColorObject(config.color)
|
||||
pfUI.uf.player.buffbar.bordercolor = GetStringColorObject(config.bordercolor)
|
||||
pfUI.uf.player.buffbar.textcolor = GetStringColorObject(config.textcolor)
|
||||
pfUI.uf.player.buffbar.anchors = {
|
||||
pfUI.uf.player,
|
||||
pfUI.uf.player and pfUI.uf.player.debuffs,
|
||||
@@ -453,9 +444,6 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
-- create player debuffbars
|
||||
if pfUI.uf.player and C.buffbar.pdebuff.enable == "1" then
|
||||
local config = C.buffbar.pdebuff
|
||||
local r, g, b, a = strsplit(",", config.color)
|
||||
local br, bg, bb, ba = strsplit(",", config.bordercolor)
|
||||
local tr, tg, tb, ta = strsplit(",", config.textcolor)
|
||||
|
||||
pfUI.uf.player.debuffbar = CreateBuffBarFrame("Player", "HARMFUL")
|
||||
pfUI.uf.player.debuffbar:SetWidth(config.width == "-1" and pfUI.uf.player:GetWidth() or config.width)
|
||||
@@ -463,9 +451,9 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
pfUI.uf.player.debuffbar.threshold = tonumber(config.threshold)
|
||||
pfUI.uf.player.debuffbar.config = config
|
||||
pfUI.uf.player.debuffbar.buffcmp = config.sort == "asc" and asc or desc
|
||||
pfUI.uf.player.debuffbar.color = { r = r, g = g, b = b, a = a }
|
||||
pfUI.uf.player.debuffbar.bordercolor = { r = br, g = bg, b = bb, a = ba }
|
||||
pfUI.uf.player.debuffbar.textcolor = { r = tr, g = tg, b = tb, a = ta }
|
||||
pfUI.uf.player.debuffbar.color = GetStringColorObject(config.color)
|
||||
pfUI.uf.player.debuffbar.bordercolor = GetStringColorObject(config.bordercolor)
|
||||
pfUI.uf.player.debuffbar.textcolor = GetStringColorObject(config.textcolor)
|
||||
pfUI.uf.player.debuffbar.anchors = {
|
||||
pfUI.uf.player,
|
||||
pfUI.uf.player and pfUI.uf.player.buffbar and pfUI.uf.player.buffbar.bars,
|
||||
@@ -481,18 +469,15 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
-- create target debuffbars
|
||||
if pfUI.uf.target and C.buffbar.tdebuff.enable == "1" then
|
||||
local config = C.buffbar.tdebuff
|
||||
local r, g, b, a = strsplit(",", config.color)
|
||||
local br, bg, bb, ba = strsplit(",", config.bordercolor)
|
||||
local tr, tg, tb, ta = strsplit(",", config.textcolor)
|
||||
|
||||
pfUI.uf.target.debuffbar = CreateBuffBarFrame("Target", "HARMFUL")
|
||||
pfUI.uf.target.debuffbar:SetWidth(config.width == "-1" and pfUI.uf.target:GetWidth() or config.width)
|
||||
pfUI.uf.target.debuffbar:SetHeight(config.height)
|
||||
pfUI.uf.target.debuffbar.config = config
|
||||
pfUI.uf.target.debuffbar.buffcmp = config.sort == "asc" and asc or desc
|
||||
pfUI.uf.target.debuffbar.color = { r = r, g = g, b = b, a = a }
|
||||
pfUI.uf.target.debuffbar.bordercolor = { r = br, g = bg, b = bb, a = ba }
|
||||
pfUI.uf.target.debuffbar.textcolor = { r = tr, g = tg, b = tb, a = ta }
|
||||
pfUI.uf.target.debuffbar.color = GetStringColorObject(config.color)
|
||||
pfUI.uf.target.debuffbar.bordercolor = GetStringColorObject(config.bordercolor)
|
||||
pfUI.uf.target.debuffbar.textcolor = GetStringColorObject(config.textcolor)
|
||||
pfUI.uf.target.debuffbar.threshold = tonumber(config.threshold)
|
||||
pfUI.uf.target.debuffbar.anchors = {
|
||||
pfUI.uf.target,
|
||||
|
||||
+328
-205
@@ -16,6 +16,165 @@ pfUI:RegisterModule("castbar", function ()
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear cast state on the bar and start the fade-out. On a normal end the bar
|
||||
-- is left at its current fill (a completed cast is already ~full; nothing
|
||||
-- snaps to full, which used to flash for a frame when the next cast stamped
|
||||
-- the bar). When `failed` is set for a cast that was actually in progress, the
|
||||
-- bar flashes full red before fading — the cancelled-cast indicator.
|
||||
local function ClearBar(cb, failed)
|
||||
local wasActive = cb.endTime ~= nil
|
||||
cb.startTime, cb.endTime, cb.isChannel = nil, nil, nil
|
||||
cb.activeName, cb.spellID = nil, nil
|
||||
cb.isTradeskill = nil
|
||||
cb.tradeskillTotal, cb.tradeskillCompleted, cb.tradeskillSpellID = nil, nil, nil
|
||||
cb.tradeskillSingleMs, cb.currentCraftStart = nil, nil
|
||||
cb.lastMax = nil
|
||||
cb.delay = 0
|
||||
if failed and wasActive then
|
||||
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar.failcolor))
|
||||
cb.bar:SetMinMaxValues(0, 1)
|
||||
cb.bar:SetValue(1)
|
||||
end
|
||||
if cb.bar.spark then cb.bar.spark:Hide() end
|
||||
cb.fadeout = 1
|
||||
end
|
||||
|
||||
-- "Spellname (N)" label for an active tradeskill merge — N is the number
|
||||
-- of crafts remaining. Falls back to the bare spell name when ≤1 left.
|
||||
local function UpdateTradeskillLabel(cb)
|
||||
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))
|
||||
else
|
||||
cb.bar.left:SetText(cb.activeName)
|
||||
end
|
||||
end
|
||||
|
||||
-- Stretch the bar to span all queued crafts (Quartz-style merge): the bar
|
||||
-- fills continuously across the chain instead of resetting per craft.
|
||||
-- Also arms the per-craft spark — a thin marker that crosses the bar once
|
||||
-- per craft (so it moves N× faster than the main fill on an N-stack).
|
||||
local function EnterTradeskillMerge(cb, startMs, endMs, count)
|
||||
cb.tradeskillTotal = count
|
||||
cb.tradeskillCompleted = 0
|
||||
cb.tradeskillSpellID = cb.spellID
|
||||
local single = endMs - startMs
|
||||
cb.tradeskillSingleMs = single
|
||||
cb.currentCraftStart = startMs
|
||||
local mergedEnd = startMs + single * count
|
||||
cb.endTime = mergedEnd
|
||||
local duration = (mergedEnd - startMs) / 1000
|
||||
cb.bar:SetMinMaxValues(0, duration)
|
||||
cb.lastMax = duration
|
||||
if cb.bar.spark then cb.bar.spark:Show() end
|
||||
UpdateTradeskillLabel(cb)
|
||||
end
|
||||
|
||||
-- Mark the start of craft 2..N within an active merge (called from the
|
||||
-- UNIT_SPELLCAST_START handler). Resets the per-craft spark to the left
|
||||
-- edge of the bar.
|
||||
local function StartTradeskillCraft(cb)
|
||||
cb.currentCraftStart = GetTime() * 1000
|
||||
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
|
||||
cb.endTime = cb.currentCraftStart + cb.tradeskillSingleMs * remaining
|
||||
local duration = (cb.endTime - cb.startTime) / 1000
|
||||
cb.bar:SetMinMaxValues(0, duration)
|
||||
cb.lastMax = duration
|
||||
UpdateTradeskillLabel(cb)
|
||||
end
|
||||
|
||||
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
|
||||
-- then animates the fill from this state without touching C_Spell.
|
||||
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank)
|
||||
cb.startTime = startMs
|
||||
cb.endTime = endMs
|
||||
cb.isChannel = isChannel
|
||||
cb.spellID = spellID
|
||||
cb.activeName = name
|
||||
cb.isTradeskill = isTradeskill
|
||||
cb.delay = (delayMs or 0) / 1000
|
||||
cb:SetAlpha(1)
|
||||
cb.fadeout = nil
|
||||
|
||||
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
|
||||
|
||||
-- Rank: prefer the value the UNIT_SPELLCAST_* event delivered (arg5, passed
|
||||
-- through by RefreshBar). Only the retarget re-poll has no event in hand, so
|
||||
-- it falls back to a lookup.
|
||||
if not rank and spellID then
|
||||
rank = C_Spell.GetSpellSubtext(spellID) or ""
|
||||
end
|
||||
rank = rank or ""
|
||||
local spellname = (cb.showname and name) and (name .. " ") or ""
|
||||
local rankstr = (cb.showrank and rank ~= "") and string.format("|cffaaffcc[%s]|r", rank) or ""
|
||||
cb.bar.left:SetText(spellname .. rankstr)
|
||||
|
||||
if tex and cb.showicon then
|
||||
local size = cb:GetHeight()
|
||||
cb.icon:Show()
|
||||
cb.icon:SetSize(size, size)
|
||||
cb.icon.texture:SetTexture(tex)
|
||||
cb.bar:SetPoint("TOPLEFT", cb.icon, "TOPRIGHT", cb.spacing, 0)
|
||||
else
|
||||
cb.bar:SetPoint("TOPLEFT", cb, 0, 0)
|
||||
cb.icon:Hide()
|
||||
end
|
||||
|
||||
local duration = (endMs - startMs) / 1000
|
||||
if cb.showlag then
|
||||
local _, _, lag = GetNetStats()
|
||||
cb.bar.lag:SetWidth(math.min(cb:GetWidth(), cb:GetWidth() / duration * (lag/1000)))
|
||||
cb.bar.lag:Show()
|
||||
else
|
||||
cb.bar.lag:Hide()
|
||||
end
|
||||
|
||||
cb.bar:SetMinMaxValues(0, duration)
|
||||
cb.lastMax = duration
|
||||
|
||||
-- Prime the fill on this frame. StampBar otherwise leaves the previous
|
||||
-- value in place (ClearBar, run on the prior cast's STOP, leaves it full),
|
||||
-- so the bar would flash full for the frame between here and the next
|
||||
-- OnUpdate tick. Reset the throttle too so the timer text updates promptly.
|
||||
local nowSec = GetTime()
|
||||
local cur = isChannel and (endMs / 1000 - nowSec) or (nowSec - startMs / 1000)
|
||||
if cur < 0 then cur = 0 elseif cur > duration then cur = duration end
|
||||
cb.bar:SetValue(cur)
|
||||
cb.tick = 0
|
||||
end
|
||||
|
||||
-- One-shot poll: read C_Spell for the bar's unit, stamp or clear. Called
|
||||
-- from event handlers (cast start, target/focus change), never per-frame.
|
||||
local function RefreshBar(cb, rank)
|
||||
local query = cb.unitstr ~= "" and cb.unitstr or cb.unitname
|
||||
if not query or (cb.unitstr ~= "" and not UnitExists(cb.unitstr)) then
|
||||
ClearBar(cb)
|
||||
return
|
||||
end
|
||||
local name, _, tex, startMs, endMs, isTradeskill, _, _, spellID, _, delayMs = C_Spell.UnitCastingInfo(query)
|
||||
local isChan
|
||||
if not name then
|
||||
name, _, tex, startMs, endMs, _, _, spellID = C_Spell.UnitChannelInfo(query)
|
||||
isChan = true
|
||||
end
|
||||
-- Synthetic fallback for abilities the engine treats as instant-cast but
|
||||
-- that have a meaningful wait window (e.g. Turtle WoW Steady Shot on the
|
||||
-- ranged-swing queue). Per-unit table populated by module-side hooks.
|
||||
if not name and pfUI.synthetic_casts and pfUI.synthetic_casts[query] then
|
||||
local s = pfUI.synthetic_casts[query]
|
||||
if s.endMs > GetTime() * 1000 then
|
||||
name, tex, startMs, endMs, spellID = s.name, s.icon, s.startMs, s.endMs, s.spellID
|
||||
isChan = nil
|
||||
end
|
||||
end
|
||||
if name and startMs and endMs then
|
||||
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill, rank)
|
||||
else
|
||||
ClearBar(cb)
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateCastbar(name, parent, unitstr, unitname)
|
||||
local cb = CreateFrame("Frame", name, parent or UIParent)
|
||||
|
||||
@@ -29,8 +188,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
-- icon
|
||||
cb.icon = CreateFrame("Frame", nil, cb)
|
||||
cb.icon:SetPoint("TOPLEFT", 0, 0)
|
||||
cb.icon:SetHeight(16)
|
||||
cb.icon:SetWidth(16)
|
||||
cb.icon:SetSize(16, 16)
|
||||
|
||||
cb.icon.texture = cb.icon:CreateTexture(nil, "OVERLAY")
|
||||
cb.icon.texture:SetAllPoints()
|
||||
@@ -58,7 +216,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
cb.bar.left:SetFontObject(GameFontWhite)
|
||||
cb.bar.left:SetTextColor(1,1,1,1)
|
||||
cb.bar.left:SetFont(font, font_size, "OUTLINE")
|
||||
cb.bar.left:SetJustifyH("left")
|
||||
cb.bar.left:SetJustifyH(C.castbar[unitstr].namealign or "LEFT")
|
||||
|
||||
-- text right
|
||||
cb.bar.right = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||||
@@ -69,245 +227,199 @@ pfUI:RegisterModule("castbar", function ()
|
||||
cb.bar.right:SetFontObject(GameFontWhite)
|
||||
cb.bar.right:SetTextColor(1,1,1,1)
|
||||
cb.bar.right:SetFont(font, font_size, "OUTLINE")
|
||||
cb.bar.right:SetJustifyH("right")
|
||||
cb.bar.right:SetJustifyH(C.castbar[unitstr].timealign or "RIGHT")
|
||||
|
||||
cb.bar.lag = cb.bar:CreateTexture(nil, "OVERLAY")
|
||||
cb.bar.lag:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0)
|
||||
cb.bar.lag:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", 0, 0)
|
||||
cb.bar.lag:SetTexture(1,.2,.2,.2)
|
||||
|
||||
-- OnUpdate script with throttle for performance optimization
|
||||
-- Per-craft progress spark for tradeskill merge — a thin vertical line
|
||||
-- that crosses the bar once per craft in the chain (so on a 5-stack it
|
||||
-- moves 5x faster than the main fill). Position is updated by OnUpdate
|
||||
-- while a merge is active; hidden otherwise.
|
||||
cb.bar.spark = cb.bar:CreateTexture(nil, "OVERLAY")
|
||||
cb.bar.spark:SetTexture(1, 1, 1, 0.8)
|
||||
cb.bar.spark:SetWidth(2)
|
||||
cb.bar.spark:SetPoint("TOP", cb.bar, "TOPLEFT", 0, 0)
|
||||
cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0)
|
||||
cb.bar.spark:Hide()
|
||||
|
||||
-- OnUpdate animates the bar fill and fades it out on completion. All
|
||||
-- state transitions (cast start/stop/interrupt, channel start/stop,
|
||||
-- pushback) come from the event handler below — we never poll C_Spell
|
||||
-- here.
|
||||
cb:SetScript("OnUpdate", function()
|
||||
-- Throttle for performance
|
||||
if (this.tick or 0) > GetTime() then return end
|
||||
this.tick = GetTime() + 0.020 -- ~50 FPS for smooth castbar
|
||||
this.tick = GetTime() + 0.020 -- ~50 FPS
|
||||
|
||||
if this.drag and this.drag:IsShown() then
|
||||
this:SetAlpha(1)
|
||||
return
|
||||
end
|
||||
|
||||
if not UnitExists(this.unitstr) then
|
||||
this:SetAlpha(0)
|
||||
end
|
||||
|
||||
if this.fadeout and this:GetAlpha() > 0 then
|
||||
if this:GetAlpha() == 0 then
|
||||
this.fadeout = nil
|
||||
end
|
||||
|
||||
this:SetAlpha(this:GetAlpha()-0.05)
|
||||
this:SetAlpha(this:GetAlpha() - 0.05)
|
||||
if this:GetAlpha() <= 0 then this.fadeout = nil end
|
||||
return
|
||||
end
|
||||
|
||||
local channel = nil
|
||||
local query = this.unitstr ~= "" and this.unitstr or this.unitname
|
||||
if not query then return end
|
||||
|
||||
-- Check if we have a GUID-based focus (Turtle WoW native GUID)
|
||||
local focusGuid = nil
|
||||
if this.unitstr and string.find(this.unitstr, "^0x") then
|
||||
focusGuid = this.unitstr
|
||||
elseif this.unitstr and this.unitstr == "player" and UnitGUID then
|
||||
focusGuid = UnitGUID("player")
|
||||
elseif this.unitstr and this.unitstr ~= "player" then
|
||||
local guid = UnitGUID(this.unitstr)
|
||||
if guid then focusGuid = guid end
|
||||
end
|
||||
this.focusGuid = focusGuid
|
||||
|
||||
-- Try libdebuff_casts first for GUID-based units (works with Turtle GUID + Nampower events)
|
||||
local cast, nameSubtext, text, texture, startTime, endTime
|
||||
local castBlocked = false
|
||||
if focusGuid and pfUI.libdebuff_casts and pfUI.libdebuff_casts[focusGuid] then
|
||||
local castData = pfUI.libdebuff_casts[focusGuid]
|
||||
if castData.event == "CAST" or castData.event == "FAIL" then
|
||||
castBlocked = true
|
||||
pfUI.libdebuff_casts[focusGuid] = nil
|
||||
elseif (castData.event == "START" or castData.event == "CHANNEL") and castData.endTime and castData.endTime > GetTime() then
|
||||
cast = castData.spellName
|
||||
texture = castData.icon
|
||||
startTime = castData.startTime * 1000
|
||||
endTime = castData.endTime * 1000
|
||||
-- Try to get rank from spell DB via spellID (nameSubtext is not stored in libdebuff_casts)
|
||||
if castData.spellID and GetSpellRecField then
|
||||
nameSubtext = GetSpellRecField(castData.spellID, "rank") or ""
|
||||
else
|
||||
nameSubtext = ""
|
||||
end
|
||||
if castData.event == "CHANNEL" then
|
||||
channel = cast
|
||||
end
|
||||
end
|
||||
if not this.endTime then
|
||||
if this:GetAlpha() ~= 0 then this:SetAlpha(0) end
|
||||
return
|
||||
end
|
||||
|
||||
local useLibcastForPlayer = this.unitstr == "player"
|
||||
|
||||
-- For player: use player name to query libcast.db directly
|
||||
if not cast and useLibcastForPlayer then
|
||||
query = UnitName("player")
|
||||
-- Non-player bars: if the unit disappears (target died / detarget),
|
||||
-- drop the bar immediately.
|
||||
if this.unitstr ~= "" and this.unitstr ~= "player" and not UnitExists(this.unitstr) then
|
||||
ClearBar(this)
|
||||
return
|
||||
end
|
||||
|
||||
-- Fallback: pfGetCastInfo only when no focusGuid (Nampower not available for this unit).
|
||||
-- If we have a focusGuid, libdebuff is authoritative - don't fall back to libcast
|
||||
-- even if no cast is active (prevents false positives e.g. spellbook clicks on CD spells).
|
||||
if not cast and not castBlocked and not focusGuid and pfGetCastInfo then
|
||||
cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill = pfGetCastInfo(query)
|
||||
local now = GetTime()
|
||||
local endSec = this.endTime / 1000
|
||||
if now >= endSec then
|
||||
ClearBar(this)
|
||||
return
|
||||
end
|
||||
|
||||
if not cast and not castBlocked and not focusGuid and pfGetChannelInfo then
|
||||
channel, nameSubtext, text, texture, startTime, endTime, isTradeSkill = pfGetChannelInfo(this.unitstr or this.unitname)
|
||||
cast = channel
|
||||
local startSec = this.startTime / 1000
|
||||
local max = endSec - startSec
|
||||
local cur = this.isChannel and (endSec - now) or (now - startSec)
|
||||
if cur > max then cur = max end
|
||||
if cur < 0 then cur = 0 end
|
||||
|
||||
this.bar:SetValue(cur)
|
||||
|
||||
-- Per-craft spark for a tradeskill merge: position by (now - current
|
||||
-- craft's start) / single-craft duration, clamped to [0, 1]. Snaps back
|
||||
-- to the left edge each time StartTradeskillCraft fires for craft N+1.
|
||||
if this.tradeskillTotal and this.tradeskillSingleMs and this.currentCraftStart then
|
||||
local craftElapsed = now * 1000 - this.currentCraftStart
|
||||
local p = craftElapsed / this.tradeskillSingleMs
|
||||
if p < 0 then p = 0 elseif p > 1 then p = 1 end
|
||||
local x = p * this.bar:GetWidth() - 1
|
||||
this.bar.spark:ClearAllPoints()
|
||||
this.bar.spark:SetPoint("TOP", this.bar, "TOPLEFT", x, 0)
|
||||
this.bar.spark:SetPoint("BOTTOM", this.bar, "BOTTOMLEFT", x, 0)
|
||||
end
|
||||
|
||||
if cast then
|
||||
local duration = endTime - startTime
|
||||
local max = duration / 1000
|
||||
local cur = GetTime() - startTime / 1000
|
||||
|
||||
this:SetAlpha(1)
|
||||
|
||||
local spellname = this.showname and cast and cast .. " " or ""
|
||||
local rank = this.showrank and nameSubtext and nameSubtext ~= "" and string.format("|cffaaffcc[%s]|r", nameSubtext) or ""
|
||||
|
||||
if this.endTime ~= endTime then
|
||||
this.bar:SetStatusBarColor(strsplit(",", C.appearance.castbar[(channel and "channelcolor" or "castbarcolor")]))
|
||||
this.bar.left:SetText(spellname .. rank)
|
||||
this.fadeout = nil
|
||||
this.endTime = endTime
|
||||
|
||||
-- set texture
|
||||
if texture and this.showicon then
|
||||
local size = this:GetHeight()
|
||||
this.icon:Show()
|
||||
this.icon:SetHeight(size)
|
||||
this.icon:SetWidth(size)
|
||||
|
||||
-- Override with item icon from libdebuff_casts or persistent item icon cache
|
||||
local useTexture = texture
|
||||
local useItemName = nil
|
||||
if pfUI.libdebuff_casts or pfUI.libdebuff_item_icons then
|
||||
local castGuid = nil
|
||||
if this.unitstr and UnitExists then
|
||||
local guid = UnitGUID(this.unitstr)
|
||||
castGuid = guid
|
||||
end
|
||||
if castGuid then
|
||||
-- First check active cast data
|
||||
if pfUI.libdebuff_casts and pfUI.libdebuff_casts[castGuid] and pfUI.libdebuff_casts[castGuid].itemID then
|
||||
useTexture = pfUI.libdebuff_casts[castGuid].icon or texture
|
||||
-- Fallback to persistent item icon cache
|
||||
elseif pfUI.libdebuff_item_icons and pfUI.libdebuff_item_icons[castGuid] then
|
||||
useTexture = pfUI.libdebuff_item_icons[castGuid].icon or texture
|
||||
useItemName = pfUI.libdebuff_item_icons[castGuid].name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
this.icon.texture:SetTexture(useTexture)
|
||||
this.bar:SetPoint("TOPLEFT", this.icon, "TOPRIGHT", this.spacing, 0)
|
||||
|
||||
-- Override spell name with item name for item-triggered casts
|
||||
if useItemName and this.showname then
|
||||
this.bar.left:SetText(useItemName .. " " .. rank)
|
||||
end
|
||||
else
|
||||
this.bar:SetPoint("TOPLEFT", this, 0, 0)
|
||||
this.icon:Hide()
|
||||
end
|
||||
|
||||
if this.showlag then
|
||||
local _, _, lag = GetNetStats()
|
||||
local width = this:GetWidth() / (duration/1000) * (lag/1000)
|
||||
this.bar.lag:SetWidth(math.min(this:GetWidth(), width))
|
||||
else
|
||||
this.bar.lag:Hide()
|
||||
end
|
||||
if this.showtimer then
|
||||
if (this.delay or 0) > 0 then
|
||||
local prefix = "|cffffaaaa" .. (this.isChannel and "-" or "+") .. FormatCastbarTime(this.delay) .. " |r "
|
||||
this.bar.right:SetText(prefix .. FormatCastbarTime(cur) .. " / " .. FormatCastbarTime(max))
|
||||
else
|
||||
this.bar.right:SetText(FormatCastbarTime(cur) .. " / " .. FormatCastbarTime(max))
|
||||
end
|
||||
|
||||
local newMax = duration / 1000
|
||||
if this.lastMax ~= newMax then
|
||||
this.bar:SetMinMaxValues(0, newMax)
|
||||
this.lastMax = newMax
|
||||
end
|
||||
|
||||
if channel then
|
||||
cur = max + startTime/1000 - GetTime()
|
||||
end
|
||||
|
||||
cur = cur > max and max or cur
|
||||
cur = cur < 0 and 0 or cur
|
||||
|
||||
this.bar:SetValue(cur)
|
||||
|
||||
if this.showtimer then
|
||||
if this.delay and this.delay > 0 then
|
||||
local delay = "|cffffaaaa" .. (channel and "-" or "+") .. FormatCastbarTime(this.delay) .. " |r "
|
||||
this.bar.right:SetText(delay .. FormatCastbarTime(cur) .. " / " .. FormatCastbarTime(max))
|
||||
else
|
||||
this.bar.right:SetText(FormatCastbarTime(cur) .. " / " .. FormatCastbarTime(max))
|
||||
end
|
||||
end
|
||||
|
||||
this.fadeout = nil
|
||||
else
|
||||
this.bar:SetMinMaxValues(1,100)
|
||||
this.bar:SetValue(100)
|
||||
this.lastMax = nil
|
||||
this.fadeout = 1
|
||||
this.delay = 0
|
||||
this.itemIconApplied = nil
|
||||
end
|
||||
end)
|
||||
|
||||
-- register for spell delay
|
||||
-- Prefer Nampower's SPELL_DELAYED_SELF (gives casterGuid + delayMs directly).
|
||||
-- Fall back to vanilla SPELLCAST_DELAYED if Nampower is not available.
|
||||
local playerarg = nil
|
||||
local function ApplyPushback(delayMs)
|
||||
if not delayMs or delayMs <= 0 or not this.endTime then return end
|
||||
this.delay = (this.delay or 0) + delayMs / 1000
|
||||
this.endTime = this.endTime + delayMs
|
||||
local focusGuid = this.focusGuid
|
||||
if focusGuid and pfUI.libdebuff_casts and pfUI.libdebuff_casts[focusGuid] then
|
||||
pfUI.libdebuff_casts[focusGuid].endTime = this.endTime / 1000
|
||||
end
|
||||
-- Cast lifecycle, entirely on ClassicAPI's UNIT_SPELLCAST_* events. They
|
||||
-- fire per unit token: arg1=="player" for the player's own casts, and the
|
||||
-- remote token(s) ("target", "focus", ...) for other units -- so one set of
|
||||
-- events drives every bar with no Nampower dependency. The handler routes an
|
||||
-- event to this bar when arg1 matches its unit, or -- since the player's own
|
||||
-- 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")
|
||||
if unitstr == "target" then
|
||||
cb:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
elseif unitstr == "focus" then
|
||||
cb:RegisterEvent("PLAYER_FOCUS_CHANGED")
|
||||
end
|
||||
|
||||
cb:RegisterEvent("SPELL_DELAYED_SELF")
|
||||
cb:RegisterEvent(CASTBAR_EVENT_CAST_DELAY)
|
||||
cb:RegisterEvent(CASTBAR_EVENT_CHANNEL_DELAY)
|
||||
cb:RegisterEvent(CASTBAR_EVENT_CAST_START)
|
||||
cb:RegisterEvent(CASTBAR_EVENT_CHANNEL_START)
|
||||
cb:SetScript("OnEvent", function()
|
||||
if this.unitstr and not UnitIsUnit(this.unitstr, "player") then return end
|
||||
local unit = this.unitstr
|
||||
|
||||
if event == "SPELL_DELAYED_SELF" then
|
||||
-- arg1=casterGuid, arg2=delayMs (Nampower, most accurate)
|
||||
ApplyPushback(arg2)
|
||||
|
||||
elseif event == CASTBAR_EVENT_CAST_DELAY then
|
||||
-- SPELLCAST_DELAYED fallback intentionally removed - addon requires Nampower.
|
||||
-- Cast pushback is handled by SPELL_DELAYED_SELF above.
|
||||
if event == "PLAYER_TARGET_CHANGED" or event == "PLAYER_FOCUS_CHANGED" then
|
||||
RefreshBar(this)
|
||||
return
|
||||
end
|
||||
|
||||
elseif event == CASTBAR_EVENT_CHANNEL_DELAY then
|
||||
-- SPELLCAST_CHANNEL_UPDATE fires when a channel is pushed back by damage.
|
||||
-- arg1 = new remaining time in ms. Channel ends sooner = newEndTime < this.endTime.
|
||||
if not this.endTime or not arg1 then return end
|
||||
local newEndTime = GetTime() * 1000 + arg1
|
||||
local diff = this.endTime - newEndTime -- positive = time lost to pushback
|
||||
if diff > 50 then
|
||||
this.delay = (this.delay or 0) + diff / 1000
|
||||
this.endTime = newEndTime
|
||||
local focusGuid = this.focusGuid
|
||||
if focusGuid and pfUI.libdebuff_casts and pfUI.libdebuff_casts[focusGuid] then
|
||||
pfUI.libdebuff_casts[focusGuid].endTime = newEndTime / 1000
|
||||
-- UNIT_SPELLCAST_* fire per unit token (arg1). Handle an event when it's
|
||||
-- for this bar's unit, or -- since the player's own casts only ever fire
|
||||
-- arg1=="player" -- when this bar's unit currently resolves to the player
|
||||
-- (target=self / focus=self).
|
||||
-- Args: arg1=unit, arg2=castGUID, arg3=spellID, arg4=name, arg5=rank.
|
||||
if arg1 ~= unit and not (arg1 == "player" and UnitIsUnit(unit, "player")) then
|
||||
return
|
||||
end
|
||||
|
||||
if event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
|
||||
-- START also fires per craft in a same-spell chain, so an active merge
|
||||
-- just resyncs the current craft's spark/label instead of restamping.
|
||||
if this.tradeskillTotal then
|
||||
StartTradeskillCraft(this)
|
||||
else
|
||||
RefreshBar(this, arg5)
|
||||
if this.isTradeskill and (this.pendingTradeskillCount or 0) > 1
|
||||
and C.castbar.player.mergetradeskill == "1" then
|
||||
EnterTradeskillMerge(this, this.startTime, this.endTime, this.pendingTradeskillCount)
|
||||
end
|
||||
this.pendingTradeskillCount = nil
|
||||
end
|
||||
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
|
||||
-- Per-craft completion during a tradeskill merge (arg3 = spellID):
|
||||
-- count and clear when the chain is done. A no-op for normal casts,
|
||||
-- which are cleared by UNIT_SPELLCAST_STOP.
|
||||
if this.tradeskillTotal and arg3 == this.tradeskillSpellID then
|
||||
this.tradeskillCompleted = (this.tradeskillCompleted or 0) + 1
|
||||
if this.tradeskillCompleted >= this.tradeskillTotal then
|
||||
ClearBar(this)
|
||||
else
|
||||
UpdateTradeskillLabel(this)
|
||||
end
|
||||
end
|
||||
elseif event == "UNIT_SPELLCAST_CHANNEL_STOP" then
|
||||
-- A channel's stop can arrive after a following cast already claimed
|
||||
-- the bar (channel->cast transition); only clear if a channel is
|
||||
-- actually being shown, so it doesn't wipe an active cast bar.
|
||||
if this.isChannel then ClearBar(this) end
|
||||
elseif event == "UNIT_SPELLCAST_STOP" then
|
||||
-- STOP fires between crafts in a merge too (each craft is a new cast);
|
||||
-- UNIT_SPELLCAST_SUCCEEDED owns the count, so only a non-merge cast
|
||||
-- clears here.
|
||||
if not this.tradeskillTotal then ClearBar(this) end
|
||||
elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
|
||||
ClearBar(this, true)
|
||||
elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then
|
||||
-- Pushback: a cast delayed later or a channel shortened. Following
|
||||
-- Quartz, re-poll just the times and accumulate the shift into a running
|
||||
-- this.delay for the +/- indicator, rather than a full restamp (which
|
||||
-- would reset that total and re-render icon/text). ClassicAPI moves
|
||||
-- endMs (SpellDelayed_h bumps g_cast.endMs; the MSG_CHANNEL_UPDATE
|
||||
-- co-hook rewrites g_channel.endMs) while startMs stays put, so we diff
|
||||
-- endMs. delay is a positive magnitude; OnUpdate signs it "+" for casts
|
||||
-- and "-" for channels. Skipped during a tradeskill merge.
|
||||
if this.endTime and not this.tradeskillTotal then
|
||||
local query = this.unitstr ~= "" and this.unitstr or this.unitname
|
||||
local startMs, endMs
|
||||
if this.isChannel then
|
||||
local _, _, _, s, e = C_Spell.UnitChannelInfo(query)
|
||||
startMs, endMs = s, e
|
||||
else
|
||||
local _, _, _, s, e = C_Spell.UnitCastingInfo(query)
|
||||
startMs, endMs = s, e
|
||||
end
|
||||
if startMs and endMs then
|
||||
local shift = this.isChannel and (this.endTime - endMs) or (endMs - this.endTime)
|
||||
this.delay = (this.delay or 0) + shift / 1000
|
||||
this.startTime = startMs
|
||||
this.endTime = endMs
|
||||
local newDuration = (endMs - startMs) / 1000
|
||||
this.bar:SetMinMaxValues(0, newDuration)
|
||||
this.lastMax = newDuration
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == CASTBAR_EVENT_CAST_START or event == CASTBAR_EVENT_CHANNEL_START then
|
||||
playerarg = true
|
||||
this.delay = 0
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -350,6 +462,17 @@ pfUI:RegisterModule("castbar", function ()
|
||||
end
|
||||
|
||||
UpdateMovable(pfUI.castbar.player)
|
||||
|
||||
-- Tradeskill merge: hook DoTradeSkill so the player castbar knows the
|
||||
-- requested count before the first UNIT_SPELLCAST_START fires. Always-on
|
||||
-- hook (the config knob is read at event time so toggling takes effect on
|
||||
-- the next craft without a /reload). DoTradeSkill is synchronous; the
|
||||
-- server roundtrip to UNIT_SPELLCAST_START gives us plenty of time.
|
||||
hooksecurefunc("DoTradeSkill", function(index, num)
|
||||
if pfUI.castbar.player then
|
||||
pfUI.castbar.player.pendingTradeskillCount = tonumber(num) or 1
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- [[ pfTargetCastbar ]] --
|
||||
|
||||
+15
-13
@@ -82,10 +82,10 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
if C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
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 = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
pfUI.chat.left.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -257,10 +257,10 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
if C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
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 = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
pfUI.chat.right.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -429,7 +429,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
_G["ChatFrame" .. i .. "TabFlash"].Show = function() return end
|
||||
end
|
||||
|
||||
local _, class = UnitClass("player")
|
||||
local class = UnitClassBase("player")
|
||||
local classColor = PFUI_CLASS_COLORS[class]
|
||||
_G["ChatFrame" .. i .. "TabText"]:SetTextColor((classColor.r + .3) * .5, (classColor.g + .3) * .5, (classColor.b + .3) * .5, 1)
|
||||
_G["ChatFrame" .. i .. "TabText"]:SetFont(panelfont,panelfont_size, "OUTLINE")
|
||||
@@ -675,7 +675,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
-- read and parse whisper color settings
|
||||
local cr, cg, cb, ca = strsplit(",", C.chat.global.whisper)
|
||||
local cr, cg, cb, ca = GetStringColor(C.chat.global.whisper)
|
||||
cr, cg, cb = tonumber(cr), tonumber(cg), tonumber(cb)
|
||||
local wcol = rgbhex(cr, cg, cb)
|
||||
|
||||
@@ -706,10 +706,10 @@ pfUI:RegisterModule("chat", function ()
|
||||
_G.CHAT_WHISPER_INFORM_GET = '[W]' .. default
|
||||
end
|
||||
|
||||
local r,g,b,a = strsplit(",", C.chat.text.timecolor)
|
||||
local r,g,b,a = GetStringColor(C.chat.text.timecolor)
|
||||
local timecolorhex = rgbhex(r,g,b,a)
|
||||
|
||||
local r,g,b = strsplit(",", C.chat.text.unknowncolor)
|
||||
local r,g,b = GetStringColor(C.chat.text.unknowncolor)
|
||||
local unknowncolorhex = rgbhex(r,g,b)
|
||||
|
||||
-- Suppress FriendsFrame's WHO_LIST_UPDATE handling for our DLL-issued
|
||||
@@ -810,23 +810,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)
|
||||
|
||||
@@ -96,8 +96,7 @@ pfUI:RegisterModule("chatcopy", function ()
|
||||
scroll:Hide()
|
||||
|
||||
local editbox = CreateFrame("EditBox", "pfChatCopyBox" .. i, scroll)
|
||||
editbox:SetHeight(frame:GetHeight())
|
||||
editbox:SetWidth(frame:GetWidth())
|
||||
editbox:SetSize(frame:GetSize())
|
||||
editbox:SetAllPoints(scroll)
|
||||
editbox:SetTextColor(1,1,1,1)
|
||||
editbox:SetFontObject(ChatFontNormal)
|
||||
|
||||
@@ -5,7 +5,7 @@ pfUI:RegisterModule("combopoints", function ()
|
||||
ComboFrame:Hide()
|
||||
ComboFrame:UnregisterAllEvents()
|
||||
|
||||
local _, class = UnitClass("player")
|
||||
local class = UnitClassBase("player")
|
||||
local combo_width = C["unitframes"]["combowidth"]
|
||||
local combo_height = C["unitframes"]["comboheight"]
|
||||
pfUI.combopoints = {}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
pfUI:RegisterModule("cooldown", function ()
|
||||
-- cache values
|
||||
local lowcolor = {strsplit(",", C.appearance.cd.lowcolor)}
|
||||
local normalcolor = {strsplit(",", C.appearance.cd.normalcolor)}
|
||||
local minutecolor = {strsplit(",", C.appearance.cd.minutecolor)}
|
||||
local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
|
||||
local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
|
||||
-- local lowcolor = {strsplit(",", C.appearance.cd.lowcolor)}
|
||||
-- local normalcolor = {strsplit(",", C.appearance.cd.normalcolor)}
|
||||
-- local minutecolor = {strsplit(",", C.appearance.cd.minutecolor)}
|
||||
-- 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
|
||||
if not parent then this:Hide() return end
|
||||
parent_name = parent:GetName()
|
||||
|
||||
-- avoid to set cooldowns on invalid frames
|
||||
|
||||
+8
-11
@@ -8,9 +8,9 @@ pfUI:RegisterModule("easteregg", function ()
|
||||
local pvpking = CreateFrame("Frame", "pfPvPKing", UIParent)
|
||||
pvpking:Hide()
|
||||
|
||||
pvpking:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
pvpking:RegisterEvent("PLAYER_FLAGS_CHANGED")
|
||||
pvpking:SetScript("OnEvent", function()
|
||||
if strfind(arg1, "You are now", 1) and strfind(arg1, "(AFK)", 1) then
|
||||
if UnitIsAFK('player') then
|
||||
_G.CHAT_FLAG_AFK = title .. " "
|
||||
this.time = GetTime()
|
||||
this:Show()
|
||||
@@ -61,14 +61,13 @@ pfUI:RegisterModule("easteregg", function ()
|
||||
end)
|
||||
|
||||
-- trigger fireworks when being AFK
|
||||
fireworks:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
fireworks:RegisterEvent("PLAYER_FLAGS_CHANGED")
|
||||
fireworks:SetScript("OnEvent", function()
|
||||
if strfind(arg1, _G.MARKED_AFK) or strfind(arg1, _G.MARKED_AFK_MESSAGE) then
|
||||
local isAFK = UnitIsAFK('player')
|
||||
if isAFK then
|
||||
this:SetAlpha(0)
|
||||
this:Show()
|
||||
elseif strfind(arg1, _G.CLEARED_AFK) then
|
||||
this:Hide()
|
||||
end
|
||||
this:SetShown(isAFK)
|
||||
end)
|
||||
|
||||
-- basic explosion animation
|
||||
@@ -124,8 +123,7 @@ pfUI:RegisterModule("easteregg", function ()
|
||||
local f = GetExplosion()
|
||||
f:ClearAllPoints()
|
||||
f:SetPoint("CENTER", fireworks, "TOPLEFT", x, y)
|
||||
f:SetWidth(25)
|
||||
f:SetHeight(25)
|
||||
f:SetSize(25, 25)
|
||||
f.tex:SetTexture(1,1,1,.5)
|
||||
f:SetAlpha(1)
|
||||
f:Show()
|
||||
@@ -135,8 +133,7 @@ pfUI:RegisterModule("easteregg", function ()
|
||||
local f = GetExplosion()
|
||||
f:ClearAllPoints()
|
||||
f:SetPoint("CENTER", fireworks, "TOPLEFT", x+(math.random(0,100)-50), y+(math.random(0,100)-50))
|
||||
f:SetWidth(2)
|
||||
f:SetHeight(2)
|
||||
f:SetSize(2, 2)
|
||||
f.tex:SetTexture(math.random(),math.random(),math.random(),1)
|
||||
f:SetAlpha(1)
|
||||
f:Show()
|
||||
|
||||
@@ -2,7 +2,7 @@ local function getAdjustedTickTimer()
|
||||
local adjustedEnergyTick = 2
|
||||
|
||||
-- Check rogue talents and compute energy tick timing reduction for Combat spec (1.18.0 Blade Rush Talent)
|
||||
if UnitClass("player") == "Rogue" then
|
||||
if UnitClassBase("player") == "ROGUE" then
|
||||
local _, _, _, _, currRank = GetTalentInfo(2, 16)
|
||||
local bladeRushRank = currRank or 0
|
||||
|
||||
@@ -32,10 +32,10 @@ pfUI:RegisterModule("energytick", function()
|
||||
energytick:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS")
|
||||
|
||||
energytick:SetScript("OnEvent", function()
|
||||
if UnitPowerType("player") == 0 and C.unitframes.player.manatick == "1" then
|
||||
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
|
||||
this.mode = "MANA"
|
||||
this:Show()
|
||||
elseif UnitPowerType("player") == 3 and C.unitframes.player.energy == "1" then
|
||||
elseif UnitPowerType("player") == Enum.PowerType.Energy and C.unitframes.player.energy == "1" then
|
||||
this.mode = "ENERGY"
|
||||
this:Show()
|
||||
else
|
||||
@@ -51,11 +51,11 @@ pfUI:RegisterModule("energytick", function()
|
||||
end
|
||||
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
this.lastMana = UnitMana("player")
|
||||
this.lastMana = UnitPower("player")
|
||||
end
|
||||
|
||||
if (event == "UNIT_MANA" or event == "UNIT_ENERGY") and arg1 == "player" then
|
||||
this.currentMana = UnitMana("player")
|
||||
this.currentMana = UnitPower("player")
|
||||
local diff = 0
|
||||
if this.lastMana then
|
||||
diff = this.currentMana - this.lastMana
|
||||
@@ -64,7 +64,7 @@ pfUI:RegisterModule("energytick", function()
|
||||
if this.mode == "MANA" and diff < 0 then
|
||||
this.target = 5
|
||||
elseif this.mode == "MANA" and diff > 0 then
|
||||
if UnitMana("player") >= UnitManaMax("player") then
|
||||
if UnitPower("player") >= UnitPowerMax("player") then
|
||||
this.start = nil
|
||||
this.spark:SetAlpha(0)
|
||||
this:Hide()
|
||||
@@ -105,7 +105,7 @@ pfUI:RegisterModule("energytick", function()
|
||||
|
||||
if this.current > this.max then
|
||||
-- Don't restart tick timer if mana is full
|
||||
if this.mode == "MANA" and UnitMana("player") >= UnitManaMax("player") then
|
||||
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
|
||||
this.start = nil
|
||||
this.spark:SetAlpha(0)
|
||||
return
|
||||
|
||||
+125
-220
@@ -1,243 +1,148 @@
|
||||
pfUI:RegisterModule("eqcompare", function ()
|
||||
local sides = { "Left", "Right" }
|
||||
local loc = pfUI.cache["locale"]
|
||||
for key, value in pairs(L["itemtypes"]) do setglobal(key, value) end
|
||||
INVTYPE_WEAPON_OTHER = INVTYPE_WEAPON.."_other"
|
||||
INVTYPE_FINGER_OTHER = INVTYPE_FINGER.."_other"
|
||||
INVTYPE_TRINKET_OTHER = INVTYPE_TRINKET.."_other"
|
||||
pfUI.eqcompare = {}
|
||||
|
||||
local function AddHeader(tooltip)
|
||||
local name = tooltip:GetName()
|
||||
local function ShowCompareItem(self, link, shift)
|
||||
self = self or GameTooltip
|
||||
shift = shift or IsShiftKeyDown()
|
||||
|
||||
-- shift all entries one line down
|
||||
for i=tooltip:NumLines(), 1, -1 do
|
||||
for _, side in pairs(sides) do
|
||||
local current = _G[name.."Text"..side..i]
|
||||
local below = _G[name.."Text"..side..i+1]
|
||||
if not link or (not shift and (C.tooltip.compare.showalways ~= "1" or C_Item.IsEquippedItem(link))) then
|
||||
return
|
||||
end
|
||||
|
||||
if current and current:IsShown() then
|
||||
local text = current:GetText()
|
||||
local r, g, b = current:GetTextColor()
|
||||
local shoppingTooltip1, shoppingTooltip2 = unpack(self.shoppingTooltips or { ShoppingTooltip1, ShoppingTooltip2 });
|
||||
|
||||
if text and text ~= "" then
|
||||
if tooltip:NumLines() < i+1 then
|
||||
-- add new line if required
|
||||
tooltip:AddLine(text, r, g, b, true)
|
||||
else
|
||||
-- update existing lines
|
||||
below:SetText(text)
|
||||
below:SetTextColor(r, g, b)
|
||||
below:Show()
|
||||
local SEPARATION = 6;
|
||||
local backdrop = shoppingTooltip1.GetBackdrop and shoppingTooltip1:GetBackdrop();
|
||||
local GAP = SEPARATION + ((type(backdrop) == "table" and backdrop.edgeSize) or 0);
|
||||
|
||||
-- hide processed line
|
||||
current:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
local item1 = nil;
|
||||
local item2 = nil;
|
||||
local side = "left";
|
||||
if ( shoppingTooltip1:SetHyperlinkCompareItem(link, 1, shift, self) ) then
|
||||
item1 = true;
|
||||
end
|
||||
if ( shoppingTooltip2:SetHyperlinkCompareItem(link, 2, shift, self) ) then
|
||||
item2 = true;
|
||||
end
|
||||
|
||||
-- find correct side
|
||||
local rightDist = 0;
|
||||
local leftPos = self:GetLeft();
|
||||
local rightPos = self:GetRight();
|
||||
if ( not rightPos ) then
|
||||
rightPos = 0;
|
||||
end
|
||||
if ( not leftPos ) then
|
||||
leftPos = 0;
|
||||
end
|
||||
|
||||
rightDist = GetScreenWidth() - rightPos;
|
||||
|
||||
if (leftPos and (rightDist < leftPos)) then
|
||||
side = "left";
|
||||
else
|
||||
side = "right";
|
||||
end
|
||||
|
||||
-- see if we should slide the tooltip
|
||||
if ( self:GetAnchorType() and self:GetAnchorType() ~= "ANCHOR_PRESERVE" ) then
|
||||
local totalWidth = 0;
|
||||
if ( item1 ) then
|
||||
totalWidth = totalWidth + shoppingTooltip1:GetWidth();
|
||||
end
|
||||
if ( item2 ) then
|
||||
totalWidth = totalWidth + shoppingTooltip2:GetWidth();
|
||||
end
|
||||
|
||||
if ( (side == "left") and (totalWidth > leftPos) ) then
|
||||
self:SetAnchorType(self:GetAnchorType(), (totalWidth - leftPos), 0);
|
||||
elseif ( (side == "right") and (rightPos + totalWidth) > GetScreenWidth() ) then
|
||||
self:SetAnchorType(self:GetAnchorType(), -((rightPos + totalWidth) - GetScreenWidth()), 0);
|
||||
end
|
||||
end
|
||||
|
||||
-- add label to first line
|
||||
_G[name.."TextLeft1"]:SetTextColor(.5, .5, .5, 1)
|
||||
_G[name.."TextLeft1"]:SetText(CURRENTLY_EQUIPPED)
|
||||
_G[name.."TextLeft1"]:Show()
|
||||
if ( item1 ) then
|
||||
shoppingTooltip1:SetOwner(self, "ANCHOR_NONE");
|
||||
shoppingTooltip1:ClearAllPoints();
|
||||
if ( side and side == "left" ) then
|
||||
shoppingTooltip1:SetPoint("TOPRIGHT", self, "TOPLEFT", -GAP, -10);
|
||||
else
|
||||
shoppingTooltip1:SetPoint("TOPLEFT", self, "TOPRIGHT", GAP, -10);
|
||||
end
|
||||
shoppingTooltip1:SetHyperlinkCompareItem(link, 1, shift, self);
|
||||
shoppingTooltip1:Show();
|
||||
|
||||
-- update tooltip sizes
|
||||
tooltip:Show()
|
||||
if ( item2 ) then
|
||||
shoppingTooltip2:SetOwner(shoppingTooltip1, "ANCHOR_NONE");
|
||||
shoppingTooltip2:ClearAllPoints();
|
||||
if ( side and side == "left" ) then
|
||||
shoppingTooltip2:SetPoint("TOPRIGHT", shoppingTooltip1, "TOPLEFT", -GAP, 0);
|
||||
else
|
||||
shoppingTooltip2:SetPoint("TOPLEFT", shoppingTooltip1, "TOPRIGHT", GAP, 0);
|
||||
end
|
||||
shoppingTooltip2:SetHyperlinkCompareItem(link, 2, shift, self);
|
||||
shoppingTooltip2:Show();
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local slotTable = {
|
||||
[INVTYPE_2HWEAPON] = "MainHandSlot",
|
||||
[INVTYPE_BODY] = "ShirtSlot",
|
||||
[INVTYPE_CHEST] = "ChestSlot",
|
||||
[INVTYPE_CLOAK] = "BackSlot",
|
||||
[INVTYPE_FEET] = "FeetSlot",
|
||||
[INVTYPE_FINGER] = "Finger0Slot",
|
||||
[INVTYPE_FINGER_OTHER] = "Finger1Slot",
|
||||
[INVTYPE_HAND] = "HandsSlot",
|
||||
[INVTYPE_HEAD] = "HeadSlot",
|
||||
[INVTYPE_HOLDABLE] = "SecondaryHandSlot",
|
||||
[INVTYPE_LEGS] = "LegsSlot",
|
||||
[INVTYPE_NECK] = "NeckSlot",
|
||||
[INVTYPE_RANGED] = "RangedSlot",
|
||||
[INVTYPE_RELIC] = "RangedSlot",
|
||||
[INVTYPE_ROBE] = "ChestSlot",
|
||||
[INVTYPE_SHIELD] = "SecondaryHandSlot",
|
||||
[INVTYPE_SHOULDER] = "ShoulderSlot",
|
||||
[INVTYPE_TABARD] = "TabardSlot",
|
||||
[INVTYPE_TRINKET] = "Trinket0Slot",
|
||||
[INVTYPE_TRINKET_OTHER] = "Trinket1Slot",
|
||||
[INVTYPE_WAIST] = "WaistSlot",
|
||||
[INVTYPE_WEAPON] = "MainHandSlot",
|
||||
[INVTYPE_WEAPON_OTHER] = "SecondaryHandSlot",
|
||||
[INVTYPE_WEAPONMAINHAND] = "MainHandSlot",
|
||||
[INVTYPE_WEAPONOFFHAND] = "SecondaryHandSlot",
|
||||
[INVTYPE_WRIST] = "WristSlot",
|
||||
local prevMerchant = ShoppingTooltip1.SetMerchantCompareItem
|
||||
local function SetMerchantCompareItem(self, index, compareItem)
|
||||
if compareItem == 1 then
|
||||
ShowCompareItem(nil, GetMerchantItemLink(index), 1)
|
||||
return false
|
||||
end
|
||||
return prevMerchant and prevMerchant(self, index, compareItem)
|
||||
end
|
||||
|
||||
[INVTYPE_WAND] = "RangedSlot",
|
||||
[INVTYPE_GUN] = "RangedSlot",
|
||||
[INVTYPE_PROJECTILE] = "AmmoSlot",
|
||||
[INVTYPE_CROSSBOW] = "RangedSlot",
|
||||
[INVTYPE_THROWN] = "RangedSlot",
|
||||
local prevAuction = ShoppingTooltip1.SetAuctionCompareItem
|
||||
local function SetAuctionCompareItem(self, type, index, compareItem)
|
||||
if compareItem == 1 then
|
||||
ShowCompareItem(nil, GetAuctionItemLink(type, index), 1)
|
||||
return false
|
||||
end
|
||||
return prevAuction and prevAuction(self, type, index, compareItem)
|
||||
end
|
||||
|
||||
ShoppingTooltip1.SetMerchantCompareItem = SetMerchantCompareItem
|
||||
ShoppingTooltip2.SetMerchantCompareItem = SetMerchantCompareItem
|
||||
ShoppingTooltip1.SetAuctionCompareItem = SetAuctionCompareItem
|
||||
ShoppingTooltip2.SetAuctionCompareItem = SetAuctionCompareItem
|
||||
|
||||
local TooltipHooks = {
|
||||
SetLootRollItem = GetLootRollItemLink,
|
||||
SetLootItem = GetLootSlotLink,
|
||||
SetQuestLogItem = GetQuestLogItemLink,
|
||||
SetQuestItem = GetQuestItemLink,
|
||||
SetHyperlink = function(link) return link end,
|
||||
SetBagItem = GetContainerItemLink,
|
||||
SetInboxItem = GetInboxItemLink,
|
||||
SetInventoryItem = GetInventoryItemLink,
|
||||
SetTradeSkillItem = function(skillIndex, reagentIndex)
|
||||
if reagentIndex then
|
||||
return GetTradeSkillReagentItemLink(skillIndex, reagentIndex)
|
||||
else
|
||||
return GetTradeSkillItemLink(skillIndex)
|
||||
end
|
||||
end,
|
||||
SetAuctionSellItem = GetAuctionSellItemLink,
|
||||
SetTradePlayerItem = GetTradePlayerItemLink,
|
||||
SetTradeTargetItem = GetTradeTargetItemLink
|
||||
}
|
||||
|
||||
local function startsWith(str, start)
|
||||
return string.sub(str, 1, string.len(start)) == start
|
||||
end
|
||||
|
||||
local function ExtractAttributes(tooltip)
|
||||
local name = tooltip:GetName()
|
||||
|
||||
-- get the name/header of the last set comparison tooltip
|
||||
local comparetooltip = pfUI.eqcompare.tooltip:GetName()
|
||||
local iname = _G[comparetooltip .. "TextLeft1"] and _G[comparetooltip .. "TextLeft1"]:GetText()
|
||||
|
||||
-- only run once per item
|
||||
if tooltip.pfCompLastName == iname then return end
|
||||
|
||||
tooltip.pfCompData = {}
|
||||
tooltip.pfCompLastName = iname
|
||||
|
||||
for i=1,30 do
|
||||
local widget = _G[name.."TextLeft"..i]
|
||||
if widget and widget:GetObjectType() == "FontString" then
|
||||
local text = widget:GetText()
|
||||
if text and not string.find(text, "-", 1, true) then
|
||||
local start = 1
|
||||
if startsWith(text, "\+") or startsWith(text, "\(") then start = 2 end
|
||||
|
||||
local space = string.find(text, " ", 1, true)
|
||||
if space then
|
||||
local value = tonumber(string.sub(text, start, space-1))
|
||||
if value and text then
|
||||
-- we've found an attr
|
||||
local attr = string.sub(text, space, string.len(text))
|
||||
tooltip.pfCompData[attr] = { value = tonumber(value), widget = widget }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local function makeHook(getter)
|
||||
return function(tooltip, arg1, arg2, arg3)
|
||||
ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
|
||||
end
|
||||
end
|
||||
|
||||
local function CompareAttributes(data, targetData)
|
||||
if not data then return end
|
||||
|
||||
for attr,v in pairs(data) do
|
||||
if targetData then
|
||||
local target = targetData[attr]
|
||||
if target then
|
||||
if v.value ~= target.value and v.widget:GetText() then
|
||||
if v.value > target.value then
|
||||
if not strfind(v.widget:GetText(), "|cff88ff88") and not strfind(v.widget:GetText(), "|cffff8888") then
|
||||
v.widget:SetText(v.widget:GetText() .. "|cff88ff88 (+" .. round(v.value - target.value, 1) .. ")")
|
||||
end
|
||||
elseif not v.widget.compSet then
|
||||
if not strfind(v.widget:GetText(), "|cff88ff88") and not strfind(v.widget:GetText(), "|cffff8888") then
|
||||
v.widget:SetText(v.widget:GetText() .. "|cffff8888 (-" .. round(target.value - v.value, 1) .. ")")
|
||||
end
|
||||
end
|
||||
target.processed = true
|
||||
else
|
||||
target.processed = true
|
||||
end
|
||||
else
|
||||
-- this attribute doesnt exist in target
|
||||
if v.widget and v.widget:GetText() then
|
||||
if not strfind(v.widget:GetText(), "|cff88ff88") and not strfind(v.widget:GetText(), "|cffff8888") then
|
||||
v.widget:SetText(v.widget:GetText() .. "|cff88ff88 (+" .. v.value .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _,target in pairs(targetData) do
|
||||
if target and not target.processed then
|
||||
-- we are an extra value
|
||||
local text = target.widget:GetText()
|
||||
if text and not strfind(text, "|cff88ff88") and not strfind(text, "|cffff8888") then
|
||||
target.widget:SetText(text .. "|cff88ff88 (+" .. target.value .. ")")
|
||||
end
|
||||
end
|
||||
local function HookTooltip(tooltip)
|
||||
for setter, getter in pairs(TooltipHooks) do
|
||||
hooksecurefunc(tooltip, setter, makeHook(getter))
|
||||
end
|
||||
end
|
||||
|
||||
pfUI.eqcompare = {}
|
||||
pfUI.eqcompare.GameTooltipShow = function()
|
||||
-- use this tooltip for the next comparison
|
||||
pfUI.eqcompare.tooltip = this
|
||||
HookTooltip(GameTooltip)
|
||||
|
||||
if not IsShiftKeyDown() and C.tooltip.compare.showalways ~= "1" then return end
|
||||
local rawborder, border = GetBorderSize()
|
||||
|
||||
for i=1,this:NumLines() do
|
||||
local tmpText = _G[this:GetName() .. "TextLeft"..i]
|
||||
for slotType, slotName in pairs(slotTable) do
|
||||
if tmpText:GetText() == slotType then
|
||||
local slotID = GetInventorySlotInfo(slotTable[slotType])
|
||||
|
||||
-- determine screen part
|
||||
local ltrigger = GetScreenWidth() / 2
|
||||
local x = GetCursorPosition()
|
||||
x = x / UIParent:GetEffectiveScale()
|
||||
if x > ltrigger then ltrigger = nil end
|
||||
|
||||
-- first tooltip
|
||||
ShoppingTooltip1:SetOwner(this, "ANCHOR_NONE")
|
||||
ShoppingTooltip1:ClearAllPoints()
|
||||
|
||||
if ltrigger then
|
||||
ShoppingTooltip1:SetPoint("BOTTOMLEFT", this, "BOTTOMRIGHT", 0, 0)
|
||||
else
|
||||
ShoppingTooltip1:SetPoint("BOTTOMRIGHT", this, "BOTTOMLEFT", -border*2-1, 0)
|
||||
end
|
||||
|
||||
ShoppingTooltip1:SetInventoryItem("player", slotID)
|
||||
ShoppingTooltip1:Show()
|
||||
AddHeader(ShoppingTooltip1)
|
||||
|
||||
-- second tooltip
|
||||
if slotTable[slotType .. "_other"] then
|
||||
local slotID_other = GetInventorySlotInfo(slotTable[slotType .. "_other"])
|
||||
|
||||
ShoppingTooltip2:SetOwner(this, "ANCHOR_NONE")
|
||||
ShoppingTooltip2:ClearAllPoints()
|
||||
|
||||
if ltrigger then
|
||||
ShoppingTooltip2:SetPoint("BOTTOMLEFT", ShoppingTooltip1, "BOTTOMRIGHT", 0, 0)
|
||||
else
|
||||
ShoppingTooltip2:SetPoint("BOTTOMRIGHT", ShoppingTooltip1, "BOTTOMLEFT", -border*2-1, 0)
|
||||
end
|
||||
|
||||
ShoppingTooltip2:SetInventoryItem("player", slotID_other)
|
||||
ShoppingTooltip2:Show()
|
||||
AddHeader(ShoppingTooltip2)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- add HookScript method if not already existing
|
||||
GameTooltip.HookScript = GameTooltip.HookScript or HookScript
|
||||
ShoppingTooltip1.HookScript = ShoppingTooltip1.HookScript or HookScript
|
||||
ShoppingTooltip2.HookScript = ShoppingTooltip2.HookScript or HookScript
|
||||
|
||||
pfUI.eqcompare.ShoppingTooltipShow = function()
|
||||
-- abort if no comparison tooltip has been set
|
||||
if not pfUI.eqcompare.tooltip then return end
|
||||
|
||||
ExtractAttributes(this)
|
||||
ExtractAttributes(pfUI.eqcompare.tooltip)
|
||||
CompareAttributes(pfUI.eqcompare.tooltip.pfCompData, this.pfCompData)
|
||||
end
|
||||
|
||||
-- Add Gametooltip Hooks
|
||||
GameTooltip:HookScript("OnShow", pfUI.eqcompare.GameTooltipShow)
|
||||
if C.tooltip.compare.basestats == "1" then
|
||||
ShoppingTooltip1:HookScript("OnShow", pfUI.eqcompare.ShoppingTooltipShow)
|
||||
ShoppingTooltip2:HookScript("OnShow", pfUI.eqcompare.ShoppingTooltipShow)
|
||||
end
|
||||
pfUI.eqcompare.HookTooltip = HookTooltip
|
||||
end)
|
||||
|
||||
+96
-273
@@ -1,11 +1,10 @@
|
||||
-- Equipment Manager module
|
||||
-- Backport of the 4.3.4 GearManagerDialog UI on top of ClassicAPI's
|
||||
-- C_EquipmentSet.* API. Adds a 6th tab to CharacterFrame.
|
||||
-- Backport of the 4.3.4 GearManagerDialog UI on top of ClassicAPI's C_EquipmentSet.* API.
|
||||
|
||||
pfUI:RegisterModule("equipmentmanager", function()
|
||||
if not C_EquipmentSet or not C_EquipmentSet.CanUseEquipmentSets() then return end
|
||||
|
||||
pfUI.equipmentmanager = pfUI.equipmentmanager or {}
|
||||
pfUI.equipmentmanager = {}
|
||||
|
||||
local SET_ROW_HEIGHT = 36
|
||||
|
||||
@@ -14,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
|
||||
@@ -55,7 +55,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local function EquipSet(setID)
|
||||
if not setID then return end
|
||||
if C_EquipmentSet.EquipmentSetContainsLockedItems(setID) then
|
||||
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT or "Locked items in set", 1, .1, .1, 1)
|
||||
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT, 1, .1, .1, 1)
|
||||
return
|
||||
end
|
||||
ClearCursor()
|
||||
@@ -68,8 +68,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local frame = CreateFrame("Frame", "pfEquipmentManagerFrame", CharacterFrame)
|
||||
frame:SetWidth(220)
|
||||
frame:SetHeight(350)
|
||||
frame:SetSize(220, 350)
|
||||
frame:SetFrameStrata("HIGH")
|
||||
frame:SetScript("OnShow", function()
|
||||
this:ClearAllPoints()
|
||||
@@ -78,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)
|
||||
@@ -86,7 +85,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
frame.title:SetPoint("TOP", frame, "TOP", 0, -10)
|
||||
frame.title:SetText(T["Equipment Manager"] or "Equipment Manager")
|
||||
frame.title:SetText(EQUIPMENT_MANAGER)
|
||||
|
||||
local closeBtn = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
|
||||
closeBtn:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4)
|
||||
@@ -98,15 +97,14 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local toggleBtn = CreateFrame("Button", "pfEqMgrToggleButton", PaperDollFrame)
|
||||
toggleBtn:SetWidth(28)
|
||||
toggleBtn:SetHeight(28)
|
||||
toggleBtn:SetSize(28, 28)
|
||||
toggleBtn:SetPoint("BOTTOM", CharacterHandsSlot, "TOP", 0, 4)
|
||||
toggleBtn:SetNormalTexture(pfUI.path.."\\img\\UI-GearManager-Button")
|
||||
toggleBtn:SetPushedTexture(pfUI.path.."\\img\\UI-GearManager-Button-Pushed")
|
||||
toggleBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
|
||||
toggleBtn:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText(T["Equipment Manager"] or "Equipment Manager")
|
||||
GameTooltip:SetText(PAPERDOLL_EQUIPMENTMANAGER)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
toggleBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
@@ -126,13 +124,13 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local btnEquip = CreateFrame("Button", "pfEqMgrEquip", frame, "UIPanelButtonTemplate")
|
||||
btnEquip:SetWidth(86); btnEquip:SetHeight(22); btnEquip:SetText(T["Equip"] or "Equip")
|
||||
btnEquip:SetSize(86, 22); btnEquip:SetText(EQUIPSET_EQUIP)
|
||||
btnEquip:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -30)
|
||||
SkinButton(btnEquip)
|
||||
btnEquip:SetScript("OnClick", function() EquipSet(selectedSetID) end)
|
||||
|
||||
local btnSave = CreateFrame("Button", "pfEqMgrSave", frame, "UIPanelButtonTemplate")
|
||||
btnSave:SetWidth(86); btnSave:SetHeight(22); btnSave:SetText(T["Save"] or "Save")
|
||||
btnSave:SetSize(86, 22); btnSave:SetText(SAVE)
|
||||
btnSave:SetPoint("LEFT", btnEquip, "RIGHT", 6, 0)
|
||||
SkinButton(btnSave)
|
||||
btnSave:SetScript("OnClick", function()
|
||||
@@ -141,7 +139,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
if not name then return end
|
||||
local targetID = selectedSetID
|
||||
StaticPopupDialogs["PFUI_EQMGR_SAVE_CONFIRM"] = {
|
||||
text = string.format(T["Would you like to save the equipment set '%s'?"] or "Would you like to save the equipment set '%s'?", name),
|
||||
text = string.format(CONFIRM_SAVE_EQUIPMENT_SET, name),
|
||||
button1 = YES, button2 = NO,
|
||||
OnAccept = function()
|
||||
C_EquipmentSet.ClearIgnoredSlotsForSave()
|
||||
@@ -166,7 +164,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
local rowMenu = CreateFrame("Frame", "pfEqMgrRowMenu", UIParent)
|
||||
rowMenu:SetFrameStrata("DIALOG")
|
||||
rowMenu:SetWidth(140); rowMenu:SetHeight(50)
|
||||
rowMenu:SetSize(140, 50);
|
||||
rowMenu:Hide()
|
||||
CreateBackdrop(rowMenu, nil, nil, .95)
|
||||
CreateBackdropShadow(rowMenu)
|
||||
@@ -185,9 +183,9 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
tinsert(UISpecialFrames, "pfEqMgrRowMenu") -- Escape closes it
|
||||
|
||||
rowMenu.changeBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
|
||||
rowMenu.changeBtn:SetWidth(130); rowMenu.changeBtn:SetHeight(20)
|
||||
rowMenu.changeBtn:SetSize(130, 20);
|
||||
rowMenu.changeBtn:SetPoint("TOPLEFT", rowMenu, "TOPLEFT", 5, -3)
|
||||
rowMenu.changeBtn:SetText(T["Change Name/Icon"] or "Change Name/Icon")
|
||||
rowMenu.changeBtn:SetText(EQUIPMENT_SET_EDIT)
|
||||
SkinButton(rowMenu.changeBtn)
|
||||
rowMenu.changeBtn:SetScript("OnClick", function()
|
||||
rowMenu:Hide()
|
||||
@@ -198,9 +196,9 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end)
|
||||
|
||||
rowMenu.deleteBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
|
||||
rowMenu.deleteBtn:SetWidth(130); rowMenu.deleteBtn:SetHeight(20)
|
||||
rowMenu.deleteBtn:SetSize(130, 20);
|
||||
rowMenu.deleteBtn:SetPoint("TOP", rowMenu.changeBtn, "BOTTOM", 0, -2)
|
||||
rowMenu.deleteBtn:SetText(T["Delete"] or "Delete")
|
||||
rowMenu.deleteBtn:SetText(DELETE)
|
||||
SkinButton(rowMenu.deleteBtn)
|
||||
rowMenu.deleteBtn:SetScript("OnClick", function()
|
||||
rowMenu:Hide()
|
||||
@@ -208,7 +206,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local targetID = rowMenu.targetSetID
|
||||
local name = C_EquipmentSet.GetEquipmentSetInfo(targetID)
|
||||
StaticPopupDialogs["PFUI_EQMGR_DELETE"] = {
|
||||
text = string.format(T["Delete equipment set '%s'?"] or "Delete equipment set '%s'?", name or "?"),
|
||||
text = string.format(CONFIRM_DELETE_EQUIPMENT_SET, name or "?"),
|
||||
button1 = YES, button2 = NO,
|
||||
OnAccept = function()
|
||||
pendingIgnoredToggles[targetID] = nil
|
||||
@@ -229,9 +227,8 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local LIST_ROW_STRIDE = SET_ROW_HEIGHT + 2
|
||||
local listFrame = CreateFrame("Frame", nil, frame)
|
||||
listFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -58)
|
||||
listFrame:SetWidth(180)
|
||||
-- Height fits N rows + (N-1) inter-row gaps + 3px top/bottom padding.
|
||||
listFrame:SetHeight(LIST_VISIBLE_ROWS * LIST_ROW_STRIDE + 4)
|
||||
listFrame:SetSize(180, LIST_VISIBLE_ROWS * LIST_ROW_STRIDE + 4)
|
||||
CreateBackdrop(listFrame, nil, nil, .75)
|
||||
|
||||
-- Mouse-wheel scroll: list of [sets..., newSetRow] is virtualized
|
||||
@@ -247,15 +244,13 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local setRows = {}
|
||||
local function CreateSetRow()
|
||||
local row = CreateFrame("Button", nil, listFrame)
|
||||
row:SetWidth(170)
|
||||
row:SetHeight(SET_ROW_HEIGHT)
|
||||
row:SetSize(170, SET_ROW_HEIGHT)
|
||||
-- Position is set per-Refresh based on the row's visible slot;
|
||||
-- start anchored to avoid uninitialized geometry before first Refresh.
|
||||
row:SetPoint("TOPLEFT", listFrame, "TOPLEFT", 5, -3)
|
||||
|
||||
row.icon = row:CreateTexture(nil, "ARTWORK")
|
||||
row.icon:SetWidth(30)
|
||||
row.icon:SetHeight(30)
|
||||
row.icon:SetSize(30, 30)
|
||||
row.icon:SetPoint("LEFT", row, "LEFT", 2, 0)
|
||||
row.icon:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
@@ -270,7 +265,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
row.highlight:Hide()
|
||||
|
||||
row.gear = CreateFrame("Button", nil, row)
|
||||
row.gear:SetWidth(16); row.gear:SetHeight(16)
|
||||
row.gear:SetSize(16, 16);
|
||||
row.gear:SetPoint("RIGHT", row, "RIGHT", -4, 0)
|
||||
row.gear.tex = row.gear:CreateTexture(nil, "ARTWORK")
|
||||
row.gear.tex:SetAllPoints(row.gear)
|
||||
@@ -306,27 +301,27 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
pfUI.equipmentmanager.Refresh()
|
||||
end)
|
||||
|
||||
-- Hover handling: OnLeave fires when the cursor moves onto a child
|
||||
-- (the gear button becomes the topmost mouse target). Use an
|
||||
-- OnUpdate poll so the gear stays shown while the cursor is over
|
||||
-- the row OR the gear itself.
|
||||
-- Show the gear and tooltip while the cursor is over the row or its
|
||||
-- gear child. The gear sits inside the row's rectangle, so a single
|
||||
-- MouseIsOver(row) check covers both. OnLeave on the row and the gear
|
||||
-- catches every exit path, so no OnUpdate poll is needed.
|
||||
local function HideRowHover()
|
||||
if MouseIsOver(row) then return end
|
||||
GameTooltip:Hide()
|
||||
row.gear:Hide()
|
||||
end
|
||||
row:SetScript("OnEnter", function()
|
||||
if not this.setID then return end
|
||||
local name = C_EquipmentSet.GetEquipmentSetInfo(this.setID)
|
||||
if not row.setID then return end
|
||||
local name = C_EquipmentSet.GetEquipmentSetInfo(row.setID)
|
||||
if name then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetOwner(row, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetEquipmentSet(name)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
row.gear:Show()
|
||||
this:SetScript("OnUpdate", function()
|
||||
if not MouseIsOver(this) and not MouseIsOver(row.gear) then
|
||||
this:SetScript("OnUpdate", nil)
|
||||
GameTooltip:Hide()
|
||||
row.gear:Hide()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
row:SetScript("OnLeave", HideRowHover)
|
||||
row.gear:SetScript("OnLeave", HideRowHover)
|
||||
|
||||
return row
|
||||
end
|
||||
@@ -338,10 +333,10 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- specific set index (row[i] always shows ids[i]).
|
||||
|
||||
local newSetRow = CreateFrame("Button", nil, listFrame)
|
||||
newSetRow:SetWidth(170); newSetRow:SetHeight(SET_ROW_HEIGHT)
|
||||
newSetRow:SetSize(170, SET_ROW_HEIGHT);
|
||||
|
||||
newSetRow.icon = newSetRow:CreateTexture(nil, "ARTWORK")
|
||||
newSetRow.icon:SetWidth(24); newSetRow.icon:SetHeight(24)
|
||||
newSetRow.icon:SetSize(24, 24);
|
||||
newSetRow.icon:SetPoint("LEFT", newSetRow, "LEFT", 5, 0)
|
||||
newSetRow.icon:SetTexture(pfUI.path.."\\img\\Character-Plus")
|
||||
|
||||
@@ -349,7 +344,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
newSetRow.text:SetPoint("LEFT", newSetRow.icon, "RIGHT", 8, 0)
|
||||
newSetRow.text:SetPoint("RIGHT", newSetRow, "RIGHT", -4, 0)
|
||||
newSetRow.text:SetJustifyH("LEFT")
|
||||
newSetRow.text:SetText(T["New Set"] or "New Set")
|
||||
newSetRow.text:SetText(PAPERDOLL_NEWEQUIPMENTSET)
|
||||
newSetRow.text:SetTextColor(0.2, 1, 0.2)
|
||||
|
||||
newSetRow.highlight = newSetRow:CreateTexture(nil, "BACKGROUND")
|
||||
@@ -362,8 +357,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
local function MakeButton(name, label, parent, anchor, ax, ay, width)
|
||||
local b = CreateFrame("Button", name, parent, "UIPanelButtonTemplate")
|
||||
b:SetWidth(width or 70)
|
||||
b:SetHeight(22)
|
||||
b:SetSize(width or 70, 22)
|
||||
b:SetText(label)
|
||||
b:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", ax, ay)
|
||||
SkinButton(b)
|
||||
@@ -376,8 +370,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
local namePopup = CreateFrame("Frame", "pfEqMgrNamePopup", UIParent)
|
||||
namePopup:SetFrameStrata("DIALOG")
|
||||
namePopup:SetWidth(472)
|
||||
namePopup:SetHeight(498)
|
||||
namePopup:SetSize(472, 484)
|
||||
namePopup:SetPoint("CENTER", UIParent, "CENTER")
|
||||
namePopup:Hide()
|
||||
CreateBackdrop(namePopup, nil, nil, .9)
|
||||
@@ -388,200 +381,28 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
namePopup:SetScript("OnDragStart", function() this:StartMoving() end)
|
||||
namePopup:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
||||
|
||||
namePopup.title = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
namePopup.title:SetPoint("TOP", namePopup, "TOP", 0, -10)
|
||||
namePopup.title:SetText(T["Save Set"] or "Save Set")
|
||||
-- Equipment seed leads the grid with gear-relevant item icons. The widget
|
||||
-- owns the name field, preview, grid, filter, and search; this module keeps
|
||||
-- the OK/Cancel buttons and the create/save/rename flow below.
|
||||
local iconPicker = CreateIconPicker("pfEqMgrIcon", namePopup,
|
||||
IconDataProviderExtraType.Equipment, GEARSETS_POPUP_TEXT)
|
||||
|
||||
namePopup.nameLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
namePopup.nameLabel:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -34)
|
||||
namePopup.nameLabel:SetText(T["Enter Set Name (Max 16 Characters):"] or "Enter Set Name (Max 16 Characters):")
|
||||
local btnPopupOK = MakeButton("pfEqMgrPopupOK", OKAY, namePopup, namePopup, 14, -340, 80)
|
||||
|
||||
namePopup.editbox = CreateFrame("EditBox", "pfEqMgrNameEdit", namePopup, "InputBoxTemplate")
|
||||
namePopup.editbox:SetWidth(280)
|
||||
namePopup.editbox:SetHeight(20)
|
||||
namePopup.editbox:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -52)
|
||||
namePopup.editbox:SetAutoFocus(false)
|
||||
namePopup.editbox:SetMaxLetters(16)
|
||||
CreateBackdrop(namePopup.editbox)
|
||||
|
||||
namePopup.selectedLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
namePopup.selectedLabel:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", -14, -28)
|
||||
namePopup.selectedLabel:SetText(T["Currently Selected"] or "Currently Selected")
|
||||
namePopup.selectedLabel:SetTextColor(1, 0.82, 0)
|
||||
|
||||
namePopup.selectedPreview = CreateFrame("Frame", nil, namePopup)
|
||||
namePopup.selectedPreview:SetWidth(42)
|
||||
namePopup.selectedPreview:SetHeight(42)
|
||||
namePopup.selectedPreview:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", -14, -44)
|
||||
CreateBackdrop(namePopup.selectedPreview)
|
||||
namePopup.selectedPreview.tex = namePopup.selectedPreview:CreateTexture(nil, "ARTWORK")
|
||||
namePopup.selectedPreview.tex:SetAllPoints(namePopup.selectedPreview)
|
||||
namePopup.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
namePopup.iconLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
namePopup.iconLabel:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -100)
|
||||
namePopup.iconLabel:SetText(T["Choose an Icon:"] or "Choose an Icon:")
|
||||
|
||||
-- Icon picker: 10×8 grid of buttons + scroll
|
||||
local ICON_GRID_COLS = 10
|
||||
local ICON_GRID_ROWS = 8
|
||||
local ICON_BTN_SIZE = 36
|
||||
local ICON_BTN_PAD = 6
|
||||
|
||||
local iconScroll = CreateFrame("ScrollFrame", "pfEqMgrIconScroll", namePopup, "FauxScrollFrameTemplate")
|
||||
iconScroll:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -130)
|
||||
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
||||
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
||||
|
||||
-- IconDataProviderMixin owns the icon DB, dedup, and lazy load.
|
||||
-- Init on first picker open; release on hide so the cache GCs.
|
||||
local provider = nil
|
||||
-- Selection is tracked by PATH (not index) so it survives filter
|
||||
-- changes: a spell icon you picked still saves correctly even after
|
||||
-- you switch the filter to "Items" and it's no longer in the visible list.
|
||||
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
|
||||
local selectedIconPath = QUESTION_MARK
|
||||
|
||||
local function EnsureProvider()
|
||||
if not provider then
|
||||
provider = CreateAndInitFromMixin(IconDataProviderMixin,
|
||||
IconDataProviderExtraType.Equipment)
|
||||
end
|
||||
end
|
||||
|
||||
-- Anchor scrollbar to iconScroll's right edge so its position tracks
|
||||
-- the icon grid rather than the popup. -16/+16 vertical insets are the
|
||||
-- standard up/down arrow spacing for UIPanelScrollBarTemplate.
|
||||
local scrollbar = _G["pfEqMgrIconScrollScrollBar"]
|
||||
if scrollbar then
|
||||
scrollbar:ClearAllPoints()
|
||||
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
|
||||
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
|
||||
SkinScrollbar(scrollbar)
|
||||
end
|
||||
|
||||
-- Filter dropdown: "All Icons" / "Spells" / "Items" (top right of icon area).
|
||||
local filterDropdown = CreateFrame("Frame", "pfEqMgrIconFilter", namePopup, "UIDropDownMenuTemplate")
|
||||
filterDropdown:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", 0, -94)
|
||||
local currentFilter = "all"
|
||||
local function ApplyFilter(value)
|
||||
currentFilter = value
|
||||
UIDropDownMenu_SetSelectedValue(filterDropdown, value)
|
||||
if provider then
|
||||
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
|
||||
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
|
||||
else provider:SetIconTypes(nil) end
|
||||
-- Don't reset selection — selectedIconPath persists. If the
|
||||
-- selected icon isn't in the new filter, no grid entry will be
|
||||
-- highlighted but Save will still write the chosen icon.
|
||||
pfUI.equipmentmanager.RefreshIconGrid()
|
||||
end
|
||||
end
|
||||
UIDropDownMenu_Initialize(filterDropdown, function()
|
||||
local info
|
||||
info = {}; info.text = T["All Icons"] or "All Icons"; info.value = "all"
|
||||
info.func = function() ApplyFilter("all") end
|
||||
info.checked = currentFilter == "all"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
info = {}; info.text = T["Spells"] or "Spells"; info.value = "spells"
|
||||
info.func = function() ApplyFilter("spells") end
|
||||
info.checked = currentFilter == "spells"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
info = {}; info.text = T["Items"] or "Items"; info.value = "items"
|
||||
info.func = function() ApplyFilter("items") end
|
||||
info.checked = currentFilter == "items"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
end)
|
||||
UIDropDownMenu_SetWidth(120, filterDropdown)
|
||||
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
|
||||
SkinDropDown(filterDropdown)
|
||||
|
||||
local iconButtons = {}
|
||||
for r = 1, ICON_GRID_ROWS do
|
||||
for c = 1, ICON_GRID_COLS do
|
||||
local i = (r - 1) * ICON_GRID_COLS + c
|
||||
local btn = CreateFrame("Button", nil, namePopup)
|
||||
btn:SetWidth(ICON_BTN_SIZE)
|
||||
btn:SetHeight(ICON_BTN_SIZE)
|
||||
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
|
||||
CreateBackdrop(btn)
|
||||
btn.texture = btn:CreateTexture(nil, "ARTWORK")
|
||||
btn.texture:SetAllPoints(btn)
|
||||
btn.texture:SetTexCoord(.08, .92, .08, .92)
|
||||
btn.gridIndex = i
|
||||
btn:SetScript("OnClick", function()
|
||||
if this.iconIndex and provider then
|
||||
local path = provider:GetIconByIndex(this.iconIndex)
|
||||
if path then selectedIconPath = path end
|
||||
pfUI.equipmentmanager.RefreshIconGrid()
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnEnter", function()
|
||||
if not this.iconIndex or not provider then return end
|
||||
local path = provider:GetIconByIndex(this.iconIndex)
|
||||
if type(path) == "string" then
|
||||
local name = string.gsub(path, "^.-INTERFACE\\\\ICONS\\\\", "")
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText(name)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnLeave", GameTooltip_Hide)
|
||||
iconButtons[i] = btn
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.equipmentmanager.RefreshIconGrid()
|
||||
EnsureProvider()
|
||||
local numIcons = provider:GetNumIcons()
|
||||
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
|
||||
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
|
||||
local offset = FauxScrollFrame_GetOffset(iconScroll)
|
||||
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
|
||||
local listIdx = i + offset * ICON_GRID_COLS
|
||||
local btn = iconButtons[i]
|
||||
if listIdx <= numIcons then
|
||||
btn:Show()
|
||||
btn.iconIndex = listIdx
|
||||
local path = provider:GetIconByIndex(listIdx)
|
||||
btn.texture:SetTexture(path)
|
||||
if path == selectedIconPath then
|
||||
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
|
||||
else
|
||||
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
|
||||
end
|
||||
else
|
||||
btn:Hide()
|
||||
btn.iconIndex = nil
|
||||
end
|
||||
end
|
||||
-- Sync the "Currently Selected" preview from the path directly so it
|
||||
-- still shows the chosen icon when filtered out of the grid.
|
||||
namePopup.selectedPreview.tex:SetTexture(selectedIconPath)
|
||||
end
|
||||
|
||||
iconScroll:SetScript("OnVerticalScroll", function()
|
||||
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() pfUI.equipmentmanager.RefreshIconGrid() end)
|
||||
end)
|
||||
|
||||
namePopup:SetScript("OnHide", function()
|
||||
if provider then provider:Release(); provider = nil end
|
||||
end)
|
||||
|
||||
local btnPopupOK = MakeButton("pfEqMgrPopupOK", T["OK"] or "OK", namePopup, namePopup, 14, -340, 80)
|
||||
btnPopupOK:ClearAllPoints()
|
||||
btnPopupOK:SetPoint("BOTTOMLEFT", namePopup, "BOTTOMLEFT", 14, 12)
|
||||
|
||||
local btnPopupCancel = MakeButton("pfEqMgrPopupCancel", T["Cancel"] or "Cancel", namePopup, namePopup, 0, 0, 80)
|
||||
local btnPopupCancel = MakeButton("pfEqMgrPopupCancel", CANCEL, namePopup, namePopup, 0, 0, 80)
|
||||
btnPopupCancel:ClearAllPoints()
|
||||
btnPopupCancel:SetPoint("BOTTOMRIGHT", namePopup, "BOTTOMRIGHT", -14, 12)
|
||||
btnPopupCancel:SetScript("OnClick", function() namePopup:Hide() end)
|
||||
|
||||
-- Okay | Cancel, both anchored to the bottom-right corner.
|
||||
btnPopupOK:ClearAllPoints()
|
||||
btnPopupOK:SetPoint("BOTTOMRIGHT", btnPopupCancel, "BOTTOMLEFT", -6, 0)
|
||||
|
||||
btnPopupOK:SetScript("OnClick", function()
|
||||
local name = namePopup.editbox:GetText()
|
||||
if not name or name == "" then return end
|
||||
-- Strip the prefix to match ClassicAPI's persisted short-form basenames.
|
||||
local iconForSave = string.gsub(selectedIconPath, "INTERFACE\\ICONS\\", "")
|
||||
local iconForSave = string.gsub(iconPicker.GetIcon(), "INTERFACE\\ICONS\\", "")
|
||||
if pendingAction == "new" then
|
||||
C_EquipmentSet.CreateEquipmentSet(name, iconForSave)
|
||||
C_EquipmentSet.ClearIgnoredSlotsForSave()
|
||||
@@ -610,31 +431,20 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
function OpenNamePopup(action, prefillName, prefillIcon)
|
||||
pendingAction = action
|
||||
namePopup.editbox:SetText(prefillName or "")
|
||||
EnsureProvider()
|
||||
if prefillIcon then
|
||||
local short = string.gsub(prefillIcon, "INTERFACE\\ICONS\\", "")
|
||||
selectedIconPath = "INTERFACE\\ICONS\\" .. strupper(short)
|
||||
-- Stored icons come back either as a full path or a bare basename
|
||||
-- (see the row rendering in Refresh). Only prepend the prefix when
|
||||
-- there's no path separator, so a full path isn't double-prefixed.
|
||||
iconPicker.SetIcon(string.find(prefillIcon, "\\") and prefillIcon
|
||||
or ("INTERFACE\\ICONS\\" .. prefillIcon))
|
||||
else
|
||||
selectedIconPath = QUESTION_MARK
|
||||
end
|
||||
if action == "rename" then
|
||||
namePopup.title:SetText(T["Rename Set"] or "Rename Set")
|
||||
iconScroll:Hide()
|
||||
for _, b in ipairs(iconButtons) do b:Hide() end
|
||||
namePopup.iconLabel:Hide()
|
||||
namePopup.selectedLabel:Hide()
|
||||
namePopup.selectedPreview:Hide()
|
||||
filterDropdown:Hide()
|
||||
else
|
||||
namePopup.title:SetText(action == "new" and (T["Name Set"] or "Name Set") or (T["Save Set"] or "Save Set"))
|
||||
iconScroll:Show()
|
||||
namePopup.iconLabel:Show()
|
||||
namePopup.selectedLabel:Show()
|
||||
namePopup.selectedPreview:Show()
|
||||
filterDropdown:Show()
|
||||
iconPicker.SetIcon(nil)
|
||||
end
|
||||
namePopup.search:SetText("")
|
||||
-- Rename only changes the name, so hide the whole icon-picking area.
|
||||
iconPicker.SetIconAreaShown(action ~= "rename")
|
||||
namePopup:Show()
|
||||
if action ~= "rename" then pfUI.equipmentmanager.RefreshIconGrid() end
|
||||
if action ~= "rename" then iconPicker.Refresh() end
|
||||
namePopup.editbox:SetFocus()
|
||||
end
|
||||
|
||||
@@ -643,9 +453,13 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local flyout = CreateFrame("Frame", "pfEqMgrFlyout", UIParent)
|
||||
-- The name/icon popup is sidecar-only, so it closes with the sidecar.
|
||||
-- Popout arrows and the flyout are reconciled by UpdatePopouts: by
|
||||
-- default they follow the sidecar, but with the "Always Show Equipment
|
||||
-- Slot Flyouts" option they stay while the paperdoll is open.
|
||||
frame:SetScript("OnHide", function()
|
||||
for _, b in ipairs(popoutButtons) do b:Hide() end
|
||||
flyout:Hide()
|
||||
namePopup:Hide()
|
||||
UpdatePopouts()
|
||||
end)
|
||||
flyout:SetFrameStrata("DIALOG")
|
||||
flyout:Hide()
|
||||
@@ -676,7 +490,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end
|
||||
end
|
||||
ClearCursor()
|
||||
UIErrorsFrame:AddMessage(EQUIPMENT_MANAGER_BAGS_FULL or "Your bags are full.", 1, .1, .1, 1)
|
||||
UIErrorsFrame:AddMessage(ERR_EQUIPMENT_MANAGER_BAGS_FULL, 1, .1, .1, 1)
|
||||
end
|
||||
|
||||
local function MakeFlyoutButton(i)
|
||||
@@ -692,11 +506,11 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
b:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
if this.specialAction == "placeInBags" then
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_PLACE_IN_BAGS or "Place in Bags", 1, 1, 1)
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_PLACE_IN_BAGS, 1, 1, 1)
|
||||
elseif this.specialAction == "ignore" then
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_IGNORE_SLOT or "Ignore this slot", 1, 1, 1)
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_IGNORE_SLOT, 1, 1, 1)
|
||||
elseif this.specialAction == "unignore" then
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_UNIGNORE_SLOT or "Stop ignoring this slot", 1, 1, 1)
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_UNIGNORE_SLOT, 1, 1, 1)
|
||||
elseif this.bag then
|
||||
GameTooltip:SetBagItem(this.bag, this.slot)
|
||||
elseif this.invSlot then
|
||||
@@ -704,7 +518,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
b:SetScript("OnLeave", GameTooltip_Hide)
|
||||
b:SetScript("OnClick", function()
|
||||
if this.specialAction == "placeInBags" then
|
||||
UnequipToBags(flyout.targetInvSlot)
|
||||
@@ -809,7 +623,6 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
if num == 0 then
|
||||
flyout:Hide()
|
||||
UIErrorsFrame:AddMessage(T["No matching items in bags"] or "No matching items in bags", 1, 1, 0, 1)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -938,15 +751,26 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end
|
||||
end
|
||||
|
||||
-- Tie popout visibility to the EM sidecar. They appear when the
|
||||
-- sidecar opens, hide when it closes, and the flyout closes too.
|
||||
HookScript(frame, "OnShow", function()
|
||||
for _, b in ipairs(popoutButtons) do b:Show() end
|
||||
end)
|
||||
HookScript(frame, "OnHide", function()
|
||||
for _, b in ipairs(popoutButtons) do b:Hide() end
|
||||
if flyout then flyout:Hide() 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
|
||||
@@ -1048,11 +872,10 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
events:RegisterEvent("EQUIPMENT_SETS_CHANGED")
|
||||
events:RegisterEvent("EQUIPMENT_SWAP_FINISHED")
|
||||
events:RegisterEvent("EQUIPMENT_SWAP_PENDING")
|
||||
events:RegisterEvent("BAG_UPDATE_DELAYED") -- ClassicAPI debounced; ~4x fewer refreshes than BAG_UPDATE
|
||||
events:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||||
events:RegisterEvent("BAG_UPDATE_DELAYED")
|
||||
events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
||||
events:SetScript("OnEvent", function()
|
||||
if not frame:IsShown() then return end
|
||||
if event == "UNIT_INVENTORY_CHANGED" and arg1 ~= "player" then return end
|
||||
pfUI.equipmentmanager.Refresh()
|
||||
end)
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ pfUI:RegisterModule("farmmode", function ()
|
||||
Minimap_ZoomOut()
|
||||
end
|
||||
|
||||
_G.SLASH_PFFARMMAP1, _G.SLASH_PFFARMMAP2 = "/farm", "/farmmode"
|
||||
_G.SlashCmdList.PFFARMMAP = ToggleFarmMode
|
||||
pfUI.api.RegisterSlashCommand("PFFARMMAP", { "/farm", "/farmmode" }, ToggleFarmMode, true)
|
||||
|
||||
pfUI.farmmap = CreateFrame("Minimap", "pfFarmMap", UIParent)
|
||||
pfUI.farmmap:Hide()
|
||||
@@ -42,8 +41,9 @@ pfUI:RegisterModule("farmmode", function ()
|
||||
pfUI.farmmap:RegisterForDrag("LeftButton")
|
||||
pfUI.farmmap:SetScript("OnMouseWheel", function()
|
||||
if IsControlKeyDown() then
|
||||
this:SetWidth(this:GetWidth() + (arg1 > 0 and 10 or -10))
|
||||
this:SetHeight(this:GetHeight() + (arg1 > 0 and 10 or -10))
|
||||
local adjust = (arg1 > 0 and 10 or -10)
|
||||
local width, height = this:GetSize()
|
||||
this:SetSize(width + adjust, height + adjust)
|
||||
Minimap_ZoomIn()
|
||||
Minimap_ZoomOut()
|
||||
elseif IsShiftKeyDown() then
|
||||
@@ -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)
|
||||
|
||||
+6
-25
@@ -1,29 +1,10 @@
|
||||
pfUI:RegisterModule("feigndeath", function ()
|
||||
local cache = { }
|
||||
local scanner = libtipscan:GetScanner("feigndeath")
|
||||
local healthbar = scanner:GetChildren()
|
||||
|
||||
local cache_update = CreateFrame("Frame")
|
||||
cache_update:RegisterEvent("UNIT_HEALTH")
|
||||
cache_update:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
cache_update:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_TARGET_CHANGED" and UnitIsDead("target") then
|
||||
scanner:SetUnit("target")
|
||||
cache[UnitName("target")] = healthbar:GetValue()
|
||||
elseif event == "UNIT_HEALTH" and UnitIsDead(arg1) and UnitName(arg1) then
|
||||
scanner:SetUnit(arg1)
|
||||
cache[UnitName(arg1)] = healthbar:GetValue()
|
||||
elseif event == "UNIT_HEALTH" and UnitName(arg1) then
|
||||
cache[UnitName(arg1)] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
local oldUnitHealth = UnitHealth
|
||||
function UnitHealth(arg)
|
||||
if UnitIsDead(arg) and cache[UnitName(arg)] then
|
||||
return cache[UnitName(arg)]
|
||||
else
|
||||
return oldUnitHealth(arg)
|
||||
local oldUnitHealth = _G.UnitHealth
|
||||
_G.UnitHealth = function(unit)
|
||||
if UnitIsFeignDeath(unit) then
|
||||
local hp = GetUnitField(unit, "health")
|
||||
if hp and hp > 0 then return hp end
|
||||
end
|
||||
return oldUnitHealth(unit)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -46,6 +46,11 @@ pfUI:RegisterModule("firstrun", function ()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if not self.completed then
|
||||
self.completed = true
|
||||
pfUI.events:TriggerEvent("firstrun:complete")
|
||||
end
|
||||
end
|
||||
|
||||
-- main function to create wizard windows
|
||||
@@ -258,7 +263,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
|
||||
|
||||
@@ -278,7 +283,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
|
||||
@@ -300,7 +305,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
|
||||
|
||||
+12
-15
@@ -2,20 +2,22 @@ pfUI:RegisterModule("focus", function ()
|
||||
-- do not go further on disabled UFs
|
||||
if C.unitframes.disable == "1" then return end
|
||||
|
||||
pfUI.uf.focus = pfUI.uf:CreateUnitFrame("Focus", nil, C.unitframes.focus, .2)
|
||||
pfUI.uf.focus = pfUI.uf:CreateUnitFrame("Focus", nil, C.unitframes.focus)
|
||||
pfUI.uf.focus:UpdateFrameSize()
|
||||
pfUI.uf.focus:SetPoint("BOTTOMLEFT", UIParent, "BOTTOM", 220, 220)
|
||||
UpdateMovable(pfUI.uf.focus)
|
||||
pfUI.uf.focus:Hide()
|
||||
|
||||
pfUI.uf.focustarget = pfUI.uf:CreateUnitFrame("FocusTarget", nil, C.unitframes.focustarget, .2)
|
||||
pfUI.uf.focustarget = pfUI.uf:CreateUnitFrame("FocusTarget", nil, C.unitframes.focustarget)
|
||||
pfUI.uf.focustarget:UpdateFrameSize()
|
||||
pfUI.uf.focustarget:SetPoint("BOTTOMLEFT", pfUI.uf.focus, "TOP", 0, 10)
|
||||
UpdateMovable(pfUI.uf.focustarget)
|
||||
pfUI.uf.focustarget:Hide()
|
||||
|
||||
-- PLAYER_FOCUS_CHANGED drives immediate refresh on focus assign / clear.
|
||||
-- The frame's 0.2s tick keeps health/power/aura data fresh between events.
|
||||
-- Between events, ClassicAPI fires UNIT_* (health/mana/aura/...) with
|
||||
-- arg1 == "focus" and arg1 == "focustarget", so both frames update
|
||||
-- event-driven like target and need no polling tick.
|
||||
local refresher = CreateFrame("Frame")
|
||||
refresher:RegisterEvent("PLAYER_FOCUS_CHANGED")
|
||||
refresher:SetScript("OnEvent", function()
|
||||
@@ -30,8 +32,7 @@ end)
|
||||
-- /focusname is pfUI-specific because the engine has no name→GUID
|
||||
-- lookup for off-screen units — we resolve via a short target-swap.
|
||||
|
||||
SLASH_PFFOCUSNAME1, SLASH_PFFOCUSNAME2 = '/focusname', '/pffocusname'
|
||||
function SlashCmdList.PFFOCUSNAME(msg)
|
||||
pfUI.api.RegisterSlashCommand("PFFOCUSNAME", { '/focusname', '/pffocusname' }, function(msg)
|
||||
if msg == "" then return end
|
||||
|
||||
local prevGUID = UnitGUID("target")
|
||||
@@ -50,10 +51,8 @@ function SlashCmdList.PFFOCUSNAME(msg)
|
||||
FocusUnit("target")
|
||||
end
|
||||
|
||||
local restore = CreateFrame("Frame")
|
||||
restore:SetScript("OnUpdate", function()
|
||||
RunNextFrame(function()
|
||||
UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
|
||||
restore:SetScript("OnUpdate", nil)
|
||||
end)
|
||||
|
||||
if prevGUID and prevGUID ~= "0x0000000000000000" then
|
||||
@@ -63,10 +62,9 @@ function SlashCmdList.PFFOCUSNAME(msg)
|
||||
else
|
||||
ClearTarget()
|
||||
end
|
||||
end
|
||||
end, true)
|
||||
|
||||
SLASH_PFCASTFOCUS1, SLASH_PFCASTFOCUS2 = '/castfocus', '/pfcastfocus'
|
||||
function SlashCmdList.PFCASTFOCUS(msg)
|
||||
pfUI.api.RegisterSlashCommand("PFCASTFOCUS", { '/castfocus', '/pfcastfocus' }, function(msg)
|
||||
local focusGUID = UnitGUID("focus")
|
||||
if not focusGUID or focusGUID == "0x0000000000000000" then
|
||||
UIErrorsFrame:AddMessage(SPELL_FAILED_BAD_TARGETS, 1, 0, 0)
|
||||
@@ -107,10 +105,9 @@ function SlashCmdList.PFCASTFOCUS(msg)
|
||||
else
|
||||
TargetLastTarget()
|
||||
end
|
||||
end
|
||||
end, true)
|
||||
|
||||
SLASH_PFSWAPFOCUS1, SLASH_PFSWAPFOCUS2 = '/swapfocus', '/pfswapfocus'
|
||||
function SlashCmdList.PFSWAPFOCUS(msg)
|
||||
pfUI.api.RegisterSlashCommand("PFSWAPFOCUS", { '/swapfocus', '/pfswapfocus' }, function(msg)
|
||||
local targetGUID = UnitGUID("target")
|
||||
local oldFocusGUID = UnitGUID("focus")
|
||||
|
||||
@@ -120,4 +117,4 @@ function SlashCmdList.PFSWAPFOCUS(msg)
|
||||
TargetUnit(oldFocusGUID)
|
||||
end
|
||||
end
|
||||
end
|
||||
end, true)
|
||||
|
||||
@@ -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)
|
||||
+75
-31
@@ -212,6 +212,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
if not this:GetParent():IsShown() then
|
||||
category[config] = r .. "," .. g .. "," .. b .. "," .. a
|
||||
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
|
||||
pfUI.events:TriggerEvent("config:changed", category, config)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -278,6 +279,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
if this:GetText() ~= this:GetParent().category[this:GetParent().config] then
|
||||
this:GetParent().category[this:GetParent().config] = this:GetText()
|
||||
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
|
||||
pfUI.events:TriggerEvent("config:changed", category, config)
|
||||
end
|
||||
this:SetTextColor(.2,1,.8,1)
|
||||
else
|
||||
@@ -328,6 +330,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
end
|
||||
|
||||
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
|
||||
pfUI.events:TriggerEvent("config:changed", category, config)
|
||||
end)
|
||||
|
||||
if category[config] == "1" then frame.input:SetChecked() end
|
||||
@@ -360,6 +363,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
if category and category[config] ~= value then
|
||||
category[config] = value
|
||||
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
|
||||
pfUI.events:TriggerEvent("config:changed", category, config)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -394,6 +398,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
end
|
||||
category[config] = newconf
|
||||
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
|
||||
pfUI.events:TriggerEvent("config:changed", category, config)
|
||||
frame.input:UpdateMenu()
|
||||
end)
|
||||
|
||||
@@ -409,6 +414,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateQuestionDialog(T["New entry:"], function()
|
||||
category[config] = category[config] .. "#" .. this:GetParent().input:GetText()
|
||||
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
|
||||
pfUI.events:TriggerEvent("config:changed", category, config)
|
||||
frame.input:UpdateMenu()
|
||||
end, false, true)
|
||||
end)
|
||||
@@ -906,6 +912,11 @@ pfUI:RegisterModule("gui", function ()
|
||||
"1:" .. T["1 Decimal (2.1)"],
|
||||
"2:" .. T["2 Decimals (2.14)"],
|
||||
},
|
||||
["castbaralign"] = {
|
||||
"LEFT:" .. T["Left"],
|
||||
"CENTER:" .. T["Center"],
|
||||
"RIGHT:" .. T["Right"],
|
||||
},
|
||||
["orientation"] = {
|
||||
"HORIZONTAL:" .. T["Horizontal"],
|
||||
"VERTICAL:" .. T["Vertical"],
|
||||
@@ -950,7 +961,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
"16:" .. T["Very Slow"],
|
||||
},
|
||||
["uf_rangecheck_mode"] = {
|
||||
"vanilla:" .. T["Vanilla (Spellbook)"],
|
||||
"vanilla:" .. T["ClassicAPI (UnitInRange)"],
|
||||
"unitxp:" .. T["UnitXP (Precise)"],
|
||||
},
|
||||
["uf_raidlayout"] = {
|
||||
@@ -1057,6 +1068,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
"unitrev:" .. T["Unit String (Reverse)"],
|
||||
"name:" .. T["Name"],
|
||||
"nameshort:" .. T["Name (Short)"],
|
||||
"ownername:" .. T["Owner Name"],
|
||||
"level:" .. T["Level"],
|
||||
"class:" .. T["Class"],
|
||||
"namehealth:" .. T["Name | Health Missing"],
|
||||
@@ -2108,10 +2120,9 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(nil, T["Enable 40y-Range Check"], C.unitframes, "rangecheck", "checkbox", nil, nil, nil, nil)
|
||||
CreateConfig(nil, T["Range Check Mode"], C.unitframes, "rangecheck_mode", "dropdown", pfUI.gui.dropdowns.uf_rangecheck_mode, nil, nil, nil)
|
||||
CreateConfig(nil, T["UnitXP Range Threshold (yards)"], C.unitframes, "rangecheck_distance", nil, nil, nil, nil, nil)
|
||||
CreateConfig(nil, T["Range Check Interval"], C.unitframes, "rangechecki", "dropdown", pfUI.gui.dropdowns.uf_rangecheckinterval, nil, nil, nil)
|
||||
CreateConfig(nil, T["Use Raid Frames To Display Group Members"], C.unitframes, "raidforgroup", "checkbox")
|
||||
CreateConfig(nil, T["Always Show Self In Raid Frames"], C.unitframes, "selfinraid", "checkbox")
|
||||
CreateConfig(nil, T["Show Self In Raid Frames When Solo"], C.unitframes, "selfinraid", "checkbox")
|
||||
CreateConfig(nil, T["Show Self In Group Frames"], C.unitframes, "selfingroup", "checkbox")
|
||||
CreateConfig(nil, T["Use Raid Frames To Display Group Members"], C.unitframes, "raidforgroup", "checkbox")
|
||||
CreateConfig(nil, T["Hide Group Frames While In Raid"], C.unitframes.group, "hide_in_raid", "checkbox")
|
||||
CreateConfig(nil, T["Max Amount Of Raid Frames"], C.unitframes, "maxraid", "dropdown", pfUI.gui.dropdowns.maxraid)
|
||||
|
||||
@@ -2132,6 +2143,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
|
||||
CreateConfig(nil, T["Druid Settings"], nil, nil, "header")
|
||||
CreateConfig(nil, T["Show Druid Mana Bar"], C.unitframes, "druidmanabar", "checkbox", nil, nil, nil, nil)
|
||||
CreateConfig(nil, T["Show Druid Mana Bar Text"], C.unitframes, "druidmanatext", "checkbox", nil, nil, nil, nil)
|
||||
CreateConfig(nil, T["Druid Mana Bar Height"], C.unitframes, "druidmanaheight", nil, nil, nil, nil, nil)
|
||||
CreateConfig(nil, T["Druid Mana Bar Width (-1 = auto)"], C.unitframes, "druidmanawidth", nil, nil, nil, nil, nil)
|
||||
CreateConfig(nil, T["Druid Mana Bar X-Offset"], C.unitframes, "druidmanaoffx", nil, nil, nil, nil, nil)
|
||||
@@ -2174,6 +2186,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
[10] = { "grouptarget", T["Group-Target"]},
|
||||
[11] = { "grouppet", T["Group-Pet"] },
|
||||
[12] = { "raid", T["Raid"] },
|
||||
[13] = { "raidpet", T["Raid-Pet"] },
|
||||
}
|
||||
|
||||
CreateGUIEntry(T["Unit Frames"], T["Click Casting"], function()
|
||||
@@ -2223,10 +2236,10 @@ pfUI:RegisterModule("gui", function ()
|
||||
U.ptarget = U["pettarget"]
|
||||
U.grouptarget = U["group"]
|
||||
U.grouppet = U["group"]
|
||||
U.raidpet = U["raid"]
|
||||
|
||||
-- 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)
|
||||
@@ -2267,6 +2280,13 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(U["raid"], T["Raid Padding"], C.unitframes[c], "raidpadding")
|
||||
CreateConfig(U["raid"], T["Raid Layout"], C.unitframes[c], "raidlayout", "dropdown", pfUI.gui.dropdowns.uf_raidlayout)
|
||||
CreateConfig(U["raid"], T["Raid Fill Direction"], C.unitframes[c], "raidfill", "dropdown", pfUI.gui.dropdowns.orientation)
|
||||
CreateConfig(U["raid"], T["Collapse Empty Slots"], C.unitframes[c], "collapse", "checkbox")
|
||||
elseif c == "raidpet" then
|
||||
CreateConfig(U[c], T["Layout"], nil, nil, "header")
|
||||
CreateConfig(U["raid"], T["Raid Padding"], C.unitframes[c], "raidpadding")
|
||||
CreateConfig(U["raid"], T["Raid Layout"], C.unitframes[c], "raidlayout", "dropdown", pfUI.gui.dropdowns.uf_raidlayout)
|
||||
CreateConfig(U["raid"], T["Raid Fill Direction"], C.unitframes[c], "raidfill", "dropdown", pfUI.gui.dropdowns.orientation)
|
||||
CreateConfig(U["raid"], T["Collapse Empty Slots"], C.unitframes[c], "collapse", "checkbox")
|
||||
end
|
||||
|
||||
CreateConfig(U[c], T["Healthbar"], nil, nil, "header")
|
||||
@@ -2380,11 +2400,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
|
||||
if c == "player" then
|
||||
CreateConfig(nil, T["Player SP/Haste Display"], nil, nil, "header")
|
||||
CreateConfig(nil, T["Haste Display"], C.unitframes[c], "display_haste", "dropdown", {
|
||||
"0:"..T["None"],
|
||||
"1:"..T["Haste (cast speed increase)"],
|
||||
"2:"..T["Effective Haste (Haste * cast time reduction)"], -- Only affects mages/warlocks I believe
|
||||
})
|
||||
CreateConfig(nil, T["Haste Display"], C.unitframes[c], "display_haste", "checkbox")
|
||||
CreateConfig(nil, T["Haste Display Color"], C.unitframes[c], "display_haste_color", "color")
|
||||
CreateConfig(nil, T["Display Spell Power"], C.unitframes[c], "display_spellpower", "checkbox")
|
||||
CreateConfig(nil, T["Use Custom Spell Power Color (unchecked = biggest school color)"], C.unitframes[c], "display_sp_color_override", "checkbox")
|
||||
@@ -2395,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()
|
||||
@@ -2406,6 +2423,8 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(nil, T["Enable Item Quality Color For Equipment Only"], C.appearance.bags, "borderonlygear", "checkbox")
|
||||
CreateConfig(nil, T["Highlight Unusable Items"], C.appearance.bags, "unusable", "checkbox")
|
||||
CreateConfig(nil, T["Unusable Item Color"], C.appearance.bags, "unusable_color", "color")
|
||||
CreateConfig(nil, T["Highlight New Items"], C.appearance.bags, "newitem", "checkbox")
|
||||
CreateConfig(nil, T["New Item Color"], C.appearance.bags, "newitem_color", "color")
|
||||
CreateConfig(nil, T["Enable Movable Bags"], C.appearance.bags, "movable", "checkbox")
|
||||
CreateConfig(nil, T["Anchor Bags Above Chat"], C.appearance.bags, "abovechat", "checkbox")
|
||||
CreateConfig(nil, T["Hide Chat When Bags Are Opened"], C.appearance.bags, "hidechat", "checkbox")
|
||||
@@ -2416,6 +2435,8 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(nil, T["Auto Sell Grey Items"], C.global, "autosell", "checkbox")
|
||||
CreateConfig(nil, T["Auto Repair Items"], C.global, "autorepair", "checkbox")
|
||||
CreateConfig(nil, T["Auto Sort When Opening Bags"], C.appearance.bags, "autoSortOnOpen", "checkbox")
|
||||
CreateConfig(nil, T["Reverse Sort Direction (Last Bag First)"], C.appearance.bags, "sortreverse", "checkbox")
|
||||
CreateConfig(nil, T["Reverse Sort Priority (Hearthstone Last)"], C.appearance.bags, "sortprioreverse", "checkbox")
|
||||
end)
|
||||
|
||||
CreateGUIEntry(T["Loot"], nil, function()
|
||||
@@ -2566,6 +2587,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(U["bars"], T["Switch Pages On Shift Key Press"], C.bars, "pagemastershift", "checkbox")
|
||||
CreateConfig(U["bars"], T["Switch Pages On Ctrl Key Press"], C.bars, "pagemasterctrl", "checkbox")
|
||||
CreateConfig(U["bars"], T["Switch Pages On Druid Stealth"], C.bars, "druidstealth", "checkbox")
|
||||
CreateConfig(U["bars"], T["Switch Pages On Priest Shadowform"], C.bars, "priestshadow", "checkbox")
|
||||
CreateConfig(nil, T["Range Based Hunter Paging"], C.bars, "hunterbar", "checkbox", nil, nil, nil, nil)
|
||||
end)
|
||||
|
||||
@@ -2741,71 +2763,92 @@ 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["Custom Transparency"], C.tooltip, "alpha")
|
||||
CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
|
||||
CreateConfig(nil, T["Compare Item Base Stats"], C.tooltip.compare, "basestats", "checkbox")
|
||||
|
||||
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"], nil, function()
|
||||
CreateGUIEntry(T["Castbar"], T["General"], function()
|
||||
CreateConfig(nil, T["Use Unit Fonts"], C.castbar, "use_unitfonts", "checkbox")
|
||||
CreateConfig(nil, T["Casting Color"], C.appearance.castbar, "castbarcolor", "color")
|
||||
CreateConfig(nil, T["Channeling Color"], C.appearance.castbar, "channelcolor", "color")
|
||||
CreateConfig(nil, T["Castbar Texture"], C.appearance.castbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
|
||||
CreateConfig(nil, T["Disable Blizzard Castbar"], C.castbar.player, "hide_blizz", "checkbox")
|
||||
end)
|
||||
|
||||
CreateConfig(nil, T["Player Castbar"], nil, nil, "header")
|
||||
CreateGUIEntry(T["Castbar"], T["Player"], function()
|
||||
CreateConfig(nil, T["Disable Player Castbar"], C.castbar.player, "hide_pfui", "checkbox")
|
||||
CreateConfig(nil, T["Castbar Width"], C.castbar.player, "width")
|
||||
CreateConfig(nil, T["Castbar Height"], C.castbar.player, "height")
|
||||
CreateConfig(nil, T["Show Spell Icon"], C.castbar.player, "showicon", "checkbox")
|
||||
CreateConfig(nil, T["Show Spell Name"], C.castbar.player, "showname", "checkbox")
|
||||
CreateConfig(nil, T["Spell Name Alignment"], C.castbar.player, "namealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
|
||||
CreateConfig(nil, T["Spell Name X Offset"], C.castbar.player, "txtleftoffx")
|
||||
CreateConfig(nil, T["Spell Name Y Offset"], C.castbar.player, "txtleftoffy")
|
||||
CreateConfig(nil, T["Show Timer"], C.castbar.player, "showtimer", "checkbox")
|
||||
CreateConfig(nil, T["Left Text X Offset"], C.castbar.player, "txtleftoffx")
|
||||
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.player, "txtleftoffy")
|
||||
CreateConfig(nil, T["Timer Alignment"], C.castbar.player, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
|
||||
CreateConfig(nil, T["Timer X Offset"], C.castbar.player, "txtrightoffx")
|
||||
CreateConfig(nil, T["Timer Y Offset"], C.castbar.player, "txtrightoffy")
|
||||
CreateConfig(nil, T["Show Lag"], C.castbar.player, "showlag", "checkbox")
|
||||
CreateConfig(nil, T["Show Rank"], C.castbar.player, "showrank", "checkbox")
|
||||
CreateConfig(nil, T["Right Text X Offset"], C.castbar.player, "txtrightoffx")
|
||||
CreateConfig(nil, T["Right Text Y Offset"], C.castbar.player, "txtrightoffy")
|
||||
CreateConfig(nil, T["Merge Tradeskill Casts"], C.castbar.player, "mergetradeskill", "checkbox")
|
||||
end)
|
||||
|
||||
CreateConfig(nil, T["Target Castbar"], nil, nil, "header")
|
||||
CreateGUIEntry(T["Castbar"], T["Target"], function()
|
||||
CreateConfig(nil, T["Disable Target Castbar"], C.castbar.target, "hide_pfui", "checkbox")
|
||||
CreateConfig(nil, T["Castbar Width"], C.castbar.target, "width")
|
||||
CreateConfig(nil, T["Castbar Height"], C.castbar.target, "height")
|
||||
CreateConfig(nil, T["Show Spell Icon"], C.castbar.target, "showicon", "checkbox")
|
||||
CreateConfig(nil, T["Show Spell Name"], C.castbar.target, "showname", "checkbox")
|
||||
CreateConfig(nil, T["Spell Name Alignment"], C.castbar.target, "namealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
|
||||
CreateConfig(nil, T["Spell Name X Offset"], C.castbar.target, "txtleftoffx")
|
||||
CreateConfig(nil, T["Spell Name Y Offset"], C.castbar.target, "txtleftoffy")
|
||||
CreateConfig(nil, T["Show Timer"], C.castbar.target, "showtimer", "checkbox")
|
||||
CreateConfig(nil, T["Left Text X Offset"], C.castbar.target, "txtleftoffx")
|
||||
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.target, "txtleftoffy")
|
||||
CreateConfig(nil, T["Timer Alignment"], C.castbar.target, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
|
||||
CreateConfig(nil, T["Timer X Offset"], C.castbar.target, "txtrightoffx")
|
||||
CreateConfig(nil, T["Timer Y Offset"], C.castbar.target, "txtrightoffy")
|
||||
CreateConfig(nil, T["Show Lag"], C.castbar.target, "showlag", "checkbox")
|
||||
CreateConfig(nil, T["Show Rank"], C.castbar.target, "showrank", "checkbox")
|
||||
CreateConfig(nil, T["Right Text X Offset"], C.castbar.target, "txtrightoffx")
|
||||
CreateConfig(nil, T["Right Text Y Offset"], C.castbar.target, "txtrightoffy")
|
||||
end)
|
||||
|
||||
CreateConfig(nil, T["Focus Castbar"], nil, nil, "header")
|
||||
CreateGUIEntry(T["Castbar"], T["Focus"], function()
|
||||
CreateConfig(nil, T["Disable Focus Castbar"], C.castbar.focus, "hide_pfui", "checkbox")
|
||||
CreateConfig(nil, T["Castbar Width"], C.castbar.focus, "width")
|
||||
CreateConfig(nil, T["Castbar Height"], C.castbar.focus, "height")
|
||||
CreateConfig(nil, T["Show Spell Icon"], C.castbar.focus, "showicon", "checkbox")
|
||||
CreateConfig(nil, T["Show Spell Name"], C.castbar.focus, "showname", "checkbox")
|
||||
CreateConfig(nil, T["Spell Name Alignment"], C.castbar.focus, "namealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
|
||||
CreateConfig(nil, T["Spell Name X Offset"], C.castbar.focus, "txtleftoffx")
|
||||
CreateConfig(nil, T["Spell Name Y Offset"], C.castbar.focus, "txtleftoffy")
|
||||
CreateConfig(nil, T["Show Timer"], C.castbar.focus, "showtimer", "checkbox")
|
||||
CreateConfig(nil, T["Left Text X Offset"], C.castbar.focus, "txtleftoffx")
|
||||
CreateConfig(nil, T["Left Text Y Offset"], C.castbar.focus, "txtleftoffy")
|
||||
CreateConfig(nil, T["Timer Alignment"], C.castbar.focus, "timealign", "dropdown", pfUI.gui.dropdowns.castbaralign)
|
||||
CreateConfig(nil, T["Timer X Offset"], C.castbar.focus, "txtrightoffx")
|
||||
CreateConfig(nil, T["Timer Y Offset"], C.castbar.focus, "txtrightoffy")
|
||||
CreateConfig(nil, T["Show Lag"], C.castbar.focus, "showlag", "checkbox")
|
||||
CreateConfig(nil, T["Show Rank"], C.castbar.focus, "showrank", "checkbox")
|
||||
CreateConfig(nil, T["Right Text X Offset"], C.castbar.focus, "txtrightoffx")
|
||||
CreateConfig(nil, T["Right Text Y Offset"], C.castbar.focus, "txtrightoffy")
|
||||
end)
|
||||
|
||||
CreateGUIEntry(T["Chat"], nil, function()
|
||||
@@ -2895,10 +2938,10 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(U["nameplates"], T["Enable Debuffs"], C.nameplates, "showdebuffs", "checkbox")
|
||||
CreateConfig(U["nameplates"], T["Show Debuffs on Hostile"], C.nameplates, "showdebuffs_hostile", "checkbox")
|
||||
CreateConfig(U["nameplates"], T["Show Debuffs on Friendly"], C.nameplates, "showdebuffs_friendly", "checkbox")
|
||||
CreateConfig(U["nameplates"], T["Only Show Your Debuffs"], C.nameplates, "owndebuffs", "checkbox")
|
||||
CreateConfig(U["nameplates"], T["Debuff Position"], C.nameplates.debuffs, "position", "dropdown", pfUI.gui.dropdowns.debuffposition)
|
||||
CreateConfig(U["nameplates"], T["Debuff Icon Offset"], C.nameplates, "debuffoffset")
|
||||
CreateConfig(U["nameplates"], T["Debuff Icon Size"], C.nameplates, "debuffsize")
|
||||
CreateConfig(U["nameplates"], T["Estimate Debuffs"], C.nameplates, "guessdebuffs", "checkbox")
|
||||
CreateConfig(U["nameplates"], T["Show Debuff Stacks"], C.nameplates.debuffs, "showstacks", "checkbox")
|
||||
CreateConfig(U["nameplates"], T["Enable Debuff Timers"], C.nameplates, "debufftimers", "checkbox")
|
||||
CreateConfig(U["nameplates"], T["Show Timer Text"], C.nameplates, "debufftext", "checkbox")
|
||||
@@ -2956,6 +2999,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")
|
||||
|
||||
+34
-9
@@ -1,10 +1,8 @@
|
||||
pfUI:RegisterModule("hunterbar", function ()
|
||||
local _,class = UnitClass("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)
|
||||
+15
-30
@@ -2,26 +2,15 @@
|
||||
-- Announces Innervate casts via raid/party/battleground chat
|
||||
-- Registers AURA_CAST events directly - zero polling, pure event-driven
|
||||
|
||||
pfUI:RegisterNewModule("innervatecall", "Innervate Callout", "DRUID")
|
||||
pfUI:RegisterModule("innervatecall", function ()
|
||||
-- Requires Nampower for AURA_CAST events
|
||||
if not GetNampowerVersion then return end
|
||||
|
||||
-- Only load for druids
|
||||
local _, playerClass = UnitClass("player")
|
||||
if playerClass ~= "DRUID" then return end
|
||||
if UnitClassBase("player") ~= "DRUID" then return end
|
||||
|
||||
local INNERVATE_SPELLID = 29166
|
||||
|
||||
-- Cache player GUID
|
||||
local playerGuid = nil
|
||||
local function GetPlayerGuid()
|
||||
if not playerGuid and UnitGUID then
|
||||
playerGuid = UnitGUID("player")
|
||||
end
|
||||
return playerGuid
|
||||
end
|
||||
|
||||
-- GUID → name resolution for the target. UnitTokenFromGUID walks the
|
||||
-- engine's known unit tokens (player, party, raid, target, ...) and
|
||||
-- returns the first one currently bound to the GUID, so we don't have
|
||||
@@ -39,11 +28,11 @@ pfUI:RegisterModule("innervatecall", function ()
|
||||
return "BATTLEGROUND"
|
||||
end
|
||||
|
||||
if GetNumRaidMembers() > 0 then
|
||||
if IsInRaid() then
|
||||
return "RAID"
|
||||
end
|
||||
|
||||
if GetNumPartyMembers() > 0 then
|
||||
if IsInGroup() then
|
||||
return "PARTY"
|
||||
end
|
||||
|
||||
@@ -75,7 +64,7 @@ pfUI:RegisterModule("innervatecall", function ()
|
||||
if spellId ~= INNERVATE_SPELLID then return end
|
||||
|
||||
-- Only announce our own casts
|
||||
if casterGuid ~= GetPlayerGuid() then return end
|
||||
if not IsPlayerGuid(casterGuid) then return end
|
||||
|
||||
-- Resolve target name from GUID
|
||||
local targetName = ResolveTargetName(targetGuid) or "Unknown"
|
||||
@@ -86,24 +75,20 @@ 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
|
||||
|
||||
local readyAt = GetTime() + cdRemaining
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if GetTime() >= readyAt then
|
||||
frame:SetScript("OnUpdate", nil)
|
||||
local ch = GetAnnounceChannel()
|
||||
if ch then
|
||||
SendChatMessage(">> Innervate is ready <<", ch)
|
||||
end
|
||||
C_Timer.After(cdRemaining, function()
|
||||
local ch = GetAnnounceChannel()
|
||||
if ch then
|
||||
SendChatMessage(">> Innervate is ready <<", ch)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+8
-18
@@ -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
|
||||
@@ -236,7 +226,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
if (candidate) then
|
||||
index_to_name[i] = candidate
|
||||
name_to_index[candidate] = i
|
||||
randoms[table.getn(randoms)+1]=i
|
||||
table.insert(randoms, i)
|
||||
if candidate == pfUI.loot.me then
|
||||
pfUI.loot.my_index = i
|
||||
end
|
||||
@@ -323,7 +313,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
end
|
||||
|
||||
function pfUI.loot:InitGroupDropDown()
|
||||
local inRaid = UnitInRaid("player")
|
||||
local inRaid = IsInRaid()
|
||||
if UIDROPDOWNMENU_MENU_LEVEL == 1 then
|
||||
if ( inRaid ) then
|
||||
pfUI.loot:BuildRaidMenu(UIDROPDOWNMENU_MENU_LEVEL)
|
||||
@@ -693,7 +683,7 @@ pfUI:RegisterModule("loot", function ()
|
||||
else -- not an eligible candidate for that item
|
||||
pfUI.loot.rollers[who] = {roll=tonumber(roll),value="disabled"}
|
||||
end
|
||||
pfUI.loot.rollers_sorted[table.getn(pfUI.loot.rollers_sorted)+1]={who=who,roll=tonumber(roll),value=pfUI.loot.rollers[who].value}
|
||||
table.insert(pfUI.loot.rollers_sorted, {who=who,roll=tonumber(roll),value=pfUI.loot.rollers[who].value})
|
||||
end
|
||||
end
|
||||
table.sort(pfUI.loot.rollers_sorted,function(a,b)
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
pfUI:RegisterModule("loothistory", function ()
|
||||
local rawborder, border = GetBorderSize()
|
||||
|
||||
-- Layout
|
||||
local ITEM_H, PLAYER_H = 24, 18
|
||||
local ITEM_W, PLAYER_W = 350, 330
|
||||
|
||||
-- rollType constants returned by C_LootHistory.GetPlayerInfo (0/1/2; vanilla
|
||||
-- has no disenchant roll).
|
||||
local ROLL_PASS, ROLL_NEED, ROLL_GREED = 0, 1, 2
|
||||
local ROLL_TEX = {
|
||||
[ROLL_NEED] = "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
|
||||
[ROLL_GREED] = "Interface\\Buttons\\UI-GroupLoot-Coin-Up",
|
||||
[ROLL_PASS] = "Interface\\Buttons\\UI-GroupLoot-Pass-Up",
|
||||
}
|
||||
local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check"
|
||||
local QUESTIONMARK = "Interface\\Icons\\INV_Misc_QuestionMark"
|
||||
|
||||
-- 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
|
||||
local qc = item:GetItemQualityColor()
|
||||
if qc then r, g, b = qc.r, qc.g, qc.b end
|
||||
f.icon:SetTexture(item:GetItemIcon())
|
||||
f.iconbg:SetBackdropBorderColor(r, g, b, 1)
|
||||
f.name:SetText(item:GetItemName() or UNKNOWN)
|
||||
f.name:SetTextColor(r, g, b)
|
||||
end
|
||||
|
||||
local function ShowRetrieving(f)
|
||||
f.icon:SetTexture(QUESTIONMARK)
|
||||
f.iconbg:SetBackdropBorderColor(1, .3, .3, 1)
|
||||
f.name:SetText(T["Retrieving item information..."])
|
||||
f.name:SetTextColor(1, .3, .3)
|
||||
end
|
||||
|
||||
-- expansion state keyed on the stable rollID (survives ring-index shifts)
|
||||
local expanded = {}
|
||||
|
||||
-- ==========================================================================
|
||||
-- Window
|
||||
-- ==========================================================================
|
||||
pfUI.loothistory = CreateFrame("Frame", "pfLootHistory", UIParent)
|
||||
pfUI.loothistory:SetFrameStrata("DIALOG")
|
||||
pfUI.loothistory:SetSize(380, 490)
|
||||
pfUI.loothistory:SetPoint("CENTER", 0, 0)
|
||||
pfUI.loothistory:SetMovable(true)
|
||||
pfUI.loothistory:EnableMouse(true)
|
||||
pfUI.loothistory:RegisterForDrag("LeftButton")
|
||||
pfUI.loothistory:SetScript("OnDragStart", function() this:StartMoving() end)
|
||||
pfUI.loothistory:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
||||
pfUI.loothistory:Hide()
|
||||
|
||||
CreateBackdrop(pfUI.loothistory, nil, true, .75)
|
||||
CreateBackdropShadow(pfUI.loothistory)
|
||||
tinsert(UISpecialFrames, "pfLootHistory")
|
||||
|
||||
pfUI.loothistory.caption = pfUI.loothistory:CreateFontString("Status", "LOW", "GameFontNormal")
|
||||
pfUI.loothistory.caption:SetFont(pfUI.font_default, C.global.font_size + 4, "OUTLINE")
|
||||
pfUI.loothistory.caption:SetTextColor(.2, 1, .8, 1)
|
||||
pfUI.loothistory.caption:SetPoint("TOP", 0, -10)
|
||||
pfUI.loothistory.caption:SetText(T["Loot History"])
|
||||
|
||||
-- close button
|
||||
pfUI.loothistory.close = CreateFrame("Button", nil, pfUI.loothistory)
|
||||
pfUI.loothistory.close:SetPoint("TOPRIGHT", -border*2, -border*2)
|
||||
CreateBackdrop(pfUI.loothistory.close)
|
||||
pfUI.loothistory.close:SetSize(15, 15)
|
||||
pfUI.loothistory.close.texture = pfUI.loothistory.close:CreateTexture("pfLootHistoryClose")
|
||||
pfUI.loothistory.close.texture:SetTexture(pfUI.media["img:close"])
|
||||
pfUI.loothistory.close.texture:SetPoint("TOPLEFT", pfUI.loothistory.close, "TOPLEFT", 4, -4)
|
||||
pfUI.loothistory.close.texture:SetPoint("BOTTOMRIGHT", pfUI.loothistory.close, "BOTTOMRIGHT", -4, 4)
|
||||
pfUI.loothistory.close.texture:SetVertexColor(1, .25, .25, 1)
|
||||
pfUI.loothistory.close:SetScript("OnEnter", function()
|
||||
CreateBackdrop(pfUI.loothistory.close)
|
||||
pfUI.loothistory.close.backdrop:SetBackdropBorderColor(1, .25, .25, 1)
|
||||
end)
|
||||
pfUI.loothistory.close:SetScript("OnLeave", function() CreateBackdrop(pfUI.loothistory.close) end)
|
||||
pfUI.loothistory.close:SetScript("OnClick", function() pfUI.loothistory:Hide() end)
|
||||
|
||||
-- clear button
|
||||
pfUI.loothistory.clear = CreateFrame("Button", nil, pfUI.loothistory, "UIPanelButtonTemplate")
|
||||
SkinButton(pfUI.loothistory.clear)
|
||||
pfUI.loothistory.clear:SetSize(60, 16)
|
||||
pfUI.loothistory.clear:SetPoint("TOPLEFT", 10, -8)
|
||||
pfUI.loothistory.clear:SetText(T["Clear"])
|
||||
pfUI.loothistory.clear:SetScript("OnClick", function() C_LootHistory.Clear() end)
|
||||
|
||||
-- scroll frame
|
||||
pfUI.loothistory.scroll = CreateScrollFrame("pfLootHistoryScroll", pfUI.loothistory)
|
||||
pfUI.loothistory.scroll:SetSize(360, 440)
|
||||
pfUI.loothistory.scroll:SetPoint("BOTTOM", 0, 10)
|
||||
|
||||
pfUI.loothistory.scroll.backdrop = CreateFrame("Frame", nil, pfUI.loothistory.scroll)
|
||||
pfUI.loothistory.scroll.backdrop:SetFrameLevel(1)
|
||||
pfUI.loothistory.scroll.backdrop:SetPoint("TOPLEFT", pfUI.loothistory.scroll, "TOPLEFT", -5, 5)
|
||||
pfUI.loothistory.scroll.backdrop:SetPoint("BOTTOMRIGHT", pfUI.loothistory.scroll, "BOTTOMRIGHT", 5, -5)
|
||||
CreateBackdrop(pfUI.loothistory.scroll.backdrop, nil, true)
|
||||
|
||||
local list = CreateScrollChild("pfLootHistoryList", pfUI.loothistory.scroll)
|
||||
pfUI.loothistory.list = list
|
||||
|
||||
-- ==========================================================================
|
||||
-- Frame pools
|
||||
-- ==========================================================================
|
||||
local itemFrames = {}
|
||||
local usedPlayers, freePlayers = {}, {}
|
||||
|
||||
local FullUpdate -- forward declaration (toggle handlers call it)
|
||||
|
||||
local function CreateItemFrame()
|
||||
local f = CreateFrame("Button", nil, list)
|
||||
f:SetSize(ITEM_W, ITEM_H)
|
||||
f:SetBackdrop(pfUI.backdrop_hover)
|
||||
f:SetBackdropBorderColor(1, 1, 1, .04)
|
||||
f:EnableMouse(1)
|
||||
|
||||
-- expand / collapse toggle
|
||||
f.toggle = CreateFrame("Button", nil, f)
|
||||
f.toggle:SetSize(14, 14)
|
||||
f.toggle:SetPoint("LEFT", 4, 0)
|
||||
f.toggle:SetScript("OnClick", function()
|
||||
local id = f.rollID
|
||||
if id then expanded[id] = not expanded[id]; FullUpdate() end
|
||||
end)
|
||||
|
||||
-- icon + quality-colored border
|
||||
f.iconbg = CreateFrame("Frame", nil, f)
|
||||
f.iconbg:SetSize(ITEM_H - 8, ITEM_H - 8)
|
||||
f.iconbg:SetPoint("LEFT", f.toggle, "RIGHT", 4, 0)
|
||||
CreateBackdrop(f.iconbg, nil, true)
|
||||
f.icon = f.iconbg:CreateTexture(nil, "ARTWORK")
|
||||
f.icon:SetPoint("TOPLEFT", f.iconbg, "TOPLEFT", 2, -2)
|
||||
f.icon:SetPoint("BOTTOMRIGHT", f.iconbg, "BOTTOMRIGHT", -2, 2)
|
||||
f.icon:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
-- winner block (right side, shown for decided rolls)
|
||||
f.winicon = f:CreateTexture(nil, "OVERLAY")
|
||||
f.winicon:SetSize(14, 14)
|
||||
f.winicon:SetPoint("RIGHT", f, "RIGHT", -6, 0)
|
||||
|
||||
f.winroll = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
f.winroll:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
f.winroll:SetPoint("RIGHT", f.winicon, "LEFT", -2, 0)
|
||||
f.winroll:SetTextColor(1, 1, 1, 1)
|
||||
|
||||
f.winname = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
f.winname:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
f.winname:SetPoint("RIGHT", f.winroll, "LEFT", -4, 0)
|
||||
f.winname:SetJustifyH("RIGHT")
|
||||
|
||||
-- item name (leaves room on the right for the winner block)
|
||||
f.name = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
f.name:SetPoint("LEFT", f.iconbg, "RIGHT", 5, 0)
|
||||
f.name:SetPoint("RIGHT", f, "RIGHT", -100, 0)
|
||||
f.name:SetJustifyH("LEFT")
|
||||
|
||||
f:SetScript("OnEnter", function()
|
||||
this:SetBackdropBorderColor(1, 1, 1, .08)
|
||||
if this.itemLink then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetHyperlink(this.itemLink)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
end)
|
||||
f:SetScript("OnLeave", function()
|
||||
this:SetBackdropBorderColor(1, 1, 1, .04)
|
||||
GameTooltip:Hide()
|
||||
end)
|
||||
f:SetScript("OnClick", function()
|
||||
local id = this.rollID
|
||||
if id then expanded[id] = not expanded[id]; FullUpdate() end
|
||||
end)
|
||||
|
||||
return f
|
||||
end
|
||||
|
||||
local function CreatePlayerFrame()
|
||||
local f = CreateFrame("Frame", nil, list)
|
||||
f:SetSize(PLAYER_W, PLAYER_H)
|
||||
|
||||
-- name is indented to leave room for the winner checkmark on its left
|
||||
f.name = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
f.name:SetPoint("LEFT", 20, 0)
|
||||
f.name:SetJustifyH("LEFT")
|
||||
|
||||
f.rollicon = f:CreateTexture(nil, "OVERLAY")
|
||||
f.rollicon:SetSize(16, 16)
|
||||
f.rollicon:SetPoint("RIGHT", -4, 0)
|
||||
|
||||
f.rolltext = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
|
||||
f.rolltext:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
f.rolltext:SetPoint("RIGHT", f.rollicon, "LEFT", -3, 0)
|
||||
f.rolltext:SetTextColor(1, 1, 1, 1)
|
||||
|
||||
-- winner checkmark, just left of the player name (matches reference)
|
||||
f.winmark = f:CreateTexture(nil, "OVERLAY")
|
||||
f.winmark:SetSize(16, 16)
|
||||
f.winmark:SetTexture(WINMARK)
|
||||
f.winmark:SetPoint("RIGHT", f.name, "LEFT", -1, 0)
|
||||
|
||||
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 function SetToggleTexture(toggle, isExpanded)
|
||||
if isExpanded then
|
||||
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
|
||||
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
|
||||
else
|
||||
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
|
||||
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
|
||||
end
|
||||
end
|
||||
|
||||
-- ==========================================================================
|
||||
-- Rendering
|
||||
-- ==========================================================================
|
||||
local function UpdateItemFrame(f, i)
|
||||
local rollID, itemLink, numPlayers, isDone, winnerIdx = C_LootHistory.GetItem(i)
|
||||
f.rollID = rollID
|
||||
f.itemIdx = i
|
||||
f.itemLink = itemLink
|
||||
f.numPlayers = numPlayers or 0
|
||||
f.isDone = isDone
|
||||
|
||||
local isExpanded = rollID and expanded[rollID]
|
||||
SetToggleTexture(f.toggle, isExpanded)
|
||||
|
||||
-- Item icon/name/quality via the ClassicAPI Item mixin. When the item
|
||||
-- isn't cached yet, show a placeholder and re-paint this row from the
|
||||
-- ContinueOnItemLoad callback (guarded on rollID: rows are pooled, so the
|
||||
-- callback must no-op if the row has since been reused for another roll).
|
||||
local item = itemLink and Item:CreateFromItemLink(itemLink)
|
||||
if item and not item:IsItemEmpty() then
|
||||
if item:IsItemDataCached() then
|
||||
RenderItemVisual(f, item)
|
||||
else
|
||||
ShowRetrieving(f)
|
||||
local pending = rollID
|
||||
item:ContinueOnItemLoad(function()
|
||||
if f.rollID == pending then RenderItemVisual(f, item) end
|
||||
end)
|
||||
end
|
||||
else
|
||||
ShowRetrieving(f)
|
||||
end
|
||||
|
||||
-- winner summary only on a decided, collapsed row
|
||||
if isDone and not isExpanded then
|
||||
if winnerIdx then
|
||||
local wname, wclass, wrollType, wroll = C_LootHistory.GetPlayerInfo(i, winnerIdx)
|
||||
f.winicon:SetTexture(ROLL_TEX[wrollType] or ROLL_TEX[ROLL_NEED])
|
||||
f.winicon:Show()
|
||||
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(PFUI_CLASS_COLORS[wclass]:GetRGB())
|
||||
f.winname:Show()
|
||||
else
|
||||
-- nobody won: everyone passed
|
||||
f.winicon:SetTexture(ROLL_TEX[ROLL_PASS])
|
||||
f.winicon:Show()
|
||||
f.winroll:SetText("")
|
||||
f.winroll:Show()
|
||||
f.winname:SetText(T["All players passed"])
|
||||
f.winname:SetTextColor(1, .4, .4)
|
||||
f.winname:Show()
|
||||
end
|
||||
else
|
||||
f.winicon:Hide()
|
||||
f.winroll:Hide()
|
||||
f.winname:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
|
||||
pf.name:SetText(name or UNKNOWN)
|
||||
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
|
||||
|
||||
if isWinner then pf.winmark:Show() else pf.winmark:Hide() end
|
||||
end
|
||||
|
||||
-- A player row is worth showing while the roll is undecided (see everyone),
|
||||
-- or afterwards only if they actually rolled or it's you (hide the passers'
|
||||
-- noise on a decided roll) — mirrors Blizzard's ShouldDisplayPlayer.
|
||||
local function ShouldDisplayPlayer(isDone, roll, isMe)
|
||||
return isMe or (roll and roll > 0) or not isDone
|
||||
end
|
||||
|
||||
function FullUpdate()
|
||||
if not pfUI.loothistory:IsShown() then return end
|
||||
RecycleAllPlayers()
|
||||
|
||||
local num = C_LootHistory.GetNumItems()
|
||||
local y = -2
|
||||
|
||||
for i = 1, num do
|
||||
local f = itemFrames[i] or CreateItemFrame()
|
||||
itemFrames[i] = f
|
||||
UpdateItemFrame(f, i)
|
||||
f:ClearAllPoints()
|
||||
f:SetPoint("TOPLEFT", list, "TOPLEFT", 4, y)
|
||||
f:Show()
|
||||
y = y - ITEM_H - 2
|
||||
|
||||
if f.rollID and expanded[f.rollID] then
|
||||
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()
|
||||
RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
|
||||
pf:ClearAllPoints()
|
||||
pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y)
|
||||
pf:Show()
|
||||
y = y - PLAYER_H
|
||||
end
|
||||
end
|
||||
y = y - 2
|
||||
end
|
||||
end
|
||||
|
||||
for i = num + 1, table.getn(itemFrames) do
|
||||
itemFrames[i]:Hide()
|
||||
end
|
||||
|
||||
-- Resize the scroll child and re-attach it: SetHeight alone on a
|
||||
-- SetAllPoints'd child doesn't make the ScrollFrame recompute its scroll
|
||||
-- range, so a dynamically-grown list wouldn't scroll until a /reload.
|
||||
list:SetHeight(math.max(1, -y + 2))
|
||||
pfUI.loothistory.scroll:SetScrollChild(list)
|
||||
pfUI.loothistory.scroll:UpdateScrollState()
|
||||
end
|
||||
|
||||
pfUI.loothistory:SetScript("OnShow", function() FullUpdate() end)
|
||||
|
||||
-- ==========================================================================
|
||||
-- Events
|
||||
-- ==========================================================================
|
||||
local events = CreateFrame("Frame")
|
||||
events:RegisterEvent("LOOT_HISTORY_FULL_UPDATE")
|
||||
events:RegisterEvent("LOOT_HISTORY_ROLL_CHANGED")
|
||||
events:RegisterEvent("LOOT_HISTORY_ROLL_COMPLETE")
|
||||
events:SetScript("OnEvent", function()
|
||||
-- auto-show on a new roll opening / completing, if enabled
|
||||
if C.loothistory.autoshow == "1"
|
||||
and (event == "LOOT_HISTORY_FULL_UPDATE" or event == "LOOT_HISTORY_ROLL_COMPLETE")
|
||||
and not pfUI.loothistory:IsShown() then
|
||||
pfUI.loothistory:Show() -- OnShow runs FullUpdate
|
||||
return
|
||||
end
|
||||
FullUpdate() -- no-op while hidden
|
||||
end)
|
||||
|
||||
-- ==========================================================================
|
||||
-- Slash command
|
||||
-- ==========================================================================
|
||||
local function Toggle()
|
||||
pfUI.loothistory:SetShown(not pfUI.loothistory:IsShown())
|
||||
end
|
||||
|
||||
pfUI.api.RegisterSlashCommand("PFLOOTHISTORY", { "/loothistory", "/pfloothistory" }, Toggle, true)
|
||||
end)
|
||||
@@ -0,0 +1,128 @@
|
||||
-- Macro icon picker
|
||||
-- Replaces Blizzard's MacroPopupFrame (the "name + icon" dialog opened by the
|
||||
-- New / Change-Icon buttons) with a pfUI-native picker driven by
|
||||
-- IconDataProviderMixin, so the full spell + item + loose icon set is
|
||||
-- available instead of the stock spell-only list. Saving goes through
|
||||
-- C_Macro.CreateMacro / C_Macro.EditMacro, which take the icon as a texture
|
||||
-- string -- so arbitrary icons (including index-less INV_* item icons) persist
|
||||
-- on both the modern (Turtle/Octo) and stock-vanilla macro UIs. The surrounding
|
||||
-- MacroFrame (list + body editor) is Blizzard's, already skinned elsewhere.
|
||||
pfUI:RegisterModule("macroicons", function ()
|
||||
HookAddonOrVariable("Blizzard_MacroUI", function()
|
||||
-- ============================================================
|
||||
-- Popup frame
|
||||
-- ============================================================
|
||||
|
||||
local picker = CreateFrame("Frame", "pfMacroIconPicker", UIParent)
|
||||
picker:SetFrameStrata("DIALOG")
|
||||
picker:SetSize(472, 484)
|
||||
-- Starts anchored to the right of the macro panel (re-anchored at open
|
||||
-- once the skin's backdrop exists); dragging pins it in place after.
|
||||
picker:SetPoint("BOTTOMLEFT", MacroFrame, "BOTTOMRIGHT", 8, 0)
|
||||
picker:Hide()
|
||||
CreateBackdrop(picker, nil, nil, .9)
|
||||
CreateBackdropShadow(picker)
|
||||
picker:EnableMouse(true)
|
||||
picker:SetMovable(true)
|
||||
picker:RegisterForDrag("LeftButton")
|
||||
picker:SetScript("OnDragStart", function() this:StartMoving() end)
|
||||
picker:SetScript("OnDragStop", function()
|
||||
this:StopMovingOrSizing()
|
||||
this.userMoved = true
|
||||
end)
|
||||
|
||||
-- Spellbook seed leads the grid with class-relevant spell/talent icons.
|
||||
local iconPicker = CreateIconPicker("pfMacroIcon", picker,
|
||||
IconDataProviderExtraType.Spellbook, MACRO_POPUP_TEXT)
|
||||
|
||||
-- ============================================================
|
||||
-- Open / save
|
||||
-- ============================================================
|
||||
|
||||
-- Blizzard's popup disables these while it is open; mirror that so the
|
||||
-- underlying frame can't be double-driven, then let MacroFrame_Update
|
||||
-- restore the correct states on close.
|
||||
local function SetMacroButtons(enabled)
|
||||
local m = enabled and "Enable" or "Disable"
|
||||
MacroNewButton[m](MacroNewButton)
|
||||
MacroEditButton[m](MacroEditButton)
|
||||
MacroDeleteButton[m](MacroDeleteButton)
|
||||
end
|
||||
|
||||
local function OpenMacroPopup(mode)
|
||||
if mode == "edit" and MacroFrame.selectedMacro then
|
||||
picker.mode = "edit"
|
||||
local name, texture = GetMacroInfo(MacroFrame.selectedMacro)
|
||||
picker.editbox:SetText(name or "")
|
||||
iconPicker.SetIcon(texture)
|
||||
else
|
||||
picker.mode = "new"
|
||||
picker.editbox:SetText("")
|
||||
iconPicker.SetIcon(nil)
|
||||
end
|
||||
picker.search:SetText("")
|
||||
if not picker.userMoved then
|
||||
picker:ClearAllPoints()
|
||||
picker:SetPoint("BOTTOMLEFT", MacroFrame.backdrop or MacroFrame, "BOTTOMRIGHT", 8, 0)
|
||||
end
|
||||
SetMacroButtons(false)
|
||||
picker:Show()
|
||||
picker.editbox:SetFocus()
|
||||
iconPicker.Refresh()
|
||||
end
|
||||
|
||||
local function SaveMacroPopup()
|
||||
local text = picker.editbox:GetText()
|
||||
if text == "" then return end
|
||||
|
||||
local icon = iconPicker.GetIcon()
|
||||
if picker.mode == "edit" and MacroFrame.selectedMacro then
|
||||
C_Macro.EditMacro(MacroFrame.selectedMacro, text, icon, nil)
|
||||
MacroFrame_SelectMacro(MacroFrame.selectedMacro)
|
||||
else
|
||||
local idx = C_Macro.CreateMacro(text, icon, nil, (MacroFrame.macroBase or 0) > 0)
|
||||
if idx then MacroFrame_SelectMacro(idx) end
|
||||
end
|
||||
picker:Hide()
|
||||
end
|
||||
|
||||
picker:SetScript("OnHide", function() MacroFrame_Update() end)
|
||||
|
||||
picker.editbox:SetScript("OnEnterPressed", SaveMacroPopup)
|
||||
picker.editbox:SetScript("OnEscapePressed", function() picker:Hide() end)
|
||||
|
||||
-- OK / Cancel
|
||||
local okay = CreateFrame("Button", "pfMacroIconPickerOkay", picker, "UIPanelButtonTemplate")
|
||||
okay:SetSize(80, 22)
|
||||
okay:SetText(OKAY)
|
||||
okay:SetScript("OnClick", SaveMacroPopup)
|
||||
SkinButton(okay)
|
||||
|
||||
local cancel = CreateFrame("Button", "pfMacroIconPickerCancel", picker, "UIPanelButtonTemplate")
|
||||
cancel:SetSize(80, 22)
|
||||
cancel:SetText(CANCEL)
|
||||
cancel:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -14, 12)
|
||||
cancel:SetScript("OnClick", function() picker:Hide() end)
|
||||
SkinButton(cancel)
|
||||
|
||||
-- Okay | Cancel, both anchored to the bottom-right corner.
|
||||
okay:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -6, 0)
|
||||
|
||||
-- ============================================================
|
||||
-- Take over the New / Change-Icon buttons; retire Blizzard's popup
|
||||
-- ============================================================
|
||||
|
||||
MacroNewButton:SetScript("OnClick", function()
|
||||
MacroFrame_SaveMacro()
|
||||
OpenMacroPopup("new")
|
||||
end)
|
||||
|
||||
MacroEditButton:SetScript("OnClick", function()
|
||||
MacroFrame_SaveMacro()
|
||||
OpenMacroPopup("edit")
|
||||
end)
|
||||
|
||||
-- Closing the macro window takes the picker with it.
|
||||
MacroFrame:HookScript("OnHide", function() picker:Hide() end)
|
||||
end)
|
||||
end)
|
||||
+8
-15
@@ -2,19 +2,19 @@ pfUI:RegisterModule("macrotweak", function ()
|
||||
local conflictAddons = { "Supermacro", "SuperCleveRoidMacros", "UltimaMacros" }
|
||||
local disabled = false
|
||||
|
||||
local function CheckConflicts()
|
||||
for _, name in pairs(conflictAddons) do
|
||||
if IsAddOnLoaded(name) then
|
||||
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.")
|
||||
disabled = true
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
disabled = true
|
||||
end)
|
||||
end
|
||||
|
||||
-- do not write macro calls into chat input history
|
||||
if ChatFrameEditBox._AddHistoryLine then
|
||||
-- (install once: _AddHistoryLine is our backup slot and is nil until we set it)
|
||||
if not ChatFrameEditBox._AddHistoryLine then
|
||||
local userinput
|
||||
ChatFrameEditBox._AddHistoryLine = ChatFrameEditBox.AddHistoryLine
|
||||
ChatFrameEditBox.AddHistoryLine = function(self, text)
|
||||
@@ -64,11 +64,4 @@ pfUI:RegisterModule("macrotweak", function ()
|
||||
UseInventoryItem(slot)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Check conflicts after one tick so all addons have finished loading
|
||||
local watcher = CreateFrame("Frame")
|
||||
watcher:SetScript("OnUpdate", function()
|
||||
this:SetScript("OnUpdate", nil)
|
||||
CheckConflicts()
|
||||
end)
|
||||
end)
|
||||
+16
-16
@@ -25,11 +25,7 @@ pfUI:RegisterModule("map", function ()
|
||||
pfUI.map = { UpdateConfig = UpdateTooltipScale }
|
||||
|
||||
function _G.ToggleWorldMap()
|
||||
if WorldMapFrame:IsShown() then
|
||||
WorldMapFrame:Hide()
|
||||
else
|
||||
WorldMapFrame:Show()
|
||||
end
|
||||
WorldMapFrame:SetShown(not WorldMapFrame:IsShown())
|
||||
end
|
||||
|
||||
C.position["WorldMapFrame"] = C.position["WorldMapFrame"] or { alpha = 1.0, scale = 0.7 }
|
||||
@@ -54,7 +50,7 @@ pfUI:RegisterModule("map", function ()
|
||||
if not this.hooked then
|
||||
this.hooked = true
|
||||
|
||||
HookScript(WorldMapFrame, "OnShow", function()
|
||||
WorldMapFrame:HookScript("OnShow", function()
|
||||
-- customize
|
||||
this:EnableKeyboard(false)
|
||||
this:EnableMouseWheel(1)
|
||||
@@ -66,7 +62,7 @@ pfUI:RegisterModule("map", function ()
|
||||
pfOrigSetMapToCurrentZone()
|
||||
end)
|
||||
|
||||
HookScript(WorldMapFrame, "OnMouseWheel", function()
|
||||
WorldMapFrame:HookScript("OnMouseWheel", function()
|
||||
if IsShiftKeyDown() then
|
||||
alpha = clamp(WorldMapFrame:GetAlpha() + arg1/10, 0.1, 1.0)
|
||||
WorldMapFrame:SetAlpha(alpha)
|
||||
@@ -81,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)
|
||||
@@ -91,11 +93,11 @@ pfUI:RegisterModule("map", function ()
|
||||
SaveMovable(this, true)
|
||||
end)
|
||||
|
||||
HookScript(WorldMapFrame, "OnDragStart", function()
|
||||
WorldMapFrame:HookScript("OnDragStart", function()
|
||||
WorldMapFrame:StartMoving()
|
||||
end)
|
||||
|
||||
HookScript(WorldMapFrame, "OnDragStop",function()
|
||||
WorldMapFrame:HookScript("OnDragStop",function()
|
||||
WorldMapFrame:StopMovingOrSizing()
|
||||
SaveMovable(this, true)
|
||||
end)
|
||||
@@ -107,8 +109,8 @@ pfUI:RegisterModule("map", function ()
|
||||
|
||||
WorldMapFrame:ClearAllPoints()
|
||||
WorldMapFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
||||
WorldMapFrame:SetWidth(WorldMapButton:GetWidth() + 15)
|
||||
WorldMapFrame:SetHeight(WorldMapButton:GetHeight() + 55)
|
||||
local wmWidth, wmHeight = WorldMapButton:GetSize()
|
||||
WorldMapFrame:SetSize(wmWidth + 15, wmHeight + 55)
|
||||
LoadMovable(WorldMapFrame)
|
||||
|
||||
-- skin
|
||||
@@ -130,8 +132,7 @@ pfUI:RegisterModule("map", function ()
|
||||
btn:SetHighlightTexture("")
|
||||
btn.text = _G["pfUI_map_autozoneswitchText"]
|
||||
CreateBackdrop(btn, nil, true)
|
||||
btn:SetWidth(14)
|
||||
btn:SetHeight(14)
|
||||
btn:SetSize(14, 14)
|
||||
btn:SetPoint("RIGHT", WorldMapContinentDropDown, "LEFT", -8, 2)
|
||||
btn.text:ClearAllPoints()
|
||||
btn.text:SetPoint("RIGHT", btn, "LEFT", -4, 1)
|
||||
@@ -169,8 +170,7 @@ pfUI:RegisterModule("map", function ()
|
||||
WorldMapButton.coords.text:SetJustifyH("RIGHT")
|
||||
|
||||
WorldMapButton.coords:SetScript("OnUpdate", function()
|
||||
local width = WorldMapButton:GetWidth()
|
||||
local height = WorldMapButton:GetHeight()
|
||||
local width, height = WorldMapButton:GetSize()
|
||||
local mx, my = WorldMapButton:GetCenter()
|
||||
local scale = WorldMapButton:GetEffectiveScale()
|
||||
local x, y = GetCursorPosition()
|
||||
|
||||
@@ -50,7 +50,7 @@ pfUI:RegisterModule("mapcolors", function ()
|
||||
end
|
||||
|
||||
local function UpdateUnitFrames(unit_button_name)
|
||||
if GetNumRaidMembers() > 0 then
|
||||
if IsInRaid() then
|
||||
for i=1, MAX_RAID_MEMBERS do
|
||||
local frame_name = unit_button_name.."Raid"..i
|
||||
local frame = _G[frame_name]
|
||||
@@ -68,7 +68,7 @@ pfUI:RegisterModule("mapcolors", function ()
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif GetNumPartyMembers() > 0 then
|
||||
elseif IsInGroup() then
|
||||
for i=1, MAX_PARTY_MEMBERS do
|
||||
local frame_name = unit_button_name.."Party"..i
|
||||
local frame = _G[frame_name]
|
||||
@@ -85,7 +85,7 @@ pfUI:RegisterModule("mapcolors", function ()
|
||||
end
|
||||
|
||||
local function ColorizeName(frame)
|
||||
local _, class = UnitClass(frame.unit)
|
||||
local class = UnitClassBase(frame.unit)
|
||||
local color = PFUI_CLASS_COLORS[class]
|
||||
frame.name = frame.name or UnitName(frame.unit)
|
||||
frame.name = '|c'..color.colorStr..frame.name..'|r'
|
||||
|
||||
+38
-74
@@ -30,25 +30,6 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
pfUI.mapreveal:UpdateConfig()
|
||||
end)
|
||||
|
||||
local function unpack_hash(prefix, hash)
|
||||
local _, stored_prefix, textureName, textureWidth, textureHeight, offsetX, offsetY, mapPointX, mapPointY, name
|
||||
_, _, stored_prefix, textureName, textureWidth, textureHeight, offsetX, offsetY = string.find(hash, "^([|]?)([^:]+):([^:]+):([^:]+):([^:]+):([^:]+)")
|
||||
if (not textureName or not offsetY) then
|
||||
return
|
||||
end
|
||||
if (offsetY) then
|
||||
_, _, mapPointX, mapPointY = string.find(hash,"^[|]?[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:([^:]+):([^:]+)")
|
||||
end
|
||||
if (not mapPointY) then
|
||||
mapPointX = 0 mapPointY = 0
|
||||
end
|
||||
if (stored_prefix ~= "|") then
|
||||
name = textureName
|
||||
textureName = string.format("%s%s",prefix,textureName)
|
||||
end
|
||||
return textureName, textureWidth + 0, textureHeight + 0, offsetX + 0, offsetY + 0, mapPointX + 0, mapPointY + 0, name
|
||||
end
|
||||
|
||||
local explores = {}
|
||||
local explorecaches = {}
|
||||
local alreadyknown = {} -- per-zone accumulator: { [zone] = { [texName] = true } }
|
||||
@@ -71,30 +52,23 @@ 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
|
||||
|
||||
-- overlay data with lazy init
|
||||
local overlayData = setmetatable(pfMapOverlayData, {__index = function(t,k)
|
||||
local v = {}
|
||||
rawset(t,k,v)
|
||||
return v
|
||||
end})
|
||||
|
||||
local function pfWorldMapFrame_Update()
|
||||
-- clear stale caches
|
||||
for k in pairs(explorecaches) do explorecaches[k] = nil end
|
||||
@@ -108,7 +82,6 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
local mapFileName = GetMapInfo()
|
||||
if not mapFileName then mapFileName = "World" end
|
||||
|
||||
local prefix = string.format("Interface\\WorldMap\\%s\\", mapFileName)
|
||||
local numOverlays = GetNumMapOverlays()
|
||||
|
||||
-- accumulate explored overlays per zone (never clear, only add)
|
||||
@@ -123,12 +96,18 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
-- hide explore icons
|
||||
for _, frame in pairs(explores) do frame:Hide() end
|
||||
|
||||
local zoneData = overlayData[mapFileName]
|
||||
-- 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, hash in ipairs(zoneData) do
|
||||
local textureName, textureWidth, textureHeight, offsetX, offsetY, mapPointX, mapPointY, name = unpack_hash(prefix, hash)
|
||||
if not textureName then break end
|
||||
for i, 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
|
||||
local textureHeight = overlay.textureHeight
|
||||
local offsetX = overlay.offsetX
|
||||
local offsetY = overlay.offsetY
|
||||
|
||||
-- explore magnifying glass icon
|
||||
explores[i] = explores[i] or CreateFrame("Frame", nil, WorldMapDetailFrame)
|
||||
@@ -141,11 +120,14 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
explore:EnableMouse(true)
|
||||
explore:SetFrameLevel(255)
|
||||
explore.name = mapFileName .. " (" .. name .. ")"
|
||||
explore.area = name -- cache key: explorecaches is keyed by the plain area name
|
||||
explore.tex = explore.tex or explore:CreateTexture("", "OVERLAY")
|
||||
explore.tex:SetBlendMode("ADD")
|
||||
explore.tex:SetTexCoord(.08, .92, .08, .92)
|
||||
explore.tex:SetAllPoints()
|
||||
|
||||
-- `alreadyknown` stores the FULL paths GetMapOverlayInfo returns,
|
||||
-- so compare with the full path, not the bare name.
|
||||
if C.appearance.worldmap.mapexploration == "1" and not zoneKnown[string.upper(textureName)] then
|
||||
explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
|
||||
explore:Show()
|
||||
@@ -155,49 +137,31 @@ pfUI:RegisterModule("mapreveal", function ()
|
||||
|
||||
-- render overlay texture tiles on BORDER draw layer
|
||||
-- Blizzard's explored overlays on ARTWORK draw on top of BORDER
|
||||
--
|
||||
-- overlay.tiles is pre-resolved by ClassicAPI: per-tile file /
|
||||
-- draw size / texcoords / canvas position, with Octo's data
|
||||
-- quirks (sliver columns the DBC rect rounds away, foreign tiles
|
||||
-- appended to the number sequence, upscaled re-exports) already
|
||||
-- disambiguated from the actual BLP dimensions. No 256px grid
|
||||
-- math here — deriving the grid from textureWidth/Height is
|
||||
-- exactly what shears quirky overlays (e.g. Icepoint's Kaneq'nuun).
|
||||
if C.appearance.worldmap.mapreveal == "1" then
|
||||
local numH = math.ceil(textureWidth / 256)
|
||||
local numV = math.ceil(textureHeight / 256)
|
||||
local texPixW, texFileW, texPixH, texFileH
|
||||
for _, tile in ipairs(overlay.tiles) do
|
||||
textureCount = textureCount + 1
|
||||
local tex = pfGetOverlay(textureCount)
|
||||
|
||||
for j = 1, numV do
|
||||
if j < numV then
|
||||
texPixH = 256
|
||||
texFileH = 256
|
||||
else
|
||||
texPixH = mod(textureHeight, 256)
|
||||
if texPixH == 0 then texPixH = 256 end
|
||||
texFileH = 16
|
||||
while texFileH < texPixH do texFileH = texFileH * 2 end
|
||||
end
|
||||
tex:SetWidth(tile.width)
|
||||
tex:SetHeight(tile.height)
|
||||
tex:SetTexCoord(0, tile.texCoordX, 0, tile.texCoordY)
|
||||
tex:ClearAllPoints()
|
||||
tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", tile.offsetX, -tile.offsetY)
|
||||
tex:SetTexture(tile.file)
|
||||
|
||||
for k = 1, numH do
|
||||
textureCount = textureCount + 1
|
||||
local tex = pfGetOverlay(textureCount)
|
||||
explorecaches[name] = explorecaches[name] or {}
|
||||
explorecaches[name][tex] = true
|
||||
|
||||
if k < numH then
|
||||
texPixW = 256
|
||||
texFileW = 256
|
||||
else
|
||||
texPixW = mod(textureWidth, 256)
|
||||
if texPixW == 0 then texPixW = 256 end
|
||||
texFileW = 16
|
||||
while texFileW < texPixW do texFileW = texFileW * 2 end
|
||||
end
|
||||
|
||||
tex:SetWidth(texPixW)
|
||||
tex:SetHeight(texPixH)
|
||||
tex:SetTexCoord(0, texPixW/texFileW, 0, texPixH/texFileH)
|
||||
tex:ClearAllPoints()
|
||||
tex:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", offsetX + 256*(k-1), -(offsetY + 256*(j-1)))
|
||||
tex:SetTexture(string.format("%s%s", textureName, ((j-1)*numH + k)))
|
||||
|
||||
explorecaches[name] = explorecaches[name] or {}
|
||||
explorecaches[name][tex] = true
|
||||
|
||||
tex:SetVertexColor(r,g,b,a)
|
||||
tex:Show()
|
||||
end
|
||||
tex:SetVertexColor(r,g,b,a)
|
||||
tex:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+54
-60
@@ -1,58 +1,32 @@
|
||||
pfUI:RegisterNewModule("marktracking", "Mark Tracker")
|
||||
pfUI:RegisterModule("marktracking", function ()
|
||||
-- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower)
|
||||
if not UnitExists("mark1") and not UnitExists("mark8") then
|
||||
if not pcall(function() UnitExists("mark1") end) then return end
|
||||
end
|
||||
|
||||
local rawborder, border = GetBorderSize()
|
||||
|
||||
-- Parse color strings "r,g,b,a" into components
|
||||
local function ParseColor(str, dr, dg, db, da)
|
||||
if not str or str == "" then return dr, dg, db, da end
|
||||
local _, _, r, g, b, a = string.find(str, "([%d%.]+),([%d%.]+),([%d%.]+),([%d%.]+)")
|
||||
if r then
|
||||
return tonumber(r) or dr, tonumber(g) or dg, tonumber(b) or db, tonumber(a) or da
|
||||
end
|
||||
return dr, dg, db, da
|
||||
end
|
||||
|
||||
local markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star
|
||||
local markerTokens = {}
|
||||
for i = 1, 8 do markerTokens[i] = "mark" .. i end
|
||||
|
||||
-- Default colors per marker
|
||||
local defaultColors = {
|
||||
[1] = { 1.0, 0.9, 0.0, 1 }, -- star: yellow
|
||||
[2] = { 1.0, 0.5, 0.0, 1 }, -- circle: orange
|
||||
[3] = { 0.8, 0.0, 0.8, 1 }, -- diamond: purple
|
||||
[4] = { 0.0, 0.8, 0.0, 1 }, -- triangle: green
|
||||
[5] = { 0.7, 0.7, 0.7, 1 }, -- moon: silver
|
||||
[6] = { 0.0, 0.4, 0.9, 1 }, -- square: blue
|
||||
[7] = { 0.9, 0.0, 0.0, 1 }, -- cross: red
|
||||
[8] = { 1.0, 1.0, 1.0, 1 }, -- skull: white
|
||||
}
|
||||
|
||||
local markerConfigKeys = {
|
||||
[1] = "raidmarkercolor_star",
|
||||
[2] = "raidmarkercolor_circle",
|
||||
[3] = "raidmarkercolor_diamond",
|
||||
[4] = "raidmarkercolor_triangle",
|
||||
[5] = "raidmarkercolor_moon",
|
||||
[6] = "raidmarkercolor_square",
|
||||
[7] = "raidmarkercolor_cross",
|
||||
[8] = "raidmarkercolor_skull",
|
||||
"raidmarkercolor_star",
|
||||
"raidmarkercolor_circle",
|
||||
"raidmarkercolor_diamond",
|
||||
"raidmarkercolor_triangle",
|
||||
"raidmarkercolor_moon",
|
||||
"raidmarkercolor_square",
|
||||
"raidmarkercolor_cross",
|
||||
"raidmarkercolor_skull",
|
||||
}
|
||||
|
||||
local markerColors = {}
|
||||
for i = 1, 8 do
|
||||
local d = defaultColors[i]
|
||||
local r, g, b, a = ParseColor(C.unitframes[markerConfigKeys[i]], d[1], d[2], d[3], d[4])
|
||||
markerColors[i] = { r, g, b, a }
|
||||
for i, markKey in ipairs(markerConfigKeys) do
|
||||
markerTokens[i] = "mark" .. i
|
||||
local r, g, b, a = GetStringColor(C.unitframes[markKey])
|
||||
markerColors[i] = { tonumber(r), tonumber(g), tonumber(b), tonumber(a) }
|
||||
end
|
||||
|
||||
local FALLBACK_INTERVAL = 1.0 -- safety net for units that come into range after marker was set
|
||||
local elapsed = 0
|
||||
local isUnlocked = false
|
||||
local ROW_HEIGHT = tonumber(C.unitframes.raidmarkerheight) or 14
|
||||
local BAR_WIDTH = tonumber(C.unitframes.raidmarkerwidth) or 80
|
||||
@@ -126,19 +100,16 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
local i = markerOrder[idx]
|
||||
|
||||
local row = CreateFrame("Button", nil, pfUI.marktracking)
|
||||
row:SetWidth(TOTAL_ROW_WIDTH)
|
||||
row:SetHeight(ROW_HEIGHT)
|
||||
row:SetSize(TOTAL_ROW_WIDTH, ROW_HEIGHT)
|
||||
row:Hide()
|
||||
|
||||
row:RegisterForClicks("LeftButtonUp")
|
||||
row:SetScript("OnClick", function()
|
||||
TargetUnit(markerTokens[this.markerIndex])
|
||||
end)
|
||||
row:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||||
row:SetAttribute("type1", "target")
|
||||
row:SetAttribute("type2", "menu")
|
||||
|
||||
-- raid icon
|
||||
row.icon = row:CreateTexture(nil, "ARTWORK")
|
||||
row.icon:SetWidth(ROW_HEIGHT)
|
||||
row.icon:SetHeight(ROW_HEIGHT)
|
||||
row.icon:SetSize(ROW_HEIGHT, ROW_HEIGHT)
|
||||
row.icon:SetPoint("LEFT", row, "LEFT", 1, 0)
|
||||
local markTex = C.unitframes.blizzard_raidicons == "1" and "Interface\\TargetingFrame\\UI-RaidTargetingIcons" or pfUI.media["img:raidicons"]
|
||||
row.icon:SetTexture(markTex)
|
||||
@@ -146,8 +117,7 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
|
||||
-- portrait (right side)
|
||||
row.portrait = row:CreateTexture(nil, "ARTWORK")
|
||||
row.portrait:SetWidth(PORTRAIT_SIZE)
|
||||
row.portrait:SetHeight(PORTRAIT_SIZE)
|
||||
row.portrait:SetSize(PORTRAIT_SIZE, PORTRAIT_SIZE)
|
||||
row.portrait:SetPoint("RIGHT", row, "RIGHT", -1, 0)
|
||||
row.portrait:SetTexCoord(.1, .9, .1, .9)
|
||||
if not rm_showportrait then row.portrait:Hide() end
|
||||
@@ -205,8 +175,8 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
local token = markerTokens[i]
|
||||
|
||||
if UnitExists(token) and not UnitIsDead(token) then
|
||||
local hp = UnitHealth(token)
|
||||
local maxhp = UnitHealthMax(token)
|
||||
local hp, maxhp = UnitHealth(token), UnitHealthMax(token)
|
||||
row:SetAttribute('unit', token)
|
||||
|
||||
if hp and maxhp and maxhp > 0 and hp > 0 then
|
||||
local pct = hp / maxhp
|
||||
@@ -268,6 +238,27 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
end
|
||||
end
|
||||
|
||||
-- Fast per-row refresh for a single mark's UNIT_HEALTH/UNIT_MAXHEALTH: moves
|
||||
-- just the bar + hp text. If the mark's visibility flips (comes into range,
|
||||
-- dies, hp crosses 0) the visible rows re-pack, so hand off to UpdateDisplay.
|
||||
local function UpdateRow(i)
|
||||
if isUnlocked then return end
|
||||
local row = pfUI.marktracking.rows[i]
|
||||
if not row then return end
|
||||
local token = markerTokens[i]
|
||||
local hp, maxhp = UnitHealth(token), UnitHealthMax(token)
|
||||
local shouldShow = UnitExists(token) and not UnitIsDead(token)
|
||||
and hp and maxhp and maxhp > 0 and hp > 0 and true or false
|
||||
if shouldShow ~= (row:IsShown() and true or false) then
|
||||
UpdateDisplay()
|
||||
return
|
||||
end
|
||||
if not shouldShow then return end
|
||||
local pct = hp / maxhp
|
||||
row.health:SetValue(pct)
|
||||
if rm_showpct then row.hptext:SetText(math.ceil(pct * 100) .. "%") end
|
||||
end
|
||||
|
||||
-- Unlock mode: show fixed 1-row placeholder so positioning works correctly
|
||||
if pfUI.unlock then
|
||||
local origShow = pfUI.unlock:GetScript("OnShow")
|
||||
@@ -293,24 +284,27 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
-- Event-driven scanner frame
|
||||
local scanner = CreateFrame("Frame")
|
||||
|
||||
-- RAID_TARGET_UPDATE: fires when a raid marker is set/cleared
|
||||
-- PLAYER_ENTERING_WORLD: fires on login, reload, and zone transitions
|
||||
-- UNIT_HEALTH/UNIT_MAXHEALTH: fires on HP changes for real-time bar updates
|
||||
-- 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.
|
||||
scanner:RegisterEvent("RAID_TARGET_UPDATE")
|
||||
scanner:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
scanner:RegisterEvent("UNIT_HEALTH")
|
||||
scanner:RegisterEvent("UNIT_MAXHEALTH")
|
||||
|
||||
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)"))
|
||||
if i then UpdateRow(i) end
|
||||
return
|
||||
end
|
||||
UpdateDisplay()
|
||||
end)
|
||||
|
||||
-- Fallback poll at 1s: catches units that come into range AFTER a marker was set
|
||||
-- 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)
|
||||
scanner:SetScript("OnUpdate", function()
|
||||
elapsed = elapsed + arg1
|
||||
if elapsed < FALLBACK_INTERVAL then return end
|
||||
elapsed = 0
|
||||
UpdateDisplay()
|
||||
end)
|
||||
C_Timer.NewTicker(FALLBACK_INTERVAL, UpdateDisplay)
|
||||
end)
|
||||
+16
-35
@@ -33,11 +33,9 @@ pfUI:RegisterModule("minimap", function ()
|
||||
pfUI.minimap.UpdateConfig = function(self)
|
||||
size = tonumber(C.appearance.minimap.size) or 140
|
||||
|
||||
pfUI.minimap:SetWidth(size)
|
||||
pfUI.minimap:SetHeight(size)
|
||||
pfUI.minimap:SetSize(size, size)
|
||||
|
||||
Minimap:SetWidth(size)
|
||||
Minimap:SetHeight(size)
|
||||
Minimap:SetSize(size, size)
|
||||
|
||||
-- vanilla+tbc: do the best to detect the minimap arrow
|
||||
local arrowscale = tonumber(C.appearance.minimap.arrowscale)
|
||||
@@ -133,18 +131,16 @@ pfUI:RegisterModule("minimap", function ()
|
||||
|
||||
-- Create coordinates text frame with location configurable
|
||||
pfUI.minimapCoordinates = CreateFrame("Frame", "pfMinimapCoord", pfUI.minimap)
|
||||
pfUI.minimapCoordinates:SetScript("OnUpdate", function()
|
||||
-- Throttle to update coords every 0.1 seconds
|
||||
if ( this.tick or 0) > GetTime() then return end
|
||||
this.tick = GetTime() + .1
|
||||
|
||||
-- Update coords every 0.1 seconds
|
||||
C_Timer.NewTicker(0.1, function()
|
||||
if C.appearance.minimap.coordstext == "off" then return end
|
||||
|
||||
this.posX, this.posY = GetPlayerMapPosition("player")
|
||||
if this.posX ~= 0 and this.posY ~= 0 then
|
||||
this.text:SetText(string.format("%.1f, %.1f", round(this.posX * 100, 1), round(this.posY * 100, 1)))
|
||||
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)))
|
||||
else
|
||||
this.text:SetText("|cffffaaaaN/A")
|
||||
coord.text:SetText("|cffffaaaaN/A")
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -158,8 +154,7 @@ pfUI:RegisterModule("minimap", function ()
|
||||
pfUI.minimapCoordinates:SetPoint("BOTTOMLEFT", 3, 3)
|
||||
end
|
||||
|
||||
pfUI.minimapCoordinates:SetHeight(C.global.font_size)
|
||||
pfUI.minimapCoordinates:SetWidth(Minimap:GetWidth())
|
||||
pfUI.minimapCoordinates:SetSize(Minimap:GetWidth(), C.global.font_size)
|
||||
pfUI.minimapCoordinates.text = pfUI.minimapCoordinates:CreateFontString("MinimapCoordinatesText", "LOW", "GameFontNormal")
|
||||
pfUI.minimapCoordinates.text:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
||||
pfUI.minimapCoordinates.text:SetTextColor(1,1,1,1)
|
||||
@@ -171,19 +166,14 @@ pfUI:RegisterModule("minimap", function ()
|
||||
pfUI.minimapCoordinates.text:SetJustifyH("LEFT")
|
||||
end
|
||||
|
||||
if C.appearance.minimap.coordstext ~= "on" then
|
||||
pfUI.minimapCoordinates:Hide()
|
||||
else
|
||||
pfUI.minimapCoordinates:Show()
|
||||
end
|
||||
pfUI.minimapCoordinates:SetShown(C.appearance.minimap.coordstext == "on")
|
||||
|
||||
-- Create zone text frame in top center of minimap
|
||||
pfUI.minimapZone = CreateFrame("Frame", "pfMinimapZone", pfUI.minimap)
|
||||
pfUI.minimapZone:RegisterEvent("MINIMAP_ZONE_CHANGED")
|
||||
pfUI.minimapZone:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
pfUI.minimapZone:SetPoint("TOP", 0, -3)
|
||||
pfUI.minimapZone:SetHeight(C.global.font_size + 2)
|
||||
pfUI.minimapZone:SetWidth(Minimap:GetWidth())
|
||||
pfUI.minimapZone:SetSize(Minimap:GetWidth(), C.global.font_size + 2)
|
||||
pfUI.minimapZone.text = pfUI.minimapZone:CreateFontString("minimapZoneText", "LOW", "GameFontNormal")
|
||||
pfUI.minimapZone.text:SetFont(pfUI.font_default, C.global.font_size + 2, "OUTLINE")
|
||||
pfUI.minimapZone.text:SetAllPoints(pfUI.minimapZone)
|
||||
@@ -205,17 +195,13 @@ pfUI:RegisterModule("minimap", function ()
|
||||
elseif pvp == "contested" then
|
||||
pfUI.minimapZone.text:SetTextColor(1.0, 0.7, 0)
|
||||
else
|
||||
pfUI.minimapZone.text:SetTextColor(1, 1, 1, 1)
|
||||
pfUI.minimapZone.text:SetTextColor(WHITE_FONT_COLOR:GetRGBA())
|
||||
end
|
||||
pfUI.minimapZone.text:SetText(GetMinimapZoneText())
|
||||
end
|
||||
end)
|
||||
|
||||
if C.appearance.minimap.zonetext ~= "on" then
|
||||
pfUI.minimapZone:Hide()
|
||||
else
|
||||
pfUI.minimapZone:Show()
|
||||
end
|
||||
pfUI.minimapZone:SetShown(C.appearance.minimap.zonetext == "on")
|
||||
|
||||
-- Minimap hover event
|
||||
-- Update and toggle showing of coordinates and zone text on mouse enter/leave
|
||||
@@ -241,8 +227,7 @@ pfUI:RegisterModule("minimap", function ()
|
||||
pfUI.minimap.pvpicon:RegisterEvent("UPDATE_FACTION")
|
||||
pfUI.minimap.pvpicon:RegisterEvent("UNIT_FACTION")
|
||||
pfUI.minimap.pvpicon:SetFrameStrata("HIGH")
|
||||
pfUI.minimap.pvpicon:SetWidth(16)
|
||||
pfUI.minimap.pvpicon:SetHeight(16)
|
||||
pfUI.minimap.pvpicon:SetSize(16, 16)
|
||||
pfUI.minimap.pvpicon:SetAlpha(.5)
|
||||
pfUI.minimap.pvpicon:SetParent(pfUI.minimap)
|
||||
pfUI.minimap.pvpicon:SetPoint("BOTTOMRIGHT", pfUI.minimap, "BOTTOMRIGHT", -5, 5)
|
||||
@@ -251,11 +236,7 @@ pfUI:RegisterModule("minimap", function ()
|
||||
pfUI.minimap.pvpicon.texture:SetAllPoints(pfUI.minimap.pvpicon)
|
||||
|
||||
pfUI.minimap.pvpicon:SetScript("OnEvent", function()
|
||||
if C.unitframes.player.showPVPMinimap == "1" and UnitIsPVP("player") then
|
||||
pfUI.minimap.pvpicon:Show()
|
||||
else
|
||||
pfUI.minimap.pvpicon:Hide()
|
||||
end
|
||||
pfUI.minimap.pvpicon:SetShown(C.unitframes.player.showPVPMinimap == "1" and UnitIsPVP("player"))
|
||||
end)
|
||||
|
||||
end)
|
||||
|
||||
+11
-78
@@ -1,39 +1,5 @@
|
||||
pfUI:RegisterModule("mouseover", function ()
|
||||
pfUI.uf.mouseover = CreateFrame("Frame", "pfMouseOver", UIParent)
|
||||
|
||||
-- Prepare a list of units that can be used via SpellTargetUnit
|
||||
local st_units = { [1] = "player", [2] = "target", [3] = "mouseover" }
|
||||
for i=1, MAX_PARTY_MEMBERS do table.insert(st_units, "party"..i) end
|
||||
for i=1, MAX_RAID_MEMBERS do table.insert(st_units, "raid"..i) end
|
||||
|
||||
-- Try to find a valid (friendly) unitstring that can be used for
|
||||
-- SpellTargetUnit(unit) to avoid another target switch
|
||||
local function GetUnitString(unit)
|
||||
for index, unitstr in pairs(st_units) do
|
||||
if UnitIsUnit(unit, unitstr) then
|
||||
return unitstr
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Same as CastSpellByName but with disabled AutoSelfCast
|
||||
local function NoSelfCast(spell, onself)
|
||||
local cvar_selfcast = GetCVar("AutoSelfCast")
|
||||
|
||||
if cvar_selfcast ~= "0" then
|
||||
SetCVar("AutoSelfCast", "0")
|
||||
pcall(CastSpellByName, spell, onself)
|
||||
SetCVar("AutoSelfCast", cvar_selfcast)
|
||||
else
|
||||
CastSpellByName(spell, onself)
|
||||
end
|
||||
end
|
||||
|
||||
_G.SLASH_PFCAST1, _G.SLASH_PFCAST2 = "/pfcast", "/pfmouse"
|
||||
function SlashCmdList.PFCAST(msg)
|
||||
local restore_target = true
|
||||
pfUI.api.RegisterSlashCommand("PFCAST", { "/pfcast", "/pfmouse" }, function(msg)
|
||||
local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg)
|
||||
local unit = "mouseover"
|
||||
|
||||
@@ -50,51 +16,18 @@ pfUI:RegisterModule("mouseover", function ()
|
||||
end
|
||||
end
|
||||
|
||||
-- Nampower: CastSpellByName supports a second unit parameter directly.
|
||||
-- unit is already resolved to "mouseover", "target" or "player" at this point.
|
||||
if not func and GetNampowerVersion then
|
||||
-- Spell-name path: Nampower's CastSpellByName takes a second unit
|
||||
-- parameter directly, no target swap dance required.
|
||||
if not func then
|
||||
CastSpellByName(msg, unit)
|
||||
return
|
||||
end
|
||||
|
||||
-- If target and mouseover are friendly units, we can't use spell target as it
|
||||
-- would cast on the target instead of the mouseover. However, if the mouseover
|
||||
-- is friendly and the target is not, we can try to obtain the best unitstring
|
||||
-- for the later SpellTargetUnit() call.
|
||||
local unitstr = not UnitCanAssist("player", "target") and UnitCanAssist("player", unit) and GetUnitString(unit)
|
||||
|
||||
if UnitIsUnit("target", unit) or (not func and unitstr) then
|
||||
-- no target change required, we can either use spell target
|
||||
-- or the unit is already our current target.
|
||||
restore_target = false
|
||||
else
|
||||
-- The spelltarget can't be used here, we need to switch
|
||||
-- and restore the target during spell cast
|
||||
TargetUnit(unit)
|
||||
end
|
||||
|
||||
if func then
|
||||
func()
|
||||
else
|
||||
-- write temporary unit name
|
||||
pfUI.uf.mouseover.unit = unit
|
||||
|
||||
-- cast without self cast cvar setting
|
||||
-- to allow spells to use spelltarget
|
||||
NoSelfCast(msg)
|
||||
|
||||
-- set spell target to unitstring (or selfcast)
|
||||
if SpellIsTargeting() then SpellTargetUnit(unitstr or "player") end
|
||||
|
||||
-- clean up spell target in error case
|
||||
if SpellIsTargeting() then SpellStopTargeting() end
|
||||
|
||||
-- remove temporary mouseover unit
|
||||
pfUI.uf.mouseover.unit = nil
|
||||
end
|
||||
|
||||
if restore_target then
|
||||
TargetLastTarget()
|
||||
end
|
||||
end
|
||||
-- Macro path: switch target so the macro's spell calls land on `unit`,
|
||||
-- then restore.
|
||||
local restore_target = not UnitIsUnit("target", unit)
|
||||
if restore_target then TargetUnit(unit) end
|
||||
func()
|
||||
if restore_target then TargetLastTarget() end
|
||||
end, true)
|
||||
end)
|
||||
+342
-453
File diff suppressed because it is too large
Load Diff
+19
-220
@@ -15,8 +15,7 @@ pfUI:RegisterModule("nampower", function ()
|
||||
|
||||
pfUI.spellqueue = CreateFrame("Frame", "pfSpellQueue", UIParent)
|
||||
pfUI.spellqueue:SetFrameStrata("HIGH")
|
||||
pfUI.spellqueue:SetWidth(size)
|
||||
pfUI.spellqueue:SetHeight(size)
|
||||
pfUI.spellqueue:SetSize(size, size)
|
||||
pfUI.spellqueue:Hide()
|
||||
|
||||
-- Position near player castbar if available
|
||||
@@ -53,8 +52,7 @@ pfUI:RegisterModule("nampower", function ()
|
||||
return
|
||||
end
|
||||
|
||||
local eventCode = arg1
|
||||
local spellId = arg2
|
||||
local eventCode, spellId = arg1, arg2
|
||||
|
||||
if eventCode == NORMAL_QUEUED or eventCode == NON_GCD_QUEUED or eventCode == ON_SWING_QUEUED then
|
||||
local texture = C_Spell.GetSpellTexture(spellId)
|
||||
@@ -74,21 +72,21 @@ pfUI:RegisterModule("nampower", function ()
|
||||
-- Shows when reactive abilities like Overpower, Revenge, Execute are usable
|
||||
if C.unitframes.reactive_indicator == "1" then
|
||||
local size = tonumber(C.unitframes.reactive_size) or 28
|
||||
local _, class = UnitClass("player")
|
||||
local class = UnitClassBase("player")
|
||||
|
||||
-- Reactive spells by class
|
||||
local reactiveSpells = {
|
||||
WARRIOR = {
|
||||
{ name = "Overpower", texture = "Interface\\Icons\\Ability_MeleeDamage" },
|
||||
{ name = "Revenge", texture = "Interface\\Icons\\Ability_Warrior_Revenge" },
|
||||
{ name = "Execute", texture = "Interface\\Icons\\INV_Sword_48" },
|
||||
7384, -- Overpower
|
||||
6572, -- Revenge
|
||||
5283, -- Execute
|
||||
},
|
||||
ROGUE = {
|
||||
{ name = "Riposte", texture = "Interface\\Icons\\Ability_Warrior_Challange" },
|
||||
76, -- Riposte
|
||||
},
|
||||
HUNTER = {
|
||||
{ name = "Mongoose Bite", texture = "Interface\\Icons\\Ability_Hunter_SwiftStrike" },
|
||||
{ name = "Counterattack", texture = "Interface\\Icons\\Ability_Warrior_Challange" },
|
||||
1495, -- Mongoose Bite
|
||||
19306, -- Counterattack
|
||||
},
|
||||
}
|
||||
|
||||
@@ -97,21 +95,19 @@ pfUI:RegisterModule("nampower", function ()
|
||||
pfUI.reactive = CreateFrame("Frame", "pfReactiveIndicator", UIParent)
|
||||
pfUI.reactive:SetFrameStrata("HIGH")
|
||||
local spellCount = table.getn(spells)
|
||||
pfUI.reactive:SetWidth(size * spellCount + 4 * (spellCount - 1))
|
||||
pfUI.reactive:SetHeight(size)
|
||||
pfUI.reactive:SetSize(size * spellCount + 4 * (spellCount - 1), size)
|
||||
pfUI.reactive:SetPoint("CENTER", UIParent, "CENTER", 0, -200)
|
||||
pfUI.reactive:Hide()
|
||||
|
||||
pfUI.reactive.icons = {}
|
||||
for i, spell in ipairs(spells) do
|
||||
local icon = CreateFrame("Frame", nil, pfUI.reactive)
|
||||
icon:SetWidth(size)
|
||||
icon:SetHeight(size)
|
||||
icon:SetSize(size, size)
|
||||
icon:SetPoint("LEFT", pfUI.reactive, "LEFT", (i-1) * (size + 4), 0)
|
||||
|
||||
icon.texture = icon:CreateTexture(nil, "ARTWORK")
|
||||
icon.texture:SetAllPoints(icon)
|
||||
icon.texture:SetTexture(spell.texture)
|
||||
icon.texture:SetTexture(C_Spell.GetSpellTexture(spell))
|
||||
icon.texture:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
icon.glow = icon:CreateTexture(nil, "OVERLAY")
|
||||
@@ -122,7 +118,7 @@ pfUI:RegisterModule("nampower", function ()
|
||||
|
||||
CreateBackdrop(icon)
|
||||
icon:Hide()
|
||||
icon.spellName = spell.name
|
||||
icon.spellName = C_Spell.GetSpellName(spell)
|
||||
pfUI.reactive.icons[i] = icon
|
||||
end
|
||||
|
||||
@@ -132,27 +128,17 @@ pfUI:RegisterModule("nampower", function ()
|
||||
local anyVisible = false
|
||||
for _, icon in ipairs(this.icons) do
|
||||
local usable = C_Spell.IsSpellUsable(icon.spellName)
|
||||
if usable then
|
||||
icon:Show()
|
||||
anyVisible = true
|
||||
else
|
||||
icon:Hide()
|
||||
end
|
||||
end
|
||||
if anyVisible then
|
||||
this:Show()
|
||||
else
|
||||
this:Hide()
|
||||
icon:SetShown(usable)
|
||||
anyVisible = anyVisible or usable
|
||||
end
|
||||
this:SetShown(anyVisible)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- /disenchantall slash command (DisenchantAll is Nampower-provided)
|
||||
if DisenchantAll then
|
||||
_G.SLASH_PFDISENCHANTALL1 = "/disenchantall"
|
||||
_G.SLASH_PFDISENCHANTALL2 = "/dea"
|
||||
SlashCmdList["PFDISENCHANTALL"] = function(msg)
|
||||
pfUI.api.RegisterSlashCommand("PFDISENCHANTALL", { "/disenchantall", "/dea" }, function(msg)
|
||||
-- DisenchantAll(itemIdOrName | quality, [includeSoulbound]).
|
||||
-- Quality is a string keyword ("greens", "blues", "purples", or pipe-
|
||||
-- combined). Numbers are interpreted as item IDs, not quality levels.
|
||||
@@ -160,195 +146,8 @@ pfUI:RegisterModule("nampower", function ()
|
||||
local arg = (msg and msg ~= "") and msg or "greens"
|
||||
local target = tonumber(arg) or arg
|
||||
DisenchantAll(target)
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: DisenchantAll(" .. tostring(target) .. ")")
|
||||
end
|
||||
print("|cff33ffccpfUI|r: DisenchantAll(" .. tostring(target) .. ")")
|
||||
end, true)
|
||||
end
|
||||
|
||||
-- Druid Secondary Mana Bar
|
||||
-- Shows base mana when druid is in shapeshift form (Bear/Cat uses Rage/Energy)
|
||||
-- Uses Nampower's GetUnitField to get base mana values
|
||||
-- Fully self-contained: uses its own config settings from C.unitframes.druidmana*
|
||||
if GetUnitField and pfUI.uf and pfUI_config.unitframes.druidmanabar == "1" then
|
||||
local rawborder, default_border = GetBorderSize("unitframes")
|
||||
local DC = C.unitframes -- druid mana config lives here as druidmana* keys
|
||||
|
||||
-- Shared helper: create a druid mana bar on a unit frame
|
||||
local function CreateDruidManaBar(parent, unit)
|
||||
if not parent then return nil end
|
||||
|
||||
local parentConfig = parent.config
|
||||
|
||||
-- Read own config values
|
||||
local dmHeight = tonumber(DC.druidmanaheight) or 10
|
||||
local dmWidth = DC.druidmanawidth or "-1"
|
||||
local dmOffX = tonumber(DC.druidmanaoffx) or 0
|
||||
local dmOffY = tonumber(DC.druidmanaoffy) or 0
|
||||
local dmSpace = tonumber(DC.druidmanaspace) or -3
|
||||
local dmTexture = DC.druidmanatexture or "Interface\\AddOns\\pfUI\\img\\bar"
|
||||
|
||||
local bar = CreateFrame("StatusBar", "pfDruidMana_" .. unit, parent)
|
||||
bar:SetFrameStrata(parent:GetFrameStrata())
|
||||
bar:SetFrameLevel(parent:GetFrameLevel() + 5)
|
||||
bar:SetStatusBarTexture(pfUI.media[dmTexture] or dmTexture)
|
||||
|
||||
-- Bar color: use same manacolor logic as the normal power bar
|
||||
local manacolor = parentConfig.defcolor == "0" and parentConfig.manacolor or C.unitframes.manacolor
|
||||
local r, g, b, a = pfUI.api.strsplit(",", manacolor)
|
||||
bar:SetStatusBarColor(tonumber(r) or .25, tonumber(g) or .25, tonumber(b) or 1, tonumber(a) or 1)
|
||||
|
||||
-- Size: own width/height, fallback to parent power bar width if -1
|
||||
local width = dmWidth ~= "-1" and tonumber(dmWidth) or nil
|
||||
if width then
|
||||
bar:SetWidth(width)
|
||||
end
|
||||
bar:SetHeight(dmHeight)
|
||||
|
||||
-- Position below the power bar with own spacing + offsets
|
||||
local spacing = -2 * default_border - dmSpace
|
||||
if width then
|
||||
-- Fixed width: use single point with offset
|
||||
bar:SetPoint("TOP", parent.power, "BOTTOM", dmOffX, spacing + dmOffY)
|
||||
else
|
||||
-- Auto width: anchor to both sides of power bar
|
||||
bar:SetPoint("TOPLEFT", parent.power, "BOTTOMLEFT", dmOffX, spacing + dmOffY)
|
||||
bar:SetPoint("TOPRIGHT", parent.power, "BOTTOMRIGHT", dmOffX, spacing + dmOffY)
|
||||
end
|
||||
bar:Hide()
|
||||
|
||||
CreateBackdrop(bar)
|
||||
CreateBackdropShadow(bar)
|
||||
|
||||
-- Font settings (same logic as power bar)
|
||||
local fontname = pfUI.font_unit
|
||||
local fontsize = tonumber(pfUI_config.global.font_unit_size)
|
||||
local fontstyle = pfUI_config.global.font_unit_style
|
||||
|
||||
if parentConfig.customfont == "1" then
|
||||
fontname = pfUI.media[parentConfig.customfont_name]
|
||||
fontsize = tonumber(parentConfig.customfont_size)
|
||||
fontstyle = parentConfig.customfont_style
|
||||
end
|
||||
|
||||
-- Text color (always mana-colored)
|
||||
local tr, tg, tb = ManaBarColor[0].r, ManaBarColor[0].g, ManaBarColor[0].b
|
||||
if C.unitframes.pastel == "1" then
|
||||
tr, tg, tb = (tr + .75) * .5, (tg + .75) * .5, (tb + .75) * .5
|
||||
end
|
||||
|
||||
-- Single center text showing current/max
|
||||
bar.text = bar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
bar.text:SetFontObject(GameFontWhite)
|
||||
bar.text:SetFont(fontname, fontsize, fontstyle)
|
||||
bar.text:SetPoint("CENTER", bar, "CENTER", 0, 0)
|
||||
bar.text:SetJustifyH("CENTER")
|
||||
bar.text:SetTextColor(tr, tg, tb, 1)
|
||||
|
||||
return bar
|
||||
end
|
||||
|
||||
-- Shared helper: update druid mana bar values and text
|
||||
local function UpdateDruidManaBar(bar, unit)
|
||||
if not UnitExists(unit) then
|
||||
bar:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- For non-player units, only show if the target is a Druid
|
||||
if unit ~= "player" then
|
||||
local _, unitClass = UnitClass(unit)
|
||||
if unitClass ~= "DRUID" then
|
||||
bar:Hide()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local powerType = UnitPowerType(unit)
|
||||
|
||||
-- Only show when NOT using mana (i.e., in Bear/Cat form)
|
||||
if powerType == 0 then
|
||||
bar:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- Get base mana using Nampower's GetUnitField
|
||||
local baseMana, baseMaxMana
|
||||
local guid = UnitGUID(unit)
|
||||
|
||||
if guid then
|
||||
baseMana = GetUnitField(guid, "power1")
|
||||
baseMaxMana = GetUnitField(guid, "maxPower1")
|
||||
end
|
||||
|
||||
-- Round down power values (Nampower can return decimals)
|
||||
if baseMana then baseMana = math.floor(baseMana) end
|
||||
if baseMaxMana then baseMaxMana = math.floor(baseMaxMana) end
|
||||
|
||||
if type(baseMana) ~= "number" or type(baseMaxMana) ~= "number" or baseMaxMana == 0 then
|
||||
bar:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- Update bar
|
||||
bar:SetMinMaxValues(0, baseMaxMana)
|
||||
bar:SetValue(baseMana)
|
||||
|
||||
-- Always show current/max
|
||||
bar.text:SetText(string.format("%s/%s", Abbreviate(baseMana), Abbreviate(baseMaxMana)))
|
||||
|
||||
bar:Show()
|
||||
end
|
||||
|
||||
-- ===== Player Druid Mana Bar =====
|
||||
local _, playerClass = UnitClass("player")
|
||||
if pfUI.uf.player and playerClass == "DRUID" then
|
||||
local playerMana = CreateDruidManaBar(pfUI.uf.player, "player")
|
||||
|
||||
if playerMana then
|
||||
playerMana:RegisterEvent("UNIT_MANA")
|
||||
playerMana:RegisterEvent("UNIT_MAXMANA")
|
||||
playerMana:RegisterEvent("UNIT_DISPLAYPOWER")
|
||||
playerMana:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
|
||||
playerMana:RegisterEvent("PLAYER_LOGOUT")
|
||||
playerMana:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_LOGOUT" then
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
return
|
||||
end
|
||||
if arg1 == nil or arg1 == "player" then
|
||||
UpdateDruidManaBar(playerMana, "player")
|
||||
end
|
||||
end)
|
||||
|
||||
-- Initial update
|
||||
UpdateDruidManaBar(playerMana, "player")
|
||||
end
|
||||
end
|
||||
|
||||
-- ===== Target Druid Mana Bar =====
|
||||
if pfUI.uf.target then
|
||||
local targetMana = CreateDruidManaBar(pfUI.uf.target, "target")
|
||||
|
||||
if targetMana then
|
||||
targetMana:RegisterEvent("UNIT_MANA")
|
||||
targetMana:RegisterEvent("UNIT_MAXMANA")
|
||||
targetMana:RegisterEvent("UNIT_DISPLAYPOWER")
|
||||
targetMana:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
targetMana:RegisterEvent("PLAYER_LOGOUT")
|
||||
targetMana:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_LOGOUT" then
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
return
|
||||
end
|
||||
if event == "PLAYER_TARGET_CHANGED" or arg1 == nil or arg1 == "target" then
|
||||
UpdateDruidManaBar(targetMana, "target")
|
||||
end
|
||||
end)
|
||||
|
||||
-- Initial update
|
||||
UpdateDruidManaBar(targetMana, "target")
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,68 @@
|
||||
pfUI:RegisterModule("newitem", function ()
|
||||
if not pfUI.bag then return end
|
||||
if C.appearance.bags.newitem ~= "1" then return end
|
||||
|
||||
pfUI.newitem = {}
|
||||
|
||||
local r, g, b, a = pfUI.api.GetStringColor(C.appearance.bags.newitem_color)
|
||||
|
||||
function pfUI.newitem:UpdateSlot(bag, slot)
|
||||
if bag < 0 or bag > 4 then return end
|
||||
if not pfUI.bags[bag] then return end
|
||||
if not pfUI.bags[bag].slots[slot] then return end
|
||||
|
||||
local frame = pfUI.bags[bag].slots[slot].frame
|
||||
|
||||
if frame.hasItem and C_NewItems.IsNewItem(bag, slot) then
|
||||
if not frame.newitem then
|
||||
local glow = frame:CreateTexture(nil, "OVERLAY")
|
||||
glow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
|
||||
glow:SetBlendMode("ADD")
|
||||
glow:SetVertexColor(r, g, b, a)
|
||||
glow:SetPoint("CENTER", frame, "CENTER")
|
||||
glow:Hide()
|
||||
glow.RefreshSize = function(g)
|
||||
local w = g:GetParent():GetWidth()
|
||||
if w > 0 then g:SetSize(w * 1.8, w * 1.8) end
|
||||
end
|
||||
frame.newitem = glow
|
||||
|
||||
frame:HookScript("OnEnter", function()
|
||||
C_NewItems.RemoveNewItem(bag, slot)
|
||||
end)
|
||||
end
|
||||
frame.newitem:RefreshSize()
|
||||
frame.newitem:Show()
|
||||
elseif frame.newitem and frame.newitem:IsShown() then
|
||||
frame.newitem:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- The new-item set can change without any slot's contents changing (an item
|
||||
-- acknowledged, pruned when it leaves the bags, or ClearAll) -- re-evaluate
|
||||
-- every decorated slot when that happens.
|
||||
function pfUI.newitem:RefreshAll()
|
||||
for bag in pairs(pfUI.bags) do
|
||||
local slots = pfUI.bags[bag] and pfUI.bags[bag].slots
|
||||
if slots then
|
||||
for slot in pairs(slots) do
|
||||
pfUI.newitem:UpdateSlot(bag, slot)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- per-slot: pfUI re-runs UpdateSlot whenever a slot's contents change.
|
||||
hooksecurefunc(pfUI.bag, "UpdateSlot", function(self, bag, slot)
|
||||
pfUI.newitem:UpdateSlot(bag, slot)
|
||||
end)
|
||||
|
||||
EventRegistry:RegisterFrameEventAndCallback("BAG_NEW_ITEMS_UPDATED", function()
|
||||
pfUI.newitem:RefreshAll()
|
||||
end)
|
||||
|
||||
pfUI.events:RegisterCallback("bag:closed", function(_, object)
|
||||
if object then return end
|
||||
C_NewItems.ClearAll()
|
||||
end, "newitem")
|
||||
end)
|
||||
+27
-55
@@ -37,18 +37,12 @@ pfUI:RegisterModule("panel", function()
|
||||
return
|
||||
end
|
||||
if arg1 == "LeftButton" then
|
||||
if widget.timerFrame:IsShown() then
|
||||
widget.timerFrame:Hide()
|
||||
else
|
||||
widget.timerFrame:Show()
|
||||
end
|
||||
widget.timerFrame:SetShown(not widget.timerFrame:IsShown())
|
||||
elseif arg1 == "RightButton" then
|
||||
widget.timerFrame.Snapshot = GetTime()
|
||||
end
|
||||
end
|
||||
widget:SetScript("OnUpdate",function()
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
|
||||
|
||||
C_Timer.NewTicker(1, function()
|
||||
local secondsenabled = C.panel.seconds == "1"
|
||||
local fmt
|
||||
if C.global.twentyfour == "0" then
|
||||
@@ -62,8 +56,7 @@ pfUI:RegisterModule("panel", function()
|
||||
|
||||
widget.timerFrame = CreateFrame("Frame", "pfUITimer", UIParent)
|
||||
widget.timerFrame:Hide()
|
||||
widget.timerFrame:SetWidth(120)
|
||||
widget.timerFrame:SetHeight(35)
|
||||
widget.timerFrame:SetSize(120, 35)
|
||||
widget.timerFrame:SetPoint("TOP", 0, -100)
|
||||
UpdateMovable(widget.timerFrame)
|
||||
|
||||
@@ -103,11 +96,9 @@ pfUI:RegisterModule("panel", function()
|
||||
pfUI.panel:OutputPanel("combat", T["Combat"] .. ": " .. NOT_APPLICABLE)
|
||||
end
|
||||
end)
|
||||
widget.combat:SetScript("OnUpdate", function()
|
||||
if not this.tick then this.tick = GetTime() end
|
||||
if GetTime() <= this.tick + 1 then return else this.tick = GetTime() end
|
||||
if this.combat then
|
||||
pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - this.combat)))
|
||||
C_Timer.NewTicker(1, function()
|
||||
if widget.combat.combat then
|
||||
pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - widget.combat.combat)))
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -153,9 +144,7 @@ pfUI:RegisterModule("panel", function()
|
||||
pfUI.addons:Show()
|
||||
end
|
||||
end
|
||||
widget:SetScript("OnUpdate",function()
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
|
||||
|
||||
C_Timer.NewTicker(1, function()
|
||||
fps = floor(GetFramerate())
|
||||
_, _, lag = GetNetStats()
|
||||
|
||||
@@ -307,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
|
||||
|
||||
@@ -326,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
|
||||
@@ -346,7 +327,7 @@ pfUI:RegisterModule("panel", function()
|
||||
widget:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
widget.Tooltip = function()
|
||||
-- skip without guild
|
||||
if not GetGuildInfo("player") then return end
|
||||
if not IsInGuild() then return end
|
||||
|
||||
local raidparty = {}
|
||||
for i=1,4 do -- detect people in group
|
||||
@@ -398,7 +379,7 @@ pfUI:RegisterModule("panel", function()
|
||||
end
|
||||
widget.Click = function() ToggleFriendsFrame(3) end
|
||||
widget:SetScript("OnEvent", function()
|
||||
if GetGuildInfo("player") then
|
||||
if IsInGuild() then
|
||||
local count = 0
|
||||
for i = 1, GetNumGuildMembers() do
|
||||
local _, _, _, _, _, _, _, _, online = GetGuildRosterInfo(i)
|
||||
@@ -411,9 +392,8 @@ pfUI:RegisterModule("panel", function()
|
||||
end
|
||||
end)
|
||||
|
||||
widget:SetScript("OnUpdate",function()
|
||||
if ( this.tick or 60) > GetTime() then return else this.tick = GetTime() + 60 end
|
||||
if GetGuildInfo("player") then GuildRoster() end
|
||||
C_Timer.NewTicker(60, function()
|
||||
if IsInGuild() then GuildRoster() end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -424,7 +404,8 @@ pfUI:RegisterModule("panel", function()
|
||||
widget:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
widget:RegisterEvent("PLAYER_DEAD")
|
||||
widget:RegisterEvent("PLAYER_UNGHOST")
|
||||
widget:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||||
widget:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- ClassicAPI: equip/unequip changes total durability
|
||||
widget:RegisterEvent("UPDATE_INVENTORY_DURABILITY") -- ClassicAPI: combat wear / repair
|
||||
|
||||
widget.Click = function() ToggleCharacter("PaperDollFrame") end
|
||||
widget.Tooltip = function()
|
||||
@@ -456,8 +437,6 @@ pfUI:RegisterModule("panel", function()
|
||||
end
|
||||
end
|
||||
widget:SetScript("OnEvent", function()
|
||||
if event == "UNIT_INVENTORY_CHANGED" and arg1 ~= "player" then return end
|
||||
|
||||
local totalCurr, totalMax = 0, 0
|
||||
for id = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do
|
||||
local cur, max = GetInventoryItemDurability(id)
|
||||
@@ -493,11 +472,7 @@ pfUI:RegisterModule("panel", function()
|
||||
GameTooltip:Show()
|
||||
end
|
||||
widget.Click = function()
|
||||
if WorldMapFrame:IsShown() then
|
||||
WorldMapFrame:Hide()
|
||||
else
|
||||
WorldMapFrame:Show()
|
||||
end
|
||||
WorldMapFrame:SetShown(not WorldMapFrame:IsShown())
|
||||
end
|
||||
widget:SetScript("OnEvent", function()
|
||||
pfUI.panel:OutputPanel("zone", GetMinimapZoneText(), widget.Tooltip, widget.Click)
|
||||
@@ -533,7 +508,7 @@ pfUI:RegisterModule("panel", function()
|
||||
-- Hearthstone bind location
|
||||
local hearth = CreateFrame("Frame", "pfPanelBindLocation", UIParent)
|
||||
hearth:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
hearth:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
hearth:RegisterEvent("HEARTHSTONE_BOUND")
|
||||
hearth:SetScript("OnEvent", function()
|
||||
pfUI.panel:OutputPanel("bindlocation", T["Hearthstone"] .. ": " .. (GetBindLocation() or T["Not Set"]))
|
||||
end)
|
||||
@@ -646,8 +621,7 @@ pfUI:RegisterModule("panel", function()
|
||||
local frame = CreateFrame("Button", nil, parent)
|
||||
frame:SetFrameLevel(0)
|
||||
frame:ClearAllPoints()
|
||||
frame:SetWidth(width)
|
||||
frame:SetHeight(parent:GetHeight())
|
||||
frame:SetSize(width, parent:GetHeight())
|
||||
frame:SetPoint(location, 0, 0)
|
||||
frame.text = frame:CreateFontString("Status", "LOW", "GameFontNormal")
|
||||
frame.text:ClearAllPoints()
|
||||
@@ -688,8 +662,7 @@ pfUI:RegisterModule("panel", function()
|
||||
local imgstring = "img:" .. leftright
|
||||
parent.texture:SetTexture(pfUI.media[imgstring])
|
||||
parent.texture:SetPoint("CENTER", 0, 0)
|
||||
parent.texture:SetWidth(8)
|
||||
parent.texture:SetHeight(8)
|
||||
parent.texture:SetSize(8, 8)
|
||||
parent.texture:SetVertexColor(.25,.25,.25,1)
|
||||
return parent.texture
|
||||
end
|
||||
@@ -711,7 +684,7 @@ pfUI:RegisterModule("panel", function()
|
||||
UpdateMovable(pfUI.panel.left)
|
||||
|
||||
pfUI.panel.left.hide:SetScript("OnClick", function()
|
||||
if pfUI.chat.left:IsShown() then pfUI.chat.left:Hide() else pfUI.chat.left:Show() end
|
||||
pfUI.chat.left:SetShown(not pfUI.chat.left:IsShown())
|
||||
end)
|
||||
|
||||
if not pfUI.chat then pfUI.panel.left.hide:Hide() end
|
||||
@@ -748,7 +721,7 @@ pfUI:RegisterModule("panel", function()
|
||||
UpdateMovable(pfUI.panel.right)
|
||||
|
||||
pfUI.panel.right.hide:SetScript("OnClick", function()
|
||||
if pfUI.chat.right:IsShown() then pfUI.chat.right:Hide() else pfUI.chat.right:Show() end
|
||||
pfUI.chat.right:SetShown(not pfUI.chat.right:IsShown())
|
||||
end)
|
||||
|
||||
if not pfUI.chat then pfUI.panel.right.hide:Hide() end
|
||||
@@ -797,8 +770,7 @@ pfUI:RegisterModule("panel", function()
|
||||
pfUI.panel.microbutton = CreateFrame("Frame", "pfPanelMicroButton", UIParent)
|
||||
pfUI.panel.microbutton:SetPoint("TOP", pfUI.panel.minimap, "BOTTOM", 0, -2*default_border)
|
||||
UpdateMovable(pfUI.panel.microbutton)
|
||||
pfUI.panel.microbutton:SetHeight(23)
|
||||
pfUI.panel.microbutton:SetWidth(145)
|
||||
pfUI.panel.microbutton:SetSize(145, 23)
|
||||
pfUI.panel.microbutton:SetFrameStrata("MEDIUM")
|
||||
|
||||
for i=1,table.getn(MICRO_BUTTONS) do
|
||||
|
||||
+42
-80
@@ -34,54 +34,25 @@ pfUI:RegisterModule("player", function ()
|
||||
playerFrame.infoTopCenterText:SetHeight(14)
|
||||
end
|
||||
|
||||
local _, myclass = UnitClass("player")
|
||||
local myclass = UnitClassBase("player")
|
||||
playerFrame.myclass = myclass
|
||||
playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER"
|
||||
|
||||
-- Compute class-based casting speed modifier and cache on the frame.
|
||||
-- This is re-evaluated on LEARNED_SPELL_IN_TAB (with 1s delay) so talent changes are handled.
|
||||
-- Not sure if there are any other effects that give % cast reduction time
|
||||
local function UpdatePlayerModCastingTime()
|
||||
playerFrame.modCastingTime = 1
|
||||
if myclass == "MAGE" then
|
||||
local _, _, _, _, acceleratedArcana = GetTalentInfo(1, 16)
|
||||
if acceleratedArcana and acceleratedArcana > 0 then
|
||||
playerFrame.modCastingTime = 0.95
|
||||
end
|
||||
elseif myclass == "WARLOCK" then
|
||||
local _, _, _, _, rapidDeter = GetTalentInfo(1, 14)
|
||||
if rapidDeter and rapidDeter > 0 then
|
||||
playerFrame.modCastingTime = 1 - (rapidDeter * 0.03)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local talentFrame = CreateFrame("Frame")
|
||||
talentFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
talentFrame:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
||||
talentFrame:SetScript("OnEvent", function()
|
||||
-- Delay 1s for both PLAYER_ENTERING_WORLD and LEARNED_SPELL_IN_TAB
|
||||
local checkAt = GetTime() + 1
|
||||
talentFrame:SetScript("OnUpdate", function()
|
||||
if GetTime() >= checkAt then
|
||||
talentFrame:SetScript("OnUpdate", nil)
|
||||
UpdatePlayerModCastingTime()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset
|
||||
local function cfgColorToHex(colorStr)
|
||||
if not colorStr or colorStr == "" then return nil end
|
||||
local r, g, b = strsplit(",", colorStr)
|
||||
r, g, b = tonumber(r), tonumber(g), tonumber(b)
|
||||
if not r or not g or not b then return nil end
|
||||
return string.format("%02X%02X%02X", r * 255, g * 255, b * 255)
|
||||
end
|
||||
local cfgColorToHexTable = setmetatable({}, {
|
||||
__index = function (t, colorStr)
|
||||
if not colorStr or colorStr == "" then return nil end
|
||||
local r, g, b = GetStringColor(colorStr)
|
||||
if not r or not g or not b then return nil end
|
||||
local hex = C_ColorUtil.GenerateTextColorCode({ r=r, g = g, b=b})
|
||||
rawset(t, colorStr, hex)
|
||||
return hex
|
||||
end
|
||||
})
|
||||
|
||||
-- SP school colors indexed by GetSpellPower("net") return order
|
||||
-- SP school colors indexed by GetSpellBonusDamage's 1-based school order
|
||||
-- (1=phys, 2=holy, 3=fire, 4=nature, 5=frost, 6=shadow, 7=arcane)
|
||||
local spColors = { "FFFFFF", "FFFF80", "FF8000", "4DFF4D", "80FFFF", "9482C9", "FFFFFF" }
|
||||
local spColors = { "FFFFFFFF", "FFFFFF80", "FFFF8000", "FF4DFF4D", "FF80FFFF", "FF9482C9", "FFFFFFFF" }
|
||||
|
||||
-- Default SP school per class used as tiebreaker when multiple schools are equal
|
||||
local spDefaultSchool = {
|
||||
@@ -92,54 +63,45 @@ pfUI:RegisterModule("player", function ()
|
||||
|
||||
-- Compute and cache the haste/SP text; called from OnUpdate, throttled to 0.25s
|
||||
local function UpdateInfoText()
|
||||
if not GetUnitField then return end -- do nothing for older nampower
|
||||
|
||||
local cfg = playerFrame.config
|
||||
if not cfg then
|
||||
return
|
||||
end
|
||||
local hasteMode = cfg.display_haste -- "0"=none, "1"=modCastSpeed, "2"=modCastSpeed*modCastingTime
|
||||
local showSP = cfg.display_spellpower == "1"
|
||||
|
||||
-- display_haste: "0"=hidden, "1"=show cast-speed haste (UnitSpellHaste,
|
||||
-- from UNIT_MOD_CAST_SPEED). Talent/spell-specific cast-time reductions
|
||||
-- show up in the actual cast bar via C_Spell.UnitCastingInfo; folding them
|
||||
-- in here too was mixing two different concepts into one number.
|
||||
local isSpellCaster = playerFrame.isSpellCaster
|
||||
if (hasteMode == "0" or not isSpellCaster) and not showSP then
|
||||
local showHaste = isSpellCaster and cfg.display_haste == "1"
|
||||
local showSP = isSpellCaster and cfg.display_spellpower == "1"
|
||||
|
||||
if not showHaste and not showSP then
|
||||
playerFrame.infoTopCenterText:SetText("")
|
||||
return
|
||||
end
|
||||
|
||||
local haste = GetUnitField("player", "modCastSpeed")
|
||||
local modCastingTime = playerFrame.modCastingTime or 1
|
||||
local text = ""
|
||||
local parts = {}
|
||||
|
||||
if isSpellCaster and haste then
|
||||
local hasteHex = cfgColorToHex(cfg.display_haste_color) or "FFFFFF"
|
||||
if hasteMode == "1" then
|
||||
text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / haste - 1) * 100)
|
||||
elseif hasteMode == "2" then
|
||||
text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / (haste * modCastingTime) - 1) * 100)
|
||||
end
|
||||
if showHaste then
|
||||
local hex = cfgColorToHexTable[cfg.display_haste_color] or "FFFFFFFF"
|
||||
table.insert(parts, string.format("|c%s%.1f%%|r", hex, UnitSpellHaste("player")))
|
||||
end
|
||||
|
||||
if showSP and isSpellCaster then
|
||||
local schools = { GetSpellPower("net") }
|
||||
local defSchool = spDefaultSchool[myclass] or 2
|
||||
local maxSP = schools[defSchool] or 0
|
||||
local maxColor = spColors[defSchool]
|
||||
if showSP then
|
||||
local school = spDefaultSchool[myclass] or 2
|
||||
local maxSP = GetSpellBonusDamage(school) or 0
|
||||
for i = 2, 7 do -- skip physical (1)
|
||||
local v = schools[i] or 0
|
||||
if v > maxSP then
|
||||
maxSP = v
|
||||
maxColor = spColors[i]
|
||||
end
|
||||
local v = GetSpellBonusDamage(i) or 0
|
||||
if v > maxSP then maxSP, school = v, i end
|
||||
end
|
||||
|
||||
if maxSP > 0 then
|
||||
local spHex = (cfg.display_sp_color_override == "1" and cfgColorToHex(cfg.display_sp_color)) or maxColor
|
||||
if text ~= "" then text = text .. " " end
|
||||
text = text .. string.format("|cff%s+%d SP|r", spHex, maxSP)
|
||||
local hex = (cfg.display_sp_color_override == "1" and cfgColorToHexTable[cfg.display_sp_color]) or spColors[school]
|
||||
table.insert(parts, string.format("|c%s+%d SP|r", hex, maxSP))
|
||||
end
|
||||
end
|
||||
|
||||
playerFrame.infoTopCenterText:SetText(text)
|
||||
playerFrame.infoTopCenterText:SetText(table.concat(parts, " "))
|
||||
end
|
||||
|
||||
-- Keep a reference to the generic UF UpdateConfig so we can chain it
|
||||
@@ -151,21 +113,21 @@ pfUI:RegisterModule("player", function ()
|
||||
end
|
||||
|
||||
-- Add throttle to player frame OnUpdate
|
||||
-- Throttle the unit frame's existing OnUpdate to ~20 FPS so the per-frame
|
||||
-- work stays cheap.
|
||||
if pfUI.uf.player:GetScript("OnUpdate") then
|
||||
local originalOnUpdate = pfUI.uf.player:GetScript("OnUpdate")
|
||||
pfUI.uf.player:SetScript("OnUpdate", function()
|
||||
if (this.throttleTick or 0) > GetTime() then
|
||||
return
|
||||
end
|
||||
this.throttleTick = GetTime() + 0.05 -- Default: 20 FPS
|
||||
if (this.throttleTick or 0) > GetTime() then return end
|
||||
this.throttleTick = GetTime() + 0.05
|
||||
originalOnUpdate()
|
||||
if (this.infoTextTick or 0) <= GetTime() then
|
||||
this.infoTextTick = GetTime() + 0.25 -- Don't need to update haste/SP text as often
|
||||
UpdateInfoText()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Haste / spell-power overlay text — refreshes 4×/sec on its own ticker,
|
||||
-- independent of the unit frame's OnUpdate cadence.
|
||||
C_Timer.NewTicker(0.25, UpdateInfoText)
|
||||
|
||||
-- Replace default's RESET_INSTANCES button with an always working one
|
||||
UnitPopupButtons["RESET_INSTANCES_FIX"] = { text = RESET_INSTANCES, dist = 0 }
|
||||
for id, text in pairs(UnitPopupMenus["SELF"]) do
|
||||
|
||||
+57
-36
@@ -1,8 +1,43 @@
|
||||
pfUI:RegisterModule("questitem", function ()
|
||||
-- [itemID] = { index = questLogIndex, count = requiredCount }. Rebuilt
|
||||
-- on QUEST_LOG_UPDATE from ClassicAPI's per-quest cached requirements.
|
||||
local requiredItems = {}
|
||||
|
||||
local function AddQuest(questID)
|
||||
local details = C_QuestLog.GetQuestDetails(questID)
|
||||
if not details then return false end
|
||||
if details.requirements then
|
||||
for _, req in ipairs(details.requirements) do
|
||||
if req.kind == "item" and req.id and req.id > 0 then
|
||||
requiredItems[req.id] = {
|
||||
questID = questID,
|
||||
title = details.title,
|
||||
level = details.level,
|
||||
count = req.count,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function RemoveQuest(questID)
|
||||
for itemID, entry in pairs(requiredItems) do
|
||||
if entry.questID == questID then requiredItems[itemID] = nil end
|
||||
end
|
||||
end
|
||||
|
||||
local function Seed()
|
||||
for k in pairs(requiredItems) do requiredItems[k] = nil end
|
||||
local complete = true
|
||||
local i = 1
|
||||
while true do
|
||||
local questID = C_QuestLog.GetQuestIDForLogIndex(i)
|
||||
if questID == nil then break end
|
||||
if questID > 0 and not AddQuest(questID) then complete = false end
|
||||
i = i + 1
|
||||
end
|
||||
return complete
|
||||
end
|
||||
|
||||
local function AddTooltip(frame, itemID)
|
||||
if not itemID then return end
|
||||
if C.tooltip.questitem.showquest ~= "1" then return end
|
||||
@@ -19,7 +54,7 @@ pfUI:RegisterModule("questitem", function ()
|
||||
if not entry and not replace then return end
|
||||
|
||||
local quest, level = UNKNOWN, 255
|
||||
if entry then quest, level = GetQuestLogTitle(entry.index) end
|
||||
if entry then quest, level = entry.title, entry.level end
|
||||
if not quest then return end
|
||||
|
||||
local color = GetDifficultyColor(level)
|
||||
@@ -42,48 +77,37 @@ pfUI:RegisterModule("questitem", function ()
|
||||
pfUI.questitem = CreateFrame("Frame", "pfQuestItemScanner", UIParent)
|
||||
pfUI.questitem:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
pfUI.questitem:RegisterEvent("QUEST_LOG_UPDATE")
|
||||
pfUI.questitem:RegisterEvent("QUEST_ACCEPTED")
|
||||
pfUI.questitem:RegisterEvent("QUEST_REMOVED")
|
||||
pfUI.questitem:SetScript("OnEvent", function()
|
||||
-- debounce rebuilds — QUEST_LOG_UPDATE fires in bursts
|
||||
this.run = GetTime() + .5
|
||||
if C.tooltip.questitem.showquest ~= "1" then return end
|
||||
if event == "QUEST_ACCEPTED" then -- arg1 = logIndex, arg2 = questID
|
||||
if not AddQuest(arg2) then -- cache cold (rare) -> reseed
|
||||
this.seeding = true
|
||||
this.run = GetTime() + .5
|
||||
end
|
||||
elseif event == "QUEST_REMOVED" then -- arg1 = questID
|
||||
RemoveQuest(arg1)
|
||||
elseif event == "PLAYER_ENTERING_WORLD" then
|
||||
this.seeding = true -- seed pre-existing quests
|
||||
this.run = GetTime() + .5
|
||||
elseif event == "QUEST_LOG_UPDATE" and this.seeding then
|
||||
this.run = GetTime() + .5 -- keep retrying while cache warms
|
||||
end
|
||||
end)
|
||||
|
||||
pfUI.questitem:SetScript("OnUpdate", function()
|
||||
if C.tooltip.questitem.showquest ~= "1" then return end
|
||||
if not this.run or GetTime() < this.run then return end
|
||||
|
||||
for k in pairs(requiredItems) do requiredItems[k] = nil end
|
||||
|
||||
-- GetQuestIDForLogIndex returns nil past the end, 0 for headers, else
|
||||
-- the questID. GetQuestDetails reads the engine's static-info cache —
|
||||
-- nil if not yet populated; we'll catch it on the next refresh.
|
||||
local i = 1
|
||||
while true do
|
||||
local questID = C_QuestLog.GetQuestIDForLogIndex(i)
|
||||
if questID == nil then break end
|
||||
if questID > 0 then
|
||||
local details = C_QuestLog.GetQuestDetails(questID)
|
||||
if details and details.requirements then
|
||||
for _, req in ipairs(details.requirements) do
|
||||
if req.kind == "item" and req.id and req.id > 0 then
|
||||
requiredItems[req.id] = { index = i, count = req.count }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
this.run = nil
|
||||
if Seed() then this.seeding = nil end
|
||||
end)
|
||||
|
||||
-- reload quest entries on config change
|
||||
pfUI.questitem.UpdateConfig = function()
|
||||
if C.tooltip.questitem.showquest ~= "1" then return end
|
||||
pfUI.questitem.seeding = true
|
||||
pfUI.questitem.run = GetTime() + .5
|
||||
end
|
||||
|
||||
-- regular tooltip: catch every Show via a child frame's OnShow, then ask
|
||||
-- the tooltip directly for the item it's displaying. Replaces a libtooltip
|
||||
-- indirection that did the same query with extra caching layers.
|
||||
pfUI.questitem.tooltip = CreateFrame("Frame", "pfQuestItems", GameTooltip)
|
||||
pfUI.questitem.tooltip:SetScript("OnShow", function()
|
||||
if GameTooltip:HasItem() then
|
||||
@@ -92,9 +116,6 @@ pfUI:RegisterModule("questitem", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
-- itemref tooltip (chat link clicks): hooksecurefunc runs after SetItemRef
|
||||
-- populates ItemRefTooltip, so we just read the item back out of the tooltip
|
||||
-- instead of re-parsing the "item:NNN" out of the link string.
|
||||
hooksecurefunc("SetItemRef", function()
|
||||
if IsModifierKeyDown() then return end
|
||||
if ItemRefTooltip:HasItem() then
|
||||
|
||||
+120
-10
@@ -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)
|
||||
@@ -13,11 +15,32 @@ pfUI:RegisterModule("raid", function ()
|
||||
local rawborder, default_border = GetBorderSize("chat")
|
||||
local cluster = CreateFrame("Frame", "pfRaidCluster", UIParent)
|
||||
cluster:SetFrameLevel(20)
|
||||
cluster:SetWidth(120)
|
||||
cluster:SetHeight(10)
|
||||
cluster:SetSize(120, 10)
|
||||
cluster:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", default_border*2, C.chat.left.height + default_border*5)
|
||||
UpdateMovable(cluster)
|
||||
|
||||
-- Separate, independently-movable block that mirrors the raid grid layout
|
||||
-- for pet frames (raidpet1..40). Defaults to the right of the raid grid.
|
||||
local petcluster = CreateFrame("Frame", "pfRaidPetCluster", UIParent)
|
||||
petcluster:SetFrameLevel(20)
|
||||
petcluster:SetSize(120, 10)
|
||||
petcluster:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", default_border*2 + 300, C.chat.left.height + default_border*5)
|
||||
UpdateMovable(petcluster)
|
||||
|
||||
-- flat pool of pet frames, laid out by LayoutPets (mirror or collapsed)
|
||||
pfUI.uf.raid.pets = {}
|
||||
|
||||
-- 1-based grid slot -> (row, col) for the current fill direction, matching
|
||||
-- the raid grid's own fill loops.
|
||||
local function SlotToCoord(slot, fill, x, y)
|
||||
slot = slot - 1
|
||||
if fill == "VERTICAL" then
|
||||
return floor(slot / y) + 1, mod(slot, y) + 1
|
||||
else
|
||||
return mod(slot, x) + 1, floor(slot / x) + 1
|
||||
end
|
||||
end
|
||||
|
||||
pfUI.uf.raid.tanksfirst = {
|
||||
["PF_TANK_TOGGLE"] = { T["Toggle as Tank"], "toggleTank" }
|
||||
}
|
||||
@@ -28,6 +51,8 @@ pfUI:RegisterModule("raid", function ()
|
||||
function pfUI.uf.raid:UpdateConfig()
|
||||
local rawborder, default_border = GetBorderSize("unitframes")
|
||||
maxraid = tonumber(C.unitframes.maxraid)
|
||||
local showpets = C.unitframes.raidpet.visible == "1"
|
||||
self.showpets = showpets
|
||||
|
||||
for i=1,maxraid do
|
||||
pfUI.uf.raid[i] = pfUI.uf.raid[i] or pfUI.uf:CreateUnitFrame("Raid", i, C.unitframes.raid)
|
||||
@@ -36,6 +61,18 @@ pfUI:RegisterModule("raid", function ()
|
||||
|
||||
pfUI.uf.raid[i]:UpdateConfig()
|
||||
pfUI.uf.raid[i]:UpdateFrameSize()
|
||||
|
||||
if showpets then
|
||||
self.pets[i] = self.pets[i] or pfUI.uf:CreateUnitFrame("RaidPet", i, C.unitframes.raidpet, 0.5)
|
||||
self.pets[i]:SetParent(petcluster)
|
||||
self.pets[i]:SetFrameLevel(5)
|
||||
self.pets[i]:UpdateConfig()
|
||||
self.pets[i]:UpdateFrameSize()
|
||||
elseif self.pets[i] then
|
||||
self.pets[i]:UpdateConfig()
|
||||
self.pets[i]:Hide()
|
||||
RemoveMovable(self.pets[i])
|
||||
end
|
||||
end
|
||||
|
||||
local i = 1
|
||||
@@ -47,6 +84,17 @@ pfUI:RegisterModule("raid", function ()
|
||||
local _, _, x, y = string.find(layout,"(.+)x(.+)")
|
||||
x, y = tonumber(x), tonumber(y)
|
||||
|
||||
if showpets then
|
||||
local petcfg = C.unitframes.raidpet
|
||||
local _, _, px, py = string.find(petcfg.raidlayout, "(.+)x(.+)")
|
||||
self.petgrid = {
|
||||
fill = petcfg.raidfill, x = tonumber(px), y = tonumber(py),
|
||||
pad = tonumber(petcfg.raidpadding) * GetPerfectPixel(),
|
||||
w = self.pets[1]:GetWidth()+2*default_border,
|
||||
h = self.pets[1]:GetHeight()+2*default_border,
|
||||
}
|
||||
end
|
||||
|
||||
if fill == "VERTICAL" then
|
||||
for r=1, x do for g=1, y do
|
||||
if pfUI.uf.raid[i] then
|
||||
@@ -66,6 +114,50 @@ pfUI:RegisterModule("raid", function ()
|
||||
i = i + 1
|
||||
end end
|
||||
end
|
||||
|
||||
self:LayoutPets()
|
||||
|
||||
self:Show()
|
||||
end
|
||||
|
||||
function pfUI.uf.raid:LayoutPets()
|
||||
if not self.showpets or not self.petgrid then return end
|
||||
local grid = self.petgrid
|
||||
|
||||
local function place(pet, cell, id)
|
||||
pet.id = id
|
||||
local r, g = SlotToCoord(cell, grid.fill, grid.x, grid.y)
|
||||
pet:ClearAllPoints()
|
||||
pet:SetPoint("BOTTOMLEFT", petcluster, "BOTTOMLEFT", (r-1)*(grid.pad+grid.w), (g-1)*(grid.pad+grid.h))
|
||||
UpdateMovable(pet, true)
|
||||
pet:UpdateVisibility()
|
||||
end
|
||||
|
||||
if pfUI.uf.showall then
|
||||
for id = 1, maxraid do
|
||||
if self.pets[id] then place(self.pets[id], id, id) 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
|
||||
for id = 1, maxraid do
|
||||
if UnitExists("raidpet"..id) and self.pets[k+1] then
|
||||
k = k + 1
|
||||
place(self.pets[k], k, id)
|
||||
end
|
||||
end
|
||||
for j = k+1, maxraid do
|
||||
if self.pets[j] then self.pets[j].id = 0 self.pets[j]:Hide() end
|
||||
end
|
||||
else
|
||||
-- Mirror: cell N always shows raidpet<N> at a fixed position.
|
||||
for id = 1, maxraid do
|
||||
if self.pets[id] then place(self.pets[id], id, id) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pfUI.uf.raid:UpdateConfig()
|
||||
@@ -76,13 +168,22 @@ pfUI:RegisterModule("raid", function ()
|
||||
frame:UpdateVisibility()
|
||||
end
|
||||
|
||||
-- add units to the beginning of their groups
|
||||
-- add units to their groups; collapse packs everyone into the leading slots
|
||||
function pfUI.uf.raid:AddUnitToGroup(index, group)
|
||||
for subindex = 1, 5 do
|
||||
local ids = subindex + 5*(group-1)
|
||||
if pfUI.uf.raid[ids] and pfUI.uf.raid[ids].id == 0 and pfUI.uf.raid[ids].config.visible == "1" then
|
||||
SetRaidIndex(pfUI.uf.raid[ids], index)
|
||||
return
|
||||
if C.unitframes.raid.collapse == "1" then
|
||||
for ids = 1, maxraid do
|
||||
if pfUI.uf.raid[ids] and pfUI.uf.raid[ids].id == 0 and pfUI.uf.raid[ids].config.visible == "1" then
|
||||
SetRaidIndex(pfUI.uf.raid[ids], index)
|
||||
return
|
||||
end
|
||||
end
|
||||
else
|
||||
for subindex = 1, 5 do
|
||||
local ids = subindex + 5*(group-1)
|
||||
if pfUI.uf.raid[ids] and pfUI.uf.raid[ids].id == 0 and pfUI.uf.raid[ids].config.visible == "1" then
|
||||
SetRaidIndex(pfUI.uf.raid[ids], index)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -92,7 +193,14 @@ pfUI:RegisterModule("raid", function ()
|
||||
pfUI.uf.raid:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
||||
pfUI.uf.raid:RegisterEvent("PARTY_LEADER_CHANGED")
|
||||
pfUI.uf.raid:RegisterEvent("VARIABLES_LOADED")
|
||||
pfUI.uf.raid:RegisterEvent("UNIT_PET")
|
||||
pfUI.uf.raid:SetScript("OnEvent", function()
|
||||
if event == "UNIT_PET" then
|
||||
if this.showpets and C.unitframes.raidpet.collapse == "1" then
|
||||
this:LayoutPets()
|
||||
end
|
||||
return
|
||||
end
|
||||
this:Show()
|
||||
-- Debounce: delay update by 0.5s to batch rapid roster changes (mass swaps)
|
||||
this.pendingUpdate = GetTime() + 0.5
|
||||
@@ -106,7 +214,7 @@ pfUI:RegisterModule("raid", function ()
|
||||
this.pendingUpdate = nil
|
||||
|
||||
-- don't proceed without raid
|
||||
if not UnitInRaid("player") then return end
|
||||
if not IsInRaid() then return end
|
||||
|
||||
-- clear all existing frames
|
||||
for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end
|
||||
@@ -127,6 +235,8 @@ pfUI:RegisterModule("raid", function ()
|
||||
end
|
||||
end
|
||||
|
||||
this:LayoutPets()
|
||||
|
||||
-- Smart GUID-based updates: only refresh frames where unit changed
|
||||
if pfUI.uf.guidTracker then
|
||||
local tracker = pfUI.uf.guidTracker
|
||||
|
||||
+9
-4
@@ -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 = {}
|
||||
@@ -42,7 +46,8 @@ pfUI:RegisterModule("roll", function ()
|
||||
end
|
||||
|
||||
local _, _, itemLink = string.find(hyperlink, "(item:%d+:%d+:%d+:%d+)")
|
||||
local itemName = GetItemInfo(itemLink)
|
||||
local itemName = C_Item.GetItemInfo(itemLink)
|
||||
if not itemName then return end -- uncached item: avoid cache[nil] "table index is nil"
|
||||
|
||||
-- delete obsolete tables
|
||||
if pfUI.roll.cache[itemName] and pfUI.roll.cache[itemName]["TIMESTAMP"] < GetTime() - 60 then
|
||||
@@ -234,7 +239,7 @@ pfUI:RegisterModule("roll", function ()
|
||||
f.time.bar:SetAllPoints(f.time)
|
||||
f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"])
|
||||
f.time.bar:SetMinMaxValues(0, 100)
|
||||
local r, g, b, a = strsplit(",", C.appearance.border.color)
|
||||
local r, g, b, a = GetStringColor(C.appearance.border.color)
|
||||
f.time.bar:SetStatusBarColor(r, g, b)
|
||||
f.time.bar:SetValue(20)
|
||||
f.time.bar:SetScript("OnUpdate", function()
|
||||
|
||||
+137
-14
@@ -1,13 +1,11 @@
|
||||
pfUI:RegisterModule("sellvalue", function ()
|
||||
local function AddVendorPrices(frame, id, count)
|
||||
if not id then return end
|
||||
-- Sell price comes from the engine (item DBC); buy price from pfSellData
|
||||
-- (curated vendor data, since vendor purchase prices aren't a static field).
|
||||
local sell = C_Item.GetItemSellPriceByID(id) or 0
|
||||
local buy = pfSellData[id]
|
||||
if sell == 0 and not buy then return end
|
||||
|
||||
if C.tooltip.vendor.showalways == "1" or IsShiftKeyDown() then
|
||||
if C.tooltip.vendor.showalways == "1" or IsShiftKeyDown() then
|
||||
frame:AddLine(" ")
|
||||
|
||||
if sell > 0 then
|
||||
@@ -25,21 +23,12 @@ pfUI:RegisterModule("sellvalue", function ()
|
||||
frame:AddDoubleLine(T["Buy"] .. ":", CreateGoldString(buy), 1, 1, 1)
|
||||
end
|
||||
end
|
||||
elseif not MerchantFrame:IsShown() and sell > 0 then
|
||||
SetTooltipMoney(frame, sell * count)
|
||||
end
|
||||
frame:Show()
|
||||
end
|
||||
|
||||
pfUI.sellvalue = CreateFrame("Frame", "pfGameTooltip", GameTooltip)
|
||||
pfUI.sellvalue:SetScript("OnShow", function()
|
||||
if GameTooltip:HasItem() then
|
||||
local _, _, id = GameTooltip:GetItem()
|
||||
if id then
|
||||
local count = tonumber(libtooltip:GetItemCount()) or 1
|
||||
AddVendorPrices(GameTooltip, id, math.max(count, 1))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
hooksecurefunc("SetItemRef", function()
|
||||
if IsModifierKeyDown() then return end
|
||||
if ItemRefTooltip:HasItem() then
|
||||
@@ -47,4 +36,138 @@ pfUI:RegisterModule("sellvalue", function ()
|
||||
if id then AddVendorPrices(ItemRefTooltip, id, 1) end
|
||||
end
|
||||
end)
|
||||
|
||||
local TooltipHooks = {
|
||||
SetLootRollItem = {
|
||||
id = GetLootRollItemID,
|
||||
count = function(slot)
|
||||
local _, _, count = GetLootRollItemInfo(slot)
|
||||
return count
|
||||
end
|
||||
},
|
||||
SetLootItem = {
|
||||
id = GetLootSlotItemID,
|
||||
count = function(slot)
|
||||
local _, _, count = GetLootSlotInfo(slot)
|
||||
return count
|
||||
end
|
||||
},
|
||||
SetQuestLogItem = {
|
||||
id = GetQuestLogItemID,
|
||||
count = function(type, index)
|
||||
local itemCount, _;
|
||||
if type == "choice" then
|
||||
_, _, itemCount = GetQuestLogChoiceInfo(index);
|
||||
else
|
||||
_, _, itemCount = GetQuestLogRewardInfo(index)
|
||||
end
|
||||
return itemCount
|
||||
end,
|
||||
},
|
||||
SetQuestItem = {
|
||||
id = GetQuestItemID,
|
||||
count = function(type, index)
|
||||
local _, _, count = GetQuestItemInfo(type, index);
|
||||
return count
|
||||
end,
|
||||
},
|
||||
SetHyperlink = { id = C_Item.GetItemInfoInstant },
|
||||
SetBagItem = {
|
||||
id = C_Container.GetContainerItemID,
|
||||
count = function(container, slot)
|
||||
local _, count = GetContainerItemInfo(container, slot)
|
||||
return count
|
||||
end,
|
||||
},
|
||||
SetInboxItem = {
|
||||
id = GetInboxItemID,
|
||||
count = function(index)
|
||||
local _, _, _, count = GetInboxItem(index)
|
||||
return count
|
||||
end,
|
||||
},
|
||||
SetSendMailItem = {
|
||||
id = function()
|
||||
local _, id = GetSendMailItemLink()
|
||||
return id
|
||||
end,
|
||||
count = function()
|
||||
local _, _, count = GetSendMailItem()
|
||||
return count
|
||||
end,
|
||||
},
|
||||
SetInventoryItem = { id = GetInventoryItemID },
|
||||
SetTradeSkillItem = {
|
||||
id = function(skillIndex, reagentIndex)
|
||||
if reagentIndex then
|
||||
return GetTradeSkillReagentItemID(skillIndex, reagentIndex)
|
||||
else
|
||||
return GetTradeSkillItemID(skillIndex)
|
||||
end
|
||||
end,
|
||||
count = function(skillIndex, reagentIndex)
|
||||
if reagentIndex then
|
||||
local _, _, itemCount = GetTradeSkillReagentInfo(skillIndex, reagentIndex)
|
||||
return itemCount
|
||||
else
|
||||
return GetTradeSkillNumMade(skillIndex)
|
||||
end
|
||||
end,
|
||||
},
|
||||
SetAuctionItem = {
|
||||
id = GetAuctionItemLink,
|
||||
count = function(viewType, index)
|
||||
local _, _, count = GetAuctionItemInfo(viewType, index)
|
||||
return count
|
||||
end,
|
||||
},
|
||||
SetAuctionSellItem = { id = GetAuctionSellItemLink },
|
||||
SetTradePlayerItem = {
|
||||
id = GetTradePlayerItemLink,
|
||||
count = function(id)
|
||||
local _, _, count = GetTradePlayerItemInfo(id)
|
||||
return count
|
||||
end,
|
||||
},
|
||||
SetTradeTargetItem = {
|
||||
id = GetTradeTargetItemLink,
|
||||
count = function(id)
|
||||
local _, _, count = GetTradeTargetItemInfo(id)
|
||||
return count
|
||||
end,
|
||||
},
|
||||
SetMerchantItem = {
|
||||
id = GetMerchantItemID,
|
||||
count = function(index)
|
||||
local _, _, _, itemCount = GetMerchantItemInfo(index)
|
||||
return itemCount
|
||||
end
|
||||
},
|
||||
SetCraftItem = {
|
||||
id = function(recipeIndex, reagentIndex)
|
||||
return GetCraftReagentItemID(recipeIndex, reagentIndex)
|
||||
end
|
||||
},
|
||||
SetBuybackItem = {
|
||||
id = C_MerchantFrame.GetBuybackItemID,
|
||||
count = function(slotIndex)
|
||||
local _, _, _, itemCount = GetBuybackItemInfo(slotIndex)
|
||||
return itemCount
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
local function makeHook(entry)
|
||||
return function(tooltip, arg1, arg2, arg3)
|
||||
AddVendorPrices(tooltip, entry.id(arg1, arg2, arg3), entry.count and entry.count(arg1, arg2, arg3) or 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function HookTooltip(tooltip)
|
||||
for setter, entry in pairs(TooltipHooks) do
|
||||
hooksecurefunc(tooltip, setter, makeHook(entry))
|
||||
end
|
||||
end
|
||||
|
||||
HookTooltip(GameTooltip)
|
||||
end)
|
||||
|
||||
+133
-197
@@ -1,93 +1,78 @@
|
||||
pfUI:RegisterModule("share", function ()
|
||||
local function serialize(tbl, comp, name, ignored, spacing)
|
||||
local spacing = spacing or ""
|
||||
local match = nil
|
||||
local tname = ( spacing == "" and "" or "[\"" ) .. name .. ( spacing == "" and "" or "\"]" )
|
||||
local str = spacing .. tname .. " = {\n"
|
||||
-- Profile sharing rides ClassicAPI's C_EncodingUtil:
|
||||
-- export: diff vs defaults -> SerializeCBOR -> CompressString (zlib)
|
||||
-- -> EncodeBase64, prefixed with the format magic
|
||||
-- import: the reverse, into a plain table — no loadstring, so a
|
||||
-- pasted profile can't execute code
|
||||
-- The Decode/Encode button converts between the transport blob and an
|
||||
-- editable JSON view of the same table.
|
||||
local MAGIC = "!pf1!"
|
||||
|
||||
-- Deep-diff `tbl` against `default`, keeping only changed or added
|
||||
-- values. `ignored` keys are skipped at the top level only (matches the
|
||||
-- old serializer: "position"/"disabled" when Ignore Layout is checked).
|
||||
-- Returns nil when nothing differs.
|
||||
local function DiffConfig(tbl, default, ignored)
|
||||
local diff = nil
|
||||
for k, v in pairs(tbl) do
|
||||
if not ( ignored[k] and spacing == "" ) and ( not comp or not comp[k] or comp[k] ~= tbl[k] ) then
|
||||
if not ( ignored and ignored[k] ) then
|
||||
local dv = default and default[k]
|
||||
if type(v) == "table" then
|
||||
local result = serialize(tbl[k], comp and comp[k], k, ignored, spacing .. " ")
|
||||
if result then
|
||||
match = true
|
||||
str = str .. result
|
||||
local sub = DiffConfig(v, type(dv) == "table" and dv or nil)
|
||||
if sub then
|
||||
diff = diff or {}
|
||||
diff[k] = sub
|
||||
end
|
||||
elseif type(v) == "string" then
|
||||
match = true
|
||||
local escaped = string.gsub(v, "\\", "\\\\")
|
||||
escaped = string.gsub(escaped, "\"", "\\\"")
|
||||
str = str .. spacing .. " [\""..k.."\"] = \"".. escaped .."\",\n"
|
||||
elseif type(v) == "number" then
|
||||
match = true
|
||||
str = str .. spacing .. " [\""..k.."\"] = ".. string.gsub(v, "\\", "\\\\") ..",\n"
|
||||
elseif v ~= dv and ( type(v) == "string" or type(v) == "number" or type(v) == "boolean" ) then
|
||||
diff = diff or {}
|
||||
diff[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
str = str .. spacing .. "}" .. ( spacing == "" and "" or "," ) .. "\n"
|
||||
return match and str or nil
|
||||
return diff
|
||||
end
|
||||
|
||||
local function compress(input)
|
||||
-- based on Rochet2's lzw compression
|
||||
if type(input) ~= "string" then
|
||||
return nil
|
||||
-- EditBoxes don't soft-wrap one giant unbroken line; chunk the blob.
|
||||
local function wrap(str, width)
|
||||
local out = {}
|
||||
for i = 1, strlen(str), width do
|
||||
table.insert(out, strsub(str, i, i + width - 1))
|
||||
end
|
||||
local len = strlen(input)
|
||||
if len <= 1 then
|
||||
return "u"..input
|
||||
end
|
||||
|
||||
local dict = {}
|
||||
for i = 0, 255 do
|
||||
local ic, iic = strchar(i), strchar(i, 0)
|
||||
dict[ic] = iic
|
||||
end
|
||||
local a, b = 0, 1
|
||||
|
||||
local result = {"c"}
|
||||
local resultlen = 1
|
||||
local n = 2
|
||||
local word = ""
|
||||
for i = 1, len do
|
||||
local c = strsub(input, i, i)
|
||||
local wc = word..c
|
||||
if not dict[wc] then
|
||||
local write = dict[word]
|
||||
if not write then
|
||||
return nil
|
||||
end
|
||||
result[n] = write
|
||||
resultlen = resultlen + strlen(write)
|
||||
n = n+1
|
||||
if len <= resultlen then
|
||||
return "u"..input
|
||||
end
|
||||
local str = wc
|
||||
if a >= 256 then
|
||||
a, b = 0, b+1
|
||||
if b >= 256 then
|
||||
dict = {}
|
||||
b = 1
|
||||
end
|
||||
end
|
||||
dict[str] = strchar(a,b)
|
||||
a = a+1
|
||||
word = c
|
||||
else
|
||||
word = wc
|
||||
end
|
||||
end
|
||||
result[n] = dict[word]
|
||||
resultlen = resultlen+strlen(result[n])
|
||||
n = n+1
|
||||
if len <= resultlen then
|
||||
return "u"..input
|
||||
end
|
||||
return table.concat(result)
|
||||
return table.concat(out, "\n")
|
||||
end
|
||||
|
||||
local function Encode(tbl)
|
||||
return MAGIC .. wrap(C_EncodingUtil.EncodeBase64(
|
||||
C_EncodingUtil.CompressString(
|
||||
C_EncodingUtil.SerializeCBOR(tbl))), 92)
|
||||
end
|
||||
|
||||
local function Decode(text)
|
||||
if not text then return nil end
|
||||
text = gsub(text, "%s", "")
|
||||
if strsub(text, 1, strlen(MAGIC)) ~= MAGIC then return nil end
|
||||
local ok, result = pcall(function()
|
||||
return C_EncodingUtil.DeserializeCBOR(
|
||||
C_EncodingUtil.DecompressString(
|
||||
C_EncodingUtil.DecodeBase64(strsub(text, strlen(MAGIC) + 1))))
|
||||
end)
|
||||
if ok and type(result) == "table" then return result end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function DecodeJSON(text)
|
||||
if not text or gsub(text, "%s", "") == "" then return nil end
|
||||
local ok, result = pcall(function()
|
||||
return C_EncodingUtil.DeserializeJSON(text)
|
||||
end)
|
||||
if ok and type(result) == "table" then return result end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Legacy import (pre-!pf1! exports): base64 -> LZW -> Lua source
|
||||
-- "pfUI_config = {...}". The base64 layer is standard and handled by
|
||||
-- C_EncodingUtil.DecodeBase64; only the custom LZW format needs the old
|
||||
-- Lua decoder. Import-only — new exports always use the CBOR pipeline.
|
||||
local function decompress(input)
|
||||
-- based on Rochet2's lzw compression
|
||||
if type(input) ~= "string" or strlen(input) < 1 then
|
||||
@@ -159,92 +144,32 @@ pfUI:RegisterModule("share", function ()
|
||||
return table.concat(result)
|
||||
end
|
||||
|
||||
local function enc(to_encode)
|
||||
local index_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||
local bit_pattern = ''
|
||||
local encoded = ''
|
||||
local trailing = ''
|
||||
local function DecodeLegacy(text)
|
||||
if not text or gsub(text, "%s", "") == "" then return nil end
|
||||
|
||||
for i = 1, string.len(to_encode) do
|
||||
local remaining = tonumber(string.byte(string.sub(to_encode, i, i)))
|
||||
local bin_bits = ''
|
||||
for i = 7, 0, -1 do
|
||||
local current_power = math.pow(2, i)
|
||||
if remaining >= current_power then
|
||||
bin_bits = bin_bits .. '1'
|
||||
remaining = remaining - current_power
|
||||
else
|
||||
bin_bits = bin_bits .. '0'
|
||||
end
|
||||
end
|
||||
bit_pattern = bit_pattern .. bin_bits
|
||||
-- encoded blob? peel base64 + LZW down to Lua source. Raw (already
|
||||
-- decoded) source pastes are accepted as-is.
|
||||
local source = text
|
||||
local stripped = gsub(text, "%s", "")
|
||||
local ok, decoded = pcall(function()
|
||||
return C_EncodingUtil.DecodeBase64(stripped)
|
||||
end)
|
||||
if ok and decoded then
|
||||
local decompressed = decompress(decoded)
|
||||
if decompressed then source = decompressed end
|
||||
end
|
||||
|
||||
if mod(string.len(bit_pattern), 3) == 2 then
|
||||
trailing = '=='
|
||||
bit_pattern = bit_pattern .. '0000000000000000'
|
||||
elseif mod(string.len(bit_pattern), 3) == 1 then
|
||||
trailing = '='
|
||||
bit_pattern = bit_pattern .. '00000000'
|
||||
end
|
||||
local chunk = loadstring(source)
|
||||
if not chunk then return nil end
|
||||
|
||||
local count = 0
|
||||
for i = 1, string.len(bit_pattern), 6 do
|
||||
local byte = string.sub(bit_pattern, i, i+5)
|
||||
local offset = tonumber(tonumber(byte, 2))
|
||||
encoded = encoded .. string.sub(index_table, offset+1, offset+1)
|
||||
count = count + 1
|
||||
if count >= 92 then
|
||||
encoded = encoded .. "\n"
|
||||
count = 0
|
||||
end
|
||||
end
|
||||
|
||||
return string.sub(encoded, 1, -1 - string.len(trailing)) .. trailing
|
||||
end
|
||||
|
||||
local function dec(to_decode)
|
||||
local index_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||
local padded = gsub(to_decode,"%s", "")
|
||||
local unpadded = gsub(padded,"=", "")
|
||||
local bit_pattern = ''
|
||||
local decoded = ''
|
||||
|
||||
to_decode = gsub(to_decode,"\n", "")
|
||||
to_decode = gsub(to_decode," ", "")
|
||||
|
||||
for i = 1, string.len(unpadded) do
|
||||
local char = string.sub(to_decode, i, i)
|
||||
local offset, _ = string.find(index_table, char)
|
||||
if offset == nil then return nil end
|
||||
|
||||
local remaining = tonumber(offset-1)
|
||||
local bin_bits = ''
|
||||
for i = 7, 0, -1 do
|
||||
local current_power = math.pow(2, i)
|
||||
if remaining >= current_power then
|
||||
bin_bits = bin_bits .. '1'
|
||||
remaining = remaining - current_power
|
||||
else
|
||||
bin_bits = bin_bits .. '0'
|
||||
end
|
||||
end
|
||||
|
||||
bit_pattern = bit_pattern .. string.sub(bin_bits, 3)
|
||||
end
|
||||
|
||||
for i = 1, string.len(bit_pattern), 8 do
|
||||
local byte = string.sub(bit_pattern, i, i+7)
|
||||
decoded = decoded .. strchar(tonumber(byte, 2))
|
||||
end
|
||||
|
||||
local padding_length = string.len(padded)-string.len(unpadded)
|
||||
|
||||
if (padding_length == 1 or padding_length == 2) then
|
||||
decoded = string.sub(decoded,1,-2)
|
||||
end
|
||||
|
||||
return decoded
|
||||
-- Sandbox the chunk: an empty environment means it can assign its
|
||||
-- config table but can't reach any global or API function — a legacy
|
||||
-- profile string is data, never code.
|
||||
local sandbox = {}
|
||||
setfenv(chunk, sandbox)
|
||||
if not pcall(chunk) then return nil end
|
||||
if type(sandbox.pfUI_config) == "table" then return sandbox.pfUI_config end
|
||||
return nil
|
||||
end
|
||||
|
||||
do -- Window
|
||||
@@ -309,33 +234,43 @@ pfUI:RegisterModule("share", function ()
|
||||
this:GetParent():UpdateScrollChildRect()
|
||||
this:GetParent():UpdateScrollState()
|
||||
|
||||
local _, error = loadstring(f.scroll.text:GetText())
|
||||
if error or string.gsub(this:GetText(), " ", "") == "" then
|
||||
f.loadButton:Disable()
|
||||
f.loadButton.text:SetTextColor(1,.5,.5,1)
|
||||
else
|
||||
local text = this:GetText()
|
||||
local blob = Decode(text)
|
||||
local json = not blob and DecodeJSON(text) or nil
|
||||
local legacy = not blob and not json and DecodeLegacy(text) or nil
|
||||
|
||||
if blob or json or legacy then
|
||||
f.loadButton:Enable()
|
||||
f.loadButton.text:SetTextColor(.5,1,.5,1)
|
||||
else
|
||||
f.loadButton:Disable()
|
||||
f.loadButton.text:SetTextColor(1,.5,.5,1)
|
||||
end
|
||||
|
||||
local trydec = dec(this:GetText())
|
||||
if string.gsub(this:GetText(), " ", "") == "" then
|
||||
f.readButton.text:SetText(T["N/A"])
|
||||
f.readButton:Disable()
|
||||
elseif not trydec or trydec == "" then
|
||||
f.readButton:Enable()
|
||||
f.readButton.text:SetText(T["Encode"])
|
||||
f.readButton.func = function()
|
||||
local compressed = enc(compress(f.scroll.text:GetText()))
|
||||
f.scroll.text:SetText(compressed)
|
||||
end
|
||||
else
|
||||
if blob or legacy then
|
||||
-- decoding a legacy string yields the JSON view; re-encoding it
|
||||
-- from there produces a new-format blob (migration path)
|
||||
f.readButton:Enable()
|
||||
f.readButton.text:SetText(T["Decode"])
|
||||
f.readButton.func = function()
|
||||
local uncompressed = decompress(dec(f.scroll.text:GetText()))
|
||||
f.scroll.text:SetText(uncompressed)
|
||||
local current = f.scroll.text:GetText()
|
||||
local config = Decode(current) or DecodeLegacy(current)
|
||||
if config then
|
||||
f.scroll.text:SetText(C_EncodingUtil.SerializeJSON(config))
|
||||
end
|
||||
end
|
||||
elseif json then
|
||||
f.readButton:Enable()
|
||||
f.readButton.text:SetText(T["Encode"])
|
||||
f.readButton.func = function()
|
||||
local config = DecodeJSON(f.scroll.text:GetText())
|
||||
if config then
|
||||
f.scroll.text:SetText(Encode(config))
|
||||
end
|
||||
end
|
||||
else
|
||||
f.readButton:Disable()
|
||||
f.readButton.text:SetText(T["N/A"])
|
||||
end
|
||||
end)
|
||||
f.scroll:SetScrollChild(f.scroll.text)
|
||||
@@ -387,21 +322,23 @@ pfUI:RegisterModule("share", function ()
|
||||
f.loadButton.text:SetFont(pfUI.font_default, pfUI_config.global.font_size, "OUTLINE")
|
||||
f.loadButton.text:SetText(T["Import"])
|
||||
f.loadButton:SetScript("OnClick", function()
|
||||
local ImportConfig, error = loadstring(f.scroll.text:GetText())
|
||||
if not error and f.scroll.text:GetText() ~= "" then
|
||||
ImportConfig()
|
||||
pfUI:LoadConfig()
|
||||
local text = f.scroll.text:GetText()
|
||||
local config = Decode(text) or DecodeJSON(text) or DecodeLegacy(text)
|
||||
if not config then return end
|
||||
|
||||
-- Skip firstrun wizard when importing a shared profile
|
||||
-- The imported config is a complete setup, no wizard needed
|
||||
if pfUI.firstrun and pfUI.firstrun.steps then
|
||||
for _, step in pairs(pfUI.firstrun.steps) do
|
||||
pfUI_init[step.name] = true
|
||||
end
|
||||
_G.pfUI_config = config
|
||||
C = _G.pfUI_config
|
||||
pfUI:LoadConfig()
|
||||
|
||||
-- Skip firstrun wizard when importing a shared profile
|
||||
-- The imported config is a complete setup, no wizard needed
|
||||
if pfUI.firstrun and pfUI.firstrun.steps then
|
||||
for _, step in pairs(pfUI.firstrun.steps) do
|
||||
pfUI_init[step.name] = true
|
||||
end
|
||||
|
||||
CreateQuestionDialog(T["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"], ReloadUI)
|
||||
end
|
||||
|
||||
CreateQuestionDialog(T["Some settings need to reload the UI to take effect.\nDo you want to reloadUI now?"], ReloadUI)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -445,16 +382,15 @@ pfUI:RegisterModule("share", function ()
|
||||
ignored["position"] = f.ignorePosition:GetChecked()
|
||||
ignored["disabled"] = f.ignorePosition:GetChecked()
|
||||
|
||||
local compressed = enc(compress(serialize(myconfig, defconfig, "pfUI_config", ignored)))
|
||||
f.scroll.text:SetText(compressed)
|
||||
f.scroll.text.value = compressed
|
||||
local encoded = Encode(DiffConfig(myconfig, defconfig, ignored) or {})
|
||||
f.scroll.text:SetText(encoded)
|
||||
f.scroll.text.value = encoded
|
||||
f.scroll:SetVerticalScroll(0)
|
||||
end)
|
||||
end
|
||||
|
||||
_G.SLASH_PFEXPORT1, _G.SLASH_PFEXPORT2, _G.SLASH_PFEXPORT3 = "/export", "/import", "/share"
|
||||
function SlashCmdList.PFEXPORT(msg, editbox)
|
||||
pfUI.api.RegisterSlashCommand("PFEXPORT", { "/export", "/import", "/share" }, function(msg, editbox)
|
||||
f:Show()
|
||||
end
|
||||
end, true)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
+65
-67
@@ -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 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
|
||||
@@ -103,7 +102,7 @@ pfUI:RegisterModule("socialmod", function ()
|
||||
friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status))
|
||||
end
|
||||
|
||||
friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), level, class))
|
||||
friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), info.level, info.className))
|
||||
caption:SetVertexColor(1,1,1,.9)
|
||||
friendInfo:SetVertexColor(1,1,1,.9)
|
||||
else
|
||||
@@ -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:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), max).." |cffaaaaaa"..format(WHO_FRAME_SHOWN_TEMPLATE, MAX_WHOS_FROM_SERVER))
|
||||
else
|
||||
WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), num).." |cffaaaaaa"..format(WHO_FRAME_SHOWN_TEMPLATE, 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
|
||||
|
||||
+15
-106
@@ -2,8 +2,7 @@
|
||||
-- https://github.com/balakethelock/SuperWoW
|
||||
|
||||
-- DLL Status Check Command (always available)
|
||||
SLASH_PFDLLSTATUS1 = "/pfdll"
|
||||
SlashCmdList["PFDLLSTATUS"] = function()
|
||||
pfUI.api.RegisterSlashCommand("PFDLLSTATUS", { "/pfdll" }, function()
|
||||
local chat = DEFAULT_CHAT_FRAME
|
||||
chat:AddMessage("|cff33ffccpfUI|r: DLL Status Check")
|
||||
|
||||
@@ -23,6 +22,13 @@ SlashCmdList["PFDLLSTATUS"] = function()
|
||||
chat:AddMessage(" |cffff0000Nampower|r: Not detected")
|
||||
end
|
||||
|
||||
-- ClassicAPI
|
||||
if CLASSIC_API_VERSION then
|
||||
chat:AddMessage(" |cff00ff00ClassicAPI|r: v" .. CLASSIC_API_VERSION)
|
||||
else
|
||||
chat:AddMessage(" |cffff0000ClassicAPI|r: Not detected")
|
||||
end
|
||||
|
||||
-- Check if castbar exists for indicator positioning
|
||||
if pfUI.castbar and pfUI.castbar.player then
|
||||
chat:AddMessage(" |cff00ff00Castbar|r: Available for indicator anchoring")
|
||||
@@ -36,7 +42,7 @@ SlashCmdList["PFDLLSTATUS"] = function()
|
||||
else
|
||||
chat:AddMessage(" |cffff0000Target frame|r: NOT found")
|
||||
end
|
||||
end
|
||||
end, true)
|
||||
|
||||
pfUI:RegisterModule("superwow", function ()
|
||||
if SetAutoloot and SpellInfo and not SUPERWOW_VERSION then
|
||||
@@ -69,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)
|
||||
@@ -166,110 +175,10 @@ pfUI:RegisterModule("superwow", function ()
|
||||
end
|
||||
|
||||
-- Add slash command for clickthrough toggle
|
||||
_G.SLASH_PFCLICKTHROUGH1 = "/clickthrough"
|
||||
_G.SLASH_PFCLICKTHROUGH2 = "/ct"
|
||||
SlashCmdList["PFCLICKTHROUGH"] = function()
|
||||
pfUI.api.RegisterSlashCommand("PFCLICKTHROUGH", { "/clickthrough", "/ct" }, function()
|
||||
local enabled = pfUI.api.ToggleClickthrough()
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: Clickthrough mode " .. (enabled and "|cff00ff00enabled|r" or "|cffff0000disabled|r"))
|
||||
end
|
||||
end, true)
|
||||
end
|
||||
|
||||
-- Enhance libcast with SuperWoW data for NPCs and other players
|
||||
-- Player casts use SPELLCAST_* events for proper pushback handling
|
||||
local supercast = CreateFrame("Frame")
|
||||
local playerGuid = nil
|
||||
|
||||
supercast:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
supercast:RegisterEvent("UNIT_CASTEVENT")
|
||||
supercast:RegisterEvent("PLAYER_LOGOUT")
|
||||
supercast:SetScript("OnEvent", function()
|
||||
-- Handle shutdown to prevent crash 132
|
||||
if event == "PLAYER_LOGOUT" then
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
-- Cache player GUID
|
||||
if UnitExists then
|
||||
local guid = UnitGUID("player")
|
||||
playerGuid = guid
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local guid = arg1
|
||||
local isPlayer = guid == playerGuid
|
||||
|
||||
-- For non-player units: disable combat parsing events (one-time init)
|
||||
if not isPlayer and not supercast.init then
|
||||
-- disable combat parsing events in superwow mode (for non-player units)
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PARTY_BUFF")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE")
|
||||
libcast:UnregisterEvent("CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF")
|
||||
supercast.init = true
|
||||
end
|
||||
|
||||
if arg3 == "START" or arg3 == "CAST" or arg3 == "CHANNEL" then
|
||||
local target = arg2
|
||||
local event_type = arg3
|
||||
local spell_id = arg4
|
||||
local timer = arg5
|
||||
|
||||
local spell = C_Spell.GetSpellName(spell_id) or UNKNOWN
|
||||
local icon = C_Spell.GetSpellTexture(spell_id) or "Interface\\Icons\\INV_Misc_QuestionMark"
|
||||
|
||||
-- skip on buff procs during cast
|
||||
if event_type == "CAST" then
|
||||
if not libcast.db[guid] or libcast.db[guid].cast ~= spell then
|
||||
-- ignore casts without 'START' event, while there is already another cast.
|
||||
-- those events can be for example a frost shield proc while casting frostbolt.
|
||||
-- we want to keep the cast itself, so we simply skip those.
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- For player: store in libcast.db[playerName] so pushback tracking works
|
||||
-- For others: store by GUID
|
||||
local dbKey = isPlayer and UnitName("player") or guid
|
||||
|
||||
-- add cast action to the database
|
||||
if not libcast.db[dbKey] then libcast.db[dbKey] = {} end
|
||||
libcast.db[dbKey].cast = spell
|
||||
libcast.db[dbKey].rank = nil
|
||||
libcast.db[dbKey].start = GetTime()
|
||||
libcast.db[dbKey].casttime = timer or 0
|
||||
libcast.db[dbKey].icon = icon
|
||||
libcast.db[dbKey].channel = event_type == "CHANNEL" or false
|
||||
elseif arg3 == "FAIL" then
|
||||
-- For player: use playerName, for others: use GUID
|
||||
local dbKey = isPlayer and UnitName("player") or guid
|
||||
|
||||
-- delete all cast entries
|
||||
if libcast.db[dbKey] then
|
||||
libcast.db[dbKey].cast = nil
|
||||
libcast.db[dbKey].rank = nil
|
||||
libcast.db[dbKey].start = nil
|
||||
libcast.db[dbKey].casttime = nil
|
||||
libcast.db[dbKey].icon = nil
|
||||
libcast.db[dbKey].channel = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
+139
-105
@@ -1,4 +1,3 @@
|
||||
pfUI:RegisterNewModule("swingtimer", "Swing Timer")
|
||||
pfUI:RegisterModule("swingtimer", function ()
|
||||
local rawborder, border = GetBorderSize()
|
||||
|
||||
@@ -23,60 +22,34 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
pendingCastSpellId = nil,
|
||||
mhFrozenAt = nil,
|
||||
hsQueued = false, cleaveQueued = false, maulQueued = false,
|
||||
hsSeenCurrent = false, cleaveSeenCurrent = false, maulSeenCurrent = false,
|
||||
isWarrior = false,
|
||||
isDruid = false,
|
||||
cachedHSSlots = {}, cachedCleaveSlots = {},
|
||||
cachedHSSlots = {}, cachedCleaveSlots = {}, cachedMaulSlots = {},
|
||||
useSpellQueueEvent = false,
|
||||
playerGUID = nil,
|
||||
swingThrottle = 0,
|
||||
onSwingCache = {},
|
||||
}
|
||||
|
||||
-- Ranged spell IDs
|
||||
local RANGED_SPELLIDS = {
|
||||
[75] = true, -- Auto Shot (Hunter)
|
||||
[2764] = true, -- Throw (Warrior/Rogue)
|
||||
}
|
||||
-- Wand "Shoot" runs on the ranged bar but is INDEPENDENT of the melee
|
||||
-- swing clock — casters melee-weave between mainhand swings and wand
|
||||
-- fires, both timers tick concurrently. Every other ranged-auto-attack
|
||||
-- (Hunter Auto Shot, any future auto-repeat ranged spell) replaces MH.
|
||||
local WAND_SHOOT_SPELLID = 5019
|
||||
local THROW_SPELLID = 2764 -- one-shot ranged, not auto-repeat
|
||||
|
||||
-- Spells that DELAY the swing timer by their cast duration but do NOT reset it.
|
||||
-- Slam: vanilla behavior on Turtle WoW - delays swing, does not reset.
|
||||
-- Hammer of Wrath: Turtle WoW changed behavior - does not reset swing timer.
|
||||
local swingDelaySpells = {
|
||||
[1464] = true, [8820] = true, [11604] = true, [11605] = true, -- Slam R1-R4
|
||||
[24275] = true, [24274] = true, [24239] = true, -- Hammer of Wrath R1-R3
|
||||
}
|
||||
|
||||
-- SPELL_ATTR_ON_NEXT_SWING (bit 2, value 4): 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 ATTR_ON_NEXT_SWING = 4
|
||||
local function IsOnSwingSpell(spellId)
|
||||
if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end
|
||||
local rec = GetSpellRec(spellId)
|
||||
local result = rec and bit.band(rec.attributes, ATTR_ON_NEXT_SWING) ~= 0 or false
|
||||
local result = C_Spell.IsNextMeleeSpell(spellId)
|
||||
S.onSwingCache[spellId] = result
|
||||
return result
|
||||
end
|
||||
|
||||
-- Heroic Strike spell IDs (all ranks)
|
||||
local hsSpellIDs = {
|
||||
[78] = true, [284] = true, [285] = true, [1608] = true,
|
||||
[11564] = true, [11565] = true, [11566] = true, [11567] = true,
|
||||
}
|
||||
|
||||
-- Cleave spell IDs (all ranks)
|
||||
local cleaveSpellIDs = {
|
||||
[845] = true, [7369] = true, [11608] = true, [11609] = true,
|
||||
[20569] = true,
|
||||
}
|
||||
|
||||
-- Maul spell IDs (all ranks 1 to 7)
|
||||
local maulSpellIDs = {
|
||||
[6807] = true,
|
||||
[6808] = true,
|
||||
[6809] = true,
|
||||
[8972] = true,
|
||||
[9745] = true, [9880] = true, [9881] = true,
|
||||
}
|
||||
-- HS / Cleave / Maul are detected dynamically via IsOnSwingSpell (the
|
||||
-- SPELL_ATTR_ON_NEXT_SWING bit) + name comparison against the rank-1
|
||||
-- canonical names cached below — no per-rank ID maintenance.
|
||||
|
||||
-- Read config
|
||||
local sw_width = tonumber(C.unitframes.swingtimerwidth) or 200
|
||||
@@ -103,7 +76,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local ohR, ohG, ohB, ohA = ParseColor(C.unitframes.swingtimerohcolor, 0.3, 0.8, 0.3, 1)
|
||||
local raR, raG, raB, raA = ParseColor(C.unitframes.swingtimerrangedcolor, 0.3, 0.6, 1.0, 1)
|
||||
local rwR, rwG, rwB, rwA = ParseColor(C.unitframes.swingtimerrangedwarncolor, 0.9, 0.0, 0.0, 1)
|
||||
local isHunter = UnitClass("player") == "Hunter"
|
||||
local isHunter = UnitClassBase("player") == "HUNTER"
|
||||
local mhDefaultR, mhDefaultG, mhDefaultB = mhR, mhG, mhB
|
||||
|
||||
|
||||
@@ -311,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
|
||||
@@ -355,14 +330,17 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
pfUI.swingtimer:Show()
|
||||
end
|
||||
|
||||
-- Reset ranged countdown
|
||||
local function ResetRanged()
|
||||
-- Reset ranged countdown. replaceMH=true (Hunter Auto Shot, Throw) stops
|
||||
-- the melee swing clock while ranged ticks; replaceMH=false (wand Shoot)
|
||||
-- leaves it running for melee weaving.
|
||||
local function ResetRanged(replaceMH)
|
||||
if not sw_showranged then return end
|
||||
UpdateWeaponSpeeds()
|
||||
if S.raSpeed <= 0 then return end
|
||||
-- Ranged replaces MH bar
|
||||
S.mhActive = false
|
||||
pfUI.swingtimer.mainhand:Hide()
|
||||
if replaceMH then
|
||||
S.mhActive = false
|
||||
pfUI.swingtimer.mainhand:Hide()
|
||||
end
|
||||
S.raTimerMax = S.raSpeed
|
||||
S.raTimer = S.raSpeed
|
||||
S.raActive = true
|
||||
@@ -406,21 +384,48 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
pfUI.swingtimer:Hide()
|
||||
end
|
||||
|
||||
-- HS/Cleave helpers. Canonical rank-1 spellIDs resolve to the localized
|
||||
-- spell name once, so the per-slot comparison is locale-independent without
|
||||
-- per-rank hardcoding (every rank of Heroic Strike returns the same name).
|
||||
local HS_NAME = C_Spell.GetSpellName(78) -- Heroic Strike (Rank 1)
|
||||
local CLEAVE_NAME = C_Spell.GetSpellName(845) -- Cleave (Rank 1)
|
||||
-- HS/Cleave/Maul helpers. Canonical rank-1 spellIDs resolve to the
|
||||
-- localized spell name once, so per-spell classification is rank- and
|
||||
-- locale-independent without per-rank ID hardcoding (every rank of
|
||||
-- Heroic Strike etc. returns the same name).
|
||||
local HS_NAME = C_Spell.GetSpellName(78) -- Heroic Strike (Rank 1)
|
||||
local CLEAVE_NAME = C_Spell.GetSpellName(845) -- Cleave (Rank 1)
|
||||
local MAUL_NAME = C_Spell.GetSpellName(6807) -- Maul (Rank 1)
|
||||
|
||||
-- Classify an on-next-swing spell as "hs" / "cleave" / "maul" / nil.
|
||||
-- IsOnSwingSpell gates the family (ATTR_ON_NEXT_SWING bit 0x04) so
|
||||
-- unrelated spells return nil cheaply.
|
||||
local function ClassifyOnSwingSpell(spellId)
|
||||
if not IsOnSwingSpell(spellId) then return nil end
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
if name == HS_NAME then return "hs"
|
||||
elseif name == CLEAVE_NAME then return "cleave"
|
||||
elseif name == MAUL_NAME then return "maul"
|
||||
end
|
||||
end
|
||||
|
||||
-- Set the queue flags from a ClassifyOnSwingSpell result. nil = no-op
|
||||
-- (preserves prior flag state when the queued spell isn't one we
|
||||
-- color-code, matching the legacy table-lookup behavior).
|
||||
local function SetQueuedKind(kind)
|
||||
if not kind then return end
|
||||
S.hsQueued = (kind == "hs")
|
||||
S.cleaveQueued = (kind == "cleave")
|
||||
S.maulQueued = (kind == "maul")
|
||||
S.hsSeenCurrent, S.cleaveSeenCurrent, S.maulSeenCurrent = false, false, false
|
||||
end
|
||||
|
||||
local function RebuildQueueSlotCache()
|
||||
if not S.isWarrior or not sw_hsqueue or S.useSpellQueueEvent then return end
|
||||
if not sw_hsqueue then return end
|
||||
S.cachedHSSlots = {}
|
||||
S.cachedCleaveSlots = {}
|
||||
S.cachedMaulSlots = {}
|
||||
if not (S.isWarrior or S.isDruid) then return end
|
||||
for slot = 1, 120 do
|
||||
local kind, id = GetActionInfo(slot)
|
||||
local name
|
||||
if kind == "spell" then
|
||||
name = GetSpellInfo(id)
|
||||
name = C_Spell.GetSpellName(id)
|
||||
elseif kind == "macro" then
|
||||
name = GetMacroSpell(id)
|
||||
end
|
||||
@@ -428,6 +433,8 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
table.insert(S.cachedHSSlots, slot)
|
||||
elseif name == CLEAVE_NAME then
|
||||
table.insert(S.cachedCleaveSlots, slot)
|
||||
elseif name == MAUL_NAME then
|
||||
table.insert(S.cachedMaulSlots, slot)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -439,9 +446,30 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
return false
|
||||
end
|
||||
|
||||
-- Reconcile a stale event-driven queue flag against the client's current
|
||||
-- action. Not every de-queue emits a SPELL_QUEUE pop — pressing Esc or
|
||||
-- re-pressing to cancel an on-swing spell doesn't — so the flag alone stays
|
||||
-- set. IsCurrentAction, which the client clears on cancel, is the reconciling
|
||||
-- signal, but only after it has confirmed the ability as current at least once
|
||||
-- (`seen`): a nampower-initiated cast may never flip IsCurrentAction, and must
|
||||
-- keep its color until its own pop/resolve rather than be cleared early.
|
||||
-- Returns the updated (queued, seen).
|
||||
local function ReconcileQueued(queued, slots, seen)
|
||||
if not queued then return false, false end
|
||||
if CheckQueuedAction(slots) then return true, true end
|
||||
if seen then return false, false end -- was current, now gone -> cancelled
|
||||
return true, false -- never confirmed current -> keep
|
||||
end
|
||||
|
||||
local function IsHSOrCleaveQueued()
|
||||
if not sw_hsqueue or not S.isWarrior then return false, false end
|
||||
if S.useSpellQueueEvent then return S.hsQueued, S.cleaveQueued end
|
||||
if S.useSpellQueueEvent then
|
||||
S.hsQueued, S.hsSeenCurrent =
|
||||
ReconcileQueued(S.hsQueued, S.cachedHSSlots, S.hsSeenCurrent)
|
||||
S.cleaveQueued, S.cleaveSeenCurrent =
|
||||
ReconcileQueued(S.cleaveQueued, S.cachedCleaveSlots, S.cleaveSeenCurrent)
|
||||
return S.hsQueued, S.cleaveQueued
|
||||
end
|
||||
return CheckQueuedAction(S.cachedHSSlots), CheckQueuedAction(S.cachedCleaveSlots)
|
||||
end
|
||||
|
||||
@@ -520,6 +548,10 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
-- HS/Cleave color
|
||||
local curR, curG, curB = mhDefaultR, mhDefaultG, mhDefaultB
|
||||
if sw_hsqueue then
|
||||
if S.isDruid and S.useSpellQueueEvent then
|
||||
S.maulQueued, S.maulSeenCurrent =
|
||||
ReconcileQueued(S.maulQueued, S.cachedMaulSlots, S.maulSeenCurrent)
|
||||
end
|
||||
if S.maulQueued and S.isDruid then
|
||||
curR, curG, curB = 1.0, 0.55, 0.0 -- orange for druid maul queue
|
||||
elseif S.isWarrior then
|
||||
@@ -694,11 +726,16 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local spellStartFrame = CreateFrame("Frame")
|
||||
spellStartFrame:RegisterEvent("SPELL_START_SELF")
|
||||
spellStartFrame:SetScript("OnEvent", function()
|
||||
if arg1 and arg1 > 0 then
|
||||
S.pendingCastSpellId = arg1
|
||||
-- Slam (and other delay-only spells): freeze the swing timer during cast
|
||||
-- so it pauses instead of ticking down and expiring mid-cast
|
||||
if swingDelaySpells[arg1] and S.mhActive then
|
||||
if not (arg1 and arg1 > 0) then return end
|
||||
S.pendingCastSpellId = arg1
|
||||
-- Freeze the swing timer for cast-time spells that DON'T reset auto-
|
||||
-- attack on completion (Slam, Hammer of Wrath on Turtle, etc.) — those
|
||||
-- let the swing resume from where it paused. C_Spell.ResetsMeleeSwing
|
||||
-- mirrors the server rule; spells that reset don't need freezing (they
|
||||
-- reset on SPELL_GO_SELF). Subsumes the old hardcoded swingDelaySpells
|
||||
-- list (no list maintenance for new Slam-style spells).
|
||||
if S.mhActive then
|
||||
if not C_Spell.ResetsMeleeSwing(arg1) then
|
||||
S.mhFrozenAt = GetTime()
|
||||
end
|
||||
end
|
||||
@@ -718,52 +755,56 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
-- SPELL_GO hook via libdebuff
|
||||
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
|
||||
pfUI.libdebuff_spell_go_hooks["swingtimer"] = function(spellId)
|
||||
if RANGED_SPELLIDS[spellId] then
|
||||
ResetRanged()
|
||||
-- C_Spell.IsRangedAutoAttackSpell catches both Auto Shot (75) and
|
||||
-- wand Shoot (5019) via Spell.dbc's AUTO_REPEAT attribute (covers
|
||||
-- any future auto-repeat ranged spell automatically). Wand is the
|
||||
-- one independent of the MH swing — everything else replaces it.
|
||||
-- Throw isn't auto-repeat (single-shot) so it's handled explicitly.
|
||||
if C_Spell.IsRangedAutoAttackSpell(spellId) then
|
||||
ResetRanged(spellId ~= WAND_SHOOT_SPELLID)
|
||||
return
|
||||
elseif swingDelaySpells[spellId] then
|
||||
-- Swing-delay spells (Slam, Hammer of Wrath on Turtle WoW):
|
||||
-- Delay the swing timer by cast duration, do NOT reset it.
|
||||
if S.mhFrozenAt then
|
||||
local castDuration = GetTime() - S.mhFrozenAt
|
||||
S.mhTimer = S.mhTimer + castDuration
|
||||
S.mhTimerMax = S.mhTimerMax + castDuration
|
||||
S.mhFrozenAt = nil
|
||||
end
|
||||
S.pendingCastSpellId = nil
|
||||
elseif spellId == THROW_SPELLID then
|
||||
ResetRanged(true)
|
||||
return
|
||||
elseif hsSpellIDs[spellId] or cleaveSpellIDs[spellId] or maulSpellIDs[spellId] or IsOnSwingSpell(spellId) then
|
||||
S.hsQueued = false; S.cleaveQueued = false
|
||||
S.maulQueued = false
|
||||
ResetMH()
|
||||
elseif cleaveSpellIDs[spellId] then
|
||||
S.hsQueued = false; S.cleaveQueued = false
|
||||
elseif IsOnSwingSpell(spellId) then
|
||||
-- On-next-swing ability (HS / Cleave / Maul / Raptor Strike / etc.)
|
||||
-- — the swing fires as the spell consumes it. Drop the queued color.
|
||||
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
|
||||
ResetMH()
|
||||
else
|
||||
-- Any spell with interruptFlags > 0 resets the swing timer
|
||||
-- (Moonfire, Faerie Fire, Wrath, Starfire etc. - NOT Insect Swarm which has flags=0)
|
||||
local _rec = GetSpellRec(spellId)
|
||||
if _rec and _rec.interruptFlags and _rec.interruptFlags > 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
|
||||
S.mhTimer = S.mhSpeed
|
||||
end
|
||||
if S.ohActive and S.ohSpeed > 0 then
|
||||
S.ohTimerMax = S.ohSpeed
|
||||
S.ohTimer = S.ohSpeed
|
||||
end
|
||||
elseif S.mhFrozenAt then
|
||||
local castDuration = GetTime() - S.mhFrozenAt
|
||||
S.mhTimer = S.mhTimer + castDuration
|
||||
S.mhTimerMax = S.mhTimerMax + castDuration
|
||||
S.mhFrozenAt = nil
|
||||
end
|
||||
end
|
||||
S.pendingCastSpellId = nil
|
||||
end
|
||||
|
||||
-- SPELL_CAST_EVENT hook: HS/Cleave queue tracking
|
||||
-- SPELL_CAST_EVENT hook: HS/Cleave/Maul queue tracking
|
||||
pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {}
|
||||
pfUI.libdebuff_spell_cast_hooks["swingtimer"] = function(success, spellId)
|
||||
if hsSpellIDs[spellId] then
|
||||
S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false
|
||||
elseif cleaveSpellIDs[spellId] then
|
||||
S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false
|
||||
elseif maulSpellIDs[spellId] then
|
||||
S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false
|
||||
end
|
||||
local kind = ClassifyOnSwingSpell(spellId)
|
||||
if not kind then return end
|
||||
S.useSpellQueueEvent = true
|
||||
SetQueuedKind(kind)
|
||||
end
|
||||
|
||||
|
||||
@@ -771,7 +812,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
events:RegisterEvent("AUTO_ATTACK_SELF")
|
||||
events:RegisterEvent("AUTO_ATTACK_OTHER")
|
||||
events:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
events:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||||
events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- ClassicAPI: per-slot, equipment-only
|
||||
events:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
events:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
@@ -814,8 +855,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
elseif event == "AUTO_ATTACK_OTHER" then
|
||||
-- Parry haste: enemy attacked the player and player parried
|
||||
local targetGuid = arg2
|
||||
if not targetGuid or not S.playerGUID then return end
|
||||
if targetGuid ~= S.playerGUID then return end
|
||||
if not targetGuid or not IsPlayerGuid(targetGuid) then return end
|
||||
local victimState = arg5 or 0
|
||||
-- VICTIMSTATE_PARRY = 3
|
||||
-- Vanilla: parry reduces the NEXT swing timer by 40% of weapon speed,
|
||||
@@ -847,13 +887,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local spellId = arg2 or 0
|
||||
if eventCode == ON_SWING_QUEUED then
|
||||
S.useSpellQueueEvent = true
|
||||
if hsSpellIDs[spellId] then
|
||||
S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false
|
||||
elseif cleaveSpellIDs[spellId] then
|
||||
S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false
|
||||
elseif maulSpellIDs[spellId] then
|
||||
S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false
|
||||
end
|
||||
SetQueuedKind(ClassifyOnSwingSpell(spellId))
|
||||
elseif eventCode == ON_SWING_QUEUE_POPPED then
|
||||
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
|
||||
end
|
||||
@@ -865,15 +899,15 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
S.autoAttackActive = false
|
||||
|
||||
elseif event == "PLAYER_ENTERING_WORLD" then
|
||||
local _, class = UnitClass("player")
|
||||
local class = UnitClassBase("player")
|
||||
S.isWarrior = (class == "WARRIOR")
|
||||
S.isDruid = (class == "DRUID")
|
||||
S.playerGUID = UnitGUID("player")
|
||||
UpdateWeaponSpeeds()
|
||||
RebuildQueueSlotCache()
|
||||
|
||||
elseif event == "UNIT_INVENTORY_CHANGED" then
|
||||
if arg1 and arg1 ~= "player" then return end
|
||||
elseif event == "PLAYER_EQUIPMENT_CHANGED" then
|
||||
-- arg1 = changed slot; only weapon slots (main/off/ranged) affect swing speed
|
||||
if not INVSLOTS_EQUIPABLE_IN_COMBAT[arg1] then return end
|
||||
UpdateWeaponSpeeds()
|
||||
if S.ohSpeed == 0 then
|
||||
S.ohActive = false
|
||||
@@ -898,7 +932,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
S.maulQueued = false
|
||||
|
||||
elseif event == "UNIT_DIED" then
|
||||
if arg1 and arg1 == S.playerGUID then
|
||||
if IsPlayerGuid(arg1) then
|
||||
ResetAll()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -100,10 +100,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
CreateBackdropShadow(KLHTM_Frame)
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
KLHTM_Frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
KLHTM_Frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -199,10 +199,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
CreateBackdropShadow(TWTMain)
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
TWTMain.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
TWTMain.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -267,7 +267,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
SW_BarFrame1_Selector:Hide()
|
||||
|
||||
-- let user select mode by clicking the title
|
||||
HookScript(SW_BarFrame1_Title, "OnMouseUp", function()
|
||||
SW_BarFrame1_Title:HookScript("OnMouseUp", function()
|
||||
local page = SW_Settings and SW_Settings.BarFrames and SW_Settings.BarFrames.SW_BarFrame1 and SW_Settings.BarFrames.SW_BarFrame1.Selected
|
||||
if page then
|
||||
local target = (arg1 == "LeftButton") and (page + 1) or (arg1 == "RightButton") and (page - 1)
|
||||
@@ -328,10 +328,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
CreateBackdropShadow(SW_BarFrame1)
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
SW_BarFrame1.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
SW_BarFrame1.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -943,8 +943,8 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
CreateBackdropShadow(AtlasLootTooltip)
|
||||
|
||||
if pfUI.eqcompare then
|
||||
HookScript(AtlasLootTooltip, "OnShow", pfUI.eqcompare.GameTooltipShow)
|
||||
HookScript(AtlasLootTooltip, "OnHide", function()
|
||||
pfUI.eqcompare.HookTooltip(AtlasLootTooltip)
|
||||
AtlasLootTooltip:HookScript("OnHide", function()
|
||||
ShoppingTooltip1:Hide()
|
||||
ShoppingTooltip2:Hide()
|
||||
end)
|
||||
@@ -1182,7 +1182,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
-- use pfUI frames to draw healComm predictions
|
||||
local pfHookHealCommSendAddonMessage = HealComm.SendAddonMessage
|
||||
function HealComm.SendAddonMessage(this, msg)
|
||||
if not UnitInRaid("player") and GetNumPartyMembers() < 1 then
|
||||
if not IsInGroup() then
|
||||
libpredict:ParseChatMessage(UnitName("player"), msg, "HealComm")
|
||||
end
|
||||
pfHookHealCommSendAddonMessage(this, msg)
|
||||
|
||||
+101
-4
@@ -244,10 +244,10 @@ pfUI:RegisterModule("thirdparty", function()
|
||||
end
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
window.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
window.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -350,10 +447,10 @@ pfUI:RegisterModule("thirdparty", function()
|
||||
CreateBackdropShadow(frame)
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
end
|
||||
|
||||
+174
-41
@@ -3,8 +3,7 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
|
||||
pfUI.tooltip = CreateFrame('Frame', "pfTooltip", GameTooltip)
|
||||
pfUI.tooltip.anchorframe = CreateFrame('Frame', "pfTooltipAnchor", UIParent)
|
||||
pfUI.tooltip.anchorframe:SetWidth(128)
|
||||
pfUI.tooltip.anchorframe:SetHeight(72)
|
||||
pfUI.tooltip.anchorframe:SetSize(128, 72)
|
||||
pfUI.tooltip.anchorframe:SetPoint("TOP", UIParent, "TOP", 0, -50)
|
||||
pfUI.tooltip.anchorframe:Hide()
|
||||
UpdateMovable(pfUI.tooltip.anchorframe)
|
||||
@@ -24,36 +23,54 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
end
|
||||
|
||||
if C.tooltip.position == "cursor" then
|
||||
-- Cursor mode makes the tooltip follow the mouse. The client has no
|
||||
-- mouse-move event, so following means polling GetCursorPosition() via an
|
||||
-- invisible follower frame that the tooltip anchors to. The follower is
|
||||
-- only shown while a tooltip is visible -- an OnUpdate fires only while its
|
||||
-- frame is shown, so the poll stops the moment the tooltip hides instead
|
||||
-- of running forever.
|
||||
local follower, Reposition
|
||||
if C.tooltip.cursoralign ~= "native" then
|
||||
local size = tonumber(C.tooltip.cursoroffset) * 2
|
||||
follower = CreateFrame("Frame", nil, UIParent)
|
||||
follower:SetSize(size, size)
|
||||
follower:Hide()
|
||||
|
||||
Reposition = function()
|
||||
local scale = UIParent:GetScale()
|
||||
local x, y = GetCursorPosition()
|
||||
follower:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x/scale, y/scale)
|
||||
if C.tooltip.cursoralign == "top" then
|
||||
follower:SetWidth(GameTooltip:GetWidth())
|
||||
end
|
||||
end
|
||||
|
||||
follower:SetScript("OnUpdate", function()
|
||||
-- throttle - cursor following doesn't need to be every frame
|
||||
if (this.tick or 0) > GetTime() then return end
|
||||
this.tick = GetTime() + (pfUI.throttle and pfUI.throttle:Get("tooltip_cursor") or 0.1)
|
||||
Reposition()
|
||||
end)
|
||||
|
||||
-- stop polling as soon as the tooltip is gone
|
||||
pfUI.tooltip:SetScript("OnHide", function() follower:Hide() end)
|
||||
end
|
||||
|
||||
function _G.GameTooltip_SetDefaultAnchor(tooltip, parent)
|
||||
tooltip:SetOwner(parent, "ANCHOR_CURSOR")
|
||||
if C.tooltip.cursoralign ~= "native" then
|
||||
-- create mouse follow frame
|
||||
if not tooltip.cursor then
|
||||
tooltip.cursor = CreateFrame("Frame", nil, UIParent)
|
||||
tooltip.cursor:SetWidth(tonumber(C.tooltip.cursoroffset) * 2)
|
||||
tooltip.cursor:SetHeight(tonumber(C.tooltip.cursoroffset) * 2)
|
||||
tooltip.cursor:SetScript("OnUpdate", function()
|
||||
-- throttle - cursor following doesn't need to be every frame
|
||||
if (this.tick or 0) > GetTime() then return end
|
||||
this.tick = GetTime() + (pfUI.throttle and pfUI.throttle:Get("tooltip_cursor") or 0.1)
|
||||
if not follower then return end
|
||||
|
||||
local scale = UIParent:GetScale()
|
||||
local x, y = GetCursorPosition()
|
||||
this:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x/scale, y/scale)
|
||||
if C.tooltip.cursoralign == "top" then
|
||||
tooltip.cursor:SetWidth(tooltip:GetWidth())
|
||||
end
|
||||
end)
|
||||
end
|
||||
-- position the follower right away so the tooltip doesn't flash at a
|
||||
-- stale spot before the first OnUpdate tick
|
||||
follower:Show()
|
||||
Reposition()
|
||||
|
||||
-- adjust tooltip to mouse frame
|
||||
if C.tooltip.cursoralign == "top" then
|
||||
tooltip:SetPoint("BOTTOMLEFT", tooltip.cursor, "TOPLEFT", 0, 0)
|
||||
elseif C.tooltip.cursoralign == "left" then
|
||||
tooltip:SetPoint("BOTTOMRIGHT", tooltip.cursor, "LEFT", 0, 0)
|
||||
elseif C.tooltip.cursoralign == "right" then
|
||||
tooltip:SetPoint("BOTTOMLEFT", tooltip.cursor, "RIGHT", 0, 0)
|
||||
end
|
||||
if C.tooltip.cursoralign == "top" then
|
||||
tooltip:SetPoint("BOTTOMLEFT", follower, "TOPLEFT", 0, 0)
|
||||
elseif C.tooltip.cursoralign == "left" then
|
||||
tooltip:SetPoint("BOTTOMRIGHT", follower, "LEFT", 0, 0)
|
||||
elseif C.tooltip.cursoralign == "right" then
|
||||
tooltip:SetPoint("BOTTOMLEFT", follower, "RIGHT", 0, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -146,14 +163,6 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
rhp, rhpmax = hp, hpmax
|
||||
elseif pfUI.libhealth and pfUI.libhealth.enabled then
|
||||
rhp, rhpmax, estimated = pfUI.libhealth:GetUnitHealthByName(this.name, this.level, tonumber(hp), tonumber(hpmax))
|
||||
elseif MobHealthFrame then
|
||||
local index = (this.name or "") .. ":" .. (this.level or "")
|
||||
local ppp = MobHealth_PPP(index)
|
||||
if perc and ppp and ppp > 0 and not UnitIsUnit("mouseover", "pet") then
|
||||
rhp = round(hp * ppp)
|
||||
rhpmax = round(100 * ppp)
|
||||
estimated = true
|
||||
end
|
||||
end
|
||||
|
||||
if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then
|
||||
@@ -176,12 +185,101 @@ 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
|
||||
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
|
||||
for i = 1, BUFF_MAX do
|
||||
local aura = C_UnitAuras.GetBuffDataByIndex(unit, i)
|
||||
if not aura then break end
|
||||
|
||||
count = count + 1
|
||||
local icon = pfUI.tooltip.buffpool:Acquire()
|
||||
icon.texture:SetTexture(aura.icon)
|
||||
icon.stacks:SetText(aura.applications and aura.applications > 1 and aura.applications or "")
|
||||
|
||||
icon.expirationTime = aura.expirationTime
|
||||
local timeleft = aura.expirationTime and aura.expirationTime > 0 and (aura.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 libtooltip:GetItemID() then
|
||||
GameTooltip:AddLine(T["ItemID"] .. ": " .. libtooltip:GetItemID(), .25,.5,1)
|
||||
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
|
||||
|
||||
@@ -191,7 +289,7 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
local pvpname = UnitPVPName(unit)
|
||||
local name = UnitName(unit)
|
||||
local validTarget, target = pcall(UnitName, unit .. 'target')
|
||||
local _, class = UnitClass(unit)
|
||||
local class = UnitClassBase(unit)
|
||||
local guild, rankstr, rankid = GetGuildInfo(unit)
|
||||
local reaction = UnitReaction(unit, "player")
|
||||
local pvptitle = gsub(gsub(pvpname or name, name, "", 1), "^%s*(.-)%s*$", "%1")
|
||||
@@ -226,7 +324,7 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
end
|
||||
|
||||
if validTarget and target then
|
||||
local _, targetClass = UnitClass(unit .. "target")
|
||||
local targetClass = UnitClassBase(unit .. "target")
|
||||
local targetReaction = UnitReaction("player",unit .. "target")
|
||||
if UnitIsPlayer(unit .. "target") and targetClass then
|
||||
local color = PFUI_CLASS_COLORS[targetClass]
|
||||
@@ -241,6 +339,21 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
end
|
||||
end
|
||||
|
||||
if C.tooltip.movespeed == "1" then
|
||||
local currentSpeed = GetUnitSpeed(unit)
|
||||
if currentSpeed and currentSpeed > 0 then
|
||||
local pct = floor(currentSpeed / 7 * 100 + 0.5)
|
||||
GameTooltip:AddLine(T["Speed"] .. ": " .. pct .. "%", 0.7, 0.7, 1)
|
||||
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
|
||||
@@ -248,4 +361,24 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
end
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
if C.tooltip.aurasource == "1" or C.tooltip.spellid == "1" then
|
||||
hooksecurefunc(GameTooltip, "SetUnitAura", function(self, ...)
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex(unpack(arg))
|
||||
if not aura then return end
|
||||
if C.tooltip.aurasource == "1" then
|
||||
local caster = aura.sourceUnit and UnitName(aura.sourceUnit)
|
||||
if not caster and aura.sourceGUID then
|
||||
caster = UnitNameFromGUID(aura.sourceGUID)
|
||||
end
|
||||
if caster and caster ~= "" then
|
||||
self:AddLine(T["Cast by"] .. ": " .. caster, .25,.5,1)
|
||||
end
|
||||
end
|
||||
if C.tooltip.spellid == "1" then
|
||||
self:AddLine(T["SpellID"] .. ": " .. aura.spellId, .25,.5,1)
|
||||
end
|
||||
self:Show()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
+32
-45
@@ -1,64 +1,53 @@
|
||||
pfUI:RegisterModule("totems", function ()
|
||||
local _, class = UnitClass("player")
|
||||
|
||||
local slots = {
|
||||
[FIRE_TOTEM_SLOT] = { r = .5, g = .2, b = .1 },
|
||||
[EARTH_TOTEM_SLOT] = { r = .2, g = .4, b = .1 },
|
||||
[WATER_TOTEM_SLOT] = { r = .1, g = .4, b = .6 },
|
||||
[AIR_TOTEM_SLOT] = { r = .4, g = .1, b = .7 },
|
||||
local slotColors = {
|
||||
[FIRE_TOTEM_SLOT] = CreateColor(.5, .2, .1),
|
||||
[EARTH_TOTEM_SLOT] = CreateColor(.2, .4, .1),
|
||||
[WATER_TOTEM_SLOT] = CreateColor(.1, .4, .6),
|
||||
[AIR_TOTEM_SLOT] = CreateColor(.4, .1, .7),
|
||||
}
|
||||
|
||||
local totems = CreateFrame("Frame", "pfTotems", UIParent)
|
||||
totems:RegisterEvent("PLAYER_TOTEM_UPDATE")
|
||||
totems:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
totems:SetScript("OnEvent", function(self)
|
||||
totems:RefreshList()
|
||||
totems:SetScript("OnEvent", function()
|
||||
this:RefreshList()
|
||||
end)
|
||||
|
||||
if class == "SHAMAN" then
|
||||
-- there's no totem event in vanilla using ticks instead
|
||||
local eventemu = CreateFrame("Frame")
|
||||
eventemu:SetScript("OnUpdate", function()
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .5 end
|
||||
totems:RefreshList()
|
||||
end)
|
||||
end
|
||||
|
||||
totems.OnEnter = function(self)
|
||||
if not this.id then return end
|
||||
local active, name, start, duration, icon = GetTotemInfo(this.id)
|
||||
if not name or not active then return end -- Prüfen ob name gültig ist
|
||||
local color = slots[this.id]
|
||||
local id = this:GetID()
|
||||
local spellID = select(7, GetTotemInfo(id))
|
||||
if not spellID or spellID == 0 then return end
|
||||
GameTooltip:SetOwner(this, "ANCHOR_LEFT")
|
||||
GameTooltip:SetText(name, color.r+.2, color.g+.2, color.b+.2)
|
||||
GameTooltip:SetSpellByID(spellID)
|
||||
GameTooltip:AddDoubleLine(T["Left Click"], T["Recast Totem"], nil, nil, nil, WHITE_FONT_COLOR:GetRGB())
|
||||
GameTooltip:AddDoubleLine(T["Right Click"], T["Target Totem"], nil, nil, nil, WHITE_FONT_COLOR:GetRGB())
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
totems.OnLeave = function(self)
|
||||
GameTooltip:Hide()
|
||||
end
|
||||
|
||||
totems.OnLeave = GameTooltip_Hide
|
||||
|
||||
totems.OnClick = function(self)
|
||||
if this.id and arg1 and arg1 == "LeftButton" then
|
||||
-- Try to recast totem on left click
|
||||
local active, name, start, duration, icon = GetTotemInfo(this.id)
|
||||
if name then CastSpellByName(name) end
|
||||
local id = this:GetID()
|
||||
if arg1 == "LeftButton" then
|
||||
local spellID = select(7, GetTotemInfo(id))
|
||||
if spellID and spellID > 0 then CastSpell(FindSpellBookSlotByID(spellID)) end
|
||||
elseif arg1 == "RightButton" then
|
||||
TargetTotem(id)
|
||||
end
|
||||
end
|
||||
|
||||
totems.RefreshList = function(self)
|
||||
local count = 0
|
||||
for i = 1, MAX_TOTEMS do
|
||||
local active, name, start, duration, icon = GetTotemInfo(i)
|
||||
local _, _, start, duration, icon = GetTotemInfo(i)
|
||||
|
||||
if active and icon and icon ~= "" then
|
||||
if start and start > 0 and icon and icon ~= "" then
|
||||
count = count + 1
|
||||
local color = slots[i]
|
||||
|
||||
self.bar[count]:Show()
|
||||
self.bar[count]:SetBackdropBorderColor(color.r, color.g, color.b)
|
||||
self.bar[count]:SetBackdropBorderColor(slotColors[i]:GetRGB())
|
||||
self.bar[count].icon:SetTexture(icon)
|
||||
self.bar[count].id = i
|
||||
self.bar[count]:SetID(i)
|
||||
|
||||
CooldownFrame_SetTimer(self.bar[count].cd, start, duration, 1)
|
||||
end
|
||||
@@ -77,14 +66,14 @@ end
|
||||
self:Show()
|
||||
end
|
||||
|
||||
local count = count and count > 0 and count or MAX_TOTEMS
|
||||
count = count and count > 0 and count or MAX_TOTEMS
|
||||
|
||||
local thickness = self.iconsize + self.spacing*2
|
||||
local length = thickness * count
|
||||
if pfUI_config.totems.direction == "HORIZONTAL" then
|
||||
self:SetHeight(self.iconsize + self.spacing*2)
|
||||
self:SetWidth(self.spacing*2 + self.iconsize + (count-1)*(self.iconsize + self.spacing*2))
|
||||
self:SetSize(length, thickness)
|
||||
else
|
||||
self:SetWidth(self.iconsize + self.spacing*2)
|
||||
self:SetHeight(self.spacing*2 + self.iconsize + (count-1)*(self.iconsize + self.spacing*2))
|
||||
self:SetSize(thickness, length)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -114,8 +103,7 @@ end
|
||||
end
|
||||
end
|
||||
|
||||
self.bar[i]:SetHeight(self.iconsize)
|
||||
self.bar[i]:SetWidth(self.iconsize)
|
||||
self.bar[i]:SetSize(self.iconsize, self.iconsize)
|
||||
CreateBackdrop(self.bar[i], nil, true)
|
||||
|
||||
self.bar[i].icon = self.bar[i].icon or self.bar[i]:CreateTexture(nil, "ARTWORK")
|
||||
@@ -123,8 +111,7 @@ end
|
||||
SetAllPointsOffset(self.bar[i].icon, self.bar[i], 2,-2)
|
||||
|
||||
self.bar[i].cdbg = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i])
|
||||
self.bar[i].cdbg:SetHeight(self.iconsize - 3)
|
||||
self.bar[i].cdbg:SetWidth(self.iconsize - 3)
|
||||
self.bar[i].cdbg:SetSize(self.iconsize - 3, self.iconsize - 3)
|
||||
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
|
||||
self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
|
||||
self.bar[i].cd.pfCooldownStyleAnimation = 1
|
||||
|
||||
@@ -128,7 +128,7 @@ pfUI:RegisterModule("tracking", function ()
|
||||
end)
|
||||
|
||||
function pfUI.tracking:RefreshSpells()
|
||||
local _, playerClass = UnitClass("player")
|
||||
local playerClass = UnitClassBase("player")
|
||||
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(playerClass)
|
||||
|
||||
-- Build set of valid SpellIDs for this class
|
||||
|
||||
+92
-949
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user