diff --git a/plugins/basecomm.sp b/plugins/basecomm.sp
new file mode 100644
index 00000000..a1b02019
--- /dev/null
+++ b/plugins/basecomm.sp
@@ -0,0 +1,414 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Communication Plugin
+ * Provides fucntionality for controlling communication on the server
+ *
+ * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program 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
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include
+#include
+
+#pragma semicolon 1
+
+public Plugin:myinfo =
+{
+ name = "Basic Comm Control",
+ author = "AlliedModders LLC",
+ description = "Provides methods of controllingg communication.",
+ version = SOURCEMOD_VERSION,
+ url = "http://www.sourcemod.net/"
+};
+
+new bool:g_Muted[MAXPLAYERS+1]; // Is the player muted?
+new bool:g_Gagged[MAXPLAYERS+1]; // Is the player gagged?
+
+new Handle:g_Cvar_Deadtalk = INVALID_HANDLE; // Holds the handle for sm_deadtalk
+new Handle:g_Cvar_Alltalk = INVALID_HANDLE; // Holds the handle for sv_alltalk
+new bool:g_Hooked = false; // Tracks if we've hooked events for deadtalk
+
+public OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+ LoadTranslations("basecomm.phrases");
+
+ g_Cvar_Deadtalk = CreateConVar("sm_deadtalk", "0", "Controls how dead communicate. 0 - Off. 1 - Dead players ignore teams. 2 - Dead players talk to living teammates.", 0, true, 0.0, true, 2.0);
+ g_Cvar_Alltalk = FindConVar("sv_alltalk");
+
+ RegConsoleCmd("say", Command_Say);
+ RegConsoleCmd("say_team", Command_Say);
+
+ RegAdminCmd("sm_mute", Command_Mute, ADMFLAG_CHAT, "sm_mute - Removes a player's ability to use voice.");
+ RegAdminCmd("sm_gag", Command_Gag, ADMFLAG_CHAT, "sm_gag - Removes a player's ability to use chat.");
+ RegAdminCmd("sm_silence", Command_Silence, ADMFLAG_CHAT, "sm_silence - Removes a player's ability to use voice or chat.");
+
+ RegAdminCmd("sm_unmute", Command_Unmute, ADMFLAG_CHAT, "sm_unmute - Restores a player's ability to use voice.");
+ RegAdminCmd("sm_ungag", Command_Ungag, ADMFLAG_CHAT, "sm_ungag - Restores a player's ability to use chat.");
+ RegAdminCmd("sm_unsilence", Command_Unsilence, ADMFLAG_CHAT, "sm_unsilence - Restores a player's ability to use voice and chat.");
+
+ HookConVarChange(g_Cvar_Deadtalk, ConVarChange_Deadtalk);
+}
+
+public ConVarChange_Deadtalk(Handle:convar, const String:oldValue[], const String:newValue[])
+{
+ if (GetConVarInt(g_Cvar_Deadtalk))
+ {
+ HookEvent("player_spawn", Event_PlayerSpawn, EventHookMode_Post);
+ HookEvent("player_death", Event_PlayerDeath, EventHookMode_Post);
+ g_Hooked = true;
+ }
+ else if (g_Hooked)
+ {
+ UnhookEvent("player_spawn", Event_PlayerSpawn);
+ UnhookEvent("player_death", Event_PlayerDeath);
+ g_Hooked = false;
+ }
+}
+
+
+public bool:OnClientConnect(client, String:rejectmsg[], maxlen)
+{
+ g_Gagged[client] = false;
+ g_Muted[client] = false;
+
+ return true;
+}
+
+public Action:Command_Say(client, args)
+{
+ if (client)
+ {
+ if (g_Gagged[client])
+ {
+ return Plugin_Handled;
+ }
+ }
+
+ return Plugin_Continue;
+}
+
+public Action:Command_Mute(client, args)
+{
+ if (args < 1)
+ {
+ ReplyToCommand(client, "[SM] Usage: sm_mute ");
+ return Plugin_Handled;
+ }
+
+ decl String:arg[64];
+ GetCmdArg(1, arg, sizeof(arg));
+
+ new target = FindTarget(client, arg);
+ if (target == -1)
+ {
+ return Plugin_Handled;
+ }
+
+ if (g_Muted[target])
+ {
+ ReplyToCommand(client, "%t", "Already Muted");
+ return Plugin_Handled;
+ }
+
+ g_Muted[target] = true;
+ SetClientListeningFlags(target, VOICE_MUTED);
+
+ decl String:name[64];
+ GetClientName(target, name, sizeof(name));
+
+ ShowActivity(client, "%t", "Player Muted", name);
+ ReplyToCommand(client, "%t", "Player Muted", name);
+ LogAction(client, target, "\"%L\" muted \"%L\"", client, target);
+
+ return Plugin_Handled;
+}
+
+public Action:Command_Gag(client, args)
+{
+ if (args < 1)
+ {
+ ReplyToCommand(client, "[SM] Usage: sm_gag ");
+ return Plugin_Handled;
+ }
+
+ decl String:arg[64];
+ GetCmdArg(1, arg, sizeof(arg));
+
+ new target = FindTarget(client, arg);
+ if (target == -1)
+ {
+ return Plugin_Handled;
+ }
+
+ if (g_Gagged[target])
+ {
+ ReplyToCommand(client, "%t", "Already Gagged");
+ return Plugin_Handled;
+ }
+
+ g_Gagged[target] = true;
+
+ decl String:name[64];
+ GetClientName(target, name, sizeof(name));
+
+ ShowActivity(client, "%t", "Player Gagged", name);
+ ReplyToCommand(client, "%t", "Player Gagged", name);
+ LogAction(client, target, "\"%L\" gagged \"%L\"", client, target);
+
+ return Plugin_Handled;
+}
+
+public Action:Command_Silence(client, args)
+{
+ if (args < 1)
+ {
+ ReplyToCommand(client, "[SM] Usage: sm_silence ");
+ return Plugin_Handled;
+ }
+
+ decl String:arg[64];
+ GetCmdArg(1, arg, sizeof(arg));
+
+ new target = FindTarget(client, arg);
+ if (target == -1)
+ {
+ return Plugin_Handled;
+ }
+
+ if (g_Gagged[target] && g_Muted[target])
+ {
+ ReplyToCommand(client, "%t", "Already Silenced");
+ return Plugin_Handled;
+ }
+
+ decl String:name[64];
+ GetClientName(target, name, sizeof(name));
+
+ if (!g_Gagged[target])
+ {
+ g_Gagged[target] = true;
+
+ ShowActivity(client, "%t", "Player Gagged", name);
+ ReplyToCommand(client, "%t", "Player Gagged", name);
+ LogAction(client, target, "\"%L\" gagged \"%L\"", client, target);
+ }
+
+ if (!g_Muted[target])
+ {
+ g_Muted[target] = true;
+ SetClientListeningFlags(target, VOICE_MUTED);
+
+ ShowActivity(client, "%t", "Player Muted", name);
+ ReplyToCommand(client, "%t", "Player Muted", name);
+ LogAction(client, target, "\"%L\" muted \"%L\"", client, target);
+ }
+
+ return Plugin_Handled;
+}
+
+public Action:Command_Unmute(client, args)
+{
+ if (args < 1)
+ {
+ ReplyToCommand(client, "[SM] Usage: sm_unmute ");
+ return Plugin_Handled;
+ }
+
+ decl String:arg[64];
+ GetCmdArg(1, arg, sizeof(arg));
+
+ new target = FindTarget(client, arg);
+ if (target == -1)
+ {
+ return Plugin_Handled;
+ }
+
+ if (!g_Muted[target])
+ {
+ ReplyToCommand(client, "%t", "Player Not Muted");
+ return Plugin_Handled;
+ }
+
+ g_Muted[target] = false;
+ if (GetConVarInt(g_Cvar_Deadtalk) == 1 && !IsPlayerAlive(target))
+ {
+ SetClientListeningFlags(target, VOICE_LISTENALL);
+ }
+ else if (GetConVarInt(g_Cvar_Deadtalk) == 2 && !IsPlayerAlive(target))
+ {
+ SetClientListeningFlags(target, VOICE_TEAM);
+ }
+ else
+ {
+ SetClientListeningFlags(target, VOICE_NORMAL);
+ }
+
+ decl String:name[64];
+ GetClientName(target, name, sizeof(name));
+
+ ShowActivity(client, "%t", "Player Unmuted", name);
+ ReplyToCommand(client, "%t", "Player Unmuted", name);
+ LogAction(client, target, "\"%L\" unmuted \"%L\"", client, target);
+
+ return Plugin_Handled;
+}
+
+public Action:Command_Ungag(client, args)
+{
+ if (args < 1)
+ {
+ ReplyToCommand(client, "[SM] Usage: sm_ungag ");
+ return Plugin_Handled;
+ }
+
+ decl String:arg[64];
+ GetCmdArg(1, arg, sizeof(arg));
+
+ new target = FindTarget(client, arg);
+ if (target == -1)
+ {
+ return Plugin_Handled;
+ }
+
+ if (!g_Gagged[target])
+ {
+ ReplyToCommand(client, "%t", "Player Not Gagged");
+ return Plugin_Handled;
+ }
+
+ g_Gagged[target] = false;
+
+ decl String:name[64];
+ GetClientName(target, name, sizeof(name));
+
+ ShowActivity(client, "%t", "Player Ungagged", name);
+ ReplyToCommand(client, "%t", "Player Ungagged", name);
+ LogAction(client, target, "\"%L\" ungagged \"%L\"", client, target);
+
+ return Plugin_Handled;
+}
+
+public Action:Command_Unsilence(client, args)
+{
+ if (args < 1)
+ {
+ ReplyToCommand(client, "[SM] Usage: sm_unsilence ");
+ return Plugin_Handled;
+ }
+
+ decl String:arg[64];
+ GetCmdArg(1, arg, sizeof(arg));
+
+ new target = FindTarget(client, arg);
+ if (target == -1)
+ {
+ return Plugin_Handled;
+ }
+
+ if (!g_Gagged[target] && !g_Muted[target])
+ {
+ ReplyToCommand(client, "%t", "Player Not Silenced");
+ return Plugin_Handled;
+ }
+
+ decl String:name[64];
+ GetClientName(target, name, sizeof(name));
+
+ if (g_Gagged[target])
+ {
+ g_Gagged[target] = false;
+
+ ShowActivity(client, "%t", "Player Ungagged", name);
+ ReplyToCommand(client, "%t", "Player Ungagged", name);
+ LogAction(client, target, "\"%L\" ungagged \"%L\"", client, target);
+ }
+
+ if (g_Muted[target])
+ {
+ g_Muted[target] = false;
+
+ if (GetConVarInt(g_Cvar_Deadtalk) == 1 && !IsPlayerAlive(target))
+ {
+ SetClientListeningFlags(target, VOICE_LISTENALL);
+ }
+ else if (GetConVarInt(g_Cvar_Deadtalk) == 2 && !IsPlayerAlive(target))
+ {
+ SetClientListeningFlags(target, VOICE_TEAM);
+ }
+ else
+ {
+ SetClientListeningFlags(target, VOICE_NORMAL);
+ }
+
+ ShowActivity(client, "%t", "Player Unmuted", name);
+ ReplyToCommand(client, "%t", "Player Unmuted", name);
+ LogAction(client, target, "\"%L\" unmuted \"%L\"", client, target);
+ }
+
+ return Plugin_Handled;
+}
+
+public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
+{
+ new client = GetClientOfUserId(GetEventInt(event, "userid"));
+
+ if (g_Muted[client])
+ {
+ SetClientListeningFlags(client, VOICE_MUTED);
+ }
+ else
+ {
+ SetClientListeningFlags(client, VOICE_NORMAL);
+ }
+}
+
+public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
+{
+ new client = GetClientOfUserId(GetEventInt(event, "userid"));
+
+ if (g_Muted[client])
+ {
+ SetClientListeningFlags(client, VOICE_MUTED);
+ return;
+ }
+
+ if (GetConVarBool(g_Cvar_Alltalk))
+ {
+ SetClientListeningFlags(client, VOICE_NORMAL);
+ return;
+ }
+
+ if (GetConVarInt(g_Cvar_Deadtalk) == 1)
+ {
+ SetClientListeningFlags(client, VOICE_LISTENALL);
+ }
+ else if (GetConVarInt(g_Cvar_Deadtalk) == 2)
+ {
+ SetClientListeningFlags(client, VOICE_TEAM);
+ }
+}
\ No newline at end of file
diff --git a/plugins/include/console.inc b/plugins/include/console.inc
index 26f88171..95096c24 100644
--- a/plugins/include/console.inc
+++ b/plugins/include/console.inc
@@ -669,3 +669,14 @@ native bool:CheckCommandAccess(client,
const String:command[],
flags,
bool:override_only=false);
+
+/**
+ * Returns true if the supplied character is valid in a ConVar name.
+ *
+ * @param c Character to validate.
+ * @return True is valid for ConVars, false otherwise
+ */
+stock bool:IsValidConVarChar(c)
+{
+ return (c == '_' || IsCharAlpha(c) || IsCharNumeric(c));
+}
diff --git a/plugins/randomcycle.sp b/plugins/randomcycle.sp
new file mode 100644
index 00000000..8eb1cc81
--- /dev/null
+++ b/plugins/randomcycle.sp
@@ -0,0 +1,189 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Random Map Cycle Plugin
+ * Randomly picks a map from the mapcycle.
+ *
+ * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program 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
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#pragma semicolon 1
+#include
+
+public Plugin:myinfo =
+{
+ name = "RandomCycle",
+ author = "AlliedModders LLC",
+ description = "Randomly chooses the next map.",
+ version = SOURCEMOD_VERSION,
+ url = "http://www.sourcemod.net/"
+};
+
+new Handle:g_Cvar_Nextmap = INVALID_HANDLE;
+new Handle:g_Cvar_Mapfile = INVALID_HANDLE;
+new Handle:g_Cvar_ExcludeMaps = INVALID_HANDLE;
+
+new Handle:g_MapList = INVALID_HANDLE;
+new Handle:g_OldMapList = INVALID_HANDLE;
+new g_mapFileTime;
+
+public OnPluginStart()
+{
+ g_MapList = CreateArray(33);
+ g_OldMapList = CreateArray(33);
+
+ g_Cvar_Mapfile = CreateConVar("sm_randomcycle_file", "configs/maps.ini", "Map file to use. (Def sourcemod/configs/maps.ini)");
+ g_Cvar_ExcludeMaps = CreateConVar("sm_randomcycle_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0);
+
+ AutoExecConfig(true, "randomcycle");
+}
+
+public OnMapStart()
+{
+ g_Cvar_Nextmap = FindConVar("sm_nextmap");
+
+ if (g_Cvar_Nextmap == INVALID_HANDLE)
+ {
+ LogError("FATAL: Cannot find sm_nextmap cvar. RandomCycle not loaded.");
+ SetFailState("sm_nextmap not found");
+ }
+
+ if (LoadMaps())
+ {
+ CreateTimer(5.0, Timer_RandomizeNextmap); // Small delay to give Nextmap time to complete OnMapStart()
+ }
+}
+
+public Action:Timer_RandomizeNextmap(Handle:timer)
+{
+ decl String:map[32];
+
+ new bool:oldMaps = false;
+ if (GetArraySize(g_MapList) > GetConVarInt(g_Cvar_ExcludeMaps))
+ {
+ oldMaps = true;
+ }
+
+ new b = GetRandomInt(0, GetArraySize(g_MapList) - 1);
+ GetArrayString(g_MapList, b, map, sizeof(map));
+
+ while (oldMaps && IsStringInArray(g_OldMapList, map))
+ {
+ b = GetRandomInt(0, GetArraySize(g_MapList) - 1);
+ GetArrayString(g_MapList, b, map, sizeof(map));
+ }
+
+ SetConVarString(g_Cvar_Nextmap, map);
+ PushArrayString(g_OldMapList, map);
+
+ if (GetArraySize(g_OldMapList) > GetConVarInt(g_Cvar_ExcludeMaps))
+ {
+ RemoveFromArray(g_OldMapList, 0);
+ }
+
+ LogMessage("RandomCycle has chosen %s for the nextmap.", map);
+
+ return Plugin_Stop;
+}
+
+LoadMaps()
+{
+ new bool:fileFound;
+
+ decl String:mapPath[256], String:mapFile[64];
+ GetConVarString(g_Cvar_Mapfile, mapFile, 64);
+ BuildPath(Path_SM, mapPath, sizeof(mapFile), mapFile);
+ fileFound = FileExists(mapPath);
+ if (!fileFound)
+ {
+ new Handle:mapCycleFile = FindConVar("mapcyclefile");
+ GetConVarString(mapCycleFile, mapPath, sizeof(mapPath));
+ fileFound = FileExists(mapPath);
+ }
+
+ if (!fileFound)
+ {
+ LogError("Unable to locate sm_randomcycle_file or mapcyclefile, no maps loaded.");
+
+ if (g_MapList != INVALID_HANDLE)
+ {
+ ClearArray(g_MapList);
+ }
+
+ return 0;
+ }
+
+ // If the file hasn't changed, there's no reason to reload
+ // all of the maps.
+ new fileTime = GetFileTime(mapPath, FileTime_LastChange);
+ if (g_mapFileTime == fileTime)
+ {
+ return GetArraySize(g_MapList);
+ }
+
+ g_mapFileTime = fileTime;
+
+ // Reset the array
+ if (g_MapList != INVALID_HANDLE)
+ {
+ ClearArray(g_MapList);
+ }
+
+ LogMessage("[SM] Loading Random Cycle map file [%s]", mapPath);
+
+ new Handle:file = OpenFile(mapPath, "rt");
+ if (file == INVALID_HANDLE)
+ {
+ LogError("[SM] Could not open file: %s", mapPath);
+ return 0;
+ }
+
+ decl String:currentMap[32];
+ GetCurrentMap(currentMap, sizeof(currentMap));
+
+ decl String:buffer[64], len;
+ while (!IsEndOfFile(file) && ReadFileLine(file, buffer, sizeof(buffer)))
+ {
+ TrimString(buffer);
+
+ if ((len = StrContains(buffer, ".bsp", false)) != -1)
+ {
+ buffer[len] = '\0';
+ }
+
+ if (buffer[0] == '\0' || !IsValidConVarChar(buffer[0]) || !IsMapValid(buffer)
+ || strcmp(currentMap, buffer, false) == 0)
+ {
+ continue;
+ }
+
+ PushArrayString(g_MapList, buffer);
+ }
+
+ CloseHandle(file);
+ return GetArraySize(g_MapList);
+}
\ No newline at end of file
diff --git a/translations/basecomm.phrases.txt b/translations/basecomm.phrases.txt
new file mode 100644
index 00000000..ef3484c2
--- /dev/null
+++ b/translations/basecomm.phrases.txt
@@ -0,0 +1,66 @@
+"Phrases"
+{
+ "Already Muted"
+ {
+ "en" "Target player is already muted."
+ "de" "Gewählte Zielperson ist bereits stummgeschaltet."
+ }
+
+ "Player Muted"
+ {
+ "#format" "{1:s}"
+ "en" "{1} has been muted."
+ "de" "{1} wurde stummgeschaltet."
+ }
+
+ "Player Not Muted"
+ {
+ "en" "Target player is not muted."
+ "de" "Gewählte Zielperson ist nicht stummgeschaltet."
+ }
+
+ "Player Unmuted"
+ {
+ "#format" "{1:s}"
+ "en" "{1} has been unmuted."
+ "de" "{1} wurde die Sprache wiedergegeben."
+ }
+
+ "Already Gagged"
+ {
+ "en" "Target player is already gagged."
+ "de" "Zielperson ist bereits geknebelt."
+ }
+
+ "Player Gagged"
+ {
+ "#format" "{1:s}"
+ "en" "{1} has been gagged."
+ "de" "{1} wurde geknebelt."
+ }
+
+ "Player Not Gagged"
+ {
+ "en" "Target player is not gagged."
+ "de" "Zielperson ist nicht geknebelt."
+ }
+
+ "Player Ungagged"
+ {
+ "#format" "{1:s}"
+ "en" "{1} has been ungagged."
+ "de" "{1} wurde der Chat wieder freigeschaltet."
+ }
+
+ "Already Silenced"
+ {
+ "en" "Target player is already silenced."
+ "de" "Zielperson schweigt bereites."
+ }
+
+ "Player Not Silenced"
+ {
+ "en" "Target player is not silenced."
+ "de" "Zielperson ist schweigt nicht."
+ }
+}
\ No newline at end of file