annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 135:57be71eccc0a v63

Small azerite gear fix.
author yellowfive
date Sun, 12 Aug 2018 23:36:17 -0700
parents a0894ffebd15
children 6dc0e8e9f960
rev   line source
yellowfive@57 1 -- AskMrRobot-Serializer will serialize and communicate character data between users.
yellowfive@57 2
yellowfive@135 3 local MAJOR, MINOR = "AskMrRobot-Serializer", 63
yellowfive@57 4 local Amr, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
yellowfive@57 5
yellowfive@57 6 if not Amr then return end -- already loaded by something else
yellowfive@57 7
yellowfive@57 8 -- event and comm used for player snapshotting on entering combat
yellowfive@57 9 LibStub("AceEvent-3.0"):Embed(Amr)
yellowfive@57 10 LibStub("AceComm-3.0"):Embed(Amr)
yellowfive@57 11
yellowfive@57 12 ----------------------------------------------------------------------------------------
yellowfive@57 13 -- Constants
yellowfive@57 14 ----------------------------------------------------------------------------------------
yellowfive@57 15
yellowfive@57 16 -- prefix used for communicating gear snapshots created by the AMR serializer
yellowfive@57 17 Amr.ChatPrefix = "_AMRS"
yellowfive@57 18
yellowfive@57 19 -- map of region ids to AMR region names
yellowfive@57 20 Amr.RegionNames = {
yellowfive@57 21 [1] = "US",
yellowfive@57 22 [2] = "KR",
yellowfive@57 23 [3] = "EU",
yellowfive@57 24 [4] = "TW",
yellowfive@57 25 [5] = "CN"
yellowfive@57 26 }
yellowfive@57 27
yellowfive@57 28 -- map of the skillLine returned by profession API to the AMR profession name
yellowfive@57 29 Amr.ProfessionSkillLineToName = {
yellowfive@57 30 [794] = "Archaeology",
yellowfive@57 31 [171] = "Alchemy",
yellowfive@57 32 [164] = "Blacksmithing",
yellowfive@57 33 [185] = "Cooking",
yellowfive@57 34 [333] = "Enchanting",
yellowfive@57 35 [202] = "Engineering",
yellowfive@57 36 [129] = "First Aid",
yellowfive@57 37 [356] = "Fishing",
yellowfive@57 38 [182] = "Herbalism",
yellowfive@57 39 [773] = "Inscription",
yellowfive@57 40 [755] = "Jewelcrafting",
yellowfive@57 41 [165] = "Leatherworking",
yellowfive@57 42 [186] = "Mining",
yellowfive@57 43 [393] = "Skinning",
yellowfive@57 44 [197] = "Tailoring"
yellowfive@57 45 }
yellowfive@57 46
yellowfive@57 47 -- all slot IDs that we care about, ordered in AMR standard display order
yellowfive@57 48 Amr.SlotIds = { 16, 17, 1, 2, 3, 15, 5, 9, 10, 6, 7, 8, 11, 12, 13, 14 }
yellowfive@57 49
yellowfive@57 50 Amr.SpecIds = {
yellowfive@57 51 [250] = 1, -- DeathKnightBlood
yellowfive@57 52 [251] = 2, -- DeathKnightFrost
yellowfive@57 53 [252] = 3, -- DeathKnightUnholy
yellowfive@81 54 [577] = 4, -- DemonHunterHavoc
yellowfive@81 55 [581] = 5, -- DemonHunterVengeance
yellowfive@81 56 [102] = 6, -- DruidBalance
yellowfive@81 57 [103] = 7, -- DruidFeral
yellowfive@81 58 [104] = 8, -- DruidGuardian
yellowfive@81 59 [105] = 9, -- DruidRestoration
yellowfive@81 60 [253] = 10, -- HunterBeastMastery
yellowfive@81 61 [254] = 11, -- HunterMarksmanship
yellowfive@81 62 [255] = 12, -- HunterSurvival
yellowfive@81 63 [62] = 13, -- MageArcane
yellowfive@81 64 [63] = 14, -- MageFire
yellowfive@81 65 [64] = 15, -- MageFrost
yellowfive@81 66 [268] = 16, -- MonkBrewmaster
yellowfive@81 67 [270] = 17, -- MonkMistweaver
yellowfive@81 68 [269] = 18, -- MonkWindwalker
yellowfive@81 69 [65] = 19, -- PaladinHoly
yellowfive@81 70 [66] = 20, -- PaladinProtection
yellowfive@81 71 [70] = 21, -- PaladinRetribution
yellowfive@81 72 [256] = 22, -- PriestDiscipline
yellowfive@81 73 [257] = 23, -- PriestHoly
yellowfive@81 74 [258] = 24, -- PriestShadow
yellowfive@81 75 [259] = 25, -- RogueAssassination
yellowfive@81 76 [260] = 26, -- RogueOutlaw
yellowfive@81 77 [261] = 27, -- RogueSubtlety
yellowfive@81 78 [262] = 28, -- ShamanElemental
yellowfive@81 79 [263] = 29, -- ShamanEnhancement
yellowfive@81 80 [264] = 30, -- ShamanRestoration
yellowfive@81 81 [265] = 31, -- WarlockAffliction
yellowfive@81 82 [266] = 32, -- WarlockDemonology
yellowfive@81 83 [267] = 33, -- WarlockDestruction
yellowfive@81 84 [71] = 34, -- WarriorArms
yellowfive@81 85 [72] = 35, -- WarriorFury
yellowfive@81 86 [73] = 36 -- WarriorProtection
yellowfive@57 87 }
yellowfive@57 88
yellowfive@57 89 Amr.ClassIds = {
yellowfive@57 90 ["NONE"] = 0,
yellowfive@57 91 ["DEATHKNIGHT"] = 1,
yellowfive@81 92 ["DEMONHUNTER"] = 2,
yellowfive@81 93 ["DRUID"] = 3,
yellowfive@81 94 ["HUNTER"] = 4,
yellowfive@81 95 ["MAGE"] = 5,
yellowfive@81 96 ["MONK"] = 6,
yellowfive@81 97 ["PALADIN"] = 7,
yellowfive@81 98 ["PRIEST"] = 8,
yellowfive@81 99 ["ROGUE"] = 9,
yellowfive@81 100 ["SHAMAN"] = 10,
yellowfive@81 101 ["WARLOCK"] = 11,
yellowfive@81 102 ["WARRIOR"] = 12,
yellowfive@57 103 }
yellowfive@57 104
yellowfive@57 105 Amr.ProfessionIds = {
yellowfive@57 106 ["None"] = 0,
yellowfive@57 107 ["Mining"] = 1,
yellowfive@57 108 ["Skinning"] = 2,
yellowfive@57 109 ["Herbalism"] = 3,
yellowfive@57 110 ["Enchanting"] = 4,
yellowfive@57 111 ["Jewelcrafting"] = 5,
yellowfive@57 112 ["Engineering"] = 6,
yellowfive@57 113 ["Blacksmithing"] = 7,
yellowfive@57 114 ["Leatherworking"] = 8,
yellowfive@57 115 ["Inscription"] = 9,
yellowfive@57 116 ["Tailoring"] = 10,
yellowfive@57 117 ["Alchemy"] = 11,
yellowfive@57 118 ["Fishing"] = 12,
yellowfive@57 119 ["Cooking"] = 13,
yellowfive@57 120 ["First Aid"] = 14,
yellowfive@57 121 ["Archaeology"] = 15
yellowfive@57 122 }
yellowfive@57 123
yellowfive@57 124 Amr.RaceIds = {
yellowfive@57 125 ["None"] = 0,
yellowfive@57 126 ["BloodElf"] = 1,
yellowfive@57 127 ["Draenei"] = 2,
yellowfive@57 128 ["Dwarf"] = 3,
yellowfive@57 129 ["Gnome"] = 4,
yellowfive@57 130 ["Human"] = 5,
yellowfive@57 131 ["NightElf"] = 6,
yellowfive@57 132 ["Orc"] = 7,
yellowfive@57 133 ["Tauren"] = 8,
yellowfive@57 134 ["Troll"] = 9,
yellowfive@57 135 ["Scourge"] = 10,
yellowfive@57 136 ["Undead"] = 10,
yellowfive@57 137 ["Goblin"] = 11,
yellowfive@57 138 ["Worgen"] = 12,
yellowfive@120 139 ["Pandaren"] = 13,
yellowfive@120 140 ["Nightborne"] = 14,
yellowfive@120 141 ["HighmountainTauren"] = 15,
yellowfive@120 142 ["VoidElf"] = 16,
yellowfive@135 143 ["LightforgedDraenei"] = 17,
yellowfive@135 144 ["DarkIronDwarf"] = 18,
yellowfive@135 145 ["MagharOrc"] = 19
yellowfive@57 146 }
yellowfive@57 147
yellowfive@57 148 Amr.FactionIds = {
yellowfive@57 149 ["None"] = 0,
yellowfive@57 150 ["Alliance"] = 1,
yellowfive@57 151 ["Horde"] = 2
yellowfive@57 152 }
yellowfive@57 153
yellowfive@57 154 Amr.InstanceIds = {
yellowfive@93 155 EmeraldNightmare = 1520,
yellowfive@104 156 Nighthold = 1530,
yellowfive@110 157 TrialOfValor = 1648,
yellowfive@118 158 TombOfSargeras = 1676,
yellowfive@118 159 Antorus = 1712
yellowfive@57 160 }
yellowfive@57 161
yellowfive@57 162 -- instances that AskMrRobot currently supports logging for
yellowfive@57 163 Amr.SupportedInstanceIds = {
yellowfive@93 164 [1520] = true,
yellowfive@104 165 [1530] = true,
yellowfive@110 166 [1648] = true,
yellowfive@118 167 [1676] = true,
yellowfive@118 168 [1712] = true
yellowfive@57 169 }
yellowfive@57 170
yellowfive@57 171
yellowfive@57 172 ----------------------------------------------------------------------------------------
yellowfive@57 173 -- Public Utility Methods
yellowfive@57 174 ----------------------------------------------------------------------------------------
yellowfive@57 175
yellowfive@81 176 local function readBonusIdList(parts, first, last)
yellowfive@124 177 local ret = {}
yellowfive@81 178 for i = first, last do
yellowfive@81 179 table.insert(ret, tonumber(parts[i]))
yellowfive@81 180 end
yellowfive@81 181 table.sort(ret)
yellowfive@81 182 return ret
yellowfive@81 183 end
yellowfive@81 184
yellowfive@124 185 -- 1 2 3 4 5 6 7 8 9 10 11 12
yellowfive@124 186 -- itemId:ench:gem1 :gem2 :gem3 :gem4:suf:uid:lvl:spec:flags :instdiffid:numbonusIDs:bonusIDs1...n :varies:?:relic bonus ids
yellowfive@124 187 --|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
yellowfive@124 188 --
yellowfive@57 189 -- get an object with all of the parts of the item link format that we care about
yellowfive@57 190 function Amr.ParseItemLink(itemLink)
yellowfive@57 191 if not itemLink then return nil end
yellowfive@57 192
yellowfive@57 193 local str = string.match(itemLink, "|Hitem:([\-%d:]+)|")
yellowfive@57 194 if not str then return nil end
yellowfive@57 195
yellowfive@57 196 local parts = { strsplit(":", str) }
yellowfive@57 197
yellowfive@124 198 local item = {}
yellowfive@124 199 item.link = itemLink
yellowfive@81 200 item.id = tonumber(parts[1]) or 0
yellowfive@81 201 item.enchantId = tonumber(parts[2]) or 0
yellowfive@81 202 item.gemIds = { tonumber(parts[3]) or 0, tonumber(parts[4]) or 0, tonumber(parts[5]) or 0, tonumber(parts[6]) or 0 }
yellowfive@81 203 item.suffixId = math.abs(tonumber(parts[7]) or 0) -- convert suffix to positive number, that's what we use in our code
yellowfive@81 204 -- part 8 is some unique ID... we never really used it
yellowfive@81 205 -- part 9 is current player level
yellowfive@81 206 -- part 10 is player spec
yellowfive@81 207 local upgradeIdType = tonumber(parts[11]) or 0 -- part 11 indicates what kind of upgrade ID is just after the bonus IDs
yellowfive@81 208 -- part 12 is instance difficulty id
yellowfive@57 209
yellowfive@81 210 local numBonuses = tonumber(parts[13]) or 0
yellowfive@81 211 local offset = numBonuses
yellowfive@81 212 if numBonuses > 0 then
yellowfive@81 213 item.bonusIds = readBonusIdList(parts, 14, 13 + numBonuses)
yellowfive@57 214 end
yellowfive@69 215
yellowfive@81 216 item.upgradeId = 0
yellowfive@81 217 item.level = 0
yellowfive@81 218
yellowfive@124 219 -- the next part after bonus IDs depends on the upgrade id type
yellowfive@81 220 if upgradeIdType == 4 then
yellowfive@81 221 item.upgradeId = tonumber(parts[14 + offset]) or 0
yellowfive@81 222 elseif upgradeIdType == 512 then
yellowfive@81 223 item.level = tonumber(parts[14 + offset]) or 0
yellowfive@124 224 elseif #parts > 16 + offset then
yellowfive@124 225 -- check for relic info
yellowfive@124 226 item.relicBonusIds = { nil, nil, nil }
yellowfive@124 227 numBonuses = tonumber(parts[16 + offset])
yellowfive@124 228 if numBonuses then
yellowfive@124 229 if numBonuses > 0 then
yellowfive@124 230 item.relicBonusIds[1] = readBonusIdList(parts, 17 + offset, 16 + offset + numBonuses)
yellowfive@124 231 end
yellowfive@124 232
yellowfive@129 233 offset = offset + numBonuses
yellowfive@124 234 if #parts > 17 + offset then
yellowfive@124 235 numBonuses = tonumber(parts[17 + offset])
yellowfive@129 236 if numBonuses then
yellowfive@129 237 if numBonuses > 0 then
yellowfive@129 238 item.relicBonusIds[2] = readBonusIdList(parts, 18 + offset, 17 + offset + numBonuses)
yellowfive@129 239 end
yellowfive@129 240
yellowfive@129 241 offset= offset + numBonuses
yellowfive@129 242 if #parts > 18 + offset then
yellowfive@129 243 numBonuses = tonumber(parts[18 + offset])
yellowfive@129 244 if numBonuses then
yellowfive@129 245 if numBonuses > 0 then
yellowfive@129 246 item.relicBonusIds[3] = readBonusIdList(parts, 19 + offset, 18 + offset + numBonuses)
yellowfive@129 247 end
yellowfive@129 248 end
yellowfive@129 249 end
yellowfive@124 250 end
yellowfive@124 251 end
yellowfive@124 252 end
yellowfive@69 253 end
yellowfive@81 254
yellowfive@57 255 return item
yellowfive@57 256 end
yellowfive@57 257
yellowfive@135 258 local AZERITE_EMPOWERED_BONUS_ID = 4775
yellowfive@135 259
yellowfive@135 260 function Amr.GetItemUniqueId(item, noUpgrade, noAzeriteEmpoweredBonusId)
yellowfive@81 261 if not item then return "" end
yellowfive@81 262 local ret = item.id .. ""
yellowfive@81 263 if item.bonusIds then
yellowfive@135 264 for i = 1, #item.bonusIds do
yellowfive@135 265 if not noAzeriteEmpoweredBonusId or item.bonusIds[i] ~= AZERITE_EMPOWERED_BONUS_ID then
yellowfive@135 266 ret = ret .. "b" .. item.bonusIds[i]
yellowfive@135 267 end
yellowfive@81 268 end
yellowfive@81 269 end
yellowfive@81 270 if item.suffixId ~= 0 then
yellowfive@81 271 ret = ret .. "s" .. item.suffixId
yellowfive@81 272 end
yellowfive@81 273 if not noUpgrade and item.upgradeId ~= 0 then
yellowfive@81 274 ret = ret .. "u" .. item.upgradeId
yellowfive@81 275 end
yellowfive@81 276 if item.level ~= 0 then
yellowfive@81 277 ret = ret .. "v" .. item.level
yellowfive@81 278 end
yellowfive@81 279 return ret
yellowfive@81 280 end
yellowfive@81 281
yellowfive@57 282 -- returns true if this is an instance that AskMrRobot supports for logging
yellowfive@57 283 function Amr.IsSupportedInstanceId(instanceMapID)
yellowfive@57 284 if Amr.SupportedInstanceIds[tonumber(instanceMapID)] then
yellowfive@57 285 return true
yellowfive@57 286 else
yellowfive@57 287 return false
yellowfive@57 288 end
yellowfive@57 289 end
yellowfive@57 290
yellowfive@57 291 -- returns true if currently in a supported instance for logging
yellowfive@57 292 function Amr.IsSupportedInstance()
yellowfive@133 293 local _, _, _, _, _, _, _, instanceMapID = GetInstanceInfo()
yellowfive@57 294 return Amr.IsSupportedInstanceId(instanceMapID)
yellowfive@57 295 end
yellowfive@57 296
yellowfive@133 297 --[[
yellowfive@81 298 -- scanning tooltip b/c for some odd reason the api has no way to get basic item properties...
yellowfive@81 299 -- so you have to generate a fake item tooltip and search for pre-defined strings in the display text
yellowfive@81 300 local _scanTt
yellowfive@81 301 function Amr.GetScanningTooltip()
yellowfive@81 302 if not _scanTt then
yellowfive@81 303 _scanTt = CreateFrame("GameTooltip", "AmrUiScanTooltip", nil, "GameTooltipTemplate")
yellowfive@81 304 _scanTt:SetOwner(UIParent, "ANCHOR_NONE")
yellowfive@81 305 end
yellowfive@81 306 return _scanTt
yellowfive@81 307 end
yellowfive@81 308
yellowfive@81 309 -- get the item tooltip for the specified item in one of your bags, or if bagId is nil, an equipped item, or if slotId is also nil, the specified item link
yellowfive@81 310 function Amr.GetItemTooltip(bagId, slotId, link)
yellowfive@81 311 local tt = Amr.GetScanningTooltip()
yellowfive@81 312 tt:ClearLines()
yellowfive@81 313 if bagId then
yellowfive@81 314 tt:SetBagItem(bagId, slotId)
yellowfive@81 315 elseif slotId then
yellowfive@81 316 tt:SetInventoryItem("player", slotId)
yellowfive@81 317 else
yellowfive@81 318 tt:SetHyperlink(link)
yellowfive@81 319 end
yellowfive@81 320 return tt
yellowfive@81 321 end
yellowfive@133 322 ]]
yellowfive@81 323
yellowfive@133 324 --[[
yellowfive@124 325 function Amr.GetItemLevel(bagId, slotId, link)
yellowfive@81 326 local itemLevelPattern = _G["ITEM_LEVEL"]:gsub("%%d", "(%%d+)")
yellowfive@81 327 local tt = Amr.GetItemTooltip(bagId, slotId, link)
yellowfive@81 328
yellowfive@81 329 local regions = { tt:GetRegions() }
yellowfive@81 330 for i, region in ipairs(regions) do
yellowfive@81 331 if region and region:GetObjectType() == "FontString" then
yellowfive@81 332 local text = region:GetText()
yellowfive@81 333 if text then
yellowfive@81 334 ilvl = tonumber(text:match(itemLevelPattern))
yellowfive@81 335 if ilvl then
yellowfive@81 336 return ilvl
yellowfive@81 337 end
yellowfive@81 338 end
yellowfive@81 339 end
yellowfive@81 340 end
yellowfive@81 341
yellowfive@81 342 -- 0 means we couldn't find it for whatever reason
yellowfive@81 343 return 0
yellowfive@81 344 end
yellowfive@133 345 ]]
yellowfive@81 346
yellowfive@57 347
yellowfive@57 348 ----------------------------------------------------------------------------------------
yellowfive@57 349 -- Character Reading
yellowfive@57 350 ----------------------------------------------------------------------------------------
yellowfive@57 351
yellowfive@57 352 local function readProfessionInfo(prof, ret)
yellowfive@57 353 if prof then
yellowfive@133 354 local _, _, skillLevel, _, _, _, skillLine = GetProfessionInfo(prof);
yellowfive@57 355 if Amr.ProfessionSkillLineToName[skillLine] ~= nil then
yellowfive@57 356 ret.Professions[Amr.ProfessionSkillLineToName[skillLine]] = skillLevel;
yellowfive@57 357 end
yellowfive@57 358 end
yellowfive@57 359 end
yellowfive@57 360
yellowfive@124 361 -- get specs
yellowfive@81 362 local function readSpecs(ret)
yellowfive@57 363
yellowfive@81 364 for pos = 1, 4 do
yellowfive@57 365 -- spec, convert game spec id to one of our spec ids
yellowfive@81 366 local specId = GetSpecializationInfo(pos)
yellowfive@57 367 if specId then
yellowfive@81 368 ret.Specs[pos] = Amr.SpecIds[specId]
yellowfive@57 369 end
yellowfive@57 370 end
yellowfive@57 371 end
yellowfive@57 372
yellowfive@124 373 local function dump(o)
yellowfive@124 374 if type(o) == 'table' then
yellowfive@124 375 local s = '{ '
yellowfive@124 376 for k,v in pairs(o) do
yellowfive@124 377 if type(k) ~= 'number' then k = '"'..k..'"' end
yellowfive@124 378 s = s .. '['..k..'] = ' .. dump(v) .. ','
yellowfive@124 379 end
yellowfive@124 380 return s .. '} '
yellowfive@124 381 else
yellowfive@124 382 return tostring(o)
yellowfive@124 383 end
yellowfive@124 384 end
yellowfive@124 385
yellowfive@124 386 -- read azerite powers on the item in loc and put it on itemData
yellowfive@124 387 function Amr.ReadAzeritePowers(loc)
yellowfive@124 388 local ret = {}
yellowfive@124 389 local hasSome = false
yellowfive@124 390
yellowfive@124 391 local tiers = C_AzeriteEmpoweredItem.GetAllTierInfo(loc)
yellowfive@124 392 for tier, tierInfo in ipairs(tiers) do
yellowfive@124 393 for _, power in ipairs(tierInfo.azeritePowerIDs) do
yellowfive@124 394 if C_AzeriteEmpoweredItem.IsPowerSelected(loc, power) then
yellowfive@124 395 local powerInfo = C_AzeriteEmpoweredItem.GetPowerInfo(power)
yellowfive@124 396 table.insert(ret, powerInfo.spellID)
yellowfive@124 397 hasSome = true
yellowfive@124 398 end
yellowfive@124 399 end
yellowfive@124 400 end
yellowfive@124 401
yellowfive@124 402 if hasSome then
yellowfive@124 403 return ret
yellowfive@124 404 else
yellowfive@124 405 return nil
yellowfive@124 406 end
yellowfive@124 407 end
yellowfive@124 408
yellowfive@57 409 -- get currently equipped items, store with currently active spec
yellowfive@57 410 local function readEquippedItems(ret)
yellowfive@124 411 local equippedItems = {};
yellowfive@124 412 local loc = ItemLocation.CreateEmpty()
yellowfive@57 413 for slotNum = 1, #Amr.SlotIds do
yellowfive@57 414 local slotId = Amr.SlotIds[slotNum]
yellowfive@57 415 local itemLink = GetInventoryItemLink("player", slotId)
yellowfive@57 416 if itemLink then
yellowfive@124 417 local itemData = Amr.ParseItemLink(itemLink)
yellowfive@124 418 if itemData then
yellowfive@124 419 -- see if this is an azerite item and read azerite power ids
yellowfive@124 420 loc:SetEquipmentSlot(slotId)
yellowfive@124 421 if C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItem(loc) then
yellowfive@124 422 local powers = Amr.ReadAzeritePowers(loc)
yellowfive@124 423 if powers then
yellowfive@124 424 itemData.azerite = powers
yellowfive@124 425 end
yellowfive@124 426 end
yellowfive@124 427
yellowfive@124 428 equippedItems[slotId] = itemData
yellowfive@124 429 end
yellowfive@57 430 end
yellowfive@57 431 end
yellowfive@57 432
yellowfive@57 433 -- store last-seen equipped gear for each spec
yellowfive@81 434 ret.Equipped[GetSpecialization()] = equippedItems
yellowfive@57 435 end
yellowfive@57 436
yellowfive@124 437 local function readHeartOfAzerothLevel(ret)
yellowfive@124 438 local azeriteItemLocation = C_AzeriteItem.FindActiveAzeriteItem();
yellowfive@124 439 if azeriteItemLocation then
yellowfive@124 440 local azeriteItem = Item:CreateFromItemLocation(azeriteItemLocation);
yellowfive@124 441 ret.HeartOfAzerothLevel = C_AzeriteItem.GetPowerLevel(azeriteItemLocation)
yellowfive@124 442 else
yellowfive@124 443 ret.HeartOfAzerothLevel = 0
yellowfive@124 444 end
yellowfive@124 445 end
yellowfive@124 446
yellowfive@124 447 -- Get just the player's currently equipped gear
yellowfive@124 448 function Amr:GetEquipped()
yellowfive@124 449 local ret= {}
yellowfive@124 450 ret.Equipped = {}
yellowfive@124 451 readEquippedItems(ret)
yellowfive@124 452 return ret
yellowfive@124 453 end
yellowfive@124 454
yellowfive@57 455 -- Get all data about the player as an object, includes:
yellowfive@57 456 -- serializer version
yellowfive@57 457 -- region/realm/name
yellowfive@57 458 -- guild
yellowfive@57 459 -- race
yellowfive@57 460 -- faction
yellowfive@57 461 -- level
yellowfive@57 462 -- professions
yellowfive@81 463 -- spec/talent for all specs
yellowfive@57 464 -- equipped gear for the current spec
yellowfive@57 465 --
yellowfive@81 466 function Amr:GetPlayerData()
yellowfive@57 467
yellowfive@57 468 local ret = {}
yellowfive@57 469
yellowfive@57 470 ret.Region = Amr.RegionNames[GetCurrentRegion()]
yellowfive@57 471 ret.Realm = GetRealmName()
yellowfive@57 472 ret.Name = UnitName("player")
yellowfive@57 473 ret.Guild = GetGuildInfo("player")
yellowfive@81 474 ret.ActiveSpec = GetSpecialization()
yellowfive@57 475 ret.Level = UnitLevel("player");
yellowfive@124 476 readHeartOfAzerothLevel(ret)
yellowfive@124 477
yellowfive@133 478 local _, clsEn = UnitClass("player")
yellowfive@57 479 ret.Class = clsEn;
yellowfive@57 480
yellowfive@133 481 local _, raceEn = UnitRace("player")
yellowfive@57 482 ret.Race = raceEn;
yellowfive@57 483 ret.Faction = UnitFactionGroup("player")
yellowfive@57 484
yellowfive@57 485 ret.Professions = {};
yellowfive@57 486 local prof1, prof2, archaeology, fishing, cooking, firstAid = GetProfessions();
yellowfive@57 487 readProfessionInfo(prof1, ret)
yellowfive@57 488 readProfessionInfo(prof2, ret)
yellowfive@57 489 readProfessionInfo(archaeology, ret)
yellowfive@57 490 readProfessionInfo(fishing, ret)
yellowfive@57 491 readProfessionInfo(cooking, ret)
yellowfive@57 492 readProfessionInfo(firstAid, ret)
yellowfive@57 493
yellowfive@57 494 ret.Specs = {}
yellowfive@57 495 ret.Talents = {}
yellowfive@81 496 readSpecs(ret)
yellowfive@81 497
yellowfive@124 498 ret.Equipped = {}
yellowfive@57 499 readEquippedItems(ret)
yellowfive@57 500
yellowfive@57 501 return ret
yellowfive@57 502 end
yellowfive@57 503
yellowfive@57 504
yellowfive@57 505 ----------------------------------------------------------------------------------------
yellowfive@57 506 -- Serialization
yellowfive@57 507 ----------------------------------------------------------------------------------------
yellowfive@57 508
yellowfive@57 509 local function toCompressedNumberList(list)
yellowfive@57 510 -- ensure the values are numbers, sorted from lowest to highest
yellowfive@57 511 local nums = {}
yellowfive@57 512 for i, v in ipairs(list) do
yellowfive@57 513 table.insert(nums, tonumber(v))
yellowfive@57 514 end
yellowfive@57 515 table.sort(nums)
yellowfive@57 516
yellowfive@57 517 local ret = {}
yellowfive@57 518 local prev = 0
yellowfive@57 519 for i, v in ipairs(nums) do
yellowfive@57 520 local diff = v - prev
yellowfive@57 521 table.insert(ret, diff)
yellowfive@57 522 prev = v
yellowfive@57 523 end
yellowfive@57 524
yellowfive@57 525 return table.concat(ret, ",")
yellowfive@57 526 end
yellowfive@57 527
yellowfive@57 528 -- make this utility publicly available
yellowfive@57 529 function Amr:ToCompressedNumberList(list)
yellowfive@57 530 return toCompressedNumberList(list)
yellowfive@57 531 end
yellowfive@57 532
yellowfive@57 533 -- appends a list of items to the export
yellowfive@57 534 local function appendItemsToExport(fields, itemObjects)
yellowfive@57 535
yellowfive@57 536 -- sort by item id so we can compress it more easily
yellowfive@57 537 table.sort(itemObjects, function(a, b) return a.id < b.id end)
yellowfive@57 538
yellowfive@57 539 -- append to the export string
yellowfive@57 540 local prevItemId = 0
yellowfive@57 541 local prevGemId = 0
yellowfive@57 542 local prevEnchantId = 0
yellowfive@57 543 local prevUpgradeId = 0
yellowfive@57 544 local prevBonusId = 0
yellowfive@81 545 local prevLevel = 0
yellowfive@124 546 local prevAzeriteId = 0
yellowfive@124 547 local prevRelicBonusId = 0
yellowfive@57 548 for i, itemData in ipairs(itemObjects) do
yellowfive@57 549 local itemParts = {}
yellowfive@57 550
yellowfive@57 551 table.insert(itemParts, itemData.id - prevItemId)
yellowfive@57 552 prevItemId = itemData.id
yellowfive@57 553
yellowfive@57 554 if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end
yellowfive@124 555 --if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end
yellowfive@57 556 if itemData.upgradeId ~= 0 then
yellowfive@57 557 table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId))
yellowfive@57 558 prevUpgradeId = itemData.upgradeId
yellowfive@57 559 end
yellowfive@81 560 if itemData.level ~= 0 then
yellowfive@81 561 table.insert(itemParts, "v" .. (itemData.level - prevLevel))
yellowfive@81 562 prevLevel = itemData.level
yellowfive@81 563 end
yellowfive@57 564 if itemData.bonusIds then
yellowfive@57 565 for bIndex, bValue in ipairs(itemData.bonusIds) do
yellowfive@57 566 table.insert(itemParts, "b" .. (bValue - prevBonusId))
yellowfive@57 567 prevBonusId = bValue
yellowfive@57 568 end
yellowfive@124 569 end
yellowfive@124 570
yellowfive@124 571 if itemData.azerite then
yellowfive@124 572 for aIndex, aValue in ipairs(itemData.azerite) do
yellowfive@124 573 table.insert(itemParts, "a" .. (aValue - prevAzeriteId))
yellowfive@124 574 prevAzeriteId = aValue
yellowfive@124 575 end
yellowfive@124 576 end
yellowfive@81 577
yellowfive@81 578 if itemData.gemIds[1] ~= 0 then
yellowfive@81 579 table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId))
yellowfive@81 580 prevGemId = itemData.gemIds[1]
yellowfive@81 581 end
yellowfive@81 582 if itemData.gemIds[2] ~= 0 then
yellowfive@81 583 table.insert(itemParts, "y" .. (itemData.gemIds[2] - prevGemId))
yellowfive@81 584 prevGemId = itemData.gemIds[2]
yellowfive@81 585 end
yellowfive@81 586 if itemData.gemIds[3] ~= 0 then
yellowfive@81 587 table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId))
yellowfive@81 588 prevGemId = itemData.gemIds[3]
yellowfive@124 589 end
yellowfive@81 590
yellowfive@57 591 if itemData.enchantId ~= 0 then
yellowfive@57 592 table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId))
yellowfive@57 593 prevEnchantId = itemData.enchantId
yellowfive@57 594 end
yellowfive@124 595
yellowfive@124 596 if itemData.relicBonusIds and itemData.relicBonusIds[1] ~= nil then
yellowfive@124 597 for bIndex, bValue in ipairs(itemData.relicBonusIds[1]) do
yellowfive@124 598 table.insert(itemParts, "p" .. (bValue - prevRelicBonusId))
yellowfive@124 599 prevRelicBonusId = bValue
yellowfive@124 600 end
yellowfive@124 601 end
yellowfive@124 602
yellowfive@124 603 if itemData.relicBonusIds and itemData.relicBonusIds[2] ~= nil then
yellowfive@124 604 for bIndex, bValue in ipairs(itemData.relicBonusIds[2]) do
yellowfive@124 605 table.insert(itemParts, "q" .. (bValue - prevRelicBonusId))
yellowfive@124 606 prevRelicBonusId = bValue
yellowfive@124 607 end
yellowfive@124 608 end
yellowfive@124 609
yellowfive@124 610 if itemData.relicBonusIds and itemData.relicBonusIds[3] ~= nil then
yellowfive@124 611 for bIndex, bValue in ipairs(itemData.relicBonusIds[3]) do
yellowfive@124 612 table.insert(itemParts, "r" .. (bValue - prevRelicBonusId))
yellowfive@124 613 prevRelicBonusId = bValue
yellowfive@124 614 end
yellowfive@124 615 end
yellowfive@124 616
yellowfive@57 617 table.insert(fields, table.concat(itemParts, ""))
yellowfive@57 618 end
yellowfive@57 619 end
yellowfive@57 620
yellowfive@57 621 -- Serialize just the identity portion of a player (region/realm/name) in the same format used by the full serialization
yellowfive@57 622 function Amr:SerializePlayerIdentity(data)
yellowfive@57 623 local fields = {}
yellowfive@57 624 table.insert(fields, MINOR)
yellowfive@57 625 table.insert(fields, data.Region)
yellowfive@57 626 table.insert(fields, data.Realm)
yellowfive@57 627 table.insert(fields, data.Name)
yellowfive@57 628 return "$" .. table.concat(fields, ";") .. "$"
yellowfive@57 629 end
yellowfive@57 630
yellowfive@57 631 -- Serialize player data gathered by GetPlayerData. This can be augmented with extra data if desired (augmenting used mainly by AskMrRobot addon).
yellowfive@57 632 -- Pass complete = true to do a complete export of this extra information, otherwise it is ignored.
yellowfive@57 633 -- Extra data can include:
yellowfive@57 634 -- equipped gear for the player's inactive spec, slot id to item link dictionary
yellowfive@57 635 -- Reputations
yellowfive@57 636 -- BagItems, BankItems, VoidItems, lists of item links
yellowfive@57 637 --
yellowfive@57 638 function Amr:SerializePlayerData(data, complete)
yellowfive@57 639
yellowfive@57 640 local fields = {}
yellowfive@57 641
yellowfive@57 642 -- compressed string uses a fixed order rather than inserting identifiers
yellowfive@57 643 table.insert(fields, MINOR)
yellowfive@57 644 table.insert(fields, data.Region)
yellowfive@57 645 table.insert(fields, data.Realm)
yellowfive@57 646 table.insert(fields, data.Name)
yellowfive@57 647
yellowfive@57 648 -- guild name
yellowfive@57 649 if data.Guild == nil then
yellowfive@57 650 table.insert(fields, "")
yellowfive@57 651 else
yellowfive@57 652 table.insert(fields, data.Guild)
yellowfive@57 653 end
yellowfive@57 654
yellowfive@57 655 -- race, default to pandaren if we can't read it for some reason
yellowfive@57 656 local raceval = Amr.RaceIds[data.Race]
yellowfive@57 657 if raceval == nil then raceval = 13 end
yellowfive@57 658 table.insert(fields, raceval)
yellowfive@57 659
yellowfive@57 660 -- faction, default to alliance if we can't read it for some reason
yellowfive@57 661 raceval = Amr.FactionIds[data.Faction]
yellowfive@57 662 if raceval == nil then raceval = 1 end
yellowfive@57 663 table.insert(fields, raceval)
yellowfive@57 664
yellowfive@124 665 table.insert(fields, data.Level)
yellowfive@124 666 table.insert(fields, data.HeartOfAzerothLevel)
yellowfive@57 667
yellowfive@57 668 local profs = {}
yellowfive@57 669 local noprofs = true
yellowfive@57 670 if data.Professions then
yellowfive@57 671 for k, v in pairs(data.Professions) do
yellowfive@57 672 local profval = Amr.ProfessionIds[k]
yellowfive@57 673 if profval ~= nil then
yellowfive@57 674 noprofs = false
yellowfive@57 675 table.insert(profs, profval .. ":" .. v)
yellowfive@57 676 end
yellowfive@57 677 end
yellowfive@57 678 end
yellowfive@57 679
yellowfive@57 680 if noprofs then
yellowfive@57 681 table.insert(profs, "0:0")
yellowfive@57 682 end
yellowfive@57 683
yellowfive@57 684 table.insert(fields, table.concat(profs, ","))
yellowfive@57 685
yellowfive@57 686 -- export specs
yellowfive@57 687 table.insert(fields, data.ActiveSpec)
yellowfive@81 688 for spec = 1, 4 do
yellowfive@57 689 if data.Specs[spec] and (complete or spec == data.ActiveSpec) then
yellowfive@57 690 table.insert(fields, ".s" .. spec) -- indicates the start of a spec block
yellowfive@81 691 table.insert(fields, data.Specs[spec])
yellowfive@124 692 table.insert(fields, data.Talents[spec] or "")
yellowfive@57 693 end
yellowfive@57 694 end
yellowfive@57 695
yellowfive@57 696 -- export equipped gear
yellowfive@57 697 if data.Equipped then
yellowfive@81 698 for spec = 1, 4 do
yellowfive@57 699 if data.Equipped[spec] and (complete or spec == data.ActiveSpec) then
yellowfive@57 700 table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block
yellowfive@57 701
yellowfive@57 702 local itemObjects = {}
yellowfive@124 703 for k, itemData in pairs(data.Equipped[spec]) do
yellowfive@57 704 itemData.slot = k
yellowfive@57 705 table.insert(itemObjects, itemData)
yellowfive@57 706 end
yellowfive@57 707
yellowfive@57 708 appendItemsToExport(fields, itemObjects)
yellowfive@57 709 end
yellowfive@57 710 end
yellowfive@57 711 end
yellowfive@57 712
yellowfive@124 713 -- if doing a complete export, include bank/bag items too
yellowfive@124 714 if complete then
yellowfive@124 715
yellowfive@57 716 local itemObjects = {}
yellowfive@57 717 if data.BagItems then
yellowfive@124 718 for i, itemData in ipairs(data.BagItems) do
yellowfive@124 719 if itemData then
yellowfive@57 720 table.insert(itemObjects, itemData)
yellowfive@57 721 end
yellowfive@57 722 end
yellowfive@57 723 end
yellowfive@127 724 if data.BankItems then
yellowfive@124 725 for i, itemData in ipairs(data.BankItems) do
yellowfive@127 726 if itemData then
yellowfive@57 727 table.insert(itemObjects, itemData)
yellowfive@57 728 end
yellowfive@57 729 end
yellowfive@124 730 end
yellowfive@124 731
yellowfive@57 732 table.insert(fields, ".inv")
yellowfive@57 733 appendItemsToExport(fields, itemObjects)
yellowfive@57 734 end
yellowfive@57 735
yellowfive@57 736 return "$" .. table.concat(fields, ";") .. "$"
yellowfive@57 737
yellowfive@57 738 end
yellowfive@57 739
yellowfive@57 740 -- Shortcut for the common use case: serialize the player's currently active setup with no extras.
yellowfive@57 741 function Amr:SerializePlayer()
yellowfive@57 742 local data = self:GetPlayerData()
yellowfive@57 743 return self:SerializePlayerData(data)
yellowfive@57 744 end
yellowfive@57 745
yellowfive@81 746 --[[
yellowfive@57 747 ----------------------------------------------------------------------------------------------------------------------
yellowfive@57 748 -- Character Snapshots
yellowfive@81 749 -- This feature snapshots a player's gear/talents/artifact when entering combat. It is enabled by default. Consumers
yellowfive@57 750 -- of this library can create a setting to enable/disable it as desired per a user setting.
yellowfive@57 751 --
yellowfive@57 752 -- You should register for the AMR_SNAPSHOT_STATE_CHANGED message (sent via AceEvent-3.0 messaging) to ensure that
yellowfive@57 753 -- your addon settings stay in sync with any other addon that may also be trying to control the enabled state.
yellowfive@57 754 --
yellowfive@57 755 -- Note that if a user has the main AMR addon installed, it will always enable snapshotting, and override any attempt
yellowfive@57 756 -- to disable it by immediately re-enabling it and thus re-triggering AMR_SNAPSHOT_STATE_CHANGED.
yellowfive@57 757 ----------------------------------------------------------------------------------------------------------------------
yellowfive@57 758 Amr._snapshotEnabled = true
yellowfive@57 759
yellowfive@57 760 -- Enable snapshotting of character data when entering combat. Sends this player's character data to anyone logging with the AskMrRobot addon.
yellowfive@57 761 function Amr:EnableSnapshots()
yellowfive@57 762 self._snapshotEnabled = true
yellowfive@57 763 self:SendMessage("AMR_SNAPSHOT_STATE_CHANGED", self._snapshotEnabled)
yellowfive@57 764 end
yellowfive@57 765
yellowfive@57 766 -- Disable snapshotting of character data when entering combat.
yellowfive@57 767 function Amr:DisableSnapshots()
yellowfive@57 768 self._snapshotEnabled = false
yellowfive@57 769 self:SendMessage("AMR_SNAPSHOT_STATE_CHANGED", self._snapshotEnabled)
yellowfive@57 770 end
yellowfive@57 771
yellowfive@57 772 function Amr:IsSnapshotEnabled()
yellowfive@57 773 return self._snapshotEnabled
yellowfive@57 774 end
yellowfive@57 775
yellowfive@57 776
yellowfive@57 777 function Amr:PLAYER_REGEN_DISABLED()
yellowfive@57 778 --function Amr:GARRISON_MISSION_NPC_OPENED()
yellowfive@57 779
yellowfive@57 780 -- send data about this character when a player enters combat in a supported zone
yellowfive@57 781 if self._snapshotEnabled and Amr.IsSupportedInstance() then
yellowfive@57 782 local t = time()
yellowfive@57 783 local player = self:GetPlayerData()
yellowfive@57 784 local msg = self:SerializePlayerData(player)
yellowfive@57 785 msg = string.format("%s\r%s\n%s\n%s\n%s\n%s", MINOR, t, player.Region, player.Realm, player.Name, msg)
yellowfive@57 786
yellowfive@57 787 self:SendCommMessage(Amr.ChatPrefix, msg, "RAID")
yellowfive@57 788 end
yellowfive@57 789 end
yellowfive@57 790
yellowfive@57 791 Amr:RegisterEvent("PLAYER_REGEN_DISABLED")
yellowfive@81 792 --Amr:RegisterEvent("GARRISON_MISSION_NPC_OPENED") -- for debugging, fire this event when open mission table
yellowfive@122 793 ]]