annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 173:556cb7c32580 v81

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