# HG changeset patch # User yellowfive # Date 1531846659 25200 # Node ID e31b02b2448817ae16bb0f49c46e779c115fa5ab # Parent 7a6364917f86764d531410d6ff89a92730290438 Updated for 8.0 pre-patch and BfA. diff -r 7a6364917f86 -r e31b02b24488 AskMrRobot-Serializer/AskMrRobot-Serializer.lua --- a/AskMrRobot-Serializer/AskMrRobot-Serializer.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/AskMrRobot-Serializer/AskMrRobot-Serializer.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,7 +1,6 @@ -- AskMrRobot-Serializer will serialize and communicate character data between users. --- This is used primarily to associate character information to logs uploaded to askmrrobot.com. -local MAJOR, MINOR = "AskMrRobot-Serializer", 57 +local MAJOR, MINOR = "AskMrRobot-Serializer", 58 local Amr, oldminor = LibStub:NewLibrary(MAJOR, MINOR) if not Amr then return end -- already loaded by something else @@ -167,468 +166,13 @@ [1712] = true } --- just to make life easier, maps ID of each artifact weapon to the spec number (1-4) -Amr.ArtifactIdToSpecNumber = { - [128402] = 1, -- DK - [128292] = 2, - [128403] = 3, - [127829] = 1, -- DH - [128832] = 2, - [128858] = 1, -- Druid - [128860] = 2, - [128821] = 3, - [128306] = 4, - [128861] = 1, -- Hunter - [128826] = 2, - [128808] = 3, - [127857] = 1, -- Mage - [128820] = 2, - [128862] = 3, - [128938] = 1, -- Monk - [128937] = 2, - [128940] = 3, - [128823] = 1, -- Paladin - [128866] = 2, - [120978] = 3, - [128868] = 1, -- Priest - [128825] = 2, - [128827] = 3, - [128870] = 1, -- Rogue - [128872] = 2, - [128476] = 3, - [128935] = 1, -- Shaman - [128819] = 2, - [128911] = 3, - [128942] = 1, -- Warlock - [128943] = 2, - [128941] = 3, - [128910] = 1, -- Warrior - [128908] = 2, - [128289] = 3, - - --[128293] = 2, -- Frost OH - --[127830] = 1, -- Havoc OH - --[128831] = 2, -- Vengeance OH - --[128859] = 2, -- Feral OH - --[128822] = 3, -- Guardian OH - --[133959] = 2, -- Fire OH - --[133948] = 3, -- Windwalker OH - --[128867] = 2, -- Prot MH - --[133958] = 3, -- Shadow OH - --[128869] = 1, -- Rogue OH's - --[134552] = 2, - --[128479] = 3, - --[128936] = 1, -- Shaman OH's - --[128873] = 2, - --[128934] = 3, - --[137246] = 2, -- Demo MH - --[134553] = 2, -- Fury OH - --[128288] = 3 -- Prot MH -} - --- IDs of set tokens that we would care about in a player's inventory -Amr.SetTokenIds = { - [127970] = true, - [127969] = true, - [127968] = true, - [127967] = true, - [127966] = true, - [127965] = true, - [127964] = true, - [127963] = true, - [127962] = true, - [127961] = true, - [127960] = true, - [127959] = true, - [127958] = true, - [127957] = true, - [127956] = true, - [127955] = true, - [127954] = true, - [127953] = true, - [120285] = true, - [120284] = true, - [120283] = true, - [120282] = true, - [120281] = true, - [120280] = true, - [120279] = true, - [120278] = true, - [120277] = true, - [120256] = true, - [120255] = true, - [120254] = true, - [120253] = true, - [120252] = true, - [120251] = true, - [120250] = true, - [120249] = true, - [120248] = true, - [120247] = true, - [120246] = true, - [120245] = true, - [120244] = true, - [120243] = true, - [120242] = true, - [120241] = true, - [120240] = true, - [120239] = true, - [120238] = true, - [120237] = true, - [120236] = true, - [120235] = true, - [120234] = true, - [120233] = true, - [120232] = true, - [120231] = true, - [120230] = true, - [120229] = true, - [120228] = true, - [120227] = true, - [120226] = true, - [120225] = true, - [120224] = true, - [120223] = true, - [120222] = true, - [120221] = true, - [120220] = true, - [120219] = true, - [120218] = true, - [120217] = true, - [120216] = true, - [120215] = true, - [120214] = true, - [120213] = true, - [120212] = true, - [120211] = true, - [120210] = true, - [120209] = true, - [120208] = true, - [120207] = true, - [120206] = true, - [119323] = true, - [119322] = true, - [119321] = true, - [119320] = true, - [119319] = true, - [119318] = true, - [119316] = true, - [119315] = true, - [119314] = true, - [119313] = true, - [119312] = true, - [119311] = true, - [119310] = true, - [119309] = true, - [119308] = true, - [119307] = true, - [119306] = true, - [119305] = true, - [105868] = true, - [105867] = true, - [105866] = true, - [105865] = true, - [105864] = true, - [105863] = true, - [105862] = true, - [105861] = true, - [105860] = true, - [105859] = true, - [105858] = true, - [105857] = true, - [99756] = true, - [99755] = true, - [99754] = true, - [99753] = true, - [99752] = true, - [99751] = true, - [99750] = true, - [99749] = true, - [99748] = true, - [99747] = true, - [99746] = true, - [99745] = true, - [99744] = true, - [99743] = true, - [99742] = true, - [99740] = true, - [99739] = true, - [99738] = true, - [99737] = true, - [99736] = true, - [99735] = true, - [99734] = true, - [99733] = true, - [99732] = true, - [99731] = true, - [99730] = true, - [99729] = true, - [99728] = true, - [99727] = true, - [99726] = true, - [99725] = true, - [99724] = true, - [99723] = true, - [99722] = true, - [99721] = true, - [99720] = true, - [99719] = true, - [99718] = true, - [99717] = true, - [99716] = true, - [99715] = true, - [99714] = true, - [99713] = true, - [99712] = true, - [99711] = true, - [99710] = true, - [99709] = true, - [99708] = true, - [99707] = true, - [99706] = true, - [99705] = true, - [99704] = true, - [99703] = true, - [99702] = true, - [99701] = true, - [99700] = true, - [99699] = true, - [99698] = true, - [99697] = true, - [99696] = true, - [99695] = true, - [99694] = true, - [99693] = true, - [99692] = true, - [99691] = true, - [99690] = true, - [99689] = true, - [99688] = true, - [99687] = true, - [99686] = true, - [99685] = true, - [99684] = true, - [99683] = true, - [99682] = true, - [99681] = true, - [99680] = true, - [99679] = true, - [99678] = true, - [99677] = true, - [99676] = true, - [99675] = true, - [99674] = true, - [99673] = true, - [99672] = true, - [99671] = true, - [99670] = true, - [99669] = true, - [99668] = true, - [99667] = true, - [96701] = true, - [96700] = true, - [96699] = true, - [96633] = true, - [96632] = true, - [96631] = true, - [96625] = true, - [96624] = true, - [96623] = true, - [96601] = true, - [96600] = true, - [96599] = true, - [96568] = true, - [96567] = true, - [96566] = true, - [95957] = true, - [95956] = true, - [95955] = true, - [95889] = true, - [95888] = true, - [95887] = true, - [95881] = true, - [95880] = true, - [95879] = true, - [95857] = true, - [95856] = true, - [95855] = true, - [95824] = true, - [95823] = true, - [95822] = true, - [95583] = true, - [95582] = true, - [95581] = true, - [95580] = true, - [95579] = true, - [95578] = true, - [95577] = true, - [95576] = true, - [95575] = true, - [95574] = true, - [95573] = true, - [95572] = true, - [95571] = true, - [95570] = true, - [95569] = true, - [89278] = true, - [89277] = true, - [89276] = true, - [89275] = true, - [89274] = true, - [89273] = true, - [89272] = true, - [89271] = true, - [89270] = true, - [89269] = true, - [89268] = true, - [89267] = true, - [89266] = true, - [89265] = true, - [89264] = true, - [89263] = true, - [89262] = true, - [89261] = true, - [89260] = true, - [89259] = true, - [89258] = true, - [89257] = true, - [89256] = true, - [89255] = true, - [89254] = true, - [89253] = true, - [89252] = true, - [89251] = true, - [89250] = true, - [89249] = true, - [89248] = true, - [89247] = true, - [89246] = true, - [89245] = true, - [89244] = true, - [89243] = true, - [89242] = true, - [89241] = true, - [89240] = true, - [89239] = true, - [89238] = true, - [89237] = true, - [89236] = true, - [89235] = true, - [89234] = true, - [78876] = true, - [78875] = true, - [78874] = true, - [78873] = true, - [78872] = true, - [78871] = true, - [78867] = true, - [78866] = true, - [78865] = true, - [78864] = true, - [78863] = true, - [78862] = true, - [78861] = true, - [78860] = true, - [78859] = true, - [78858] = true, - [78857] = true, - [78856] = true, - [78855] = true, - [78854] = true, - [78853] = true, - [78849] = true, - [78848] = true, - [78847] = true, - [78184] = true, - [78183] = true, - [78181] = true, - [78180] = true, - [78179] = true, - [78178] = true, - [78176] = true, - [78175] = true, - [78174] = true, - [78173] = true, - [78171] = true, - [78170] = true, - [71687] = true, - [71686] = true, - [71685] = true, - [71683] = true, - [71682] = true, - [71680] = true, - [71679] = true, - [71678] = true, - [71676] = true, - [71675] = true, - [71673] = true, - [71672] = true, - [71671] = true, - [71669] = true, - [71668] = true, - [67431] = true, - [67430] = true, - [67429] = true, - [67428] = true, - [67427] = true, - [67426] = true, - [67425] = true, - [67424] = true, - [67423] = true, - [66998] = true, - [65089] = true, - [65088] = true, - [65087] = true, - [63684] = true, - [63683] = true, - [63682] = true, - [51320] = true, - [45652] = true, - [45651] = true, - [45650] = true, - [45649] = true, - [45648] = true, - [45647] = true, - [45643] = true, - [45642] = true, - [45641] = true, - [40630] = true, - [40629] = true, - [40628] = true, - [40621] = true, - [40620] = true, - [40619] = true, - [40618] = true, - [40617] = true, - [40616] = true, - [34544] = true, - [31100] = true, - [31099] = true, - [31098] = true, - [31097] = true, - [31096] = true, - [31095] = true, - [30247] = true, - [30246] = true, - [30245] = true, - [30244] = true, - [30243] = true, - [30242] = true, - [29767] = true, - [29766] = true, - [29765] = true, - [29761] = true, - [29760] = true, - [29759] = true -} - ---------------------------------------------------------------------------------------- -- Public Utility Methods ---------------------------------------------------------------------------------------- local function readBonusIdList(parts, first, last) - local ret = {} + local ret = {} for i = first, last do table.insert(ret, tonumber(parts[i])) end @@ -636,22 +180,10 @@ return ret end -local function setRelicId(item, index, relicBonuses) - local relicId = item.gemIds[index] .. "" - for i = 1, #relicBonuses do - relicId = relicId .. "." .. relicBonuses[i] - end - local list = item.gemItemIds or {} - list[i] = relicId -end - ---|color|Hitem:135820:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:playerlevel:spec?:flags:11:numBonusIDs:bonusID1:bonusID2...:playerlevelwhengotitem, 296 for warrior artifact:upgrade ID?:num artifact bonuses?:artifact bonus 1:artifact bonus 2:artifact bonus 3:[item name] --- 133004 for relic on my warrior, gem2 --- 296::3:767:1507:1809:[item name] this is for warrior artifact with the above relic in storm slot, for parts after the bonus IDs - ---|cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:unknown:unknown:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r - --- item link format: |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:unknown:unknown:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r +-- 1 2 3 4 5 6 7 8 9 10 11 12 +-- itemId:ench:gem1 :gem2 :gem3 :gem4:suf:uid:lvl:spec:flags :instdiffid:numbonusIDs:bonusIDs1...n :varies:?:relic bonus ids +--|cffe6cc80|Hitem:128866: :152046:147100:152025: : : :110:66 :16777472:9 :4 :736:1494:1490:1495:709 :1:3:3610:1472:3528:3:3562:1483:3528:3:3610:1477:3336|h[Truthguard]|h|r +-- -- get an object with all of the parts of the item link format that we care about function Amr.ParseItemLink(itemLink) if not itemLink then return nil end @@ -661,7 +193,8 @@ local parts = { strsplit(":", str) } - local item = {} + local item = {} + item.link = itemLink item.id = tonumber(parts[1]) or 0 item.enchantId = tonumber(parts[2]) or 0 item.gemIds = { tonumber(parts[3]) or 0, tonumber(parts[4]) or 0, tonumber(parts[5]) or 0, tonumber(parts[6]) or 0 } @@ -681,39 +214,38 @@ item.upgradeId = 0 item.level = 0 - -- the next part after bonus IDs depends on the upgrade id type; is either the "drop level" or upgrade ID, or not sure for artifacts + -- the next part after bonus IDs depends on the upgrade id type if upgradeIdType == 4 then item.upgradeId = tonumber(parts[14 + offset]) or 0 elseif upgradeIdType == 512 then item.level = tonumber(parts[14 + offset]) or 0 + elseif #parts > 16 + offset then + -- check for relic info + item.relicBonusIds = { nil, nil, nil } + numBonuses = tonumber(parts[16 + offset]) + if numBonuses then + if numBonuses > 0 then + item.relicBonusIds[1] = readBonusIdList(parts, 17 + offset, 16 + offset + numBonuses) + end + + offset= offset + numBonuses + if #parts > 17 + offset then + numBonuses = tonumber(parts[17 + offset]) + if numBonuses > 0 then + item.relicBonusIds[2] = readBonusIdList(parts, 18 + offset, 17 + offset + numBonuses) + end + + offset= offset + numBonuses + if #parts > 18 + offset then + numBonuses = tonumber(parts[18 + offset]) + if numBonuses > 0 then + item.relicBonusIds[3] = readBonusIdList(parts, 19 + offset, 18 + offset + numBonuses) + end + end + end + end end - -- ignore relic stuff in the item link for now, we read the relic information directly and save it with artifact power info - --[[ - -- the next part is the number of bonus IDs on the first relic slot of the artifact - numBonuses = tonumber(parts[15 + offset]) or 0 - if numBonuses > 0 then - local relicBonuses = readBonusIdList(16 + offset, 15 + offset + numBonuses, parts) - setRelicId(item, 1, relicBonuses) - end - - -- second relic slot bonus IDs - offset = offset + numBonuses - numBonuses = tonumber(parts[16 + offset]) or 0 - if numBonuses > 0 then - local relicBonuses = readBonusIdList(17 + offset, 16 + offset + numBonuses, parts) - setRelicId(item, 2, relicBonuses) - end - - -- third relic slot bonus IDs - offset = offset + numBonuses - numBonuses = tonumber(parts[17 + offset]) or 0 - if numBonuses > 0 then - local relicBonuses = readBonusIdList(18 + offset, 17 + offset + numBonuses, parts) - setRelicId(item, 3, relicBonuses) - end - ]] - return item end @@ -801,7 +333,7 @@ return tt end -function Amr.GetItemLevel(bagId, slotId, link) +function Amr.GetItemLevel(bagId, slotId, link) local itemLevelPattern = _G["ITEM_LEVEL"]:gsub("%%d", "(%%d+)") local tt = Amr.GetItemTooltip(bagId, slotId, link) @@ -836,46 +368,7 @@ end end ---[[ -local function getTalents(specPos) - local talentInfo = {} - local maxTiers = 7 - for tier = 1, maxTiers do - for col = 1, 3 do - local id, name, _, _, _, spellId, _, t, c, selected = GetTalentInfoBySpecialization(specPos, tier, col) - if selected then - talentInfo[tier] = col - end - end - end - - local str = "" - for i = 1, maxTiers do - if talentInfo[i] then - str = str .. talentInfo[i] - else - str = str .. '0' - end - end - - return str -end -]] - ---[[ -local function getGlyphs(specGroup) - local glyphs = {} - for i = 1, NUM_GLYPH_SLOTS do - local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, specGroup) - if (glyphID) then - table.insert(glyphs, glyphSpellID) - end - end - return glyphs; -end -]] - --- get specs and talents +-- get specs local function readSpecs(ret) for pos = 1, 4 do @@ -883,20 +376,67 @@ local specId = GetSpecializationInfo(pos) if specId then ret.Specs[pos] = Amr.SpecIds[specId] - -- TODO: figure out how to read inactive spec talents if possible... used to be able to but they changed it - --ret.Talents[pos] = getTalents(pos) end end end +local function dump(o) + if type(o) == 'table' then + local s = '{ ' + for k,v in pairs(o) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. dump(v) .. ',' + end + return s .. '} ' + else + return tostring(o) + end +end + +-- read azerite powers on the item in loc and put it on itemData +function Amr.ReadAzeritePowers(loc) + local ret = {} + local hasSome = false + + local tiers = C_AzeriteEmpoweredItem.GetAllTierInfo(loc) + for tier, tierInfo in ipairs(tiers) do + for _, power in ipairs(tierInfo.azeritePowerIDs) do + if C_AzeriteEmpoweredItem.IsPowerSelected(loc, power) then + local powerInfo = C_AzeriteEmpoweredItem.GetPowerInfo(power) + table.insert(ret, powerInfo.spellID) + hasSome = true + end + end + end + + if hasSome then + return ret + else + return nil + end +end + -- get currently equipped items, store with currently active spec local function readEquippedItems(ret) - local equippedItems = {}; + local equippedItems = {}; + local loc = ItemLocation.CreateEmpty() for slotNum = 1, #Amr.SlotIds do local slotId = Amr.SlotIds[slotNum] local itemLink = GetInventoryItemLink("player", slotId) if itemLink then - equippedItems[slotId] = itemLink + local itemData = Amr.ParseItemLink(itemLink) + if itemData then + -- see if this is an azerite item and read azerite power ids + loc:SetEquipmentSlot(slotId) + if C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItem(loc) then + local powers = Amr.ReadAzeritePowers(loc) + if powers then + itemData.azerite = powers + end + end + + equippedItems[slotId] = itemData + end end end @@ -904,6 +444,24 @@ ret.Equipped[GetSpecialization()] = equippedItems end +local function readHeartOfAzerothLevel(ret) + local azeriteItemLocation = C_AzeriteItem.FindActiveAzeriteItem(); + if azeriteItemLocation then + local azeriteItem = Item:CreateFromItemLocation(azeriteItemLocation); + ret.HeartOfAzerothLevel = C_AzeriteItem.GetPowerLevel(azeriteItemLocation) + else + ret.HeartOfAzerothLevel = 0 + end +end + +-- Get just the player's currently equipped gear +function Amr:GetEquipped() + local ret= {} + ret.Equipped = {} + readEquippedItems(ret) + return ret +end + -- Get all data about the player as an object, includes: -- serializer version -- region/realm/name @@ -913,7 +471,6 @@ -- level -- professions -- spec/talent for all specs --- artifact for current spec -- equipped gear for the current spec -- function Amr:GetPlayerData() @@ -926,7 +483,8 @@ ret.Guild = GetGuildInfo("player") ret.ActiveSpec = GetSpecialization() ret.Level = UnitLevel("player"); - + readHeartOfAzerothLevel(ret) + local cls, clsEn = UnitClass("player") ret.Class = clsEn; @@ -947,9 +505,7 @@ ret.Talents = {} readSpecs(ret) - ret.Artifacts = {} - - ret.Equipped = {} + ret.Equipped = {} readEquippedItems(ret) return ret @@ -997,20 +553,16 @@ local prevUpgradeId = 0 local prevBonusId = 0 local prevLevel = 0 + local prevAzeriteId = 0 + local prevRelicBonusId = 0 for i, itemData in ipairs(itemObjects) do local itemParts = {} - -- for now export the item level of artifacts as the "drop level" because it is a pain in the ass to figure it out from the bonus IDs - --local _, _, quality = GetItemInfo(itemData.link) - --if quality == 6 then - -- itemData.level = Amr.GetItemLevel(nil, nil, itemData.link) - --end - table.insert(itemParts, itemData.id - prevItemId) prevItemId = itemData.id if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end - if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end + --if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end if itemData.upgradeId ~= 0 then table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId)) prevUpgradeId = itemData.upgradeId @@ -1024,7 +576,14 @@ table.insert(itemParts, "b" .. (bValue - prevBonusId)) prevBonusId = bValue end - end + end + + if itemData.azerite then + for aIndex, aValue in ipairs(itemData.azerite) do + table.insert(itemParts, "a" .. (aValue - prevAzeriteId)) + prevAzeriteId = aValue + end + end if itemData.gemIds[1] ~= 0 then table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId)) @@ -1037,46 +596,38 @@ if itemData.gemIds[3] ~= 0 then table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId)) prevGemId = itemData.gemIds[3] - end + end if itemData.enchantId ~= 0 then table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId)) prevEnchantId = itemData.enchantId end - + + if itemData.relicBonusIds and itemData.relicBonusIds[1] ~= nil then + for bIndex, bValue in ipairs(itemData.relicBonusIds[1]) do + table.insert(itemParts, "p" .. (bValue - prevRelicBonusId)) + prevRelicBonusId = bValue + end + end + + if itemData.relicBonusIds and itemData.relicBonusIds[2] ~= nil then + for bIndex, bValue in ipairs(itemData.relicBonusIds[2]) do + table.insert(itemParts, "q" .. (bValue - prevRelicBonusId)) + prevRelicBonusId = bValue + end + end + + if itemData.relicBonusIds and itemData.relicBonusIds[3] ~= nil then + for bIndex, bValue in ipairs(itemData.relicBonusIds[3]) do + table.insert(itemParts, "r" .. (bValue - prevRelicBonusId)) + prevRelicBonusId = bValue + end + end + table.insert(fields, table.concat(itemParts, "")) end end -local function serializeCrucibleInfo(fields, info, pos, prevPowerId) - - if not info.Powers or not info.Active then - return prevPowerId - end - - local parts = {} - - if pos < 4 then - table.insert(parts, pos) - else - local relic = Amr.ParseItemLink(info.ItemLink) - table.insert(parts, Amr.GetItemUniqueId(relic) or "0") - end - - for i,powerId in ipairs(info.Powers) do - table.insert(parts, (powerId - prevPowerId) .. "") - prevPowerId = powerId - end - - for i,active in ipairs(info.Active) do - table.insert(parts, active and "1" or "0") - end - - table.insert(fields, table.concat(parts, ",")) - - return prevPowerId -end - -- Serialize just the identity portion of a player (region/realm/name) in the same format used by the full serialization function Amr:SerializePlayerIdentity(data) local fields = {} @@ -1121,7 +672,8 @@ if raceval == nil then raceval = 1 end table.insert(fields, raceval) - table.insert(fields, data.Level) + table.insert(fields, data.Level) + table.insert(fields, data.HeartOfAzerothLevel) local profs = {} local noprofs = true @@ -1147,49 +699,7 @@ if data.Specs[spec] and (complete or spec == data.ActiveSpec) then table.insert(fields, ".s" .. spec) -- indicates the start of a spec block table.insert(fields, data.Specs[spec]) - table.insert(fields, data.Talents[spec] or "") - - local powerids = {} - local powerranks = {} - local reliclinks = {} - local crucibleinfos = {} - - local artifactInfo = data.Artifacts and data.Artifacts[spec] - if artifactInfo and artifactInfo.Powers then - for k, v in spairs(artifactInfo.Powers) do - table.insert(powerids, k) - table.insert(powerranks, v) - end - end - if artifactInfo and artifactInfo.Relics then - for i, link in ipairs(artifactInfo.Relics) do - local relic = Amr.ParseItemLink(link) - table.insert(reliclinks, Amr.GetItemUniqueId(relic) or "") - end - end - if artifactInfo and artifactInfo.Crucible then - local prevPowerId = 0 - for i = 1,3 do - local relicInfo = #artifactInfo.Crucible.Equipped >= i and artifactInfo.Crucible.Equipped[i] - if relicInfo then - prevPowerId = serializeCrucibleInfo(crucibleinfos, relicInfo, i, prevPowerId) - end - end - if artifactInfo.Crucible.Previewed then - for k,relicInfo in pairs(artifactInfo.Crucible.Previewed) do - if relicInfo then - prevPowerId = serializeCrucibleInfo(crucibleinfos, relicInfo, 4, prevPowerId) - end - end - end - end - - table.insert(fields, toCompressedNumberList(powerids)) - table.insert(fields, table.concat(powerranks, ",")) - table.insert(fields, table.concat(reliclinks, ",")) - table.insert(fields, table.concat(crucibleinfos, "/")) - - --table.insert(fields, toCompressedNumberList(data.Glyphs[spec])) + table.insert(fields, data.Talents[spec] or "") end end @@ -1200,10 +710,8 @@ table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block local itemObjects = {} - for k, v in pairs(data.Equipped[spec]) do - local itemData = Amr.ParseItemLink(v) + for k, itemData in pairs(data.Equipped[spec]) do itemData.slot = k - itemData.link = v table.insert(itemObjects, itemData) end @@ -1212,58 +720,25 @@ end end - -- if doing a complete export, include reputations and bank/bag items too - if complete then - - -- export reputations - local reps = {} - table.insert(reps, "_") - --[[ - local noreps = true - if data.Reputations then - for k, v in pairs(data.Reputations) do - noreps = false - table.insert(reps, k .. ":" .. v) - end - end - if noreps then - table.insert(reps, "_") - end - ]] - - table.insert(fields, ".r") - table.insert(fields, table.concat(reps, ",")) - - -- export bag and bank + -- if doing a complete export, include bank/bag items too + if complete then + local itemObjects = {} if data.BagItems then - for i, v in ipairs(data.BagItems) do - local itemData = Amr.ParseItemLink(v) - if itemData ~= nil --[[and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id])]] then - itemData.link = v + for i, itemData in ipairs(data.BagItems) do + if itemData then table.insert(itemObjects, itemData) end end end if data.BankItems then - for i, v in ipairs(data.BankItems) do - local itemData = Amr.ParseItemLink(v) - if itemData ~= nil --[[and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id])]] then - itemData.link = v + for i, itemData in ipairs(data.BankItems) do + if itemData then table.insert(itemObjects, itemData) end end - end - if data.VoidItems then - for i, v in ipairs(data.VoidItems) do - local itemData = Amr.ParseItemLink(v) - if itemData ~= nil --[[and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id])]] then - itemData.link = v - table.insert(itemObjects, itemData) - end - end - end - + end + table.insert(fields, ".inv") appendItemsToExport(fields, itemObjects) end diff -r 7a6364917f86 -r e31b02b24488 AskMrRobot.toc --- a/AskMrRobot.toc Mon Feb 12 19:34:09 2018 -0800 +++ b/AskMrRobot.toc Tue Jul 17 09:57:39 2018 -0700 @@ -1,10 +1,10 @@ -## Interface: 70300 +## Interface: 80000 ## Title: Ask Mr. Robot ## Author: Team Robot, Inc. -## Version: 57 +## Version: 58 ## Notes: Gear import/export, combat logging, and more. ## URL: www.askmrrobot.com -## SavedVariables: AskMrRobotDb3 +## SavedVariables: AskMrRobotDb4 Libs\LibStub\LibStub.lua Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml @@ -22,8 +22,8 @@ localization\enUS.lua localization\ruRU.lua localization\ptBR.lua -##localization\frFR.lua -##localization\itIT.lua +localization\frFR.lua +localization\itIT.lua AskMrRobot-Serializer\AskMrRobot-Serializer.xml @@ -48,6 +48,4 @@ Shopping.lua Gear.lua CombatLog.lua -Loot.lua -TeamOptimizer.lua Options.lua diff -r 7a6364917f86 -r e31b02b24488 CombatLog.lua --- a/CombatLog.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/CombatLog.lua Tue Jul 17 09:57:39 2018 -0700 @@ -23,36 +23,36 @@ _autoChecks[instanceId] = {} local lbl = AceGUI:Create("AmrUiLabel") + container:AddChild(lbl) lbl:SetWidth(200) lbl:SetText(L.InstanceNames[instanceId]) lbl:SetFont(Amr.CreateFont("Regular", 20, Amr.Colors.White)) - container:AddChild(lbl) local line = AceGUI:Create("AmrUiPanel") line:SetHeight(1) line:SetBackgroundColor(Amr.Colors.White) + container:AddChild(line) line:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -7) line:SetPoint("TOPRIGHT", lbl.frame, "BOTTOMRIGHT", 0, -7) - container:AddChild(line) local chkMythic = createDifficultyCheckBox(instanceId, Amr.Difficulties.Mythic) + container:AddChild(chkMythic) chkMythic:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", 0, -8) - container:AddChild(chkMythic) local chkNormal = createDifficultyCheckBox(instanceId, Amr.Difficulties.Normal) + container:AddChild(chkNormal) chkNormal:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", 0, -30) - container:AddChild(chkNormal) -- find the widest of mythic/normal local w = math.max(chkMythic:GetWidth(), chkNormal:GetWidth()) local chkHeroic = createDifficultyCheckBox(instanceId, Amr.Difficulties.Heroic) + container:AddChild(chkHeroic) chkHeroic:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", w + 20, -8) - container:AddChild(chkHeroic) local chkLfr = createDifficultyCheckBox(instanceId, Amr.Difficulties.Lfr) + container:AddChild(chkLfr) chkLfr:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", w + 20, -30) - container:AddChild(chkLfr) return lbl, chkNormal end @@ -67,17 +67,17 @@ _btnToggle:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) _btnToggle:SetWidth(200) _btnToggle:SetHeight(26) - _btnToggle:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40) _btnToggle:SetCallback("OnClick", function() Amr:ToggleLogging() end) container:AddChild(_btnToggle) + _btnToggle:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40) _lblLogging = AceGUI:Create("AmrUiLabel") + container:AddChild(_lblLogging) _lblLogging:SetText(L.LogNote) _lblLogging:SetWidth(200) _lblLogging:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.BrightGreen)) _lblLogging:SetJustifyH("MIDDLE") _lblLogging:SetPoint("TOP", _btnToggle.frame, "BOTTOM", 0, -5) - container:AddChild(_lblLogging) local btnReload = AceGUI:Create("AmrUiButton") btnReload:SetText(L.LogButtonReloadText) @@ -85,9 +85,9 @@ btnReload:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) btnReload:SetWidth(200) btnReload:SetHeight(26) - btnReload:SetPoint("TOPLEFT", _btnToggle.frame, "TOPRIGHT", 40, 0) btnReload:SetCallback("OnClick", ReloadUI) container:AddChild(btnReload) + btnReload:SetPoint("TOPLEFT", _btnToggle.frame, "TOPRIGHT", 40, 0) --[[ local lbl = AceGUI:Create("AmrUiLabel") @@ -160,17 +160,17 @@ -- auto-logging controls local lbl = AceGUI:Create("AmrUiLabel") + container:AddChild(lbl) lbl:SetWidth(600) lbl:SetText(L.LogAutoTitle) lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive)) lbl:SetPoint("TOPLEFT", _btnToggle.frame, "BOTTOMLEFT", 0, -40) - container:AddChild(lbl) _chkAutoAll = AceGUI:Create("AmrUiCheckBox") _chkAutoAll:SetText(L.LogAutoAllText) - _chkAutoAll:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -15) _chkAutoAll:SetCallback("OnClick", function(widget) Amr:ToggleAllAutoLog() end) container:AddChild(_chkAutoAll) + _chkAutoAll:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -15) _autoChecks = {} @@ -190,8 +190,9 @@ table.insert(autoLbls, autoLbl) table.insert(autoChks, autoChk) end - autoSections = nil - + autoLbls = nil + autoChks = nil + -- instructions --[[ lbl = AceGUI:Create("AmrUiLabel") diff -r 7a6364917f86 -r e31b02b24488 Constants.lua --- a/Constants.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Constants.lua Tue Jul 17 09:57:39 2018 -0700 @@ -2,11 +2,10 @@ local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true) -- min import version that we will read from the website -Amr.MIN_IMPORT_VERSION = 36 +Amr.MIN_IMPORT_VERSION = 58 --- min addon version that we will support for inter-addon communication for e.g. the team optimizer --- last update to version 36 for Legion pre-patch -Amr.MIN_ADDON_VERSION = 44 +-- min addon version that we will support for inter-addon communication +Amr.MIN_ADDON_VERSION = 58 -- import some constants from the serializer for convenience Amr.ChatPrefix = Amr.Serializer.ChatPrefix @@ -22,11 +21,9 @@ Amr.ParseItemLink = Amr.Serializer.ParseItemLink Amr.IsSupportedInstanceId = Amr.Serializer.IsSupportedInstanceId Amr.IsSupportedInstance = Amr.Serializer.IsSupportedInstance -Amr.SetTokenIds = Amr.Serializer.SetTokenIds Amr.GetItemTooltip = Amr.Serializer.GetItemTooltip Amr.GetItemLevel = Amr.Serializer.GetItemLevel Amr.GetItemUniqueId = Amr.Serializer.GetItemUniqueId -Amr.ArtifactIdToSpecNumber = Amr.Serializer.ArtifactIdToSpecNumber -- map of slot ID to display text Amr.SlotDisplayText = { @@ -129,8 +126,10 @@ -- Item Methods ------------------------------------------------------------------------------------------ --- item link format: |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:upgradeId:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r - +-- 1 2 3 4 5 6 7 8 9 10 11 12 +-- itemId:ench:gem1 :gem2 :gem3 :gem4:suf:uid:lvl:spec:flags :instdiffid:numbonusIDs:bonusIDs1...n :varies:?:relic bonus ids +--|cffe6cc80|Hitem:128866: :152046:147100:152025: : : :110:66 :16777472:9 :4 :736:1494:1490:1495:709 :1:3:3610:1472:3528:3:3562:1483:3528:3:3610:1477:3336|h[Truthguard]|h|r +-- function Amr.CreateItemLink(itemObj) if itemObj == nil or itemObj.id == nil or itemObj.id == 0 then return nil end @@ -161,8 +160,6 @@ table.insert(parts, 4) elseif itemObj.level and itemObj.level ~= 0 then table.insert(parts, 512) - elseif itemObj.relicBonusIds then - table.insert(parts, 256) else table.insert(parts, 0) end @@ -186,25 +183,11 @@ else table.insert(parts, 0) end - - -- sometimes we provide relic bonus IDs - if itemObj.relicBonusIds then - for i = 1,3 do - local bonusList = itemObj.relicBonusIds[i] - if bonusList and #bonusList > 0 then - table.insert(parts, #bonusList) - for bi, bv in ipairs(bonusList) do - table.insert(parts, bv) - end - else - table.insert(parts, 0) - end - end - else - table.insert(parts, 0) - table.insert(parts, 0) - table.insert(parts, 0) - end + + -- we don't bother with relic bonus ids anymore when generating links + table.insert(parts, 0) + table.insert(parts, 0) + table.insert(parts, 0) return table.concat(parts, ":") end diff -r 7a6364917f86 -r e31b02b24488 Core.lua --- a/Core.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Core.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,9 +1,3 @@ --- AskMrRobot --- Does cool stuff associated with askmrrobot.com: --- Import/Export gear and optimization solutions from/to the website --- Improve the combat logging experience and augment it with extra data not available directly in the log file --- Team Optimizer convenience functionality - AskMrRobot = LibStub("AceAddon-3.0"):NewAddon("AskMrRobot", "AceEvent-3.0", "AceComm-3.0", "AceConsole-3.0", "AceSerializer-3.0") local Amr = AskMrRobot Amr.Serializer = LibStub("AskMrRobot-Serializer") @@ -13,8 +7,7 @@ -- types of inter-addon messages that we receive, used to parcel them out to the proper handlers Amr.MessageTypes = { Version = "_V", - VersionRequest = "_VR", - Team = "_T" + VersionRequest = "_VR" } local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true) @@ -27,11 +20,11 @@ icon = "Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\icon", OnClick = function(self, button, down) if button == "LeftButton" then - if IsControlKeyDown() then - Amr:Wipe() - else + --if IsControlKeyDown() then + -- Amr:Wipe() + --else Amr:Toggle() - end + --end elseif button == "RightButton" then Amr:EquipGearSet() end @@ -52,31 +45,18 @@ char = { FirstUse = true, -- true if this is first time use, gets cleared after seeing the export help splash window Talents = {}, -- for each spec, selected talents - Artifacts = {}, -- for each spec, artifact info - Equipped = {}, -- for each spec, slot id to item link - BagItems = {}, -- list of item links for bag - BankItems = {}, -- list of item links for bank - VoidItems = {}, -- list of item links for void storage + Equipped = {}, -- for each spec, slot id to item info + BagItems = {}, -- list of item info for bags + BankItems = {}, -- list of item info for bank BagItemsAndCounts = {}, -- used mainly for the shopping list BankItemsAndCounts = {}, -- used mainly for the shopping list - GearSets = {}, -- imported gear sets, key by spec group (1 or 2), slot id to item object - ExtraItemData = {}, -- for each spec group (1 or 2): mainly for legacy support, item id to object with socketColor and duplicateId information - ExtraGemData = {}, -- for each spec group (1 or 2): gem enchant id to gem display information, and data used to detect identical gems (mainly for legacy support) - ExtraEnchantData = {}, -- for each spec group (1 or 2): enchant id to enchant display information and material information + GearSets = {}, -- imported gear sets + ExtraEnchantData = {}, -- enchant id to enchant display information and material information Logging = { -- character logging settings Enabled = false, -- whether logging is currently on or not LastZone = nil, -- last zone the player was in LastDiff = nil, -- last difficulty for the last zone the player was in LastWipe = nil -- last time a wipe was called by this player - }, - TeamOpt = { - AllItems = {}, -- all equippable items no matter where it is, list of item unique ids, used to determine when a player gains a new equippable item - History = {}, -- history of drops since joining the current group - Rolls = {}, -- current loot choices for a loot distribution in progress - Role = nil, -- Leader or Member, changes UI to the mode most appropriate for this user - Loot = {}, -- the last loot seen by the master looter - LootGuid = nil, -- guid of the last unit looted by the master looter, will be "container" if there is no target - LootInProgress = false -- true if looting is currently in progress } }, profile = { @@ -84,7 +64,6 @@ hide = false }, window = {}, -- main window position settings - lootWindow = {}, -- loot window position settings shopWindow = {}, -- shopping list window position settings options = { autoGear = false, -- auto-equip saved gear sets when changing specs @@ -103,16 +82,11 @@ Wipes = {}, -- times that a wipe was called PlayerData = {}, -- player data gathered at fight start PlayerExtras = {} -- player extra data like auras, gathered at fight start - }, - TeamOpt = { -- this stuff is stored globally in case a player e.g. switches to an alt in a raid group - LootGear = {}, -- gear info that needs to be transmitted with the next loot - Rankings = {}, -- last rankings imported by the loot ranker - RankingString = nil -- last ranking string imported, kept around for efficient serialization } } } - Amr.db = LibStub("AceDB-3.0"):New("AskMrRobotDb3", defaults) + Amr.db = LibStub("AceDB-3.0"):New("AskMrRobotDb4", defaults) -- set defaults for auto logging; if a new zone is added and some other stuff was turned on, turn on the new zone too local hasSomeLogging = false @@ -175,7 +149,6 @@ Amr:InitializeGear() Amr:InitializeExport() Amr:InitializeCombatLog() - Amr:InitializeTeamOpt() end) end @@ -226,10 +199,10 @@ hide = "Hide", show = "Show", toggle = "Toggle", - equip = "EquipGearSet", -- parameter is "primary" or "secondary", or no parameter to toggle + equip = "EquipGearSet", version = "PrintVersions", - wipe = "Wipe", - undowipe = "UndoWipe", + --wipe = "Wipe", + --undowipe = "UndoWipe", reset = "Reset", test = "Test" } @@ -449,6 +422,20 @@ return string.sub(str, 1, string.len(prefix)) == prefix end +function Amr.IsEmpty(table) + return next(table) == nil +end + +function Amr.Contains(table, value) + if not table then return false end + for k,v in pairs(table) do + if v == value then + return true + end + end + return false +end + -- helper to get the unit identifiers (e.g. to pass to GetUnitName) for all members of the player's current group/raid function Amr:GetGroupUnitIdentifiers() @@ -527,12 +514,21 @@ return false end --- helper to determine if we can equip an item (it is already soulbound or account bound) +-- helper to determine if we can equip an item (it is soulbound) function Amr:CanEquip(bagId, slotId) - local tt = Amr.GetItemTooltip(bagId, slotId) - if self:IsTextInTooltip(tt, ITEM_SOULBOUND) then return true end - if self:IsTextInTooltip(tt, ITEM_BNETACCOUNTBOUND) then return true end - if self:IsTextInTooltip(tt, ITEM_ACCOUNTBOUND) then return true end + local item = Item:CreateFromBagAndSlot(bagId, slotId) + if item then + local loc = item:GetItemLocation() + return C_Item.IsBound(loc) + else + -- for now just return true if we can't find the item... will get an error trying to equip if it isn't bound + return true + end + + --local tt = Amr.GetItemTooltip(bagId, slotId) + --if self:IsTextInTooltip(tt, ITEM_SOULBOUND) then return true end + --if self:IsTextInTooltip(tt, ITEM_BNETACCOUNTBOUND) then return true end + --if self:IsTextInTooltip(tt, ITEM_ACCOUNTBOUND) then return true end end -- helper to determine if an item has a unique constraint @@ -584,6 +580,7 @@ -- any other kind of message is ignored if the version is too old if not ver or ver < Amr.MIN_ADDON_VERSION then return end + --[[ if Amr.StartsWith(message, Amr.MessageTypes.Team) then -- if fully initialized, process team optimizer messages if Amr["ProcessTeamMessage"] then @@ -595,6 +592,7 @@ self:ProcessPlayerSnapshot(message) end end + ]] end @@ -635,26 +633,60 @@ ---------------------------------------------------------------------------------------- -- Debugging ---------------------------------------------------------------------------------------- +function Amr:dump(o) + if type(o) == 'table' then + local s = '{ ' + for k,v in pairs(o) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. Amr:dump(v) .. ',' + end + return s .. '} ' + else + return tostring(o) + end +end + function Amr:Test() + --local itemLink = GetInventoryItemLink("player", 17) + + --print(itemLink) + + --local blah = Amr.ParseItemLink(itemLink) + + --print(dump(blah.relicBonusIds)) + + --[[ + --print(NUM_BANKBAGSLOTS) + + local bagId = NUM_BAG_SLOTS + 1 + + local item = Item:CreateFromBagAndSlot(bagId, 2) + if item then + print(item:GetItemName()) + else + print("no item") + end + + local numSlots = GetContainerNumSlots(bagId) + print(numSlots .. " bag slots") + ]] + + -- EquipItemByName + + --[[ + for slotId = 1, numSlots do + local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) + if itemLink ~= nil then + print(slotId .. " " .. itemLink) + end + end + ]] + + --[[ local s = "|cff0070dd|Hitem:127224:5337:0:0:0:0:0:0:100:105:512:22:2:615:656:100|h[Staff of Polarities]|h|r" Amr.GetItemInfo(s, function(obj, name, link, quality, iLevel) print(iLevel) end) + ]] end - ---[[ -function Amr:Test(val1, val2, val3) - - local link = GetLootSlotLink(tonumber(val1)) - local index = Amr:TestLootIndex(link) - print("loot index: " .. index) - - if val2 then - local candidate = Amr:TestLootCandidate(link, val2, val3) - print("loot candidate: " .. candidate) - - GiveMasterLoot(index, candidate) - end -end -]] diff -r 7a6364917f86 -r e31b02b24488 Export.lua --- a/Export.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Export.lua Tue Jul 17 09:57:39 2018 -0700 @@ -7,10 +7,10 @@ local function createLabel(container, text, width) local lbl = AceGUI:Create("AmrUiLabel") + container:AddChild(lbl) lbl:SetWidth(width or 800) lbl:SetText(text) lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) - container:AddChild(lbl) return lbl end @@ -50,23 +50,15 @@ lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -15) - lbl = createLabel(panel, L.ExportSplash3, 650) - lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) - lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -15) - - lbl2 = createLabel(panel, L.ExportSplash4, 650) - lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) - lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -15) - local btn = AceGUI:Create("AmrUiButton") btn:SetText(L.ExportSplashClose) btn:SetBackgroundColor(Amr.Colors.Green) btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) btn:SetWidth(120) btn:SetHeight(28) - btn:SetPoint("BOTTOM", panel.content, "BOTTOM", 0, 20) btn:SetCallback("OnClick", onSplashClose) panel:AddChild(btn) + btn:SetPoint("BOTTOM", panel.content, "BOTTOM", 0, 20) end -- renders the main UI for the Export tab @@ -88,10 +80,10 @@ _txt = AceGUI:Create("AmrUiTextarea") _txt:SetWidth(800) _txt:SetHeight(300) - _txt:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -20) _txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text)) _txt:SetCallback("OnTextChanged", onTextChanged) container:AddChild(_txt) + _txt:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -20) local data = self:ExportCharacter() local txt = Amr.Serializer:SerializePlayerData(data, true) @@ -119,24 +111,32 @@ -- use some local variables to deal with the fact that a user can close the bank before a scan completes local _lastBankBagId = nil local _lastBankSlotId = nil +local _bankOpen = false local function scanBag(bagId, isBank, bagTable, bagItemsWithCount) local numSlots = GetContainerNumSlots(bagId) + local loc = ItemLocation.CreateEmpty() for slotId = 1, numSlots do local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) if itemLink ~= nil then local itemData = Amr.Serializer.ParseItemLink(itemLink) if itemData ~= nil then - - -- only add equippable items to bag data - --if IsEquippableItem(itemLink) or Amr.SetTokenIds[itemData.id] then - if isBank then - _lastBankBagId = bagId - _lastBankSlotId = slotId - end + + -- see if this is an azerite item and read azerite power ids + loc:SetBagAndSlot(bagId, slotId) + if C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItem(loc) then + local powers = Amr.ReadAzeritePowers(loc) + if powers then + itemData.azerite = powers + end + end + + if isBank then + _lastBankBagId = bagId + _lastBankSlotId = slotId + end - table.insert(bagTable, itemLink) - --end + table.insert(bagTable, itemData) -- all items and counts, used for e.g. shopping list and reagents, etc. if bagItemsWithCount then @@ -151,14 +151,12 @@ end end --- get the player's current gear and save it, also returns the data from GetPlayerData for efficiency -local function getEquipped() - local data = Amr.Serializer:GetPlayerData() - local spec = GetSpecialization() +-- cache the currently equipped gear for this spec +local function cacheEquipped() + local data = Amr.Serializer:GetEquipped() + local spec = GetSpecialization() Amr.db.char.Equipped[spec] = data.Equipped[spec] - - return data end local function scanBags() @@ -184,20 +182,52 @@ scanBag(BANK_CONTAINER, true, bankItems, itemsAndCounts) scanBag(REAGENTBANK_CONTAINER, true, bankItems, itemsAndCounts) for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do - scanBag(bagId, true, bankItems, itemsAndCounts) + local bagItems = {} + local bagItemsAndCounts = {} + scanBag(bagId, true, bagItems, bagItemsAndCounts) + + bankItems[bagId] = bagItems + itemsAndCounts[bagId] = bagItemsAndCounts end -- see if the scan completed before the window closed, otherwise we don't overwrite with partial data - if _lastBankBagId ~= nil then + if _bankOpen and _lastBankBagId then local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId) - if itemLink ~= nil then --still open + if itemLink then --still open Amr.db.char.BankItems = bankItems Amr.db.char.BankItemsAndCounts = itemsAndCounts end end - end +local function onBankOpened() + _bankOpen = true + scanBank() +end + +local function onBankClosed() + _bankOpen = false +end + +-- if a bank bag is updated while the bank is open, re-scan that bag +local function onBankUpdated(bagID) + if _bankOpen and (bagID == BANK_CONTAINER or bagID == REAGENTBANK_CONTAINER or (bagID >= NUM_BAG_SLOTS + 1 and bagID <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS)) then + local bagItems = {} + local bagItemsAndCounts = {} + scanBag(bagID, true, bagItems, bagItemsAndCounts) + + -- see if the scan completed before the window closed, otherwise we don't overwrite with partial data + if _bankOpen and _lastBankBagId == bagID then + local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId) + if itemLink then + Amr.db.char.BankItems[bagID] = bagItems + Amr.db.char.BankItemsAndCounts[bagID] = bagItemsAndCounts + end + end + end +end + +--[[ -- scan the player's void storage and save the contents, must be at void storage local function scanVoid() @@ -222,25 +252,7 @@ end end - -local function getRepStanding(factionId) - local name, description, standingId, _ = GetFactionInfoByID(factionId) - return standingId - 1; -- our rep enum correspond to what the armory returns, are 1 less than what the game returns -end - -local function getReputations() - local reps = {} - - local repList = {1375,1376,1270,1269,1341,1337,1387,1388,1435} - for i, repId in pairs(repList) do - local standing = getRepStanding(repId) - if standing >= 0 then - reps[repId] = standing - end - end - - return reps -end +]] local function scanTalents() local specPos = GetSpecialization() @@ -269,279 +281,36 @@ Amr.db.char.Talents[specPos] = str end -local function scanCrucible() - if not Amr.db or not Amr.db.char or not Amr.db.char.Artifacts or not C_ArtifactRelicForgeUI or not C_ArtifactRelicForgeUI.GetSocketedRelicTalents then return end - - local equipped = {} - local preview = nil - - for i = 1,4 do - local talents = nil - if i == 4 then - talents = C_ArtifactRelicForgeUI.GetPreviewRelicTalents() - --talents = nil - else - talents = C_ArtifactRelicForgeUI.GetSocketedRelicTalents(i) - - --[[ - -- test data - if i == 1 then - talents = {} - table.insert(talents, { - powerID = 1739, - isChosen = true - }) - table.insert(talents, { - powerID = 1781, - isChosen = true - }) - table.insert(talents, { - powerID = 1770, - isChosen = false - }) - table.insert(talents, { - powerID = 791, - isChosen = false - }) - table.insert(talents, { - powerID = 786, - isChosen = false - }) - table.insert(talents, { - powerID = 1537, - isChosen = false - }) - end - ]] - end - - if talents then - local obj = { - Powers = {}, - Active = {} - } - - if i == 4 then - obj.ItemLink = C_ArtifactRelicForgeUI.GetPreviewRelicItemLink() - if not obj.ItemLink then - talents = nil - else - preview = obj - end - else - table.insert(equipped, obj) - end - - if talents then - for k,v in ipairs(talents) do - table.insert(obj.Powers, v.powerID) - table.insert(obj.Active, v.isChosen) - end - end - - elseif i ~= 4 then - table.insert(equipped, {}) - end - end - - - local itemID = C_ArtifactUI.GetArtifactInfo() - local spec = Amr.ArtifactIdToSpecNumber[itemID] - - if spec then - - -- sometimes this event can fire when no crucible data is available, don't overwrite non-blank crucible data with blank crucible data - local badEquipped = false - if Amr.db.char.Artifacts[spec] then - local oldCrucible = Amr.db.char.Artifacts[spec].Crucible - if oldCrucible then - if #oldCrucible.Equipped > 0 and oldCrucible.Equipped[1] and not equipped[1] then - badEquipped = true - end - end - end - - local dataz = Amr.db.char.Artifacts[spec] - if not dataz then - dataz = {} - Amr.db.char.Artifacts[spec] = dataz - end - - if not dataz.Crucible then - dataz.Crucible = { - Equipped = {}, - Previewed = {} - } - end - - local crucible = dataz.Crucible - - if not badEquipped then - crucible.Equipped = equipped - end - - if preview then - local previewKey = {} - table.insert(previewKey, preview.ItemLink) - for i,v in ipairs(preview.Powers) do - table.insert(previewKey, v .. "=" .. tostring(preview.Active[i])) - end - previewKey = table.concat(previewKey, "_") - - if not crucible.Previewed then - crucible.Previewed = {} - end - crucible.Previewed[previewKey] = preview - end - end -end - -local function pruneCrucible() - if not Amr.db or not Amr.db.char or not Amr.db.char.Artifacts then return end - - local spec = GetSpecialization() - local dataz = Amr.db.char.Artifacts[spec] - if not dataz or not dataz.Crucible then return end - - local crucible = dataz.Crucible - - -- this was old format, transform to new format - if crucible.Inventory then - if not crucible.Previewed then - crucible.Previewed = {} - end - - for link,preview in pairs(crucible.Inventory) do - local previewKey = {} - table.insert(previewKey, preview.ItemLink) - for i,v in ipairs(preview.Powers) do - table.insert(previewKey, v .. "=" .. tostring(preview.Active[i])) - end - previewKey = table.concat(previewKey, "_") - - crucible.Previewed[previewKey] = preview - end - - crucible.Inventory = nil - end - - -- get a hash of every owned, but not-equipped item - local ownedItems = {} - if Amr.db.char.BagItems then - for i,link in ipairs(Amr.db.char.BagItems) do - ownedItems[link] = true - end - end - if Amr.db.char.BankItems then - for i,link in ipairs(Amr.db.char.BankItems) do - ownedItems[link] = true - end - end - if Amr.db.char.VoidItems then - for i,link in ipairs(Amr.db.char.VoidItems) do - ownedItems[link] = true - end - end - - -- prune out any previewed relics that the player no longer owns - if crucible.Previewed then - local toRemove = {} - for k,v in pairs(crucible.Previewed) do - if not ownedItems[v.ItemLink] then - table.insert(toRemove, k) - end - end - for i,v in ipairs(toRemove) do - crucible.Previewed[v] = nil - end - end - -end - -local function scanArtifact() - if not Amr.db or not Amr.db.char or not Amr.db.char.Artifacts then return end - - local powers = C_ArtifactUI.GetPowers() - if not powers then return end - - local powerRanks = {} - for k,v in pairs(powers) do - local powerInfo = C_ArtifactUI.GetPowerInfo(v) - if powerInfo.currentRank - powerInfo.bonusRanks > 0 then - powerRanks[v] = powerInfo.currentRank - powerInfo.bonusRanks - end - end - - local relicInfo = {} - for i = 1,3 do - local _, _, _, link = C_ArtifactUI.GetRelicInfo(i); - table.insert(relicInfo, link or "") - end - - -- make sure that the artifact UI didn't get closed while we were reading it, GetPowers seems to return nil unless it is open - powers = C_ArtifactUI.GetPowers() - if not powers then return end - - -- use the artifact item ID to figure out which spec this is for, since you can open your artifact on any spec - local itemID = C_ArtifactUI.GetArtifactInfo() - local spec = Amr.ArtifactIdToSpecNumber[itemID] - --local spec = GetSpecialization() - - if spec then - - -- sometimes this event can fire when no relic data is available, don't overwrite non-blank relic data with blank relic data - if Amr.db.char.Artifacts[spec] then - local oldRelics = Amr.db.char.Artifacts[spec].Relics - if oldRelics then - for i = 1,3 do - if oldRelics[i] and oldRelics[i] ~= "" and (not relicInfo[i] or relicInfo[i] == "") then - relicInfo[i] = oldRelics[i] - end - end - end - end - - local dataz = Amr.db.char.Artifacts[spec] - if not dataz then - dataz = {} - Amr.db.char.Artifacts[spec] = dataz - end - - if not dataz.Crucible then - dataz.Crucible = { - Equipped = {}, - Inventory = {} - } - end - - dataz.Powers = powerRanks - dataz.Relics = relicInfo - - end - - --scanCrucible() -end - -- Returns a data object containing all information about the current player needed for an export: -- gear, spec, reputations, bag, bank, and void storage items. function Amr:ExportCharacter() - local data = getEquipped() + -- get all necessary player data + local data = Amr.Serializer:GetPlayerData() + + -- cache latest-seen equipped gear for current spec + local spec = GetSpecialization() + Amr.db.char.Equipped[spec] = data.Equipped[spec] + + -- scan current inventory just before export so that it is always fresh scanBags() -- scan current spec's talents just before exporting scanTalents() - -- prune crucible info just before each time we export - pruneCrucible() - - data.Talents = Amr.db.char.Talents - data.Artifacts = Amr.db.char.Artifacts - data.Equipped = Amr.db.char.Equipped - data.Reputations = getReputations() + data.Talents = Amr.db.char.Talents + data.Equipped = Amr.db.char.Equipped data.BagItems = Amr.db.char.BagItems - data.BankItems = Amr.db.char.BankItems - data.VoidItems = Amr.db.char.VoidItems + + -- flatten bank data (which is stored by bag for more efficient updating) + data.BankItems = {} + for k,v in pairs(Amr.db.char.BankItems) do + for i,v2 in ipairs(v) do + table.insert(data.BankItems, v2) + end + end + + --data.VoidItems = Amr.db.char.VoidItems return data end @@ -549,19 +318,17 @@ function Amr:InitializeExport() Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID) if unitID and unitID ~= "player" then return end - getEquipped() + cacheEquipped() end) end -Amr:AddEventHandler("BANKFRAME_OPENED", scanBank) -Amr:AddEventHandler("PLAYERBANKSLOTS_CHANGED", scanBank) +Amr:AddEventHandler("BANKFRAME_OPENED", onBankOpened) +Amr:AddEventHandler("BANKFRAME_CLOSED", onBankClosed) +Amr:AddEventHandler("BAG_UPDATE", onBankUpdated) -Amr:AddEventHandler("VOID_STORAGE_OPEN", scanVoid) -Amr:AddEventHandler("VOID_STORAGE_CONTENTS_UPDATE", scanVoid) -Amr:AddEventHandler("VOID_STORAGE_DEPOSIT_UPDATE", scanVoid) -Amr:AddEventHandler("VOID_STORAGE_UPDATE", scanVoid) +--Amr:AddEventHandler("VOID_STORAGE_OPEN", scanVoid) +--Amr:AddEventHandler("VOID_STORAGE_CONTENTS_UPDATE", scanVoid) +--Amr:AddEventHandler("VOID_STORAGE_DEPOSIT_UPDATE", scanVoid) +--Amr:AddEventHandler("VOID_STORAGE_UPDATE", scanVoid) Amr:AddEventHandler("PLAYER_TALENT_UPDATE", scanTalents) -Amr:AddEventHandler("ARTIFACT_UPDATE", scanArtifact) -Amr:AddEventHandler("ARTIFACT_RELIC_FORGE_UPDATE", scanCrucible) -Amr:AddEventHandler("ARTIFACT_RELIC_FORGE_PREVIEW_RELIC_CHANGED", scanCrucible) diff -r 7a6364917f86 -r e31b02b24488 Gear.lua --- a/Gear.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Gear.lua Tue Jul 17 09:57:39 2018 -0700 @@ -7,9 +7,17 @@ -- Returns a number indicating how different two items are (0 means the same, higher means more different) local function countItemDifferences(item1, item2) - if item1 == nil and item2 == nil then return 0 end - - -- different items (id + bonus ids + suffix, constitutes a different physical drop) + -- both nil, the same + if not item1 and not item2 then + return 0 + end + + -- one nil and other not, or different id, totally different + if (not item1 and item2) or (item1 and not item2) or item1.id ~= item2.id then + return 10000 + end + + -- different versions of same item (id + bonus ids + suffix + drop level, constitutes a different physical drop) if Amr.GetItemUniqueId(item1, true) ~= Amr.GetItemUniqueId(item2, true) then return 1000 end @@ -17,7 +25,42 @@ -- different upgrade levels of the same item if item1.upgradeId ~= item2.upgradeId then return 100 - end + end + + -- different azerite powers + local aztDiffs = 0 + if item1.azerite or item2.azerite then + if item1.azerite and not item2.azerite then + aztDiffs = #item1.azerite * 10 + elseif item2.azerite and not item1.azerite then + aztDiffs = #item2.azerite * 10 + else + -- count up number in item1 but missing from item2 + for i = 1, #item1.azerite do + local missing = false + for j = 1, #item2.azerite do + if item2[j] == item1[i] then + missing = false + end + end + if missing then + aztDiffs = aztDiffs + 10 + end + end + -- count up number in item2 but missing from item1 + for i = 1, #item2.azerite do + local missing = false + for j = 1, #item1.azerite do + if item1[j] == item2[i] then + missing = false + end + end + if missing then + aztDiffs = aztDiffs + 10 + end + end + end + end -- different gems local gemDiffs = 0 @@ -33,26 +76,24 @@ enchantDiff = 1 end - return gemDiffs + enchantDiff + return aztDiffs + gemDiffs + enchantDiff end -- given a table of items (keyed or indexed doesn't matter) find closest match to item, or nil if none are a match -local function findMatchingItemFromTable(item, list, bestLink, bestItem, bestDiff, bestLoc, usedItems, tableType) +local function findMatchingItemFromTable(item, list, bestItem, bestDiff, bestLoc, usedItems, tableType) if not list then return nil end local found = false - for k,v in pairs(list) do - local listItem = Amr.ParseItemLink(v) + for k,listItem in pairs(list) do if listItem then local diff = countItemDifferences(item, listItem) if diff < bestDiff then -- each physical item can only be used once, the usedItems table has items we can't use in this search local key = string.format("%s_%s", tableType, k) if not usedItems[key] then - bestLink = v bestItem = listItem bestDiff = diff - bestLoc = string.format("%s_%s", tableType, k) + bestLoc = key found = true end end @@ -60,24 +101,27 @@ end end - return bestLink, bestItem, bestDiff, bestLoc + return bestItem, bestDiff, bestLoc end --- search the player's equipped gear, bag, bank, and void storage for an item that best matches the specified item +-- search the player's equipped gear, bag, and bank for an item that best matches the specified item function Amr:FindMatchingItem(item, player, usedItems) if not item then return nil end local equipped = player.Equipped and player.Equipped[player.ActiveSpec] or nil - local bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, equipped, nil, nil, 1000, nil, usedItems, "equip") - bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BagItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "bag") - bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BankItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "bank") - bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.VoidItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "void") + local bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, equipped, nil, 10000, nil, usedItems, "equip") + bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BagItems, bestItem, bestDiff, bestLoc, usedItems, "bag") + if player.BankItems then + for bagId,bagList in pairs(player.BankItems) do + bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, bagList, bestItem, bestDiff, bestLoc, usedItems, "bank" .. bagId) + end + end - if bestDiff >= 1000 then - return nil, nil, 1000 + if bestDiff >= 10000 then + return nil, 10000 else usedItems[bestLoc] = true - return bestLink, bestItem, bestDiff + return bestItem, bestDiff end end @@ -86,25 +130,64 @@ local panelBlank = AceGUI:Create("AmrUiPanel") panelBlank:SetLayout("None") panelBlank:SetBackgroundColor(Amr.Colors.Black, 0.4) + container:AddChild(panelBlank) panelBlank:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0) panelBlank:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") - container:AddChild(panelBlank) local lbl = AceGUI:Create("AmrUiLabel") + panelBlank:AddChild(lbl) lbl:SetText(L.GearBlank) lbl:SetWidth(700) lbl:SetJustifyH("MIDDLE") lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) lbl:SetPoint("BOTTOM", panelBlank.content, "CENTER", 0, 20) - panelBlank:AddChild(lbl) local lbl2 = AceGUI:Create("AmrUiLabel") + panelBlank:AddChild(lbl2) lbl2:SetText(L.GearBlank2) lbl2:SetWidth(700) lbl2:SetJustifyH("MIDDLE") lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) lbl2:SetPoint("TOP", lbl.frame, "CENTER", 0, -20) - panelBlank:AddChild(lbl2) +end + +-- helper to create a widget for showing a socket or azerite power +local function createSocketWidget(panelMods, prevWidget, prevIsSocket, isEquipped) + + -- highlight for socket that doesn't match + local socketBorder = AceGUI:Create("AmrUiPanel") + panelMods:AddChild(socketBorder) + if not prevIsSocket then + socketBorder:SetPoint("LEFT", prevWidget.frame, "RIGHT", 30, 0) + else + socketBorder:SetPoint("LEFT", prevWidget.frame, "RIGHT", 2, 0) + end + socketBorder:SetLayout("None") + socketBorder:SetBackgroundColor(Amr.Colors.Black, isEquipped and 0 or 1) + socketBorder:SetWidth(26) + socketBorder:SetHeight(26) + if isEquipped then + socketBorder:SetAlpha(0.3) + end + + local socketBg = AceGUI:Create("AmrUiIcon") + socketBorder:AddChild(socketBg) + socketBg:SetPoint("TOPLEFT", socketBorder.content, "TOPLEFT", 1, -1) + socketBg:SetLayout("None") + socketBg:SetBorderWidth(2) + socketBg:SetIconBorderColor(Amr.Colors.Green, isEquipped and 0 or 1) + socketBg:SetWidth(24) + socketBg:SetHeight(24) + + local socketIcon = AceGUI:Create("AmrUiIcon") + socketBg:AddChild(socketIcon) + socketIcon:SetPoint("CENTER", socketBg.content, "CENTER") + socketIcon:SetBorderWidth(1) + socketIcon:SetIconBorderColor(Amr.Colors.White) + socketIcon:SetWidth(18) + socketIcon:SetHeight(18) + + return socketBorder, socketIcon end local function renderGear(spec, container) @@ -120,16 +203,16 @@ local panelGear = AceGUI:Create("AmrUiPanel") panelGear:SetLayout("None") panelGear:SetBackgroundColor(Amr.Colors.Black, 0.3) + container:AddChild(panelGear) panelGear:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0) panelGear:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT", -300, 0) - container:AddChild(panelGear) local panelMods = AceGUI:Create("AmrUiPanel") panelMods:SetLayout("None") + panelMods:SetBackgroundColor(Amr.Colors.Black, 0.3) + container:AddChild(panelMods) panelMods:SetPoint("TOPLEFT", panelGear.frame, "TOPRIGHT", 15, 0) panelMods:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") - panelMods:SetBackgroundColor(Amr.Colors.Black, 0.3) - container:AddChild(panelMods) -- spec icon local icon = AceGUI:Create("AmrUiIcon") @@ -145,8 +228,8 @@ end icon:SetIcon("Interface\\Icons\\" .. Amr.SpecIcons[iconSpec]) + panelGear:AddChild(icon) icon:SetPoint("TOPLEFT", panelGear.content, "TOPLEFT", 10, -10) - panelGear:AddChild(icon) local btnEquip = AceGUI:Create("AmrUiButton") btnEquip:SetText(L.GearButtonEquip(L.SpecsShort[player.Specs[spec]])) @@ -154,12 +237,12 @@ btnEquip:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) btnEquip:SetWidth(300) btnEquip:SetHeight(26) - btnEquip:SetPoint("LEFT", icon.frame, "RIGHT", 40, 0) - btnEquip:SetPoint("RIGHT", panelGear.content, "RIGHT", -40, 0) btnEquip:SetCallback("OnClick", function(widget) Amr:EquipGearSet(spec) end) panelGear:AddChild(btnEquip) + btnEquip:SetPoint("LEFT", icon.frame, "RIGHT", 40, 0) + btnEquip:SetPoint("RIGHT", panelGear.content, "RIGHT", -40, 0) -- each physical item can only be used once, this tracks ones we have already used local usedItems = {} @@ -169,8 +252,8 @@ for slotNum = 1, #Amr.SlotIds do local slotId = Amr.SlotIds[slotNum] - local equippedItemLink = equipped and equipped[slotId] or nil - local equippedItem = Amr.ParseItemLink(equippedItemLink) + local equippedItem = equipped and equipped[slotId] or nil + local equippedItemLink = equipped and equipped.link or nil local optimalItem = gear[slotId] local optimalItemLink = Amr.CreateItemLink(optimalItem) @@ -179,41 +262,43 @@ if equippedItem and optimalItem and Amr.GetItemUniqueId(equippedItem) == Amr.GetItemUniqueId(optimalItem) then isEquipped = true end + + local isAzerite = optimalItem and C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItemByID(optimalItem.id) -- find the item in the player's inventory that best matches what the optimization wants to use - local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) + local matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) -- slot label local lbl = AceGUI:Create("AmrUiLabel") + panelGear:AddChild(lbl) + lbl:SetPoint("TOPLEFT", prevElem.frame, "BOTTOMLEFT", 0, -12) lbl:SetText(Amr.SlotDisplayText[slotId]) lbl:SetWidth(85) lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) - lbl:SetPoint("TOPLEFT", prevElem.frame, "BOTTOMLEFT", 0, -12) - panelGear:AddChild(lbl) prevElem = lbl -- ilvl label local lblIlvl = AceGUI:Create("AmrUiLabel") + panelGear:AddChild(lblIlvl) + lblIlvl:SetPoint("TOPLEFT", lbl.frame, "TOPRIGHT", 0, 0) lblIlvl:SetWidth(45) lblIlvl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) - lblIlvl:SetPoint("TOPLEFT", lbl.frame, "TOPRIGHT", 0, 0) - panelGear:AddChild(lblIlvl) -- equipped label local lblEquipped = AceGUI:Create("AmrUiLabel") + panelGear:AddChild(lblEquipped) + lblEquipped:SetPoint("TOPLEFT", lblIlvl.frame, "TOPRIGHT", 0, 0) lblEquipped:SetWidth(20) lblEquipped:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) - lblEquipped:SetPoint("TOPLEFT", lblIlvl.frame, "TOPRIGHT", 0, 0) lblEquipped:SetText(isEquipped and "E" or "") - panelGear:AddChild(lblEquipped) -- item name/link label local lblItem = AceGUI:Create("AmrUiLabel") + panelGear:AddChild(lblItem) + lblItem:SetPoint("TOPLEFT", lblEquipped.frame, "TOPRIGHT", 0, 0) lblItem:SetWordWrap(false) lblItem:SetWidth(345) lblItem:SetFont(Amr.CreateFont(isEquipped and "Regular" or "Bold", isEquipped and 14 or 15, Amr.Colors.White)) - lblItem:SetPoint("TOPLEFT", lblEquipped.frame, "TOPRIGHT", 0, 0) - panelGear:AddChild(lblItem) -- fill the name/ilvl labels, which may require asynchronous loading of item information if optimalItemLink then @@ -221,107 +306,105 @@ -- set item name, tooltip, and ilvl obj.nameLabel:SetText(link:gsub("%[", ""):gsub("%]", "")) - -- not quite right but whatever... close enough if quality == 6 then - local tmprel = optimalItem.relicBonusIds - optimalItem.relicBonusIds = nil - link = Amr.CreateItemLink(optimalItem) - optimalItem.relicBonusIds = tmprel + -- not quite right but whatever... close enough, artifacts are a thing of the past + local tmprel = obj.optimalItem.relicBonusIds + obj.optimalItem.relicBonusIds = nil + link = Amr.CreateItemLink(obj.optimalItem) + obj.optimalItem.relicBonusIds = tmprel + + -- for artifacts, we consider it equipped if the item id alone matches + if obj.equippedItem and obj.equippedItem.id == obj.optimalItem.id then + obj.isEquipped = true + end + obj.equipLabel:SetText(obj.isEquipped and "E" or "") end - Amr:SetItemTooltip(obj.nameLabel, link) + Amr:SetItemTooltip(obj.nameLabel, link, "ANCHOR_TOPRIGHT") - -- the game's info gives the wrong item level, so we have to scan for it - --iLevel = (quality ~= 6 or optimalItem.relicBonusIds) and Amr.GetItemLevel(nil, nil, link) or "" - obj.ilvlLabel:SetText(iLevel) - - end, { ilvlLabel = lblIlvl, nameLabel = lblItem }) + local itemObj = Item:CreateFromItemLink(link) + if itemObj then + -- game's GetItemInfo method returns the wrong ilvl sometimes, so use the new item api to get it + iLevel = itemObj:GetCurrentItemLevel() + end + obj.ilvlLabel:SetText(iLevel) + + end, { + ilvlLabel = lblIlvl, + nameLabel = lblItem, + equipLabel = lblEquipped, + optimalItem = optimalItem, + equippedItem = equippedItem, + isEquipped = isEquipped + }) end -- modifications if optimalItem then - local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id] - -- gems - if itemInfo and itemInfo.socketColors then - local prevSocket = nil - for i = 1, #itemInfo.socketColors do + -- gems or azerite powers + local prevSocket = nil + + if isAzerite then + local azt = optimalItem.azerite or {} + for i,spellId in ipairs(azt) do + if spellId and spellId ~= 0 then + local equippedAzt = equippedItem and equippedItem.azerite or {} + local isPowerActive = Amr.Contains(equippedAzt, spellId) + + local socketBorder, socketIcon = createSocketWidget(panelMods, prevSocket or lblItem, prevSocket, isPowerActive) + + -- set icon and tooltip + local spellName, _, spellIcon = GetSpellInfo(spellId) + socketIcon:SetIcon(spellIcon) + Amr:SetSpellTooltip(socketIcon, spellId, "ANCHOR_TOPRIGHT") + + prevSocket = socketBorder + end + end + else + for i = 1, #optimalItem.gemIds do + -- we rely on the fact that the gear sets coming back from the site will almost always have all sockets filled, + -- because it's a pain to get the actual number of sockets on an item from within the game local g = optimalItem.gemIds[i] - local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g + if g == 0 then break end + + local isGemEquipped = matchItem and matchItem.gemIds and matchItem.gemIds[i] == g - -- highlight for gem that doesn't match - local socketBorder = AceGUI:Create("AmrUiPanel") - socketBorder:SetLayout("None") - socketBorder:SetBackgroundColor(Amr.Colors.Black, isGemEquipped and 0 or 1) - socketBorder:SetWidth(26) - socketBorder:SetHeight(26) - if not prevSocket then - socketBorder:SetPoint("LEFT", lblItem.frame, "RIGHT", 30, 0) - else - socketBorder:SetPoint("LEFT", prevSocket.frame, "RIGHT", 2, 0) - end - if isGemEquipped then - socketBorder:SetAlpha(0.3) - end - panelMods:AddChild(socketBorder) - - local socketBg = AceGUI:Create("AmrUiIcon") - socketBg:SetLayout("None") - socketBg:SetBorderWidth(2) - socketBg:SetIconBorderColor(Amr.Colors.Green, isGemEquipped and 0 or 1) - socketBg:SetWidth(24) - socketBg:SetHeight(24) - socketBg:SetPoint("TOPLEFT", socketBorder.content, "TOPLEFT", 1, -1) - socketBorder:AddChild(socketBg) - - local socketIcon = AceGUI:Create("AmrUiIcon") - socketIcon:SetBorderWidth(1) - socketIcon:SetIconBorderColor(Amr.Colors.White) - socketIcon:SetWidth(18) - socketIcon:SetHeight(18) - socketIcon:SetPoint("CENTER", socketBg.content, "CENTER") - socketBg:AddChild(socketIcon) + local socketBorder, socketIcon = createSocketWidget(panelMods, prevSocket or lblItem, prevSocket, isGemEquipped) -- get icon for optimized gem - if g ~= 0 then - local gemInfo = Amr.db.char.ExtraGemData[spec][g] - if gemInfo then - local gident = gemInfo.id - if optimalItem.relicBonusIds then - gident = Amr.CreateItemLink({ id = gemInfo.id, enchantId = 0, gemIds = {0,0,0,0}, suffixId = 0, bonusIds = optimalItem.relicBonusIds[i]}) - end - Amr.GetItemInfo(gident, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) - -- set icon and a tooltip - obj:SetIcon(texture) - Amr:SetItemTooltip(obj, link) - end, socketIcon) - end - end + Amr.GetItemInfo(g, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) + -- set icon and a tooltip + obj:SetIcon(texture) + Amr:SetItemTooltip(obj, link, "ANCHOR_TOPRIGHT") + end, socketIcon) prevSocket = socketBorder end end - + -- enchant if optimalItem.enchantId and optimalItem.enchantId ~= 0 then local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == optimalItem.enchantId local lblEnchant = AceGUI:Create("AmrUiLabel") + panelMods:AddChild(lblEnchant) + lblEnchant:SetPoint("TOPLEFT", lblItem.frame, "TOPRIGHT", 130, 0) lblEnchant:SetWordWrap(false) lblEnchant:SetWidth(170) lblEnchant:SetFont(Amr.CreateFont(isEnchantEquipped and "Regular" or "Bold", 14, isEnchantEquipped and Amr.Colors.TextGray or Amr.Colors.White)) - lblEnchant:SetPoint("TOPLEFT", lblItem.frame, "TOPRIGHT", 130, 0) - local enchInfo = Amr.db.char.ExtraEnchantData[spec][optimalItem.enchantId] + local enchInfo = Amr.db.char.ExtraEnchantData[optimalItem.enchantId] if enchInfo then lblEnchant:SetText(enchInfo.text) Amr.GetItemInfo(enchInfo.itemId, function(obj, name, link) - Amr:SetItemTooltip(obj, link) + Amr:SetItemTooltip(obj, link, "ANCHOR_TOPRIGHT") end, lblEnchant) --Amr:SetSpellTooltip(lblEnchant, enchInfo.spellId) end - panelMods:AddChild(lblEnchant) + end end @@ -349,40 +432,40 @@ btnImport:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) btnImport:SetWidth(120) btnImport:SetHeight(26) - btnImport:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -81) btnImport:SetCallback("OnClick", onImportClick) container:AddChild(btnImport) + btnImport:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -81) local lbl = AceGUI:Create("AmrUiLabel") + container:AddChild(lbl) lbl:SetText(L.GearImportNote) lbl:SetWidth(100) lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan)) lbl:SetJustifyH("MIDDLE") lbl:SetPoint("TOP", btnImport.frame, "BOTTOM", 0, -5) - container:AddChild(lbl) local lbl2 = AceGUI:Create("AmrUiLabel") + container:AddChild(lbl2) lbl2:SetText(L.GearTipTitle) lbl2:SetWidth(140) lbl2:SetFont(Amr.CreateFont("Italic", 20, Amr.Colors.Text)) lbl2:SetJustifyH("MIDDLE") lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -50) - container:AddChild(lbl2) lbl = AceGUI:Create("AmrUiLabel") + container:AddChild(lbl) lbl:SetText(L.GearTipText) lbl:SetWidth(140) lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text)) lbl:SetJustifyH("MIDDLE") lbl:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -5) - container:AddChild(lbl) lbl2 = AceGUI:Create("AmrUiLabel") + container:AddChild(lbl2) lbl2:SetText(L.GearTipCommands) lbl2:SetWidth(130) lbl2:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text)) lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 10, -5) - container:AddChild(lbl2) local t = AceGUI:Create("AmrUiTabGroup") t:SetLayout("None") @@ -397,9 +480,9 @@ t:SetTabs(tabz) t:SetCallback("OnGroupSelected", onGearTabSelected) + container:AddChild(t) t:SetPoint("TOPLEFT", container.content, "TOPLEFT", 144, -30) t:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") - container:AddChild(t) _gearTabs = t; local btnShop = AceGUI:Create("AmrUiButton") @@ -408,9 +491,9 @@ btnShop:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) btnShop:SetWidth(245) btnShop:SetHeight(26) - btnShop:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", -20, -25) btnShop:SetCallback("OnClick", function(widget) Amr:ShowShopWindow() end) container:AddChild(btnShop) + btnShop:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", -20, -25) if not _activeTab then _activeTab = tostring(GetSpecialization()) @@ -443,56 +526,13 @@ -- Gear Set Management ------------------------------------------------------------------------------------------------ local _waitingForSpec = 0 -local _waitingForItemLock = nil -local _pendingEquip = nil -local _pendingRemove = nil +local _pendingGearOps = nil +local _currentGearOp = nil +local _itemLockAction = nil +local _gearOpPasses = 0 +local _gearOpWaiting = nil --- scan a bag for the best matching item -local function scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) - local numSlots = GetContainerNumSlots(bagId) - for slotId = 1, numSlots do - local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) - -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked - if itemLink then - local bagItem = Amr.ParseItemLink(itemLink) - if bagItem ~= nil then - local diff = countItemDifferences(item, bagItem) - if diff < bestDiff then - bestItem = { bag = bagId, slot = slotId } - bestDiff = diff - bestLink = itemLink - end - end - end - end - return bestItem, bestDiff, bestLink -end - -local function onEquipGearSetComplete() - if Amr.db.profile.options.disableEm then return end - - -- create an equipment manager set - local specId, specName = GetSpecializationInfo(GetSpecialization()) - - local item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_MAINHAND)) - if not item or not Amr.ArtifactIdToSpecNumber[item.id] then - item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_OFFHAND)) - if item and not Amr.ArtifactIdToSpecNumber[item.id] then - item = nil - end - end - if item then - Amr.GetItemInfo(item.id, function(customArg, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) - local setname = "AMR " .. specName - local setid = C_EquipmentSet.GetEquipmentSetID(setname) - if setid then - C_EquipmentSet.SaveEquipmentSet(setid, texture) - else - C_EquipmentSet.CreateEquipmentSet(setname, texture) - end - end) - end -end +local beginEquipGearSet, processCurrentGearOp, nextGearOp -- find the first empty slot in the player's backpack+bags local function findFirstEmptyBagSlot() @@ -516,30 +556,35 @@ return nil, nil end -local function finishEquipGearSet() - if not _pendingEquip then return end - - _pendingEquip.tries = _pendingEquip.tries + 1 - if _pendingEquip.tries > 16 then - -- too many tries, just give up (shouldn't happen but just to be safe) - _pendingEquip = nil - else - -- start over again, trying any items that could not be equipped in the previous pass (unique constraints) - Amr:EquipGearSet(_pendingEquip.spec) +-- scan a bag for the best matching item +local function scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) + local numSlots = GetContainerNumSlots(bagId) + for slotId = 1, numSlots do + local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) + -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked + if itemLink then + local bagItem = Amr.ParseItemLink(itemLink) + if bagItem ~= nil then + local diff = countItemDifferences(item, bagItem) + if diff < bestDiff then + bestItem = { bag = bagId, slot = slotId } + bestDiff = diff + bestLink = itemLink + end + end + end end + return bestItem, bestDiff, bestLink end --- equip the next slot in a pending equip -local function tryEquipNextItem() - if not _pendingEquip then return end - - local item = _pendingEquip.itemsToEquip[_pendingEquip.nextSlot] - +-- 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 +local function findCurrentGearOpItem() + + local item = _currentGearOp.items[_currentGearOp.nextSlot] + local bestItem = nil local bestLink = nil - local bestDiff = 1000 - - -- find the best matching item + local bestDiff = 10000 -- inventory bestItem, bestDiff, bestLink = scanBagForItem(item, BACKPACK_CONTAINER, bestItem, bestDiff, bestLink) @@ -550,11 +595,11 @@ -- 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) for slotNum = 1, #Amr.SlotIds do local slotId = Amr.SlotIds[slotNum] - if not _pendingEquip.doneSlots[slotId] then + if _currentGearOp.slotsRemaining[slotId] then local itemLink = GetInventoryItemLink("player", slotId) if itemLink then local invItem = Amr.ParseItemLink(itemLink) - if invItem ~= nil then + if invItem then local diff = countItemDifferences(item, invItem) if diff < bestDiff then bestItem = { slot = slotId } @@ -567,251 +612,429 @@ end -- bank - bestItem, bestDiff, bestLink = scanBagForItem(item, BANK_CONTAINER, bestItem, bestDiff, bestLink) - for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do - bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) + if bestDiff > 0 then + bestItem, bestDiff, bestLink = scanBagForItem(item, BANK_CONTAINER, bestItem, bestDiff, bestLink) + for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do + bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink) + end end + + return bestItem, bestDiff, bestLink +end + +-- on completion, create an equipment manager set if desired +local function onEquipGearSetComplete() + if Amr.db.profile.options.disableEm then return end - ClearCursor() + -- create an equipment manager set + local specId, specName = GetSpecializationInfo(GetSpecialization()) - if not bestItem then - -- stop if we can't find an item - Amr:Print(L.GearEquipErrorNotFound) - Amr:Print(L.GearEquipErrorNotFound2) - _pendingEquip = nil - return - - 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 + local item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_MAINHAND)) + if not item then + item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_OFFHAND)) + end + if item then + Amr.GetItemInfo(item.id, function(customArg, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) + local setname = "AMR " .. specName + local setid = C_EquipmentSet.GetEquipmentSetID(setname) + if setid then + C_EquipmentSet.SaveEquipmentSet(setid, texture) + else + C_EquipmentSet.CreateEquipmentSet(setname, texture) + end + end) + end +end + +-- stop any currently in-progress gear swapping operation and clean up +local function disposeGearOp() + _pendingGearOps = nil + _currentGearOp = nil + _itemLockAction = nil + _gearOpPasses = 0 + _gearOpWaiting = nil + + -- make sure the gear tab is still in sync + Amr:RefreshGearTab() +end + +-- initialize a gear op to start running it +local function initializeGearOp(op, spec, pos) + op.pos = pos + op.spec = spec + + -- fill the remaining slot list and set the starting slot + op.nextSlot = nil + op.slotsRemaining = {} + op.isWaiting = false + for slotId, item in pairs(op.items) do + op.slotsRemaining[slotId] = true + if not op.nextSlot then + op.nextSlot = slotId + end + end +end + +function processCurrentGearOp() + if not _currentGearOp then return end + + if _currentGearOp.remove then + -- remove the next item + + -- check if the slot is already empty + local itemLink = GetInventoryItemLink("player", _currentGearOp.nextSlot) + if not itemLink then + nextGearOp() + return + end + -- find first empty bag slot local invBag, invSlot = findFirstEmptyBagSlot() if not invBag then -- stop if bags are too full Amr:Print(L.GearEquipErrorBagFull) - _pendingEquip = nil + disposeGearOp() return end - -- move from bank to bag - PickupContainerItem(bestItem.bag, bestItem.slot) + PickupInventoryItem(_currentGearOp.nextSlot) PickupContainerItem(invBag, invSlot) - -- set flag so that when we clear cursor and release the item lock, we can respond to the event and continue - _waitingForItemLock = { + -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor + _itemLockAction = { bagId = invBag, - slotId = invSlot + slotId = invSlot, + isRemove = true } + + ClearCursor() + -- wait for remove to complete + else + -- equip the next item + local bestItem, bestDiff, bestLink = findCurrentGearOpItem() + + _itemLockAction = nil ClearCursor() - - -- now we need to wait for game event to continue and try this item again after it is in our bag - return - else - if not Amr:CanEquip(bestItem.bag, bestItem.slot) then + + if not bestItem then + -- stop if we can't find an item + Amr:Print(L.GearEquipErrorNotFound) + Amr:Print(L.GearEquipErrorNotFound2) + disposeGearOp() + + 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 + -- find first empty bag slot + local invBag, invSlot = findFirstEmptyBagSlot() + if not invBag then + -- stop if bags are too full + Amr:Print(L.GearEquipErrorBagFull) + disposeGearOp() + return + end + + -- move from bank to bag + PickupContainerItem(bestItem.bag, bestItem.slot) + PickupContainerItem(invBag, invSlot) + + -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor + _itemLockAction = { + bagId = invBag, + slotId = invSlot, + isBank = true + } + + ClearCursor() + -- now we need to wait for game event to continue and try this item again after it is in our bag and unlocked + + elseif (bestItem.bag or bestItem.bag == 0) and not Amr:CanEquip(bestItem.bag, bestItem.slot) then -- if an item is not soulbound, then warn the user and quit Amr:Print(L.GearEquipErrorSoulbound(bestLink)) - _pendingEquip = nil - return + disposeGearOp() + else - local slotId = _pendingEquip.nextSlot - + -- an item in the player's bags or already equipped, equip it - _pendingEquip.bag = bestItem.bag - _pendingEquip.slot = bestItem.slot - _pendingEquip.destSlot = slotId - if bestItem.bag then PickupContainerItem(bestItem.bag, bestItem.slot) else + _gearOpWaiting.inventory[bestItem.slot] = true PickupInventoryItem(bestItem.slot) end - PickupInventoryItem(slotId) - ClearCursor() + _gearOpWaiting.inventory[_currentGearOp.nextSlot] = true + PickupInventoryItem(_currentGearOp.nextSlot) + + -- don't wait for now, do all equips at once + --[[ + -- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor + _itemLockAction = { + bagId = bestItem.bag, + slotId = bestItem.slot, + invSlot = _currentGearOp.nextSlot, + isEquip = true + } + ]] + + ClearCursor() + nextGearOp() + end + + end +end + +-- when a gear op completes successfully, this will advance to the next op or finish +function nextGearOp() + if not _currentGearOp then return end + + local spec = _currentGearOp.spec + local pos = _currentGearOp.pos + local passes = _gearOpPasses + + -- mark the slot as done and move to the next + if _currentGearOp.nextSlot then + _currentGearOp.slotsRemaining[_currentGearOp.nextSlot] = nil + _currentGearOp.nextSlot = nil + for slotId, item in pairs(_currentGearOp.items) do + if _currentGearOp.slotsRemaining[slotId] then + _currentGearOp.nextSlot = slotId + break + end + end + end + + if not _currentGearOp.nextSlot then + -- see if anything is still in progress and we want to wait for it before continuing + local inProgress = not Amr.IsEmpty(_gearOpWaiting.inventory) + + if (_currentGearOp.wait or _currentGearOp.remove) and inProgress then + -- this will cause the item unlock handler to call nextGearOp again when all in-progress swaps have unlocked related slots + _currentGearOp.isWaiting = true + else + _currentGearOp = _pendingGearOps[pos + 1] + if _currentGearOp then + -- we have another op, do it + initializeGearOp(_currentGearOp, spec, pos + 1) + processCurrentGearOp() + else + -- we are done + disposeGearOp() + + -- this will check if not all items were swapped, and either finish up, try again, or abort if have tried too many times + beginEquipGearSet(spec, passes + 1) + end + end + else + -- do the next item + processCurrentGearOp() + end + +end + +local function handleItemUnlocked(bagId, slotId) + + -- mark anything that is waiting as unlocked if it is no longer locked + if _currentGearOp and _gearOpWaiting then + for i,s in ipairs(Amr.SlotIds) do + if not IsInventoryItemLocked(s) then + _gearOpWaiting.inventory[s] = nil + end + end + end + + if _itemLockAction then + if _itemLockAction.isRemove then + -- waiting for a specific remove op to finish before continuing + if bagId == _itemLockAction.bagId and slotId == _itemLockAction.slotId then + _itemLockAction = nil + nextGearOp() + end + elseif _itemLockAction.isBank then + -- waiting for an item to move from bank into inventory, then reprocess the current op + if bagId == _itemLockAction.bagId and slotId == _itemLockAction.slotId then + _itemLockAction = nil + processCurrentGearOp() + end + + elseif _itemLockAction.isEquip then + -- this is not currently used... we do all equips at once usually, but could go back to this if it causes problems + + -- waiting for a specific equip op to finish - -- wait for game events to continue + -- inventory slot we're swapping to is still locked, can't continue yet + if IsInventoryItemLocked(_itemLockAction.invSlot) then return end + + if _itemLockAction.bagId then + local _, _, locked = GetContainerItemInfo(_itemLockAction.bagId, _itemLockAction.slotId) + -- the bag slot we're swapping from is still locked, can't continue yet + if locked then return end + else + -- inventory slot we're swapping from is still locked, can't continue yet + if IsInventoryItemLocked(_itemLockAction.slotId) then return end + end + + _itemLockAction = nil + nextGearOp() + else + -- unknown... shouldn't happen + _itemLockAction = nil end + else + + -- not waiting on a specific action, check if we are waiting for all locked slots to open up and they are done + if _currentGearOp and _gearOpWaiting and _currentGearOp.isWaiting and Amr.IsEmpty(_gearOpWaiting.inventory) then + nextGearOp() + end end end -local function removeNextItem() - if not _pendingRemove then return end - - local list = _pendingRemove.slotsToRemove - local slot = list[#list - _pendingRemove.remaining + 1] - - -- find first empty bag slot - local invBag, invSlot = findFirstEmptyBagSlot() - if not invBag then - -- stop if bags are too full - Amr:Print(L.GearEquipErrorBagFull) - _pendingRemove = nil - _pendingEquip = nil - return +local function shuffle(tbl) + local size = #tbl + for i = size, 1, -1 do + local rand = math.random(size) + tbl[i], tbl[rand] = tbl[rand], tbl[i] end - - PickupInventoryItem(slot) - PickupContainerItem(invBag, invSlot) - - -- set flag so that when we clear cursor and release the item lock, we can respond to the event and continue - _waitingForItemLock = { - bagId = invBag, - slotId = invSlot, - isRemove = true - } - - ClearCursor() + return tbl end -local function onItemUnlocked(bagId, slotId) - - if _waitingForItemLock then - -- waiting on a move from bank to bags to complete, or waiting on removing an item to complete, just continue as normal afterwards - if bagId == _waitingForItemLock.bagId and slotId == _waitingForItemLock.slotId then - local isremove = _waitingForItemLock.isRemove - _waitingForItemLock = nil - - if isremove then - _pendingRemove.remaining = _pendingRemove.remaining - 1 - if _pendingRemove.remaining > 0 then - removeNextItem() - else - -- we have removed all items that we want to remove, now do the equip - _pendingRemove = nil - tryEquipNextItem() - end - else - tryEquipNextItem() - end - end - - elseif _pendingEquip and _pendingEquip.destSlot then - -- waiting on an item swap to complete successfully so that we can go on to the next item - - -- inventory slot we're swapping to is still locked, can't continue yet - if IsInventoryItemLocked(_pendingEquip.destSlot) then return end - - if _pendingEquip.bag then - local _, _, locked = GetContainerItemInfo(_pendingEquip.bag, _pendingEquip.slot) - -- the bag slot we're swapping from is still locked, can't continue yet - if locked then return end - else - -- inventory slot we're swapping from is still locked, can't continue yet - if IsInventoryItemLocked(_pendingEquip.slot) then return end - end - - -- move on to the next item, this item is done or could not be swapped - - local item = _pendingEquip.itemsToEquip[_pendingEquip.destSlot] - local itemLink = GetInventoryItemLink("player", _pendingEquip.destSlot) - if itemLink then - local invItem = Amr.ParseItemLink(itemLink) - if invItem ~= nil then - local diff = countItemDifferences(item, invItem) - if diff == 0 then - _pendingEquip.doneSlots[_pendingEquip.destSlot] = true - end - end - end - - _pendingEquip.itemsToEquip[_pendingEquip.destSlot] = nil - _pendingEquip.destSlot = nil - _pendingEquip.bag = nil - _pendingEquip.slot = nil - - _pendingEquip.remaining = _pendingEquip.remaining - 1 - if _pendingEquip.remaining > 0 then - for slotId, item in pairs(_pendingEquip.itemsToEquip) do - _pendingEquip.nextSlot = slotId - break - end - tryEquipNextItem() - else - finishEquipGearSet() - end - - end -end - -local function startEquipGearSet(spec) +function beginEquipGearSet(spec, passes) local gear = Amr.db.char.GearSets[spec] if not gear then Amr:Print(L.GearEquipErrorEmpty) return end - + + -- ensure all our stored data is up to date local player = Amr:ExportCharacter() - local itemsToEquip = {} + local itemsToEquip = { + legendaries = {}, + weapons = {}, + rings = {}, + trinkets = {}, + others = {}, + blanks = {} + } local remaining = 0 - local usedItems = {} - local firstSlot = nil - - -- check for items that need to be equipped - for slotNum = 1, #Amr.SlotIds do - local slotId = Amr.SlotIds[slotNum] - + local usedItems = {} + + -- check for items that need to be equipped, do in a random order to try and defeat any unique constraint issues we might hit + local slots = {} + for i,s in ipairs(Amr.SlotIds) do + table.insert(slots, s) + end + shuffle(slots) + + for i,slotId in ipairs(slots) do + + -- we do stuff in batches that avoids most unique conflicts + local list = itemsToEquip.others + if slotId == 16 or slotId == 17 then + list = itemsToEquip.weapons + elseif slotId == 11 or slotId == 12 then + list = itemsToEquip.rings + elseif slotId == 13 or slotId == 14 then + list = itemsToEquip.trinkets + end + local old = player.Equipped[spec][slotId] - old = Amr.ParseItemLink(old) - local new = gear[slotId] + local prevRemaining = remaining if new then - local diff = countItemDifferences(old, new) - if diff < 1000 then - -- same item, see if inventory has one that is closer (e.g. a duplicate item with correct enchants/gems) - local bestLink, bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems) - if bestDiff and bestDiff < diff then - itemsToEquip[slotId] = new + -- if the new thing is an artifact, only match the item id + local newItem = Item:CreateFromItemID(new.id) + local quality = newItem and newItem:GetItemQuality() or 0 + if quality == 6 then + if not old or new.id ~= old.id then + list[slotId] = new remaining = remaining + 1 end - else - itemsToEquip[slotId] = new - remaining = remaining + 1 + else + local diff = countItemDifferences(old, new) + if diff > 0 and diff < 1000 then + -- same item, see if inventory has one that is closer (e.g. a duplicate item with correct enchants/gems) + local bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems) + if bestDiff and bestDiff < diff then + new = bestItem + diff = bestDiff + end + end + + if diff > 0 then + list[slotId] = new + remaining = remaining + 1 + end + end + else + -- need to remove this item + itemsToEquip.blanks[slotId] = {} + remaining = remaining + 1 + end + + if remaining > prevRemaining then + -- 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 + if old then + local oldItem = Item:CreateFromItemID(old.id) + if oldItem and oldItem:GetItemQuality() == 5 then + itemsToEquip.legendaries[slotId] = {} + end end end end + + if remaining > 0 then - if remaining > 0 then - -- if this is not our first try, then remove weapons before starting - local toRemove = {} - local removesRemaining = 0 - if _pendingEquip and _pendingEquip.tries > 0 then - for slotId, item in pairs(itemsToEquip) do - if slotId == 16 or slotId == 17 then - table.insert(toRemove, slotId) - removesRemaining = removesRemaining + 1 - end - end + if passes < 5 then + _pendingGearOps = {} + + if not Amr.IsEmpty(itemsToEquip.blanks) then + -- if gear set wants slots to be blank, do that first + table.insert(_pendingGearOps, { items = itemsToEquip.blanks, remove = true, label = "blanks" }) + end + if not Amr.IsEmpty(itemsToEquip.weapons) then + -- change weapons first: remove both, wait, then equip new ones + table.insert(_pendingGearOps, { items = itemsToEquip.weapons, remove = true, label = "remove weapons" }) + table.insert(_pendingGearOps, { items = itemsToEquip.weapons, wait = true, label = "equip weapons" }) + end + if not Amr.IsEmpty(itemsToEquip.legendaries) then + -- remove any legendaries, wait + table.insert(_pendingGearOps, { items = itemsToEquip.legendaries, remove = true, label = "remove legendaries" }) + end + if not Amr.IsEmpty(itemsToEquip.rings) then + -- remove both rings, wait, then equip new ones + table.insert(_pendingGearOps, { items = itemsToEquip.rings, remove = true, label = "remove rings" }) + table.insert(_pendingGearOps, { items = itemsToEquip.rings, wait = true, label = "equip rings" }) + end + if not Amr.IsEmpty(itemsToEquip.trinkets) then + -- remove both trinkets, wait, then equip new ones + table.insert(_pendingGearOps, { items = itemsToEquip.trinkets, remove = true, label = "remove trinkets" }) + table.insert(_pendingGearOps, { items = itemsToEquip.trinkets, wait = true, label = "equip trinkets" }) + end + if not Amr.IsEmpty(itemsToEquip.others) then + -- equip all other items, wait for completion + table.insert(_pendingGearOps, { items = itemsToEquip.others, wait = true, label = "equip others" }) + end + + -- make the last operation wait no matter what, before this gets called again to check if everything succeeded + _pendingGearOps[#_pendingGearOps].wait = true + + if not _gearOpWaiting then + _gearOpWaiting = { inventory = {} } + end + + _gearOpPasses = passes + _currentGearOp = _pendingGearOps[1] + initializeGearOp(_currentGearOp, spec, 1) + + processCurrentGearOp() + else + -- TODO: print message that gear set couldn't be equipped end - - _pendingEquip = { - tries = _pendingEquip and _pendingEquip.spec == spec and _pendingEquip.tries or 0, - spec = spec, - itemsToEquip = itemsToEquip, - remaining = remaining, - doneSlots = _pendingEquip and _pendingEquip.spec == spec and _pendingEquip.doneSlots or {}, - nextSlot = firstSlot - } - -- starting item - for slotId, item in pairs(_pendingEquip.itemsToEquip) do - _pendingEquip.nextSlot = slotId - break - end - - if removesRemaining > 0 then - _pendingRemove = { - slotsToRemove = toRemove, - remaining = removesRemaining - } - removeNextItem() - else - tryEquipNextItem() - end else - _pendingEquip = nil onEquipGearSetComplete() - end + end end local function onActiveTalentGroupChanged() @@ -821,7 +1044,7 @@ if currentSpec == _waitingForSpec or auto then -- spec is what we want, now equip the gear - startEquipGearSet(currentSpec) + beginEquipGearSet(currentSpec, 0) end _waitingForSpec = 0 @@ -856,7 +1079,7 @@ end end --- moves any gear in bags to the bank if not part of main or off spec gear set +-- moves any gear in bags to the bank if not part of a gear set function Amr:CleanBags() -- TODO: implement end @@ -874,8 +1097,12 @@ Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID) if unitID and unitID ~= "player" then return end + + -- don't update during a gear operation, wait until it is totally finished + if _pendingGearOps then return end + Amr:RefreshGearTab() end) - Amr:AddEventHandler("ITEM_UNLOCKED", onItemUnlocked) + Amr:AddEventHandler("ITEM_UNLOCKED", handleItemUnlocked) end diff -r 7a6364917f86 -r e31b02b24488 Import.lua --- a/Import.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Import.lua Tue Jul 17 09:57:39 2018 -0700 @@ -42,18 +42,18 @@ local panelImport = Amr:RenderCoverChrome(container, 700, 450) local lbl = AceGUI:Create("AmrUiLabel") + panelImport:AddChild(lbl) lbl:SetWidth(600) lbl:SetText(L.ImportHeader) lbl:SetPoint("TOP", panelImport.content, "TOP", 0, -10) - panelImport:AddChild(lbl) _txtImport = AceGUI:Create("AmrUiTextarea") _txtImport:SetWidth(600) _txtImport:SetHeight(300) - _txtImport:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10) _txtImport:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text)) _txtImport:SetCallback("OnEnterPressed", onTextEnterPressed) panelImport:AddChild(_txtImport) + _txtImport:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10) local btnImportOk = AceGUI:Create("AmrUiButton") btnImportOk:SetText(L.ImportButtonOk) @@ -61,9 +61,9 @@ btnImportOk:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) btnImportOk:SetWidth(120) btnImportOk:SetHeight(28) - btnImportOk:SetPoint("TOPLEFT", _txtImport.frame, "BOTTOMLEFT", 0, -10) btnImportOk:SetCallback("OnClick", onImportOkClick) panelImport:AddChild(btnImportOk) + btnImportOk:SetPoint("TOPLEFT", _txtImport.frame, "BOTTOMLEFT", 0, -10) local btnImportCancel = AceGUI:Create("AmrUiButton") btnImportCancel:SetText(L.ImportButtonCancel) @@ -71,16 +71,16 @@ btnImportCancel:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) btnImportCancel:SetWidth(120) btnImportCancel:SetHeight(28) - btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0) btnImportCancel:SetCallback("OnClick", onImportCancelClick) panelImport:AddChild(btnImportCancel) + btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0) _lblError = AceGUI:Create("AmrUiLabel") + panelImport:AddChild(_lblError) _lblError:SetWidth(600) _lblError:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Red)) _lblError:SetText("") _lblError:SetPoint("TOPLEFT", btnImportOk.frame, "BOTTOMLEFT", 0, -20) - panelImport:AddChild(_lblError) if fromOverwolf then -- show a cover preventing interaction until we receive data from overwolf @@ -88,18 +88,18 @@ _panelCover:SetLayout("None") _panelCover:EnableMouse(true) _panelCover:SetBackgroundColor(Amr.Colors.Black, 0.75) + panelImport:AddChild(_panelCover) _panelCover:SetPoint("TOPLEFT", panelImport.frame, "TOPLEFT") _panelCover:SetPoint("BOTTOMRIGHT", panelImport.frame, "BOTTOMRIGHT") - panelImport:AddChild(_panelCover) local coverMsg = AceGUI:Create("AmrUiLabel") + _panelCover:AddChild(coverMsg) coverMsg:SetWidth(500) coverMsg:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan)) coverMsg:SetJustifyH("MIDDLE") coverMsg:SetJustifyV("MIDDLE") coverMsg:SetText(L.ImportOverwolfWait) coverMsg:SetPoint("CENTER", _panelCover.frame, "CENTER", 0, 20) - _panelCover:AddChild(coverMsg) -- after adding, set cover to sit on top of everything _panelCover:SetStrata("FULLSCREEN_DIALOG") @@ -189,12 +189,9 @@ end -- if we make it this far, the data is valid, so read item information - local specSlot = tonumber(parts[10]) + local specSlot = tonumber(parts[11]) local importData = {} - - local itemInfo = {} - local gemInfo = {} local enchantInfo = {} local prevItemId = 0 @@ -202,7 +199,8 @@ local prevEnchantId = 0 local prevUpgradeId = 0 local prevBonusId = 0 - local prevLevel = 0 + local prevLevel = 0 + local prevAzeriteId = 0 local digits = { ["-"] = true, ["0"] = true, @@ -216,17 +214,14 @@ ["8"] = true, ["9"] = true, } - for i = 18, #parts do + for i = 16, #parts do local itemString = parts[i] if itemString ~= "" and itemString ~= "_" then local tokens = {} local bonusIds = {} - local relicBonusIds = {} - table.insert(relicBonusIds, {}) - table.insert(relicBonusIds, {}) - table.insert(relicBonusIds, {}) - local hasRelics = false + local azerite = {} local hasBonuses = false + local hasAzerites = false local token = "" local prop = "i" local tokenComplete = false @@ -258,20 +253,17 @@ elseif prop == "e" then val = val + prevEnchantId prevEnchantId = val + elseif prop == "a" then + val = val + prevAzeriteId + prevAzeriteId = val end if prop == "b" then table.insert(bonusIds, val) hasBonuses = true - elseif prop == "m" then - table.insert(relicBonusIds[1], val) - hasRelics = true - elseif prop == "n" then - table.insert(relicBonusIds[2], val) - hasRelics = true - elseif prop == "o" then - table.insert(relicBonusIds[3], val) - hasRelics = true + elseif prop == "a" then + table.insert(azerite, val) + hasAzerites = true else tokens[prop] = val end @@ -299,33 +291,14 @@ table.insert(obj.gemIds, tokens["y"] or 0) table.insert(obj.gemIds, tokens["z"] or 0) table.insert(obj.gemIds, 0) - - if hasRelics then - obj.relicBonusIds = relicBonusIds - end if hasBonuses then obj.bonusIds = bonusIds end - local itemObj = {} - itemObj.id = obj.id - itemInfo[obj.id] = itemObj - - -- look for any socket color information, add to our extra data - if tokens["c"] then - itemObj.socketColors = {} - for j = 1, string.len(tokens["c"]) do - table.insert(itemObj.socketColors, tonumber(string.sub(tokens["c"], j, j))) - end + if hasAzerites then + obj.azerite = azerite end - - -- look for item ID duplicate info, deals with old SoO items - if tokens["d"] then - itemObj.duplicateId = tonumber(tokens["d"]) - itemInfo[itemObj.duplicateId] = itemObj - end - end end @@ -334,35 +307,7 @@ for i = 1, #parts do local infoParts = { strsplit("\\", parts[i]) } - if infoParts[1] == "g" then - - local gemObj = {} - gemObj.enchantId = tonumber(infoParts[2]) - gemObj.id = tonumber(infoParts[3]) - - local identicalGems = infoParts[4] - if string.len(identicalGems) > 0 then - gemObj.identicalGroup = {} - identicalGems = { strsplit(",", identicalGems) } - for j = 1, #identicalGems do - gemObj.identicalGroup[tonumber(identicalGems[j])] = true - end - end - - gemObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.StatsShort[s] end) - if infoParts[6] == nil or string.len(infoParts[6]) == 0 then - gemObj.identicalItemGroup = {[gemObj.id]=true} - else - local identicalIds = { strsplit(',', infoParts[6]) } - gemObj.identicalItemGroup = {} - for j = 1, #identicalIds do - gemObj.identicalItemGroup[tonumber(identicalIds[j])] = true - end - end - - gemInfo[gemObj.id] = gemObj - - elseif infoParts[1] == "e" then + if infoParts[1] == "e" then local enchObj = {} enchObj.id = tonumber(infoParts[2]) @@ -380,8 +325,7 @@ end end - enchantInfo[enchObj.id] = enchObj - + enchantInfo[enchObj.id] = enchObj end end @@ -397,15 +341,14 @@ print(blah) print("bad item: " .. v.id) end - end - - + end else -- we have succeeded, record the result Amr.db.char.GearSets[specSlot] = importData - Amr.db.char.ExtraItemData[specSlot] = itemInfo - Amr.db.char.ExtraGemData[specSlot] = gemInfo - Amr.db.char.ExtraEnchantData[specSlot] = enchantInfo + + for k,v in pairs(enchantInfo) do + Amr.db.char.ExtraEnchantData[k] = v + end -- also update shopping list after import Amr:UpdateShoppingData(currentPlayerData) diff -r 7a6364917f86 -r e31b02b24488 Libs/AceComm-3.0/AceComm-3.0.lua --- a/Libs/AceComm-3.0/AceComm-3.0.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceComm-3.0/AceComm-3.0.lua Tue Jul 17 09:57:39 2018 -0700 @@ -9,7 +9,7 @@ -- make into AceComm. -- @class file -- @name AceComm-3.0 --- @release $Id: AceComm-3.0.lua 1107 2014-02-19 16:40:32Z nevcairiel $ +-- @release $Id: AceComm-3.0.lua 1174 2018-05-14 17:29:49Z h.leppkes@gmail.com $ --[[ AceComm-3.0 @@ -17,15 +17,14 @@ ]] -local MAJOR, MINOR = "AceComm-3.0", 9 +local CallbackHandler = LibStub("CallbackHandler-1.0") +local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib") +local MAJOR, MINOR = "AceComm-3.0", 12 local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR) if not AceComm then return end -local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") -local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib") - -- Lua APIs local type, next, pairs, tostring = type, next, pairs, tostring local strsub, strfind = string.sub, string.find @@ -66,7 +65,11 @@ if #prefix > 16 then -- TODO: 15? error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters") end - RegisterAddonMessagePrefix(prefix) + if C_ChatInfo then + C_ChatInfo.RegisterAddonMessagePrefix(prefix) + else + RegisterAddonMessagePrefix(prefix) + end return AceComm._RegisterComm(self, prefix, method) -- created by CallbackHandler end @@ -86,7 +89,7 @@ if not( type(prefix)=="string" and type(text)=="string" and type(distribution)=="string" and - (target==nil or type(target)=="string") and + (target==nil or type(target)=="string" or type(target)=="number") and (prio=="BULK" or prio=="NORMAL" or prio=="ALERT") ) then error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2) diff -r 7a6364917f86 -r e31b02b24488 Libs/AceComm-3.0/ChatThrottleLib.lua --- a/Libs/AceComm-3.0/ChatThrottleLib.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceComm-3.0/ChatThrottleLib.lua Tue Jul 17 09:57:39 2018 -0700 @@ -23,7 +23,7 @@ -- LICENSE: ChatThrottleLib is released into the Public Domain -- -local CTL_VERSION = 23 +local CTL_VERSION = 24 local _G = _G @@ -213,9 +213,15 @@ return ChatThrottleLib.Hook_SendChatMessage(...) end) --SendAddonMessage - hooksecurefunc("SendAddonMessage", function(...) - return ChatThrottleLib.Hook_SendAddonMessage(...) - end) + if _G.C_ChatInfo then + hooksecurefunc(_G.C_ChatInfo, "SendAddonMessage", function(...) + return ChatThrottleLib.Hook_SendAddonMessage(...) + end) + else + hooksecurefunc("SendAddonMessage", function(...) + return ChatThrottleLib.Hook_SendAddonMessage(...) + end) + end end self.nBypass = 0 end @@ -461,7 +467,7 @@ local nSize = text:len(); - if RegisterAddonMessagePrefix then + if C_ChatInfo or RegisterAddonMessagePrefix then if nSize>255 then error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2) end @@ -478,7 +484,11 @@ if not self.bQueueing and nSize < self:UpdateAvail() then self.avail = self.avail - nSize bMyTraffic = true - _G.SendAddonMessage(prefix, text, chattype, target) + if _G.C_ChatInfo then + _G.C_ChatInfo.SendAddonMessage(prefix, text, chattype, target) + else + _G.SendAddonMessage(prefix, text, chattype, target) + end bMyTraffic = false self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize if callbackFn then @@ -490,7 +500,7 @@ -- Message needs to be queued local msg = NewMsg() - msg.f = _G.SendAddonMessage + msg.f = _G.C_ChatInfo and _G.C_ChatInfo.SendAddonMessage or _G.SendAddonMessage msg[1] = prefix msg[2] = text msg[3] = chattype diff -r 7a6364917f86 -r e31b02b24488 Libs/AceEvent-3.0/AceEvent-3.0.lua --- a/Libs/AceEvent-3.0/AceEvent-3.0.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceEvent-3.0/AceEvent-3.0.lua Tue Jul 17 09:57:39 2018 -0700 @@ -9,8 +9,10 @@ -- make into AceEvent. -- @class file -- @name AceEvent-3.0 --- @release $Id: AceEvent-3.0.lua 975 2010-10-23 11:26:18Z nevcairiel $ -local MAJOR, MINOR = "AceEvent-3.0", 3 +-- @release $Id: AceEvent-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $ +local CallbackHandler = LibStub("CallbackHandler-1.0") + +local MAJOR, MINOR = "AceEvent-3.0", 4 local AceEvent = LibStub:NewLibrary(MAJOR, MINOR) if not AceEvent then return end @@ -18,8 +20,6 @@ -- Lua APIs local pairs = pairs -local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") - AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/AceGUI-3.0.lua --- a/Libs/AceGUI-3.0/AceGUI-3.0.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/AceGUI-3.0.lua Tue Jul 17 09:57:39 2018 -0700 @@ -24,8 +24,8 @@ -- f:AddChild(btn) -- @class file -- @name AceGUI-3.0 --- @release $Id: AceGUI-3.0.lua 1102 2013-10-25 14:15:23Z nevcairiel $ -local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 34 +-- @release $Id: AceGUI-3.0.lua 1177 2018-06-25 12:12:48Z nevcairiel $ +local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 36 local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR) if not AceGUI then return end -- No upgrade needed @@ -811,3 +811,221 @@ height = height + rowheight + 3 safecall(content.obj.LayoutFinished, content.obj, nil, height) end) + +-- Get alignment method and value. Possible alignment methods are a callback, a number, "start", "middle", "end", "fill" or "TOPLEFT", "BOTTOMRIGHT" etc. +local GetCellAlign = function (dir, tableObj, colObj, cellObj, cell, child) + local fn = cellObj and (cellObj["align" .. dir] or cellObj.align) + or colObj and (colObj["align" .. dir] or colObj.align) + or tableObj["align" .. dir] or tableObj.align + or "CENTERLEFT" + local child, cell, val = child or 0, cell or 0, nil + + if type(fn) == "string" then + fn = fn:lower() + fn = dir == "V" and (fn:sub(1, 3) == "top" and "start" or fn:sub(1, 6) == "bottom" and "end" or fn:sub(1, 6) == "center" and "middle") + or dir == "H" and (fn:sub(-4) == "left" and "start" or fn:sub(-5) == "right" and "end" or fn:sub(-6) == "center" and "middle") + or fn + val = (fn == "start" or fn == "fill") and 0 or fn == "end" and cell - child or (cell - child) / 2 + elseif type(fn) == "function" then + val = fn(child or 0, cell, dir) + else + val = fn + end + + return fn, max(0, min(val, cell)) +end + +-- Get width or height for multiple cells combined +local GetCellDimension = function (dir, laneDim, from, to, space) + local dim = 0 + for cell=from,to do + dim = dim + (laneDim[cell] or 0) + end + return dim + max(0, to - from) * (space or 0) +end + +--[[ Options +============ +Container: + - columns ({col, col, ...}): Column settings. "col" can be a number (<= 0: content width, <1: rel. width, <10: weight, >=10: abs. width) or a table with column setting. + - space, spaceH, spaceV: Overall, horizontal and vertical spacing between cells. + - align, alignH, alignV: Overall, horizontal and vertical cell alignment. See GetCellAlign() for possible values. +Columns: + - width: Fixed column width (nil or <=0: content width, <1: rel. width, >=1: abs. width). + - min or 1: Min width for content based width + - max or 2: Max width for content based width + - weight: Flexible column width. The leftover width after accounting for fixed-width columns is distributed to weighted columns according to their weights. + - align, alignH, alignV: Overwrites the container setting for alignment. +Cell: + - colspan: Makes a cell span multiple columns. + - rowspan: Makes a cell span multiple rows. + - align, alignH, alignV: Overwrites the container and column setting for alignment. +]] +AceGUI:RegisterLayout("Table", + function (content, children) + local obj = content.obj + obj:PauseLayout() + + local tableObj = obj:GetUserData("table") + local cols = tableObj.columns + local spaceH = tableObj.spaceH or tableObj.space or 0 + local spaceV = tableObj.spaceV or tableObj.space or 0 + local totalH = (content:GetWidth() or content.width or 0) - spaceH * (#cols - 1) + + -- We need to reuse these because layout events can come in very frequently + local layoutCache = obj:GetUserData("layoutCache") + if not layoutCache then + layoutCache = {{}, {}, {}, {}, {}, {}} + obj:SetUserData("layoutCache", layoutCache) + end + local t, laneH, laneV, rowspans, rowStart, colStart = unpack(layoutCache) + + -- Create the grid + local n, slotFound = 0 + for i,child in ipairs(children) do + if child:IsShown() then + repeat + n = n + 1 + local col = (n - 1) % #cols + 1 + local row = ceil(n / #cols) + local rowspan = rowspans[col] + local cell = rowspan and rowspan.child or child + local cellObj = cell:GetUserData("cell") + slotFound = not rowspan + + -- Rowspan + if not rowspan and cellObj and cellObj.rowspan then + rowspan = {child = child, from = row, to = row + cellObj.rowspan - 1} + rowspans[col] = rowspan + end + if rowspan and i == #children then + rowspan.to = row + end + + -- Colspan + local colspan = max(0, min((cellObj and cellObj.colspan or 1) - 1, #cols - col)) + n = n + colspan + + -- Place the cell + if not rowspan or rowspan.to == row then + t[n] = cell + rowStart[cell] = rowspan and rowspan.from or row + colStart[cell] = col + + if rowspan then + rowspans[col] = nil + end + end + until slotFound + end + end + + local rows = ceil(n / #cols) + + -- Determine fixed size cols and collect weights + local extantH, totalWeight = totalH, 0 + for col,colObj in ipairs(cols) do + laneH[col] = 0 + + if type(colObj) == "number" then + colObj = {[colObj >= 1 and colObj < 10 and "weight" or "width"] = colObj} + cols[col] = colObj + end + + if colObj.weight then + -- Weight + totalWeight = totalWeight + (colObj.weight or 1) + else + if not colObj.width or colObj.width <= 0 then + -- Content width + for row=1,rows do + local child = t[(row - 1) * #cols + col] + if child then + local f = child.frame + f:ClearAllPoints() + local childH = f:GetWidth() or 0 + + laneH[col] = max(laneH[col], childH - GetCellDimension("H", laneH, colStart[child], col - 1, spaceH)) + end + end + + laneH[col] = max(colObj.min or colObj[1] or 0, min(laneH[col], colObj.max or colObj[2] or laneH[col])) + else + -- Rel./Abs. width + laneH[col] = colObj.width < 1 and colObj.width * totalH or colObj.width + end + extantH = max(0, extantH - laneH[col]) + end + end + + -- Determine sizes based on weight + local scale = totalWeight > 0 and extantH / totalWeight or 0 + for col,colObj in pairs(cols) do + if colObj.weight then + laneH[col] = scale * colObj.weight + end + end + + -- Arrange children + for row=1,rows do + local rowV = 0 + + -- Horizontal placement and sizing + for col=1,#cols do + local child = t[(row - 1) * #cols + col] + if child then + local colObj = cols[colStart[child]] + local cellObj = child:GetUserData("cell") + local offsetH = GetCellDimension("H", laneH, 1, colStart[child] - 1, spaceH) + (colStart[child] == 1 and 0 or spaceH) + local cellH = GetCellDimension("H", laneH, colStart[child], col, spaceH) + + local f = child.frame + f:ClearAllPoints() + local childH = f:GetWidth() or 0 + + local alignFn, align = GetCellAlign("H", tableObj, colObj, cellObj, cellH, childH) + f:SetPoint("LEFT", content, offsetH + align, 0) + if child:IsFullWidth() or alignFn == "fill" or childH > cellH then + f:SetPoint("RIGHT", content, "LEFT", offsetH + align + cellH, 0) + end + + if child.DoLayout then + child:DoLayout() + end + + rowV = max(rowV, (f:GetHeight() or 0) - GetCellDimension("V", laneV, rowStart[child], row - 1, spaceV)) + end + end + + laneV[row] = rowV + + -- Vertical placement and sizing + for col=1,#cols do + local child = t[(row - 1) * #cols + col] + if child then + local colObj = cols[colStart[child]] + local cellObj = child:GetUserData("cell") + local offsetV = GetCellDimension("V", laneV, 1, rowStart[child] - 1, spaceV) + (rowStart[child] == 1 and 0 or spaceV) + local cellV = GetCellDimension("V", laneV, rowStart[child], row, spaceV) + + local f = child.frame + local childV = f:GetHeight() or 0 + + local alignFn, align = GetCellAlign("V", tableObj, colObj, cellObj, cellV, childV) + if child:IsFullHeight() or alignFn == "fill" then + f:SetHeight(cellV) + end + f:SetPoint("TOP", content, 0, -(offsetV + align)) + end + end + end + + -- Calculate total height + local totalV = GetCellDimension("V", laneV, 1, #laneV, spaceV) + + -- Cleanup + for _,v in pairs(layoutCache) do wipe(v) end + + safecall(obj.LayoutFinished, obj, nil, totalV) + obj:ResumeLayout() + end) diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,7 +1,7 @@ --[[----------------------------------------------------------------------------- Frame Container -------------------------------------------------------------------------------]] -local Type, Version = "Frame", 25 +local Type, Version = "Frame", 26 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -21,7 +21,7 @@ Scripts -------------------------------------------------------------------------------]] local function Button_OnClick(frame) - PlaySound("gsTitleOptionExit") + PlaySound(799) -- SOUNDKIT.GS_TITLE_OPTION_EXIT frame.obj:Hide() end diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua Tue Jul 17 09:57:39 2018 -0700 @@ -2,7 +2,7 @@ TabGroup Container Container that uses tabs on top to switch between groups. -------------------------------------------------------------------------------]] -local Type, Version = "TabGroup", 35 +local Type, Version = "TabGroup", 36 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -63,7 +63,7 @@ -------------------------------------------------------------------------------]] local function Tab_OnClick(frame) if not (frame.selected or frame.disabled) then - PlaySound("igCharacterInfoTab") + PlaySound(841) -- SOUNDKIT.IG_CHARACTER_INFO_TAB frame.obj:SelectTab(frame.value) end end diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua Tue Jul 17 09:57:39 2018 -0700 @@ -2,10 +2,12 @@ TreeGroup Container Container that uses a tree control to switch between groups. -------------------------------------------------------------------------------]] -local Type, Version = "TreeGroup", 40 +local Type, Version = "TreeGroup", 41 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end +local WoW80 = select(4, GetBuildInfo()) >= 80000 + -- Lua APIs local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type local math_min, math_max, floor = math.min, math.max, floor @@ -162,7 +164,7 @@ local function FirstFrameUpdate(frame) local self = frame.obj frame:SetScript("OnUpdate", nil) - self:RefreshTree() + self:RefreshTree(nil, true) end local function BuildUniqueValue(...) @@ -300,6 +302,8 @@ ["OnRelease"] = function(self) self.status = nil + self.tree = nil + self.frame:SetScript("OnUpdate", nil) for k, v in pairs(self.localstatus) do if k == "groups" then for k2 in pairs(v) do @@ -388,8 +392,8 @@ end end, - ["RefreshTree"] = function(self,scrollToSelection) - local buttons = self.buttons + ["RefreshTree"] = function(self,scrollToSelection,fromOnUpdate) + local buttons = self.buttons local lines = self.lines for i, v in ipairs(buttons) do @@ -420,6 +424,12 @@ local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18)) if maxlines <= 0 then return end + -- workaround for lag spikes on WoW 8.0 + if WoW80 and self.frame:GetParent() == UIParent and not fromOnUpdate then + self.frame:SetScript("OnUpdate", FirstFrameUpdate) + return + end + local first, last scrollToSelection = status.scrollToSelection diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua Tue Jul 17 09:57:39 2018 -0700 @@ -21,7 +21,7 @@ ]] do local Type = "Window" - local Version = 5 + local Version = 6 local function frameOnShow(this) this.obj:Fire("OnShow") @@ -32,7 +32,7 @@ end local function closeOnClick(this) - PlaySound("gsTitleOptionExit") + PlaySound(799) -- SOUNDKIT.GS_TITLE_OPTION_EXIT this.obj:Hide() end diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua Tue Jul 17 09:57:39 2018 -0700 @@ -2,7 +2,7 @@ Button Widget Graphical Button. -------------------------------------------------------------------------------]] -local Type, Version = "Button", 23 +local Type, Version = "Button", 24 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -18,7 +18,7 @@ -------------------------------------------------------------------------------]] local function Button_OnClick(frame, ...) AceGUI:ClearFocus() - PlaySound("igMainMenuOption") + PlaySound(852) -- SOUNDKIT.IG_MAINMENU_OPTION frame.obj:Fire("OnClick", ...) end diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,7 +1,7 @@ --[[----------------------------------------------------------------------------- Checkbox Widget -------------------------------------------------------------------------------]] -local Type, Version = "CheckBox", 22 +local Type, Version = "CheckBox", 23 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -60,9 +60,9 @@ self:ToggleChecked() if self.checked then - PlaySound("igMainMenuOptionCheckBoxOn") + PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON else -- for both nil and false (tristate) - PlaySound("igMainMenuOptionCheckBoxOff") + PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF end self:Fire("OnValueChanged", self.checked) diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,4 +1,4 @@ ---[[ $Id: AceGUIWidget-DropDown-Items.lua 1153 2016-11-20 09:57:15Z nevcairiel $ ]]-- +--[[ $Id: AceGUIWidget-DropDown-Items.lua 1167 2017-08-29 22:08:48Z funkydude $ ]]-- local AceGUI = LibStub("AceGUI-3.0") @@ -323,7 +323,7 @@ -- Does not close the pullout on click. do local widgetType = "Dropdown-Item-Toggle" - local widgetVersion = 3 + local widgetVersion = 4 local function UpdateToggle(self) if self.value then @@ -343,9 +343,9 @@ if self.disabled then return end self.value = not self.value if self.value then - PlaySound("igMainMenuOptionCheckBoxOn") + PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON else - PlaySound("igMainMenuOptionCheckBoxOff") + PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF end UpdateToggle(self) self:Fire("OnValueChanged", self.value) diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,4 +1,4 @@ ---[[ $Id: AceGUIWidget-DropDown.lua 1116 2014-10-12 08:15:46Z nevcairiel $ ]]-- +--[[ $Id: AceGUIWidget-DropDown.lua 1167 2017-08-29 22:08:48Z funkydude $ ]]-- local AceGUI = LibStub("AceGUI-3.0") -- Lua APIs @@ -356,7 +356,7 @@ do local widgetType = "Dropdown" - local widgetVersion = 30 + local widgetVersion = 31 --[[ Static data ]]-- @@ -381,7 +381,7 @@ local function Dropdown_TogglePullout(this) local self = this.obj - PlaySound("igMainMenuOptionCheckBoxOn") -- missleading name, but the Blizzard code uses this sound + PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON if self.open then self.open = nil self.pullout:Close() diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,7 +1,7 @@ --[[----------------------------------------------------------------------------- EditBox Widget -------------------------------------------------------------------------------]] -local Type, Version = "EditBox", 26 +local Type, Version = "EditBox", 28 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -73,7 +73,7 @@ local value = frame:GetText() local cancel = self:Fire("OnEnterPressed", value) if not cancel then - PlaySound("igMainMenuOptionCheckBoxOn") + PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON HideButton(self) end end @@ -81,23 +81,21 @@ local function EditBox_OnReceiveDrag(frame) local self = frame.obj local type, id, info = GetCursorInfo() + local name if type == "item" then - self:SetText(info) - self:Fire("OnEnterPressed", info) - ClearCursor() + name = info elseif type == "spell" then - local name = GetSpellInfo(id, info) + name = GetSpellInfo(id, info) + elseif type == "macro" then + name = GetMacroInfo(id) + end + if name then self:SetText(name) self:Fire("OnEnterPressed", name) ClearCursor() - elseif type == "macro" then - local name = GetMacroInfo(id) - self:SetText(name) - self:Fire("OnEnterPressed", name) - ClearCursor() + HideButton(self) + AceGUI:ClearFocus() end - HideButton(self) - AceGUI:ClearFocus() end local function EditBox_OnTextChanged(frame) diff -r 7a6364917f86 -r e31b02b24488 Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua --- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua Tue Jul 17 09:57:39 2018 -0700 @@ -2,7 +2,7 @@ Slider Widget Graphical Slider, like, for Range values. -------------------------------------------------------------------------------]] -local Type, Version = "Slider", 21 +local Type, Version = "Slider", 22 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -108,7 +108,7 @@ end if value then - PlaySound("igMainMenuOptionCheckBoxOn") + PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON self.slider:SetValue(value) self:Fire("OnMouseUp", value) end diff -r 7a6364917f86 -r e31b02b24488 Options.lua --- a/Options.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Options.lua Tue Jul 17 09:57:39 2018 -0700 @@ -29,7 +29,7 @@ end end -local function onCheckClick(widget) +local function onCheckClick(widget) local setting = widget:GetUserData("setting") local val = widget:GetChecked() @@ -56,11 +56,11 @@ container:AddChild(chk) local desc = AceGUI:Create("AmrUiLabel") + container:AddChild(desc) desc:SetWidth(800) desc:SetText(description) desc:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan)) desc:SetPoint("TOPLEFT", chk.frame, "BOTTOMLEFT", 24, -3) - container:AddChild(desc) return chk, desc end @@ -77,18 +77,18 @@ container:AddChild(txt) local lbl = AceGUI:Create("AmrUiLabel") + container:AddChild(lbl) lbl:SetWidth(600) lbl:SetText(text) lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) lbl:SetPoint("LEFT", txt.frame, "RIGHT", 6, 0) - container:AddChild(lbl) local desc = AceGUI:Create("AmrUiLabel") + container:AddChild(desc) desc:SetWidth(800) desc:SetText(description) desc:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan)) desc:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -4) - container:AddChild(desc) return txt, desc end @@ -97,11 +97,11 @@ function Amr:RenderTabOptions(container) local header = AceGUI:Create("AmrUiLabel") + container:AddChild(header) header:SetWidth(600) header:SetText(L.OptionsHeaderGeneral) header:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive)) header:SetPoint("TOPLEFT", container.content, "TOPLEFT", 12, -40) - container:AddChild(header) local desc, desc2 @@ -126,6 +126,10 @@ function Amr:ReleaseTabOptions() _chkMinimap = nil + _chkAutoGear = nil + _chkAh = nil + _chkEm = nil + _txtScale = nil end function Amr:RefreshOptionsUi() diff -r 7a6364917f86 -r e31b02b24488 Shopping.lua --- a/Shopping.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/Shopping.lua Tue Jul 17 09:57:39 2018 -0700 @@ -59,7 +59,9 @@ end end + function Amr:ShowShopWindow() + if not _frameShop then _frameShop = AceGUI:Create("AmrUiFrame") _frameShop:SetStatusTable(Amr.db.profile.shopWindow) -- window position is remembered in db @@ -76,13 +78,13 @@ end local lbl = AceGUI:Create("AmrUiLabel") + _frameShop:AddChild(lbl) lbl:SetWidth(400) lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White)) lbl:SetText(L.ShopTitle) lbl:SetWordWrap(false) lbl:SetJustifyH("CENTER") lbl:SetPoint("TOP", _frameShop.content, "TOP", 0, 30) - _frameShop:AddChild(lbl) lbl:SetCallback("OnMouseDown", function(widget) _frameShop:StartMove() end) lbl:SetCallback("OnMouseUp", function(widget) _frameShop:EndMove() end) @@ -90,40 +92,40 @@ -- player picker _cboPlayers = AceGUI:Create("AmrUiDropDown") _cboPlayers:SetWidth(400) + _frameShop:AddChild(_cboPlayers) _cboPlayers:SetPoint("TOPLEFT", _frameShop.content, "TOPLEFT", 0, -30) - _frameShop:AddChild(_cboPlayers) -- spec pickers _chk1 = AceGUI:Create("AmrUiCheckBox") - _chk1:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -20) _chk1:SetUserData("spec", 1) _chk1:SetCallback("OnClick", onSpecClick) _frameShop:AddChild(_chk1) + _chk1:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -20) _chk2 = AceGUI:Create("AmrUiCheckBox") - _chk2:SetPoint("LEFT", _chk1.frame, "RIGHT", 30, 0) _chk2:SetUserData("spec", 2) _chk2:SetCallback("OnClick", onSpecClick) _frameShop:AddChild(_chk2) + _chk2:SetPoint("LEFT", _chk1.frame, "RIGHT", 30, 0) _chk3 = AceGUI:Create("AmrUiCheckBox") - _chk3:SetPoint("LEFT", _chk2.frame, "RIGHT", 30, 0) _chk3:SetUserData("spec", 3) _chk3:SetCallback("OnClick", onSpecClick) _frameShop:AddChild(_chk3) + _chk3:SetPoint("LEFT", _chk2.frame, "RIGHT", 30, 0) _chk4 = AceGUI:Create("AmrUiCheckBox") - _chk4:SetPoint("LEFT", _chk3.frame, "RIGHT", 30, 0) _chk4:SetUserData("spec", 4) _chk4:SetCallback("OnClick", onSpecClick) _frameShop:AddChild(_chk4) + _chk4:SetPoint("LEFT", _chk3.frame, "RIGHT", 30, 0) _panelContent = AceGUI:Create("AmrUiPanel") _panelContent:SetLayout("None") _panelContent:SetTransparent() + _frameShop:AddChild(_panelContent) _panelContent:SetPoint("TOPLEFT", _chk1.frame, "BOTTOMLEFT", 0, -10) _panelContent:SetPoint("BOTTOMRIGHT", _frameShop.content, "BOTTOMRIGHT") - _frameShop:AddChild(_panelContent) -- update shopping list data local player = Amr:ExportCharacter() @@ -168,11 +170,11 @@ scroll:AddChild(panel) local lbl = AceGUI:Create("AmrUiLabel") + panel:AddChild(lbl) lbl:SetWidth(w) lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive)) lbl:SetText(header) lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT") - panel:AddChild(lbl) for itemId, count in pairs(list) do panel = AceGUI:Create("AmrUiPanel") @@ -183,20 +185,20 @@ scroll:AddChild(panel) lbl = AceGUI:Create("AmrUiLabel") + panel:AddChild(lbl) lbl:SetWidth(40) lbl:SetWordWrap(false) lbl:SetFont(Amr.CreateFont("Bold", 20, Amr.Colors.White)) lbl:SetText(count .. "x") lbl:SetPoint("LEFT", panel.content, "LEFT") - panel:AddChild(lbl) local icon = AceGUI:Create("AmrUiIcon") icon:SetBorderWidth(1) icon:SetIconBorderColor(Amr.Colors.White) icon:SetWidth(18) icon:SetHeight(18) + panel:AddChild(icon) icon:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0) - panel:AddChild(icon) local btn = AceGUI:Create("AmrUiTextButton") btn:SetWidth(w - 30 - 18 - 15) @@ -204,9 +206,9 @@ btn:SetWordWrap(false) btn:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.White)) btn:SetHoverFont(Amr.CreateFont("Bold", 14, Amr.Colors.White)) - btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0) btn:SetCallback("OnClick", onItemClick) panel:AddChild(btn) + btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0) Amr.GetItemInfo(itemId, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) -- set icon, name, and a tooltip @@ -228,9 +230,13 @@ ret = ret + list[itemId] end - list = Amr.db.char.BankItemsAndCounts - if list and list[itemId] then - ret = ret + list[itemId] + local bankBags = Amr.db.char.BankItemsAndCounts + if bankBags then + for bagId,bagList in pairs(bankBags) do + if bagList[itemId] then + ret = ret + bagList[itemId] + end + end end return ret @@ -279,11 +285,11 @@ _panelContent:SetLayout("None") local lbl = AceGUI:Create("AmrUiLabel") + _panelContent:AddChild(lbl) lbl:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan)) lbl:SetText(L.ShopEmpty) lbl:SetJustifyH("CENTER") lbl:SetPoint("TOP", _panelContent.content, "TOP", 0, -30) - _panelContent:AddChild(lbl) else local allStuff = { gems = {}, enchants = {}, materials = {} } local hasStuff = false @@ -354,7 +360,8 @@ end --- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize, grouped by inventory ID so that we can combine multiple specs without double-counting +-- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize, +-- grouped by inventory ID so that we can combine multiple specs without double-counting local function getShoppingData(player, gear, spec) local ret = {} @@ -363,15 +370,14 @@ local usedItems = {} for slotId, optimalItem in pairs(gear) do - local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) - local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id] + local matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) local inventoryId = optimalItem.inventoryId or -1 -- find gem/enchant differences on the best-matching item - -- gems, but skip artifact relics (will have relicBonusIds set) - if not optimalItem.relicBonusIds and itemInfo and itemInfo.socketColors then - for i = 1, #itemInfo.socketColors do + -- gems + if not optimalItem.relicBonusIds and (not matchItem or not matchItem.relicBonusIds) then + for i = 1, 3 do local g = optimalItem.gemIds[i] local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g @@ -390,15 +396,8 @@ local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == e if not isEnchantEquipped then - -- enchant info, look in all spec extra info cache - local enchInfo = nil - for specPos = 1,4 do - if Amr.db.char.ExtraEnchantData[specPos] then - enchInfo = Amr.db.char.ExtraEnchantData[specPos][e] - if enchInfo then break end - end - end - + -- enchant info + local enchInfo = Amr.db.char.ExtraEnchantData[e] if enchInfo then if not ret[inventoryId] then ret[inventoryId] = { gems = {}, enchants = {}, materials = {} } @@ -428,8 +427,13 @@ local enchantItemIdToId = {} - for spec, gear in pairs(Amr.db.char.GearSets) do - required.stuff[spec] = getShoppingData(player, gear, spec) + for i, spec in ipairs(required.specs) do + local gear = Amr.db.char.GearSets[spec] + if gear then + required.stuff[i] = getShoppingData(player, gear, spec) + else + required.stuff[i] = {} + end end Amr.db.global.Shopping[player.Name .. "-" .. player.Realm] = required diff -r 7a6364917f86 -r e31b02b24488 localization/enUS.lua --- a/localization/enUS.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/localization/enUS.lua Tue Jul 17 09:57:39 2018 -0700 @@ -148,7 +148,6 @@ L.TabExportText = "Export" L.TabGearText = "Gear" L.TabLogText = "Combat Logs" -L.TabTeamText = "Team Optimizer" L.TabOptionsText = "Options" L.VersionChatTitle = "AMR Addon Version:" @@ -161,15 +160,13 @@ ------------------------------------------------------------------------]] L.ExportTitle = "Export Instructions" L.ExportHelp1 = "1. Copy the text below by pressing Ctrl+C (or Cmd+C on a Mac)" -L.ExportHelp2 = "2. Go to https://www.askmrrobot.com/wow/gear and open the character picker" -L.ExportHelp3 = "3. Paste into the textbox under the ADDON section" +L.ExportHelp2 = "2. Go to https://www.askmrrobot.com and open the character picker" +L.ExportHelp3 = "3. Paste into the textbox under the AMR ADDON section" L.ExportSplashTitle = "Getting Started" L.ExportSplashSubtitle = "This is your first time using the new version of the addon. Do the following things to initialize your item database:" L.ExportSplash1 = "1. Activate each of your specs once and equip your latest gear for each spec" -L.ExportSplash2 = "2. Equip and open your artifact weapon for each spec" -L.ExportSplash3 = "3. Open your bank and leave it open for at least two seconds" -L.ExportSplash4 = "4. If you have gear in void storage, open it and leave it open for at least two seconds" +L.ExportSplash2 = "2. Open your bank and leave it open for at least two seconds" L.ExportSplashClose = "Continue" @@ -187,7 +184,7 @@ L.GearEquipErrorCombat = "Cannot change spec/gear while in combat!" L.GearEquipErrorEmpty = "No saved gear set could be found for the current spec." L.GearEquipErrorNotFound = "An item in your saved gear set could not be equipped." -L.GearEquipErrorNotFound2 = "Try opening your bank and running this command again, or check your void storage." +L.GearEquipErrorNotFound2 = "Try opening your bank and running this command again." L.GearEquipErrorBagFull = "There is not enough room in your bags to equip your saved gear set." L.GearEquipErrorSoulbound = function(itemLink) return itemLink .. " could not be equipped because it is not bound to you." @@ -291,104 +288,6 @@ --[[---------------------------------------------------------------------- -Team Optimizer Tab -------------------------------------------------------------------------]] -L.TeamTabLeaderText = "Loot Ranker" -L.TeamTabMemberText = "Team Member" - -L.TeamSplashHeader = "How do you usually use the Team Optimizer?" -L.TeamSplashLeaderLabel = "I am the person who uses askmrrobot.com to rank the loot for our group" -L.TeamSplashMemberLabel = "Someone else handles using the Team Optimizer for me" - -L.TeamMemberText = "Just sit back and relax, everything is being handled by your team leaders." -L.TeamMemberShowLootLabel = "Looting is in progress!" -L.TeamMemberShowLoot = "Show Loot Window" - -L.TeamButtonVersionText = "Check for Addon" -L.TeamButtonExportRosterText = "Export Roster" -L.TeamButtonExportLootText = "Export Loot" -L.TeamButtonExportClose = "Close" -L.TeamButtonImportRankingsText = "Import Rankings" -L.TeamButtonStartLootText = "Start Looting" -L.TeamButtonResumeLootText = "Resume Looting" - -L.TeamExportVersionLabel = "The Team Optimizer is more accurate and easier to use if everyone in your group has the AskMrRobot addon." -L.TeamExportRosterLabel = "Do this once at the beginning of your raid to initialize the Team Optimizer on the web. If someone joins or leaves your group during a raid, do it again and press 'Import without Reload' on the web to do a quick update of just the players that changed." -L.TeamExportLootLabel = "Export all drops from the last looted boss and rank them all at once on the web. Rankings automatically account for loot won on previous bosses or bonus rolls!" -L.TeamExportLootLabel2 = "Requires Master Loot" -L.TeamImportRankingsLabel = "Import ranking data from askmrrobot.com so that you can easily view and distribute loot in-game." -L.TeamStartLootLabel = function(numItems) - return numItems .. " items were imported." -end - -L.TeamHistoryTitle = "Loot History" -L.TeamHistoryNoGroup = "You are not in a group or raid." -L.TeamHistoryEmpty = "No loot has been handed out yet." - -L.TeamVersionTitle = "Version Check" -L.TeamVersionNoGroup = "You are not in a group or raid." -L.TeamVersionGood = "Everyone in your group has the addon!" -L.TeamVersionMissing = "NOT INSTALLED" -L.TeamVersionOld = "OUT OF DATE" - -L.TeamExportRosterLoading = "Gathering player data, please wait..." - -L.TeamAlertNoGroup = "You are not in a group!" -L.TeamAlertNoLoot = "Nothing has been master-looted recently in your party or raid!" - -L.TeamExportHelp = "Press Ctrl+C (or Cmd+C on a Mac) to copy the text below." -L.TeamExportRosterText = "Then go to the Team Optimizer on the website and paste into the roster import box." -L.TeamExportLootText = "Then go to the Team Optimizer on the website and paste into the loot import box." --- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet - -L.TeamImportRankingsHeader = "Press Ctrl+V (Cmd+V on a Mac) to paste data from the website into the box below." - -L.TeamLootOptionNeed = "Need" -L.TeamLootOptionOff = "Off Spec" -L.TeamLootOptionGreed = "Greed" -L.TeamLootOptionPass = "Pass" -L.TeamLootOptionDisenchant = "Disenchant" - - ---[[---------------------------------------------------------------------- -Loot Window -------------------------------------------------------------------------]] -L.LootTitle = "Loot!" -L.LootEmpty = "There is no loot to hand out right now!" -L.LootHelpItems = "For each drop..." -L.LootHelpRanks = "Choose an option:" -L.LootHelpMaster = "Then master loot:" - -L.LootIneligible = "You are not eligible for this drop." - -L.LootRankHeaderRank = "Rank" -L.LootRankHeaderScore = "Percent Upgrade" -L.LootRankHeaderScoreDisenchant = "Player" - --- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max -L.LootRankHeaderNeed = "Need" -L.LootRankHeaderOff = "Off" -L.LootRankHeaderGreed = "Greed" -L.LootRankHeaderPass = "Pass" -L.LootRankHeaderRoll = "Roll" - -L.LootRankLabelOff = "off spec" -L.LootRankLabelDisenchant = "disenchanter" -L.LootRankLabelMasterLooter = "master looter" -L.LootRankLabelNoAddon = "addon not running" - -L.LootMasterRollText = "Roll" -L.LootMasterDisenchantText = "Disenchant/Sell" - -L.LootMasterRollLabel = "automatic /roll for everyone" -L.LootMasterDisenchantLabel = "check this if no one wants this item" -L.LootMasterGiveLoot = "Give Loot" -L.LootMasterGiveDisenchant = "Give for Disenchant/Sell" - -L.LootMasterGiveFail = "Either this item or this master loot candidate could not be found :(" - - ---[[---------------------------------------------------------------------- Options Tab ------------------------------------------------------------------------]] L.OptionsHeaderGeneral = "General Options" diff -r 7a6364917f86 -r e31b02b24488 localization/frFR.lua --- a/localization/frFR.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/localization/frFR.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,17 +1,3 @@ ---[[------------------------------------------------------------------------------------------------------------- -Master Localization File (English) - -Instructions for Translators: -1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German. -2. At the top, replace "enUS" in the first code line with your locale, and change the next parameter from true to false. -3. Change all the English strings in your file as appropriate. - -Note that a couple of the "strings" are functions that are provided variables. Feel free to modify these -functions as necessary to output an appropriately worded statement in your language (but don't change the parameters). -If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you. ----------------------------------------------------------------------------------------------------------------]] - --- replace enUS with your locale local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "frFR", false) if L then @@ -138,9 +124,7 @@ L.MinimapTooltip = [[Clic gauche pour ouvrir la fenêtre Ask Mr. Robot. -Clic droit pour changer de spé et equipper le stuff sauvegardé pour cette spé. - -Ctrl + Clic gauche pour marquer un essai comme wipe.]] +Clic droit pour changer de spé et equipper le stuff sauvegardé pour cette spé.]] L.MainStatusText = function(version, url) return version .. " chargée. Documentation disponible à " .. url @@ -149,7 +133,6 @@ L.TabExportText = "Exporter" L.TabGearText = "Stuff" L.TabLogText = "Combat Logs" -L.TabTeamText = "Team Optimizer" L.TabOptionsText = "Options" L.VersionChatTitle = "Version Add-on AMR:" @@ -162,15 +145,13 @@ ------------------------------------------------------------------------]] L.ExportTitle = "Instructions pour exporter" L.ExportHelp1 = "1. Copie le texte ci-dessous en appuyant Ctrl+C (ou Cmd+C sur un Mac)" -L.ExportHelp2 = "2. Va sur http://www.askmrrobot.com/wow/player et charge ton perso" -L.ExportHelp3 = "3. Clique le lien \"IMPORT (from addon)\" juste au dessus du nom de ton perso" -L.ExportHelp4 = "4. Colle le texte dans la zone de texte sur le site et clique sur \"Import!\"" +L.ExportHelp2 = "2. Va sur https://www.askmrrobot.com et charge ton perso" +L.ExportHelp3 = "3. Paste into the textbox under the AMR ADDON section" -- TODO L.ExportSplashTitle = "Comment Démarrer" L.ExportSplashSubtitle = "S'il s'agit de ta première utilisation de cette nouvelle version de l'add-on, procède comme suit pour initialiser la base de données d'items :" L.ExportSplash1 = "1. Active chacune de tes spés une fois et pour chaque spé, équippe le stuff approprié" L.ExportSplash2 = "2. Ouvre la fenêtre de ta banque et laisse la ouverte pendant au moins deux secondes" -L.ExportSplash3 = "3. Si tu as du stuff dans ta banque du vide (void storage), ouvre la fenêtre et laisse la ouverte pendant au moins deux secondes" L.ExportSplashClose = "Continuer" @@ -188,7 +169,7 @@ L.GearEquipErrorCombat = "Impossible de changer de spé/stuff pendant un combat !" L.GearEquipErrorEmpty = "Pas de stuff sauvegardé pour la spé active." L.GearEquipErrorNotFound = "Un item de ton stuff sauvegardé pour la spee n'a pas pu être équippé." -L.GearEquipErrorNotFound2 = "Essaie d'ouvrir la fenêtre de la banque et de lancer cette commande de nouveau, ou bien vérifie ta banque du vide." +L.GearEquipErrorNotFound2 = "Essaie d'ouvrir la fenêtre de la banque et de lancer cette commande de nouveau." L.GearEquipErrorBagFull = "Pas assez de place dans tes sacs pour équipper ton stuff sauvegardé." L.GearEquipErrorSoulbound = function(itemLink) return itemLink .. " n'a pas pu être équippé car il n'est pas lié quand ramassé." @@ -292,104 +273,6 @@ --[[---------------------------------------------------------------------- -Team Optimizer Tab -------------------------------------------------------------------------]] -L.TeamTabLeaderText = "Master Loot" -L.TeamTabMemberText = "Membre du Raid" - -L.TeamSplashHeader = "Comment utilises-tu d'habitude le Team Optimizer ?" -L.TeamSplashLeaderLabel = "Je suis la personne qui utilise askmrrobot.com pour classer et distributer le loot pour notre groupe." -L.TeamSplashMemberLabel = "Quelqu'un d'autre utilise l'optimiseur de Groupe / Raid pour moi." - -L.TeamMemberText = "Tu peux te relaxer, tout est géré par les leaders de ton groupe." -L.TeamMemberShowLootLabel = "Loot en cours !" -L.TeamMemberShowLoot = "Voir la fenêtre de loot" - -L.TeamButtonVersionText = "Vérifier la version de l'add-on" -L.TeamButtonExportRosterText = "Exporter Roster" -L.TeamButtonExportLootText = "Exporter Loot" -L.TeamButtonExportClose = "Fermer" -L.TeamButtonImportRankingsText = "Importer Rankings" -L.TeamButtonStartLootText = "Commencer Loot" -L.TeamButtonResumeLootText = "Continuer Loot" - -L.TeamExportVersionLabel = "Le Team Optimizer est plus précis et plus facile à utiliser si tout le monde dans le groupe utilise l'add-on AskMrRobot." -L.TeamExportRosterLabel = "Fais ceci une fois au début du raid pour initialiser l'Optimizer sur le site. Si quelqu'un rejoint le groupe ou le quitte, refais cette étape et appuie sur ``Import without Reload'' sur le site pour prendre en compte les joueurs qui ont changé." -L.TeamExportLootLabel = "Exporte tous les drops du dernier boss looté et les classe tous sur le site. Les classements prennent en compte le loot précédemment gagné ainsi que les bonus rolls !" -L.TeamExportLootLabel2 = "Requiert Master Loot" -L.TeamImportRankingsLabel = "Importe les données de classement depuis askmrrobot.com, afin que tu puisses facilement voir comment distribuer le loot." -L.TeamStartLootLabel = function(numItems) - return numItems .. " items importés." -end - -L.TeamHistoryTitle = "Historique de loot" -L.TeamHistoryNoGroup = "Tu n'es pas dans un groupe ou un raid." -L.TeamHistoryEmpty = "L'add-on n'a pas encore enregistré de distribution de loot." - -L.TeamVersionTitle = "Version Check" -L.TeamVersionNoGroup = "Tu n'es pas dans un groupe ou un raid." -L.TeamVersionGood = "Tout le monde dans ton groupe a l'add-on !" -L.TeamVersionMissing = "PAS INSTALLE" -L.TeamVersionOld = "PAS A JOUR" - -L.TeamExportRosterLoading = "En train d'obtenir les données des joueurs..." - -L.TeamAlertNoGroup = "Tu n'es pas dans un groupe !" -L.TeamAlertNoLoot = "Il n'y a pas eu de loot distribué par master-loot recémment dans ton groupe ou raid !" - -L.TeamExportHelp = "Appuie sur Ctrl+C (ou Cmd+C sur un Mac) pour copier le texte ci-dessous." -L.TeamExportRosterText = "Ensuite va dans le Team Optimizer sur le site et colle le texte dans la zone de texte pour importer le roster." -L.TeamExportLootText = "Ensuite va dans le Team Optimizer sur le site et colle le texte dans la zone de texte pour importer le loot." --- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet - -L.TeamImportRankingsHeader = "Appuie sur Ctrl+V (ou Cmd+V sur un Mac) pour coller les données du site dans la zone de texte ci-dessous." - -L.TeamLootOptionNeed = "Need" -L.TeamLootOptionOff = "Off Spé" -L.TeamLootOptionGreed = "Greed" -L.TeamLootOptionPass = "Pass" -L.TeamLootOptionDisenchant = "Désenchanter" - - ---[[---------------------------------------------------------------------- -Loot Window -------------------------------------------------------------------------]] -L.LootTitle = "Loot!" -L.LootEmpty = "Il n'y a pas de loot à distribuer actuellement !" -L.LootHelpItems = "Pour chaque drop..." -L.LootHelpRanks = "Choisis une option :" -L.LootHelpMaster = "Puis master loot :" - -L.LootIneligible = "Tu n'es pas éligible pour ce drop." - -L.LootRankHeaderRank = "Classement" -L.LootRankHeaderScore = "Pourcentage d'upgrade" -L.LootRankHeaderScoreDisenchant = "Joueur" - --- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max -L.LootRankHeaderNeed = "Need" -L.LootRankHeaderOff = "Off" -L.LootRankHeaderGreed = "Greed" -L.LootRankHeaderPass = "Pass" -L.LootRankHeaderRoll = "Roll" - -L.LootRankLabelOff = "off spé" -L.LootRankLabelDisenchant = "désenchanteur" -L.LootRankLabelMasterLooter = "master looter" -L.LootRankLabelNoAddon = "add-on pas activé" - -L.LootMasterRollText = "Roll" -L.LootMasterDisenchantText = "Désenchanter/Vendre" - -L.LootMasterRollLabel = "/roll automatique pour tout le monde" -L.LootMasterDisenchantLabel = "coche si personne ne veut de l'item" -L.LootMasterGiveLoot = "Donner Loot" -L.LootMasterGiveDisenchant = "Donner pour désenchanter/vendre" - -L.LootMasterGiveFail = "Soit cet item, soit le joueur à qui il aurait du être donné n'a pas été trouvé." - - ---[[---------------------------------------------------------------------- Options Tab ------------------------------------------------------------------------]] L.OptionsHeaderGeneral = "Options Générales" @@ -403,6 +286,7 @@ L.OptionsShopAhName = "Voir la shopping list automatiquement à l'hôtel des ventes" L.OptionsShopAhDesc = "Quand tu ouvres l'hôtel des ventes, la fenêtre de la shopping list s'ouvre automatiquement. Tu peux cliquer sur un item dans la shopping list pour le chercher automatiquement dans l'hôtel des ventes." +-- TODO L.OptionsUiScaleName = "Ask Mr. Robot UI scale" L.OptionsUiScaleDesc = "Enter a value between 0.5 and 1.5 to change the scale of the Ask Mr. Robot user interface, press Enter, then close/open the window for it take effect. If the positioning gets messed up, use the /amr reset command." diff -r 7a6364917f86 -r e31b02b24488 localization/itIT.lua --- a/localization/itIT.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/localization/itIT.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,17 +1,3 @@ ---[[------------------------------------------------------------------------------------------------------------- -Master Localization File (English) - -Instructions for Translators: -1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German. -2. At the top, replace "enUS" in the first code line with your locale. -3. Change all the English strings in your file as appropriate. - -Note that a couple of the "strings" are functions that are provided variables. Feel free to modify these -functions as necessary to output an appropriately worded statement in your language (but don't change the parameters). -If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you. ----------------------------------------------------------------------------------------------------------------]] - --- replace enUS with your locale local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "itIT", false) if L then @@ -138,9 +124,7 @@ L.MinimapTooltip = [[Clic Sinistro per aprire l'interfaccia di Ask Mr Robot. -Tasto Destro per cambiare spec ed equip collegato. - -Ctrl + Clic Sinistro per segnare il wipe.]] +Tasto Destro per cambiare spec ed equip collegato.]] L.MainStatusText = function(version, url) return version .. " loaded. Documentazione disponibile su " .. url @@ -149,7 +133,6 @@ L.TabExportText = "Esporta" L.TabGearText = "Equip" L.TabLogText = "Combat Logs" -L.TabTeamText = "Ottimizzatore Raid" L.TabOptionsText = "Opzioni" L.VersionChatTitle = "Versione Addon AMR:" @@ -162,15 +145,13 @@ ------------------------------------------------------------------------]] L.ExportTitle = "Istruzioni di Esportazione" L.ExportHelp1 = "1. Copia il testo qui sotto premendo Ctrl+C (o Cmd+C su un Mac)" -L.ExportHelp2 = "2. Vai su http://www.askmrrobot.com/wow/player e carica il tuo personaggio" -L.ExportHelp3 = "3. Premi la scritta verde IMPORT (from addon) subito sopra il nome del tuo personaggio" -L.ExportHelp4 = "4. Incolla all'interno dell'area di testoe premi il tasto Import!" +L.ExportHelp2 = "2. Vai su https://www.askmrrobot.com e carica il tuo personaggio" +L.ExportHelp3 = "3. Paste into the textbox under the AMR ADDON section" -- TODO L.ExportSplashTitle = "Per Cominciare" L.ExportSplashSubtitle = "Questa è la tua prima volta con la nuova versione dell'addon. Esegui queste operazioni per creare il Database del tuo equipaggiamento:" L.ExportSplash1 = "1. Attiva Ognuna delle tue spec ed equipaggia gli oggetti migliori che hai" L.ExportSplash2 = "2. Apri la tua banca e lasciala aperta almeno per 2 secondi" -L.ExportSplash3 = "3. Se hai dell'equipaggiamento nella Banca Eterea, Aprila e lasciala aperta per almeno due secondi" L.ExportSplashClose = "Continua" @@ -292,104 +273,6 @@ --[[---------------------------------------------------------------------- -Team Optimizer Tab -------------------------------------------------------------------------]] -L.TeamTabLeaderText = "Loot Ranker" -L.TeamTabMemberText = "Membro" - -L.TeamSplashHeader = "Come uso di solito l'ottimizzatore di Incursione?" -L.TeamSplashLeaderLabel = "Sono la persona che usa askmrrobot.com per determinare il miglior utilizzatore per il bottino" -L.TeamSplashMemberLabel = "Qualcun'altro utilizza l'ottimizzatore per me" - -L.TeamMemberText = "Rilassati, tutto è gestito dal master looter." -L.TeamMemberShowLootLabel = "Looting in progress!" -L.TeamMemberShowLoot = "Mostra finestra Bottino" - -L.TeamButtonVersionText = "Verifica L'addon" -L.TeamButtonExportRosterText = "Esporta Lista" -L.TeamButtonExportLootText = "Esporta Bottino" -L.TeamButtonExportClose = "Chiudi" -L.TeamButtonImportRankingsText = "Importa Punteggi" -L.TeamButtonStartLootText = "Inizia Predazione" -L.TeamButtonResumeLootText = "Continua Predazione" - -L.TeamExportVersionLabel = "L'ottimizzatore di Incursione è più accurato e facile da utilizzare se tutti nel tuo gruppo hanno installato l'addon di AskMrRobot." -L.TeamExportRosterLabel = "Fallo una volta all'inizio del raid per inizializzare l'addon sul web. Se qualcuno entra od esce dal tuo gruppo durante un'incursione, fallo di nuovo e premi 'Import without Reload' sul web per effettuare un aggiornamento rapido dei giocatori che sono cambiati." -L.TeamExportLootLabel = "Esporta tutti i drop dell'ultimo boss lootato ed inseriscili nella graduatoria sul web. L'inserimento tiene conto automaticamente dei loot vinti dai boss precedenti o con un tiro bonus!" -L.TeamExportLootLabel2 = "Richiedere Master Loot" -L.TeamImportRankingsLabel = "Importa i punteggi da askmrrobot.com così da semplificarti la distribuzione del bottino in gioco." -L.TeamStartLootLabel = function(numItems) - return numItems .. " oggetti sono stati importati." -end - -L.TeamHistoryTitle = "Loot History" -L.TeamHistoryNoGroup = "Non sei in gruppo o in incursione." -L.TeamHistoryEmpty = "Nessun loot è ancora stato assegnato." - -L.TeamVersionTitle = "Controlla Versione" -L.TeamVersionNoGroup = "Non sei in gruppo o in incursione." -L.TeamVersionGood = "Tutti nel tuo gruppo hanno l'addon!" -L.TeamVersionMissing = "NON INSTALLATO" -L.TeamVersionOld = "VERSIONE OBSOLETA" - -L.TeamExportRosterLoading = "Gathering player data, please wait..." - -L.TeamAlertNoGroup = "Non sei in un gruppo!" -L.TeamAlertNoLoot = "Non è stato lootato niente recentemente nel tuo gruppo o incursione!" - -L.TeamExportHelp = "Premi Ctrl+C (o Cmd+C su un Mac) per copiatre il testo seguente." -L.TeamExportRosterText = "Quindi vai sul Team Optimizer sul sito ed incolla all'interno della roster import box." -L.TeamExportLootText = "Quindi vai sul Team Optimizer sul sito ed incolla all'interno della loot import box." --- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet - -L.TeamImportRankingsHeader = "Premi Ctrl+V (Cmd+V su un Mac) per incollare i dati nella textbox." - -L.TeamLootOptionNeed = "Need" -L.TeamLootOptionOff = "Off Spec" -L.TeamLootOptionGreed = "Greed" -L.TeamLootOptionPass = "Pass" -L.TeamLootOptionDisenchant = "Disenchant" - - ---[[---------------------------------------------------------------------- -Loot Window -------------------------------------------------------------------------]] -L.LootTitle = "Loot!" -L.LootEmpty = " Non c'è nessun loot da gestire!" -L.LootHelpItems = "Per ogni drop..." -L.LootHelpRanks = "Scegli un opzione:" -L.LootHelpMaster = "Quindi master loota:" - -L.LootIneligible = "Non sei eleggibile per questo Loot." - -L.LootRankHeaderRank = "Rank" -L.LootRankHeaderScore = "Percentuale di Upgrade" -L.LootRankHeaderScoreDisenchant = "Giocatore" - --- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max -L.LootRankHeaderNeed = "Need" -L.LootRankHeaderOff = "Off" -L.LootRankHeaderGreed = "Greed" -L.LootRankHeaderPass = "Pass" -L.LootRankHeaderRoll = "Roll" - -L.LootRankLabelOff = "off spec" -L.LootRankLabelDisenchant = "disenchanter" -L.LootRankLabelMasterLooter = "master looter" -L.LootRankLabelNoAddon = "addon non funzionante" - -L.LootMasterRollText = "Roll" -L.LootMasterDisenchantText = "Disenchant/Sell" - -L.LootMasterRollLabel = "/roll automatico per tutti" -L.LootMasterDisenchantLabel = "Clicca qui se nessuno vuole il loot" -L.LootMasterGiveLoot = "Dai Loot" -L.LootMasterGiveDisenchant = "Dai loot per Disenchant/Sell" - -L.LootMasterGiveFail = "Questo item o il vincitore non possono esser trovati :(" - - ---[[---------------------------------------------------------------------- Options Tab ------------------------------------------------------------------------]] L.OptionsHeaderGeneral = "General Options" diff -r 7a6364917f86 -r e31b02b24488 localization/ptBR.lua --- a/localization/ptBR.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/localization/ptBR.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,17 +1,3 @@ ---[[------------------------------------------------------------------------------------------------------------- -Master Localization File (English) - -Instructions for Translators: -1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German. -2. At the top, replace "enUS" in the first code line with your locale, and change the next parameter from true to false. -3. Change all the English strings in your file as appropriate. - -Note that a couple of the "strings" are functions that are provided variables. Feel free to modify these -functions as necessary to output an appropriately worded statement in your language (but don't change the parameters). -If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you. ----------------------------------------------------------------------------------------------------------------]] - --- replace enUS with your locale local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "ptBR", false) if L then @@ -148,7 +134,6 @@ L.TabExportText = "Exportar" L.TabGearText = "Equipamento" L.TabLogText = "Logs" -L.TabTeamText = "Otimizador de Equipe" L.TabOptionsText = "Opções" L.VersionChatTitle = "Versão do Addon AMR:" @@ -161,15 +146,13 @@ ------------------------------------------------------------------------]] L.ExportTitle = "Instruções de Exportação" L.ExportHelp1 = "1. Copie o texto abaixo pressionando Ctrl+C (ou Cmd+C em um Mac)" -L.ExportHelp2 = "2. Vá para https://www.askmrrobot.com/wow/gear e clique no seletor de personagem" +L.ExportHelp2 = "2. Vá para https://www.askmrrobot.com e clique no seletor de personagem" L.ExportHelp3 = "3. Cole o texto copiado na caixa de texto da seção ADDON" L.ExportSplashTitle = "Começando" L.ExportSplashSubtitle = "Esta é a primeira vez que você usa a nova versão do addon. Faça o seguinte para inicializar o banco de dados dos seus itens:" L.ExportSplash1 = "1. Ative cada um dos seus specs uma vez e equipe seu equipamento atual para cada spec" -L.ExportSplash2 = "2. Equipe sua arma de artefato para cada spec e abra a tela de artefato" -L.ExportSplash3 = "3. Abra seu banco e deixe-o aberto por pelo menos dois segundos" -L.ExportSplash4 = "4. Se você possuir equipamento no cofre etéreo, abra-o e deixe-o aberto por pelo menos dois segundos" +L.ExportSplash2 = "2. Abra seu banco e deixe-o aberto por pelo menos dois segundos" L.ExportSplashClose = "Continuar" @@ -291,104 +274,6 @@ --[[---------------------------------------------------------------------- -Team Optimizer Tab -------------------------------------------------------------------------]] -L.TeamTabLeaderText = "Rankeador de Saque" -L.TeamTabMemberText = "Membro do Grupo" - -L.TeamSplashHeader = "Como você costuma usar o Otimizador de Grupo?" -L.TeamSplashLeaderLabel = "Eu sou a pessoa que usa o askmrrobot.com para classificar o saque para nosso grupo" -L.TeamSplashMemberLabel = "Outra pessoa usa o Otimizador de Grupo pra mim" - -L.TeamMemberText = "Pegue a pipoca e relaxe, tudo está sendo feito pelos líderes do seu grupo." -L.TeamMemberShowLootLabel = "Saque em progresso!" -L.TeamMemberShowLoot = "Mostrar Janela de Saque" - -L.TeamButtonVersionText = "Verificar o Addon" -L.TeamButtonExportRosterText = "Exportar Lista" -L.TeamButtonExportLootText = "Exportar Saque" -L.TeamButtonExportClose = "Fechar" -L.TeamButtonImportRankingsText = "Importar Rankings" -L.TeamButtonStartLootText = "Iniciar Saque" -L.TeamButtonResumeLootText = "Resumir Saque" - -L.TeamExportVersionLabel = "O Otimizador de Grupo é mais preciso e fácil de usar se todos no seu grupo possuirem o addon do AskMrRobot." -L.TeamExportRosterLabel = "Faça isso uma vez no início da sua raid para inicializar o Otimizador de Grupo na internet. Se alguém entrar ou sair do seu grupo durante uma raid, aperte novamente 'Import without Reload' na internet para fazer uma atualização rápida apenas desses jogadores." -L.TeamExportLootLabel = "Exporta todos os espólios do último chefe saqueado e cria um ranking de todos de uma só vez na internet. Os Rankings automaticamente contam como saque vencido nos chefes anteriores ou nos saques extra!" -L.TeamExportLootLabel2 = "Requer Mestre de Saque" -L.TeamImportRankingsLabel = "Importar dados de ranking de askmrrobot.com, assim você pode facilmente ver e distribuir o saque no jogo." -L.TeamStartLootLabel = function(numItems) - return numItems .. " itens foram importados." -end - -L.TeamHistoryTitle = "Histórico de Saque" -L.TeamHistoryNoGroup = "Você não está em um grupo ou raid." -L.TeamHistoryEmpty = "Nenhum saque foi distribuído ainda." - -L.TeamVersionTitle = "Verificação de Versão" -L.TeamVersionNoGroup = "Você não está em um grupo ou raid.." -L.TeamVersionGood = "Todos no seu grupo possuem o addon!" -L.TeamVersionMissing = "NÃO INSTALADO" -L.TeamVersionOld = "DESATUALIZADO" - -L.TeamExportRosterLoading = "Coletando dados dos jogadores, aguarde..." - -L.TeamAlertNoGroup = "Você não está em um grupo!" -L.TeamAlertNoLoot = "Nada foi distribuído via mestre de saque recentemente em seu grupo ou raid!" - -L.TeamExportHelp = "Aperte Ctrl+C (ou Cmd+C em um Mac) para copiar o texto abaixo." -L.TeamExportRosterText = "Em seguida vá para o Team Optimizer no website e cole na caixa de importação das listagens." -L.TeamExportLootText = "Em seguida vá para o Team Optimizer no website e cole na caixa de importação de saque." --- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet - -L.TeamImportRankingsHeader = "Aperte Ctrl+V (Cmd+V em um Mac) para colar os dados do website na caixa abaixo." - -L.TeamLootOptionNeed = "Necessidade" -L.TeamLootOptionOff = "Off Spec" -L.TeamLootOptionGreed = "Ganância" -L.TeamLootOptionPass = "Passar" -L.TeamLootOptionDisenchant = "Desencantar" - - ---[[---------------------------------------------------------------------- -Loot Window -------------------------------------------------------------------------]] -L.LootTitle = "Saque!" -L.LootEmpty = "Não há saque para distribuir agora!" -L.LootHelpItems = "Para cada saque..." -L.LootHelpRanks = "Escolha uma opção:" -L.LootHelpMaster = "Então mestre de saque:" - -L.LootIneligible = "Você não é elegível para este saque." - -L.LootRankHeaderRank = "Rank" -L.LootRankHeaderScore = "Porcentagem de Upgrade" -L.LootRankHeaderScoreDisenchant = "Jogador" - --- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max -L.LootRankHeaderNeed = "Need" -L.LootRankHeaderOff = "Off" -L.LootRankHeaderGreed = "Greed" -L.LootRankHeaderPass = "Pass" -L.LootRankHeaderRoll = "Roll" - -L.LootRankLabelOff = "spec secundária" -L.LootRankLabelDisenchant = "desencantador" -L.LootRankLabelMasterLooter = "mestre de saque" -L.LootRankLabelNoAddon = "addon não está executando" - -L.LootMasterRollText = "Rolar" -L.LootMasterDisenchantText = "Desencantar/Vender" - -L.LootMasterRollLabel = "/roll automático para todos" -L.LootMasterDisenchantLabel = "marque isso se ninguém deseja este item" -L.LootMasterGiveLoot = "Entregar Saque" -L.LootMasterGiveDisenchant = "Entregar para Desencantar/Vender" - -L.LootMasterGiveFail = "Ambos o item ou o candidato ao saque não puderam ser encontrados :(" - - ---[[---------------------------------------------------------------------- Options Tab ------------------------------------------------------------------------]] L.OptionsHeaderGeneral = "Opções Gerais" diff -r 7a6364917f86 -r e31b02b24488 localization/ruRU.lua --- a/localization/ruRU.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/localization/ruRU.lua Tue Jul 17 09:57:39 2018 -0700 @@ -1,17 +1,3 @@ ---[[------------------------------------------------------------------------------------------------------------- -Master Localization File (English) - -Instructions for Translators: -1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German. -2. At the top, replace "enUS" in the first code line with your locale, and change the next parameter from true to false. -3. Change all the English strings in your file as appropriate. - -Note that a couple of the "strings" are functions that are provided variables. Feel free to modify these -functions as necessary to output an appropriately worded statement in your language (but don't change the parameters). -If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you. ----------------------------------------------------------------------------------------------------------------]] - --- replace enUS with your locale local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "ruRU", false) if L then @@ -139,9 +125,7 @@ L.MinimapTooltip = [[Левый клик для открытия окна Ask Mr. Robot. -Правый клик для смены спека и экипирования актуальных предметов. - -Ctrl + ЛКМ отметить бой как вайп.]] +Правый клик для смены спека и экипирования актуальных предметов.]] L.MainStatusText = function(version, url) return version .. " загружен. Инструкции доступны на " .. url @@ -150,7 +134,6 @@ L.TabExportText = "Экспорт" L.TabGearText = "Экипировка" L.TabLogText = "Логи" -L.TabTeamText = "Оптимизатор" L.TabOptionsText = "Опции" L.VersionChatTitle = "AMR Addon Version:" @@ -163,7 +146,7 @@ ------------------------------------------------------------------------]] L.ExportTitle = "Инструкции Экспорта" L.ExportHelp1 = "1. Скопируйте текст ниже нажав Ctrl+C (или Cmd+C на Mac)" -L.ExportHelp2 = "2. Перейдите на https://www.askmrrobot.com/wow/gear и выберете своего персонажа" +L.ExportHelp2 = "2. Перейдите на https://www.askmrrobot.com и выберете своего персонажа" L.ExportHelp3 = "3. Вставьте это в тектовое поле в разделее ADDON" L.ExportSplashTitle = "Начало работы" @@ -293,104 +276,6 @@ --[[---------------------------------------------------------------------- -Team Optimizer Tab -------------------------------------------------------------------------]] -L.TeamTabLeaderText = "Распределить добычу" -L.TeamTabMemberText = "Команда" - -L.TeamSplashHeader = "Как вы планируете использовать Team Optimizer?" -L.TeamSplashLeaderLabel = "Я тот, кто использует AskMrRobot для распределения добычи в рейде" -L.TeamSplashMemberLabel = "Пускай другие используют Team Optimizer за меня" - -L.TeamMemberText = "Всё, бобер, выдыхай. Просто сядь и расслабься, все будет сделано Вашим лидером." -L.TeamMemberShowLootLabel = "Распределние добычи в процессе!" -L.TeamMemberShowLoot = "Показать окно добычи" - -L.TeamButtonVersionText = "Наличии аддона" -L.TeamButtonExportRosterText = "Экспорт ростера" -L.TeamButtonExportLootText = "Экспорт добычи" -L.TeamButtonExportClose = "Закрыть" -L.TeamButtonImportRankingsText = "Импорт Ранкинга" -L.TeamButtonStartLootText = "Начать распределение" -L.TeamButtonResumeLootText = "Продолжить распределять" - -L.TeamExportVersionLabel = "Team Optimizer является более точным и простыв в использовании если у всех в группе есть аддон AskMrRobot." -L.TeamExportRosterLabel = "Сделайте это один раз в начале рейда для инициализации ростера в Team Optimizer на сайте. Если кто-то присоединяется или уходит из рейда во время РТ, сделайте тоже самое, только нажмите 'Import without Reload' на сайте, для быстрого обновления игроков который поменялись." -L.TeamExportLootLabel = "Экспорт всего лута с последнего убитого босса на сайт AskMrRobot для дальнейшей работы с ним. Rankings automatically account for loot won on previous bosses or bonus rolls!" -L.TeamExportLootLabel2 = "Требуется Мастер Лут" -L.TeamImportRankingsLabel = "Импорт данных лута с сайта askmrrobot.com чтобы вы могли просматривать и легко распределять добычу в рейде." -L.TeamStartLootLabel = function(numItems) - return numItems .. " предметов было импортировано." -end - -L.TeamHistoryTitle = "История добычи" -L.TeamHistoryNoGroup = "Вы находитесь вне группы или рейда." -L.TeamHistoryEmpty = "Добыча ещё не была распределена." - -L.TeamVersionTitle = "Проверка версий" -L.TeamVersionNoGroup = "Вы находитесь вне группы или рейда." -L.TeamVersionGood = "У всех участников группы/рейда установлен аддон!" -L.TeamVersionMissing = "НЕ УСТАНОВЛЕН" -L.TeamVersionOld = "УСТАРЕВШИЙ" - -L.TeamExportRosterLoading = "Собираем Ваших рейдеров по крупице, пожалуйста подождите..." - -L.TeamAlertNoGroup = "Вы находитесь без группы!" -L.TeamAlertNoLoot = "В последнее время с помощью мастер лута нечего не было распределено!" - -L.TeamExportHelp = "Нажмите Ctrl+C (или Cmd+C на Mac) чтобы скопировать текст ниже." -L.TeamExportRosterText = "Далее идете на сайт, вкладка Team Optimizer, и вставляете в поле во вкладке Roster." -L.TeamExportLootText = "Далее переходите во вкладке Team Optimizer на Rank Items и вставляете так же туда Лут." --- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet - -L.TeamImportRankingsHeader = "Нажмите Ctrl+V (Cmd+V на Mac) чтобы вставить данные с сайта в поле ниже." - -L.TeamLootOptionNeed = "Нужно" -L.TeamLootOptionOff = "Офф спек" -L.TeamLootOptionGreed = "Продать" -L.TeamLootOptionPass = "Пропуск" -L.TeamLootOptionDisenchant = "Распылить" - - ---[[---------------------------------------------------------------------- -Loot Window -------------------------------------------------------------------------]] -L.LootTitle = "Лут!" -L.LootEmpty = "Здесь нет лута для раздачи прямо сейчас!" -L.LootHelpItems = "For each drop..." -L.LootHelpRanks = "Выберите опцию:" -L.LootHelpMaster = "Мастер лут:" - -L.LootIneligible = "У Вас нет прав на этот предмет." - -L.LootRankHeaderRank = "Ранг" -L.LootRankHeaderScore = "Процент улучшения" -L.LootRankHeaderScoreDisenchant = "Игрок" - --- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max -L.LootRankHeaderNeed = "Нужно" -L.LootRankHeaderOff = "Офф" -L.LootRankHeaderGreed = "Продать" -L.LootRankHeaderPass = "Пас" -L.LootRankHeaderRoll = "Ролл" - -L.LootRankLabelOff = "офф спек" -L.LootRankLabelDisenchant = "дизинчантер" -L.LootRankLabelMasterLooter = "Мастер лутер" -L.LootRankLabelNoAddon = "АДДОН НЕ УСТАНОВЛЕН" - -L.LootMasterRollText = "Ролл" -L.LootMasterDisenchantText = "Распылить/Продать" - -L.LootMasterRollLabel = "автоматически делает /roll за всех" -L.LootMasterDisenchantLabel = "выберете это, если предмет никому не нужен" -L.LootMasterGiveLoot = "Отдать" -L.LootMasterGiveDisenchant = "Отдать на Распыл/Продажу" - -L.LootMasterGiveFail = "Этот предмет или выбранный игрок не найден :(" - - ---[[---------------------------------------------------------------------- Options Tab ------------------------------------------------------------------------]] L.OptionsHeaderGeneral = "Основные настройки" diff -r 7a6364917f86 -r e31b02b24488 ui/AmrUiCheckBox.lua --- a/ui/AmrUiCheckBox.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/ui/AmrUiCheckBox.lua Tue Jul 17 09:57:39 2018 -0700 @@ -20,6 +20,8 @@ -------------------------------------------------------------------------------]] local function buttonOnClick(frame, ...) AceGUI:ClearFocus() + frame.obj:SetChecked(not frame.obj.isChecked) + --PlaySound("igMainMenuOption") frame.obj:Fire("OnClick", ...) end @@ -37,6 +39,10 @@ self.frame:ClearAllPoints() end, + --["OnRelease"] = function(self) + -- print(self.name .. " released") + --end, + ["SetText"] = function(self, text) self.label:SetText(text) self.frame:SetWidth(16 + 6 + self.label:GetStringWidth()) @@ -47,12 +53,18 @@ end, ["SetChecked"] = function(self, checked) - -- not sure if WoW expects boolean type or not, too lazy to find out so just cast it - self.frame:SetChecked(not not checked) + self.isChecked = not not checked + if checked then + self.texNormal:Hide() + self.texCheck:Show() + else + self.texCheck:Hide() + self.texNormal:Show() + end end, - ["GetChecked"] = function(self) - return self.frame:GetChecked() + ["GetChecked"] = function(self) + return self.isChecked end, ["GetWidth"] = function(self) @@ -82,7 +94,7 @@ -------------------------------------------------------------------------------]] local function Constructor() local name = "AmrUiCheckBox" .. AceGUI:GetNextWidgetNum(Type) - local frame = CreateFrame("CheckButton", name, UIParent) + local frame = CreateFrame("Button", nil, UIParent) frame:SetHeight(16) frame:SetPushedTextOffset(0, 0) frame:Hide() @@ -92,29 +104,34 @@ -- unchecked texture local texNormal = frame:CreateTexture(nil, "BACKGROUND") + texNormal.name = name texNormal:SetWidth(16) texNormal:SetHeight(16) texNormal:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\check-off") texNormal:SetPoint("LEFT", frame, "LEFT") - frame:SetNormalTexture(texNormal) -- checked texture - local texCheck = frame:CreateTexture(nil, "BORDER") + local texCheck = frame:CreateTexture(nil, "BACKGROUND") + texCheck.name = name + texCheck:SetWidth(16) + texCheck:SetHeight(16) texCheck:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\check-on") texCheck:SetPoint("LEFT", frame, "LEFT") - frame:SetCheckedTexture(texCheck) + texCheck:Hide() -- label - local lbl = frame:CreateFontString(nil, "ARTWORK") + local lbl = frame:CreateFontString(nil, "BACKGROUND") lbl:SetJustifyV("MIDDLE") lbl:SetPoint("LEFT", texNormal, "RIGHT", 8, 0) - frame:SetFontString(lbl) local widget = { texNormal = texNormal, + texCheck = texCheck, label = lbl, frame = frame, - type = Type + type = Type, + isChecked = false, + name = name } for method, func in pairs(methods) do widget[method] = func diff -r 7a6364917f86 -r e31b02b24488 ui/AmrUiLabel.lua --- a/ui/AmrUiLabel.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/ui/AmrUiLabel.lua Tue Jul 17 09:57:39 2018 -0700 @@ -76,7 +76,7 @@ -- reset the flag self.resizing = nil -- run the update explicitly - updateSize(self) + --updateSize(self) end, -- ["OnRelease"] = nil, @@ -134,6 +134,7 @@ frame:SetScript("OnMouseUp", frameOnMouseUp) local label = frame:CreateFontString(nil, "ARTWORK") + label:SetPoint("TOPLEFT") label:SetFontObject(Amr.CreateFont("Regular", 16, Amr.Colors.Text)) -- create widget diff -r 7a6364917f86 -r e31b02b24488 ui/AmrUiTabGroup.lua --- a/ui/AmrUiTabGroup.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/ui/AmrUiTabGroup.lua Tue Jul 17 09:57:39 2018 -0700 @@ -225,7 +225,7 @@ -------------------------------------------------------------------------------]] local function Constructor() local num = AceGUI:GetNextWidgetNum(Type) - local frame = CreateFrame("Frame",nil,UIParent) + local frame = CreateFrame("Frame", nil, UIParent) frame:SetHeight(100) frame:SetWidth(100) frame:SetFrameStrata("FULLSCREEN_DIALOG") diff -r 7a6364917f86 -r e31b02b24488 ui/Ui.lua --- a/ui/Ui.lua Mon Feb 12 19:34:09 2018 -0800 +++ b/ui/Ui.lua Tue Jul 17 09:57:39 2018 -0700 @@ -168,24 +168,25 @@ -- some status text local lblStatus = AceGUI:Create("AmrUiLabel") + f:AddChild(lblStatus) lblStatus:SetWidth(900) lblStatus:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan)) lblStatus:SetText("Ask Mr. Robot " .. L.MainStatusText("v" .. GetAddOnMetadata(Amr.ADDON_NAME, "Version"), "https://www.askmrrobot.com/wow/addon")) lblStatus:SetJustifyH("CENTER") lblStatus:SetWordWrap(false) lblStatus:SetPoint("TOP", f.content, "BOTTOM") - f:AddChild(lblStatus) -- create the main UI container local c = AceGUI:Create("AmrUiPanel") c:SetLayout("Fill") c:SetBackgroundColor(Amr.Colors.Black, 0) + f:AddChild(c) c:SetPoint("TOPLEFT", f.content, "TOPLEFT") c:SetPoint("BOTTOMRIGHT", f.content, "BOTTOMRIGHT") - f:AddChild(c) -- create the main tab strip local t = AceGUI:Create("AmrUiTabGroup") + c:AddChild(t) t:SetLayout("None") t:SetTabs({ {text=L.TabExportText, value="Export"}, @@ -195,16 +196,15 @@ {text=L.TabOptionsText, value="Options"} }) t:SetCallback("OnGroupSelected", onMainTabSelected) - c:AddChild(t) -- create the cover/overlay container c = AceGUI:Create("AmrUiPanel") c:SetLayout("None") c:EnableMouse(true) c:SetBackgroundColor(Amr.Colors.Black, 0.75) + f:AddChild(c) c:SetPoint("TOPLEFT", f.frame, "TOPLEFT") c:SetPoint("BOTTOMRIGHT", f.frame, "BOTTOMRIGHT") - f:AddChild(c) -- after adding, set cover to sit on top of everything, then hide it c:SetStrata("FULLSCREEN_DIALOG") @@ -213,13 +213,13 @@ -- put standard cover ui elements (label, cancel button) local coverMsg = AceGUI:Create("AmrUiLabel") + c:AddChild(coverMsg) coverMsg:SetWidth(600) coverMsg:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan)) coverMsg:SetJustifyH("MIDDLE") coverMsg:SetJustifyV("MIDDLE") coverMsg:SetText("") coverMsg:SetPoint("CENTER", c.frame, "CENTER", 0, 20) - c:AddChild(coverMsg) local coverCancel = AceGUI:Create("AmrUiTextButton") coverCancel:SetWidth(200) @@ -227,8 +227,8 @@ coverCancel:SetText(L.CoverCancel) coverCancel:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextHeaderDisabled)) coverCancel:SetHoverFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextHeaderActive)) + c:AddChild(coverCancel) coverCancel:SetPoint("CENTER", c.frame, "CENTER", 0, -20) - c:AddChild(coverCancel) coverCancel:SetCallback("OnClick", function(widget) Amr:HideCover() @@ -238,9 +238,9 @@ local coverContent = AceGUI:Create("AmrUiPanel") coverContent:SetLayout("None") coverContent:SetBackgroundColor(Amr.Colors.Black, 0) + c:AddChild(coverContent) coverContent:SetPoint("TOPLEFT", c.frame, "TOPLEFT") coverContent:SetPoint("BOTTOMRIGHT", c.frame, "BOTTOMRIGHT") - c:AddChild(coverContent) _mainFrame = f _mainTabs = t @@ -288,23 +288,23 @@ border:SetBackgroundColor(Amr.Colors.BorderBlue) border:SetWidth(400) border:SetHeight(150) + container:AddChild(border) border:SetPoint("CENTER", container.frame, "CENTER") - container:AddChild(border) local bg = AceGUI:Create("AmrUiPanel") bg:SetLayout("None") bg:SetBackgroundColor(Amr.Colors.Bg) + border:AddChild(bg) bg:SetPoint("TOPLEFT", border.frame, "TOPLEFT", 1, -1) bg:SetPoint("BOTTOMRIGHT", border.frame, "BOTTOMRIGHT", -1, 1) - border:AddChild(bg) local lbl = AceGUI:Create("AmrUiLabel") + bg:AddChild(lbl) lbl:SetWidth(360) lbl:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.Text)) lbl:SetJustifyH("CENTER") lbl:SetText(message) lbl:SetPoint("TOP", bg.content, "TOP", 0, -20) - bg:AddChild(lbl) local btn = AceGUI:Create("AmrUiButton") btn:SetBackgroundColor(Amr.Colors.Orange) @@ -312,8 +312,8 @@ btn:SetWidth(120) btn:SetHeight(26) btn:SetText(btnText) + bg:AddChild(btn) btn:SetPoint("BOTTOM", bg.content, "BOTTOM", 0, 20) - bg:AddChild(btn) btn:SetCallback("OnClick", function(widget) Amr:HideCover() @@ -410,15 +410,15 @@ border:SetBackgroundColor(Amr.Colors.BorderBlue) border:SetWidth(width + 2) border:SetHeight(height + 2) + container:AddChild(border) border:SetPoint("CENTER", container.frame, "CENTER") - container:AddChild(border) local bg = AceGUI:Create("AmrUiPanel") bg:SetLayout("None") bg:SetBackgroundColor(Amr.Colors.Bg) + border:AddChild(bg) bg:SetPoint("TOPLEFT", border.frame, "TOPLEFT", 1, -1) bg:SetPoint("BOTTOMRIGHT", border.frame, "BOTTOMRIGHT", -1, 1) - border:AddChild(bg) return bg, border end