Files
Nanami-UI/GearScore.lua
2026-03-16 13:48:46 +08:00

1134 lines
46 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--------------------------------------------------------------------------------
-- Nanami-UI: GearScore (GearScore.lua)
-- 基于乌龟服(Turtle WoW)属性收益理论的装备评分系统
-- 评分 1-10: 衡量该装备对当前职业各天赋的适配度
-- 考虑: 属性权重 × 装备类型兼容性 × 等级适配
--------------------------------------------------------------------------------
SFrames.GearScore = {}
local GS = SFrames.GearScore
--------------------------------------------------------------------------------
-- Item budget cost per unit of each stat (for normalization)
-- Higher cost = stat is "rarer/more expensive" per item budget point
--------------------------------------------------------------------------------
local BUDGET_COST = {
STR = 1.0, AGI = 1.0, STA = 1.0, INT = 1.0, SPI = 1.0,
CRIT = 14.0, TOHIT = 15.0, RANGEDCRIT = 14.0,
SPELLCRIT = 14.0, SPELLTOHIT = 15.0,
ATTACKPOWER = 0.5, RANGEDATTACKPOWER = 0.5,
DMG = 0.86, HEAL = 0.58,
DEFENSE = 1.5, DODGE = 12.0, PARRY = 12.0, BLOCK = 12.0,
BLOCKVALUE = 0.5, ARMOR = 0.05, BASEARMOR = 0.02,
HEALTHREG = 2.0, MANAREG = 2.0,
HEALTH = 0.07, MANA = 0.07,
WEAPONDPS = 3.0,
}
--------------------------------------------------------------------------------
-- EP Weight Tables (tab = talent tab index for primary spec detection)
--------------------------------------------------------------------------------
local WEIGHTS = {
WARRIOR = {
specs = {
{ name = "武器", tab = 1, color = "ffC79C6E",
w = { STR=2.0, AGI=1.4, STA=0.1, TOHIT=18, CRIT=25, ATTACKPOWER=1.0,
HEALTH=0.1, WEAPONDPS=14, BASEARMOR=0.01 } },
{ name = "狂怒", tab = 2, color = "ffC79C6E",
w = { STR=2.2, AGI=1.6, STA=0.1, TOHIT=20, CRIT=22, ATTACKPOWER=1.0,
HEALTH=0.1, WEAPONDPS=12, BASEARMOR=0.01 } },
{ name = "防护", tab = 3, color = "ff69CCF0",
w = { STR=1.0, AGI=1.8, STA=2.5, TOHIT=10, CRIT=3, ATTACKPOWER=0.5,
DEFENSE=1.5, DODGE=12, PARRY=12, BLOCK=8, BLOCKVALUE=0.5,
ARMOR=0.12, HEALTH=0.25, HEALTHREG=0.5, WEAPONDPS=4, BASEARMOR=0.05 } },
},
-- 硬核物理(战士型): 耐 > 力 > 敏 > 攻强
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, STR=2.0, AGI=1.5, ATTACKPOWER=1.0,
TOHIT=5, CRIT=5, DEFENSE=1.0, DODGE=8, PARRY=5, BLOCK=4, BLOCKVALUE=0.3,
ARMOR=0.08, HEALTH=0.2, HEALTHREG=2.0, WEAPONDPS=5, BASEARMOR=0.03 } },
},
PALADIN = {
specs = {
{ name = "神圣", tab = 1, color = "ff00FF96",
w = { INT=0.35, SPI=0.4, STA=0.05, HEAL=1.0, DMG=0.3, SPELLCRIT=6, MANAREG=3.0,
MANA=0.02, BASEARMOR=0.005 } },
{ name = "防护", tab = 2, color = "ff69CCF0",
w = { STR=1.2, AGI=1.0, STA=2.5, INT=0.3, TOHIT=10, CRIT=5, ATTACKPOWER=0.5, DMG=0.4,
DEFENSE=1.5, DODGE=12, PARRY=12, BLOCK=10, BLOCKVALUE=0.5,
ARMOR=0.12, HEALTH=0.25, MANAREG=1.5, WEAPONDPS=3, BASEARMOR=0.05 } },
{ name = "惩戒", tab = 3, color = "ffF58CBA",
w = { STR=2.0, AGI=1.0, STA=0.1, INT=0.25, TOHIT=16, CRIT=20, SPELLCRIT=10,
ATTACKPOWER=1.0, DMG=0.6, HEAL=0.05, WEAPONDPS=12, BASEARMOR=0.01 } },
},
-- 硬核圣骑士: 耐 > 力 > 智 = 敏 = 精
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, STR=2.0, INT=1.5, AGI=1.5, SPI=1.5,
TOHIT=5, CRIT=5, ATTACKPOWER=0.5, HEAL=0.8, DMG=0.3,
DEFENSE=0.8, DODGE=5, ARMOR=0.06, HEALTH=0.2,
HEALTHREG=1.5, MANAREG=1.5, WEAPONDPS=4, BASEARMOR=0.03 } },
},
HUNTER = {
specs = {
{ name = "野兽", tab = 1, color = "ffABD473",
w = { AGI=2.4, STR=0.3, STA=0.1, INT=0.2, TOHIT=18, CRIT=22, RANGEDCRIT=22,
ATTACKPOWER=0.8, RANGEDATTACKPOWER=1.0, MANAREG=1.5, WEAPONDPS=10, BASEARMOR=0.005 } },
{ name = "射击", tab = 2, color = "ffABD473",
w = { AGI=2.4, STR=0.3, STA=0.1, INT=0.2, TOHIT=18, CRIT=22, RANGEDCRIT=22,
ATTACKPOWER=0.8, RANGEDATTACKPOWER=1.0, MANAREG=1.5, WEAPONDPS=10, BASEARMOR=0.005 } },
{ name = "生存", tab = 3, color = "ffABD473",
w = { AGI=2.0, STR=0.8, STA=0.3, INT=0.2, SPI=0.3, TOHIT=16, CRIT=18, RANGEDCRIT=18,
ATTACKPOWER=1.0, RANGEDATTACKPOWER=1.0, MANAREG=1.0, WEAPONDPS=8, BASEARMOR=0.008 } },
},
-- 硬核猎人: 耐 > 敏 > 智 > 力
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, AGI=2.0, INT=1.0, STR=0.5, SPI=1.5,
TOHIT=5, CRIT=5, RANGEDCRIT=5, ATTACKPOWER=0.4, RANGEDATTACKPOWER=0.5,
ARMOR=0.06, HEALTH=0.2, HEALTHREG=2.5, WEAPONDPS=3, BASEARMOR=0.02 } },
},
ROGUE = {
specs = {
{ name = "刺杀", tab = 1, color = "ffFFF569",
w = { AGI=2.0, STR=1.0, STA=0.1, TOHIT=18, CRIT=25, ATTACKPOWER=1.0,
WEAPONDPS=10, BASEARMOR=0.008 } },
{ name = "战斗", tab = 2, color = "ffFFF569",
w = { AGI=2.0, STR=1.0, STA=0.1, TOHIT=20, CRIT=22, ATTACKPOWER=1.0,
WEAPONDPS=14, BASEARMOR=0.008 } },
{ name = "敏锐", tab = 3, color = "ffFFF569",
w = { AGI=2.2, STR=1.0, STA=0.5, TOHIT=16, CRIT=20, ATTACKPOWER=1.0, DODGE=5,
WEAPONDPS=8, BASEARMOR=0.01 } },
},
-- 硬核潜行者: 耐 > 敏 > 力 > 攻强
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, AGI=2.5, STR=1.5, ATTACKPOWER=1.0,
TOHIT=5, CRIT=5, DODGE=5, ARMOR=0.06,
HEALTH=0.2, HEALTHREG=2.0, WEAPONDPS=5, BASEARMOR=0.02 } },
},
PRIEST = {
specs = {
{ name = "戒律", tab = 1, color = "ff00FF96",
w = { INT=0.35, SPI=0.55, STA=0.05, HEAL=1.0, DMG=0.3, SPELLCRIT=5, MANAREG=3.0,
MANA=0.02, BASEARMOR=0.003 } },
{ name = "神圣", tab = 2, color = "ff00FF96",
w = { INT=0.35, SPI=0.55, STA=0.05, HEAL=1.0, DMG=0.3, SPELLCRIT=5, MANAREG=3.0,
MANA=0.02, BASEARMOR=0.003 } },
{ name = "暗影", tab = 3, color = "ff9482C9",
w = { INT=0.15, SPI=0.35, STA=0.05, DMG=1.0, HEAL=0.05, SPELLCRIT=6, SPELLTOHIT=14,
MANAREG=1.5, BASEARMOR=0.003 } },
},
-- 硬核法系(牧师): 耐 > 智 = 治疗 > 精
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, INT=2.0, HEAL=2.0, DMG=1.5, SPI=1.5,
SPELLCRIT=3, SPELLTOHIT=5, MANAREG=2.5,
ARMOR=0.06, HEALTH=0.15, HEALTHREG=1.0, BASEARMOR=0.02 } },
},
SHAMAN = {
specs = {
{ name = "元素", tab = 1, color = "ff0070DE",
w = { INT=0.2, SPI=0.1, STA=0.05, DMG=1.0, SPELLCRIT=8, SPELLTOHIT=14,
ATTACKPOWER=0.1, MANAREG=2.0, BASEARMOR=0.005 } },
{ name = "增强", tab = 2, color = "ff0070DE",
w = { STR=2.0, AGI=1.6, STA=0.1, INT=0.2, TOHIT=18, CRIT=22, ATTACKPOWER=1.0,
DMG=0.3, SPELLCRIT=5, MANAREG=1.0, WEAPONDPS=14, BASEARMOR=0.01 } },
{ name = "恢复", tab = 3, color = "ff00FF96",
w = { INT=0.35, SPI=0.3, STA=0.05, HEAL=1.0, DMG=0.2, SPELLCRIT=5, MANAREG=3.5,
MANA=0.02, BASEARMOR=0.005 } },
},
-- 硬核萨满(混合): 耐 > 力=敏 > 智=精
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, STR=1.5, AGI=1.5, INT=1.0, SPI=1.0,
TOHIT=5, CRIT=5, ATTACKPOWER=0.5, HEAL=0.8, DMG=0.5,
MANAREG=2.0, ARMOR=0.06, HEALTH=0.2, HEALTHREG=1.5,
WEAPONDPS=4, BASEARMOR=0.02 } },
},
MAGE = {
specs = {
{ name = "奥术", tab = 1, color = "ff69CCF0",
w = { INT=0.2, SPI=0.15, STA=0.4, DMG=1.0, SPELLCRIT=7, SPELLTOHIT=14, MANAREG=2.0,
MANA=0.01, BASEARMOR=0.003 } },
{ name = "火焰", tab = 2, color = "ff69CCF0",
w = { INT=0.15, SPI=0.1, STA=0.4, DMG=1.0, SPELLCRIT=9, SPELLTOHIT=14, MANAREG=2.0,
MANA=0.01, BASEARMOR=0.003 } },
{ name = "冰霜", tab = 3, color = "ff69CCF0",
w = { INT=0.15, SPI=0.1, STA=0.4, DMG=1.0, SPELLCRIT=7, SPELLTOHIT=14, MANAREG=2.0,
MANA=0.01, BASEARMOR=0.003 } },
},
-- 硬核法系(法师): 耐 > 智 = 法伤 > 精
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, INT=2.0, DMG=2.0, SPI=1.5,
SPELLCRIT=3, SPELLTOHIT=5, MANAREG=2.5,
ARMOR=0.08, HEALTH=0.15, HEALTHREG=1.0, BASEARMOR=0.02 } },
},
WARLOCK = {
specs = {
{ name = "痛苦", tab = 1, color = "ff9482C9",
w = { INT=0.15, SPI=0.1, STA=0.4, DMG=1.0, SPELLCRIT=4, SPELLTOHIT=14, MANAREG=1.0,
HEALTH=0.08, BASEARMOR=0.003 } },
{ name = "恶魔", tab = 2, color = "ff9482C9",
w = { INT=0.15, SPI=0.1, STA=0.4, DMG=1.0, SPELLCRIT=6, SPELLTOHIT=14, MANAREG=1.5,
BASEARMOR=0.003 } },
{ name = "毁灭", tab = 3, color = "ff9482C9",
w = { INT=0.15, SPI=0.1, STA=0.4, DMG=1.0, SPELLCRIT=8, SPELLTOHIT=14, MANAREG=1.5,
BASEARMOR=0.003 } },
},
-- 硬核法系(术士): 耐 > 智 = 法伤 > 精
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, INT=2.0, DMG=2.0, SPI=1.0,
SPELLCRIT=3, SPELLTOHIT=5, MANAREG=2.0,
ARMOR=0.06, HEALTH=0.15, HEALTHREG=1.0, BASEARMOR=0.02 } },
},
DRUID = {
specs = {
{ name = "平衡", tab = 1, color = "ffFF7D0A",
w = { INT=0.2, SPI=0.15, STA=0.05, DMG=1.0, HEAL=0.1, SPELLCRIT=7, SPELLTOHIT=14,
MANAREG=2.0, MANA=0.01, BASEARMOR=0.005 } },
{ name = "野猫", tab = 2, color = "ffFF7D0A",
w = { STR=2.4, AGI=1.4, STA=0.1, TOHIT=18, CRIT=22, ATTACKPOWER=1.0, DODGE=2,
WEAPONDPS=2, BASEARMOR=0.008 } },
{ name = "野熊", tab = 2, color = "ff69CCF0",
w = { STR=1.5, AGI=2.0, STA=2.5, TOHIT=8, CRIT=5, ATTACKPOWER=0.5,
DEFENSE=1.2, DODGE=12, ARMOR=0.12, HEALTH=0.25, BASEARMOR=0.05 } },
{ name = "恢复", tab = 3, color = "ff00FF96",
w = { INT=0.35, SPI=0.45, STA=0.05, HEAL=1.0, DMG=0.2, SPELLCRIT=5, MANAREG=3.0,
MANA=0.02, BASEARMOR=0.005 } },
},
-- 硬核德鲁伊(混合偏生存): 耐 > 敏 > 力 > 智 = 精
hc = { name = "硬核", color = "ffFF4444",
w = { STA=3.0, AGI=2.0, STR=1.5, INT=1.0, SPI=1.0,
TOHIT=5, CRIT=5, ATTACKPOWER=0.5, HEAL=0.8, DMG=0.5,
DODGE=5, ARMOR=0.06, HEALTH=0.2, HEALTHREG=2.0,
WEAPONDPS=2, BASEARMOR=0.02 } },
},
}
--------------------------------------------------------------------------------
-- Armor type compatibility (class × armor subclass × level)
-- 1.0 = ideal, 0 = should never wear this
--------------------------------------------------------------------------------
local ARMOR_COMPAT = {
WARRIOR = { Plate=1.0, Mail=0.3, Leather=0.1, Cloth=0.05 },
PALADIN = { Plate=1.0, Mail=0.3, Leather=0.1, Cloth=0.05 },
HUNTER = { Mail=1.0, Leather=0.5, Cloth=0.1 },
SHAMAN = { Mail=1.0, Leather=0.5, Cloth=0.1 },
ROGUE = { Leather=1.0, Cloth=0.3 },
DRUID = { Leather=1.0, Cloth=0.3 },
PRIEST = { Cloth=1.0 },
MAGE = { Cloth=1.0 },
WARLOCK = { Cloth=1.0 },
}
local ARMOR_COMPAT_LOW = {
WARRIOR = { Mail=1.0, Leather=0.6, Cloth=0.1 },
PALADIN = { Mail=1.0, Leather=0.6, Cloth=0.1 },
HUNTER = { Leather=1.0, Cloth=0.3 },
SHAMAN = { Leather=1.0, Cloth=0.3 },
ROGUE = { Leather=1.0, Cloth=0.3 },
DRUID = { Leather=1.0, Cloth=0.3 },
PRIEST = { Cloth=1.0 },
MAGE = { Cloth=1.0 },
WARLOCK = { Cloth=1.0 },
}
--------------------------------------------------------------------------------
-- Equipment slot compatibility per spec (CLASS_specIdx)
-- 2H weapons for tanks = 0.05, shields for DPS = low, etc.
-- Missing entries default to 1.0
--------------------------------------------------------------------------------
local SLOT_COMPAT = {
WARRIOR_1 = { INVTYPE_SHIELD = 0.1, INVTYPE_HOLDABLE = 0.05 },
WARRIOR_2 = { INVTYPE_SHIELD = 0.1, INVTYPE_HOLDABLE = 0.05 },
WARRIOR_3 = { INVTYPE_2HWEAPON = 0.05, INVTYPE_HOLDABLE = 0.05 },
PALADIN_1 = { INVTYPE_2HWEAPON = 0.3 },
PALADIN_2 = { INVTYPE_2HWEAPON = 0.05 },
PALADIN_3 = { INVTYPE_SHIELD = 0.2, INVTYPE_HOLDABLE = 0.1 },
HUNTER_1 = { INVTYPE_SHIELD = 0 },
HUNTER_2 = { INVTYPE_SHIELD = 0 },
HUNTER_3 = { INVTYPE_SHIELD = 0 },
ROGUE_1 = { INVTYPE_2HWEAPON = 0, INVTYPE_SHIELD = 0, INVTYPE_HOLDABLE = 0 },
ROGUE_2 = { INVTYPE_2HWEAPON = 0, INVTYPE_SHIELD = 0, INVTYPE_HOLDABLE = 0 },
ROGUE_3 = { INVTYPE_2HWEAPON = 0, INVTYPE_SHIELD = 0, INVTYPE_HOLDABLE = 0 },
SHAMAN_1 = { INVTYPE_2HWEAPON = 0.3 },
SHAMAN_2 = { INVTYPE_SHIELD = 0.3 },
DRUID_1 = { INVTYPE_SHIELD = 0 },
DRUID_2 = { INVTYPE_SHIELD = 0, INVTYPE_HOLDABLE = 0 },
DRUID_3 = { INVTYPE_SHIELD = 0, INVTYPE_HOLDABLE = 0 },
DRUID_4 = { INVTYPE_SHIELD = 0 },
MAGE_1 = { INVTYPE_SHIELD = 0 },
MAGE_2 = { INVTYPE_SHIELD = 0 },
MAGE_3 = { INVTYPE_SHIELD = 0 },
WARLOCK_1 = { INVTYPE_SHIELD = 0 },
WARLOCK_2 = { INVTYPE_SHIELD = 0 },
WARLOCK_3 = { INVTYPE_SHIELD = 0 },
PRIEST_1 = { INVTYPE_SHIELD = 0 },
PRIEST_2 = { INVTYPE_SHIELD = 0 },
PRIEST_3 = { INVTYPE_SHIELD = 0 },
}
--------------------------------------------------------------------------------
-- Level-based weight adjustments
--------------------------------------------------------------------------------
local function AdjustWeightsForLevel(baseWeights, level, isHC)
if level >= 55 then return baseWeights end
local adj = {}
for k, v in pairs(baseWeights) do adj[k] = v end
if isHC then
-- HC: STA stays high at all levels (survival is the point)
-- Only reduce hit/crit which are less useful at low levels
if level <= 20 then
adj.SPI = (adj.SPI or 0) * 1.3
adj.TOHIT = (adj.TOHIT or 0) * 0.2
adj.SPELLTOHIT = (adj.SPELLTOHIT or 0) * 0.2
adj.HEALTHREG = (adj.HEALTHREG or 0) + 1.0
adj.ARMOR = (adj.ARMOR or 0) + 0.03
elseif level <= 40 then
adj.SPI = (adj.SPI or 0) * 1.15
adj.ARMOR = (adj.ARMOR or 0) + 0.02
adj.TOHIT = (adj.TOHIT or 0) * 0.5
adj.SPELLTOHIT = (adj.SPELLTOHIT or 0) * 0.5
else
adj.TOHIT = (adj.TOHIT or 0) * 0.8
adj.SPELLTOHIT = (adj.SPELLTOHIT or 0) * 0.8
end
else
if level <= 20 then
adj.SPI = (adj.SPI or 0) * 2.5 + 0.5
adj.STA = (adj.STA or 0) * 0.3
adj.TOHIT = (adj.TOHIT or 0) * 0.2
adj.SPELLTOHIT = (adj.SPELLTOHIT or 0) * 0.2
adj.HEALTHREG = (adj.HEALTHREG or 0) + 1.5
adj.ARMOR = (adj.ARMOR or 0) + 0.03
elseif level <= 40 then
adj.SPI = (adj.SPI or 0) * 1.5 + 0.2
adj.ARMOR = (adj.ARMOR or 0) + 0.02
adj.TOHIT = (adj.TOHIT or 0) * 0.6
adj.SPELLTOHIT = (adj.SPELLTOHIT or 0) * 0.6
else
adj.TOHIT = (adj.TOHIT or 0) * 0.85
adj.SPELLTOHIT = (adj.SPELLTOHIT or 0) * 0.85
end
end
return adj
end
--------------------------------------------------------------------------------
-- Player info helpers
--------------------------------------------------------------------------------
local function GetPlayerClassToken()
local _, classEN = UnitClass("player")
if classEN then return string.upper(classEN) end
return nil
end
local function GetPlayerLevel()
return UnitLevel("player") or 60
end
local function GetPrimaryTabIndex()
if not GetNumTalentTabs then return 1 end
local maxPts, maxIdx = 0, 1
for i = 1, GetNumTalentTabs() do
local _, _, pts = GetTalentTabInfo(i)
if pts and pts > maxPts then
maxPts = pts
maxIdx = i
end
end
return maxIdx
end
local function IsPlayerHardcore()
if IsHardcore and IsHardcore("player") then return true end
if C_TurtleWoW and C_TurtleWoW.IsHardcore and C_TurtleWoW.IsHardcore("player") then
return true
end
return false
end
--------------------------------------------------------------------------------
-- Item link/info utilities
--------------------------------------------------------------------------------
local function ExtractItemString(link)
if not link then return nil end
local _, _, itemStr = string.find(link, "(item:[%-?%d:]+)")
return itemStr
end
-- Item quality detection from link color code
-- WoW link format: |cffXXXXXX|Hitem:...|h[Name]|h|r
local QUALITY_FROM_COLOR = {
["9d9d9d"] = 0, -- Poor (gray)
["ffffff"] = 1, -- Common (white)
["1eff00"] = 2, -- Uncommon (green)
["0070dd"] = 3, -- Rare (blue)
["a335ee"] = 4, -- Epic (purple)
["ff8000"] = 5, -- Legendary (orange)
["e6cc80"] = 6, -- Artifact
}
local QUALITY_MULT = {
[0] = 0, -- Poor: skip entirely
[1] = 0.50, -- Common: penalty
[2] = 0.90, -- Uncommon: slight penalty
[3] = 0.95, -- Rare: near full
[4] = 1.0, -- Epic: full
[5] = 1.0, -- Legendary: full
[6] = 1.0, -- Artifact: full
}
local function GetQualityFromLink(link)
if not link then return -1 end
local _, _, hex = string.find(link, "|cff(%x%x%x%x%x%x)")
if hex then
return QUALITY_FROM_COLOR[string.lower(hex)] or 1
end
return 1
end
local function GetQualityFromTooltip(tooltip)
if not tooltip then return 1 end
local tipName = tooltip:GetName()
if not tipName then return 1 end
local nameObj = getglobal(tipName .. "TextLeft1")
if not nameObj then return 1 end
local r, g, b = nameObj:GetTextColor()
if not r then return 1 end
-- Gray: r≈0.62, g≈0.62, b≈0.62
if r < 0.7 and g < 0.7 and b < 0.7 then return 0 end
-- White: r=1, g=1, b=1
if r > 0.95 and g > 0.95 and b > 0.95 then return 1 end
-- Green: r≈0.12, g=1, b=0
if g > 0.9 and r < 0.2 then return 2 end
-- Blue: r=0, g≈0.44, b≈0.87
if b > 0.8 and r < 0.1 then return 3 end
-- Purple: r≈0.64, g≈0.21, b≈0.93
if b > 0.85 and r > 0.5 then return 4 end
-- Orange: r=1, g≈0.5, b=0
if r > 0.9 and g > 0.4 and b < 0.1 then return 5 end
return 1
end
-- Normalize item class/subclass strings (Chinese client support)
local ARMOR_CLASS_NAMES = { Armor=true, ["护甲"]=true, ["铠甲"]=true }
local ARMOR_MISC_NAMES = { Miscellaneous=true, ["其它"]=true, ["杂项"]=true, ["其他"]=true }
local SHIELD_NAMES = { Shield=true, Shields=true, ["盾牌"]=true, [""]=true }
local SUBCLASS_NORMALIZE = {
Plate="Plate", ["板甲"]="Plate", ["铠甲"]="Plate",
Mail="Mail", ["锁甲"]="Mail", ["链甲"]="Mail",
Leather="Leather", ["皮甲"]="Leather", ["皮革"]="Leather",
Cloth="Cloth", ["布甲"]="Cloth", ["布料"]="Cloth",
}
-- Parse equipLoc and armor type directly from tooltip text
local SLOT_TEXT_TO_INVTYPE = {
["Head"]="INVTYPE_HEAD", ["头部"]="INVTYPE_HEAD",
["Neck"]="INVTYPE_NECK", ["颈部"]="INVTYPE_NECK",
["Shoulder"]="INVTYPE_SHOULDER", ["肩部"]="INVTYPE_SHOULDER",
["Shirt"]="INVTYPE_BODY", ["衬衣"]="INVTYPE_BODY",
["Chest"]="INVTYPE_CHEST", ["胸部"]="INVTYPE_CHEST",
["Robe"]="INVTYPE_ROBE", ["胸甲"]="INVTYPE_ROBE",
["Waist"]="INVTYPE_WAIST", ["腰部"]="INVTYPE_WAIST",
["Legs"]="INVTYPE_LEGS", ["腿部"]="INVTYPE_LEGS",
["Feet"]="INVTYPE_FEET", [""]="INVTYPE_FEET", [""]="INVTYPE_FEET",
["Wrist"]="INVTYPE_WRIST", ["手腕"]="INVTYPE_WRIST",
["Hands"]="INVTYPE_HAND", [""]="INVTYPE_HAND",
["Finger"]="INVTYPE_FINGER", ["手指"]="INVTYPE_FINGER",
["Trinket"]="INVTYPE_TRINKET", ["饰品"]="INVTYPE_TRINKET",
["Back"]="INVTYPE_CLOAK", ["背部"]="INVTYPE_CLOAK",
["Main Hand"]="INVTYPE_WEAPONMAINHAND", ["主手"]="INVTYPE_WEAPONMAINHAND",
["One-Hand"]="INVTYPE_WEAPON", ["单手"]="INVTYPE_WEAPON",
["Off Hand"]="INVTYPE_WEAPONOFFHAND", ["副手"]="INVTYPE_WEAPONOFFHAND",
["Two-Hand"]="INVTYPE_2HWEAPON", ["双手"]="INVTYPE_2HWEAPON",
["Held In Off-hand"]="INVTYPE_HOLDABLE", ["副手物品"]="INVTYPE_HOLDABLE",
["Ranged"]="INVTYPE_RANGED", ["远程"]="INVTYPE_RANGED",
["Thrown"]="INVTYPE_THROWN", ["投掷"]="INVTYPE_THROWN",
["Relic"]="INVTYPE_RELIC", ["圣物"]="INVTYPE_RELIC",
["Shield"]="INVTYPE_SHIELD", ["盾牌"]="INVTYPE_SHIELD",
}
local function ParseEquipLocFromTooltip(tooltip)
if not tooltip then return nil, nil, nil end
local tipName = tooltip:GetName()
if not tipName then return nil, nil, nil end
local numLines = tooltip:NumLines()
if not numLines or numLines < 2 then return nil, nil, nil end
local foundEquipLoc, foundArmorType, foundItemClass
for i = 2, math.min(numLines, 8) do
local leftObj = getglobal(tipName .. "TextLeft" .. i)
local rightObj = getglobal(tipName .. "TextRight" .. i)
if leftObj then
local leftText = leftObj:GetText()
if leftText and leftText ~= "" then
local invtype = SLOT_TEXT_TO_INVTYPE[leftText]
if invtype then
foundEquipLoc = invtype
if rightObj then
local rightText = rightObj:GetText()
if rightText and rightText ~= "" then
foundArmorType = rightText
if ARMOR_CLASS_NAMES[rightText] or SUBCLASS_NORMALIZE[rightText]
or SHIELD_NAMES[rightText] then
foundItemClass = "Armor"
end
end
end
break
end
end
end
end
return foundEquipLoc, foundArmorType, foundItemClass
end
local GS_EQUIP_LOCS = {
INVTYPE_HEAD=true, INVTYPE_NECK=true, INVTYPE_SHOULDER=true,
INVTYPE_BODY=true, INVTYPE_CHEST=true, INVTYPE_ROBE=true,
INVTYPE_WAIST=true, INVTYPE_LEGS=true, INVTYPE_FEET=true,
INVTYPE_WRIST=true, INVTYPE_HAND=true,
INVTYPE_FINGER=true, INVTYPE_TRINKET=true,
INVTYPE_CLOAK=true,
INVTYPE_WEAPON=true, INVTYPE_2HWEAPON=true, INVTYPE_WEAPONMAINHAND=true,
INVTYPE_SHIELD=true, INVTYPE_WEAPONOFFHAND=true, INVTYPE_HOLDABLE=true,
INVTYPE_RANGED=true, INVTYPE_RANGEDRIGHT=true, INVTYPE_THROWN=true,
INVTYPE_RELIC=true,
}
--------------------------------------------------------------------------------
-- Tooltip text parser (zero external dependency)
--------------------------------------------------------------------------------
local STAT_PATTERNS = {
-- Weapon DPS (float, e.g. "(每秒伤害19.7)" or "(12.3 damage per second)")
{ p = "%((%d+%.?%d*) damage per second%)", s = "WEAPONDPS" },
{ p = "每秒伤害(%d+%.?%d*)", s = "WEAPONDPS" },
{ p = "每秒(%d+%.?%d*)点伤害", s = "WEAPONDPS" },
-- Base armor value ("63点护甲" / "123 护甲" / "500 Armor", no + prefix)
{ p = "^(%d+)点护甲", s = "BASEARMOR" },
{ p = "^(%d+) 点护甲", s = "BASEARMOR" },
{ p = "^(%d+) 护甲$", s = "BASEARMOR" },
{ p = "^(%d+) Armor$", s = "BASEARMOR" },
-- Bonus armor ("+30 Armor" / "+30 护甲" from enchants)
{ p = "%+(%d+) Armor", s = "ARMOR" },
{ p = "%+(%d+) 护甲", s = "ARMOR" },
{ p = "护甲值提高(%d+)", s = "ARMOR" },
-- Primary stats
{ p = "%+(%d+) Strength", s = "STR" },
{ p = "%+(%d+) Agility", s = "AGI" },
{ p = "%+(%d+) Stamina", s = "STA" },
{ p = "%+(%d+) Intellect", s = "INT" },
{ p = "%+(%d+) Spirit", s = "SPI" },
{ p = "%+(%d+) 力量", s = "STR" },
{ p = "%+(%d+) 敏捷", s = "AGI" },
{ p = "%+(%d+) 耐力", s = "STA" },
{ p = "%+(%d+) 智力", s = "INT" },
{ p = "%+(%d+) 精神", s = "SPI" },
-- Crit (spell > ranged > melee, more specific patterns first)
{ p = "critical strike with spells by (%d+)%%", s = "SPELLCRIT" },
{ p = "法术暴击.-(%d+)%%", s = "SPELLCRIT" },
{ p = "法术.-致命一击.-(%d+)%%", s = "SPELLCRIT" },
{ p = "critical strike with ranged weapons by (%d+)%%", s = "RANGEDCRIT" },
{ p = "远程暴击.-(%d+)%%", s = "RANGEDCRIT" },
{ p = "critical strike by (%d+)%%", s = "CRIT" },
{ p = "致命一击.-(%d+)%%", s = "CRIT" },
{ p = "暴击.-(%d+)%%", s = "CRIT" },
-- Hit
{ p = "hit with spells by (%d+)%%", s = "SPELLTOHIT" },
{ p = "法术命中.-(%d+)%%", s = "SPELLTOHIT" },
{ p = "chance to hit by (%d+)%%", s = "TOHIT" },
{ p = "命中.-(%d+)%%", s = "TOHIT" },
-- Attack Power
{ p = "%+(%d+) ranged Attack Power", s = "RANGEDATTACKPOWER" },
{ p = "%+(%d+) 远程攻击强度", s = "RANGEDATTACKPOWER" },
{ p = "远程攻击强度提高(%d+)", s = "RANGEDATTACKPOWER" },
{ p = "%+(%d+) Attack Power", s = "ATTACKPOWER" },
{ p = "%+(%d+) 攻击强度", s = "ATTACKPOWER" },
{ p = "攻击强度提高(%d+)", s = "ATTACKPOWER" },
-- Spell damage + healing (combined, MUST be before individual patterns)
{ p = "damage and healing done by magical spells and effects by up to (%d+)", s = "DMG", a = "HEAL" },
{ p = "伤害和治疗效果.-最多(%d+)", s = "DMG", a = "HEAL" },
{ p = "法术伤害和治疗.-(%d+)", s = "DMG", a = "HEAL" },
-- Healing only
{ p = "healing done by spells and effects by up to (%d+)", s = "HEAL" },
{ p = "Increases healing done by up to (%d+)", s = "HEAL" },
{ p = "治疗效果.-最多(%d+)", s = "HEAL" },
{ p = "治疗法术.-最多(%d+)", s = "HEAL" },
{ p = "治疗量.-最多(%d+)", s = "HEAL" },
-- Spell damage only
{ p = "damage done by magical spells and effects by up to (%d+)", s = "DMG" },
{ p = "法术伤害.-最多(%d+)", s = "DMG" },
{ p = "魔法伤害.-最多(%d+)", s = "DMG" },
-- Mana/health regen
{ p = "Restores (%d+) mana per 5 sec", s = "MANAREG" },
{ p = "(%d+) mana per 5 sec", s = "MANAREG" },
{ p = "每5秒恢复(%d+)点法力", s = "MANAREG" },
{ p = "每5秒回复(%d+)点法力", s = "MANAREG" },
{ p = "5秒回复(%d+)点法力", s = "MANAREG" },
{ p = "Restores (%d+) health per 5 sec", s = "HEALTHREG" },
{ p = "每5秒恢复(%d+)点生命", s = "HEALTHREG" },
{ p = "每5秒回复(%d+)点生命", s = "HEALTHREG" },
-- Defense
{ p = "Increased Defense %+(%d+)", s = "DEFENSE" },
{ p = "Defense %+(%d+)", s = "DEFENSE" },
{ p = "%+(%d+) Defense", s = "DEFENSE" },
{ p = "防御技能提高(%d+)", s = "DEFENSE" },
{ p = "防御等级提高(%d+)", s = "DEFENSE" },
-- Avoidance
{ p = "dodge.-by (%d+)%%", s = "DODGE" },
{ p = "躲闪.-(%d+)%%", s = "DODGE" },
{ p = "parry.-by (%d+)%%", s = "PARRY" },
{ p = "招架.-(%d+)%%", s = "PARRY" },
{ p = "block attacks.-by (%d+)%%", s = "BLOCK" },
{ p = "格挡几率.-(%d+)%%", s = "BLOCK" },
{ p = "格挡率.-(%d+)%%", s = "BLOCK" },
{ p = "block value.-by (%d+)", s = "BLOCKVALUE" },
{ p = "格挡值.-(%d+)", s = "BLOCKVALUE" },
-- HP/Mana
{ p = "%+(%d+) Health", s = "HEALTH" },
{ p = "%+(%d+) Hit Points", s = "HEALTH" },
{ p = "%+(%d+) 生命值", s = "HEALTH" },
{ p = "%+(%d+) Mana", s = "MANA" },
{ p = "%+(%d+) 法力值", s = "MANA" },
}
--------------------------------------------------------------------------------
-- Debug helper: /gsdebug to toggle
--------------------------------------------------------------------------------
local GS_DEBUG = false
local function GSDebug(msg)
if GS_DEBUG and DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage("|cffFFFF00[GS]|r " .. tostring(msg))
end
end
SLASH_GSDEBUG1 = "/gsdebug"
SlashCmdList["GSDEBUG"] = function()
GS_DEBUG = not GS_DEBUG
DEFAULT_CHAT_FRAME:AddMessage("|cffFFFF00[GearScore]|r Debug: " .. (GS_DEBUG and "ON" or "OFF"))
end
--------------------------------------------------------------------------------
-- Stat parser: scan a VISIBLE tooltip's text lines directly
-- This is the most reliable method — no hidden tooltip, no library needed
--------------------------------------------------------------------------------
local function ScanTooltipForStats(tooltip)
if not tooltip then return nil end
local tipName = tooltip:GetName()
if not tipName then return nil end
local numLines = tooltip:NumLines()
if not numLines or numLines < 2 then return nil end
local stats = {}
for i = 2, numLines do
local lineObj = getglobal(tipName .. "TextLeft" .. i)
if lineObj then
local text = lineObj:GetText()
if text and text ~= "" then
for _, pat in ipairs(STAT_PATTERNS) do
local _, _, val = string.find(text, pat.p)
if val then
local n = tonumber(val)
if n and n > 0 then
stats[pat.s] = (stats[pat.s] or 0) + n
if pat.a then
stats[pat.a] = (stats[pat.a] or 0) + n
end
GSDebug(" L" .. i .. ": " .. pat.s .. "=" .. n .. " (" .. text .. ")")
end
break
end
end
end
end
end
local hasAny = false
for _ in pairs(stats) do hasAny = true; break end
return hasAny and stats or nil
end
local function ParseItemWithLib(link)
if not link then return nil end
if not AceLibrary or not AceLibrary.HasInstance then return nil end
if not AceLibrary:HasInstance("ItemBonusLib-1.0") then return nil end
local lib = AceLibrary("ItemBonusLib-1.0")
if not lib or not lib.ScanItem then return nil end
local ok, result = pcall(function() return lib:ScanItem(link, true) end)
if ok and result then
local hasAny = false
for _ in pairs(result) do hasAny = true; break end
if hasAny then return result end
end
return nil
end
--------------------------------------------------------------------------------
-- Compatibility multipliers
--------------------------------------------------------------------------------
local function GetArmorCompat(classToken, itemClass, itemSubClass, level)
if not itemClass or not ARMOR_CLASS_NAMES[itemClass] then return 1.0 end
if not itemSubClass or itemSubClass == "" then return 1.0 end
if ARMOR_MISC_NAMES[itemSubClass] then return 1.0 end
if SHIELD_NAMES[itemSubClass] then return 1.0 end
local normalizedSub = SUBCLASS_NORMALIZE[itemSubClass]
if not normalizedSub then return 1.0 end
local tbl = (level < 40) and ARMOR_COMPAT_LOW or ARMOR_COMPAT
local classTbl = tbl[classToken]
if not classTbl then return 1.0 end
return classTbl[normalizedSub] or 0.3
end
local function GetSlotCompat(classToken, specIdx, equipLoc)
if not equipLoc or equipLoc == "" then return 1.0 end
local key = classToken .. "_" .. specIdx
local tbl = SLOT_COMPAT[key]
if not tbl then return 1.0 end
local val = tbl[equipLoc]
if val ~= nil then return val end
return 1.0
end
--------------------------------------------------------------------------------
-- Core scoring: 1-10 normalized scale
-- Score = (actual_EP / ideal_EP) * 10 * equipment_compat
-- ideal_EP = total_budget * best_efficiency_for_spec
--------------------------------------------------------------------------------
local function GetBestEfficiency(weights)
local best = 0
for stat, w in pairs(weights) do
local cost = BUDGET_COST[stat]
if cost and cost > 0 and w > 0 then
local eff = w / cost
if eff > best then best = eff end
end
end
return best
end
local function CalcRawEP(bonuses, weights)
if not bonuses or not weights then return 0 end
local ep = 0
for stat, value in pairs(bonuses) do
local w = weights[stat]
if w and w > 0 then
ep = ep + value * w
end
end
return ep
end
local function CalcTotalBudget(bonuses)
if not bonuses then return 0 end
local total = 0
for stat, value in pairs(bonuses) do
local cost = BUDGET_COST[stat]
if cost and cost > 0 then
total = total + math.abs(value) * cost
end
end
return total
end
local function CalcNormalizedScore(bonuses, weights, armorCompat, slotCompat)
local rawEP = CalcRawEP(bonuses, weights)
local totalBudget = CalcTotalBudget(bonuses)
local bestEff = GetBestEfficiency(weights)
if totalBudget <= 0 or bestEff <= 0 then return 0 end
local idealEP = totalBudget * bestEff
local rawScore = (rawEP / idealEP) * 10
local compat = (armorCompat or 1.0) * (slotCompat or 1.0)
local finalScore = rawScore * compat
if finalScore < 0 then finalScore = 0 end
if finalScore > 10 then finalScore = 10 end
return math.floor(finalScore * 10 + 0.5) / 10
end
--------------------------------------------------------------------------------
-- Score color (1-10 scale)
--------------------------------------------------------------------------------
local function ScoreColorHex(score)
if score >= 9 then return "ffFF8800" end
if score >= 7 then return "ff00FF00" end
if score >= 5 then return "ffFFFFFF" end
if score >= 3 then return "ffFFFF00" end
return "ffFF4444"
end
local function ScoreLabel(score)
if score >= 9 then return "极品" end
if score >= 7 then return "优秀" end
if score >= 5 then return "适用" end
if score >= 3 then return "一般" end
return "不适"
end
--------------------------------------------------------------------------------
-- Main tooltip function
--------------------------------------------------------------------------------
function GS:AddScoreToTooltip(tooltip, link)
if not tooltip or not link then return end
if SFramesDB and SFramesDB.gearScore == false then return end
if tooltip._gsScoreAdded then return end
local classToken = GetPlayerClassToken()
if not classToken then return end
local classData = WEIGHTS[classToken]
if not classData then return end
GSDebug("Processing: " .. tostring(link))
-- Step 0: Check item quality — skip gray, penalize white
local quality = GetQualityFromLink(link)
if quality < 0 then
quality = GetQualityFromTooltip(tooltip)
end
local qualityMult = QUALITY_MULT[quality] or 0.75
if quality == 0 then
GSDebug("Skipped: poor quality (gray) item")
return
end
GSDebug("Quality=" .. quality .. " mult=" .. qualityMult)
-- Step 1: Try GetItemInfo with extracted item string (more reliable than full link)
local equipLoc, itemClass, itemSubClass
pcall(function()
local itemStr = ExtractItemString(link)
local infoArg = itemStr or link
GSDebug("GetItemInfo arg: " .. tostring(infoArg))
local results = { GetItemInfo(infoArg) }
if results[1] then
itemClass = results[6]
itemSubClass = results[7]
for idx = 8, 10 do
local v = results[idx]
if type(v) == "string" and (v == "" or string.find(v, "^INVTYPE")) then
equipLoc = v
break
end
end
end
GSDebug("GetItemInfo: equip=" .. tostring(equipLoc)
.. " class=" .. tostring(itemClass) .. " sub=" .. tostring(itemSubClass))
end)
-- Step 2: If GetItemInfo failed, parse equip slot from tooltip text
if not equipLoc then
local ttEquip, ttArmor, ttClass = ParseEquipLocFromTooltip(tooltip)
if ttEquip then
equipLoc = ttEquip
if ttArmor and not itemSubClass then itemSubClass = ttArmor end
if ttClass and not itemClass then itemClass = ttClass end
GSDebug("Parsed from tooltip: equip=" .. tostring(equipLoc) .. " armor=" .. tostring(ttArmor))
end
end
-- Skip bags, ammo, tabards
if equipLoc == "INVTYPE_BAG" or equipLoc == "INVTYPE_AMMO" or equipLoc == "INVTYPE_TABARD" then
GSDebug("Skipped: bag/ammo/tabard")
return
end
-- Skip non-equippable items (only if we got valid info)
if equipLoc and equipLoc ~= "" and not GS_EQUIP_LOCS[equipLoc] then
GSDebug("Skipped: not equippable (" .. tostring(equipLoc) .. ")")
return
end
-- Step 3: Parse stats - try library first, then scan visible tooltip text
local bonuses = ParseItemWithLib(link)
if bonuses then
GSDebug("Stats from ItemBonusLib")
else
bonuses = ScanTooltipForStats(tooltip)
if bonuses then
GSDebug("Stats from tooltip text")
end
end
if not bonuses then
GSDebug("No stats found, skipping")
return
end
-- Step 4: No equipLoc = not equipment (potion, food, etc.) → skip
if not equipLoc or equipLoc == "" then
GSDebug("No equipLoc, not equipment, skipping")
return
end
local level = GetPlayerLevel()
local primaryTab = GetPrimaryTabIndex()
local isHC = IsPlayerHardcore()
local armorCompat = GetArmorCompat(classToken, itemClass, itemSubClass, level)
GSDebug("ArmorCompat=" .. armorCompat .. " class=" .. classToken)
local specs = classData.specs
if not specs or table.getn(specs) == 0 then return end
local scores = {}
local anyShow = false
for i, spec in ipairs(specs) do
local w = AdjustWeightsForLevel(spec.w, level, false)
local slotCompat = GetSlotCompat(classToken, i, equipLoc)
local s = CalcNormalizedScore(bonuses, w, armorCompat, slotCompat)
s = math.floor(s * qualityMult * 10 + 0.5) / 10
if s < 1.0 and s > 0 then s = 1.0 end
table.insert(scores, {
name = spec.name,
color = spec.color,
score = s,
isPrimary = (spec.tab == primaryTab),
label = ScoreLabel(s),
})
if s > 0 then anyShow = true end
end
local hcScore = 0
if classData.hc then
local hw = AdjustWeightsForLevel(classData.hc.w, level, true)
hcScore = CalcNormalizedScore(bonuses, hw, armorCompat, 1.0)
hcScore = math.floor(hcScore * qualityMult * 10 + 0.5) / 10
if hcScore < 1.0 and hcScore > 0 then hcScore = 1.0 end
if hcScore > 0 then anyShow = true end
end
if not anyShow then return end
tooltip._gsScoreAdded = true
tooltip:AddLine(" ")
tooltip:AddLine("|cffffd700── 装备评分 ──|r")
for _, sd in ipairs(scores) do
local star = sd.isPrimary and "" or " "
local sStr = string.format("%.1f", sd.score)
local sColor = ScoreColorHex(sd.score)
local left = star .. "|c" .. sd.color .. sd.name .. "|r"
local right = "|c" .. sColor .. sStr .. " " .. sd.label .. "|r"
tooltip:AddDoubleLine(left, right, 1,1,1, 1,1,1)
end
if classData.hc and hcScore > 0 then
local hcSColor = ScoreColorHex(hcScore)
local hcStar = isHC and "" or " "
local left = hcStar .. "|c" .. classData.hc.color .. "硬核|r"
local right = "|c" .. hcSColor .. string.format("%.1f", hcScore)
.. " " .. ScoreLabel(hcScore) .. "|r"
tooltip:AddDoubleLine(left, right, 1,1,1, 1,1,1)
end
tooltip:Show()
end
--------------------------------------------------------------------------------
-- Self-contained tooltip hooks
--------------------------------------------------------------------------------
local function GS_TryEnhance(tooltip, link)
if not link then return end
pcall(function() GS:AddScoreToTooltip(tooltip, link) end)
end
function GS:HookTooltips()
if self._hooked then return end
self._hooked = true
local origHide = GameTooltip:GetScript("OnHide")
GameTooltip:SetScript("OnHide", function()
this._gsScoreAdded = nil
if origHide then origHide() end
end)
local origBag = GameTooltip.SetBagItem
GameTooltip.SetBagItem = function(self, bag, slot)
self._gsScoreAdded = nil
local r1, r2, r3 = origBag(self, bag, slot)
GS_TryEnhance(self, GetContainerItemLink(bag, slot))
return r1, r2, r3
end
local origInv = GameTooltip.SetInventoryItem
GameTooltip.SetInventoryItem = function(self, unit, slotId)
self._gsScoreAdded = nil
local r1, r2, r3 = origInv(self, unit, slotId)
if unit and slotId then
GS_TryEnhance(self, GetInventoryItemLink(unit, slotId))
end
return r1, r2, r3
end
if GameTooltip.SetMerchantItem then
local orig = GameTooltip.SetMerchantItem
GameTooltip.SetMerchantItem = function(self, idx)
self._gsScoreAdded = nil
orig(self, idx)
if GetMerchantItemLink then GS_TryEnhance(self, GetMerchantItemLink(idx)) end
end
end
if GameTooltip.SetQuestItem then
local orig = GameTooltip.SetQuestItem
GameTooltip.SetQuestItem = function(self, qtype, idx)
self._gsScoreAdded = nil
orig(self, qtype, idx)
if GetQuestItemLink then GS_TryEnhance(self, GetQuestItemLink(qtype, idx)) end
end
end
if GameTooltip.SetQuestLogItem then
local orig = GameTooltip.SetQuestLogItem
GameTooltip.SetQuestLogItem = function(self, itype, idx)
self._gsScoreAdded = nil
orig(self, itype, idx)
if GetQuestLogItemLink then GS_TryEnhance(self, GetQuestLogItemLink(itype, idx)) end
end
end
if GameTooltip.SetLootItem then
local orig = GameTooltip.SetLootItem
GameTooltip.SetLootItem = function(self, idx)
self._gsScoreAdded = nil
orig(self, idx)
if GetLootSlotLink then GS_TryEnhance(self, GetLootSlotLink(idx)) end
end
end
if GameTooltip.SetLootRollItem then
local orig = GameTooltip.SetLootRollItem
GameTooltip.SetLootRollItem = function(self, rollId)
self._gsScoreAdded = nil
orig(self, rollId)
if GetLootRollItemLink then GS_TryEnhance(self, GetLootRollItemLink(rollId)) end
end
end
if GameTooltip.SetAuctionItem then
local orig = GameTooltip.SetAuctionItem
GameTooltip.SetAuctionItem = function(self, atype, idx)
self._gsScoreAdded = nil
orig(self, atype, idx)
if GetAuctionItemLink then GS_TryEnhance(self, GetAuctionItemLink(atype, idx)) end
end
end
if GameTooltip.SetTradeSkillItem then
local orig = GameTooltip.SetTradeSkillItem
GameTooltip.SetTradeSkillItem = function(self, skillIdx, reagentIdx)
self._gsScoreAdded = nil
orig(self, skillIdx, reagentIdx)
local link
if reagentIdx then
if GetTradeSkillReagentItemLink then link = GetTradeSkillReagentItemLink(skillIdx, reagentIdx) end
else
if GetTradeSkillItemLink then link = GetTradeSkillItemLink(skillIdx) end
end
GS_TryEnhance(self, link)
end
end
if GameTooltip.SetCraftItem then
local orig = GameTooltip.SetCraftItem
GameTooltip.SetCraftItem = function(self, skill, slot)
self._gsScoreAdded = nil
orig(self, skill, slot)
if GetCraftReagentItemLink then GS_TryEnhance(self, GetCraftReagentItemLink(skill, slot)) end
end
end
if GameTooltip.SetTradePlayerItem then
local orig = GameTooltip.SetTradePlayerItem
GameTooltip.SetTradePlayerItem = function(self, idx)
self._gsScoreAdded = nil
orig(self, idx)
if GetTradePlayerItemLink then GS_TryEnhance(self, GetTradePlayerItemLink(idx)) end
end
end
if GameTooltip.SetTradeTargetItem then
local orig = GameTooltip.SetTradeTargetItem
GameTooltip.SetTradeTargetItem = function(self, idx)
self._gsScoreAdded = nil
orig(self, idx)
if GetTradeTargetItemLink then GS_TryEnhance(self, GetTradeTargetItemLink(idx)) end
end
end
if GameTooltip.SetInboxItem then
local orig = GameTooltip.SetInboxItem
GameTooltip.SetInboxItem = function(self, mailID, attachIdx)
self._gsScoreAdded = nil
orig(self, mailID, attachIdx)
local left1 = getglobal("GameTooltipTextLeft1")
if left1 and GetItemLinkByName then
GS_TryEnhance(self, GetItemLinkByName(left1:GetText()))
end
end
end
local origRef = SetItemRef
if origRef then
SetItemRef = function(link, text, button)
origRef(link, text, button)
if IsAltKeyDown() or IsShiftKeyDown() or IsControlKeyDown() then return end
pcall(function()
local _, _, itemStr = string.find(link or "", "(item:[%-?%d:]+)")
if itemStr then
ItemRefTooltip._gsScoreAdded = nil
GS:AddScoreToTooltip(ItemRefTooltip, itemStr)
end
end)
end
end
end
--------------------------------------------------------------------------------
-- Initialize
--------------------------------------------------------------------------------
local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
initFrame:SetScript("OnEvent", function()
initFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
GS:HookTooltips()
end)