Quantcast

Added a Roll Manager and did some cleanups in other parts.

F16Gaming (Laptop) [02-09-12 - 12:08]
Added a Roll Manager and did some cleanups in other parts.
Filename
ChatManager.lua
Command.lua
CommandManager.lua
Events.lua
Events_Chat.lua
GroupTools.lua
RollManager.lua
String.lua
load.xml
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" />