diff --git a/README.md b/README.md index 0088fff..dc7d079 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Useful things: * [globalbans](#globalbans) * [l4d2_rollback](#l4d2_rollback) * [l4d2_autorestart](#l4d2_autorestart) +* [l4d2_TKStopper](#l4d2_TKStopper) ### Modified Others * [200IQBots_FlyYouFools](#200IQBots_FlyYouFools) @@ -270,4 +271,18 @@ Currently auto triggers: ### l4d2_autorestart Plugin that automatically restarts server when the server is NOT hibernating, with bots around and no players. -This fixes an issue with (shitty) custom maps that force sb_all_bot_game to 1 and disable hibernation \ No newline at end of file +This fixes an issue with (shitty) custom maps that force sb_all_bot_game to 1 and disable hibernation + +### l4d2_TKStopper +Plugin that prevents team killers by checking multiple criterias. Default system is as: +Any survivor that attacks another non-bot, idle or not idle player: + 1. If done within first 2 minutes of joining OR during finale vehicle arrival is cancelled + 2. If they do over threshold (default 70 HP), they are banned for 60 minutes + 3. Lastly, if its just normal friendly fire and they are not an admin, its reversed to them at full damage, and to victim at 1/2 +Friendly fire counts are forgotten on map changes and after 30 seconds of last FF. + +* **Cvars:** + * `l4d2_tk_forgiveness_time <#>` - The minimum amount of seconds to pass (in seconds) where a player's previous accumulated FF is forgive. Default is 30s + * `l4d2_tk_bantime` - How long in minutes should a player be banned for? 0 for permanently. Default is 60 + * `l4d2_tk_ban_ff_threshold` - How much damage does a player need to do before being instantly banned. Default 70 HP + * `4d2_tk_ban_join_time` - Upto how many minutes should any new player's FF be ignored. Default is 2 Minutes \ No newline at end of file diff --git a/plugins/l4d2_TKStopper.smx b/plugins/l4d2_TKStopper.smx new file mode 100644 index 0000000..19d593e Binary files /dev/null and b/plugins/l4d2_TKStopper.smx differ diff --git a/scripting/l4d2_TKStopper.sp b/scripting/l4d2_TKStopper.sp new file mode 100644 index 0000000..d465a8f --- /dev/null +++ b/scripting/l4d2_TKStopper.sp @@ -0,0 +1,115 @@ +#pragma semicolon 1 +#pragma newdecls required + +//#define DEBUG + +#define PLUGIN_VERSION "1.0" +#define FF_BAN_THRESHOLD 100.0 +#define FF_BAN_JOIN_MINUTES_THRESHOLD 2 +#define FF_BAN_MINUTES 60 + +#include +#include +#include +#include + +/*TODO: Implement if player joins (non-admin) and instantly does over 50 HP of damage, STOP all damage, temp ban + +*/ + +bool lateLoaded, IsFinaleEnding; +int iJoinTime[MAXPLAYERS+1]; +float playerTotalDamageFF[MAXPLAYERS+1]; +int lastFF[MAXPLAYERS+1]; + +ConVar hForgivenessTime, hBanTime, hThreshold, hJoinTime; + +public Plugin myinfo = +{ + name = "__templateName__", + author = "jackzmc", + description = "", + version = PLUGIN_VERSION, + url = "" +}; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + if(late) { + lateLoaded = true; + } +} + +public void OnPluginStart() +{ + EngineVersion g_Game = GetEngineVersion(); + if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) + { + SetFailState("This plugin is for L4D/L4D2 only."); + } + + hForgivenessTime = CreateConVar("l4d2_tk_forgiveness_time", "30", "The minimum amount of time to pass (in seconds) where a player's previous accumulated FF is forgiven"); + hBanTime = CreateConVar("l4d2_tk_bantime", "60", "How long in minutes should a player be banned for? 0 for permanently"); + hThreshold = CreateConVar("l4d2_tk_ban_ff_threshold", "70.0", "How much damage does a player need to do before being instantly banned"); + hJoinTime = CreateConVar("l4d2_tk_ban_join_time", "2", "Upto how many minutes should any new player be subjected to instant bans on any FF"); + + AutoExecConfig(true, "l4d2_tkstopper"); + + HookEvent("finale_vehicle_ready", Event_FinaleVehicleReady); + + if(lateLoaded) { + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) { + SDKHook(i, SDKHook_OnTakeDamage, Event_OnTakeDamage); + } + } + } +} + +public void Event_FinaleVehicleReady(Event event, const char[] name, bool dontBroadcast) { + IsFinaleEnding = true; +} + +public void OnMapEnd() { + IsFinaleEnding = false; +} + +public void OnClientPutInServer(int client) { + iJoinTime[client] = GetTime(); + SDKHook(client, SDKHook_OnTakeDamage, Event_OnTakeDamage); +} + +public void OnClientDisconnect(int client) { + playerTotalDamageFF[client] = 0.0; +} + +public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) { + if(damage > 0.0 && damagetype & (DMG_BLAST|DMG_BURN|DMG_BLAST_SURFACE) == 0 && GetClientTeam(victim) == 2 && GetClientTeam(attacker) == 2 && attacker != victim) { + //Allow friendly firing BOTS that aren't idle players: + if(IsFakeClient(victim) && GetEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") == 0) { + return Plugin_Continue; + } + int time = GetTime(); + if(time - lastFF[attacker] > hForgivenessTime.IntValue) { + playerTotalDamageFF[attacker] = 0.0; + } + playerTotalDamageFF[attacker] += damage; + lastFF[attacker] = time; + if(GetUserAdmin(attacker) == INVALID_ADMIN_ID) { + if(playerTotalDamageFF[attacker] > hThreshold.IntValue) { + LogMessage("[NOTICE] Banning %N for excessive FF (%f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); + NotifyAllAdmins("[Notice] Banning %N for excessive FF (%f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); + BanClient(attacker, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF", "Excessive Friendly Fire", "TKStopper"); + return Plugin_Stop; + } + + if(IsFinaleEnding || GetTime() - iJoinTime[attacker] <= hJoinTime.IntValue / 60000) { + return Plugin_Stop; + }else{ + SDKHooks_TakeDamage(attacker, attacker, attacker, damage / 3.0); + damage /= 2.0; + } + return Plugin_Stop; + } + } + return Plugin_Continue; +}