diff --git a/init/modules.xml b/init/modules.xml
index 182c993a..c9b0069f 100644
--- a/init/modules.xml
+++ b/init/modules.xml
@@ -81,4 +81,5 @@
+
\ No newline at end of file
diff --git a/libs/libdebuff.lua b/libs/libdebuff.lua
index 3498079e..b05a099b 100644
--- a/libs/libdebuff.lua
+++ b/libs/libdebuff.lua
@@ -376,11 +376,9 @@ local function GetDebuffSlotMap(guid)
local texture = libdebuff:GetSpellIcon(spellId)
local stacks = (auraApps and auraApps[auraSlot] or 0) + 1
local dtype = nil
- if GetSpellRecField then
- local dispelId = GetSpellRecField(spellId, "dispel")
- if dispelId and dispelId > 0 then
- dtype = dispelTypeMap[dispelId]
- end
+ local dispelId = C_Spell.GetSpellDispelType(spellId)
+ if dispelId and dispelId > 0 then
+ dtype = dispelTypeMap[dispelId]
end
map[displaySlot] = {
auraSlot = auraSlot,
diff --git a/libs/libunitscan.lua b/libs/libunitscan.lua
index ad00ed8c..fed17d81 100644
--- a/libs/libunitscan.lua
+++ b/libs/libunitscan.lua
@@ -96,13 +96,12 @@ libunitscan:SetScript("OnEvent", function()
RememberByUnit("player", name, class)
elseif event == "FRIENDLIST_UPDATE" then
- local name, class, level
for i = 1, GetNumFriends() do
- name, level, class = GetFriendInfo(i)
- class = L["class"][class] or nil
- -- friendlist updates due to friend going off-line return level 0, let's not overwrite good older values
- level = level > 0 and level or nil
- AddData("players", name, class, level)
+ local info = C_FriendList.GetFriendInfoByIndex(i)
+ if info then
+ local level = info.level > 0 and info.level or nil
+ AddData("players", info.name, info.classFilename, level)
+ end
end
elseif event == "GUILD_ROSTER_UPDATE" then
@@ -137,11 +136,11 @@ libunitscan:SetScript("OnEvent", function()
end
elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then
- local name, class, level, guild, _
- for i = 1, GetNumWhoResults() do
- name, guild, level, _, class, _ = GetWhoInfo(i)
- class = L["class"][class] or nil
- AddData("players", name, class, level, nil, guild)
+ for i = 1, C_FriendList.GetNumWhoResults() do
+ local info = C_FriendList.GetWhoInfo(i)
+ if info then
+ AddData("players", info.fullName, info.filename, info.level, nil, info.fullGuildName)
+ end
end
elseif event == "UPDATE_MOUSEOVER_UNIT" or event == "PLAYER_TARGET_CHANGED" or event == "NAME_PLATE_UNIT_ADDED" then
diff --git a/modules/friendnotes.lua b/modules/friendnotes.lua
new file mode 100644
index 00000000..dfc51466
--- /dev/null
+++ b/modules/friendnotes.lua
@@ -0,0 +1,79 @@
+-- Friend notes
+-- Client-side notes for friends via ClassicAPI's C_FriendList note API
+-- (SetFriendNotes / GetFriendInfo(.notes)). Adds an "Edit Note" entry to the
+-- friend right-click menu (the "FRIEND" UnitPopup) and shows the note in a
+-- tooltip when you hover a friend in the list.
+pfUI:RegisterModule("friendnotes", function ()
+ local EDIT_TOKEN = "PFUI_FRIEND_NOTE"
+
+ -- Note editor. In 1.12 StaticPopup, OnAccept runs with `this` = the OK
+ -- button (the dialog is this:GetParent()); the friend name arrives as the
+ -- data argument passed to StaticPopup_Show.
+ StaticPopupDialogs["PFUI_FRIEND_NOTE_EDIT"] = {
+ text = SET_FRIENDNOTE_LABEL,
+ button1 = SAVE,
+ button2 = CANCEL,
+ hasEditBox = 1,
+ maxLetters = 128,
+ OnAccept = function(name)
+ C_FriendList.SetFriendNotes(name, _G[this:GetParent():GetName().."EditBox"]:GetText())
+ end,
+ EditBoxOnEnterPressed = function(name)
+ C_FriendList.SetFriendNotes(name, this:GetText())
+ this:GetParent():Hide()
+ end,
+ EditBoxOnEscapePressed = function() this:GetParent():Hide() end,
+ timeout = 0, whileDead = 1, hideOnEscape = 1,
+ }
+
+ local function OpenNoteEditor(name)
+ if not name or name == "" then return end
+ local info = C_FriendList.GetFriendInfo(name)
+ local dialog = StaticPopup_Show("PFUI_FRIEND_NOTE_EDIT", name, nil, name)
+ if not dialog then return end
+ local editbox = _G[dialog:GetName().."EditBox"]
+ editbox:SetText((info and info.notes) or "")
+ editbox:HighlightText()
+ editbox:SetFocus()
+ end
+
+ -- Add "Edit Note" to the friend right-click menu, just before Cancel.
+ UnitPopupButtons[EDIT_TOKEN] = { text = SET_NOTE, dist = 0 }
+ local friendMenu = UnitPopupMenus["FRIEND"]
+ table.insert(friendMenu, table.getn(friendMenu), EDIT_TOKEN)
+
+ -- Route our entry to the editor and close the menu; delegate the rest. A
+ -- bare assignment lands on pfUI.env, so set the global explicitly.
+ local UnitPopup_OnClick_orig = UnitPopup_OnClick
+ _G.UnitPopup_OnClick = function()
+ if this and this.value == EDIT_TOKEN then
+ local dropdown = _G[UIDROPDOWNMENU_INIT_MENU]
+ local name = dropdown and dropdown.name
+ CloseDropDownMenus()
+ OpenNoteEditor(name)
+ return
+ end
+ return UnitPopup_OnClick_orig()
+ end
+
+ -- Show the note in a tooltip while hovering a friend in the list. Friend
+ -- buttons carry their friend index via SetID (see FriendsFrame_Update).
+ local function FriendButton_OnEnter()
+ local index = this:GetID()
+ if not index or index < 1 then return end
+ local info = C_FriendList.GetFriendInfoByIndex(index)
+ if not info or not info.notes or info.notes == "" then return end
+ GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
+ GameTooltip:SetText(info.name or "")
+ GameTooltip:AddLine(info.notes, 1, 1, 1, 1)
+ GameTooltip:Show()
+ end
+
+ for i = 1, FRIENDS_TO_DISPLAY do
+ local button = _G["FriendsFrameFriendButton"..i]
+ if button then
+ button:HookScript("OnEnter", FriendButton_OnEnter)
+ button:HookScript("OnLeave", GameTooltip_Hide)
+ end
+ end
+end)
diff --git a/modules/panel.lua b/modules/panel.lua
index 11a078b9..39bfe6f6 100644
--- a/modules/panel.lua
+++ b/modules/panel.lua
@@ -296,18 +296,18 @@ pfUI:RegisterModule("panel", function()
local playerzone = GetRealZoneText()
for friendIndex=1, all do
- local friend_name, friend_level, friend_class, friend_area, friend_connected = GetFriendInfo(friendIndex)
- if friend_connected and friend_class and friend_level then
+ local info = C_FriendList.GetFriendInfoByIndex(friendIndex)
+ if info and info.connected and info.classFilename and info.level then
if not init then
GameTooltip_SetDefaultAnchor(GameTooltip, this)
GameTooltip:ClearLines()
GameTooltip:AddLine("|cff555555" .. T["Friends Online"])
init = true
end
- local ccolor = PFUI_CLASS_COLORS[L["class"][friend_class]] or { 1, 1, 1 }
- local lcolor = GetDifficultyColor(tonumber(friend_level)) or { 1, 1, 1 }
- local zcolor = friend_area == playerzone and "|cff33ffcc" or "|cffcccccc"
- GameTooltip:AddDoubleLine(rgbhex(ccolor) .. friend_name .. rgbhex(lcolor) .. " [" .. friend_level .. "]", zcolor .. friend_area)
+ local ccolor = PFUI_CLASS_COLORS[info.classFilename]
+ local lcolor = GetDifficultyColor(tonumber(info.level)) or { 1, 1, 1 }
+ local zcolor = info.area == playerzone and "|cff33ffcc" or "|cffcccccc"
+ GameTooltip:AddDoubleLine(ccolor:WrapTextInColorCode(info.name) .. rgbhex(lcolor) .. " [" .. info.level .. "]", zcolor .. info.area)
end
end
@@ -315,15 +315,7 @@ pfUI:RegisterModule("panel", function()
end
widget.Click = function() ToggleFriendsFrame(1) end
widget:SetScript("OnEvent", function()
- local online = 0
- local all = GetNumFriends()
- for friendIndex=1, all do
- local friend_name, friend_level, friend_class, friend_area, friend_connected = GetFriendInfo(friendIndex)
- if ( friend_connected ) then
- online = online + 1
- end
- end
-
+ local online = C_FriendList.GetNumOnlineFriends()
pfUI.panel:OutputPanel("friends", FRIENDS .. ": " .. online, widget.Tooltip, widget.Click)
end)
end
diff --git a/modules/socialmod.lua b/modules/socialmod.lua
index 4cf14cf3..b13a660d 100644
--- a/modules/socialmod.lua
+++ b/modules/socialmod.lua
@@ -77,20 +77,19 @@ pfUI:RegisterModule("socialmod", function ()
local off = FauxScrollFrame_GetOffset(FriendsFrameFriendsScrollFrame)
for i=1, FRIENDS_TO_DISPLAY do
- local name, level, class, zone, connected, status = GetFriendInfo(off + i)
- if not name or name == _G.UNKNOWN then break end
+ local info = C_FriendList.GetFriendInfoByIndex(off + i)
+ if not info or not info.name or info.name == _G.UNKNOWN then break end
+ local name = info.name
local friendName = _G["FriendsFrameFriendButton"..i.."ButtonTextName"]
local friendLoc = _G["FriendsFrameFriendButton"..i..FRIENDS_NAME_LOCATION]
local friendInfo = _G["FriendsFrameFriendButton"..i.."ButtonTextInfo"]
local caption = friendName or friendLoc
- if connected then
- if not class or class == _G.UNKNOWN then break end
- local ccolor = PFUI_CLASS_COLORS[L["class"][class]] or { 1, 1, 1 }
- local lcolor = GetDifficultyColor(tonumber(level)) or { 1, 1, 1 }
-
- zone = ( zone == playerzone and "|cffffffff" or "|cffcccccc" ) .. zone .. "|r"
- local cname = rgbhex(ccolor) .. name .. "|r"
+ if info.connected then
+ local ccolor = PFUI_CLASS_COLORS[info.classFilename]
+ local status = info.afk and CHAT_FLAG_AFK or info.dnd and CHAT_FLAG_DND or ""
+ local zone = ( info.area == playerzone and "|cffffffff" or "|cffcccccc" ) .. info.area .. "|r"
+ local cname = ccolor:WrapTextInColorCode(name)
if playerdb[name] then
playerdb[name].lastseen = date("%a %d-%b-%Y")
playerdb[name].cname = cname
@@ -103,7 +102,7 @@ pfUI:RegisterModule("socialmod", function ()
friendLoc:SetText(format(TEXT(FRIENDS_LIST_TEMPLATE), cname, zone, status))
end
- friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), level, class))
+ friendInfo:SetText(format(TEXT(FRIENDS_LEVEL_TEMPLATE), info.level, info.className))
caption:SetVertexColor(1,1,1,.9)
friendInfo:SetVertexColor(1,1,1,.9)
else
@@ -124,74 +123,73 @@ pfUI:RegisterModule("socialmod", function ()
do -- add colors to who list
hooksecurefunc("WhoList_Update", function()
- local num, max = GetNumWhoResults()
+ local num, max = C_FriendList.GetNumWhoResults()
local off = FauxScrollFrame_GetOffset(WhoListScrollFrame)
local playerzone = GetRealZoneText()
local playerrace = UnitRace("player")
local playerguild = GetGuildInfo("player")
+ if num + 1 >= MAX_WHOS_FROM_SERVER then
+ WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), max).." |cffaaaaaa"..format(WHO_FRAME_SHOWN_TEMPLATE, MAX_WHOS_FROM_SERVER))
+ else
+ WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), num).." |cffaaaaaa"..format(WHO_FRAME_SHOWN_TEMPLATE, num))
+ end
+
for i=1, WHOS_TO_DISPLAY do
- local name, guild, level, race, class, zone = GetWhoInfo(off + i)
- local displayedText = ""
+ local info = C_FriendList.GetWhoInfo(off + i)
+ if info then
+ -- filename is the class token, so no L["class"] reversal is needed
+ local class = info.filename
- if num + 1 >= MAX_WHOS_FROM_SERVER then
- displayedText = format(WHO_FRAME_SHOWN_TEMPLATE, MAX_WHOS_FROM_SERVER)
- WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), max).." |cffaaaaaa"..displayedText)
- else
- displayedText = format(WHO_FRAME_SHOWN_TEMPLATE, num)
- WhoFrameTotals:SetText("|cffffffff" .. format(GetText("WHO_FRAME_TOTAL_TEMPLATE", nil, num), num).." |cffaaaaaa"..displayedText)
- end
+ _G["WhoFrameButton"..i.."Name"]:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
- class = L["class"][class]
-
- _G["WhoFrameButton"..i.."Name"]:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
-
- if (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 1) then
- if (zone == playerzone) then
- _G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
- else
- _G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
- end
-
- elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 2) then
- if (guild == playerguild) then
- _G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
- else
- _G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
- end
-
- elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 3) then
- if (race == playerrace) then
- _G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
- else
- _G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
- end
- end
-
- if class then
- local classicon = _G["WhoFrameButton"..i].classicon
- local coords = CLASS_ICON_TCOORDS[class]
- local color = PFUI_CLASS_COLORS[class]
-
- -- do we have classicons? (skin enabled?)
- if classicon then
- _G["WhoFrameButton"..i.."Class"]:SetTextColor(0,0,0,0)
- _G["WhoFrameButton"..i.."Name"]:SetTextColor(color.r,color.g,color.b,1)
-
- if coords then
- classicon:Show()
- classicon:SetTexCoord(unpack(coords))
+ if (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 1) then
+ if (info.area == playerzone) then
+ _G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
else
- classicon:Hide()
+ _G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
end
- else
- _G["WhoFrameButton"..i.."Class"]:SetTextColor(color.r,color.g,color.b,1)
- end
- end
- local color = GetDifficultyColor(level)
- _G["WhoFrameButton"..i.."Level"]:SetTextColor(color.r, color.g, color.b)
+ elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 2) then
+ if (info.fullGuildName == playerguild) then
+ _G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
+ else
+ _G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
+ end
+
+ elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 3) then
+ if (info.raceStr == playerrace) then
+ _G["WhoFrameButton"..i.."Variable"]:SetTextColor(.5, 1, 1)
+ else
+ _G["WhoFrameButton"..i.."Variable"]:SetTextColor(1, 1, 1)
+ end
+ end
+
+ if class then
+ local classicon = _G["WhoFrameButton"..i].classicon
+ local coords = CLASS_ICON_TCOORDS[class]
+ local color = PFUI_CLASS_COLORS[class]
+
+ -- do we have classicons? (skin enabled?)
+ if classicon then
+ _G["WhoFrameButton"..i.."Class"]:SetTextColor(0,0,0,0)
+ _G["WhoFrameButton"..i.."Name"]:SetTextColor(color.r,color.g,color.b,1)
+
+ if coords then
+ classicon:Show()
+ classicon:SetTexCoord(unpack(coords))
+ else
+ classicon:Hide()
+ end
+ else
+ _G["WhoFrameButton"..i.."Class"]:SetTextColor(color.r,color.g,color.b,1)
+ end
+ end
+
+ local color = GetDifficultyColor(info.level)
+ _G["WhoFrameButton"..i.."Level"]:SetTextColor(color.r, color.g, color.b)
+ end
end
end)
end
diff --git a/modules/swingtimer.lua b/modules/swingtimer.lua
index 38100f86..d9d5a99a 100644
--- a/modules/swingtimer.lua
+++ b/modules/swingtimer.lua
@@ -9,11 +9,6 @@ pfUI:RegisterModule("swingtimer", function ()
local ON_SWING_QUEUED = 0
local ON_SWING_QUEUE_POPPED = 1
- -- Spell.dbc bits used to mirror server-side swing-reset rules.
- local FLAG_AUTOATTACK = tonumber("0x08", 16) -- SPELL_INTERRUPT_FLAG_AUTOATTACK
- local ATTR_KEEP_SWINGS = tonumber("0x20000", 16) -- SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS
- local ATTR_ON_NEXT_SWING = tonumber("0x04", 16) -- SPELL_ATTR_ON_NEXT_SWING
-
-- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max)
local S = {
mhTimer = 0, mhTimerMax = 1,
@@ -43,12 +38,11 @@ pfUI:RegisterModule("swingtimer", function ()
local WAND_SHOOT_SPELLID = 5019
local THROW_SPELLID = 2764 -- one-shot ranged, not auto-repeat
- -- ATTR_ON_NEXT_SWING: spell replaces next auto-attack swing.
+ -- On-next-swing spells replace the next auto-attack swing.
-- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically.
local function IsOnSwingSpell(spellId)
if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end
- local attr = GetSpellRecField(spellId, "attributes") or 0
- local result = bit.band(attr, ATTR_ON_NEXT_SWING) ~= 0
+ local result = C_Spell.IsNextMeleeSpell(spellId)
S.onSwingCache[spellId] = result
return result
end
@@ -734,13 +728,12 @@ pfUI:RegisterModule("swingtimer", function ()
S.pendingCastSpellId = arg1
-- Freeze the swing timer for cast-time spells that DON'T reset auto-
-- attack on completion (Slam, Hammer of Wrath on Turtle, etc.) — those
- -- let the swing resume from where it paused. Detect dynamically via the
- -- absent AUTOATTACK interrupt flag (8); spells with that bit reset on
- -- SPELL_GO_SELF so freezing isn't necessary. Subsumes the old hardcoded
- -- swingDelaySpells list (no list maintenance for new Slam-style spells).
+ -- let the swing resume from where it paused. C_Spell.ResetsMeleeSwing
+ -- mirrors the server rule; spells that reset don't need freezing (they
+ -- reset on SPELL_GO_SELF). Subsumes the old hardcoded swingDelaySpells
+ -- list (no list maintenance for new Slam-style spells).
if S.mhActive then
- local iflags = GetSpellRecField(arg1, "interruptFlags") or 0
- if bit.band(iflags, FLAG_AUTOATTACK) == 0 then
+ if not C_Spell.ResetsMeleeSwing(arg1) then
S.mhFrozenAt = GetTime()
end
end
@@ -777,17 +770,13 @@ pfUI:RegisterModule("swingtimer", function ()
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
ResetMH()
else
- -- Mirror the server rule for "does this spell reset the auto-attack
- -- swing" (Spell::IsMeleeAttackResetSpell in Turtle's core):
- -- InterruptFlags has SPELL_INTERRUPT_FLAG_AUTOATTACK (0x08)
- -- AND AttributesEx2 lacks NOT_RESET_AUTO_ACTIONS (0x20000).
- -- If neither path resets and we're holding a frozen-swing-during-cast
- -- (mhFrozenAt set by SPELL_START_SELF for non-AUTOATTACK spells), this
- -- is a Slam-style cast — push the timer forward by the cast duration
- -- so the bar resumes from where it paused.
- local iflags = GetSpellRecField(spellId, "interruptFlags") or 0
- if bit.band(iflags, FLAG_AUTOATTACK) ~= 0
- and bit.band(GetSpellRecField(spellId, "attributesEx2") or 0, ATTR_KEEP_SWINGS) == 0 then
+ -- C_Spell.ResetsMeleeSwing mirrors the server rule (Turtle's
+ -- Spell::IsMeleeAttackResetSpell): InterruptFlags has AUTOATTACK and
+ -- AttributesEx2 lacks NOT_RESET_AUTO_ACTIONS. When the spell resets the
+ -- swing, snap the timers to full. Otherwise (elseif) a frozen-swing-
+ -- during-cast is a Slam-style cast — push the timer forward by the cast
+ -- duration so the bar resumes from where it paused.
+ if C_Spell.ResetsMeleeSwing(spellId) then
if S.mhActive and S.mhSpeed > 0 then
UpdateWeaponSpeeds()
S.mhTimerMax = S.mhSpeed
diff --git a/pfUI.lua b/pfUI.lua
index b8d6532b..3e956e72 100644
--- a/pfUI.lua
+++ b/pfUI.lua
@@ -23,7 +23,7 @@ do
-- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required.
- local PFUI_CLASSIC_API_MIN = 10903 -- (X*10000 + Y*100 + Z)
+ local PFUI_CLASSIC_API_MIN = 10906 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"