vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --===========================================================================--
2 ---------------------------- LootTracker by PNB ----------------------------
3 --===========================================================================--
4 -- LootTracker.lua
5 --
6 -- Primary entry points to the AddOn
7 --===========================================================================--
8  
9 --===========================================================================--
10 -- Constants
11 --===========================================================================--
12  
13 LT_Version = "1.5.1";
14  
15 LT_MinTooltipMode = 1;
16 LT_MaxTooltipMode = 3;
17  
18 LT_QualityColors = {
19 ["-2"] = {r=0.00, g=0.88, b=1.00},
20 ["-1"] = {r=0.89, g=0.82, b=0.39},
21 ["0"] = {r=0.50, g=0.50, b=0.50},
22 ["1"] = {r=1.00, g=1.00, b=1.00},
23 ["2"] = {r=0.12, g=1.00, b=0.00},
24 ["3"] = {r=0.00, g=0.44, b=0.87},
25 ["4"] = {r=0.64, g=0.21, b=0.93},
26 ["5"] = {r=1.00, g=1.00, b=1.00}
27 };
28  
29 LT_MinQuality = -2
30 LT_MaxQuality = 5;
31  
32 LT_MoneyColors = {
33 [0] = {r=0.89, g=0.82, b=0.39},
34 [1] = {r=0.71, g=0.71, b=0.71},
35 [2] = {r=0.66, g=0.33, b=0.11}
36 };
37  
38 LT_White = {r=1.00, g=1.00, b=1.00};
39  
40  
41 --===========================================================================--
42 -- Saved Variables
43 -- These are the values that will be persisted from session to session.
44 --===========================================================================--
45  
46 ------------------------------------------------------------------------------
47 -- LT_Data holds all data on items, kills, and players for the current session
48 -- and for any other saved sessions.
49 -- TODO: Make this also forked per-character
50 ------------------------------------------------------------------------------
51 LT_Data = { }
52 -- ["Realm - Player"]
53 -- ["SessionName"]
54 -- Items
55 -- ["ItemName"]
56 -- Name
57 -- Quality
58 -- Value
59 -- Zones (list)
60 -- TimesLooted (list)
61 -- Recipients (list)
62 -- Sources (list)
63 -- Kills
64 -- ["KillName"]
65 -- Name
66 -- Level
67 -- TimesKilled (list)
68 -- Drops (list)
69 -- Players
70 -- ["PlayerName"]
71 -- Name
72 -- Level
73 -- TimesKilled (list)
74 -- LootReceived (list)
75 -- Gear (list)
76  
77 ------------------------------------------------------------------------------
78 -- LT_Settings holds configuration values.
79 ------------------------------------------------------------------------------
80 LT_Settings = { }
81 -- ["Realm - Player"]
82 -- CurrentSession
83 -- DebugLevel
84 -- JustMyLoot
85 -- ChatColor
86 -- QualityThreshold
87 -- TooltipShowItems
88 -- TooltipShowKills
89 -- TooltipShowPlayers
90 -- IgnoreCraftLoot
91 -- IgnoreQuestLoot
92 -- IgnoreVendorLoot
93  
94  
95 --===========================================================================--
96 -- Globals
97 -- These values are global, but not saved when the session ends.
98 --===========================================================================--
99  
100 LT_RealmAndPlayer = "";
101 LT_ChangeListeners = nil;
102  
103 ------------------------------------------------------------------------------
104 -- LT_PendingLoot holds onto any items we've seen in a loot window, but
105 -- haven't yet linked to a recipient.
106 ------------------------------------------------------------------------------
107 LT_PendingLoot = {};
108 -- ["ItemName"]
109 -- Name
110 -- Source
111 -- Quantity (sometimes)
112 -- Value (sometimes)
113  
114 ------------------------------------------------------------------------------
115 -- LT_PendingLootBySlot holds the same information as LT_PendingLoot, but its
116 -- indexed by the original loot slot index, and the table is cleared as soon
117 -- as the loot window is closed. This table is used to handle the LootRemoved
118 -- event.
119 ------------------------------------------------------------------------------
120 LT_PendingLootBySlot = {};
121 -- ["Slot"]
122  
123  
124 ------------------------------------------------------------------------------
125 -- LT_PendingTarget holds onto any units we've targeted.
126 ------------------------------------------------------------------------------
127 LT_PendingTargets = {};
128 -- ["TargetName"]
129 -- Class
130 -- Level
131  
132  
133 --===========================================================================--
134 -- Frame Hooks
135 -- These are the functions linked from LootTracker.xml
136 --===========================================================================--
137  
138 ------------------------------------------------------------------------------
139 -- OnLoad
140 -- Invoked when the addon is loaded. Gives us a chance to register for any
141 -- events we might care about.
142 ------------------------------------------------------------------------------
143  
144 function LootTracker_OnLoad()
145  
146 this:RegisterEvent("VARIABLES_LOADED");
147 this:RegisterEvent("ADDON_LOADED");
148  
149 this:RegisterEvent("CHAT_MSG_LOOT");
150 this:RegisterEvent("CHAT_MSG_MONEY");
151 this:RegisterEvent("CHAT_MSG_COMBAT_FRIENDLY_DEATH");
152 this:RegisterEvent("CHAT_MSG_COMBAT_HOSTILE_DEATH");
153 this:RegisterEvent("CHAT_MSG_SYSTEM");
154 this:RegisterEvent("CHAT_MSG_COMBAT_FACTION_CHANGE");
155 this:RegisterEvent("CHAT_MSG_COMBAT_HONOR_GAIN");
156 this:RegisterEvent("CHAT_MSG_COMBAT_XP_GAIN");
157 this:RegisterEvent("CHAT_MSG_SKILL");
158  
159 this:RegisterEvent("PLAYER_TARGET_CHANGED");
160  
161 this:RegisterEvent("LOOT_OPENED");
162 this:RegisterEvent("LOOT_CLOSED");
163 this:RegisterEvent("LOOT_SLOT_CLEARED");
164  
165 end
166  
167  
168 ------------------------------------------------------------------------------
169 -- OnEvent
170 -- Invoked when any of our registered events are fired
171 ------------------------------------------------------------------------------
172  
173 function LootTracker_OnEvent(event, arg1)
174  
175 if (event == "VARIABLES_LOADED") then
176 LT_OnStartup();
177 return;
178 end
179  
180 if (event == "ADDON_LOADED") then
181 return;
182 end
183  
184 if (event == "CHAT_MSG_LOOT") then
185 LT_OnLootMessage(arg1);
186 return;
187 end
188  
189 if (event == "CHAT_MSG_MONEY") then
190 LT_OnMoneyMessage(arg1);
191 return;
192 end
193  
194 if (event == "CHAT_MSG_COMBAT_FRIENDLY_DEATH") then
195 LT_OnFriendlyDeathMessage(arg1);
196 return;
197 end
198  
199 if (event == "CHAT_MSG_COMBAT_HOSTILE_DEATH") then
200 LT_OnHostileDeathMessage(arg1);
201 return;
202 end
203  
204 if (event == "CHAT_MSG_SYSTEM") then
205 LT_OnSystemMessage(arg1);
206 return;
207 end
208  
209 if (event == "CHAT_MSG_COMBAT_FACTION_CHANGE") then
210 LT_OnFactionChange(arg1);
211 return;
212 end
213  
214 if (event == "CHAT_MSG_COMBAT_HONOR_GAIN") then
215 LT_OnGainHonor(arg1);
216 return;
217 end
218  
219 if (event == "CHAT_MSG_COMBAT_XP_GAIN") then
220 LT_OnGainExperience(arg1);
221 return;
222 end
223  
224 if (event == "CHAT_MSG_SKILL") then
225 LT_OnSkillMessage(arg1);
226 return;
227 end
228  
229 if (event == "LOOT_OPENED") then
230 LT_OnLootBegin();
231 return;
232 end
233  
234 if (event == "LOOT_CLOSED") then
235 LT_OnLootEnd();
236 return;
237 end
238  
239 if (event == "LOOT_SLOT_CLEARED") then
240 LT_OnLootSlotCleared(arg1);
241 return;
242 end
243  
244 if (event == "PLAYER_TARGET_CHANGED") then
245 LT_OnTargetChanged();
246 return;
247 end
248  
249 LT_DebugMessage(2, "Unexpected event: " .. event);
250  
251 end
252  
253  
254  
255 --===========================================================================--
256 -- Event Handlers
257 --===========================================================================--
258  
259 ------------------------------------------------------------------------------
260 -- OnStartup
261 ------------------------------------------------------------------------------
262  
263 function LT_OnStartup()
264  
265 -- Hook into console commands
266 SlashCmdList["LT_"] = LT_OnSlashCommand;
267  
268  
269 -- Get realm and player names
270 local realm = GetCVar("realmName");
271 local player = UnitName("player");
272 LT_RealmAndPlayer = realm .. " - " .. player;
273  
274  
275 -- Initialize the LT_Settings structure
276 local settings = LT_GetSettings();
277  
278 if (settings.CurrentSession == nil) then
279 settings.CurrentSession = LT_DEFAULT_SESSIONNAME;
280 end
281  
282 if (settings.DebugLevel == nil) then
283 settings.DebugLevel = 0;
284 end
285  
286 if (settings.JustMyLoot == nil) then
287 settings.JustMyLoot = false;
288 end
289  
290 if (settings.ChatColor == nil) then
291 settings.ChatColor = {r=0.78,g=0.55,b=1.0};
292 else
293 -- Convert from 0-255 range to 0-1 range
294 if (settings.ChatColor.r > 1.0) then
295 settings.ChatColor.r = settings.ChatColor.r / 255;
296 end
297 if (settings.ChatColor.g > 1.0) then
298 settings.ChatColor.g = settings.ChatColor.g / 255;
299 end
300 if (settings.ChatColor.b > 1.0) then
301 settings.ChatColor.b = settings.ChatColor.b / 255;
302 end
303 end
304  
305 if (settings.QualityThreshold == nil) then
306 settings.QualityThreshold = 2;
307 end
308  
309 if (settings.TooltipMode == nil) then
310 settings.TooltipMode = LT_MinTooltipMode;
311 end
312 if (settings.TooltipShowItems == nil) then
313 settings.TooltipShowItems = true;
314 end
315 if (settings.TooltipShowKills == nil) then
316 settings.TooltipShowKills = true;
317 end
318 if (settings.TooltipShowPlayers == nil) then
319 settings.TooltipShowPlayers = false;
320 end
321  
322 if (settings.IgnoreCraftLoot == nil) then
323 settings.IgnoreCraftLoot = true;
324 end
325 if (settings.IgnoreQuestLoot == nil) then
326 settings.IgnoreQuestLoot = false;
327 end
328 if (settings.IgnoreVendorLoot == nil) then
329 settings.IgnoreVendorLoot = true;
330 end
331  
332 -- Initialize the LT_Data structure
333 if (LT_Data == nil) then
334 LT_Data = {};
335 end
336  
337 -- Check the version
338 LT_ValidateSession();
339  
340  
341 -- Initialize the settings UI
342 -- TODO: Does this really do anything?
343 UIPanelWindows["LT_SettingsUI"] = {area = "center", pushable = 0};
344  
345  
346 -- Output a "We've loaded" message
347 -- TODO: Why isn't this appropriately colored?
348 LT_Message(string.format(LT_STARTUP, LT_Version));
349  
350 end
351  
352  
353 ------------------------------------------------------------------------------
354 -- OnLootMessage
355 -- There's been a loot message in chat - process it.
356 ------------------------------------------------------------------------------
357  
358 function LT_OnLootMessage(text)
359  
360 LT_DebugMessage(4, "Raw Loot Message: " .. text);
361 local settings = LT_GetSettings();
362  
363 -- SomeOtherPlayer receives loot: [Heavy Leather]
364 local beginMatch, endMatch, recipient, itemLink = string.find(text, LT_LOOT_RECEIVED);
365  
366 if (beginMatch) then
367  
368 LT_OnParsedLootMessage(recipient, itemLink);
369  
370 return;
371 end
372  
373 -- You receive loot: [Heavy Leather]
374 local beginMatch, endMatch, itemLink = string.find(text, LT_LOOT_RECEIVED_YOU);
375  
376 if (beginMatch) then
377  
378 LT_OnParsedLootMessage(LT_YOU, itemLink);
379  
380 return;
381 end
382  
383  
384 -- You receive item: [Warlords Deck]
385 local beginMatch, endMatch, itemLink = string.find(text, LT_LOOT_ITEM);
386  
387 if (beginMatch) then
388  
389 if (not settings.IgnoreVendorLoot) then
390  
391 -- This match covers both bought items and combined items (like Warlords Deck).
392 -- We need the setting to disable it for people who don't want to include
393 -- purchased ammo etc., but it'd be nice to be able to sort out true vendor
394 -- items from quest created items.
395  
396 local item, player = LT_OnParsedLootMessage(LT_YOU, itemLink);
397  
398 -- Mark the source as "Vendor"
399 LT_AddSource(item, LT_SOURCE_VENDOR, 1);
400  
401 else
402 LT_DebugMessage(2, "Ignoring vendor loot.");
403 end
404  
405 return;
406 end
407  
408 -- "You create: [Copper Bar]"
409 local beginMatch, endMatch, itemLink = string.find(text, LT_LOOT_CREATED);
410 if (beginMatch) then
411  
412 if (not settings.IgnoreCraftLoot) then
413  
414 -- Keep processing the loot message
415 local item, player = LT_OnParsedLootMessage(LT_YOU, itemLink);
416  
417 -- Mark the source as "Craft"
418 LT_AddSource(item, LT_SOURCE_CRAFT, 1);
419  
420 else
421 LT_DebugMessage(2, "Ignoring craft loot.");
422 end
423  
424 return;
425 end
426  
427 -- "You won [Meaty Bat Wing]"
428 local beginMatch, endMatch, recipient, itemLink = string.find(text, LT_LOOT_WON);
429 if (beginMatch) then
430  
431 -- TODO: Do something with this message
432 LT_DebugMessage(1, string.format("%s won %s", recipient, itemLink));
433  
434 end
435  
436 LT_DebugMessage(2, "Loot message did not match pattern: " .. text);
437  
438 end
439  
440  
441 ------------------------------------------------------------------------------
442 -- OnParsedLootMessage
443 -- Process a loot message.
444 ------------------------------------------------------------------------------
445  
446 function LT_OnParsedLootMessage(recipientName, itemLink)
447  
448 -- This shouldn't happen, but validate that we got our out parameters.
449 if (recipientName == nil) then
450 LT_DebugMessage(2, "Recipient was nil");
451 return;
452 end
453 if (itemLink == nil) then
454 LT_DebugMessage(2, "Item link was nil");
455 return;
456 end
457  
458 -- Map "You" to a player name
459 if (recipientName == LT_YOU) then
460 recipientName = UnitName("player");
461 else
462 -- If we're configured to only consider local loot, and this isn't
463 -- going to us, bail now.
464 if (LT_IsPlayerSolo()) then
465 LT_DebugMessage(2, "Ignoring loot going to another player.");
466 return;
467 end
468 end
469  
470 -- Get the Id from the link
471 local itemId = LT_ExtractItemIDFromChatLink(itemLink);
472  
473 LT_DebugMessage(4, "Extracted item id: " .. itemId);
474  
475 local item, quantity = LT_CreateItem(itemId);
476 local player = LT_CreatePlayer(recipientName);
477  
478 -- Bail out if we were unable to create either an item or player object.
479 if (item == nil) then
480 LT_DebugMessage(2, "Error creating item object");
481 return;
482 end
483 if (player == nil) then
484 LT_DebugMessage(2, "Error creating player object");
485 return;
486 end
487  
488 for i = 1, quantity do
489 LT_AddLootToPlayer(item, player);
490 end
491  
492 return item, player;
493  
494 end
495  
496  
497 ------------------------------------------------------------------------------
498 -- OnFriendlyDeathMessage
499 -- There's been a death message in chat - process it.
500 ------------------------------------------------------------------------------
501  
502 function LT_OnFriendlyDeathMessage(text)
503  
504 LT_DebugMessage(4, "Raw (Friendly) Death Message: " .. text);
505  
506 local beginMatch, endMatch, deceased = string.find(text, LT_FRIENDLY_DEATH);
507  
508 if (beginMatch) then
509 -- This shouldn't happen, but validate that we got our out parameters.
510 if (deceased == nil) then
511 LT_DebugMessage(2, "Deceased was nil");
512 return;
513 end
514  
515 local unitId = nil;
516  
517 -- Map "You" to a player name
518 if (deceased == LT_YOU) then
519 deceased = UnitName("player");
520 unitId = "player";
521 else
522 unitId = LT_GetPlayerUnitID(deceased);
523 end
524  
525 if (unitId == nil) then
526 LT_DebugMessage(2, "Unable to get UnitID for player: " .. deceased);
527 return;
528 end
529  
530 -- We don't want to bother logging non-party deaths.
531 if (not UnitInParty(unitId) and not UnitInRaid(unitId)) then
532 LT_DebugMessage(2, "Ignoring death of non-party character: " .. deceased);
533 end
534  
535 -- Create an entry for this player if there isn't one already.
536 local player = LT_CreatePlayer(deceased);
537  
538 local timeOfDeath = date();
539  
540 -- Log the time of death.
541 if (player.TimesKilled == nil) then
542 player.TimesKilled = { timeOfDeath };
543 else
544 table.insert(player.TimesKilled, timeOfDeath);
545 end
546  
547 LT_DebugMessage(2, "Player died: " .. player.Name);
548  
549 return;
550 else
551 LT_DebugMessage(3, "Friendly death message did not match pattern: " .. text);
552 end
553  
554 end
555  
556  
557 ------------------------------------------------------------------------------
558 -- OnHostileDeathMessage
559 -- There's been a death message in chat - process it.
560 ------------------------------------------------------------------------------
561  
562 function LT_OnHostileDeathMessage(text)
563  
564 LT_DebugMessage(4, "Raw (Hostile) Death Message: " .. text);
565  
566 local beginMatch, endMatch, deceased = string.find(text, LT_HOSTILE_DEATH);
567  
568 if (beginMatch) then
569 -- This shouldn't happen, but validate that we got our out parameters.
570 if (deceased == nil) then
571 LT_DebugMessage(2, "Deceased was nil");
572 return;
573 end
574  
575 -- TODO: If this kill did not come from our group, don't count it.
576 -- Use tapped state to detect this... somehow?
577  
578 -- Log this kill
579 kill = LT_CreateKill(deceased);
580  
581 return;
582 else
583 LT_DebugMessage(3, "Hostile death message did not match pattern: " .. text);
584 end
585  
586 end
587  
588  
589 ------------------------------------------------------------------------------
590 -- OnSystemMessage
591 -- There's been a system message in chat - process it.
592 ------------------------------------------------------------------------------
593  
594 function LT_OnSystemMessage(text)
595  
596 LT_DebugMessage(4, "Raw System Message: " .. text);
597  
598 -- Received item: [Super Duper Quest Reward]
599 local beginMatch, endMatch, itemLink = string.find(text, LT_RECEIVED_ITEM);
600 if (beginMatch) then
601  
602 local settings = LT_GetSettings();
603 if (not settings.IgnoreQuestLoot) then
604  
605 -- This shouldn't happen, but validate that we got our out parameters.
606 if (itemLink == nil) then
607 LT_DebugMessage(2, "Link was nil");
608 return;
609 end
610  
611 -- Keep processing the loot message
612 local item, player = LT_OnParsedLootMessage(LT_YOU, itemLink);
613  
614 -- Mark the source as "Quest"
615 LT_AddSource(item, LT_SOURCE_QUEST, 1);
616  
617 else
618 LT_DebugMessage(2, "Ignoring quest loot.");
619 end
620  
621 return;
622 end
623  
624 -- Received 7 Silver.
625 local beginMatch, endMatch, amount = string.find(text, LT_RECEIVED_MONEY);
626 if (beginMatch) then
627  
628 local settings = LT_GetSettings();
629 if (not settings.IgnoreQuestLoot) then
630  
631 LT_DebugMessage(2, "Quest money reward: " .. tostring(amount));
632  
633 local item = LT_OnMoneyLootText(amount, false);
634  
635 -- Mark the source as "Quest"
636 LT_AddSource(item, LT_SOURCE_QUEST, 1);
637  
638 else
639 LT_DebugMessage(2, "Ignoring quest money reward.");
640 end
641  
642 return;
643 end
644  
645 -- Discovered Ratchet: 105 experience gained
646 local beginMatch, endMatch, amount = string.find(text, LT_EXPERIENCE_GAINED_EXPLORE);
647 if (beginMatch) then
648  
649 LT_OnGainExperienceAmount(tonumber(amount), LT_SOURCE_EXPLORATION);
650  
651 return;
652  
653 end
654  
655 -- Experience gained: 1950
656 local beginMatch, endMatch, amount = string.find(text, LT_EXPERIENCE_GAINED_QUEST);
657 if (beginMatch) then
658  
659 LT_OnGainExperienceAmount(tonumber(amount), LT_SOURCE_QUEST);
660  
661 return;
662  
663 end
664  
665 LT_DebugMessage(4, "System message did not match pattern: " .. text);
666  
667 end
668  
669  
670 ------------------------------------------------------------------------------
671 -- OnFactionChange
672 -- There's been a faction message in chat - process it.
673 ------------------------------------------------------------------------------
674  
675 function LT_OnFactionChange(text)
676  
677 LT_DebugMessage(4, "Raw faction change: " .. text);
678  
679 -- Your reputation with Timbermaw Hold has very slightly increased (5 reputation gained)
680 local beginMatch, endMatch, faction = string.find(text, LT_REPUTATION_FACTION);
681 if (beginMatch) then
682  
683 local beginMatch, endMatch, amount = string.find(text, LT_REPUTATION_GAINED);
684 if (beginMatch) then
685  
686 amount = tonumber(amount);
687  
688 LT_DebugMessage(2, string.format("Gained %d %s reputation", amount, faction));
689  
690 LT_OnReputationChange(faction, amount)
691  
692 return;
693 end
694  
695 local beginMatch, endMatch, amount = string.find(text, LT_REPUTATION_LOST);
696 if (beginMatch) then
697  
698 amount = tonumber(amount);
699  
700 LT_DebugMessage(2, string.format("Lost %d %s reputation", amount, faction));
701  
702 LT_OnReputationChange(faction, -amount)
703  
704 end
705  
706 end
707  
708 -- You are now Neutral with Booty Bay
709  
710 LT_DebugMessage(2, "Faction message did not match pattern: " .. text);
711  
712 end
713  
714  
715 ------------------------------------------------------------------------------
716 -- OnGainHonor
717 -- There's been an honor message in chat - process it.
718 ------------------------------------------------------------------------------
719  
720 function LT_OnGainHonor(text)
721  
722 LT_DebugMessage(4, "Raw honor change: " .. text);
723  
724 -- Rolbek dies, honorable kill Rank: Knight (Estimated Honor Points: 32)
725 local beginMatch, endMatch, amount = string.find(text, LT_HONOR_GAINED_KILL);
726 if (beginMatch) then
727  
728 LT_OnGainHonorAmount(tonumber(amount), LT_SOURCE_KILL);
729 return;
730 end
731  
732 -- You have been awarded 198 honor points.
733 local beginMatch, endMatch, amount = string.find(text, LT_HONOR_GAINED_AWARD);
734 if (beginMatch) then
735  
736 LT_OnGainHonorAmount(tonumber(amount), nil);
737 return;
738 end
739  
740 LT_DebugMessage(1, "Honor message did not match pattern: " .. text);
741  
742 end
743  
744  
745 ------------------------------------------------------------------------------
746 -- OnGainExperience
747 -- There's been an experience message in chat - process it.
748 ------------------------------------------------------------------------------
749  
750 function LT_OnGainExperience(text)
751  
752 LT_DebugMessage(4, "Raw experience change: " .. text);
753  
754 -- Greater Plainstrider dies, you gain 144 experience. (+72 Rested bonus)
755 local beginMatch, endMatch, amount = string.find(text, LT_EXPERIENCE_GAINED_KILL);
756 if (beginMatch) then
757  
758 LT_OnGainExperienceAmount(tonumber(amount), LT_SOURCE_KILL);
759  
760 return;
761  
762 end
763  
764 -- You gain 440 experience
765 local beginMatch, endMatch, amount = string.find(text, LT_EXPERIENCE_GAINED);
766 if (beginMatch) then
767  
768 -- Ignore these and instead use the system message
769 LT_DebugMessage(2, "Ignoring duplicate experience message for " .. amount);
770 return;
771  
772 end
773  
774 LT_DebugMessage(1, "Experience message did not match pattern: " .. text);
775  
776 end
777  
778  
779 ------------------------------------------------------------------------------
780 -- OnSkillMessage
781 -- There's been an skill message in chat - process it.
782 ------------------------------------------------------------------------------
783  
784 function LT_OnSkillMessage(text)
785  
786 LT_DebugMessage(4, "Raw skill message: " .. text);
787  
788 -- Your skill in Staves has increased to 64
789 local beginMatch, endMatch, skill, value = string.find(text, LT_SKILL_GAINED);
790 if (beginMatch) then
791  
792 value = tonumber(value);
793  
794 -- We're assuming that skill changes always come in units of one.
795 -- If this isn't true we'll need to do more work and remember the old
796 -- skill value.
797 local amount = 1;
798  
799 LT_DebugMessage(1, string.format("Skill change: %s, %d", skill, value));
800  
801 -- Create a fake item entry to represent the experience
802 local rawItem = {};
803 rawItem.Quality = -2;
804 rawItem.Name = string.format(LT_SKILL_TYPE, skill);
805 rawItem.Class = LT_SKILL;
806 rawItem.SubClass = skill;
807  
808 -- Create the real item entry
809 local item = LT_CreateItemFromRaw(rawItem, nil);
810  
811 if (item ~= nil) then
812  
813 local recipient = UnitName("player");
814  
815 -- Add the receiving player to the item's Recipients list
816 if (item.Recipients == nil) then
817 item.Recipients = { };
818 end
819 LT_AddCountEntry(item.Recipients, recipient, amount);
820  
821 return item;
822  
823 end
824  
825 return;
826 end
827  
828 LT_DebugMessage(1, "Skill message did not match pattern: " .. text);
829  
830 end
831  
832  
833 ------------------------------------------------------------------------------
834 -- OnMoneyMessage
835 -- There's been a mmoney loot message in chat - process it.
836 ------------------------------------------------------------------------------
837  
838 function LT_OnMoneyMessage(text)
839  
840 LT_DebugMessage(4, "Raw Money Message: " .. text);
841  
842 -- "You loot 1 Silver, 10 Copper"
843 local beginMatch, endMatch, amount = string.find(text, LT_MONEY_LOOT);
844 if (beginMatch) then
845 -- This shouldn't happen, but validate that we got our out parameters.
846 if (amount == nil) then
847 LT_DebugMessage(2, "Amount was nil");
848 return;
849 end
850  
851 -- Non-shared money
852  
853 -- We ignore non-shared loot messages that come from chat if we're in
854 -- "solo" mode. We'll get a LT_OnLootSlotCleared event for those ones
855 -- consistently. We only get the chat message when it's a shift-click
856 -- loot.
857 if (not LT_IsPlayerSolo()) then
858 LT_OnMoneyLootText(amount, false);
859 else
860 LT_DebugMessage(2, "Ignoring loot chat message.");
861 end
862  
863 return;
864 end
865  
866 -- "Your share of the loot is 2 Silver, 21 Copper."
867 local beginMatch, endMatch, amount = string.find(text, LT_SHARED_MONEY_LOOT);
868 if (beginMatch) then
869 -- This shouldn't happen, but validate that we got our out parameters.
870 if (amount == nil) then
871 LT_DebugMessage(2, "Amount was nil");
872 return;
873 end
874  
875 -- Shared money
876 LT_OnMoneyLootText(amount, true);
877  
878 return;
879 end
880  
881 LT_DebugMessage(2, "Money message did not match pattern: " .. text);
882  
883 end
884  
885  
886 ------------------------------------------------------------------------------
887 -- OnMoneyLootText
888 -- Parse and process cash loot
889 ------------------------------------------------------------------------------
890  
891 function LT_OnMoneyLootText(text, shared)
892  
893 -- 1 Gold, 2 Silver, 21 Copper."
894 local value, gold, silver, copper = LT_ParseMoney(text);
895  
896 local sharedString = "";
897 if (shared) then
898 sharedString = " (shared)"
899 end
900 LT_DebugMessage(2, "Looted Money" .. sharedString .. ": " .. tostring(gold) .. "g " .. tostring(silver) .. "s " .. tostring(copper) .. "c");
901  
902 return LT_OnMoneyLoot(value, shared, nil);
903  
904 end
905  
906  
907 ------------------------------------------------------------------------------
908 -- OnMoneyLoot
909 -- Process cash loot
910 ------------------------------------------------------------------------------
911  
912 function LT_OnMoneyLoot(value, shared, source)
913  
914 local rawItem = {};
915 rawItem.Name = LT_MONEY;
916 rawItem.Quality = -1;
917 rawItem.Class = LT_MONEY;
918  
919 local item = LT_CreateItemFromRaw(rawItem, source);
920  
921 if (item ~= nil) then
922 if (item.Value == nil) then
923 item.Value = 0;
924 end
925  
926 -- The Value of the Money item is an average drop size.
927 -- Add this new loot value into the running average.
928  
929 local oldValue = item.Value;
930 local newCount = getn(item.TimesLooted);
931  
932 local oldTotalMoneyLooted = oldValue * (newCount - 1);
933 local newTotalMoneyLooted = oldTotalMoneyLooted + value;
934 local newAverage = newTotalMoneyLooted / newCount;
935  
936 item.Value = newAverage;
937  
938 -- If this is shared loot, go to "Everyone", otherwise go to the local player.
939 local recipient = LT_EVERYONE;
940 if (not shared) then
941 recipient = UnitName("player");
942 end
943  
944 -- Add the receiving player to the item's Recipients list
945 if (item.Recipients == nil) then
946 item.Recipients = { };
947 end
948 LT_AddCountEntry(item.Recipients, recipient, 1);
949  
950 return item;
951  
952 end
953  
954 end
955  
956  
957 ------------------------------------------------------------------------------
958 -- OnReputationChange
959 -- There has been a reputation gain/loss.
960 ------------------------------------------------------------------------------
961  
962 function LT_OnReputationChange(faction, amount)
963  
964 LT_DebugMessage(1, string.format("Faction gain: %s - %d", faction, amount));
965  
966 -- Create a fake item entry to represent the reputation
967 local rawItem = {};
968 rawItem.Quality = -2;
969  
970 -- Give separate names for gain/loss
971 local formatString = LT_REPUTATION_TYPE;
972 if (amount < 0) then
973 formatString = LT_REPUTATION_TYPELOST;
974 end
975  
976 rawItem.Name = string.format(formatString, faction);
977 rawItem.Class = LT_REPUTATION;
978 rawItem.SubClass = faction;
979  
980  
981 -- Create the final item entry
982 local item = LT_CreateItemFromRaw(rawItem, nil);
983  
984 if (item ~= nil) then
985  
986 local recipient = UnitName("player");
987  
988 -- Add the receiving player to the item's Recipients list
989 if (item.Recipients == nil) then
990 item.Recipients = { };
991 end
992 LT_AddCountEntry(item.Recipients, recipient, amount);
993  
994 return item;
995  
996 end
997  
998 end
999  
1000  
1001 ------------------------------------------------------------------------------
1002 -- OnGainExperienceAmount
1003 -- There's been an experience gain.
1004 ------------------------------------------------------------------------------
1005  
1006 function LT_OnGainExperienceAmount(amount, source)
1007  
1008 LT_DebugMessage(1, string.format("Experience gain: %d (%s)", amount, source));
1009  
1010 -- Create a fake item entry to represent the experience
1011 local rawItem = {};
1012 rawItem.Quality = -2;
1013 rawItem.Name = LT_EXPERIENCE;
1014 rawItem.Class = LT_EXPERIENCE;
1015  
1016 -- Create the real item entry
1017 local item = LT_CreateItemFromRaw(rawItem, nil);
1018  
1019 if (item ~= nil) then
1020  
1021 local recipient = UnitName("player");
1022  
1023 -- Add the receiving player to the item's Recipients list
1024 if (item.Recipients == nil) then
1025 item.Recipients = { };
1026 end
1027 LT_AddCountEntry(item.Recipients, recipient, amount);
1028  
1029 if (source ~= nil) then
1030 LT_AddSource(item, source, amount);
1031 else
1032 LT_DebugMessage(1, "Unspecified source for experience");
1033 end
1034  
1035 return item;
1036  
1037 end
1038  
1039  
1040  
1041 end
1042  
1043  
1044 ------------------------------------------------------------------------------
1045 -- OnGainHonorAmount
1046 -- There's been an honor gain.
1047 ------------------------------------------------------------------------------
1048  
1049 function LT_OnGainHonorAmount(amount, source)
1050  
1051 LT_DebugMessage(1, string.format("Honor gain: %d", amount));
1052  
1053 -- Create a fake item entry to represent the honor
1054 local rawItem = {};
1055 rawItem.Quality = -2;
1056 rawItem.Name = LT_HONOR;
1057 rawItem.Class = LT_HONOR;
1058  
1059 -- Create the real item entry
1060 local item = LT_CreateItemFromRaw(rawItem, nil);
1061  
1062 if (item ~= nil) then
1063  
1064 local recipient = UnitName("player");
1065  
1066 -- Add the receiving player to the item's Recipients list
1067 if (item.Recipients == nil) then
1068 item.Recipients = { };
1069 end
1070 LT_AddCountEntry(item.Recipients, recipient, amount);
1071  
1072 if (source ~= nil) then
1073 LT_AddSource(item, source, amount);
1074 else
1075 LT_DebugMessage(1, "Unspecified source for honor");
1076 end
1077  
1078 return item;
1079  
1080 end
1081  
1082 end
1083  
1084  
1085 ------------------------------------------------------------------------------
1086 -- OnLootBegin
1087 -- The current player has opened the loot window.
1088 ------------------------------------------------------------------------------
1089  
1090 function LT_OnLootBegin()
1091  
1092 LT_PendingLootBySlot = {};
1093  
1094 -- Determine the target being looted
1095 local source = UnitName("target");
1096  
1097 if (source == nil) then
1098 LT_DebugMessage(3, "Looting (Unknown)");
1099 else
1100 LT_DebugMessage(3, "Looting " .. source);
1101 end
1102  
1103 local lootCount = GetNumLootItems();
1104  
1105 for slot = 1, lootCount, 1 do
1106  
1107 -- Create a pending loot entry
1108 LT_CreatePendingLoot(slot, source);
1109 end
1110  
1111 end
1112  
1113  
1114 ------------------------------------------------------------------------------
1115 -- OnLootEnd
1116 -- The current player has closed the loot window.
1117 ------------------------------------------------------------------------------
1118  
1119 function LT_OnLootEnd()
1120  
1121 LT_DebugMessage(3, "Done looting");
1122  
1123 end
1124  
1125  
1126 ------------------------------------------------------------------------------
1127 -- OnLootEnd
1128 -- The current player has closed the loot window.
1129 ------------------------------------------------------------------------------
1130  
1131 function LT_OnLootSlotCleared(slot)
1132  
1133 LT_DebugMessage(3, "Slot looted: " .. slot);
1134  
1135 if (LT_PendingLootBySlot == nil) then
1136 LT_DebugMessage(2, "Invalid slot looting. Expected pending loot table to still be valid.");
1137 return;
1138 end
1139  
1140 local loot = LT_PendingLootBySlot[slot];
1141  
1142 if (loot == nil) then
1143 LT_DebugMessage(2, "Invalid slot looting. Expected pending loot item to still be valid for slot " .. slot .. ".");
1144 return;
1145 end
1146  
1147 LT_DebugMessage(4, "Loot cleared: " .. loot.Name .. " (slot " .. slot .. ")");
1148  
1149 local settings = LT_GetSettings();
1150 if (loot.Name == LT_MONEY) then
1151 -- Disabling because we can't determine at this point if this will
1152 -- end up being a shared (split) money drop or not. We will always
1153 -- get this event for the local player, and so when solo-ing this is
1154 -- the best event to hook (since the chat message is *not* always
1155 -- sent). However, when in a group we don't want to think the local
1156 -- player will receive 100% of the cash when in fact it will be split.
1157 if (LT_IsPlayerSolo()) then
1158 -- Non-shared loot
1159 local item = LT_OnMoneyLoot(loot.Value, false, loot.Source);
1160 end
1161  
1162 LT_PendingLoot[loot.Name] = nil;
1163 end
1164  
1165 LT_PendingLootBySlot[slot] = nil;
1166  
1167 end
1168  
1169  
1170 ------------------------------------------------------------------------------
1171 -- OnTargetChanged
1172 -- The current player has changed who they're targeting.
1173 ------------------------------------------------------------------------------
1174  
1175 function LT_OnTargetChanged()
1176  
1177 LT_CreatePendingTarget();
1178  
1179 end
1180  
1181  
1182 ------------------------------------------------------------------------------
1183 -- SlashCommand
1184 -- Handles console commands.
1185 ------------------------------------------------------------------------------
1186  
1187 function LT_OnSlashCommand(rawArgument)
1188  
1189 local command, value = LT_GetCommandAndValue(rawArgument);
1190  
1191 local settings = LT_GetSettings();
1192  
1193 if (LT_StrIsNilOrEmpty(command)) then
1194 command = LT_SLASHCOMMAND_SETTINGS;
1195 end
1196  
1197 if (command == LT_SLASHCOMMAND_DEBUG) then
1198  
1199 if (value ~= nil) then
1200 local level = tonumber(value);
1201 if (level == nil) then
1202 LT_Message(LT_SLASHCOMMAND_DEBUG_ERROR);
1203 else
1204 settings.DebugLevel = level;
1205 end
1206 end
1207  
1208 LT_Message(string.format(LT_SLASHCOMMAND_DEBUG_QUERY, settings.DebugLevel));
1209 return;
1210  
1211 elseif (command == LT_SLASHCOMMAND_JUSTMYLOOT) then
1212  
1213 if (value ~= nil) then
1214 settings.JustMyLoot = value;
1215 else
1216 LT_Message(string.format(LT_SLASHCOMMAND_JUSTMYLOOT_QUERY, tostring(settings.JustMyLoot)));
1217 end;
1218 return;
1219  
1220 elseif (command == LT_SLASHCOMMAND_SESSION) then
1221  
1222 if (value ~= nil) then
1223 LT_SetSession(value);
1224 else
1225 LT_Message(string.format(LT_SLASHCOMMAND_SESSION_QUERY, settings.CurrentSession));
1226 end;
1227  
1228 return;
1229  
1230 elseif (command == LT_SLASHCOMMAND_SESSIONS) then
1231  
1232 LT_Message(LT_SLASHCOMMAND_SESSIONS_QUERY);
1233 LT_MessageIndent();
1234  
1235 local sessions = LT_GetAvailableSessions();
1236 foreach(sessions, function(k,v)
1237 LT_Message(LT_FormatSessionName(v, true));
1238 end);
1239  
1240 LT_MessageUnindent();
1241  
1242 return;
1243  
1244 elseif (command == LT_SLASHCOMMAND_EXPORT) then
1245  
1246 LT_ExportSession(value);
1247 return;
1248  
1249 elseif (command == LT_SLASHCOMMAND_SUMMARY) then
1250  
1251 LT_OutputSummary(value);
1252 return;
1253  
1254 elseif (command == LT_SLASHCOMMAND_CHATCOLOR) then
1255  
1256 if (value ~= nil) then
1257  
1258 -- TODO: Validate that this parses comma separated RGB values as well as space values,
1259 -- Because I don't think it does...
1260 local beginMatch, endMatch, r, g, b = string.find(value, "(%d+)[%s,](%d+)[%s,](%d+)");
1261  
1262 if (beginMatch) then
1263  
1264 if ((r == nil) or (g == nil) or (b == nil)) then
1265 LT_Message(LT_SLASHCOMMAND_CHATCOLOR_ERROR);
1266 else
1267 settings.ChatColor = {};
1268 settings.ChatColor.r = r;
1269 settings.ChatColor.g = g;
1270 settings.ChatColor.b = b;
1271 end
1272 else
1273 LT_Message(LT_SLASHCOMMAND_CHATCOLOR_ERROR);
1274 end
1275 end;
1276  
1277 LT_Message(string.format(LT_SLASHCOMMAND_CHATCOLOR_QUERY, settings.ChatColor.r, settings.ChatColor.g, settings.ChatColor.b));
1278 return;
1279  
1280 elseif (command == LT_SLASHCOMMAND_THRESHOLD) then
1281 if (value ~= nil) then
1282 settings.QualityThreshold = tonumber(value);
1283 LT_FireChange();
1284 else
1285 LT_Message(LT_SLASHCOMMAND_THRESHOLD_ERROR);
1286 end
1287  
1288 LT_Message(string.format(LT_SLASHCOMMAND_THRESHOLD_QUERY, settings.QualityThreshold));
1289 return;
1290  
1291 elseif (command == LT_SLASHCOMMAND_RESET) then
1292  
1293 LT_ResetSession(value);
1294 return;
1295  
1296 elseif (command == LT_SLASHCOMMAND_UPDATE) then
1297  
1298 if (value ~= nil) then
1299 LT_UpdateObject(value);
1300 else
1301 LT_Message(LT_SLASHCOMMAND_UPDATE_ERROR);
1302 end
1303 return;
1304  
1305 elseif (command == LT_SLASHCOMMAND_TRANSFER) then
1306  
1307 if (value ~= nil) then
1308 -- /lt transfer playerNameSource playerNameTarget itemName
1309 local beginMatch, endMatch, playerNameSource, playerNameTarget, itemName = string.find(value, "([^%s]*)%s([^%s]*)%s(.*)");
1310 if (beginMatch) then
1311 LT_TransferItem(playerNameSource, playerNameTarget, itemName);
1312 else
1313 LT_Message(LT_SLASHCOMMAND_TRANSFER_ERROR);
1314 end
1315  
1316 end
1317  
1318 return;
1319  
1320 elseif (command == LT_SLASHCOMMAND_SETTINGS) then
1321  
1322 if (LT_SettingsUI:IsShown()) then
1323 HideUIPanel(LT_SettingsUI);
1324 else
1325 ShowUIPanel(LT_SettingsUI);
1326 end
1327  
1328 return;
1329  
1330 elseif (command == LT_SLASHCOMMAND_HELP) then
1331  
1332 -- Fall through to the help code below.
1333  
1334 elseif (rawArgument ~= nil) then
1335  
1336 LT_Message(string.format(LT_SLASHCOMMAND_ERROR, rawArgument));
1337  
1338 end
1339  
1340 -- Show the help
1341 LT_Message(string.format(LT_HELPMESSAGE_1, LT_Version));
1342 LT_Message(LT_HELPMESSAGE_2);
1343 LT_Message(LT_HELPMESSAGE_3);
1344 LT_Message(LT_HELPMESSAGE_4);
1345 LT_Message(LT_HELPMESSAGE_5);
1346 LT_Message(LT_HELPMESSAGE_6);
1347 LT_Message(LT_HELPMESSAGE_7);
1348 LT_Message(LT_HELPMESSAGE_8);
1349 LT_Message(LT_HELPMESSAGE_9);
1350 LT_Message(LT_HELPMESSAGE_10);
1351 LT_Message(LT_HELPMESSAGE_11);
1352 LT_Message(LT_HELPMESSAGE_12);
1353  
1354 end
1355  
1356  
1357 ------------------------------------------------------------------------------
1358 -- AddListener
1359 ------------------------------------------------------------------------------
1360  
1361 function LT_AddListener(event)
1362  
1363 if (LT_ChangeListeners == nil) then
1364 LT_ChangeListeners = {};
1365 end
1366  
1367 tinsert(LT_ChangeListeners, event);
1368  
1369 end
1370  
1371  
1372 ------------------------------------------------------------------------------
1373 -- RemoveListener
1374 ------------------------------------------------------------------------------
1375  
1376 function LT_RemoveListener(event)
1377  
1378 if (LT_ChangeListeners == nil) then
1379 return;
1380 end
1381  
1382 tremove(LT_ChangeListeners, LT_IndexOf(LT_ChangeListeners, event));
1383  
1384 end
1385  
1386  
1387 ------------------------------------------------------------------------------
1388 -- FireChange
1389 ------------------------------------------------------------------------------
1390  
1391 function LT_FireChange()
1392  
1393 if (LT_ChangeListeners ~= nil) then
1394 foreach(LT_ChangeListeners, function(k,v)
1395 v();
1396 end);
1397 end
1398  
1399 end
1400