annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 189:21a69c63fee8 v92

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