From d1620023eccd1fd33c2e015665b7569ff7474e5c Mon Sep 17 00:00:00 2001 From: "F16Gaming (Laptop)" Date: Thu, 9 Feb 2012 13:08:18 +0100 Subject: [PATCH] Added a Roll Manager and did some cleanups in other parts. --- ChatManager.lua | 17 +++- Command.lua | 4 + CommandManager.lua | 129 +++++++++++++++++++++++++++- Events.lua | 11 +++ Events_Chat.lua | 6 ++ GroupTools.lua | 9 ++ RollManager.lua | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++ String.lua | 23 +++++ load.xml | 1 + 9 files changed, 439 insertions(+), 2 deletions(-) create mode 100644 RollManager.lua 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 " + 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 " @@ -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 ." 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 " + 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 " + 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 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 . +--]] + +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 @@