diff --git a/api/api.lua b/api/api.lua index cef7360b..51adaff6 100644 --- a/api/api.lua +++ b/api/api.lua @@ -397,14 +397,33 @@ end -- Returns an itemLink for the given itemname -- 'name' [string] name of the item -- returns: [string] entire itemLink for the given item +local itemLinkCache = {} +local itemLinkMisses = {} +local ITEMLINK_MAX_SCANS = 2 function pfUI.api.GetItemLinkByName(name) - for itemID = 1, 25818 do + -- GetInboxItem() hands us a nil name for attachment-less mail + if not name then return end + -- cache successful resolutions so repeated lookups (e.g. per inbox click) are free + if itemLinkCache[name] then return itemLinkCache[name] end + + -- Failures have to be counted, not just retried. An unresolvable name walks + -- the entire id range and finds nothing -- a visible hitch on every tooltip + -- hover. Allow a couple of attempts (rendering the tooltip caches the item, + -- so the next hover usually resolves), then stop scanning for that name. + local misses = itemLinkMisses[name] or 0 + if misses >= ITEMLINK_MAX_SCANS then return end + + -- Octo/Turtle custom items live well past the 25818 vanilla ceiling + for itemID = 1, 61000 do local itemName = C_Item.GetItemNameByID(itemID) if itemName and itemName == name then local _, itemLink = C_Item.GetItemInfo(itemID) + itemLinkCache[name] = itemLink return itemLink end end + + itemLinkMisses[name] = misses + 1 end -- [ FindItem ] @@ -1089,10 +1108,17 @@ end function pfUI.api.GetPerfectPixel() if pfUI.pixel then return pfUI.pixel end - if pfUI_config.appearance.border.pixelperfect == "1" then - local scale = GetCVar("useUiScale") == "1" and GetCVar("uiScale") or "1" - local resolution = GetCVar("gxResolution") - local _, _, screenwidth, screenheight = strfind(resolution, "(.+)x(.+)") + local resolution = GetCVar("gxResolution") or "" + local _, _, screenheight = strfind(resolution, "x(%d+)") + screenheight = tonumber(screenheight) + + if pfUI_config.appearance.border.pixelperfect == "1" and screenheight then + -- The uiScale cvar is not a reliable source: it is capped at 1.0 while both + -- the pixelperfect module and the firstrun slider push UIParent past it via + -- SetScale, and it is ignored completely while useUiScale is off. Ask the + -- frame itself instead, it always reports what is really on screen. + local scale = UIParent:GetEffectiveScale() + if not scale or scale <= 0 then scale = 1 end pfUI.pixel = 768 / screenheight / scale pfUI.pixel = pfUI.pixel > 1 and 1 or pfUI.pixel diff --git a/init/modules.xml b/init/modules.xml index c9b0069f..e7a32b38 100644 --- a/init/modules.xml +++ b/init/modules.xml @@ -1,4 +1,7 @@ + + @@ -53,7 +56,6 @@ - diff --git a/libs/libpredict.lua b/libs/libpredict.lua index 84b65b7f..5fae50c3 100644 --- a/libs/libpredict.lua +++ b/libs/libpredict.lua @@ -140,7 +140,7 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu -- Discard pending and trust the actual targetGuid from SPELL_START_SELF. if pending.targetGuid == targetGuid then local name = UnitName(pending.targetGuid) - if name and name ~= UNKNOWNOBJECT and name ~= UKNOWNBEING then + if name and name ~= UNKNOWNOBJECT and name ~= UNKNOWNBEING then pendingTarget = name pendingTargetGuid = pending.targetGuid end @@ -1222,7 +1222,7 @@ libpredict.sender:SetScript("OnEvent", function() end) function libpredict:GetHotDuration(unit, spell) - if unit == UNKNOWNOBJECT or unit == UNKOWNBEING then return end + if unit == UNKNOWNOBJECT or unit == UNKNOWNBEING then return end -- NEW: Try libdebuff first (Nampower AURA_CAST events) if pfUI.api.libdebuff and pfUI.api.libdebuff.GetBestAuraCast then diff --git a/modules/bags.lua b/modules/bags.lua index a88a8f71..ae950c33 100644 --- a/modules/bags.lua +++ b/modules/bags.lua @@ -1085,21 +1085,6 @@ pfUI:RegisterModule("bags", function () frame.search.edit:ClearFocus() end) - frame.search:SetScript("OnHide", function() - frame.search.edit:SetText(T["Search"]) - for bag = -2, 11 do - if pfUI.bags[bag] then - local bagsize = GetContainerNumSlots(bag) - if bag == -2 and pfUI.bag.showKeyring == true then bagsize = GetKeyRingSize() end - for slot = 1, bagsize do - if pfUI.bags[bag] and pfUI.bags[bag].slots[slot] then - pfUI.bags[bag].slots[slot].frame:SetAlpha(1) - end - end - end - end - end) - frame.search.edit:SetScript("OnMouseUp", function() if arg1 == "RightButton" then this:ClearFocus() diff --git a/modules/buffwatch.lua b/modules/buffwatch.lua index 59be8814..8d7b0d15 100644 --- a/modules/buffwatch.lua +++ b/modules/buffwatch.lua @@ -90,12 +90,14 @@ pfUI:RegisterModule("buffwatch", function () if val == skill then return end end config.whitelist = config.whitelist .. "#" .. skill + fcache[tostring(config)] = nil -- invalidate so the new entry takes effect immediately DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now whitelisted."]) elseif IsShiftKeyDown() then for _, val in pairs({strsplit("#", config.blacklist)}) do if val == skill then return end end config.blacklist = config.blacklist .. "#" .. skill + fcache[tostring(config)] = nil -- invalidate so the new entry takes effect immediately DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now blacklisted."]) end elseif this.parent.unit == "player" then diff --git a/modules/chat.lua b/modules/chat.lua index 2db0f099..d14c7b9f 100644 --- a/modules/chat.lua +++ b/modules/chat.lua @@ -810,23 +810,25 @@ pfUI:RegisterModule("chat", function () end end + -- detect the whisper prefix BEFORE prepending a timestamp; the timestamp + -- pushes the whisper colour code off position 1 and the find below fails + local isWhisper = C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1 + -- show timestamp in chat if C.chat.text.time == "1" then text = timecolorhex .. tleft .. date(C.chat.text.timeformat) .. tright .. "|r " .. text end -- save chat history - if C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1 then + if isWhisper then SaveChatHistory(frame:GetID(), string.gsub(text, wcol, ""), cr, cg, cb) else SaveChatHistory(frame:GetID(), text, a1, a2, a3) end - if C.chat.global.whispermod == "1" then + if isWhisper then -- patch incoming whisper string to match the colors - if string.find(text, wcol, 1) == 1 then - text = string.gsub(text, "|r", "|r" .. wcol) - end + text = string.gsub(text, "|r", "|r" .. wcol) end frame:HookAddMessage(text, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) diff --git a/modules/macrotweak.lua b/modules/macrotweak.lua index 781bee51..9169a5bc 100644 --- a/modules/macrotweak.lua +++ b/modules/macrotweak.lua @@ -13,7 +13,8 @@ pfUI:RegisterModule("macrotweak", function () end -- do not write macro calls into chat input history - if ChatFrameEditBox._AddHistoryLine then + -- (install once: _AddHistoryLine is our backup slot and is nil until we set it) + if not ChatFrameEditBox._AddHistoryLine then local userinput ChatFrameEditBox._AddHistoryLine = ChatFrameEditBox.AddHistoryLine ChatFrameEditBox.AddHistoryLine = function(self, text) diff --git a/modules/mapreveal.lua b/modules/mapreveal.lua index fd177470..ebceb3e6 100644 --- a/modules/mapreveal.lua +++ b/modules/mapreveal.lua @@ -52,19 +52,19 @@ pfUI:RegisterModule("mapreveal", function () WorldMapTooltip:AddLine(this.name, 1, 1, 1) WorldMapTooltip:Show() - if not explorecaches[this.name] then return end + if not explorecaches[this.area] then return end if C.appearance.worldmap.mapreveal == "0" then return end - for texture in pairs(explorecaches[this.name]) do + for texture in pairs(explorecaches[this.area]) do texture:SetVertexColor(1,1,1,1) end end local exploreLeave = function() WorldMapTooltip:Hide() - if not explorecaches[this.name] then return end + if not explorecaches[this.area] then return end if C.appearance.worldmap.mapreveal == "0" then return end local r,g,b,a = GetStringColor(C.appearance.worldmap.mapreveal_color) - for texture in pairs(explorecaches[this.name]) do + for texture in pairs(explorecaches[this.area]) do texture:SetVertexColor(r,g,b,a) end end @@ -120,6 +120,7 @@ pfUI:RegisterModule("mapreveal", function () explore:EnableMouse(true) explore:SetFrameLevel(255) explore.name = mapFileName .. " (" .. name .. ")" + explore.area = name -- cache key: explorecaches is keyed by the plain area name explore.tex = explore.tex or explore:CreateTexture("", "OVERLAY") explore.tex:SetBlendMode("ADD") explore.tex:SetTexCoord(.08, .92, .08, .92) diff --git a/modules/raid.lua b/modules/raid.lua index 1be1c174..81f4389f 100644 --- a/modules/raid.lua +++ b/modules/raid.lua @@ -4,7 +4,9 @@ pfUI:RegisterModule("raid", function () -- tell RaidFrame.lua pfUI replaces party frames HookAddonOrVariable("Blizzard_RaidUI", function() - GROUP_REPLACE_PARTY = "1" + -- must reach _G: RaidFrame.lua reads this global, and pfUI.env has + -- __index but no __newindex, so a bare assignment stays in the sandbox + _G.GROUP_REPLACE_PARTY = "1" end) pfUI.uf.raid = CreateFrame("Frame", "pfRaidUpdater", UIParent) diff --git a/modules/roll.lua b/modules/roll.lua index 9f1c81f8..3475555b 100644 --- a/modules/roll.lua +++ b/modules/roll.lua @@ -7,8 +7,12 @@ pfUI:RegisterModule("roll", function () local LOOT_ROLL_NEED = string.gsub(LOOT_ROLL_NEED, "%%s|Hitem:%%d:%%d:%%d:%%d|h%[%%s%]|h%%s", "%%s") local LOOT_ROLL_PASSED = string.gsub(LOOT_ROLL_PASSED, "%%s|Hitem:%%d:%%d:%%d:%%d|h%[%%s%]|h%%s", "%%s") - -- try to detect the everyone string - local _, _, everyone, _ = strfind(LOOT_ROLL_ALL_PASSED, LOOT_ROLL_PASSED) + -- detect the "everyone passed" subject exactly as the loot scanner will capture + -- it: feed a dummy item into LOOT_ROLL_ALL_PASSED and run the same LOOT_ROLL_PASSED + -- match. (The old strfind had no captures, so `everyone` was always nil and + -- "Everyone has passed on: X" got counted as a fake roller.) + local everyoneSample = string.gsub(LOOT_ROLL_ALL_PASSED, "%%s", "x") + local everyone = cmatch(everyoneSample, LOOT_ROLL_PASSED) pfUI.roll.blacklist = { YOU, everyone } pfUI.roll.cache = {} diff --git a/modules/socialmod.lua b/modules/socialmod.lua index b13a660d..59b00a7f 100644 --- a/modules/socialmod.lua +++ b/modules/socialmod.lua @@ -4,7 +4,7 @@ pfUI:RegisterModule("socialmod", function () pfUI.socialmod:RegisterEvent("CHAT_MSG_SYSTEM") pfUI.socialmod:SetScript("OnEvent", function() local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS) - name = cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S) + name = name or cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S) if name and playerdb[name] and playerdb[name].cname then playerdb[name].lastseen = date("%a %d-%b-%Y") end diff --git a/modules/superwow.lua b/modules/superwow.lua index eedcbb91..cbc4a136 100644 --- a/modules/superwow.lua +++ b/modules/superwow.lua @@ -75,7 +75,10 @@ pfUI:RegisterModule("superwow", function () DEFAULT_CHAT_FRAME:AddMessage("-> https://github.com/balakethelock/SuperWoW/releases/") end - if SUPERWOW_VERSION == "1.5" then + -- compare numerically: an exact string match silently drops this on any + -- SuperWoW release past 1.5 + local swVersion = tonumber(SUPERWOW_VERSION) + if swVersion and swVersion >= 1.5 then QueueFunction(function() local pfCombatText_AddMessage = _G.CombatText_AddMessage _G.CombatText_AddMessage = function(message, a, b, c, d, e, f) diff --git a/modules/swingtimer.lua b/modules/swingtimer.lua index d9d5a99a..c80f8492 100644 --- a/modules/swingtimer.lua +++ b/modules/swingtimer.lua @@ -284,7 +284,9 @@ pfUI:RegisterModule("swingtimer", function () UpdateMovable(pfUI.swingtimer.ranged) -- OH weapon detection - local OH_WEAPON_TYPES = { [13]=true, [21]=true } + -- OH weapon detection: slot 17 (off hand) accepts one-hand (13) and off-hand + -- weapons (22); main-hand-only (21) can never sit there. + local OH_WEAPON_TYPES = { [13]=true, [22]=true } local function HasOffhandWeapon() local l = GetInventoryItemLink("player", 17) if not l then return false end diff --git a/modules/unitxp.lua b/modules/unitxp.lua index e130e6cf..ffa7f8b8 100644 --- a/modules/unitxp.lua +++ b/modules/unitxp.lua @@ -275,7 +275,9 @@ pfUI:RegisterModule("unitxp", function () -- Also notify on BG queue pop local origBattlefieldPortShow = BattlefieldFrame_Show if origBattlefieldPortShow then - BattlefieldFrame_Show = function() + -- pfUI.env has __index but no __newindex, so a bare global assignment + -- inside a RegisterModule closure never leaves the sandbox + _G.BattlefieldFrame_Show = function() pcall(UnitXP, "notify", "taskbarIcon") pcall(UnitXP, "notify", "systemSound") return origBattlefieldPortShow()