mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-06 06:03:21 +00:00
bump
This commit is contained in:
parent
ff10667846
commit
9bd6ebc290
17 changed files with 1143 additions and 197 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -55,8 +55,14 @@ public void Event_PlayerTeam(Event event, const char[] name, bool dontBroadcast)
|
|||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Dropped BabybackRibs from server (Disconnect by user.) L 02/16/2022 - 10:38:53: [SM] Exception reported: No valid ban method flags specified L 02/16/2022 - 10:38:53: [SM] Blaming: L4D2FFKickProtection.smx L 02/16/2022 - 10:38:53: [SM] Call stack trace: L 02/16/2022 - 10:38:53: [SM] [0] BanClient L 02/16/2022 - 10:38:53: [SM] [1] Line 78, s:\Jackz\Documents\Sourcepawn\scripting\L4D2FFKickProtection.sp::VoteStart Potential vote being called Client "Andean Brain Surgeon" connected (70.112.126.195:27005). String Table dictionary for downloadables should be rebuilt, only found 39 of 51 strings in dictionary String Table dictionary for soundprecache */
|
||||
|
||||
public Action VoteStart(int client, const char[] command, int argc) {
|
||||
if(!IsClientInGame(client)) {
|
||||
PrintToServer("Preventing vote from user not in game: %N", client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(GetClientCount(true) == 0 || client == 0) return Plugin_Handled; //prevent votes while server is empty or if server tries calling vote
|
||||
if(argc >= 1) {
|
||||
static char issue[32];
|
||||
|
@ -67,7 +73,7 @@ public Action VoteStart(int client, const char[] command, int argc) {
|
|||
static char option[32];
|
||||
GetCmdArg(2, option, sizeof(option));
|
||||
|
||||
if(strlen(option) < 1) { //empty userid/console can't call votes
|
||||
if(strlen(option) > 1) { //empty userid/console can't call votes
|
||||
int target = GetClientOfUserId(StringToInt(option));
|
||||
if(target == 0) return Plugin_Continue; //invalid, pass it through
|
||||
AdminId callerAdmin = GetUserAdmin(client);
|
||||
|
|
|
@ -39,6 +39,8 @@ static EngineVersion g_Game;
|
|||
|
||||
//L4d2 Specific
|
||||
static char L4D2_ZDifficulty[16];
|
||||
//Generic
|
||||
static char currentGamemode[32];
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||||
{
|
||||
|
@ -58,7 +60,7 @@ public void OnPluginStart() {
|
|||
}
|
||||
|
||||
hLogCvarChanges = CreateConVar("sm_activitymonitor_log_cvar", "0", "Should this plugin log cvar changes (when using sm_cvar from console)");
|
||||
ConVar hServerID = CreateConVar("sm_activitymonitor_id", "", "The name to use for the 'server' column");
|
||||
ConVar hServerID = CreateConVar("sm_activitymonitor_id", "", "The name to use for the 'server' column", FCVAR_DONTRECORD);
|
||||
hServerID.GetString(serverID, sizeof(serverID));
|
||||
hServerID.AddChangeHook(CVAR_ServerIDChanged);
|
||||
|
||||
|
@ -71,31 +73,49 @@ public void OnPluginStart() {
|
|||
zDifficulty.GetString(L4D2_ZDifficulty, sizeof(L4D2_ZDifficulty));
|
||||
CVAR_DifficultyChanged(zDifficulty, "", L4D2_ZDifficulty);
|
||||
zDifficulty.AddChangeHook(CVAR_DifficultyChanged);
|
||||
|
||||
zDifficulty.GetString(L4D2_ZDifficulty, sizeof(L4D2_ZDifficulty));
|
||||
CVAR_DifficultyChanged(zDifficulty, "", L4D2_ZDifficulty);
|
||||
zDifficulty.AddChangeHook(CVAR_DifficultyChanged);
|
||||
}
|
||||
|
||||
ConVar mpGamemode = FindConVar("mp_gamemode");
|
||||
if(mpGamemode != null) {
|
||||
mpGamemode.GetString(currentGamemode, sizeof(currentGamemode));
|
||||
mpGamemode.AddChangeHook(CVAR_GamemodeChanged);
|
||||
}
|
||||
|
||||
|
||||
if(!lateLoaded) {
|
||||
AddLog("INFO", "", "", "Server has started up");
|
||||
}
|
||||
|
||||
pushTimer = CreateTimer(60.0, Timer_PushLogs, _, TIMER_REPEAT);
|
||||
|
||||
// AutoExecConfig(true, "activitymonitor");
|
||||
}
|
||||
|
||||
public void OnPluginEnd() {
|
||||
TriggerTimer(pushTimer, true);
|
||||
}
|
||||
|
||||
public void OnMapStart() {
|
||||
static char curMap[64];
|
||||
GetCurrentMap(curMap, sizeof(curMap));
|
||||
if(!StrEqual(lastMap, curMap)) {
|
||||
strcopy(lastMap, sizeof(lastMap), curMap);
|
||||
if(g_Game == Engine_Left4Dead2 || g_Game == Engine_Left4Dead)
|
||||
Format(curMap, sizeof(curMap), "Map changed to %s (%s)", curMap, L4D2_ZDifficulty);
|
||||
Format(curMap, sizeof(curMap), "Map changed to %s (%s %s)", curMap, L4D2_ZDifficulty, currentGamemode);
|
||||
else
|
||||
Format(curMap, sizeof(curMap), "Map changed to %s", curMap);
|
||||
Format(curMap, sizeof(curMap), "Map changed to %s (%s)", curMap, currentGamemode);
|
||||
AddLog("INFO", "", "", curMap);
|
||||
}
|
||||
TriggerTimer(pushTimer, true);
|
||||
}
|
||||
|
||||
public void CVAR_GamemodeChanged(ConVar convar, const char[] oldValue, const char[] newValue) {
|
||||
strcopy(currentGamemode, sizeof(currentGamemode), newValue);
|
||||
}
|
||||
|
||||
public void CVAR_ServerIDChanged(ConVar convar, const char[] oldValue, const char[] newValue) {
|
||||
strcopy(serverID, sizeof(serverID), newValue);
|
||||
}
|
||||
|
@ -131,6 +151,7 @@ public Action Timer_PushLogs(Handle h) {
|
|||
static char query[1024];
|
||||
static Log log;
|
||||
int length = logs.Length;
|
||||
Transaction transaction = new Transaction();
|
||||
if(length > 0) {
|
||||
for(int i = 0; i < length; i++) {
|
||||
logs.GetArray(i, log, sizeof(log));
|
||||
|
@ -142,26 +163,30 @@ public Action Timer_PushLogs(Handle h) {
|
|||
log.targetSteamID,
|
||||
log.message
|
||||
);
|
||||
g_db.Query(SQL_Callback, query, _, DBPrio_Low);
|
||||
// g_db.Query(DB_PushLogsCB, query, _, DBPrio_Low);
|
||||
transaction.AddQuery(query);
|
||||
}
|
||||
// Incase a new item was added while pushing, don't clear it:
|
||||
logs.Resize(logs.Length - length);
|
||||
}
|
||||
g_db.Execute(transaction, _, SQL_TransactionFailed, length, DBPrio_Low);
|
||||
}
|
||||
public void SQL_Callback(Database db, DBResultSet results, const char[] error, any data) {
|
||||
public void DB_PushLogsCB(Database db, DBResultSet results, const char[] error, any data) {
|
||||
if(results == null) PrintToServer("[ACTM] Log error: %s", error);
|
||||
}
|
||||
public void SQL_TransactionFailed(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) {
|
||||
PrintToServer("[ActivityMonitor] Push failure: %s at query %d/%d", error, failIndex, numQueries);
|
||||
}
|
||||
|
||||
public void Event_Connection(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(client > 0 && !IsFakeClient(client)) {
|
||||
static char clientName[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, clientName, sizeof(clientName));
|
||||
if((name[7] == 'f')) {
|
||||
//connection
|
||||
AddLog("JOIN", clientName, "", "");
|
||||
} else {
|
||||
AddLog("QUIT", clientName, "", "");
|
||||
if(GetClientAuthId(client, AuthId_Steam2, clientName, sizeof(clientName))) {
|
||||
if(name[7] == 'f') {
|
||||
AddLog("JOIN", clientName, "", "");
|
||||
} else {
|
||||
AddLog("QUIT", clientName, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -198,7 +223,7 @@ public void Event_L4D2_Death(Event event, const char[] name, bool dontBroadcast)
|
|||
if(IsFakeClient(victim)) GetClientName(victim, victimName, sizeof(victimName));
|
||||
else GetClientAuthId(victim, AuthId_Steam2, victimName, sizeof(victimName));
|
||||
|
||||
if(attacker > 0) {
|
||||
if(attacker > 0 && attacker != victim) {
|
||||
if(IsFakeClient(attacker)) GetClientName(attacker, attackerName, sizeof(attackerName));
|
||||
else GetClientAuthId(attacker, AuthId_Steam2, attackerName, sizeof(attackerName));
|
||||
|
||||
|
@ -220,7 +245,7 @@ public void Event_L4D2_Incapped(Event event, const char[] name, bool dontBroadca
|
|||
if(IsFakeClient(victim)) GetClientName(victim, victimName, sizeof(victimName));
|
||||
else GetClientAuthId(victim, AuthId_Steam2, victimName, sizeof(victimName));
|
||||
|
||||
if(attacker > 0) {
|
||||
if(attacker > 0 && attacker != victim) {
|
||||
if(IsFakeClient(attacker)) GetClientName(attacker, attackerName, sizeof(attackerName));
|
||||
else GetClientAuthId(attacker, AuthId_Steam2, attackerName, sizeof(attackerName));
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ public void OnClientAuthorized(int client, const char[] auth) {
|
|||
if(!StrEqual(auth, "BOT", true)) {
|
||||
static char query[256], ip[32];
|
||||
GetClientIP(client, ip, sizeof(ip));
|
||||
Format(query, sizeof(query), "SELECT `reason`, `steamid`, `expired` FROM `bans` WHERE `steamid` LIKE 'STEAM_%:%:%s' OR ip = '?'", auth[10], ip);
|
||||
g_db.Format(query, sizeof(query), "SELECT `reason`, `steamid`, `expired` FROM `bans` WHERE `steamid` LIKE 'STEAM_%%:%%:%s' OR ip = '%s'", auth[10], ip);
|
||||
g_db.Query(DB_OnConnectCheck, query, GetClientUserId(client), DBPrio_High);
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ public Action OnBanIdentity(const char[] identity, int time, int flags, const ch
|
|||
}else{
|
||||
Format(expiresDate, sizeof(expiresDate), "NULL");
|
||||
}
|
||||
Format(query, sizeof(query), "INSERT INTO bans"
|
||||
g_db.Format(query, sizeof(query), "INSERT INTO bans"
|
||||
..."(steamid, reason, expires, executor, ip_banned)"
|
||||
..."VALUES ('%s', '%s', %s, '%s', 0)",
|
||||
identity,
|
||||
|
@ -100,15 +100,19 @@ public Action OnBanIdentity(const char[] identity, int time, int flags, const ch
|
|||
|
||||
public Action OnBanClient(int client, int time, int flags, const char[] reason, const char[] kick_message, const char[] command, any source) {
|
||||
char executor[32], identity[32], ip[32];
|
||||
if(source > 0 && source <= MaxClients) {
|
||||
GetClientAuthId(source, AuthId_Steam2, executor, sizeof(executor));
|
||||
GetClientAuthId(client, AuthId_Steam2, identity, sizeof(identity));
|
||||
|
||||
DataPack pack;
|
||||
if(source > 0 && source <= MaxClients && IsClientConnected(source) && GetClientAuthId(source, AuthId_Steam2, executor, sizeof(executor))) {
|
||||
pack = new DataPack();
|
||||
pack.WriteString(identity);
|
||||
pack.WriteCell(source);
|
||||
}else{
|
||||
executor = "CONSOLE";
|
||||
}
|
||||
|
||||
if(GetUserAdmin(client) != INVALID_ADMIN_ID) return Plugin_Stop;
|
||||
|
||||
GetClientAuthId(client, AuthId_Steam2, identity, sizeof(identity));
|
||||
GetClientIP(client, ip, sizeof(ip));
|
||||
|
||||
static char query[255];
|
||||
|
@ -118,7 +122,8 @@ public Action OnBanClient(int client, int time, int flags, const char[] reason,
|
|||
} else {
|
||||
Format(expiresDate, sizeof(expiresDate), "NULL");
|
||||
}
|
||||
Format(query, sizeof(query), "INSERT INTO bans"
|
||||
|
||||
g_db.Format(query, sizeof(query), "INSERT INTO bans"
|
||||
..."(steamid, ip, reason, expires, executor, ip_banned)"
|
||||
..."VALUES ('%s', '%s', '%s', FROM_UNIXTIME(%s), '%s', 0)",
|
||||
identity,
|
||||
|
@ -128,7 +133,7 @@ public Action OnBanClient(int client, int time, int flags, const char[] reason,
|
|||
executor
|
||||
);
|
||||
|
||||
g_db.Query(DB_OnBanQuery, query);
|
||||
g_db.Query(DB_OnBanQuery, query, pack);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
|
@ -154,19 +159,19 @@ public void DB_OnConnectCheck(Database db, DBResultSet results, const char[] err
|
|||
KickClient(client, "Could not authenticate at this time.");
|
||||
LogMessage("Could not connect to database to authorize user '%N' (#%d)", client, user);
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
//No failure, check the data.
|
||||
if(results.RowCount > 0 && client) {
|
||||
results.FetchRow();
|
||||
if(client > 0 && results.FetchRow()) { //Is there a ban found?
|
||||
static char reason[128], steamid[64];
|
||||
DBResult reasonResult;
|
||||
results.FetchString(1, steamid, sizeof(steamid));
|
||||
bool expired = results.FetchInt(2) == 1;
|
||||
if(results.IsFieldNull(2)) {
|
||||
bool expired = results.FetchInt(2) == 1; //Check if computed column 'expired' is true
|
||||
if(results.IsFieldNull(2)) { //If expired null, delete i guess. lol
|
||||
DeleteBan(steamid);
|
||||
}else{
|
||||
} else {
|
||||
results.FetchString(0, reason, sizeof(reason), reasonResult);
|
||||
if(!expired) {
|
||||
LogMessage("%N is banned: %s", client, reason);
|
||||
if(hKickType.IntValue > 0) {
|
||||
if(reasonResult == DBVal_Data)
|
||||
KickClient(client, "You have been banned: %s", reason);
|
||||
|
@ -179,7 +184,9 @@ public void DB_OnConnectCheck(Database db, DBResultSet results, const char[] err
|
|||
static char query[128];
|
||||
g_db.Format(query, sizeof(query), "UPDATE bans SET times_tried=times_tried+1 WHERE steamid = '%s'", steamid);
|
||||
g_db.Query(DB_OnBanQuery, query);
|
||||
}else{
|
||||
} else {
|
||||
LogMessage("%N was previously banned: %s", client, reason);
|
||||
// User was previously banned
|
||||
PrintChatToAdmins("%N has a previously expired ban of reason \"%s\"", client, reason);
|
||||
}
|
||||
}
|
||||
|
@ -197,6 +204,19 @@ void DeleteBan(const char[] steamid) {
|
|||
public void DB_OnBanQuery(Database db, DBResultSet results, const char[] error, any data) {
|
||||
if(db == INVALID_HANDLE || results == null) {
|
||||
LogError("DB_OnBanQuery returned error: %s", error);
|
||||
DataPack pack = data;
|
||||
if(pack != null) {
|
||||
pack.Reset();
|
||||
static char id[32];
|
||||
pack.ReadString(id, sizeof(id));
|
||||
int source = pack.ReadCell();
|
||||
|
||||
if(StrContains(error, "Duplicate entry") > 0) {
|
||||
PrintToChat(source, "Could not ban \"%s\", as they were previously banned. Please edit the ban manually on the website (or yell at jackz).", id);
|
||||
} else {
|
||||
PrintToChat(source, "Could not ban \"%s\" due to an error: %s", id, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -250,6 +250,7 @@ public Action Command_ApplyUser(int client, int args) {
|
|||
ReplyToTargetError(client, target_count);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
if(args == 2) {
|
||||
static char key[32];
|
||||
for(int i = 0; i < categories.Length; i++) {
|
||||
|
@ -261,7 +262,10 @@ public Action Command_ApplyUser(int client, int args) {
|
|||
}
|
||||
}
|
||||
ReplyToCommand(client, "[FTT] Unknown category: '%s'", arg2);
|
||||
} else if(args == 1) {
|
||||
SetupCategoryMenu(client, GetClientUserId(target_list[0]));
|
||||
}
|
||||
|
||||
SilentMenuSelected[client] = false;
|
||||
SetupCategoryMenu(client, target_list[0]);
|
||||
}
|
||||
|
@ -453,9 +457,8 @@ public Action Command_MarkPendingTroll(int client, int args) {
|
|||
menu.SetTitle("Choose a troll to mark");
|
||||
static char userid[8], display[16];
|
||||
for(int i = 1; i < MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
|
||||
AdminId admin = GetUserAdmin(i);
|
||||
if(admin == INVALID_ADMIN_ID) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i)) {
|
||||
if(GetUserAdmin(i) == INVALID_ADMIN_ID) {
|
||||
Format(userid, sizeof(userid), "%d", GetClientUserId(i));
|
||||
GetClientName(i, display, sizeof(display));
|
||||
menu.AddItem(userid, display);
|
||||
|
@ -487,9 +490,9 @@ public Action Command_MarkPendingTroll(int client, int args) {
|
|||
return Plugin_Handled;
|
||||
}
|
||||
int target = target_list[0];
|
||||
if(GetClientTeam(target) == 2) {
|
||||
if (GetClientTeam(target) == 2) {
|
||||
ToggleMarkPlayer(client, target);
|
||||
}else{
|
||||
} else {
|
||||
ReplyToCommand(client, "Player does not exist or is not a survivor.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -322,8 +322,9 @@ public void StopItemGive(int client) {
|
|||
|
||||
void SetupCategoryMenu(int client, int victimUserID) {
|
||||
Menu categoryMenu = new Menu(ChooseCategoryHandler);
|
||||
static char category[64], id[8];
|
||||
Format(category, sizeof(category), "%N: Choose troll category", GetClientOfUserId(victimUserID));
|
||||
static char category[64], id[16];
|
||||
// Title with [ in name cause failure
|
||||
Format(category, sizeof(category), "Choose troll category");
|
||||
categoryMenu.SetTitle(category);
|
||||
|
||||
Format(id, sizeof(id), "%d|-1", victimUserID);
|
||||
|
@ -365,7 +366,7 @@ void ShowTrollCombosMenu(int client, int victimUserID) {
|
|||
void ShowTrollMenu(int client, bool isComboList) {
|
||||
Menu menu = isComboList ? new Menu(ChoosePlayerHandlerForCombos) : new Menu(ChoosePlayerHandler);
|
||||
menu.SetTitle("Choose a player to troll");
|
||||
static char userid[8], display[32];
|
||||
static char userid[8], display[64];
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
|
||||
IntToString(GetClientUserId(i), userid, sizeof(userid));
|
||||
|
|
462
scripting/include/system2.inc
Normal file
462
scripting/include/system2.inc
Normal file
|
@ -0,0 +1,462 @@
|
|||
/**
|
||||
* -----------------------------------------------------
|
||||
* File system2.inc
|
||||
* Authors David Ordnung
|
||||
* License GPLv3
|
||||
* Web http://dordnung.de
|
||||
* -----------------------------------------------------
|
||||
*
|
||||
* Copyright (C) 2013-2020 David Ordnung
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#if defined _system2_included
|
||||
#endinput
|
||||
#endif
|
||||
|
||||
#define _system2_included
|
||||
|
||||
// Include request stuff
|
||||
#include <system2/request>
|
||||
|
||||
|
||||
/**
|
||||
* Max length of a command when using formatted natives.
|
||||
*/
|
||||
#define CMD_MAX_LENGTH 2048
|
||||
|
||||
|
||||
/**
|
||||
* A list of possible compression levels for the System2_Compress native.
|
||||
*/
|
||||
enum CompressLevel
|
||||
{
|
||||
LEVEL_1, // Weekest
|
||||
LEVEL_3,
|
||||
LEVEL_5,
|
||||
LEVEL_7,
|
||||
LEVEL_9 // Strongest
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A list of possible archive formats for the System2_Compress native.
|
||||
*/
|
||||
enum CompressArchive
|
||||
{
|
||||
ARCHIVE_ZIP,
|
||||
ARCHIVE_7Z,
|
||||
ARCHIVE_GZIP,
|
||||
ARCHIVE_BZIP2,
|
||||
ARCHIVE_TAR
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A list of possible operating systems for the System2_GetOS native.
|
||||
*/
|
||||
enum OS
|
||||
{
|
||||
OS_UNKNOWN, // OS couldn't be determined
|
||||
OS_WINDOWS, // Windows
|
||||
OS_UNIX, // Linux / Unix
|
||||
OS_MAC // MAC
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called when finished with the System2_CopyFile native.
|
||||
*
|
||||
* @param success Whether copying was successful (will fail if couldn't open 'from' or 'to' file).
|
||||
* @param from Path to file that was copied.
|
||||
* @param to Path to the new copied file.
|
||||
* @param data Data passed to the copy native.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
typeset System2CopyCallback
|
||||
{
|
||||
function void (bool success, const char[] from, const char[] to, any data);
|
||||
function void (bool success, const char[] from, const char[] to);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called when finished with System2_ExecuteThreaded or System2_ExecuteFormattedThreaded native.
|
||||
* The output will only be valid in the callback and will be destroyed afterwards.
|
||||
*
|
||||
* @param success Whether the execution was successful or not.
|
||||
* This not means that the command itself was successful!
|
||||
* Check the ExitStatus of the output for this.
|
||||
* @param command The executed command.
|
||||
* @param output Output of the execution. Is null if success is false.
|
||||
* Can't be deleted, as it will be destroyed after the callback!
|
||||
* @param data Data passed to the execution native.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
typeset System2ExecuteCallback
|
||||
{
|
||||
function void (bool success, const char[] command, System2ExecuteOutput output, any data);
|
||||
function void (bool success, const char[] command, System2ExecuteOutput output);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Methodmap for the output of an execution.
|
||||
*/
|
||||
methodmap System2ExecuteOutput < Handle {
|
||||
/**
|
||||
* Retrieves the output of the command execution.
|
||||
*
|
||||
* @param output Buffer to store the output in.
|
||||
* @param maxlength Maxlength of the output buffer.
|
||||
* @param start Start byte to start reading from.
|
||||
* You can use this to retrieve the output step by step.
|
||||
* @param delimiter Delimiter until which the content should be retrieved.
|
||||
* @param include Whether the delimiter should be included or not.
|
||||
*
|
||||
* @return Number of read bytes.
|
||||
* @error Invalid Output.
|
||||
*/
|
||||
public native int GetOutput(char[] output, int maxlength, int start = 0, const char[] delimiter = "", bool include = true);
|
||||
|
||||
property int Length {
|
||||
/**
|
||||
* Returns the length of the complete output.
|
||||
*
|
||||
* @return Length of the output.
|
||||
* @error Invalid Output.
|
||||
*/
|
||||
public native get();
|
||||
}
|
||||
|
||||
property int ExitStatus {
|
||||
/**
|
||||
* Returns the exit status of the execution.
|
||||
*
|
||||
* @return The exit status.
|
||||
* @error Invalid Output.
|
||||
*/
|
||||
public native get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Copies a file to another location.
|
||||
*
|
||||
* @param callback Callback function when finished with copying.
|
||||
* @param from Path to the file to copy.
|
||||
* @param to Path to the file to copy to (including filename). File will be replaced if it exists.
|
||||
* @param data Additional data to pass to the callback.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void System2_CopyFile(System2CopyCallback callback, const char[] from, const char[] to, any data = 0);
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether 7-ZIP was found and is executable.
|
||||
*
|
||||
* @param execPath Buffer which will store the path to the 7-ZIP executable.
|
||||
* Can be used for example when showing an error message.
|
||||
* @param maxlength Maxlength of the buffer.
|
||||
* @param force32Bit Whether to force using the 32 bit version of 7-ZIP, otherwise the appropriate version will be used.
|
||||
*
|
||||
* @return True if 7-ZIP executable could be found and is executable, otherwise false.
|
||||
*/
|
||||
native bool System2_Check7ZIP(char[] execPath, int maxlength, bool force32Bit = false);
|
||||
|
||||
/**
|
||||
* Compresses a file or folder to an archive.
|
||||
*
|
||||
* @param callback Callback function when finished with compressing.
|
||||
* @param path Path to the file / folder to compress.
|
||||
* @param archive Path to the archive file to compress to (including filename).
|
||||
* @param archiveType Archive type to use.
|
||||
* @param level Compress level to use.
|
||||
* @param data Additional data to pass to the callback.
|
||||
* @param force32Bit Whether to force using the 32 bit version of 7-ZIP, otherwise the appropriate version will be used.
|
||||
*
|
||||
* @return True if compress command could be fired, false when 7-ZIP executable couldn't be found or is not executable.
|
||||
*/
|
||||
native bool System2_Compress(System2ExecuteCallback callback, const char[] path, const char[] archive, CompressArchive archiveType = ARCHIVE_ZIP, CompressLevel level = LEVEL_9, any data = 0, bool force32Bit = false);
|
||||
|
||||
/**
|
||||
* Extracts a lot of archive types with 7zip.
|
||||
*
|
||||
* @param callback Callback function when finished with extracting.
|
||||
* @param archive Path to the archive file to extract.
|
||||
* @param extractDir Path to the directory to extract to.
|
||||
* @param data Additional data to pass to the callback.
|
||||
* @param force32Bit Whether to force using the 32 bit version of 7-ZIP, otherwise the appropriate version will be used.
|
||||
*
|
||||
* @return True if extract command could be fired, false when 7-ZIP executable couldn't be found or is not executable.
|
||||
*/
|
||||
native bool System2_Extract(System2ExecuteCallback callback, const char[] archive, const char[] extractDir, any data = 0, bool force32Bit = false);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Executes a threaded system command.
|
||||
* Hint: Append 2>&1 to your command to retrieve also output to stderr.
|
||||
*
|
||||
* @param callback Callback function when command was executed.
|
||||
* @param command Command to execute.
|
||||
* @param data Data to pass to the callback.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void System2_ExecuteThreaded(System2ExecuteCallback callback, const char[] command, any data = 0);
|
||||
|
||||
/**
|
||||
* Executes a threaded system command with support for a formatted command.
|
||||
* Note that the maxlength of the command is CMD_MAX_LENGTH, use System2_ExecuteThreaded if you need more.
|
||||
* Hint: Append 2>&1 to your command to retrieve also output to stderr.
|
||||
*
|
||||
* @param callback Callback function when command was executed.
|
||||
* @param data Data to pass to the callback.
|
||||
* @param command Command string format.
|
||||
* @param ... Command string arguments.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void System2_ExecuteFormattedThreaded(System2ExecuteCallback callback, any data, const char[] command, any ...);
|
||||
|
||||
/**
|
||||
* Executes a non threaded system command.
|
||||
* Hint: Append 2>&1 to your command to retrieve also output to stderr.
|
||||
*
|
||||
* @param output Buffer to store the output in.
|
||||
* @param maxlength Maxlength of the output buffer.
|
||||
* @param command Command to execute.
|
||||
*
|
||||
* @return True on success, otherwise false.
|
||||
*/
|
||||
native bool System2_Execute(char[] output, int maxlength, const char[] command);
|
||||
|
||||
/**
|
||||
* Executes a non threaded system command with support for a formatted command.
|
||||
* Note that the maxlength of the command is CMD_MAX_LENGTH, use System2_Execute if you need more.
|
||||
* Hint: Append 2>&1 to your command to retrieve also output to stderr.
|
||||
*
|
||||
* @param output Buffer to store the output in.
|
||||
* @param maxlength Maxlength of the output buffer.
|
||||
* @param command Command string format.
|
||||
* @param ... Command string arguments.
|
||||
*
|
||||
* @return True on success, otherwise false.
|
||||
*/
|
||||
native bool System2_ExecuteFormatted(char[] output, int maxlength, const char[] command, any ...);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the absolute path to the gamedir of the current running game (e.g. /home/.../.../cstrike).
|
||||
* You may need this when executing system commands.
|
||||
*
|
||||
* @param gamedir Buffer to store gamedir in.
|
||||
* @param maxlength Maxlength of the buffer.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void System2_GetGameDir(char[] gamedir, int maxlength);
|
||||
|
||||
/**
|
||||
* Returns the server's operating system.
|
||||
*
|
||||
* @return OS_UNKNOWN, OS_WINDOWS, OS_UNIX, OS_MAC.
|
||||
*/
|
||||
native OS System2_GetOS();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the MD5 hex hash of a string.
|
||||
*
|
||||
* @param string String to retrieve the MD5 hash of.
|
||||
* @param buffer Buffer to store MD5 hash in.
|
||||
* @param maxlength Maxlength of the buffer. Should be greater or equal to 33 (32 MD5 + 1 terminator).
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void System2_GetStringMD5(const char[] str, char[] buffer, int maxlength);
|
||||
|
||||
/**
|
||||
* Retrieves the MD5 hex hash of a files content.
|
||||
*
|
||||
* @param file The path to the file.
|
||||
* @param buffer Buffer to store MD5 hash in.
|
||||
* @param maxlength Maxlength of the buffer. Should be greater or equal to 33 (32 MD5 + 1 terminator).
|
||||
*
|
||||
* @return True on success, false when file couldn't be opened.
|
||||
*/
|
||||
native bool System2_GetFileMD5(const char[] file, char[] buffer, int maxlength);
|
||||
|
||||
/**
|
||||
* Retrieves the CRC32 hex hash of a string.
|
||||
*
|
||||
* @param string The string to retrieve the CRC32 hash of.
|
||||
* @param buffer Buffer to store CRC32 hash in.
|
||||
* @param maxlength Maxlength of the buffer. Should be greater or equal to 9 (8 CRC32 + 1 terminator).
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void System2_GetStringCRC32(const char[] str, char[] buffer, int maxlength);
|
||||
|
||||
/**
|
||||
* Retrieves the CRC32 hex hash of a files content.
|
||||
*
|
||||
* @param file The path to the file.
|
||||
* @param buffer Buffer to store CRC32 hash in.
|
||||
* @param maxlength Maxlength of the buffer. Should be greater or equal to 9 (8 CRC32 + 1 terminator).
|
||||
*
|
||||
* @return True on success, false when file couldn't be opened.
|
||||
*/
|
||||
native bool System2_GetFileCRC32(const char[] file, char[] buffer, int maxlength);
|
||||
|
||||
|
||||
// Include legacy stuff
|
||||
#include <system2/legacy>
|
||||
|
||||
|
||||
public Extension __ext_system2 =
|
||||
{
|
||||
name = "System2",
|
||||
file = "system2.ext",
|
||||
|
||||
#if defined AUTOLOAD_EXTENSIONS
|
||||
autoload = 1,
|
||||
#else
|
||||
autoload = 0,
|
||||
#endif
|
||||
|
||||
#if defined REQUIRE_EXTENSIONS
|
||||
required = 1,
|
||||
#else
|
||||
required = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#if !defined REQUIRE_EXTENSIONS
|
||||
public void __ext_system2_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("System2Request.SetURL");
|
||||
MarkNativeAsOptional("System2Request.GetURL");
|
||||
MarkNativeAsOptional("System2Request.SetPort");
|
||||
MarkNativeAsOptional("System2Request.GetPort");
|
||||
MarkNativeAsOptional("System2Request.SetOutputFile");
|
||||
MarkNativeAsOptional("System2Request.GetOutputFile");
|
||||
MarkNativeAsOptional("System2Request.SetVerifySSL");
|
||||
MarkNativeAsOptional("System2Request.GetVerifySSL");
|
||||
MarkNativeAsOptional("System2Request.SetProxy");
|
||||
MarkNativeAsOptional("System2Request.SetProxyAuthentication");
|
||||
MarkNativeAsOptional("System2Request.Timeout.get");
|
||||
MarkNativeAsOptional("System2Request.Timeout.set");
|
||||
MarkNativeAsOptional("System2Request.Any.get");
|
||||
MarkNativeAsOptional("System2Request.Any.set");
|
||||
|
||||
MarkNativeAsOptional("System2HTTPRequest.System2HTTPRequest");
|
||||
MarkNativeAsOptional("System2HTTPRequest.SetProgressCallback");
|
||||
MarkNativeAsOptional("System2HTTPRequest.SetData");
|
||||
MarkNativeAsOptional("System2HTTPRequest.GetData");
|
||||
MarkNativeAsOptional("System2HTTPRequest.SetHeader");
|
||||
MarkNativeAsOptional("System2HTTPRequest.GetHeader");
|
||||
MarkNativeAsOptional("System2HTTPRequest.GetHeaderName");
|
||||
MarkNativeAsOptional("System2HTTPRequest.SetUserAgent");
|
||||
MarkNativeAsOptional("System2HTTPRequest.SetBasicAuthentication");
|
||||
MarkNativeAsOptional("System2HTTPRequest.GET");
|
||||
MarkNativeAsOptional("System2HTTPRequest.POST");
|
||||
MarkNativeAsOptional("System2HTTPRequest.PUT");
|
||||
MarkNativeAsOptional("System2HTTPRequest.PATCH");
|
||||
MarkNativeAsOptional("System2HTTPRequest.DELETE");
|
||||
MarkNativeAsOptional("System2HTTPRequest.HEAD");
|
||||
MarkNativeAsOptional("System2HTTPRequest.FollowRedirects.get");
|
||||
MarkNativeAsOptional("System2HTTPRequest.FollowRedirects.set");
|
||||
MarkNativeAsOptional("System2HTTPRequest.Headers.get");
|
||||
|
||||
MarkNativeAsOptional("System2FTPRequest.System2FTPRequest");
|
||||
MarkNativeAsOptional("System2FTPRequest.SetProgressCallback");
|
||||
MarkNativeAsOptional("System2FTPRequest.SetAuthentication");
|
||||
MarkNativeAsOptional("System2FTPRequest.SetInputFile");
|
||||
MarkNativeAsOptional("System2FTPRequest.GetInputFile");
|
||||
MarkNativeAsOptional("System2FTPRequest.StartRequest");
|
||||
MarkNativeAsOptional("System2FTPRequest.AppendToFile.get");
|
||||
MarkNativeAsOptional("System2FTPRequest.AppendToFile.set");
|
||||
MarkNativeAsOptional("System2FTPRequest.CreateMissingDirs.get");
|
||||
MarkNativeAsOptional("System2FTPRequest.CreateMissingDirs.set");
|
||||
MarkNativeAsOptional("System2FTPRequest.ListFilenamesOnly.get");
|
||||
MarkNativeAsOptional("System2FTPRequest.ListFilenamesOnly.set");
|
||||
|
||||
MarkNativeAsOptional("System2Response.GetLastURL");
|
||||
MarkNativeAsOptional("System2Response.GetContent");
|
||||
MarkNativeAsOptional("System2Response.ContentLength.get");
|
||||
MarkNativeAsOptional("System2Response.StatusCode.get");
|
||||
MarkNativeAsOptional("System2Response.TotalTime.get");
|
||||
MarkNativeAsOptional("System2Response.DownloadSize.get");
|
||||
MarkNativeAsOptional("System2Response.UploadSize.get");
|
||||
MarkNativeAsOptional("System2Response.DownloadSpeed.get");
|
||||
MarkNativeAsOptional("System2Response.UploadSpeed.get");
|
||||
|
||||
MarkNativeAsOptional("System2HTTPResponse.GetContentType");
|
||||
MarkNativeAsOptional("System2HTTPResponse.GetHeader");
|
||||
MarkNativeAsOptional("System2HTTPResponse.GetHeaderName");
|
||||
MarkNativeAsOptional("System2HTTPResponse.GetHeadersCount");
|
||||
MarkNativeAsOptional("System2HTTPResponse.HTTPVersion.get");
|
||||
MarkNativeAsOptional("System2HTTPResponse.Headers.get");
|
||||
|
||||
MarkNativeAsOptional("System2_URLEncode");
|
||||
MarkNativeAsOptional("System2_URLDecode");
|
||||
|
||||
MarkNativeAsOptional("System2_CopyFile");
|
||||
|
||||
MarkNativeAsOptional("System2_Check7ZIP");
|
||||
MarkNativeAsOptional("System2_Compress");
|
||||
MarkNativeAsOptional("System2_Extract");
|
||||
|
||||
MarkNativeAsOptional("System2_ExecuteThreaded");
|
||||
MarkNativeAsOptional("System2_ExecuteFormattedThreaded");
|
||||
MarkNativeAsOptional("System2ExecuteOutput.GetOutput");
|
||||
MarkNativeAsOptional("System2ExecuteOutput.Length.get");
|
||||
MarkNativeAsOptional("System2ExecuteOutput.ExitStatus.get");
|
||||
|
||||
MarkNativeAsOptional("System2_Execute");
|
||||
MarkNativeAsOptional("System2_ExecuteFormatted");
|
||||
|
||||
MarkNativeAsOptional("System2_GetGameDir");
|
||||
MarkNativeAsOptional("System2_GetOS");
|
||||
|
||||
MarkNativeAsOptional("System2_GetStringMD5");
|
||||
MarkNativeAsOptional("System2_GetFileMD5");
|
||||
MarkNativeAsOptional("System2_GetStringCRC32");
|
||||
MarkNativeAsOptional("System2_GetFileCRC32");
|
||||
|
||||
// Deprecated v2 stuff
|
||||
MarkNativeAsOptional("System2_GetPage");
|
||||
MarkNativeAsOptional("System2_DownloadFile");
|
||||
MarkNativeAsOptional("System2_DownloadFTPFile");
|
||||
MarkNativeAsOptional("System2_UploadFTPFile");
|
||||
MarkNativeAsOptional("System2_CompressFile");
|
||||
MarkNativeAsOptional("System2_ExtractArchive");
|
||||
MarkNativeAsOptional("System2_RunThreadCommand");
|
||||
MarkNativeAsOptional("System2_RunThreadCommandWithData");
|
||||
MarkNativeAsOptional("System2_RunCommand");
|
||||
}
|
||||
#endif
|
|
@ -11,15 +11,40 @@
|
|||
#include <jutils>
|
||||
#include <left4dhooks>
|
||||
|
||||
bool lateLoaded, isFinaleEnding;
|
||||
bool isPlayerTroll[MAXPLAYERS+1], isImmune[MAXPLAYERS+1], isUnderAttack[MAXPLAYERS+1];
|
||||
int iJoinTime[MAXPLAYERS+1];
|
||||
int iIdleStartTime[MAXPLAYERS+1];
|
||||
int iLastFFTime[MAXPLAYERS+1];
|
||||
int iJumpAttempts[MAXPLAYERS+1];
|
||||
enum {
|
||||
Immune_None,
|
||||
Immune_TK = 1,
|
||||
Immune_RFF = 2
|
||||
}
|
||||
|
||||
float playerTotalDamageFF[MAXPLAYERS+1];
|
||||
float autoFFScaleFactor[MAXPLAYERS+1];
|
||||
bool lateLoaded, isFinaleEnding;
|
||||
// bool isPlayerTroll[MAXPLAYERS+1], isUnderAttack[MAXPLAYERS+1];
|
||||
// ImmunityFlag immunityFlags[MAXPLAYERS+1];
|
||||
// int iJoinTime[MAXPLAYERS+1];
|
||||
// int iIdleStartTime[MAXPLAYERS+1];
|
||||
// int iLastFFTime[MAXPLAYERS+1];
|
||||
// int iJumpAttempts[MAXPLAYERS+1];
|
||||
|
||||
// float playerTotalDamageFF[MAXPLAYERS+1];
|
||||
// float autoFFScaleFactor[MAXPLAYERS+1];
|
||||
|
||||
enum struct PlayerData {
|
||||
int joinTime;
|
||||
int idleStartTime;
|
||||
int lastFFTime;
|
||||
int jumpAttempts;
|
||||
|
||||
float TKDamageBuffer;
|
||||
float totalDamageFF;
|
||||
float autoRFFScaleFactor;
|
||||
|
||||
bool isTroll;
|
||||
bool underAttack;
|
||||
|
||||
int immunityFlags;
|
||||
}
|
||||
|
||||
PlayerData pData[MAXPLAYERS+1];
|
||||
|
||||
ConVar hForgivenessTime, hBanTime, hThreshold, hJoinTime, hTKAction, hSuicideAction, hSuicideLimit, hFFAutoScaleAmount, hFFAutoScaleForgivenessAmount, hFFAutoScaleMaxRatio, hFFAutoScaleIgnoreAdmins;
|
||||
|
||||
|
@ -43,17 +68,17 @@ public void OnPluginStart() {
|
|||
SetFailState("This plugin is for L4D/L4D2 only.");
|
||||
}
|
||||
|
||||
hForgivenessTime = CreateConVar("l4d2_tk_forgiveness_time", "15", "The minimum amount of time to pass (in seconds) where a player's previous accumulated FF is forgiven", FCVAR_NONE, true, 0.0);
|
||||
hBanTime = CreateConVar("l4d2_tk_bantime", "120", "How long in minutes should a player be banned for? 0 for permanently", FCVAR_NONE, true, 0.0);
|
||||
hForgivenessTime = CreateConVar("l4d2_tk_forgiveness_time", "15", "The minimum amount of time to pass (in seconds) where a player's previous accumulated teamkiller-detection FF is forgiven", FCVAR_NONE, true, 0.0);
|
||||
hBanTime = CreateConVar("l4d2_tk_bantime", "60", "How long in minutes should a player be banned for? 0 for permanently", FCVAR_NONE, true, 0.0);
|
||||
hThreshold = CreateConVar("l4d2_tk_ban_ff_threshold", "75.0", "How much damage does a player need to do before being instantly banned", FCVAR_NONE, true, 0.0);
|
||||
hJoinTime = CreateConVar("l4d2_tk_ban_join_time", "2", "Upto how many minutes should any new player be subjected to instant bans on any FF", FCVAR_NONE, true, 0.0);
|
||||
hJoinTime = CreateConVar("l4d2_tk_ban_join_time", "2", "Upto how many minutes should any new player's ff be disabled? Used for jump attempts detection. also", FCVAR_NONE, true, 0.0);
|
||||
hTKAction = CreateConVar("l4d2_tk_action", "3", "How should the TK be punished?\n0 = No action (No message), 1 = Kick, 2 = Instant Ban, 3 = Ban on disconnect", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hSuicideAction = CreateConVar("l4d2_suicide_action", "3", "How should a suicider be punished?\n0 = No action (No message), 1 = Kick, 2 = Instant Ban, 3 = Ban on disconnect", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hSuicideLimit = CreateConVar("l4d2_suicide_limit", "1", "How many attempts does a new joined player have until action is taken for suiciding?", FCVAR_NONE, true, 0.0);
|
||||
// Reverse FF Auto Scale
|
||||
hFFAutoScaleAmount = CreateConVar("l4d2_tk_auto_ff_rate", "0.02", "The rate at which auto reverse-ff is scaled by.", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleMaxRatio = CreateConVar("l4d2_tk_auto_ff_max_ratio", "5.0", "The maximum amount that the reverse ff can go. 0.0 for unlimited", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleForgivenessAmount = CreateConVar("l4d2_tk_auto_ff_forgive_rate", "0.03", "This amount times amount of minutes since last ff is removed from ff rate", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleForgivenessAmount = CreateConVar("l4d2_tk_auto_ff_forgive_rate", "0.05", "This amount times amount of minutes since last ff is removed from ff rate", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleIgnoreAdmins = CreateConVar("l4d2_tk_auto_ff_ignore_admins", "1", "Should automatic reverse ff ignore admins? 0 = Admins are subjected\n1 = Admins are excempt", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
|
||||
AutoExecConfig(true, "l4d2_tkstopper");
|
||||
|
@ -79,8 +104,9 @@ public void OnPluginStart() {
|
|||
HookEvent("bot_player_replace", Event_BotToPlayer);
|
||||
|
||||
|
||||
RegAdminCmd("sm_ignore", Command_IgnorePlayer, ADMFLAG_KICK, "Makes a player immune for any anti trolling detection for a session");
|
||||
RegAdminCmd("sm_ignore", Command_IgnorePlayer, ADMFLAG_KICK, "Makes a player immune for any anti trolling detection or reverse-ff for a session");
|
||||
RegAdminCmd("sm_tkinfo", Command_TKInfo, ADMFLAG_KICK, "Debug info for TKSTopper");
|
||||
RegAdminCmd("sm_review", Command_TKInfo, ADMFLAG_KICK, "Review FF info for a player");
|
||||
|
||||
if(lateLoaded) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
|
@ -96,7 +122,7 @@ public void OnPluginStart() {
|
|||
public Action Event_ChargerCarry(Event event, const char[] name, bool dontBroadcast) {
|
||||
int victim = GetClientOfUserId(event.GetInt("victim"));
|
||||
if(victim) {
|
||||
isUnderAttack[victim] = StrEqual(name, "charger_carry_start");
|
||||
pData[victim].underAttack = StrEqual(name, "charger_carry_start");
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
@ -104,7 +130,7 @@ public Action Event_ChargerCarry(Event event, const char[] name, bool dontBroadc
|
|||
public Action Event_HunterPounce(Event event, const char[] name, bool dontBroadcast) {
|
||||
int victim = GetClientOfUserId(event.GetInt("victim"));
|
||||
if(victim) {
|
||||
isUnderAttack[victim] = StrEqual(name, "lunge_pounce");
|
||||
pData[victim].underAttack = StrEqual(name, "lunge_pounce");
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
@ -112,14 +138,14 @@ public Action Event_HunterPounce(Event event, const char[] name, bool dontBroadc
|
|||
public Action Event_SmokerChoke(Event event, const char[] name, bool dontBroadcast) {
|
||||
int victim = GetClientOfUserId(event.GetInt("victim"));
|
||||
if(victim) {
|
||||
isUnderAttack[victim] = StrEqual(name, "choke_start");
|
||||
pData[victim].underAttack = StrEqual(name, "choke_start");
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
public Action Event_JockeyRide(Event event, const char[] name, bool dontBroadcast) {
|
||||
int victim = GetClientOfUserId(event.GetInt("victim"));
|
||||
if(victim) {
|
||||
isUnderAttack[victim] = StrEqual(name, "jockey_ride");
|
||||
pData[victim].underAttack = StrEqual(name, "jockey_ride");
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
@ -134,14 +160,14 @@ public Action Event_BotToPlayer(Event event, const char[] name, bool dontBroadca
|
|||
|
||||
// If a player has been idle for over 600s (10 min), reset to them "just joined"
|
||||
// Purpose: Some trolls idle till end and then attack @ escape, or "gain trust"
|
||||
if(GetTime() - iIdleStartTime[player] >= 600) {
|
||||
iJoinTime[player] = GetTime();
|
||||
if(GetTime() - pData[player].idleStartTime >= 600) {
|
||||
pData[player].joinTime = GetTime();
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
public Action Event_PlayerToBot(Event event, char[] name, bool dontBroadcast) {
|
||||
int player = GetClientOfUserId(event.GetInt("player"));
|
||||
iIdleStartTime[player] = GetTime();
|
||||
pData[player].idleStartTime = GetTime();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -150,8 +176,8 @@ public Action Event_PlayerToBot(Event event, char[] name, bool dontBroadcast) {
|
|||
public void Event_FinaleVehicleReady(Event event, const char[] name, bool dontBroadcast) {
|
||||
isFinaleEnding = true;
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(isPlayerTroll[i] && IsClientConnected(i) && IsClientInGame(i)) {
|
||||
PrintChatToAdmins("Note: %N is still marked as troll and will be banned after this game. Use /ignore to ignore them.", i);
|
||||
if(pData[i].isTroll && IsClientConnected(i) && IsClientInGame(i)) {
|
||||
PrintChatToAdmins("Note: %N is still marked as troll and will be banned after this game. Use \"/ignore <player> tk\" to ignore them.", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,38 +187,60 @@ public void OnMapEnd() {
|
|||
}
|
||||
|
||||
public void OnClientPutInServer(int client) {
|
||||
iJoinTime[client] = GetTime();
|
||||
iLastFFTime[client] = GetTime();
|
||||
pData[client].joinTime = pData[client].idleStartTime = GetTime();
|
||||
SDKHook(client, SDKHook_OnTakeDamage, Event_OnTakeDamage);
|
||||
}
|
||||
|
||||
public void OnClientPostAdminCheck(int client) {
|
||||
if(GetUserAdmin(client) != INVALID_ADMIN_ID) {
|
||||
pData[client].immunityFlags = Immune_TK | Immune_RFF;
|
||||
}
|
||||
}
|
||||
|
||||
// Called on map changes, so only reset some variables:
|
||||
public void OnClientDisconnect(int client) {
|
||||
playerTotalDamageFF[client] = 0.0;
|
||||
isUnderAttack[client] = false;
|
||||
iJumpAttempts[client] = 0;
|
||||
pData[client].TKDamageBuffer = 0.0;
|
||||
pData[client].jumpAttempts = 0;
|
||||
pData[client].underAttack = false;
|
||||
}
|
||||
|
||||
// Only clear things when they fully left on their own accord:
|
||||
public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(client > 0 && isPlayerTroll[client]) {
|
||||
BanClient(client, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF", "Excessive Friendly Fire", "TKStopper");
|
||||
if(client > 0 && !IsFakeClient(client) && GetClientTeam(client) <= 2) {
|
||||
if (pData[client].isTroll) {
|
||||
BanClient(client, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF", "Excessive Friendly Fire", "TKStopper");
|
||||
pData[client].isTroll = false;
|
||||
pData[client].autoRFFScaleFactor = 0.0;
|
||||
pData[client].totalDamageFF = 0.0;
|
||||
}
|
||||
|
||||
float minutesSinceiLastFFTime = (GetTime() - pData[client].lastFFTime) / 60.0;
|
||||
float activeRate = pData[client].autoRFFScaleFactor - (minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue);
|
||||
if(activeRate < 0.0) activeRate = 0.0;
|
||||
PrintToConsoleAll("[TKStopper] FF Summary for %N:", client);
|
||||
PrintToConsoleAll("\t\t%.2f TK-FF buffer (%.2f total ff) | %.3f (buf %f) rFF rate | lastff %.1f min ago | %d suicide jumps",
|
||||
pData[client].TKDamageBuffer,
|
||||
pData[client].totalDamageFF,
|
||||
activeRate,
|
||||
pData[client].autoRFFScaleFactor,
|
||||
minutesSinceiLastFFTime,
|
||||
pData[client].jumpAttempts
|
||||
);
|
||||
}
|
||||
isPlayerTroll[client] = false;
|
||||
autoFFScaleFactor[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 && victim <= MaxClients && attacker <= MaxClients && attacker > 0 && victim > 0 && attacker != victim) {
|
||||
if(GetClientTeam(victim) != GetClientTeam(attacker) || attacker == victim) return Plugin_Continue;
|
||||
if(damage > 0.0 && victim <= MaxClients && attacker <= MaxClients && attacker > 0 && victim > 0) {
|
||||
if(GetClientTeam(victim) != GetClientTeam(attacker) || attacker == victim)
|
||||
return Plugin_Continue;
|
||||
else if(damagetype & DMG_BURN && IsFakeClient(attacker) && GetClientTeam(attacker) == 2) {
|
||||
// Ignore damage from fire caused by bots (players who left after causing fire)
|
||||
damage = 0.0;
|
||||
return Plugin_Changed;
|
||||
}
|
||||
// Otherwise if attacker was ignored or is a bot, stop here and let vanilla handle it
|
||||
else if(isImmune[attacker] || IsFakeClient(attacker)) return Plugin_Continue;
|
||||
else if(pData[attacker].immunityFlags & Immune_TK || IsFakeClient(attacker)) return Plugin_Continue;
|
||||
|
||||
bool isAdmin = GetUserAdmin(attacker) != INVALID_ADMIN_ID;
|
||||
|
||||
|
@ -200,24 +248,23 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
//if(IsFakeClient(victim) && !HasEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") || GetEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") == 0) return Plugin_Continue;
|
||||
|
||||
// Stop all damage early if already marked as troll
|
||||
if(isPlayerTroll[attacker]) {
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, autoFFScaleFactor[attacker] * damage);
|
||||
|
||||
if(pData[attacker].isTroll) {
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, pData[attacker].autoRFFScaleFactor * damage);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
// Allow vanilla-damage if being attacked by special (example, charger carry)
|
||||
if(isUnderAttack[victim]) return Plugin_Continue;
|
||||
if(pData[victim].underAttack) return Plugin_Continue;
|
||||
|
||||
// Is damage not caused by fire or pipebombs?
|
||||
bool isDamageDirect = damagetype & (DMG_BLAST|DMG_BURN|DMG_BLAST_SURFACE) == 0;
|
||||
int time = GetTime();
|
||||
// If is a fall within first 2 minutes, do appropiate action
|
||||
if(!isAdmin && damagetype & DMG_FALL && attacker == victim && damage > 0.0 && time - iJoinTime[victim] <= hJoinTime.IntValue * 60) {
|
||||
iJumpAttempts[victim]++;
|
||||
if(!isAdmin && damagetype & DMG_FALL && attacker == victim && damage > 0.0 && time - pData[victim].joinTime <= hJoinTime.IntValue * 60) {
|
||||
pData[victim].jumpAttempts++;
|
||||
float pos[3];
|
||||
GetNearestPlayerPosition(victim, pos);
|
||||
PrintToConsoleAdmins("%N within join time (%d min), attempted to fall");
|
||||
if(iJumpAttempts[victim] > hSuicideLimit.IntValue) {
|
||||
if(pData[victim].jumpAttempts > hSuicideLimit.IntValue) {
|
||||
if(hSuicideAction.IntValue == 1) {
|
||||
LogMessage("[NOTICE] Kicking %N for suicide attempts", victim, hBanTime.IntValue);
|
||||
NotifyAllAdmins("[Notice] Kicking %N for suicide attempts", victim, hBanTime.IntValue);
|
||||
|
@ -228,60 +275,78 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
BanClient(victim, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Suicide fall attempts", "Troll", "TKStopper");
|
||||
} else if(hSuicideAction.IntValue == 3) {
|
||||
LogMessage("[NOTICE] %N will be banned for suicide attempts for %d minutes. ", victim, hBanTime.IntValue);
|
||||
NotifyAllAdmins("[Notice] %N will be banned for suicide attempts for %d minutes. Use /ignore <player> to make them immune.", victim, hBanTime.IntValue);
|
||||
isPlayerTroll[victim] = true;
|
||||
NotifyAllAdmins("[Notice] %N will be banned for suicide attempts for %d minutes. Use \"/ignore <player> tk\" to make them immune.", victim, hBanTime.IntValue);
|
||||
pData[victim].isTroll = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(attacker == victim) return Plugin_Continue;
|
||||
|
||||
// Forgive player based on threshold, resetting accumlated damage
|
||||
if(time - iLastFFTime[attacker] > hForgivenessTime.IntValue) {
|
||||
playerTotalDamageFF[attacker] = 0.0;
|
||||
if(time - pData[attacker].lastFFTime > hForgivenessTime.IntValue) {
|
||||
pData[attacker].TKDamageBuffer = 0.0;
|
||||
}
|
||||
playerTotalDamageFF[attacker] += damage;
|
||||
pData[attacker].TKDamageBuffer += damage;
|
||||
pData[attacker].totalDamageFF += damage;
|
||||
|
||||
// Auto reverse ff logic
|
||||
iLastFFTime[attacker] = time;
|
||||
if(isDamageDirect && (!hFFAutoScaleIgnoreAdmins.BoolValue || !isAdmin)) {
|
||||
int prevFFTime = pData[attacker].lastFFTime;
|
||||
pData[attacker].lastFFTime = time;
|
||||
// If not immune to RFF, damage is direct, _or admin shit_
|
||||
if(~pData[attacker].immunityFlags & Immune_RFF && isDamageDirect && (!hFFAutoScaleIgnoreAdmins.BoolValue || !isAdmin)) {
|
||||
// Decrement any forgiven ratio (computed on demand)
|
||||
float minutesSinceiLastFFTime = (time - iLastFFTime[attacker]) / 60.0;
|
||||
autoFFScaleFactor[attacker] -= minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue;
|
||||
if(autoFFScaleFactor[attacker] < 0.0) {
|
||||
autoFFScaleFactor[attacker] = 0.0;
|
||||
float minutesSinceiLastFFTime = (time - pData[attacker].lastFFTime) / 60.0;
|
||||
pData[attacker].autoRFFScaleFactor -= minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue;
|
||||
if(pData[attacker].autoRFFScaleFactor < 0.0) {
|
||||
pData[attacker].autoRFFScaleFactor = 0.0;
|
||||
}
|
||||
// Then calculate a new reverse ff ratio
|
||||
autoFFScaleFactor[attacker] += hFFAutoScaleAmount.FloatValue * damage;
|
||||
if(isPlayerTroll[attacker]) {
|
||||
autoFFScaleFactor[attacker] *= 2;
|
||||
pData[attacker].autoRFFScaleFactor += hFFAutoScaleAmount.FloatValue * damage;
|
||||
if(pData[attacker].isTroll) {
|
||||
pData[attacker].autoRFFScaleFactor *= 2;
|
||||
}
|
||||
|
||||
if(!isPlayerTroll[attacker] && hFFAutoScaleMaxRatio.FloatValue > 0.0 && autoFFScaleFactor[attacker] > hFFAutoScaleMaxRatio.FloatValue) {
|
||||
autoFFScaleFactor[attacker] = hFFAutoScaleMaxRatio.FloatValue;
|
||||
// Cap max damage only for non-trolls
|
||||
if(!pData[attacker].isTroll && hFFAutoScaleMaxRatio.FloatValue > 0.0 && pData[attacker].autoRFFScaleFactor > hFFAutoScaleMaxRatio.FloatValue) {
|
||||
pData[attacker].autoRFFScaleFactor = hFFAutoScaleMaxRatio.FloatValue;
|
||||
}
|
||||
}
|
||||
pData[attacker].lastFFTime = time;
|
||||
|
||||
// Check for excessive friendly fire damage in short timespan
|
||||
if(!isAdmin && playerTotalDamageFF[attacker] > hThreshold.IntValue && !isFinaleEnding && isDamageDirect) {
|
||||
LogAction(-1, attacker, "Excessive FF (%.2f HP) (%.2f RFF Rate)", playerTotalDamageFF[attacker], autoFFScaleFactor[attacker]);
|
||||
// If not immune to TK, if over TK threshold, not when escaping, and direct damage
|
||||
if(~pData[attacker].immunityFlags & Immune_TK && pData[attacker].TKDamageBuffer > hThreshold.IntValue && !isFinaleEnding && isDamageDirect) {
|
||||
float diffJoinMin = (float(GetTime()) - float(pData[attacker].joinTime)) / 60.0;
|
||||
float lastFFMin = (float(GetTime()) - float(prevFFTime)) / 60.0;
|
||||
LogAction(-1, attacker, "Excessive FF (%.2f HP/%.2f total) (%.2f RFF Rate) (joined %.1fm ago) (%.1fmin last FF)",
|
||||
pData[attacker].TKDamageBuffer,
|
||||
pData[attacker].totalDamageFF,
|
||||
pData[attacker].autoRFFScaleFactor,
|
||||
diffJoinMin,
|
||||
lastFFMin
|
||||
);
|
||||
if(hTKAction.IntValue == 1) {
|
||||
LogMessage("[NOTICE] Kicking %N for excessive FF (%.2f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue);
|
||||
NotifyAllAdmins("[Notice] Kicking %N for excessive FF (%.2f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue);
|
||||
LogMessage("[TKStopper] Kicking %N for excessive FF (%.2f HP)", attacker, pData[attacker].TKDamageBuffer);
|
||||
NotifyAllAdmins("[Notice] Kicking %N for excessive FF (%.2f HP)", attacker, pData[attacker].TKDamageBuffer);
|
||||
KickClient(attacker, "Excessive FF");
|
||||
} else if(hTKAction.IntValue == 2) {
|
||||
LogMessage("[NOTICE] Banning %N for excessive FF (%.2f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue);
|
||||
NotifyAllAdmins("[Notice] Banning %N for excessive FF (%.2f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue);
|
||||
LogMessage("[TKStopper] Banning %N for excessive FF (%.2f HP) for %d minutes.", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue);
|
||||
NotifyAllAdmins("[Notice] Banning %N for excessive FF (%.2f HP) for %d minutes.", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue);
|
||||
BanClient(attacker, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF", "Excessive Friendly Fire", "TKStopper");
|
||||
} else if(hTKAction.IntValue == 3) {
|
||||
LogMessage("[NOTICE] %N will be banned for FF on disconnect (%.2f HP) for %d minutes. ", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue);
|
||||
NotifyAllAdmins("[Notice] %N will be banned for FF on disconnect (%.2f HP) for %d minutes. Use /ignore <player> to make them immune.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue);
|
||||
isPlayerTroll[attacker] = true;
|
||||
LogMessage("[TKStopper] %N will be banned for FF on disconnect (%.2f HP) for %d minutes. ", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue);
|
||||
NotifyAllAdmins("[Notice] %N will be banned for FF on disconnect (%.2f HP) for %d minutes. Use \"/ignore <player> tk\" to make them immune.", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue);
|
||||
pData[attacker].isTroll = true;
|
||||
}
|
||||
damage = 0.0;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
||||
// Modify damages based on criteria
|
||||
if(iJumpAttempts[victim] > 0 || L4D_IsInFirstCheckpoint(victim) || L4D_IsInLastCheckpoint(victim) || time - iJoinTime[attacker] <= hJoinTime.IntValue * 60) {
|
||||
if(pData[victim].jumpAttempts > 0
|
||||
|| L4D_IsInFirstCheckpoint(victim) || L4D_IsInLastCheckpoint(victim)
|
||||
|| time - pData[attacker].joinTime <= hJoinTime.IntValue * 60
|
||||
) {
|
||||
/*
|
||||
If the amount of seconds since they joined is <= the minimum join time cvar (min) threshold
|
||||
or if the player is in a saferoom
|
||||
|
@ -291,17 +356,19 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
damage = 0.0;
|
||||
return Plugin_Handled;
|
||||
}else if(isFinaleEnding) {
|
||||
// Keep admins immune if escape vehicle out
|
||||
if(isAdmin) return Plugin_Continue;
|
||||
// Keep admins immune if escape vehicle out, or if victim is a bot
|
||||
if(isAdmin || IsFakeClient(victim)) return Plugin_Continue;
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, damage * 2.0);
|
||||
damage = 0.0;
|
||||
return Plugin_Changed;
|
||||
}else if(isDamageDirect) { // Ignore fire and propane damage, mistakes can happen
|
||||
}else if(isDamageDirect && !isAdmin && pData[attacker].autoRFFScaleFactor > 0.3) { // Ignore fire and propane damage, mistakes can happen
|
||||
// Apply their reverse ff damage, and have victim take a decreasing amount
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, autoFFScaleFactor[attacker] * damage);
|
||||
if(isPlayerTroll[attacker]) return Plugin_Stop;
|
||||
if(autoFFScaleFactor[attacker] > 1.0)
|
||||
damage /= autoFFScaleFactor[attacker];
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, pData[attacker].autoRFFScaleFactor * damage);
|
||||
if(pData[attacker].isTroll) return Plugin_Stop;
|
||||
else if(pData[attacker].immunityFlags & Immune_RFF) return Plugin_Continue;
|
||||
|
||||
if(pData[attacker].autoRFFScaleFactor > 1.0)
|
||||
damage /= pData[attacker].autoRFFScaleFactor;
|
||||
else
|
||||
damage /= 2.0;
|
||||
return Plugin_Changed;
|
||||
|
@ -314,25 +381,99 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
|
||||
public Action Command_TKInfo(int client, int args) {
|
||||
int time = GetTime();
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
|
||||
float minutesSinceiLastFFTime = (time - iLastFFTime[i]) / 60.0;
|
||||
float activeRate = autoFFScaleFactor[i] - (minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue);
|
||||
if(activeRate < 0.0) {
|
||||
activeRate = 0.0;
|
||||
}
|
||||
ReplyToCommand(client, "%N: %f TK-FF buffer | %.3f (buf %f), reverse FF rate | last ff %.1f min ago | %d suicide jumps", i, playerTotalDamageFF[i], activeRate, autoFFScaleFactor[i], minutesSinceiLastFFTime, iJumpAttempts[i]);
|
||||
if(args > 0) {
|
||||
static char arg1[32];
|
||||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
|
||||
static char target_name[MAX_TARGET_LENGTH];
|
||||
int target_list[1], target_count;
|
||||
bool tn_is_ml;
|
||||
|
||||
if ((target_count = ProcessTargetString(
|
||||
arg1,
|
||||
client,
|
||||
target_list,
|
||||
1,
|
||||
COMMAND_FILTER_NO_MULTI,
|
||||
target_name,
|
||||
sizeof(target_name),
|
||||
tn_is_ml)) <= 0
|
||||
|| target_count == 0) {
|
||||
ReplyToTargetError(client, target_count);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
int target = target_list[0];
|
||||
ReplyToCommand(client, "FF Review for '%N':", target);
|
||||
if(pData[target].isTroll) {
|
||||
ReplyToCommand(client, "- will be banned on disconnect for TK -", target);
|
||||
}
|
||||
if(pData[target].immunityFlags == Immune_TK) {
|
||||
ReplyToCommand(client, "Immunity: Teamkiller Detection", target);
|
||||
} else if(pData[target].immunityFlags == Immune_RFF) {
|
||||
ReplyToCommand(client, "Immunity: Auto reverse-ff", target);
|
||||
} else if(view_as<int>(pData[target].immunityFlags) > 0) {
|
||||
ReplyToCommand(client, "Immunity: Teamkiller Detection, Auto reverse-ff", target);
|
||||
} else {
|
||||
ReplyToCommand(client, "Immunity: (none, use /ignore <player> [immunity] to toggle)", target);
|
||||
}
|
||||
float minutesSinceiLastFFTime = (time - pData[target].lastFFTime) / 60.0;
|
||||
float activeRate = pData[target].autoRFFScaleFactor - (minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue);
|
||||
if(activeRate < 0.0) activeRate = 0.0;
|
||||
|
||||
ReplyToCommand(client, "Total FF Damage: %.1f (%.1f min ago last ff)", pData[target].totalDamageFF, minutesSinceiLastFFTime);
|
||||
if(~pData[target].immunityFlags & Immune_TK)
|
||||
ReplyToCommand(client, "Recent FF (TKDetectBuff): %.1f", pData[target].TKDamageBuffer);
|
||||
if(~pData[target].immunityFlags & Immune_RFF)
|
||||
ReplyToCommand(client, "Auto Reverse-FF: %.1f return rate", activeRate);
|
||||
ReplyToCommand(client, "Attempted suicide jumps: %d", pData[target].jumpAttempts);
|
||||
} else {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
|
||||
float minutesSinceiLastFFTime = (time - pData[i].lastFFTime) / 60.0;
|
||||
float activeRate = pData[i].autoRFFScaleFactor - (minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue);
|
||||
if(activeRate < 0.0) {
|
||||
activeRate = 0.0;
|
||||
}
|
||||
ReplyToCommand(client, "%20N: %.2f TK-FF buf (%.2f total) | %.3f (buf %f) rFF rate | lastff %.1f min ago | %d suicide jumps",
|
||||
i,
|
||||
pData[i].TKDamageBuffer,
|
||||
pData[i].totalDamageFF,
|
||||
activeRate,
|
||||
pData[i].autoRFFScaleFactor,
|
||||
minutesSinceiLastFFTime,
|
||||
pData[i].jumpAttempts
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
||||
public Action Command_IgnorePlayer(int client, int args) {
|
||||
char arg1[32];
|
||||
char arg1[32], arg2[16];
|
||||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
GetCmdArg(2, arg2, sizeof(arg2));
|
||||
|
||||
if(args < 2) {
|
||||
ReplyToCommand(client, "Usage: sm_ignore <player> <tk/teamkill/rff/reverseff>");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
if(StrEqual(arg2, "tk") || StrEqual(arg2, "teamkill")) {
|
||||
flags = Immune_TK;
|
||||
} else if(StrEqual(arg2, "all") || StrEqual(arg2, "a")) {
|
||||
flags = Immune_TK | Immune_RFF;
|
||||
} else if(StrEqual(arg2, "reverseff") || StrEqual(arg2, "rff")) {
|
||||
flags = Immune_RFF;
|
||||
} else {
|
||||
ReplyToCommand(client, "Usage: sm_ignore <player> <tk/teamkill/rff/reverseff>");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char target_name[MAX_TARGET_LENGTH];
|
||||
int target_list[1], target_count;
|
||||
int target_list[MAXPLAYERS+1], target_count;
|
||||
bool tn_is_ml;
|
||||
|
||||
if ((target_count = ProcessTargetString(
|
||||
|
@ -349,23 +490,38 @@ public Action Command_IgnorePlayer(int client, int args) {
|
|||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
for(int i = 0; i < target_count; i++) {
|
||||
for (int i = 0; i < target_count; i++) {
|
||||
int target = target_list[i];
|
||||
if(GetUserAdmin(target) != INVALID_ADMIN_ID) {
|
||||
if (GetUserAdmin(target) != INVALID_ADMIN_ID) {
|
||||
ReplyToCommand(client, "%N is an admin and is already immune.");
|
||||
}else{
|
||||
if(isImmune[target]) {
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
if (flags & Immune_TK) {
|
||||
if (pData[target].immunityFlags & Immune_TK) {
|
||||
LogAction(client, target, "\"%L\" re-enabled teamkiller detection for \"%L\"", client, target);
|
||||
ShowActivity2(client, "[FTT] ", "%N has re-enabled teamkiller detection for %N", client, target);
|
||||
} else {
|
||||
LogAction(client, target, "\"%L\" ignored teamkiller detection for \"%L\"", client, target);
|
||||
ShowActivity2(client, "[FTT] ", "%N has ignored teamkiller detection for %N", client, target);
|
||||
}
|
||||
isImmune[target] = !isImmune[target];
|
||||
}
|
||||
isPlayerTroll[target] = false;
|
||||
}
|
||||
pData[target].immunityFlags ^= Immune_TK;
|
||||
}
|
||||
|
||||
if (flags & Immune_RFF) {
|
||||
if (pData[target].immunityFlags & Immune_RFF) {
|
||||
LogAction(client, target, "\"%L\" re-enabled auto reverse friendly-fire for \"%L\"", client, target);
|
||||
ShowActivity2(client, "[FTT] ", "%N has re-enabled auto reverse friendly-fire for %N", client, target);
|
||||
} else {
|
||||
LogAction(client, target, "\"%L\" disabled auto reverse friendly-fire for \"%L\"", client, target);
|
||||
ShowActivity2(client, "[FTT] ", "%N has disabled auto reverse friendly-fire for %N", client, target);
|
||||
pData[target].autoRFFScaleFactor = 0.0;
|
||||
}
|
||||
pData[target].immunityFlags ^= Immune_RFF;
|
||||
}
|
||||
|
||||
pData[target].isTroll = false;
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
@ -390,8 +546,8 @@ stock bool GetNearestPlayerPosition(int client, float pos[3]) {
|
|||
return lowestID > 0;
|
||||
}
|
||||
|
||||
static char buffer[254];
|
||||
stock void PrintChatToAdmins(const char[] format, any ...) {
|
||||
char buffer[254];
|
||||
VFormat(buffer, sizeof(buffer), format, 2);
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||
|
@ -405,7 +561,6 @@ stock void PrintChatToAdmins(const char[] format, any ...) {
|
|||
}
|
||||
|
||||
stock void PrintToConsoleAdmins(const char[] format, any ...) {
|
||||
char buffer[254];
|
||||
VFormat(buffer, sizeof(buffer), format, 2);
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||
|
|
|
@ -212,6 +212,7 @@ stock int SpawnSurvivor(const float vPos[3], const float vAng[3], const char[] m
|
|||
TeleportEntity(bot_client_id, vPos, NULL_VECTOR, NULL_VECTOR);
|
||||
|
||||
SetEntityModel(bot_client_id, model); //set entity model to custom survivor model
|
||||
CreateTimer(6.0, Timer_Move, bot_user_id);
|
||||
return bot_user_id;
|
||||
}
|
||||
void AvoidCharacter(int type, bool avoid) {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <left4dhooks>
|
||||
#include <l4d_info_editor>
|
||||
#include <jutils>
|
||||
#include <l4d2_weapon_stocks>
|
||||
|
||||
#define L4D2_WEPUPGFLAG_NONE (0 << 0)
|
||||
#define L4D2_WEPUPGFLAG_INCENDIARY (1 << 0)
|
||||
|
@ -48,6 +49,10 @@
|
|||
|
||||
#define TANK_CLASS_ID 8
|
||||
|
||||
// configurable:
|
||||
#define PLAYER_DROP_TIMEOUT_SECONDS 120000.0
|
||||
|
||||
|
||||
/* 5+ Tank Improvement
|
||||
Every X amount of L4D2_OnChooseVictim() calls,
|
||||
if a player has done < Y amount since last call
|
||||
|
@ -71,22 +76,59 @@ public Plugin myinfo =
|
|||
url = ""
|
||||
};
|
||||
|
||||
static ConVar hExtraItemBasePercentage, hAddExtraKits, hMinPlayers, hUpdateMinPlayers, hMinPlayersSaferoomDoor, hSaferoomDoorWaitSeconds, hSaferoomDoorAutoOpen, hEPIHudState, hExtraFinaleTank, cvDropDisconnectTime;
|
||||
static ConVar hExtraItemBasePercentage, hAddExtraKits, hMinPlayers, hUpdateMinPlayers, hMinPlayersSaferoomDoor, hSaferoomDoorWaitSeconds, hSaferoomDoorAutoOpen, hEPIHudState, hExtraFinaleTank, cvDropDisconnectTime, hSplitTankChance;
|
||||
static int extraKitsAmount, extraKitsStarted, abmExtraCount, firstSaferoomDoorEntity, playersLoadedIn, playerstoWaitFor;
|
||||
static int currentChapter;
|
||||
static bool isCheckpointReached, isLateLoaded, firstGiven, isFailureRound;
|
||||
static ArrayList ammoPacks;
|
||||
static Handle updateHudTimer;
|
||||
static char gamemode[32];
|
||||
|
||||
static bool allowTankSplit = true;
|
||||
|
||||
enum State {
|
||||
State_Empty,
|
||||
State_PendingEmpty,
|
||||
State_Active
|
||||
}
|
||||
|
||||
enum struct PlayerData {
|
||||
bool itemGiven; //Is player being given an item (such that the next pickup event is ignored)
|
||||
bool isUnderAttack; //Is the player under attack (by any special)
|
||||
bool active;
|
||||
State state;
|
||||
}
|
||||
|
||||
enum struct PlayerInventory {
|
||||
int timestamp;
|
||||
bool isAlive;
|
||||
|
||||
WeaponId itemID[6]; //int -> char?
|
||||
bool lasers;
|
||||
char meleeID[32];
|
||||
|
||||
int primaryHealth;
|
||||
int tempHealth;
|
||||
|
||||
char model[64];
|
||||
int survivorType;
|
||||
}
|
||||
|
||||
PlayerData playerData[MAXPLAYERS+1];
|
||||
|
||||
/*
|
||||
TODO:
|
||||
1. Save player inventory on:
|
||||
a. Disconnect (saferoom disconnect too)
|
||||
b. Periodically?
|
||||
2. On new map join (OnClientPutInServer¿) check following item matches:
|
||||
a. primary weapon
|
||||
b. secondary weapon (excl melee)
|
||||
If a || b != saved items, then their character was dropped/swapped
|
||||
Restore from saved inventory
|
||||
*/
|
||||
|
||||
static StringMap weaponMaxClipSizes;
|
||||
static StringMap pInv;
|
||||
|
||||
static char HUD_SCRIPT[] = "ExtraPlayerHUD <- { Fields = { players = { slot = g_ModeScript.HUD_RIGHT_BOT, dataval = \"%s\", flags = g_ModeScript.HUD_FLAG_ALIGN_LEFT | g_ModeScript.HUD_FLAG_TEAM_SURVIVORS | g_ModeScript.HUD_FLAG_NOBG } } }; HUDSetLayout( ExtraPlayerHUD ); HUDPlace( g_ModeScript.HUD_RIGHT_BOT, 0.72, 0.78, 0.3, 0.3 ); g_ModeScript";
|
||||
|
||||
|
@ -111,6 +153,7 @@ public void OnPluginStart() {
|
|||
}
|
||||
|
||||
weaponMaxClipSizes = new StringMap();
|
||||
pInv = new StringMap();
|
||||
ammoPacks = new ArrayList(2); //<int entityID, ArrayList clients>
|
||||
|
||||
HookEvent("player_spawn", Event_PlayerSpawn);
|
||||
|
@ -122,11 +165,12 @@ public void OnPluginStart() {
|
|||
HookEvent("round_end", Event_RoundEnd);
|
||||
HookEvent("map_transition", Event_MapTransition);
|
||||
HookEvent("game_start", Event_GameStart);
|
||||
HookEvent("game_end", Event_GameStart);
|
||||
HookEvent("round_freeze_end", Event_RoundFreezeEnd);
|
||||
HookEvent("tank_spawn", Event_TankSpawn);
|
||||
|
||||
//Special Event Tracking
|
||||
HookEvent("player_disconnect", Event_PlayerDisconnect);
|
||||
HookEvent("player_team", Event_PlayerTeam);
|
||||
|
||||
HookEvent("charger_carry_start", Event_ChargerCarry);
|
||||
HookEvent("charger_carry_end", Event_ChargerCarry);
|
||||
|
@ -150,8 +194,9 @@ public void OnPluginStart() {
|
|||
hSaferoomDoorWaitSeconds = CreateConVar("l4d2_extraitems_doorunlock_wait", "55", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0);
|
||||
hSaferoomDoorAutoOpen = CreateConVar("l4d2_extraitems_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0);
|
||||
hEPIHudState = CreateConVar("l4d2_extraitems_hudstate", "1", "Controls when the hud displays.\n0 -> OFF, 1 = When 5+ players, 2 = ALWAYS", FCVAR_NONE, true, 0.0, true, 2.0);
|
||||
hExtraFinaleTank = CreateConVar("l4d2_extraitems_extra_finale_tank", "1", "0 = Normal tank spawning, 1 = Two tanks spawn on second stage (half health)", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
cvDropDisconnectTime = CreateConVar("l4d2_extraitems_disconnect_time", "120", "The amount of seconds after a player has actually disconnected, where their character slot will be void. 0 to disable", FCVAR_NONE, true, 0.0);
|
||||
hExtraFinaleTank = CreateConVar("l4d2_extraitems_extra_tanks", "3", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hSplitTankChance = CreateConVar("l4d2_extraitems_splittank_chance", "0.5", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
cvDropDisconnectTime = CreateConVar("l4d2_extraitems_disconnect_time", "120.0", "The amount of seconds after a player has actually disconnected, where their character slot will be void. 0 to disable", FCVAR_NONE, true, 0.0);
|
||||
|
||||
hEPIHudState.AddChangeHook(Cvar_HudStateChange);
|
||||
|
||||
|
@ -196,6 +241,22 @@ public void OnPluginStart() {
|
|||
RegAdminCmd("sm_epi_items", Command_RunExtraItems, ADMFLAG_CHEATS);
|
||||
#endif
|
||||
|
||||
CreateTimer(10.0, Timer_ForceUpdateInventories, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
public Action Timer_ForceUpdateInventories(Handle h) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
|
||||
GetPlayerInventory(i);
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client) {
|
||||
if(!IsFakeClient(client) && GetClientTeam(client) == 2 && !StrEqual(gamemode, "hideandseek")) {
|
||||
CreateTimer(0.2, Timer_CheckInventory, client);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -348,7 +409,7 @@ int extraTankHP;
|
|||
FinaleStage finaleStage;
|
||||
|
||||
public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) {
|
||||
if(finaleType == FINALE_STARTED && abmExtraCount > 4 && hExtraFinaleTank.BoolValue) {
|
||||
if(finaleType == FINALE_STARTED && abmExtraCount > 4) {
|
||||
finaleStage = Stage_FinaleActive;
|
||||
PrintToConsoleAll("[EPI] Finale started and over threshold");
|
||||
} else if(finaleType == FINALE_TANK) {
|
||||
|
@ -367,45 +428,42 @@ public void Event_TankSpawn(Event event, const char[] name, bool dontBroadcast)
|
|||
int user = event.GetInt("userid");
|
||||
int tank = GetClientOfUserId(user);
|
||||
if(tank > 0 && IsFakeClient(tank) && abmExtraCount > 4 && hExtraFinaleTank.BoolValue) {
|
||||
if(finaleStage == Stage_FinaleTank2) {
|
||||
if(finaleStage == Stage_FinaleTank2 && allowTankSplit && hExtraFinaleTank.IntValue & 2) {
|
||||
PrintToConsoleAll("[EPI] Second tank spawned, setting health.");
|
||||
// Sets health in half, sets finaleStage to health
|
||||
CreateTimer(5.0, Timer_SpawnFinaleTank, user);
|
||||
} else if(finaleStage == Stage_FinaleDuplicatePending) {
|
||||
PrintToConsoleAll("[EPI] Third & final tank spawned");
|
||||
RequestFrame(Frame_SetExtraTankHealth, user);
|
||||
} else if(finaleStage == Stage_Inactive && GetSurvivorsCount() > 6) {
|
||||
PrintToConsoleAll("[EPI] Creating a split tank");
|
||||
} else if(finaleStage == Stage_Inactive && allowTankSplit && hExtraFinaleTank.IntValue & 1 && GetSurvivorsCount() > 6) {
|
||||
finaleStage = Stage_TankSplit;
|
||||
// Half their HP, assign half to self and for next tank
|
||||
int hp = GetEntProp(tank, Prop_Send, "m_iHealth") / 2;
|
||||
SetEntProp(tank, Prop_Send, "m_iHealth", hp);
|
||||
extraTankHP = hp;
|
||||
CreateTimer(11.0, Timer_SplitTank, user);
|
||||
if(GetRandomFloat() <= hSplitTankChance.FloatValue) {
|
||||
// Half their HP, assign half to self and for next tank
|
||||
int hp = GetEntProp(tank, Prop_Send, "m_iHealth") / 2;
|
||||
PrintToConsoleAll("[EPI] Creating a split tank (hp=%d)", hp);
|
||||
extraTankHP = hp;
|
||||
CreateTimer(0.2, Timer_SetHealth, user);
|
||||
CreateTimer(11.0, Timer_SpawnSplitTank, user);
|
||||
}
|
||||
// Then, summon the next tank
|
||||
} else if(finaleStage == Stage_TankSplit) {
|
||||
|
||||
CreateTimer(0.2, Timer_SetHealth, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
public Action Timer_SpawnFinaleTank(Handle t, int user) {
|
||||
if(finaleStage == Stage_TankSplit) {
|
||||
if(finaleStage == Stage_FinaleTank2) {
|
||||
ServerCommand("sm_forcespecial tank");
|
||||
finaleStage = Stage_Inactive;
|
||||
}
|
||||
}
|
||||
public Action Timer_SplitTank(Handle t, int user) {
|
||||
int tank = GetClientOfUserId(user);
|
||||
if(tank > 0 && finaleStage == Stage_Inactive) {
|
||||
finaleStage = Stage_TankSplit;
|
||||
// Half their HP, assign half to self and for next tank
|
||||
int hp = GetEntProp(tank, Prop_Send, "m_iHealth") / 2;
|
||||
SetEntProp(tank, Prop_Send, "m_iHealth", hp);
|
||||
extraTankHP = hp;
|
||||
// Then, summon the next tank
|
||||
ServerCommand("sm_forcespecial tank");
|
||||
} else {
|
||||
finaleStage = Stage_Inactive;
|
||||
public Action Timer_SpawnSplitTank(Handle t, int user) {
|
||||
ServerCommand("sm_forcespecial tank");
|
||||
}
|
||||
public Action Timer_SetHealth(Handle h, int user) {
|
||||
int client = GetClientOfUserId(user);
|
||||
if(client > 0 ) {
|
||||
SetEntProp(client, Prop_Send, "m_iHealth", extraTankHP);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -437,8 +495,12 @@ public Action Event_GameStart(Event event, const char[] name, bool dontBroadcast
|
|||
extraKitsStarted = 0;
|
||||
abmExtraCount = 4;
|
||||
hMinPlayers.IntValue = 4;
|
||||
currentChapter = 0;
|
||||
pInv.Clear();
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
playerData[i].state = State_Empty;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
|
||||
}
|
||||
|
||||
public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
|
@ -447,7 +509,7 @@ public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBr
|
|||
CreateTimer(1.5, Timer_RemoveInvincibility, client);
|
||||
SDKHook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
|
||||
if(!IsFakeClient(client)) {
|
||||
playerData[client].active = true;
|
||||
playerData[client].state = State_Active;
|
||||
if(L4D_IsFirstMapInScenario() && !firstGiven) {
|
||||
//Check if all clients are ready, and survivor count is > 4.
|
||||
if(AreAllClientsReady()) {
|
||||
|
@ -470,11 +532,12 @@ public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBr
|
|||
// TODO: Check if Timer_UpdateMinPlayers is needed, or if this works:
|
||||
// Never decrease abmExtraCount
|
||||
int newCount = GetRealSurvivorsCount();
|
||||
if(newCount > abmExtraCount) {
|
||||
if(newCount > abmExtraCount && abmExtraCount > 4) {
|
||||
abmExtraCount = newCount;
|
||||
hMinPlayers.IntValue = abmExtraCount;
|
||||
}
|
||||
// If 5 survivors, then set them up, TP them.
|
||||
if(abmExtraCount > 4) {
|
||||
if(newCount > 4) {
|
||||
RequestFrame(Frame_SetupNewClient, client);
|
||||
}
|
||||
}
|
||||
|
@ -516,28 +579,65 @@ public Action Event_PlayerSpawn(Event event, const char[] name, bool dontBroadca
|
|||
|
||||
}
|
||||
|
||||
public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
|
||||
int userid = event.GetInt("userid");
|
||||
int client = GetClientOfUserId(userid);
|
||||
if(client > 0 && !IsFakeClient(client)) {
|
||||
DataPack pack = new DataPack();
|
||||
pack.WriteCell(userid);
|
||||
pack.WriteCell(client);
|
||||
CreateDataTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, pack);
|
||||
public Action Timer_CheckInventory(Handle h, int client) {
|
||||
if(IsClientConnected(client) && IsClientInGame(client) && GetClientTeam(client) == 2 && DoesInventoryDiffer(client)) {
|
||||
PrintToConsoleAll("[EPI] Detected mismatch inventory for %N, restoring", client);
|
||||
RestoreInventory(client);
|
||||
}
|
||||
}
|
||||
|
||||
public Action Timer_DropSurvivor(Handle h, DataPack pack) {
|
||||
int userid = pack.ReadCell();
|
||||
int client = GetClientOfUserId(userid);
|
||||
// Check if player is not connected, if not, drop their existing status
|
||||
if(client == 0) {
|
||||
client = pack.ReadCell();
|
||||
if(client == 0) //In the case that someone took their client index, don't inactivate them:
|
||||
playerData[client].active = false;
|
||||
public void OnClientDisconnect(int client) {
|
||||
if(GetClientTeam(client) == 2 && !IsFakeClient(client))
|
||||
SaveInventory(client);
|
||||
}
|
||||
|
||||
public void Event_PlayerTeam(Event event, const char[] name, bool dontBroadcast) {
|
||||
if(event.GetBool("disconnect")) {
|
||||
int userid = event.GetInt("userid");
|
||||
int client = GetClientOfUserId(userid);
|
||||
int team = event.GetInt("team");
|
||||
if(client > 0 && !event.GetBool("isbot") && team == 2) {
|
||||
playerData[client].state = State_PendingEmpty;
|
||||
/*DataPack pack;
|
||||
CreateDataTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, pack);
|
||||
pack.WriteCell(userid);
|
||||
pack.WriteCell(client);*/
|
||||
CreateTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action Timer_DropSurvivor(Handle h, int client) {
|
||||
if(playerData[client].state == State_PendingEmpty) {
|
||||
playerData[client].state = State_Empty;
|
||||
PrintToConsoleAll("[EPI] Dropping survivor %d. hMinPlayers-pre:%d", client, hMinPlayers.IntValue);
|
||||
hMinPlayers.IntValue = --abmExtraCount;
|
||||
if(hMinPlayers.IntValue < 4) {
|
||||
hMinPlayers.IntValue = 4;
|
||||
PrintToConsoleAll("[EPI!!] hMinPlayers dropped below 4. This is a bug, please report to jackz.");
|
||||
PrintToServer("[EPI!!] hMinPlayers dropped below 4. This is a bug, please report to jackz.");
|
||||
}
|
||||
DropDroppedInventories();
|
||||
}
|
||||
}
|
||||
|
||||
/*public Action Timer_DropSurvivor(Handle h, DataPack pack) {
|
||||
pack.Reset();
|
||||
int userid = pack.ReadCell();
|
||||
int client = pack.ReadCell();
|
||||
// If the userid occupying client index is diff (or 0)
|
||||
if(GetClientOfUserId(userid) != client) {
|
||||
// If player was not replaced
|
||||
if(!IsClientConnected(client)) {
|
||||
PrintToConsoleAll("Dropping disconnected player after inactivity. UID:%d, index:%d, new MinPlayers: %d", userid, client, hMinPlayers.IntValue-1);
|
||||
//playerData[client].active = false;
|
||||
abmExtraCount--;
|
||||
hMinPlayers.IntValue--;
|
||||
}
|
||||
}
|
||||
DropDroppedInventories();
|
||||
}*/
|
||||
|
||||
/////////////////////////////////////////
|
||||
/////// Events
|
||||
/////////////////////////////////////////
|
||||
|
@ -560,12 +660,42 @@ public Action L4D_OnIsTeamFull(int team, bool &full) {
|
|||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
char TIER1_WEAPONS[5][] = {
|
||||
"shotgun_chrome",
|
||||
"pumpshotgun",
|
||||
"smg",
|
||||
"smg_silenced",
|
||||
"smg_mp5"
|
||||
};
|
||||
|
||||
char TIER2_WEAPONS[9][] = {
|
||||
"autoshotgun",
|
||||
"rifle_ak47",
|
||||
"sniper_military",
|
||||
"rifle_sg552",
|
||||
"rifle_desert",
|
||||
"sniper_scout",
|
||||
"rifle",
|
||||
"hunting_rifle",
|
||||
"shotgun_spas"
|
||||
};
|
||||
|
||||
public void Frame_SetupNewClient(int client) {
|
||||
if(!DoesClientHaveKit(client)) {
|
||||
int item = GivePlayerItem(client, "weapon_first_aid_kit");
|
||||
EquipPlayerWeapon(client, item);
|
||||
}
|
||||
|
||||
static char weapon[32];
|
||||
if(currentChapter == 1 || (currentChapter == 2 && GetRandomFloat() < 0.3)) {
|
||||
Format(weapon, sizeof(weapon), "weapon_%s", TIER1_WEAPONS[GetRandomInt(0,4)]);
|
||||
} else {
|
||||
Format(weapon, sizeof(weapon), "weapon_%s", TIER2_WEAPONS[GetRandomInt(0,8)]);
|
||||
}
|
||||
|
||||
// Find a suitable weapon to spawn with
|
||||
|
||||
|
||||
// static float spawnPos[3];
|
||||
// if(GetIdealPositionInSurvivorFlow(client, spawnPos))
|
||||
// TeleportEntity(client, spawnPos, NULL_VECTOR, NULL_VECTOR);
|
||||
|
@ -618,14 +748,28 @@ public void OnMapStart() {
|
|||
}else if(!L4D_IsFirstMapInScenario()) {
|
||||
//Re-set value incase it reset.
|
||||
//hMinPlayers.IntValue = abmExtraCount;
|
||||
currentChapter++;
|
||||
}else if(L4D_IsMissionFinalMap()) {
|
||||
//Add extra kits for finales
|
||||
static char curMap[64];
|
||||
GetCurrentMap(curMap, sizeof(curMap));
|
||||
|
||||
if(StrEqual(curMap, "c4m5_milltown_escape")) {
|
||||
allowTankSplit = false;
|
||||
} else {
|
||||
allowTankSplit = true;
|
||||
}
|
||||
|
||||
int extraKits = GetSurvivorsCount() - 4;
|
||||
if(extraKits > 0) {
|
||||
extraKitsAmount += extraKits;
|
||||
extraKitsStarted = extraKitsAmount;
|
||||
}
|
||||
currentChapter++;
|
||||
} else {
|
||||
currentChapter++;
|
||||
}
|
||||
|
||||
|
||||
if(!isLateLoaded) {
|
||||
isLateLoaded = false;
|
||||
|
@ -690,7 +834,7 @@ public void EntityOutput_OnStartTouchSaferoom(const char[] output, int caller, i
|
|||
float averageTeamHP = GetAverageHP();
|
||||
if(averageTeamHP <= 30.0) extraPlayers += (extraPlayers / 2); //if perm. health < 30, give an extra 4 on top of the extra
|
||||
else if(averageTeamHP <= 50.0) extraPlayers = (extraPlayers / 3); //if the team's average health is less than 50 (permament) then give another
|
||||
//Chance to get 1-2 extra kits (might need to be nerfed or restricted to > 50 HP)
|
||||
//Chance to get an extra kit (might need to be nerfed or restricted to > 50 HP)
|
||||
if(GetRandomFloat() < 0.3 && averageTeamHP <= 80.0) ++extraPlayers;
|
||||
|
||||
|
||||
|
@ -701,6 +845,8 @@ public void EntityOutput_OnStartTouchSaferoom(const char[] output, int caller, i
|
|||
extraKitsAmount = extraPlayers;
|
||||
|
||||
extraKitsStarted = extraKitsAmount;
|
||||
|
||||
hMinPlayers.IntValue = abmExtraCount;
|
||||
PrintToConsoleAll("CHECKPOINT REACHED BY %N | EXTRA KITS: %d", client, extraPlayers);
|
||||
PrintToServer("Player entered saferoom. Providing %d extra kits", extraKitsAmount);
|
||||
}
|
||||
|
@ -917,11 +1063,11 @@ public Action Timer_UpdateHud(Handle h) {
|
|||
}
|
||||
//TOOD: Move to bool instead of ent prop
|
||||
if(!IsPlayerAlive(i))
|
||||
Format(data, sizeof(data), "Dead");
|
||||
Format(data, sizeof(data), "xx");
|
||||
else if(GetEntProp(i, Prop_Send, "m_bIsOnThirdStrike") == 1)
|
||||
Format(data, sizeof(data), "+%d b&&w %s%s%s", health, items[i].throwable, items[i].usable, items[i].consumable);
|
||||
Format(data, sizeof(data), "+%d b&w %s%s%s", health, items[i].throwable, items[i].usable, items[i].consumable);
|
||||
else if(GetEntProp(i, Prop_Send, "m_isIncapacitated") == 1) {
|
||||
Format(data, sizeof(data), "+%d (down)", health);
|
||||
Format(data, sizeof(data), "+%d --", health);
|
||||
}else{
|
||||
Format(data, sizeof(data), "+%d %s%s%s", health, items[i].throwable, items[i].usable, items[i].consumable);
|
||||
}
|
||||
|
@ -994,6 +1140,108 @@ public void PopulateItems() {
|
|||
/////////////////////////////////////
|
||||
/// Stocks
|
||||
////////////////////////////////////
|
||||
// enum struct PlayerData {
|
||||
// bool itemGiven; //Is player being given an item (such that the next pickup event is ignored)
|
||||
// bool isUnderAttack; //Is the player under attack (by any special)
|
||||
// bool active;
|
||||
|
||||
// WeaponId itemID[6]; //int -> char?
|
||||
// bool lasers;
|
||||
// char meleeID[32];
|
||||
|
||||
|
||||
// int primaryHealth;
|
||||
// int tempHealth;
|
||||
|
||||
// char model[32];
|
||||
// }
|
||||
|
||||
void DropDroppedInventories() {
|
||||
StringMapSnapshot snapshot = pInv.Snapshot();
|
||||
static PlayerInventory inv;
|
||||
static char buffer[32];
|
||||
int time = GetTime();
|
||||
for(int i = 0; i < snapshot.Length; i++) {
|
||||
snapshot.GetKey(i, buffer, sizeof(buffer));
|
||||
pInv.GetArray(buffer, inv, sizeof(inv));
|
||||
if(time - inv.timestamp > PLAYER_DROP_TIMEOUT_SECONDS) {
|
||||
pInv.Remove(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SaveInventory(int client) {
|
||||
|
||||
PlayerInventory inventory;
|
||||
inventory.timestamp = GetTime();
|
||||
inventory.isAlive = IsPlayerAlive(client);
|
||||
|
||||
inventory.primaryHealth = GetClientHealth(client);
|
||||
GetClientModel(client, inventory.model, 64);
|
||||
inventory.survivorType = GetEntProp(client, Prop_Send, "m_survivorCharacter");
|
||||
|
||||
int weapon;
|
||||
static char buffer[32];
|
||||
for(int i = 5; i >= 0; i--) {
|
||||
weapon = GetPlayerWeaponSlot(client, i);
|
||||
inventory.itemID[i] = IdentifyWeapon(weapon);
|
||||
}
|
||||
if(inventory.itemID[0] != WEPID_MELEE && inventory.itemID[0] != WEPID_NONE)
|
||||
inventory.lasers = GetEntProp(weapon, Prop_Send, "m_upgradeBitVec") == 4;
|
||||
|
||||
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
||||
pInv.SetArray(buffer, inventory, sizeof(inventory));
|
||||
}
|
||||
|
||||
void RestoreInventory(int client) {
|
||||
static char buffer[32];
|
||||
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
||||
PlayerInventory inventory;
|
||||
pInv.GetArray(buffer, inventory, sizeof(inventory));
|
||||
|
||||
PrintToConsoleAll("[debug:RINV] health=%d primaryID=%d secondID=%d throw=%d kit=%d pill=%d surv=%d", inventory.primaryHealth, inventory.itemID[0], inventory.itemID[1], inventory.itemID[2], inventory.itemID[3], inventory.itemID[4], inventory.itemID[5], inventory.survivorType);
|
||||
|
||||
return;
|
||||
SetEntityModel(client, inventory.model);
|
||||
SetEntProp(client, Prop_Send, "m_survivorCharacter", inventory.survivorType);
|
||||
|
||||
if(inventory.isAlive) {
|
||||
SetEntProp(client, Prop_Send, "m_iHealth", inventory.primaryHealth);
|
||||
|
||||
int weapon;
|
||||
for(int i = 5; i >= 0; i--) {
|
||||
WeaponId id = inventory.itemID[i];
|
||||
if(id != WEPID_NONE) {
|
||||
if(id == WEPID_MELEE)
|
||||
GetWeaponName(id, buffer, sizeof(buffer));
|
||||
else
|
||||
GetMeleeWeaponName(id, buffer, sizeof(buffer));
|
||||
weapon = GiveClientWeapon(client, buffer);
|
||||
}
|
||||
}
|
||||
if(inventory.lasers) {
|
||||
SetEntProp(weapon, Prop_Send, "m_upgradeBitVec", 4);
|
||||
}
|
||||
}
|
||||
|
||||
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
||||
pInv.Remove(buffer);
|
||||
}
|
||||
|
||||
bool DoesInventoryDiffer(int client) {
|
||||
static char buffer[32];
|
||||
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
||||
PlayerInventory inventory;
|
||||
if(!pInv.GetArray(buffer, inventory, sizeof(inventory)) || inventory.timestamp == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WeaponId currentPrimary = IdentifyWeapon(GetPlayerWeaponSlot(client, 0));
|
||||
WeaponId currentSecondary = IdentifyWeapon(GetPlayerWeaponSlot(client, 1));
|
||||
WeaponId storedPrimary = inventory.itemID[0];
|
||||
WeaponId storedSecondary = inventory.itemID[1];
|
||||
|
||||
return currentPrimary != storedPrimary || currentSecondary != storedSecondary;
|
||||
}
|
||||
|
||||
stock int FindFirstSurvivor() {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
|
@ -1034,6 +1282,16 @@ stock int GetSurvivorsCount() {
|
|||
return count;
|
||||
}
|
||||
|
||||
stock int GetActiveCount() {
|
||||
int count;
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && playerData[i].state == State_Active) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
stock int GetRealSurvivorsCount() {
|
||||
#if defined DEBUG_FORCE_PLAYERS
|
||||
return DEBUG_FORCE_PLAYERS;
|
||||
|
@ -1167,19 +1425,33 @@ int FindCabinetIndex(int cabinetId) {
|
|||
}
|
||||
|
||||
void GetPlayerInventory(int client) {
|
||||
char item[16];
|
||||
GetClientWeaponName(client, 2, item, sizeof(item));
|
||||
items[client].throwable[0] = CharToUpper(item[7]);
|
||||
items[client].throwable[1] = '\0';
|
||||
static char item[16];
|
||||
if(GetClientWeaponName(client, 2, item, sizeof(item))) {
|
||||
items[client].throwable[0] = CharToUpper(item[7]);
|
||||
if(items[client].throwable[0] == 'V') {
|
||||
items[client].throwable[0] = 'B'; //Replace [V]omitjar with [B]ile
|
||||
}
|
||||
items[client].throwable[1] = '\0';
|
||||
} else {
|
||||
items[client].throwable[0] = '\0';
|
||||
}
|
||||
|
||||
if(GetClientWeaponName(client, 3, item, sizeof(item))) {
|
||||
items[client].usable[0] = CharToUpper(item[7]);
|
||||
items[client].usable[1] = '\0';
|
||||
if(items[client].throwable[0] == 'F') {
|
||||
items[client].throwable[0] = '+'; //Replace [V]omitjar with [B]ile
|
||||
}
|
||||
} else {
|
||||
items[client].usable[0] = '-';
|
||||
items[client].usable[1] = '\0';
|
||||
}
|
||||
|
||||
if(GetClientWeaponName(client, 4, item, sizeof(item))) {
|
||||
items[client].consumable[0] = CharToUpper(item[7]);
|
||||
items[client].consumable[1] = '\0';
|
||||
} else {
|
||||
items[client].consumable[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <ftt>
|
||||
#include <multicolors>
|
||||
#include <activitymonitor>
|
||||
#include <l4d_anti_rush>
|
||||
|
||||
|
||||
public Plugin myinfo =
|
||||
|
@ -28,8 +29,6 @@ public Plugin myinfo =
|
|||
url = ""
|
||||
};
|
||||
|
||||
//TODO: Make bots target player. Possibly automatic . See https://i.jackz.me/2021/05/NVIDIA_Share_2021-05-05_19-36-51.png
|
||||
//TODO: Friendly trolling VS punishment trolling
|
||||
//TODO: Trolls: Force take pills, Survivor Bot Magnet
|
||||
|
||||
|
||||
|
@ -68,11 +67,11 @@ public void OnPluginStart() {
|
|||
hShoveFailChance = CreateConVar("sm_ftt_shove_fail_chance", "0.65", "The % chance that a shove fails", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hBadThrowHitSelf = CreateConVar("sm_ftt_badthrow_fail_chance", "1", "The % chance that on a throw, they will instead hit themselves. 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hBotReverseFFDefend = CreateConVar("sm_ftt_bot_defend", "1", "Should bots defend themselves?\n0 = OFF\n1 = Will retaliate against non-admins\n2 = Anyone", FCVAR_NONE, true, 0.0, true, 2.0);
|
||||
hBotDefendChance = CreateConVar("sm_ftt_bot_defend_chance", "0.75", "% Chance bots will defend themselves.", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
|
||||
hSbFriendlyFire = FindConVar("sb_friendlyfire");
|
||||
|
||||
if(hBotReverseFFDefend.IntValue > 0) hSbFriendlyFire.BoolValue = true;
|
||||
|
||||
|
||||
hBotReverseFFDefend.AddChangeHook(Change_BotDefend);
|
||||
|
||||
RegAdminCmd("sm_ftl", Command_ListTheTrolls, ADMFLAG_KICK, "Lists all the trolls currently ingame.");
|
||||
|
@ -84,7 +83,7 @@ public void OnPluginStart() {
|
|||
RegAdminCmd("sm_mark", Command_MarkPendingTroll, ADMFLAG_KICK, "Marks a player as to be banned on disconnect");
|
||||
RegAdminCmd("sm_ftp", Command_FeedTheCrescendoTroll, ADMFLAG_KICK, "Applies a manual punish on the last crescendo activator");
|
||||
RegAdminCmd("sm_ftc", Command_ApplyComboTrolls, ADMFLAG_KICK, "Applies predefined combinations of trolls");
|
||||
RegAdminCmd("sm_witch_attack", Command_WitchAttack, ADMFLAG_CHEATS, "Makes all witches target a player");
|
||||
RegAdminCmd("sm_witch_attack", Command_WitchAttack, ADMFLAG_CHEATS, "Makes all witches target a player");
|
||||
RegAdminCmd("sm_insta", Command_InstaSpecial, ADMFLAG_KICK, "Spawns a special that targets them, close to them.");
|
||||
RegAdminCmd("sm_instaface", Command_InstaSpecialFace, ADMFLAG_KICK, "Spawns a special that targets them, right in their face.");
|
||||
RegAdminCmd("sm_inface", Command_InstaSpecialFace, ADMFLAG_KICK, "Spawns a special that targets them, right in their face.");
|
||||
|
@ -118,6 +117,7 @@ public void Change_ThrowInterval(ConVar convar, const char[] oldValue, const cha
|
|||
}
|
||||
}
|
||||
|
||||
// Turn on bot FF if bot defend enabled
|
||||
public void Change_BotDefend(ConVar convar, const char[] oldValue, const char[] newValue) {
|
||||
hSbFriendlyFire.IntValue = convar.IntValue != 0;
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ bool IsPlayerFarDistance(int client, float distance) {
|
|||
}
|
||||
}
|
||||
float difference = highestFlow - secondHighestFlow;
|
||||
PrintToConsoleAll("Flow Check | Player=%N Flow=%f Delta=%f", farthestClient, highestFlow, difference);
|
||||
PrintToConsoleAll("Flow Check | Player1=%N Flow1=%f Delta=%f", farthestClient, highestFlow, difference);
|
||||
PrintToConsoleAll("Flow Check | Player2=%N Flow2=%f", secondClient, secondHighestFlow);
|
||||
return client == farthestClient && difference > distance;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
|
|||
float dist = GetVectorDistance(clientPos, tankPos);
|
||||
// Only add targets who are far enough away from tank
|
||||
if(dist > 3000.0) {
|
||||
PrintToConsoleAll("Adding player %N to possible victim list. Dist=%f, Dmg=%d", i, dist, totalTankDamage[i]);
|
||||
PrintToConsoleAll("[TankPriority/debug] Adding player %N to possible victim list. Dist=%f Dmg=%d", i, dist, totalTankDamage[i]);
|
||||
int index = clients.Push(i);
|
||||
clients.Set(index, dist, 1);
|
||||
}
|
||||
|
@ -74,8 +74,9 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
|
|||
//TODO: Possibly clear totalTankDamage
|
||||
return Plugin_Changed;
|
||||
}
|
||||
|
||||
if(tankChosenVictim[attacker] > 0) {
|
||||
if(IsClientConnected(tankChosenVictim[attacker]) && IsClientInGame(tankChosenVictim[attacker])) {
|
||||
if(IsClientConnected(tankChosenVictim[attacker]) && IsClientInGame(tankChosenVictim[attacker]) && IsPlayerAlive(tankChosenVictim[attacker]) && !IsPlayerIncapacitated(tankChosenVictim[attacker])) {
|
||||
curTarget = tankChosenVictim[attacker];
|
||||
return Plugin_Changed;
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue