annotate AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 197:23b740b4c93a v96

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