annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 157:544a8d2d83d6 v74

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