390 lines
11 KiB
Lua
390 lines
11 KiB
Lua
local addonName, ns = ...
|
|
|
|
local Amibis = {}
|
|
ns.Amibis = Amibis
|
|
Amibis.version = "1.0.0"
|
|
|
|
local DEFAULT_AMIBIS_DB = {
|
|
selectedPhase = "T5",
|
|
}
|
|
|
|
local DEFAULT_AMIBIS_CHAR_DB = {
|
|
frameX = nil,
|
|
frameY = nil,
|
|
framePoint = nil,
|
|
frameRelPoint = nil,
|
|
overrideSpec = nil,
|
|
}
|
|
|
|
local function InitializeDB()
|
|
if not AmibisDB then
|
|
AmibisDB = CopyTable(DEFAULT_AMIBIS_DB)
|
|
else
|
|
for k, v in pairs(DEFAULT_AMIBIS_DB) do
|
|
if AmibisDB[k] == nil then
|
|
AmibisDB[k] = v
|
|
end
|
|
end
|
|
end
|
|
|
|
if not AmibisCharDB then
|
|
AmibisCharDB = CopyTable(DEFAULT_AMIBIS_CHAR_DB)
|
|
else
|
|
for k, v in pairs(DEFAULT_AMIBIS_CHAR_DB) do
|
|
if AmibisCharDB[k] == nil then
|
|
AmibisCharDB[k] = v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Amibis:GetPlayerClass()
|
|
return select(2, UnitClass("player"))
|
|
end
|
|
|
|
function Amibis:GetPlayerSpec()
|
|
local class = self:GetPlayerClass()
|
|
local specNames = {
|
|
["PRIEST"] = { "Discipline", "Holy", "Shadow" },
|
|
["MAGE"] = { "Arcane", "Fire", "Frost" },
|
|
["WARLOCK"] = { "Affliction", "Demonology", "Destruction" },
|
|
["DRUID"] = { "Balance", "Feral Combat", "Restoration" },
|
|
["PALADIN"] = { "Holy", "Protection", "Retribution" },
|
|
["SHAMAN"] = { "Elemental", "Enhancement", "Restoration" },
|
|
["WARRIOR"] = { "Arms", "Fury", "Protection" },
|
|
["ROGUE"] = { "Assassination", "Combat", "Subtlety" },
|
|
["HUNTER"] = { "Beast Mastery", "Marksmanship", "Survival" },
|
|
}
|
|
|
|
if not specNames[class] then
|
|
return nil
|
|
end
|
|
|
|
local maxPoints = 0
|
|
local specIndex = 1
|
|
|
|
for i = 1, 3 do
|
|
local r1, r2, r3, r4 = GetTalentTabInfo(i)
|
|
local points = tonumber(r1) or tonumber(r2) or tonumber(r3) or tonumber(r4) or 0
|
|
if points > maxPoints then
|
|
maxPoints = points
|
|
specIndex = i
|
|
end
|
|
end
|
|
|
|
return specNames[class][specIndex]
|
|
end
|
|
|
|
function Amibis:GetEquippedItems()
|
|
local items = {}
|
|
local slotMap = {
|
|
["Head"] = 1,
|
|
["Neck"] = 2,
|
|
["Shoulder"] = 3,
|
|
["Back"] = 15,
|
|
["Chest"] = 5,
|
|
["Wrist"] = 9,
|
|
["Hands"] = 10,
|
|
["Waist"] = 6,
|
|
["Legs"] = 7,
|
|
["Feet"] = 8,
|
|
["Finger0"] = 11,
|
|
["Finger1"] = 12,
|
|
["Trinket0"] = 13,
|
|
["Trinket1"] = 14,
|
|
["MainHand"] = 16,
|
|
["SecondaryHand"] = 17,
|
|
["Ranged"] = 18,
|
|
}
|
|
|
|
for slotName, slotID in pairs(slotMap) do
|
|
local link = GetInventoryItemLink("player", slotID)
|
|
if link then
|
|
local itemID = tonumber(link:match("item:(%d+)"))
|
|
local name = GetItemInfo(itemID) or "Unknown"
|
|
items[slotName] = {
|
|
itemID = itemID,
|
|
name = name,
|
|
link = link,
|
|
slotID = slotID,
|
|
}
|
|
else
|
|
items[slotName] = nil
|
|
end
|
|
end
|
|
|
|
return items
|
|
end
|
|
|
|
local STAT_MAP = {
|
|
["ITEM_MOD_HEALING_DONE_SHORT"] = "healing",
|
|
["ITEM_MOD_SPELL_HEALING_DONE_SHORT"] = "healing",
|
|
["ITEM_MOD_HEALING_DONE"] = "healing",
|
|
["ITEM_MOD_SPELL_HEALING_DONE"] = "healing",
|
|
["ITEM_MOD_DAMAGE_DONE_SHORT"] = "spellDamage",
|
|
["ITEM_MOD_SPELL_DAMAGE_DONE_SHORT"] = "spellDamage",
|
|
["ITEM_MOD_DAMAGE_DONE"] = "spellDamage",
|
|
["ITEM_MOD_SPELL_DAMAGE_DONE"] = "spellDamage",
|
|
["ITEM_MOD_INTELLECT_SHORT"] = "intellect",
|
|
["ITEM_MOD_SPIRIT_SHORT"] = "spirit",
|
|
["ITEM_MOD_STAMINA_SHORT"] = "stamina",
|
|
["ITEM_MOD_MANA_REGENERATION_SHORT"] = "mp5",
|
|
["ITEM_MOD_POWER_REGEN0_SHORT"] = "mp5",
|
|
["ITEM_MOD_HASTE_RATING_SHORT"] = "haste",
|
|
["ITEM_MOD_SPELL_HASTE_RATING_SHORT"] = "haste",
|
|
["ITEM_MOD_SPELL_CRIT_RATING_SHORT"] = "crit",
|
|
["ITEM_MOD_CRIT_RATING_SHORT"] = "crit",
|
|
["ITEM_MOD_SPELL_POWER_SHORT"] = "spellPower",
|
|
["ITEM_MOD_SPELL_DMG_SHORT"] = "spellDamage",
|
|
["ITEM_MOD_HEALING_SHORT"] = "healing",
|
|
}
|
|
|
|
function Amibis:ScanItemStats(itemID)
|
|
if not itemID then return nil end
|
|
|
|
local _, link = GetItemInfo(itemID)
|
|
if not link then
|
|
link = "item:" .. itemID .. ":0:0:0:0:0:0:0:0"
|
|
end
|
|
|
|
local stats = GetItemStats(link)
|
|
if not stats then return nil end
|
|
|
|
local result = {}
|
|
for wowKey, ourKey in pairs(STAT_MAP) do
|
|
if stats[wowKey] then
|
|
result[ourKey] = (result[ourKey] or 0) + stats[wowKey]
|
|
end
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
function Amibis:AggregateStats(items)
|
|
local totals = {
|
|
healing = 0,
|
|
spellDamage = 0,
|
|
intellect = 0,
|
|
spirit = 0,
|
|
stamina = 0,
|
|
mp5 = 0,
|
|
haste = 0,
|
|
crit = 0,
|
|
spellPower = 0,
|
|
}
|
|
|
|
for slotName, item in pairs(items) do
|
|
if item and item.itemID then
|
|
local stats = self:ScanItemStats(item.itemID)
|
|
if stats then
|
|
for k, v in pairs(stats) do
|
|
totals[k] = (totals[k] or 0) + v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return totals
|
|
end
|
|
|
|
function Amibis:CompareGear()
|
|
local class = self:GetPlayerClass()
|
|
local spec = AmibisCharDB.overrideSpec or self:GetPlayerSpec()
|
|
local phase = AmibisDB.selectedPhase or "T5"
|
|
|
|
local bisList = ns.Data.BISLists:GetBISList(class, spec, phase)
|
|
if not bisList then
|
|
return {
|
|
class = class,
|
|
spec = spec,
|
|
phase = phase,
|
|
hasBISList = false,
|
|
slots = {},
|
|
bisCount = 0,
|
|
totalSlots = 0,
|
|
biggestUpgrade = nil,
|
|
}
|
|
end
|
|
|
|
local equipped = self:GetEquippedItems()
|
|
local slots = {}
|
|
local bisCount = 0
|
|
local totalSlots = 0
|
|
local biggestUpgrade = nil
|
|
|
|
for slotName, bisItem in pairs(bisList) do
|
|
totalSlots = totalSlots + 1
|
|
local equippedItem = equipped[slotName]
|
|
local isBIS = false
|
|
|
|
if equippedItem and equippedItem.itemID == bisItem.itemID then
|
|
isBIS = true
|
|
bisCount = bisCount + 1
|
|
end
|
|
|
|
local itemLevelDiff = nil
|
|
if equippedItem and not isBIS then
|
|
local equippedILvl = GetItemInfo(equippedItem.itemID) and select(4, GetItemInfo(equippedItem.itemID))
|
|
local bisILvl = GetItemInfo(bisItem.itemID) and select(4, GetItemInfo(bisItem.itemID))
|
|
if equippedILvl and bisILvl then
|
|
itemLevelDiff = bisILvl - equippedILvl
|
|
end
|
|
end
|
|
|
|
if not isBIS and equippedItem then
|
|
if not biggestUpgrade or (itemLevelDiff and biggestUpgrade.itemLevelDiff and itemLevelDiff > biggestUpgrade.itemLevelDiff) then
|
|
biggestUpgrade = {
|
|
slot = slotName,
|
|
equipped = equippedItem,
|
|
bis = bisItem,
|
|
itemLevelDiff = itemLevelDiff,
|
|
}
|
|
elseif not biggestUpgrade then
|
|
biggestUpgrade = {
|
|
slot = slotName,
|
|
equipped = equippedItem,
|
|
bis = bisItem,
|
|
itemLevelDiff = itemLevelDiff,
|
|
}
|
|
end
|
|
elseif not isBIS and not biggestUpgrade then
|
|
biggestUpgrade = {
|
|
slot = slotName,
|
|
equipped = nil,
|
|
bis = bisItem,
|
|
itemLevelDiff = nil,
|
|
}
|
|
end
|
|
|
|
table.insert(slots, {
|
|
slotName = slotName,
|
|
equipped = equippedItem,
|
|
bis = bisItem,
|
|
isBIS = isBIS,
|
|
itemLevelDiff = itemLevelDiff,
|
|
})
|
|
end
|
|
|
|
table.sort(slots, function(a, b)
|
|
if a.isBIS ~= b.isBIS then
|
|
return b.isBIS
|
|
end
|
|
return a.slotName < b.slotName
|
|
end)
|
|
|
|
local equippedStats = self:AggregateStats(equipped)
|
|
|
|
for slotName, bisItem in pairs(bisList) do
|
|
GetItemInfo(bisItem.itemID)
|
|
end
|
|
|
|
local bisItems = {}
|
|
for slotName, bisItem in pairs(bisList) do
|
|
bisItems[slotName] = { itemID = bisItem.itemID }
|
|
end
|
|
local bisStats = self:AggregateStats(bisItems)
|
|
|
|
local statsComplete = true
|
|
for slotName, bisItem in pairs(bisList) do
|
|
local _, link = GetItemInfo(bisItem.itemID)
|
|
if link then
|
|
local s = GetItemStats(link)
|
|
if not s or not next(s) then
|
|
statsComplete = false
|
|
break
|
|
end
|
|
else
|
|
statsComplete = false
|
|
break
|
|
end
|
|
end
|
|
|
|
return {
|
|
class = class,
|
|
spec = spec,
|
|
phase = phase,
|
|
hasBISList = true,
|
|
slots = slots,
|
|
bisCount = bisCount,
|
|
totalSlots = totalSlots,
|
|
biggestUpgrade = biggestUpgrade,
|
|
equippedStats = equippedStats,
|
|
bisStats = bisStats,
|
|
statsComplete = statsComplete,
|
|
}
|
|
end
|
|
|
|
function Amibis:SaveFramePosition(frame)
|
|
local point, _, relPoint, x, y = frame:GetPoint()
|
|
AmibisCharDB.framePoint = point
|
|
AmibisCharDB.frameRelPoint = relPoint
|
|
AmibisCharDB.frameX = x
|
|
AmibisCharDB.frameY = y
|
|
end
|
|
|
|
function Amibis:LoadFramePosition(frame)
|
|
local point = AmibisCharDB.framePoint
|
|
local relPoint = AmibisCharDB.frameRelPoint
|
|
local x = AmibisCharDB.frameX
|
|
local y = AmibisCharDB.frameY
|
|
|
|
if point and x and y then
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint(point, UIParent, relPoint or point, x, y)
|
|
else
|
|
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
|
end
|
|
end
|
|
|
|
function Amibis:InitializeUI()
|
|
if ns.UI and ns.UI.Initialize then
|
|
ns.UI.Initialize()
|
|
end
|
|
end
|
|
|
|
function Amibis:ToggleFrame()
|
|
if ns.UI and ns.UI.ToggleFrame then
|
|
ns.UI.ToggleFrame()
|
|
end
|
|
end
|
|
|
|
local eventFrame = CreateFrame("Frame")
|
|
eventFrame:RegisterEvent("ADDON_LOADED")
|
|
eventFrame:RegisterEvent("PLAYER_LOGIN")
|
|
eventFrame:RegisterEvent("PLAYER_TALENT_UPDATE")
|
|
|
|
eventFrame:SetScript("OnEvent", function(self, event, ...)
|
|
if event == "ADDON_LOADED" then
|
|
local addon = ...
|
|
if addon == addonName then
|
|
InitializeDB()
|
|
end
|
|
elseif event == "PLAYER_LOGIN" then
|
|
Amibis:InitializeUI()
|
|
elseif event == "PLAYER_TALENT_UPDATE" then
|
|
if ns.UI and ns.UI.RefreshUI then
|
|
ns.UI.RefreshUI()
|
|
end
|
|
end
|
|
end)
|
|
|
|
SLASH_AMIBIS1 = "/amibis"
|
|
SlashCmdList["AMIBIS"] = function(msg)
|
|
local cmd = msg:trim():lower()
|
|
if cmd == "" then
|
|
Amibis:ToggleFrame()
|
|
elseif cmd:find("^spec%s+") then
|
|
local spec = cmd:match("^spec%s+(.+)")
|
|
if spec and spec ~= "" then
|
|
spec = spec:gsub("^%l", string.upper)
|
|
AmibisCharDB.overrideSpec = spec
|
|
print("Amibis: Spec override set to " .. spec .. ". Type /amibis spec clear to reset.")
|
|
end
|
|
elseif cmd == "spec clear" then
|
|
AmibisCharDB.overrideSpec = nil
|
|
print("Amibis: Spec override cleared.")
|
|
else
|
|
Amibis:ToggleFrame()
|
|
end
|
|
end
|