From 1967dd956e6e1f9845bccd8cc440fda589853f20 Mon Sep 17 00:00:00 2001 From: F16Gaming Date: Thu, 22 Mar 2012 21:13:34 +0100 Subject: [PATCH] Added LootManager, various other cleanups and fixes done. --- Command.lua | 2 +- CommandManager.lua | 163 +++++++++++++++++++++++++++++++++++----------------- LootManager.lua | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ PlayerManager.lua | 2 +- RollManager.lua | 28 +++++---- String.lua | 41 +++++++++++-- Table.lua | 21 +++++++ load.xml | 1 + 8 files changed, 348 insertions(+), 68 deletions(-) create mode 100644 LootManager.lua diff --git a/Command.lua b/Command.lua index 23ffe50..2481902 100644 --- a/Command.lua +++ b/Command.lua @@ -32,7 +32,7 @@ Command = { Name = "Command", Version = GetAddOnMetadata("Command", "Version"), - VersionNum = 4, -- Increment on every release + VersionNum = 5, -- Increment on every release VersionChecked = false, -- Prevent spam of "New Version" notice Loaded = false, VarVersion = 2, diff --git a/CommandManager.lua b/CommandManager.lua index 092b853..948b54e 100644 --- a/CommandManager.lua +++ b/CommandManager.lua @@ -38,6 +38,7 @@ local CM = C.CommandManager local PM = C.PlayerManager local QM = C.QueueManager local RM = C.RollManager +local LM = C.LootManager local GT = C.GroupTools local CES = C.Extensions.String local CET = C.Extensions.Table @@ -77,6 +78,7 @@ end -- @param command Command name to check. -- function CM:HasCommand(command) + command = command:lower() if self.Commands[command] then return true end for _,v in pairs(self.Commands) do if CET:HasValue(v.Alias, command) then return true end @@ -89,6 +91,7 @@ end -- @return Callback for the command, nil if no command was found. -- function CM:GetCommand(command) + command = command:lower() if self.Commands[command] then return self.Commands[command] end @@ -99,6 +102,7 @@ function CM:GetCommand(command) end function CM:GetRealName(name) + name = name:lower() if self.Commands[name] then return name end for k,v in pairs(self.Commands) do if CET:HasValue(v.Alias, name) then return k end @@ -130,6 +134,7 @@ end -- @return Error message if not successful, otherwise nil. -- function CM:HandleCommand(command, args, isChat, player) + command = command:lower() local cmd = self:GetCommand(command) if cmd then if isChat then @@ -154,6 +159,9 @@ function CM:AutoHelp() end CM:Register({"__DEFAULT__", "help", "h"}, PM.Access.Local, function(args, sender, isChat) + if isChat then + return "Type !commands for a listing of commands available." + end CM:AutoHelp() return "End of help message." end, "Prints this help message.") @@ -164,11 +172,7 @@ CM:Register({"commands", "cmds", "cmdlist", "listcmds", "listcommands", "command all = args[1] == "all" end local cmds = CM:GetCommands(all) - local msg = "" - for _,v in pairs(cmds) do - msg = msg .. v .. ", " - end - return CES:Cut(msg, 240) + return CES:Fit(cmds, 240, ", ") -- Max length is 255, "[Command] " takes up 10. This leaves us with 5 characters grace. end, "Print all registered commands.") CM:Register({"version", "ver", "v"}, PM.Access.Groups.User.Level, function(args, sender, isChat) @@ -176,35 +180,41 @@ CM:Register({"version", "ver", "v"}, PM.Access.Groups.User.Level, function(args, end, "Print the version of Command") CM:Register({"lock", "lockdown"}, PM.Access.Groups.Admin.Level, function(args, sender, isChat) - if #args <= 0 then - return false, "Too few arguments. Usage: lock " + if type(args[1]) == "string" then + return PM:SetLocked(PM:GetOrCreatePlayer(args[1]), true) + else + return PM:SetLocked(sender, true) end - local player = PM:GetOrCreatePlayer(args[1]) - return PM:SetLocked(player, true) end, "Lock a player.") CM:Register({"unlock", "open"}, PM.Access.Groups.Admin.Level, function(args, sender, isChat) - if #args <= 0 then - return false, "Too few arguments. Usage: lock " + if type(args[1]) == "string" then + return PM:SetLocked(PM:GetOrCreatePlayer(args[1]), false) + else + return PM:SetLocked(sender, false) end - local player = PM:GetOrCreatePlayer(args[1]) - return PM:SetLocked(player, false) end, "Unlock a player.") CM:Register({"getaccess"}, PM.Access.Groups.User.Level, function(args, sender, isChat) - if #args <= 0 then - return false, "Too few arguments. Usage: getaccess " + local msg = "%s's access is %d (%s)" + if type(args[1]) == "string" then + local player = PM:GetOrCreatePlayer(args[1]) + return msg:format(player.Info.Name, PM.Access.Groups[player.Info.Group].Level, player.Info.Group) + else + return msg:format(sender.Info.Name, PM.Access.Groups[sender.Info.Group].Level, sender.Info.Group) end - local player = PM:GetOrCreatePlayer(args[1]) - return tostring(PM.Access.Groups[player.Info.Group].Level) .. " (" .. tostring(player.Info.Group) .. ")" end, "Get the access level of a user.") CM:Register({"setaccess"}, PM.Access.Local, function(args, sender, isChat) - if #args <= 1 then - return false, "Too few arguments. Usage: setaccess " + if #args < 1 then + return false, "Too few arguments. Usage: setaccess [player] " + end + if #args >= 2 then + local player = PM:GetOrCreatePlayer(args[1]) + return PM:SetAccessGroup(player, args[2]) + else + return PM:SetAccessGroup(sender, args[1]) end - local player = PM:GetOrCreatePlayer(args[1]) - return PM:SetAccessGroup(player, args[2]) end, "Set the access level of a user.") CM:Register({"owner"}, PM.Access.Local, function(args, sender, isChat) @@ -216,30 +226,32 @@ CM:Register({"owner"}, PM.Access.Local, function(args, sender, isChat) end, "Promote a player to owner rank.") CM:Register({"admin"}, PM.Access.Groups.Owner.Level, function(args, sender, isChat) - if #args <= 0 then - return false, "Missing argument: name" - end if isChat then return false, "This command is not allowed to be used from the chat." end + if #args <= 0 then + return false, "Missing argument: name" + end local player = PM:GetOrCreatePlayer(args[1]) return PM:SetAdmin(player) end, "Promote a player to admin rank.") CM:Register({"op"}, PM.Access.Groups.Admin.Level, function(args, sender, isChat) - if #args <= 0 then - return false, "Missing argument: name" + if type(args[1]) == "string" then + local player = PM:GetOrCreatePlayer(args[1]) + return PM:SetOp(player) + else + return PM:SetOp(sender) end - local player = PM:GetOrCreatePlayer(args[1]) - return PM:SetOp(player) end, "Promote a player to op rank.") CM:Register({"user"}, PM.Access.Groups.Op.Level, function(args, sender, isChat) - if #args <= 0 then - return false, "Missing argument: name" + if type(args[1]) == "string" then + local player = PM:GetOrCreatePlayer(args[1]) + return PM:SetUser(player) + else + return PM:SetUser(sender) end - local player = PM:GetOrCreatePlayer(args[1]) - return PM:SetUser(player) end, "Promote a player to user rank.") CM:Register({"ban"}, PM.Access.Groups.Admin.Level, function(args, sender, isChat) @@ -251,11 +263,12 @@ CM:Register({"ban"}, PM.Access.Groups.Admin.Level, function(args, sender, isChat end, "Ban a player.") CM:Register({"invite", "inv"}, PM.Access.Groups.User.Level, function(args, sender, isChat) - if #args <= 0 then - return false, "Missing argument: name" + if type(args[1]) == "string" then + local player = PM:GetOrCreatePlayer(args[1]) + return PM:Invite(player, sender) + else + return PM:Invite(sender, sender) end - local player = PM:GetOrCreatePlayer(args[1]) - return PM:Invite(player, sender) end, "Invite a player to group.") CM:Register({"inviteme", "invme"}, PM.Access.Groups.User.Level, function(args, sender, isChat) @@ -496,7 +509,7 @@ CM:Register({"readycheck", "rc"}, PM.Access.Groups.Op.Level, function(args, send return false, "Ready check not running or I have already responded." end local arg = tostring(args[1]):lower() - if arg == "accept" or arg == "yes" then + if arg:match("^[ay]") then -- Accept C.Data.ReadyCheckRunning = false if ReadyCheckFrameYesButton then ReadyCheckFrameYesButton:Click() @@ -504,7 +517,7 @@ CM:Register({"readycheck", "rc"}, PM.Access.Groups.Op.Level, function(args, send ConfirmReadyCheck(true) status = GetReadyCheckStatus("player") return "Accepted ready check." - elseif arg == "decline" or arg == "no" then + elseif arg:match("^[dn]") then -- Decline C.Data.ReadyCheckRunning = false if ReadyCheckFrameNoButton then ReadyCheckFrameNoButton:Click() @@ -518,12 +531,52 @@ CM:Register({"readycheck", "rc"}, PM.Access.Groups.Op.Level, function(args, send return false, "Failed to accept or decline ready check." end, "Respond to ready check or initate a new one.") +CM:Register({"loot", "l"}, PM.Access.Groups.Op.Level, function(args, sender, isChat) + local usage = "Usage: loot " + if #args <= 0 then + return false, usage + end + args[1] = args[1]:lower() + if args[1]:match("^ty") or args[1]:match("^me") or args[1] == "t" then + if #args < 2 then + return false, "No loot method specified." + end + local method = args[2]:lower() + return LM:SetLootMethod(method, args[3]) + elseif args[1]:match("^th") or args[1]:match("^l") then + if #args < 2 then + return false, "No loot threshold specified." + end + return LM:SetLootThreshold(args[2]) + elseif args[1]:match("^m") then + if #args < 2 then + return false, "No master looter specified." + end + return LM:SetLootMaster(args[2]) + elseif args[1]:match("^p") then + local p = args[2] + if type(p) == "string" then + if p:lower():match("^[eay]") then + p = true + elseif p:lower():match("^[dn]") then + p = false + else + p = nil + end + else + p = nil + end + return LM:SetLootPass(p) + end + return false, usage +end, "Provides various loot functions.") + CM:Register({"roll", "r"}, PM.Access.Groups.Op.Level, function(args, sender, isChat) if #args <= 0 then return RM:StartRoll(sender.Info.Name) end args[1] = args[1]:lower() - if args[1] == "start" then + if args[1]:match("^sta") then if #args < 2 then return false, "Usage: roll start <[time] [item]>" end @@ -545,39 +598,39 @@ CM:Register({"roll", "r"}, PM.Access.Groups.Op.Level, function(args, sender, isC end end return RM:StartRoll(sender.Info.Name, item, time) - elseif args[1] == "pass" then + elseif args[1]:match("^p") then return RM:PassRoll(sender.Info.Name) - elseif args[1] == "stop" then + elseif args[1]:match("^sto") then return RM:StopRoll() - elseif args[1] == "time" then + elseif args[1]:match("^t") then return RM:GetTime() - elseif args[1] == "do" then + elseif args[1]:match("^d") then local min, max if #args >= 3 then min = tonumber(args[2]) max = tonumber(args[3]) end - if not min and (args[2] == "pass" or args[2] == "p" or args[2] == "skip") then + if not min and args[2]:match("^[ps]") then return RM:PassRoll() else return RM:DoRoll(min, max) end - elseif args[1] == "set" then + elseif args[1]:match("^se") then if #args < 3 then - return false, "Usage: roll set " + return false, "Usage: roll set " end args[2] = args[2]:lower() - if args[2] == "min" then + if args[2]:match("^mi") then return RM:SetMin(tonumber(args[3])) - elseif args[2] == "max" then + elseif args[2]:match("^ma") then return RM:SetMax(tonumber(args[3])) - elseif args[2] == "time" then + elseif args[2]:match("^t") then return RM:SetTime(tonumber(args[3])) else - return false, "Usage: roll set " + return false, "Usage: roll set " end end - return false, "Usage: roll [start||stop||time||do||set]" + return false, "Usage: roll [start||stop||pass||time||do||set]" end, "Provides tools for managing or starting/stopping rolls.") for i,v in ipairs(CM.Slash) do @@ -596,7 +649,13 @@ SlashCmdList[C.Name:upper()] = function(msg, editBox) end local result, err = CM:HandleCommand(cmd, t, false, PM:GetOrCreatePlayer(UnitName("player"))) if result then - C.Logger:Normal(tostring(result)) + if type(result) == "table" then + for _,v in ipairs(result) do + C.Logger:Normal(tostring(v)) + end + else + C.Logger:Normal(tostring(result)) + end else C.Logger:Error(tostring(err)) end diff --git a/LootManager.lua b/LootManager.lua new file mode 100644 index 0000000..2e06edb --- /dev/null +++ b/LootManager.lua @@ -0,0 +1,158 @@ +--[[ + * Copyright (c) 2011 by Adam Hellberg. + * + * This file is part of Command. + * + * Command is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Command is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Command. If not, see . +--]] + +local C = Command + +--- Table holding all LootManager methods. +-- This is referenced "LM" in LootManager.lua. +-- @name Command.LootManager +-- @class table +-- +C.LootManager = { +} + +local LM = C.LootManager +local GT = C.GroupTools + +local function ParseLootMethod(method) + if type(method) ~= "string" then return "group" end + method = method:lower() + if method:match("^f") then + return "freeforall" + elseif method:match("^g") then + return "group" + elseif method:match("^m") then + return "master" + elseif method:match("^n") then + return "needbeforegreed" + elseif method:match("^r") then + return "roundrobin" + end + return "group" +end + +local function ParseThreshold(threshold) + if type(threshold) == "string" then + threshold = threshold:lower() + if threshold:match("^[ug]") then -- Uncommon/Green + return 2 + elseif threshold:match("^[rsb]") then -- Rare/Superior/Blue + return 3 + elseif threshold:match("^[ep]") then -- Epic/Purple + return 4 + elseif threshold:match("^[lo]") then -- Legendary/Orange + return 5 + elseif threshold:match("^a") then -- Artifact + return 6 + elseif threshold:match("^h") then -- Heirloom + return 7 + end + elseif type(threshold) == "number" then + return threshold + end + return 0 +end + +local function PrettyThreshold(level) + if level == 2 then + return "Uncommon" + elseif level == 3 then + return "Rare" + elseif level == 4 then + return "Epic" + elseif level == 5 then + return "Legendary" + elseif level == 6 then + return "Artifact" + elseif level == 7 then + return "Heirloom" + end + return "Unknown" +end + +function LM:SetLootMethod(method, master) + if not GT:IsGroupLeader() then + return false, "Unable to change loot method, not group leader." + end + method = ParseLootMethod(method) + if method == GetLootMethod() then + return false, "The loot method is already set to " .. method .. "!" + elseif method == "master" then + if not master then + master = UnitName("player") + --return false, "A master looter must be specified when setting loot method to Master Loot." + elseif not GT:IsInGroup(master) then + return false, ("%q is not in the group and cannot be set as the master looter."):format(master) + end + SetLootMethod(method, master) + return ("Successfully set the loot method to %s (%s)!"):format(method, master) + end + SetLootMethod(method) + return ("Successfully set the loot method to %s!"):format(method) +end + +function LM:SetLootMaster(master) + if not GT:IsGroupLeader() then + return false, "Unable to change master looter, not group leader." + end + local method = GetLootMethod() + if method ~= "master" then + return false, "Cannot set master looter when loot method is set to " .. method + end + if not master then + return false, "Master looter not specified." + elseif not GT:IsInGroup(master) then + return false, ("%q is not in the group and cannot be set as the master looter."):format(master) + end + SetLootMethod("master", master) + return ("Successfully set %s as the master looter!"):format(master) +end + +function LM:SetLootThreshold(threshold) + if not GT:IsGroupLeader() then + return false, "Unable to change loot threshold, not group leader." + end + threshold = ParseThreshold(threshold) + if threshold < 2 or threshold > 7 then + return false, "Invalid loot threshold specified, please specify a loot threshold between 2 and 7 (inclusive)." + end + SetLootThreshold(threshold) + return "Successfully set the loot threshold to " .. PrettyThreshold(threshold) .. "!" +end + +function LM:SetLootPass(pass) + local msg = UnitName("player") .. " is %s passing on loot." + if type(pass) == "nil" then + local current = GetOptOutOfLoot() + SetOptOutOfLoot(not current) + if current then + msg = msg:format("not") + else + msg = msg:format("now") + end + else + SetOptOutOfLoot(pass) + if pass then + msg = msg:format("now") + else + msg = msg:format("not") + end + end + return msg +end diff --git a/PlayerManager.lua b/PlayerManager.lua index 22cd028..1494f1b 100644 --- a/PlayerManager.lua +++ b/PlayerManager.lua @@ -178,7 +178,7 @@ end -- @return Player from list of players if exists, otherwise a new player object. -- function PM:GetOrCreatePlayer(name) - name = name:gsub("^%l", string.upper) + name = name:lower():gsub("^%l", string.upper) if CET:HasKey(Players, name) then return Players[name] else diff --git a/RollManager.lua b/RollManager.lua index c6c1c5c..1018b4d 100644 --- a/RollManager.lua +++ b/RollManager.lua @@ -58,13 +58,13 @@ local function RollTimerUpdate(_, elapsed) RollTimer.LastWarning = ceil(RollTimer.Current) end - if RollTimer.Current < RollTimer.Time then return end + if RollTimer.Current < RollTimer.Time and RollCount < RM.NumGroupMembers then return end RollTimer.Frame:SetScript("OnUpdate", nil) RollTimer.Current = 0 RollTimer.LastWarning = 0 - RM:StopRoll(true, true) + RM:StopRoll(true, RollCount < RM.NumGroupMembers) end function RM:Init() @@ -184,7 +184,7 @@ function RM:AddRoll(name, roll) Rollers[name] = tonumber(roll) RollCount = RollCount + 1 CM:SendMessage(("%d/%d players have rolled!"):format(RollCount, self.NumGroupMembers), "SMART") - if RollCount >= self.NumGroupMembers then RM:StopRoll(true) end + --if RollCount >= self.NumGroupMembers then RM:StopRoll(true) end end function RM:PassRoll(name) @@ -194,7 +194,7 @@ function RM:PassRoll(name) end Rollers[name] = -1 RollCount = RollCount + 1 - if RollCount >= self.NumGroupMembers then RM:StopRoll(true) end + --if RollCount >= self.NumGroupMembers then RM:StopRoll(true) end return ("%s has passed on the roll."):format(name) end @@ -217,21 +217,29 @@ function RM:AnnounceResult(expire) return end local name - local roll = 0 + local roll = -1 -- Minimum roll is 0 local additional = {} local numAdditional = 0 for k,v in pairs(Rollers) do - if tonumber(v) > roll then - roll = tonumber(v) + local r = tonumber(v) + if r > roll then + roll = r name = k wipe(additional) - elseif tonumber(v) == roll then - additional[k] = tonumber(v) + elseif r == roll and r ~= -1 then + additional[k] = r numAdditional = numAdditional + 1 end end local msg - if numAdditional <= 0 then + if roll == -1 then + msg = "Everyone passed on the roll! There is no winner" + if self.Item then + msg = msg .. " for " .. self.Item + end + msg = msg .. "." + CM:SendMessage(msg, "SMART") + elseif numAdditional <= 0 then msg = "The winner is: " .. name .. "! With a roll of " .. roll if self.Item then msg = msg .. " for " .. self.Item diff --git a/String.lua b/String.lua index 56adca8..f56166a 100644 --- a/String.lua +++ b/String.lua @@ -17,12 +17,12 @@ * along with Command. If not, see . --]] -if type(Command.Extensions) ~= "table" then - Command.Extensions = {} -end - local C = Command +if type(C.Extensions) ~= "table" then + C.Extensions = {} +end + --- Table containing all String methods. -- This is referenced "CES" in String.lua. -- @name Command.Extentions.String @@ -107,3 +107,36 @@ function CES:Cut(s, l) end return t end + +--- Split a string into parts to fit the length specified. +-- Works like Cut() except keeps words together to make it more pretty. +-- @param s The string to fit. +-- @param l Max length of each part. +-- @param d Delimiter to separate each word with. +-- @return Table containing the parts. +-- +function CES:Fit(s, l, d) + if (type(s) ~= "string" and type(s) ~= "table") or type(l) ~= "number" then + error("Invalid parameters passed, expected [string, number], got: [" .. type(s) .. ", " .. type(l) .. "]!") + return + end + d = d or " " + if type(s) == "table" then s = C.Extensions.Table:Join(s) end + if s:len() <= l then return s end + local words = self:Split(s) + local parts = {} + local part = "" + for i=1, #words do + part = part .. words[i] + if i >= #words then + table.insert(parts, part) + break + elseif (part .. words[i + 1]):len() >= l then + table.insert(parts, part) + part = "" + else + part = part .. d + end + end + return parts +end diff --git a/Table.lua b/Table.lua index 697ecb7..aeb3d81 100644 --- a/Table.lua +++ b/Table.lua @@ -75,3 +75,24 @@ function CET:Copy(tbl, cache) end return copy end + +--- Join table values together to create a string. +-- @param tbl Table to join. +-- @param d Delimiter to use between values. +-- @return String containing the joined values of the table. +-- +function CET:Join(tbl, d) + if type(tbl) ~= "table" then + error("Expected argument of type [table], got [" .. type(tbl) .. "]!") + return + end + d = d or " " + if #tbl <= 0 then return "" end + local s = tostring(tbl[1]) + if #tbl > 1 then + for i=2, #tbl do + s = s .. d .. tostring(tbl[i]) + end + end + return s +end diff --git a/load.xml b/load.xml index cada1d3..3f79a87 100644 --- a/load.xml +++ b/load.xml @@ -27,6 +27,7 @@