1503 lines
57 KiB
Lua
1503 lines
57 KiB
Lua
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 <GuildName> — 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
|