adam@17: local _, AskMrRobot = ... adam@17: local L = AskMrRobot.L; adam@17: adam@17: -- initialize the GearComparisonTab class adam@17: AskMrRobot.GearComparisonTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame) adam@17: adam@17: -- stores the results of the last gear comparison adam@17: AskMrRobot.ComparisonResult = { adam@17: items = {}, adam@17: gems = {}, adam@17: enchants = {} adam@17: } adam@17: adam@17: local function createTabButton(tab, text, index) adam@17: local button = CreateFrame("Button", "GearComparisonTab" .. index, tab, "TabButtonTemplate") adam@17: if index == 1 then adam@17: button:SetPoint("TOPLEFT") adam@17: else adam@17: button:SetPoint("LEFT", "GearComparisonTab" .. (index - 1), "RIGHT") adam@17: end adam@17: adam@17: button:SetText(text) adam@17: button:SetWidth(50) adam@17: button:SetHeight(20) adam@17: button:SetID(index) adam@17: button:SetScript("OnClick", AskMrRobot.GearComparisonTab.tabButtonClick) adam@17: return button adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:tabButtonClick() adam@17: local tab = self:GetParent() adam@17: local id = self:GetID() adam@17: PanelTemplates_SetTab(tab, id) adam@17: for i = 1, #tab.tabs do adam@17: local t = tab.tabs[i] adam@17: if t:GetID() == id then adam@17: t:Show() adam@17: else adam@17: t:Hide() adam@17: end adam@17: end adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:new(parent) adam@17: adam@17: local tab = AskMrRobot.Frame:new("GearComparison", parent) adam@17: setmetatable(tab, { __index = AskMrRobot.GearComparisonTab }) adam@17: tab:SetPoint("TOPLEFT") adam@17: tab:SetPoint("BOTTOMRIGHT") adam@17: tab:Hide() adam@17: adam@17: tab.initialized = false adam@17: adam@17: tab.tabButtons = { adam@17: createTabButton(tab, L.AMR_UI_BUTTON_IMPORT, 1), adam@17: createTabButton(tab, L.AMR_UI_BUTTON_SUMMARY, 2), adam@17: createTabButton(tab, L.AMR_UI_BUTTON_GEMS, 3), adam@17: createTabButton(tab, L.AMR_UI_BUTTON_ENCHANTS, 4), adam@17: createTabButton(tab, L.AMR_UI_BUTTON_SHOPPING_LIST, 5) adam@17: } adam@17: adam@17: PanelTemplates_SetNumTabs(tab, 5) adam@17: PanelTemplates_SetTab(tab, 1) adam@17: for i = 1, #tab.tabButtons do adam@17: PanelTemplates_TabResize(tab.tabButtons[i], 0) adam@17: end adam@17: adam@17: PanelTemplates_DisableTab(tab, 2) adam@17: PanelTemplates_DisableTab(tab, 3) adam@17: PanelTemplates_DisableTab(tab, 4) adam@17: PanelTemplates_DisableTab(tab, 5) adam@17: adam@17: -- create the import tab adam@17: tab.importTab = AskMrRobot.ImportTab:new(tab) adam@17: tab.importTab:SetID(1) adam@17: adam@17: -- set the tab left of the tab adam@17: tab.importTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30) adam@17: adam@17: tab.summaryTab = AskMrRobot.SummaryTab:new(tab) adam@17: tab.summaryTab:SetID(2) adam@17: tab.summaryTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30) adam@17: adam@17: tab.gemTab = AskMrRobot.GemTab:new(tab) adam@17: tab.gemTab:SetID(3) adam@17: tab.gemTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30) adam@17: adam@17: tab.enchantTab = AskMrRobot.EnchantTab:new(tab) adam@17: tab.enchantTab:SetID(4) adam@17: tab.enchantTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30) adam@17: adam@17: tab.shoppingTab = AskMrRobot.ShoppingListTab:new(tab) adam@17: tab.shoppingTab:SetID(5) adam@17: tab.shoppingTab:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30) adam@17: adam@17: tab.tabs = {tab.importTab, tab.summaryTab, tab.gemTab, tab.enchantTab, tab.shoppingTab} adam@17: adam@17: -- show the first tab adam@17: tab.importTab:Show() adam@17: adam@17: -- setup the import button to run the import adam@17: tab.importTab.button:SetScript("OnClick", function() adam@17: tab:Import() adam@17: tab.tabButtonClick(tab.tabButtons[2]) adam@17: end) adam@17: adam@17: tab:SetScript("OnShow", AskMrRobot.GearComparisonTab.OnShow) adam@17: adam@17: --tab:RegisterEvent("ITEM_PUSH") adam@17: --tab:RegisterEvent("DELETE_ITEM_CONFIRM") adam@17: tab:RegisterEvent("SOCKET_INFO_CLOSE") adam@17: tab:RegisterEvent("SOCKET_INFO_UPDATE") adam@17: tab:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED") adam@17: tab:RegisterEvent("BAG_UPDATE") adam@17: adam@17: tab:SetScript("OnEvent", AskMrRobot.GearComparisonTab.OnEvent) adam@17: adam@17: return tab adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:On_SOCKET_INFO_CLOSE() adam@17: self:Import() adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:On_SOCKET_INFO_UPDATE() adam@17: self:Import() adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:On_PLAYER_SPECIALIZATION_CHANGED() adam@17: self:Import() adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:On_BAG_UPDATE() adam@17: self:Import() adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:On_ITEM_PUSH() adam@17: self:Import() adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:On_DELETE_ITEM_CONFIRM() adam@17: self:Import() adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:OnShow() adam@17: if not self.initialized then adam@17: self.initialized = true adam@17: adam@17: -- on first show, load the last import adam@17: if AmrDb.LastCharacterImport and AmrDb.LastCharacterImport ~= "" then adam@17: self.importTab:SetImportText(AmrDb.LastCharacterImport) adam@17: self:Import() adam@17: else adam@17: self:Update() adam@17: end adam@17: else adam@17: self:Update() adam@17: end adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:Import() adam@17: adam@17: -- example string adam@17: -- $2;Brill (EU);Yellowfive;Twisted Legion;11;2;90;7:600,9:600;1;s1;34;2123320;68164,47782,7833;q1;99195s7u493x4647y0c22e4823;1s3u0x0y0c11e90;6s10u0x0y0c11e-481;1s1u0x383y-383c41;3047s15u12x0c1e-8;3109s5u-12x0y0z0c112e445;55s8u12x0c1e-442;5371s16u0b450x0c1e18;1691s13u-14b-1;238s11u0b0x-60c3;28s2u0b0;21s6u2b0x60y0z0c130;29s14u0b0;1s9u-2b0e-30;62s12u0b0x0c1;95s17u2b0x0c1$g\4647\76697\76631,76697,83146\20 _CriticalStrike_@g\5030\95344\\Indomitable@g\4587\76636\76570,76636,83144\20 _CriticalStrike_@e\4823\83765\122388\19 _Strength_, 11 _CriticalStrike_\72163=1,76061=1@e\4913\87585\113047\20 _Strength_, 5 _CriticalStrike_\39354=1,79254=3@e\4432\74721\104419\11 _Strength_\74249=3,74250=1,74247=1@e\4424\74713\104404\12 _CriticalStrike_\74250=1@e\4869\85559\124091\9 _Stamina_\72120=4@e\4427\74716\104408\11 _CriticalStrike_\74249=2,74250=1@e\4445\74727\104440\Colossus\74247=3@e\4415\74704\104390\18 _Strength_\74248=3 adam@17: adam@17: local err = AskMrRobot.ImportCharacter(self.importTab:GetImportText()) adam@17: -- goto the summary tab adam@17: self.summaryTab:showImportError(err) adam@17: PanelTemplates_EnableTab(self, 2) adam@17: if err then adam@17: PanelTemplates_DisableTab(self, 3) adam@17: PanelTemplates_DisableTab(self, 4) adam@17: PanelTemplates_DisableTab(self, 5) adam@17: else adam@17: PanelTemplates_EnableTab(self, 3) adam@17: PanelTemplates_EnableTab(self, 4) adam@17: PanelTemplates_EnableTab(self, 5) adam@17: self:Update() adam@17: end adam@17: end adam@17: adam@17: -- update the panel and state adam@17: function AskMrRobot.GearComparisonTab:Update() adam@17: -- update the comparison adam@17: if self.summaryTab then adam@17: AskMrRobot.GearComparisonTab.Compare() adam@17: self.summaryTab:showBadItems() adam@17: self.gemTab:Update() adam@17: self.enchantTab:Update() adam@17: self.shoppingTab:Update() adam@17: end adam@17: end adam@17: adam@17: adam@17: -- Helper for checking for swapped items, returns a number indicating how different two items are (0 means the same, higher means more different) adam@17: local function countItemDifferences(item1, item2) adam@17: if item1 == nil and item2 == nil then return 0 end adam@17: adam@17: -- different items (id + bonus ids + suffix, constitutes a different physical drop) adam@17: if AskMrRobot.getItemUniqueId(item1, true) ~= AskMrRobot.getItemUniqueId(item2, true) then adam@17: if item1 == nil or item2 == nil then return 1000 end adam@17: -- do a check to deal with SoO item variants, see if we have duplicate ID information adam@17: local info = AskMrRobot.ExtraItemData[item2.id] adam@17: if info == nil then adam@17: info = AskMrRobot.ExtraItemData[item1.id] adam@17: if info == nil or info.duplicateId ~= item2.id then adam@17: return 1000 adam@17: end adam@17: elseif info.duplicateId ~= item1.id then adam@17: return 1000 adam@17: end adam@17: end adam@17: adam@17: -- different upgrade levels of the same item (only for older gear, player has control over upgrade level) adam@17: if item1.upgradeId ~= item2.upgradeId then adam@17: return 100 adam@17: end adam@17: adam@17: -- different gems adam@17: local gemDiffs = 0 adam@17: for i = 1, 3 do adam@17: if item1.gemIds[i] ~= item2.gemIds[i] then adam@17: gemDiffs = gemDiffs + 1 adam@17: end adam@17: end adam@17: adam@17: local enchantDiff = 0 adam@17: if item1.enchantId ~= item2.enchantId then adam@17: enchantDiff = 1 adam@17: end adam@17: adam@17: return gemDiffs + enchantDiff adam@17: end adam@17: adam@17: function AskMrRobot.GearComparisonTab:OnEvent(event, ...) adam@17: local handler = self["On_" .. event] adam@17: if handler then adam@17: handler(self, ...) adam@17: end adam@17: end adam@17: adam@17: -- modifies data2 such that differences between data1 and data2 in the two specified slots is the smallest adam@17: local function checkSwappedItems(data1, data2, slot1, slot2) adam@17: local diff = countItemDifferences(data1[slot1], data2[slot1]) + countItemDifferences(data1[slot2], data2[slot2]) adam@17: local swappedDiff adam@17: if diff > 0 then adam@17: swappedDiff = countItemDifferences(data1[slot1], data2[slot2]) + countItemDifferences(data1[slot2], data2[slot1]) adam@17: if swappedDiff < diff then adam@17: local temp = data2[slot1] adam@17: data2[slot1] = data2[slot2] adam@17: data2[slot2] = temp adam@17: end adam@17: end adam@17: end adam@17: adam@17: -- compare the last import data to the player's current state adam@17: function AskMrRobot.GearComparisonTab.Compare() adam@17: if not AskMrRobot.ImportData then return end adam@17: adam@17: AskMrRobot.SaveAll() adam@17: adam@17: -- first parse the player's equipped gear into item objects adam@17: local equipped = {} adam@17: for k, v in pairs(AmrDb.Equipped[AmrDb.ActiveSpec]) do adam@17: equipped[k] = AskMrRobot.parseItemLink(v) adam@17: end adam@17: adam@17: -- swap finger/trinket in AskMrRobot.ImportData such that the number of differences is the smallest adam@17: checkSwappedItems(equipped, AskMrRobot.ImportData, INVSLOT_FINGER1, INVSLOT_FINGER2) adam@17: checkSwappedItems(equipped, AskMrRobot.ImportData, INVSLOT_TRINKET1, INVSLOT_TRINKET2) adam@17: adam@17: -- clear previous comparison result adam@17: AskMrRobot.ComparisonResult = { adam@17: items = {}, adam@17: gems = {}, adam@17: enchants = {} adam@17: } adam@17: adam@17: local result = { adam@17: items = {}, adam@17: gems = {}, adam@17: enchants = {} adam@17: } adam@17: adam@17: -- determine specific differences adam@17: for i,slotId in ipairs(AskMrRobot.slotIds) do adam@17: local itemEquipped = equipped[slotId] adam@17: local itemImported = AskMrRobot.ImportData[slotId] adam@17: adam@17: local itemsDifferent = AskMrRobot.getItemUniqueId(itemEquipped) ~= AskMrRobot.getItemUniqueId(itemImported) adam@17: if itemsDifferent and itemEquipped ~= nil and itemImported ~= nil then adam@17: -- do an extra check for old versions of SoO items, our server code always converts to new, equivalent version, but need to check backwards for the addon adam@17: local info = AskMrRobot.ExtraItemData[itemImported.id] adam@17: if info and info.duplicateId == itemEquipped.id then adam@17: itemsDifferent = false adam@17: end adam@17: end adam@17: adam@17: if itemsDifferent then adam@17: -- the items are different adam@17: local needsUpgrade = false adam@17: if itemEquipped and itemImported and itemEquipped.id == itemImported.id and itemImported.upgradeId > itemEquipped.upgradeId then adam@17: needsUpgrade = true adam@17: end adam@17: result.items[slotId] = { adam@17: current = itemEquipped, adam@17: optimized = itemImported, adam@17: needsUpgrade = needsUpgrade adam@17: } adam@17: elseif itemEquipped then adam@17: if AskMrRobot.ExtraItemData[itemEquipped.id] and AskMrRobot.ExtraItemData[itemEquipped.id].socketColors then adam@17: -- items are same, check for gem/enchant differences adam@17: -- NOTE: we used to do a bunch of fancy gem checks, but we can ditch all that logic b/c WoD gems are much simpler (no socket bonuses, gem/socket colors to worry about) adam@17: local hasBadGems = false adam@17: for g = 1, #AskMrRobot.ExtraItemData[itemEquipped.id].socketColors do adam@17: if not AskMrRobot.AreGemsCompatible(itemEquipped.gemIds[g], itemImported.gemIds[g]) then adam@17: hasBadGems = true adam@17: break adam@17: end adam@17: end adam@17: adam@17: if hasBadGems then adam@17: result.gems[slotId] = { adam@17: current = {}, adam@17: optimized = {} adam@17: } adam@17: adam@17: for g = 1, #AskMrRobot.ExtraItemData[itemEquipped.id].socketColors do adam@17: result.gems[slotId].current[g] = itemEquipped.gemIds[g] adam@17: result.gems[slotId].optimized[g] = itemImported.gemIds[g] adam@17: end adam@17: end adam@17: end adam@17: adam@17: if itemEquipped.enchantId ~= itemImported.enchantId then adam@17: result.enchants[slotId] = { adam@17: current = itemEquipped.enchantId, adam@17: optimized = itemImported.enchantId adam@17: } adam@17: end adam@17: end adam@17: end adam@17: adam@17: -- only set the new result if it is completely successful adam@17: AskMrRobot.ComparisonResult = result adam@17: end adam@17: adam@17: -- checks our extra gem information to see if the two gems are functionally equivalent adam@17: function AskMrRobot.AreGemsCompatible(gemId1, gemId2) adam@17: if gemId1 == gemId2 then return true end adam@17: adam@17: -- see if we have extra gem information adam@17: local extraInfo = AskMrRobot.ExtraGemData[gemId1] adam@17: if not extraInfo then adam@17: extraInfo = AskMrRobot.ExtraGemData[gemId2] adam@17: end adam@17: if extraInfo == nil or extraInfo.identicalGroup == nil then return false end adam@17: adam@17: -- if identicalGroup contains both gem ids, they are equivalent adam@17: return extraInfo.identicalGroup[gemId1] and extraInfo.identicalGroup[gemId2] adam@17: end