annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 171:5d25f06e0633 v80

Interface version bumped.
author yellowfive
date Mon, 30 Sep 2019 15:11:16 -0700
parents d90a740a1d0b
children 556cb7c32580
rev   line source
yellowfive@57 1 -- AskMrRobot-Serializer will serialize and communicate character data between users.
yellowfive@57 2
yellowfive@171 3 local MAJOR, MINOR = "AskMrRobot-Serializer", 80
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@155 145 ["MagharOrc"] = 19,
yellowfive@155 146 ["ZandalariTroll"] = 20,
yellowfive@157 147 ["KulTiran"] = 21
yellowfive@57 148 }
yellowfive@57 149
yellowfive@57 150 Amr.FactionIds = {
yellowfive@57 151 ["None"] = 0,
yellowfive@57 152 ["Alliance"] = 1,
yellowfive@57 153 ["Horde"] = 2
yellowfive@57 154 }
yellowfive@57 155
yellowfive@57 156 Amr.InstanceIds = {
yellowfive@153 157 Uldir = 1861,
yellowfive@155 158 Dazar = 2070,
yellowfive@167 159 Storms = 2096,
yellowfive@167 160 Palace = 2164
yellowfive@57 161 }
yellowfive@57 162
yellowfive@57 163 -- instances that AskMrRobot currently supports logging for
yellowfive@57 164 Amr.SupportedInstanceIds = {
yellowfive@153 165 [1861] = true,
yellowfive@155 166 [2070] = true,
yellowfive@167 167 [2096] = true,
yellowfive@167 168 [2164] = 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@165 497
yellowfive@165 498 -- these get updated later, since need to cache info for inactive specs
yellowfive@165 499 ret.UnlockedEssences = {}
yellowfive@165 500 ret.Essences = {}
yellowfive@81 501
yellowfive@124 502 ret.Equipped = {}
yellowfive@57 503 readEquippedItems(ret)
yellowfive@57 504
yellowfive@57 505 return ret
yellowfive@57 506 end
yellowfive@57 507
yellowfive@57 508
yellowfive@57 509 ----------------------------------------------------------------------------------------
yellowfive@57 510 -- Serialization
yellowfive@57 511 ----------------------------------------------------------------------------------------
yellowfive@57 512
yellowfive@57 513 local function toCompressedNumberList(list)
yellowfive@57 514 -- ensure the values are numbers, sorted from lowest to highest
yellowfive@57 515 local nums = {}
yellowfive@57 516 for i, v in ipairs(list) do
yellowfive@57 517 table.insert(nums, tonumber(v))
yellowfive@57 518 end
yellowfive@57 519 table.sort(nums)
yellowfive@57 520
yellowfive@57 521 local ret = {}
yellowfive@57 522 local prev = 0
yellowfive@57 523 for i, v in ipairs(nums) do
yellowfive@57 524 local diff = v - prev
yellowfive@57 525 table.insert(ret, diff)
yellowfive@57 526 prev = v
yellowfive@57 527 end
yellowfive@57 528
yellowfive@57 529 return table.concat(ret, ",")
yellowfive@57 530 end
yellowfive@57 531
yellowfive@57 532 -- make this utility publicly available
yellowfive@57 533 function Amr:ToCompressedNumberList(list)
yellowfive@57 534 return toCompressedNumberList(list)
yellowfive@57 535 end
yellowfive@57 536
yellowfive@57 537 -- appends a list of items to the export
yellowfive@57 538 local function appendItemsToExport(fields, itemObjects)
yellowfive@57 539
yellowfive@57 540 -- sort by item id so we can compress it more easily
yellowfive@57 541 table.sort(itemObjects, function(a, b) return a.id < b.id end)
yellowfive@57 542
yellowfive@57 543 -- append to the export string
yellowfive@57 544 local prevItemId = 0
yellowfive@57 545 local prevGemId = 0
yellowfive@57 546 local prevEnchantId = 0
yellowfive@57 547 local prevUpgradeId = 0
yellowfive@57 548 local prevBonusId = 0
yellowfive@81 549 local prevLevel = 0
yellowfive@124 550 local prevAzeriteId = 0
yellowfive@124 551 local prevRelicBonusId = 0
yellowfive@57 552 for i, itemData in ipairs(itemObjects) do
yellowfive@57 553 local itemParts = {}
yellowfive@57 554
yellowfive@57 555 table.insert(itemParts, itemData.id - prevItemId)
yellowfive@57 556 prevItemId = itemData.id
yellowfive@57 557
yellowfive@57 558 if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end
yellowfive@124 559 --if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end
yellowfive@57 560 if itemData.upgradeId ~= 0 then
yellowfive@57 561 table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId))
yellowfive@57 562 prevUpgradeId = itemData.upgradeId
yellowfive@57 563 end
yellowfive@81 564 if itemData.level ~= 0 then
yellowfive@81 565 table.insert(itemParts, "v" .. (itemData.level - prevLevel))
yellowfive@81 566 prevLevel = itemData.level
yellowfive@81 567 end
yellowfive@57 568 if itemData.bonusIds then
yellowfive@57 569 for bIndex, bValue in ipairs(itemData.bonusIds) do
yellowfive@57 570 table.insert(itemParts, "b" .. (bValue - prevBonusId))
yellowfive@57 571 prevBonusId = bValue
yellowfive@57 572 end
yellowfive@124 573 end
yellowfive@124 574
yellowfive@124 575 if itemData.azerite then
yellowfive@124 576 for aIndex, aValue in ipairs(itemData.azerite) do
yellowfive@124 577 table.insert(itemParts, "a" .. (aValue - prevAzeriteId))
yellowfive@124 578 prevAzeriteId = aValue
yellowfive@124 579 end
yellowfive@124 580 end
yellowfive@81 581
yellowfive@81 582 if itemData.gemIds[1] ~= 0 then
yellowfive@81 583 table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId))
yellowfive@81 584 prevGemId = itemData.gemIds[1]
yellowfive@81 585 end
yellowfive@81 586 if itemData.gemIds[2] ~= 0 then
yellowfive@81 587 table.insert(itemParts, "y" .. (itemData.gemIds[2] - prevGemId))
yellowfive@81 588 prevGemId = itemData.gemIds[2]
yellowfive@81 589 end
yellowfive@81 590 if itemData.gemIds[3] ~= 0 then
yellowfive@81 591 table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId))
yellowfive@81 592 prevGemId = itemData.gemIds[3]
yellowfive@124 593 end
yellowfive@81 594
yellowfive@57 595 if itemData.enchantId ~= 0 then
yellowfive@57 596 table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId))
yellowfive@57 597 prevEnchantId = itemData.enchantId
yellowfive@57 598 end
yellowfive@124 599
yellowfive@124 600 if itemData.relicBonusIds and itemData.relicBonusIds[1] ~= nil then
yellowfive@124 601 for bIndex, bValue in ipairs(itemData.relicBonusIds[1]) do
yellowfive@124 602 table.insert(itemParts, "p" .. (bValue - prevRelicBonusId))
yellowfive@124 603 prevRelicBonusId = bValue
yellowfive@124 604 end
yellowfive@124 605 end
yellowfive@124 606
yellowfive@124 607 if itemData.relicBonusIds and itemData.relicBonusIds[2] ~= nil then
yellowfive@124 608 for bIndex, bValue in ipairs(itemData.relicBonusIds[2]) do
yellowfive@124 609 table.insert(itemParts, "q" .. (bValue - prevRelicBonusId))
yellowfive@124 610 prevRelicBonusId = bValue
yellowfive@124 611 end
yellowfive@124 612 end
yellowfive@124 613
yellowfive@124 614 if itemData.relicBonusIds and itemData.relicBonusIds[3] ~= nil then
yellowfive@124 615 for bIndex, bValue in ipairs(itemData.relicBonusIds[3]) do
yellowfive@124 616 table.insert(itemParts, "r" .. (bValue - prevRelicBonusId))
yellowfive@124 617 prevRelicBonusId = bValue
yellowfive@124 618 end
yellowfive@124 619 end
yellowfive@124 620
yellowfive@57 621 table.insert(fields, table.concat(itemParts, ""))
yellowfive@57 622 end
yellowfive@57 623 end
yellowfive@57 624
yellowfive@57 625 -- Serialize just the identity portion of a player (region/realm/name) in the same format used by the full serialization
yellowfive@57 626 function Amr:SerializePlayerIdentity(data)
yellowfive@57 627 local fields = {}
yellowfive@57 628 table.insert(fields, MINOR)
yellowfive@57 629 table.insert(fields, data.Region)
yellowfive@57 630 table.insert(fields, data.Realm)
yellowfive@57 631 table.insert(fields, data.Name)
yellowfive@57 632 return "$" .. table.concat(fields, ";") .. "$"
yellowfive@57 633 end
yellowfive@57 634
yellowfive@57 635 -- Serialize player data gathered by GetPlayerData. This can be augmented with extra data if desired (augmenting used mainly by AskMrRobot addon).
yellowfive@57 636 -- Pass complete = true to do a complete export of this extra information, otherwise it is ignored.
yellowfive@57 637 -- Extra data can include:
yellowfive@57 638 -- equipped gear for the player's inactive spec, slot id to item link dictionary
yellowfive@57 639 -- Reputations
yellowfive@57 640 -- BagItems, BankItems, VoidItems, lists of item links
yellowfive@57 641 --
yellowfive@57 642 function Amr:SerializePlayerData(data, complete)
yellowfive@57 643
yellowfive@57 644 local fields = {}
yellowfive@57 645
yellowfive@57 646 -- compressed string uses a fixed order rather than inserting identifiers
yellowfive@57 647 table.insert(fields, MINOR)
yellowfive@57 648 table.insert(fields, data.Region)
yellowfive@57 649 table.insert(fields, data.Realm)
yellowfive@57 650 table.insert(fields, data.Name)
yellowfive@57 651
yellowfive@57 652 -- guild name
yellowfive@57 653 if data.Guild == nil then
yellowfive@57 654 table.insert(fields, "")
yellowfive@57 655 else
yellowfive@57 656 table.insert(fields, data.Guild)
yellowfive@57 657 end
yellowfive@57 658
yellowfive@57 659 -- race, default to pandaren if we can't read it for some reason
yellowfive@57 660 local raceval = Amr.RaceIds[data.Race]
yellowfive@57 661 if raceval == nil then raceval = 13 end
yellowfive@57 662 table.insert(fields, raceval)
yellowfive@57 663
yellowfive@57 664 -- faction, default to alliance if we can't read it for some reason
yellowfive@57 665 raceval = Amr.FactionIds[data.Faction]
yellowfive@57 666 if raceval == nil then raceval = 1 end
yellowfive@57 667 table.insert(fields, raceval)
yellowfive@57 668
yellowfive@124 669 table.insert(fields, data.Level)
yellowfive@124 670 table.insert(fields, data.HeartOfAzerothLevel)
yellowfive@57 671
yellowfive@57 672 local profs = {}
yellowfive@57 673 local noprofs = true
yellowfive@57 674 if data.Professions then
yellowfive@57 675 for k, v in pairs(data.Professions) do
yellowfive@57 676 local profval = Amr.ProfessionIds[k]
yellowfive@57 677 if profval ~= nil then
yellowfive@57 678 noprofs = false
yellowfive@57 679 table.insert(profs, profval .. ":" .. v)
yellowfive@57 680 end
yellowfive@57 681 end
yellowfive@57 682 end
yellowfive@57 683
yellowfive@57 684 if noprofs then
yellowfive@57 685 table.insert(profs, "0:0")
yellowfive@57 686 end
yellowfive@57 687
yellowfive@57 688 table.insert(fields, table.concat(profs, ","))
yellowfive@57 689
yellowfive@57 690 -- export specs
yellowfive@57 691 table.insert(fields, data.ActiveSpec)
yellowfive@81 692 for spec = 1, 4 do
yellowfive@57 693 if data.Specs[spec] and (complete or spec == data.ActiveSpec) then
yellowfive@57 694 table.insert(fields, ".s" .. spec) -- indicates the start of a spec block
yellowfive@81 695 table.insert(fields, data.Specs[spec])
yellowfive@165 696 table.insert(fields, data.Talents[spec] or "")
yellowfive@165 697
yellowfive@165 698 local essences = {}
yellowfive@165 699 if data.Essences and data.Essences[spec] then
yellowfive@165 700 for i, ess in ipairs(data.Essences[spec]) do
yellowfive@165 701 table.insert(essences, table.concat(ess, "."))
yellowfive@165 702 end
yellowfive@165 703 end
yellowfive@165 704 table.insert(fields, table.concat(essences, "_"))
yellowfive@57 705 end
yellowfive@57 706 end
yellowfive@57 707
yellowfive@57 708 -- export equipped gear
yellowfive@57 709 if data.Equipped then
yellowfive@81 710 for spec = 1, 4 do
yellowfive@57 711 if data.Equipped[spec] and (complete or spec == data.ActiveSpec) then
yellowfive@57 712 table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block
yellowfive@57 713
yellowfive@57 714 local itemObjects = {}
yellowfive@124 715 for k, itemData in pairs(data.Equipped[spec]) do
yellowfive@57 716 itemData.slot = k
yellowfive@57 717 table.insert(itemObjects, itemData)
yellowfive@57 718 end
yellowfive@57 719
yellowfive@57 720 appendItemsToExport(fields, itemObjects)
yellowfive@57 721 end
yellowfive@57 722 end
yellowfive@57 723 end
yellowfive@165 724
yellowfive@165 725 -- export unlocked essences
yellowfive@165 726 if data.UnlockedEssences then
yellowfive@165 727 table.insert(fields, ".ess")
yellowfive@165 728 for i, ess in ipairs(data.UnlockedEssences) do
yellowfive@165 729 table.insert(fields, table.concat(ess, "_"))
yellowfive@165 730 end
yellowfive@165 731 end
yellowfive@57 732
yellowfive@124 733 -- if doing a complete export, include bank/bag items too
yellowfive@124 734 if complete then
yellowfive@124 735
yellowfive@57 736 local itemObjects = {}
yellowfive@57 737 if data.BagItems then
yellowfive@124 738 for i, itemData in ipairs(data.BagItems) do
yellowfive@124 739 if itemData then
yellowfive@57 740 table.insert(itemObjects, itemData)
yellowfive@57 741 end
yellowfive@57 742 end
yellowfive@57 743 end
yellowfive@127 744 if data.BankItems then
yellowfive@124 745 for i, itemData in ipairs(data.BankItems) do
yellowfive@127 746 if itemData then
yellowfive@57 747 table.insert(itemObjects, itemData)
yellowfive@57 748 end
yellowfive@57 749 end
yellowfive@124 750 end
yellowfive@124 751
yellowfive@57 752 table.insert(fields, ".inv")
yellowfive@57 753 appendItemsToExport(fields, itemObjects)
yellowfive@57 754 end
yellowfive@57 755
yellowfive@57 756 return "$" .. table.concat(fields, ";") .. "$"
yellowfive@57 757
yellowfive@57 758 end
yellowfive@57 759
yellowfive@165 760 --[[
yellowfive@57 761 -- Shortcut for the common use case: serialize the player's currently active setup with no extras.
yellowfive@57 762 function Amr:SerializePlayer()
yellowfive@57 763 local data = self:GetPlayerData()
yellowfive@57 764 return self:SerializePlayerData(data)
yellowfive@57 765 end
yellowfive@165 766 ]]
yellowfive@57 767
yellowfive@81 768 --[[
yellowfive@57 769 ----------------------------------------------------------------------------------------------------------------------
yellowfive@57 770 -- Character Snapshots
yellowfive@81 771 -- This feature snapshots a player's gear/talents/artifact when entering combat. It is enabled by default. Consumers
yellowfive@57 772 -- of this library can create a setting to enable/disable it as desired per a user setting.
yellowfive@57 773 --
yellowfive@57 774 -- You should register for the AMR_SNAPSHOT_STATE_CHANGED message (sent via AceEvent-3.0 messaging) to ensure that
yellowfive@57 775 -- your addon settings stay in sync with any other addon that may also be trying to control the enabled state.
yellowfive@57 776 --
yellowfive@57 777 -- Note that if a user has the main AMR addon installed, it will always enable snapshotting, and override any attempt
yellowfive@57 778 -- to disable it by immediately re-enabling it and thus re-triggering AMR_SNAPSHOT_STATE_CHANGED.
yellowfive@57 779 ----------------------------------------------------------------------------------------------------------------------
yellowfive@57 780 Amr._snapshotEnabled = true
yellowfive@57 781
yellowfive@57 782 -- Enable snapshotting of character data when entering combat. Sends this player's character data to anyone logging with the AskMrRobot addon.
yellowfive@57 783 function Amr:EnableSnapshots()
yellowfive@57 784 self._snapshotEnabled = true
yellowfive@57 785 self:SendMessage("AMR_SNAPSHOT_STATE_CHANGED", self._snapshotEnabled)
yellowfive@57 786 end
yellowfive@57 787
yellowfive@57 788 -- Disable snapshotting of character data when entering combat.
yellowfive@57 789 function Amr:DisableSnapshots()
yellowfive@57 790 self._snapshotEnabled = false
yellowfive@57 791 self:SendMessage("AMR_SNAPSHOT_STATE_CHANGED", self._snapshotEnabled)
yellowfive@57 792 end
yellowfive@57 793
yellowfive@57 794 function Amr:IsSnapshotEnabled()
yellowfive@57 795 return self._snapshotEnabled
yellowfive@57 796 end
yellowfive@57 797
yellowfive@57 798
yellowfive@57 799 function Amr:PLAYER_REGEN_DISABLED()
yellowfive@57 800 --function Amr:GARRISON_MISSION_NPC_OPENED()
yellowfive@57 801
yellowfive@57 802 -- send data about this character when a player enters combat in a supported zone
yellowfive@57 803 if self._snapshotEnabled and Amr.IsSupportedInstance() then
yellowfive@57 804 local t = time()
yellowfive@57 805 local player = self:GetPlayerData()
yellowfive@57 806 local msg = self:SerializePlayerData(player)
yellowfive@57 807 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 808
yellowfive@57 809 self:SendCommMessage(Amr.ChatPrefix, msg, "RAID")
yellowfive@57 810 end
yellowfive@57 811 end
yellowfive@57 812
yellowfive@57 813 Amr:RegisterEvent("PLAYER_REGEN_DISABLED")
yellowfive@81 814 --Amr:RegisterEvent("GARRISON_MISSION_NPC_OPENED") -- for debugging, fire this event when open mission table
yellowfive@122 815 ]]