annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 137:6dc0e8e9f960 v64

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