Mercurial > wow > askmrrobot
comparison Gear.lua @ 124:e31b02b24488
Updated for 8.0 pre-patch and BfA.
author | yellowfive |
---|---|
date | Tue, 17 Jul 2018 09:57:39 -0700 |
parents | f1da233629be |
children | d9a059484b22 |
comparison
equal
deleted
inserted
replaced
123:7a6364917f86 | 124:e31b02b24488 |
---|---|
5 local _gearTabs | 5 local _gearTabs |
6 local _activeTab | 6 local _activeTab |
7 | 7 |
8 -- Returns a number indicating how different two items are (0 means the same, higher means more different) | 8 -- Returns a number indicating how different two items are (0 means the same, higher means more different) |
9 local function countItemDifferences(item1, item2) | 9 local function countItemDifferences(item1, item2) |
10 if item1 == nil and item2 == nil then return 0 end | 10 -- both nil, the same |
11 | 11 if not item1 and not item2 then |
12 -- different items (id + bonus ids + suffix, constitutes a different physical drop) | 12 return 0 |
13 end | |
14 | |
15 -- one nil and other not, or different id, totally different | |
16 if (not item1 and item2) or (item1 and not item2) or item1.id ~= item2.id then | |
17 return 10000 | |
18 end | |
19 | |
20 -- different versions of same item (id + bonus ids + suffix + drop level, constitutes a different physical drop) | |
13 if Amr.GetItemUniqueId(item1, true) ~= Amr.GetItemUniqueId(item2, true) then | 21 if Amr.GetItemUniqueId(item1, true) ~= Amr.GetItemUniqueId(item2, true) then |
14 return 1000 | 22 return 1000 |
15 end | 23 end |
16 | 24 |
17 -- different upgrade levels of the same item | 25 -- different upgrade levels of the same item |
18 if item1.upgradeId ~= item2.upgradeId then | 26 if item1.upgradeId ~= item2.upgradeId then |
19 return 100 | 27 return 100 |
20 end | 28 end |
29 | |
30 -- different azerite powers | |
31 local aztDiffs = 0 | |
32 if item1.azerite or item2.azerite then | |
33 if item1.azerite and not item2.azerite then | |
34 aztDiffs = #item1.azerite * 10 | |
35 elseif item2.azerite and not item1.azerite then | |
36 aztDiffs = #item2.azerite * 10 | |
37 else | |
38 -- count up number in item1 but missing from item2 | |
39 for i = 1, #item1.azerite do | |
40 local missing = false | |
41 for j = 1, #item2.azerite do | |
42 if item2[j] == item1[i] then | |
43 missing = false | |
44 end | |
45 end | |
46 if missing then | |
47 aztDiffs = aztDiffs + 10 | |
48 end | |
49 end | |
50 -- count up number in item2 but missing from item1 | |
51 for i = 1, #item2.azerite do | |
52 local missing = false | |
53 for j = 1, #item1.azerite do | |
54 if item1[j] == item2[i] then | |
55 missing = false | |
56 end | |
57 end | |
58 if missing then | |
59 aztDiffs = aztDiffs + 10 | |
60 end | |
61 end | |
62 end | |
63 end | |
21 | 64 |
22 -- different gems | 65 -- different gems |
23 local gemDiffs = 0 | 66 local gemDiffs = 0 |
24 for i = 1, 3 do | 67 for i = 1, 3 do |
25 if item1.gemIds[i] ~= item2.gemIds[i] then | 68 if item1.gemIds[i] ~= item2.gemIds[i] then |
31 local enchantDiff = 0 | 74 local enchantDiff = 0 |
32 if item1.enchantId ~= item2.enchantId then | 75 if item1.enchantId ~= item2.enchantId then |
33 enchantDiff = 1 | 76 enchantDiff = 1 |
34 end | 77 end |
35 | 78 |
36 return gemDiffs + enchantDiff | 79 return aztDiffs + gemDiffs + enchantDiff |
37 end | 80 end |
38 | 81 |
39 -- given a table of items (keyed or indexed doesn't matter) find closest match to item, or nil if none are a match | 82 -- given a table of items (keyed or indexed doesn't matter) find closest match to item, or nil if none are a match |
40 local function findMatchingItemFromTable(item, list, bestLink, bestItem, bestDiff, bestLoc, usedItems, tableType) | 83 local function findMatchingItemFromTable(item, list, bestItem, bestDiff, bestLoc, usedItems, tableType) |
41 if not list then return nil end | 84 if not list then return nil end |
42 | 85 |
43 local found = false | 86 local found = false |
44 for k,v in pairs(list) do | 87 for k,listItem in pairs(list) do |
45 local listItem = Amr.ParseItemLink(v) | |
46 if listItem then | 88 if listItem then |
47 local diff = countItemDifferences(item, listItem) | 89 local diff = countItemDifferences(item, listItem) |
48 if diff < bestDiff then | 90 if diff < bestDiff then |
49 -- each physical item can only be used once, the usedItems table has items we can't use in this search | 91 -- each physical item can only be used once, the usedItems table has items we can't use in this search |
50 local key = string.format("%s_%s", tableType, k) | 92 local key = string.format("%s_%s", tableType, k) |
51 if not usedItems[key] then | 93 if not usedItems[key] then |
52 bestLink = v | |
53 bestItem = listItem | 94 bestItem = listItem |
54 bestDiff = diff | 95 bestDiff = diff |
55 bestLoc = string.format("%s_%s", tableType, k) | 96 bestLoc = key |
56 found = true | 97 found = true |
57 end | 98 end |
58 end | 99 end |
59 if found then break end | 100 if found then break end |
60 end | 101 end |
61 end | 102 end |
62 | 103 |
63 return bestLink, bestItem, bestDiff, bestLoc | 104 return bestItem, bestDiff, bestLoc |
64 end | 105 end |
65 | 106 |
66 -- search the player's equipped gear, bag, bank, and void storage for an item that best matches the specified item | 107 -- search the player's equipped gear, bag, and bank for an item that best matches the specified item |
67 function Amr:FindMatchingItem(item, player, usedItems) | 108 function Amr:FindMatchingItem(item, player, usedItems) |
68 if not item then return nil end | 109 if not item then return nil end |
69 | 110 |
70 local equipped = player.Equipped and player.Equipped[player.ActiveSpec] or nil | 111 local equipped = player.Equipped and player.Equipped[player.ActiveSpec] or nil |
71 local bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, equipped, nil, nil, 1000, nil, usedItems, "equip") | 112 local bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, equipped, nil, 10000, nil, usedItems, "equip") |
72 bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BagItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "bag") | 113 bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BagItems, bestItem, bestDiff, bestLoc, usedItems, "bag") |
73 bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BankItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "bank") | 114 if player.BankItems then |
74 bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.VoidItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "void") | 115 for bagId,bagList in pairs(player.BankItems) do |
75 | 116 bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, bagList, bestItem, bestDiff, bestLoc, usedItems, "bank" .. bagId) |
76 if bestDiff >= 1000 then | 117 end |
77 return nil, nil, 1000 | 118 end |
119 | |
120 if bestDiff >= 10000 then | |
121 return nil, 10000 | |
78 else | 122 else |
79 usedItems[bestLoc] = true | 123 usedItems[bestLoc] = true |
80 return bestLink, bestItem, bestDiff | 124 return bestItem, bestDiff |
81 end | 125 end |
82 end | 126 end |
83 | 127 |
84 local function renderEmptyGear(container) | 128 local function renderEmptyGear(container) |
85 | 129 |
86 local panelBlank = AceGUI:Create("AmrUiPanel") | 130 local panelBlank = AceGUI:Create("AmrUiPanel") |
87 panelBlank:SetLayout("None") | 131 panelBlank:SetLayout("None") |
88 panelBlank:SetBackgroundColor(Amr.Colors.Black, 0.4) | 132 panelBlank:SetBackgroundColor(Amr.Colors.Black, 0.4) |
133 container:AddChild(panelBlank) | |
89 panelBlank:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0) | 134 panelBlank:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0) |
90 panelBlank:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") | 135 panelBlank:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") |
91 container:AddChild(panelBlank) | |
92 | 136 |
93 local lbl = AceGUI:Create("AmrUiLabel") | 137 local lbl = AceGUI:Create("AmrUiLabel") |
138 panelBlank:AddChild(lbl) | |
94 lbl:SetText(L.GearBlank) | 139 lbl:SetText(L.GearBlank) |
95 lbl:SetWidth(700) | 140 lbl:SetWidth(700) |
96 lbl:SetJustifyH("MIDDLE") | 141 lbl:SetJustifyH("MIDDLE") |
97 lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) | 142 lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) |
98 lbl:SetPoint("BOTTOM", panelBlank.content, "CENTER", 0, 20) | 143 lbl:SetPoint("BOTTOM", panelBlank.content, "CENTER", 0, 20) |
99 panelBlank:AddChild(lbl) | |
100 | 144 |
101 local lbl2 = AceGUI:Create("AmrUiLabel") | 145 local lbl2 = AceGUI:Create("AmrUiLabel") |
146 panelBlank:AddChild(lbl2) | |
102 lbl2:SetText(L.GearBlank2) | 147 lbl2:SetText(L.GearBlank2) |
103 lbl2:SetWidth(700) | 148 lbl2:SetWidth(700) |
104 lbl2:SetJustifyH("MIDDLE") | 149 lbl2:SetJustifyH("MIDDLE") |
105 lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) | 150 lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) |
106 lbl2:SetPoint("TOP", lbl.frame, "CENTER", 0, -20) | 151 lbl2:SetPoint("TOP", lbl.frame, "CENTER", 0, -20) |
107 panelBlank:AddChild(lbl2) | 152 end |
153 | |
154 -- helper to create a widget for showing a socket or azerite power | |
155 local function createSocketWidget(panelMods, prevWidget, prevIsSocket, isEquipped) | |
156 | |
157 -- highlight for socket that doesn't match | |
158 local socketBorder = AceGUI:Create("AmrUiPanel") | |
159 panelMods:AddChild(socketBorder) | |
160 if not prevIsSocket then | |
161 socketBorder:SetPoint("LEFT", prevWidget.frame, "RIGHT", 30, 0) | |
162 else | |
163 socketBorder:SetPoint("LEFT", prevWidget.frame, "RIGHT", 2, 0) | |
164 end | |
165 socketBorder:SetLayout("None") | |
166 socketBorder:SetBackgroundColor(Amr.Colors.Black, isEquipped and 0 or 1) | |
167 socketBorder:SetWidth(26) | |
168 socketBorder:SetHeight(26) | |
169 if isEquipped then | |
170 socketBorder:SetAlpha(0.3) | |
171 end | |
172 | |
173 local socketBg = AceGUI:Create("AmrUiIcon") | |
174 socketBorder:AddChild(socketBg) | |
175 socketBg:SetPoint("TOPLEFT", socketBorder.content, "TOPLEFT", 1, -1) | |
176 socketBg:SetLayout("None") | |
177 socketBg:SetBorderWidth(2) | |
178 socketBg:SetIconBorderColor(Amr.Colors.Green, isEquipped and 0 or 1) | |
179 socketBg:SetWidth(24) | |
180 socketBg:SetHeight(24) | |
181 | |
182 local socketIcon = AceGUI:Create("AmrUiIcon") | |
183 socketBg:AddChild(socketIcon) | |
184 socketIcon:SetPoint("CENTER", socketBg.content, "CENTER") | |
185 socketIcon:SetBorderWidth(1) | |
186 socketIcon:SetIconBorderColor(Amr.Colors.White) | |
187 socketIcon:SetWidth(18) | |
188 socketIcon:SetHeight(18) | |
189 | |
190 return socketBorder, socketIcon | |
108 end | 191 end |
109 | 192 |
110 local function renderGear(spec, container) | 193 local function renderGear(spec, container) |
111 | 194 |
112 local player = Amr:ExportCharacter() | 195 local player = Amr:ExportCharacter() |
118 renderEmptyGear(container) | 201 renderEmptyGear(container) |
119 else | 202 else |
120 local panelGear = AceGUI:Create("AmrUiPanel") | 203 local panelGear = AceGUI:Create("AmrUiPanel") |
121 panelGear:SetLayout("None") | 204 panelGear:SetLayout("None") |
122 panelGear:SetBackgroundColor(Amr.Colors.Black, 0.3) | 205 panelGear:SetBackgroundColor(Amr.Colors.Black, 0.3) |
206 container:AddChild(panelGear) | |
123 panelGear:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0) | 207 panelGear:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0) |
124 panelGear:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT", -300, 0) | 208 panelGear:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT", -300, 0) |
125 container:AddChild(panelGear) | |
126 | 209 |
127 local panelMods = AceGUI:Create("AmrUiPanel") | 210 local panelMods = AceGUI:Create("AmrUiPanel") |
128 panelMods:SetLayout("None") | 211 panelMods:SetLayout("None") |
212 panelMods:SetBackgroundColor(Amr.Colors.Black, 0.3) | |
213 container:AddChild(panelMods) | |
129 panelMods:SetPoint("TOPLEFT", panelGear.frame, "TOPRIGHT", 15, 0) | 214 panelMods:SetPoint("TOPLEFT", panelGear.frame, "TOPRIGHT", 15, 0) |
130 panelMods:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") | 215 panelMods:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") |
131 panelMods:SetBackgroundColor(Amr.Colors.Black, 0.3) | |
132 container:AddChild(panelMods) | |
133 | 216 |
134 -- spec icon | 217 -- spec icon |
135 local icon = AceGUI:Create("AmrUiIcon") | 218 local icon = AceGUI:Create("AmrUiIcon") |
136 icon:SetIconBorderColor(Amr.Colors.Classes[player.Class]) | 219 icon:SetIconBorderColor(Amr.Colors.Classes[player.Class]) |
137 icon:SetWidth(48) | 220 icon:SetWidth(48) |
143 else | 226 else |
144 iconSpec = player.Specs[spec] | 227 iconSpec = player.Specs[spec] |
145 end | 228 end |
146 | 229 |
147 icon:SetIcon("Interface\\Icons\\" .. Amr.SpecIcons[iconSpec]) | 230 icon:SetIcon("Interface\\Icons\\" .. Amr.SpecIcons[iconSpec]) |
231 panelGear:AddChild(icon) | |
148 icon:SetPoint("TOPLEFT", panelGear.content, "TOPLEFT", 10, -10) | 232 icon:SetPoint("TOPLEFT", panelGear.content, "TOPLEFT", 10, -10) |
149 panelGear:AddChild(icon) | |
150 | 233 |
151 local btnEquip = AceGUI:Create("AmrUiButton") | 234 local btnEquip = AceGUI:Create("AmrUiButton") |
152 btnEquip:SetText(L.GearButtonEquip(L.SpecsShort[player.Specs[spec]])) | 235 btnEquip:SetText(L.GearButtonEquip(L.SpecsShort[player.Specs[spec]])) |
153 btnEquip:SetBackgroundColor(Amr.Colors.Green) | 236 btnEquip:SetBackgroundColor(Amr.Colors.Green) |
154 btnEquip:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) | 237 btnEquip:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) |
155 btnEquip:SetWidth(300) | 238 btnEquip:SetWidth(300) |
156 btnEquip:SetHeight(26) | 239 btnEquip:SetHeight(26) |
157 btnEquip:SetPoint("LEFT", icon.frame, "RIGHT", 40, 0) | |
158 btnEquip:SetPoint("RIGHT", panelGear.content, "RIGHT", -40, 0) | |
159 btnEquip:SetCallback("OnClick", function(widget) | 240 btnEquip:SetCallback("OnClick", function(widget) |
160 Amr:EquipGearSet(spec) | 241 Amr:EquipGearSet(spec) |
161 end) | 242 end) |
162 panelGear:AddChild(btnEquip) | 243 panelGear:AddChild(btnEquip) |
244 btnEquip:SetPoint("LEFT", icon.frame, "RIGHT", 40, 0) | |
245 btnEquip:SetPoint("RIGHT", panelGear.content, "RIGHT", -40, 0) | |
163 | 246 |
164 -- each physical item can only be used once, this tracks ones we have already used | 247 -- each physical item can only be used once, this tracks ones we have already used |
165 local usedItems = {} | 248 local usedItems = {} |
166 | 249 |
167 -- gear list | 250 -- gear list |
168 local prevElem = icon | 251 local prevElem = icon |
169 for slotNum = 1, #Amr.SlotIds do | 252 for slotNum = 1, #Amr.SlotIds do |
170 local slotId = Amr.SlotIds[slotNum] | 253 local slotId = Amr.SlotIds[slotNum] |
171 | 254 |
172 local equippedItemLink = equipped and equipped[slotId] or nil | 255 local equippedItem = equipped and equipped[slotId] or nil |
173 local equippedItem = Amr.ParseItemLink(equippedItemLink) | 256 local equippedItemLink = equipped and equipped.link or nil |
174 local optimalItem = gear[slotId] | 257 local optimalItem = gear[slotId] |
175 local optimalItemLink = Amr.CreateItemLink(optimalItem) | 258 local optimalItemLink = Amr.CreateItemLink(optimalItem) |
176 | 259 |
177 -- see if item is currently equipped, is false if don't have any item for that slot (e.g. OH for a 2-hander) | 260 -- see if item is currently equipped, is false if don't have any item for that slot (e.g. OH for a 2-hander) |
178 local isEquipped = false | 261 local isEquipped = false |
179 if equippedItem and optimalItem and Amr.GetItemUniqueId(equippedItem) == Amr.GetItemUniqueId(optimalItem) then | 262 if equippedItem and optimalItem and Amr.GetItemUniqueId(equippedItem) == Amr.GetItemUniqueId(optimalItem) then |
180 isEquipped = true | 263 isEquipped = true |
181 end | 264 end |
265 | |
266 local isAzerite = optimalItem and C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItemByID(optimalItem.id) | |
182 | 267 |
183 -- find the item in the player's inventory that best matches what the optimization wants to use | 268 -- find the item in the player's inventory that best matches what the optimization wants to use |
184 local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) | 269 local matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) |
185 | 270 |
186 -- slot label | 271 -- slot label |
187 local lbl = AceGUI:Create("AmrUiLabel") | 272 local lbl = AceGUI:Create("AmrUiLabel") |
273 panelGear:AddChild(lbl) | |
274 lbl:SetPoint("TOPLEFT", prevElem.frame, "BOTTOMLEFT", 0, -12) | |
188 lbl:SetText(Amr.SlotDisplayText[slotId]) | 275 lbl:SetText(Amr.SlotDisplayText[slotId]) |
189 lbl:SetWidth(85) | 276 lbl:SetWidth(85) |
190 lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) | 277 lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) |
191 lbl:SetPoint("TOPLEFT", prevElem.frame, "BOTTOMLEFT", 0, -12) | |
192 panelGear:AddChild(lbl) | |
193 prevElem = lbl | 278 prevElem = lbl |
194 | 279 |
195 -- ilvl label | 280 -- ilvl label |
196 local lblIlvl = AceGUI:Create("AmrUiLabel") | 281 local lblIlvl = AceGUI:Create("AmrUiLabel") |
282 panelGear:AddChild(lblIlvl) | |
283 lblIlvl:SetPoint("TOPLEFT", lbl.frame, "TOPRIGHT", 0, 0) | |
197 lblIlvl:SetWidth(45) | 284 lblIlvl:SetWidth(45) |
198 lblIlvl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) | 285 lblIlvl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) |
199 lblIlvl:SetPoint("TOPLEFT", lbl.frame, "TOPRIGHT", 0, 0) | |
200 panelGear:AddChild(lblIlvl) | |
201 | 286 |
202 -- equipped label | 287 -- equipped label |
203 local lblEquipped = AceGUI:Create("AmrUiLabel") | 288 local lblEquipped = AceGUI:Create("AmrUiLabel") |
289 panelGear:AddChild(lblEquipped) | |
290 lblEquipped:SetPoint("TOPLEFT", lblIlvl.frame, "TOPRIGHT", 0, 0) | |
204 lblEquipped:SetWidth(20) | 291 lblEquipped:SetWidth(20) |
205 lblEquipped:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) | 292 lblEquipped:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) |
206 lblEquipped:SetPoint("TOPLEFT", lblIlvl.frame, "TOPRIGHT", 0, 0) | |
207 lblEquipped:SetText(isEquipped and "E" or "") | 293 lblEquipped:SetText(isEquipped and "E" or "") |
208 panelGear:AddChild(lblEquipped) | |
209 | 294 |
210 -- item name/link label | 295 -- item name/link label |
211 local lblItem = AceGUI:Create("AmrUiLabel") | 296 local lblItem = AceGUI:Create("AmrUiLabel") |
297 panelGear:AddChild(lblItem) | |
298 lblItem:SetPoint("TOPLEFT", lblEquipped.frame, "TOPRIGHT", 0, 0) | |
212 lblItem:SetWordWrap(false) | 299 lblItem:SetWordWrap(false) |
213 lblItem:SetWidth(345) | 300 lblItem:SetWidth(345) |
214 lblItem:SetFont(Amr.CreateFont(isEquipped and "Regular" or "Bold", isEquipped and 14 or 15, Amr.Colors.White)) | 301 lblItem:SetFont(Amr.CreateFont(isEquipped and "Regular" or "Bold", isEquipped and 14 or 15, Amr.Colors.White)) |
215 lblItem:SetPoint("TOPLEFT", lblEquipped.frame, "TOPRIGHT", 0, 0) | |
216 panelGear:AddChild(lblItem) | |
217 | 302 |
218 -- fill the name/ilvl labels, which may require asynchronous loading of item information | 303 -- fill the name/ilvl labels, which may require asynchronous loading of item information |
219 if optimalItemLink then | 304 if optimalItemLink then |
220 Amr.GetItemInfo(optimalItemLink, function(obj, name, link, quality, iLevel) | 305 Amr.GetItemInfo(optimalItemLink, function(obj, name, link, quality, iLevel) |
221 -- set item name, tooltip, and ilvl | 306 -- set item name, tooltip, and ilvl |
222 obj.nameLabel:SetText(link:gsub("%[", ""):gsub("%]", "")) | 307 obj.nameLabel:SetText(link:gsub("%[", ""):gsub("%]", "")) |
223 | 308 |
224 -- not quite right but whatever... close enough | |
225 if quality == 6 then | 309 if quality == 6 then |
226 local tmprel = optimalItem.relicBonusIds | 310 -- not quite right but whatever... close enough, artifacts are a thing of the past |
227 optimalItem.relicBonusIds = nil | 311 local tmprel = obj.optimalItem.relicBonusIds |
228 link = Amr.CreateItemLink(optimalItem) | 312 obj.optimalItem.relicBonusIds = nil |
229 optimalItem.relicBonusIds = tmprel | 313 link = Amr.CreateItemLink(obj.optimalItem) |
314 obj.optimalItem.relicBonusIds = tmprel | |
315 | |
316 -- for artifacts, we consider it equipped if the item id alone matches | |
317 if obj.equippedItem and obj.equippedItem.id == obj.optimalItem.id then | |
318 obj.isEquipped = true | |
319 end | |
320 obj.equipLabel:SetText(obj.isEquipped and "E" or "") | |
230 end | 321 end |
231 | 322 |
232 Amr:SetItemTooltip(obj.nameLabel, link) | 323 Amr:SetItemTooltip(obj.nameLabel, link, "ANCHOR_TOPRIGHT") |
233 | 324 |
234 -- the game's info gives the wrong item level, so we have to scan for it | 325 local itemObj = Item:CreateFromItemLink(link) |
235 --iLevel = (quality ~= 6 or optimalItem.relicBonusIds) and Amr.GetItemLevel(nil, nil, link) or "" | 326 if itemObj then |
236 obj.ilvlLabel:SetText(iLevel) | 327 -- game's GetItemInfo method returns the wrong ilvl sometimes, so use the new item api to get it |
237 | 328 iLevel = itemObj:GetCurrentItemLevel() |
238 end, { ilvlLabel = lblIlvl, nameLabel = lblItem }) | 329 end |
330 obj.ilvlLabel:SetText(iLevel) | |
331 | |
332 end, { | |
333 ilvlLabel = lblIlvl, | |
334 nameLabel = lblItem, | |
335 equipLabel = lblEquipped, | |
336 optimalItem = optimalItem, | |
337 equippedItem = equippedItem, | |
338 isEquipped = isEquipped | |
339 }) | |
239 end | 340 end |
240 | 341 |
241 -- modifications | 342 -- modifications |
242 if optimalItem then | 343 if optimalItem then |
243 local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id] | 344 |
244 | 345 -- gems or azerite powers |
245 -- gems | 346 local prevSocket = nil |
246 if itemInfo and itemInfo.socketColors then | 347 |
247 local prevSocket = nil | 348 if isAzerite then |
248 for i = 1, #itemInfo.socketColors do | 349 local azt = optimalItem.azerite or {} |
350 for i,spellId in ipairs(azt) do | |
351 if spellId and spellId ~= 0 then | |
352 local equippedAzt = equippedItem and equippedItem.azerite or {} | |
353 local isPowerActive = Amr.Contains(equippedAzt, spellId) | |
354 | |
355 local socketBorder, socketIcon = createSocketWidget(panelMods, prevSocket or lblItem, prevSocket, isPowerActive) | |
356 | |
357 -- set icon and tooltip | |
358 local spellName, _, spellIcon = GetSpellInfo(spellId) | |
359 socketIcon:SetIcon(spellIcon) | |
360 Amr:SetSpellTooltip(socketIcon, spellId, "ANCHOR_TOPRIGHT") | |
361 | |
362 prevSocket = socketBorder | |
363 end | |
364 end | |
365 else | |
366 for i = 1, #optimalItem.gemIds do | |
367 -- we rely on the fact that the gear sets coming back from the site will almost always have all sockets filled, | |
368 -- because it's a pain to get the actual number of sockets on an item from within the game | |
249 local g = optimalItem.gemIds[i] | 369 local g = optimalItem.gemIds[i] |
250 local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g | 370 if g == 0 then break end |
371 | |
372 local isGemEquipped = matchItem and matchItem.gemIds and matchItem.gemIds[i] == g | |
251 | 373 |
252 -- highlight for gem that doesn't match | 374 local socketBorder, socketIcon = createSocketWidget(panelMods, prevSocket or lblItem, prevSocket, isGemEquipped) |
253 local socketBorder = AceGUI:Create("AmrUiPanel") | |
254 socketBorder:SetLayout("None") | |
255 socketBorder:SetBackgroundColor(Amr.Colors.Black, isGemEquipped and 0 or 1) | |
256 socketBorder:SetWidth(26) | |
257 socketBorder:SetHeight(26) | |
258 if not prevSocket then | |
259 socketBorder:SetPoint("LEFT", lblItem.frame, "RIGHT", 30, 0) | |
260 else | |
261 socketBorder:SetPoint("LEFT", prevSocket.frame, "RIGHT", 2, 0) | |
262 end | |
263 if isGemEquipped then | |
264 socketBorder:SetAlpha(0.3) | |
265 end | |
266 panelMods:AddChild(socketBorder) | |
267 | |
268 local socketBg = AceGUI:Create("AmrUiIcon") | |
269 socketBg:SetLayout("None") | |
270 socketBg:SetBorderWidth(2) | |
271 socketBg:SetIconBorderColor(Amr.Colors.Green, isGemEquipped and 0 or 1) | |
272 socketBg:SetWidth(24) | |
273 socketBg:SetHeight(24) | |
274 socketBg:SetPoint("TOPLEFT", socketBorder.content, "TOPLEFT", 1, -1) | |
275 socketBorder:AddChild(socketBg) | |
276 | |
277 local socketIcon = AceGUI:Create("AmrUiIcon") | |
278 socketIcon:SetBorderWidth(1) | |
279 socketIcon:SetIconBorderColor(Amr.Colors.White) | |
280 socketIcon:SetWidth(18) | |
281 socketIcon:SetHeight(18) | |
282 socketIcon:SetPoint("CENTER", socketBg.content, "CENTER") | |
283 socketBg:AddChild(socketIcon) | |
284 | 375 |
285 -- get icon for optimized gem | 376 -- get icon for optimized gem |
286 if g ~= 0 then | 377 Amr.GetItemInfo(g, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) |
287 local gemInfo = Amr.db.char.ExtraGemData[spec][g] | 378 -- set icon and a tooltip |
288 if gemInfo then | 379 obj:SetIcon(texture) |
289 local gident = gemInfo.id | 380 Amr:SetItemTooltip(obj, link, "ANCHOR_TOPRIGHT") |
290 if optimalItem.relicBonusIds then | 381 end, socketIcon) |
291 gident = Amr.CreateItemLink({ id = gemInfo.id, enchantId = 0, gemIds = {0,0,0,0}, suffixId = 0, bonusIds = optimalItem.relicBonusIds[i]}) | |
292 end | |
293 Amr.GetItemInfo(gident, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) | |
294 -- set icon and a tooltip | |
295 obj:SetIcon(texture) | |
296 Amr:SetItemTooltip(obj, link) | |
297 end, socketIcon) | |
298 end | |
299 end | |
300 | 382 |
301 prevSocket = socketBorder | 383 prevSocket = socketBorder |
302 end | 384 end |
303 end | 385 end |
304 | 386 |
305 -- enchant | 387 -- enchant |
306 if optimalItem.enchantId and optimalItem.enchantId ~= 0 then | 388 if optimalItem.enchantId and optimalItem.enchantId ~= 0 then |
307 local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == optimalItem.enchantId | 389 local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == optimalItem.enchantId |
308 | 390 |
309 local lblEnchant = AceGUI:Create("AmrUiLabel") | 391 local lblEnchant = AceGUI:Create("AmrUiLabel") |
392 panelMods:AddChild(lblEnchant) | |
393 lblEnchant:SetPoint("TOPLEFT", lblItem.frame, "TOPRIGHT", 130, 0) | |
310 lblEnchant:SetWordWrap(false) | 394 lblEnchant:SetWordWrap(false) |
311 lblEnchant:SetWidth(170) | 395 lblEnchant:SetWidth(170) |
312 lblEnchant:SetFont(Amr.CreateFont(isEnchantEquipped and "Regular" or "Bold", 14, isEnchantEquipped and Amr.Colors.TextGray or Amr.Colors.White)) | 396 lblEnchant:SetFont(Amr.CreateFont(isEnchantEquipped and "Regular" or "Bold", 14, isEnchantEquipped and Amr.Colors.TextGray or Amr.Colors.White)) |
313 lblEnchant:SetPoint("TOPLEFT", lblItem.frame, "TOPRIGHT", 130, 0) | |
314 | 397 |
315 local enchInfo = Amr.db.char.ExtraEnchantData[spec][optimalItem.enchantId] | 398 local enchInfo = Amr.db.char.ExtraEnchantData[optimalItem.enchantId] |
316 if enchInfo then | 399 if enchInfo then |
317 lblEnchant:SetText(enchInfo.text) | 400 lblEnchant:SetText(enchInfo.text) |
318 | 401 |
319 Amr.GetItemInfo(enchInfo.itemId, function(obj, name, link) | 402 Amr.GetItemInfo(enchInfo.itemId, function(obj, name, link) |
320 Amr:SetItemTooltip(obj, link) | 403 Amr:SetItemTooltip(obj, link, "ANCHOR_TOPRIGHT") |
321 end, lblEnchant) | 404 end, lblEnchant) |
322 --Amr:SetSpellTooltip(lblEnchant, enchInfo.spellId) | 405 --Amr:SetSpellTooltip(lblEnchant, enchInfo.spellId) |
323 end | 406 end |
324 panelMods:AddChild(lblEnchant) | 407 |
325 end | 408 end |
326 end | 409 end |
327 | 410 |
328 prevElem = lbl | 411 prevElem = lbl |
329 end | 412 end |
347 btnImport:SetText(L.GearButtonImportText) | 430 btnImport:SetText(L.GearButtonImportText) |
348 btnImport:SetBackgroundColor(Amr.Colors.Orange) | 431 btnImport:SetBackgroundColor(Amr.Colors.Orange) |
349 btnImport:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) | 432 btnImport:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) |
350 btnImport:SetWidth(120) | 433 btnImport:SetWidth(120) |
351 btnImport:SetHeight(26) | 434 btnImport:SetHeight(26) |
352 btnImport:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -81) | |
353 btnImport:SetCallback("OnClick", onImportClick) | 435 btnImport:SetCallback("OnClick", onImportClick) |
354 container:AddChild(btnImport) | 436 container:AddChild(btnImport) |
437 btnImport:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -81) | |
355 | 438 |
356 local lbl = AceGUI:Create("AmrUiLabel") | 439 local lbl = AceGUI:Create("AmrUiLabel") |
440 container:AddChild(lbl) | |
357 lbl:SetText(L.GearImportNote) | 441 lbl:SetText(L.GearImportNote) |
358 lbl:SetWidth(100) | 442 lbl:SetWidth(100) |
359 lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan)) | 443 lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan)) |
360 lbl:SetJustifyH("MIDDLE") | 444 lbl:SetJustifyH("MIDDLE") |
361 lbl:SetPoint("TOP", btnImport.frame, "BOTTOM", 0, -5) | 445 lbl:SetPoint("TOP", btnImport.frame, "BOTTOM", 0, -5) |
362 container:AddChild(lbl) | |
363 | 446 |
364 local lbl2 = AceGUI:Create("AmrUiLabel") | 447 local lbl2 = AceGUI:Create("AmrUiLabel") |
448 container:AddChild(lbl2) | |
365 lbl2:SetText(L.GearTipTitle) | 449 lbl2:SetText(L.GearTipTitle) |
366 lbl2:SetWidth(140) | 450 lbl2:SetWidth(140) |
367 lbl2:SetFont(Amr.CreateFont("Italic", 20, Amr.Colors.Text)) | 451 lbl2:SetFont(Amr.CreateFont("Italic", 20, Amr.Colors.Text)) |
368 lbl2:SetJustifyH("MIDDLE") | 452 lbl2:SetJustifyH("MIDDLE") |
369 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -50) | 453 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -50) |
370 container:AddChild(lbl2) | |
371 | 454 |
372 lbl = AceGUI:Create("AmrUiLabel") | 455 lbl = AceGUI:Create("AmrUiLabel") |
456 container:AddChild(lbl) | |
373 lbl:SetText(L.GearTipText) | 457 lbl:SetText(L.GearTipText) |
374 lbl:SetWidth(140) | 458 lbl:SetWidth(140) |
375 lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text)) | 459 lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text)) |
376 lbl:SetJustifyH("MIDDLE") | 460 lbl:SetJustifyH("MIDDLE") |
377 lbl:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -5) | 461 lbl:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -5) |
378 container:AddChild(lbl) | |
379 | 462 |
380 lbl2 = AceGUI:Create("AmrUiLabel") | 463 lbl2 = AceGUI:Create("AmrUiLabel") |
464 container:AddChild(lbl2) | |
381 lbl2:SetText(L.GearTipCommands) | 465 lbl2:SetText(L.GearTipCommands) |
382 lbl2:SetWidth(130) | 466 lbl2:SetWidth(130) |
383 lbl2:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text)) | 467 lbl2:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text)) |
384 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 10, -5) | 468 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 10, -5) |
385 container:AddChild(lbl2) | |
386 | 469 |
387 local t = AceGUI:Create("AmrUiTabGroup") | 470 local t = AceGUI:Create("AmrUiTabGroup") |
388 t:SetLayout("None") | 471 t:SetLayout("None") |
389 | 472 |
390 local tabz = {} | 473 local tabz = {} |
395 end | 478 end |
396 end | 479 end |
397 | 480 |
398 t:SetTabs(tabz) | 481 t:SetTabs(tabz) |
399 t:SetCallback("OnGroupSelected", onGearTabSelected) | 482 t:SetCallback("OnGroupSelected", onGearTabSelected) |
483 container:AddChild(t) | |
400 t:SetPoint("TOPLEFT", container.content, "TOPLEFT", 144, -30) | 484 t:SetPoint("TOPLEFT", container.content, "TOPLEFT", 144, -30) |
401 t:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") | 485 t:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") |
402 container:AddChild(t) | |
403 _gearTabs = t; | 486 _gearTabs = t; |
404 | 487 |
405 local btnShop = AceGUI:Create("AmrUiButton") | 488 local btnShop = AceGUI:Create("AmrUiButton") |
406 btnShop:SetText(L.GearButtonShop) | 489 btnShop:SetText(L.GearButtonShop) |
407 btnShop:SetBackgroundColor(Amr.Colors.Blue) | 490 btnShop:SetBackgroundColor(Amr.Colors.Blue) |
408 btnShop:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) | 491 btnShop:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) |
409 btnShop:SetWidth(245) | 492 btnShop:SetWidth(245) |
410 btnShop:SetHeight(26) | 493 btnShop:SetHeight(26) |
411 btnShop:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", -20, -25) | |
412 btnShop:SetCallback("OnClick", function(widget) Amr:ShowShopWindow() end) | 494 btnShop:SetCallback("OnClick", function(widget) Amr:ShowShopWindow() end) |
413 container:AddChild(btnShop) | 495 container:AddChild(btnShop) |
496 btnShop:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", -20, -25) | |
414 | 497 |
415 if not _activeTab then | 498 if not _activeTab then |
416 _activeTab = tostring(GetSpecialization()) | 499 _activeTab = tostring(GetSpecialization()) |
417 end | 500 end |
418 | 501 |
441 | 524 |
442 ------------------------------------------------------------------------------------------------ | 525 ------------------------------------------------------------------------------------------------ |
443 -- Gear Set Management | 526 -- Gear Set Management |
444 ------------------------------------------------------------------------------------------------ | 527 ------------------------------------------------------------------------------------------------ |
445 local _waitingForSpec = 0 | 528 local _waitingForSpec = 0 |
446 local _waitingForItemLock = nil | 529 local _pendingGearOps = nil |
447 local _pendingEquip = nil | 530 local _currentGearOp = nil |
448 local _pendingRemove = nil | 531 local _itemLockAction = nil |
532 local _gearOpPasses = 0 | |
533 local _gearOpWaiting = nil | |
534 | |
535 local beginEquipGearSet, processCurrentGearOp, nextGearOp | |
536 | |
537 -- find the first empty slot in the player's backpack+bags | |
538 local function findFirstEmptyBagSlot() | |
539 | |
540 local bagIds = {} | |
541 table.insert(bagIds, BACKPACK_CONTAINER) | |
542 for bagId = 1, NUM_BAG_SLOTS do | |
543 table.insert(bagIds, bagId) | |
544 end | |
545 | |
546 for i, bagId in ipairs(bagIds) do | |
547 local numSlots = GetContainerNumSlots(bagId) | |
548 for slotId = 1, numSlots do | |
549 local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) | |
550 if not itemLink then | |
551 return bagId, slotId | |
552 end | |
553 end | |
554 end | |
555 | |
556 return nil, nil | |
557 end | |
449 | 558 |
450 -- scan a bag for the best matching item | 559 -- scan a bag for the best matching item |
451 local function scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) | 560 local function scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) |
452 local numSlots = GetContainerNumSlots(bagId) | 561 local numSlots = GetContainerNumSlots(bagId) |
453 for slotId = 1, numSlots do | 562 for slotId = 1, numSlots do |
466 end | 575 end |
467 end | 576 end |
468 return bestItem, bestDiff, bestLink | 577 return bestItem, bestDiff, bestLink |
469 end | 578 end |
470 | 579 |
580 -- find the item in the player's inventory that best matches the current gear op item, favoring stuff already equipped, then in bags, then in bank | |
581 local function findCurrentGearOpItem() | |
582 | |
583 local item = _currentGearOp.items[_currentGearOp.nextSlot] | |
584 | |
585 local bestItem = nil | |
586 local bestLink = nil | |
587 local bestDiff = 10000 | |
588 | |
589 -- inventory | |
590 bestItem, bestDiff, bestLink = scanBagForItem(item, BACKPACK_CONTAINER, bestItem, bestDiff, bestLink) | |
591 for bagId = 1, NUM_BAG_SLOTS do | |
592 bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) | |
593 end | |
594 | |
595 -- equipped items, but skip slots we have just equipped (to avoid e.g. just moving 2 of the same item back and forth between mh oh weapon slots) | |
596 for slotNum = 1, #Amr.SlotIds do | |
597 local slotId = Amr.SlotIds[slotNum] | |
598 if _currentGearOp.slotsRemaining[slotId] then | |
599 local itemLink = GetInventoryItemLink("player", slotId) | |
600 if itemLink then | |
601 local invItem = Amr.ParseItemLink(itemLink) | |
602 if invItem then | |
603 local diff = countItemDifferences(item, invItem) | |
604 if diff < bestDiff then | |
605 bestItem = { slot = slotId } | |
606 bestDiff = diff | |
607 bestLink = itemLink | |
608 end | |
609 end | |
610 end | |
611 end | |
612 end | |
613 | |
614 -- bank | |
615 if bestDiff > 0 then | |
616 bestItem, bestDiff, bestLink = scanBagForItem(item, BANK_CONTAINER, bestItem, bestDiff, bestLink) | |
617 for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do | |
618 bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) | |
619 end | |
620 end | |
621 | |
622 return bestItem, bestDiff, bestLink | |
623 end | |
624 | |
625 -- on completion, create an equipment manager set if desired | |
471 local function onEquipGearSetComplete() | 626 local function onEquipGearSetComplete() |
472 if Amr.db.profile.options.disableEm then return end | 627 if Amr.db.profile.options.disableEm then return end |
473 | 628 |
474 -- create an equipment manager set | 629 -- create an equipment manager set |
475 local specId, specName = GetSpecializationInfo(GetSpecialization()) | 630 local specId, specName = GetSpecializationInfo(GetSpecialization()) |
476 | 631 |
477 local item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_MAINHAND)) | 632 local item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_MAINHAND)) |
478 if not item or not Amr.ArtifactIdToSpecNumber[item.id] then | 633 if not item then |
479 item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_OFFHAND)) | 634 item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_OFFHAND)) |
480 if item and not Amr.ArtifactIdToSpecNumber[item.id] then | |
481 item = nil | |
482 end | |
483 end | 635 end |
484 if item then | 636 if item then |
485 Amr.GetItemInfo(item.id, function(customArg, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) | 637 Amr.GetItemInfo(item.id, function(customArg, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) |
486 local setname = "AMR " .. specName | 638 local setname = "AMR " .. specName |
487 local setid = C_EquipmentSet.GetEquipmentSetID(setname) | 639 local setid = C_EquipmentSet.GetEquipmentSetID(setname) |
492 end | 644 end |
493 end) | 645 end) |
494 end | 646 end |
495 end | 647 end |
496 | 648 |
497 -- find the first empty slot in the player's backpack+bags | 649 -- stop any currently in-progress gear swapping operation and clean up |
498 local function findFirstEmptyBagSlot() | 650 local function disposeGearOp() |
499 | 651 _pendingGearOps = nil |
500 local bagIds = {} | 652 _currentGearOp = nil |
501 table.insert(bagIds, BACKPACK_CONTAINER) | 653 _itemLockAction = nil |
502 for bagId = 1, NUM_BAG_SLOTS do | 654 _gearOpPasses = 0 |
503 table.insert(bagIds, bagId) | 655 _gearOpWaiting = nil |
504 end | 656 |
505 | 657 -- make sure the gear tab is still in sync |
506 for i, bagId in ipairs(bagIds) do | 658 Amr:RefreshGearTab() |
507 local numSlots = GetContainerNumSlots(bagId) | 659 end |
508 for slotId = 1, numSlots do | 660 |
509 local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) | 661 -- initialize a gear op to start running it |
510 if not itemLink then | 662 local function initializeGearOp(op, spec, pos) |
511 return bagId, slotId | 663 op.pos = pos |
512 end | 664 op.spec = spec |
513 end | 665 |
514 end | 666 -- fill the remaining slot list and set the starting slot |
515 | 667 op.nextSlot = nil |
516 return nil, nil | 668 op.slotsRemaining = {} |
517 end | 669 op.isWaiting = false |
518 | 670 for slotId, item in pairs(op.items) do |
519 local function finishEquipGearSet() | 671 op.slotsRemaining[slotId] = true |
520 if not _pendingEquip then return end | 672 if not op.nextSlot then |
521 | 673 op.nextSlot = slotId |
522 _pendingEquip.tries = _pendingEquip.tries + 1 | 674 end |
523 if _pendingEquip.tries > 16 then | 675 end |
524 -- too many tries, just give up (shouldn't happen but just to be safe) | 676 end |
525 _pendingEquip = nil | 677 |
526 else | 678 function processCurrentGearOp() |
527 -- start over again, trying any items that could not be equipped in the previous pass (unique constraints) | 679 if not _currentGearOp then return end |
528 Amr:EquipGearSet(_pendingEquip.spec) | 680 |
529 end | 681 if _currentGearOp.remove then |
530 end | 682 -- remove the next item |
531 | 683 |
532 -- equip the next slot in a pending equip | 684 -- check if the slot is already empty |
533 local function tryEquipNextItem() | 685 local itemLink = GetInventoryItemLink("player", _currentGearOp.nextSlot) |
534 if not _pendingEquip then return end | 686 if not itemLink then |
535 | 687 nextGearOp() |
536 local item = _pendingEquip.itemsToEquip[_pendingEquip.nextSlot] | 688 return |
537 | 689 end |
538 local bestItem = nil | 690 |
539 local bestLink = nil | |
540 local bestDiff = 1000 | |
541 | |
542 -- find the best matching item | |
543 | |
544 -- inventory | |
545 bestItem, bestDiff, bestLink = scanBagForItem(item, BACKPACK_CONTAINER, bestItem, bestDiff, bestLink) | |
546 for bagId = 1, NUM_BAG_SLOTS do | |
547 bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) | |
548 end | |
549 | |
550 -- equipped items, but skip slots we have just equipped (to avoid e.g. just moving 2 of the same item back and forth between mh oh weapon slots) | |
551 for slotNum = 1, #Amr.SlotIds do | |
552 local slotId = Amr.SlotIds[slotNum] | |
553 if not _pendingEquip.doneSlots[slotId] then | |
554 local itemLink = GetInventoryItemLink("player", slotId) | |
555 if itemLink then | |
556 local invItem = Amr.ParseItemLink(itemLink) | |
557 if invItem ~= nil then | |
558 local diff = countItemDifferences(item, invItem) | |
559 if diff < bestDiff then | |
560 bestItem = { slot = slotId } | |
561 bestDiff = diff | |
562 bestLink = itemLink | |
563 end | |
564 end | |
565 end | |
566 end | |
567 end | |
568 | |
569 -- bank | |
570 bestItem, bestDiff, bestLink = scanBagForItem(item, BANK_CONTAINER, bestItem, bestDiff, bestLink) | |
571 for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do | |
572 bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) | |
573 end | |
574 | |
575 ClearCursor() | |
576 | |
577 if not bestItem then | |
578 -- stop if we can't find an item | |
579 Amr:Print(L.GearEquipErrorNotFound) | |
580 Amr:Print(L.GearEquipErrorNotFound2) | |
581 _pendingEquip = nil | |
582 return | |
583 | |
584 elseif bestItem and bestItem.bag and (bestItem.bag == BANK_CONTAINER or bestItem.bag >= NUM_BAG_SLOTS + 1 and bestItem.bag <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS) then | |
585 -- find first empty bag slot | 691 -- find first empty bag slot |
586 local invBag, invSlot = findFirstEmptyBagSlot() | 692 local invBag, invSlot = findFirstEmptyBagSlot() |
587 if not invBag then | 693 if not invBag then |
588 -- stop if bags are too full | 694 -- stop if bags are too full |
589 Amr:Print(L.GearEquipErrorBagFull) | 695 Amr:Print(L.GearEquipErrorBagFull) |
590 _pendingEquip = nil | 696 disposeGearOp() |
591 return | 697 return |
592 end | 698 end |
593 | 699 |
594 -- move from bank to bag | 700 PickupInventoryItem(_currentGearOp.nextSlot) |
595 PickupContainerItem(bestItem.bag, bestItem.slot) | |
596 PickupContainerItem(invBag, invSlot) | 701 PickupContainerItem(invBag, invSlot) |
597 | 702 |
598 -- set flag so that when we clear cursor and release the item lock, we can respond to the event and continue | 703 -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor |
599 _waitingForItemLock = { | 704 _itemLockAction = { |
600 bagId = invBag, | 705 bagId = invBag, |
601 slotId = invSlot | 706 slotId = invSlot, |
707 isRemove = true | |
602 } | 708 } |
709 | |
710 ClearCursor() | |
711 -- wait for remove to complete | |
712 else | |
713 -- equip the next item | |
603 | 714 |
715 local bestItem, bestDiff, bestLink = findCurrentGearOpItem() | |
716 | |
717 _itemLockAction = nil | |
604 ClearCursor() | 718 ClearCursor() |
605 | 719 |
606 -- now we need to wait for game event to continue and try this item again after it is in our bag | 720 if not bestItem then |
607 return | 721 -- stop if we can't find an item |
608 else | 722 Amr:Print(L.GearEquipErrorNotFound) |
609 if not Amr:CanEquip(bestItem.bag, bestItem.slot) then | 723 Amr:Print(L.GearEquipErrorNotFound2) |
724 disposeGearOp() | |
725 | |
726 elseif bestItem and bestItem.bag and (bestItem.bag == BANK_CONTAINER or bestItem.bag >= NUM_BAG_SLOTS + 1 and bestItem.bag <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS) then | |
727 -- find first empty bag slot | |
728 local invBag, invSlot = findFirstEmptyBagSlot() | |
729 if not invBag then | |
730 -- stop if bags are too full | |
731 Amr:Print(L.GearEquipErrorBagFull) | |
732 disposeGearOp() | |
733 return | |
734 end | |
735 | |
736 -- move from bank to bag | |
737 PickupContainerItem(bestItem.bag, bestItem.slot) | |
738 PickupContainerItem(invBag, invSlot) | |
739 | |
740 -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor | |
741 _itemLockAction = { | |
742 bagId = invBag, | |
743 slotId = invSlot, | |
744 isBank = true | |
745 } | |
746 | |
747 ClearCursor() | |
748 -- now we need to wait for game event to continue and try this item again after it is in our bag and unlocked | |
749 | |
750 elseif (bestItem.bag or bestItem.bag == 0) and not Amr:CanEquip(bestItem.bag, bestItem.slot) then | |
610 -- if an item is not soulbound, then warn the user and quit | 751 -- if an item is not soulbound, then warn the user and quit |
611 Amr:Print(L.GearEquipErrorSoulbound(bestLink)) | 752 Amr:Print(L.GearEquipErrorSoulbound(bestLink)) |
612 _pendingEquip = nil | 753 disposeGearOp() |
613 return | 754 |
614 else | 755 else |
615 local slotId = _pendingEquip.nextSlot | 756 |
616 | |
617 -- an item in the player's bags or already equipped, equip it | 757 -- an item in the player's bags or already equipped, equip it |
618 _pendingEquip.bag = bestItem.bag | |
619 _pendingEquip.slot = bestItem.slot | |
620 _pendingEquip.destSlot = slotId | |
621 | |
622 if bestItem.bag then | 758 if bestItem.bag then |
623 PickupContainerItem(bestItem.bag, bestItem.slot) | 759 PickupContainerItem(bestItem.bag, bestItem.slot) |
624 else | 760 else |
761 _gearOpWaiting.inventory[bestItem.slot] = true | |
625 PickupInventoryItem(bestItem.slot) | 762 PickupInventoryItem(bestItem.slot) |
626 end | 763 end |
627 PickupInventoryItem(slotId) | 764 _gearOpWaiting.inventory[_currentGearOp.nextSlot] = true |
628 ClearCursor() | 765 PickupInventoryItem(_currentGearOp.nextSlot) |
629 | 766 |
630 -- wait for game events to continue | 767 -- don't wait for now, do all equips at once |
631 end | 768 --[[ |
632 end | 769 -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor |
633 | 770 _itemLockAction = { |
634 end | 771 bagId = bestItem.bag, |
635 | 772 slotId = bestItem.slot, |
636 local function removeNextItem() | 773 invSlot = _currentGearOp.nextSlot, |
637 if not _pendingRemove then return end | 774 isEquip = true |
638 | 775 } |
639 local list = _pendingRemove.slotsToRemove | 776 ]] |
640 local slot = list[#list - _pendingRemove.remaining + 1] | 777 |
641 | 778 ClearCursor() |
642 -- find first empty bag slot | 779 nextGearOp() |
643 local invBag, invSlot = findFirstEmptyBagSlot() | 780 end |
644 if not invBag then | 781 |
645 -- stop if bags are too full | 782 end |
646 Amr:Print(L.GearEquipErrorBagFull) | 783 end |
647 _pendingRemove = nil | 784 |
648 _pendingEquip = nil | 785 -- when a gear op completes successfully, this will advance to the next op or finish |
649 return | 786 function nextGearOp() |
650 end | 787 if not _currentGearOp then return end |
651 | 788 |
652 PickupInventoryItem(slot) | 789 local spec = _currentGearOp.spec |
653 PickupContainerItem(invBag, invSlot) | 790 local pos = _currentGearOp.pos |
654 | 791 local passes = _gearOpPasses |
655 -- set flag so that when we clear cursor and release the item lock, we can respond to the event and continue | 792 |
656 _waitingForItemLock = { | 793 -- mark the slot as done and move to the next |
657 bagId = invBag, | 794 if _currentGearOp.nextSlot then |
658 slotId = invSlot, | 795 _currentGearOp.slotsRemaining[_currentGearOp.nextSlot] = nil |
659 isRemove = true | 796 _currentGearOp.nextSlot = nil |
660 } | 797 for slotId, item in pairs(_currentGearOp.items) do |
661 | 798 if _currentGearOp.slotsRemaining[slotId] then |
662 ClearCursor() | 799 _currentGearOp.nextSlot = slotId |
663 end | 800 break |
664 | 801 end |
665 local function onItemUnlocked(bagId, slotId) | 802 end |
666 | 803 end |
667 if _waitingForItemLock then | 804 |
668 -- waiting on a move from bank to bags to complete, or waiting on removing an item to complete, just continue as normal afterwards | 805 if not _currentGearOp.nextSlot then |
669 if bagId == _waitingForItemLock.bagId and slotId == _waitingForItemLock.slotId then | 806 -- see if anything is still in progress and we want to wait for it before continuing |
670 local isremove = _waitingForItemLock.isRemove | 807 local inProgress = not Amr.IsEmpty(_gearOpWaiting.inventory) |
671 _waitingForItemLock = nil | 808 |
672 | 809 if (_currentGearOp.wait or _currentGearOp.remove) and inProgress then |
673 if isremove then | 810 -- this will cause the item unlock handler to call nextGearOp again when all in-progress swaps have unlocked related slots |
674 _pendingRemove.remaining = _pendingRemove.remaining - 1 | 811 _currentGearOp.isWaiting = true |
675 if _pendingRemove.remaining > 0 then | 812 else |
676 removeNextItem() | 813 _currentGearOp = _pendingGearOps[pos + 1] |
677 else | 814 if _currentGearOp then |
678 -- we have removed all items that we want to remove, now do the equip | 815 -- we have another op, do it |
679 _pendingRemove = nil | 816 initializeGearOp(_currentGearOp, spec, pos + 1) |
680 tryEquipNextItem() | 817 processCurrentGearOp() |
681 end | |
682 else | 818 else |
683 tryEquipNextItem() | 819 -- we are done |
684 end | 820 disposeGearOp() |
685 end | 821 |
822 -- this will check if not all items were swapped, and either finish up, try again, or abort if have tried too many times | |
823 beginEquipGearSet(spec, passes + 1) | |
824 end | |
825 end | |
826 else | |
827 -- do the next item | |
828 processCurrentGearOp() | |
829 end | |
830 | |
831 end | |
832 | |
833 local function handleItemUnlocked(bagId, slotId) | |
834 | |
835 -- mark anything that is waiting as unlocked if it is no longer locked | |
836 if _currentGearOp and _gearOpWaiting then | |
837 for i,s in ipairs(Amr.SlotIds) do | |
838 if not IsInventoryItemLocked(s) then | |
839 _gearOpWaiting.inventory[s] = nil | |
840 end | |
841 end | |
842 end | |
843 | |
844 if _itemLockAction then | |
845 if _itemLockAction.isRemove then | |
846 -- waiting for a specific remove op to finish before continuing | |
847 if bagId == _itemLockAction.bagId and slotId == _itemLockAction.slotId then | |
848 _itemLockAction = nil | |
849 nextGearOp() | |
850 end | |
851 elseif _itemLockAction.isBank then | |
852 -- waiting for an item to move from bank into inventory, then reprocess the current op | |
853 if bagId == _itemLockAction.bagId and slotId == _itemLockAction.slotId then | |
854 _itemLockAction = nil | |
855 processCurrentGearOp() | |
856 end | |
857 | |
858 elseif _itemLockAction.isEquip then | |
859 -- this is not currently used... we do all equips at once usually, but could go back to this if it causes problems | |
860 | |
861 -- waiting for a specific equip op to finish | |
862 | |
863 -- inventory slot we're swapping to is still locked, can't continue yet | |
864 if IsInventoryItemLocked(_itemLockAction.invSlot) then return end | |
865 | |
866 if _itemLockAction.bagId then | |
867 local _, _, locked = GetContainerItemInfo(_itemLockAction.bagId, _itemLockAction.slotId) | |
868 -- the bag slot we're swapping from is still locked, can't continue yet | |
869 if locked then return end | |
870 else | |
871 -- inventory slot we're swapping from is still locked, can't continue yet | |
872 if IsInventoryItemLocked(_itemLockAction.slotId) then return end | |
873 end | |
874 | |
875 _itemLockAction = nil | |
876 nextGearOp() | |
877 else | |
878 -- unknown... shouldn't happen | |
879 _itemLockAction = nil | |
880 end | |
881 else | |
686 | 882 |
687 elseif _pendingEquip and _pendingEquip.destSlot then | 883 -- not waiting on a specific action, check if we are waiting for all locked slots to open up and they are done |
688 -- waiting on an item swap to complete successfully so that we can go on to the next item | 884 if _currentGearOp and _gearOpWaiting and _currentGearOp.isWaiting and Amr.IsEmpty(_gearOpWaiting.inventory) then |
689 | 885 nextGearOp() |
690 -- inventory slot we're swapping to is still locked, can't continue yet | 886 end |
691 if IsInventoryItemLocked(_pendingEquip.destSlot) then return end | 887 end |
692 | 888 |
693 if _pendingEquip.bag then | 889 end |
694 local _, _, locked = GetContainerItemInfo(_pendingEquip.bag, _pendingEquip.slot) | 890 |
695 -- the bag slot we're swapping from is still locked, can't continue yet | 891 local function shuffle(tbl) |
696 if locked then return end | 892 local size = #tbl |
697 else | 893 for i = size, 1, -1 do |
698 -- inventory slot we're swapping from is still locked, can't continue yet | 894 local rand = math.random(size) |
699 if IsInventoryItemLocked(_pendingEquip.slot) then return end | 895 tbl[i], tbl[rand] = tbl[rand], tbl[i] |
700 end | 896 end |
701 | 897 return tbl |
702 -- move on to the next item, this item is done or could not be swapped | 898 end |
703 | 899 |
704 local item = _pendingEquip.itemsToEquip[_pendingEquip.destSlot] | 900 function beginEquipGearSet(spec, passes) |
705 local itemLink = GetInventoryItemLink("player", _pendingEquip.destSlot) | |
706 if itemLink then | |
707 local invItem = Amr.ParseItemLink(itemLink) | |
708 if invItem ~= nil then | |
709 local diff = countItemDifferences(item, invItem) | |
710 if diff == 0 then | |
711 _pendingEquip.doneSlots[_pendingEquip.destSlot] = true | |
712 end | |
713 end | |
714 end | |
715 | |
716 _pendingEquip.itemsToEquip[_pendingEquip.destSlot] = nil | |
717 _pendingEquip.destSlot = nil | |
718 _pendingEquip.bag = nil | |
719 _pendingEquip.slot = nil | |
720 | |
721 _pendingEquip.remaining = _pendingEquip.remaining - 1 | |
722 if _pendingEquip.remaining > 0 then | |
723 for slotId, item in pairs(_pendingEquip.itemsToEquip) do | |
724 _pendingEquip.nextSlot = slotId | |
725 break | |
726 end | |
727 tryEquipNextItem() | |
728 else | |
729 finishEquipGearSet() | |
730 end | |
731 | |
732 end | |
733 end | |
734 | |
735 local function startEquipGearSet(spec) | |
736 | 901 |
737 local gear = Amr.db.char.GearSets[spec] | 902 local gear = Amr.db.char.GearSets[spec] |
738 if not gear then | 903 if not gear then |
739 Amr:Print(L.GearEquipErrorEmpty) | 904 Amr:Print(L.GearEquipErrorEmpty) |
740 return | 905 return |
741 end | 906 end |
742 | 907 |
908 -- ensure all our stored data is up to date | |
743 local player = Amr:ExportCharacter() | 909 local player = Amr:ExportCharacter() |
744 | 910 |
745 local itemsToEquip = {} | 911 local itemsToEquip = { |
912 legendaries = {}, | |
913 weapons = {}, | |
914 rings = {}, | |
915 trinkets = {}, | |
916 others = {}, | |
917 blanks = {} | |
918 } | |
746 local remaining = 0 | 919 local remaining = 0 |
747 local usedItems = {} | 920 local usedItems = {} |
748 local firstSlot = nil | 921 |
749 | 922 -- check for items that need to be equipped, do in a random order to try and defeat any unique constraint issues we might hit |
750 -- check for items that need to be equipped | 923 local slots = {} |
751 for slotNum = 1, #Amr.SlotIds do | 924 for i,s in ipairs(Amr.SlotIds) do |
752 local slotId = Amr.SlotIds[slotNum] | 925 table.insert(slots, s) |
753 | 926 end |
927 shuffle(slots) | |
928 | |
929 for i,slotId in ipairs(slots) do | |
930 | |
931 -- we do stuff in batches that avoids most unique conflicts | |
932 local list = itemsToEquip.others | |
933 if slotId == 16 or slotId == 17 then | |
934 list = itemsToEquip.weapons | |
935 elseif slotId == 11 or slotId == 12 then | |
936 list = itemsToEquip.rings | |
937 elseif slotId == 13 or slotId == 14 then | |
938 list = itemsToEquip.trinkets | |
939 end | |
940 | |
754 local old = player.Equipped[spec][slotId] | 941 local old = player.Equipped[spec][slotId] |
755 old = Amr.ParseItemLink(old) | |
756 | |
757 local new = gear[slotId] | 942 local new = gear[slotId] |
943 local prevRemaining = remaining | |
758 if new then | 944 if new then |
759 local diff = countItemDifferences(old, new) | 945 -- if the new thing is an artifact, only match the item id |
760 if diff < 1000 then | 946 local newItem = Item:CreateFromItemID(new.id) |
761 -- same item, see if inventory has one that is closer (e.g. a duplicate item with correct enchants/gems) | 947 local quality = newItem and newItem:GetItemQuality() or 0 |
762 local bestLink, bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems) | 948 if quality == 6 then |
763 if bestDiff and bestDiff < diff then | 949 if not old or new.id ~= old.id then |
764 itemsToEquip[slotId] = new | 950 list[slotId] = new |
765 remaining = remaining + 1 | 951 remaining = remaining + 1 |
766 end | 952 end |
767 else | 953 else |
768 itemsToEquip[slotId] = new | 954 local diff = countItemDifferences(old, new) |
769 remaining = remaining + 1 | 955 if diff > 0 and diff < 1000 then |
770 end | 956 -- same item, see if inventory has one that is closer (e.g. a duplicate item with correct enchants/gems) |
771 end | 957 local bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems) |
772 end | 958 if bestDiff and bestDiff < diff then |
773 | 959 new = bestItem |
960 diff = bestDiff | |
961 end | |
962 end | |
963 | |
964 if diff > 0 then | |
965 list[slotId] = new | |
966 remaining = remaining + 1 | |
967 end | |
968 end | |
969 else | |
970 -- need to remove this item | |
971 itemsToEquip.blanks[slotId] = {} | |
972 remaining = remaining + 1 | |
973 end | |
974 | |
975 if remaining > prevRemaining then | |
976 -- if we need to swap this slot, see if the old item is a legendary, add a step to remove those first to avoid conflicts | |
977 if old then | |
978 local oldItem = Item:CreateFromItemID(old.id) | |
979 if oldItem and oldItem:GetItemQuality() == 5 then | |
980 itemsToEquip.legendaries[slotId] = {} | |
981 end | |
982 end | |
983 end | |
984 end | |
985 | |
774 if remaining > 0 then | 986 if remaining > 0 then |
775 -- if this is not our first try, then remove weapons before starting | 987 |
776 local toRemove = {} | 988 if passes < 5 then |
777 local removesRemaining = 0 | 989 _pendingGearOps = {} |
778 if _pendingEquip and _pendingEquip.tries > 0 then | 990 |
779 for slotId, item in pairs(itemsToEquip) do | 991 if not Amr.IsEmpty(itemsToEquip.blanks) then |
780 if slotId == 16 or slotId == 17 then | 992 -- if gear set wants slots to be blank, do that first |
781 table.insert(toRemove, slotId) | 993 table.insert(_pendingGearOps, { items = itemsToEquip.blanks, remove = true, label = "blanks" }) |
782 removesRemaining = removesRemaining + 1 | 994 end |
783 end | 995 if not Amr.IsEmpty(itemsToEquip.weapons) then |
784 end | 996 -- change weapons first: remove both, wait, then equip new ones |
785 end | 997 table.insert(_pendingGearOps, { items = itemsToEquip.weapons, remove = true, label = "remove weapons" }) |
786 | 998 table.insert(_pendingGearOps, { items = itemsToEquip.weapons, wait = true, label = "equip weapons" }) |
787 _pendingEquip = { | 999 end |
788 tries = _pendingEquip and _pendingEquip.spec == spec and _pendingEquip.tries or 0, | 1000 if not Amr.IsEmpty(itemsToEquip.legendaries) then |
789 spec = spec, | 1001 -- remove any legendaries, wait |
790 itemsToEquip = itemsToEquip, | 1002 table.insert(_pendingGearOps, { items = itemsToEquip.legendaries, remove = true, label = "remove legendaries" }) |
791 remaining = remaining, | 1003 end |
792 doneSlots = _pendingEquip and _pendingEquip.spec == spec and _pendingEquip.doneSlots or {}, | 1004 if not Amr.IsEmpty(itemsToEquip.rings) then |
793 nextSlot = firstSlot | 1005 -- remove both rings, wait, then equip new ones |
794 } | 1006 table.insert(_pendingGearOps, { items = itemsToEquip.rings, remove = true, label = "remove rings" }) |
795 | 1007 table.insert(_pendingGearOps, { items = itemsToEquip.rings, wait = true, label = "equip rings" }) |
796 -- starting item | 1008 end |
797 for slotId, item in pairs(_pendingEquip.itemsToEquip) do | 1009 if not Amr.IsEmpty(itemsToEquip.trinkets) then |
798 _pendingEquip.nextSlot = slotId | 1010 -- remove both trinkets, wait, then equip new ones |
799 break | 1011 table.insert(_pendingGearOps, { items = itemsToEquip.trinkets, remove = true, label = "remove trinkets" }) |
800 end | 1012 table.insert(_pendingGearOps, { items = itemsToEquip.trinkets, wait = true, label = "equip trinkets" }) |
801 | 1013 end |
802 if removesRemaining > 0 then | 1014 if not Amr.IsEmpty(itemsToEquip.others) then |
803 _pendingRemove = { | 1015 -- equip all other items, wait for completion |
804 slotsToRemove = toRemove, | 1016 table.insert(_pendingGearOps, { items = itemsToEquip.others, wait = true, label = "equip others" }) |
805 remaining = removesRemaining | 1017 end |
806 } | 1018 |
807 removeNextItem() | 1019 -- make the last operation wait no matter what, before this gets called again to check if everything succeeded |
1020 _pendingGearOps[#_pendingGearOps].wait = true | |
1021 | |
1022 if not _gearOpWaiting then | |
1023 _gearOpWaiting = { inventory = {} } | |
1024 end | |
1025 | |
1026 _gearOpPasses = passes | |
1027 _currentGearOp = _pendingGearOps[1] | |
1028 initializeGearOp(_currentGearOp, spec, 1) | |
1029 | |
1030 processCurrentGearOp() | |
808 else | 1031 else |
809 tryEquipNextItem() | 1032 -- TODO: print message that gear set couldn't be equipped |
810 end | 1033 end |
1034 | |
811 else | 1035 else |
812 _pendingEquip = nil | |
813 onEquipGearSetComplete() | 1036 onEquipGearSetComplete() |
814 end | 1037 end |
815 end | 1038 end |
816 | 1039 |
817 local function onActiveTalentGroupChanged() | 1040 local function onActiveTalentGroupChanged() |
818 | 1041 |
819 local auto = Amr.db.profile.options.autoGear | 1042 local auto = Amr.db.profile.options.autoGear |
820 local currentSpec = GetSpecialization() | 1043 local currentSpec = GetSpecialization() |
821 | 1044 |
822 if currentSpec == _waitingForSpec or auto then | 1045 if currentSpec == _waitingForSpec or auto then |
823 -- spec is what we want, now equip the gear | 1046 -- spec is what we want, now equip the gear |
824 startEquipGearSet(currentSpec) | 1047 beginEquipGearSet(currentSpec, 0) |
825 end | 1048 end |
826 | 1049 |
827 _waitingForSpec = 0 | 1050 _waitingForSpec = 0 |
828 end | 1051 end |
829 | 1052 |
854 else | 1077 else |
855 onActiveTalentGroupChanged() | 1078 onActiveTalentGroupChanged() |
856 end | 1079 end |
857 end | 1080 end |
858 | 1081 |
859 -- moves any gear in bags to the bank if not part of main or off spec gear set | 1082 -- moves any gear in bags to the bank if not part of a gear set |
860 function Amr:CleanBags() | 1083 function Amr:CleanBags() |
861 -- TODO: implement | 1084 -- TODO: implement |
862 end | 1085 end |
863 | 1086 |
864 --[[ | 1087 --[[ |
872 | 1095 |
873 --Amr:AddEventHandler("CHAT_MSG_CHANNEL", testfunc) | 1096 --Amr:AddEventHandler("CHAT_MSG_CHANNEL", testfunc) |
874 | 1097 |
875 Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID) | 1098 Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID) |
876 if unitID and unitID ~= "player" then return end | 1099 if unitID and unitID ~= "player" then return end |
1100 | |
1101 -- don't update during a gear operation, wait until it is totally finished | |
1102 if _pendingGearOps then return end | |
1103 | |
877 Amr:RefreshGearTab() | 1104 Amr:RefreshGearTab() |
878 end) | 1105 end) |
879 | 1106 |
880 Amr:AddEventHandler("ITEM_UNLOCKED", onItemUnlocked) | 1107 Amr:AddEventHandler("ITEM_UNLOCKED", handleItemUnlocked) |
881 end | 1108 end |