├── .editorconfig ├── README ├── afkicker.sp ├── autorecorder.sp ├── inschat.sp ├── jail.sp ├── mapstats.sp ├── nextmap_poplimits.sp ├── nmrihstats.sp ├── qpdis.sp ├── setheads.sp ├── shipchat.sp ├── teambalancer.sp ├── teamswap.sp ├── tkmanager.sp ├── translations ├── jail.phrases.txt └── tkmanager.phrases.txt └── whitelist.sp /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for standardising the coding style between different editors 2 | # http://editorconfig.org/ 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = tab 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | These are various SourceMod plugins I've written. Some are complete, 2 | others not so much. Officially supported plugins are listed at 3 | http://www.sourcemod.net/plugins.php?author=StevoTVR&search=1 4 | 5 | ^^ Those are the ones that I know work or at least worked at some 6 | point. The others might still be useful or even work... -------------------------------------------------------------------------------- /afkicker.sp: -------------------------------------------------------------------------------- 1 | #pragma semicolon 1 2 | #include 3 | 4 | public Plugin:myinfo = 5 | { 6 | name = "AFK Kicker", 7 | author = "Stevo.TVR", 8 | description = "Kicks clients who are AFK for a certain period", 9 | version = "1.1", 10 | url = "http://www.theville.org/" 11 | } 12 | 13 | new Handle:sm_afk_enabled = INVALID_HANDLE; 14 | new Handle:sm_afk_maxtime = INVALID_HANDLE; 15 | new Handle:sm_afk_minplayers = INVALID_HANDLE; 16 | new Handle:sm_afk_immunity = INVALID_HANDLE; 17 | 18 | new Float:clientAngle[MAXPLAYERS+1]; 19 | new clientAFKTime[MAXPLAYERS+1]; 20 | new bool:clientAlive[MAXPLAYERS+1]; 21 | 22 | public OnPluginStart() 23 | { 24 | sm_afk_enabled = CreateConVar("sm_afk_enabled", "1", "Enable AFK kicker", _, true, 0.0, true, 1.0); 25 | sm_afk_maxtime = CreateConVar("sm_afk_maxtime", "120", "Time a player must be AFK to be kicked", _, true, 0.0); 26 | sm_afk_minplayers = CreateConVar("sm_afk_minplayers", "0", "Number of clients that must be on the server before kicking AFK players", _, true, 0.0); 27 | sm_afk_immunity = CreateConVar("sm_afk_immunity", "1", "Sets whether or not admins are immune to the AFK kicker", _, true, 0.0, true, 1.0); 28 | AutoExecConfig(true, "afk_kicker"); 29 | 30 | HookEvent("player_spawn", Event_PlayerSpawn); 31 | HookEvent("player_death", Event_PlayerDeath); 32 | } 33 | 34 | public OnMapStart() 35 | { 36 | new maxPlayers = GetMaxClients(); 37 | 38 | for(new i = 1; i <= maxPlayers; i++) 39 | { 40 | clientAFKTime[i] = 0; 41 | clientAlive[i] = false; 42 | } 43 | 44 | CreateTimer(10.0, Timer_CheckClients, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); 45 | } 46 | 47 | public OnClientDisconnect(client) 48 | { 49 | clientAFKTime[client] = 0; 50 | clientAlive[client] = false; 51 | } 52 | 53 | public Action:Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) 54 | { 55 | new client = GetClientOfUserId(GetEventInt(event, "userid")); 56 | clientAFKTime[client] = 0; 57 | if(!GetEventBool(event, "dead")) 58 | clientAlive[client] = true; 59 | } 60 | 61 | public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) 62 | { 63 | new client = GetClientOfUserId(GetEventInt(event, "userid")); 64 | clientAlive[client] = false; 65 | } 66 | 67 | public Action:Timer_CheckClients(Handle:Timer) 68 | { 69 | if(GetConVarBool(sm_afk_enabled) && GetClientCount(true) >= GetConVarInt(sm_afk_minplayers)) 70 | { 71 | new maxPlayers = GetMaxClients(); 72 | 73 | for(new i = 1; i <= maxPlayers; i++) 74 | { 75 | if(!IsClientConnected(i) || !IsClientInGame(i) || IsFakeClient(i) || !clientAlive[i] || IsImmune(i)) 76 | continue; 77 | 78 | new Float:angle[3]; 79 | GetClientAbsAngles(i, angle); 80 | if(clientAngle[i] == angle[1]) 81 | { 82 | clientAFKTime[i] += 10; 83 | if(clientAFKTime[i] >= GetConVarInt(sm_afk_maxtime)) 84 | KickClient(i, "You were AFK too long"); 85 | } 86 | else 87 | { 88 | clientAFKTime[i] = 0; 89 | clientAngle[i] = angle[1]; 90 | } 91 | } 92 | } 93 | } 94 | 95 | public IsImmune(client) 96 | { 97 | new bool:immune = false; 98 | if(GetConVarBool(sm_afk_immunity)) 99 | { 100 | if(((GetUserFlagBits(client) & ADMFLAG_ROOT) == ADMFLAG_ROOT) 101 | || ((GetUserFlagBits(client) & ADMFLAG_CUSTOM1) == ADMFLAG_CUSTOM1)) 102 | immune = true; 103 | } 104 | return immune; 105 | } -------------------------------------------------------------------------------- /autorecorder.sp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Auto Recorder 4 | * http://forums.alliedmods.net/showthread.php?t=92072 5 | * 6 | * Description: 7 | * Automates SourceTV recording based on player count 8 | * and time of day. Also allows admins to manually record. 9 | * 10 | * Changelog 11 | * May 09, 2009 - v.1.0.0: 12 | * [*] Initial Release 13 | * May 11, 2009 - v.1.1.0: 14 | * [+] Added path cvar to control where demos are stored 15 | * [*] Changed manual recording to override automatic recording 16 | * [+] Added seconds to demo names 17 | * May 04, 2016 - v.1.1.1: 18 | * [*] Changed demo file names to replace slashes with hyphens [ajmadsen] 19 | * Aug 26, 2016 - v.1.2.0: 20 | * [*] Now ignores bots in the player count by default 21 | * [*] The SourceTV client is now always ignored in the player count 22 | * [+] Added sm_autorecord_ignorebots to control whether to ignore bots 23 | * [*] Now checks the status of the server immediately when a setting is changed 24 | * Jun 21, 2017 - v.1.3.0: 25 | * [*] Fixed minimum player count setting being off by one 26 | * [*] Fixed player counting code getting out of range 27 | * [*] Updated source code to the new syntax 28 | * Apr 15, 2022 - v.1.3.1: 29 | * [*] Increased the length limit of the map name in the demo filename 30 | * [*] Fixed workshop map demo filenames missing the .dem extension 31 | * 32 | */ 33 | 34 | #include 35 | 36 | #pragma semicolon 1 37 | #pragma newdecls required 38 | 39 | #define PLUGIN_VERSION "1.3.1" 40 | 41 | public Plugin myinfo = 42 | { 43 | name = "Auto Recorder", 44 | author = "Stevo.TVR", 45 | description = "Automates SourceTV recording based on player count and time of day.", 46 | version = PLUGIN_VERSION, 47 | url = "http://www.theville.org" 48 | } 49 | 50 | ConVar g_hTvEnabled = null; 51 | ConVar g_hAutoRecord = null; 52 | ConVar g_hMinPlayersStart = null; 53 | ConVar g_hIgnoreBots = null; 54 | ConVar g_hTimeStart = null; 55 | ConVar g_hTimeStop = null; 56 | ConVar g_hFinishMap = null; 57 | ConVar g_hDemoPath = null; 58 | 59 | bool g_bIsRecording = false; 60 | bool g_bIsManual = false; 61 | 62 | public void OnPluginStart() 63 | { 64 | CreateConVar("sm_autorecord_version", PLUGIN_VERSION, "Auto Recorder plugin version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD); 65 | 66 | g_hAutoRecord = CreateConVar("sm_autorecord_enable", "1", "Enable automatic recording", _, true, 0.0, true, 1.0); 67 | g_hMinPlayersStart = CreateConVar("sm_autorecord_minplayers", "4", "Minimum players on server to start recording", _, true, 0.0); 68 | g_hIgnoreBots = CreateConVar("sm_autorecord_ignorebots", "1", "Ignore bots in the player count", _, true, 0.0, true, 1.0); 69 | g_hTimeStart = CreateConVar("sm_autorecord_timestart", "-1", "Hour in the day to start recording (0-23, -1 disables)"); 70 | g_hTimeStop = CreateConVar("sm_autorecord_timestop", "-1", "Hour in the day to stop recording (0-23, -1 disables)"); 71 | g_hFinishMap = CreateConVar("sm_autorecord_finishmap", "1", "If 1, continue recording until the map ends", _, true, 0.0, true, 1.0); 72 | g_hDemoPath = CreateConVar("sm_autorecord_path", ".", "Path to store recorded demos"); 73 | 74 | AutoExecConfig(true, "autorecorder"); 75 | 76 | RegAdminCmd("sm_record", Command_Record, ADMFLAG_KICK, "Starts a SourceTV demo"); 77 | RegAdminCmd("sm_stoprecord", Command_StopRecord, ADMFLAG_KICK, "Stops the current SourceTV demo"); 78 | 79 | g_hTvEnabled = FindConVar("tv_enable"); 80 | 81 | char sPath[PLATFORM_MAX_PATH]; 82 | g_hDemoPath.GetString(sPath, sizeof(sPath)); 83 | if(!DirExists(sPath)) 84 | { 85 | InitDirectory(sPath); 86 | } 87 | 88 | g_hMinPlayersStart.AddChangeHook(OnConVarChanged); 89 | g_hIgnoreBots.AddChangeHook(OnConVarChanged); 90 | g_hTimeStart.AddChangeHook(OnConVarChanged); 91 | g_hTimeStop.AddChangeHook(OnConVarChanged); 92 | g_hDemoPath.AddChangeHook(OnConVarChanged); 93 | 94 | CreateTimer(300.0, Timer_CheckStatus, _, TIMER_REPEAT); 95 | 96 | StopRecord(); 97 | CheckStatus(); 98 | } 99 | 100 | public void OnConVarChanged(ConVar convar, const char[] oldValue, const char [] newValue) 101 | { 102 | if(convar == g_hDemoPath) 103 | { 104 | if(!DirExists(newValue)) 105 | { 106 | InitDirectory(newValue); 107 | } 108 | } 109 | else 110 | { 111 | CheckStatus(); 112 | } 113 | } 114 | 115 | public void OnMapEnd() 116 | { 117 | if(g_bIsRecording) 118 | { 119 | StopRecord(); 120 | g_bIsManual = false; 121 | } 122 | } 123 | 124 | public void OnClientPutInServer(int client) 125 | { 126 | CheckStatus(); 127 | } 128 | 129 | public void OnClientDisconnect_Post(int client) 130 | { 131 | CheckStatus(); 132 | } 133 | 134 | public Action Timer_CheckStatus(Handle timer) 135 | { 136 | CheckStatus(); 137 | } 138 | 139 | public Action Command_Record(int client, int args) 140 | { 141 | if(g_bIsRecording) 142 | { 143 | ReplyToCommand(client, "[SM] SourceTV is already recording!"); 144 | return Plugin_Handled; 145 | } 146 | 147 | StartRecord(); 148 | g_bIsManual = true; 149 | 150 | ReplyToCommand(client, "[SM] SourceTV is now recording..."); 151 | 152 | return Plugin_Handled; 153 | } 154 | 155 | public Action Command_StopRecord(int client, int args) 156 | { 157 | if(!g_bIsRecording) 158 | { 159 | ReplyToCommand(client, "[SM] SourceTV is not recording!"); 160 | return Plugin_Handled; 161 | } 162 | 163 | StopRecord(); 164 | 165 | if(g_bIsManual) 166 | { 167 | g_bIsManual = false; 168 | CheckStatus(); 169 | } 170 | 171 | ReplyToCommand(client, "[SM] Stopped recording."); 172 | 173 | return Plugin_Handled; 174 | } 175 | 176 | void CheckStatus() 177 | { 178 | if(g_hAutoRecord.BoolValue && !g_bIsManual) 179 | { 180 | int iMinClients = g_hMinPlayersStart.IntValue; 181 | 182 | int iTimeStart = g_hTimeStart.IntValue; 183 | int iTimeStop = g_hTimeStop.IntValue; 184 | bool bReverseTimes = (iTimeStart > iTimeStop); 185 | 186 | char sCurrentTime[4]; 187 | FormatTime(sCurrentTime, sizeof(sCurrentTime), "%H", GetTime()); 188 | int iCurrentTime = StringToInt(sCurrentTime); 189 | 190 | if(GetPlayerCount() >= iMinClients && (iTimeStart < 0 || (iCurrentTime >= iTimeStart && (bReverseTimes || iCurrentTime < iTimeStop)))) 191 | { 192 | StartRecord(); 193 | } 194 | else if(g_bIsRecording && !g_hFinishMap.BoolValue && (iTimeStop < 0 || iCurrentTime >= iTimeStop)) 195 | { 196 | StopRecord(); 197 | } 198 | } 199 | } 200 | 201 | int GetPlayerCount() 202 | { 203 | bool bIgnoreBots = g_hIgnoreBots.BoolValue; 204 | 205 | int iNumPlayers = 0; 206 | for(int i = 1; i <= MaxClients; i++) 207 | { 208 | if(IsClientConnected(i) && (!bIgnoreBots || !IsFakeClient(i))) 209 | { 210 | iNumPlayers++; 211 | } 212 | } 213 | 214 | if(!bIgnoreBots) 215 | { 216 | iNumPlayers--; 217 | } 218 | 219 | return iNumPlayers; 220 | } 221 | 222 | void StartRecord() 223 | { 224 | if(g_hTvEnabled.BoolValue && !g_bIsRecording) 225 | { 226 | char sPath[PLATFORM_MAX_PATH]; 227 | char sTime[16]; 228 | char sMap[48]; 229 | 230 | g_hDemoPath.GetString(sPath, sizeof(sPath)); 231 | FormatTime(sTime, sizeof(sTime), "%Y%m%d-%H%M%S", GetTime()); 232 | GetCurrentMap(sMap, sizeof(sMap)); 233 | 234 | // replace slashes in map path name with dashes, to prevent fail on workshop maps 235 | ReplaceString(sMap, sizeof(sMap), "/", "-", false); 236 | // replace periods in map path name with underscores, so workshop map demos still get a .dem extension 237 | ReplaceString(sMap, sizeof(sMap), ".", "_", false); 238 | 239 | ServerCommand("tv_record \"%s/auto-%s-%s\"", sPath, sTime, sMap); 240 | g_bIsRecording = true; 241 | 242 | LogMessage("Recording to auto-%s-%s.dem", sTime, sMap); 243 | } 244 | } 245 | 246 | void StopRecord() 247 | { 248 | if(g_hTvEnabled.BoolValue) 249 | { 250 | ServerCommand("tv_stoprecord"); 251 | g_bIsRecording = false; 252 | } 253 | } 254 | 255 | void InitDirectory(const char[] sDir) 256 | { 257 | char sPieces[32][PLATFORM_MAX_PATH]; 258 | char sPath[PLATFORM_MAX_PATH]; 259 | int iNumPieces = ExplodeString(sDir, "/", sPieces, sizeof(sPieces), sizeof(sPieces[])); 260 | 261 | for(int i = 0; i < iNumPieces; i++) 262 | { 263 | Format(sPath, sizeof(sPath), "%s/%s", sPath, sPieces[i]); 264 | if(!DirExists(sPath)) 265 | { 266 | CreateDirectory(sPath, 509); 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /inschat.sp: -------------------------------------------------------------------------------- 1 | #pragma semicolon 1 2 | #include 3 | 4 | public Plugin:myinfo = 5 | { 6 | name = "Insurgency Chat", 7 | author = "Stevo.TVR", 8 | description = "Enables logging of Insurgency chat messages and converts it to regular chat that plugins recognize", 9 | version = "1.3", 10 | url = "http://www.theville.org/" 11 | }; 12 | 13 | #define CHAT_SYMBOL '@' 14 | 15 | new Handle:g_logEnable; 16 | 17 | public OnPluginStart() 18 | { 19 | g_logEnable = CreateConVar("inschat_logging", "1", "Enable logging of chat", _, true, 0.0, true, 1.0); 20 | RegConsoleCmd("say2", Command_Say); 21 | RegServerCmd("say", Server_Say); 22 | } 23 | 24 | public Action:Command_Say(client, args) 25 | { 26 | new String:text[192], String:cmd[16], String:name[MAX_NAME_LENGTH], String:steamid[64], String:team[32]; 27 | new startidx = 4; 28 | 29 | if (GetCmdArgString(text, sizeof(text)) < 1 || client == 0) 30 | { 31 | return Plugin_Continue; 32 | } 33 | 34 | if (text[strlen(text)-1] == '"') 35 | { 36 | text[strlen(text)-1] = '\0'; 37 | startidx += 1; 38 | } 39 | 40 | if (text[0] == '1') 41 | { 42 | cmd = "say"; 43 | } 44 | else 45 | { 46 | cmd = "say_team"; 47 | } 48 | 49 | GetClientName(client, name, sizeof(name)); 50 | GetClientAuthString(client, steamid, sizeof(steamid)); 51 | switch (GetClientTeam(client)) 52 | { 53 | case 1: 54 | { 55 | team = "U.S. Marines"; 56 | } 57 | case 2: 58 | { 59 | team = "Iraqi Insurgents"; 60 | } 61 | default: 62 | { 63 | team = "Unassigned"; 64 | } 65 | } 66 | 67 | if (GetConVarBool(g_logEnable)) 68 | { 69 | LogToGame("\"%s<%i><%s><%s>\" %s \"%s\"", name, GetClientUserId(client), steamid, team, cmd, text[startidx]); 70 | } 71 | FakeClientCommandEx(client, "%s %s", cmd, text[startidx]); 72 | 73 | if (text[startidx] == CHAT_SYMBOL || IsChatTrigger()) 74 | { 75 | return Plugin_Handled; 76 | } 77 | 78 | return Plugin_Continue; 79 | } 80 | 81 | public Action:Server_Say(args) 82 | { 83 | new String:text[192]; 84 | new startidx = 0; 85 | 86 | if (GetCmdArgString(text, sizeof(text)) < 1) 87 | { 88 | return Plugin_Continue; 89 | } 90 | 91 | if (text[strlen(text)-1] == '"') 92 | { 93 | text[strlen(text)-1] = '\0'; 94 | startidx += 1; 95 | } 96 | 97 | PrintToChatAll("Console: %s", text[startidx]); 98 | 99 | return Plugin_Handled; 100 | } 101 | -------------------------------------------------------------------------------- /jail.sp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Jail 4 | * 5 | * Description: 6 | * Sends players to a specified location on the map and 7 | * forces them to stay there until released. Not even 8 | * death or a reconnect will allow them to escape! 9 | * 10 | * 11 | * Changelog 12 | * Sep 28, 2009 - v.1.0: 13 | * [*] Initial Release 14 | * 15 | */ 16 | 17 | #pragma semicolon 1 18 | #include 19 | #include 20 | #undef REQUIRE_PLUGIN 21 | #include 22 | 23 | #define PLUGIN_VERSION "1.0" 24 | 25 | public Plugin:myinfo = 26 | { 27 | name = "Jail", 28 | author = "Stevo.TVR", 29 | description = "Forces players to stay in a specified location", 30 | version = PLUGIN_VERSION, 31 | url = "http://www.theville.org/" 32 | } 33 | 34 | new Handle:g_hTopMenu = INVALID_HANDLE; 35 | new Handle:g_hMapData = INVALID_HANDLE; 36 | new Handle:g_hJailed = INVALID_HANDLE; 37 | 38 | new bool:g_bJailed[MAXPLAYERS+1] = {false, ...}; 39 | new bool:g_bReady = false; 40 | new Float:g_vOrigin[3], Float:g_vAngle[3]; 41 | 42 | public OnPluginStart() 43 | { 44 | RegAdminCmd("sm_jail", Command_Jail, ADMFLAG_KICK, "Jail/Release player"); 45 | RegAdminCmd("sm_jail_saveloc", Command_Saveloc, ADMFLAG_KICK, "Save jail coordinates"); 46 | 47 | HookEvent("player_spawn", Event_PlayerSpawn); 48 | 49 | LoadTranslations("common.phrases"); 50 | LoadTranslations("jail.phrases"); 51 | 52 | g_hMapData = CreateKeyValues("Jail"); 53 | g_hJailed = CreateTrie(); 54 | 55 | LoadSaveData(); 56 | 57 | new Handle:topmenu; 58 | if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) 59 | { 60 | OnAdminMenuReady(topmenu); 61 | } 62 | } 63 | 64 | public OnMapStart() 65 | { 66 | decl String:sMap[64]; 67 | GetCurrentMap(sMap, sizeof(sMap)); 68 | 69 | if(KvJumpToKey(g_hMapData, sMap)) 70 | { 71 | KvGetVector(g_hMapData, "origin", g_vOrigin); 72 | KvGetVector(g_hMapData, "angle", g_vAngle); 73 | KvRewind(g_hMapData); 74 | g_bReady = true; 75 | } 76 | else 77 | { 78 | g_bReady = false; 79 | } 80 | 81 | ClearTrie(g_hJailed); 82 | } 83 | 84 | public OnClientAuthorized(client, const String:auth[]) 85 | { 86 | new bool:value; 87 | if(GetTrieValue(g_hJailed, auth, value)) 88 | { 89 | g_bJailed[client] = true; 90 | } 91 | } 92 | 93 | public OnClientDisconnect(client) 94 | { 95 | g_bJailed[client] = false; 96 | } 97 | 98 | public Action:Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) 99 | { 100 | new client = GetClientOfUserId(GetEventInt(event, "userid")); 101 | if(g_bJailed[client] && g_bReady) 102 | { 103 | TeleportPlayer(client); 104 | } 105 | } 106 | 107 | public Action:Command_Jail(client, args) 108 | { 109 | if(args < 1) 110 | { 111 | ReplyToCommand(client, "[SM] Usage: sm_jail "); 112 | return Plugin_Handled; 113 | } 114 | 115 | if(!g_bReady) 116 | { 117 | ReplyToCommand(client, "[SM] %t", "No coords"); 118 | return Plugin_Handled; 119 | } 120 | 121 | decl String:sTarget[64]; 122 | GetCmdArg(1, sTarget, sizeof(sTarget)); 123 | new target = FindTarget(client, sTarget); 124 | 125 | if(target > 0) 126 | { 127 | JailPlayer(client, target); 128 | } 129 | 130 | return Plugin_Handled; 131 | } 132 | 133 | public Action:Command_Saveloc(client, args) 134 | { 135 | if(IsClientInGame(client)) 136 | { 137 | GetClientAbsOrigin(client, g_vOrigin); 138 | GetClientAbsAngles(client, g_vAngle); 139 | 140 | decl String:sMap[64]; 141 | GetCurrentMap(sMap, sizeof(sMap)); 142 | 143 | KvJumpToKey(g_hMapData, sMap, true); 144 | KvSetVector(g_hMapData, "origin", g_vOrigin); 145 | KvSetVector(g_hMapData, "angle", g_vAngle); 146 | KvRewind(g_hMapData); 147 | 148 | LoadSaveData(true); 149 | 150 | g_bReady = true; 151 | 152 | ReplyToCommand(client, "[SM] %t", "Coords saved", sMap); 153 | LogAction(client, -1, "\"%L\" saved jail coordinates for map %s", client, sMap); 154 | } 155 | 156 | return Plugin_Handled; 157 | } 158 | 159 | JailPlayer(client, target) 160 | { 161 | if(g_bReady) 162 | { 163 | decl String:auth[64], String:sTargetName[MAX_NAME_LENGTH], String:sMessage[128]; 164 | GetClientAuthString(target, auth, sizeof(auth)); 165 | GetClientName(target, sTargetName, sizeof(sTargetName)); 166 | 167 | if(!g_bJailed[target]) 168 | { 169 | SetTrieValue(g_hJailed, auth, true); 170 | g_bJailed[target] = true; 171 | TeleportPlayer(target); 172 | 173 | ShowActivity2(client, "[SM] ", "%t", "Jailed player", sTargetName); 174 | Format(sMessage, sizeof(sMessage), "\x01[SM] \x03%t", "Jailed"); 175 | SayText2(target, target, sMessage); 176 | LogAction(client, target, "\"%L\" jailed \"%L\"", client, target); 177 | } 178 | else 179 | { 180 | RemoveFromTrie(g_hJailed, auth); 181 | g_bJailed[target] = false; 182 | 183 | if(IsPlayerAlive(target)) 184 | { 185 | DispatchSpawn(target); 186 | } 187 | 188 | ShowActivity2(client, "[SM] ", "%t", "Released player", sTargetName); 189 | Format(sMessage, sizeof(sMessage), "\x01[SM] \x04%t", "Released"); 190 | SayText2(target, target, sMessage); 191 | LogAction(client, target, "\"%L\" released \"%L\"", client, target); 192 | } 193 | } 194 | } 195 | 196 | TeleportPlayer(client) 197 | { 198 | if(IsPlayerAlive(client)) 199 | { 200 | TeleportEntity(client, g_vOrigin, g_vAngle, NULL_VECTOR); 201 | } 202 | } 203 | 204 | LoadSaveData(bool:save = false) 205 | { 206 | decl String:sPath[PLATFORM_MAX_PATH]; 207 | BuildPath(PathType:Path_SM, sPath, sizeof(sPath), "data/jail.txt"); 208 | 209 | if(save) 210 | { 211 | KeyValuesToFile(g_hMapData, sPath); 212 | } 213 | else 214 | { 215 | if(!FileToKeyValues(g_hMapData, sPath)) 216 | { 217 | SetFailState("Unable to load map data file"); 218 | } 219 | } 220 | } 221 | 222 | SayText2(client_index, author_index, const String:message[]) 223 | { 224 | new Handle:buffer = StartMessageOne("SayText2", client_index); 225 | if(buffer != INVALID_HANDLE) 226 | { 227 | BfWriteByte(buffer, author_index); 228 | BfWriteByte(buffer, true); 229 | BfWriteString(buffer, message); 230 | EndMessage(); 231 | } 232 | } 233 | 234 | /* 235 | * =================== 236 | * Menu Stuff 237 | * =================== 238 | */ 239 | public OnLibraryRemoved(const String:name[]) 240 | { 241 | if(StrEqual(name, "adminmenu")) 242 | { 243 | g_hTopMenu = INVALID_HANDLE; 244 | } 245 | } 246 | 247 | public OnAdminMenuReady(Handle:topmenu) 248 | { 249 | if(topmenu == g_hTopMenu) 250 | { 251 | return; 252 | } 253 | 254 | g_hTopMenu = topmenu; 255 | 256 | new TopMenuObject:player_commands = FindTopMenuCategory(g_hTopMenu, ADMINMENU_PLAYERCOMMANDS); 257 | 258 | if(player_commands != INVALID_TOPMENUOBJECT) 259 | { 260 | AddToTopMenu(g_hTopMenu, "sm_jail", TopMenuObject_Item, AdminMenu_Jail, player_commands, "sm_jail", ADMFLAG_KICK); 261 | } 262 | } 263 | 264 | DisplayJailMenu(client) 265 | { 266 | new Handle:menu = CreateMenu(MenuHandler_Jail); 267 | 268 | decl String:title[100]; 269 | Format(title, sizeof(title), "%t", "Jail menu"); 270 | SetMenuTitle(menu, title); 271 | SetMenuExitBackButton(menu, true); 272 | 273 | AddTargetsToMenu(menu, client, false, false); 274 | 275 | DisplayMenu(menu, client, MENU_TIME_FOREVER); 276 | } 277 | 278 | public AdminMenu_Jail(Handle:topmenu, TopMenuAction:action, TopMenuObject:object_id, param, String:buffer[], maxlength) 279 | { 280 | if(action == TopMenuAction_DisplayOption) 281 | { 282 | Format(buffer, maxlength, "%t", "Jail menu"); 283 | } 284 | else if(action == TopMenuAction_SelectOption) 285 | { 286 | DisplayJailMenu(param); 287 | } 288 | } 289 | 290 | public MenuHandler_Jail(Handle:menu, MenuAction:action, param1, param2) 291 | { 292 | if(action == MenuAction_End) 293 | { 294 | CloseHandle(menu); 295 | } 296 | else if(action == MenuAction_Cancel) 297 | { 298 | if(param2 == MenuCancel_ExitBack && g_hTopMenu != INVALID_HANDLE) 299 | { 300 | DisplayTopMenu(g_hTopMenu, param1, TopMenuPosition_LastCategory); 301 | } 302 | } 303 | else if(action == MenuAction_Select) 304 | { 305 | decl String:info[32]; 306 | new userid, target; 307 | 308 | GetMenuItem(menu, param2, info, sizeof(info)); 309 | userid = StringToInt(info); 310 | 311 | if(!g_bReady) 312 | { 313 | PrintToChat(param1, "[SM] %t", "No coords"); 314 | } 315 | else if((target = GetClientOfUserId(userid)) == 0) 316 | { 317 | PrintToChat(param1, "[SM] %t", "Player no longer available"); 318 | } 319 | else if(!CanUserTarget(param1, target)) 320 | { 321 | PrintToChat(param1, "[SM] %t", "Unable to target"); 322 | } 323 | else 324 | { 325 | JailPlayer(param1, target); 326 | } 327 | } 328 | } 329 | 330 | // WE MUST PUSH LITTLE KART -------------------------------------------------------------------------------- /mapstats.sp: -------------------------------------------------------------------------------- 1 | #pragma semicolon 1 2 | #include 3 | 4 | #define PLUGIN_VERSION "1.1.4" 5 | 6 | public Plugin:myinfo = 7 | { 8 | name = "Map Stats", 9 | author = "Stevo.TVR", 10 | description = "Records server population stats for maps", 11 | version = PLUGIN_VERSION, 12 | url = "http://www.theville.org" 13 | } 14 | 15 | new Handle:hDatabase = INVALID_HANDLE; 16 | new Handle:hTimer = INVALID_HANDLE; 17 | new Handle:hPlayerTrie = INVALID_HANDLE; 18 | new Handle:hSnapshots = INVALID_HANDLE; 19 | 20 | new Handle:sm_mapstats_interval = INVALID_HANDLE; 21 | 22 | new String:g_mapName[128]; 23 | new g_hostip; 24 | new g_hostport; 25 | new g_serverId; 26 | new g_mapId; 27 | new g_userId[MAXPLAYERS+1]; 28 | new g_playerJoins; 29 | new g_playerQuits; 30 | new g_mapStartTime; 31 | new bool:g_waitingForPlayers = true; 32 | new bool:g_mapChanging = false; 33 | 34 | public OnPluginStart() 35 | { 36 | CreateConVar("sm_mapstats_version", PLUGIN_VERSION, "Map Stats plugin version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD); 37 | sm_mapstats_interval = CreateConVar("sm_mapstats_interval", "300.0", "Number of seconds between population snapshots", _, true, 30.0); 38 | 39 | HookConVarChange(sm_mapstats_interval, ConVarChanged); 40 | AutoExecConfig(true, "mapstats"); 41 | 42 | HookEvent("player_disconnect", Event_PlayerDisconnect); 43 | 44 | hPlayerTrie = CreateTrie(); 45 | hSnapshots = CreateArray(); 46 | 47 | SQL_TConnect(T_DBConnect, "mapstats"); 48 | } 49 | 50 | public OnConfigsExecuted() 51 | { 52 | hTimer = CreateTimer(GetConVarFloat(sm_mapstats_interval), Timer_Snapshot, _, TIMER_REPEAT); 53 | } 54 | 55 | public ConVarChanged(Handle:convar, const String:oldValue[], const String:newValue[]) 56 | { 57 | if(hTimer != INVALID_HANDLE) 58 | { 59 | CloseHandle(hTimer); 60 | hTimer = CreateTimer(GetConVarFloat(sm_mapstats_interval), Timer_Snapshot, _, TIMER_REPEAT); 61 | } 62 | } 63 | 64 | public OnMapStart() 65 | { 66 | g_mapStartTime = GetTime(); 67 | LoadMap(); 68 | } 69 | 70 | LoadMap() 71 | { 72 | if(hDatabase != INVALID_HANDLE) 73 | { 74 | decl String:query[512], String:mapName[64]; 75 | GetCurrentMap(mapName, sizeof(mapName)); 76 | SQL_EscapeString(hDatabase, mapName, g_mapName, sizeof(g_mapName)); 77 | Format(query, sizeof(query), "SELECT `id` FROM `mapstats_maps` WHERE `name` = '%s';", g_mapName); 78 | SQL_TQuery(hDatabase, T_FetchMapId, query); 79 | } 80 | } 81 | 82 | public OnMapEnd() 83 | { 84 | if(!g_waitingForPlayers) 85 | { 86 | SendSummary(); 87 | } 88 | g_mapId = 0; 89 | g_mapChanging = true; 90 | CreateTimer(30.0, Timer_Mapchange); 91 | } 92 | 93 | SendSummary() 94 | { 95 | if(hDatabase != INVALID_HANDLE && g_serverId > 0 && g_mapId > 0) 96 | { 97 | new avgPop, num = GetArraySize(hSnapshots); 98 | if(num > 0) 99 | { 100 | for(new i = 0; i < num; i++) 101 | { 102 | avgPop += GetArrayCell(hSnapshots, i); 103 | } 104 | avgPop /= num; 105 | 106 | decl String:query[512]; 107 | Format(query, sizeof(query), "INSERT INTO `mapstats_summary` (`serverid`, `mapid`, `popavg`, `quits`, `joins`, `duration`) VALUES (%d, %d, %d, %d, %d, %d);", g_serverId, g_mapId, avgPop, g_playerQuits, g_playerJoins, GetTime() - g_mapStartTime); 108 | SQL_TQuery(hDatabase, T_FastQuery, query); 109 | } 110 | } 111 | 112 | g_playerQuits = 0; 113 | g_playerJoins = 0; 114 | ClearTrie(hPlayerTrie); 115 | ClearArray(hSnapshots); 116 | } 117 | 118 | public OnClientAuthorized(client, const String:auth[]) 119 | { 120 | if(g_waitingForPlayers) 121 | { 122 | TakeSnapshot(0); 123 | g_mapStartTime = GetTime(); 124 | g_waitingForPlayers = false; 125 | } 126 | 127 | new uid = GetClientUserId(client); 128 | if(g_userId[client] != uid) 129 | { 130 | g_userId[client] = uid; 131 | new val; 132 | if(GetTrieValue(hPlayerTrie, auth, val)) 133 | { 134 | g_playerQuits--; 135 | } 136 | else 137 | { 138 | g_playerJoins++; 139 | } 140 | } 141 | 142 | SetTrieValue(hPlayerTrie, auth, true); 143 | } 144 | 145 | public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) 146 | { 147 | g_playerQuits++; 148 | } 149 | 150 | public Action:Timer_Snapshot(Handle:Timer) 151 | { 152 | if(!g_waitingForPlayers && !g_mapChanging) 153 | { 154 | new pop = GetRealClientCount(); 155 | if(pop > 0) 156 | { 157 | PushArrayCell(hSnapshots, pop); 158 | TakeSnapshot(pop); 159 | } 160 | else 161 | { 162 | TakeSnapshot(0); 163 | SendSummary(); 164 | g_waitingForPlayers = true; 165 | } 166 | } 167 | } 168 | 169 | public Action:Timer_Mapchange(Handle:Timer) 170 | { 171 | g_mapChanging = false; 172 | } 173 | 174 | TakeSnapshot(pop) 175 | { 176 | if(hDatabase != INVALID_HANDLE && g_serverId > 0 && g_mapId > 0) 177 | { 178 | decl String:query[512]; 179 | Format(query, sizeof(query), "INSERT INTO `mapstats_pop` (`serverid`, `mapid`, `pop`) VALUES (%d, %d, %d);", g_serverId, g_mapId, pop); 180 | SQL_TQuery(hDatabase, T_FastQuery, query); 181 | } 182 | } 183 | 184 | GetRealClientCount() 185 | { 186 | new clients = 0; 187 | for(new i = 1; i <= MaxClients; i++) 188 | { 189 | if(IsClientInGame(i) && !IsFakeClient(i)) 190 | { 191 | clients++; 192 | } 193 | } 194 | return clients; 195 | } 196 | 197 | // Threaded DB stuff 198 | public T_DBConnect(Handle:owner, Handle:hndl, const String:error[], any:data) 199 | { 200 | if(hndl == INVALID_HANDLE) 201 | { 202 | SetFailState(error); 203 | } 204 | hDatabase = hndl; 205 | SQL_TQuery(hndl, T_FastQuery, "CREATE TABLE IF NOT EXISTS `mapstats_servers` (`id` int NOT NULL AUTO_INCREMENT, `ip` varchar(64) NOT NULL, `name` text NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ip` (`ip`)) ENGINE=InnoDB;"); 206 | SQL_TQuery(hndl, T_FastQuery, "CREATE TABLE IF NOT EXISTS `mapstats_maps` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`)) ENGINE=InnoDB;"); 207 | SQL_TQuery(hndl, T_FastQuery, "CREATE TABLE IF NOT EXISTS `mapstats_pop` (`serverid` int NOT NULL, `mapid` int NOT NULL, `pop` int NOT NULL, `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB;"); 208 | SQL_TQuery(hndl, T_FastQuery, "CREATE TABLE IF NOT EXISTS `mapstats_summary` (`serverid` int NOT NULL, `mapid` int NOT NULL, `popavg` int NOT NULL, `quits` int NOT NULL, `joins` int NOT NULL, `duration` int NOT NULL, `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB;"); 209 | 210 | g_hostip = GetConVarInt(FindConVar("hostip")); 211 | g_hostport = GetConVarInt(FindConVar("hostport")); 212 | 213 | decl String:query[512]; 214 | Format(query, sizeof(query), "SELECT `id` FROM `mapstats_servers` WHERE `ip` = '%d:%d';", g_hostip, g_hostport); 215 | SQL_TQuery(hndl, T_FetchServerId, query); 216 | 217 | LoadMap(); 218 | } 219 | 220 | public T_FetchServerId(Handle:owner, Handle:hndl, const String:error[], any:data) 221 | { 222 | decl String:serverName[256], String:serverNameSafe[512], String:query[1024]; 223 | GetConVarString(FindConVar("hostname"), serverName, sizeof(serverName)); 224 | SQL_EscapeString(hDatabase, serverName, serverNameSafe, sizeof(serverNameSafe)); 225 | 226 | if(hndl != INVALID_HANDLE) 227 | { 228 | if(SQL_GetRowCount(hndl) > 0) 229 | { 230 | if(SQL_FetchRow(hndl)) 231 | { 232 | g_serverId = SQL_FetchInt(hndl, 0); 233 | Format(query, sizeof(query), "UPDATE `mapstats_servers` SET `name` = '%s' WHERE `id` = %d;", serverNameSafe, g_serverId); 234 | SQL_TQuery(hDatabase, T_FastQuery, query); 235 | return; 236 | } 237 | } 238 | } 239 | 240 | Format(query, sizeof(query), "INSERT INTO `mapstats_servers` (`ip`, `name`) VALUES ('%d:%d', '%s');", g_hostip, g_hostport, serverNameSafe); 241 | SQL_TQuery(hDatabase, T_InsertServer, query); 242 | } 243 | 244 | public T_InsertServer(Handle:owner, Handle:hndl, const String:error[], any:data) 245 | { 246 | if(hndl != INVALID_HANDLE && SQL_GetAffectedRows(owner) > 0) 247 | { 248 | g_serverId = SQL_GetInsertId(owner); 249 | } 250 | } 251 | 252 | public T_FetchMapId(Handle:owner, Handle:hndl, const String:error[], any:data) 253 | { 254 | if(hndl != INVALID_HANDLE) 255 | { 256 | if(SQL_GetRowCount(hndl) > 0) 257 | { 258 | if(SQL_FetchRow(hndl)) 259 | { 260 | g_mapId = SQL_FetchInt(hndl, 0); 261 | return; 262 | } 263 | } 264 | } 265 | 266 | decl String:query[512]; 267 | Format(query, sizeof(query), "INSERT INTO `mapstats_maps` (`name`) VALUES ('%s');", g_mapName); 268 | SQL_TQuery(hDatabase, T_InsertMap, query); 269 | } 270 | 271 | public T_InsertMap(Handle:owner, Handle:hndl, const String:error[], any:data) 272 | { 273 | if(hndl != INVALID_HANDLE && SQL_GetAffectedRows(owner) > 0) 274 | { 275 | g_mapId = SQL_GetInsertId(owner); 276 | } 277 | } 278 | 279 | public T_FastQuery(Handle:owner, Handle:hndl, const String:error[], any:data) 280 | { 281 | // Nothing to do 282 | } -------------------------------------------------------------------------------- /nextmap_poplimits.sp: -------------------------------------------------------------------------------- 1 | /** 2 | * vim: set ts=4 : 3 | * ============================================================================= 4 | * SourceMod Nextmap Plugin 5 | * Adds sm_nextmap cvar for changing map and nextmap chat trigger. 6 | * 7 | * SourceMod (C)2004-2014 AlliedModders LLC. All rights reserved. 8 | * ============================================================================= 9 | * 10 | * This program is free software; you can redistribute it and/or modify it under 11 | * the terms of the GNU General Public License, version 3.0, as published by the 12 | * Free Software Foundation. 13 | * 14 | * This program is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 | * details. 18 | * 19 | * You should have received a copy of the GNU General Public License along with 20 | * this program. If not, see . 21 | * 22 | * As a special exception, AlliedModders LLC gives you permission to link the 23 | * code of this program (as well as its derivative works) to "Half-Life 2," the 24 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 25 | * by the Valve Corporation. You must obey the GNU General Public License in 26 | * all respects for all other code used. Additionally, AlliedModders LLC grants 27 | * this exception to all derivative works. AlliedModders LLC defines further 28 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 29 | * or . 30 | * 31 | * Version: $Id$ 32 | */ 33 | 34 | #include 35 | #include "include/nextmap.inc" 36 | 37 | #pragma semicolon 1 38 | #pragma newdecls required 39 | 40 | public Plugin myinfo = 41 | { 42 | name = "Nextmap w/ Population Limits", 43 | author = "AlliedModders LLC/StevoTVR", 44 | description = "Provides nextmap and sm_nextmap, with population limits", 45 | version = SOURCEMOD_VERSION, 46 | url = "http://www.sourcemod.net/" 47 | }; 48 | 49 | int g_MapPos = -1; 50 | int g_NextMapPos = -1; 51 | ArrayList g_MapList = null; 52 | ArrayList g_MapLimits = null; 53 | 54 | int g_CurrentMapStartTime; 55 | 56 | bool g_NextMapLock; 57 | 58 | public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) 59 | { 60 | char game[128]; 61 | GetGameFolderName(game, sizeof(game)); 62 | 63 | if (StrEqual(game, "left4dead", false) 64 | || StrEqual(game, "dystopia", false) 65 | || StrEqual(game, "synergy", false) 66 | || StrEqual(game, "left4dead2", false) 67 | || StrEqual(game, "garrysmod", false) 68 | || StrEqual(game, "swarm", false) 69 | || StrEqual(game, "dota", false) 70 | || StrEqual(game, "bms", false) 71 | || GetEngineVersion() == Engine_Insurgency) 72 | { 73 | strcopy(error, err_max, "Nextmap is incompatible with this game"); 74 | return APLRes_SilentFailure; 75 | } 76 | 77 | DisablePlugin("nextmap"); 78 | DisablePlugin("mapchooser"); 79 | 80 | return APLRes_Success; 81 | } 82 | 83 | public void OnPluginStart() 84 | { 85 | LoadTranslations("common.phrases"); 86 | LoadTranslations("nextmap.phrases"); 87 | LoadTranslations("mapchooser.phrases"); 88 | 89 | int size = ByteCountToCells(PLATFORM_MAX_PATH); 90 | g_MapList = new ArrayList(size); 91 | g_MapLimits = new ArrayList(2); 92 | 93 | RegAdminCmd("sm_maphistory", Command_MapHistory, ADMFLAG_CHANGEMAP, "Shows the most recent maps played"); 94 | RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); 95 | RegConsoleCmd("listmaps", Command_List); 96 | 97 | CreateTimer(60.0, Timer_Update, _, TIMER_REPEAT); 98 | } 99 | 100 | public void OnMapStart() 101 | { 102 | g_CurrentMapStartTime = GetTime(); 103 | g_NextMapLock = false; 104 | } 105 | 106 | public void OnConfigsExecuted() 107 | { 108 | ReadMapCycle(); 109 | g_MapPos = -1; 110 | } 111 | 112 | public Action Command_List(int client, int args) 113 | { 114 | PrintToConsole(client, "Map Cycle:"); 115 | 116 | int mapCount = g_MapList.Length; 117 | char mapName[PLATFORM_MAX_PATH]; 118 | int limits[2]; 119 | for (int i = 0; i < mapCount; i++) 120 | { 121 | g_MapList.GetString(i, mapName, sizeof(mapName)); 122 | g_MapLimits.GetArray(i, limits); 123 | int min = limits[0]; 124 | int max = limits[1] > 0 ? limits[1] : MaxClients; 125 | PrintToConsole(client, "%s (%i - %i pl.)", mapName, min, max); 126 | } 127 | 128 | return Plugin_Handled; 129 | } 130 | 131 | public Action Command_SetNextmap(int client, int args) 132 | { 133 | if (args < 1) 134 | { 135 | ReplyToCommand(client, "[SM] Usage: sm_setnextmap "); 136 | return Plugin_Handled; 137 | } 138 | 139 | char map[PLATFORM_MAX_PATH]; 140 | GetCmdArg(1, map, sizeof(map)); 141 | 142 | if (!IsMapValid(map)) 143 | { 144 | ReplyToCommand(client, "[SM] %t", "Map was not found", map); 145 | return Plugin_Handled; 146 | } 147 | 148 | ShowActivity(client, "%t", "Changed Next Map", map); 149 | LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); 150 | 151 | SetNextMap(map); 152 | g_NextMapLock = true; 153 | 154 | return Plugin_Handled; 155 | } 156 | 157 | public Action Timer_Update(Handle timer) 158 | { 159 | if (!g_NextMapLock) 160 | FindAndSetNextMap(); 161 | } 162 | 163 | void FindAndSetNextMap() 164 | { 165 | int mapCount = g_MapList.Length; 166 | char mapName[PLATFORM_MAX_PATH]; 167 | 168 | if (g_MapPos == -1) 169 | { 170 | char current[PLATFORM_MAX_PATH]; 171 | GetCurrentMap(current, sizeof(current)); 172 | 173 | for (int i = 0; i < mapCount; i++) 174 | { 175 | g_MapList.GetString(i, mapName, sizeof(mapName)); 176 | if (strcmp(current, mapName, false) == 0) 177 | { 178 | g_MapPos = i; 179 | break; 180 | } 181 | } 182 | 183 | if (g_MapPos == -1) 184 | g_MapPos = 0; 185 | } 186 | 187 | g_NextMapPos = g_MapPos; 188 | if (mapCount > 1) 189 | { 190 | int pop = GetClientCount(); 191 | int limits[2]; 192 | int nextMap = g_NextMapPos + 1; 193 | 194 | if (nextMap >= mapCount) 195 | nextMap = 0; 196 | 197 | while (nextMap != g_MapPos) 198 | { 199 | g_MapLimits.GetArray(nextMap, limits); 200 | 201 | if (limits[0] <= pop && (limits[1] == 0 || limits[1] >= pop)) 202 | { 203 | break; 204 | } 205 | 206 | nextMap++; 207 | 208 | if (nextMap >= mapCount) 209 | nextMap = 0; 210 | } 211 | 212 | g_NextMapPos = nextMap; 213 | } 214 | 215 | g_MapList.GetString(g_NextMapPos, mapName, sizeof(mapName)); 216 | SetNextMap(mapName); 217 | } 218 | 219 | void ReadMapCycle() 220 | { 221 | char fileName[PLATFORM_MAX_PATH]; 222 | if (!FindMapCycle(fileName, sizeof(fileName))) 223 | { 224 | LogError("FATAL: Cannot load map cycle. Nextmap not loaded."); 225 | SetFailState("Mapcycle Not Found"); 226 | } 227 | 228 | File mapCycle = OpenFile(fileName, "r", true); 229 | if (mapCycle != null) 230 | { 231 | g_MapList.Clear(); 232 | 233 | char line[PLATFORM_MAX_PATH]; 234 | char entry[3][PLATFORM_MAX_PATH]; 235 | int limits[2]; 236 | while (mapCycle.ReadLine(line, sizeof(line))) 237 | { 238 | // format: map_name [min_players] [max_players] 239 | int num = ExplodeString(line, " ", entry, sizeof(entry), sizeof(entry[])); 240 | 241 | // add map name 242 | TrimString(entry[0]); 243 | 244 | if (!IsMapValid(entry[0])) 245 | continue; 246 | 247 | g_MapList.PushString(entry[0]); 248 | 249 | // add population limits for map entry 250 | limits[0] = num > 1 ? StringToInt(entry[1]) : 0; 251 | limits[1] = num > 2 ? StringToInt(entry[2]) : 0; 252 | g_MapLimits.PushArray(limits); 253 | } 254 | 255 | mapCycle.Close(); 256 | } 257 | } 258 | 259 | bool FindMapCycle(char[] buffer, int maxlength) 260 | { 261 | char path[PLATFORM_MAX_PATH]; 262 | char fileName[PLATFORM_MAX_PATH]; 263 | ConVar mapCycleFile = FindConVar("mapcyclefile"); 264 | mapCycleFile.GetString(fileName, sizeof(fileName)); 265 | 266 | Format(path, sizeof(path), "cfg/%s", fileName); 267 | if (!FileExists(path, true)) 268 | { 269 | Format(path, sizeof(path), "%s", fileName); 270 | if (!FileExists(path, true)) 271 | { 272 | Format(path, sizeof(path), "cfg/mapcycle_default.txt"); 273 | if (!FileExists(path, true)) 274 | return false; 275 | } 276 | } 277 | 278 | strcopy(buffer, maxlength, path); 279 | return true; 280 | } 281 | 282 | public Action Command_MapHistory(int client, int args) 283 | { 284 | int mapCount = GetMapHistorySize(); 285 | 286 | char mapName[PLATFORM_MAX_PATH]; 287 | char changeReason[100]; 288 | char timeString[100]; 289 | char playedTime[100]; 290 | int startTime; 291 | 292 | int lastMapStartTime = g_CurrentMapStartTime; 293 | 294 | PrintToConsole(client, "%t:\n", "Map History"); 295 | PrintToConsole(client, "%t : %t : %t : %t", "Map", "Started", "Played Time", "Reason"); 296 | 297 | GetCurrentMap(mapName, sizeof(mapName)); 298 | PrintToConsole(client, "%02i. %s (%t)", 0, mapName, "Current Map"); 299 | 300 | for (int i=0; i 0) 323 | { 324 | return Format(buffer, maxlen, "%id %ih %im", days, hours, (seconds >= 30) ? minutes+1 : minutes); 325 | } 326 | else if (hours > 0) 327 | { 328 | return Format(buffer, maxlen, "%ih %im", hours, (seconds >= 30) ? minutes+1 : minutes); 329 | } 330 | else if (minutes > 0) 331 | { 332 | return Format(buffer, maxlen, "%im", (seconds >= 30) ? minutes+1 : minutes); 333 | } 334 | else 335 | { 336 | return Format(buffer, maxlen, "%is", seconds); 337 | } 338 | } 339 | 340 | // Taken from SourceBans 341 | bool DisablePlugin(const char[] file) 342 | { 343 | char sNewPath[PLATFORM_MAX_PATH + 1]; 344 | char sOldPath[PLATFORM_MAX_PATH + 1]; 345 | BuildPath(Path_SM, sNewPath, sizeof(sNewPath), "plugins/disabled/%s.smx", file); 346 | BuildPath(Path_SM, sOldPath, sizeof(sOldPath), "plugins/%s.smx", file); 347 | 348 | // If plugins/.smx does not exist, ignore 349 | if (!FileExists(sOldPath)) 350 | return false; 351 | 352 | // If plugins/disabled/.smx exists, delete it 353 | if (FileExists(sNewPath)) 354 | DeleteFile(sNewPath); 355 | 356 | // Unload plugins/.smx and move it to plugins/disabled/.smx 357 | ServerCommand("sm plugins unload %s", file); 358 | RenameFile(sNewPath, sOldPath); 359 | LogMessage("plugins/%s.smx was unloaded and moved to plugins/disabled/%s.smx", file, file); 360 | return true; 361 | } 362 | -------------------------------------------------------------------------------- /nmrihstats.sp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Simple NMRiH Stats 4 | * https://forums.alliedmods.net/showthread.php?t=230459 5 | * 6 | * Description: 7 | * This is a basic point-based stats plugin for No More Room in Hell, with rank 8 | * and top10 commands. Stat data is stored in a configurable database. 9 | * 10 | * Default Configuration: 11 | * - Zombie kill: +1 12 | * - Headshot bonus: +1 13 | * - Death: -20 14 | * - Team kill: -20 15 | * - Extraction: +50 16 | * 17 | * 18 | * Changelog 19 | * Dec 22, 2013 - v.0.5: 20 | * [+] Added sm_stats_start_at_avg ConVar to toggle players starting at the 21 | * average or 0 22 | * [*] Players cannot start with negative points 23 | * [*] Fixed bug with sm_stats_setpoints targetting 24 | * [-] Disabled reward for objectives due to game event unreliability 25 | * [*] Adjusted default reward values (-20 for death, +50 for extraction) 26 | * Dec 11, 2013 - v.0.4: 27 | * [+] Added commands to set players' point values manually 28 | * [+] Added command to reset the stats database 29 | * [-] Removed sm_stats_startpoints ConVar 30 | * [*] New players now start at the average score of all players 31 | * [*] Fixed server being able to call rank and top10 commands 32 | * [*] Fixed plugin overriding database changes from other sources 33 | * Dec 01, 2013 - v.0.3: 34 | * [+] Added detection of fire kills 35 | * [+] Added headshot bonus 36 | * [+] Added point award for getting extracted 37 | * [+] Added team point award for completing an objective 38 | * [+] Added chat triggers for rank and top10 39 | * Nov 27, 2013 - v.0.2: 40 | * [+] Added sm_stats_startpoints ConVar 41 | * [+] Added stat notifications in players' chat area 42 | * [*] Fixed race condition with database connection 43 | * [*] Only updates names when needed 44 | * [*] Only allows loading on NMRiH 45 | * Nov 25, 2013 - v.0.1: 46 | * [*] Initial Release 47 | * 48 | */ 49 | 50 | #pragma semicolon 1 51 | #include 52 | 53 | #define PLUGIN_VERSION "0.5" 54 | //#define DEBUG 55 | 56 | public Plugin:myinfo = 57 | { 58 | name = "Simple NMRiH Stats", 59 | author = "Stevo.TVR", 60 | description = "Basic point-based stats for No More Room in Hell", 61 | version = PLUGIN_VERSION, 62 | url = "https://forums.alliedmods.net/showthread.php?t=230459" 63 | } 64 | 65 | new Handle:hDatabase = INVALID_HANDLE; 66 | 67 | new Handle:sm_stats_killpoints = INVALID_HANDLE; 68 | new Handle:sm_stats_deathpoints = INVALID_HANDLE; 69 | new Handle:sm_stats_tkpoints = INVALID_HANDLE; 70 | new Handle:sm_stats_headshot_bonus = INVALID_HANDLE; 71 | new Handle:sm_stats_extractionpoints = INVALID_HANDLE; 72 | //new Handle:sm_stats_objectivepoints = INVALID_HANDLE; 73 | new Handle:sm_stats_start_at_avg = INVALID_HANDLE; 74 | 75 | new clientPoints[MAXPLAYERS+1]; 76 | new clientKills[MAXPLAYERS+1]; 77 | new clientDeaths[MAXPLAYERS+1]; 78 | 79 | new clientKillsSinceNotify[MAXPLAYERS+1]; 80 | new clientKillPointsSinceNotify[MAXPLAYERS+1]; 81 | 82 | new totalPlayers; 83 | new resetCode; 84 | 85 | public OnPluginStart() 86 | { 87 | decl String:game[16]; 88 | GetGameFolderName(game, sizeof(game)); 89 | if(strcmp(game, "nmrih", false) != 0) 90 | { 91 | SetFailState("Unsupported game!"); 92 | } 93 | 94 | CreateConVar("sm_nmrihstats_version", PLUGIN_VERSION, "Simple NMRiH Stats version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD); 95 | sm_stats_killpoints = CreateConVar("sm_stats_killpoints", "1", "Points to award for a zombie kill"); 96 | sm_stats_deathpoints = CreateConVar("sm_stats_deathpoints", "-20", "Points to award for getting killed"); 97 | sm_stats_tkpoints = CreateConVar("sm_stats_tkpoints", "-20", "Points to award for killing a teammate"); 98 | sm_stats_headshot_bonus = CreateConVar("sm_stats_headshot_bonus", "1", "Bonus points to award for headshots on top of sm_stats_killpoints"); 99 | sm_stats_extractionpoints = CreateConVar("sm_stats_extractionpoints", "50", "Points to award for getting extracted"); 100 | //sm_stats_objectivepoints = CreateConVar("sm_stats_objectivepoints", "5", "Points to award team for completing an objective"); 101 | sm_stats_start_at_avg = CreateConVar("sm_stats_start_at_avg", "1", "New players start with the average of all player scores (0 = players start at 0)", _, true, 0.0, true, 1.0); 102 | 103 | AutoExecConfig(true, "nmrihstats"); 104 | 105 | RegConsoleCmd("sm_rank", Command_Rank, "Displays your current rank"); 106 | RegConsoleCmd("sm_top10", Command_Top10, "Lists top 10 players"); 107 | 108 | RegAdminCmd("sm_stats_setpoints", Command_SetPoints, ADMFLAG_RCON, "Set a player's stat points"); 109 | RegAdminCmd("sm_stats_setpoints_id", Command_SetPointsId, ADMFLAG_RCON, "Set a SteamID's stat points"); 110 | RegAdminCmd("sm_stats_reset", Command_Reset, ADMFLAG_RCON, "Reset all stats. Use once to get code, use with code to confirm."); 111 | 112 | LoadTranslations("common.phrases"); 113 | 114 | HookEvent("player_death", Event_PlayerDeath); 115 | HookEvent("npc_killed", Event_NPCKilled); 116 | HookEvent("zombie_killed_by_fire", Event_ZombieKilledByFire); 117 | HookEvent("zombie_head_split", Event_ZombieHeadSplit); 118 | HookEvent("player_extracted", Event_PlayerExtracted); 119 | //HookEvent("objective_complete", Event_ObjectiveComplete); 120 | HookEvent("player_changename", Event_ChangeName); 121 | 122 | ConnectDatabase(); 123 | 124 | CreateTimer(300.0, Timer_PlayerKillsNotify, _, TIMER_REPEAT); 125 | } 126 | 127 | public ConnectDatabase() 128 | { 129 | new String:db[] = "storage-local"; 130 | if(SQL_CheckConfig("nmrihstats")) 131 | { 132 | db = "nmrihstats"; 133 | } 134 | decl String:error[256]; 135 | hDatabase = SQL_Connect(db, true, error, sizeof(error)); 136 | if(hDatabase == INVALID_HANDLE) 137 | { 138 | SetFailState(error); 139 | } 140 | 141 | SQL_TQuery(hDatabase, T_FastQuery, "CREATE TABLE IF NOT EXISTS nmrihstats (steam_id VARCHAR(64) PRIMARY KEY, name TEXT, points INTEGER, kills INTEGER, deaths INTEGER);"); 142 | } 143 | 144 | public OnMapStart() 145 | { 146 | for(new i = 1; i <= MaxClients; i++) 147 | { 148 | if(IsClientAuthorized(i) && !IsFakeClient(i)) 149 | { 150 | decl String:query[1024], String:auth[64]; 151 | GetClientAuthString(i, auth, sizeof(auth)); 152 | Format(query, sizeof(query), "SELECT name, points, kills, deaths FROM nmrihstats WHERE steam_id = '%s' LIMIT 1;", auth); 153 | SQL_TQuery(hDatabase, T_LoadPlayer, query, i); 154 | } 155 | } 156 | } 157 | 158 | public OnClientAuthorized(client, const String:auth[]) 159 | { 160 | if(IsFakeClient(client)) 161 | return; 162 | 163 | clientKillPointsSinceNotify[client] = 0; 164 | clientKillsSinceNotify[client] = 0; 165 | 166 | decl String:query[1024]; 167 | Format(query, sizeof(query), "SELECT name, points, kills, deaths FROM nmrihstats WHERE steam_id = '%s' LIMIT 1;", auth); 168 | SQL_TQuery(hDatabase, T_LoadPlayer, query, client); 169 | } 170 | 171 | public T_LoadPlayer(Handle:owner, Handle:hndl, const String:error[], any:client) 172 | { 173 | if(!IsClientAuthorized(client) || IsFakeClient(client)) 174 | return; 175 | 176 | if(hndl != INVALID_HANDLE) 177 | { 178 | if(SQL_FetchRow(hndl)) 179 | { 180 | decl String:authid[64], String:playername[64], String:dbname[64]; 181 | GetClientAuthString(client, authid, sizeof(authid)); 182 | GetClientName(client, playername, sizeof(playername)); 183 | 184 | SQL_FetchString(hndl, 0, dbname, sizeof(dbname)); 185 | if(strcmp(playername, dbname) != 0) 186 | { 187 | UpdatePlayerName(authid, playername); 188 | } 189 | 190 | clientPoints[client] = SQL_FetchInt(hndl, 1); 191 | clientKills[client] = SQL_FetchInt(hndl, 2); 192 | clientDeaths[client] = SQL_FetchInt(hndl, 3); 193 | 194 | #if defined DEBUG 195 | LogMessage("Loaded player: %L [%dp %dk %dd]", client, clientPoints[client], clientKills[client], clientDeaths[client]); 196 | #endif 197 | } 198 | else 199 | { 200 | if(GetConVarBool(sm_stats_start_at_avg)) 201 | { 202 | SQL_TQuery(hDatabase, T_AddPlayer, "SELECT AVG(points) FROM nmrihstats;", client); 203 | } 204 | else 205 | { 206 | AddPlayer(client, 0); 207 | } 208 | } 209 | } 210 | } 211 | 212 | public T_AddPlayer(Handle:owner, Handle:hndl, const String:error[], any:client) 213 | { 214 | if(hndl != INVALID_HANDLE) 215 | { 216 | if(SQL_FetchRow(hndl)) 217 | { 218 | new points = SQL_FetchInt(hndl, 0); 219 | if(points < 0) 220 | points = 0; 221 | 222 | AddPlayer(client, points); 223 | } 224 | } 225 | } 226 | 227 | public AddPlayer(client, points) 228 | { 229 | if(!IsClientAuthorized(client) || IsFakeClient(client)) 230 | return; 231 | 232 | decl String:query[1024], String:authid[64], String:playername[64], String:escname[129]; 233 | GetClientAuthString(client, authid, sizeof(authid)); 234 | GetClientName(client, playername, sizeof(playername)); 235 | SQL_EscapeString(hDatabase, playername, escname, sizeof(escname)); 236 | 237 | Format(query, sizeof(query), "INSERT INTO nmrihstats VALUES ('%s', '%s', %d, 0, 0);", authid, escname, points); 238 | SQL_TQuery(hDatabase, T_FastQuery, query); 239 | 240 | clientPoints[client] = points; 241 | clientKills[client] = 0; 242 | clientDeaths[client] = 0; 243 | 244 | #if defined DEBUG 245 | LogMessage("Adding player: %L [%dp %dk %dd]", client, clientPoints[client], clientKills[client], clientDeaths[client]); 246 | #endif 247 | } 248 | 249 | public Action:Timer_PlayerKillsNotify(Handle:timer) 250 | { 251 | for(new i = 1; i <= MaxClients; i++) 252 | { 253 | if(IsClientInGame(i) && !IsFakeClient(i) && clientKillPointsSinceNotify[i] != 0) 254 | { 255 | new change = clientKillPointsSinceNotify[i], kills = clientKillsSinceNotify[i]; 256 | PrintToChat(i, "\x04[Stats]\x01 %s%d point%s (%d) for killing %d zombie%s", (change >= 0 ? "+" : ""), change, (change != -1 && change != 1 ? "s" : ""), clientPoints[i], kills, (kills > 1 ? "s" : "")); 257 | clientKillPointsSinceNotify[i] = 0; 258 | clientKillsSinceNotify[i] = 0; 259 | } 260 | } 261 | } 262 | 263 | public Action:Command_Rank(client, args) 264 | { 265 | ShowRank(client); 266 | return Plugin_Handled; 267 | } 268 | 269 | public ShowRank(client) 270 | { 271 | if(client == 0) 272 | return; 273 | 274 | SQL_TQuery(hDatabase, T_UpdateTotalQuery, "SELECT COUNT(*) FROM nmrihstats;"); 275 | 276 | decl String:query[1024]; 277 | Format(query, sizeof(query), "SELECT points FROM nmrihstats WHERE points > %d ORDER BY points ASC;", clientPoints[client]); 278 | SQL_TQuery(hDatabase, T_RankQuery, query, client); 279 | } 280 | 281 | public T_UpdateTotalQuery(Handle:owner, Handle:hndl, const String:error[], any:client) 282 | { 283 | if(hndl != INVALID_HANDLE && SQL_FetchRow(hndl)) 284 | { 285 | totalPlayers = SQL_FetchInt(hndl, 0); 286 | } 287 | } 288 | 289 | public T_RankQuery(Handle:owner, Handle:hndl, const String:error[], any:client) 290 | { 291 | if(hndl == INVALID_HANDLE || !IsClientInGame(client)) 292 | return; 293 | 294 | new rank = 1, rankdiff = 0; 295 | if(SQL_FetchRow(hndl)) 296 | { 297 | rank = SQL_GetRowCount(hndl) + 1; 298 | rankdiff = SQL_FetchInt(hndl, 0) - clientPoints[client]; 299 | } 300 | PrintToChatAll("\x01Player \x04%N\x01 is rank \x04%d\x01 of \x04%d\x01 total tracked player%s with \x04%d\x01 point%s and is \x04%d\x01 point%s away from the next rank.", client, rank, totalPlayers, totalPlayers != 1 ? "s" : "", clientPoints[client], clientPoints[client] != 1 ? "s" : "", rankdiff, rankdiff != 1 ? "s" : ""); 301 | } 302 | 303 | public Action:Command_Top10(client, args) 304 | { 305 | ShowTop10(client); 306 | return Plugin_Handled; 307 | } 308 | 309 | public ShowTop10(client) 310 | { 311 | if(client == 0) 312 | return; 313 | 314 | SQL_TQuery(hDatabase, T_Top10Query, "SELECT name, points FROM nmrihstats ORDER BY points DESC LIMIT 10;", client); 315 | } 316 | 317 | public T_Top10Query(Handle:owner, Handle:hndl, const String:error[], any:client) 318 | { 319 | if(hndl == INVALID_HANDLE || !IsClientInGame(client)) 320 | return; 321 | 322 | decl String:name[64], String:line[128]; 323 | new i; 324 | PrintToChat(client, "\x04Top 10 players:"); 325 | while(SQL_FetchRow(hndl)) 326 | { 327 | SQL_FetchString(hndl, 0, name, sizeof(name)); 328 | Format(line, sizeof(line), "\x04#%d.\x01 %s (%d)", ++i, name, SQL_FetchInt(hndl, 1)); 329 | new Handle:data; 330 | CreateDataTimer(5.0 - 0.5 * i, Timer_Top10, data); 331 | WritePackCell(data, client); 332 | WritePackString(data, line); 333 | } 334 | } 335 | 336 | public Action:Timer_Top10(Handle:timer, Handle:hndl) 337 | { 338 | ResetPack(hndl); 339 | new client = ReadPackCell(hndl); 340 | if(IsClientInGame(client)) 341 | { 342 | decl String:line[128]; 343 | ReadPackString(hndl, line, sizeof(line)); 344 | PrintToChat(client, line); 345 | } 346 | } 347 | 348 | public OnClientSayCommand_Post(client, const String:command[], const String:sArgs[]) 349 | { 350 | decl String:text[192]; 351 | new startidx = 0; 352 | 353 | if(strcopy(text, sizeof(text), sArgs) < 1) 354 | { 355 | return; 356 | } 357 | 358 | if(text[0] == '"') 359 | { 360 | startidx = 1; 361 | } 362 | 363 | if((strcmp(command, "say2", false) == 0) && strlen(sArgs) >= 4) 364 | startidx += 4; 365 | 366 | if(strcmp(text[startidx], "rank", false) == 0 367 | || strcmp(text[startidx], "/rank", false) == 0 368 | || strcmp(text[startidx], "!rank", false) == 0) 369 | { 370 | ShowRank(client); 371 | } 372 | else if(strcmp(text[startidx], "top10", false) == 0 373 | || strcmp(text[startidx], "/top10", false) == 0 374 | || strcmp(text[startidx], "!top10", false) == 0) 375 | { 376 | ShowTop10(client); 377 | } 378 | } 379 | 380 | public Action:Command_SetPoints(client, args) 381 | { 382 | if(args < 2) 383 | { 384 | ReplyToCommand(client, "[SM] Usage: sm_stats_setpoints <#userid|name>"); 385 | return Plugin_Handled; 386 | } 387 | 388 | decl String:pointsString[64], String:targetString[256]; 389 | 390 | GetCmdArg(1, pointsString, sizeof(pointsString)); 391 | new points = StringToInt(pointsString); 392 | 393 | GetCmdArg(2, targetString, sizeof(targetString)); 394 | decl targets[64], String:tn[MAX_TARGET_LENGTH], bool:tn_is_ml; 395 | new count = ProcessTargetString(targetString, client, targets, sizeof(targets), COMMAND_FILTER_NO_BOTS, tn, sizeof(tn), tn_is_ml); 396 | if(count < 1) 397 | { 398 | ReplyToTargetError(client, count); 399 | return Plugin_Handled; 400 | } 401 | 402 | decl String:auth[64]; 403 | for(new i = 0; i < count; i++) 404 | { 405 | if(IsClientAuthorized(targets[i])) 406 | { 407 | GetClientAuthString(targets[i], auth, sizeof(auth)); 408 | SetPoints(auth, points); 409 | clientPoints[targets[i]] = points; 410 | } 411 | } 412 | 413 | return Plugin_Handled; 414 | } 415 | 416 | public Action:Command_SetPointsId(client, args) 417 | { 418 | if(args < 2) 419 | { 420 | ReplyToCommand(client, "[SM] Usage: sm_stats_setpoints_id "); 421 | return Plugin_Handled; 422 | } 423 | 424 | decl String:pointsString[64], String:targetString[64], String:auth[129]; 425 | 426 | GetCmdArg(1, pointsString, sizeof(pointsString)); 427 | new points = StringToInt(pointsString); 428 | 429 | GetCmdArg(2, targetString, sizeof(targetString)); 430 | if(strncmp(targetString, "STEAM_", 6) != 0 || targetString[7] != ':') 431 | { 432 | ReplyToCommand(client, "[SM] %t", "Invalid SteamID specified"); 433 | return Plugin_Handled; 434 | } 435 | 436 | SQL_EscapeString(hDatabase, targetString, auth, sizeof(auth)); 437 | SetPoints(auth, points); 438 | 439 | for(new i = 1; i <= MaxClients; i++) 440 | { 441 | if(IsClientAuthorized(i)) 442 | { 443 | GetClientAuthString(i, auth, sizeof(auth)); 444 | if(strcmp(targetString, auth) == 0) 445 | { 446 | clientPoints[i] = points; 447 | break; 448 | } 449 | } 450 | } 451 | 452 | return Plugin_Handled; 453 | } 454 | 455 | public SetPoints(const String:auth[], points) 456 | { 457 | decl String:query[1024]; 458 | Format(query, sizeof(query), "UPDATE nmrihstats SET points = %d WHERE steam_id = '%s';", points, auth); 459 | SQL_TQuery(hDatabase, T_FastQuery, query); 460 | } 461 | 462 | public Action:Command_Reset(client, args) 463 | { 464 | if(args < 1) 465 | { 466 | resetCode = GetRandomInt(100000, 999999); 467 | ReplyToCommand(client, "[SM] Usage: sm_stats_reset %d", resetCode); 468 | return Plugin_Handled; 469 | } 470 | 471 | decl String:arg[8]; 472 | GetCmdArg(1, arg, sizeof(arg)); 473 | new code = StringToInt(arg); 474 | 475 | if(resetCode == 0 || resetCode != code) 476 | { 477 | ReplyToCommand(client, "[SM] Error: Incorrect confirmation code!"); 478 | return Plugin_Handled; 479 | } 480 | 481 | resetCode = 0; 482 | SQL_TQuery(hDatabase, T_FastQuery,"DELETE FROM nmrihstats", 0, DBPrio_High); 483 | ReplyToCommand(client, "[SM] Success! The stats database has been reset."); 484 | ReplyToCommand(client, "[SM] Reload the plugin or change map on all servers using this database"); 485 | 486 | return Plugin_Handled; 487 | } 488 | 489 | public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) 490 | { 491 | new client = GetClientOfUserId(GetEventInt(event, "userid")); 492 | new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); 493 | 494 | if(client == 0 || IsFakeClient(client) || !IsClientAuthorized(client)) 495 | return Plugin_Continue; 496 | 497 | if(attacker != 0 && attacker != client && !IsFakeClient(attacker) && IsClientAuthorized(attacker)) 498 | { 499 | new change = GetConVarInt(sm_stats_tkpoints); 500 | if(change == 0) 501 | return Plugin_Continue; 502 | 503 | clientPoints[attacker] += change; 504 | 505 | PrintToChat(attacker, "\x04[Stats]\x01 %s%d point%s (%d) for killing a teammate!", (change >= 0 ? "+" : ""), change, (change != -1 && change != 1 ? "s" : ""), clientPoints[attacker]); 506 | 507 | #if defined DEBUG 508 | LogMessage("Player %L (%d) %d for killing a teammate", attacker, clientPoints[attacker], GetConVarInt(sm_stats_tkpoints)); 509 | #endif 510 | 511 | decl String:query[1024], String:authid[64]; 512 | GetClientAuthString(attacker, authid, sizeof(authid)); 513 | Format(query, sizeof(query), "UPDATE nmrihstats SET points = points + %d WHERE steam_id = '%s';", change, authid); 514 | SQL_TQuery(hDatabase, T_FastQuery, query); 515 | 516 | return Plugin_Continue; 517 | } 518 | 519 | new change = GetConVarInt(sm_stats_deathpoints); 520 | clientDeaths[client]++; 521 | clientPoints[client] += change; 522 | 523 | PrintToChat(client, "\x04[Stats]\x01 %s%d point%s (%d) for getting killed", (change >= 0 ? "+" : ""), change, (change != -1 && change != 1 ? "s" : ""), clientPoints[client]); 524 | 525 | #if defined DEBUG 526 | LogMessage("Player %L (%d) %d for getting killed", client, clientPoints[client], GetConVarInt(sm_stats_deathpoints)); 527 | #endif 528 | 529 | decl String:query[1024], String:authid[64]; 530 | GetClientAuthString(client, authid, sizeof(authid)); 531 | Format(query, sizeof(query), "UPDATE nmrihstats SET points = points + %d, deaths = deaths + 1 WHERE steam_id = '%s';", change, authid); 532 | SQL_TQuery(hDatabase, T_FastQuery, query); 533 | 534 | return Plugin_Continue; 535 | } 536 | 537 | public Action:Event_NPCKilled(Handle:event, const String:name[], bool:dontBroadcast) 538 | { 539 | new client = GetEventInt(event, "killeridx"); 540 | ZombieKilled(client); 541 | return Plugin_Continue; 542 | } 543 | 544 | public Action:Event_ZombieKilledByFire(Handle:event, const String:name[], bool:dontBroadcast) 545 | { 546 | new client = GetEventInt(event, "igniter_id"); 547 | #if defined DEBUG 548 | if(client > 0 && client <= MaxClients && !IsFakeClient(client) && IsClientAuthorized(client)) 549 | LogMessage("Player %L killed a zombie with fire!", client); 550 | #endif 551 | ZombieKilled(client); 552 | return Plugin_Continue; 553 | } 554 | 555 | public ZombieKilled(client) 556 | { 557 | if(client == 0 || client > MaxClients || IsFakeClient(client) || !IsClientAuthorized(client)) 558 | return; 559 | 560 | new change = GetConVarInt(sm_stats_killpoints); 561 | clientKills[client]++; 562 | clientPoints[client] += change; 563 | 564 | clientKillsSinceNotify[client]++; 565 | clientKillPointsSinceNotify[client] += change; 566 | 567 | #if defined DEBUG 568 | LogMessage("Player %L (%d) %d for killing a zombie", client, clientPoints[client], GetConVarInt(sm_stats_killpoints)); 569 | #endif 570 | 571 | decl String:query[1024], String:authid[64]; 572 | GetClientAuthString(client, authid, sizeof(authid)); 573 | Format(query, sizeof(query), "UPDATE nmrihstats SET points = points + %d, kills = kills + 1 WHERE steam_id = '%s';", change, authid); 574 | SQL_TQuery(hDatabase, T_FastQuery, query); 575 | } 576 | 577 | public Action:Event_ZombieHeadSplit(Handle:event, const String:name[], bool:dontBroadcast) 578 | { 579 | new client = GetEventInt(event, "player_id"); 580 | 581 | if(client == 0 || client > MaxClients || IsFakeClient(client) || !IsClientAuthorized(client)) 582 | return Plugin_Continue; 583 | 584 | new change = GetConVarInt(sm_stats_headshot_bonus); 585 | if(change == 0) 586 | return Plugin_Continue; 587 | 588 | clientPoints[client] += change; 589 | 590 | clientKillPointsSinceNotify[client] += change; 591 | 592 | #if defined DEBUG 593 | LogMessage("Player %L (%d) %d for headshot!", client, clientPoints[client], GetConVarInt(sm_stats_headshot_bonus)); 594 | #endif 595 | 596 | decl String:query[1024], String:authid[64]; 597 | GetClientAuthString(client, authid, sizeof(authid)); 598 | Format(query, sizeof(query), "UPDATE nmrihstats SET points = points + %d WHERE steam_id = '%s';", change, authid); 599 | SQL_TQuery(hDatabase, T_FastQuery, query); 600 | 601 | return Plugin_Continue; 602 | } 603 | 604 | public Action:Event_PlayerExtracted(Handle:event, const String:name[], bool:dontBroadcast) 605 | { 606 | new client = GetEventInt(event, "player_id"); 607 | 608 | if(client == 0 || client > MaxClients || IsFakeClient(client) || !IsClientAuthorized(client)) 609 | return Plugin_Continue; 610 | 611 | new change = GetConVarInt(sm_stats_extractionpoints); 612 | if(change == 0) 613 | return Plugin_Continue; 614 | 615 | clientPoints[client] += change; 616 | 617 | PrintToChat(client, "\x04[Stats]\x01 %s%d point%s (%d) for getting extracted", (change >= 0 ? "+" : ""), change, (change != -1 && change != 1 ? "s" : ""), clientPoints[client]); 618 | 619 | #if defined DEBUG 620 | LogMessage("Player %L (%d) %d for getting extracted", client, clientPoints[client], GetConVarInt(sm_stats_extractionpoints)); 621 | #endif 622 | 623 | decl String:query[1024], String:authid[64]; 624 | GetClientAuthString(client, authid, sizeof(authid)); 625 | Format(query, sizeof(query), "UPDATE nmrihstats SET points = points + %d WHERE steam_id = '%s';", change, authid); 626 | SQL_TQuery(hDatabase, T_FastQuery, query); 627 | 628 | return Plugin_Continue; 629 | } 630 | 631 | /*public Action:Event_ObjectiveComplete(Handle:event, const String:name[], bool:dontBroadcast) 632 | { 633 | #if defined DEBUG 634 | new id = GetEventInt(event, "id"); 635 | decl String:objname[64]; 636 | GetEventString(event, "name", objname, sizeof(objname)); 637 | LogMessage("Objective Complete: id = %d name = %s", id, objname); 638 | #endif 639 | 640 | new change = GetConVarInt(sm_stats_objectivepoints); 641 | if(change == 0) 642 | return Plugin_Continue; 643 | 644 | for(new i = 1; i <= MaxClients; i++) 645 | { 646 | if(!IsClientAuthorized(i) || !IsClientInGame(i) || IsFakeClient(i) || !IsPlayerAlive(i)) 647 | continue; 648 | 649 | clientPoints[i] += change; 650 | 651 | PrintToChat(i, "\x04[Stats]\x01 %s%d point%s (%d) for completing an objective", (change >= 0 ? "+" : ""), change, (change != -1 && change != 1 ? "s" : ""), clientPoints[i]); 652 | 653 | #if defined DEBUG 654 | LogMessage("Player %L (%d) %d for completing an objective", i, clientPoints[i], GetConVarInt(sm_stats_objectivepoints)); 655 | #endif 656 | 657 | decl String:query[1024], String:authid[64]; 658 | GetClientAuthString(i, authid, sizeof(authid)); 659 | Format(query, sizeof(query), "UPDATE nmrihstats SET points = points + %d WHERE steam_id = '%s';", change, authid); 660 | SQL_TQuery(hDatabase, T_FastQuery, query); 661 | } 662 | 663 | return Plugin_Continue; 664 | }*/ 665 | 666 | public Action:Event_ChangeName(Handle:event, const String:name[], bool:dontBroadcast) 667 | { 668 | new client = GetClientOfUserId(GetEventInt(event, "userid")); 669 | 670 | if(client != 0 && !IsFakeClient(client)) 671 | { 672 | decl String:authid[64], String:playername[64]; 673 | GetClientAuthString(client, authid, sizeof(authid)); 674 | GetEventString(event, "newname", playername, sizeof(playername)); 675 | UpdatePlayerName(authid, playername); 676 | } 677 | 678 | return Plugin_Continue; 679 | } 680 | 681 | public T_FastQuery(Handle:owner, Handle:hndl, const String:error[], any:data) 682 | { 683 | // Nothing to do 684 | } 685 | 686 | public UpdatePlayerName(const String:authid[], const String:name[]) 687 | { 688 | decl String:query[1024], String:escname[129]; 689 | SQL_EscapeString(hDatabase, name, escname, sizeof(escname)); 690 | Format(query, sizeof(query), "UPDATE nmrihstats SET name = '%s' WHERE steam_id = '%s';", escname, authid); 691 | SQL_TQuery(hDatabase, T_FastQuery, query); 692 | 693 | #if defined DEBUG 694 | LogMessage("Updating name: %s", name); 695 | #endif 696 | } 697 | -------------------------------------------------------------------------------- /qpdis.sp: -------------------------------------------------------------------------------- 1 | #pragma semicolon 1 2 | #include 3 | 4 | public Plugin:myinfo = 5 | { 6 | name = "Quickplay Disabler", 7 | author = "Stevo.TVR", 8 | description = "Disables Quickplay at the specified player count", 9 | version = "1.0", 10 | url = "http://www.theville.org/" 11 | } 12 | 13 | new Handle:sm_qp_enabled = INVALID_HANDLE; 14 | new Handle:sm_qp_maxplayers = INVALID_HANDLE; 15 | 16 | public OnPluginStart() 17 | { 18 | sm_qp_enabled = CreateConVar("sm_qp_enabled", "1", "Enable Quickplay", _, true, 0.0, true, 1.0); 19 | sm_qp_maxplayers = CreateConVar("sm_qp_maxplayers", "22", "Number of players at which to disable Quickplay", _, true, 0.0); 20 | AutoExecConfig(true, "qpdis"); 21 | } 22 | 23 | public OnClientPutInServer(client) 24 | { 25 | CheckLimit(); 26 | } 27 | 28 | public OnClientDisconnect_Post(client) 29 | { 30 | CheckLimit(); 31 | } 32 | 33 | CheckLimit() 34 | { 35 | new bool:enabled = false; 36 | if(GetConVarBool(sm_qp_enabled)) 37 | { 38 | enabled = GetClientCount() < GetConVarInt(sm_qp_maxplayers); 39 | } 40 | 41 | SetConVarInt(FindConVar("tf_server_identity_disable_quickplay"), enabled ? 0 : 1); 42 | } 43 | -------------------------------------------------------------------------------- /setheads.sp: -------------------------------------------------------------------------------- 1 | #pragma semicolon 1 2 | #include 3 | #include 4 | 5 | #define PLUGIN_VERSION "1.0" 6 | 7 | public Plugin:myinfo = 8 | { 9 | name = "Set Heads", 10 | author = "StevoTVR", 11 | description = "Sets all Demos' heads to 4.", 12 | version = PLUGIN_VERSION, 13 | url = "http://www.theville.org" 14 | } 15 | 16 | new Handle:sm_setheads_enable = INVALID_HANDLE; 17 | 18 | public OnPluginStart() 19 | { 20 | sm_setheads_enable = CreateConVar("sm_setheads_enable", "0", "Enable head count enforcement"); 21 | HookEvent("player_spawn", Event_PlayerSpawn); 22 | } 23 | public Action:Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) 24 | { 25 | if(GetConVarBool(sm_setheads_enable)) 26 | { 27 | new client = GetClientOfUserId(GetEventInt(event, "userid")); 28 | CreateTimer(0.25, Timer_SetHeadsDelay, client); 29 | } 30 | return Plugin_Continue; 31 | } 32 | public Action:Timer_SetHeadsDelay(Handle:timer, any:client) 33 | { 34 | if (IsClientInGame(client)) 35 | { 36 | new TFClassType:class = TF2_GetPlayerClass(client); 37 | if(class == TFClass_DemoMan) 38 | { 39 | SetEntData(client, FindSendPropInfo("CTFPlayer", "m_iDecapitations"), 4, 4, true); 40 | SetEntData(client, FindSendPropInfo("CTFPlayer", "m_iHealth"), 210, 4, true); 41 | SetEntDataFloat(client, FindSendPropInfo("CTFPlayer", "m_flMaxspeed"), 370.0, true); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /shipchat.sp: -------------------------------------------------------------------------------- 1 | #pragma semicolon 1 2 | #include 3 | 4 | public Plugin:myinfo = 5 | { 6 | name = "TheShip Chat", 7 | author = "Stevo.TVR", 8 | description = "Converts chat in TheShip to regular chat that plugins recognize", 9 | version = "1.1", 10 | url = "http://www.theville.org" 11 | } 12 | 13 | #define CHAT_SYMBOL '@' 14 | #define SILENT_TRIGGER '!' 15 | 16 | public OnPluginStart() 17 | { 18 | RegConsoleCmd("say", Command_Say); 19 | } 20 | 21 | public Action:Command_Say(client, args) 22 | { 23 | new String:text[192], String:prefix[8], startidx; 24 | 25 | if (GetCmdArgString(text, sizeof(text)) < 1 || client == 0) 26 | { 27 | return Plugin_Continue; 28 | } 29 | 30 | StripQuotes(text); 31 | startidx = SplitString(text, " ", prefix, sizeof(prefix)); 32 | 33 | if(StrEqual(prefix, "/p", true)) 34 | { 35 | FakeClientCommandEx(client, "say %s", text[startidx]); 36 | if(text[startidx] == CHAT_SYMBOL || text[startidx] == SILENT_TRIGGER) 37 | return Plugin_Handled; 38 | } 39 | else if(StrEqual(prefix, "/t", true) || StrEqual(prefix, "/i/s", true)) 40 | { 41 | FakeClientCommandEx(client, "say_team %s", text[startidx]); 42 | if(text[startidx] == CHAT_SYMBOL || text[startidx] == SILENT_TRIGGER) 43 | return Plugin_Handled; 44 | } 45 | 46 | return Plugin_Continue; 47 | } -------------------------------------------------------------------------------- /teambalancer.sp: -------------------------------------------------------------------------------- 1 | #include 2 | #pragma semicolon 1 3 | 4 | #define PLUGIN_VERSION "1.0" 5 | 6 | public Plugin:myinfo = 7 | { 8 | name = "Team Balancer", 9 | author = "Stevo.TVR", 10 | description = "Keeps the teams even", 11 | version = PLUGIN_VERSION, 12 | url = "http://www.theville.org/" 13 | } 14 | 15 | #define TEAM1 1 16 | #define TEAM2 2 17 | 18 | new Handle:sm_tb_enabled = INVALID_HANDLE; 19 | new Handle:sm_tb_limitteams = INVALID_HANDLE; 20 | new Handle:sm_tb_immunity = INVALID_HANDLE; 21 | 22 | new bool:active = false; 23 | new bool:playerDead[MAXPLAYERS+1]; 24 | 25 | public OnPluginStart() 26 | { 27 | CreateConVar("sm_teambalancer_version", PLUGIN_VERSION, "Insurgency Team Balancer version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); 28 | sm_tb_enabled = CreateConVar("sm_tb_enabled", "1", "Enable team balancing", FCVAR_PLUGIN, true, 0.0, true, 1.0); 29 | sm_tb_limitteams = CreateConVar("sm_tb_limitteams", "1", "Maximum difference between team counts", FCVAR_PLUGIN, true, 0.0); 30 | sm_tb_immunity = CreateConVar("sm_tb_immunity", "1", "Make admins immune to team balancing", FCVAR_PLUGIN, true, 0.0, true, 1.0); 31 | 32 | AutoExecConfig(true, "teambalancer"); 33 | 34 | HookEvent("player_death", Event_PlayerDeath); 35 | HookEvent("player_spawn", Event_PlayerSpawn); 36 | 37 | CreateTimer(10.0, Timer_CheckTeams, _, TIMER_REPEAT); 38 | } 39 | 40 | public OnMapStart() 41 | { 42 | new maxPlayers = GetMaxClients(); 43 | for(new i = 1; i <= maxPlayers; i++) 44 | playerDead[i] = true; 45 | } 46 | 47 | public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) 48 | { 49 | new client = GetClientOfUserId(GetEventInt(event, "userid")); 50 | playerDead[client] = true; 51 | } 52 | 53 | public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) 54 | { 55 | new client = GetClientOfUserId(GetEventInt(event, "userid")); 56 | playerDead[client] = false; 57 | } 58 | 59 | public Action:Timer_CheckTeams(Handle:Timer) 60 | { 61 | if(!GetConVarBool(sm_tb_enabled) || active) 62 | return; 63 | 64 | if(findUnevenTeam()) 65 | { 66 | active = true; 67 | PrintToChatAll("Teams will auto-balance in 5 seconds..."); 68 | CreateTimer(4.0, triggerBalance); 69 | } 70 | } 71 | 72 | public Action:triggerBalance(Handle:Timer) 73 | { 74 | CreateTimer(1.0, balanceTeams, _, TIMER_REPEAT); 75 | } 76 | 77 | public Action:balanceTeams(Handle:Timer) 78 | { 79 | new team = findUnevenTeam(); 80 | if(team) 81 | { 82 | new maxPlayers = GetMaxClients(); 83 | // new arrayPlayers[maxPlayers]; 84 | 85 | // for(new i = 1; i <= maxPlayers; i++) 86 | // arrayPlayers[i] = GetClientUserId(i); 87 | 88 | // SortIntegers(arrayPlayers, maxPlayers, Sort_Descending); 89 | 90 | for(new i = 1; i <= maxPlayers; i++) 91 | { 92 | if(IsClientConnected(i) && IsClientInGame(i) && playerDead[i] && !IsImmune(i)) 93 | { 94 | if(GetClientTeam(i) == team) 95 | { 96 | switchPlayerTeam(i); 97 | active = false; 98 | return Plugin_Stop; 99 | } 100 | } 101 | } 102 | } else { 103 | active = false; 104 | return Plugin_Stop; 105 | } 106 | return Plugin_Continue; 107 | } 108 | 109 | public switchPlayerTeam(client) 110 | { 111 | new team = GetClientTeam(client); 112 | 113 | if(team == TEAM1) 114 | ChangeClientTeam(client, TEAM2); 115 | 116 | if(team == TEAM2) 117 | ChangeClientTeam(client, TEAM1); 118 | } 119 | 120 | public findUnevenTeam() 121 | { 122 | new team1 = 0; 123 | new team2 = 0; 124 | new maxPlayers = GetMaxClients(); 125 | 126 | for(new i = 1; i <= maxPlayers; i++) 127 | { 128 | if(IsClientConnected(i) && IsClientInGame(i)) 129 | { 130 | new clientTeam = GetClientTeam(i); 131 | 132 | if(clientTeam == TEAM1) 133 | team1++; 134 | 135 | if(clientTeam == TEAM2) 136 | team2++; 137 | } 138 | } 139 | 140 | if((team1 - team2) > GetConVarInt(sm_tb_limitteams)) 141 | return TEAM1; 142 | 143 | if((team2 - team1) > GetConVarInt(sm_tb_limitteams)) 144 | return TEAM2; 145 | 146 | return false; 147 | } 148 | 149 | public IsImmune(client) 150 | { 151 | new bool:immune = false; 152 | if(GetConVarBool(sm_tb_immunity)) 153 | { 154 | if(((GetUserFlagBits(client) & ADMFLAG_ROOT) == ADMFLAG_ROOT) 155 | || ((GetUserFlagBits(client) & ADMFLAG_CUSTOM1) == ADMFLAG_CUSTOM1)) 156 | immune = true; 157 | } 158 | return immune; 159 | } -------------------------------------------------------------------------------- /teamswap.sp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #pragma semicolon 1 4 | 5 | #define PLUGIN_VERSION "1.0" 6 | 7 | public Plugin:myinfo = 8 | { 9 | name = "Team Swap", 10 | author = "Stevo.TVR", 11 | description = "Swaps players' team", 12 | version = PLUGIN_VERSION, 13 | url = "http://www.theville.org/" 14 | } 15 | 16 | #define TEAM1 1 17 | #define TEAM2 2 18 | 19 | public OnPluginStart() 20 | { 21 | CreateConVar("sm_teamswap_version", PLUGIN_VERSION, "Team Swap version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); 22 | RegAdminCmd("sm_teamswap", CommandTeamSwap, ADMFLAG_KICK, "Swaps player's team"); 23 | LoadTranslations("core.phrases"); 24 | LoadTranslations("common.phrases"); 25 | } 26 | 27 | public Action:CommandTeamSwap(client, args) 28 | { 29 | if(args < 1) 30 | { 31 | ReplyToCommand(client, "[SM] Usage: sm_teamswap <#userid|name>"); 32 | return Plugin_Handled; 33 | } 34 | 35 | new String:target[64], String:target_name[MAX_TARGET_LENGTH]; 36 | new targetArray[MAXPLAYERS]; 37 | new numtargets; 38 | new bool:tn_is_ml; 39 | 40 | GetCmdArg(1, target, sizeof(target)); 41 | 42 | numtargets = ProcessTargetString(target, client, targetArray, MAXPLAYERS, 0, target_name, sizeof(target_name), tn_is_ml); 43 | 44 | if(numtargets <= 0) 45 | { 46 | ReplyToTargetError(client, numtargets); 47 | return Plugin_Handled; 48 | } 49 | 50 | for(new i = 0; i < numtargets; i++) 51 | { 52 | if(IsClientInGame(targetArray[i])) 53 | switchPlayerTeam(client, targetArray[i]); 54 | } 55 | return Plugin_Handled; 56 | } 57 | 58 | public switchPlayerTeam(client, target) 59 | { 60 | LogAction(client, target, "\"%L\" swapped team of \"%L\"", client, target); 61 | ShowActivity(client, "swapped team of %N", target); 62 | new team = GetClientTeam(target); 63 | 64 | if(team == TEAM1) 65 | ChangeClientTeam(target, TEAM2); 66 | 67 | if(team == TEAM2) 68 | ChangeClientTeam(target, TEAM1); 69 | 70 | DispatchSpawn(target); 71 | } -------------------------------------------------------------------------------- /tkmanager.sp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * TK Manager 4 | * https://forums.alliedmods.net/showthread.php?t=79880 5 | * 6 | * Description: 7 | * This is a basic automated team kill/wound manager. It does not use 8 | * any forgive menus or input from players, it just uses a point 9 | * system to detect intentional team killers. Players can gain points 10 | * for team kills and team wounds, and lose points for enemy kills. 11 | * When the player reaches the limit, it either kicks or bans the 12 | * player depending on configuration. 13 | * 14 | * 15 | * Changelog 16 | * Nov 23, 2016 - v.1.11: 17 | * [*] Fixed error when disabling team wound detection when the server is hibernating 18 | * Nov 26, 2013 - v.1.10: 19 | * [*] Fixed race condition with database connection 20 | * [*] Fixed consecutive TK count not being reset when sm_tk_numkills = 0 21 | * [*] Optimized event binding 22 | * Nov 25, 2013 - v.1.9.1: 23 | * [*] Fixed hooking npc_killed on the wrong games 24 | * Nov 18, 2013 - v.1.9: 25 | * [+] Added detection of NPC kills in NMRiH 26 | *` [+] Added MySQL support 27 | * Mar 22, 2011 - v.1.8: 28 | * [+] Added translation support 29 | * Jun 03, 2010 - v.1.7: 30 | * [*] Fixed error on database connection 31 | * Jun 02, 2010 - v.1.6: 32 | * [*] Converted to threaded queries 33 | * Sep 22, 2009 - v.1.5: 34 | * [*] Fixed late database connection 35 | * Aug 31, 2009 - v.1.4: 36 | * [+] Added check to avoid duplicate bans 37 | * [*] Changed some ConVar descriptions for clarity 38 | * Aug 30, 2009 - v.1.3: 39 | * [+] Added sm_tk_maxtk_punishtype and sm_tk_maxtk_bantime 40 | * [+] Added sm_tk_db to set which database configuration to use 41 | * [*] Changed immunity to be based on any admin access 42 | * Nov 28, 2008 - v.1.2: 43 | * [*] Added sm_tk_displaymode 44 | * Nov 03, 2008 - v.1.1: 45 | * [*] Changed database connection to use the standard named configuration method 46 | * [*] Fixed version cvar being added to tk_manager.cfg 47 | * Oct 31, 2008 - v.1.0: 48 | * [*] Initial Release 49 | * 50 | */ 51 | 52 | #pragma semicolon 1 53 | #include 54 | 55 | #define PLUGIN_VERSION "1.11" 56 | //#define DEBUG 57 | 58 | public Plugin:myinfo = 59 | { 60 | name = "TK Manager", 61 | author = "Stevo.TVR", 62 | description = "Manages Team Kills based on a point system", 63 | version = PLUGIN_VERSION, 64 | url = "http://www.theville.org/" 65 | } 66 | 67 | new Handle:hDatabase = INVALID_HANDLE; 68 | 69 | new Handle:sm_tk_maxpoints = INVALID_HANDLE; 70 | new Handle:sm_tk_punishtype = INVALID_HANDLE; 71 | new Handle:sm_tk_bantime = INVALID_HANDLE; 72 | new Handle:sm_tk_numtw = INVALID_HANDLE; 73 | new Handle:sm_tk_numkills = INVALID_HANDLE; 74 | new Handle:sm_tk_maxtk = INVALID_HANDLE; 75 | new Handle:sm_tk_maxtk_punishtype = INVALID_HANDLE; 76 | new Handle:sm_tk_maxtk_bantime = INVALID_HANDLE; 77 | new Handle:sm_tk_immunity = INVALID_HANDLE; 78 | new Handle:sm_tk_persist = INVALID_HANDLE; 79 | new Handle:sm_tk_displaymode = INVALID_HANDLE; 80 | new Handle:sm_tk_db = INVALID_HANDLE; 81 | 82 | new clientTKPoints[MAXPLAYERS+1]; 83 | new clientTW[MAXPLAYERS+1]; 84 | new clientTK[MAXPLAYERS+1]; 85 | new clientKills[MAXPLAYERS+1]; 86 | new bool:clientLocked[MAXPLAYERS+1]; 87 | 88 | public OnPluginStart() 89 | { 90 | CreateConVar("sm_tkmanager_version", PLUGIN_VERSION, "TK Manager version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD); 91 | sm_tk_maxpoints = CreateConVar("sm_tk_maxpoints", "12", "Number of TK points before kick/ban", _, true, 0.0); 92 | sm_tk_punishtype = CreateConVar("sm_tk_punishtype", "0", "Action to take when sm_tk_maxpoints is reached (0 = ban, 1 = kick)", _, true, 0.0, true, 1.0); 93 | sm_tk_bantime = CreateConVar("sm_tk_bantime", "0", "Amount of time to ban if using sm_tk_punishtype 0 (0 = perm)", _, true, 0.0); 94 | sm_tk_numtw = CreateConVar("sm_tk_numtw", "3", "Number of team wounds to add 1 TK point (0 to disable team wound detection)", _, true, 0.0); 95 | sm_tk_numkills = CreateConVar("sm_tk_numkills", "2", "Number of real kills to subtract 1 TK point (0 to disable)", _, true, 0.0); 96 | sm_tk_maxtk = CreateConVar("sm_tk_maxtk", "4", "Number of consecutive team kills before kick/ban (0 to disable)", _, true, 0.0); 97 | sm_tk_maxtk_punishtype = CreateConVar("sm_tk_maxtk_punishtype", "1", "Action to take after maximum consecutive team kills (0 = ban, 1 = kick)", _, true, 0.0, true, 1.0); 98 | sm_tk_maxtk_bantime = CreateConVar("sm_tk_maxtk_bantime", "30", "Amount of time to ban if using sm_tk_maxtk_punishtype 0 (0 = perm)", _, true, 0.0); 99 | sm_tk_immunity = CreateConVar("sm_tk_immunity", "1", "Sets whether admins are immune to the TK manager", _, true, 0.0, true, 1.0); 100 | sm_tk_persist = CreateConVar("sm_tk_persist", "0", "Save TK points across map change", _, true, 0.0, true, 1.0); 101 | sm_tk_displaymode = CreateConVar("sm_tk_displaymode", "0", "Mode of displaying Team Kills (0 = None, 1 = Show Admins, 2 = Show All)", _, true, 0.0, true, 2.0); 102 | sm_tk_db = CreateConVar("sm_tk_db", "storage-local", "The named database config to use for storing TK points"); 103 | 104 | AutoExecConfig(true, "tk_manager"); 105 | 106 | HookEvent("player_death", Event_PlayerDeath); 107 | HookEvent("player_hurt", Event_PlayerHurt); 108 | 109 | decl String:game[16]; 110 | GetGameFolderName(game, sizeof(game)); 111 | if(strcmp(game, "nmrih", false) == 0) 112 | { 113 | HookEvent("npc_killed", Event_NPCKilled); 114 | } 115 | 116 | LoadTranslations("tkmanager.phrases"); 117 | } 118 | 119 | public OnConfigsExecuted() 120 | { 121 | if(hDatabase == INVALID_HANDLE) 122 | { 123 | decl String:db[64], String:error[256]; 124 | GetConVarString(sm_tk_db, db, sizeof(db)); 125 | hDatabase = SQL_Connect(db, true, error, sizeof(error)); 126 | 127 | if(hDatabase == INVALID_HANDLE) 128 | { 129 | SetFailState(error); 130 | } 131 | SQL_TQuery(hDatabase, T_FastQuery, "CREATE TABLE IF NOT EXISTS tkmanager (steam_id VARCHAR(64) PRIMARY KEY, tkpoints INTEGER, numtk INTEGER, numtw INTEGER, numkills INTEGER);"); 132 | } 133 | 134 | if(!GetConVarBool(sm_tk_persist)) 135 | { 136 | SQL_TQuery(hDatabase, T_FastQuery, "DELETE FROM tkmanager;"); 137 | } 138 | } 139 | 140 | public OnClientAuthorized(client, const String:auth[]) 141 | { 142 | clientTKPoints[client] = 0; 143 | clientTW[client] = 0; 144 | clientTK[client] = 0; 145 | clientKills[client] = 0; 146 | 147 | if(IsFakeClient(client)) 148 | return; 149 | 150 | decl String:query[1024]; 151 | Format(query, sizeof(query), "SELECT * FROM tkmanager WHERE steam_id = '%s' LIMIT 1;", auth); 152 | SQL_TQuery(hDatabase, T_LoadPlayer, query, client); 153 | } 154 | 155 | public T_LoadPlayer(Handle:owner, Handle:hndl, const String:error[], any:client) 156 | { 157 | if(hndl != INVALID_HANDLE) 158 | { 159 | if(SQL_FetchRow(hndl)) 160 | { 161 | clientTKPoints[client] = SQL_FetchInt(hndl, 1); 162 | clientTK[client] = SQL_FetchInt(hndl, 2); 163 | clientTW[client] = SQL_FetchInt(hndl, 3); 164 | clientKills[client] = SQL_FetchInt(hndl, 4); 165 | } 166 | } 167 | 168 | clientLocked[client] = false; 169 | 170 | #if defined DEBUG 171 | LogMessage("User %N has: %d TK points, %d TK, %d TW, %d kills", client, clientTKPoints[client], clientTK[client], clientTW[client], clientKills[client]); 172 | #endif 173 | } 174 | 175 | public OnClientDisconnect(client) 176 | { 177 | if(IsFakeClient(client)) 178 | return; 179 | 180 | decl String:query[1024], String:authid[64]; 181 | GetClientAuthId(client, AuthId_Engine, authid, sizeof(authid), true); 182 | 183 | if(clientTKPoints[client] == 0 && clientTK[client] == 0) 184 | { 185 | Format(query, sizeof(query), "DELETE FROM tkmanager WHERE steam_id = '%s';", authid); 186 | } 187 | else 188 | { 189 | Format(query, sizeof(query), "REPLACE INTO tkmanager VALUES('%s', %d, %d, %d, %d);", authid, clientTKPoints[client], clientTK[client], clientTW[client], clientKills[client]); 190 | } 191 | SQL_TQuery(hDatabase, T_FastQuery, query, sizeof(query)); 192 | 193 | #if defined DEBUG 194 | LogMessage("Saving %N: %d TK points, %d TK, %d TW, %d kills", client, clientTKPoints[client], clientTK[client], clientTW[client], clientKills[client]); 195 | #endif 196 | } 197 | 198 | public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) 199 | { 200 | new user = GetClientOfUserId(GetEventInt(event, "attacker")); 201 | new victim = GetClientOfUserId(GetEventInt(event, "userid")); 202 | 203 | if(user == 0 || user == victim || IsFakeClient(user) || IsImmune(user)) 204 | return Plugin_Continue; 205 | 206 | new team1 = GetClientTeam(user); 207 | new team2 = GetClientTeam(victim); 208 | 209 | if(team1 == team2) 210 | { 211 | clientTK[user]++; 212 | clientTKPoints[user]++; 213 | 214 | #if defined DEBUG 215 | LogMessage("User %N has: %d (+1) TK points, %d TK (+1)", user, clientTKPoints[user], clientTK[user]); 216 | #endif 217 | 218 | if(clientTKPoints[user] >= GetConVarInt(sm_tk_maxpoints)) 219 | { 220 | HandleClient(user, true); 221 | } 222 | else if(GetConVarInt(sm_tk_maxtk) > 0 && clientTK[user] >= GetConVarInt(sm_tk_maxtk)) 223 | { 224 | HandleClient(user, false); 225 | } 226 | else 227 | { 228 | PrintToChat(user, "[TK Manager] %t (%t: %d, %t: %d)", "Gained", "Total", clientTKPoints[user], "Limit", GetConVarInt(sm_tk_maxpoints)); 229 | } 230 | 231 | new mode = GetConVarInt(sm_tk_displaymode); 232 | if(mode > 0) 233 | { 234 | for(new i = 1; i <= MaxClients; i++) 235 | { 236 | if(!IsClientInGame(i)) 237 | continue; 238 | 239 | if(mode == 2 || GetUserAdmin(user) != INVALID_ADMIN_ID) 240 | PrintToChat(i, "[TK Manager] %N %t %N", user, "Team killed", victim); 241 | } 242 | } 243 | } 244 | else if(clientTKPoints[user] > 0) 245 | { 246 | clientKills[user]++; 247 | clientTK[user] = 0; 248 | 249 | if(clientKills[user] >= GetConVarInt(sm_tk_numkills) > 0) 250 | { 251 | clientTKPoints[user]--; 252 | clientKills[user] = 0; 253 | } 254 | 255 | #if defined DEBUG 256 | LogMessage("User %N has: %d TK points, %d TK, %d (+1) kills", user, clientTKPoints[user], clientTK[user], clientKills[user]); 257 | #endif 258 | } 259 | return Plugin_Continue; 260 | } 261 | 262 | public Action:Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast) 263 | { 264 | if(GetConVarInt(sm_tk_numtw) < 1) 265 | return Plugin_Continue; 266 | 267 | new user = GetClientOfUserId(GetEventInt(event, "attacker")); 268 | new victim = GetClientOfUserId(GetEventInt(event, "userid")); 269 | 270 | if(user == 0 || user == victim || IsFakeClient(user) || IsImmune(user)) 271 | return Plugin_Continue; 272 | 273 | new team1 = GetClientTeam(user); 274 | new team2 = GetClientTeam(victim); 275 | 276 | if(team1 == team2) 277 | { 278 | clientTW[user]++; 279 | 280 | #if defined DEBUG 281 | LogMessage("User %N has: %d TK points, %d TK, %d (+1) TW, %d kills", user, clientTKPoints[user], clientTK[user], clientTW[user], clientKills[user]); 282 | #endif 283 | 284 | if(clientTW[user] >= GetConVarInt(sm_tk_numtw)) 285 | { 286 | clientTKPoints[user]++ ; 287 | clientTW[user] = 0; 288 | } 289 | if(clientTKPoints[user] >= GetConVarInt(sm_tk_maxpoints)) 290 | { 291 | HandleClient(user, true); 292 | } 293 | } 294 | return Plugin_Continue; 295 | } 296 | 297 | public Action:Event_NPCKilled(Handle:event, const String:name[], bool:dontBroadcast) 298 | { 299 | new user = GetEventInt(event, "killeridx"); 300 | 301 | if(user == 0 || user > MaxClients || IsFakeClient(user) || IsImmune(user)) 302 | return Plugin_Continue; 303 | 304 | if(clientTKPoints[user] > 0) 305 | { 306 | clientKills[user]++; 307 | clientTK[user] = 0; 308 | 309 | if(clientKills[user] >= GetConVarInt(sm_tk_numkills) > 0) 310 | { 311 | clientTKPoints[user]--; 312 | clientKills[user] = 0; 313 | } 314 | #if defined DEBUG 315 | LogMessage("User %N has: %d TK points, %d TK, %d (+1) kills", user, clientTKPoints[user], clientTK[user], clientKills[user]); 316 | #endif 317 | } 318 | return Plugin_Continue; 319 | } 320 | 321 | public HandleClient(client, bool:tkLimit) 322 | { 323 | if(IsClientConnected(client) && !clientLocked[client]) 324 | { 325 | clientLocked[client] = true; 326 | if(tkLimit) 327 | { 328 | if(GetConVarInt(sm_tk_punishtype) == 0) 329 | { 330 | ServerCommand("sm_ban #%d %d \"[TK Manager] %T\"", GetClientUserId(client), GetConVarInt(sm_tk_bantime), "Ban reason", LANG_SERVER); 331 | LogAction(0, client, "\"%L\" %T", client, "Banned", LANG_SERVER); 332 | } 333 | else 334 | { 335 | KickClient(client, "%t", "Kicked"); 336 | } 337 | } 338 | else 339 | { 340 | if(GetConVarInt(sm_tk_maxtk_punishtype) == 0) 341 | { 342 | ServerCommand("sm_ban #%d %d \"[TK Manager] %T\"", GetClientUserId(client), GetConVarInt(sm_tk_maxtk_bantime), "Ban reason", LANG_SERVER); 343 | LogAction(0, client, "\"%L\" %T", client, "Banned", LANG_SERVER); 344 | } 345 | else 346 | { 347 | KickClient(client, "%t", "Kicked"); 348 | } 349 | } 350 | } 351 | } 352 | 353 | public T_FastQuery(Handle:owner, Handle:hndl, const String:error[], any:data) 354 | { 355 | // Nothing to do 356 | } 357 | 358 | public IsImmune(client) 359 | { 360 | new bool:immune = false; 361 | if(GetConVarBool(sm_tk_immunity)) 362 | { 363 | if(GetUserAdmin(client) != INVALID_ADMIN_ID) 364 | immune = true; 365 | } 366 | return immune; 367 | } -------------------------------------------------------------------------------- /translations/jail.phrases.txt: -------------------------------------------------------------------------------- 1 | "Phrases" 2 | { 3 | "Jailed player" 4 | { 5 | "en" "sent %s to jail" 6 | } 7 | "Released player" 8 | { 9 | "en" "released %s from jail" 10 | } 11 | "Jail menu" 12 | { 13 | "en" "Jail/Release Player" 14 | } 15 | "Jailed" 16 | { 17 | "en" "You have been sent to jail!" 18 | } 19 | "Released" 20 | { 21 | "en" "You have been released from jail" 22 | } 23 | "No coords" 24 | { 25 | "en" "Jail coordinates have not been set for this map" 26 | } 27 | "Coords saved" 28 | { 29 | "en" "Jail coordinates saved for map: %s" 30 | } 31 | } -------------------------------------------------------------------------------- /translations/tkmanager.phrases.txt: -------------------------------------------------------------------------------- 1 | "Phrases" 2 | { 3 | "Gained" 4 | { 5 | "en" "You have gained 1 TK point" 6 | } 7 | "Total" 8 | { 9 | "en" "Total" 10 | } 11 | "Limit" 12 | { 13 | "en" "Limit" 14 | } 15 | "Team killed" 16 | { 17 | "en" "team killed" 18 | } 19 | "Kicked" 20 | { 21 | "en" "You were kicked for excessive team killing" 22 | } 23 | "Banned" 24 | { 25 | "en" "was banned for excessive team killing" 26 | } 27 | "Ban reason" 28 | { 29 | "en" "Excessive Team Killing" 30 | } 31 | } -------------------------------------------------------------------------------- /whitelist.sp: -------------------------------------------------------------------------------- 1 | #include 2 | #pragma semicolon 1 3 | 4 | #define PLUGIN_VERSION "1.1" 5 | 6 | public Plugin:myinfo = 7 | { 8 | name = "Player Whitelist", 9 | author = "Stevo.TVR", 10 | description = "Restricts server to SteamIDs listed in the whitelist", 11 | version = PLUGIN_VERSION, 12 | url = "http://www.theville.org/" 13 | } 14 | 15 | // maximum SteamIDs the plugin can handle; increase value as needed 16 | #define WHITELIST_MAX 255 17 | 18 | new Handle:sm_whitelist_enable = INVALID_HANDLE; 19 | new Handle:sm_whitelist_immunity = INVALID_HANDLE; 20 | 21 | new String:whitelist[WHITELIST_MAX][64]; 22 | new listlen; 23 | 24 | public OnPluginStart() 25 | { 26 | CreateConVar("sm_whitelist_version", PLUGIN_VERSION, "Server Whitelist plugin version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD); 27 | sm_whitelist_enable = CreateConVar("sm_whitelist_enable", "1", "Enable server whitelist", _, true, 0.0, true, 1.0); 28 | sm_whitelist_immunity = CreateConVar("sm_whitelist_immunity", "1", "Automatically grant admins access", _, true, 0.0, true, 1.0); 29 | 30 | AutoExecConfig(true, "whitelist"); 31 | 32 | RegAdminCmd("sm_whitelist_reload", CommandReload, ADMFLAG_GENERIC, "Reloads server whitelist"); 33 | RegAdminCmd("sm_whitelist_list", CommandList, ADMFLAG_GENERIC, "List all SteamIDs in whitelist"); 34 | RegAdminCmd("sm_whitelist_add", CommandAdd, ADMFLAG_CONVARS, "Adds a SteamID to the whitelist"); 35 | 36 | HookConVarChange(sm_whitelist_enable, OnEnableChange); 37 | 38 | LoadList(); 39 | } 40 | 41 | public OnClientPostAdminCheck(client) 42 | { 43 | if(GetConVarBool(sm_whitelist_enable) && !IsFakeClient(client) && !IsImmune(client)) 44 | { 45 | new String:auth[64]; 46 | GetClientAuthString(client, auth, sizeof(auth)); 47 | new bool:allow = false; 48 | for(new i; i < listlen; i++) 49 | { 50 | if(strcmp(auth, whitelist[i]) == 0) 51 | { 52 | allow = true; 53 | break; 54 | } 55 | } 56 | 57 | if(!allow) 58 | { 59 | KickClient(client, "%s is not listed on the server whitelist", auth); 60 | } 61 | } 62 | } 63 | 64 | public OnEnableChange(Handle:cvar, const String:oldVal[], const String:newVal[]) 65 | { 66 | if(StringToInt(newVal) > 0) 67 | { 68 | LoadList(); 69 | } 70 | } 71 | 72 | public Action:CommandReload(client, args) 73 | { 74 | LoadList(); 75 | ReplyToCommand(client, "[Whitelist] %d SteamIDs loaded from whitelist", listlen); 76 | return Plugin_Handled; 77 | } 78 | 79 | public Action:CommandList(client, args) 80 | { 81 | PrintToConsole(client, "[Whitelist] Listing current whitelist (%d items):", listlen); 82 | for(new i; i < listlen; i++) 83 | { 84 | PrintToConsole(client, "%s", whitelist[i]); 85 | } 86 | return Plugin_Handled; 87 | } 88 | 89 | public Action:CommandAdd(client, args) 90 | { 91 | if(args < 1) 92 | { 93 | ReplyToCommand(client, "[SM] Usage: sm_whitelist_add "); 94 | return Plugin_Handled; 95 | } 96 | new String:steamid[64]; 97 | GetCmdArg(1, steamid, sizeof(steamid)); 98 | TrimString(steamid); 99 | 100 | new String:path[PLATFORM_MAX_PATH]; 101 | BuildPath(PathType:Path_SM, path, sizeof(path), "configs/whitelist.txt"); 102 | 103 | new Handle:file = OpenFile(path, "a"); 104 | if(file != INVALID_HANDLE) 105 | { 106 | WriteFileLine(file, steamid); 107 | whitelist[listlen] = steamid; 108 | listlen++; 109 | 110 | ReplyToCommand(client, "[SM] %s successfully added to whitelist", steamid); 111 | } 112 | else 113 | { 114 | ReplyToCommand(client, "[SM] Failed to open %s for writing", path); 115 | } 116 | CloseHandle(file); 117 | 118 | return Plugin_Handled; 119 | } 120 | 121 | public LoadList() 122 | { 123 | new String:path[PLATFORM_MAX_PATH]; 124 | BuildPath(PathType:Path_SM, path, sizeof(path), "configs/whitelist.txt"); 125 | 126 | new Handle:file = OpenFile(path, "r"); 127 | if(file == INVALID_HANDLE) 128 | { 129 | SetFailState("[Whitelist] Unable to read file %s", path); 130 | } 131 | 132 | listlen = 0; 133 | new String:steamid[64]; 134 | while(!IsEndOfFile(file) && ReadFileLine(file, steamid, sizeof(steamid))) 135 | { 136 | if (steamid[0] == ';' || !IsCharAlpha(steamid[0])) 137 | { 138 | continue; 139 | } 140 | new len = strlen(steamid); 141 | for (new i; i < len; i++) 142 | { 143 | if (IsCharSpace(steamid[i]) || steamid[i] == ';') 144 | { 145 | steamid[i] = '\0'; 146 | break; 147 | } 148 | } 149 | whitelist[listlen] = steamid; 150 | listlen++; 151 | } 152 | 153 | CloseHandle(file); 154 | } 155 | 156 | public IsImmune(client) 157 | { 158 | new bool:immune = false; 159 | if(GetConVarBool(sm_whitelist_immunity)) 160 | { 161 | if(GetUserAdmin(client) != INVALID_ADMIN_ID) 162 | { 163 | immune = true; 164 | } 165 | } 166 | return immune; 167 | } --------------------------------------------------------------------------------