annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 187:180cb1458674 v91

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