-------------------------------------------------------------------------------- -- 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)