--[[ - = .--._ Utility was constructed by Blaze The Cat. - - ~_= =~_- = - `. `-. Helpers: ==~_ = =_ ~ - = .-' `. - NotablyPrettyCrazy: The script's crash test dummy. Always found a way to break my things. --=~_ - ~ == - = .' _..:._ - Kitoko: Helps a lot with debugging and suggested most of these commands. Thanks! ---=~ _~ = =- = `. .--.' `. - Spectrum: For helping with testing. --=_-=- ~= _ - = - _.' `. .--.: - Anyone else who visited my testing servers. Thank you! -=_~ -- = = ~- .' : : : -=-_ ~= = - _-`--. : .--: D Well, I'm not one for words because I want to keep adding things. -=~ _= = -~_= `; .'.: ,`---'@ Go ahead, have a ball. --=_= = ~- -= .' .' `._ `-.__.' --== ~_ - = =- .' .' _.`---' Version 0.3b --=~_= = - = ~ .'--'' . `-..__.--. jgs--==~ _= - ~-= =-~_- `-..___( ===; I totally stole this ASCII art --==~_==- =__ ~-= - - .' `---' <-- Looks like a wrench for a hand. ]]-- --Please report any and all warnings and how they happen. I strive to make this the most pester-free script possible. --TO DO: Clean up everything and arrange them into sections. --This is here to prevent double-adding. It could result in doubled messages and incorrect timers! That would be a mess I will not clean up for you. Nope. assert(utility == nil, "The variable \"utility\" is already defined in the global table. Either Utility is already loaded or some script already claimed this. You need to resolve this before adding Utility!\nIn other words, you committed Utilityception.") rawset(_G, "utility", {}) -- You can use this to check if Utility is present! --Shows the status of this version of Utility. utility.version = " 0.3b " utility.iteration = " 36j" utility.subversion = false local nodestatus = {} --To keep myself from checking for nil all the time. for a=0,31 do nodestatus[a] = true end --Helper values and various things. utility.msg = {} utility.msg["teamcolor"] = { --Extended team name printout by number [0] = "\x86" .. "%s" .. "\x80", --Spectator/Not playing at all. [1] = "\x82" .. "%s" .. "\x80", --Playing and unaffiliated. [2] = "\x85" .. "%s" .. "\x80", --Red Team [3] = "\x84" .. "%s" .. "\x80", --Blue Team [4] = "\x83" .. "%s" .. "\x80", --Not-IT [5] = "\x87" .. "%s" .. "\x80" --IT } --utility.msg["insignia"] = { --Symbols to represent permissions. --} utility.msg["overlay"] = { --Normal chat, team based chat, admin chat, and PMs [0] = "\x86" .. "[SPECT] %s " .. "\x86", --Spectator/Not playing at all. [1] = "\x82" .. "[NEUTRAL] %s " .. "\x82", --Playing and unaffiliated. [2] = "\x85" .. "[RED] %s " .. "\x85", --Red Team [3] = "\x84" .. "[BLUE] %s " .. "\x84", --Blue Team [4] = "\x83" .. "[NOT-IT] %s " .. "\x83", --Not-IT [5] = "\x87" .. "[IT] %s " .. "\x87", --IT [6] = "\x83" .. "**" .. "\x86" .. " [%s" .. "\x86" .. " -> %s" .. "\x86" .. "] %s", --Sent Private Message [7] = "<%s" .. "\x80" .. "> %s", --Default message. [8] = "\x87" .. "**" .. "\x86" .. " [%s" .. "\x86" .. " -> %s" .. "\x86" .. "] %s", --Recieved Private Message [9] = "\x81" .. "[ADMIN] %s " .. "\x81", --Spectator/Not playing at all. } utility.msg["spam"] = { --Spam silence reasons. [0] = "Spamming the chat.", [1] = "spamming the team chat.", [2] = "Spamming private messages.", [3] = "Spamming global notices.", [4] = "Spamming the admin chat." } utility.gametype = {} utility.gametype["tol"] = { --Used in the Gametype Command. Transfers TOL and matches it with the core modes. [GT_COOP] = TOL_COOP, [GT_COMPETITION] = TOL_COMPETITION, [GT_RACE] = TOL_RACE, [GT_MATCH] = TOL_MATCH, [GT_TEAMMATCH] = TOL_MATCH, [GT_TAG] = TOL_TAG, [GT_HIDEANDSEEK] = TOL_TAG, [GT_CTF] = TOL_CTF } utility.gametype["names"] = { --String matches for Gametypes in conjunction with numbers. [GT_COOP] = "coop", [GT_COMPETITION] = "competition", [GT_RACE] = "race", [GT_MATCH] = "match", [GT_TEAMMATCH] = "teammatch", [GT_TAG] = "tag", [GT_HIDEANDSEEK] = "hideandseek", [GT_CTF] = "ctf" } --Both of these allow commands and variables to have specific additional values attached to them object-style. utility.cmdlist = {} utility.cmdlist["name"] = {} utility.cmdlist["helptext"] = {} utility.cmdlist["flags"] = {} utility.cmdlist["num"] = 0 utility.varlist = {} utility.varlist["name"] = {} utility.varlist["helptext"] = {} utility.varlist["flags"] = {} utility.varlist["vars"] = {} utility.varlist["num"] = 0 --Basic helpers for command and variable adding to give them a help text. utility.AddUtilCommand = function(name, helptext, func, flags) -- Allow help for each command. local num = utility.cmdlist.num + 1 utility.cmdlist.name[num] = name utility.cmdlist.helptext[num] = helptext utility.cmdlist.flags[num] = flags COM_AddCommand(name, func, tonumber(flags)) utility.cmdlist.num = $1 + 1 end utility.AddUtilVar = function(name, helptext, default, flags, limits, func) -- Allow help for each variable too. local num = utility.varlist.num + 1 utility.varlist.name[num] = name utility.varlist.helptext[num] = helptext utility.varlist.flags[num] = flags utility.varlist.num = $1 + 1 return CV_RegisterVar({name, tostring(default), tonumber(flags), limits}) end --Scans the nodes to get the node number of a player. utility.PlayerToNode = function(player) for x = 0, 31 do if players[x] ~= nil if players[x] == player return x end end end end utility.vote = {} utility.coop = {} utility.spam = {} utility.coop["timelimit"] = utility.AddUtilVar("util_coop_exittime", "Sets how many minutes pass until the Co-Op level is completed automatically, in minutes. Setting it to 0 disables it. Ranges from 0 to 30, default is 0.", "0", CV_NETVAR, {MIN=0, MAX=30}) utility.vote["votetime"] = utility.AddUtilVar("util_vote_time", "Changes how many seconds are on the timer when a vote takes place. Ranges from 10 to 300, default is 30.", "30", CV_NETVAR, {MIN=10, MAX=300}) utility.vote["votedelay"] = utility.AddUtilVar("util_vote_delay", "Changes how many seconds until a user can call a vote again. Ranges from 0 to 61356675, default 300.", "30", CV_NETVAR, {MIN=0, MAX=61356675}) utility.vote["spectatorvote"] = utility.AddUtilVar("util_vote_specvote", "Determines if spectators are allowed to take part in a vote. Either Yes or No, default is Yes.", "Yes", CV_NETVAR, CV_YesNo) utility.spam["time"] = utility.AddUtilVar("util_spam_time", "The minimum time threshold in which to silence players. This time threshold is checked on every number of messages specified by util_chat_num. Ranges from 0 to 30, default 3. Setting this to 0 disables spam checks.", "3", CV_NETVAR, {MIN=0, MAX=30}) utility.spam["num"] = utility.AddUtilVar("util_spam_num", "The number of messages to be regarded by the time threshold on each iteration. Ranges from 0 to 30, default 5. Setting this to 0 disables spam checks.", "5", CV_NETVAR, {MIN=0, MAX=30}) utility.spam["repeats"] = utility.AddUtilVar("util_spam_repeats", "Decides whether repeated messages should be allowed. Either Yes or No, default is No.", "No", CV_NETVAR, CV_YesNo) --Permission checker. If true, it will do nothing. If false, it will print to the player that they lack permission. Make nomsg not nil to hide the permission deny message. utility.IsAdmin = function (p, nomsg) local perm = 0 local termadmin = false if terminal ~= nil perm = p.servperm if perm ~= nil if perm & terminal.permissions.FULLCONTROL or perm & terminal.permissions.PLAYERMANAGE termadmin = true end end end if p == server or p == admin or termadmin == true return true else if nomsg == nil CONS_Printf(p, "You don't have permission to use this Utility command/variable.") end return false end end local function getSymbol(player) --Added to support Terminal prefixes. Because, adding multiple server tools at once works great, right? --Permcolor is a local funtion and not in the global namespace, so I added my own color hierarchy. if player == server then return "\x81" .. "~" end -- Server if admin ~= nil if player == admin then return "\x86" .. "*" end --Vanilla Admin end local p = player.servperm if terminal.permissions.text.L0 ~= nil --Fuck, it's Jazzterm, let's support it. if p == 1 then return "" end end if not p then return "" end -- No permissions! D: if (p & terminal.permissions.FULLCONTROL) then return "\x85" .. "&" end -- Admin if (p & terminal.permissions.PLAYERMANAGE) then return "\x87" .. "@" end -- Operator if admin ~= nil if (p == admin) then return "\x87" .. "@" end -- Operator end if (p & terminal.permissions.GAMEMANAGE) then return "\x83" .. "%" end -- Half-Op if (p & terminal.permissions.SELFCHEATS) then return "\x84" .. "+" end -- Cheater end utility.ExtendedTeam = function (player) if player.mo == nil return 0 else if player.spectator == 1 return 0 end if gametype == GT_TAG or gametype == GT_HIDEANDSEEK if player.pflags & PF_TAGIT return 5 else return 4 end elseif gametype == GT_TEAMMATCH or gametype == GT_CTF if player.ctfteam == 1 return 2 elseif player.ctfteam == 2 return 3 else return 1 end end return 1 end end utility.Insignia = function(player) if terminal ~= nil --Let's use their symbols. return getSymbol(player) else if player == server return "\x81" .. "~" .. "\x80" elseif player == admin return "\x87" .. "@" .. "\x80" else return "" end end end utility.TeamedName = function(player) --Converts a player's name to a symbol-marked team-colored name. local name = player.name if player == server if server.isdedicated == true --Use the dedicated server name. name = server.utility.dediname end end return utility.Insignia(player) .. string.format(utility.msg.teamcolor[utility.ExtendedTeam(player)], name) end --Avast ye server hosts! This is one of the most abusable commands for voting. If you really want to stay in the same game mode, I suggest you keep this set to disabled. local function Cmd_Gametype(p, gamemode) --Allows live changing of the gametype! This will take you to the first available map of that gametype. if p.utility == nil then return end if utility.IsAdmin(p) == false then return end if multiplayer == true if gamemode == nil CONS_Printf(p, "Gametype is \"" .. utility.gametype.names[gametype] .. "\" (" .. tostring(gametype) .. ")") else if p == server or p == admin or splitscreen == true local leveltype = -1 local wantedmap = -1 if tonumber(gamemode) ~= nil if tonumber(gamemode) > 0 and tonumber(gamemode) < 8 leveltype = tonumber(gamemode) end else for a=0,7 do if utility.gametype.names[a] == string.lower(gamemode) leveltype = a break end end end if leveltype ~= -1 local tol = utility.gametype.tol[leveltype] for b=1,1035 do if mapheaderinfo[b] ~= nil if mapheaderinfo[b].typeoflevel & tol wantedmap = b break end end end if wantedmap == -1 CONS_Printf(p, "No maps for this gametype! Toto, we're not in Kansas anymore...") --HOW?!? else COM_BufInsertText(server, "map " .. G_BuildMapName(wantedmap) .. " -gametype " .. tostring(leveltype)) end else CONS_Printf(p, "Invalid game mode.") end else CONS_Printf(p, "Gametype is \"" .. utility.gametype.names[gametype] .. "\" (" .. tostring(gametype) .. ")") end end else CONS_Printf(p, "This command is exclusive to multiplayer.") end end --Marks a player as silenced. They will be unable to chat in public or team chats. local function A_SilencePlayer(adm, victim, seconds, reason) if victim ~= nil victim.utility.silenced = true victim.utility.quiettime = seconds * TICRATE local msg = "" local name if reason ~= nil msg = " Reason: " .. reason end if adm == nil name = "\x81" .. "Console" .. "\x80" else name = utility.TeamedName(adm) end local setit = "" local forgetit = "" if victim.utility.quiettime > 0 setit = "You have silenced " forgetit = "You have been silenced for " else setit = "You undid and silenced " forgetit = "Your silence has been updated to " end if adm ~= nil CONS_Printf(adm, setit .. utility.TeamedName(victim) .. " for " .. tostring(seconds) .. " seconds." .. msg) end CONS_Printf(victim, forgetit .. tostring(seconds) .. " seconds by " .. name .. "." .. msg) for p in players.iterate if utility.IsAdmin(p, true) == true CONS_Printf(p, "\x87" .. "ADMIN: " .. "\x80" .. name .. " silenced " .. utility.TeamedName(victim) .. " for " .. tostring(seconds) .. " seconds." .. msg) end end if server.isdedicated == true CONS_Printf(server, "\x87" .. "ADMIN: " .. "\x80" .. name .. " silenced " .. utility.TeamedName(victim) .. " for " .. tostring(seconds) .. " seconds." .. msg) end else CONS_Printf(adm, "Player doesn't exist!") end end --Reverses the other thing. local function A_UnsilencePlayer(adm, victim) if victim ~= nil if victim.utility.quiettime == 0 CONS_Printf(adm, "This player is not silenced.") return end victim.utility.silenced = false victim.utility.quiettime = 0 CONS_Printf(adm, "You unsilenced " .. utility.TeamedName(victim) .. "." ) CONS_Printf(victim, "You have been unsilenced by " .. utility.TeamedName(adm) .. "." ) for p in players.iterate if utility.IsAdmin(p, true) == true CONS_Printf(p, "\x87" .. "ADMIN: " .. "\x80" .. utility.TeamedName(adm) .. " unsilenced " .. utility.TeamedName(victim) .. ".") end end if server.isdedicated == true CONS_Printf(server, "\x87" .. "ADMIN: " .. "\x80" .. utility.TeamedName(adm) .. " unsilenced " .. utility.TeamedName(victim) .. ".") end else CONS_Printf(adm, "Player doesn't exist!") end end --Find the node of a player. local function F_GetNode(player) for a=0,31 do if players[a] ~= nil if players[a] == player return a end end end end local function F_FindPlayer(nodename) --Finds a player via node or name on one input. Returns the node number if valid, returns -1 otherwise. if nodename == nil return -1 end if tonumber(nodename) ~= nil --We have a number. if tonumber(nodename) <=31 and tonumber(nodename) >= 0 if players[tonumber(nodename)] ~= nil if players[tonumber(nodename)].valid return tonumber(nodename) end end else return -1 end else local matchnode = 99 for a=0,31 do if players[a] ~= nil if players[a].valid if string.lower(players[a].name) == string.lower(nodename) matchnode = a end end end end if matchnode ~= 99 return matchnode end end return -1 end utility.votablecmds = {} utility.votablecmds["name"] = {} utility.votablecmds["allow"] = {} utility.votablecmds["logic"] = {} utility.votablecmds["minvoters"] = utility.AddUtilVar("util_minvoters", "This determines the minimum amount of players required before a vote can occur. Minimum of 1, maximum of 32.", "2", CV_NETVAR, {MIN=1, MAX=32}) --You can use this function below to define your own commands that should be votable by players. utility.AddVotableCommand = function(name, logic, allowed) local num = #utility.votablecmds.name if num == nil num = 0 end if name == nil or logic == nil print("Bad call to AddVotableCommand. Review your code and try again.") else utility.votablecmds.name[num+1] = name utility.votablecmds.logic[num+1] = logic local allowit = "No" if allowed ~= nil if allowed == true allowit = "Yes" end end utility.votablecmds.allow[num+1] = utility.AddUtilVar("util_votecmd_" .. name, "This is a permission flag that determines whether the specifid command can be queued up via the callvote command. This variable controls the rights to call the command \"" .. name .. "\". Set this to Yes or No.", allowit, CV_NETVAR, CV_YesNo) end end --Tries to start a vote, and if it can, do it. local function A_StartVote (starter, command, reason, exec) if server.globals.vote.started == true CONS_Printf(starter, "Voting could not commence, for there is already one in progress.") elseif command == nil CONS_Printf(starter, "Please specify a command.") else server.globals.vote.started = true server.globals.vote.time = utility.vote.votetime.value * TICRATE server.globals.vote.cmd = command server.globals.vote.exec = exec server.globals.vote.reason = reason server.globals.vote.yes = 0 server.globals.vote.no = 0 server.globals.vote.total = 0 for player in players.iterate player.utility.voted = 0 end print("\x82" .. utility.TeamedName(starter) .. " calls a vote \"" .. command .. "\"") end end --Maps the letters to values to quickly do extended name math. local mapletters = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"} local lettervalue1 = {} local lettervalue2 = {} for a=1,26 do lettervalue1[mapletters[a]] = 100 + ((a-1) * 36) lettervalue2[mapletters[a]] = a + 9 end utility.MapXXAsMapnum = function (str) local mapxx = string.gsub(string.lower(str), "map", "") local mapnum = 0 if str == nil return -1 end if string.len(mapxx) ~= 2 return -1 end local first = string.lower(string.sub(mapxx, 1, 1)) local second = string.lower(string.sub(mapxx, 2, 2)) if first == nil or second == nil return -1 else if tonumber(first) == nil --Probably a letter. if lettervalue1[first] == nil return -1 end mapnum = $1 + lettervalue1[first] if tonumber(second) == nil --Probably a letter. if lettervalue1[second] == nil return -1 end mapnum = $1 + lettervalue2[second] else mapnum = $1 + tonumber(second) end else if tonumber(mapxx) == nil return -1 else mapnum = $1 + tonumber(mapxx) end end end return mapnum end --Quickly determines the delay between the next vote for the specified player. local function V_VoteDelay () return (utility.vote.votetime.value + utility.vote.votedelay.value + 5)*TICRATE end --Various vote logic. local function Vote_Kick (p, command, target, reason) local node = F_FindPlayer(target) if node ~= -1 A_StartVote(p, "kick \"" .. players[node].name .. "\"", reason, "kick " .. node .. " \"" .. reason .. "\"") p.utility.votedelay = 335*TICRATE else CONS_Printf(p, "Invalid target player.") end end local function Vote_Exitlevel (p, command, target, reason) if target == nil target = "No reason given" end reason = target A_StartVote(p, "exitlevel", reason, "exitlevel") p.utility.votedelay = V_VoteDelay() end local function Vote_Timelimit (p, command, target, reason) if reason == nil reason = "No reason given" end if target == nil CONS_Printf(p, "You must supply a number of minutes.") return end local number = tonumber(target) if number == nil number = 0 end if number <= 30 and number >= 0 A_StartVote(p, "timelimit " .. tostring(number), reason, "timelimit " .. tostring(number)) p.utility.votedelay = V_VoteDelay() else CONS_Printf(p, "Number of minutes should be between 0 and 30") end end local function Vote_Pointlimit (p, command, target, reason) if reason == nil reason = "No reason given" end if target == nil CONS_Printf(p, "You must supply a number of points.") return end local number = tonumber(target) if number == nil number = 0 end if number <= 2147483647 and number >= 0 A_StartVote(p, "pointlimit " .. tostring(number), reason, "pointlimit " .. tostring(number)) p.utility.votedelay = V_VoteDelay() else CONS_Printf(p, "Number of points should be between 0 and 2147483647") end end local function Vote_Map (p, command, target, reason) if reason == nil reason = "No reason given" end if target == nil CONS_Printf(p, "You must supply a map to go to in the form of MAPxx") return else local mapnumber = utility.MapXXAsMapnum(target) if mapnumber == -1 CONS_Printf(p, "Invalid map; Please supply a MAPXX value. This can be the level code (e.g. 03, B1, M3) or a map name (like MAP24)") else if mapheaderinfo[mapnumber] == nil CONS_Printf(p, "Invalid map; That map apparently does not exist.") else local mapcode = target if string.len(target) == 2 mapcode = "map" .. target end local act = "" if mapheaderinfo[mapnumber].actnum > 0 act = " " .. tostring(mapheaderinfo[mapnumber].actnum) end A_StartVote(p, "Go to " .. mapheaderinfo[mapnumber].lvlttl .. act .. " (" .. mapcode .. ")", reason, "map " .. mapcode) p.utility.votedelay = V_VoteDelay() end end end local number = tonumber(target) end local function Vote_Gametype (p, command, target, reason) if reason == nil reason = "No reason given" end if target == nil CONS_Printf(p, "You must supply a gametype to play.") return end local number = -1 if tonumber(target) == nil for a=0,7 do if utility.gametype.names[a] == string.lower(target) number = a break end end else number = tonumber(trget) end if number >= 0 and number <= 7 A_StartVote(p, "gametype \"" .. utility.gametype.names[number] .. "\"", reason, "gametype " .. target) p.utility.votedelay = V_VoteDelay() else CONS_Printf(p, "Please specify a valid gametype. Gametypes: 0/coop, 1/competition, 2/race, 3/match, 4/teammatch, 5/tag, 6/hideandseek, 7/ctf") end end --Putting the votes into the table with the function. utility.AddVotableCommand("kick", Vote_Kick, false) utility.AddVotableCommand("exitlevel", Vote_Exitlevel, true) utility.AddVotableCommand("timelimit", Vote_Timelimit, true) utility.AddVotableCommand("pointlimit", Vote_Pointlimit, true) utility.AddVotableCommand("map", Vote_Map, true) utility.AddVotableCommand("gametype", Vote_Gametype, false) --Democracy! local function Cmd_Vote(p, response) if p.utility == nil then return end if p.mo == nil if p.jointime == 0 CONS_Printf(p, "You need be in-game to vote. If you're the dedicated server, you can stop a vote with the cancelvote command.") return else if p.spectator == true and utility.vote.spectatorvote.value == 0 CONS_Printf(p, "The server does not allow spectators to vote.") return end end end if response == nil CONS_Printf(p, "Usage: vote ") return end if splitscreen CONS_Printf(p, "This command is redundant in splitscreen play.") elseif not multiplayer CONS_Printf(p, "This command is only usable in multiplayer.") elseif server.globals.vote.started == false or server.globals.vote.result ~= "none" CONS_Printf(p, "Nobody is calling a vote right now.") else if p.utility.voted == 0 if string.lower(response) == "yes" p.utility.voted = 1 server.globals.vote.yes = $1 + 1 server.globals.vote.total = $1 + 1 print("\x82" .. p.name .. " votes \"" .. "\x83" .. "YES".. "\x82" .. "\".") elseif string.lower(response) == "no" p.utility.voted = 2 server.globals.vote.no = $1 + 1 server.globals.vote.total = $1 + 1 print("\x82" .. p.name .. " votes '" .. "\x85" .. "NO".. "\x82" .. "'.") else CONS_Printf(p, "Use 'yes' or 'no'.") end else CONS_Printf(p, "You have already voted.") end end end --Anarchy! local function Cmd_Cancelvote (p) if p.utility == nil then return end if utility.IsAdmin(p) == false then return end if splitscreen CONS_Printf(p, "This command is redundant in splitscreen play.") end if multiplayer if server.globals.vote.started == true server.globals.vote.time = 0 server.globals.vote.yes = 0 server.globals.vote.no = 0 server.globals.vote.total = 0 print("\x82" .. utility.TeamedName(p) .. " forcibly cancelled the vote.") else CONS_Printf(p, "No vote in progress.") end else CONS_Printf(p, "This command is only usable in multiplayer.") end end --Made for ease of use (And because the Dedicated Server cannot type underscores) local function Cmd_Votepermission (p, cmd, value) if p.utility == nil then return end if utility.IsAdmin(p) == false then return end if cmd == nil CONS_Printf(p, "Please supply a command. votepermission ") CONS_Printf(p, "You may use a numerical ID or the command name. Try showvotecmds for a list.") return else local command = string.lower(cmd) local cmdnum = 0 local loopnum = #utility.votablecmds.name if loopnum == nil CONS_Printf(p, "You must have a votable command before you decide permissions.") end if tonumber(command) ~= nil cmdnum = tonumber(command) else for x=1,loopnum if utility.votablecmds.name[x] == command cmdnum = x break end end end if utility.votablecmds.name[cmdnum] == nil --Doesn't exist. cmdnum = 0 end if cmdnum ~= 0 if value == nil value = "nothing" end if string.lower(value) == "allow" if utility.votablecmds.allow[cmdnum].value == 1 CONS_Printf(p, "Vote permission for command \"" .. utility.votablecmds.name[cmdnum] .. "\" is already set to " .. "\x83" .. "ALLOW" .. "\x80" .. ". Value unchanged.") else print("Vote permission for command \"" .. utility.votablecmds.name[cmdnum] .. "\" was set to " .. "\x83" .. "ALLOW" .. "\x80" .. ".") COM_BufInsertText(server, "util_vote_" .. utility.votablecmds.name[cmdnum] .. " 1") end elseif string.lower(value) == "deny" if utility.votablecmds.allow[cmdnum].value == 0 CONS_Printf(p, "Vote permission for command \"" .. utility.votablecmds.name[cmdnum] .. "\" is already set to " .. "\x85" .. "DENY" .. "\x80" .. ". Value unchanged.") else print("Vote permission for command \"" .. utility.votablecmds.name[cmdnum] .. "\" was set to " .. "\x85" .. "DENY" .. "\x80" .. ".") COM_BufInsertText(server, "util_vote_" .. utility.votablecmds.name[cmdnum] .. " 0") end else if utility.votablecmds.allow[cmdnum].value == 1 CONS_Printf(p, "Vote permission for command \"" .. utility.votablecmds.name[cmdnum] .. "\" is currently set to " .. "\x83" .. "ALLOW" .. "\x80" .. ".") elseif utility.votablecmds.allow[cmdnum].value == 0 CONS_Printf(p, "Vote permission for command \"" .. utility.votablecmds.name[cmdnum] .. "\" is currently set to " .. "\x85" .. "DENY" .. "\x80" .. ".") else CONS_Printf(p, "Vote permission for command \"" .. utility.votablecmds.name[cmdnum] .. "\" is currently set to " .. "\x82" .. "AMBIUOUS" .. "\x80" .. ". This is probably an issue.") end end else CONS_Printf(p, "This is not a valid votable command.") end end end --Let's become a permanent deadweight! utility.BecomeAFK = function(player) if player.mo == nil CONS_Printf(player, "You need to be an actual in-game player.") else if player.utility.afk == false if player.utility.afkdelay == 0 if player.bonustime == true CONS_Printf(player, "You cannot AFK right now. Finish the NiGHTS Stage.") else player.utility.afk = true player.utility.afkdelay = 30 * TICRATE --Can't do this again for a while. if player.exiting == 0 player.exiting = 1 end player.utility.lock.x = player.mo.x player.utility.lock.y = player.mo.y player.utility.lock.z = player.mo.z player.utility.lock.momx = player.mo.momx player.utility.lock.momy = player.mo.momy player.utility.lock.momz = player.mo.momz print(utility.TeamedName(player) .. " is now AFK.") end else CONS_Printf(player, "You must wait " .. tostring(player.utility.afkdelay / TICRATE) .. " seconds before going AFK again.") end elseif player.utility.afk == true player.utility.afk = false --if (player.mo.subsector.sector.special / 4096) ~= 2 --NOTE: Please change this to a map-based one-time toggle and don't trust the map or user input on this. if player.utility ~= nil if player.utility.levelcomplete == false player.exiting = 0 end else player.exiting = 0 end --end player.mo.flags2 = $ & !MF2_SHADOW print(utility.TeamedName(player) .. " is no longer AFK.") end end end utility.TotalVoters = function () local numplayers = 0 for player in players.iterate if player.mo ~= nil numplayers = $1 + 1 end if utility.vote.spectatorvote.value == 1 if player.spectator == true numplayers = $1 + 1 end end end return numplayers end --Calculates the votes and tells the game to stop the vote immediately if a winning vote percentage has been reached. --Yes must have MORE than 50% to win. No only needs AT LEAST HALF to win. local function Vote_IsVoteDone(numplayers) local neededyes = (numplayers / 2) + 1 local neededno = (numplayers+1) / 2 if numplayers == 1 neededyes = 1 neededno = 1 end if server.globals.vote.no >= neededno return 1 elseif server.globals.vote.yes >= neededyes return 2 elseif server.globals.vote.total >= numplayers return 3 else return 0 end end local function Cmd_Showvotecmds (p) local num = #utility.votablecmds.name if num == nil CONS_Printf(p, "No votable commands in the list.") return else CONS_Printf(p, "Votable commands:") local starred = " " for x=1,num if utility.votablecmds.allow[x].value == 1 starred = " " .. "\x82" .. "*" .. "\x80" .. " " else starred = " " end CONS_Printf(p, " " .. tostring(x) .. ":" .. starred .. utility.votablecmds.name[x]) end CONS_Printf(p, "A " .. "\x82" .. "*" .. "\x80" .. " represents a command votable by all players.") end end local function Cmd_Callvote (p, command, target, reason) if p.utility == nil then return end local numvoters = utility.TotalVoters() if numvoters == 0 CONS_Printf(p, "Nobody is around for the vote. Try again later.") return end if utility.IsAdmin(p, true) == false --You can call a vote anyway if you're an admin. if p.spectator == true and utility.vote.spectatorvote.value == 0 CONS_Printf(p, "The server does not allow spectators to call a vote.") return end if numvoters < utility.votablecmds.minvoters.value CONS_Printf(p, "The server requires at least " .. tostring(utility.votablecmds.minvoters.value) .. " players to start a vote.") return end end if command == nil CONS_Printf(p, "Usage: callvote [reason]") CONS_Printf(p, "Calls a vote. The reason is optional.") return end if reason == nil reason = "No reason given" end if splitscreen CONS_Printf(p, "This command is redundant in splitscreen play.") elseif not multiplayer CONS_Printf(p, "This command is only usable in multiplayer.") else if p.utility.votedelay < 1 or p == admin or p == server local cmd = string.lower(command) local num = #utility.votablecmds.name if num == nil print("This server has no votable commands. At all. Hah, is this a joke?") return end for x=1,num if utility.votablecmds.name[x] == cmd if utility.votablecmds.allow[x].value == 1 or p == admin or p == server utility.votablecmds.logic[x](p, command, target, reason) else CONS_Printf(p, "The server/admin has disabled this command from vote calling.") end return end end CONS_Printf(p, "This isn't a votable command.") else CONS_Printf(p, "You can call a vote after " .. tostring(p.utility.votedelay / TICRATE) .. " seconds.") end end end local function Cmd_Afk (p) if p.utility == nil then return end if multiplayer == true if gametype == GT_COOP utility.BecomeAFK(p) else CONS_Printf(p, "Only usable in Co-Op. Sorry!") end else CONS_Printf(p, "This command is exclusive to multiplayer; use pause instead.") end end local function Cmd_Afk2 (p) if p.utility == nil then return end if multiplayer == true if gametype == GT_COOP utility.BecomeAFK(players[1]) else CONS_Printf(p, "Only usable in Co-Op. Sorry!") end else CONS_Printf(p, "This command is exclusive to multiplayer; use pause instead.") end end local function Cmd_Silence (p, node, seconds, reason) if p.utility == nil then return end if utility.IsAdmin(p) == false then return end if splitscreen CONS_Printf(p, "This command is redundant in splitscreen play.") elseif not multiplayer CONS_Printf(p, "This command is only usable in multiplayer.") else local newseconds = 0 if tonumber(seconds) == nil newseconds = 60 else newseconds = tonumber(seconds) end if newseconds < 5 and newseconds > 61356675 --Any higher and we will wrap! Do not want. newseconds = 60 end if F_FindPlayer(node) >= 0 A_SilencePlayer(p, players[F_FindPlayer(node)], newseconds, reason) else CONS_Printf(p, "That player does not exist.") end end end local function Cmd_Unsilence (p, node) if p.utility == nil then return end if utility.IsAdmin(p) == false then return end if splitscreen CONS_Printf(p, "This command is redundant in splitscreen play.") elseif not multiplayer CONS_Printf(p, "This command is only usable in multiplayer.") else if F_FindPlayer(node) >= 0 A_UnsilencePlayer(p, players[F_FindPlayer(node)]) else CONS_Printf(p, "That player does not exist.") end end end local function Cmd_Ignore (p, node) if p.utility == nil then return end if splitscreen CONS_Printf(p, "This command is redundant in splitscreen play.") elseif not multiplayer CONS_Printf(p, "This command is only usable in multiplayer.") elseif F_FindPlayer(node) == -1 CONS_Printf(p, "That player does not exist.") elseif players[F_FindPlayer(node)] == p CONS_Printf(p, "You cannot ignore yourself.") else if F_FindPlayer(node) >= 0 p.utility.ignore[F_FindPlayer(node)] = true CONS_Printf(p, "Now ignoring node " .. tostring(F_FindPlayer(node)) .. ".") end end end local function Cmd_Unignore (p, node) if p.utility == nil then return end if splitscreen CONS_Printf(p, "This command is redundant in splitscreen play.") elseif not multiplayer CONS_Printf(p, "This command is only usable in multiplayer.") elseif F_FindPlayer(node) == -1 CONS_Printf(p, "That player does not exist..") else if F_FindPlayer(node) >= 0 p.utility.ignore[F_FindPlayer(node)] = false CONS_Printf(p, "Not ignoring node " .. tostring(F_FindPlayer(node)) .. " anymore.") end end end --Change the music for everyone in the server. local function Cmd_Changemus (p, str) if p.utility == nil then return end if utility.IsAdmin(p) == false then return end if str == nil then return end for play in players.iterate COM_BufInsertText(play, "tunes " .. str) end print("Music changed to " .. str .. " by " .. utility.TeamedName(p) .. ".") end --Change the sky for everyone in the server. local function Cmd_Changesky (p, str) if p.utility == nil then return end if utility.IsAdmin(p) == false then return end if str == nil then return end if tonumber(str) == nil CONS_Printf(p, "Supply a sky number.") return end P_SetupLevelSky(tonumber(str)) print("Sky changed to " .. str .. " by " .. utility.TeamedName(p) .. ".") end --Refreshes the Notice board if there is none left to be displayed. local function F_RestartNotice() server.globals.notice.num = 0 server.globals.notice.msg = {[0] = "None"} server.globals.notice.duration = {[0] = 0} server.globals.notice.delay = {[0] = 0} server.globals.notice.color = {[0] = 0} server.globals.notice.current = 0 end --Post a notice. Color currently unused. utility.AddNotice = function(p, message, seconds, delay, color) local num = server.globals.notice.num + 1 server.globals.notice.msg[num] = message server.globals.notice.duration[num] = seconds*TICRATE + delay*TICRATE server.globals.notice.delay[num] = seconds*TICRATE server.globals.notice.color[num] = color server.globals.notice.num = num CONS_Printf(p, "Put notice #" .. tostring(server.globals.notice.num) .. " into the Noticeboard queue.") end local function F_Concatenate (p, nilmsg, ...) local str = "" if ... == nil CONS_Printf(p, nilmsg) return else local words = {...} for a=1,#words do str = $1 .. words[a] .. " " --The leading space is okay, I guess. It won't matter too much... end end if str == "" CONS_Printf(p, nilmsg) end return str end local function Cmd_SayAdmin (p, ...) if p.utility == nil then return end if utility.IsAdmin(p) == false then return end local message = F_Concatenate(p, "sayadmin : send a message to all other admins", ...) if message == nil then return end utility.NewMsg(p, 4, nil, message) --Send an admin message. end --Sends a notice to the noticeboard. Much more featured than csay. local function Cmd_Notice (p, seconds, message, delay) if p.utility == nil then return end if utility.IsAdmin(p) == false then return end if seconds == nil or message == nil CONS_Printf(p, "Syntax: notice [delay]") return else if tonumber(seconds) == nil CONS_Printf(p, "Syntax: notice [delay]") return end end local sec = tonumber(seconds) local wait = tonumber(delay) if wait == nil wait = 0 end local msg = "= " .. utility.TeamedName(p) .. " says =\n" .. string.gsub(message, "\\", "\n") if sec == 0 F_RestartNotice() print(utility.TeamedName(p) .. " reset the notice queue with reason \"" .. message .. "\"") else if sec > 60 or sec < 0 CONS_Printf(p, "Please use a time between 1-60 seconds. This is to avoid notice-hogging. 0 resets the notice queue.") return else if wait > 60 or wait < 0 CONS_Printf(p, "Please use a delay between 1-60 seconds. This is to avoid notice-hogging. 0 resets the notice queue.") return else utility.AddNotice(p, msg, sec, wait, 0) end end end end --Legacy command replacement for csay. local function Cmd_Csay (p, ...) local message = F_Concatenate(p, "csay : Display a 5-second annoyance text.", ...) if message ~= nil Cmd_Notice(p, 5, message, 0) end end --Lists commands and variables in Utility local function Cmd_Util (p, cmd) local val = "" if cmd == nil val = "no_command" else if string.lower(cmd) == "short" val = "short" else val = cmd end end if val ~= "no_command" and val ~= "short" local text = string.lower(val) for a=1,utility.cmdlist.num do if val == utility.cmdlist.name[a] CONS_Printf(p, "Help for command \"" .. val .. "\":") CONS_Printf(p, utility.cmdlist.helptext[a]) return end end for b=1,utility.varlist.num do if val == utility.varlist.name[b] CONS_Printf(p, "Help for variable \"" .. val .. "\":") CONS_Printf(p, utility.varlist.helptext[b]) return end end CONS_Printf(p, "Couldn't find that command/variable. Try typing \"util\" in the console for a list, or \"util short\" for a shortform list.") else local list = " :" CONS_Printf(p, "\x82" .. "List of Utility commands:") for a=1,utility.cmdlist.num do if val == "short" list = $1 .. " " .. utility.cmdlist.name[a] else CONS_Printf(p, " " .. tostring(a) .. ": " .. utility.cmdlist.name[a]) end end if val == "short" CONS_Printf(p, list) end list = " :" CONS_Printf(p, "\x82" .. "List of Utility variables:") for b=1,utility.varlist.num do if val == "short" list = $1 .. " " .. utility.varlist.name[b] else CONS_Printf(p, " " .. tostring(b) .. ": " .. utility.varlist.name[b]) end end if val == "short" CONS_Printf(p, list) end CONS_Printf(p, "\x82" .. "For more information about a command or variable, use \"util \" to show its help text.") end end --Returns the version. local function Cmd_Utilversion (p) CONS_Printf(p, "Version" .. utility.version .. "(Iteration" .. utility.iteration .. ")") if utility.subversion == true CONS_Printf(p, "\x83" .. "This is a sub-version. (Not an MB release)") end end --Update server name. local function Cmd_Updatename (p) if server.isdedicated == true COM_BufInsertText(server, "dediname " .. server.utility.dediname) --Update the servername. end end local function F_ResetVote () server.globals.vote.time = -3500 server.globals.vote.cmd = "Nothing" server.globals.vote.exec = "english" server.globals.vote.yes = 0 server.globals.vote.no = 0 server.globals.vote.total = 0 server.globals.vote.reason = "None." server.globals.vote.started = false server.globals.vote.result = "none" end addHook("MapChange", function (mapnum) if server.globals ~= nil if server.globals.vote.cmd == "exitlevel" or string.lower(string.sub(server.globals.vote.cmd, 1, 3)) == "map" F_ResetVote() --End a map vote or exitlevel vote early if the map gets changed. end for player in players.iterate if player.utility ~= nil player.utility.levelcomplete = false end end end end) --Ironically this means the dedicated server can have colored names where regular players cannot. local function Cmd_Dediname (p, newname) if p.utility == nil then return end if server.isdedicated == false CONS_Printf(p, "This is only usable if the server is dedicated.") elseif server.isdedicated == true if p == server local oldname = server.utility.dediname if string.lower(server.utility.dediname) ~= string.sub(string.lower(newname), 1, 23) print(server.utility.dediname .. " renamed to " .. newname) server.utility.dediname = string.sub(newname, 1, 23) server.name = newname end else CONS_Printf(p, "Only the server can do this.") end end end --A helper for the ammo macro. Speaking of macros... utility.weapontoname = { [0] = "Ringslinger", [WEP_AUTO] = "Automatic", [WEP_BOUNCE] = "Bounce", [WEP_SCATTER] = "Scatter", [WEP_GRENADE] = "Grenade", [WEP_EXPLODE] = "Explosion", [WEP_RAIL] = "Rail" } utility.macrotable = {} utility.macrotable["match"] = {} utility.macrotable["logic"] = {} utility.macrotable["sample"] = {} utility.macrotable["num"] = 0 --This is chat macro! It will take strings for you and turn them into something else based on conditions and logic. You can add your own with this function. utility.AddChatMacro = function(matchtext, logic, sample) local num = utility.macrotable.num utility.macrotable.match[num+1] = matchtext utility.macrotable.logic[num+1] = logic utility.macrotable.sample[num+1] = false if sample ~= nil if sample == true utility.macrotable.sample[num+1] = true end end utility.macrotable.num = $1 + 1 print("Added chat macro \"" .. matchtext .. "\" as ID " .. tostring(num+1) .. ".") end local function CM_White (player) return "\x80" end local function CM_Purple (player) return "\x81" end local function CM_Yellow (player) return "\x82" end local function CM_Green (player) return "\x83" end local function CM_Blue (player) return "\x84" end local function CM_Red (player) return "\x85" end local function CM_Grey (player) return "\x86" end local function CM_Orange (player) return "\x87" end --Bunch of macros to replace common game metrics. local function CM_Rings (player) if player.health < 1 return "0" else return tostring(player.health - 1) end end local function CM_Health (player) return tostring(player.health) end local function CM_Lives (player) return tostring(player.lives) end local function CM_Random (player) return tostring(P_RandomRange(1,100)) end local function CM_Location (player) if player.mo == nil return "Ethereal Plane" end return "X:" .. tostring(player.mo.x/FRACUNIT) .. " Y:" .. tostring(player.mo.y/FRACUNIT) .. " Z:" .. tostring(player.mo.x/FRACUNIT) end local function CM_Score (player) return tostring(player.score) end local function CM_Time (player) local centi = tostring(G_TicsToCentiseconds(player.realtime)) local sec = tostring(G_TicsToSeconds(player.realtime)) if G_TicsToCentiseconds(player.realtime) < 10 centi = "0" .. tostring(G_TicsToCentiseconds(player.realtime)) end if G_TicsToSeconds(player.realtime) < 10 sec = "0" .. tostring(G_TicsToSeconds(player.realtime)) end return tostring(G_TicsToMinutes(player.realtime, true)) .. ":" .. sec .. "." .. centi end local function CM_Name (player) return tostring(player.name) end local function CM_Weapon (player) if player.currentweapon == 0 and player.powers[pw_infinityring] > 0 return "Infinity" else return utility.weapontoname[player.currentweapon] end end local function CM_Ammo (player) if player.currentweapon == nil return "nil" end if player.currentweapon == 0 if player.powers[pw_infinityring] > 0 return tostring(player.powers[pw_infinityring]) else local rings = (player.health - 1) if rings < 0 rings = 0 end return tostring(rings) end else return tostring(player.powers[player.currentweapon + 10]) end end local function CM_Coinflip (player) local a = P_RandomRange(0,1) if a == 0 return "Heads" else return "Tails" end end local function CM_Random (player) return tostring(P_RandomRange(1,100)) end utility.AddChatMacro("$rings", CM_Rings) utility.AddChatMacro("$health", CM_Health) utility.AddChatMacro("$lives", CM_Lives) utility.AddChatMacro("$location", CM_Location) utility.AddChatMacro("$score", CM_Score) utility.AddChatMacro("$time", CM_Time) utility.AddChatMacro("$name", CM_Name) utility.AddChatMacro("$weapon", CM_Weapon) utility.AddChatMacro("$ammo", CM_Ammo) utility.AddChatMacro("$coin", CM_Coinflip) utility.AddChatMacro("$roll", CM_Random) utility.AddChatMacro("/ca", CM_White, true) utility.AddChatMacro("/cb", CM_Blue, true) utility.AddChatMacro("/cc", CM_Purple, true) utility.AddChatMacro("/cd", CM_Red, true) utility.AddChatMacro("/ce", CM_Yellow, true) utility.AddChatMacro("/cf", CM_Orange, true) utility.AddChatMacro("/cg", CM_Green, true) utility.AddChatMacro("/ch", CM_Grey, true) utility.ChatMacro = function(player, msg) --print(tostring(#utility.macrotable.match)) if #utility.macrotable.match == nil --Can't replace without data here. return msg end local msg2 = msg local replacement = "" for x=1,#utility.macrotable.match --print("Old: " .. msg2 .. " | " .. utility.macrotable.match[x] .. " | " .. utility.macrotable.logic[x](player)) msg2 = string.gsub($1, utility.macrotable.match[x], utility.macrotable.logic[x](player)) --print("New: " .. msg2) end return msg2 end local function Cmd_MacroList (p) CONS_Printf(p, "\x82" .. "Chat macros are strings that, when said, will be replaced with a value. Good for binds! is a placeholder string. It does not appear, and is only there to show an effect.") CONS_Printf(p, "\x82" .. "Macro : Example") for a=1,utility.macrotable.num do local sample = utility.macrotable.logic[a](p) if utility.macrotable.sample[a] == true sample = $1 .. "" end CONS_Printf(p, utility.macrotable.match[a] .. " : " .. sample) end end --Adding all the commands in the table! Whew. utility.AddUtilCommand("util", "Lists you Utility's additional commands and variables. If you supply a command or console variable as a parameter, it will give you a description on said command/variable. If you supply the parameter \"short\", you will be given a condensed list, like seen in the vanilla help menu. NOTE: To add your own commands to this list, please use utility.AddUtilCommand() and utility.AddUtilVar()", Cmd_Util, 0) utility.AddUtilCommand("csay", "This command lets you send a notice to all players. Syntax: csay - This will send a notice command for 5 seconds. The notice command will give you the ability to change the time frame. Only usable by the Server/Admin", Cmd_Csay, 0) utility.AddUtilCommand("notice", "This command lets you add a notice to the global notice queue. Your name will be added to it automatically. Syntax: notice [delay] - seconds is the number of seconds you want it to be displayed for, and message is the message to show. A delay in seconds between having this appear is optional. If not supplied, it will be zero. To clear all notices in the queue, use a seconds value of 0. Only usable by the Server/Admin", Cmd_Notice, 0) utility.AddUtilCommand("gametype", "This command, when not given a parameter, will check the current gametype. When given a parameter, it will seek the first available map for that gametype, and warp you to it. You can use either names, or numbers. coop=0, competition=1, race=2, match=3, teammatch=4, tag=5, hideandseek=6, ctf=7", Cmd_Gametype, 0) utility.AddUtilCommand("vote", "A command that will let you vote in a callvote poll. Use a parameter of yes and no to vote yes and no respectively. This command is only usable in non-splitscreen multiplayer, when a poll is in progress,", Cmd_Vote, 0) utility.AddUtilCommand("cancelvote", "This forcibly stops a poll that is in progress, nullifying its result. This command is only usable in non-splitscreen multiplayer, when a poll is in progress.", Cmd_Cancelvote, 0) utility.AddUtilCommand("votepermission", "Allows the control of the vote permission variables. Syntax: votepermission [allow/deny] - When allow/deny is not specified, it will tell you the current setting. You can see the list of votable commands with \"showvotecmds\". Alternatively, you can change the util_vote_* console variables.", Cmd_Votepermission, 0) utility.AddUtilCommand("afk", "Toggles you between AFK mode, which will make you transparent. You will also count towards exiting the level. Each time you leave AFK mode, you have a 30-second cooldown before you can use it again. Multiplayer Co-Op only.", Cmd_Afk, 0) utility.AddUtilCommand("afk2", "Player 2 equivalent of command \"afk\". Toggles you between AFK mode, which will make you transparent. You will also count towards level exiting. Each time you leave AFK mode, you have a 30-second cooldown before you can use it again. Multiplayer Co-Op only.", Cmd_Afk2, 2) utility.AddUtilCommand("showvotecmds", "This will list the commands that can be used in the callvote command. It will also list which commands can be polled by anyone, or the server/admin only.", Cmd_Showvotecmds, 0) utility.AddUtilCommand("callvote", "This allows a player to start a vote to execute a specified command. To respond in the poll, use \"vote yes\" or \"vote no\" in the console. Syntax: callvote [reason] - The parameter for the command is not always present; take \"exitlevel\" for example, which has no parameter. The reason is optional. If you are not the server or an admin, you incurr a 300 second cooldown before the next time you can call a vote.", Cmd_Callvote, 0) utility.AddUtilCommand("silence", "Silences a user, disallowing them from using normal chat and team chat. NOTE: Does not stop sayto. It is up to individual players to block private messages. Syntax: silence [seconds] [reason] - The name of the player can be used instead of the node number. Reason is optional. If the seconds value is not given, it will default to 60.", Cmd_Silence, 0) utility.AddUtilCommand("unsilence", "The reverse of silencing a user, prematurely ending their silent treatment. Syntax: silence - The name of the player can be used instead of the node number.", Cmd_Unsilence, 0) utility.AddUtilCommand("ignore", "This will permanently ignore the target player and all their communication. NOTE: This is only local to you, others will be able to see their chatter if they allow it. If the target player leaves, the ignore is lifted. Syntax: ignore - The name of the player can be used instead of the node number.", Cmd_Ignore, 0) utility.AddUtilCommand("unignore", "This will undo an ignore on the target player. Syntax: unignore - The name of the player can be used instead of the node number.", Cmd_Unignore, 0) utility.AddUtilCommand("changemus", "Changes the music for everyone, like using tunes in the console. NOTE: Not updated for joining players.", Cmd_Changemus, 0) utility.AddUtilCommand("changesky", "Changes the sky for everyone.", Cmd_Changesky, 0) utility.AddUtilCommand("sayadmin", "Allows anyone with @ or higher terminal permission or vanilla admin/host privilege to talk amongst each other in an admin-only chat.", Cmd_SayAdmin, 0) utility.AddUtilCommand("macrolist", "Displays all the currently available chat macro replacements along with example return values.", Cmd_MacroList, 0) utility.AddUtilCommand("dediname", "Rename as the dedicated server. Only useable by a dedicated server, obviously.", Cmd_Dediname, 0) utility.AddUtilCommand("updatename", "Tool for updating the dedicated servername to all players as a workaround to server variable updating.", Cmd_Updatename, 0) utility.AddUtilCommand("utilversion", "Prints out your current version of Utility.", Cmd_Utilversion, 0) utility.TryMsg = function(source, msgtype, target) --See if we can send this message to this player. This does not actually send a message! if source.utility == nil then return false end if target.utility == nil then return false end if target.utility ~= nil if target.utility.ignore[utility.PlayerToNode(source)] == true and source ~= target return false end end if msgtype == 1 if utility.ExtendedTeam(source) == utility.ExtendedTeam(target) or utility.ExtendedTeam(target) == 0 --Spectator return true else return false end elseif msgtype == 4 --Admin chat. if utility.IsAdmin(target, true) == true return true else return false end end return true end utility.MsgOutput = function(player, msg, msgtype, target, name1, name2) local message = "" local action = false local name = utility.TeamedName(player) if string.lower(string.sub(msg, 1, 4)) == "/me " message = "\x82" .. "* %s " .. "\x82" .. string.sub(msg, 5) action = true else message = msg end if msgtype == 0 if action == true return "\x03" .. string.format(message, name) else return "\x03" .. string.format(utility.msg.overlay[7], name, msg) end elseif msgtype == 1 if action == true return "\x03" .. string.format(utility.msg.overlay[utility.ExtendedTeam(player)], string.format(message, name)) else return "\x03" .. string.format(utility.msg.overlay[utility.ExtendedTeam(player)], player.name .. ": " .. msg) end elseif msgtype == 2 if player == target return string.format(utility.msg.overlay[6], name1, name2, string.format(message, name)) else return string.format(utility.msg.overlay[8], name1, name2, string.format(message, name)) end elseif msgtype == 4 if action == true return "\x03" .. string.format(utility.msg.overlay[9], string.format(message, name)) else return "\x03" .. string.format(utility.msg.overlay[9], name .. ": " .. msg) end end end utility.DoMsg = function(source, target, msg, msgtype, name1, name2) --Actually sends a message if it can. if source.utility == nil then return end if target.utility == nil then return end if utility.TryMsg(source, msgtype, target) == true if msgtype == 2 S_StartSound(nil, 154, target) end CONS_Printf(target, utility.MsgOutput(source, msg, msgtype, target, name1, name2)) end end utility.PostMsgToPlayers = function(source, msg, msgtype, target) if msgtype == 2 utility.DoMsg(source, source, msg, msgtype, utility.TeamedName(source), utility.TeamedName(target)) utility.DoMsg(source, target, msg, msgtype, utility.TeamedName(source), utility.TeamedName(target)) else for player in players.iterate utility.DoMsg(source, player, msg, msgtype) end if server.isdedicated == true utility.DoMsg(source, server, msg, msgtype) end end end utility.NewMsg = function(source, flags, target, msg) if source.utility == nil CONS_Printf(source, "Your player variables are not ready yet!") return end if utility.spam.repeats.value == 0 and source ~= server if source.utility.spam.lastmsg == nil source.utility.spam.lastmsg = msg else if source.utility.spam.lastmsg == msg CONS_Printf(source, "You're repeating yourself.") return true end end end source.utility.spam.lastmsg = msg if flags ~= 2 --Allow private chatter and flags ~= 4 --Allow admin chatter. if source.utility ~= nil if source.utility.silenced == true CONS_Printf(source, "You cannot send global or team messages for " .. tostring(source.utility.quiettime / TICRATE) .. " seconds.") return true end end end local message = utility.ChatMacro(source, msg) utility.PostMsgToPlayers(source, message, flags, target) if utility.spam.time.value == 0 or utility.spam.num.value == 0 source.utility.spam.history = {} else table.insert(source.utility.spam.history, 1, source.jointime) local numlines = #source.utility.spam.history while numlines > utility.spam.num.value table.remove(source.utility.spam.history, (utility.spam.num.value + 1)) --Remove the last entry numlines = $1 - 1 end if numlines == utility.spam.num.value if abs(source.utility.spam.history[1] - source.utility.spam.history[numlines]) <= (utility.spam.time.value * TICRATE) --You're spamming! and source ~= server --But we can "trust" the server. If you don't like the server spamming, go find another server or make your own. Science. if source.utility.quiettime == 0 A_SilencePlayer(nil, source, 60, utility.msg.spam[flags]) end end end end return true end addHook("PlayerMsg", utility.NewMsg) local client local clientval = 0 utility.CheckRedFlag = function(mobj, touch) for play in players.iterate if play.ctfteam == 1 return false end end touch.player.utility.noflag = TICRATE*5 return true end utility.CheckBlueFlag = function(mobj, touch) for play in players.iterate if play.ctfteam == 2 return false end end touch.player.utility.noflag = TICRATE*5 return true end --Stops players from picking up a CTF flag when the other team has no players. addHook("TouchSpecial", utility.CheckRedFlag, MT_REDFLAG) addHook("TouchSpecial", utility.CheckBlueFlag, MT_BLUEFLAG) addHook("ThinkFrame", do for player in players.iterate if player.utility == nil --Set up the player's variables. player.utility = {} player.utility["silenced"] = false player.utility["quiettime"] = 0 --Silence time still remaining. player.utility["ignore"] = {} --Ignore table. player.utility["voted"] = 0 player.utility["votedelay"] = 30 * TICRATE player.utility["afk"] = false player.utility["afkdelay"] = 0 player.utility["noflag"] = 0 player.utility.lock = {} --For AFK mode. player.utility.lock["x"] = 0 player.utility.lock["y"] = 0 player.utility.lock["z"] = 0 player.utility.lock["momx"] = 0 player.utility.lock["momy"] = 0 player.utility.lock["momz"] = 0 player.utility.spam = {} player.utility.spam["history"] = {} --List of previous times the user sent a message. Used for spam prevention. player.utility.levelcomplete = false for a=0,31 do --Node ignore table player.utility.ignore[a] = false end COM_BufInsertText(player, "bind \"-\" \"vote yes\"") --I've had debate over these being here. But, quite frankly, a lot more have been making use of it COM_BufInsertText(player, "bind \"=\" \"vote no\"") --than people complaining about it, so it's going to stay there. Sorry. end end if utility.coop.timelimit.value ~= 0 and gametype == GT_COOP local tics = utility.coop.timelimit.value * TICRATE * 60 if tics == leveltime COM_BufInsertText(server, "exitlevel") end end server.isdedicated = false if server.mo == nil and server.jointime == 0 and server.spectator == false server.isdedicated = true if server.utility == nil --Set up the server's variables in case this is a dedicated server. server.utility = {} server.utility["silenced"] = false server.utility["quiettime"] = 0 server.utility["ignore"] = {} server.utility["voted"] = 0 server.utility["votedelay"] = 30 * TICRATE server.utility["afk"] = false server.utility["dediname"] = "Server" server.utility.spam = {} server.utility.spam["history"] = {} end else server.isdedicated = false end if server.globals == nil --Set up the global variables used by everyone. server.globals = {} server.globals["vote"] = {} server.globals.vote["time"] = -3500 server.globals.vote["cmd"] = "Nothing" server.globals.vote["exec"] = "The detest" server.globals.vote["yes"] = 0 server.globals.vote["no"] = 0 server.globals.vote["total"] = 0 server.globals.vote["reason"] = "None." server.globals.vote["started"] = false server.globals.vote["result"] = "none" server.globals["notice"] = {} server.globals.notice["num"] = 0 server.globals.notice["msg"] = {[0] = "None"} server.globals.notice["duration"] = {[0] = 0} server.globals.notice["delay"] = {[0] = 0} --How long to wait until the next notice. server.globals.notice["color"] = {[0] = 0} --To be added! server.globals.notice["current"] = 0 --The notice number we are currently on. end if server.globals ~= nil if server.globals.vote.started == true if server.globals.vote.time > 0 server.globals.vote.time = $1 - 1 local numplayers = 0 if Vote_IsVoteDone(utility.TotalVoters()) > 0 server.globals.vote.time = 0 end elseif server.globals.vote.time > -175 if server.globals.vote.yes > server.globals.vote.no server.globals.vote.result = "yes" else server.globals.vote.result = "no" end server.globals.vote.time = $1 - 1 elseif server.globals.vote.time == -175 if server.globals.vote.result == "yes" COM_BufInsertText(server, server.globals.vote.exec) end F_ResetVote() end end if server.globals.notice.num > 0 local cur = server.globals.notice.current --print(tostring(cur) .. " " .. tostring(server.globals.notice.duration[cur])) if server.globals.notice.duration[cur] == 0 server.globals.notice.current = cur + 1 if server.globals.notice.msg[cur+1] == nil --Notice board is finished. F_RestartNotice() server.globals.notice.current = 0 else print(server.globals.notice.msg[cur+1]) end else if server.globals.notice.duration[cur] == server.globals.notice.delay[cur] S_StartSound(nil, 140) end server.globals.notice.duration[cur] = $1 - 1 end end end if server.isdedicated == true for player in players.iterate if client ~= nil if player == client and clientval == 2 COM_BufInsertText(player, "updatename") --Update the dedicated server's name. clientval = 3 end end end end for player in players.iterate --Player-based timers and triggers. if player.utility ~= nil --Must be a prepared player. if gametype ~= GT_COOP and player.utility.afk == true player.utility.afk = false CONS_Printf(player, "You are no longer AFK, as this game mode has spectating.") end if player.utility.afk == true if leveltime == 1 player.utility.lock["x"] = player.mo.x player.utility.lock["y"] = player.mo.y player.utility.lock["z"] = player.mo.z player.utility.lock["momx"] = 0 player.utility.lock["momy"] = 0 player.utility.lock["momz"] = 0 player.exiting = 1 end if leveltime > 1 P_TeleportMove(player.mo, player.utility.lock.x, player.utility.lock.y, player.utility.lock.z) player.mo.momx = player.utility.lock.momx player.mo.momy = player.utility.lock.momy player.mo.momz = player.utility.lock.momz end if player.mo ~= nil player.mo.flags2 = $ | MF2_SHADOW end elseif player.utility.afk == false if player.exiting > 0 player.utility.levelcomplete = true end if player.utility.afkdelay > 0 player.utility.afkdelay = $1 - 1 end end player.utility.votedelay = $1 - 1 if player.utility.silenced == true player.utility.quiettime = $1 - 1 if player.utility.quiettime == 0 player.utility.silenced = false CONS_Printf(player, "Your silence has worn off.") if server.isdedicated == true CONS_Printf(server, "\x87" .. "ADMIN: " .. "\x80" .. "The silence for " .. utility.TeamedName(player) .. " has worn off.") end for p in players.iterate if utility.IsAdmin(p, true) CONS_Printf(p, "\x87" .. "ADMIN: " .. "\x80" .. "The silence for " .. utility.TeamedName(player) .. " has worn off.") end end end end if player.utility.noflag > 0 player.utility.noflag = $1 - 1 end end end for a=0,31 do --Node ignore table if players[a] == nil if nodestatus[a] == true for player in players.iterate if player.utility ~= nil player.utility.ignore[a] = false --Reset ignores. end end end nodestatus[a] = false else nodestatus[a] = true end end end) hud.add(function (v, player) if server.globals ~= nil local num = server.globals.notice.current if num > 0 if server.globals.notice.duration[num] <= server.globals.notice.delay[num] v.drawString(40, 120, server.globals.notice.msg[num], 0) end end if server.globals.vote.started == true if server.globals.vote.time > 0 v.drawString(200, 8, "VOTE NOW", V_MONOSPACE|V_REDMAP, "center") v.drawString(200, 16, server.globals.vote.cmd, 0, "center") v.drawString(200, 24, "Reason: " .. server.globals.vote.reason, 0, "center") v.drawString(200, 32, "Time left: " .. tostring(server.globals.vote.time / TICRATE), V_REDMAP, "center") v.drawString(200, 40, "Yes: " .. server.globals.vote.yes .. " No: " .. server.globals.vote.no, 0, "center") elseif server.globals.vote.time > -175 if server.globals.vote.result == "yes" v.drawString(200, 16, "VOTE PASSED", V_MONOSPACE|V_REDMAP, "center") else v.drawString(200, 16, "VOTE FAILED", V_MONOSPACE|V_REDMAP, "center") end end end if utility.coop.timelimit.value ~= 0 and gametype == GT_COOP local tics = (utility.coop.timelimit.value * TICRATE * 60) - leveltime if tics < (60*TICRATE) and tics > 0 if tics < (10*TICRATE) v.drawString(160, 192, "EXITLEVEL IN " .. tostring(tics / TICRATE) .. " SECONDS.", V_REDMAP, "center") else v.drawString(160, 192, "EXITLEVEL IN " .. tostring(tics / TICRATE) .. " SECONDS.", V_YELLOWMAP, "center") end end end end if player.utility ~= nil if player.utility.noflag > 0 v.drawString(160, 192, "No players on the other team!", V_REDMAP, "center") end end end) hud.add(function (catch, player) client = player if player.jointime < 70 clientval = 0 else if clientval < 2 clientval = $1 + 1 end end end) if utility.subversion == true print("\x83" .. "This is a sub-version (Not an MB release) so please report bugs you find.") end