20 Commits
Author SHA1 Message Date
Brues fa1dc5637e Bump ClassicAPI min to 1.7.4
Totem functions!
2026-07-20 22:17:49 -05:00
Brues 8506f5ffbf Localize the totem tooltip click hints
Add a Left Click / Right Click hint to the totem icon tooltip via
AddDoubleLine, matching the panel.lua convention. Reuses the existing
"Left Click" / "Right Click" strings and adds "Recast Totem" /
"Target Totem" as new translation keys (stubbed across all locales; they
fall back to English until translated).

Also drop the now-orphaned "Range Check Interval" string from every
locale manifest -- the setting was removed when librange stopped
scanning, but its translation stubs were left behind.
2026-07-20 22:16:05 -05:00
Brues 9c529512d1 Replace libtotem with ClassicAPI's native totem tracker
ClassicAPI now ships GetTotemInfo/GetTotemTimeLeft/GetTotemDuration/
TargetTotem plus a native PLAYER_TOTEM_UPDATE event, backed by a
data-driven tracker (slot from the Spell.dbc summon effect, duration from
SpellDuration.dbc, and object-manager death detection). That's exactly
what libtotem hand-rolled -- and better -- so delete the library outright:
its spellid/icon tables, the CastSpellByName/CastSpell/UseAction hooks,
the SPELL_GO commit path, and the active-totem bookkeeping.

modules/totems.lua becomes a thin consumer of the native API:
- Driven by PLAYER_TOTEM_UPDATE; drop the shaman tick-poller that only
  existed because vanilla had no totem event.
- Fix the GetTotemInfo call sites: the native 1st return is tool presence,
  not "summoned", so key active state on name/start instead.
- Right-click a totem icon to TargetTotem it; tooltip shows remaining time
  via GetTotemTimeLeft.

modules/turtle-wow.lua: drop the Totemic Recall handler that poked
libtotem:Clean() -- the native tracker detects the totems despawning and
clears the slots itself. Also clean up the now-orphaned translation string
and a stale libtotem mention in libdebuff's comment.
2026-07-20 21:51:08 -05:00
Brues 88d0f7bf74 Use table.wipe to reset spell caches
Clear existing spell cache tables with table.wipe instead of creating new tables when LEARNED_SPELL_IN_TAB fires. This preserves existing table references (spellmaxrank, spellindex, spellinfo), avoiding stale references and potential bugs while being slightly more efficient. Change applied in libs/libspell.lua.
2026-07-20 21:35:31 -05:00
Brues a3ff20782d Use table.wipe and table.insert for tables
Replace custom wipe implementation with a call to table.wipe in api/api.lua and update its doc comment to explain behavior (resets Lua 5.0 length via luaL_setn, advises using table.insert). Replace manual table.getn(t)+1 array appends with table.insert in modules/loot.lua (two sites). Makes table operations safer and more idiomatic, avoiding manual metatable handling and getn-based append idioms.
2026-07-20 21:16:12 -05:00
Brues 88c2462fbd CLASS_SORT_ORDER is already defined by !!!ClassicAPI 2026-07-20 21:15:25 -05:00
Brues e28a7e5606 Optimize isempty function using next() 2026-07-20 18:46:09 -05:00
Brues dfa74c5756 Make the bag sort family-aware and locale-independent
Two fixes to libbagsort's planning stage.

Family-aware placement: the sort treated every bag in the list as
interchangeable storage, so with a quiver / soul / profession bag in the
set it would try to swap a general item into a slot that can't hold it.
The client rejects that swap, but the grid was updated as if it
succeeded, desyncing the plan and corrupting the sort from there on. Now
each destination slot is tagged with its bag's family (via ClassicAPI's
GetItemFamily, which reports bag families reliably) and every item is
routed to a cell that accepts it -- matching specialty bag first,
overflowing to general -- so nothing lands where it can't go and family
items consolidate into their bags for free.

Locale-independent categories: SortCategoryPrefix compared the localized
itemType/quality strings from GetItemInfo, which silently miscategorize
everything on a non-enUS client. Switch to the numeric classID/quality
via C_Item.GetItemInfo and ClassicAPI's Enum.ItemClass / Enum.ItemQuality.
2026-07-20 17:46:37 -05:00
Brues 1c0c9dc019 Replace librange's position scan with ClassicAPI's UnitInRange
librange was a per-frame position scanner: it swept party/raid unit
tokens, cached each one's distance via UnitPosition, and answered range
queries from that cache. All of it existed only because 1.12 had no cheap
way to check an arbitrary unit's distance. ClassicAPI's UnitInRange does
exactly that C-side (fixed 40y healing range, position miss reported via
the second return), so the whole library collapses to a direct call.

Wins from dropping the cache:
- No staleness. The scanner's zone-death and roster-reindex bugs simply
  can't exist without a cache to go stale, so this supersedes the
  keep-alive fixes from 756e8840.
- All classes get target-frame range fading. The old target path faked a
  40y check via IsActionInRange on a healing spell found on the action
  bar, so classes without such a spell (GetRangeSlot returned nil) never
  had a working target range check.

The rangecheck == "0" master switch used to be enforced by hiding the
scanner; with no scanner, move that gate into pfUI.api.UnitInRange so
disabling the check still means nothing fades. Threshold is now 40y (the
ClassicAPI constant) rather than the old 45y. Drop the now-dead
rangechecki (Range Check Interval) setting, its GUI row, and migration.
2026-07-20 16:54:27 -05:00
Brues 801935130e Update GetItemLinkByName to use C_Item API
Replace vanilla GetItemInfo and manual link construction with C_Item.GetItemNameByID and C_Item.GetItemInfo
2026-07-20 15:11:50 -05:00
Brues 8cdced5ec0 Simplify modf function
Delegate to ClassicAPI
2026-07-20 15:10:31 -05:00
Brues 756e8840af Keep the rangecheck scanner alive across zones and roster changes
PLAYER_LEAVING_WORLD fires on every loading screen, not just logout, but
librange treated it as terminal: it latched librange_isLoggingOut and
tore OnUpdate off the frame, neither of which was ever restored. So the
first zone (into a BG, dungeon, etc.) permanently killed the distance
scan -- unitdata stopped updating and UnitInSpellRange defaulted every
unit to in-range until /reload, which the next loading screen then undid.
Only PLAYER_LOGOUT is terminal now; PLAYER_LEAVING_WORLD just hides for
the loading screen and PLAYER_ENTERING_WORLD re-shows it.

Also invalidate on RAID_ROSTER_UPDATE / PARTY_MEMBERS_CHANGED: the range
state is cached per unit token, so a roster re-index leaves each unitN
mapped to a different player with stale data. Clear the token cache and
restart the sweep so shifted/joined slots are re-evaluated within one
pass instead of inheriting the previous occupant's range.
2026-07-20 14:12:07 -05:00
Brues b2db091d54 bump min ClassicAPI version 2026-07-20 02:42:34 -05:00
Brues 5fae0bd459 Add priest shadowform auto-paging, sharing the druid-stealth flag
Mirror the druid-stealth page switch for priests: swap to the auto page
(8) while in Shadowform, back to the default page when it drops. Driven
by UPDATE_SHAPESHIFT_FORM (form ID 28), which fully covers shadowform on
its own -- no prowl-style stealth substate to chase.

Fold the new state into the existing prowl machinery instead of
duplicating it: prowl/shadowform are mutually exclusive by class, so one
shared `formpaging` flag and one page constant serve both, and the
OnUpdate switch collapses to a single class-gated block.
2026-07-20 02:40:37 -05:00
Brues 3deddf2b08 Drive questitem off QUEST_ACCEPTED/QUEST_REMOVED instead of rescanning
The item->quest map only changes when a quest enters or leaves the log,
not on progress, yet it was fully rebuilt on every QUEST_LOG_UPDATE burst
(a whole log walk + GetQuestDetails per quest). Maintain it incrementally
from ClassicAPI's QUEST_ACCEPTED / QUEST_REMOVED, keyed on the stable
questID with title/level cached at add time (the old log index shifted on
any add/remove). QUEST_LOG_UPDATE now only drives a one-time login seed
until the detail cache warms, since QUEST_ACCEPTED is suppressed for the
bulk sync; afterward it's a no-op and questing does no rebuilds.
2026-07-19 02:25:23 -05:00
Brues 650bcda001 Allocate newitem glow lazily, only for slots that go new
UpdateSlot previously created the glow texture and OnEnter acknowledge
hook on the first pass over every slot, then toggled visibility. Move
both inside the "is new" branch so a slot only allocates when it actually
holds a new item -- most bag/bank slots never do. Behavior is unchanged:
the glow is still created once per slot and reused, and the hook is
installed exactly when it's first needed.
2026-07-17 16:04:03 -05:00
Brues 931fe6c22d Harden the ClassicAPI-missing path so the disable notice can show
pfUI's TOC depends on the !!!ClassicAPI addon, so a nil CLASSIC_API_VERSION
means the addon loaded but the DLL isn't present -- yet the graceful-disable
branch itself called ClassicAPI APIs (EventUtil.ContinueOnPlayerLogin, and
Mixin/CallbackRegistryMixin for pfUI.events), so it crashed instead of
informing the user.

- Defer the popup with a bare PLAYER_ENTERING_WORLD frame (stock 1.12)
  instead of EventUtil/IsLoggedIn/PLAYER_LOGIN (ClassicAPI-era).
- Read the editbox via getglobal rather than _G.
- Guard pfUI.events creation behind `not pfUI.disabled`.
- Reword the notice: since the addon is present but the DLL isn't (or is
  outdated), tell the user the addon ships bundled with the DLL and to
  delete the !!!ClassicAPI folder and (re)install the latest release.
2026-07-17 15:12:30 -05:00
Brues bd4a40b0f2 Add newitem module: highlight freshly-acquired bag items
New module built on ClassicAPI's C_NewItems + BAG_NEW_ITEMS_UPDATED.
Glows bag slots (bags 0-4) holding items acquired since login, keyed on
item GUID so the flag survives rearranging. Hovering an item
acknowledges it (RemoveNewItem); closing the bags clears the rest via
ClearAll. Glow is UI-ActionButton-Border, sized off the slot width so it
tracks the icon_size config.

To stay decoupled from the bag frames, the bag module now broadcasts a
reusable "bag:closed" event through pfUI.events from its OnHide (carrying
the container so subscribers can tell backpack from bank), guarded so the
initial setup Hide() doesn't fire a phantom close.

Config: appearance.bags.newitem + newitem_color, with GUI toggles.
2026-07-17 14:47:22 -05:00
Brues 31ff2773b3 Defer macrotweak conflicts; use SetSize
Replace the previous RunNextFrame conflict check with per-addon EventUtil.ContinueOnAddOnLoaded calls in modules/macrotweak.lua so macrotweak is disabled as soon as known conflicting addons load. Simplify variable naming and ensure the disabled flag is set correctly. In skins/blizzard/macro.lua replace separate SetHeight/SetWidth calls with a single SetSize(150,22) for MacroEditButton to tidy UI sizing.
2026-07-15 13:45:06 -05:00
Brues 8176b906df Re-anchor tradeskill merge bar to real craft starts
EnterTradeskillMerge sized the merged bar as startMs + single*count, a
zero-latency assumption. Each craft boundary actually costs a server
round-trip, so the real chain runs ~(N-1)*lag longer than the bar
assumed. At the clear the overall fill was clamped to full and looked
done, but the fast per-craft spark tracks real time and came up short by
the accumulated lag -- freezing partway on the final craft.

StartTradeskillCraft now re-pins endTime to each craft's real start
(remaining crafts each still take `single`), so on the last craft
remaining==1 and endTime lands on its true completion. The spark and the
overall progress now reach the right edge together.
2026-07-13 12:34:45 -05:00
30 changed files with 362 additions and 689 deletions
+17 -29
View File
@@ -102,11 +102,7 @@ end
-- 'tbl' [table] the table that shall be checked -- 'tbl' [table] the table that shall be checked
-- return: [boolean] result of the check. -- return: [boolean] result of the check.
function pfUI.api.isempty(tbl) function pfUI.api.isempty(tbl)
if not tbl then return true end return next(tbl or {}) == nil
for k, v in pairs(tbl) do
return false
end
return true
end end
-- [ checkversion ] -- [ checkversion ]
@@ -143,6 +139,11 @@ function pfUI.api.UnitInRange(unit)
return 1 return 1
end 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 -- UnitXP precise mode: skip librange entirely, use direct distance check
if C.unitframes.rangecheck_mode == "unitxp" and _G.UnitXP then if C.unitframes.rangecheck_mode == "unitxp" and _G.UnitXP then
local threshold = tonumber(C.unitframes.rangecheck_distance) or 40 local threshold = tonumber(C.unitframes.rangecheck_distance) or 40
@@ -266,11 +267,7 @@ end
-- 'f' [float] the number to breakdown. -- 'f' [float] the number to breakdown.
-- returns: [int],[float] whole and fractional part. -- returns: [int],[float] whole and fractional part.
function pfUI.api.modf(f) function pfUI.api.modf(f)
if modf then return modf(f) end return math.modf(f)
if f > 0 then
return math.floor(f), mod(f,1)
end
return math.ceil(f), mod(f,1)
end end
-- [ GetServerEpoch ] -- [ GetServerEpoch ]
@@ -389,10 +386,10 @@ end
-- returns: [string] entire itemLink for the given item -- returns: [string] entire itemLink for the given item
function pfUI.api.GetItemLinkByName(name) function pfUI.api.GetItemLinkByName(name)
for itemID = 1, 25818 do for itemID = 1, 25818 do
local itemName, hyperLink, itemQuality = GetItemInfo(itemID) local itemName = C_Item.GetItemNameByID(itemID)
if (itemName and itemName == name) then if itemName and itemName == name then
local _, _, _, hex = GetItemQualityColor(tonumber(itemQuality)) local _, itemLink = C_Item.GetItemInfo(itemID)
return hex.. "|H"..hyperLink.."|h["..itemName.."]|h|r" return itemLink
end end
end end
end end
@@ -719,24 +716,15 @@ function pfUI.api.CopyTable(src)
end end
-- [ Wipe Table ] -- [ Wipe Table ]
-- Empties a table and returns it -- Empties a table and returns it.
-- 'src' [table] the table that should be emptied. -- 'src' [table] the table that should be emptied.
-- return: [table] the emptied table. -- 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) function pfUI.api.wipe(src)
-- notes: table.insert, table.remove will have undefined behavior return table.wipe(src)
-- 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
end end
-- [ Load Movable ] -- [ Load Movable ]
+3 -8
View File
@@ -162,6 +162,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "infight", "intensity", "16") pfUI:UpdateConfig("appearance", "infight", "intensity", "16")
pfUI:UpdateConfig("appearance", "bags", "unusable", "1") pfUI:UpdateConfig("appearance", "bags", "unusable", "1")
pfUI:UpdateConfig("appearance", "bags", "unusable_color", ".9,.2,.2,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", "borderlimit", "1")
pfUI:UpdateConfig("appearance", "bags", "borderonlygear", "0") pfUI:UpdateConfig("appearance", "bags", "borderonlygear", "0")
pfUI:UpdateConfig("appearance", "bags", "fulltext", "1") pfUI:UpdateConfig("appearance", "bags", "fulltext", "1")
@@ -230,7 +232,6 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", nil, "druidmanaspace", "-3") pfUI:UpdateConfig("unitframes", nil, "druidmanaspace", "-3")
pfUI:UpdateConfig("unitframes", nil, "druidmanatexture", "Interface\\AddOns\\pfUI\\img\\bar") pfUI:UpdateConfig("unitframes", nil, "druidmanatexture", "Interface\\AddOns\\pfUI\\img\\bar")
pfUI:UpdateConfig("unitframes", nil, "rangechecki", "4")
pfUI:UpdateConfig("unitframes", nil, "combowidth", "6") pfUI:UpdateConfig("unitframes", nil, "combowidth", "6")
pfUI:UpdateConfig("unitframes", nil, "comboheight", "6") pfUI:UpdateConfig("unitframes", nil, "comboheight", "6")
pfUI:UpdateConfig("unitframes", nil, "swingtimerwidth", "200") pfUI:UpdateConfig("unitframes", nil, "swingtimerwidth", "200")
@@ -626,6 +627,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("bars", nil, "pagemastershift", "0") pfUI:UpdateConfig("bars", nil, "pagemastershift", "0")
pfUI:UpdateConfig("bars", nil, "pagemasterctrl", "0") pfUI:UpdateConfig("bars", nil, "pagemasterctrl", "0")
pfUI:UpdateConfig("bars", nil, "druidstealth", "0") pfUI:UpdateConfig("bars", nil, "druidstealth", "0")
pfUI:UpdateConfig("bars", nil, "priestshadow", "0")
pfUI:UpdateConfig("bars", nil, "showcastable", "1") pfUI:UpdateConfig("bars", nil, "showcastable", "1")
pfUI:UpdateConfig("bars", nil, "glowrange", "1") pfUI:UpdateConfig("bars", nil, "glowrange", "1")
pfUI:UpdateConfig("bars", nil, "rangecolor", "1,0.1,0.1,1") pfUI:UpdateConfig("bars", nil, "rangecolor", "1,0.1,0.1,1")
@@ -1107,13 +1109,6 @@ function pfUI:MigrateConfig()
end end
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) -- migrating legacy buff/debuff naming (> 3.5.0)
if checkversion(3, 5, 0) then if checkversion(3, 5, 0) then
local unitframes = { "player", "target", "focus", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback" } local unitframes = { "player", "target", "focus", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback" }
-17
View File
@@ -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 { CLASS_ICON_TCOORDS = CLASS_ICON_TCOORDS or {
["WARRIOR"] = {0, 0.25, 0, 0.25}, ["WARRIOR"] = {0, 0.25, 0, 0.25},
["MAGE"] = {0.25, 0.49609375, 0, 0.25}, ["MAGE"] = {0.25, 0.49609375, 0, 0.25},
+2 -2
View File
@@ -619,8 +619,8 @@ pfUI_translation["deDE"] = {
["Random Roll Announcement Rarity"] = nil, ["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil, ["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil, ["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil, ["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -782,6 +782,7 @@ pfUI_translation["deDE"] = {
["Target Castbar"] = nil, ["Target Castbar"] = nil,
["Target Debuff Bar"] = nil, ["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil, ["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = nil, ["Target-Target"] = nil,
["Target-Target-Target"] = nil, ["Target-Target-Target"] = nil,
["Text"] = nil, ["Text"] = nil,
@@ -876,7 +877,6 @@ pfUI_translation["deDE"] = {
["XP Percentage"] = nil, ["XP Percentage"] = nil,
["Yellow Border On Neutral Units"] = nil, ["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil, ["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = 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 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, ["Your items have been repaired for"] = nil,
+5 -2
View File
@@ -411,6 +411,7 @@ pfUI_translation["enUS"] = {
["Highlight Equipped Items"] = nil, ["Highlight Equipped Items"] = nil,
["Highlight Not Usable Spells"] = nil, ["Highlight Not Usable Spells"] = nil,
["Highlight Out Of Mana Spells"] = nil, ["Highlight Out Of Mana Spells"] = nil,
["Highlight New Items"] = nil,
["Highlight Out Of Range Spells"] = nil, ["Highlight Out Of Range Spells"] = nil,
["Highlight Settings That Require Reload"] = nil, ["Highlight Settings That Require Reload"] = nil,
["Highlight Unusable Items"] = nil, ["Highlight Unusable Items"] = nil,
@@ -529,6 +530,7 @@ pfUI_translation["enUS"] = {
["Network Latency"] = nil, ["Network Latency"] = nil,
["Network Up"] = nil, ["Network Up"] = nil,
["New entry:"] = nil, ["New entry:"] = nil,
["New Item Color"] = nil,
["NEW TIMER"] = nil, ["NEW TIMER"] = nil,
["Next"] = nil, ["Next"] = nil,
["Next Memory Cleanup"] = nil, ["Next Memory Cleanup"] = nil,
@@ -625,8 +627,8 @@ pfUI_translation["enUS"] = {
["Random Roll Announcement Rarity"] = nil, ["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil, ["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil, ["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil, ["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -786,12 +788,14 @@ pfUI_translation["enUS"] = {
["Switch Pages On Alt Key Press"] = nil, ["Switch Pages On Alt Key Press"] = nil,
["Switch Pages On Ctrl Key Press"] = nil, ["Switch Pages On Ctrl Key Press"] = nil,
["Switch Pages On Druid Stealth"] = nil, ["Switch Pages On Druid Stealth"] = nil,
["Switch Pages On Priest Shadowform"] = nil,
["Switch Pages On Shift Key Press"] = nil, ["Switch Pages On Shift Key Press"] = nil,
["Systeminfo"] = nil, ["Systeminfo"] = nil,
["Target"] = nil, ["Target"] = nil,
["Target Castbar"] = nil, ["Target Castbar"] = nil,
["Target Debuff Bar"] = nil, ["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil, ["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = nil, ["Target-Target"] = nil,
["Target-Target-Target"] = nil, ["Target-Target-Target"] = nil,
["Text"] = nil, ["Text"] = nil,
@@ -889,7 +893,6 @@ pfUI_translation["enUS"] = {
["XP Percentage"] = nil, ["XP Percentage"] = nil,
["Yellow Border On Neutral Units"] = nil, ["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil, ["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = 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 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, ["Your items have been repaired for"] = nil,
+2 -2
View File
@@ -619,8 +619,8 @@ pfUI_translation["esES"] = {
["Random Roll Announcement Rarity"] = "Anuncio para tirar los dados aleatoriamente", ["Random Roll Announcement Rarity"] = "Anuncio para tirar los dados aleatoriamente",
["Random Rolling"] = "Tirar los dados aleatoriamente", ["Random Rolling"] = "Tirar los dados aleatoriamente",
["Range Based Hunter Paging"] = "Paginado de alcance para cazadores", ["Range Based Hunter Paging"] = "Paginado de alcance para cazadores",
["Range Check Interval"] = "Intervalo de comprobación de alcance",
["Rank"] = "Rango", ["Rank"] = "Rango",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Borde rojo en unidades enemigas", ["Red Border On Enemy Units"] = "Borde rojo en unidades enemigas",
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -782,6 +782,7 @@ pfUI_translation["esES"] = {
["Target Castbar"] = "Barra de lanzamiento del objetivo", ["Target Castbar"] = "Barra de lanzamiento del objetivo",
["Target Debuff Bar"] = "Barra de perjuicios 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 Nameplate Zoom Factor"] = "Factor de zoom de la placa de nombre del objetivo",
["Target Totem"] = nil,
["Target-Target"] = "Objetivo-Objetivo", ["Target-Target"] = "Objetivo-Objetivo",
["Target-Target-Target"] = "Objetivo-Objetivo-Objetivo", ["Target-Target-Target"] = "Objetivo-Objetivo-Objetivo",
["Text"] = nil, ["Text"] = nil,
@@ -876,7 +877,6 @@ pfUI_translation["esES"] = {
["XP Percentage"] = "Porcentaje de exp.", ["XP Percentage"] = "Porcentaje de exp.",
["Yellow Border On Neutral Units"] = "Borde amarillo en unidades neutrales", ["Yellow Border On Neutral Units"] = "Borde amarillo en unidades neutrales",
["Yes"] = "Sí", ["Yes"] = "Sí",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Obtienes", ["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 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", ["Your items have been repaired for"] = "Tus objetos se han reparado por",
+2 -2
View File
@@ -619,8 +619,8 @@ pfUI_translation["frFR"] = {
["Random Roll Announcement Rarity"] = "Rareté des annonces des jets de dés aléatoires", ["Random Roll Announcement Rarity"] = "Rareté des annonces des jets de dés aléatoires",
["Random Rolling"] = "Lancer 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 Based Hunter Paging"] = "Pagination de distance basée sur le chasseur",
["Range Check Interval"] = "Intervalle de vérification de la distance",
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Bordure rouge sur les unités ennemies", ["Red Border On Enemy Units"] = "Bordure rouge sur les unités ennemies",
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -782,6 +782,7 @@ pfUI_translation["frFR"] = {
["Target Castbar"] = "Barre d'incantation de la cible", ["Target Castbar"] = "Barre d'incantation de la cible",
["Target Debuff Bar"] = "Barre des affaiblissements 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 Nameplate Zoom Factor"] = "Facteur de zoom du Nameplate de la cible",
["Target Totem"] = nil,
["Target-Target"] = "Cible de la cible", ["Target-Target"] = "Cible de la cible",
["Target-Target-Target"] = "Cible de la cible de la cible", ["Target-Target-Target"] = "Cible de la cible de la cible",
["Text"] = nil, ["Text"] = nil,
@@ -876,7 +877,6 @@ pfUI_translation["frFR"] = {
["XP Percentage"] = "Pourcentage de la barre d'expérience", ["XP Percentage"] = "Pourcentage de la barre d'expérience",
["Yellow Border On Neutral Units"] = "Bordure jaune sur les unités neutres", ["Yellow Border On Neutral Units"] = "Bordure jaune sur les unités neutres",
["Yes"] = "Oui", ["Yes"] = "Oui",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Vous avez", ["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 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", ["Your items have been repaired for"] = "Vos objets ont été réparés pour",
+2 -2
View File
@@ -619,8 +619,8 @@ pfUI_translation["koKR"] = {
["Random Roll Announcement Rarity"] = nil, ["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil, ["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil, ["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil, ["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -782,6 +782,7 @@ pfUI_translation["koKR"] = {
["Target Castbar"] = nil, ["Target Castbar"] = nil,
["Target Debuff Bar"] = nil, ["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil, ["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = "대상-대상", ["Target-Target"] = "대상-대상",
["Target-Target-Target"] = nil, ["Target-Target-Target"] = nil,
["Text"] = nil, ["Text"] = nil,
@@ -876,7 +877,6 @@ pfUI_translation["koKR"] = {
["XP Percentage"] = "경험치 퍼센트", ["XP Percentage"] = "경험치 퍼센트",
["Yellow Border On Neutral Units"] = nil, ["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil, ["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = 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 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, ["Your items have been repaired for"] = nil,
+2 -2
View File
@@ -619,8 +619,8 @@ pfUI_translation["ruRU"] = {
["Random Roll Announcement Rarity"] = "Оповещение случайного броска для качества предмета", ["Random Roll Announcement Rarity"] = "Оповещение случайного броска для качества предмета",
["Random Rolling"] = "Случайный бросок костей для", ["Random Rolling"] = "Случайный бросок костей для",
["Range Based Hunter Paging"] = "[|cffA9D271Охотник|r] Переключение страниц на основе диапазона", ["Range Based Hunter Paging"] = "[|cffA9D271Охотник|r] Переключение страниц на основе диапазона",
["Range Check Interval"] = "Интервал проверки диапазона",
["Rank"] = "Ранг", ["Rank"] = "Ранг",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Красные границы вражеских юнитов", ["Red Border On Enemy Units"] = "Красные границы вражеских юнитов",
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = "Региональные настройки", ["Regional Settings"] = "Региональные настройки",
@@ -782,6 +782,7 @@ pfUI_translation["ruRU"] = {
["Target Castbar"] = "Панель применения цели", ["Target Castbar"] = "Панель применения цели",
["Target Debuff Bar"] = "Панель дебаффов цели", ["Target Debuff Bar"] = "Панель дебаффов цели",
["Target Nameplate Zoom Factor"] = "Коэффициент увеличения индикатора здоровья цели", ["Target Nameplate Zoom Factor"] = "Коэффициент увеличения индикатора здоровья цели",
["Target Totem"] = nil,
["Target-Target"] = "Цель цели", ["Target-Target"] = "Цель цели",
["Target-Target-Target"] = "Цель цели цели", ["Target-Target-Target"] = "Цель цели цели",
["Text"] = nil, ["Text"] = nil,
@@ -876,7 +877,6 @@ pfUI_translation["ruRU"] = {
["XP Percentage"] = "Процент опыта", ["XP Percentage"] = "Процент опыта",
["Yellow Border On Neutral Units"] = "Желтые границы на нейтральных юнитах", ["Yellow Border On Neutral Units"] = "Желтые границы на нейтральных юнитах",
["Yes"] = "Да", ["Yes"] = "Да",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Вы получили", ["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 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"] = "Ваши предметы были отремонтированы за", ["Your items have been repaired for"] = "Ваши предметы были отремонтированы за",
+2 -2
View File
@@ -619,8 +619,8 @@ pfUI_translation["zhCN"] = {
["Random Roll Announcement Rarity"] = "随机Roll点稀有度", ["Random Roll Announcement Rarity"] = "随机Roll点稀有度",
["Random Rolling"] = "随机Roll点 物品:", ["Random Rolling"] = "随机Roll点 物品:",
["Range Based Hunter Paging"] = "启用基于范围的自动分页[|cff7fff7f猎人|r]", ["Range Based Hunter Paging"] = "启用基于范围的自动分页[|cff7fff7f猎人|r]",
["Range Check Interval"] = "范围检查间隔",
["Rank"] = "军衔", ["Rank"] = "军衔",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "显示敌方单位红色边框", ["Red Border On Enemy Units"] = "显示敌方单位红色边框",
["Red Name Text On Infight Units"] = "进战斗的单位显示红色姓名", ["Red Name Text On Infight Units"] = "进战斗的单位显示红色姓名",
["Regional Settings"] = "区域设置", ["Regional Settings"] = "区域设置",
@@ -783,6 +783,7 @@ pfUI_translation["zhCN"] = {
["Target Castbar"] = "目标施法条", ["Target Castbar"] = "目标施法条",
["Target Debuff Bar"] = "目标Debuffs条", ["Target Debuff Bar"] = "目标Debuffs条",
["Target Nameplate Zoom Factor"] = "目标姓名板缩放系数", ["Target Nameplate Zoom Factor"] = "目标姓名板缩放系数",
["Target Totem"] = nil,
["Target-Target"] = "目标的目标", ["Target-Target"] = "目标的目标",
["Target-Target-Target"] = "目标的目标的目标", ["Target-Target-Target"] = "目标的目标的目标",
["Text"] = "文本", ["Text"] = "文本",
@@ -877,7 +878,6 @@ pfUI_translation["zhCN"] = {
["XP Percentage"] = "经验百分比", ["XP Percentage"] = "经验百分比",
["Yellow Border On Neutral Units"] = "显示中立单位黄色边框", ["Yellow Border On Neutral Units"] = "显示中立单位黄色边框",
["Yes"] = "是", ["Yes"] = "是",
["You gain (.+) Mana from Totemic Recall"] = "你从图腾召回获得了(.+)点法力值",
["You got"] = "你已得到", ["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 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"] = "你的物品已经修好了", ["Your items have been repaired for"] = "你的物品已经修好了",
+2 -2
View File
@@ -619,8 +619,8 @@ pfUI_translation["zhTW"] = {
["Random Roll Announcement Rarity"] = "隨機Roll點公示稀有度", ["Random Roll Announcement Rarity"] = "隨機Roll點公示稀有度",
["Random Rolling"] = "隨機Roll點 物品:", ["Random Rolling"] = "隨機Roll點 物品:",
["Range Based Hunter Paging"] = nil, ["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = "範圍檢查間隔",
["Rank"] = nil, ["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil, ["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil, ["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil, ["Regional Settings"] = nil,
@@ -782,6 +782,7 @@ pfUI_translation["zhTW"] = {
["Target Castbar"] = nil, ["Target Castbar"] = nil,
["Target Debuff Bar"] = nil, ["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil, ["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = "目標的目標", ["Target-Target"] = "目標的目標",
["Target-Target-Target"] = nil, ["Target-Target-Target"] = nil,
["Text"] = nil, ["Text"] = nil,
@@ -876,7 +877,6 @@ pfUI_translation["zhTW"] = {
["XP Percentage"] = "經驗百分比", ["XP Percentage"] = "經驗百分比",
["Yellow Border On Neutral Units"] = nil, ["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil, ["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = 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 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"] = "你的物品已經修好了", ["Your items have been repaired for"] = "你的物品已經修好了",
-1
View File
@@ -5,7 +5,6 @@
<Include file="..\libs\librange.lua"/> <Include file="..\libs\librange.lua"/>
<Include file="..\libs\libunitscan.lua"/> <Include file="..\libs\libunitscan.lua"/>
<Include file="..\libs\libhealth.lua"/> <Include file="..\libs\libhealth.lua"/>
<Include file="..\libs\libtotem.lua"/>
<Include file="..\libs\libthrottle.lua"/> <Include file="..\libs\libthrottle.lua"/>
<Include file="..\libs\libpredict.lua"/> <Include file="..\libs\libpredict.lua"/>
<Include file="..\libs\libbagsort.lua"/> <Include file="..\libs\libbagsort.lua"/>
+1
View File
@@ -79,4 +79,5 @@
<Include file="..\modules\bgscore.lua"/> <Include file="..\modules\bgscore.lua"/>
<Include file="..\modules\equipmentmanager.lua"/> <Include file="..\modules\equipmentmanager.lua"/>
<Include file="..\modules\loothistory.lua"/> <Include file="..\modules\loothistory.lua"/>
<Include file="..\modules\newitem.lua"/>
</Ui> </Ui>
+85 -59
View File
@@ -11,26 +11,27 @@ pfUI.api.libbagsort = libbagsort
libbagsort.itemGrid = {} libbagsort.itemGrid = {}
libbagsort.bagList = nil libbagsort.bagList = nil
local HEARTHSTONE_ITEM_ID = 6948 local ItemClass = Enum.ItemClass
local ItemQuality = Enum.ItemQuality
-- Lower prefix = sorted earlier in the bag. -- Lower prefix = sorted earlier in the bag.
local function SortCategoryPrefix(itemId, itemType, itemSubType, quality) local function SortCategoryPrefix(itemId, classID, quality)
if itemId == HEARTHSTONE_ITEM_ID then return "00" end if itemId == HEARTHSTONE_ITEM_ID then return "00" end
if quality == 0 then return "13" end -- Poor (gray) always last if quality == ItemQuality.Poor then return "13" end -- gray always last
if itemType == "Weapon" or itemType == "Armor" then if classID == ItemClass.Weapon or classID == ItemClass.Armor then
if quality and quality >= 4 then return "01" end -- Epic+ gear if quality and quality >= ItemQuality.Epic then return "01" end -- Epic+ gear
if quality == 3 then return "02" end -- Rare gear if quality == ItemQuality.Rare then return "02" end -- Rare gear
if quality == 2 then return "03" end -- Uncommon gear if quality == ItemQuality.Uncommon then return "03" end -- Uncommon gear
return "04" -- Common/poor gear return "04" -- Common/poor gear
end end
if itemType == "Consumable" then return "05" end if classID == ItemClass.Consumable then return "05" end
if itemType == "Reagent" then return "06" end if classID == ItemClass.Reagent then return "06" end
if itemType == "Trade Goods" then return "07" end if classID == ItemClass.Tradegoods then return "07" end
if itemType == "Quest" then return "08" end if classID == ItemClass.Questitem then return "08" end
-- Non-gear items without a specific type, sorted by quality -- Non-gear items without a specific type, sorted by quality
if quality and quality >= 4 then return "09" end if quality and quality >= ItemQuality.Epic then return "09" end
if quality == 3 then return "10" end if quality == ItemQuality.Rare then return "10" end
if quality == 2 then return "11" end if quality == ItemQuality.Uncommon then return "11" end
return "12" return "12"
end end
@@ -41,9 +42,12 @@ local function SortCountSuffix(count)
return string.sub(s, -6) return string.sub(s, -6)
end end
local function SortKey(itemId, name, itype, subtype, quality, count) local function SortKey(itemId, name, classID, subClassID, quality, count)
return SortCategoryPrefix(itemId, itype, subtype, quality) -- Zero-pad the class/subclass so the secondary grouping sorts numerically
.. (itype or "") .. "|" .. (subtype or "") .. "|" .. (name or "zzz") .. "|" .. SortCountSuffix(count) -- (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 end
local function ClearSortData() local function ClearSortData()
@@ -112,36 +116,61 @@ local function BuildConsolidateOps(bagList)
return ops return ops
end 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 function BuildSortGrid()
local bagList = libbagsort.bagList local bagList = libbagsort.bagList
libbagsort.itemGrid = {} libbagsort.itemGrid = {}
local normalItems = {} local normalItems = {}
local poorItems = {} 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 for _, bag in ipairs(bagList) do
bagCount = bagCount + 1 local fam = BagFamily(bag)
local numSlots = GetContainerNumSlots(bag) local numSlots = GetContainerNumSlots(bag)
bagSlots[bagCount] = numSlots
if numSlots > 0 then if numSlots > 0 then
libbagsort.itemGrid[bag] = {} libbagsort.itemGrid[bag] = {}
for slot = 1, numSlots do 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) local itemId = C_Container.GetContainerItemID(bag, slot)
if itemId then if itemId then
-- pfUI's compat layer shims GetItemInfo to the modern 10-field -- C_Item.GetItemInfo is the full 18-field tuple; classID/subClassID
-- signature (inserts nil for itemLevel between quality and -- sit at positions 12/13. We categorize on those numeric class IDs
-- minlevel) — so itype/subtype sit at positions 6/7, not 5/6. -- rather than the localized itemType/itemSubType strings. (pfUI's
local name, _, quality, _, _, itype, subtype = GetItemInfo(itemId) -- 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 _, count = GetContainerItemInfo(bag, slot)
local item = { 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, srcBag = bag,
srcSlot = slot, srcSlot = slot,
curBag = bag, curBag = bag,
curSlot = slot, curSlot = slot,
} }
if quality == 0 then if quality == ItemQuality.Poor then
tinsert(poorItems, item) tinsert(poorItems, item)
else else
tinsert(normalItems, item) tinsert(normalItems, item)
@@ -157,41 +186,38 @@ local function BuildSortGrid()
-- back-to-front (last poor item lands on the last slot). -- back-to-front (last poor item lands on the last slot).
table.sort(poorItems, function(a, b) return a.key > b.key end) table.sort(poorItems, function(a, b) return a.key > b.key end)
-- Forward pass: assign normal items from slot 1 of bag 1 onward -- Forward pass: route each normal item into the next free cell that
local bagIdx, destSlot = 1, 1 -- accepts it -- a matching specialty bag first, overflowing to general.
while bagIdx <= bagCount and bagSlots[bagIdx] == 0 do local genIdx = 1
bagIdx = bagIdx + 1 local specIdx = {} -- family -> next free index into specialtyCells[family]
end
for _, item in ipairs(normalItems) do for _, item in ipairs(normalItems) do
while bagIdx <= bagCount do local cell
if destSlot <= bagSlots[bagIdx] then break end local fam = item.family
bagIdx = bagIdx + 1 if fam ~= 0 and specialtyCells[fam] then
destSlot = 1 local i = specIdx[fam] or 1
if i <= table.getn(specialtyCells[fam]) then
cell = specialtyCells[fam][i]
specIdx[fam] = i + 1
end
end end
if bagIdx > bagCount then break end if not cell and genIdx <= table.getn(generalCells) then
local grid = libbagsort.itemGrid[item.srcBag][item.srcSlot] cell = generalCells[genIdx]
grid.destBag = bagList[bagIdx] genIdx = genIdx + 1
grid.destSlot = destSlot end
destSlot = destSlot + 1 if not cell then break end
item.destBag = cell.bag
item.destSlot = cell.slot
end end
-- Reverse pass: assign poor items from the last slot of the last bag backward -- Reverse pass: poor items are general; fill remaining general cells from
local rBagIdx = bagCount -- the back, stopping before the ones the forward pass already claimed.
local rDestSlot = 0 local genBack = table.getn(generalCells)
while rBagIdx >= 1 do
if bagSlots[rBagIdx] > 0 then rDestSlot = bagSlots[rBagIdx]; break end
rBagIdx = rBagIdx - 1
end
for _, item in ipairs(poorItems) do for _, item in ipairs(poorItems) do
while rBagIdx >= 1 and rDestSlot < 1 do if genBack < genIdx then break end
rBagIdx = rBagIdx - 1 local cell = generalCells[genBack]
rDestSlot = rBagIdx >= 1 and bagSlots[rBagIdx] or 0 genBack = genBack - 1
end item.destBag = cell.bag
if rBagIdx < 1 then break end item.destSlot = cell.slot
local grid = libbagsort.itemGrid[item.srcBag][item.srcSlot]
grid.destBag = bagList[rBagIdx]
grid.destSlot = rDestSlot
rDestSlot = rDestSlot - 1
end end
end end
+1 -1
View File
@@ -16,7 +16,7 @@ setfenv(1, pfUI:GetEnvironment())
-- non-player expirationTime). What remains in libdebuff is the cast-event -- non-player expirationTime). What remains in libdebuff is the cast-event
-- bookkeeping consumed by GetBestAuraCast (libpredict HoT tracking) and the -- bookkeeping consumed by GetBestAuraCast (libpredict HoT tracking) and the
-- libdebuff_*_hooks broadcast surface (subscribers in actionbar / swingtimer -- libdebuff_*_hooks broadcast surface (subscribers in actionbar / swingtimer
-- / libtotem react to SPELL_GO and SPELL_FAILED). -- react to SPELL_GO and SPELL_FAILED).
-- return instantly when another libdebuff is already active -- return instantly when another libdebuff is already active
if pfUI.api.libdebuff then return end if pfUI.api.libdebuff then return end
+14 -164
View File
@@ -2,178 +2,28 @@
setfenv(1, pfUI:GetEnvironment()) setfenv(1, pfUI:GetEnvironment())
--[[ librange ]]-- --[[ 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) -- librange:UnitInSpellRange(unit)
-- Returns `1` if the unit is within range, `nil` otherwise. -- 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 if pfUI.api.librange then return end
local _, class = UnitClass("player") local librange = {}
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
function librange:UnitInSpellRange(unit) function librange:UnitInSpellRange(unit)
if UnitIsUnit("target", unit) then -- _G-qualified: bare `UnitInRange` resolves to pfUI.api.UnitInRange inside
if not librange.slot then return nil end -- the pfUI environment (which calls us), so this must reach ClassicAPI's
return IsActionInRange(librange.slot) == 1 and 1 or nil -- global directly or it recurses.
end local inRange, checked = _G.UnitInRange(unit)
-- position miss (e.g. a unit outside the client's sync range): we can't
local unit = librange:GetRealUnit(unit) -- tell, so default to in-range -- matches the old cache's nil behavior.
if not checked then return 1 end
if unitdata[unit] and unitdata[unit] == 1 then return inRange and 1 or nil
return 1
elseif not unitdata[unit] then
return 1
else
return nil
end
end end
-- add librange to pfUI API -- add librange to pfUI API
+3 -1
View File
@@ -118,7 +118,9 @@ end
local resetcache = CreateFrame("Frame") local resetcache = CreateFrame("Frame")
resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB") resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB")
resetcache:SetScript("OnEvent", function() resetcache:SetScript("OnEvent", function()
spellmaxrank, spellindex, spellinfo = {}, {}, {} table.wipe(spellmaxrank)
table.wipe(spellindex)
table.wipe(spellinfo)
end) end)
-- add libspell to pfUI API -- add libspell to pfUI API
-275
View File
@@ -1,275 +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 then
name = C_Spell.GetSpellName(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
local spellInfo = C_Spell.GetSpellInfo(id)
name, rank = spellInfo.name, spellInfo.rank
elseif kind == "macro" then
name, rank, spellID = GetMacroSpell(id)
end
if not name then return end
libtotem:CheckAddQueue(name, rank, GetActionTexture(slot), spellID)
end)
+29 -15
View File
@@ -853,16 +853,20 @@ pfUI:RegisterModule("actionbar", function ()
end end
end end
local cat, stealth
local inCatForm = nil local inCatForm = nil
local prowlActive = 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 CAT_FORM = 1
local SHADOWFORM = 28
local function HasCatForm() local function HasCatForm()
return GetShapeshiftFormID() == CAT_FORM and true or nil return GetShapeshiftFormID() == CAT_FORM and true or nil
end end
local function InShadowform()
return GetShapeshiftFormID() == SHADOWFORM and true or nil
end
local function FullScan() local function FullScan()
if class ~= "DRUID" then return nil end if class ~= "DRUID" then return nil end
inCatForm = HasCatForm() inCatForm = HasCatForm()
@@ -872,7 +876,7 @@ pfUI:RegisterModule("actionbar", function ()
-- pagemaster / meta page switch -- pagemaster / meta page switch
do 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 -- set temporary pagemaster bindings keybinds
if C.bars.pagemaster == "1" then if C.bars.pagemaster == "1" then
@@ -890,8 +894,10 @@ pfUI:RegisterModule("actionbar", function ()
end) end)
end end
-- setup page switch frame -- setup page switch frame. `formpaging` is the shared "auto-page is
local prowling = nil -- 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) local pageswitch = CreateFrame("Frame", "pfActionBarPageSwitch", UIParent)
pageswitch:RegisterEvent("PLAYER_ENTERING_WORLD") pageswitch:RegisterEvent("PLAYER_ENTERING_WORLD")
pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM") pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
@@ -905,10 +911,17 @@ pfUI:RegisterModule("actionbar", function ()
return return
end 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 class ~= "DRUID" then return end
if event == "PLAYER_ENTERING_WORLD" then if event == "PLAYER_ENTERING_WORLD" then
prowling = FullScan() formpaging = FullScan()
return return
end end
@@ -917,7 +930,7 @@ pfUI:RegisterModule("actionbar", function ()
inCatForm = HasCatForm() inCatForm = HasCatForm()
if not inCatForm then if not inCatForm then
prowlActive = nil prowlActive = nil
prowling = nil formpaging = nil
end end
return return
end end
@@ -927,10 +940,10 @@ pfUI:RegisterModule("actionbar", function ()
if inCatForm then if inCatForm then
if IsStealthed() then if IsStealthed() then
prowlActive = true prowlActive = true
prowling = true formpaging = true
else else
prowlActive = nil prowlActive = nil
prowling = nil formpaging = nil
end end
end end
end) end)
@@ -946,7 +959,7 @@ pfUI:RegisterModule("actionbar", function ()
if PROWL_IDS[spellId] then if PROWL_IDS[spellId] then
inCatForm = true inCatForm = true
prowlActive = true prowlActive = true
prowling = true formpaging = true
end end
end end
pageswitch:SetScript("OnUpdate", function() pageswitch:SetScript("OnUpdate", function()
@@ -964,11 +977,12 @@ pfUI:RegisterModule("actionbar", function ()
SwitchBar(default) SwitchBar(default)
end end
-- switch actionbar page if druid stealth is detected -- switch actionbar page while druid stealth / priest shadowform is active
if C.bars.druidstealth == "1" then if (class == "DRUID" and C.bars.druidstealth == "1")
if prowling and _G.CURRENT_ACTIONBAR_PAGE == 1 then or (class == "PRIEST" and C.bars.priestshadow == "1") then
SwitchBar(prowl) if formpaging and _G.CURRENT_ACTIONBAR_PAGE == 1 then
elseif not prowling and _G.CURRENT_ACTIONBAR_PAGE == 8 then SwitchBar(formpage)
elseif not formpaging and _G.CURRENT_ACTIONBAR_PAGE == 8 then
SwitchBar(default) SwitchBar(default)
end end
end end
+5
View File
@@ -337,6 +337,7 @@ pfUI:RegisterModule("bags", function ()
local chat = pfUI.chat and ( object == "bank" and pfUI.chat.left or pfUI.chat.right) or nil local chat = pfUI.chat and ( object == "bank" and pfUI.chat.left or pfUI.chat.right) or nil
frame:SetScript("OnShow", function() frame:SetScript("OnShow", function()
frame.opened = true
if C.appearance.bags.hidechat == "1" and chat and chat:IsVisible() then if C.appearance.bags.hidechat == "1" and chat and chat:IsVisible() then
frame.chatWasOpen = true frame.chatWasOpen = true
chat:Hide() chat:Hide()
@@ -355,6 +356,10 @@ pfUI:RegisterModule("bags", function ()
end end
pfUI.bag:CreateBags(object) pfUI.bag:CreateBags(object)
PlaySound("INTERFACESOUND_BACKPACKCLOSE") PlaySound("INTERFACESOUND_BACKPACKCLOSE")
if frame.opened then
frame.opened = nil
pfUI.events:TriggerEvent("bag:closed", object)
end
end) end)
end end
+5
View File
@@ -69,6 +69,11 @@ pfUI:RegisterModule("castbar", function ()
-- spark to the left edge of the bar. -- spark to the left edge of the bar.
local function StartTradeskillCraft(cb) local function StartTradeskillCraft(cb)
cb.currentCraftStart = GetTime() * 1000 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) UpdateTradeskillLabel(cb)
end end
+3 -1
View File
@@ -2119,7 +2119,6 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Enable 40y-Range Check"], C.unitframes, "rangecheck", "checkbox", nil, nil, nil, nil) 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["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["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["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["Always Show Self In Raid Frames"], C.unitframes, "selfinraid", "checkbox")
CreateConfig(nil, T["Show Self In Group Frames"], C.unitframes, "selfingroup", "checkbox") CreateConfig(nil, T["Show Self In Group Frames"], C.unitframes, "selfingroup", "checkbox")
@@ -2413,6 +2412,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Enable Item Quality Color For Equipment Only"], C.appearance.bags, "borderonlygear", "checkbox") 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["Highlight Unusable Items"], C.appearance.bags, "unusable", "checkbox")
CreateConfig(nil, T["Unusable Item Color"], C.appearance.bags, "unusable_color", "color") 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["Enable Movable Bags"], C.appearance.bags, "movable", "checkbox")
CreateConfig(nil, T["Anchor Bags Above Chat"], C.appearance.bags, "abovechat", "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") CreateConfig(nil, T["Hide Chat When Bags Are Opened"], C.appearance.bags, "hidechat", "checkbox")
@@ -2573,6 +2574,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 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 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 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) CreateConfig(nil, T["Range Based Hunter Paging"], C.bars, "hunterbar", "checkbox", nil, nil, nil, nil)
end) end)
+2 -2
View File
@@ -236,7 +236,7 @@ pfUI:RegisterModule("loot", function ()
if (candidate) then if (candidate) then
index_to_name[i] = candidate index_to_name[i] = candidate
name_to_index[candidate] = i name_to_index[candidate] = i
randoms[table.getn(randoms)+1]=i table.insert(randoms, i)
if candidate == pfUI.loot.me then if candidate == pfUI.loot.me then
pfUI.loot.my_index = i pfUI.loot.my_index = i
end end
@@ -693,7 +693,7 @@ pfUI:RegisterModule("loot", function ()
else -- not an eligible candidate for that item else -- not an eligible candidate for that item
pfUI.loot.rollers[who] = {roll=tonumber(roll),value="disabled"} pfUI.loot.rollers[who] = {roll=tonumber(roll),value="disabled"}
end 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
end end
table.sort(pfUI.loot.rollers_sorted,function(a,b) table.sort(pfUI.loot.rollers_sorted,function(a,b)
+6 -10
View File
@@ -2,15 +2,14 @@ pfUI:RegisterModule("macrotweak", function ()
local conflictAddons = { "Supermacro", "SuperCleveRoidMacros", "UltimaMacros" } local conflictAddons = { "Supermacro", "SuperCleveRoidMacros", "UltimaMacros" }
local disabled = false local disabled = false
local function CheckConflicts() for _, addon in pairs(conflictAddons) do
for _, name in pairs(conflictAddons) do local name = addon
if IsAddOnLoaded(name) then EventUtil.ContinueOnAddOnLoaded(name, function()
if not disabled then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: " .. name .. " found, macrotweak disabled.") DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: " .. name .. " found, macrotweak disabled.")
disabled = true
return true
end end
end disabled = true
return false end)
end end
-- do not write macro calls into chat input history -- do not write macro calls into chat input history
@@ -64,7 +63,4 @@ pfUI:RegisterModule("macrotweak", function ()
UseInventoryItem(slot) UseInventoryItem(slot)
end end
end) end)
-- Check conflicts after one tick so all addons have finished loading
RunNextFrame(CheckConflicts)
end) end)
+68
View File
@@ -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 color = CreateColor(strsplit(",", 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(color:GetRGBA())
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)
+57 -36
View File
@@ -1,8 +1,43 @@
pfUI:RegisterModule("questitem", function () pfUI:RegisterModule("questitem", function ()
-- [itemID] = { index = questLogIndex, count = requiredCount }. Rebuilt
-- on QUEST_LOG_UPDATE from ClassicAPI's per-quest cached requirements.
local requiredItems = {} 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) local function AddTooltip(frame, itemID)
if not itemID then return end if not itemID then return end
if C.tooltip.questitem.showquest ~= "1" 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 if not entry and not replace then return end
local quest, level = UNKNOWN, 255 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 if not quest then return end
local color = GetDifficultyColor(level) local color = GetDifficultyColor(level)
@@ -42,48 +77,37 @@ pfUI:RegisterModule("questitem", function ()
pfUI.questitem = CreateFrame("Frame", "pfQuestItemScanner", UIParent) pfUI.questitem = CreateFrame("Frame", "pfQuestItemScanner", UIParent)
pfUI.questitem:RegisterEvent("PLAYER_ENTERING_WORLD") pfUI.questitem:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.questitem:RegisterEvent("QUEST_LOG_UPDATE") pfUI.questitem:RegisterEvent("QUEST_LOG_UPDATE")
pfUI.questitem:RegisterEvent("QUEST_ACCEPTED")
pfUI.questitem:RegisterEvent("QUEST_REMOVED")
pfUI.questitem:SetScript("OnEvent", function() pfUI.questitem:SetScript("OnEvent", function()
-- debounce rebuilds — QUEST_LOG_UPDATE fires in bursts if C.tooltip.questitem.showquest ~= "1" then return end
this.run = GetTime() + .5 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) end)
pfUI.questitem:SetScript("OnUpdate", function() 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 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 this.run = nil
if Seed() then this.seeding = nil end
end) end)
-- reload quest entries on config change
pfUI.questitem.UpdateConfig = function() pfUI.questitem.UpdateConfig = function()
if C.tooltip.questitem.showquest ~= "1" then return end
pfUI.questitem.seeding = true
pfUI.questitem.run = GetTime() + .5 pfUI.questitem.run = GetTime() + .5
end 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 = CreateFrame("Frame", "pfQuestItems", GameTooltip)
pfUI.questitem.tooltip:SetScript("OnShow", function() pfUI.questitem.tooltip:SetScript("OnShow", function()
if GameTooltip:HasItem() then if GameTooltip:HasItem() then
@@ -92,9 +116,6 @@ pfUI:RegisterModule("questitem", function ()
end end
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() hooksecurefunc("SetItemRef", function()
if IsModifierKeyDown() then return end if IsModifierKeyDown() then return end
if ItemRefTooltip:HasItem() then if ItemRefTooltip:HasItem() then
+20 -32
View File
@@ -1,6 +1,4 @@
pfUI:RegisterModule("totems", function () pfUI:RegisterModule("totems", function ()
local _, class = UnitClass("player")
local slots = { local slots = {
[FIRE_TOTEM_SLOT] = { r = .5, g = .2, b = .1 }, [FIRE_TOTEM_SLOT] = { r = .5, g = .2, b = .1 },
[EARTH_TOTEM_SLOT] = { r = .2, g = .4, b = .1 }, [EARTH_TOTEM_SLOT] = { r = .2, g = .4, b = .1 },
@@ -15,50 +13,42 @@ pfUI:RegisterModule("totems", function ()
totems:RefreshList() totems:RefreshList()
end) 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) totems.OnEnter = function(self)
if not this.id then return end local id = this:GetID()
local active, name, start, duration, icon = GetTotemInfo(this.id) local spellID = select(7, GetTotemInfo(id))
if not name or not active then return end -- Prüfen ob name gültig ist if not spellID or spellID == 0 then return end
local color = slots[this.id]
GameTooltip:SetOwner(this, "ANCHOR_LEFT") GameTooltip:SetOwner(this, "ANCHOR_LEFT")
GameTooltip:SetText(name, color.r+.2, color.g+.2, color.b+.2) GameTooltip:SetSpell(FindSpellBookSlotByID(spellID))
GameTooltip:AddDoubleLine(T["Left Click"], "|cffffffff" .. T["Recast Totem"])
GameTooltip:AddDoubleLine(T["Right Click"], "|cffffffff" .. T["Target Totem"])
GameTooltip:Show() GameTooltip:Show()
end
totems.OnLeave = function(self)
GameTooltip:Hide()
end end
totems.OnLeave = GameTooltip_Hide
totems.OnClick = function(self) totems.OnClick = function(self)
if this.id and arg1 and arg1 == "LeftButton" then local id = this:GetID()
-- Try to recast totem on left click if arg1 == "LeftButton" then
local active, name, start, duration, icon = GetTotemInfo(this.id) local _, name = GetTotemInfo(id)
if name then CastSpellByName(name) end if name and name ~= "" then CastSpellByName(name) end
elseif arg1 == "RightButton" then
TargetTotem(id)
end end
end end
totems.RefreshList = function(self) totems.RefreshList = function(self)
local count = 0 local count = 0
for i = 1, MAX_TOTEMS do 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 count = count + 1
local color = slots[i] local color = slots[i]
self.bar[count]:Show() self.bar[count]:Show()
self.bar[count]:SetBackdropBorderColor(color.r, color.g, color.b) self.bar[count]:SetBackdropBorderColor(color.r, color.g, color.b)
self.bar[count].icon:SetTexture(icon) 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) CooldownFrame_SetTimer(self.bar[count].cd, start, duration, 1)
end end
@@ -77,7 +67,7 @@ end
self:Show() self:Show()
end end
local count = count and count > 0 and count or MAX_TOTEMS count = count and count > 0 and count or MAX_TOTEMS
if pfUI_config.totems.direction == "HORIZONTAL" then if pfUI_config.totems.direction == "HORIZONTAL" then
self:SetHeight(self.iconsize + self.spacing*2) self:SetHeight(self.iconsize + self.spacing*2)
@@ -114,8 +104,7 @@ end
end end
end end
self.bar[i]:SetHeight(self.iconsize) self.bar[i]:SetSize(self.iconsize, self.iconsize)
self.bar[i]:SetWidth(self.iconsize)
CreateBackdrop(self.bar[i], nil, true) CreateBackdrop(self.bar[i], nil, true)
self.bar[i].icon = self.bar[i].icon or self.bar[i]:CreateTexture(nil, "ARTWORK") self.bar[i].icon = self.bar[i].icon or self.bar[i]:CreateTexture(nil, "ARTWORK")
@@ -123,8 +112,7 @@ end
SetAllPointsOffset(self.bar[i].icon, self.bar[i], 2,-2) 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 = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i])
self.bar[i].cdbg:SetHeight(self.iconsize - 3) self.bar[i].cdbg:SetSize(self.iconsize - 3, self.iconsize - 3)
self.bar[i].cdbg:SetWidth(self.iconsize - 3)
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0) self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate") self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
self.bar[i].cd.pfCooldownStyleAnimation = 1 self.bar[i].cd.pfCooldownStyleAnimation = 1
-12
View File
@@ -86,18 +86,6 @@ pfUI:RegisterModule("turtle-wow", function ()
L["debuffs"]['Moonfire'] = {[1]=9.0,[2]=18.0,[3]=18.0,[4]=18.0,[5]=18.0,[6]=18.0,[7]=18.0,[8]=18.0,[9]=18.0,[10]=18.0,[0]=18.0} L["debuffs"]['Moonfire'] = {[1]=9.0,[2]=18.0,[3]=18.0,[4]=18.0,[5]=18.0,[6]=18.0,[7]=18.0,[8]=18.0,[9]=18.0,[10]=18.0,[0]=18.0}
L["debuffs"]['Deep Wound'] = {[0]=6.0} L["debuffs"]['Deep Wound'] = {[0]=6.0}
-- turtle wow totemic recall clear totem indicators
local _, class = UnitClass("player")
if libtotem and class == "SHAMAN" then
local trecall = CreateFrame("Frame", "pfTotemsRecall", UIParent)
trecall:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF")
trecall:SetScript("OnEvent", function()
if arg1 and string.find(arg1, T["You gain (.+) Mana from Totemic Recall"]) then
for i = 1, 4 do libtotem:Clean(i) end
end
end)
end
local delay = CreateFrame("Frame") local delay = CreateFrame("Frame")
delay:SetScript("OnUpdate", function() delay:SetScript("OnUpdate", function()
this:Hide() this:Hide()
+23 -8
View File
@@ -23,7 +23,7 @@ do
-- ClassicAPI dependency check. -- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus -- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required. -- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 10605 -- (X*10000 + Y*100 + Z) local PFUI_CLASSIC_API_MIN = 10704 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI" local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest" local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
@@ -36,9 +36,16 @@ do
if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN) local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN)
pfUI.disabled = true pfUI.disabled = true
EventUtil.ContinueOnPlayerLogin(function() local detail
if not CLASSIC_API_VERSION then
detail = "The ClassicAPI DLL isn't loaded. The |cff33ffcc!!!ClassicAPI|r addon ships bundled with it -- delete your |cff33ffcc!!!ClassicAPI|r folder and install the latest release from:"
else
detail = "ClassicAPI " .. minVersion .. " or newer is required. Delete your |cff33ffcc!!!ClassicAPI|r folder and reinstall the latest release from:"
end
local function ShowRequiredPopup()
StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = { StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = {
text = "This fork of |cff33ffccpf|cffffffffUI|r requires ClassicAPI\n " .. minVersion .. " or newer.\n\nAll |cff33ffccpf|cffffffffUI|r modules have been disabled.\nInstall ClassicAPI from:", text = "|cff33ffccpf|cffffffffUI|r has been disabled.\n\n" .. detail,
button1 = OKAY, button1 = OKAY,
hasEditBox = 1, hasEditBox = 1,
editBoxWidth = 280, editBoxWidth = 280,
@@ -47,7 +54,7 @@ do
hideOnEscape = 1, hideOnEscape = 1,
preferredIndex = 3, preferredIndex = 3,
OnShow = function() OnShow = function()
local editBox = _G[this:GetName().."EditBox"] local editBox = getglobal(this:GetName().."EditBox")
if editBox then if editBox then
editBox:SetText(PFUI_CLASSIC_API_LATEST_URL) editBox:SetText(PFUI_CLASSIC_API_LATEST_URL)
editBox:HighlightText() editBox:HighlightText()
@@ -57,9 +64,15 @@ do
} }
StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED") StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED")
DEFAULT_CHAT_FRAME:AddMessage( DEFAULT_CHAT_FRAME:AddMessage(
"This fork of |cff33ffccpf|cffffffffUI|r requires ClassicAPI " .. minVersion .. "+. Get it at " .. PFUI_CLASSIC_API_LATEST_URL, "|cff33ffccpf|cffffffffUI|r disabled: " .. detail .. " " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.3, 0.3 1, 0.3, 0.3
) )
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
loginFrame:SetScript("OnEvent", function()
loginFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
ShowRequiredPopup()
end) end)
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then
EventUtil.ContinueOnPlayerLogin(function() EventUtil.ContinueOnPlayerLogin(function()
@@ -97,9 +110,11 @@ pfUI.movables = {}
pfUI.version = {} pfUI.version = {}
pfUI.env = {} pfUI.env = {}
pfUI.events = Mixin({}, CallbackRegistryMixin) if not pfUI.disabled then
pfUI.events:OnLoad() pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events:SetUndefinedEventsAllowed(true) pfUI.events:OnLoad()
pfUI.events:SetUndefinedEventsAllowed(true)
end
-- check if macro addons are loaded (disables macrotweak/macroscan) -- check if macro addons are loaded (disables macrotweak/macroscan)
function pfUI:MacroAddonsLoaded() function pfUI:MacroAddonsLoaded()
+1 -2
View File
@@ -45,8 +45,7 @@ pfUI:RegisterSkin("Macro", function ()
MacroNewButton:ClearAllPoints() MacroNewButton:ClearAllPoints()
MacroNewButton:SetPoint("RIGHT", MacroExitButton, "LEFT", -2*bpad, 0) MacroNewButton:SetPoint("RIGHT", MacroExitButton, "LEFT", -2*bpad, 0)
MacroEditButton:SetHeight(22) MacroEditButton:SetSize(150, 22)
MacroEditButton:SetWidth(150)
MacroEditButton:ClearAllPoints() MacroEditButton:ClearAllPoints()
MacroEditButton:SetPoint("BOTTOMLEFT", MacroFrameSelectedMacroButton, "BOTTOMRIGHT", 6, -2) MacroEditButton:SetPoint("BOTTOMLEFT", MacroFrameSelectedMacroButton, "BOTTOMRIGHT", 6, -2)