Mercurial > wow > askmrrobot
comparison AskMrRobot-Serializer/AskMrRobot-Serializer.lua @ 57:01b63b8ed811 v21
total rewrite to version 21
author | yellowfive |
---|---|
date | Fri, 05 Jun 2015 11:05:15 -0700 |
parents | |
children | ee701ce45354 |
comparison
equal
deleted
inserted
replaced
56:75431c084aa0 | 57:01b63b8ed811 |
---|---|
1 -- AskMrRobot-Serializer will serialize and communicate character data between users. | |
2 -- This is used primarily to associate character information to logs uploaded to askmrrobot.com. | |
3 | |
4 local MAJOR, MINOR = "AskMrRobot-Serializer", 21 | |
5 local Amr, oldminor = LibStub:NewLibrary(MAJOR, MINOR) | |
6 | |
7 if not Amr then return end -- already loaded by something else | |
8 | |
9 -- event and comm used for player snapshotting on entering combat | |
10 LibStub("AceEvent-3.0"):Embed(Amr) | |
11 LibStub("AceComm-3.0"):Embed(Amr) | |
12 | |
13 ---------------------------------------------------------------------------------------- | |
14 -- Constants | |
15 ---------------------------------------------------------------------------------------- | |
16 | |
17 -- prefix used for communicating gear snapshots created by the AMR serializer | |
18 Amr.ChatPrefix = "_AMRS" | |
19 | |
20 -- map of region ids to AMR region names | |
21 Amr.RegionNames = { | |
22 [1] = "US", | |
23 [2] = "KR", | |
24 [3] = "EU", | |
25 [4] = "TW", | |
26 [5] = "CN" | |
27 } | |
28 | |
29 -- map of the skillLine returned by profession API to the AMR profession name | |
30 Amr.ProfessionSkillLineToName = { | |
31 [794] = "Archaeology", | |
32 [171] = "Alchemy", | |
33 [164] = "Blacksmithing", | |
34 [185] = "Cooking", | |
35 [333] = "Enchanting", | |
36 [202] = "Engineering", | |
37 [129] = "First Aid", | |
38 [356] = "Fishing", | |
39 [182] = "Herbalism", | |
40 [773] = "Inscription", | |
41 [755] = "Jewelcrafting", | |
42 [165] = "Leatherworking", | |
43 [186] = "Mining", | |
44 [393] = "Skinning", | |
45 [197] = "Tailoring" | |
46 } | |
47 | |
48 -- all slot IDs that we care about, ordered in AMR standard display order | |
49 Amr.SlotIds = { 16, 17, 1, 2, 3, 15, 5, 9, 10, 6, 7, 8, 11, 12, 13, 14 } | |
50 | |
51 Amr.SpecIds = { | |
52 [250] = 1, -- DeathKnightBlood | |
53 [251] = 2, -- DeathKnightFrost | |
54 [252] = 3, -- DeathKnightUnholy | |
55 [102] = 4, -- DruidBalance | |
56 [103] = 5, -- DruidFeral | |
57 [104] = 6, -- DruidGuardian | |
58 [105] = 7, -- DruidRestoration | |
59 [253] = 8, -- HunterBeastMastery | |
60 [254] = 9, -- HunterMarksmanship | |
61 [255] = 10, -- HunterSurvival | |
62 [62] = 11, -- MageArcane | |
63 [63] = 12, -- MageFire | |
64 [64] = 13, -- MageFrost | |
65 [268] = 14, -- MonkBrewmaster | |
66 [270] = 15, -- MonkMistweaver | |
67 [269] = 16, -- MonkWindwalker | |
68 [65] = 17, -- PaladinHoly | |
69 [66] = 18, -- PaladinProtection | |
70 [70] = 19, -- PaladinRetribution | |
71 [256] = 20, -- PriestDiscipline | |
72 [257] = 21, -- PriestHoly | |
73 [258] = 22, -- PriestShadow | |
74 [259] = 23, -- RogueAssassination | |
75 [260] = 24, -- RogueCombat | |
76 [261] = 25, -- RogueSubtlety | |
77 [262] = 26, -- ShamanElemental | |
78 [263] = 27, -- ShamanEnhancement | |
79 [264] = 28, -- ShamanRestoration | |
80 [265] = 29, -- WarlockAffliction | |
81 [266] = 30, -- WarlockDemonology | |
82 [267] = 31, -- WarlockDestruction | |
83 [71] = 32, -- WarriorArms | |
84 [72] = 33, -- WarriorFury | |
85 [73] = 34 -- WarriorProtection | |
86 } | |
87 | |
88 Amr.ClassIds = { | |
89 ["NONE"] = 0, | |
90 ["DEATHKNIGHT"] = 1, | |
91 ["DRUID"] = 2, | |
92 ["HUNTER"] = 3, | |
93 ["MAGE"] = 4, | |
94 ["MONK"] = 5, | |
95 ["PALADIN"] = 6, | |
96 ["PRIEST"] = 7, | |
97 ["ROGUE"] = 8, | |
98 ["SHAMAN"] = 9, | |
99 ["WARLOCK"] = 10, | |
100 ["WARRIOR"] = 11, | |
101 } | |
102 | |
103 Amr.ProfessionIds = { | |
104 ["None"] = 0, | |
105 ["Mining"] = 1, | |
106 ["Skinning"] = 2, | |
107 ["Herbalism"] = 3, | |
108 ["Enchanting"] = 4, | |
109 ["Jewelcrafting"] = 5, | |
110 ["Engineering"] = 6, | |
111 ["Blacksmithing"] = 7, | |
112 ["Leatherworking"] = 8, | |
113 ["Inscription"] = 9, | |
114 ["Tailoring"] = 10, | |
115 ["Alchemy"] = 11, | |
116 ["Fishing"] = 12, | |
117 ["Cooking"] = 13, | |
118 ["First Aid"] = 14, | |
119 ["Archaeology"] = 15 | |
120 } | |
121 | |
122 Amr.RaceIds = { | |
123 ["None"] = 0, | |
124 ["BloodElf"] = 1, | |
125 ["Draenei"] = 2, | |
126 ["Dwarf"] = 3, | |
127 ["Gnome"] = 4, | |
128 ["Human"] = 5, | |
129 ["NightElf"] = 6, | |
130 ["Orc"] = 7, | |
131 ["Tauren"] = 8, | |
132 ["Troll"] = 9, | |
133 ["Scourge"] = 10, | |
134 ["Undead"] = 10, | |
135 ["Goblin"] = 11, | |
136 ["Worgen"] = 12, | |
137 ["Pandaren"] = 13 | |
138 } | |
139 | |
140 Amr.FactionIds = { | |
141 ["None"] = 0, | |
142 ["Alliance"] = 1, | |
143 ["Horde"] = 2 | |
144 } | |
145 | |
146 Amr.InstanceIds = { | |
147 Auchindoun = 1182, | |
148 BloodmaulSlagMines = 1175, | |
149 GrimrailDepot = 1208, | |
150 IronDocks = 1195, | |
151 ShadowmoonBurialGrounds = 1176, | |
152 Skyreach = 1209, | |
153 TheEverbloom = 1279, | |
154 UpperBlackrockSpire = 1358, | |
155 Highmaul = 1228, | |
156 BlackrockFoundry = 1205 | |
157 } | |
158 | |
159 -- instances that AskMrRobot currently supports logging for | |
160 Amr.SupportedInstanceIds = { | |
161 --[1182] = true, | |
162 --[1175] = true, | |
163 --[1208] = true, | |
164 --[1195] = true, | |
165 --[1176] = true, | |
166 --[1209] = true, | |
167 --[1279] = true, | |
168 --[1358] = true, | |
169 [1228] = true, | |
170 [1205] = true | |
171 } | |
172 | |
173 Amr.SPEC_WARRIORPROTECTION = 34 | |
174 Amr.SUBSPEC_WARRIORPROTECTION = 38 | |
175 Amr.SUBSPEC_WARRIORPROTECTIONGLAD = 39 | |
176 Amr.SPELL_ID_GLADIATOR_STANCE = 156291 | |
177 Amr.SPELL_ID_DEFENSIVE_STANCE = 71 | |
178 | |
179 -- IDs of set tokens that we would care about in a player's inventory | |
180 Amr.SetTokenIds = { | |
181 [120285] = true, | |
182 [120284] = true, | |
183 [120283] = true, | |
184 [120282] = true, | |
185 [120281] = true, | |
186 [120280] = true, | |
187 [120279] = true, | |
188 [120278] = true, | |
189 [120277] = true, | |
190 [120256] = true, | |
191 [120255] = true, | |
192 [120254] = true, | |
193 [120253] = true, | |
194 [120252] = true, | |
195 [120251] = true, | |
196 [120250] = true, | |
197 [120249] = true, | |
198 [120248] = true, | |
199 [120247] = true, | |
200 [120246] = true, | |
201 [120245] = true, | |
202 [120244] = true, | |
203 [120243] = true, | |
204 [120242] = true, | |
205 [120241] = true, | |
206 [120240] = true, | |
207 [120239] = true, | |
208 [120238] = true, | |
209 [120237] = true, | |
210 [120236] = true, | |
211 [120235] = true, | |
212 [120234] = true, | |
213 [120233] = true, | |
214 [120232] = true, | |
215 [120231] = true, | |
216 [120230] = true, | |
217 [120229] = true, | |
218 [120228] = true, | |
219 [120227] = true, | |
220 [120226] = true, | |
221 [120225] = true, | |
222 [120224] = true, | |
223 [120223] = true, | |
224 [120222] = true, | |
225 [120221] = true, | |
226 [120220] = true, | |
227 [120219] = true, | |
228 [120218] = true, | |
229 [120217] = true, | |
230 [120216] = true, | |
231 [120215] = true, | |
232 [120214] = true, | |
233 [120213] = true, | |
234 [120212] = true, | |
235 [120211] = true, | |
236 [120210] = true, | |
237 [120209] = true, | |
238 [120208] = true, | |
239 [120207] = true, | |
240 [120206] = true, | |
241 [119323] = true, | |
242 [119322] = true, | |
243 [119321] = true, | |
244 [119320] = true, | |
245 [119319] = true, | |
246 [119318] = true, | |
247 [119316] = true, | |
248 [119315] = true, | |
249 [119314] = true, | |
250 [119313] = true, | |
251 [119312] = true, | |
252 [119311] = true, | |
253 [119310] = true, | |
254 [119309] = true, | |
255 [119308] = true, | |
256 [119307] = true, | |
257 [119306] = true, | |
258 [119305] = true, | |
259 [105868] = true, | |
260 [105867] = true, | |
261 [105866] = true, | |
262 [105865] = true, | |
263 [105864] = true, | |
264 [105863] = true, | |
265 [105862] = true, | |
266 [105861] = true, | |
267 [105860] = true, | |
268 [105859] = true, | |
269 [105858] = true, | |
270 [105857] = true, | |
271 [99756] = true, | |
272 [99755] = true, | |
273 [99754] = true, | |
274 [99753] = true, | |
275 [99752] = true, | |
276 [99751] = true, | |
277 [99750] = true, | |
278 [99749] = true, | |
279 [99748] = true, | |
280 [99747] = true, | |
281 [99746] = true, | |
282 [99745] = true, | |
283 [99744] = true, | |
284 [99743] = true, | |
285 [99742] = true, | |
286 [99740] = true, | |
287 [99739] = true, | |
288 [99738] = true, | |
289 [99737] = true, | |
290 [99736] = true, | |
291 [99735] = true, | |
292 [99734] = true, | |
293 [99733] = true, | |
294 [99732] = true, | |
295 [99731] = true, | |
296 [99730] = true, | |
297 [99729] = true, | |
298 [99728] = true, | |
299 [99727] = true, | |
300 [99726] = true, | |
301 [99725] = true, | |
302 [99724] = true, | |
303 [99723] = true, | |
304 [99722] = true, | |
305 [99721] = true, | |
306 [99720] = true, | |
307 [99719] = true, | |
308 [99718] = true, | |
309 [99717] = true, | |
310 [99716] = true, | |
311 [99715] = true, | |
312 [99714] = true, | |
313 [99713] = true, | |
314 [99712] = true, | |
315 [99711] = true, | |
316 [99710] = true, | |
317 [99709] = true, | |
318 [99708] = true, | |
319 [99707] = true, | |
320 [99706] = true, | |
321 [99705] = true, | |
322 [99704] = true, | |
323 [99703] = true, | |
324 [99702] = true, | |
325 [99701] = true, | |
326 [99700] = true, | |
327 [99699] = true, | |
328 [99698] = true, | |
329 [99697] = true, | |
330 [99696] = true, | |
331 [99695] = true, | |
332 [99694] = true, | |
333 [99693] = true, | |
334 [99692] = true, | |
335 [99691] = true, | |
336 [99690] = true, | |
337 [99689] = true, | |
338 [99688] = true, | |
339 [99687] = true, | |
340 [99686] = true, | |
341 [99685] = true, | |
342 [99684] = true, | |
343 [99683] = true, | |
344 [99682] = true, | |
345 [99681] = true, | |
346 [99680] = true, | |
347 [99679] = true, | |
348 [99678] = true, | |
349 [99677] = true, | |
350 [99676] = true, | |
351 [99675] = true, | |
352 [99674] = true, | |
353 [99673] = true, | |
354 [99672] = true, | |
355 [99671] = true, | |
356 [99670] = true, | |
357 [99669] = true, | |
358 [99668] = true, | |
359 [99667] = true, | |
360 [96701] = true, | |
361 [96700] = true, | |
362 [96699] = true, | |
363 [96633] = true, | |
364 [96632] = true, | |
365 [96631] = true, | |
366 [96625] = true, | |
367 [96624] = true, | |
368 [96623] = true, | |
369 [96601] = true, | |
370 [96600] = true, | |
371 [96599] = true, | |
372 [96568] = true, | |
373 [96567] = true, | |
374 [96566] = true, | |
375 [95957] = true, | |
376 [95956] = true, | |
377 [95955] = true, | |
378 [95889] = true, | |
379 [95888] = true, | |
380 [95887] = true, | |
381 [95881] = true, | |
382 [95880] = true, | |
383 [95879] = true, | |
384 [95857] = true, | |
385 [95856] = true, | |
386 [95855] = true, | |
387 [95824] = true, | |
388 [95823] = true, | |
389 [95822] = true, | |
390 [95583] = true, | |
391 [95582] = true, | |
392 [95581] = true, | |
393 [95580] = true, | |
394 [95579] = true, | |
395 [95578] = true, | |
396 [95577] = true, | |
397 [95576] = true, | |
398 [95575] = true, | |
399 [95574] = true, | |
400 [95573] = true, | |
401 [95572] = true, | |
402 [95571] = true, | |
403 [95570] = true, | |
404 [95569] = true, | |
405 [89278] = true, | |
406 [89277] = true, | |
407 [89276] = true, | |
408 [89275] = true, | |
409 [89274] = true, | |
410 [89273] = true, | |
411 [89272] = true, | |
412 [89271] = true, | |
413 [89270] = true, | |
414 [89269] = true, | |
415 [89268] = true, | |
416 [89267] = true, | |
417 [89266] = true, | |
418 [89265] = true, | |
419 [89264] = true, | |
420 [89263] = true, | |
421 [89262] = true, | |
422 [89261] = true, | |
423 [89260] = true, | |
424 [89259] = true, | |
425 [89258] = true, | |
426 [89257] = true, | |
427 [89256] = true, | |
428 [89255] = true, | |
429 [89254] = true, | |
430 [89253] = true, | |
431 [89252] = true, | |
432 [89251] = true, | |
433 [89250] = true, | |
434 [89249] = true, | |
435 [89248] = true, | |
436 [89247] = true, | |
437 [89246] = true, | |
438 [89245] = true, | |
439 [89244] = true, | |
440 [89243] = true, | |
441 [89242] = true, | |
442 [89241] = true, | |
443 [89240] = true, | |
444 [89239] = true, | |
445 [89238] = true, | |
446 [89237] = true, | |
447 [89236] = true, | |
448 [89235] = true, | |
449 [89234] = true, | |
450 [78876] = true, | |
451 [78875] = true, | |
452 [78874] = true, | |
453 [78873] = true, | |
454 [78872] = true, | |
455 [78871] = true, | |
456 [78867] = true, | |
457 [78866] = true, | |
458 [78865] = true, | |
459 [78864] = true, | |
460 [78863] = true, | |
461 [78862] = true, | |
462 [78861] = true, | |
463 [78860] = true, | |
464 [78859] = true, | |
465 [78858] = true, | |
466 [78857] = true, | |
467 [78856] = true, | |
468 [78855] = true, | |
469 [78854] = true, | |
470 [78853] = true, | |
471 [78849] = true, | |
472 [78848] = true, | |
473 [78847] = true, | |
474 [78184] = true, | |
475 [78183] = true, | |
476 [78181] = true, | |
477 [78180] = true, | |
478 [78179] = true, | |
479 [78178] = true, | |
480 [78176] = true, | |
481 [78175] = true, | |
482 [78174] = true, | |
483 [78173] = true, | |
484 [78171] = true, | |
485 [78170] = true, | |
486 [71687] = true, | |
487 [71686] = true, | |
488 [71685] = true, | |
489 [71683] = true, | |
490 [71682] = true, | |
491 [71680] = true, | |
492 [71679] = true, | |
493 [71678] = true, | |
494 [71676] = true, | |
495 [71675] = true, | |
496 [71673] = true, | |
497 [71672] = true, | |
498 [71671] = true, | |
499 [71669] = true, | |
500 [71668] = true, | |
501 [67431] = true, | |
502 [67430] = true, | |
503 [67429] = true, | |
504 [67428] = true, | |
505 [67427] = true, | |
506 [67426] = true, | |
507 [67425] = true, | |
508 [67424] = true, | |
509 [67423] = true, | |
510 [66998] = true, | |
511 [65089] = true, | |
512 [65088] = true, | |
513 [65087] = true, | |
514 [63684] = true, | |
515 [63683] = true, | |
516 [63682] = true, | |
517 [45652] = true, | |
518 [45651] = true, | |
519 [45650] = true, | |
520 [45649] = true, | |
521 [45648] = true, | |
522 [45647] = true, | |
523 [45643] = true, | |
524 [45642] = true, | |
525 [45641] = true, | |
526 [40630] = true, | |
527 [40629] = true, | |
528 [40628] = true, | |
529 [40621] = true, | |
530 [40620] = true, | |
531 [40619] = true, | |
532 [40618] = true, | |
533 [40617] = true, | |
534 [40616] = true, | |
535 [34544] = true, | |
536 [31100] = true, | |
537 [31099] = true, | |
538 [31098] = true, | |
539 [31097] = true, | |
540 [31096] = true, | |
541 [31095] = true, | |
542 [30247] = true, | |
543 [30246] = true, | |
544 [30245] = true, | |
545 [30244] = true, | |
546 [30243] = true, | |
547 [30242] = true, | |
548 [29767] = true, | |
549 [29766] = true, | |
550 [29765] = true, | |
551 [29761] = true, | |
552 [29760] = true, | |
553 [29759] = true | |
554 } | |
555 | |
556 | |
557 ---------------------------------------------------------------------------------------- | |
558 -- Public Utility Methods | |
559 ---------------------------------------------------------------------------------------- | |
560 | |
561 -- item link format: |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:upgradeId:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r | |
562 -- get an object with all of the parts of the item link format that we care about | |
563 function Amr.ParseItemLink(itemLink) | |
564 if not itemLink then return nil end | |
565 | |
566 local str = string.match(itemLink, "|Hitem:([\-%d:]+)|") | |
567 if not str then return nil end | |
568 | |
569 local parts = { strsplit(":", str) } | |
570 | |
571 local item = {} | |
572 item.id = tonumber(parts[1]) | |
573 item.enchantId = tonumber(parts[2]) | |
574 item.gemIds = { tonumber(parts[3]), tonumber(parts[4]), tonumber(parts[5]), tonumber(parts[6]) } | |
575 item.suffixId = math.abs(tonumber(parts[7])) -- convert suffix to positive number, that's what we use in our code | |
576 --item.uniqueId = tonumber(parts[8]) | |
577 --item.level = tonumber(parts[9]) | |
578 item.upgradeId = tonumber(parts[10]) | |
579 --item.difficultyId = tonumber(parts[11]) | |
580 | |
581 local numBonuses = tonumber(parts[12]) | |
582 if numBonuses and numBonuses > 0 then | |
583 item.bonusIds = {} | |
584 for i = 13, 12 + numBonuses do | |
585 table.insert(item.bonusIds, tonumber(parts[i])) | |
586 end | |
587 table.sort(item.bonusIds) | |
588 end | |
589 | |
590 return item | |
591 end | |
592 | |
593 -- returns true if this is an instance that AskMrRobot supports for logging | |
594 function Amr.IsSupportedInstanceId(instanceMapID) | |
595 if Amr.SupportedInstanceIds[tonumber(instanceMapID)] then | |
596 return true | |
597 else | |
598 return false | |
599 end | |
600 end | |
601 | |
602 -- returns true if currently in a supported instance for logging | |
603 function Amr.IsSupportedInstance() | |
604 local zone, _, difficultyIndex, _, _, _, _, instanceMapID = GetInstanceInfo() | |
605 return Amr.IsSupportedInstanceId(instanceMapID) | |
606 end | |
607 | |
608 | |
609 ---------------------------------------------------------------------------------------- | |
610 -- Character Reading | |
611 ---------------------------------------------------------------------------------------- | |
612 | |
613 local function readProfessionInfo(prof, ret) | |
614 if prof then | |
615 local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof); | |
616 if Amr.ProfessionSkillLineToName[skillLine] ~= nil then | |
617 ret.Professions[Amr.ProfessionSkillLineToName[skillLine]] = skillLevel; | |
618 end | |
619 end | |
620 end | |
621 | |
622 local function getSpecId(specGroup) | |
623 local spec = GetSpecialization(false, false, specGroup); | |
624 return spec and GetSpecializationInfo(spec); | |
625 end | |
626 | |
627 local function getTalents(specGroup) | |
628 local talentInfo = {} | |
629 local maxTiers = 7 | |
630 for tier = 1, maxTiers do | |
631 for col = 1, 3 do | |
632 local id, name, texture, selected, available = GetTalentInfo(tier, col, specGroup) | |
633 if selected then | |
634 talentInfo[tier] = col | |
635 end | |
636 end | |
637 end | |
638 | |
639 local str = "" | |
640 for i = 1, maxTiers do | |
641 if talentInfo[i] then | |
642 str = str .. talentInfo[i] | |
643 else | |
644 str = str .. '0' | |
645 end | |
646 end | |
647 | |
648 return str | |
649 end | |
650 | |
651 local function getGlyphs(specGroup) | |
652 local glyphs = {} | |
653 for i = 1, NUM_GLYPH_SLOTS do | |
654 local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, specGroup) | |
655 if (glyphID) then | |
656 table.insert(glyphs, glyphSpellID) | |
657 end | |
658 end | |
659 return glyphs; | |
660 end | |
661 | |
662 -- get specs, talents, and glyphs | |
663 local function readSpecs(ret, subspecs) | |
664 | |
665 for group = 1, GetNumSpecGroups() do | |
666 -- spec, convert game spec id to one of our spec ids | |
667 local specId = getSpecId(group) | |
668 if specId then | |
669 ret.Specs[group] = Amr.SpecIds[specId] | |
670 | |
671 -- if this is a protection warrior, use buffs to determine subspec | |
672 if ret.Specs[group] == Amr.SPEC_WARRIORPROTECTION then | |
673 local subspec = 0 | |
674 | |
675 if ret.ActiveSpec ~= group then | |
676 -- this spec isn't active, so we can't use current buffs to determine spec, see if any old data is compatible | |
677 if subspecs and (subspecs[group] == Amr.SUBSPEC_WARRIORPROTECTION or subspecs[group] == Amr.SUBSPEC_WARRIORPROTECTIONGLAD) then | |
678 subspec = subspecs[group] | |
679 end | |
680 else | |
681 for i=1,40 do | |
682 local name,_,_,_,_,_,_,_,_,_,spellId = UnitAura("player", i, "HELPFUL") | |
683 if not name then break end | |
684 | |
685 if spellId == Amr.SPELL_ID_DEFENSIVE_STANCE then | |
686 subspec = Amr.SUBSPEC_WARRIORPROTECTION | |
687 break | |
688 elseif spellId == Amr.SPELL_ID_GLADIATOR_STANCE then | |
689 subspec = Amr.SUBSPEC_WARRIORPROTECTIONGLAD | |
690 break | |
691 end | |
692 end | |
693 end | |
694 | |
695 if subspec == 0 then | |
696 ret.SubSpecs[group] = nil | |
697 else | |
698 ret.SubSpecs[group] = subspec | |
699 end | |
700 end | |
701 else | |
702 ret.Specs[group] = 0 | |
703 end | |
704 | |
705 ret.Talents[group] = getTalents(group) | |
706 ret.Glyphs[group] = getGlyphs(group) | |
707 end | |
708 end | |
709 | |
710 -- get currently equipped items, store with currently active spec | |
711 local function readEquippedItems(ret) | |
712 local equippedItems = {}; | |
713 for slotNum = 1, #Amr.SlotIds do | |
714 local slotId = Amr.SlotIds[slotNum] | |
715 local itemLink = GetInventoryItemLink("player", slotId) | |
716 if itemLink then | |
717 equippedItems[slotId] = itemLink | |
718 end | |
719 end | |
720 | |
721 -- store last-seen equipped gear for each spec | |
722 ret.Equipped[GetActiveSpecGroup()] = equippedItems | |
723 end | |
724 | |
725 -- Get all data about the player as an object, includes: | |
726 -- serializer version | |
727 -- region/realm/name | |
728 -- guild | |
729 -- race | |
730 -- faction | |
731 -- level | |
732 -- professions | |
733 -- spec/talent/glyphs for both specs | |
734 -- equipped gear for the current spec | |
735 -- | |
736 function Amr:GetPlayerData(subspecs) | |
737 | |
738 local ret = {} | |
739 | |
740 ret.Region = Amr.RegionNames[GetCurrentRegion()] | |
741 ret.Realm = GetRealmName() | |
742 ret.Name = UnitName("player") | |
743 ret.Guild = GetGuildInfo("player") | |
744 ret.ActiveSpec = GetActiveSpecGroup() | |
745 ret.Level = UnitLevel("player"); | |
746 | |
747 local cls, clsEn = UnitClass("player") | |
748 ret.Class = clsEn; | |
749 | |
750 local race, raceEn = UnitRace("player") | |
751 ret.Race = raceEn; | |
752 ret.Faction = UnitFactionGroup("player") | |
753 | |
754 ret.Professions = {}; | |
755 local prof1, prof2, archaeology, fishing, cooking, firstAid = GetProfessions(); | |
756 readProfessionInfo(prof1, ret) | |
757 readProfessionInfo(prof2, ret) | |
758 readProfessionInfo(archaeology, ret) | |
759 readProfessionInfo(fishing, ret) | |
760 readProfessionInfo(cooking, ret) | |
761 readProfessionInfo(firstAid, ret) | |
762 | |
763 ret.Specs = {} | |
764 ret.SubSpecs = {} -- only filled in for ambiguous cases, right now just prot/glad warrior | |
765 ret.Talents = {} | |
766 ret.Glyphs = {} | |
767 readSpecs(ret, subspecs) | |
768 | |
769 ret.Equipped = {} | |
770 readEquippedItems(ret) | |
771 | |
772 return ret | |
773 end | |
774 | |
775 | |
776 ---------------------------------------------------------------------------------------- | |
777 -- Serialization | |
778 ---------------------------------------------------------------------------------------- | |
779 | |
780 local function toCompressedNumberList(list) | |
781 -- ensure the values are numbers, sorted from lowest to highest | |
782 local nums = {} | |
783 for i, v in ipairs(list) do | |
784 table.insert(nums, tonumber(v)) | |
785 end | |
786 table.sort(nums) | |
787 | |
788 local ret = {} | |
789 local prev = 0 | |
790 for i, v in ipairs(nums) do | |
791 local diff = v - prev | |
792 table.insert(ret, diff) | |
793 prev = v | |
794 end | |
795 | |
796 return table.concat(ret, ",") | |
797 end | |
798 | |
799 -- make this utility publicly available | |
800 function Amr:ToCompressedNumberList(list) | |
801 return toCompressedNumberList(list) | |
802 end | |
803 | |
804 -- appends a list of items to the export | |
805 local function appendItemsToExport(fields, itemObjects) | |
806 | |
807 -- sort by item id so we can compress it more easily | |
808 table.sort(itemObjects, function(a, b) return a.id < b.id end) | |
809 | |
810 -- append to the export string | |
811 local prevItemId = 0 | |
812 local prevGemId = 0 | |
813 local prevEnchantId = 0 | |
814 local prevUpgradeId = 0 | |
815 local prevBonusId = 0 | |
816 for i, itemData in ipairs(itemObjects) do | |
817 local itemParts = {} | |
818 | |
819 table.insert(itemParts, itemData.id - prevItemId) | |
820 prevItemId = itemData.id | |
821 | |
822 if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end | |
823 if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end | |
824 if itemData.upgradeId ~= 0 then | |
825 table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId)) | |
826 prevUpgradeId = itemData.upgradeId | |
827 end | |
828 if itemData.bonusIds then | |
829 for bIndex, bValue in ipairs(itemData.bonusIds) do | |
830 table.insert(itemParts, "b" .. (bValue - prevBonusId)) | |
831 prevBonusId = bValue | |
832 end | |
833 end | |
834 if itemData.gemIds[1] ~= 0 then | |
835 table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId)) | |
836 prevGemId = itemData.gemIds[1] | |
837 end | |
838 if itemData.gemIds[2] ~= 0 then | |
839 table.insert(itemParts, "y" .. (itemData.gemIds[2] - prevGemId)) | |
840 prevGemId = itemData.gemIds[2] | |
841 end | |
842 if itemData.gemIds[3] ~= 0 then | |
843 table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId)) | |
844 prevGemId = itemData.gemIds[3] | |
845 end | |
846 if itemData.enchantId ~= 0 then | |
847 table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId)) | |
848 prevEnchantId = itemData.enchantId | |
849 end | |
850 | |
851 table.insert(fields, table.concat(itemParts, "")) | |
852 end | |
853 end | |
854 | |
855 -- Serialize just the identity portion of a player (region/realm/name) in the same format used by the full serialization | |
856 function Amr:SerializePlayerIdentity(data) | |
857 local fields = {} | |
858 table.insert(fields, MINOR) | |
859 table.insert(fields, data.Region) | |
860 table.insert(fields, data.Realm) | |
861 table.insert(fields, data.Name) | |
862 return "$" .. table.concat(fields, ";") .. "$" | |
863 end | |
864 | |
865 -- Serialize player data gathered by GetPlayerData. This can be augmented with extra data if desired (augmenting used mainly by AskMrRobot addon). | |
866 -- Pass complete = true to do a complete export of this extra information, otherwise it is ignored. | |
867 -- Extra data can include: | |
868 -- equipped gear for the player's inactive spec, slot id to item link dictionary | |
869 -- Reputations | |
870 -- BagItems, BankItems, VoidItems, lists of item links | |
871 -- | |
872 function Amr:SerializePlayerData(data, complete) | |
873 | |
874 local fields = {} | |
875 | |
876 -- compressed string uses a fixed order rather than inserting identifiers | |
877 table.insert(fields, MINOR) | |
878 table.insert(fields, data.Region) | |
879 table.insert(fields, data.Realm) | |
880 table.insert(fields, data.Name) | |
881 | |
882 -- guild name | |
883 if data.Guild == nil then | |
884 table.insert(fields, "") | |
885 else | |
886 table.insert(fields, data.Guild) | |
887 end | |
888 | |
889 -- race, default to pandaren if we can't read it for some reason | |
890 local raceval = Amr.RaceIds[data.Race] | |
891 if raceval == nil then raceval = 13 end | |
892 table.insert(fields, raceval) | |
893 | |
894 -- faction, default to alliance if we can't read it for some reason | |
895 raceval = Amr.FactionIds[data.Faction] | |
896 if raceval == nil then raceval = 1 end | |
897 table.insert(fields, raceval) | |
898 | |
899 table.insert(fields, data.Level) | |
900 | |
901 local profs = {} | |
902 local noprofs = true | |
903 if data.Professions then | |
904 for k, v in pairs(data.Professions) do | |
905 local profval = Amr.ProfessionIds[k] | |
906 if profval ~= nil then | |
907 noprofs = false | |
908 table.insert(profs, profval .. ":" .. v) | |
909 end | |
910 end | |
911 end | |
912 | |
913 if noprofs then | |
914 table.insert(profs, "0:0") | |
915 end | |
916 | |
917 table.insert(fields, table.concat(profs, ",")) | |
918 | |
919 -- export specs | |
920 table.insert(fields, data.ActiveSpec) | |
921 for spec = 1, 2 do | |
922 if data.Specs[spec] and (complete or spec == data.ActiveSpec) then | |
923 table.insert(fields, ".s" .. spec) -- indicates the start of a spec block | |
924 | |
925 -- we use subspec for some ambiguous specs like prot/glad warrior | |
926 if data.SubSpecs[spec] then | |
927 table.insert(fields, string.format("s%s", data.SubSpecs[spec])) | |
928 else | |
929 table.insert(fields, data.Specs[spec]) | |
930 end | |
931 | |
932 table.insert(fields, data.Talents[spec]) | |
933 table.insert(fields, toCompressedNumberList(data.Glyphs[spec])) | |
934 end | |
935 end | |
936 | |
937 -- export equipped gear | |
938 if data.Equipped then | |
939 for spec = 1, 2 do | |
940 if data.Equipped[spec] and (complete or spec == data.ActiveSpec) then | |
941 table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block | |
942 | |
943 local itemObjects = {} | |
944 for k, v in pairs(data.Equipped[spec]) do | |
945 local itemData = Amr.ParseItemLink(v) | |
946 itemData.slot = k | |
947 table.insert(itemObjects, itemData) | |
948 end | |
949 | |
950 appendItemsToExport(fields, itemObjects) | |
951 end | |
952 end | |
953 end | |
954 | |
955 -- if doing a complete export, include reputations and bank/bag items too | |
956 if complete then | |
957 | |
958 -- export reputations | |
959 local reps = {} | |
960 local noreps = true | |
961 if data.Reputations then | |
962 for k, v in pairs(data.Reputations) do | |
963 noreps = false | |
964 table.insert(reps, k .. ":" .. v) | |
965 end | |
966 end | |
967 if noreps then | |
968 table.insert(reps, "_") | |
969 end | |
970 | |
971 table.insert(fields, ".r") | |
972 table.insert(fields, table.concat(reps, ",")) | |
973 | |
974 -- export bag and bank | |
975 local itemObjects = {} | |
976 if data.BagItems then | |
977 for i, v in ipairs(data.BagItems) do | |
978 local itemData = Amr.ParseItemLink(v) | |
979 if itemData ~= nil and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id]) then | |
980 table.insert(itemObjects, itemData) | |
981 end | |
982 end | |
983 end | |
984 if data.BankItems then | |
985 for i, v in ipairs(data.BankItems) do | |
986 local itemData = Amr.ParseItemLink(v) | |
987 if itemData ~= nil and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id]) then | |
988 table.insert(itemObjects, itemData) | |
989 end | |
990 end | |
991 end | |
992 if data.VoidItems then | |
993 for i, v in ipairs(data.VoidItems) do | |
994 local itemData = Amr.ParseItemLink(v) | |
995 if itemData ~= nil and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id]) then | |
996 table.insert(itemObjects, itemData) | |
997 end | |
998 end | |
999 end | |
1000 | |
1001 table.insert(fields, ".inv") | |
1002 appendItemsToExport(fields, itemObjects) | |
1003 end | |
1004 | |
1005 return "$" .. table.concat(fields, ";") .. "$" | |
1006 | |
1007 end | |
1008 | |
1009 -- Shortcut for the common use case: serialize the player's currently active setup with no extras. | |
1010 function Amr:SerializePlayer() | |
1011 local data = self:GetPlayerData() | |
1012 return self:SerializePlayerData(data) | |
1013 end | |
1014 | |
1015 | |
1016 ---------------------------------------------------------------------------------------------------------------------- | |
1017 -- Character Snapshots | |
1018 -- This feature snapshots a player's gear/talents/glyphs when entering combat. It is enabled by default. Consumers | |
1019 -- of this library can create a setting to enable/disable it as desired per a user setting. | |
1020 -- | |
1021 -- You should register for the AMR_SNAPSHOT_STATE_CHANGED message (sent via AceEvent-3.0 messaging) to ensure that | |
1022 -- your addon settings stay in sync with any other addon that may also be trying to control the enabled state. | |
1023 -- | |
1024 -- Note that if a user has the main AMR addon installed, it will always enable snapshotting, and override any attempt | |
1025 -- to disable it by immediately re-enabling it and thus re-triggering AMR_SNAPSHOT_STATE_CHANGED. | |
1026 ---------------------------------------------------------------------------------------------------------------------- | |
1027 Amr._snapshotEnabled = true | |
1028 | |
1029 -- Enable snapshotting of character data when entering combat. Sends this player's character data to anyone logging with the AskMrRobot addon. | |
1030 function Amr:EnableSnapshots() | |
1031 self._snapshotEnabled = true | |
1032 self:SendMessage("AMR_SNAPSHOT_STATE_CHANGED", self._snapshotEnabled) | |
1033 end | |
1034 | |
1035 -- Disable snapshotting of character data when entering combat. | |
1036 function Amr:DisableSnapshots() | |
1037 self._snapshotEnabled = false | |
1038 self:SendMessage("AMR_SNAPSHOT_STATE_CHANGED", self._snapshotEnabled) | |
1039 end | |
1040 | |
1041 function Amr:IsSnapshotEnabled() | |
1042 return self._snapshotEnabled | |
1043 end | |
1044 | |
1045 | |
1046 function Amr:PLAYER_REGEN_DISABLED() | |
1047 --function Amr:GARRISON_MISSION_NPC_OPENED() | |
1048 | |
1049 -- send data about this character when a player enters combat in a supported zone | |
1050 if self._snapshotEnabled and Amr.IsSupportedInstance() then | |
1051 local t = time() | |
1052 local player = self:GetPlayerData() | |
1053 local msg = self:SerializePlayerData(player) | |
1054 msg = string.format("%s\r%s\n%s\n%s\n%s\n%s", MINOR, t, player.Region, player.Realm, player.Name, msg) | |
1055 | |
1056 self:SendCommMessage(Amr.ChatPrefix, msg, "RAID") | |
1057 end | |
1058 end | |
1059 | |
1060 Amr:RegisterEvent("PLAYER_REGEN_DISABLED") | |
1061 --Amr:RegisterEvent("GARRISON_MISSION_NPC_OPENED") -- for debugging, fire this event when open mission table |