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