diff --git a/api/config.lua b/api/config.lua index 0f5dd729..6f03987a 100644 --- a/api/config.lua +++ b/api/config.lua @@ -751,7 +751,7 @@ function pfUI:LoadConfig() pfUI:UpdateConfig("tooltip", nil, "movespeed", "0") pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8") pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0") - pfUI:UpdateConfig("tooltip", "compare", "mode", "extended") + pfUI:UpdateConfig("tooltip", "compare", "basestats", "1") pfUI:UpdateConfig("tooltip", "compare", "showalways", "0") pfUI:UpdateConfig("tooltip", "vendor", "showalways", "1") pfUI:UpdateConfig("tooltip", "questitem", "showquest", "1") @@ -1372,22 +1372,6 @@ function pfUI:MigrateConfig() end end - -- migrate tooltip.compare.basestats + extendedstats into a single mode dropdown - if pfUI_config.tooltip and pfUI_config.tooltip.compare then - local cmp = pfUI_config.tooltip.compare - if cmp.basestats or cmp.extendedstats then - if cmp.basestats == "0" then - cmp.mode = "off" - elseif cmp.extendedstats == "0" then - cmp.mode = "base" - else - cmp.mode = "extended" - end - cmp.basestats = nil - cmp.extendedstats = nil - end - end - -- Remove "Show only own debuffs" from unitframes and nameplates -- (feature was removed; only Target Debuff Bar in buffwatch keeps it) if pfUI_config.nameplates then diff --git a/modules/eqcompare.lua b/modules/eqcompare.lua index c89e1424..ecc439e2 100644 --- a/modules/eqcompare.lua +++ b/modules/eqcompare.lua @@ -1,279 +1,149 @@ pfUI:RegisterModule("eqcompare", function () - local sides = { "Left", "Right" } - - local function AddHeader(tooltip) - local name = tooltip:GetName() - - -- shift all entries one line down - for i=tooltip:NumLines(), 1, -1 do - for _, side in pairs(sides) do - local current = _G[name.."Text"..side..i] - local below = _G[name.."Text"..side..i+1] - - if current and current:IsShown() then - local text = current:GetText() - local r, g, b = current:GetTextColor() - - if text and text ~= "" then - if tooltip:NumLines() < i+1 then - -- add new line if required - tooltip:AddLine(text, r, g, b, true) - else - -- update existing lines - below:SetText(text) - below:SetTextColor(r, g, b) - below:Show() - - -- hide processed line - current:Hide() - end - end - end - end - end - - -- add label to first line - _G[name.."TextLeft1"]:SetTextColor(.5, .5, .5, 1) - _G[name.."TextLeft1"]:SetText(CURRENTLY_EQUIPPED) - _G[name.."TextLeft1"]:Show() - - -- update tooltip sizes - tooltip:Show() - end - - -- Numeric slotTable keyed by Enum.InventoryType (from - -- C_Item.GetItemInventoryTypeByID). Pair-slot types (finger / trinket / - -- one-hand weapon) list both destinations so both comparison tooltips - -- can be shown at once. - local slotTable = { - [1] = { "HeadSlot" }, - [2] = { "NeckSlot" }, - [3] = { "ShoulderSlot" }, - [4] = { "ShirtSlot" }, - [5] = { "ChestSlot" }, - [6] = { "WaistSlot" }, - [7] = { "LegsSlot" }, - [8] = { "FeetSlot" }, - [9] = { "WristSlot" }, - [10] = { "HandsSlot" }, - [11] = { "Finger0Slot", "Finger1Slot" }, - [12] = { "Trinket0Slot", "Trinket1Slot" }, - [13] = { "MainHandSlot", "SecondaryHandSlot" }, -- one-hand weapon - [14] = { "SecondaryHandSlot" }, -- shield - [15] = { "RangedSlot" }, -- ranged (bow) - [16] = { "BackSlot" }, - [17] = { "MainHandSlot" }, -- two-hand weapon - [19] = { "TabardSlot" }, - [20] = { "ChestSlot" }, -- robe - [21] = { "MainHandSlot" }, - [22] = { "SecondaryHandSlot" }, - [23] = { "SecondaryHandSlot" }, -- holdable (off-hand) - [24] = { "AmmoSlot" }, - [25] = { "RangedSlot" }, -- thrown - [26] = { "RangedSlot" }, -- ranged-right (wand/gun/crossbow) - [28] = { "RangedSlot" }, -- relic - } - - local BASE_STAT_KEYS = { - "ITEM_MOD_DAMAGE_PER_SECOND_SHORT", "ITEM_MOD_BLOCK_VALUE", - "ITEM_MOD_STRENGTH_SHORT", "ITEM_MOD_AGILITY_SHORT", - "ITEM_MOD_STAMINA_SHORT", "ITEM_MOD_INTELLECT_SHORT", - "ITEM_MOD_SPIRIT_SHORT", "ITEM_MOD_MANA_SHORT", - "ITEM_MOD_HEALTH_SHORT", - "RESISTANCE0_NAME", "RESISTANCE1_NAME", "RESISTANCE2_NAME", - "RESISTANCE3_NAME", "RESISTANCE4_NAME", "RESISTANCE5_NAME", - "RESISTANCE6_NAME", - } - local EXTENDED_STAT_KEYS = { - "ITEM_MOD_ATTACK_POWER_SHORT", - "ITEM_MOD_RANGED_ATTACK_POWER_SHORT", - "ITEM_MOD_SPELL_DAMAGE_DONE_SHORT", - "ITEM_MOD_SPELL_HEALING_DONE_SHORT", - "ITEM_MOD_CRIT_SPELL_RATING", - "ITEM_MOD_CRIT_MELEE_RATING", "ITEM_MOD_CRIT_RANGED_RATING", - "ITEM_MOD_HIT_MELEE_RATING", "ITEM_MOD_HIT_RANGED_RATING", - "ITEM_MOD_HIT_SPELL_RATING", - "ITEM_MOD_MANA_REGENERATION", "ITEM_MOD_DEFENSE_SKILL_RATING", - "ITEM_MOD_DODGE_RATING", "ITEM_MOD_PARRY_RATING", - "ITEM_MOD_BLOCK_RATING", - } - local STAT_LABELS = {} - local BASE_STAT_LABEL_TO_KEY = {} -- reverse map for inline base-stat match - do - local function noun(fmt) - return fmt and (string.gsub(string.gsub(fmt, "^%%c?%%d ", ""), "^%s+", "")) or nil - end - local baseFmt = { - ITEM_MOD_STRENGTH_SHORT = "ITEM_MOD_STRENGTH", - ITEM_MOD_AGILITY_SHORT = "ITEM_MOD_AGILITY", - ITEM_MOD_STAMINA_SHORT = "ITEM_MOD_STAMINA", - ITEM_MOD_INTELLECT_SHORT = "ITEM_MOD_INTELLECT", - ITEM_MOD_SPIRIT_SHORT = "ITEM_MOD_SPIRIT", - ITEM_MOD_MANA_SHORT = "ITEM_MOD_MANA", - ITEM_MOD_HEALTH_SHORT = "ITEM_MOD_HEALTH", - } - for shortKey, fmtKey in pairs(baseFmt) do - STAT_LABELS[shortKey] = _G[shortKey] or noun(_G[fmtKey]) or shortKey - end - for i = 0, 6 do - STAT_LABELS["RESISTANCE"..i.."_NAME"] = _G["RESISTANCE"..i.."_NAME"] - end - STAT_LABELS.ITEM_MOD_ATTACK_POWER_SHORT = _G.ITEM_MOD_ATTACK_POWER_SHORT or _G.ATTACK_POWER_TOOLTIP or "Attack Power" - STAT_LABELS.ITEM_MOD_RANGED_ATTACK_POWER_SHORT = _G.ITEM_MOD_RANGED_ATTACK_POWER_SHORT or _G.RANGED_ATTACK_POWER or "Ranged Attack Power" - STAT_LABELS.ITEM_MOD_SPELL_DAMAGE_DONE_SHORT = _G.ITEM_MOD_SPELL_DAMAGE_DONE_SHORT or "Spell Damage" - STAT_LABELS.ITEM_MOD_SPELL_HEALING_DONE_SHORT = _G.ITEM_MOD_SPELL_HEALING_DONE_SHORT or "Spell Healing" - STAT_LABELS.ITEM_MOD_CRIT_MELEE_RATING = _G.ITEM_MOD_CRIT_MELEE_RATING or "Melee Crit %" - STAT_LABELS.ITEM_MOD_CRIT_RANGED_RATING = _G.ITEM_MOD_CRIT_RANGED_RATING or "Ranged Crit %" - STAT_LABELS.ITEM_MOD_HIT_MELEE_RATING = _G.ITEM_MOD_HIT_MELEE_RATING or "Melee Hit %" - STAT_LABELS.ITEM_MOD_HIT_RANGED_RATING = _G.ITEM_MOD_HIT_RANGED_RATING or "Ranged Hit %" - STAT_LABELS.ITEM_MOD_HIT_SPELL_RATING = _G.ITEM_MOD_HIT_SPELL_RATING or "Spell Hit %" - STAT_LABELS.ITEM_MOD_MANA_REGENERATION = _G.ITEM_MOD_MANA_REGENERATION or "Mana Regen" - STAT_LABELS.ITEM_MOD_DEFENSE_SKILL_RATING = _G.ITEM_MOD_DEFENSE_SKILL_RATING or "Defense" - STAT_LABELS.ITEM_MOD_DAMAGE_PER_SECOND_SHORT = _G.ITEM_MOD_DAMAGE_PER_SECOND_SHORT or "DPS" - STAT_LABELS.ITEM_MOD_CRIT_SPELL_RATING = _G.ITEM_MOD_CRIT_SPELL_RATING or "Spell Crit %" - STAT_LABELS.ITEM_MOD_DODGE_RATING = _G.ITEM_MOD_DODGE_RATING or "Dodge Rating" - STAT_LABELS.ITEM_MOD_PARRY_RATING = _G.ITEM_MOD_PARRY_RATING or "Parry Rating" - STAT_LABELS.ITEM_MOD_BLOCK_RATING = _G.ITEM_MOD_BLOCK_RATING or "Block Rating" - STAT_LABELS.ITEM_MOD_BLOCK_VALUE = _G.ITEM_MOD_BLOCK_VALUE or "Block Value" - - -- Reverse map for base-stat inline matching (label → key) - for _, key in ipairs(BASE_STAT_KEYS) do - local label = STAT_LABELS[key] - if label then BASE_STAT_LABEL_TO_KEY[label] = key end - end - if _G.BLOCK then - BASE_STAT_LABEL_TO_KEY[_G.BLOCK] = "ITEM_MOD_BLOCK_VALUE" - end - if _G.DPS_TEMPLATE then - local n = string.gsub(_G.DPS_TEMPLATE, "%%.-f%s*", "") - n = string.gsub(n, "^[%s%(]+", "") - n = string.gsub(n, "[%s%)]+$", "") - if n ~= "" then BASE_STAT_LABEL_TO_KEY[n] = "ITEM_MOD_DAMAGE_PER_SECOND_SHORT" end - end - end - - local function AnnotateBaseStatsInline(tooltip, delta) - for _, region in ipairs({tooltip:GetRegions()}) do - if region and region.GetObjectType and region:GetObjectType() == "FontString" then - local text = region:GetText() - if text and text ~= "" - and not strfind(text, "|cff88ff88", 1, true) - and not strfind(text, "|cffff8888", 1, true) then - -- Match "value noun" tooltip lines. Base stats look like - -- "+5 Strength" / "5 Fire Resistance" / "20 Block"; DPS looks - -- like "(84.5 damage per second)". Same shape once we accept - -- an optional leading "(", an optional "+", and decimals. - local _, endIdx = strfind(text, "^%(?%+?[%d%.]+%s+") - if endIdx then - local rest = string.sub(text, endIdx + 1) - rest = gsub(rest, "[%s%)]+$", "") - local key = BASE_STAT_LABEL_TO_KEY[rest] - local v = key and delta[key] - if v and v ~= 0 then - local shown = (key == "ITEM_MOD_DAMAGE_PER_SECOND_SHORT") and round(v, 1) or v - local color = shown > 0 and "|cff88ff88" or "|cffff8888" - local sign = shown > 0 and "+" or "" - region:SetText(text .. " " .. color .. "(" .. sign .. shown .. ")|r") - end - end - end - end - end - end - - local function AppendSummaryBlock(tooltip, delta, keys) - local first = true - for _, key in ipairs(keys) do - local v = delta[key] - if v and v ~= 0 then - if first then - tooltip:AddLine(" ") - tooltip:AddLine(T["Compared to equipped:"] or "Compared to equipped:", 0.6, 0.6, 0.6) - first = false - end - local shown = (key == "ITEM_MOD_DAMAGE_PER_SECOND_SHORT") and round(v, 1) or v - local color = shown > 0 and "|cff88ff88" or "|cffff8888" - local sign = shown > 0 and "+" or "" - tooltip:AddDoubleLine(STAT_LABELS[key] or key, color .. sign .. shown .. "|r") - end - end - end - pfUI.eqcompare = {} - pfUI.eqcompare.tooltip = GameTooltip - local function AnnotateTooltip(tooltip) - if not IsShiftKeyDown() then - if C.tooltip.compare.showalways ~= "1" then return end - local owner = tooltip:GetOwner() - local parent = owner and owner:GetParent() - if parent == PaperDollFrame then return end + local function ShowCompareItem(self, link, shift) + self = self or GameTooltip + + if not link or (not IsShiftKeyDown() and (C.tooltip.compare.showalways ~= "1" or C_Item.IsEquippedItem(link))) then + return end - local _, newLink, itemID = tooltip:GetItem() - if not itemID then return end - local invType = C_Item.GetItemInventoryTypeByID(itemID) - local slots = invType and slotTable[invType] - if not slots then return end + local shoppingTooltip1, shoppingTooltip2 = unpack(self.shoppingTooltips or { ShoppingTooltip1, ShoppingTooltip2 }); - local _, border = GetBorderSize() + local SEPARATION = 6; + local backdrop = shoppingTooltip1.GetBackdrop and shoppingTooltip1:GetBackdrop(); + local GAP = SEPARATION + ((type(backdrop) == "table" and backdrop.edgeSize) or 0); - -- determine screen part - local ltrigger = GetScreenWidth() / 2 - local x = GetCursorPosition() - x = x / UIParent:GetEffectiveScale() - if x > ltrigger then ltrigger = nil end + local item1 = nil; + local item2 = nil; + local side = "left"; + if ( shoppingTooltip1:SetHyperlinkCompareItem(link, 1, shift, self) ) then + item1 = true; + end + if ( shoppingTooltip2:SetHyperlinkCompareItem(link, 2, shift, self) ) then + item2 = true; + end - -- first equipped comparison tooltip - local slotID = GetInventorySlotInfo(slots[1]) - ShoppingTooltip1:SetOwner(tooltip, "ANCHOR_NONE") - ShoppingTooltip1:ClearAllPoints() - if ltrigger then - ShoppingTooltip1:SetPoint("BOTTOMLEFT", tooltip, "BOTTOMRIGHT", 0, 0) + -- find correct side + local rightDist = 0; + local leftPos = self:GetLeft(); + local rightPos = self:GetRight(); + if ( not rightPos ) then + rightPos = 0; + end + if ( not leftPos ) then + leftPos = 0; + end + + rightDist = GetScreenWidth() - rightPos; + + if (leftPos and (rightDist < leftPos)) then + side = "left"; else - ShoppingTooltip1:SetPoint("BOTTOMRIGHT", tooltip, "BOTTOMLEFT", -border*2-1, 0) + side = "right"; end - ShoppingTooltip1:SetInventoryItem("player", slotID) - ShoppingTooltip1:Show() - AddHeader(ShoppingTooltip1) - -- second tooltip for pair slots (finger / trinket / 1H weapon) - if slots[2] then - local slotID_other = GetInventorySlotInfo(slots[2]) - ShoppingTooltip2:SetOwner(tooltip, "ANCHOR_NONE") - ShoppingTooltip2:ClearAllPoints() - if ltrigger then - ShoppingTooltip2:SetPoint("BOTTOMLEFT", ShoppingTooltip1, "BOTTOMRIGHT", 0, 0) - else - ShoppingTooltip2:SetPoint("BOTTOMRIGHT", ShoppingTooltip1, "BOTTOMLEFT", -border*2-1, 0) + -- see if we should slide the tooltip + if ( self:GetAnchorType() and self:GetAnchorType() ~= "ANCHOR_PRESERVE" ) then + local totalWidth = 0; + if ( item1 ) then + totalWidth = totalWidth + shoppingTooltip1:GetWidth(); + end + if ( item2 ) then + totalWidth = totalWidth + shoppingTooltip2:GetWidth(); + end + + if ( (side == "left") and (totalWidth > leftPos) ) then + self:SetAnchorType(self:GetAnchorType(), (totalWidth - leftPos), 0); + elseif ( (side == "right") and (rightPos + totalWidth) > GetScreenWidth() ) then + self:SetAnchorType(self:GetAnchorType(), -((rightPos + totalWidth) - GetScreenWidth()), 0); end - ShoppingTooltip2:SetInventoryItem("player", slotID_other) - ShoppingTooltip2:Show() - AddHeader(ShoppingTooltip2) end - local mode = C.tooltip.compare.mode - if mode ~= "off" then - local equippedLink = GetInventoryItemLink("player", slotID) - if newLink and equippedLink then - local delta = C_Item.GetItemStatDelta(equippedLink, newLink) - if delta then - if mode == "extended" then - local keys = {} - for _, k in ipairs(BASE_STAT_KEYS) do table.insert(keys, k) end - for _, k in ipairs(EXTENDED_STAT_KEYS) do table.insert(keys, k) end - AppendSummaryBlock(tooltip, delta, keys) - else - AnnotateBaseStatsInline(tooltip, delta) - end + if ( item1 ) then + shoppingTooltip1:SetOwner(self, "ANCHOR_NONE"); + shoppingTooltip1:ClearAllPoints(); + if ( side and side == "left" ) then + shoppingTooltip1:SetPoint("TOPRIGHT", self, "TOPLEFT", -GAP, -10); + else + shoppingTooltip1:SetPoint("TOPLEFT", self, "TOPRIGHT", GAP, -10); + end + shoppingTooltip1:SetHyperlinkCompareItem(link, 1, shift, self); + shoppingTooltip1:Show(); + + if ( item2 ) then + shoppingTooltip2:SetOwner(shoppingTooltip1, "ANCHOR_NONE"); + shoppingTooltip2:ClearAllPoints(); + if ( side and side == "left" ) then + shoppingTooltip2:SetPoint("TOPRIGHT", shoppingTooltip1, "TOPLEFT", -GAP, 0); + else + shoppingTooltip2:SetPoint("TOPLEFT", shoppingTooltip1, "TOPRIGHT", GAP, 0); end + shoppingTooltip2:SetHyperlinkCompareItem(link, 2, shift, self); + shoppingTooltip2:Show(); end end end - pfUI.eqcompare.AnnotateTooltip = AnnotateTooltip - GameTooltip.HookScript = GameTooltip.HookScript or HookScript - pfUI.eqcompare.GameTooltipShow = function() AnnotateTooltip(this) end + local prevMerchant = ShoppingTooltip1.SetMerchantCompareItem + local function SetMerchantCompareItem(self, index, compareItem) + if C.tooltip.compare.basestats == "1" and compareItem == 1 then + ShowCompareItem(nil, GetMerchantItemLink(index)) + return false + end + return prevMerchant and prevMerchant(self, index, compareItem) + end - GameTooltip:HookScript("OnShow", pfUI.eqcompare.GameTooltipShow) + local prevAuction = ShoppingTooltip1.SetAuctionCompareItem + local function SetAuctionCompareItem(self, type, index, compareItem) + if C.tooltip.compare.basestats == "1" and compareItem == 1 then + ShowCompareItem(nil, GetAuctionItemLink(type, index)) + return false + end + return prevAuction and prevAuction(self, type, index, compareItem) + end + + ShoppingTooltip1.SetMerchantCompareItem = SetMerchantCompareItem + ShoppingTooltip2.SetMerchantCompareItem = SetMerchantCompareItem + ShoppingTooltip1.SetAuctionCompareItem = SetAuctionCompareItem + ShoppingTooltip2.SetAuctionCompareItem = SetAuctionCompareItem + + local TooltipHooks = { + SetLootRollItem = GetLootRollItemLink, + SetLootItem = GetLootSlotItemLink, + SetQuestLogItem = GetQuestLogItemLink, + SetQuestItem = GetQuestItemLink, + SetHyperlink = function(link) return link end, + SetBagItem = GetContainerItemLink, + SetInboxItem = GetInboxItemLink, + SetInventoryItem = GetInventoryItemLink, + SetTradeSkillItem = function(skillIndex, reagentIndex) + if reagentIndex then + return GetTradeSkillReagentItemLink(skillIndex, reagentIndex) + else + return GetTradeSkillItemLink(skillIndex) + end + end, + SetAuctionSellItem = GetAuctionSellItemLink, + SetTradePlayerItem = GetTradePlayerItemLink, + SetTradeTargetItem = GetTradeTargetItemLink + } + + local function makeHook(getter) + return function(tooltip, arg1, arg2, arg3) + if C.tooltip.compare.basestats == "1" then + ShowCompareItem(tooltip, getter(arg1, arg2, arg3)) + end + end + end + + local function HookTooltip(tooltip) + for setter, getter in pairs(TooltipHooks) do + _G['hooksecurefunc'](tooltip, setter, makeHook(getter)) + end + end + + HookTooltip(GameTooltip) + + pfUI.eqcompare.HookTooltip = HookTooltip end) diff --git a/modules/gui.lua b/modules/gui.lua index 93893724..80505dde 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -2754,14 +2754,10 @@ pfUI:RegisterModule("gui", function () CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox") CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha") CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture) - CreateConfig(nil, T["Item Comparison"], C.tooltip.compare, "mode", "dropdown", { - "off:"..(T["Off"] or "Off"), - "base:"..(T["Base Stats"] or "Base Stats"), - "extended:"..(T["Extended Stats"] or "Extended Stats"), - }) + CreateConfig(nil, T["Compare Item Base Stats"], C.tooltip.compare, "basestats", "checkbox") local showAlways = CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox") local function gate() - local on = C.tooltip.compare.mode ~= "off" + local on = C.tooltip.compare.basestats == "1" if on then showAlways.input:Enable() showAlways.caption:SetTextColor(1, 1, 1) @@ -2772,7 +2768,7 @@ pfUI:RegisterModule("gui", function () end gate() pfUI.events:RegisterCallback("config:changed", function(_, cat, key) - if cat == C.tooltip.compare and key == "mode" then gate() end + if cat == C.tooltip.compare and key == "basestats" then gate() end end, "eqcompare-showalways-gate") CreateConfig(nil, T["Always Show Extended Vendor Values"], C.tooltip.vendor, "showalways", "checkbox") CreateConfig(U["questitem"], T["Show Related Quest On Questitems"], C.tooltip.questitem, "showquest", "checkbox") diff --git a/modules/thirdparty-vanilla.lua b/modules/thirdparty-vanilla.lua index 1fe986b4..135d5567 100644 --- a/modules/thirdparty-vanilla.lua +++ b/modules/thirdparty-vanilla.lua @@ -943,7 +943,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function() CreateBackdropShadow(AtlasLootTooltip) if pfUI.eqcompare then - HookScript(AtlasLootTooltip, "OnShow", pfUI.eqcompare.GameTooltipShow) + pfUI.eqcompare.HookTooltip(AtlasLootTooltip) HookScript(AtlasLootTooltip, "OnHide", function() ShoppingTooltip1:Hide() ShoppingTooltip2:Hide()