annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 181:5c586ff5fee5 v85

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