SFrames.FloatingTooltip = {} -------------------------------------------------------------------------------- -- Class colors for tooltip unit-name coloring -------------------------------------------------------------------------------- local TT_CLASS_COLORS = { ["WARRIOR"] = { 0.78, 0.61, 0.43 }, ["MAGE"] = { 0.41, 0.80, 0.94 }, ["ROGUE"] = { 1.00, 0.96, 0.41 }, ["DRUID"] = { 1.00, 0.49, 0.04 }, ["HUNTER"] = { 0.67, 0.83, 0.45 }, ["SHAMAN"] = { 0.14, 0.35, 1.00 }, ["PRIEST"] = { 1.00, 1.00, 1.00 }, ["WARLOCK"] = { 0.58, 0.51, 0.79 }, ["PALADIN"] = { 0.96, 0.55, 0.73 }, } local TT_CLASS_REVERSE = {} local ttClassReverseDone = false local function TT_BuildClassReverse() if ttClassReverseDone then return end ttClassReverseDone = true if LOCALIZED_CLASS_NAMES_MALE then for en, loc in pairs(LOCALIZED_CLASS_NAMES_MALE) do TT_CLASS_REVERSE[loc] = en end end if LOCALIZED_CLASS_NAMES_FEMALE then for en, loc in pairs(LOCALIZED_CLASS_NAMES_FEMALE) do TT_CLASS_REVERSE[loc] = en end end local zh = { ["战士"]="WARRIOR", ["法师"]="MAGE", ["盗贼"]="ROGUE", ["德鲁伊"]="DRUID", ["猎人"]="HUNTER", ["萨满祭司"]="SHAMAN", ["牧师"]="PRIEST", ["术士"]="WARLOCK", ["圣骑士"]="PALADIN", } for loc, en in pairs(zh) do if not TT_CLASS_REVERSE[loc] then TT_CLASS_REVERSE[loc] = en end end for en, _ in pairs(TT_CLASS_COLORS) do TT_CLASS_REVERSE[en] = en TT_CLASS_REVERSE[string.lower(en)] = en end end local function TT_GetClassToken(unit) if not UnitExists(unit) then return nil end local className, classEN = UnitClass(unit) if classEN and classEN ~= "" then return string.upper(classEN) end if className then TT_BuildClassReverse() return TT_CLASS_REVERSE[className] end return nil end local function TT_GetClassColor(classToken) if not classToken then return nil, nil, nil end if CUSTOM_CLASS_COLORS and CUSTOM_CLASS_COLORS[classToken] then local c = CUSTOM_CLASS_COLORS[classToken] return c.r, c.g, c.b end if RAID_CLASS_COLORS and RAID_CLASS_COLORS[classToken] then local c = RAID_CLASS_COLORS[classToken] return c.r, c.g, c.b end local c = TT_CLASS_COLORS[classToken] if c then return c[1], c[2], c[3] end return nil, nil, nil end local function TT_ClassHex(classToken) local r, g, b = TT_GetClassColor(classToken) if not r then return "|cffffffff" end return string.format("|cff%02x%02x%02x", r * 255, g * 255, b * 255) end -------------------------------------------------------------------------------- -- Backdrop helper (applied once, not every frame) -------------------------------------------------------------------------------- local TT_BACKDROP = { bgFile = "Interface\\Buttons\\WHITE8X8", insets = { left = 0, right = 0, top = 0, bottom = 0 }, } local function TT_ApplyBackdrop(frame) frame:SetBackdrop(TT_BACKDROP) local _A = SFrames.ActiveTheme if _A and _A.panelBg then frame:SetBackdropColor(_A.panelBg[1], _A.panelBg[2], _A.panelBg[3], 0.95) else frame:SetBackdropColor(0.08, 0.08, 0.08, 0.95) end end -------------------------------------------------------------------------------- -- Level difficulty color (fallback if GetDifficultyColor is unavailable) -------------------------------------------------------------------------------- local function TT_DifficultyColor(unitLevel) local playerLevel = UnitLevel("player") or 1 if unitLevel < 0 then return 1, 0, 0 end local diff = unitLevel - playerLevel if diff >= 5 then return 1, 0.1, 0.1 elseif diff >= 3 then return 1, 0.5, 0.25 elseif diff >= -2 then return 1, 1, 0 elseif diff >= -8 then return 0.25, 0.75, 0.25 else return 0.5, 0.5, 0.5 end end local function TT_GetClassificationText(unit) if not UnitExists(unit) then return nil end local classif = UnitClassification(unit) if classif == "rareelite" then return "|cffc57cff[稀有 精英]|r" elseif classif == "rare" then return "|cffc57cff[稀有]|r" elseif classif == "elite" then return "|cffffa500[精英]|r" elseif classif == "worldboss" then return "|cffff4040[首领]|r" end return nil end -------------------------------------------------------------------------------- -- Initialize -------------------------------------------------------------------------------- function SFrames.FloatingTooltip:Initialize() TT_ApplyBackdrop(GameTooltip) -------------------------------------------------------------------------- -- Standalone backdrop frame: a SIBLING of GameTooltip (parented to -- UIParent, not GameTooltip) at the same TOOLTIP strata but one frame- -- level below. This guarantees it renders directly behind the tooltip -- text and is completely immune to C++ SetBackdrop resets. -------------------------------------------------------------------------- if not GameTooltip._nanamiBG then GameTooltip._nanamiBG = true local bgFrame = CreateFrame("Frame", "NanamiTooltipBG", UIParent) bgFrame:SetFrameStrata("TOOLTIP") bgFrame:SetFrameLevel(math.max(1, GameTooltip:GetFrameLevel()) - 1) bgFrame:Hide() GameTooltip._nanamiBGFrame = bgFrame local bg = bgFrame:CreateTexture(nil, "ARTWORK") bg:SetTexture("Interface\\Buttons\\WHITE8X8") local _Abg = SFrames.ActiveTheme if _Abg and _Abg.panelBg then bg:SetVertexColor(_Abg.panelBg[1], _Abg.panelBg[2], _Abg.panelBg[3], 0.95) else bg:SetVertexColor(0.08, 0.08, 0.08, 0.95) end bg:SetAllPoints(bgFrame) GameTooltip._nanamiBGTex = bg bgFrame._ttVisCheck = 0 bgFrame:SetScript("OnUpdate", function() this._ttVisCheck = this._ttVisCheck + arg1 if this._ttVisCheck < 0.2 then return end this._ttVisCheck = 0 if not GameTooltip:IsVisible() then this:Hide() end end) end -------------------------------------------------------------------------- -- Health bar: position, backdrop, texture, health text -------------------------------------------------------------------------- local barFont = (SFrames and SFrames.GetFont and SFrames:GetFont()) or "Fonts\\ARIALN.TTF" GameTooltipStatusBar:SetHeight(10) GameTooltipStatusBar:ClearAllPoints() GameTooltipStatusBar:SetPoint("BOTTOMLEFT", GameTooltip, "TOPLEFT", 4, 2) GameTooltipStatusBar:SetPoint("BOTTOMRIGHT", GameTooltip, "TOPRIGHT", -4, 2) GameTooltipStatusBar:SetStatusBarTexture(SFrames:GetTexture()) GameTooltipStatusBar.bg = GameTooltipStatusBar.bg or GameTooltipStatusBar:CreateTexture(nil, "BACKGROUND") GameTooltipStatusBar.bg:SetTexture("Interface\\TARGETINGFRAME\\UI-StatusBar") GameTooltipStatusBar.bg:SetVertexColor(.1, .1, 0, .8) GameTooltipStatusBar.bg:SetAllPoints(true) if not GameTooltipStatusBar.backdrop then local bd = CreateFrame("Frame", "SFramesTooltipStatusBarBD", GameTooltipStatusBar) bd:SetPoint("TOPLEFT", GameTooltipStatusBar, "TOPLEFT", -3, 3) bd:SetPoint("BOTTOMRIGHT", GameTooltipStatusBar, "BOTTOMRIGHT", 3, -3) bd:SetBackdrop({ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 12, insets = { left = 3, right = 3, top = 3, bottom = 3 }, }) bd:SetBackdropBorderColor(.8, .8, .8, 1) GameTooltipStatusBar.backdrop = bd end if not GameTooltipStatusBar.healthText then local ht = GameTooltipStatusBar.backdrop:CreateFontString(nil, "DIALOG", "GameFontWhite") ht:SetFont(barFont, 12, "OUTLINE") ht:SetPoint("TOP", 0, 4) ht:SetNonSpaceWrap(false) GameTooltipStatusBar.healthText = ht end if not GameTooltipStatusBar._origSetColor then GameTooltipStatusBar._origSetColor = GameTooltipStatusBar.SetStatusBarColor GameTooltipStatusBar.SetStatusBarColor = function() return end end -------------------------------------------------------------------------- -- Flag: true when tooltip was positioned via GameTooltip_SetDefaultAnchor -- (world mouseover units). False for bag/bank/inventory item tooltips -- that have their own anchoring — those should NOT be cursor-followed. -------------------------------------------------------------------------- local ttUsesDefaultAnchor = false -------------------------------------------------------------------------- -- Track mouseover name/level for health estimation -------------------------------------------------------------------------- local ttMouseName, ttMouseLevel local ttHadUnit = false local barEvents = CreateFrame("Frame", nil, GameTooltipStatusBar) barEvents:RegisterEvent("UPDATE_MOUSEOVER_UNIT") barEvents:SetScript("OnEvent", function() ttMouseName = UnitName("mouseover") ttMouseLevel = UnitLevel("mouseover") if UnitExists("mouseover") then ttHadUnit = true end end) local function TT_Abbreviate(val) if val >= 1000000 then return string.format("%.1fM", val / 1000000) elseif val >= 10000 then return string.format("%.1fK", val / 1000) end return tostring(math.floor(val)) end local barThrottle = 0 barEvents:SetScript("OnUpdate", function() barThrottle = barThrottle + arg1 if barThrottle < 0.1 then return end barThrottle = 0 local hp = GameTooltipStatusBar:GetValue() local _, hpmax = GameTooltipStatusBar:GetMinMaxValues() if hpmax and hpmax > 0 then if hpmax > 100 then GameTooltipStatusBar.healthText:SetText( TT_Abbreviate(hp) .. " / " .. TT_Abbreviate(hpmax)) else GameTooltipStatusBar.healthText:SetText( string.format("%d%%", math.ceil(hp / hpmax * 100))) end else GameTooltipStatusBar.healthText:SetText("") end end) local linesFormatted = false -------------------------------------------------------------------------- -- Track tooltip owner to skip styling for world-map / pfQuest markers -------------------------------------------------------------------------- local ttOwner = nil local orig_SetOwner = GameTooltip.SetOwner GameTooltip.SetOwner = function(self, owner, anchor, xOff, yOff) ttOwner = owner ttUsesDefaultAnchor = false return orig_SetOwner(self, owner, anchor, xOff, yOff) end local function TT_IsMapMarkerTooltip() if not ttOwner then return false end if WorldMapFrame and WorldMapFrame:IsShown() then local name = ttOwner.GetName and ttOwner:GetName() if name and (string.find(name, "^pf") or string.find(name, "^WorldMap")) then return true end local parent = ttOwner.GetParent and ttOwner:GetParent() while parent do if parent == WorldMapFrame or parent == WorldMapButton then return true end parent = parent.GetParent and parent:GetParent() end end return false end local function TT_ShowBar(show) if not GameTooltipStatusBar then return end GameTooltipStatusBar:SetStatusBarTexture(SFrames:GetTexture()) if show then GameTooltipStatusBar:SetAlpha(1) GameTooltipStatusBar:Show() if GameTooltipStatusBar.backdrop then GameTooltipStatusBar.backdrop:SetAlpha(1) GameTooltipStatusBar.backdrop:Show() end else GameTooltipStatusBar:SetAlpha(0) if GameTooltipStatusBar.backdrop then GameTooltipStatusBar.backdrop:SetAlpha(0) end if GameTooltipStatusBar.healthText then GameTooltipStatusBar.healthText:SetText("") end end end -- Sync the standalone bg frame to match GameTooltip's position/size local function TT_SyncBGFrame() local bf = GameTooltip._nanamiBGFrame if not bf then return end local w = GameTooltip:GetWidth() local h = GameTooltip:GetHeight() if w and h and w > 0 and h > 0 then bf:SetWidth(w) bf:SetHeight(h) bf:ClearAllPoints() bf:SetPoint("TOPLEFT", GameTooltip, "TOPLEFT", 0, 0) bf:SetFrameLevel(math.max(1, GameTooltip:GetFrameLevel()) - 1) bf:Show() end end -- OnShow: apply backdrop, sync bg frame, reset formatting flag local orig_OnShow = GameTooltip:GetScript("OnShow") local ttIsMapMarker = false GameTooltip:SetScript("OnShow", function() if orig_OnShow then orig_OnShow() end linesFormatted = false ttIsMapMarker = TT_IsMapMarkerTooltip() TT_ApplyBackdrop(this) TT_SyncBGFrame() if not ttIsMapMarker then TT_ShowBar(UnitExists("mouseover")) end end) -- OnUpdate: line formatting (once) + cursor tracking (every frame) local orig_OnUpdate = GameTooltip:GetScript("OnUpdate") local ttFormatThrottle = 0 local ttHideGrace = 0 GameTooltip:SetScript("OnUpdate", function() if orig_OnUpdate then orig_OnUpdate() end local isCursorMode = ttUsesDefaultAnchor and SFramesDB and SFramesDB.tooltipMode == "CURSOR" and not ttIsMapMarker if isCursorMode then local hasUnit = UnitExists("mouseover") if ttHadUnit and not hasUnit then ttHideGrace = ttHideGrace + arg1 if ttHideGrace < 0.2 then TT_ShowBar(false) return end ttHideGrace = 0 TT_ShowBar(false) if GameTooltip._nanamiBGFrame then GameTooltip._nanamiBGFrame:Hide() end this:Hide() return else ttHideGrace = 0 end if not hasUnit then TT_ShowBar(false) end local x, y = GetCursorPosition() local uiScale = UIParent:GetEffectiveScale() local ttScale = this:GetScale() or 1 if uiScale and uiScale > 0 and ttScale > 0 then local effScale = uiScale * ttScale local tx = (x / effScale) + 16 local ty = (y / effScale) - 16 this:ClearAllPoints() this:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", tx, ty) TT_SyncBGFrame() end end ttFormatThrottle = ttFormatThrottle + arg1 if ttFormatThrottle < 0.05 then return end ttFormatThrottle = 0 if not linesFormatted then linesFormatted = true if not ttIsMapMarker and UnitExists("mouseover") then SFrames.FloatingTooltip:FormatLines(this) end TT_SyncBGFrame() end end) -- OnHide: hide bg frame, health bar, reset flags and owner tracking local orig_OnHide = GameTooltip:GetScript("OnHide") GameTooltip:SetScript("OnHide", function() linesFormatted = false ttOwner = nil ttHadUnit = false ttHideGrace = 0 TT_ShowBar(false) if GameTooltip._nanamiBGFrame then GameTooltip._nanamiBGFrame:Hide() end if orig_OnHide then orig_OnHide() end end) -- Tooltip Positioning logic if not SFrames.FloatingTooltip.anchor then local anchor = CreateFrame("Button", "SFramesTooltipAnchor", UIParent) anchor:SetWidth(180) anchor:SetHeight(60) anchor:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -20, 100) anchor:SetFrameStrata("HIGH") anchor:EnableMouse(true) anchor:SetMovable(true) anchor:RegisterForDrag("LeftButton") anchor:SetScript("OnDragStart", function() this:StartMoving() end) anchor:SetScript("OnDragStop", function() this:StopMovingOrSizing() if SFramesDB then local _, _, _, x, y = this:GetPoint() SFramesDB.tooltipX = x SFramesDB.tooltipY = y end end) anchor:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8", edgeFile = "Interface\\Buttons\\WHITE8x8", edgeSize = 1, insets = {left=1, right=1, top=1, bottom=1} }) anchor:SetBackdropColor(0.08, 0.08, 0.08, 0.95) anchor:SetBackdropBorderColor(0.4, 0.8, 0.4, 1) local font = (SFrames and SFrames.GetFont and SFrames:GetFont()) or "Fonts\\ARIALN.TTF" anchor.title = anchor:CreateFontString(nil, "OVERLAY") anchor.title:SetFont(font, 13, "OUTLINE") anchor.title:SetPoint("TOPLEFT", anchor, "TOPLEFT", 8, -8) anchor.title:SetText("Tooltip 模拟位置") anchor.title:SetTextColor(1, 1, 1) anchor.desc = anchor:CreateFontString(nil, "OVERLAY") anchor.desc:SetFont(font, 11, "OUTLINE") anchor.desc:SetPoint("TOPLEFT", anchor.title, "BOTTOMLEFT", 0, -4) anchor.desc:SetText("拖拽我以调整[自定义]锚点。") anchor.desc:SetTextColor(1, 0.82, 0) anchor.desc:SetJustifyH("LEFT") anchor:Hide() SFrames.FloatingTooltip.anchor = anchor end if not SFrames.FloatingTooltip.hookedAnchor then SFrames.FloatingTooltip.hookedAnchor = true local orig_GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor function GameTooltip_SetDefaultAnchor(tooltip, parent) if SFramesDB and SFramesDB.tooltipMode == "CURSOR" then tooltip:SetOwner(parent, "ANCHOR_NONE") elseif SFramesDB and SFramesDB.tooltipMode == "CUSTOM" then tooltip:SetOwner(parent, "ANCHOR_NONE") tooltip:ClearAllPoints() tooltip:SetPoint("BOTTOMRIGHT", "SFramesTooltipAnchor", "BOTTOMRIGHT", 0, 0) else orig_GameTooltip_SetDefaultAnchor(tooltip, parent) end ttUsesDefaultAnchor = true end end SFrames.FloatingTooltip:ApplyConfig() SFrames.FloatingTooltip:ApplyScale() -- WorldMapTooltip: raw textures on a child frame (SetBackdrop is unreliable) if WorldMapTooltip and not WorldMapTooltip._nanamiBG then WorldMapTooltip._nanamiBG = true local wmtBgFrame = CreateFrame("Frame", nil, WorldMapTooltip) wmtBgFrame:SetAllPoints(WorldMapTooltip) wmtBgFrame:SetFrameLevel(math.max(0, WorldMapTooltip:GetFrameLevel())) local bg = wmtBgFrame:CreateTexture(nil, "BACKGROUND") bg:SetTexture("Interface\\Buttons\\WHITE8X8") bg:SetVertexColor(0.05, 0.05, 0.05, 1) bg:SetAllPoints(wmtBgFrame) local function WMTEdge(p1, r1, p2, r2, w, h) local t = wmtBgFrame:CreateTexture(nil, "BORDER") t:SetTexture("Interface\\Buttons\\WHITE8X8") t:SetVertexColor(0.20, 0.20, 0.20, 1) t:SetPoint(p1, WorldMapTooltip, r1) t:SetPoint(p2, WorldMapTooltip, r2) if w then t:SetWidth(w) end if h then t:SetHeight(h) end end WMTEdge("TOPLEFT","TOPLEFT","TOPRIGHT","TOPRIGHT", nil, 1) WMTEdge("BOTTOMLEFT","BOTTOMLEFT","BOTTOMRIGHT","BOTTOMRIGHT", nil, 1) WMTEdge("TOPLEFT","TOPLEFT","BOTTOMLEFT","BOTTOMLEFT", 1, nil) WMTEdge("TOPRIGHT","TOPRIGHT","BOTTOMRIGHT","BOTTOMRIGHT", 1, nil) end if SFrames.ItemCompare and SFrames.ItemCompare.HookTooltips then SFrames.ItemCompare:HookTooltips() end if SFrames.Movers and SFrames.Movers.RegisterMover and self.anchor then SFrames.Movers:RegisterMover("Tooltip", self.anchor, "提示框", "BOTTOMRIGHT", "UIParent", "BOTTOMRIGHT", -20, 100) end end function SFrames.FloatingTooltip:ApplyConfig() if not self.anchor then return end local pos = SFramesDB and SFramesDB.Positions and SFramesDB.Positions["Tooltip"] if pos and pos.point and pos.relativePoint then self.anchor:ClearAllPoints() self.anchor:SetPoint(pos.point, UIParent, pos.relativePoint, pos.xOfs or 0, pos.yOfs or 0) elseif SFramesDB and SFramesDB.tooltipX and SFramesDB.tooltipY then self.anchor:ClearAllPoints() self.anchor:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", SFramesDB.tooltipX, SFramesDB.tooltipY) end end function SFrames.FloatingTooltip:ApplyScale() local scale = SFramesDB and SFramesDB.tooltipScale or 1.0 if scale < 0.5 then scale = 0.5 end if scale > 2.0 then scale = 2.0 end GameTooltip:SetScale(scale) if GameTooltip._nanamiBGFrame then GameTooltip._nanamiBGFrame:SetScale(scale) end end function SFrames.FloatingTooltip:ToggleAnchor(show) if not self.anchor then return end if show then self.anchor:Show() else self.anchor:Hide() end end function SFrames.FloatingTooltip:FormatLines(tooltip) local unit = "mouseover" if not UnitExists(unit) then return end local nameLine = GameTooltipTextLeft1 if not nameLine then return end local nameText = nameLine:GetText() if nameText and string.find(nameText, "\n") then nameText = string.gsub(nameText, "\n", " ") nameLine:SetText(nameText) end -------------------------------------------------------------------------- -- Player units -------------------------------------------------------------------------- if UnitIsPlayer(unit) then local classToken = TT_GetClassToken(unit) -- Class-color the name if classToken then local r, g, b = TT_GetClassColor(classToken) if r then nameLine:SetTextColor(r, g, b) if GameTooltipStatusBar and GameTooltipStatusBar._origSetColor then GameTooltipStatusBar._origSetColor(GameTooltipStatusBar, r, g, b) end end end -- Fetch guild info for this player local ttGuild, ttRankStr, ttRankIdx if GetGuildInfo then ttGuild, ttRankStr, ttRankIdx = GetGuildInfo(unit) end -- Iterate existing lines: enhance guild line + color level local numLines = tooltip:NumLines() for i = 2, numLines do local left = getglobal("GameTooltipTextLeft" .. i) if left then local txt = left:GetText() if txt then -- Guild line — append rank if string.find(txt, "^<.*>$") then if ttRankStr and ttRankStr ~= "" then left:SetText(txt .. " - " .. ttRankStr) end left:SetTextColor(0.30, 0.90, 0.30) end -- Level line local _, _, lvlStr = string.find(txt, "^Level (%d+)") if not lvlStr then _, _, lvlStr = string.find(txt, "^等级 (%d+)") end local lvlNum = tonumber(lvlStr) if lvlNum then local lr, lg, lb = TT_DifficultyColor(lvlNum) left:SetTextColor(lr, lg, lb) end end end end ----------------------------------------------------------------------- -- Extra info lines (appended below existing content) ----------------------------------------------------------------------- -- Guild info (if player is in a guild but Blizzard didn't show it) if ttGuild and ttRankStr then local foundGuild = false for i = 2, numLines do local left = getglobal("GameTooltipTextLeft" .. i) if left then local txt = left:GetText() or "" if string.find(txt, "^<.*>") then foundGuild = true break end end end if not foundGuild then tooltip:AddLine("<" .. ttGuild .. "> - " .. ttRankStr, 0.30, 0.90, 0.30) end end -- PvP Rank (text only; |T...|t inline textures not supported in 1.12) if UnitPVPRank and GetPVPRankInfo then local rank = UnitPVPRank(unit) if rank and rank > 0 then local rankName = GetPVPRankInfo(rank) if rankName and rankName ~= "" then tooltip:AddLine("军衔: " .. rankName, 1, 0.85, 0.35) end end end -- PvP flag + faction if UnitIsPVP and UnitIsPVP(unit) then local fTag = UnitFactionGroup and UnitFactionGroup(unit) if fTag then local pvpHex = (fTag == "Horde") and "|cffff4444" or "|cff44bbff" tooltip:AddLine(pvpHex .. "PvP " .. fTag .. "|r") end end -- Turtle WoW challenge modes (defensive checks) if IsHardcore and IsHardcore(unit) then tooltip:AddLine("|cffff3333[Hardcore]|r") end if C_TurtleWoW then if C_TurtleWoW.IsHardcore and C_TurtleWoW.IsHardcore(unit) then tooltip:AddLine("|cffff3333[Hardcore]|r") end if C_TurtleWoW.IsSurvivalist and C_TurtleWoW.IsSurvivalist(unit) then tooltip:AddLine("|cff33ff33[Survivalist]|r") end if C_TurtleWoW.IsIronMan and C_TurtleWoW.IsIronMan(unit) then tooltip:AddLine("|cffaaaaaa[Iron Man]|r") end end if UnitIsTrivial and UnitIsTrivial(unit) then tooltip:AddLine("|cff888888[休闲模式]|r") end -------------------------------------------------------------------------- -- NPC / Creature units: color health bar by reaction -------------------------------------------------------------------------- else local reaction = UnitReaction(unit, "player") if reaction then local color = UnitReactionColor and UnitReactionColor[reaction] if color and GameTooltipStatusBar and GameTooltipStatusBar._origSetColor then GameTooltipStatusBar._origSetColor(GameTooltipStatusBar, color.r, color.g, color.b) end end local classificationText = TT_GetClassificationText(unit) if classificationText then local numLines = tooltip:NumLines() local appended = false for i = 2, numLines do local left = getglobal("GameTooltipTextLeft" .. i) if left then local txt = left:GetText() if txt and (string.find(txt, "^Level ") or string.find(txt, "^等级 ")) then if not string.find(txt, "%[") then left:SetText(txt .. " " .. classificationText) end appended = true break end end end if not appended then tooltip:AddLine(classificationText) end end end -------------------------------------------------------------------------- -- Armor & Resistances for non-player units -------------------------------------------------------------------------- if not UnitIsPlayer(unit) and UnitExists(unit) then local armor = UnitResistance(unit, 0) or 0 if armor > 0 then tooltip:AddLine(" ") tooltip:AddLine(string.format("护甲: %d", armor), 0.78, 0.61, 0.43) end local resInfo = { { 2, "火焰", 1.0, 0.3, 0.3 }, { 3, "自然", 0.3, 1.0, 0.3 }, { 4, "冰霜", 0.3, 0.3, 1.0 }, { 5, "暗影", 0.6, 0.2, 0.9 }, { 6, "奥术", 1.0, 1.0, 1.0 }, { 1, "神圣", 1.0, 0.9, 0.5 }, } local hasRes = false local resLine = "" for i = 1, table.getn(resInfo) do local ri = resInfo[i] local val = UnitResistance(unit, ri[1]) or 0 if val > 0 then if hasRes then resLine = resLine .. " " end resLine = resLine .. ri[2] .. ":" .. val hasRes = true end end if hasRes then if armor <= 0 then tooltip:AddLine(" ") end tooltip:AddLine("抗性: " .. resLine, 0.7, 0.7, 0.75) end end -------------------------------------------------------------------------- -- Target of mouseover (all units) -------------------------------------------------------------------------- local tgtUnit = unit .. "target" if UnitExists(tgtUnit) then local tgtName = UnitName(tgtUnit) if tgtName then local line if UnitIsUnit(tgtUnit, "player") then local _At = SFrames.ActiveTheme local _accentHex = "ff6699" if _At and _At.accentHex then _accentHex = _At.accentHex end line = "|cffffd700目标: |r|cff" .. _accentHex .. ">> 你 <<|r" elseif UnitIsPlayer(tgtUnit) then local hex = TT_ClassHex(TT_GetClassToken(tgtUnit)) line = "|cffffd700目标: |r" .. hex .. tgtName .. "|r" else line = "|cffffd700目标: |r|cffffffff" .. tgtName .. "|r" end tooltip:AddLine(line) end end tooltip:Show() end -------------------------------------------------------------------------------- -- Item Compare: show stat differences vs currently equipped item -------------------------------------------------------------------------------- SFrames.ItemCompare = {} local IC = SFrames.ItemCompare local IC_STAT_ORDER = { "STR","AGI","STA","INT","SPI", "CRIT","TOHIT","RANGEDCRIT", "SPELLCRIT","SPELLTOHIT", "ATTACKPOWER","RANGEDATTACKPOWER", "DMG","HEAL", "DEFENSE","DODGE","PARRY","BLOCK","BLOCKVALUE", "ARMOR", "HEALTHREG","MANAREG", "HEALTH","MANA", } local IC_STAT_NAMES = { STR="力量", AGI="敏捷", STA="耐力", INT="智力", SPI="精神", CRIT="暴击%", TOHIT="命中%", RANGEDCRIT="远程暴击%", SPELLCRIT="法术暴击%", SPELLTOHIT="法术命中%", ATTACKPOWER="攻强", RANGEDATTACKPOWER="远程攻强", DMG="法伤", HEAL="治疗", DEFENSE="防御", DODGE="闪避%", PARRY="招架%", BLOCK="格挡%", BLOCKVALUE="格挡值", ARMOR="护甲", HEALTHREG="生命/5秒", MANAREG="法力/5秒", HEALTH="生命值", MANA="法力值", } local IC_EQUIP_LOC_TO_SLOT = { INVTYPE_HEAD = 1, INVTYPE_NECK = 2, INVTYPE_SHOULDER = 3, INVTYPE_BODY = 4, INVTYPE_CHEST = 5, INVTYPE_ROBE = 5, INVTYPE_WAIST = 6, INVTYPE_LEGS = 7, INVTYPE_FEET = 8, INVTYPE_WRIST = 9, INVTYPE_HAND = 10, INVTYPE_FINGER = 11, INVTYPE_TRINKET = 13, INVTYPE_CLOAK = 15, INVTYPE_WEAPON = 16, INVTYPE_2HWEAPON = 16, INVTYPE_WEAPONMAINHAND = 16, INVTYPE_SHIELD = 17, INVTYPE_WEAPONOFFHAND = 17, INVTYPE_HOLDABLE = 17, INVTYPE_RANGED = 18, INVTYPE_RANGEDRIGHT = 18, INVTYPE_THROWN = 18, INVTYPE_RELIC = 18, INVTYPE_TABARD = 19, } local function IC_GetLib() if AceLibrary and AceLibrary.HasInstance and AceLibrary:HasInstance("ItemBonusLib-1.0") then return AceLibrary("ItemBonusLib-1.0") end return nil end local function IC_GetEquipSlot(link) if not link then return nil end local _, _, _, _, _, _, _, equipLoc = GetItemInfo(link) if not equipLoc or equipLoc == "" then return nil end return IC_EQUIP_LOC_TO_SLOT[equipLoc] end local function IC_ScanBonuses(lib, link) if not lib or not link then return nil end local ok, result = pcall(function() return lib:ScanItem(link, true) end) if ok and result then return result end return nil end local function IC_AppendCompare(tooltip, newBonuses, oldBonuses) if not newBonuses then return end local hasAny = false local lines = {} for _, key in ipairs(IC_STAT_ORDER) do local nv = newBonuses[key] or 0 local ov = (oldBonuses and oldBonuses[key]) or 0 local diff = nv - ov if diff ~= 0 then local name = IC_STAT_NAMES[key] or key local text if diff > 0 then text = "|cff00ff00+" .. diff .. " " .. name .. "|r" else text = "|cffff4444" .. diff .. " " .. name .. "|r" end table.insert(lines, text) hasAny = true end end if hasAny then tooltip:AddLine(" ") tooltip:AddLine("与当前装备对比:", 0.6, 0.8, 1) for _, l in ipairs(lines) do tooltip:AddLine(l) end tooltip:Show() end end local function IC_GetItemLevel(link) if not link or not LibItem_Level then return nil end local _, _, itemId = string.find(link, "item:(%d+)") if itemId then return LibItem_Level[tonumber(itemId)] end return nil end local function IC_AppendItemLevel(tooltip, link) if SFramesDB and SFramesDB.showItemLevel == false then return end local ilvl = IC_GetItemLevel(link) if ilvl and ilvl > 0 then tooltip:AddLine("物品等级: " .. ilvl, 1, 0.82, 0) end end -------------------------------------------------------------------------------- -- Sell Price Infrastructure (GetItemInfo API + runtime cache) -------------------------------------------------------------------------------- local function IC_GetItemIdFromLink(link) if not link then return nil end local _, _, id = string.find(link, "item:(%d+)") if id then return tonumber(id) end return nil end local function IC_CacheSellPrice(itemId, copper) if not itemId or not copper or copper <= 0 then return end if not SFramesGlobalDB then SFramesGlobalDB = {} end if not SFramesGlobalDB.sellPriceCache then SFramesGlobalDB.sellPriceCache = {} end SFramesGlobalDB.sellPriceCache[itemId] = copper end local function IC_GetSellPrice(itemId) if not itemId then return nil end -- Runtime cache (persisted in SavedVariables) if SFramesGlobalDB and SFramesGlobalDB.sellPriceCache then local price = SFramesGlobalDB.sellPriceCache[itemId] if price and price > 0 then return price end end -- Fallback: aux addon if aux and aux.account_data and aux.account_data.merchant_sell then local price = aux.account_data.merchant_sell[itemId] if price and price > 0 then return price end end -- Fallback: ShaguTweaks if ShaguTweaks and ShaguTweaks.SellValueDB then local price = ShaguTweaks.SellValueDB[itemId] if price and price > 0 then return price end end return nil end --------------------------------------------------------------------------- -- GetItemInfo API: query sell price and learn/cache it -- Returns: sellPrice (copper) or nil --------------------------------------------------------------------------- local function IC_QueryAndLearnPrice(link) if not link then return nil end local itemId = IC_GetItemIdFromLink(link) -- Check cache first local cached = IC_GetSellPrice(itemId) if cached then return cached end -- Query via GetItemInfo (11th return = sellPrice) local name, _, quality, ilvl, minLvl, itemType, subType, stackCount, equipLoc, texture, sellPrice = GetItemInfo(link) if name then DEFAULT_CHAT_FRAME:AddMessage( "|cff88ccff[Nanami-SellPrice]|r GetItemInfo(" .. (itemId or "?") .. ") = " .. (name or "nil") .. ", sellPrice=" .. tostring(sellPrice or "nil")) end if sellPrice and type(sellPrice) == "number" and sellPrice > 0 then if itemId then IC_CacheSellPrice(itemId, sellPrice) end return sellPrice end return nil end local function IC_ExtractLinkFromTooltipName(tooltip) if not tooltip then return nil end local left1 = _G[tooltip:GetName() .. "TextLeft1"] if not left1 then return nil end local name = left1:GetText() if not name or name == "" then return nil end if GetItemLinkByName then local link = GetItemLinkByName(name) if link then return link end end if ShaguTweaks and ShaguTweaks.GetItemLinkByName then local link = ShaguTweaks.GetItemLinkByName(name) if link then return link end end return nil end local function IC_AddSellPrice(tooltip, link, count) if not link then return end if tooltip._nanamiSellPriceAdded then return end if MerchantFrame and MerchantFrame:IsShown() then return end local price = IC_QueryAndLearnPrice(link) if price and price > 0 then count = count or 1 if count < 1 then count = 1 end tooltip._nanamiSellPriceAdded = true SetTooltipMoney(tooltip, price * count) tooltip:Show() end end -------------------------------------------------------------------------------- local function IC_AppendItemId(tooltip, link) if SFramesDB and SFramesDB.showTooltipIDs == false then return end local itemId = IC_GetItemIdFromLink(link) if itemId then tooltip:AddLine("物品ID: " .. itemId, 0.55, 0.55, 0.70) end end local function IC_EnhanceTooltip(tooltip, link, count, skipSellPrice) if not link then return end IC_AppendItemLevel(tooltip, link) IC_AppendItemId(tooltip, link) if not SFramesDB or SFramesDB.itemCompare ~= false then local lib = IC_GetLib() if lib then local eslot = IC_GetEquipSlot(link) if eslot then local newB = IC_ScanBonuses(lib, link) local eqLink = GetInventoryItemLink("player", eslot) local oldB = IC_ScanBonuses(lib, eqLink) IC_AppendCompare(tooltip, newB, oldB) end end end if SFrames.GearScore and SFrames.GearScore.AddScoreToTooltip then pcall(function() SFrames.GearScore:AddScoreToTooltip(tooltip, link) end) end if not skipSellPrice then IC_AddSellPrice(tooltip, link, count) end tooltip:Show() end function IC:HookTooltips() if self.hooked then return end self.hooked = true --------------------------------------------------------------------------- -- OnHide cleanup: reset sell-price tracking flag --------------------------------------------------------------------------- local orig_OnHide_IC = GameTooltip:GetScript("OnHide") GameTooltip:SetScript("OnHide", function() this._nanamiSellPriceAdded = nil if orig_OnHide_IC then orig_OnHide_IC() end end) --------------------------------------------------------------------------- -- Passive sell-price caching: intercept SetTooltipMoney calls while -- processing a bag item at a merchant so we learn the unit price. --------------------------------------------------------------------------- local IC_PendingBagLink = nil local IC_PendingBagCount = nil local orig_SetTooltipMoney = SetTooltipMoney SetTooltipMoney = function(frame, money, a1, a2, a3) if orig_SetTooltipMoney then orig_SetTooltipMoney(frame, money, a1, a2, a3) end if IC_PendingBagLink and money and money > 0 then if frame == GameTooltip then local itemId = IC_GetItemIdFromLink(IC_PendingBagLink) local count = IC_PendingBagCount or 1 if count < 1 then count = 1 end if itemId then IC_CacheSellPrice(itemId, math.floor(money / count)) end end end end --------------------------------------------------------------------------- -- MERCHANT_SHOW: proactively scan all bag items to learn sell prices -- via GetItemInfo API and cache them. --------------------------------------------------------------------------- local scanFrame = CreateFrame("Frame") scanFrame:RegisterEvent("MERCHANT_SHOW") scanFrame:SetScript("OnEvent", function() for bag = 0, 4 do local numSlots = GetContainerNumSlots(bag) or 0 for slot = 1, numSlots do local link = GetContainerItemLink(bag, slot) if link then IC_QueryAndLearnPrice(link) end end end end) --------------------------------------------------------------------------- -- GameTooltip.SetBagItem --------------------------------------------------------------------------- local orig_SetBagItem = GameTooltip.SetBagItem GameTooltip.SetBagItem = function(self, bag, slot) self._nanamiSellPriceAdded = nil local link = GetContainerItemLink(bag, slot) local _, cnt = GetContainerItemInfo(bag, slot) IC_PendingBagLink = link IC_PendingBagCount = cnt or 1 local hasItem, hasCooldown, repairCost = orig_SetBagItem(self, bag, slot) IC_PendingBagLink = nil IC_PendingBagCount = nil local moneyAlreadyShown = self.hasMoney pcall(function() if link then IC_EnhanceTooltip(GameTooltip, link, cnt, moneyAlreadyShown) end end) return hasItem, hasCooldown, repairCost end --------------------------------------------------------------------------- -- GameTooltip.SetInventoryItem --------------------------------------------------------------------------- local orig_SetInvItem = GameTooltip.SetInventoryItem GameTooltip.SetInventoryItem = function(self, unit, slotId) self._nanamiSellPriceAdded = nil local hasItem, hasCooldown, repairCost = orig_SetInvItem(self, unit, slotId) local moneyAlreadyShown = self.hasMoney pcall(function() if unit == "player" and slotId then local link = GetInventoryItemLink("player", slotId) if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end end) return hasItem, hasCooldown, repairCost end --------------------------------------------------------------------------- -- GameTooltip.SetMerchantItem --------------------------------------------------------------------------- local orig_SetMerchantItem = GameTooltip.SetMerchantItem if orig_SetMerchantItem then GameTooltip.SetMerchantItem = function(self, idx) self._nanamiSellPriceAdded = nil orig_SetMerchantItem(self, idx) pcall(function() local link = GetMerchantItemLink and GetMerchantItemLink(idx) if link then IC_EnhanceTooltip(GameTooltip, link) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetQuestItem — with fallback link extraction --------------------------------------------------------------------------- local orig_SetQuestItem = GameTooltip.SetQuestItem if orig_SetQuestItem then GameTooltip.SetQuestItem = function(self, qtype, idx) self._nanamiSellPriceAdded = nil orig_SetQuestItem(self, qtype, idx) local moneyAlreadyShown = self.hasMoney pcall(function() local link if GetQuestItemLink then link = GetQuestItemLink(qtype, idx) end if not link then link = IC_ExtractLinkFromTooltipName(GameTooltip) end if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetQuestLogItem — with fallback link extraction --------------------------------------------------------------------------- local orig_SetQuestLogItem = GameTooltip.SetQuestLogItem if orig_SetQuestLogItem then GameTooltip.SetQuestLogItem = function(self, itype, idx) self._nanamiSellPriceAdded = nil orig_SetQuestLogItem(self, itype, idx) local moneyAlreadyShown = self.hasMoney pcall(function() local link if GetQuestLogItemLink then link = GetQuestLogItemLink(itype, idx) end if not link then link = IC_ExtractLinkFromTooltipName(GameTooltip) end if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetLootItem --------------------------------------------------------------------------- local orig_SetLootItem = GameTooltip.SetLootItem if orig_SetLootItem then GameTooltip.SetLootItem = function(self, idx) self._nanamiSellPriceAdded = nil orig_SetLootItem(self, idx) local moneyAlreadyShown = self.hasMoney pcall(function() local link = GetLootSlotLink and GetLootSlotLink(idx) if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetLootRollItem --------------------------------------------------------------------------- local orig_SetLootRollItem = GameTooltip.SetLootRollItem if orig_SetLootRollItem then GameTooltip.SetLootRollItem = function(self, rollId) self._nanamiSellPriceAdded = nil orig_SetLootRollItem(self, rollId) local moneyAlreadyShown = self.hasMoney pcall(function() local link = GetLootRollItemLink and GetLootRollItemLink(rollId) if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetCraftItem --------------------------------------------------------------------------- local orig_SetCraftItem = GameTooltip.SetCraftItem if orig_SetCraftItem then GameTooltip.SetCraftItem = function(self, skill, slot) self._nanamiSellPriceAdded = nil orig_SetCraftItem(self, skill, slot) local moneyAlreadyShown = self.hasMoney pcall(function() local link = GetCraftReagentItemLink and GetCraftReagentItemLink(skill, slot) if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetTradeSkillItem --------------------------------------------------------------------------- local orig_SetTradeSkillItem = GameTooltip.SetTradeSkillItem if orig_SetTradeSkillItem then GameTooltip.SetTradeSkillItem = function(self, skillIndex, reagentIndex) self._nanamiSellPriceAdded = nil orig_SetTradeSkillItem(self, skillIndex, reagentIndex) local moneyAlreadyShown = self.hasMoney pcall(function() local link if reagentIndex then if GetTradeSkillReagentItemLink then link = GetTradeSkillReagentItemLink(skillIndex, reagentIndex) end else if GetTradeSkillItemLink then link = GetTradeSkillItemLink(skillIndex) end end if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetAuctionItem --------------------------------------------------------------------------- local orig_SetAuctionItem = GameTooltip.SetAuctionItem if orig_SetAuctionItem then GameTooltip.SetAuctionItem = function(self, atype, idx) self._nanamiSellPriceAdded = nil orig_SetAuctionItem(self, atype, idx) local moneyAlreadyShown = self.hasMoney pcall(function() local _, _, cnt = GetAuctionItemInfo and GetAuctionItemInfo(atype, idx) local link = GetAuctionItemLink and GetAuctionItemLink(atype, idx) if link then IC_EnhanceTooltip(GameTooltip, link, cnt, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetTradePlayerItem --------------------------------------------------------------------------- local orig_SetTradePlayerItem = GameTooltip.SetTradePlayerItem if orig_SetTradePlayerItem then GameTooltip.SetTradePlayerItem = function(self, idx) self._nanamiSellPriceAdded = nil orig_SetTradePlayerItem(self, idx) local moneyAlreadyShown = self.hasMoney pcall(function() local link = GetTradePlayerItemLink and GetTradePlayerItemLink(idx) if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetTradeTargetItem --------------------------------------------------------------------------- local orig_SetTradeTargetItem = GameTooltip.SetTradeTargetItem if orig_SetTradeTargetItem then GameTooltip.SetTradeTargetItem = function(self, idx) self._nanamiSellPriceAdded = nil orig_SetTradeTargetItem(self, idx) local moneyAlreadyShown = self.hasMoney pcall(function() local link = GetTradeTargetItemLink and GetTradeTargetItemLink(idx) if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- GameTooltip.SetInboxItem --------------------------------------------------------------------------- local orig_SetInboxItem = GameTooltip.SetInboxItem if orig_SetInboxItem then GameTooltip.SetInboxItem = function(self, mailID, attachIdx) self._nanamiSellPriceAdded = nil orig_SetInboxItem(self, mailID, attachIdx) local moneyAlreadyShown = self.hasMoney pcall(function() local link = IC_ExtractLinkFromTooltipName(GameTooltip) if link then IC_EnhanceTooltip(GameTooltip, link, nil, moneyAlreadyShown) end end) end end --------------------------------------------------------------------------- -- SetItemRef (chat item links) --------------------------------------------------------------------------- if ItemRefTooltip and ItemRefTooltip.SetHyperlink then local orig_ItemRef_SetHyperlink = ItemRefTooltip.SetHyperlink ItemRefTooltip.SetHyperlink = function(self, link) self._nanamiSellPriceAdded = nil self._gsScoreAdded = nil local r1, r2, r3, r4 = orig_ItemRef_SetHyperlink(self, link) if IsAltKeyDown() or IsShiftKeyDown() or IsControlKeyDown() then return r1, r2, r3, r4 end pcall(function() local _, _, itemStr = string.find(link or "", "(item:[%-?%d:]+)") if itemStr then local moneyAlreadyShown = self.hasMoney IC_EnhanceTooltip(self, itemStr, nil, moneyAlreadyShown) end end) return r1, r2, r3, r4 end end --------------------------------------------------------------------------- -- Spell ID display --------------------------------------------------------------------------- local spellIdCache = {} local function TT_AddSpellIdLine(tooltip, sid) if not tooltip or not sid then return end if SFramesDB and SFramesDB.showTooltipIDs == false then return end tooltip:AddLine("法术ID: " .. sid, 0.55, 0.55, 0.70) tooltip:Show() end local function TT_ExtractSpellId(link) if not link then return nil end local _, _, id = string.find(link, "spell:(%d+)") return id end local function TT_CleanName(text) if not text then return nil end text = string.gsub(text, "|c%x%x%x%x%x%x%x%x", "") text = string.gsub(text, "|r", "") text = string.gsub(text, "|H.-|h", "") text = string.gsub(text, "|h", "") text = string.gsub(text, "^%s+", "") text = string.gsub(text, "%s+$", "") return text end local function TT_BuildSpellCache() spellIdCache = {} local function ScanBook(bookType, count) if not GetSpellLink or count == 0 then return end for i = 1, count do local name, rank = GetSpellName(i, bookType) if name then local link = GetSpellLink(i, bookType) local sid = TT_ExtractSpellId(link) if sid then spellIdCache[name] = sid local clean = TT_CleanName(name) if clean and clean ~= name then spellIdCache[clean] = sid end end end end end local bookType = BOOKTYPE_SPELL or "spell" local numTabs = GetNumSpellTabs and GetNumSpellTabs() or 0 local total = 0 for t = 1, numTabs do local _, _, _, count = GetSpellTabInfo(t) total = total + (count or 0) end ScanBook(bookType, total) if HasPetSpells then local numPet = HasPetSpells() or 0 if numPet > 0 then ScanBook(BOOKTYPE_PET or "pet", numPet) end end end local spellCacheFrame = CreateFrame("Frame") spellCacheFrame:RegisterEvent("SPELLS_CHANGED") spellCacheFrame:RegisterEvent("PLAYER_ENTERING_WORLD") spellCacheFrame:RegisterEvent("LEARNED_SPELL_IN_TAB") spellCacheFrame:RegisterEvent("PET_BAR_UPDATE") spellCacheFrame:SetScript("OnEvent", function() TT_BuildSpellCache() end) local function TT_LookupSpellId(spellBookIdx, bookType) if GetSpellLink then local link = GetSpellLink(spellBookIdx, bookType) local sid = TT_ExtractSpellId(link) if sid then return sid end end local name = GetSpellName(spellBookIdx, bookType) if name then if spellIdCache[name] then return spellIdCache[name] end local clean = TT_CleanName(name) if clean and spellIdCache[clean] then return spellIdCache[clean] end end return nil end local function TT_LookupByTooltipName(tooltip) local ttName = tooltip and tooltip:GetName() if not ttName then return nil end local left1 = _G[ttName .. "TextLeft1"] if not left1 then return nil end local raw = left1:GetText() if not raw or raw == "" then return nil end if spellIdCache[raw] then return spellIdCache[raw] end local clean = TT_CleanName(raw) if clean and spellIdCache[clean] then return spellIdCache[clean] end local noRank = string.gsub(clean or raw, "%s*%(.-%)%s*$", "") if noRank ~= (clean or raw) and spellIdCache[noRank] then return spellIdCache[noRank] end return nil end local orig_SetSpell = GameTooltip.SetSpell if orig_SetSpell then GameTooltip.SetSpell = function(self, id, bookType) orig_SetSpell(self, id, bookType) pcall(function() local sid = TT_LookupSpellId(id, bookType) if sid then TT_AddSpellIdLine(self, sid) end end) end end local orig_SetUnitBuff = GameTooltip.SetUnitBuff if orig_SetUnitBuff then GameTooltip.SetUnitBuff = function(self, unit, idx, filter) orig_SetUnitBuff(self, unit, idx, filter) pcall(function() local sid = TT_LookupByTooltipName(self) if sid then TT_AddSpellIdLine(self, sid) end end) end end local orig_SetUnitDebuff = GameTooltip.SetUnitDebuff if orig_SetUnitDebuff then GameTooltip.SetUnitDebuff = function(self, unit, idx, filter) orig_SetUnitDebuff(self, unit, idx, filter) pcall(function() local sid = TT_LookupByTooltipName(self) if sid then TT_AddSpellIdLine(self, sid) end end) end end local orig_SetCraftSpell = GameTooltip.SetCraftSpell if orig_SetCraftSpell then GameTooltip.SetCraftSpell = function(self, idx) orig_SetCraftSpell(self, idx) pcall(function() local sid = TT_LookupByTooltipName(self) if sid then TT_AddSpellIdLine(self, sid) end end) end end local orig_SetTrackingSpell = GameTooltip.SetTrackingSpell if orig_SetTrackingSpell then GameTooltip.SetTrackingSpell = function(self) orig_SetTrackingSpell(self) pcall(function() local sid = TT_LookupByTooltipName(self) if sid then TT_AddSpellIdLine(self, sid) end end) end end end