annotate Gear.lua @ 143:55823e37403b v67

Updated pt-BR translation, bug fixes.
author yellowfive
date Thu, 08 Nov 2018 21:21:10 -0800
parents c229c759a125
children df1596b1a744
rev   line source
yellowfive@57 1 local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
yellowfive@57 2 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
yellowfive@57 3 local AceGUI = LibStub("AceGUI-3.0")
yellowfive@57 4
yellowfive@139 5 local _cboSetups
yellowfive@139 6 local _panelGear
yellowfive@139 7 local _activeSetupId
yellowfive@139 8
yellowfive@139 9 local function getSetupById(id)
yellowfive@139 10 if not id then
yellowfive@139 11 id = _activeSetupId
yellowfive@139 12 end
yellowfive@139 13 local setup
yellowfive@139 14 for i,s in ipairs(Amr.db.char.GearSetups) do
yellowfive@139 15 if s.Id == id then
yellowfive@139 16 setup = s
yellowfive@139 17 break
yellowfive@139 18 end
yellowfive@139 19 end
yellowfive@139 20 return setup
yellowfive@139 21 end
yellowfive@57 22
yellowfive@57 23 -- Returns a number indicating how different two items are (0 means the same, higher means more different)
yellowfive@57 24 local function countItemDifferences(item1, item2)
yellowfive@124 25 -- both nil, the same
yellowfive@124 26 if not item1 and not item2 then
yellowfive@124 27 return 0
yellowfive@124 28 end
yellowfive@124 29
yellowfive@124 30 -- one nil and other not, or different id, totally different
yellowfive@124 31 if (not item1 and item2) or (item1 and not item2) or item1.id ~= item2.id then
yellowfive@124 32 return 10000
yellowfive@124 33 end
yellowfive@124 34
yellowfive@124 35 -- different versions of same item (id + bonus ids + suffix + drop level, constitutes a different physical drop)
yellowfive@135 36 if Amr.GetItemUniqueId(item1, true, true) ~= Amr.GetItemUniqueId(item2, true, true) then
yellowfive@57 37 return 1000
yellowfive@57 38 end
yellowfive@57 39
yellowfive@81 40 -- different upgrade levels of the same item
yellowfive@57 41 if item1.upgradeId ~= item2.upgradeId then
yellowfive@57 42 return 100
yellowfive@124 43 end
yellowfive@124 44
yellowfive@124 45 -- different azerite powers
yellowfive@124 46 local aztDiffs = 0
yellowfive@124 47 if item1.azerite or item2.azerite then
yellowfive@124 48 if item1.azerite and not item2.azerite then
yellowfive@124 49 aztDiffs = #item1.azerite * 10
yellowfive@124 50 elseif item2.azerite and not item1.azerite then
yellowfive@124 51 aztDiffs = #item2.azerite * 10
yellowfive@124 52 else
yellowfive@124 53 -- count up number in item1 but missing from item2
yellowfive@124 54 for i = 1, #item1.azerite do
yellowfive@124 55 local missing = false
yellowfive@124 56 for j = 1, #item2.azerite do
yellowfive@124 57 if item2[j] == item1[i] then
yellowfive@124 58 missing = false
yellowfive@124 59 end
yellowfive@124 60 end
yellowfive@124 61 if missing then
yellowfive@124 62 aztDiffs = aztDiffs + 10
yellowfive@124 63 end
yellowfive@124 64 end
yellowfive@124 65 -- count up number in item2 but missing from item1
yellowfive@124 66 for i = 1, #item2.azerite do
yellowfive@124 67 local missing = false
yellowfive@124 68 for j = 1, #item1.azerite do
yellowfive@124 69 if item1[j] == item2[i] then
yellowfive@124 70 missing = false
yellowfive@124 71 end
yellowfive@124 72 end
yellowfive@124 73 if missing then
yellowfive@124 74 aztDiffs = aztDiffs + 10
yellowfive@124 75 end
yellowfive@124 76 end
yellowfive@124 77 end
yellowfive@124 78 end
yellowfive@57 79
yellowfive@57 80 -- different gems
yellowfive@57 81 local gemDiffs = 0
yellowfive@57 82 for i = 1, 3 do
yellowfive@57 83 if item1.gemIds[i] ~= item2.gemIds[i] then
yellowfive@57 84 gemDiffs = gemDiffs + 1
yellowfive@57 85 end
yellowfive@57 86 end
yellowfive@57 87
yellowfive@57 88 -- different enchants
yellowfive@57 89 local enchantDiff = 0
yellowfive@57 90 if item1.enchantId ~= item2.enchantId then
yellowfive@57 91 enchantDiff = 1
yellowfive@57 92 end
yellowfive@57 93
yellowfive@124 94 return aztDiffs + gemDiffs + enchantDiff
yellowfive@57 95 end
yellowfive@57 96
yellowfive@57 97 -- given a table of items (keyed or indexed doesn't matter) find closest match to item, or nil if none are a match
yellowfive@124 98 local function findMatchingItemFromTable(item, list, bestItem, bestDiff, bestLoc, usedItems, tableType)
yellowfive@57 99 if not list then return nil end
yellowfive@57 100
yellowfive@73 101 local found = false
yellowfive@129 102 for k,listItem in pairs(list) do
yellowfive@57 103 if listItem then
yellowfive@57 104 local diff = countItemDifferences(item, listItem)
yellowfive@57 105 if diff < bestDiff then
yellowfive@57 106 -- each physical item can only be used once, the usedItems table has items we can't use in this search
yellowfive@57 107 local key = string.format("%s_%s", tableType, k)
yellowfive@57 108 if not usedItems[key] then
yellowfive@57 109 bestItem = listItem
yellowfive@57 110 bestDiff = diff
yellowfive@124 111 bestLoc = key
yellowfive@73 112 found = true
yellowfive@57 113 end
yellowfive@57 114 end
yellowfive@73 115 if found then break end
yellowfive@57 116 end
yellowfive@57 117 end
yellowfive@57 118
yellowfive@124 119 return bestItem, bestDiff, bestLoc
yellowfive@57 120 end
yellowfive@57 121
yellowfive@124 122 -- search the player's equipped gear, bag, and bank for an item that best matches the specified item
yellowfive@57 123 function Amr:FindMatchingItem(item, player, usedItems)
yellowfive@57 124 if not item then return nil end
yellowfive@57 125
yellowfive@57 126 local equipped = player.Equipped and player.Equipped[player.ActiveSpec] or nil
yellowfive@124 127 local bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, equipped, nil, 10000, nil, usedItems, "equip")
yellowfive@124 128 bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BagItems, bestItem, bestDiff, bestLoc, usedItems, "bag")
yellowfive@124 129 if player.BankItems then
yellowfive@129 130 bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BankItems, bestItem, bestDiff, bestLoc, usedItems, "bank")
yellowfive@124 131 end
yellowfive@57 132
yellowfive@124 133 if bestDiff >= 10000 then
yellowfive@124 134 return nil, 10000
yellowfive@57 135 else
yellowfive@57 136 usedItems[bestLoc] = true
yellowfive@124 137 return bestItem, bestDiff
yellowfive@57 138 end
yellowfive@57 139 end
yellowfive@57 140
yellowfive@57 141 local function renderEmptyGear(container)
yellowfive@57 142
yellowfive@57 143 local panelBlank = AceGUI:Create("AmrUiPanel")
yellowfive@57 144 panelBlank:SetLayout("None")
yellowfive@57 145 panelBlank:SetBackgroundColor(Amr.Colors.Black, 0.4)
yellowfive@124 146 container:AddChild(panelBlank)
yellowfive@57 147 panelBlank:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0)
yellowfive@57 148 panelBlank:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
yellowfive@57 149
yellowfive@57 150 local lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 151 panelBlank:AddChild(lbl)
yellowfive@57 152 lbl:SetText(L.GearBlank)
yellowfive@57 153 lbl:SetWidth(700)
yellowfive@57 154 lbl:SetJustifyH("MIDDLE")
yellowfive@57 155 lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
yellowfive@57 156 lbl:SetPoint("BOTTOM", panelBlank.content, "CENTER", 0, 20)
yellowfive@57 157
yellowfive@57 158 local lbl2 = AceGUI:Create("AmrUiLabel")
yellowfive@124 159 panelBlank:AddChild(lbl2)
yellowfive@57 160 lbl2:SetText(L.GearBlank2)
yellowfive@57 161 lbl2:SetWidth(700)
yellowfive@57 162 lbl2:SetJustifyH("MIDDLE")
yellowfive@57 163 lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
yellowfive@57 164 lbl2:SetPoint("TOP", lbl.frame, "CENTER", 0, -20)
yellowfive@124 165 end
yellowfive@124 166
yellowfive@124 167 -- helper to create a widget for showing a socket or azerite power
yellowfive@124 168 local function createSocketWidget(panelMods, prevWidget, prevIsSocket, isEquipped)
yellowfive@124 169
yellowfive@124 170 -- highlight for socket that doesn't match
yellowfive@124 171 local socketBorder = AceGUI:Create("AmrUiPanel")
yellowfive@124 172 panelMods:AddChild(socketBorder)
yellowfive@124 173 if not prevIsSocket then
yellowfive@124 174 socketBorder:SetPoint("LEFT", prevWidget.frame, "RIGHT", 30, 0)
yellowfive@124 175 else
yellowfive@124 176 socketBorder:SetPoint("LEFT", prevWidget.frame, "RIGHT", 2, 0)
yellowfive@124 177 end
yellowfive@124 178 socketBorder:SetLayout("None")
yellowfive@124 179 socketBorder:SetBackgroundColor(Amr.Colors.Black, isEquipped and 0 or 1)
yellowfive@124 180 socketBorder:SetWidth(26)
yellowfive@124 181 socketBorder:SetHeight(26)
yellowfive@124 182 if isEquipped then
yellowfive@124 183 socketBorder:SetAlpha(0.3)
yellowfive@124 184 end
yellowfive@124 185
yellowfive@124 186 local socketBg = AceGUI:Create("AmrUiIcon")
yellowfive@124 187 socketBorder:AddChild(socketBg)
yellowfive@124 188 socketBg:SetPoint("TOPLEFT", socketBorder.content, "TOPLEFT", 1, -1)
yellowfive@124 189 socketBg:SetLayout("None")
yellowfive@124 190 socketBg:SetBorderWidth(2)
yellowfive@124 191 socketBg:SetIconBorderColor(Amr.Colors.Green, isEquipped and 0 or 1)
yellowfive@124 192 socketBg:SetWidth(24)
yellowfive@124 193 socketBg:SetHeight(24)
yellowfive@124 194
yellowfive@124 195 local socketIcon = AceGUI:Create("AmrUiIcon")
yellowfive@124 196 socketBg:AddChild(socketIcon)
yellowfive@124 197 socketIcon:SetPoint("CENTER", socketBg.content, "CENTER")
yellowfive@124 198 socketIcon:SetBorderWidth(1)
yellowfive@124 199 socketIcon:SetIconBorderColor(Amr.Colors.White)
yellowfive@124 200 socketIcon:SetWidth(18)
yellowfive@124 201 socketIcon:SetHeight(18)
yellowfive@124 202
yellowfive@124 203 return socketBorder, socketIcon
yellowfive@57 204 end
yellowfive@57 205
yellowfive@139 206 local function renderGear(setupId, container)
yellowfive@139 207
yellowfive@139 208 -- release all children that were previously rendered, we gonna redo it now
yellowfive@139 209 container:ReleaseChildren()
yellowfive@57 210
yellowfive@57 211 local player = Amr:ExportCharacter()
yellowfive@139 212
yellowfive@139 213 local gear
yellowfive@139 214 local spec
yellowfive@139 215 local setupIndex
yellowfive@139 216 for i, setup in ipairs(Amr.db.char.GearSetups) do
yellowfive@139 217 if setup.Id == setupId then
yellowfive@139 218 setupIndex = i
yellowfive@139 219 gear = setup.Gear
yellowfive@139 220 spec = setup.SpecSlot
yellowfive@139 221 break
yellowfive@139 222 end
yellowfive@139 223 end
yellowfive@139 224
yellowfive@57 225 local equipped = player.Equipped[player.ActiveSpec]
yellowfive@57 226
yellowfive@57 227 if not gear then
yellowfive@57 228 -- no gear has been imported for this spec so show a message
yellowfive@57 229 renderEmptyGear(container)
yellowfive@57 230 else
yellowfive@57 231 local panelGear = AceGUI:Create("AmrUiPanel")
yellowfive@57 232 panelGear:SetLayout("None")
yellowfive@57 233 panelGear:SetBackgroundColor(Amr.Colors.Black, 0.3)
yellowfive@124 234 container:AddChild(panelGear)
yellowfive@57 235 panelGear:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0)
yellowfive@57 236 panelGear:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT", -300, 0)
yellowfive@57 237
yellowfive@57 238 local panelMods = AceGUI:Create("AmrUiPanel")
yellowfive@57 239 panelMods:SetLayout("None")
yellowfive@124 240 panelMods:SetBackgroundColor(Amr.Colors.Black, 0.3)
yellowfive@124 241 container:AddChild(panelMods)
yellowfive@57 242 panelMods:SetPoint("TOPLEFT", panelGear.frame, "TOPRIGHT", 15, 0)
yellowfive@57 243 panelMods:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
yellowfive@57 244
yellowfive@57 245 -- spec icon
yellowfive@57 246 local icon = AceGUI:Create("AmrUiIcon")
yellowfive@57 247 icon:SetIconBorderColor(Amr.Colors.Classes[player.Class])
yellowfive@57 248 icon:SetWidth(48)
yellowfive@57 249 icon:SetHeight(48)
yellowfive@57 250
yellowfive@57 251 local iconSpec
yellowfive@81 252 if player.SubSpecs and player.SubSpecs[spec] then
yellowfive@57 253 iconSpec = player.SubSpecs[spec]
yellowfive@57 254 else
yellowfive@57 255 iconSpec = player.Specs[spec]
yellowfive@57 256 end
yellowfive@57 257
yellowfive@57 258 icon:SetIcon("Interface\\Icons\\" .. Amr.SpecIcons[iconSpec])
yellowfive@124 259 panelGear:AddChild(icon)
yellowfive@57 260 icon:SetPoint("TOPLEFT", panelGear.content, "TOPLEFT", 10, -10)
yellowfive@57 261
yellowfive@57 262 local btnEquip = AceGUI:Create("AmrUiButton")
yellowfive@81 263 btnEquip:SetText(L.GearButtonEquip(L.SpecsShort[player.Specs[spec]]))
yellowfive@57 264 btnEquip:SetBackgroundColor(Amr.Colors.Green)
yellowfive@57 265 btnEquip:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
yellowfive@57 266 btnEquip:SetWidth(300)
yellowfive@57 267 btnEquip:SetHeight(26)
yellowfive@57 268 btnEquip:SetCallback("OnClick", function(widget)
yellowfive@139 269 Amr:EquipGearSet(setupIndex)
yellowfive@57 270 end)
yellowfive@57 271 panelGear:AddChild(btnEquip)
yellowfive@124 272 btnEquip:SetPoint("LEFT", icon.frame, "RIGHT", 40, 0)
yellowfive@124 273 btnEquip:SetPoint("RIGHT", panelGear.content, "RIGHT", -40, 0)
yellowfive@57 274
yellowfive@57 275 -- each physical item can only be used once, this tracks ones we have already used
yellowfive@57 276 local usedItems = {}
yellowfive@57 277
yellowfive@57 278 -- gear list
yellowfive@57 279 local prevElem = icon
yellowfive@57 280 for slotNum = 1, #Amr.SlotIds do
yellowfive@57 281 local slotId = Amr.SlotIds[slotNum]
yellowfive@57 282
yellowfive@124 283 local equippedItem = equipped and equipped[slotId] or nil
yellowfive@133 284 --local equippedItemLink = equipped and equipped.link or nil
yellowfive@57 285 local optimalItem = gear[slotId]
yellowfive@57 286 local optimalItemLink = Amr.CreateItemLink(optimalItem)
yellowfive@57 287
yellowfive@57 288 -- see if item is currently equipped, is false if don't have any item for that slot (e.g. OH for a 2-hander)
yellowfive@57 289 local isEquipped = false
yellowfive@135 290 if equippedItem and optimalItem and Amr.GetItemUniqueId(equippedItem, false, true) == Amr.GetItemUniqueId(optimalItem, false, true) then
yellowfive@57 291 isEquipped = true
yellowfive@57 292 end
yellowfive@124 293
yellowfive@124 294 local isAzerite = optimalItem and C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItemByID(optimalItem.id)
yellowfive@57 295
yellowfive@57 296 -- find the item in the player's inventory that best matches what the optimization wants to use
yellowfive@124 297 local matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
yellowfive@57 298
yellowfive@57 299 -- slot label
yellowfive@57 300 local lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 301 panelGear:AddChild(lbl)
yellowfive@124 302 lbl:SetPoint("TOPLEFT", prevElem.frame, "BOTTOMLEFT", 0, -12)
yellowfive@57 303 lbl:SetText(Amr.SlotDisplayText[slotId])
yellowfive@57 304 lbl:SetWidth(85)
yellowfive@57 305 lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
yellowfive@57 306 prevElem = lbl
yellowfive@57 307
yellowfive@57 308 -- ilvl label
yellowfive@57 309 local lblIlvl = AceGUI:Create("AmrUiLabel")
yellowfive@124 310 panelGear:AddChild(lblIlvl)
yellowfive@124 311 lblIlvl:SetPoint("TOPLEFT", lbl.frame, "TOPRIGHT", 0, 0)
yellowfive@57 312 lblIlvl:SetWidth(45)
yellowfive@57 313 lblIlvl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
yellowfive@57 314
yellowfive@57 315 -- equipped label
yellowfive@57 316 local lblEquipped = AceGUI:Create("AmrUiLabel")
yellowfive@124 317 panelGear:AddChild(lblEquipped)
yellowfive@124 318 lblEquipped:SetPoint("TOPLEFT", lblIlvl.frame, "TOPRIGHT", 0, 0)
yellowfive@57 319 lblEquipped:SetWidth(20)
yellowfive@57 320 lblEquipped:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
yellowfive@57 321 lblEquipped:SetText(isEquipped and "E" or "")
yellowfive@57 322
yellowfive@57 323 -- item name/link label
yellowfive@57 324 local lblItem = AceGUI:Create("AmrUiLabel")
yellowfive@124 325 panelGear:AddChild(lblItem)
yellowfive@124 326 lblItem:SetPoint("TOPLEFT", lblEquipped.frame, "TOPRIGHT", 0, 0)
yellowfive@57 327 lblItem:SetWordWrap(false)
yellowfive@57 328 lblItem:SetWidth(345)
yellowfive@57 329 lblItem:SetFont(Amr.CreateFont(isEquipped and "Regular" or "Bold", isEquipped and 14 or 15, Amr.Colors.White))
yellowfive@57 330
yellowfive@133 331 -- fill the name/ilvl labels, which may require asynchronous loading of item information
yellowfive@57 332 if optimalItemLink then
yellowfive@133 333 local gameItem = Item:CreateFromItemLink(optimalItemLink)
yellowfive@133 334 if gameItem then
yellowfive@133 335 local q = gameItem:GetItemQuality()
yellowfive@133 336 if q == 6 then
yellowfive@133 337 -- for artifacts, we consider it equipped if the item id alone matches
yellowfive@133 338 if equippedItem and equippedItem.id == optimalItem.id then
yellowfive@133 339 isEquipped = true
yellowfive@133 340 end
yellowfive@133 341 lblEquipped:SetText(isEquipped and "E" or "")
yellowfive@133 342 end
yellowfive@124 343
yellowfive@137 344 lblItem:SetFont(Amr.CreateFont(isEquipped and "Regular" or "Bold", isEquipped and 14 or 15, Amr.Colors.Qualities[q] or Amr.Colors.White))
yellowfive@133 345 lblItem:SetText(gameItem:GetItemName())
yellowfive@133 346 lblIlvl:SetText(gameItem:GetCurrentItemLevel())
yellowfive@133 347 Amr:SetItemTooltip(lblItem, gameItem:GetItemLink(), "ANCHOR_TOPRIGHT")
yellowfive@133 348 end
yellowfive@57 349 end
yellowfive@57 350
yellowfive@57 351 -- modifications
yellowfive@57 352 if optimalItem then
yellowfive@57 353
yellowfive@124 354 -- gems or azerite powers
yellowfive@124 355 local prevSocket = nil
yellowfive@124 356
yellowfive@124 357 if isAzerite then
yellowfive@124 358 local azt = optimalItem.azerite or {}
yellowfive@124 359 for i,spellId in ipairs(azt) do
yellowfive@124 360 if spellId and spellId ~= 0 then
yellowfive@135 361 local equippedAzt = matchItem and matchItem.azerite or {}
yellowfive@124 362 local isPowerActive = Amr.Contains(equippedAzt, spellId)
yellowfive@124 363
yellowfive@124 364 local socketBorder, socketIcon = createSocketWidget(panelMods, prevSocket or lblItem, prevSocket, isPowerActive)
yellowfive@124 365
yellowfive@124 366 -- set icon and tooltip
yellowfive@133 367 local _, _, spellIcon = GetSpellInfo(spellId)
yellowfive@124 368 socketIcon:SetIcon(spellIcon)
yellowfive@124 369 Amr:SetSpellTooltip(socketIcon, spellId, "ANCHOR_TOPRIGHT")
yellowfive@124 370
yellowfive@124 371 prevSocket = socketBorder
yellowfive@124 372 end
yellowfive@124 373 end
yellowfive@124 374 else
yellowfive@124 375 for i = 1, #optimalItem.gemIds do
yellowfive@124 376 -- we rely on the fact that the gear sets coming back from the site will almost always have all sockets filled,
yellowfive@124 377 -- because it's a pain to get the actual number of sockets on an item from within the game
yellowfive@57 378 local g = optimalItem.gemIds[i]
yellowfive@124 379 if g == 0 then break end
yellowfive@124 380
yellowfive@124 381 local isGemEquipped = matchItem and matchItem.gemIds and matchItem.gemIds[i] == g
yellowfive@57 382
yellowfive@124 383 local socketBorder, socketIcon = createSocketWidget(panelMods, prevSocket or lblItem, prevSocket, isGemEquipped)
yellowfive@57 384
yellowfive@57 385 -- get icon for optimized gem
yellowfive@133 386 local gameItem = Item:CreateFromItemID(g)
yellowfive@133 387 if gameItem then
yellowfive@133 388 socketIcon:SetIcon(gameItem:GetItemIcon())
yellowfive@133 389 Amr:SetItemTooltip(socketIcon, gameItem:GetItemLink(), "ANCHOR_TOPRIGHT")
yellowfive@133 390 end
yellowfive@89 391
yellowfive@89 392 prevSocket = socketBorder
yellowfive@57 393 end
yellowfive@57 394 end
yellowfive@124 395
yellowfive@57 396 -- enchant
yellowfive@57 397 if optimalItem.enchantId and optimalItem.enchantId ~= 0 then
yellowfive@57 398 local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == optimalItem.enchantId
yellowfive@135 399
yellowfive@57 400 local lblEnchant = AceGUI:Create("AmrUiLabel")
yellowfive@124 401 panelMods:AddChild(lblEnchant)
yellowfive@124 402 lblEnchant:SetPoint("TOPLEFT", lblItem.frame, "TOPRIGHT", 130, 0)
yellowfive@57 403 lblEnchant:SetWordWrap(false)
yellowfive@57 404 lblEnchant:SetWidth(170)
yellowfive@57 405 lblEnchant:SetFont(Amr.CreateFont(isEnchantEquipped and "Regular" or "Bold", 14, isEnchantEquipped and Amr.Colors.TextGray or Amr.Colors.White))
yellowfive@57 406
yellowfive@124 407 local enchInfo = Amr.db.char.ExtraEnchantData[optimalItem.enchantId]
yellowfive@57 408 if enchInfo then
yellowfive@57 409 lblEnchant:SetText(enchInfo.text)
yellowfive@57 410
yellowfive@133 411 local gameItem = Item:CreateFromItemID(enchInfo.itemId)
yellowfive@133 412 if gameItem then
yellowfive@133 413 Amr:SetItemTooltip(lblEnchant, gameItem:GetItemLink(), "ANCHOR_TOPRIGHT")
yellowfive@133 414 end
yellowfive@57 415 end
yellowfive@124 416
yellowfive@57 417 end
yellowfive@57 418 end
yellowfive@57 419
yellowfive@57 420 prevElem = lbl
yellowfive@57 421 end
yellowfive@57 422 end
yellowfive@57 423 end
yellowfive@57 424
yellowfive@139 425 local function onSetupChange(widget, eventName, value)
yellowfive@139 426 _activeSetupId = value
yellowfive@139 427 renderGear(_activeSetupId, _panelGear)
yellowfive@57 428 end
yellowfive@57 429
yellowfive@57 430 local function onImportClick(widget)
yellowfive@57 431 Amr:ShowImportWindow()
yellowfive@57 432 end
yellowfive@57 433
yellowfive@139 434 function Amr:PickFirstSetupForSpec()
yellowfive@139 435 local specSlot = GetSpecialization()
yellowfive@139 436 for i, setup in ipairs(Amr.db.char.GearSetups) do
yellowfive@139 437 if setup.SpecSlot == specSlot then
yellowfive@139 438 _activeSetupId = setup.Id
yellowfive@139 439 break
yellowfive@139 440 end
yellowfive@139 441 end
yellowfive@139 442 end
yellowfive@139 443
yellowfive@139 444 function Amr:GetActiveSetupId()
yellowfive@139 445 return _activeSetupId
yellowfive@139 446 end
yellowfive@139 447
yellowfive@139 448 function Amr:SetActiveSetupId(setupId)
yellowfive@139 449 _activeSetupId = setupId
yellowfive@139 450 end
yellowfive@139 451
yellowfive@139 452 function Amr:GetActiveSetupLabel()
yellowfive@139 453 if not _activeSetupId then
yellowfive@139 454 return nil
yellowfive@139 455 end
yellowfive@139 456 local setup = getSetupById(_activeSetupId)
yellowfive@139 457 if not setup then
yellowfive@139 458 return nil
yellowfive@139 459 else
yellowfive@139 460 return setup.Label
yellowfive@139 461 end
yellowfive@139 462 end
yellowfive@139 463
yellowfive@57 464 -- renders the main UI for the Gear tab
yellowfive@57 465 function Amr:RenderTabGear(container)
yellowfive@57 466
yellowfive@57 467 local btnImport = AceGUI:Create("AmrUiButton")
yellowfive@57 468 btnImport:SetText(L.GearButtonImportText)
yellowfive@57 469 btnImport:SetBackgroundColor(Amr.Colors.Orange)
yellowfive@57 470 btnImport:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
yellowfive@57 471 btnImport:SetWidth(120)
yellowfive@57 472 btnImport:SetHeight(26)
yellowfive@57 473 btnImport:SetCallback("OnClick", onImportClick)
yellowfive@57 474 container:AddChild(btnImport)
yellowfive@124 475 btnImport:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -81)
yellowfive@57 476
yellowfive@57 477 local lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 478 container:AddChild(lbl)
yellowfive@57 479 lbl:SetText(L.GearImportNote)
yellowfive@57 480 lbl:SetWidth(100)
yellowfive@57 481 lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
yellowfive@57 482 lbl:SetJustifyH("MIDDLE")
yellowfive@57 483 lbl:SetPoint("TOP", btnImport.frame, "BOTTOM", 0, -5)
yellowfive@57 484
yellowfive@57 485 local lbl2 = AceGUI:Create("AmrUiLabel")
yellowfive@124 486 container:AddChild(lbl2)
yellowfive@57 487 lbl2:SetText(L.GearTipTitle)
yellowfive@57 488 lbl2:SetWidth(140)
yellowfive@57 489 lbl2:SetFont(Amr.CreateFont("Italic", 20, Amr.Colors.Text))
yellowfive@57 490 lbl2:SetJustifyH("MIDDLE")
yellowfive@57 491 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -50)
yellowfive@57 492
yellowfive@57 493 lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 494 container:AddChild(lbl)
yellowfive@57 495 lbl:SetText(L.GearTipText)
yellowfive@57 496 lbl:SetWidth(140)
yellowfive@57 497 lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text))
yellowfive@57 498 lbl:SetJustifyH("MIDDLE")
yellowfive@57 499 lbl:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -5)
yellowfive@57 500
yellowfive@57 501 lbl2 = AceGUI:Create("AmrUiLabel")
yellowfive@124 502 container:AddChild(lbl2)
yellowfive@57 503 lbl2:SetText(L.GearTipCommands)
yellowfive@57 504 lbl2:SetWidth(130)
yellowfive@57 505 lbl2:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text))
yellowfive@57 506 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 10, -5)
yellowfive@57 507
yellowfive@139 508 _cboSetups = AceGUI:Create("AmrUiDropDown")
yellowfive@139 509 _cboSetups:SetWidth(300)
yellowfive@139 510 container:AddChild(_cboSetups)
yellowfive@139 511 _cboSetups:SetPoint("TOPLEFT", container.content, "TOPLEFT", 150, -27.5)
yellowfive@81 512
yellowfive@139 513 _panelGear = AceGUI:Create("AmrUiPanel")
yellowfive@139 514 _panelGear:SetLayout("None")
yellowfive@139 515 _panelGear:SetBackgroundColor(Amr.Colors.Bg)
yellowfive@139 516 container:AddChild(_panelGear)
yellowfive@139 517 _panelGear:SetPoint("TOPLEFT", container.content, "TOPLEFT", 144, -58)
yellowfive@139 518 _panelGear:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
yellowfive@57 519
yellowfive@61 520 local btnShop = AceGUI:Create("AmrUiButton")
yellowfive@61 521 btnShop:SetText(L.GearButtonShop)
yellowfive@61 522 btnShop:SetBackgroundColor(Amr.Colors.Blue)
yellowfive@61 523 btnShop:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
yellowfive@61 524 btnShop:SetWidth(245)
yellowfive@61 525 btnShop:SetHeight(26)
yellowfive@61 526 btnShop:SetCallback("OnClick", function(widget) Amr:ShowShopWindow() end)
yellowfive@61 527 container:AddChild(btnShop)
yellowfive@124 528 btnShop:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", -20, -25)
yellowfive@139 529
yellowfive@139 530 -- pick a default tab based on player's current spec if none is already specified
yellowfive@139 531 if not _activeSetupId then
yellowfive@139 532 Amr:PickFirstSetupForSpec()
yellowfive@57 533 end
yellowfive@139 534
yellowfive@139 535 Amr:RefreshGearDisplay()
yellowfive@139 536
yellowfive@139 537 -- set event on dropdown after UI has been initially rendered
yellowfive@139 538 _cboSetups:SetCallback("OnChange", onSetupChange)
yellowfive@57 539 end
yellowfive@57 540
yellowfive@57 541 function Amr:ReleaseTabGear()
yellowfive@139 542 _cboSetups = nil
yellowfive@139 543 _panelGear = nil
yellowfive@57 544 end
yellowfive@57 545
yellowfive@57 546 -- refresh display of the current gear tab
yellowfive@139 547 function Amr:RefreshGearDisplay()
yellowfive@139 548
yellowfive@139 549 if not _panelGear then
yellowfive@139 550 return
yellowfive@139 551 end
yellowfive@139 552
yellowfive@139 553 -- fill the gear setup picker
yellowfive@139 554 local setupList = {}
yellowfive@139 555 for i, setup in ipairs(Amr.db.char.GearSetups) do
yellowfive@139 556 table.insert(setupList, { text = setup.Label, value = setup.Id })
yellowfive@139 557 end
yellowfive@139 558 _cboSetups:SetItems(setupList)
yellowfive@139 559
yellowfive@139 560 -- set selected value
yellowfive@139 561 local prev = _activeSetupId
yellowfive@139 562 _cboSetups:SelectItem(_activeSetupId)
yellowfive@139 563
yellowfive@139 564 if prev == _activeSetupId then
yellowfive@139 565 -- selecting will trigger the change event if it changed; if it didn't change, do a render now
yellowfive@139 566 renderGear(_activeSetupId, _panelGear)
yellowfive@139 567 end
yellowfive@57 568 end
yellowfive@57 569
yellowfive@57 570
yellowfive@57 571 ------------------------------------------------------------------------------------------------
yellowfive@57 572 -- Gear Set Management
yellowfive@57 573 ------------------------------------------------------------------------------------------------
yellowfive@57 574 local _waitingForSpec = 0
yellowfive@124 575 local _pendingGearOps = nil
yellowfive@124 576 local _currentGearOp = nil
yellowfive@124 577 local _itemLockAction = nil
yellowfive@124 578 local _gearOpPasses = 0
yellowfive@124 579 local _gearOpWaiting = nil
yellowfive@57 580
yellowfive@124 581 local beginEquipGearSet, processCurrentGearOp, nextGearOp
yellowfive@89 582
yellowfive@57 583 -- find the first empty slot in the player's backpack+bags
yellowfive@57 584 local function findFirstEmptyBagSlot()
yellowfive@57 585
yellowfive@57 586 local bagIds = {}
yellowfive@57 587 table.insert(bagIds, BACKPACK_CONTAINER)
yellowfive@57 588 for bagId = 1, NUM_BAG_SLOTS do
yellowfive@57 589 table.insert(bagIds, bagId)
yellowfive@57 590 end
yellowfive@57 591
yellowfive@57 592 for i, bagId in ipairs(bagIds) do
yellowfive@57 593 local numSlots = GetContainerNumSlots(bagId)
yellowfive@57 594 for slotId = 1, numSlots do
yellowfive@57 595 local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
yellowfive@57 596 if not itemLink then
yellowfive@57 597 return bagId, slotId
yellowfive@57 598 end
yellowfive@57 599 end
yellowfive@57 600 end
yellowfive@57 601
yellowfive@57 602 return nil, nil
yellowfive@57 603 end
yellowfive@57 604
yellowfive@124 605 -- scan a bag for the best matching item
yellowfive@124 606 local function scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
yellowfive@124 607 local numSlots = GetContainerNumSlots(bagId)
yellowfive@124 608 for slotId = 1, numSlots do
yellowfive@124 609 local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
yellowfive@124 610 -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked
yellowfive@124 611 if itemLink then
yellowfive@124 612 local bagItem = Amr.ParseItemLink(itemLink)
yellowfive@124 613 if bagItem ~= nil then
yellowfive@124 614 local diff = countItemDifferences(item, bagItem)
yellowfive@124 615 if diff < bestDiff then
yellowfive@124 616 bestItem = { bag = bagId, slot = slotId }
yellowfive@124 617 bestDiff = diff
yellowfive@124 618 bestLink = itemLink
yellowfive@124 619 end
yellowfive@124 620 end
yellowfive@124 621 end
yellowfive@57 622 end
yellowfive@124 623 return bestItem, bestDiff, bestLink
yellowfive@57 624 end
yellowfive@57 625
yellowfive@124 626 -- 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
yellowfive@124 627 local function findCurrentGearOpItem()
yellowfive@124 628
yellowfive@124 629 local item = _currentGearOp.items[_currentGearOp.nextSlot]
yellowfive@124 630
yellowfive@57 631 local bestItem = nil
yellowfive@57 632 local bestLink = nil
yellowfive@124 633 local bestDiff = 10000
yellowfive@57 634
yellowfive@73 635 -- inventory
yellowfive@73 636 bestItem, bestDiff, bestLink = scanBagForItem(item, BACKPACK_CONTAINER, bestItem, bestDiff, bestLink)
yellowfive@73 637 for bagId = 1, NUM_BAG_SLOTS do
yellowfive@73 638 bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
yellowfive@73 639 end
yellowfive@73 640
yellowfive@129 641 -- with new approach, the item to use should never be equipped, should be in bags at this point
yellowfive@129 642 --[[
yellowfive@61 643 -- 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)
yellowfive@57 644 for slotNum = 1, #Amr.SlotIds do
yellowfive@57 645 local slotId = Amr.SlotIds[slotNum]
yellowfive@124 646 if _currentGearOp.slotsRemaining[slotId] then
yellowfive@61 647 local itemLink = GetInventoryItemLink("player", slotId)
yellowfive@61 648 if itemLink then
yellowfive@61 649 local invItem = Amr.ParseItemLink(itemLink)
yellowfive@124 650 if invItem then
yellowfive@61 651 local diff = countItemDifferences(item, invItem)
yellowfive@61 652 if diff < bestDiff then
yellowfive@61 653 bestItem = { slot = slotId }
yellowfive@61 654 bestDiff = diff
yellowfive@61 655 bestLink = itemLink
yellowfive@61 656 end
yellowfive@57 657 end
yellowfive@57 658 end
yellowfive@57 659 end
yellowfive@57 660 end
yellowfive@129 661 ]]
yellowfive@129 662
yellowfive@57 663 -- bank
yellowfive@124 664 if bestDiff > 0 then
yellowfive@124 665 bestItem, bestDiff, bestLink = scanBagForItem(item, BANK_CONTAINER, bestItem, bestDiff, bestLink)
yellowfive@124 666 for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
yellowfive@124 667 bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
yellowfive@124 668 end
yellowfive@57 669 end
yellowfive@124 670
yellowfive@124 671 return bestItem, bestDiff, bestLink
yellowfive@124 672 end
yellowfive@124 673
yellowfive@143 674 local function createAmrEquipmentSet()
yellowfive@133 675
yellowfive@143 676 -- clear any currently ignored slots, ignore shirt and tabard
yellowfive@143 677 C_EquipmentSet.ClearIgnoredSlotsForSave()
yellowfive@133 678 C_EquipmentSet.IgnoreSlotForSave(INVSLOT_BODY) -- shirt
yellowfive@133 679 C_EquipmentSet.IgnoreSlotForSave(INVSLOT_TABARD)
yellowfive@133 680
yellowfive@133 681 -- for now use icon of the spec
yellowfive@133 682 local _, specName, _, setIcon = GetSpecializationInfo(GetSpecialization())
yellowfive@57 683
yellowfive@133 684 --[[
yellowfive@124 685 local item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_MAINHAND))
yellowfive@124 686 if not item then
yellowfive@124 687 item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_OFFHAND))
yellowfive@124 688 end
yellowfive@124 689 if item then
yellowfive@133 690 local itemObj = Item:CreateFromItemID(item.id)
yellowfive@133 691 if itemObj then
yellowfive@133 692 setIcon = itemObj:GetItemIcon()
yellowfive@133 693 end
yellowfive@133 694 end
yellowfive@133 695 ]]
yellowfive@133 696
yellowfive@139 697 local setup = getSetupById(_activeSetupId)
yellowfive@139 698 local setname = setup.Label -- "AMR " .. specName
yellowfive@133 699 local setid = C_EquipmentSet.GetEquipmentSetID(setname)
yellowfive@133 700 if setid then
yellowfive@133 701 C_EquipmentSet.SaveEquipmentSet(setid, setIcon)
yellowfive@133 702 else
yellowfive@133 703 C_EquipmentSet.CreateEquipmentSet(setname, setIcon)
yellowfive@124 704 end
yellowfive@124 705 end
yellowfive@124 706
yellowfive@143 707 -- on completion, create an equipment manager set if desired
yellowfive@143 708 local function onEquipGearSetComplete()
yellowfive@143 709 if Amr.db.profile.options.disableEm then return end
yellowfive@143 710
yellowfive@143 711 -- create an equipment manager set
yellowfive@143 712 createAmrEquipmentSet()
yellowfive@143 713
yellowfive@143 714 -- need to call it twice because on first load the WoW equipment manager just doesn't work
yellowfive@143 715 Amr.Wait(1, function()
yellowfive@143 716 createAmrEquipmentSet()
yellowfive@143 717 end)
yellowfive@143 718 end
yellowfive@143 719
yellowfive@124 720 -- stop any currently in-progress gear swapping operation and clean up
yellowfive@124 721 local function disposeGearOp()
yellowfive@124 722 _pendingGearOps = nil
yellowfive@124 723 _currentGearOp = nil
yellowfive@124 724 _itemLockAction = nil
yellowfive@124 725 _gearOpPasses = 0
yellowfive@124 726 _gearOpWaiting = nil
yellowfive@124 727
yellowfive@124 728 -- make sure the gear tab is still in sync
yellowfive@139 729 Amr:RefreshGearDisplay()
yellowfive@124 730 end
yellowfive@124 731
yellowfive@124 732 -- initialize a gear op to start running it
yellowfive@139 733 local function initializeGearOp(op, setupId, pos)
yellowfive@124 734 op.pos = pos
yellowfive@139 735 op.setupId = setupId
yellowfive@124 736
yellowfive@124 737 -- fill the remaining slot list and set the starting slot
yellowfive@124 738 op.nextSlot = nil
yellowfive@124 739 op.slotsRemaining = {}
yellowfive@124 740 op.isWaiting = false
yellowfive@124 741 for slotId, item in pairs(op.items) do
yellowfive@124 742 op.slotsRemaining[slotId] = true
yellowfive@124 743 if not op.nextSlot then
yellowfive@124 744 op.nextSlot = slotId
yellowfive@124 745 end
yellowfive@124 746 end
yellowfive@124 747 end
yellowfive@124 748
yellowfive@124 749 function processCurrentGearOp()
yellowfive@124 750 if not _currentGearOp then return end
yellowfive@124 751
yellowfive@124 752 if _currentGearOp.remove then
yellowfive@124 753 -- remove the next item
yellowfive@124 754
yellowfive@124 755 -- check if the slot is already empty
yellowfive@124 756 local itemLink = GetInventoryItemLink("player", _currentGearOp.nextSlot)
yellowfive@124 757 if not itemLink then
yellowfive@124 758 nextGearOp()
yellowfive@124 759 return
yellowfive@124 760 end
yellowfive@124 761
yellowfive@57 762 -- find first empty bag slot
yellowfive@57 763 local invBag, invSlot = findFirstEmptyBagSlot()
yellowfive@57 764 if not invBag then
yellowfive@57 765 -- stop if bags are too full
yellowfive@57 766 Amr:Print(L.GearEquipErrorBagFull)
yellowfive@124 767 disposeGearOp()
yellowfive@57 768 return
yellowfive@57 769 end
yellowfive@57 770
yellowfive@124 771 PickupInventoryItem(_currentGearOp.nextSlot)
yellowfive@57 772 PickupContainerItem(invBag, invSlot)
yellowfive@57 773
yellowfive@124 774 -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor
yellowfive@124 775 _itemLockAction = {
yellowfive@57 776 bagId = invBag,
yellowfive@124 777 slotId = invSlot,
yellowfive@124 778 isRemove = true
yellowfive@57 779 }
yellowfive@124 780
yellowfive@124 781 ClearCursor()
yellowfive@124 782 -- wait for remove to complete
yellowfive@124 783 else
yellowfive@124 784 -- equip the next item
yellowfive@57 785
yellowfive@124 786 local bestItem, bestDiff, bestLink = findCurrentGearOpItem()
yellowfive@124 787
yellowfive@124 788 _itemLockAction = nil
yellowfive@57 789 ClearCursor()
yellowfive@124 790
yellowfive@124 791 if not bestItem then
yellowfive@124 792 -- stop if we can't find an item
yellowfive@124 793 Amr:Print(L.GearEquipErrorNotFound)
yellowfive@124 794 Amr:Print(L.GearEquipErrorNotFound2)
yellowfive@124 795 disposeGearOp()
yellowfive@124 796
yellowfive@124 797 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
yellowfive@124 798 -- find first empty bag slot
yellowfive@124 799 local invBag, invSlot = findFirstEmptyBagSlot()
yellowfive@124 800 if not invBag then
yellowfive@124 801 -- stop if bags are too full
yellowfive@124 802 Amr:Print(L.GearEquipErrorBagFull)
yellowfive@124 803 disposeGearOp()
yellowfive@124 804 return
yellowfive@124 805 end
yellowfive@124 806
yellowfive@124 807 -- move from bank to bag
yellowfive@124 808 PickupContainerItem(bestItem.bag, bestItem.slot)
yellowfive@124 809 PickupContainerItem(invBag, invSlot)
yellowfive@124 810
yellowfive@124 811 -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor
yellowfive@124 812 _itemLockAction = {
yellowfive@124 813 bagId = invBag,
yellowfive@124 814 slotId = invSlot,
yellowfive@124 815 isBank = true
yellowfive@124 816 }
yellowfive@124 817
yellowfive@124 818 ClearCursor()
yellowfive@124 819 -- now we need to wait for game event to continue and try this item again after it is in our bag and unlocked
yellowfive@124 820
yellowfive@124 821 elseif (bestItem.bag or bestItem.bag == 0) and not Amr:CanEquip(bestItem.bag, bestItem.slot) then
yellowfive@57 822 -- if an item is not soulbound, then warn the user and quit
yellowfive@57 823 Amr:Print(L.GearEquipErrorSoulbound(bestLink))
yellowfive@124 824 disposeGearOp()
yellowfive@124 825
yellowfive@57 826 else
yellowfive@124 827
yellowfive@129 828 --print("equipping " .. bestLink .. " in slot " .. _currentGearOp.nextSlot)
yellowfive@129 829
yellowfive@57 830 -- an item in the player's bags or already equipped, equip it
yellowfive@57 831 if bestItem.bag then
yellowfive@57 832 PickupContainerItem(bestItem.bag, bestItem.slot)
yellowfive@57 833 else
yellowfive@124 834 _gearOpWaiting.inventory[bestItem.slot] = true
yellowfive@57 835 PickupInventoryItem(bestItem.slot)
yellowfive@57 836 end
yellowfive@124 837 _gearOpWaiting.inventory[_currentGearOp.nextSlot] = true
yellowfive@124 838 PickupInventoryItem(_currentGearOp.nextSlot)
yellowfive@124 839
yellowfive@124 840 -- don't wait for now, do all equips at once
yellowfive@124 841 --[[
yellowfive@124 842 -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor
yellowfive@124 843 _itemLockAction = {
yellowfive@124 844 bagId = bestItem.bag,
yellowfive@124 845 slotId = bestItem.slot,
yellowfive@124 846 invSlot = _currentGearOp.nextSlot,
yellowfive@124 847 isEquip = true
yellowfive@124 848 }
yellowfive@124 849 ]]
yellowfive@124 850
yellowfive@124 851 ClearCursor()
yellowfive@124 852 nextGearOp()
yellowfive@124 853 end
yellowfive@124 854
yellowfive@124 855 end
yellowfive@124 856 end
yellowfive@124 857
yellowfive@124 858 -- when a gear op completes successfully, this will advance to the next op or finish
yellowfive@124 859 function nextGearOp()
yellowfive@124 860 if not _currentGearOp then return end
yellowfive@124 861
yellowfive@139 862 local setupId = _currentGearOp.setupId
yellowfive@124 863 local pos = _currentGearOp.pos
yellowfive@124 864 local passes = _gearOpPasses
yellowfive@124 865
yellowfive@124 866 -- mark the slot as done and move to the next
yellowfive@124 867 if _currentGearOp.nextSlot then
yellowfive@124 868 _currentGearOp.slotsRemaining[_currentGearOp.nextSlot] = nil
yellowfive@124 869 _currentGearOp.nextSlot = nil
yellowfive@124 870 for slotId, item in pairs(_currentGearOp.items) do
yellowfive@124 871 if _currentGearOp.slotsRemaining[slotId] then
yellowfive@124 872 _currentGearOp.nextSlot = slotId
yellowfive@124 873 break
yellowfive@124 874 end
yellowfive@124 875 end
yellowfive@124 876 end
yellowfive@124 877
yellowfive@124 878 if not _currentGearOp.nextSlot then
yellowfive@124 879 -- see if anything is still in progress and we want to wait for it before continuing
yellowfive@124 880 local inProgress = not Amr.IsEmpty(_gearOpWaiting.inventory)
yellowfive@124 881
yellowfive@124 882 if (_currentGearOp.wait or _currentGearOp.remove) and inProgress then
yellowfive@124 883 -- this will cause the item unlock handler to call nextGearOp again when all in-progress swaps have unlocked related slots
yellowfive@124 884 _currentGearOp.isWaiting = true
yellowfive@124 885 else
yellowfive@124 886 _currentGearOp = _pendingGearOps[pos + 1]
yellowfive@124 887 if _currentGearOp then
yellowfive@124 888 -- we have another op, do it
yellowfive@139 889 initializeGearOp(_currentGearOp, setupId, pos + 1)
yellowfive@124 890 processCurrentGearOp()
yellowfive@124 891 else
yellowfive@124 892 -- we are done
yellowfive@124 893 disposeGearOp()
yellowfive@124 894
yellowfive@124 895 -- this will check if not all items were swapped, and either finish up, try again, or abort if have tried too many times
yellowfive@139 896 beginEquipGearSet(setupId, passes + 1)
yellowfive@124 897 end
yellowfive@124 898 end
yellowfive@124 899 else
yellowfive@124 900 -- do the next item
yellowfive@124 901 processCurrentGearOp()
yellowfive@124 902 end
yellowfive@124 903
yellowfive@124 904 end
yellowfive@124 905
yellowfive@124 906 local function handleItemUnlocked(bagId, slotId)
yellowfive@124 907
yellowfive@124 908 -- mark anything that is waiting as unlocked if it is no longer locked
yellowfive@124 909 if _currentGearOp and _gearOpWaiting then
yellowfive@124 910 for i,s in ipairs(Amr.SlotIds) do
yellowfive@124 911 if not IsInventoryItemLocked(s) then
yellowfive@124 912 _gearOpWaiting.inventory[s] = nil
yellowfive@124 913 end
yellowfive@124 914 end
yellowfive@124 915 end
yellowfive@124 916
yellowfive@124 917 if _itemLockAction then
yellowfive@124 918 if _itemLockAction.isRemove then
yellowfive@124 919 -- waiting for a specific remove op to finish before continuing
yellowfive@124 920 if bagId == _itemLockAction.bagId and slotId == _itemLockAction.slotId then
yellowfive@124 921 _itemLockAction = nil
yellowfive@124 922 nextGearOp()
yellowfive@124 923 end
yellowfive@124 924 elseif _itemLockAction.isBank then
yellowfive@124 925 -- waiting for an item to move from bank into inventory, then reprocess the current op
yellowfive@124 926 if bagId == _itemLockAction.bagId and slotId == _itemLockAction.slotId then
yellowfive@124 927 _itemLockAction = nil
yellowfive@124 928 processCurrentGearOp()
yellowfive@124 929 end
yellowfive@124 930
yellowfive@124 931 elseif _itemLockAction.isEquip then
yellowfive@124 932 -- this is not currently used... we do all equips at once usually, but could go back to this if it causes problems
yellowfive@124 933
yellowfive@124 934 -- waiting for a specific equip op to finish
yellowfive@61 935
yellowfive@124 936 -- inventory slot we're swapping to is still locked, can't continue yet
yellowfive@124 937 if IsInventoryItemLocked(_itemLockAction.invSlot) then return end
yellowfive@124 938
yellowfive@124 939 if _itemLockAction.bagId then
yellowfive@124 940 local _, _, locked = GetContainerItemInfo(_itemLockAction.bagId, _itemLockAction.slotId)
yellowfive@124 941 -- the bag slot we're swapping from is still locked, can't continue yet
yellowfive@124 942 if locked then return end
yellowfive@124 943 else
yellowfive@124 944 -- inventory slot we're swapping from is still locked, can't continue yet
yellowfive@124 945 if IsInventoryItemLocked(_itemLockAction.slotId) then return end
yellowfive@124 946 end
yellowfive@124 947
yellowfive@124 948 _itemLockAction = nil
yellowfive@124 949 nextGearOp()
yellowfive@124 950 else
yellowfive@124 951 -- unknown... shouldn't happen
yellowfive@124 952 _itemLockAction = nil
yellowfive@57 953 end
yellowfive@124 954 else
yellowfive@124 955
yellowfive@124 956 -- not waiting on a specific action, check if we are waiting for all locked slots to open up and they are done
yellowfive@124 957 if _currentGearOp and _gearOpWaiting and _currentGearOp.isWaiting and Amr.IsEmpty(_gearOpWaiting.inventory) then
yellowfive@124 958 nextGearOp()
yellowfive@124 959 end
yellowfive@57 960 end
yellowfive@57 961
yellowfive@57 962 end
yellowfive@57 963
yellowfive@124 964 local function shuffle(tbl)
yellowfive@124 965 local size = #tbl
yellowfive@124 966 for i = size, 1, -1 do
yellowfive@124 967 local rand = math.random(size)
yellowfive@124 968 tbl[i], tbl[rand] = tbl[rand], tbl[i]
yellowfive@89 969 end
yellowfive@124 970 return tbl
yellowfive@89 971 end
yellowfive@89 972
yellowfive@129 973 local _ohFirst = {
yellowfive@129 974 [20] = true, -- PaladinProtection
yellowfive@129 975 [32] = true, -- WarlockDemonology
yellowfive@129 976 [36] = true -- WarriorProtection
yellowfive@129 977 }
yellowfive@129 978
yellowfive@139 979 function beginEquipGearSet(setupId, passes)
yellowfive@57 980
yellowfive@139 981 local setup = getSetupById(setupId)
yellowfive@139 982
yellowfive@139 983 if not setup or not setup.Gear then
yellowfive@57 984 Amr:Print(L.GearEquipErrorEmpty)
yellowfive@57 985 return
yellowfive@57 986 end
yellowfive@124 987
yellowfive@139 988 local gear = setup.Gear
yellowfive@139 989 local spec = setup.SpecSlot
yellowfive@139 990
yellowfive@139 991 -- ensure all our stored data is up to date
yellowfive@57 992 local player = Amr:ExportCharacter()
yellowfive@129 993 local doOhFirst = _ohFirst[player.Specs[spec]]
yellowfive@57 994
yellowfive@124 995 local itemsToEquip = {
yellowfive@124 996 legendaries = {},
yellowfive@124 997 weapons = {},
yellowfive@129 998 mh = {},
yellowfive@129 999 oh = {},
yellowfive@124 1000 rings = {},
yellowfive@124 1001 trinkets = {},
yellowfive@124 1002 others = {},
yellowfive@124 1003 blanks = {}
yellowfive@124 1004 }
yellowfive@57 1005 local remaining = 0
yellowfive@129 1006 local usedItems = {}
yellowfive@124 1007
yellowfive@124 1008 -- check for items that need to be equipped, do in a random order to try and defeat any unique constraint issues we might hit
yellowfive@124 1009 local slots = {}
yellowfive@124 1010 for i,s in ipairs(Amr.SlotIds) do
yellowfive@124 1011 table.insert(slots, s)
yellowfive@124 1012 end
yellowfive@124 1013 shuffle(slots)
yellowfive@124 1014
yellowfive@124 1015 for i,slotId in ipairs(slots) do
yellowfive@124 1016
yellowfive@124 1017 -- we do stuff in batches that avoids most unique conflicts
yellowfive@124 1018 local list = itemsToEquip.others
yellowfive@129 1019 if slotId == 16 then
yellowfive@129 1020 list = itemsToEquip.mh
yellowfive@129 1021 elseif slotId == 17 then
yellowfive@129 1022 list = itemsToEquip.oh
yellowfive@124 1023 elseif slotId == 11 or slotId == 12 then
yellowfive@124 1024 list = itemsToEquip.rings
yellowfive@124 1025 elseif slotId == 13 or slotId == 14 then
yellowfive@124 1026 list = itemsToEquip.trinkets
yellowfive@124 1027 end
yellowfive@124 1028
yellowfive@57 1029 local old = player.Equipped[spec][slotId]
yellowfive@57 1030 local new = gear[slotId]
yellowfive@124 1031 local prevRemaining = remaining
yellowfive@69 1032 if new then
yellowfive@124 1033 -- if the new thing is an artifact, only match the item id
yellowfive@124 1034 local newItem = Item:CreateFromItemID(new.id)
yellowfive@124 1035 local quality = newItem and newItem:GetItemQuality() or 0
yellowfive@124 1036 if quality == 6 then
yellowfive@124 1037 if not old or new.id ~= old.id then
yellowfive@124 1038 list[slotId] = new
yellowfive@129 1039 if list == itemsToEquip.mh or list == itemsToEquip.oh then
yellowfive@129 1040 itemsToEquip.weapons[slotId] = {}
yellowfive@129 1041 end
yellowfive@69 1042 remaining = remaining + 1
yellowfive@69 1043 end
yellowfive@129 1044 else
yellowfive@129 1045 -- find the best matching item anywhere in the player's gear
yellowfive@129 1046 local bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems)
yellowfive@129 1047 new = bestItem
yellowfive@129 1048
yellowfive@124 1049 local diff = countItemDifferences(old, new)
yellowfive@129 1050
yellowfive@129 1051 --[[
yellowfive@124 1052 if diff > 0 and diff < 1000 then
yellowfive@124 1053 -- same item, see if inventory has one that is closer (e.g. a duplicate item with correct enchants/gems)
yellowfive@124 1054 local bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems)
yellowfive@124 1055 if bestDiff and bestDiff < diff then
yellowfive@124 1056 new = bestItem
yellowfive@124 1057 diff = bestDiff
yellowfive@124 1058 end
yellowfive@124 1059 end
yellowfive@129 1060 ]]
yellowfive@124 1061
yellowfive@129 1062 if diff > 0 then
yellowfive@124 1063 list[slotId] = new
yellowfive@129 1064 if list == itemsToEquip.mh or list == itemsToEquip.oh then
yellowfive@129 1065 itemsToEquip.weapons[slotId] = {}
yellowfive@129 1066 end
yellowfive@124 1067 remaining = remaining + 1
yellowfive@124 1068 end
yellowfive@124 1069 end
yellowfive@129 1070 elseif old then
yellowfive@124 1071 -- need to remove this item
yellowfive@124 1072 itemsToEquip.blanks[slotId] = {}
yellowfive@124 1073 remaining = remaining + 1
yellowfive@124 1074 end
yellowfive@124 1075
yellowfive@124 1076 if remaining > prevRemaining then
yellowfive@124 1077 -- 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
yellowfive@124 1078 if old then
yellowfive@124 1079 local oldItem = Item:CreateFromItemID(old.id)
yellowfive@124 1080 if oldItem and oldItem:GetItemQuality() == 5 then
yellowfive@124 1081 itemsToEquip.legendaries[slotId] = {}
yellowfive@124 1082 end
yellowfive@57 1083 end
yellowfive@57 1084 end
yellowfive@57 1085 end
yellowfive@124 1086
yellowfive@124 1087 if remaining > 0 then
yellowfive@57 1088
yellowfive@124 1089 if passes < 5 then
yellowfive@124 1090 _pendingGearOps = {}
yellowfive@124 1091
yellowfive@129 1092 if not Amr.IsEmpty(itemsToEquip.blanks) then
yellowfive@124 1093 -- if gear set wants slots to be blank, do that first
yellowfive@124 1094 table.insert(_pendingGearOps, { items = itemsToEquip.blanks, remove = true, label = "blanks" })
yellowfive@129 1095 end
yellowfive@124 1096 if not Amr.IsEmpty(itemsToEquip.weapons) then
yellowfive@129 1097 -- change weapons first: remove both, wait, then equip each weapon one by one, waiting after each
yellowfive@129 1098 table.insert(_pendingGearOps, { items = itemsToEquip.weapons, remove = true, label = "remove weapons" })
yellowfive@129 1099 local thisWeapon = doOhFirst and itemsToEquip.oh or itemsToEquip.mh
yellowfive@129 1100 if not Amr.IsEmpty(thisWeapon) then
yellowfive@129 1101 table.insert(_pendingGearOps, { items = thisWeapon, wait = true, label = "equip weapon 1" })
yellowfive@129 1102 end
yellowfive@129 1103 thisWeapon = doOhFirst and itemsToEquip.mh or itemsToEquip.oh
yellowfive@129 1104 if not Amr.IsEmpty(thisWeapon) then
yellowfive@129 1105 table.insert(_pendingGearOps, { items = thisWeapon, wait = true, label = "equip weapon 2" })
yellowfive@129 1106 end
yellowfive@124 1107 end
yellowfive@124 1108 if not Amr.IsEmpty(itemsToEquip.legendaries) then
yellowfive@124 1109 -- remove any legendaries, wait
yellowfive@124 1110 table.insert(_pendingGearOps, { items = itemsToEquip.legendaries, remove = true, label = "remove legendaries" })
yellowfive@124 1111 end
yellowfive@124 1112 if not Amr.IsEmpty(itemsToEquip.rings) then
yellowfive@124 1113 -- remove both rings, wait, then equip new ones
yellowfive@124 1114 table.insert(_pendingGearOps, { items = itemsToEquip.rings, remove = true, label = "remove rings" })
yellowfive@124 1115 table.insert(_pendingGearOps, { items = itemsToEquip.rings, wait = true, label = "equip rings" })
yellowfive@124 1116 end
yellowfive@124 1117 if not Amr.IsEmpty(itemsToEquip.trinkets) then
yellowfive@124 1118 -- remove both trinkets, wait, then equip new ones
yellowfive@124 1119 table.insert(_pendingGearOps, { items = itemsToEquip.trinkets, remove = true, label = "remove trinkets" })
yellowfive@124 1120 table.insert(_pendingGearOps, { items = itemsToEquip.trinkets, wait = true, label = "equip trinkets" })
yellowfive@124 1121 end
yellowfive@124 1122 if not Amr.IsEmpty(itemsToEquip.others) then
yellowfive@124 1123 -- equip all other items, wait for completion
yellowfive@124 1124 table.insert(_pendingGearOps, { items = itemsToEquip.others, wait = true, label = "equip others" })
yellowfive@124 1125 end
yellowfive@124 1126
yellowfive@124 1127 -- make the last operation wait no matter what, before this gets called again to check if everything succeeded
yellowfive@124 1128 _pendingGearOps[#_pendingGearOps].wait = true
yellowfive@124 1129
yellowfive@124 1130 if not _gearOpWaiting then
yellowfive@124 1131 _gearOpWaiting = { inventory = {} }
yellowfive@124 1132 end
yellowfive@124 1133
yellowfive@124 1134 _gearOpPasses = passes
yellowfive@124 1135 _currentGearOp = _pendingGearOps[1]
yellowfive@139 1136 initializeGearOp(_currentGearOp, setupId, 1)
yellowfive@124 1137
yellowfive@124 1138 processCurrentGearOp()
yellowfive@124 1139 else
yellowfive@124 1140 -- TODO: print message that gear set couldn't be equipped
yellowfive@89 1141 end
yellowfive@57 1142
yellowfive@57 1143 else
yellowfive@89 1144 onEquipGearSetComplete()
yellowfive@124 1145 end
yellowfive@57 1146 end
yellowfive@57 1147
yellowfive@57 1148 local function onActiveTalentGroupChanged()
yellowfive@81 1149
yellowfive@57 1150 local auto = Amr.db.profile.options.autoGear
yellowfive@81 1151 local currentSpec = GetSpecialization()
yellowfive@129 1152 local waitingSpec = _waitingForSpec
yellowfive@129 1153 _waitingForSpec = 0
yellowfive@57 1154
yellowfive@139 1155 -- when spec changes, change active setup to first one for this spec (does nothing if they have no setups for this spec)
yellowfive@139 1156 if _activeSetupId then
yellowfive@139 1157 local currentSetup = getSetupById(_activeSetupId)
yellowfive@139 1158 if currentSetup.SpecSlot ~= currentSpec then
yellowfive@139 1159 Amr:PickFirstSetupForSpec()
yellowfive@139 1160 end
yellowfive@139 1161 end
yellowfive@139 1162
yellowfive@129 1163 if currentSpec == waitingSpec or auto then
yellowfive@129 1164 -- spec is what we want, now equip the gear but after a short delay because the game auto-swaps artifact weapons
yellowfive@129 1165 Amr.Wait(2, function()
yellowfive@139 1166 beginEquipGearSet(_activeSetupId, 0)
yellowfive@129 1167 end)
yellowfive@57 1168 end
yellowfive@57 1169 end
yellowfive@57 1170
yellowfive@81 1171 -- activate the specified spec and then equip the saved gear set
yellowfive@139 1172 function Amr:EquipGearSet(setupIndex)
yellowfive@57 1173
yellowfive@139 1174 -- if no argument, then cycle
yellowfive@139 1175 if not setupIndex then
yellowfive@139 1176 if not _activeSetupId then
yellowfive@139 1177 Amr:PickFirstSetupForSpec()
yellowfive@139 1178 end
yellowfive@139 1179 for i,setup in ipairs(Amr.db.char.GearSetups) do
yellowfive@139 1180 if setup.Id == _activeSetupId then
yellowfive@139 1181 setupIndex = i
yellowfive@139 1182 break
yellowfive@139 1183 end
yellowfive@139 1184 end
yellowfive@139 1185 if not setupIndex then
yellowfive@139 1186 setupIndex = 1
yellowfive@139 1187 else
yellowfive@139 1188 setupIndex = setupIndex + 1
yellowfive@139 1189 end
yellowfive@57 1190 end
yellowfive@81 1191
yellowfive@139 1192 setupIndex = tonumber(setupIndex)
yellowfive@81 1193
yellowfive@139 1194 if setupIndex > #Amr.db.char.GearSetups then
yellowfive@139 1195 setupIndex = 1
yellowfive@139 1196 end
yellowfive@139 1197
yellowfive@57 1198 if UnitAffectingCombat("player") then
yellowfive@57 1199 Amr:Print(L.GearEquipErrorCombat)
yellowfive@57 1200 return
yellowfive@57 1201 end
yellowfive@57 1202
yellowfive@139 1203 _activeSetupId = Amr.db.char.GearSetups[setupIndex].Id
yellowfive@139 1204 Amr:RefreshGearDisplay()
yellowfive@139 1205
yellowfive@139 1206 local setup = Amr.db.char.GearSetups[setupIndex]
yellowfive@81 1207 local currentSpec = GetSpecialization()
yellowfive@139 1208 if currentSpec ~= setup.SpecSlot then
yellowfive@139 1209 _waitingForSpec = setup.SpecSlot
yellowfive@139 1210 SetSpecialization(setup.SpecSlot)
yellowfive@57 1211 else
yellowfive@129 1212 -- spec is what we want, now equip the gear
yellowfive@139 1213 beginEquipGearSet(_activeSetupId, 0)
yellowfive@57 1214 end
yellowfive@57 1215 end
yellowfive@57 1216
yellowfive@124 1217 -- moves any gear in bags to the bank if not part of a gear set
yellowfive@57 1218 function Amr:CleanBags()
yellowfive@57 1219 -- TODO: implement
yellowfive@57 1220 end
yellowfive@57 1221
yellowfive@81 1222 --[[
yellowfive@81 1223 local function testfunc(message)
yellowfive@81 1224 print(strsub(message, 13))
yellowfive@81 1225 end
yellowfive@81 1226 ]]
yellowfive@81 1227
yellowfive@57 1228 function Amr:InitializeGear()
yellowfive@87 1229 Amr:AddEventHandler("ACTIVE_TALENT_GROUP_CHANGED", onActiveTalentGroupChanged)
yellowfive@57 1230
yellowfive@81 1231 --Amr:AddEventHandler("CHAT_MSG_CHANNEL", testfunc)
yellowfive@81 1232
yellowfive@57 1233 Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID)
yellowfive@57 1234 if unitID and unitID ~= "player" then return end
yellowfive@124 1235
yellowfive@124 1236 -- don't update during a gear operation, wait until it is totally finished
yellowfive@124 1237 if _pendingGearOps then return end
yellowfive@124 1238
yellowfive@139 1239 Amr:RefreshGearDisplay()
yellowfive@57 1240 end)
yellowfive@57 1241
yellowfive@124 1242 Amr:AddEventHandler("ITEM_UNLOCKED", handleItemUnlocked)
yellowfive@122 1243 end