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