vanilla-wow-addons – Rev 1
?pathlinks?
local MAJOR_VERSION = "ItemBonusLib-1.0"
local MINOR_VERSION = "$Revision: 17465 $"
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if not AceLibrary:HasInstance("AceLocale-2.2") then error(MAJOR_VERSION .. " requires AceLocale-2.2") end
if not AceLibrary:HasInstance("Gratuity-2.0") then error(MAJOR_VERSION .. " requires Gratuity-2.0") end
if not AceLibrary:HasInstance("Deformat-2.0") then error(MAJOR_VERSION .. " requires Deformat-2.0") end
local DEBUG = false
local ItemBonusLib = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceConsole-2.0", "AceDebug-2.0")
ItemBonusLib:SetDebugging(DEBUG)
local Gratuity = AceLibrary("Gratuity-2.0")
local Deformat = AceLibrary("Deformat-2.0")
local L = AceLibrary("AceLocale-2.2"):new("ItemBonusLib")
-- bonuses[BONUS] = VALUE
local bonuses = {}
-- details[BONUS][SLOT] = VALUE
local details = {}
-- items[LINK].bonuses[BONUS] = VALUE
-- items[LINK].set = SETNAME
-- items[LINK].set_line = number
local items = {}
-- sets[SETNAME].count = COUNT
-- sets[SETNAME].bonuses[NUM][BONUS] = VALUE
-- sets[SETNAME].scan_count = COUNT
-- sets[SETNAME].scan_bonuses = COUNT
local sets = {}
local slots = {
["Head"] = true,
["Neck"] = true,
["Shoulder"] = true,
["Shirt"] = true,
["Chest"] = true,
["Waist"] = true,
["Legs"] = true,
["Feet"] = true,
["Wrist"] = true,
["Hands"] = true,
["Finger0"] = true,
["Finger1"] = true,
["Trinket0"] = true,
["Trinket1"] = true,
["Back"] = true,
["MainHand"] = true,
["SecondaryHand"] = true,
["Ranged"] = true,
["Tabard"] = true,
}
function ItemBonusLib:OnInitialize()
self:RegisterEvent("PLAYER_ENTERING_WORLD")
self:RegisterEvent("PLAYER_LEAVING_WORLD")
for s in pairs(slots) do
slots[s] = GetInventorySlotInfo (s.."Slot")
end
local options = {
type = "group",
desc = L["An addon to get information about bonus from equipped items"],
args = {
show = {
type = "execute",
name = L["show"],
desc = L["Show all bonuses from the current equipment"],
func = function ()
self:Print(L["Current equipment bonuses:"])
for bonus, value in pairs(bonuses) do
self:Print("%s : %d", self:GetBonusFriendlyName(bonus), value)
end
end
},
details = {
type = "execute",
name = L["details"],
desc = L["Shows bonuses with slot distribution"],
func = function ()
self:Print(L["Current equipment bonus details:"])
for bonus, detail in pairs(details) do
local s = {}
for slot, value in pairs(detail) do
table.insert(s, string.format("%s : %d", slot, value))
end
self:Print("%s : %d (%s)", self:GetBonusFriendlyName(bonus), bonuses[bonus], table.concat(s, ", "))
end
end
},
item = {
type = "text",
name = L["item"],
desc = L["show bonuses of given itemlink"],
usage = L["<itemlink>"],
get = false,
set = function (link)
local info = self:ScanItemLink(link)
self:Print(L["Bonuses for %s:"], link)
for bonus, value in pairs(info.bonuses) do
self:Print("%s : %d", self:GetBonusFriendlyName(bonus), value)
end
if info.set then
self:Print(L["Item is part of set [%s]"], info.set)
local set = sets[info.set]
for number, bonuses in pairs(set.bonuses) do
local has_bonus = number <= set.count and "*" or " "
self:Print(L[" %sBonus for %d pieces :"], has_bonus, number)
for bonus, value in pairs(bonuses) do
self:Print(" %s : %d", self:GetBonusFriendlyName(bonus), value)
end
end
end
end
},
slot = {
type = "text",
name = L["slot"],
desc = L["show bonuses of given slot"],
usage = L["<slotname>"],
get = false,
set = function (slot)
self:Print(L["Bonuses of slot %s:"], slot)
for bonus, detail in pairs(details) do
if detail[slot] then
self:Print("%s : %d", self:GetBonusFriendlyName(bonus), detail[slot])
end
end
end
},
},
}
self:RegisterChatCommand(L.CHAT_COMMANDS, options)
end
function ItemBonusLib:PLAYER_ENTERING_WORLD()
self:RegisterBucketEvent("UNIT_INVENTORY_CHANGED", 0.5)
self:ScheduleEvent(function() self:ScanEquipment() end, 1)
end
function ItemBonusLib:PLAYER_LEAVING_WORLD()
self:UnregisterBucketEvent("UNIT_INVENTORY_CHANGED")
end
function ItemBonusLib:UNIT_INVENTORY_CHANGED(units)
if units.player then
self:ScanEquipment()
end
end
local cleanItemLink
do
local s = string
local trim = function (str)
local gsub = s.gsub
str = gsub (str, "^%s+", "" )
str = gsub (str, "%s+$", "" )
str = gsub (str, "%.$", "" )
return str
end
local equip = ITEM_SPELL_TRIGGER_ONEQUIP
local l_equip = s.len(equip)
function cleanItemLink(itemLink)
local _, _, link = s.find(itemLink, "|c%x+|H(item:%d+:%d+:%d+:%d+)|h%[.-%]|h|r")
return link or itemLink
end
function ItemBonusLib:AddValue(bonuses, effect, value)
if type(effect) == "string" then
bonuses[effect] = (bonuses[effect] or 0) + value
elseif type(value) == "table" then
for i, e in ipairs(effect) do
self:AddValue (bonuses, e, value[i])
end
else
for _, e in ipairs(effect) do
self:AddValue (bonuses, e, value)
end
end
end
function ItemBonusLib:CheckPassive(bonuses, line)
for _, p in pairs(L.PATTERNS_PASSIVE) do
local _, _, value = s.find (line, "^" .. p.pattern)
if value then
self:AddValue (bonuses, p.effect, value)
return true
end
end
end
function ItemBonusLib:CheckToken(bonuses, token, value)
local t = L.PATTERNS_GENERIC_LOOKUP[token]
if t then
self:AddValue (bonuses, t, value)
return true
else
local s1, s2
for _, p in ipairs(L.PATTERNS_GENERIC_STAGE1) do
if s.find (token, p.pattern, 1, 1) then
s1 = p.effect
break
end
end
for _, p in ipairs(L.PATTERNS_GENERIC_STAGE2) do
if s.find(token, p.pattern, 1, 1) then
s2 = p.effect
break
end
end
if s1 and s2 then
self:AddValue (bonuses, s1..s2, value)
return true
end
end
self:Debug("CheckToken failed for \"%s\" (%d)", token, value)
end
function ItemBonusLib:CheckGeneric(bonuses, line)
local found
while s.len(line) > 0 do
local tmpStr
local pos = s.find (line, "/", 1, true)
if pos then
tmpStr = s.sub (line, 1, pos-1)
line = s.sub (line, pos+1)
else
tmpStr = line
line = ""
end
-- trim line
tmpStr = trim (tmpStr)
local _, _, value, token = s.find(tmpStr, "^%+(%d+)%%?(.*)$")
if not value then
_, _, token, value = s.find(tmpStr, "^(.*)%+(%d+)%%?$")
end
if token and value then
-- trim token
token = trim (token)
if self:CheckToken (bonuses, token, value) then
found = true
end
end
end
return found
end
function ItemBonusLib:CheckOther(bonuses, line)
for _, p in ipairs(L.PATTERNS_OTHER) do
local start, _, value = s.find (line, "^" .. p.pattern)
if start then
if p.value then
self:AddValue(bonuses, p.effect, p.value)
elseif value then
self:AddValue (bonuses, p.effect, value)
end
return true
end
end
end
function ItemBonusLib:AddBonusInfo(bonuses, line, no_prefix)
local found
if no_prefix then
found = self:CheckPassive(bonuses, line)
elseif s.sub (line, 0, l_equip) == equip then
found = self:CheckPassive (bonuses, s.sub(line, l_equip + 2))
end
if not found then
found = self:CheckGeneric(bonuses, line)
if not found then
found = self:CheckOther(bonuses, line)
if not found then
self:Debug("Unmatched bonus line \"%s\"", line)
end
end
end
end
end
do
local ITEM_SET_NAME = ITEM_SET_NAME
local ITEM_SET_BONUS = ITEM_SET_BONUS
local ITEM_SET_BONUS_GRAY = ITEM_SET_BONUS_GRAY
function ItemBonusLib:ScanItemLink(link)
link = cleanItemLink(link)
local info = items[link]
local scan_set
local set_name, set_count, set_total
if not info then
info = { bonuses = {} }
Gratuity:SetHyperlink(link)
for i = 2, Gratuity:NumLines() do
local line = Gratuity:GetLine(i)
set_name, set_count, set_total = Deformat(line, ITEM_SET_NAME)
if set_name then
info.set = set_name
info.set_line = i
local set = sets[set_name]
if not set or set.scan_count > set_count and set.scan_bonuses > 1 then
scan_set = true
end
break
end
self:AddBonusInfo(info.bonuses, line)
end
items[link] = info
elseif info.set then
Gratuity:SetHyperlink(link)
set_name, set_count, set_total = Deformat(Gratuity:GetLine(info.set_line), ITEM_SET_NAME)
local set = sets[set_name]
if set.scan_count > set_count and set.scan_bonuses > 1 then
scan_set = true
end
end
if scan_set then
self:Debug("Scanning set \"%s\"", set_name)
local set = { count = 0, bonuses = {}, scan_count = set_count, scan_bonuses = 0 }
for i = info.set_line + set_total + 2, Gratuity:NumLines() do
local line = Gratuity:GetLine(i)
local count, bonus
local bonus = Deformat(line, ITEM_SET_BONUS)
if bonus then
set.scan_bonuses = set.scan_bonuses + 1
count = set_count
else
count, bonus = Deformat(
line, ITEM_SET_BONUS_GRAY)
end
if not bonus then
self:Debug("Invalid set line \"%s\"", line)
-- break
else
local bonuses = set.bonuses[count] or {}
self:AddBonusInfo(bonuses, bonus, true)
set.bonuses[count] = bonuses
end
end
sets[set_name] = set
end
return info
end
end
function ItemBonusLib:ScanEquipment()
-- clean bonus information
for bonus in pairs(bonuses) do
bonuses[bonus] = nil
end
for bonus, detail in pairs(details) do
for slot in pairs(detail) do
detail[slot] = nil
end
end
for _, set in pairs(sets) do
set.count = 0
end
for slot, id in pairs(slots) do
local link = GetInventoryItemLink("player", id)
if link then
self:Debug("Scanning item %s", link)
local info = self:ScanItemLink(link)
local set = info.set
if set then
sets[set].count = sets[set].count + 1
end
for bonus, value in pairs(info.bonuses) do
bonuses[bonus] = (bonuses[bonus] or 0) + value
if not details[bonus] then
details[bonus] = {}
end
details[bonus][slot] = (details[bonus][slot] or 0) + value
end
end
end
for _, set in pairs(sets) do
for i = 2, set.count do
if set.bonuses[i] then
for bonus, value in pairs(set.bonuses[i]) do
bonuses[bonus] = (bonuses[bonus] or 0) + value
if not details[bonus] then
details[bonus] = {}
end
details[bonus].Set = (details[bonus].Set or 0) + value
end
end
end
end
self:TriggerEvent("ItemBonusLib_Update")
end
-- DEBUG
if DEBUG then
function ItemBonusLib:DumpCachedItems(clear)
DevTools_Dump(items)
if clear then
items = {}
end
end
function ItemBonusLib:DumpCachedSets(clear)
DevTools_Dump(sets)
end
function ItemBonusLib:DumpBonuses()
DevTools_Dump(bonuses)
end
function ItemBonusLib:DumpDetails()
DevTools_Dump(details)
end
function ItemBonusLib:Reload()
items = {}
sets = {}
self:ScanEquipment()
end
end
-- BonusScanner compatible API
function ItemBonusLib:GetBonus(bonus)
return bonuses[bonus] or 0
end
function ItemBonusLib:GetSlotBonuses (slotname)
local bonuses = {}
for bonus, detail in pairs(details) do
if detail[slotname] then
bonuses[bonus] = detail[slotname]
end
end
return bonuses
end
function ItemBonusLib:GetBonusDetails (bonus)
return details[bonus] or {}
end
function ItemBonusLib:GetSlotBonus (bonus, slotname)
local detail = details[bonus]
return detail and detail[slotname] or 0
end
function ItemBonusLib:GetBonusFriendlyName (bonus)
return L.NAMES[bonus] or bonus
end
function ItemBonusLib:IsActive ()
return true
end
function ItemBonusLib:ScanItem (itemlink, excludeSet)
if not excludeSet then
self:error("excludeSet can't be false on BonusScanner compatible API")
end
local name, link = GetItemInfo(itemlink)
if not name then
return
end
return self:ScanItemLink(link).bonuses
end
function ItemBonusLib:ScanTooltipFrame (frame, excludeSet)
self:error("BonusScanner:ScanTooltipFrame() is not available")
end
AceLibrary:Register(ItemBonusLib, MAJOR_VERSION, MINOR_VERSION)