annotate Import.lua @ 139:c229c759a125 v65

Update to support multiple setups per spec.
author yellowfive
date Mon, 05 Nov 2018 16:06:00 -0800
parents a0894ffebd15
children cc82eeeec1c8
rev   line source
yellowfive@57 1 local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
yellowfive@57 2 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
yellowfive@57 3 local AceGUI = LibStub("AceGUI-3.0")
yellowfive@57 4
yellowfive@57 5 local _txtImport
yellowfive@57 6 local _lblError
yellowfive@69 7 local _panelCover
yellowfive@57 8
yellowfive@57 9 local function onImportOkClick(widget)
yellowfive@57 10 local txt = _txtImport:GetText()
yellowfive@57 11 local msg = Amr:ImportCharacter(txt)
yellowfive@57 12 if msg then
yellowfive@57 13 _lblError:SetText(msg)
yellowfive@57 14 _txtImport:SetFocus(true)
yellowfive@57 15 else
yellowfive@57 16 Amr:HideCover()
yellowfive@139 17 Amr:RefreshGearDisplay()
yellowfive@57 18 end
yellowfive@57 19 end
yellowfive@57 20
yellowfive@57 21 local function onImportCancelClick(widget)
yellowfive@57 22 Amr:HideCover()
yellowfive@57 23 end
yellowfive@57 24
yellowfive@69 25 local function onTextEnterPressed(widget)
yellowfive@69 26 -- hide the overwolf cover when import data is received
yellowfive@69 27 if _panelCover then
yellowfive@69 28 _panelCover:SetVisible(false)
yellowfive@69 29 end
yellowfive@69 30
yellowfive@69 31 -- do an import if the data starts and ends with a dollar sign
yellowfive@69 32 local txt = _txtImport:GetText()
yellowfive@69 33 local txtLen = string.len(txt)
yellowfive@69 34 if txtLen > 2 and string.sub(txt, 1, 1) == '$' then
yellowfive@69 35 onImportOkClick()
yellowfive@69 36 end
yellowfive@69 37
yellowfive@69 38 end
yellowfive@69 39
yellowfive@69 40 local function renderImportWindow(container, fromOverwolf)
yellowfive@57 41
yellowfive@57 42 local panelImport = Amr:RenderCoverChrome(container, 700, 450)
yellowfive@57 43
yellowfive@57 44 local lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 45 panelImport:AddChild(lbl)
yellowfive@57 46 lbl:SetWidth(600)
yellowfive@57 47 lbl:SetText(L.ImportHeader)
yellowfive@57 48 lbl:SetPoint("TOP", panelImport.content, "TOP", 0, -10)
yellowfive@57 49
yellowfive@57 50 _txtImport = AceGUI:Create("AmrUiTextarea")
yellowfive@57 51 _txtImport:SetWidth(600)
yellowfive@57 52 _txtImport:SetHeight(300)
yellowfive@57 53 _txtImport:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
yellowfive@69 54 _txtImport:SetCallback("OnEnterPressed", onTextEnterPressed)
yellowfive@57 55 panelImport:AddChild(_txtImport)
yellowfive@124 56 _txtImport:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10)
yellowfive@57 57
yellowfive@57 58 local btnImportOk = AceGUI:Create("AmrUiButton")
yellowfive@57 59 btnImportOk:SetText(L.ImportButtonOk)
yellowfive@57 60 btnImportOk:SetBackgroundColor(Amr.Colors.Green)
yellowfive@57 61 btnImportOk:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
yellowfive@57 62 btnImportOk:SetWidth(120)
yellowfive@57 63 btnImportOk:SetHeight(28)
yellowfive@57 64 btnImportOk:SetCallback("OnClick", onImportOkClick)
yellowfive@57 65 panelImport:AddChild(btnImportOk)
yellowfive@124 66 btnImportOk:SetPoint("TOPLEFT", _txtImport.frame, "BOTTOMLEFT", 0, -10)
yellowfive@57 67
yellowfive@57 68 local btnImportCancel = AceGUI:Create("AmrUiButton")
yellowfive@57 69 btnImportCancel:SetText(L.ImportButtonCancel)
yellowfive@57 70 btnImportCancel:SetBackgroundColor(Amr.Colors.Green)
yellowfive@57 71 btnImportCancel:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
yellowfive@57 72 btnImportCancel:SetWidth(120)
yellowfive@57 73 btnImportCancel:SetHeight(28)
yellowfive@57 74 btnImportCancel:SetCallback("OnClick", onImportCancelClick)
yellowfive@57 75 panelImport:AddChild(btnImportCancel)
yellowfive@124 76 btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0)
yellowfive@57 77
yellowfive@57 78 _lblError = AceGUI:Create("AmrUiLabel")
yellowfive@124 79 panelImport:AddChild(_lblError)
yellowfive@57 80 _lblError:SetWidth(600)
yellowfive@57 81 _lblError:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Red))
yellowfive@57 82 _lblError:SetText("")
yellowfive@57 83 _lblError:SetPoint("TOPLEFT", btnImportOk.frame, "BOTTOMLEFT", 0, -20)
yellowfive@57 84
yellowfive@69 85 if fromOverwolf then
yellowfive@69 86 -- show a cover preventing interaction until we receive data from overwolf
yellowfive@69 87 _panelCover = AceGUI:Create("AmrUiPanel")
yellowfive@69 88 _panelCover:SetLayout("None")
yellowfive@69 89 _panelCover:EnableMouse(true)
yellowfive@69 90 _panelCover:SetBackgroundColor(Amr.Colors.Black, 0.75)
yellowfive@124 91 panelImport:AddChild(_panelCover)
yellowfive@69 92 _panelCover:SetPoint("TOPLEFT", panelImport.frame, "TOPLEFT")
yellowfive@69 93 _panelCover:SetPoint("BOTTOMRIGHT", panelImport.frame, "BOTTOMRIGHT")
yellowfive@69 94
yellowfive@69 95 local coverMsg = AceGUI:Create("AmrUiLabel")
yellowfive@124 96 _panelCover:AddChild(coverMsg)
yellowfive@69 97 coverMsg:SetWidth(500)
yellowfive@69 98 coverMsg:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan))
yellowfive@69 99 coverMsg:SetJustifyH("MIDDLE")
yellowfive@69 100 coverMsg:SetJustifyV("MIDDLE")
yellowfive@69 101 coverMsg:SetText(L.ImportOverwolfWait)
yellowfive@69 102 coverMsg:SetPoint("CENTER", _panelCover.frame, "CENTER", 0, 20)
yellowfive@69 103
yellowfive@69 104 -- after adding, set cover to sit on top of everything
yellowfive@69 105 _panelCover:SetStrata("FULLSCREEN_DIALOG")
yellowfive@69 106 _panelCover:SetLevel(Amr.FrameLevels.Highest)
yellowfive@69 107 end
yellowfive@57 108 end
yellowfive@57 109
yellowfive@69 110 function Amr:ShowImportWindow(fromOverwolf)
yellowfive@57 111 -- this is shown as a modal dialog
yellowfive@69 112 Amr:ShowCover(function(container)
yellowfive@69 113 renderImportWindow(container, fromOverwolf)
yellowfive@69 114 end)
yellowfive@57 115
yellowfive@57 116 _txtImport:SetText("")
yellowfive@57 117 _txtImport:SetFocus(true)
yellowfive@57 118 end
yellowfive@57 119
yellowfive@57 120 ----------------------------------------------------------------------------
yellowfive@57 121 -- Import Parsing
yellowfive@57 122 ----------------------------------------------------------------------------
yellowfive@57 123
yellowfive@57 124 --
yellowfive@57 125 -- Import a character, returning nil on success, otherwise an error message, import result stored in the db.
yellowfive@57 126 --
yellowfive@139 127 function Amr:ImportCharacter(data, isTest, isChild)
yellowfive@57 128
yellowfive@57 129 -- make sure all data is up to date before importing and get a local copy of player's current state
yellowfive@57 130 local currentPlayerData = self:ExportCharacter()
yellowfive@57 131
yellowfive@57 132 if data == nil or string.len(data) == 0 then
yellowfive@57 133 return L.ImportErrorEmpty
yellowfive@57 134 end
yellowfive@57 135
yellowfive@139 136 -- if multiple setups are included in the data, parse each individually, then quit
yellowfive@57 137 local specParts = { strsplit("\n", data) }
yellowfive@139 138 if #specParts > 1 then
yellowfive@139 139 -- clear out any previously-imported BiB setups when importing new ones (non-BiB will always be imported one at a time)
yellowfive@139 140 for i = #Amr.db.char.GearSetups, 1, -1 do
yellowfive@139 141 if Amr.db.char.GearSetups[i].IsBib then
yellowfive@139 142 table.remove(Amr.db.char.GearSetups, i)
yellowfive@139 143 end
yellowfive@139 144 end
yellowfive@139 145
yellowfive@57 146 for i = 1, #specParts do
yellowfive@139 147 local err = self:ImportCharacter(specParts[i], isTest, true)
yellowfive@57 148 if err ~= nil then
yellowfive@57 149 return err
yellowfive@57 150 end
yellowfive@139 151 end
yellowfive@139 152
yellowfive@139 153 -- ensure that all BiB setups are sorted to the top
yellowfive@139 154 local nonBib = {}
yellowfive@139 155 for i = #Amr.db.char.GearSetups, 1, -1 do
yellowfive@139 156 if not Amr.db.char.GearSetups[i].IsBib then
yellowfive@139 157 table.insert(nonBib, Amr.db.char.GearSetups[i])
yellowfive@139 158 table.remove(Amr.db.char.GearSetups, i)
yellowfive@139 159 end
yellowfive@139 160 end
yellowfive@139 161 for i, setup in ipairs(nonBib) do
yellowfive@139 162 table.insert(Amr.db.char.GearSetups, setup)
yellowfive@139 163 end
yellowfive@139 164
yellowfive@139 165 return
yellowfive@57 166 end
yellowfive@57 167
yellowfive@57 168 local data1 = { strsplit("$", data) }
yellowfive@57 169 if #data1 ~= 3 then
yellowfive@57 170 return L.ImportErrorFormat
yellowfive@57 171 end
yellowfive@57 172
yellowfive@57 173 local parts = { strsplit(";", data1[2]) }
yellowfive@57 174
yellowfive@57 175 -- require a minimum version
yellowfive@57 176 local ver = tonumber(parts[1])
yellowfive@57 177 if ver < Amr.MIN_IMPORT_VERSION then
yellowfive@57 178 return L.ImportErrorVersion
yellowfive@57 179 end
yellowfive@57 180
yellowfive@57 181 -- require name match (don't match realm due to language issues for now)
yellowfive@57 182 if not isTest then
yellowfive@57 183 local region = parts[2]
yellowfive@57 184 local realm = parts[3]
yellowfive@57 185 local name = parts[4]
yellowfive@57 186 if name ~= currentPlayerData.Name then
yellowfive@57 187 local importPlayerName = name .. " (" .. realm .. ")"
yellowfive@57 188 local you = currentPlayerData.Name .. " (" .. currentPlayerData.Realm .. ")"
yellowfive@57 189 return L.ImportErrorChar(importPlayerName, you)
yellowfive@57 190 end
yellowfive@57 191
yellowfive@57 192 -- require race match
yellowfive@57 193 local race = tonumber(parts[6])
yellowfive@57 194 if race ~= Amr.RaceIds[currentPlayerData.Race] then
yellowfive@57 195 return L.ImportErrorRace
yellowfive@57 196 end
yellowfive@57 197
yellowfive@57 198 -- require faction match
yellowfive@57 199 local faction = tonumber(parts[7])
yellowfive@57 200 if faction ~= Amr.FactionIds[currentPlayerData.Faction] then
yellowfive@57 201 return L.ImportErrorFaction
yellowfive@57 202 end
yellowfive@57 203
yellowfive@57 204 -- require level match
yellowfive@57 205 local level = tonumber(parts[8])
yellowfive@57 206 if level ~= currentPlayerData.Level then
yellowfive@57 207 return L.ImportErrorLevel
yellowfive@57 208 end
yellowfive@57 209 end
yellowfive@57 210
yellowfive@57 211 -- if we make it this far, the data is valid, so read item information
yellowfive@124 212 local specSlot = tonumber(parts[11])
yellowfive@57 213
yellowfive@57 214 local importData = {}
yellowfive@57 215 local enchantInfo = {}
yellowfive@57 216
yellowfive@57 217 local prevItemId = 0
yellowfive@57 218 local prevGemId = 0
yellowfive@57 219 local prevEnchantId = 0
yellowfive@57 220 local prevUpgradeId = 0
yellowfive@57 221 local prevBonusId = 0
yellowfive@124 222 local prevLevel = 0
yellowfive@124 223 local prevAzeriteId = 0
yellowfive@57 224 local digits = {
yellowfive@57 225 ["-"] = true,
yellowfive@57 226 ["0"] = true,
yellowfive@57 227 ["1"] = true,
yellowfive@57 228 ["2"] = true,
yellowfive@57 229 ["3"] = true,
yellowfive@57 230 ["4"] = true,
yellowfive@57 231 ["5"] = true,
yellowfive@57 232 ["6"] = true,
yellowfive@57 233 ["7"] = true,
yellowfive@57 234 ["8"] = true,
yellowfive@57 235 ["9"] = true,
yellowfive@57 236 }
yellowfive@124 237 for i = 16, #parts do
yellowfive@57 238 local itemString = parts[i]
yellowfive@57 239 if itemString ~= "" and itemString ~= "_" then
yellowfive@57 240 local tokens = {}
yellowfive@57 241 local bonusIds = {}
yellowfive@124 242 local azerite = {}
yellowfive@57 243 local hasBonuses = false
yellowfive@124 244 local hasAzerites = false
yellowfive@57 245 local token = ""
yellowfive@57 246 local prop = "i"
yellowfive@57 247 local tokenComplete = false
yellowfive@57 248 for j = 1, string.len(itemString) do
yellowfive@57 249 local c = string.sub(itemString, j, j)
yellowfive@57 250 if digits[c] == nil then
yellowfive@57 251 tokenComplete = true
yellowfive@57 252 else
yellowfive@57 253 token = token .. c
yellowfive@57 254 end
yellowfive@57 255
yellowfive@57 256 if tokenComplete or j == string.len(itemString) then
yellowfive@57 257 local val = tonumber(token)
yellowfive@57 258 if prop == "i" then
yellowfive@57 259 val = val + prevItemId
yellowfive@57 260 prevItemId = val
yellowfive@57 261 elseif prop == "u" then
yellowfive@57 262 val = val + prevUpgradeId
yellowfive@57 263 prevUpgradeId = val
yellowfive@81 264 elseif prop == "v" then
yellowfive@81 265 val = val + prevLevel
yellowfive@81 266 prevLevel = val
yellowfive@57 267 elseif prop == "b" then
yellowfive@57 268 val = val + prevBonusId
yellowfive@57 269 prevBonusId = val
yellowfive@57 270 elseif prop == "x" or prop == "y" or prop == "z" then
yellowfive@57 271 val = val + prevGemId
yellowfive@57 272 prevGemId = val
yellowfive@57 273 elseif prop == "e" then
yellowfive@57 274 val = val + prevEnchantId
yellowfive@57 275 prevEnchantId = val
yellowfive@124 276 elseif prop == "a" then
yellowfive@124 277 val = val + prevAzeriteId
yellowfive@124 278 prevAzeriteId = val
yellowfive@57 279 end
yellowfive@57 280
yellowfive@57 281 if prop == "b" then
yellowfive@57 282 table.insert(bonusIds, val)
yellowfive@57 283 hasBonuses = true
yellowfive@124 284 elseif prop == "a" then
yellowfive@124 285 table.insert(azerite, val)
yellowfive@124 286 hasAzerites = true
yellowfive@57 287 else
yellowfive@57 288 tokens[prop] = val
yellowfive@57 289 end
yellowfive@57 290
yellowfive@57 291 token = ""
yellowfive@57 292 tokenComplete = false
yellowfive@57 293
yellowfive@57 294 -- we have moved on to the next token
yellowfive@57 295 prop = c
yellowfive@57 296 end
yellowfive@57 297 end
yellowfive@57 298
yellowfive@57 299 local obj = {}
yellowfive@57 300 importData[tonumber(tokens["s"])] = obj
yellowfive@57 301
yellowfive@57 302 obj.id = tokens["i"]
yellowfive@57 303 obj.suffixId = tokens["f"] or 0
yellowfive@57 304 obj.upgradeId = tokens["u"] or 0
yellowfive@81 305 obj.level = tokens["v"] or 0
yellowfive@57 306 obj.enchantId = tokens["e"] or 0
yellowfive@89 307 obj.inventoryId = tokens["t"] or 0
yellowfive@57 308
yellowfive@57 309 obj.gemIds = {}
yellowfive@57 310 table.insert(obj.gemIds, tokens["x"] or 0)
yellowfive@57 311 table.insert(obj.gemIds, tokens["y"] or 0)
yellowfive@57 312 table.insert(obj.gemIds, tokens["z"] or 0)
yellowfive@57 313 table.insert(obj.gemIds, 0)
yellowfive@57 314
yellowfive@57 315 if hasBonuses then
yellowfive@57 316 obj.bonusIds = bonusIds
yellowfive@57 317 end
yellowfive@57 318
yellowfive@124 319 if hasAzerites then
yellowfive@124 320 obj.azerite = azerite
yellowfive@57 321 end
yellowfive@57 322 end
yellowfive@57 323 end
yellowfive@57 324
yellowfive@139 325 -- extra information contains setup id, display label, then extra enchant info
yellowfive@57 326 parts = { strsplit("@", data1[3]) }
yellowfive@139 327
yellowfive@139 328 local setupId = parts[2]
yellowfive@139 329 local setupName = parts[3]
yellowfive@139 330
yellowfive@139 331 for i = 4, #parts do
yellowfive@57 332 local infoParts = { strsplit("\\", parts[i]) }
yellowfive@57 333
yellowfive@124 334 if infoParts[1] == "e" then
yellowfive@57 335
yellowfive@57 336 local enchObj = {}
yellowfive@57 337 enchObj.id = tonumber(infoParts[2])
yellowfive@57 338 enchObj.itemId = tonumber(infoParts[3])
yellowfive@57 339 enchObj.spellId = tonumber(infoParts[4])
yellowfive@57 340 enchObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.StatsShort[s] end)
yellowfive@57 341
yellowfive@57 342 local mats = infoParts[6]
yellowfive@57 343 if string.len(mats) > 0 then
yellowfive@57 344 enchObj.materials = {}
yellowfive@57 345 mats = { strsplit(",", mats) }
yellowfive@57 346 for j = 1, #mats do
yellowfive@57 347 local kv = { strsplit("=", mats[j]) }
yellowfive@57 348 enchObj.materials[tonumber(kv[1])] = tonumber(kv[2])
yellowfive@57 349 end
yellowfive@57 350 end
yellowfive@57 351
yellowfive@124 352 enchantInfo[enchObj.id] = enchObj
yellowfive@57 353 end
yellowfive@57 354 end
yellowfive@57 355
yellowfive@57 356 if isTest then
yellowfive@57 357 print("spec " .. specSlot)
yellowfive@57 358 -- print result for debugging
yellowfive@57 359 for k,v in pairs(importData) do
yellowfive@57 360 local blah = Amr.CreateItemLink(v)
yellowfive@57 361 --print(blah)
yellowfive@57 362 local name, link = GetItemInfo(blah)
yellowfive@57 363 print(link)
yellowfive@57 364 if link == nil then
yellowfive@57 365 print(blah)
yellowfive@57 366 print("bad item: " .. v.id)
yellowfive@57 367 end
yellowfive@133 368 end
yellowfive@57 369 else
yellowfive@57 370 -- we have succeeded, record the result
yellowfive@139 371 local result = {
yellowfive@139 372 IsBib = string.sub(setupId, 1, 3) ~= "AMR",
yellowfive@139 373 SpecSlot = tonumber(specSlot),
yellowfive@139 374 Id = setupId,
yellowfive@139 375 Label = setupName,
yellowfive@139 376 Gear = importData
yellowfive@139 377 }
yellowfive@139 378
yellowfive@139 379 if not result.IsBib then
yellowfive@139 380 -- replace if this setup already exists
yellowfive@139 381 local key = -1
yellowfive@139 382 for i,setup in ipairs(Amr.db.char.GearSetups) do
yellowfive@139 383 if setup.Id == result.Id then
yellowfive@139 384 key = i
yellowfive@139 385 break
yellowfive@139 386 end
yellowfive@139 387 end
yellowfive@139 388
yellowfive@139 389 if key ~= -1 then
yellowfive@139 390 Amr.db.char.GearSetups[key] = result
yellowfive@139 391 else
yellowfive@139 392 table.insert(Amr.db.char.GearSetups, result)
yellowfive@139 393 end
yellowfive@139 394
yellowfive@139 395 if not isChild then
yellowfive@139 396 -- if doing a single import of a setup, make it active
yellowfive@139 397 Amr:SetActiveSetupId(setupId)
yellowfive@139 398 end
yellowfive@139 399 else
yellowfive@139 400 table.insert(Amr.db.char.GearSetups, result)
yellowfive@139 401 end
yellowfive@124 402
yellowfive@124 403 for k,v in pairs(enchantInfo) do
yellowfive@124 404 Amr.db.char.ExtraEnchantData[k] = v
yellowfive@124 405 end
yellowfive@57 406
yellowfive@57 407 -- also update shopping list after import
yellowfive@57 408 Amr:UpdateShoppingData(currentPlayerData)
yellowfive@57 409 end
yellowfive@57 410 end