Share Pastebin
Guest
Public paste!

bwh

By: a guest | May 5th, 2009 | Syntax: Lua | Size: 20.14 KB | Hits: 110 | Expires: Never
Copy text to clipboard
  1. local L = AceLibrary("AceLocale-2.2"):new("XLootMaster")
  2.  
  3. XLootMaster = XLoot:NewModule("XLootMaster")
  4.  
  5. XLootMaster.dewdrop = AceLibrary("Dewdrop-2.0")
  6.  
  7. local deformat = AceLibrary("Deformat-2.0")
  8.  
  9. XLootMaster.revision  = tonumber((string.gsub("$Revision: 70 $", "^%$Revision: (%d+) %$$", "%1")))
  10.  
  11. local nilTable, tokenizestring = XLoot.nilTable, XLoot.tokenizestring
  12.  
  13. ----- Module setup -----
  14. function XLootMaster:OnInitialize()
  15.         self.db = XLoot:AcquireDBNamespace("XLootMasterDB")
  16.         self.defaults = {
  17.                 mlthreshold = 3,
  18.                 mldkpthreshold = 2,
  19.                 mldkp = false,
  20.                 mlitemtip = true,
  21.                 mlplayertip = true,
  22.                 mlrandom = true,
  23.                 mlrolls = false,
  24.                 mlallrolls = true,
  25.                 rollrange = 100,
  26.                 rolltimeout = 30,
  27.                 rollmsg = "Attention! /roll [range] for [item]. Ends in [time] seconds.",
  28.                 announcemsg = "[name] awarded [item][method]",
  29.                 announcenum = 5,
  30.                 announceself = true,
  31.                 announce = { group = 2, guild = 3 , rw = 2 },
  32.         }
  33.         XLoot:RegisterDefaults("XLootMasterDB", "profile", self.defaults)
  34.         self.playerlist = {}
  35.         self.prioritylist = {}
  36.         self.rolls = {}
  37.        
  38.         self:DoOptions()
  39. end
  40.  
  41. function XLootMaster:OnEnable()
  42. XLoot:msg("Master: OnEnable()")
  43. -- self:Hook("LootFrame_OnEvent","OnEvent", true)
  44. self:RollHook()
  45. self:RegisterEvent("PARTY_MEMBERS_CHANGED");
  46. self:RegisterEvent("OPEN_MASTER_LOOT_LIST");
  47. self:RegisterEvent("UPDATE_MASTER_LOOT_LIST");
  48. end
  49.  
  50. function XLootMaster:OnDisable()
  51. self:UnregisterEvent("PARTY_MEMBERS_CHANGED");
  52. self:UnregisterEvent("OPEN_MASTER_LOOT_LIST");
  53. self:UnregisterEvent("UPDATE_MASTER_LOOT_LIST");
  54. end
  55.  
  56. function XLootMaster:OPEN_MASTER_LOOT_LIST()
  57. XLoot:msg("Master: OPEN_MASTER_LOOT_LIST()")
  58. return self:ShowMenu()
  59. end
  60.  
  61. function XLootMaster:UPDATE_MASTER_LOOT_LIST()
  62. XLoot:msg("Master: UPDATE_MASTER_LOOT_LIST()")
  63. return self.dewdrop:Refresh(1)
  64. end
  65.  
  66. function XLootMaster:RollHook()
  67.         if self.db.profile.mlrolls and not self:IsEventRegistered("CHAT_MSG_SYSTEM") then
  68.                 return self:RegisterEvent("CHAT_MSG_SYSTEM", "ChatHandler")
  69.         elseif not self.db.profile.mlrolls and self:IsEventRegistered("CHAT_MSG_SYSTEM") then
  70.                 return self:UnregisterEvent("CHAT_MSG_SYSTEM")
  71.         end
  72. end
  73.  
  74. function XLootMaster:ChatHandler(message)
  75.         if not self.rollactive then return end
  76.         local playername, roll, min, max = deformat(message, RANDOM_ROLL_RESULT)
  77.         if playername then
  78.                 local key = min.."-"..max
  79.                 if not self.rolls[key] then
  80.                         self.rolls[key] = {}
  81.                 end
  82.                 if not self.rolls[key][playername] then
  83.                         self.rolls[key][playername] = { roll = roll }
  84.                 end
  85.         end
  86. end
  87.  
  88. function XLootMaster:StartRoll()
  89.         nilTable(self.rolls)
  90.         self.rollactive = true
  91.         self.rollstamp = time()
  92.         if self.rollfinishevent then self:CancelScheduledEvent(self.rollfinishevent) end
  93.         if not self.rollupdateevent then
  94.                 self.rollupdateevent = "XLootMaster-" .. math.random()
  95.                 self:ScheduleRepeatingEvent(self.rollupdateevent, function() self.dewdrop:Refresh(2) end, 1)
  96.         end
  97.         self.rollfinishevent = "XLootMaster-" .. math.random()
  98.         self:ScheduleEvent(self.rollfinishevent, function()
  99.                                                                                                 self.rollactive = false
  100.                                                                                                 self.rollfinishevent = nil
  101.                                                                                                 if self.rollupdateevent then
  102.                                                                                                         self:CancelScheduledEvent(self.rollupdateevent)
  103.                                                                                                         self.rollupdateevent = nil
  104.                                                                                                 end
  105.                                                                                         end, self.db.profile.rolltimeout)
  106. end
  107.  
  108. function XLootMaster:ClearRolls()
  109.         nilTable(self.rolls)
  110.         if self.rollfinishevent then self:CancelScheduledEvent(self.rollfinishevent) end
  111.         if self.rollupdateevent then self:CancelScheduledEvent(self.rollupdateevent) end
  112. end
  113.  
  114. ----- Menu exhibition -----
  115. function XLootMaster:ShowMenu()
  116.         self.dewdrop:Open(UIParent,
  117.                 'children', function(level, value)
  118.                                 if GetNumRaidMembers() > 0 then
  119.                                         self:BuildRaidMenu(level, value)
  120.                                 else
  121.                                         self:BuildPartyMenu(level, value)
  122.                                 end
  123.                         end,
  124.                 'cursorX', true,
  125.                 'cursorY', true
  126.         )
  127. end
  128.  
  129. function XLootMaster:ShowPriorityMenu()
  130.         self.dewdrop:Open(UIParent,
  131.                 'children', function(level, value)
  132.                                 self:BuildPriorityMenu(level, value)
  133.                         end,
  134.                 'cursorX', true,
  135.                 'cursorY', true
  136.         )
  137. end
  138.  
  139. ----- Menu constructors -----
  140. function XLootMaster:BuildPartyMenu(level, value)
  141.         if level == 1 then
  142.                 self:InjectLootLine()
  143.                 self.dewdrop:AddLine()
  144.                 self:BuildPlayerList()
  145.                 for k, v in iteratetable(self.playerlist) do
  146.                         for k2, v2 in iteratetable(v) do
  147.                                 if k2 ~= "classname" and k2 ~= "class" then
  148.                                         self:InjectPlayer(k2, v2)
  149.                                 end                            
  150.                         end
  151.                 end
  152.         end
  153.         self:InjectRandomMenu(level, value)
  154.         self:InjectCustom(level, value)
  155.         self:InjectFooter(level, value)
  156. end
  157.  
  158. function XLootMaster:BuildRaidMenu(level, value)
  159.         if level == 1 then
  160.                 self:InjectLootLine()
  161.                 self:BuildPlayerList()
  162.                 self:InjectPriorityList()
  163.                 self:InjectClasses()
  164.                 local ownname = UnitName("player")
  165.                 self.dewdrop:AddLine(
  166.                         'text', "|cFFBBBBBB"..L["Self loot"],
  167.                         'icon', "Interface\\GossipFrame\\VendorGossipIcon",
  168.                         'iconWidth', 20,
  169.                         'iconHeight', 20,
  170.                         'closeWhenClicked', true,
  171.                         'func', function() self:GiveLoot(ownname, self:GetMLID(ownname), ownname) end)
  172.         elseif level == 2 then
  173.                 if self.playerlist[value] then
  174.                         for k, v in iteratetable(self.playerlist[value]) do
  175.                                 if k ~= "classname" and k ~= "class" then
  176.                                         self:InjectPlayer(k, v)
  177.                                 end
  178.                         end
  179.                 end
  180.         end
  181.         self:InjectRandomMenu(level, value)
  182.         self:InjectCustom(level, value)
  183.         self:InjectFooter(level, value)
  184. end
  185.  
  186. function XLootMaster:BuildPriorityMenu(level, value)
  187.         if level == 1 then
  188.                 self.dewdrop:AddLine(
  189.                         'text', "|cFF44FF44"..L["Priority Looters"],
  190.                         'icon', "Interface\\TargetingFrame\\UI-PVP-FFA",
  191.                         'iconWidth', 84,
  192.                         'iconHeight', 84,
  193.                         'isTitle', true)
  194.                 self:BuildPlayerList()
  195.                 if self.prioritylist then
  196.                         for k, v in iteratetable(self.prioritylist, "key") do
  197.                                         self.dewdrop:AddLine(
  198.                                                 'text', string.format("|cFF%s%s|r", XLoot:ClassHex(v.class), v.name),
  199.                                                 'hasArrow', true,
  200.                                                 'value', v.name)
  201.                                 end
  202.                 else
  203.                         self.dewdrop:AddLine(
  204.                                         'text', "No priority players",
  205.                                         'isTitle', true)
  206.                 end
  207.                 self.dewdrop:AddLine()
  208.                 self.dewdrop:AddLine(
  209.                         'text', "|cFF44FF44"..L["Possible victims"],
  210.                         'icon', "Interface\\TargetingFrame\\UI-TargetingFrame-Skull",
  211.                         'iconWidth', 24,
  212.                         'iconHeight', 24,
  213.                         'isTitle', true)
  214.                 self:InjectClasses()
  215.         elseif level == 2 then
  216.                 if self.prioritylist[value] then
  217.                         local player = self.prioritylist[value]
  218.                         self.dewdrop:AddLine(
  219.                                 'text', L["Move up"],
  220.                                 'disabled', player.key == 1,
  221.                                 'icon', "Interface\\MainMenuBar\\UI-MainMenu-ScrollUpButton-Down",
  222.                                 'iconWidth', 24,
  223.                                 'iconHeight', 24,
  224.                                 'arg1', value,
  225.                                 'func', function(val) self:PriorityShift(val, -1) end)
  226.                         self.dewdrop:AddLine(
  227.                                 'text', L["Move Down"],
  228.                                 'disabled', player.key == self.prioritykeys,
  229.                                 'icon', "Interface\\MainMenuBar\\UI-MainMenu-ScrollDownButton-Down",
  230.                                 'iconWidth', 24,
  231.                                 'iconHeight', 24,
  232.                                 'arg1', value,
  233.                                 'func', function(val) self:PriorityShift(val, 1) end)
  234.                         self.dewdrop:AddLine(
  235.                                 'text', "|cFFFF3311"..L["Remove"],
  236.                                 'icon', "Interface\\Glues\\Login\\Glues-CheckBox-Check",
  237.                                 'arg1', value,
  238.                                 'func', function(val) self:PriorityRemove(val) end)
  239.                                
  240.                 elseif self.playerlist[value] then
  241.         local tip = self.db.profile.mlplayertip
  242.                         for k, v in iteratetable(self.playerlist[value]) do
  243.                                 if k ~= "classname" and k ~= "class" then
  244.                                         self.dewdrop:AddLine(
  245.                                                 'text', string.format("|cFF%s%s|r", XLoot:ClassHex(v.class), k),
  246.                                                 'checked', self.prioritylist[k] and true or false,
  247.                                                 'arg1', v,
  248.                                                 'func', function(player) if self.prioritylist[player.name] then self:PriorityRemove(player.name) else self:PriorityAdd(player) end end)
  249.                                 end
  250.                         end
  251.                 end
  252.         end
  253.         self:InjectFooter(level, value)        
  254. end
  255.  
  256.  
  257. ----- Priority management -----
  258. function XLootMaster:PriorityAdd(player)
  259.         self.prioritylist[player.name] = player
  260.         if not self.prioritykeys then
  261.                 self.prioritykeys = 1
  262.         else self.prioritykeys = self.prioritykeys + 1 end
  263.         self.prioritylist[player.name].key = self.prioritykeys
  264. end
  265.  
  266. function XLootMaster:PriorityRemove(name)
  267.         local tempkey = self.prioritylist[name].key
  268.         nilTable(self.prioritylist[name])
  269.         self.prioritylist[name] = nil
  270.         self.prioritykeys = self.prioritykeys - 1
  271.         for k, v in pairs(self.prioritylist) do
  272.                 if v.key > tempkey then
  273.                         v.key = v.key-1
  274.                 end
  275.         end
  276.         if table.getn(self.prioritylist) == 0 then
  277.                 self.dewdrop:Close(2)
  278.         end
  279.         self.dewdrop:Refresh(1)
  280. end
  281.  
  282. function XLootMaster:PriorityShift(name, mod)
  283.         local key = self.prioritylist[name].key
  284.         for k, v in pairs(self.prioritylist) do
  285.                 if v.key == key + mod then
  286.                         v.key = key
  287.                         break
  288.                 end
  289.         end
  290.         self.prioritylist[name].key = key + mod
  291. end
  292.  
  293. ----- Menu components -----
  294. function XLootMaster:InjectPlayer(name, object)
  295.         local mlid = self:GetMLID(name)
  296.         local tip = self.db.profile.mlplayertip
  297.         local cname = name
  298.         if mlid then
  299.                 local name = string.format("|cFF%s%s|r", XLoot:ClassHex(object.class), name)
  300.                 self.dewdrop:AddLine(
  301.                         'text', name,
  302.                         'closeWhenClicked', true,
  303.                         'tooltipFunc', tip and GameTooltip.SetUnit or function() end,
  304.                         'tooltipArg1', tip and GameTooltip or nil,
  305.                         'tooltipArg2', tip and object.unit or nil,
  306.                         'func', function() self:GiveLoot(name, mlid, cname) end)
  307.         else
  308.                 self.dewdrop:AddLine(
  309.                         'text', name,
  310.                         'closeWhenClicked', true,
  311.                         'tooltipFunc', function() end,
  312.                         'disabled', true)
  313.         end
  314. end
  315.  
  316. function XLootMaster:InjectClasses()
  317.         for k, v in iteratetable(self.playerlist) do
  318.                 self.dewdrop:AddLine(
  319.                         'text', string.format("|cFF%s%s|r", XLoot:ClassHex(k), v.classname),
  320.                         'hasArrow', true,
  321.                         'value', k)
  322.         end
  323. end
  324.  
  325. function XLootMaster:InjectLootLine()
  326.         local icon, name, quantity, quality = GetLootSlotInfo(LootFrame.selectedSlot)
  327.         local tip = self.db.profile.mlitemtip
  328.         local link = GetLootSlotLink(LootFrame.selectedSlot)
  329.         if not link then return nil end
  330.         self.dewdrop:AddLine(
  331.                 'text', string.format("%s%s%s|r", tonumber(quantity) > 1 and tostring(quantity).."x" or "", ITEM_QUALITY_COLORS[quality].hex, name),
  332.                  'icon', icon,
  333.                  'iconWidth', 20,
  334.                  'iconHeight', 20,
  335.                  'tooltipFunc', tip and GameTooltip.SetHyperlink or function() end,
  336.                  'tooltipArg1', tip and GameTooltip or nil,
  337.                  'tooltipArg2', tip and link or nil)
  338. end
  339.  
  340. function XLootMaster:InjectRandomMenu(level, value)
  341.         local db = self.db.profile
  342.         if not db.mlrandom then return end
  343.         if level == 1 then
  344.                 self.dewdrop:AddLine()
  345.                 self.dewdrop:AddLine(
  346.                         'text', L["Random"],
  347.                         'icon', "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
  348.                         'iconWidth', 20,
  349.                         'iconHeight', 20,
  350.                         'hasArrow', true,
  351.                         'value', 'random')
  352.         elseif level == 2 and value == 'random' then
  353.                 self.dewdrop:AddLine(
  354.                         'text', L["Give to random player"],
  355.                         'icon', "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
  356.                         'iconWidth', 20,
  357.                         'iconHeight', 20,
  358.                         'closeWhenClicked', true,
  359.                         'func', function() self:GiveRandomLoot() end)
  360.                 if db.mlrolls then
  361.                         self.dewdrop:AddLine()
  362.                         self.dewdrop:AddLine(
  363.                                 'text', L["Clear list and announce new roll"],
  364.                                 'icon', 'Interface\\Buttons\\UI-GuildButton-MOTD-Up',
  365.                                 'iconWidth', 20,
  366.                                 'iconHeight', 20,
  367.                                 'func', function()
  368.                                                         self:StartRoll()
  369.                                                         local message = tokenizestring(db.rollmsg, { range = db.rollrange, item = GetLootSlotLink(LootFrame.selectedSlot), time = db.rolltimeout })
  370.                                                         self:SendMasterMessage(message)
  371.                                                         end)
  372.                         if self.rollactive then
  373.                                 local remaining = db.rolltimeout-(time()-self.rollstamp)
  374.                                 self.dewdrop:AddLine(
  375.                                         'text', remaining > 1 and string.format(L["|cFF2255FFListening... |cFF44FF44%s|cFF2255FF seconds left"], remaining) or L["|CFFBBBBBBRoll finished"],
  376.                                         'isTitle', true)
  377.                         end
  378.                         if next(self.rolls) then
  379.                                 for k, v in iteratetable(self.rolls, nil, true) do
  380.                                         local min, max = deformat(k, "%d-%d")
  381.                                         if db.mlallrolls or (min == 1 and max == db.rollrange) then
  382.                                                 self.dewdrop:AddLine()
  383.                                                 self.dewdrop:AddLine(
  384.                                                         'text', "|cFF77BBFF"..k,
  385.                                                         'icon', "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
  386.                                                         'func', function() self:AnnounceRolls(k) end)
  387.                                                 local uFPN = XLoot.unitFromPlayerName
  388.                                                 for k2, v2 in iteratetable(self.rolls[k], 'roll', true) do
  389.                                                         local rawname = k2 -- Disconnecting is bad.
  390.                                                         local unit = uFPN(k2)
  391.                                                         if UnitExists(unit) then
  392.                                                                 local name = string.format("|cff%s%s|r", XLoot:ClassHex(UnitClass(unit)), k2)
  393.                                                                 self.dewdrop:AddLine(
  394.                                                                         'text', string.format("%s - %s", v2.roll, name),
  395.                                                                         'closeWhenClicked', true,
  396.                                                                         'func', function() self:GiveLoot(name, self:GetMLID(k2), rawname); self:ClearRolls() end, true, true)
  397.                                                         end
  398.                                                 end
  399.                                         end
  400.                                 end
  401.                         end
  402.                 end
  403.         end
  404. end
  405.  
  406. function XLootMaster:InjectCustom(level, value)
  407.         -- Function to be hooked by other mods. Access the dewdrop with XLootMaster.dewdrop
  408. end
  409.  
  410. function XLootMaster:InjectFooter(level, value)
  411.         if level == 1 then
  412.                 self.dewdrop:AddLine()
  413.                 self.dewdrop:AddLine(
  414.                         'text', OPTIONS_MENU,
  415.                         'hasArrow', true,
  416.                         'value', 'options')
  417.                 self.dewdrop:AddLine(
  418.                         'text', "|cFFFF3311"..CANCEL,
  419.                         'icon', "Interface\\Glues\\Login\\Glues-CheckBox-Check",
  420.                         'closeWhenClicked', true)
  421.         elseif level == 2 then
  422.                 if value == "options" then
  423.                         self.dewdrop:AddLine(
  424.                                 'text', "|cFF44EE66"..L[">> Priority configuration"],
  425.                                 'icon', "Interface\\TargetingFrame\\UI-PVP-FFA",
  426.                                 'iconWidth', 64,
  427.                                 'iconHeight', 64,
  428.                                 'func', function() self:ShowPriorityMenu() end)
  429.                         self.dewdrop:AddLine()
  430.                         self.dewdrop:FeedAceOptionsTable( XLoot.opts.args.master, 1)
  431.                 end
  432.         elseif level == 3 then
  433.                 if XLoot.opts.args.master.args[value] then
  434.                         self.dewdrop:FeedAceOptionsTable(XLoot.opts.args.master.args[value], 2)
  435.                 end
  436.         end            
  437. end
  438.  
  439. function XLootMaster:InjectPriorityList()
  440.         if next(self.prioritylist) then
  441.                 for k, v in iteratetable(self.prioritylist, 'key') do
  442.                         self:InjectPlayer(k, v)
  443.                 end
  444.                 self.dewdrop:AddLine()
  445.         end
  446. end
  447.  
  448.  
  449. ----- Cache management -----
  450. function XLootMaster:BuildPlayerList()
  451.         if table.getn(self.playerlist) > 0 then for k, v in pairs(self.playerlist) do v = nil; self.playerlist[k] = nil end self.playerlist = { } end
  452.        
  453.         if GetNumRaidMembers() > 0 then
  454.                 for i = 1, GetNumRaidMembers() do
  455.                         self:PlayerIteration("raid"..i)
  456.                 end
  457.                
  458.         elseif GetNumPartyMembers() > 0 then
  459.                 for i = 1, GetNumPartyMembers() do
  460.                         self:PlayerIteration("party"..i)
  461.                 end
  462.                 self:PlayerIteration("player")
  463.                
  464.         else
  465.                 self:PlayerIteration("player")
  466.         end
  467. end
  468.  
  469. function XLootMaster:PlayerIteration(unit)
  470.         local classname, class = UnitClass(unit)
  471.         local name = UnitName(unit)
  472.         if not self.playerlist[class] then
  473.                 self.playerlist[class] = { class = class, classname = classname }
  474.         end
  475.         self.playerlist[class][name] = { name = name, class = class, unit = unit }
  476. end
  477.  
  478.  
  479. ----- Utility functions -----
  480. function XLootMaster:SendMasterMessage(message, skipRw)
  481.         if (IsRaidLeader() or IsRaidOfficer()) and not skipRw then
  482.                 SendChatMessage(message, "RAID_WARNING");
  483.         elseif GetNumRaidMembers() > 0 then
  484.                 SendChatMessage(message, "RAID")
  485.         else
  486.                 SendChatMessage(message, "PARTY")
  487.         end
  488. end
  489.  
  490. function XLootMaster:AnnounceRolls(bracket)
  491.         local output, key, i, max = { string.format("%s [%s]", GetLootSlotLink(LootFrame.selectedSlot), bracket) }, 1, 1, self.db.profile.announcenum
  492.         local shift, ctrl = IsShiftKeyDown(), IsControlKeyDown()
  493.         if ctrl then
  494.                 self:SendMasterMessage(output[1], true)
  495.         end
  496.         for k, v in iteratetable(self.rolls[bracket], 'roll', true) do
  497.                 if i < max or shift then
  498.                         local buffer = string.format("[%d] %s", v.roll, k)
  499.                         if ctrl then
  500.                                 self:SendMasterMessage(buffer, true)
  501.                         else
  502.                                 if strlen(output[key]..buffer) > 254 then
  503.                                         key = key + 1
  504.                                         output[key] = buffer
  505.                                 else
  506.                                         output[key] = output[key]..", "..buffer
  507.                                 end
  508.                         end
  509.                         i = i + 1
  510.                 end
  511.         end
  512.         if not ctrl then
  513.                 for k, v in ipairs(output) do
  514.                         self:SendMasterMessage(v, true)
  515.                 end
  516.         end
  517. end
  518.  
  519. -- Called whenever loot has been distributed, fed the data object.
  520. function XLootMaster:AnnounceDistribution(v)
  521.         local ann = self.db.profile.announce
  522.         local message = tokenizestring(self.db.profile.announcemsg, { name = v.name, item = v.link, method = v.method and " ("..v.method..")" or "" })
  523.         if not self.db.profile.announceself and v.name == UnitName("player") then return nil end
  524.         if ann.group ~= 1 and v.quality >= ann.group-2 then
  525.                 self:SendMasterMessage(message, true)
  526.         end
  527.         if ann.rw ~= 1 and v.quality >= ann.rw-2 and GetNumRaidMembers() > 0 then
  528.                 self:SendMasterMessage(message)
  529.         end
  530.         if ann.guild ~= 1 and v.quality >= ann.guild-2 then
  531.                 SendChatMessage(message, "GUILD")
  532.         end
  533. end
  534.  
  535. function XLootMaster:GiveLoot(name, id, plainname, method)
  536.         local link = GetLootSlotLink(LootFrame.selectedSlot)
  537.         local dialog
  538.         local data = { id = id, name = plainname or name, link = link, quality = LootFrame.selectedQuality, method = method }
  539.        
  540.         if LootFrame.selectedQuality >= self.db.profile.mldkpthreshold and self.db.profile.mldkp then
  541.                 dialog = StaticPopup_Show("CONFIRM_XLOOT_DKP_DISTRIBUTION", ITEM_QUALITY_COLORS[LootFrame.selectedQuality].hex..LootFrame.selectedItemName..FONT_COLOR_CODE_CLOSE, name)
  542.                 if dialog then
  543.                         dialog.data = data
  544.                 end
  545.                
  546.         elseif LootFrame.selectedQuality >= self.db.profile.mlthreshold then
  547.                 dialog = StaticPopup_Show("CONFIRM_XLOOT_DISTRIBUTION", ITEM_QUALITY_COLORS[LootFrame.selectedQuality].hex..LootFrame.selectedItemName..FONT_COLOR_CODE_CLOSE, name)
  548.                 if dialog then
  549.                         dialog.data = data
  550.                 end    
  551.                
  552.         else
  553.                 self:AnnounceDistribution(data)
  554.                 GiveMasterLoot(LootFrame.selectedSlot, id)
  555.         end
  556. end
  557.  
  558.  
  559. local LastWinners = {}
  560. local LastWinnersByName = {}
  561. local IsGrouped = false;
  562.  
  563. XLootMaster.LastWinnersByName = LastWinnersByName
  564.  
  565. function XLootMaster:PARTY_MEMBERS_CHANGED()
  566.         local grouped = GetNumRaidMembers() + GetNumPartyMembers() > 0;
  567.         if(grouped and not IsGrouped) then
  568.                 -- self:Print("Wiping winner cache");
  569.                 LastWinnersByName = {}          -- Wipe the "too lucky" cache when joining a new group so you're not ruining your OWN chances of getting loot. That would make XLoot an impopular addon :-)
  570.         end
  571.         IsGrouped = grouped;
  572. end
  573.  
  574.  
  575. function XLootMaster:GetRandomMLID()
  576.         -- Scroll away old recent winners
  577.         local max = GetNumRaidMembers();
  578.         if(max<1) then
  579.                 max = GetNumPartyMembers()+1;
  580.         end
  581.        
  582.         while(#LastWinners>max-1) do
  583.                 local x = (LastWinnersByName[LastWinners[1]] or 0) - 1;
  584.                 if(x<=0) then
  585.                         LastWinnersByName[LastWinners[1]] = nil;
  586.                 else
  587.                         LastWinnersByName[LastWinners[1]] = x;
  588.                 end
  589.                 table.remove(LastWinners, 1);
  590.         end
  591.        
  592.         -- Find how many people are actually eligible for loot. USUALLY == num raid members, but can be fewer as well as more. (Yes, more. Think "people leaving after killing")
  593.         while(GetMasterLootCandidate(max+1)) do
  594.                 max=max+1;
  595.         end
  596.         while(max>0 and not GetMasterLootCandidate(max)) do
  597.                 max=max-1;
  598.         end
  599.         assert(max>=1, "The WoW API reports zero candidates to receive this loot?!");
  600.        
  601.         -- Determine who gets this loot
  602.         local name, id;
  603.        
  604.         for tries=1,9 do        -- this "shouldn't" happen, but i really really hate "while true"s
  605.                 name=nil
  606.                 id = math.random(1, max)
  607.                 -- self:Print("Getting random 1--"..max..": "..id);
  608.                 name = GetMasterLootCandidate(id)
  609.                 if not name then
  610.                         assert(name, format("Couldn't GetMasterLootCandidate(%i) - chosen from between 1 and %i", id, max));
  611.                 end
  612.                
  613.                 local x = LastWinnersByName[name];
  614.                 if(not x) then
  615.                         break;  -- this one didn't win recently, so his turn
  616.                 end
  617.                
  618.                 x = x - 0.3;            -- 0.0=very fair (almost roundrobin but not quite)
  619.                                                                                 -- 1.0=very random (streaky)
  620.                                                                                 -- 0.3 is still perceived as random to an observer but avoids the worst of the streaks
  621.                
  622.                 if(x <= 0) then
  623.                         LastWinnersByName[name] = nil;
  624.                 else
  625.                         LastWinnersByName[name] = x;
  626.                 end
  627.         end
  628.        
  629.         -- Remember the winner
  630.         table.insert(LastWinners, name);
  631.         LastWinnersByName[name] = (LastWinnersByName[name] or 0) + 1;
  632.         return name, id
  633. end
  634.  
  635.  
  636. function XLootMaster:GiveRandomLoot()
  637.         local randplayer, randid = self:GetRandomMLID();
  638.         self:GiveLoot(randplayer, randid, nil, "Random")
  639. end
  640.  
  641.  
  642. function XLootMaster:GetMLID(name)
  643.         if GetNumRaidMembers() > 0 then
  644.                 for i = 1, 40 do
  645.                         if GetMasterLootCandidate(i) == name then
  646.                                 return i
  647.                         end
  648.                 end
  649.         elseif GetNumPartyMembers() > 0 then
  650.                 for i = 1, MAX_PARTY_MEMBERS+1 do
  651.                         if GetMasterLootCandidate(i) == name then
  652.                                 return i
  653.                         end
  654.                 end
  655.         end
  656.         return nil
  657. end