-
Notifications
You must be signed in to change notification settings - Fork 0
/
ml_core.lua
executable file
·841 lines (767 loc) · 30.3 KB
/
ml_core.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
--[[ RCLootCouncil by Potdisc
ml_core.lua Contains core elements for the MasterLooter
- Although possible, this module shouldn't be replaced unless closely replicated as other default modules depend on it.
- Assumes several functions in SessionFrame and VotingFrame
TODOs/NOTES:
- SendMessage() on AddItem() to let userModules know it's safe to add to lootTable. Might have to do it other places too.
]]
local addon = LibStub("AceAddon-3.0"):GetAddon("RCLootCouncil")
RCLootCouncilML = addon:NewModule("RCLootCouncilML", "AceEvent-3.0", "AceBucket-3.0", "AceComm-3.0", "AceTimer-3.0", "AceHook-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("RCLootCouncil")
local LibDialog = LibStub("LibDialog-1.0")
local Deflate = LibStub("LibDeflate")
local db;
function RCLootCouncilML:OnInitialize()
addon:Debug("ML initialized!")
end
function RCLootCouncilML:OnDisable()
addon:Debug("ML Disabled")
self:UnregisterAllEvents()
self:UnregisterAllBuckets()
self:UnregisterAllComm()
self:UnregisterAllMessages()
self:UnhookAll()
end
function RCLootCouncilML:OnEnable()
addon:Debug("ML Enabled")
db = addon:Getdb()
self.candidates = {} -- candidateName = { class, role, rank }
self.lootTable = {} -- The MLs operating lootTable
-- self.lootTable[session] = { bagged, lootSlot, awarded, name, link, quality, ilvl, type, subType, equipLoc, texture, boe }
self.awardedInBags = {} -- Awarded items that are stored in MLs inventory
self.lootInBags = {} -- Items not yet awarded but stored in bags
self.council = addon:GetCouncilInGroup() -- the currently active council
self.lootOpen = false -- is the ML lootWindow open or closed?
self.running = false -- true if we're handling a session
self:RegisterComm("RCLootCouncil", "OnCommReceived")
self:RegisterEvent("LOOT_OPENED","OnEvent")
self:RegisterEvent("LOOT_CLOSED","OnEvent")
self:RegisterBucketEvent("RAID_ROSTER_UPDATE", 10, "UpdateGroup") -- Bursts in group creation, and we should have plenty of time to handle it
self:RegisterEvent("CHAT_MSG_WHISPER","OnEvent")
self:RegisterBucketMessage("RCConfigTableChanged", 2, "ConfigTableChanged") -- The messages can burst
end
--- Add an item to the lootTable
-- @paramsig item[, bagged, slotIndex, index]
-- @param item Any: ItemID|itemString|itemLink
-- @param bagged True if the item is in the ML's inventory
-- @param slotIndex Index of the lootSlot, or nil if none - either this or 'bagged' needs to be supplied
-- @param index Index in self.lootTable, used to set data in a specific session
function RCLootCouncilML:AddItem(item, bagged, slotIndex, index)
addon:DebugLog("ML:AddItem", item, bagged, slotIndex, index)
local name, link, rarity, ilvl, iMinLevel, type, subType, iStackCount, equipLoc, texture = GetItemInfo(item)
-- Item isn't properly loaded, so update the data in 0.5 sec (Should only happen with /rc test)
if not name then
self:ScheduleTimer("Timer", 0.5, "AddItem", item, bagged, slotIndex, #self.lootTable)
GameTooltip:SetHyperlink("item:"..item) -- cace item asap
addon:Debug("Started timer:", "AddItem", "for", item)
return
end
if RCTokenTable[item] then
ilvl = RCTokenLevel[item]
equipLoc = RCTokenTable[item]
end
self.lootTable[index or #self.lootTable + 1] = {
["bagged"] = bagged,
["lootSlot"] = slotIndex,
["awarded"] = false,
["name"] = name,
["link"] = link,
["quality"] = rarity,
["ilvl"] = ilvl,
["subType"] = subType,
["equipLoc"] = equipLoc,
["texture"] = texture,
["boe"] = addon:IsItemBoE(link),
}
end
--- Removes a session from the lootTable
-- @param session The session (index) in lootTable to remove
function RCLootCouncilML:RemoveItem(session)
tremove(self.lootTable, session)
end
function RCLootCouncilML:AddCandidate(name, class, role, rank, enchant, lvl)
addon:DebugLog("ML:AddCandidate",name, class, role, rank, enchant, lvl)
self.candidates[name] = {
["class"] = class,
["role"] = role or "DAMAGER",
["rank"] = rank or "", -- Rank cannot be nil for votingFrame
["enchanter"] = enchant,
["enchant_lvl"] = lvl,
}
end
function RCLootCouncilML:RemoveCandidate(name)
addon:DebugLog("ML:RemoveCandidate", name)
self.candidates[name] = nil
end
function RCLootCouncilML:UpdateGroup(ask)
if self == nil then
self = RCLootCouncilML -- why?
end
addon:DebugLog("UpdateGroup", ask)
if type(ask) ~= "boolean" then ask = false end
local group_copy = {}
local updates = false
for name in pairs(self.candidates) do group_copy[name] = true end
if addon:IsInRaid() then
for i = 1, addon:GetNumGroupMembers() do
local name, _, _, _, _, class, _, _, _, _, _ = GetRaidRosterInfo(i)
if name then
local role = addon:GetUnitRole(name)
if group_copy[name] then -- If they're already registered
group_copy[name] = nil -- remove them from the check
else -- add them
if not ask then -- ask for playerInfo?
addon:SendCommand(name, "playerInfoRequest")
addon:SendCommand(name, "MLdb", addon.mldb) -- and send mlDB
end
self:AddCandidate(name, class, role) -- Add them in case they haven't installed the adoon
updates = true
end
end
end
elseif addon:IsInGroup() then
for i = 0, addon:GetNumGroupMembers() do
local name, class, role = nil, nil, nil
if i == 0 then
name, class, role = UnitName("player"), select(2, UnitClass("player")), addon:GetPlayerRole()
else
name, class, role = UnitName("party"..i), select(2, UnitClass("party"..i)), addon:GetUnitRole("party"..i)
end
if name then
if group_copy[name] then
group_copy[name] = nil
else
if not ask then -- ask for playerInfo?
addon:SendCommand(name, "playerInfoRequest")
addon:SendCommand(name, "MLdb", addon.mldb) -- and send mlDB
end
self:AddCandidate(name, class, role) -- Add them in case they haven't installed the adoon
updates = true
end
end
end
end
-- If anything's left in group_copy it means they left the raid, so lets remove them
for name, v in pairs(group_copy) do
if v then self:RemoveCandidate(name); updates = true end
end
if updates then
addon:SendCommand("group", "candidates", self.candidates)
local oldCouncil = self.council
self.council = addon:GetCouncilInGroup()
local council_updated = false
if #self.council ~= #oldCouncil then
council_updated = true
else
for i in ipairs(self.council) do
if self.council[i] ~= oldCouncil[i] then
council_updated = true
break
end
end
end
if council_updated then
addon:SendCommand("group", "council", self.council)
end
end
end
function RCLootCouncilML:StartSession()
addon:Debug("ML:StartSession()")
self.council = addon:GetCouncilInGroup()
-- ensure we are ready
if not self.candidates[addon.playerName] or #self.council == 0 then
addon:Print(L["Please wait a few seconds until all data has been synchronized."])
return addon:Debug("Data wasn't ready", self.candidates[addon.playerName], #self.council)
end
self.running = true
addon:SendCommand("group", "lootTable", self.lootTable)
self:AnnounceItems()
-- Start a timer to set response as offline/not installed unless we receive an ack
self:ScheduleTimer("Timer", 10, "LootSend")
end
function RCLootCouncilML:AddUserItem(item)
if self.running then return addon:Print(L["You're already running a session."]) end
self:AddItem(item, true)
addon:CallModule("sessionframe")
addon:GetActiveModule("sessionframe"):Show(self.lootTable)
end
function RCLootCouncilML:SessionFromBags()
if self.running then return addon:Print(L["You're already running a session."]) end
if #self.lootInBags == 0 then return addon:Print(L["No items to award later registered"]) end
for i, link in ipairs(self.lootInBags) do self:AddItem(link, true) end
if db.autoStart then
self:StartSession()
else
addon:CallModule("sessionframe")
addon:GetActiveModule("sessionframe"):Show(self.lootTable)
end
end
-- TODO awardedInBags should be kept in db incase the player logs out
function RCLootCouncilML:PrintAwardedInBags()
if #self.awardedInBags == 0 then return addon:Print(L["No winners registered"]) end
addon:Print(L["Following winners was registered:"])
for _, v in ipairs(self.awardedInBags) do
if self.candidates[v.winner] then
local c = addon:GetClassColor(self.candidates[v.winner].class)
local text = "|cff"..addon:RGBToHex(c.r,c.g,c.b).. v.winner .."|r"
addon:Print(v.link, "-->", text)
else
addon:Print(v.link, "-->", v.winner) -- fallback
end
end
-- IDEA Do we delete awardedInBags here or keep it?
end
function RCLootCouncilML:ConfigTableChanged(val)
-- The db was changed, so check if we should make a new mldb
-- We can do this by checking if the changed value is a key in mldb
if not addon.mldb then return self:UpdateMLdb() end -- mldb isn't made, so just make it
for val in pairs(val) do
for key in pairs(addon.mldb) do
if key == val then return self:UpdateMLdb() end
end
end
end
function RCLootCouncilML:CouncilChanged()
-- The council was changed, so send out the council
self.council = addon:GetCouncilInGroup()
addon:SendCommand("group", "council", self.council)
-- Send candidates so new council members can register it
addon:SendCommand("group", "candidates", self.candidates)
end
function RCLootCouncilML:UpdateMLdb()
-- The db has changed, so update the mldb and send the changes
addon:Debug("UpdateMLdb")
addon.mldb = self:BuildMLdb()
addon:SendCommand("group", "MLdb", addon.mldb)
end
function RCLootCouncilML:BuildMLdb()
-- Extract changes to responses
local changedResponses = {};
for i = 1, db.numButtons do
if db.responses[i].text ~= addon.defaults.profile.responses[i].text or unpack(db.responses[i].color) ~= unpack(addon.defaults.profile.responses[i].color) then
changedResponses[i] = db.responses[i]
end
end
-- Extract changed buttons
local changedButtons = {};
for i = 1, db.numButtons do
if db.buttons[i].text ~= addon.defaults.profile.buttons[i].text then
changedButtons[i] = db.buttons[i]
end
end
-- Extract changed award reasons
local changedAwardReasons = {}
for i = 1, db.numAwardReasons do
if db.awardReasons[i].text ~= addon.defaults.profile.awardReasons[i].text then
changedAwardReasons[i] = db.awardReasons[i]
end
end
return {
selfVote = db.selfVote,
multiVote = db.multiVote,
anonymousVoting = db.anonymousVoting,
allowNotes = db.allowNotes,
numButtons = db.numButtons,
hideVotes = db.hideVotes,
observe = db.observe,
awardReasons = changedAwardReasons,
buttons = changedButtons,
responses = changedResponses,
}
end
function RCLootCouncilML:NewML(newML)
addon:DebugLog("ML:NewML", newML)
if addon:UnitIsUnit(newML, "player") then -- we are the the ML
addon:SendCommand("group", "playerInfoRequest")
self:UpdateMLdb() -- Will build and send mldb
self:UpdateGroup(true)
self.council = addon:GetCouncilInGroup()
addon:SendCommand("group", "council", self.council)
-- Set a timer to send out the incoming playerInfo changes
self:ScheduleTimer("Timer", 10, "GroupUpdate")
else
self:Disable() -- We don't want to use this if we're not the ML
end
end
function RCLootCouncilML:Timer(type, ...)
if type == "AddItem" then
self:AddItem(...)
elseif type == "LootSend" then
addon:SendCommand("group", "offline_timer")
elseif type == "GroupUpdate" then
addon:SendCommand("group", "candidates", self.candidates)
end
end
function RCLootCouncilML:OnCommReceived(prefix, serializedMsg, distri, sender)
if prefix == "RCLootCouncil" then
-- data is always a table
local decoded = Deflate:DecodeForPrint(serializedMsg)
if not decoded then
return -- probably an old version or somehow a bad message idk just throw this away
end
local decompressed = Deflate:DecompressDeflate(decoded)
local test, command, data = addon:Deserialize(decompressed)
if addon:HandleXRealmComms(self, command, data, sender) then return end
addon:DebugLog("MLComm received:", command, "from:", sender, "distri:", distri)
if not addon.isMasterLooter then
addon.isMasterLooter, addon.masterLooter = addon:GetML()
end
if test and addon.isMasterLooter then -- only ML receives these commands
if command == "playerInfo" then
self:AddCandidate(unpack(data))
elseif command == "MLdb_request" then
addon:SendCommand(sender, "MLdb", addon.mldb)
elseif command == "council_request" then
self.council = addon:GetCouncilInGroup()
addon:SendCommand(sender, "council", self.council)
elseif command == "reconnect" and not addon:UnitIsUnit(sender, addon.playerName) then -- Don't receive our own reconnect
-- Someone asks for mldb, council and candidates
addon:SendCommand(sender, "MLdb", addon.mldb)
self.council = addon:GetCouncilInGroup()
addon:SendCommand(sender, "council", self.council)
--[[NOTE: For some reason this can silently fail, but adding a 1 sec timer on the rest of the calls seems to fix it
v2.0.1: With huge candidates/lootTable we get AceComm lostdatawarning "First", presumeably due to the 4kb ChatThrottleLib limit.
Bumping loottable to 4 secs is tested to work with 27 candidates + 10 items.]]
addon:ScheduleTimer("SendCommand", 1, sender, "candidates", self.candidates)
if self.running then -- Resend lootTable
addon:ScheduleTimer("SendCommand", 4, sender, "lootTable", self.lootTable)
end
addon:Debug("Responded to reconnect from", sender)
end
elseif addon.isMasterLooter then
addon:Debug("Error in deserializing ML comm: ", command)
end
end
end
function RCLootCouncilML:OnEvent(event, ...)
addon:DebugLog("ML event", event)
if event == "LOOT_OPENED" then -- IDEA Check if event LOOT_READY is useful here (also check GetLootInfo() for this)
self.lootOpen = true
if not InCombatLockdown() then
self:LootOpened()
else
addon:Print(L["You can't start a loot session while in combat."])
end
elseif event == "LOOT_CLOSED" then
self.lootOpen = false
elseif event == "CHAT_MSG_WHISPER" and addon.isMasterLooter and db.acceptWhispers then
local msg, sender = ...
if msg == "rchelp" then
self:SendWhisperHelp(sender)
elseif self.running then
self:GetItemsFromMessage(msg, sender)
end
end
end
function RCLootCouncilML:LootOpened()
if addon.isMasterLooter and GetNumLootItems() > 0 then
addon.target = GetUnitName("target") or L["Unknown/Chest"] -- capture the boss name
for i = 1, GetNumLootItems() do
-- We have reopened the loot frame, so check if we should update .lootSlot
if self.running then
local item = GetLootSlotLink(i)
for session = 1, #self.lootTable do
if item == self.lootTable[session].link then
if i ~= self.lootTable[session].lootSlot then -- It has changed!
self.lootTable[session].lootSlot = i -- and update it
end
-- Lets see if we have more of the same item in the rest of the lootTable
for ses = session, #self.lootTable do
if item == self.lootTable[ses].link then
-- We have! Lets give this one the current slot, as the first will be updated once we reach it in the main loops
self.lootTable[ses].lootSlot = i
break
end
end
break
end
end
else
if db.altClickLooting then self:ScheduleTimer("HookLootButton", 0.5, i) end -- Delay lootbutton hooking to ensure other addons have had time to build their frames
local _, _, quantity, quality = GetLootSlotInfo(i)
local item = GetLootSlotLink(i)
if self:ShouldAutoAward(item, quality) and quantity > 0 then
self:AutoAward(i, item, quality, db.autoAwardTo, db.autoAwardReason, addon.target)
elseif self:CanWeLootItem(item, quality) and quantity > 0 then -- check if our options allows us to loot it
self:AddItem(item, false, i)
elseif quantity == 0 then -- it's coin, just loot it
LootSlot(i)
end
end
end
if #self.lootTable > 0 and not self.running then
if db.autoStart then -- Settings say go
self:StartSession()
else
addon:CallModule("sessionframe")
addon:GetActiveModule("sessionframe"):Show(self.lootTable)
end
end
end
end
function RCLootCouncilML:CanWeLootItem(item, quality)
local ret = false
if db.autoLoot and (IsEquippableItem(item) or db.autolootEverything) and quality >= GetLootThreshold() and not self:IsItemIgnored(item) then -- it's something we're allowed to loot
-- Let's check if it's BoE
-- Don't bother checking if we know we want to loot it
ret = db.autolootBoE or not addon:IsItemBoE(item)
end
addon:Debug("CanWeLootItem", item, ret)
return ret
end
function RCLootCouncilML:HookLootButton(i)
local lootButton = getglobal("LootButton"..i)
if XLoot then -- hook XLoot
lootButton = getglobal("XLootButton"..i)
end
if XLootFrame then -- if XLoot 1.0
lootButton = getglobal("XLootFrameButton"..i)
end
if getglobal("ElvLootSlot"..i) then -- if ElvUI
lootButton = getglobal("ElvLootSlot"..i)
end
local hooked = self:IsHooked(lootButton, "OnClick")
if lootButton and not hooked then
addon:DebugLog("ML:HookLootButton", i)
self:HookScript(lootButton, "OnClick", "LootOnClick")
end
end
function RCLootCouncilML:LootOnClick(button)
if not IsAltKeyDown() or not db.altClickLooting or IsShiftKeyDown() or IsControlKeyDown() then return; end
addon:DebugLog("LootAltClick()", button)
if getglobal("ElvLootFrame") then
button.slot = button:GetID() -- ElvUI hack
end
-- Check we're not already looting that item
for ses, v in ipairs(self.lootTable) do
if button.slot == v.lootSlot then
addon:Print(L["The loot is already on the list"])
return
end
end
self:AddItem(GetLootSlotLink(button.slot), false, button.slot)
addon:CallModule("sessionframe")
addon:GetActiveModule("sessionframe"):Show(self.lootTable)
end
--@param session The session to award
--@param winner Nil/false if items should be stored in inventory and awarded later
--@param response The candidates response, index in db.responses
--@param reason Entry in db.awardReasons
--@returns True if awarded successfully
function RCLootCouncilML:Award(session, winner, response, reason)
addon:DebugLog("ML:Award", session, winner, response, reason)
if addon.testMode then
if winner then
addon:SendCommand("group", "awarded", session)
addon:Print(format(L["The item would now be awarded to 'player'"], winner))
self.lootTable[session].awarded = true
if self:HasAllItemsBeenAwarded() then
addon:Print(L["All items have been awarded and the loot session concluded"])
self:EndSession()
end
end
return true
end
if not self.lootTable[session].lootSlot and not self.lootTable[session].bagged then
addon:SessionError("Session "..session.." didn't have lootSlot")
return false
end
-- Determine if we should award the item now or just store it in our bags
if winner then
local awarded = false
-- give out the loot or store the result, i.e. bagged or not
if self.lootTable[session].bagged then -- indirect mode (the item is in a bag)
-- Add to the list of awarded items in MLs bags, and remove it from lootInBags
tinsert(self.awardedInBags, {link = self.lootTable[session].link, winner = winner})
tremove(self.lootInBags, session)
awarded = true
else -- Direct (we can award from a WoW loot list)
if not self.lootOpen then -- we can't give out loot without the loot window open
addon:Print(L["Unable to give out loot without the loot window open."])
--addon:Print(L["Alternatively, flag the loot as award later."])
return false
end
if self.lootTable[session].quality < GetLootThreshold() then
LootSlot(self.lootTable[session].lootSlot)
if not addon:UnitIsUnit(winner, "player") then
addon:Print(format(L["Cannot give 'item' to 'player' due to Blizzard limitations. Gave it to you for distribution."], self.lootTable[session].link, winner))
tinsert(self.awardedInBags, {link = self.lootTable[session].link, winner = winner})
end
awarded = true
else
for i = 1, MAX_RAID_MEMBERS do
if addon:UnitIsUnit(GetMasterLootCandidate(i), winner) then
addon:Debug("GiveMasterLoot", i)
GiveMasterLoot(self.lootTable[session].lootSlot, i)
awarded = true
break
end
end
end
end
if awarded then
-- flag the item as awarded and update
addon:SendCommand("group", "awarded", session)
self.lootTable[session].awarded = true -- No need to let Comms handle this
-- IDEA Switch session ?
self:AnnounceAward(winner, self.lootTable[session].link, reason and reason.text or db.responses[response].text)
if self:HasAllItemsBeenAwarded() then self:EndSession() end
else -- If we reach here it means we couldn't find a valid MasterLootCandidate, propably due to the winner is unable to receive the loot
addon:Print(format(L["Unable to give 'item' to 'player' - (player offline, left group or instance?)"], self.lootTable[session].link, winner))
end
return awarded
else -- Store in bags and award later
if not self.lootOpen then return addon:Print(L["Unable to give out loot without the loot window open."]) end
if self.lootTable[session].quality < GetLootThreshold() then
LootSlot(self.lootTable[session].lootSlot)
else
for i = 1, MAX_RAID_MEMBERS do
if addon:UnitIsUnit(GetMasterLootCandidate(i), "player") then
GiveMasterLoot(self.lootTable[session].lootSlot, i)
break
end
end
end
tinsert(self.lootInBags, self.lootTable[session].link) -- and store data
return false -- Item hasn't been awarded
end
return false
end
function RCLootCouncilML:AnnounceItems()
if not db.announceItems then return end
addon:DebugLog("ML:AnnounceItems()")
SendChatMessage(db.announceText, addon:GetAnnounceChannel(db.announceChannel))
for k,v in ipairs(self.lootTable) do
SendChatMessage(k .. ": " .. v.link, addon:GetAnnounceChannel(db.announceChannel))
end
end
function RCLootCouncilML:AnnounceAward(name, link, text)
if db.announceAward then
for k,v in pairs(db.awardText) do
if v.channel ~= "NONE" then
local message = gsub(v.text, "&p", name)
message = gsub(message, "&i", link)
message = gsub(message, "&r", text)
SendChatMessage(message, addon:GetAnnounceChannel(v.channel))
end
end
end
end
function RCLootCouncilML:ShouldAutoAward(item, quality)
if db.autoAward and quality >= db.autoAwardLowerThreshold and quality <= db.autoAwardUpperThreshold then
if db.autoAwardLowerThreshold >= GetLootThreshold() or db.autoAwardLowerThreshold < 2 then
if UnitInRaid(db.autoAwardTo) or UnitInParty(db.autoAwardTo) then -- TEST perhaps use self.group?
return true;
else
addon:Print(L["Cannot autoaward:"])
addon:Print(format(L["Could not find 'player' in the group."], db.autoAwardTo))
end
else
addon:Print(format(L["Could not Auto Award i because the Loot Threshold is too high!"], item))
end
end
return false
end
function RCLootCouncilML:AutoAward(lootIndex, item, quality, name, reason, boss)
addon:DebugLog("ML:AutoAward", lootIndex, item, quality, name, reason, boss)
local awarded = false
if db.autoAwardLowerThreshold < 2 and quality < 2 then
if addon:UnitIsUnit("player",name) then -- give it to the player
LootSlot(lootIndex)
awarded = true
else
addon:Print(L["Cannot autoaward:"])
addon:Print(format(L["You can only auto award items with a quality lower than 'quality' to yourself due to Blizaard restrictions"],"|cff1eff00"..getglobal("ITEM_QUALITY2_DESC").."|r"))
return false
end
else
for i = 1, MAX_RAID_MEMBERS do
if addon:UnitIsUnit(GetMasterLootCandidate(i), name) then
GiveMasterLoot(lootIndex,i)
awarded = true
break
end
end
end
if awarded then
addon:Print(format(L["Auto awarded 'item'"], item))
self:AnnounceAward(name, item, db.awardReasons[reason].text)
self:TrackAndLogLoot(name, item, reason, boss, 0, nil, nil, db.awardReasons[reason])
else
addon:Print(L["Cannot autoaward:"])
addon:Print(format(L["Unable to give 'item' to 'player' - (player offline, left group or instance?)"], item, name))
end
return awarded
end
local history_table = {}
function RCLootCouncilML:TrackAndLogLoot(name, item, response, boss, votes, itemReplaced1, itemReplaced2, reason)
if reason and not reason.log then return end -- Reason says don't log
if not (db.sendHistory or db.enableHistory) then return end -- No reason to do stuff when we won't use it
if addon.testMode and not addon.nnp then return end -- We shouldn't track testing awards.
local instanceName, _, _, difficultyName = GetInstanceInfo()
addon:Debug("ML:TrackAndLogLoot()")
history_table["lootWon"] = item
history_table["date"] = date("%d/%m/%y")
history_table["time"] = date("%H:%M:%S")
history_table["instance"] = instanceName.."-"..difficultyName
history_table["boss"] = boss
history_table["votes"] = votes
history_table["itemReplaced1"]= itemReplaced1
history_table["itemReplaced2"]= itemReplaced2
history_table["response"] = reason and reason.text or db.responses[response].text
history_table["responseID"] = response or reason.sort - 400 -- Changed in v2.0 (reason responseID was 0 pre v2.0)
history_table["color"] = reason and reason.color or db.responses[response].color -- New in v2.0
history_table["class"] = self.candidates[name].class -- New in v2.0
history_table["isAwardReason"] = reason and true or false -- New in v2.0
if db.sendHistory then -- Send it, and let comms handle the logging
addon:SendCommand("group", "history", name, history_table)
elseif db.enableHistory then -- Just log it
addon:SendCommand("player", "history", name, history_table)
end
end
function RCLootCouncilML:HasAllItemsBeenAwarded()
local moreItems = true
for i = 1, #self.lootTable do
if not self.lootTable[i].awarded then
moreItems = false
end
end
return moreItems
end
function RCLootCouncilML:EndSession()
addon:DebugLog("ML:EndSession()")
self.lootTable = {}
addon:SendCommand("group", "session_end")
self.running = false
self:CancelAllTimers()
if addon.testMode then -- We need to undo our ML status
addon.testMode = false
addon:ScheduleTimer("NewMLCheck", 1) -- Delay it a bit
end
addon.testMode = false
end
-- Initiates a session with the items handed
function RCLootCouncilML:Test(items)
-- check if we're added in self.group
-- (We might not be on solo test)
if not tContains(self.candidates, addon.playerName) then
self:AddCandidate(addon.playerName, addon.playerClass, addon:GetPlayerRole(), addon.guildRank)
end
-- We must send candidates now, since we can't wait the normal 10 secs
addon:SendCommand("group", "candidates", self.candidates)
-- Add the items
for session, iName in ipairs(items) do
self:AddItem(iName, false, false)
end
if db.autoStart then
addon:Print(L["Autostart isn't supported when testing"])
end
addon:CallModule("sessionframe")
addon:GetActiveModule("sessionframe"):Show(self.lootTable)
end
-- Returns true if we are ignoring the item
function RCLootCouncilML:IsItemIgnored(link)
local itemID = addon:GetItemIDFromLink(link) -- extract itemID
return tContains(db.ignore, itemID)
end
function RCLootCouncilML:GetItemsFromMessage(msg, sender)
addon:Debug("GetItemsFromMessage()", msg, sender)
if not addon.isMasterLooter then return end
local ses, arg1, arg2, arg3 = addon:GetArgs(msg, 4) -- We only require session to be correct, we can do some error checking on the rest
ses = tonumber(ses)
-- Let's test the input
if not ses or type(ses) ~= "number" or ses > #self.lootTable then return end -- We need a valid session
-- Set some locals
local item1, item2
local response = 1
if arg1:find("|Hitem:") then -- they didn't give a response
item1, item2 = arg1, arg2
else
-- No reason to continue if they didn't provide an item
if not arg2 or not arg2:find("|Hitem:") then return end
item1, item2 = arg2, arg3
-- check if the response is valid
local whisperKeys = {}
for i = 1, db.numButtons do --go through all the button
gsub(db.buttons[i]["whisperKey"], '[%w]+', function(x) tinsert(whisperKeys, {key = x, num = i}) end) -- extract the whisperKeys to a table
end
for _,v in ipairs(whisperKeys) do
if strmatch(arg1, v.key) then -- if we found a match
response = v.num
break;
end
end
end
local diff = 0
if item1 then diff = (self.lootTable[ses].ilvl - select(4, GetItemInfo(item1))) end
local toSend = {
gear1 = item1,
gear2 = item2,
ilvl = nil,
diff = diff,
note = nil,
response = response
}
addon:SendCommand("group", "response", ses, sender, toSend)
-- Let people know we've done stuff
addon:Print(format(L["Item received and added from 'player'"], sender))
SendChatMessage("[RCLootCouncil]: "..format(L["Acknowledged as 'response'"], db.responses[response].text ), "WHISPER", nil, sender)
end
function RCLootCouncilML:SendWhisperHelp(target)
addon:DebugLog("SendWhisperHelp", target)
local msg
SendChatMessage(L["whisper_guide"], "WHISPER", nil, target)
for i = 1, db.numButtons do
msg = "[RCLootCouncil]: "..db.buttons[i]["text"]..": " -- i.e. MainSpec/Need:
msg = msg..""..db.buttons[i]["whisperKey"].."." -- need, mainspec, etc
SendChatMessage(msg, "WHISPER", nil, target)
end
SendChatMessage(L["whisper_guide2"], "WHISPER", nil, target)
addon:Print(format(L["Sent whisper help to 'player'"], target))
end
--------ML Popups ------------------
LibDialog:Register("RCLOOTCOUNCIL_CONFIRM_ABORT", {
text = L["Are you sure you want to abort?"],
buttons = {
{ text = L["Yes"],
on_click = function(self)
addon:DebugLog("ML aborted session")
RCLootCouncilML:EndSession()
CloseLoot() -- close the lootlist
addon:GetActiveModule("votingframe"):EndSession(true)
end,
},
{ text = L["No"],
},
},
hide_on_escape = true,
show_while_dead = true,
})
LibDialog:Register("RCLOOTCOUNCIL_CONFIRM_AWARD", {
text = "something_went_wrong",
icon = "",
on_show = function(self, data)
local session, player = unpack(data)
self.text:SetText(format(L["Are you sure you want to give #item to #player?"], RCLootCouncilML.lootTable[session].link, player))
self.icon:SetTexture(RCLootCouncilML.lootTable[session].texture)
end,
buttons = {
{ text = L["Yes"],
on_click = function(self, data)
-- IDEA Perhaps come up with a better way of handling this
local session, player, response, reason, votes, item1, item2 = unpack(data,1,7)
local item = RCLootCouncilML.lootTable[session].link -- Store it now as we wipe lootTable after Award()
local awarded = RCLootCouncilML:Award(session, player, response, reason)
if awarded then -- log it
RCLootCouncilML:TrackAndLogLoot(player, item, response, addon.target, votes, item1, item2, reason)
end
-- We need to delay the test mode disabling so comms have a chance to be send first!
if addon.testMode and RCLootCouncilML:HasAllItemsBeenAwarded() then RCLootCouncilML:EndSession() end
end,
},
{ text = L["No"],
},
},
hide_on_escape = true,
show_while_dead = true,
})