annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 153:31386c009f03 v72

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