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 <player>"
+ 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 <player>"
+ 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 <player>"
+ 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 <player> <group>"
+ if #args < 1 then
+ return false, "Too few arguments. Usage: setaccess [player] <group>"
+ 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 <type||threshold||master||pass>"
+ 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 <min||max> <amount>"
+ return false, "Usage: roll set <min||max||time> <amount>"
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 <min||max> <amount>"
+ return false, "Usage: roll set <min||max||time> <amount>"
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 <http://www.gnu.org/licenses/>.
+--]]
+
+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 <http://www.gnu.org/licenses/>.
--]]
-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 @@
<Script file="PlayerManager.lua" />
<Script file="QueueManager.lua" />
<Script file="RollManager.lua" />
+ <Script file="LootManager.lua" />
<Script file="AddonComm.lua" />
<Script file="CommandManager.lua" />
<Script file="ChatManager.lua" />