diff --git a/ChatManager.lua b/ChatManager.lua
index d3065b1..11950c0 100644
--- a/ChatManager.lua
+++ b/ChatManager.lua
@@ -114,6 +114,15 @@ end
function CM:SendMessage(msg, channel, target)
if not self.Settings.LOCAL_ONLY then
msg = ("[%s] %s"):format(C.Name, msg)
+ if channel == "SMART" then
+ if GT:IsRaid() then
+ channel = "RAID"
+ elseif GT:IsGroup() then
+ channel = "PARTY"
+ else
+ C.Logger:Normal(msg)
+ end
+ end
SendChatMessage(msg, channel, nil, target)
else
C.Logger:Normal(msg)
@@ -174,7 +183,13 @@ function CM:HandleMessage(msg, sender, channel, target, isBN)
local player = PM:GetOrCreatePlayer(sender)
local result, err = CCM:HandleCommand(cmd, t, true, player)
if result then
- self:SendMessage(tostring(result), channel, target)
+ if type(result) == "table" then
+ for _,v in pairs(result) do
+ self:SendMessage(tostring(v), channel, target)
+ end
+ else
+ self:SendMessage(tostring(result), channel, target)
+ end
else
self:SendMessage(tostring(err), "WHISPER", sender)
end
diff --git a/Command.lua b/Command.lua
index b47f8d2..2df62f7 100644
--- a/Command.lua
+++ b/Command.lua
@@ -37,12 +37,14 @@ Command = {
Global = {},
Settings = {},
Events = {},
+ Data = {}
}
local C = Command
local Cmd
local CM
local PM
+local RM
local log
--- Initialize Command.
@@ -54,6 +56,7 @@ function C:Init()
Cmd = self.CommandManager
CM = self.ChatManager
PM = self.PlayerManager
+ RM = self.RollManager
log = self.Logger
self:LoadSavedVars()
log:Normal("AddOn loaded! Use /cmd help or !help for help. !!NYI!!")
@@ -86,6 +89,7 @@ function C:LoadSavedVars()
end
CM:Init()
PM:Init()
+ RM:Init()
Cmd:Init()
log:SetDebug(self.Settings.DEBUG)
self.Global.VERSION = self.VarVersion
diff --git a/CommandManager.lua b/CommandManager.lua
index 7a852a7..37202ec 100644
--- a/CommandManager.lua
+++ b/CommandManager.lua
@@ -38,6 +38,7 @@ C.CommandManager = {
local CM = C.CommandManager
local PM = C.PlayerManager
local QM = C.QueueManager
+local RM = C.RollManager
local GT = C.GroupTools
local CES = C.Extensions.String
local CET = C.Extensions.Table
@@ -106,6 +107,19 @@ function CM:GetRealName(name)
return nil
end
+function CM:GetCommands(all) -- NOTE: Only returns the NAMES, not the actual command function
+ local t = {}
+ for k,v in pairs(self.Commands) do
+ table.insert(t, k)
+ if all and #v.Alias > 0 then
+ for _,a in pairs(v.Alias) do
+ table.insert(t, a)
+ end
+ end
+ end
+ return t
+end
+
--- Calls command with supplied args.
-- @param command Command to call (name)
-- @param args Table with arguments for the command.
@@ -141,6 +155,19 @@ CM:Register({"__DEFAULT__", "help", "h"}, PM.Access.Local, function(args, sender
return "End of help message."
end, "Prints this help message.")
+CM:Register({"commands", "cmds", "cmdlist", "listcmds", "listcommands", "commandlist", PM.Access.Groups.User.Level, function(args, sender, isChat)
+ local all
+ if #args > 0 then
+ all = args[1] == "all"
+ end
+ local cmds = self:GetCommands(all)
+ local msg = ""
+ for _,v in pairs(cmds) do
+ msg = msg .. ", " .. v
+ end
+ return CES:Cut(msg, 200)
+end, "Print all registered commands.")
+
CM:Register({"version", "ver", "v"}, PM.Access.Groups.User.Level, function(args, sender, isChat)
if args then
if #args > 0 then
@@ -150,6 +177,14 @@ CM:Register({"version", "ver", "v"}, PM.Access.Groups.User.Level, function(args,
return C.Version, nil
end, "Print the version of Command")
+CM:Register({"getaccess"}, PM.Access.Groups.User.Level, function(args, sender, isChat)
+ if #args <= 0 then
+ return false, "Too few arguments. Usage: getaccess <player>"
+ 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>"
@@ -391,7 +426,7 @@ CM:Register({"userdeny", "udeny"}, PM.Access.Groups.Admin.Level, function(args,
return PM:PlayerAccess(player, cmd, false)
end, "Deny a user to use a specific command.")
-CM:Register({"resetuseraccess", "useraccessreset", "removeuseraccess", "useraccessremove", "rua", "uar"}, PM.Access.Admin, function(args, sender, isChat)
+CM:Register({"resetuseraccess", "useraccessreset", "removeuseraccess", "useraccessremove", "rua", "uar"}, PM.Access.Groups.Admin.Level, function(args, sender, isChat)
if #args <= 1 then
return false, "Usage: resetuseraccess <playername> <commandname>."
end
@@ -414,6 +449,98 @@ CM:Register({"toggledebug", "td", "debug", "d"}, PM.Access.Local, function(args,
return C:ToggleDebug()
end, "Toggle debugging mode on and off.")
+CM:Register({"readycheck", "rc"}, PM.Access.Groups.Op.Level, function(args, sender, isChat)
+ if #args <= 0 then
+ if GT:IsGroupLeader() or GT:IsRaidLeaderOrAssistant() then
+ C.Data.ReadyCheckRunning = true
+ local name = tostring(sender.Info.Name)
+ DoReadyCheck()
+ return name .. " issued a ready check!"
+ else
+ return false, "Can't initiate ready check when not leader or assistant."
+ end
+ end
+ local status = GetReadyCheckStatus("player")
+ if (status ~= "waiting" and status ~= nil) or GetReadyCheckTimeLeft() <= 0 or not C.Data.ReadyCheckRunning then
+ 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
+ C.Data.ReadyCheckRunning = false
+ if ReadyCheckFrameYesButton then
+ ReadyCheckFrameYesButton:Click()
+ end
+ ConfirmReadyCheck(true)
+ status = GetReadyCheckStatus("player")
+ return "Accepted ready check."
+ elseif arg == "decline" or arg == "no" then
+ C.Data.ReadyCheckRunning = false
+ if ReadyCheckFrameNoButton then
+ ReadyCheckFrameNoButton:Click()
+ end
+ ConfirmReadyCheck(false)
+ status = GetReadyCheckStatus("player")
+ return "Declined ready check."
+ else
+ return false, "Invalid argument: " .. arg
+ end
+ return false, "Failed to accept or decline ready check."
+end, "Respond to ready check or initate a new one.")
+
+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 < 2 then
+ return false, "Usage: roll start <[time] [item]>"
+ end
+ local time = tonumber(args[2])
+ local item
+ if not time then
+ item = args[2]
+ end
+ if #args >= 3 then
+ if item then
+ item = item .. " " .. args[3]
+ else
+ item = args[3]
+ end
+ if #args > 3 then
+ for i = 4, #args do
+ item = item .. " " .. args[i]
+ end
+ end
+ end
+ return RM:StartRoll(sender.Info.Name, item, time)
+ elseif args[1] == "stop" then
+ return RM:StopRoll()
+ elseif args[1] == "time" then
+ return RM:GetTime()
+ elseif args[1] == "do" then
+ local min, max
+ if #args >= 3 then
+ min = tonumber(args[2])
+ max = tonumber(args[3])
+ end
+ return RM:DoRoll(min, max)
+ elseif args[1] == "set" then
+ if #args < 3 then
+ return false, "Usage: roll set <min||max> <amount>"
+ end
+ args[2] = args[2]:lower()
+ if args[2] == "min" then
+ return RM:SetMin(tonumber(args[3]))
+ elseif args[2] == "max" then
+ return RM:SetMax(tonumber(args[3]))
+ else
+ return false, "Usage: roll set <min||max> <amount>"
+ end
+ end
+ return false, "Usage: roll [start||stop||time||do||set]"
+end, "Provides tools for managing or starting/stopping rolls.")
+
for i,v in ipairs(CM.Slash) do
_G["SLASH_" .. C.Name:upper() .. i] = "/" .. v
end
diff --git a/Events.lua b/Events.lua
index d295201..d8fddac 100644
--- a/Events.lua
+++ b/Events.lua
@@ -69,3 +69,14 @@ function C.Events.LFG_PROPOSAL_FAILED(self, ...)
QM.QueuedByCommand = false
CM:SendMessage("LFG failed, use !queue <type> to requeue.", "PARTY")
end
+
+function C.Events.READY_CHECK(self, ...)
+ if C.Data.ReadyCheckRunning then return end
+ C.Data.ReadyCheckRunning = true
+ local name = tostring(select(1, ...))
+ CM:SendMessage(name .. " issued a ready check, type !rc accept to make me accept it or !rc deny to deny it.", "SMART")
+end
+
+function C.Events.READY_CHECK_FINISHED(self, ...)
+ C.Data.ReadyCheckRunning = false
+end
diff --git a/Events_Chat.lua b/Events_Chat.lua
index 4d1500f..25ac1aa 100644
--- a/Events_Chat.lua
+++ b/Events_Chat.lua
@@ -20,6 +20,12 @@
local C = Command
local CM = C.ChatManager
+function C.Events.CHAT_MSG_SYSTEM(self, event, ...)
+ if C.RollManager.Running then
+ C.RollManager:ParseMessage((select(1, ...)))
+ end
+end
+
--[[
function C.Events.CHAT_MSG_BATTLEGROUND(self, event, ...)
local chan = CM:GetRespondChannelByEvent(event)
diff --git a/GroupTools.lua b/GroupTools.lua
index 5421e71..dd8da07 100644
--- a/GroupTools.lua
+++ b/GroupTools.lua
@@ -63,6 +63,15 @@ function GT:IsGroupLeader(name)
return UnitIsPartyLeader(name) -- or (name == "player" and not self:IsGroup())
end
+function GT:GetNumGroupMembers()
+ if not self:IsGroup() then return 0 end
+ if UnitInRaid("player") then
+ return GetNumRaidMembers()
+ else
+ return GetNumPartyMembers() + 1 -- We need to add one because it won't count the player
+ end
+end
+
--- Check if the group is full.
-- NOTE: Only checks for 5 players in a party and 40 players in a raid.
-- DOES NOT respect 10 and 25 man raids.
diff --git a/RollManager.lua b/RollManager.lua
new file mode 100644
index 0000000..a7bccc0
--- /dev/null
+++ b/RollManager.lua
@@ -0,0 +1,241 @@
+--[[
+ * 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
+local GT = C.GroupTools
+local CM
+local CES = C.Extensions.String
+local CET = C.Extensions.Table
+
+local log
+
+C.RollManager = {
+ Running = false,
+ Settings = {}
+}
+
+local RM = C.RollManager
+
+local RollFormat = "%s rolls %d (%d-%d)" -- Not Used
+local RollMatch = "(%w+) rolls (%d+) %((%d+)-(%d+)%)" -- Thanks to ITSBTH for the pattern string
+
+local Rollers = {}
+local RollCount = 0
+
+local RollTimer = {}
+RollTimer.Frame = CreateFrame("Frame")
+RollTimer.Current = 0
+
+local function RollTimerUpdate(_, elapsed)
+ if not RM.Running then
+ RollTimer.Frame:SetScript("OnUpdate", nil)
+ end
+
+ RollTimer.Current = RollTimer.Current + elapsed
+
+ local left = RollTimer.Time - RollTimer.Current
+ if not RollTimer.LastWarning then RollTimer.LastWarning = 0 end
+ if (left <= 10 and left > 0) and ceil(RollTimer.Current) - RollTimer.LastWarning >= 5 then
+ CM:SendMessage(ceil(left) .. " seconds left to roll!", "SMART")
+ RollTimer.LastWarning = ceil(RollTimer.Current)
+ end
+
+ if RollTimer.Current < RollTimer.Time then return end
+
+ RollTimer.Frame:SetScript("OnUpdate", nil)
+ RollTimer.Current = 0
+
+ RM:StopRoll(true, true)
+end
+
+function RM:Init()
+ log = C.Logger
+ CM = C.ChatManager
+ self:LoadSavedVars()
+end
+
+function RM:LoadSavedVars()
+ if type(C.Global["ROLL_MANAGER"]) ~= "table" then
+ C.Global["ROLL_MANAGER"] = {}
+ end
+ self.Settings = C.Global["ROLL_MANAGER"]
+ if type(self.Settings["MIN_ROLL"]) ~= "number" then
+ self.Settings["MIN_ROLL"] = 1
+ end
+ if type(self.Settings["MAX_ROLL"]) ~= "number" then
+ self.Settings["MAX_ROLL"] = 100
+ end
+ if type(self.Settings["DEFAULT_TIME"]) ~= "number" then
+ self.Settings["DEFAULT_TIME"] = 20
+ end
+end
+
+function RM:SetMin(amount)
+ if type(amount) ~= "number" then
+ return false, "Invalid amount passed: " .. tostring(amount)
+ end
+ if amount > self.Settings.MAX_ROLL then
+ return false, "Minimum roll number cannot be higher than maximum roll number!"
+ end
+ self.Settings.MIN_ROLL = amount
+ return "Sucessfully set minimum roll number to " .. amount .. "!"
+end
+
+function RM:SetMax(amount)
+ if type(amount) ~= "number" then
+ return false, "Invalid amount passed: " .. tostring(amount)
+ end
+ if amount < self.Settings.MIN_ROLL then
+ return false, "Maximum roll number cannot be higher than minimum roll number!"
+ end
+ self.Settings.MAX_ROLL = amount
+ return "Sucessfully set maximum roll number to " .. amount .. "!"
+end
+
+function RM:StartRoll(sender, item, time)
+ time = tonumber(time) or self.Settings.DEFAULT_TIME
+ RollTimer.Time = time
+ if not sender then
+ return false, "Could not identify sender: " .. tostring(sender) .. ". Aborting roll..."
+ end
+ self.NumGroupMembers = GT:GetNumGroupMembers()
+ if self.NumGroupMembers <= 0 then
+ return false, "Could not start roll, not enough group members!"
+ end
+ self.Running = true
+ self.Sender = sender
+ wipe(Rollers)
+ RollCount = 0
+ if item then
+ self.Item = item
+ RollTimer.Frame:SetScript("OnUpdate", RollTimerUpdate)
+ return ("%s started a roll for %s, ends in %d seconds! Type /roll %d %d"):format(self.Sender, self.Item, time, self.Settings.MIN_ROLL, self.Settings.MAX_ROLL)
+ else
+ RollTimer.Frame:SetScript("OnUpdate", RollTimerUpdate)
+ return ("%s started a roll, ends in %d seconds! Type /roll %d %d"):format(self.Sender, time, self.Settings.MIN_ROLL, self.Settings.MAX_ROLL)
+ end
+ -- We shouldn't reach this place
+ self.Running = false
+ self.Sender = nil
+ self.Item = nil
+ return false, "Unknown error occurred"
+end
+
+function RM:StopRoll(finished, expire)
+
+ if finished then
+ self:AnnounceResult(expire)
+ else
+ if not self.Running then
+ return false, "No roll is currently in progress!"
+ end
+ end
+ self.Running = false
+ self.Sender = nil
+ self.Item = nil
+ wipe(Rollers)
+ return "Roll has been stopped."
+end
+
+function RM:DoRoll(min, max)
+ min = min or self.Settings.MIN_ROLL
+ max = max or self.Settings.MAX_ROLL
+ RandomRoll(min, max)
+ return "Done! Executed RandomRoll(" .. min .. ", " .. max .. ")"
+end
+
+function RM:AddRoll(name, roll)
+ if CET:HasKey(Rollers, name) then
+ CM:SendMessage(name .. " has already rolled! (" .. Rollers[name] .. ")", "SMART")
+ return
+ end
+ 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
+end
+
+function RM:GetTime()
+ if self.Running then
+ return ("%d seconds remaining!"):format(max(ceil(RollTimer.Time - RollTimer.Current), 0))
+ else
+ return false, "No roll is currently in progress!"
+ end
+end
+
+function RM:AnnounceResult(expire)
+ if expire then
+ CM:SendMessage("Roll time expired! Results...", "SMART")
+ else
+ CM:SendMessage("Everyone has rolled! Results...", "SMART")
+ end
+ if RollCount <= 0 then
+ CM:SendMessage("Noone rolled, there is no winner!", "SMART")
+ return
+ end
+ local name
+ local roll = 0
+ local additional = {}
+ local numAdditional = 0
+ for k,v in pairs(Rollers) do
+ if tonumber(v) > roll then
+ roll = tonumber(v)
+ name = k
+ wipe(additional)
+ numAdditional = 0
+ elseif tonumber(v) == roll then
+ additional[k] = tonumber(v)
+ numAdditional = numAdditional + 1
+ end
+ end
+ local msg
+ if numAdditional <= 0 then
+ msg = "The winner is: " .. name .. "! With a roll of " .. roll
+ if self.Item then
+ msg = msg .. " for " .. self.Item
+ end
+ msg = msg .. "."
+ CM:SendMessage(msg, "SMART")
+ else
+ msg = "There are multiple winners"
+ if self.Item then
+ msg = msg .. " for " .. self.Item
+ end
+ msg = msg .. ":"
+ CM:SendMessage(msg, "SMART")
+ CM:SendMessage(name .. " with a roll of " .. roll, "SMART")
+ for k,v in pairs(additional) do
+ CM:SendMessage(k .. " with a roll of " .. v, "SMART")
+ end
+ end
+end
+
+function RM:ParseMessage(msg)
+ if not string.match(msg, RollMatch) then return end
+ local name, roll, minRoll, maxRoll = msg:match(RollMatch)
+ roll = tonumber(roll)
+ minRoll = tonumber(minRoll)
+ maxRoll = tonumber(maxRoll)
+ print(name, roll, minRoll, maxRoll)
+ if minRoll ~= self.Settings.MIN_ROLL or maxRoll ~= self.Settings.MAX_ROLL then
+ CM:SendMessage(name .. " specified too high or low roll region, not including their roll!", "SMART")
+ return
+ end
+ self:AddRoll(name, roll)
+end
diff --git a/String.lua b/String.lua
index c03b5b8..76655f2 100644
--- a/String.lua
+++ b/String.lua
@@ -64,9 +64,32 @@ end
-- @return Table containing the individual words.
--
function CES:Split(s)
+ s = s or " "
local t = {}
for token in string.gmatch(s, "[^%s]+") do
table.insert(t, token)
end
return t
end
+
+--- Cut a string into parts.
+-- @param s The String to cut.
+-- @param l Length of each string part.
+-- @return Table containing the different parts.
+--
+function CES:Cut(s, l)
+ if type(s) ~= "string" or type(l) ~= "number" then
+ error("Invalid parameters passed, expected [string, number], got: [" .. type(s) .. ", " .. type(l) .. "]!")
+ return
+ end
+ if s:len() <= l then return s end
+ local c = math.ceil(s:len() / l)
+ local pos = 1
+ local t = {}
+ for i = 1, c do
+ local part = s:sub(pos, l * i)
+ table.insert(t, part)
+ pos = l * i + 1
+ end
+ return t
+end
diff --git a/load.xml b/load.xml
index 7f76ad8..d2d81c6 100644
--- a/load.xml
+++ b/load.xml
@@ -26,6 +26,7 @@
<Script file="GroupTools.lua" />
<Script file="PlayerManager.lua" />
<Script file="QueueManager.lua" />
+ <Script file="RollManager.lua" />
<Script file="CommandManager.lua" />
<Script file="ChatManager.lua" />
<Script file="Events.lua" />