diff --git a/plugins/L4D2FFKickProtection.smx b/plugins/L4D2FFKickProtection.smx index 7520510..a9a86f0 100644 Binary files a/plugins/L4D2FFKickProtection.smx and b/plugins/L4D2FFKickProtection.smx differ diff --git a/plugins/l4d2-manual-director.smx b/plugins/l4d2-manual-director.smx index d17060e..c8c1351 100644 Binary files a/plugins/l4d2-manual-director.smx and b/plugins/l4d2-manual-director.smx differ diff --git a/plugins/l4d2_TKStopper.smx b/plugins/l4d2_TKStopper.smx index 92363ef..a24f96d 100644 Binary files a/plugins/l4d2_TKStopper.smx and b/plugins/l4d2_TKStopper.smx differ diff --git a/plugins/l4d2_feedthetrolls.smx b/plugins/l4d2_feedthetrolls.smx index eb5ba6e..122d54b 100644 Binary files a/plugins/l4d2_feedthetrolls.smx and b/plugins/l4d2_feedthetrolls.smx differ diff --git a/plugins/l4d_reservetheserver.smx b/plugins/l4d_reservetheserver.smx index 5f58a00..95ca60f 100644 Binary files a/plugins/l4d_reservetheserver.smx and b/plugins/l4d_reservetheserver.smx differ diff --git a/scripting/activitymonitor.sp b/scripting/activitymonitor.sp new file mode 100644 index 0000000..6cd7e1b --- /dev/null +++ b/scripting/activitymonitor.sp @@ -0,0 +1,261 @@ +#pragma semicolon 1 +#pragma newdecls required + +//#define DEBUG + +#define PLUGIN_VERSION "1.0" + +#include +#include +//#include + +public Plugin myinfo = +{ + name = "Admin Activity Log", + author = "jackzmc", + description = "", + version = PLUGIN_VERSION, + url = "" +}; + +enum struct Log { + char name[32]; + char clientSteamID[32]; + char targetSteamID[32]; + char message[256]; + int timestamp; +} +// Plugin data +static ArrayList logs; +static Database g_db; +static char serverID[64]; +static Handle pushTimer; +static ConVar hLogCvarChanges; +static char lastMap[64]; + +//Plugin related +static bool lateLoaded; +static EngineVersion g_Game; + +//L4d2 Specific +static char L4D2_ZDifficulty[16]; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + CreateNative("ActivityMonitor_AddLog", Native_AddLog); + lateLoaded = late; + return APLRes_Success; +} + +public void OnPluginStart() { + g_Game = GetEngineVersion(); + logs = new ArrayList(sizeof(Log)); + + if(!SQL_CheckConfig("activitymonitor")) { + SetFailState("No database entry for 'activitymonitor'; no database to connect to."); + } else if(!ConnectDB()) { + SetFailState("Failed to connect to database."); + } + + 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"); + hServerID.GetString(serverID, sizeof(serverID)); + hServerID.AddChangeHook(CVAR_ServerIDChanged); + + HookEvent("player_first_spawn", Event_Connection); + HookEvent("player_disconnect", Event_Connection); + if(g_Game == Engine_Left4Dead2 || g_Game == Engine_Left4Dead) { + HookEvent("player_incapacitated", Event_L4D2_Incapped); + HookEvent("player_death", Event_L4D2_Death); + ConVar zDifficulty = FindConVar("z_difficulty"); + zDifficulty.GetString(L4D2_ZDifficulty, sizeof(L4D2_ZDifficulty)); + CVAR_DifficultyChanged(zDifficulty, "", L4D2_ZDifficulty); + zDifficulty.AddChangeHook(CVAR_DifficultyChanged); + } + + if(!lateLoaded) { + AddLog("INFO", "", "", "Server has started up"); + } + + pushTimer = CreateTimer(60.0, Timer_PushLogs, _, TIMER_REPEAT); + + // AutoExecConfig(true, "activitymonitor"); +} + +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); + else + Format(curMap, sizeof(curMap), "Map changed to %s", curMap); + AddLog("INFO", "", "", curMap); + } + TriggerTimer(pushTimer, true); +} + +public void CVAR_ServerIDChanged(ConVar convar, const char[] oldValue, const char[] newValue) { + strcopy(serverID, sizeof(serverID), newValue); +} + +public void CVAR_DifficultyChanged(ConVar convar, const char[] oldValue, const char[] newValue) { + if(StrEqual(newValue, "Hard", false)) strcopy(L4D2_ZDifficulty, sizeof(L4D2_ZDifficulty), "Advanced"); + else if(StrEqual(newValue, "Impossible", false)) strcopy(L4D2_ZDifficulty, sizeof(L4D2_ZDifficulty), "Expert"); + else { + strcopy(L4D2_ZDifficulty, sizeof(L4D2_ZDifficulty), newValue); + // CVAR value could be lowercase 'normal', convert to 'Normal' + L4D2_ZDifficulty[0] = CharToUpper(L4D2_ZDifficulty[0]); + } +} + +bool ConnectDB() { + char error[255]; + g_db = SQL_Connect("activitymonitor", true, error, sizeof(error)); + if (g_db == null) { + LogError("Database error %s", error); + delete g_db; + return false; + } else { + PrintToServer("[ACTM] Connected to database \"activitymonitor\""); + SQL_LockDatabase(g_db); + SQL_FastQuery(g_db, "SET NAMES \"UTF8mb4\""); + SQL_UnlockDatabase(g_db); + g_db.SetCharset("utf8mb4"); + return true; + } +} + +public Action Timer_PushLogs(Handle h) { + static char query[1024]; + static Log log; + int length = logs.Length; + if(length > 0) { + for(int i = 0; i < length; i++) { + logs.GetArray(i, log, sizeof(log)); + g_db.Format(query, sizeof(query), "INSERT INTO `activity_log` (`timestamp`, `server`, `type`, `client`, `target`, `message`) VALUES (%d, NULLIF('%s', ''), '%s', NULLIF('%s', ''), NULLIF('%s', ''), NULLIF('%s', ''))", + log.timestamp, + serverID, + log.name, + log.clientSteamID, + log.targetSteamID, + log.message + ); + g_db.Query(SQL_Callback, query, _, DBPrio_Low); + } + // Incase a new item was added while pushing, don't clear it: + logs.Resize(logs.Length - length); + } +} +public void SQL_Callback(Database db, DBResultSet results, const char[] error, any data) { + if(results == null) PrintToServer("[ACTM] Log error: %s", error); +} + +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, "", ""); + } + } +} +public Action OnLogAction(Handle source, Identity identity, int client, int target, const char[] message) { + // Ignore cvar changed msgs (from server.cfg) + if(client == 0 && !hLogCvarChanges.BoolValue && strcmp(message[31], "changed cvar") >= 0) return Plugin_Continue; + + static char clientName[32], targetName[32]; + if(client == 0) clientName = "Server"; + else if(client > 0) GetClientAuthId(client, AuthId_Steam2, clientName, sizeof(clientName)); + else clientName[0] = '\0'; + + if(target > 0 && !IsFakeClient(target)) GetClientAuthId(target, AuthId_Steam2, targetName, sizeof(targetName)); + else targetName[0] = '\0'; + + AddLog("ACTION", clientName, targetName, message); + return Plugin_Continue; +} + +public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) { + if(client > 0 && !IsFakeClient(client)) { + static char clientName[32]; + GetClientAuthId(client, AuthId_Steam2, clientName, sizeof(clientName)); + AddLog("CHAT", clientName, "", sArgs); + } +} + +public void Event_L4D2_Death(Event event, const char[] name, bool dontBroadcast) { + int victim = GetClientOfUserId(event.GetInt("userid")); + int attacker = GetClientOfUserId(event.GetInt("attacker")); + if(victim > 0 && GetClientTeam(victim) == 2) { //victim is a survivor + static char victimName[32], attackerName[32]; + + if(IsFakeClient(victim)) GetClientName(victim, victimName, sizeof(victimName)); + else GetClientAuthId(victim, AuthId_Steam2, victimName, sizeof(victimName)); + + if(attacker > 0) { + if(IsFakeClient(attacker)) GetClientName(attacker, attackerName, sizeof(attackerName)); + else GetClientAuthId(attacker, AuthId_Steam2, attackerName, sizeof(attackerName)); + + AddLogCustom("STATE", attackerName, victimName, "\"%L\" killed \"%L\"", attacker, victim); + } else { + AddLogCustom("STATE", "", victimName, "\"%L\" died", victim); + } + } +} +//Jackz was incapped by Jockey +//Jackz was incapped (by world/zombie) +//Jackz was incapped by Disgruntled Pea +//Ellis was incapped [by ...] +public void Event_L4D2_Incapped(Event event, const char[] name, bool dontBroadcast) { + int victim = GetClientOfUserId(event.GetInt("userid")); + int attacker = GetClientOfUserId(event.GetInt("attacker")); + if(victim > 0 && GetClientTeam(victim) == 2) { + static char victimName[32], attackerName[32]; + if(IsFakeClient(victim)) GetClientName(victim, victimName, sizeof(victimName)); + else GetClientAuthId(victim, AuthId_Steam2, victimName, sizeof(victimName)); + + if(attacker > 0) { + if(IsFakeClient(attacker)) GetClientName(attacker, attackerName, sizeof(attackerName)); + else GetClientAuthId(attacker, AuthId_Steam2, attackerName, sizeof(attackerName)); + + AddLogCustom("STATE", attackerName, victimName, "\"%L\" incapped \"%L\"", attacker, victim); + } else { + AddLogCustom("STATE", "", victimName, "\"%L\" was incapped", victim); + } + } +} + +void AddLog(const char[] type, const char[] clientName, const char[] targetName, const char[] message) { + if(StrEqual(clientName, "Bot")) return; + Log log; + strcopy(log.name, sizeof(log.name), type); + strcopy(log.clientSteamID, sizeof(log.clientSteamID), clientName); + strcopy(log.targetSteamID, sizeof(log.targetSteamID), targetName); + strcopy(log.message, sizeof(log.message), message); + log.timestamp = GetTime(); + logs.PushArray(log); +} + +void AddLogCustom(const char[] type, const char[] clientName, const char[] targetName, const char[] format, any ...) { + static char message[254]; + if(StrEqual(clientName, "Bot")) return; + + VFormat(message, sizeof(message), format, 5); + AddLog(type, clientName, targetName, message); +} + +public any Native_AddLog(Handle plugin, int numParams) { + char type[32], clientName[32], targetName[32], message[256]; + if(GetNativeString(1, type, sizeof(type)) != SP_ERROR_NONE) return false; + if(GetNativeString(2, clientName, sizeof(clientName)) != SP_ERROR_NONE) return false; + if(GetNativeString(3, targetName, sizeof(targetName)) != SP_ERROR_NONE) return false; + if(GetNativeString(4, message, sizeof(message)) != SP_ERROR_NONE) return false; + AddLog(type, clientName, targetName, message); + return true; +} \ No newline at end of file diff --git a/scripting/include/activitymonitor.inc b/scripting/include/activitymonitor.inc new file mode 100644 index 0000000..c58c76d --- /dev/null +++ b/scripting/include/activitymonitor.inc @@ -0,0 +1,6 @@ +#if defined _activitymonitor_included_ + #endinput +#endif +#define _activitymonitor_included_ + +native bool AddLog(const char[] type, const char[] clientName, const char[] targetName, const char[] message); \ No newline at end of file diff --git a/scripting/include/feedthetrolls/base.inc b/scripting/include/feedthetrolls/base.inc index 6aa8277..b99d26c 100644 --- a/scripting/include/feedthetrolls/base.inc +++ b/scripting/include/feedthetrolls/base.inc @@ -1,5 +1,6 @@ #define MAX_TROLL_NAME_LENGTH 32 +#define MAX_TROLL_FLAG_LENGTH 32 #define MAX_TROLLS 28 //#define DEBUG 1 @@ -9,47 +10,103 @@ enum trollModifier { TrollMod_Constant = 2 } - -enum struct Troll { - char name[MAX_TROLL_NAME_LENGTH]; - char description[128]; - - bool runsOnce; - int flags; - int categoryID; - - char _id[MAX_TROLL_NAME_LENGTH]; - - void GetID(char[] name) { - strcopy(name, MAX_TROLL_NAME_LENGTH, this.name); - ReplaceString(name, MAX_TROLL_NAME_LENGTH, " ", "", false); - } - - bool HasMod(trollModifier mod) { - return ((this.flags >> (view_as(mod)) - 1) & 1) == 1; - } - - // Gets the default modifier to use - trollModifier GetDefaultMod() { - // If the flags is equal to the 2^n flag, then it must be the only flag: - if(this.flags == view_as(TrollMod_Instant)) return TrollMod_Instant; - else if(this.flags == view_as(TrollMod_Constant)) return TrollMod_Constant; - else return TrollMod_Invalid; - } - - void Activate(int victim, int activator, trollModifier modifier, bool silent = false) { - ApplyTroll(victim, this._id, activator, modifier, silent); - } +enum trollFlag { + Flag_1 = 1, + Flag_2 = 4, + Flag_3 = 8, + Flag_4 = 16, + Flag_5 = 32 } -Troll Trolls[MAX_TROLLS+1]; int ActiveTrolls[MAXPLAYERS+1]; StringMap trollKV; char trollIds[MAX_TROLLS+1][MAX_TROLL_NAME_LENGTH]; +enum struct Troll { + int id; + + char name[MAX_TROLL_NAME_LENGTH]; + char description[128]; + + int mods; + int enabledFlags; + int categoryID; + + int activeFlagClients[MAXPLAYERS+1]; + + ArrayList flagNames; + + bool HasMod(trollModifier mod) { + return ((this.mods >> (view_as(mod)) - 1) & 1) == 1; + } + + bool GetFlagName(trollFlag flag, char[] buffer, int maxlength) { + if(this.flagNames == null) return false; + this.flagNames.GetString(view_as(flag), buffer, maxlength); + return true; + } + + int AddFlag(const char[] name) { + if(this.flagNames == null) this.flagNames = new ArrayList(MAX_TROLL_FLAG_LENGTH); + int index = this.flagNames.PushString(name); + index = RoundFloat(Pow(2.0, float(index))); + this.enabledFlags |= index; + return index; + } + + bool HasFlags() { + return this.enabledFlags > 0 && this.flagNames != null; + } + + // Gets the default modifier to use + trollModifier GetDefaultMod() { + // If the flags is equal to the 2^n flag, then it must be the only flag: + if(this.mods == view_as(TrollMod_Instant)) return TrollMod_Instant; + else if(this.mods == view_as(TrollMod_Constant)) return TrollMod_Constant; + else return TrollMod_Invalid; + } + + bool IsFlagActive(int client, trollFlag flag) { + if(ActiveTrolls[client] > 0 && IsTrollActive(client, this.name) && this.enabledFlags > 0 && this.activeFlagClients[client] >= 0) { + return ((this.activeFlagClients[client] >> view_as(flag)) & 1) == 1; + } + return false; + } + + bool IsFlagNameActive(int client, const char[] flagName) { + static char buffer[MAX_TROLL_FLAG_LENGTH]; + for(int i = 0; i < this.flagNames.Length; i++) { + this.flagNames.GetString(i, buffer, sizeof(buffer)); + if(StrEqual(buffer, flagName, false)) return this.IsFlagActive(client, view_as(i)); + } + return false; + } + + void Activate(int client, int activator, trollModifier modifier = TrollMod_Invalid, int flags = 0) { + if(modifier == TrollMod_Invalid) modifier = this.GetDefaultMod(); + ApplyTroll(client, this.name, activator, modifier, false, flags); + } + + void Toggle(int client, int flags) { + ToggleTroll(client, this.name, flags); + } + + void Enable(int client, int flags) { + EnableTroll(client, this.name, flags); + } + + void Disable(int client) { + DisableTroll(client, this.name); + } +} + +Troll Trolls[MAX_TROLLS+1]; + void ResetClient(int victim, bool wipe = true) { - if(wipe) ActiveTrolls[victim] = 0; + if(wipe) { + ActiveTrolls[victim] = 0; + } SetEntityGravity(victim, 1.0); SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0); SDKUnhook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); @@ -57,24 +114,24 @@ void ResetClient(int victim, bool wipe = true) { if(wpn > -1) SDKUnhook(wpn, SDKHook_Reload, Event_WeaponReload); } + ArrayList categories; -static int categoryID = 0; -int SetupTroll(const char[] name, const char description[128], int flags) { - if(flags == 0) { +static int categoryID = -1; + +int SetupTroll(const char[] name, const char description[128], int mods) { + if(mods == 0) { ThrowError("Troll \"%s\" has no flags defined.", name); return -1; } static int i = 0; + Trolls[i].id = i; strcopy(Trolls[i].name, MAX_TROLL_NAME_LENGTH, name); strcopy(Trolls[i].description, 128, description); Trolls[i].categoryID = categoryID; - Trolls[i].flags = flags; - static char key[MAX_TROLL_NAME_LENGTH]; - strcopy(key, MAX_TROLL_NAME_LENGTH, name); - ReplaceString(key, MAX_TROLL_NAME_LENGTH, " ", "", false); - strcopy(trollIds[i], MAX_TROLL_NAME_LENGTH, key); - strcopy(Trolls[i]._id, MAX_TROLL_NAME_LENGTH, key); - trollKV.SetValue(key, i); + Trolls[i].mods = mods; + + strcopy(trollIds[i], MAX_TROLL_NAME_LENGTH, name); + trollKV.SetValue(name, i); return i++; } // Gets the Troll enum struct via name @@ -87,6 +144,13 @@ int GetTroll(const char[] name, Troll troll) { } return -1; } +int GetTrollIndex(const char[] name) { + static int i = 0; + if(trollKV.GetValue(name, i)) { + return i; + } + return -1; +} // Gets the Troll enum struct via key index // Returns index of troll enum int GetTrollByKeyIndex(int index, Troll troll) { @@ -102,70 +166,86 @@ bool GetTrollID(int index, char name[MAX_TROLL_NAME_LENGTH]) { return true; } -void ToggleTroll(int client, const char[] name) { +void ToggleTroll(int client, const char[] name, int flags = 0) { static Troll troll; int index = GetTroll(name, troll); ActiveTrolls[client] ^= 1 << view_as(index); + troll.activeFlagClients[client] = flags; } - -void ApplyTroll(int victim, const char[] name, int activator, trollModifier modifier, bool silent = false) { +void ApplyTroll(int victim, const char[] name, int activator, trollModifier modifier, bool silent = false, int flags = 0) { static Troll troll; int trollIndex = GetTroll(name, troll); if(trollIndex == -1) { PrintToServer("[FTT] %N attempted to apply unknown troll: %s", activator, name); return; } + if(GetClientTeam(victim) == 1) { //Victim is spectating, find its bot victim = FindIdlePlayerBot(victim); } - if(!ApplyAffect(victim, name, activator, modifier)) { + if(!ApplyAffect(victim, troll, activator, modifier, flags)) { return; } if(!silent) { - if(IsTrollActive(victim, name)) { - ShowActivity(activator, "deactivated troll \"%s\" on %N. ", troll.name, victim); + if(IsTrollActive(victim, troll.name)) { + ShowActivityEx(activator, "[FTT] ", "deactivated troll \"%s\" on %N. ", troll.name, victim); + LogAction(activator, victim, "\"%L\" deactivated troll \"%s\" on \"%L\"", activator, troll.name, victim); } else { - if(modifier == TrollMod_Constant) - ShowActivity(activator, "activated constant troll \"%s\" for %N. ", troll.name, victim); - else - ShowActivity(activator, "activated troll \"%s\" for %N. ", troll.name, victim); + if(modifier == TrollMod_Constant) { + ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" for %N. ", troll.name, victim); + } else { + ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" for %N. ", troll.name, victim); + } + LogAction(activator, victim, "\"%L\" activated troll \"%s\" for \"%L\"", activator, troll.name, victim); } } if(modifier == TrollMod_Constant) { ActiveTrolls[victim] ^= 1 << trollIndex; } + troll.activeFlagClients[victim] = flags; } -bool ApplyAffect(int victim, const char[] name, int activator, trollModifier modifier) { - if(StrEqual(name, "ResetTroll")) { - ShowActivity(activator, "reset troll effects for %N. ", victim); +bool ApplyAffect(int victim, Troll troll, int activator, trollModifier modifier, int flags) { + bool isActive = IsTrollActiveByRawID(victim, troll.id); + if(StrEqual(troll.name, "Reset User")) { + LogAction(activator, victim, "\"%L\" reset all troll effects for \"%L\"", activator, victim); + ShowActivityEx(activator, "[FTT] ", "reset troll effects for %N. ", victim); ActiveTrolls[victim] = 0; + troll.activeFlagClients[victim] = 0; + SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0); + SetEntityGravity(victim, 1.0); return false; - } else if(StrEqual(name, "SlowSpeed")) - SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 0.8); - else if(StrEqual(name, "HigherGravity")) - SetEntityGravity(victim, 1.3); - else if(StrEqual(name, "HalfPrimaryAmmo")) { + } else if(StrEqual(troll.name, "Slow Speed")) + if(isActive) + SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0); + else + SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 0.8); + else if(StrEqual(troll.name, "Higher Gravity")) + if(isActive) + SetEntityGravity(victim, 1.0); + else + SetEntityGravity(victim, 1.3); + else if(StrEqual(troll.name, "Half Primary Ammo")) { int current = GetPrimaryReserveAmmo(victim); SetPrimaryReserveAmmo(victim, current / 2); - } else if(StrEqual(name, "UziRules")) { - DisableTroll(victim, "NoPickup"); - DisableTroll(victim, "PrimaryDisable"); + } else if(StrEqual(troll.name, "UziRules")) { + DisableTroll(victim, "No Pickup"); + DisableTroll(victim, "Primary Disable"); SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); - } else if(StrEqual(name, "PrimaryDisable")) { + } else if(StrEqual(troll.name, "Primary Disable")) { DisableTroll(victim, "UziRules"); - DisableTroll(victim, "NoPickup"); + DisableTroll(victim, "No Pickup"); SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); - } else if(StrEqual(name, "NoPickup")) { + } else if(StrEqual(troll.name, "No Pickup")) { DisableTroll(victim, "UziRules"); - DisableTroll(victim, "PrimaryDisable"); + DisableTroll(victim, "Primary Disable"); SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); - } else if(StrEqual(name, "CameTooEarly")) { + } else if(StrEqual(troll.name, "CameTooEarly")) { ReplyToCommand(activator, "This troll mode is not implemented."); - } else if(StrEqual(name, "KillMeSoftly")) { + } else if(StrEqual(troll.name, "KillMeSoftly")) { static char wpn[32]; GetClientWeaponName(victim, 4, wpn, sizeof(wpn)); if(StrEqual(wpn, "weapon_adrenaline") || StrEqual(wpn, "weapon_pain_pills")) { @@ -177,44 +257,43 @@ bool ApplyAffect(int victim, const char[] name, int activator, trollModifier mod } //TODO: Implement TrollMod_Constant return false; - } else if(StrEqual(name, "ThrowItAll")) { + } else if(StrEqual(troll.name, "Throw It All")) { if(modifier == TrollMod_Instant) ThrowAllItems(victim); if(hThrowTimer == INVALID_HANDLE && modifier == TrollMod_Constant) { hThrowTimer = CreateTimer(hThrowItemInterval.FloatValue, Timer_ThrowTimer, _, TIMER_REPEAT); } - } else if(StrEqual(name, "Swarm")) { + } else if(StrEqual(troll.name, "Swarm")) { if(modifier == TrollMod_Instant) { L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", victim, 15000); - }else if(modifier == TrollMod_Constant) { - - }else{ - ReplyToCommand(activator, "Invalid modifier for mode."); - return false; } - } else if(StrEqual(name, "GunJam")) { + return true; + } else if(StrEqual(troll.name, "Gun Jam")) { int wpn = GetClientWeaponEntIndex(victim, 0); if(wpn > -1) SDKHook(wpn, SDKHook_Reload, Event_WeaponReload); - else + else { ReplyToCommand(activator, "Victim does not have a primary weapon."); - } else if(StrEqual(name, "VomitPlayer")) + return false; + } + } else if(StrEqual(troll.name, "Vomit Player")) L4D_CTerrorPlayer_OnVomitedUpon(victim, victim); - else if(StrEqual(name, "InfaceSpecial")) { + else if(StrEqual(troll.name, "Inface Special")) { FakeClientCommand(victim, "sm_inface"); return false; - } else if(StrEqual(name, "InstaSpecial")) { + } else if(StrEqual(troll.name, "Insta Special")) { FakeClientCommand(victim, "sm_insta"); return false; } else { #if defined DEBUG - PrintToServer("[FTT] Possibly invalid troll, no action: %s", name); + PrintToServer("[FTT] Possibly invalid troll, no action: %s", troll.name); ReplyToCommand(activator, "[FTT/Debug] If nothing occurs, this troll possibly was not implemented correctly. "); #endif } return true; } + bool IsTrollActive(int client, const char[] troll) { if(ActiveTrolls[client] == 0) return false; static int i = 0; @@ -225,9 +304,15 @@ bool IsTrollActive(int client, const char[] troll) { return false; //errors instead but compiler no like } -void EnableTroll(int client, const char[] troll) { +bool IsTrollActiveByRawID(int client, int id) { + if(ActiveTrolls[client] == 0) return false; + return ((ActiveTrolls[client] >> id) & 1) == 1; +} + + +void EnableTroll(int client, const char[] troll, int flags = 0) { if(!IsTrollActive(client, troll)) { - ToggleTroll(client, troll); + ToggleTroll(client, troll, flags); } } @@ -243,47 +328,3 @@ void SetCategory(const char[] newCat) { void GetCategory(int category, char[] buffer, int size) { categories.GetString(category, buffer, size); } - -void SetupTrolls() { - trollKV = new StringMap(); - categories = new ArrayList(ByteCountToCells(16)); - SetupTroll("Reset Troll", "Resets the user, removes all troll effects", TrollMod_Instant); - - SetCategory("Magnet / Horde"); - SetupTroll("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant); - SetupTroll("Tank Magnet", "Attracts ALL tanks to any alive target with this troll enabled", TrollMod_Constant); - SetupTroll("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant); - SetupTroll("Swarm", "Swarms a player with zombies. Requires swarm plugin", TrollMod_Instant | TrollMod_Constant); - SetupTroll("Vomit Player", "Shortcut to sm_vomitplayer. vomits the player.", TrollMod_Instant); - SetupTroll("Inface Special", "Shortcut to sm_inface", TrollMod_Instant); - SetupTroll("Insta Special", "Shortcut to sm_insta", TrollMod_Instant); - - SetCategory("Items"); - SetupTroll("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant); - SetupTroll("Bad Throw", "Player drops throwables on throw, and biles/molotovs themselves", TrollMod_Constant); - SetupTroll("No Pickup", "Prevents a player from picking up ANY (new) item. Use ThrowItAll to make them drop", TrollMod_Constant); - SetupTroll("UziRules", "Picking up a weapon gives them a UZI instead", TrollMod_Constant); - SetupTroll("Primary Disable", "Player cannot pickup any weapons, only melee/pistols", TrollMod_Constant); - SetupTroll("Half Primary Ammo", "Cuts their primary reserve ammo in half", TrollMod_Instant); - - SetCategory("Chat"); - SetupTroll("iCantSpellNoMore", "Chat messages letter will randomly changed with wrong letters", TrollMod_Instant); - SetupTroll("No Profanity", "Replaces some words with random phrases", TrollMod_Constant); - SetupTroll("Vocalize Gag", "Prevents player from sending any vocalizations (even automatic)", TrollMod_Constant); - SetupTroll("Honk", "Honk", TrollMod_Constant); - SetupTroll("Meow", "Makes the player meow", TrollMod_Constant); - - SetCategory("Health"); - SetupTroll("Damage Boost", "Makes a player take more damage than normal", TrollMod_Constant); - SetupTroll("Temp Health Quick Drain", "Makes a player's temporarily health drain very quickly", TrollMod_Constant); - SetupTroll("Slow Drain", "Will make the player slowly lose health over time", TrollMod_Constant); - SetupTroll("KillMeSoftly", "Make player eat or waste pills whenever possible", TrollMod_Instant | TrollMod_Constant); - - SetCategory("Misc"); - SetupTroll("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant); - SetupTroll("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant); - SetupTroll("Higher Gravity", "Sets player gravity to 1.3x of normal gravity", TrollMod_Constant); - SetupTroll("No Shove", "Prevents a player from shoving", TrollMod_Constant); - SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant); - //INFO: UP MAX_TROLLS when adding new trolls! -} \ No newline at end of file diff --git a/scripting/include/feedthetrolls/commands.inc b/scripting/include/feedthetrolls/commands.inc index 2b050b7..7d77b6a 100644 --- a/scripting/include/feedthetrolls/commands.inc +++ b/scripting/include/feedthetrolls/commands.inc @@ -41,15 +41,22 @@ public Action Command_InstaSpecial(int client, int args) { ReplyToCommand(client, "Unknown special \"%s\"", arg2); return Plugin_Handled; } + int successes = 0; for (int i = 0; i < target_count; i++) { int target = target_list[i]; if(GetClientTeam(target) == 2) { - SpawnSpecialNear(target, specialType); + if(SpawnSpecialNear(target, specialType)) { + LogAction(client, target, "\"%L\" spawned Insta-%s™ nearby \"%L\"", client, arg2, target); + successes++; + } else { + ReplyToCommand(client, "[FTT] Could not spawn %s near %s", arg2, target_name); + } }else{ ReplyToTargetError(client, target_count); } } - ShowActivity(client, "spawned Insta-%s™ near %s", arg2, target_name); + if(successes > 0) + ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ near %s", arg2, target_name); } @@ -99,15 +106,22 @@ public Action Command_InstaSpecialFace(int client, int args) { ReplyToCommand(client, "Unknown special \"%s\"", arg2); return Plugin_Handled; } + int successes = 0; for (int i = 0; i < target_count; i++) { int target = target_list[i]; if(GetClientTeam(target) == 2) { - SpawnSpecialInFace(target, specialType); + if(SpawnSpecialInFace(target, specialType)) { + LogAction(client, target, "\"%L\" spawned Insta-%s™ at player \"%L\"", client, arg2, target); + successes++; + } else { + ReplyToCommand(client, "[FTT] Could not spawn %s on %s", arg2, target_name); + } }else{ ReplyToTargetError(client, target_count); } } - ShowActivity(client, "spawned Insta-%s™ on %s", arg2, target_name); + if(successes > 0) + ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ on %s", arg2, target_name); } return Plugin_Handled; } @@ -143,7 +157,8 @@ public Action Command_WitchAttack(int client, int args) { while ((witch = FindEntityByClassname(witch, "witch")) != INVALID_ENT_REFERENCE) { SetWitchTarget(witch, target); - ShowActivity(client, "all witches target %s", target_name); + ShowActivityEx(client, "[FTT] ", "set all witches to target %s", target_name); + LogAction(client, target, "\"%L\" set all witches to attack \"%L\"", client, target); } }else{ ReplyToTargetError(client, target_count); @@ -157,7 +172,8 @@ public Action Command_FeedTheCrescendoTroll(int client, int args) { if(lastCrescendoUser > -1) { ActivateAutoPunish(lastCrescendoUser); ReplyToCommand(client, "Activated auto punish on %N", lastCrescendoUser); - ShowActivity(client, "activated autopunish for crescendo activator %N",lastCrescendoUser); + LogAction(client, lastCrescendoUser, "\"%L\" autopunished crescendo rusher \"%L\"", client, lastCrescendoUser); + ShowActivityEx(client, "[FTT] ", "activated autopunish for crescendo activator %N",lastCrescendoUser); }else{ ReplyToCommand(client, "No player could be found to autopunish."); } @@ -191,7 +207,8 @@ public Action Command_ResetUser(int client, int args) { for (int i = 0; i < target_count; i++) { if(ActiveTrolls[target_list[i]] > 0) { ResetClient(target_list[i], true); - ShowActivity(client, "reset troll effects on \"%N\". ", target_list[i]); + LogAction(client, target_list[i], "\"%L\" reset all troll effects for \"%L\"", client, target_list[i]); + ShowActivityEx(client, "[FTT] ", "reset troll effects for \"%N\". ", target_list[i]); } } } @@ -199,52 +216,30 @@ public Action Command_ResetUser(int client, int args) { } public Action Command_ApplyUser(int client, int args) { - if(args < 2) { - Menu menu = new Menu(ChoosePlayerHandler); - menu.SetTitle("Choose a player to troll"); - for(int i = 1; i < MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) { - static char userid[8], display[16]; - Format(userid, sizeof(userid), "%d", GetClientUserId(i)); - GetClientName(i, display, sizeof(display)); - menu.AddItem(userid, display); - } - } - menu.ExitButton = true; - menu.Display(client, 0); + if(args < 1) { + ShowTrollMenu(client); }else{ - char arg1[32], arg2[32], arg3[8]; + char arg1[32]; GetCmdArg(1, arg1, sizeof(arg1)); - GetCmdArg(2, arg2, sizeof(arg2)); - GetCmdArg(3, arg3, sizeof(arg3)); - bool silent = StrEqual(arg3, "silent") || StrEqual(arg3, "quiet") || StrEqual(arg3, "mute"); - static char name[MAX_TROLL_NAME_LENGTH]; - if(!GetTrollID(StringToInt(arg2), name)) { - ReplyToCommand(client, "Not a valid mode. Must be greater than 0. Usage: sm_fta [player] [mode]. Use sm_ftr to reset."); - }else{ - char target_name[MAX_TARGET_LENGTH]; - int target_list[MAXPLAYERS], target_count; - bool tn_is_ml; - if ((target_count = ProcessTargetString( - arg1, - client, - target_list, - MAXPLAYERS, - 0, - target_name, - sizeof(target_name), - tn_is_ml)) <= 0 - ) { - /* This function replies to the admin with a failure message */ - ReplyToTargetError(client, target_count); - return Plugin_Handled; - } - for (int i = 0; i < target_count; i++) { - if(IsClientInGame(target_list[i]) && GetClientTeam(target_list[i]) == 2) - ApplyTroll(target_list[i], name, client, TrollMod_Instant, silent); - } + 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_list[0] > 0) { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; } + SetupCategoryMenu(client, target_list[0]); } return Plugin_Handled; } @@ -272,9 +267,8 @@ public Action Command_ListTheTrolls(int client, int args) { int modeCount = 0; static char name[MAX_TROLL_NAME_LENGTH]; for(int j = 1; j <= MAX_TROLLS; j++) { - GetTrollID(j, name); - if(IsTrollActive(i, name)) { - strcopy(modeListArr[modeCount], MAX_TROLL_NAME_LENGTH, name); + if(trollIds[j][0] != '\0' && IsTrollActive(i, trollIds[j])) { + strcopy(modeListArr[modeCount], MAX_TROLL_NAME_LENGTH, trollIds[j]); modeCount++; } } diff --git a/scripting/include/feedthetrolls/events.inc b/scripting/include/feedthetrolls/events.inc index f5bf90a..8fd279b 100644 --- a/scripting/include/feedthetrolls/events.inc +++ b/scripting/include/feedthetrolls/events.inc @@ -43,7 +43,7 @@ public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadca } public Action Event_WeaponReload(int weapon) { int client = GetEntPropEnt(weapon, Prop_Send, "m_hOwner"); - if(IsTrollActive(client, "GunJam")) { + if(IsTrollActive(client, "Gun Jam")) { float dec = GetRandomFloat(0.0, 1.0); if(FloatCompare(dec, 0.50) == -1) { //10% chance gun jams return Plugin_Stop; @@ -101,7 +101,7 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { if((class == L4D2Infected_Tank && hMagnetTargetMode.IntValue & 2 == 2) || hMagnetTargetMode.IntValue & 1 == 1 ) continue; } - if(class == L4D2Infected_Tank && IsTrollActive(i, "TankMagnet") || (class != L4D2Infected_Tank && IsTrollActive(i, "SpecialMagnet"))) { + if(class == L4D2Infected_Tank && IsTrollActive(i, "Tank Magnet") || (class != L4D2Infected_Tank && IsTrollActive(i, "Special Magnet"))) { GetClientAbsOrigin(i, survPos); float dist = GetVectorDistance(survPos, spPos, true); if(closestClient == -1 || dist < closestDistance) { @@ -120,7 +120,7 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { return Plugin_Continue; } public Action L4D2_OnEntityShoved(int client, int entity, int weapon, float vecDir[3], bool bIsHighPounce) { - if(client > 0 && client <= MaxClients && IsTrollActive(client, "NoShove") && hShoveFailChance.FloatValue > GetRandomFloat()) { + if(client > 0 && client <= MaxClients && IsTrollActive(client, "No Shove") && hShoveFailChance.FloatValue > GetRandomFloat()) { return Plugin_Handled; } return Plugin_Continue; @@ -214,7 +214,7 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] PrintToServer("%N: %s", client, sArgs); CPrintToChatAll("{blue}%N {default}: %s", client, newMessage); return Plugin_Handled; - }else if(IsTrollActive(client, "NoProfanity")) { + }else if(IsTrollActive(client, "No Profanity")) { static char strings[32][MAX_PHRASE_LENGTH]; ArrayList phrases; bool foundWord = false; @@ -224,8 +224,7 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] phrases = GetPhrasesArray(strings[i]); if(phrases != null && phrases.Length > 0) { foundWord = true; - int c = phrases.GetString(GetRandomInt(0, phrases.Length - 1), strings[i], MAX_PHRASE_LENGTH); - PrintToServer("replacement: %s (%d)", strings[i], c); + phrases.GetString(GetRandomInt(0, phrases.Length - 1), strings[i], MAX_PHRASE_LENGTH); } } int length = MAX_PHRASE_LENGTH * words; @@ -233,8 +232,11 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] if(foundWord) { ImplodeStrings(strings, 32, " ", message, length); } else { - REPLACEMENT_PHRASES.GetValue("_Full Message Phrases", phrases); - phrases.GetString(GetRandomInt(0, phrases.Length - 1), message, MAX_PHRASE_LENGTH); + if(!fullMessagePhraseList) { + PrintToServer("[FTT] Error: Could not find full message phrases!!!"); + return Plugin_Continue; + } + fullMessagePhraseList.GetString(GetRandomInt(0, fullMessagePhraseList.Length - 1), message, MAX_PHRASE_LENGTH); } CPrintToChatAll("{blue}%N {default}: %s", client, message); PrintToServer("%N: %s", client, sArgs); @@ -244,7 +246,7 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] } public Action Event_ItemPickup(int client, int weapon) { - if(IsTrollActive(client, "NoPickup")) { + if(IsTrollActive(client, "No Pickup")) { return Plugin_Stop; }else{ static char wpnName[64]; @@ -270,7 +272,7 @@ public Action Event_ItemPickup(int client, int weapon) { SetCommandFlags("give", flags); return Plugin_Stop; } - }else if(IsTrollActive(client, "PrimaryDisable")) { + }else if(IsTrollActive(client, "Primary Disable")) { return Plugin_Stop; } return Plugin_Continue; @@ -290,6 +292,34 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 } return Plugin_Continue; } + static int invertedTrollIndex; + if(invertedTrollIndex <= 0) { + invertedTrollIndex = GetTrollIndex("Inverted Controls"); + } + if(IsTrollActiveByRawID(client, invertedTrollIndex)) { + if(buttons & IN_MOVELEFT || buttons & IN_MOVERIGHT) { + vel[1] = -vel[1]; + } + if(buttons & IN_FORWARD || buttons & IN_BACK) { + vel[0] = -vel[0]; + } + if(buttons & IN_JUMP) { + buttons = buttons & ~IN_JUMP | IN_DUCK; + } else if(buttons & IN_DUCK) { + buttons = buttons & ~IN_DUCK | IN_JUMP; + } + if(buttons & IN_RUN) { + buttons = buttons & ~IN_RUN | IN_WALK; + } else if(buttons & IN_WALK) { + buttons = buttons & ~IN_WALK | IN_RUN; + } + if(buttons & IN_RELOAD) { + buttons = buttons & ~IN_RELOAD | IN_ATTACK2; + } else if(buttons & IN_ATTACK2) { + buttons = buttons & ~IN_ATTACK2 | IN_RELOAD; + } + return Plugin_Changed; + } return Plugin_Continue; } @@ -300,7 +330,7 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& return Plugin_Stop; } - if(IsTrollActive(attacker, "DamageBoost")) { + if(IsTrollActive(attacker, "Damage Boost")) { damage * 2; return Plugin_Changed; } @@ -318,7 +348,8 @@ public Action SoundHook(int[] clients, int& numClients, char sample[PLATFORM_MAX lastCrescendoUser = lastButtonUser; if(IsPlayerFarDistance(lastButtonUser, AUTOPUNISH_FLOW_MIN_DISTANCE)) { NotifyAllAdmins("Autopunishing player %N for activation of event far from team", lastButtonUser); - ShowActivity(0, "activated autopunish for crescendo activator %N (auto)", lastButtonUser); + ShowActivityEx(0, "[FTT] ", "activated autopunish for crescendo activator %N (auto)", lastButtonUser); + LogAction(0, lastButtonUser, "\"%L\" automatic autopunish for crescendo activator \"%L\"", 0, lastButtonUser); ActivateAutoPunish(lastButtonUser); } lastButtonUser = -1; @@ -327,7 +358,7 @@ public Action SoundHook(int[] clients, int& numClients, char sample[PLATFORM_MAX if(IsTrollActive(entity, "Honk")) { strcopy(sample, sizeof(sample), "player/footsteps/clown/concrete1.wav"); return Plugin_Changed; - } else if(IsTrollActive(entity, "VocalizeGag")) { + } else if(IsTrollActive(entity, "Vocalize Gag")) { return Plugin_Handled; } else if(IsTrollActive(entity, "Meow")) { strcopy(sample, sizeof(sample), "custom/meow1.mp3"); @@ -351,7 +382,7 @@ public Action Event_WitchVictimSet(Event event, const char[] name, bool dontBroa continue; } - if(IsTrollActive(i, "WitchMagnet")) { + if(IsTrollActive(i, "Witch Magnet")) { GetClientAbsOrigin(i, survPos); float dist = GetVectorDistance(survPos, witchPos, true); if(closestClient == -1 || dist < closestDistance) { @@ -384,35 +415,49 @@ void EntityCreateCallback(int entity) { GetEntityClassname(entity, class, sizeof(class)); int entOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); - if(entOwner > 0 && entOwner <= MaxClients && IsTrollActive(entOwner, "BadThrow")) { - static float pos[3]; - GetClientEyePosition(entOwner, pos); - if(StrContains(class, "vomitjar", true) > -1) { - AcceptEntityInput(entity, "Kill"); - if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() > hBadThrowHitSelf.FloatValue) { - L4D_CTerrorPlayer_OnVomitedUpon(entOwner, entOwner); - EmitSoundToAll("weapons/ceda_jar/ceda_jar_explode.wav", entOwner); - FindClosestClient(entOwner, false, pos); - } - SpawnItem("vomitjar", pos); - } else if(StrContains(class, "molotov", true) > -1) { - // Burn them if no one near :) - if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() > hBadThrowHitSelf.FloatValue) { - GetClientAbsOrigin(entOwner, pos); - if(IsAnyPlayerNear(entOwner, 500.0)) { - AcceptEntityInput(entity, "Kill"); - EmitSoundToAll("weapons/molotov/molotov_detonate_1.wav", entOwner); - } else { // or delete if there is - TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); - } - } else { - SpawnItem("molotov", pos); + if(entOwner > 0 && entOwner <= MaxClients) { + if(IsTrollActive(entOwner, "Bad Throw")) { + static float pos[3]; + GetClientEyePosition(entOwner, pos); + if(StrContains(class, "vomitjar", true) > -1) { AcceptEntityInput(entity, "Kill"); + if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() > hBadThrowHitSelf.FloatValue) { + L4D_CTerrorPlayer_OnVomitedUpon(entOwner, entOwner); + EmitSoundToAll("weapons/ceda_jar/ceda_jar_explode.wav", entOwner); + FindClosestClient(entOwner, false, pos); + } + SpawnItem("vomitjar", pos); + } else if(StrContains(class, "molotov", true) > -1) { + // Burn them if no one near :) + if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() > hBadThrowHitSelf.FloatValue) { + GetClientAbsOrigin(entOwner, pos); + if(IsAnyPlayerNear(entOwner, 500.0)) { + AcceptEntityInput(entity, "Kill"); + EmitSoundToAll("weapons/molotov/molotov_detonate_1.wav", entOwner); + } else { // or delete if there is + TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + } + } else { + SpawnItem("molotov", pos); + AcceptEntityInput(entity, "Kill"); + } + } else if(StrContains(class, "pipe_bomb", true) > -1) { + if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() > hBadThrowHitSelf.FloatValue) + TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + SpawnItem("pipe_bomb", pos); } - } else if(StrContains(class, "pipe_bomb", true) > -1) { - if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() > hBadThrowHitSelf.FloatValue) - TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); - SpawnItem("pipe_bomb", pos); } } -} \ No newline at end of file +} + +int FindClosestVisibleClient(int source) { + static float pos[3], ang[3]; + GetClientEyePosition(source, pos); + GetClientEyeAngles(source, ang); + Handle handle = TR_TraceRayFilterEx(pos, ang, MASK_VISIBLE, RayType_Infinite, TraceEntityFilterPlayer, source); + return TR_GetEntityIndex(handle); +} + +public bool TraceEntityFilterPlayer(int entity, int mask, any data) { + return data != entity && entity <= MaxClients && GetClientTeam(entity) == 2 && IsPlayerAlive(entity); +} diff --git a/scripting/include/feedthetrolls/menus.inc b/scripting/include/feedthetrolls/menus.inc index 63a2e6d..10e77a3 100644 --- a/scripting/include/feedthetrolls/menus.inc +++ b/scripting/include/feedthetrolls/menus.inc @@ -12,7 +12,7 @@ public int Insta_PlayerHandler(Menu menu, MenuAction action, int client, int par Menu spMenu = new Menu(Insta_SpecialHandler); spMenu.SetTitle("Choose a Insta-Special™"); - for(int i = 1; i <= 6; i++) { + for(int i = 1; i <= 8; i++) { Format(info, sizeof(info), "%d|%d|%d", userid, instaMode, i); spMenu.AddItem(info, SPECIAL_NAMES[i-1]); } @@ -33,11 +33,19 @@ public int Insta_SpecialHandler(Menu menu, MenuAction action, int client, int pa bool inFace = StrEqual(str[1], "1"); int special = StringToInt(str[2]); if(inFace) { - SpawnSpecialInFace(target, special); - ShowActivity(client, "spawned Insta-%s™ near %N", SPECIAL_NAMES[special-1], target); + if(SpawnSpecialInFace(target, special)) { + LogAction(client, target, "\"%L\" spawned Insta-%s™ on \"%L\"", client, SPECIAL_NAMES[special-1], target); + ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ on %N", SPECIAL_NAMES[special-1], target); + } else { + ReplyToCommand(client, "Could not spawn special."); + } } else { - SpawnSpecialNear(target, special); - ShowActivity(client, "spawned Insta-%s™ near %N", SPECIAL_NAMES[special-1], target); + if(SpawnSpecialNear(target, special)) { + LogAction(client, target, "\"%L\" spawned Insta-%s™ near \"%L\"", client, SPECIAL_NAMES[special-1], target); + ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ near %N", SPECIAL_NAMES[special-1], target); + } else { + ReplyToCommand(client, "Could not spawn special."); + } } } else if (action == MenuAction_End) delete menu; @@ -66,26 +74,32 @@ public int ChoosePlayerHandler(Menu menu, MenuAction action, int param1, int par delete menu; } +static int iMenuVictimID[MAXPLAYERS+1]; public int ChooseCategoryHandler(Menu menu, MenuAction action, int param1, int param2) { - if (action == MenuAction_Select) { + if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) + ShowTrollMenu(param1); + else if (action == MenuAction_Select) { static char info[32]; menu.GetItem(param2, info, sizeof(info)); static char str[2][8]; ExplodeString(info, "|", str, 2, 8, false); int userid = StringToInt(str[0]); + iMenuVictimID[param1] = userid; int category = StringToInt(str[1]); + + // Reset troll: + if(category == -1) { + ApplyTroll(GetClientOfUserId(userid), "Reset User", param1, TrollMod_Instant); + return; + } Menu trollMenu = new Menu(ChooseModeMenuHandler); GetCategory(category, info, sizeof(info)); Format(info, sizeof(info), "Category: %s", info); trollMenu.SetTitle(info); - //TODO: Update + static Troll troll; - // add 'return' btn - Format(info, sizeof(info), "%d|-1", userid); - trollMenu.AddItem(info, "<= Go Back"); - // Add all menus that have same category for(int i = 0; i < trollKV.Size; i++) { GetTrollByKeyIndex(i, troll); @@ -95,30 +109,32 @@ public int ChooseCategoryHandler(Menu menu, MenuAction action, int param1, int p } } trollMenu.ExitButton = true; + trollMenu.ExitBackButton = true; trollMenu.Display(param1, 0); } else if (action == MenuAction_End) delete menu; } public int ChooseModeMenuHandler(Menu menu, MenuAction action, int param1, int param2) { - if (action == MenuAction_Select) { - static char info[16]; + if (action == MenuAction_Cancel) { + if(param2 == MenuCancel_ExitBack) { + if(iMenuVictimID[param1] == 0) { + ReplyToCommand(param1, "FTT: Could not acquire player"); + } + SetupCategoryMenu(param1, iMenuVictimID[param1]); + } + } else if (action == MenuAction_Select) { + static char info[32]; menu.GetItem(param2, info, sizeof(info)); static char str[2][8]; ExplodeString(info, "|", str, 2, 8, false); int userid = StringToInt(str[0]); int client = GetClientOfUserId(userid); int keyIndex = StringToInt(str[1]); - if(keyIndex == -1) { - SetupCategoryMenu(param1, userid); - return; - } static Troll troll; - static char trollID[MAX_TROLL_NAME_LENGTH]; GetTrollByKeyIndex(keyIndex, troll); - troll.GetID(trollID); //If troll has multiple flags, prompt: - if(StrEqual(trollID, "ThrowItAll")) { + if(StrEqual(troll.name, "Throw It All")) { // Setup menu to call itself, but with an extra data point Menu itmMenu = new Menu(ChooseClumsySlotHandler); itmMenu.SetTitle("Choose Item To Throw"); @@ -138,10 +154,11 @@ public int ChooseModeMenuHandler(Menu menu, MenuAction action, int param1, int p itmMenu.Display(param1, 0); } else if(troll.HasMod(TrollMod_Instant) && troll.HasMod(TrollMod_Constant)) { Menu modiferMenu = new Menu(ChooseTrollModiferHandler); - modiferMenu.SetTitle("Choose Troll Modifer Option"); + Format(info, sizeof(info), "%s: Choose Modifier", troll.name); + modiferMenu.SetTitle(info); Format(info, sizeof(info), "%d|%d|1", userid, keyIndex); - modiferMenu.AddItem(info, "Activate once"); + modiferMenu.AddItem(info, "Activate Once"); Format(info, sizeof(info), "%d|%d|2", userid, keyIndex); modiferMenu.AddItem(info, "Activate Periodically"); Format(info, sizeof(info), "%d|%d|3", userid, keyIndex); @@ -150,7 +167,7 @@ public int ChooseModeMenuHandler(Menu menu, MenuAction action, int param1, int p modiferMenu.ExitButton = true; modiferMenu.Display(param1, 0); } else { - ApplyTroll(client, trollID, param1, troll.GetDefaultMod()); + troll.Activate(client, param1); } } else if (action == MenuAction_End) delete menu; @@ -164,15 +181,19 @@ public int ChooseClumsySlotHandler(Menu menu, MenuAction action, int param1, int ExplodeString(info, "|", str, 2, 8, false); int client = GetClientOfUserId(StringToInt(str[0])); int slot = StringToInt(str[1]); - PrintToChatAll("%d", slot); - if(slot == -1) { + if(client == 0) { + ReplyToCommand(param1, "FTT: Could not acquire player"); + return; + }else if(slot == -1) { for(int i = 0; i <= 4; i++) { ThrowItemToClosestPlayer(client, i); } } else { ThrowItemToClosestPlayer(client, slot); } - ShowActivity(param1, "activated troll \"Throw It All\" for %N. ", client); + LogAction(param1, client, "\"%L\" activated troll \"Throw It all\" for \"%L\"", param1, client); + + ShowActivityEx(param1, "[FTT] ", "activated troll \"Throw It All\" for %N. ", client); } else if (action == MenuAction_End) delete menu; } @@ -187,15 +208,18 @@ public int ChooseTrollModiferHandler(Menu menu, MenuAction action, int param1, i int keyIndex = StringToInt(str[1]); int flags = StringToInt(str[2]); + if(client == 0) { + ReplyToCommand(param1, "FTT: Could not acquire player"); + return; + } + static Troll troll; GetTrollByKeyIndex(keyIndex, troll); if(flags == 1 || flags == 3) troll.Activate(client, param1, TrollMod_Instant); - // ApplyTroll(client, trollID, param1, TrollMod_Instant); if(flags == 2 || flags == 3) - troll.Activate(client, param1, TrollMod_Constant); - // ApplyTroll(client, trollID, param1, TrollMod_Constant); + troll.Activate(client, param1, TrollMod_Constant); } else if (action == MenuAction_End) delete menu; } @@ -206,13 +230,37 @@ public void StopItemGive(int client) { void SetupCategoryMenu(int client, int victimUserID) { Menu categoryMenu = new Menu(ChooseCategoryHandler); - categoryMenu.SetTitle("Choose a troll category"); - static char category[16], id[8]; + static char category[64], id[8]; + Format(category, sizeof(category), "%N: Choose troll category", GetClientOfUserId(victimUserID)); + categoryMenu.SetTitle(category); + + Format(id, sizeof(id), "%d|-1", victimUserID); + categoryMenu.AddItem(id, "Reset User"); for(int i = 0; i < categories.Length; i++) { categories.GetString(i, category, sizeof(category)); Format(id, sizeof(id), "%d|%d", victimUserID, i); categoryMenu.AddItem(id, category); } categoryMenu.ExitButton = true; + categoryMenu.ExitBackButton = true; categoryMenu.Display(client, 0); +} + +void ShowTrollMenu(int client) { + Menu menu = new Menu(ChoosePlayerHandler); + menu.SetTitle("Choose a player to troll"); + static char userid[8], display[32]; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) { + IntToString(GetClientUserId(i), userid, sizeof(userid)); + int specClient = GetSpectatorClient(i); + if(specClient > 0) + Format(display, sizeof(display), "%N (Idle)", specClient); + else + GetClientName(i, display, sizeof(display)); + menu.AddItem(userid, display); + } + } + menu.ExitButton = true; + menu.Display(client, 0); } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/misc.inc b/scripting/include/feedthetrolls/misc.inc index aee892c..968d3d8 100644 --- a/scripting/include/feedthetrolls/misc.inc +++ b/scripting/include/feedthetrolls/misc.inc @@ -1,13 +1,14 @@ #define AUTOPUNISH_FLOW_MIN_DISTANCE 5000.0 #define AUTOPUNISH_MODE_COUNT 3 +// #define DEBUG_PHRASE_LOAD 1 void ActivateAutoPunish(int client) { if(hAutoPunish.IntValue & 2 == 2) - ApplyTroll(lastButtonUser, "SpecialMagnet", 0, TrollMod_Constant); + ApplyTroll(lastButtonUser, "Special Magnet", 0, TrollMod_Constant); if(hAutoPunish.IntValue & 1 == 1) - ApplyTroll(lastButtonUser, "TankMagnet", 0, TrollMod_Constant); + ApplyTroll(lastButtonUser, "Tank Magnet", 0, TrollMod_Constant); if(hAutoPunish.IntValue & 8 == 8) - ApplyTroll(lastButtonUser, "VomitPlayer", 0, TrollMod_Instant); + ApplyTroll(lastButtonUser, "Vomit Player", 0, TrollMod_Instant); else if(hAutoPunish.IntValue & 4 == 4) ApplyTroll(lastButtonUser, "Swarm", 0, TrollMod_Instant); if(hAutoPunishExpire.IntValue > 0) { @@ -29,7 +30,8 @@ void SetWitchTarget(int witch, int target) { bool ToggleMarkPlayer(int client, int target) { if(g_PendingBanTroll[target]) { g_PendingBanTroll[target] = false; - ShowActivity(client, "unmarked %N as troll", target); + LogAction(client, target, "\"%L\" unmarked \"%L\" as troll", client, target); + ShowActivityEx(client, "[FTT] ", "unmarked %N as troll", target); return true; }else{ AdminId admin_client = GetUserAdmin(client); @@ -41,7 +43,8 @@ bool ToggleMarkPlayer(int client, int target) { Call_Finish(); g_PendingBanTroll[target] = true; EnableTroll(target, "NoProfanity"); - ShowActivity(client, "marked %N as troll", target); + LogAction(client, target, "\"%L\" marked \"%L\" as troll", client, target); + ShowActivityEx(client, "[FTT] ", "marked %N as troll", target); return true; }else{ ReplyToCommand(client, "cannot mark %N as troll as they are an admin.", target); @@ -68,6 +71,7 @@ stock bool IsPlayerIncapped(int client) { #define MAX_PHRASES_PER_WORD 8 #define MAX_PHRASE_LENGTH 191 StringMap REPLACEMENT_PHRASES; +ArrayList fullMessagePhraseList; /* Example: exWord { @@ -104,6 +108,13 @@ void LoadPhrases() { phrases.PushString(phrase); } i = 0; + if(StrEqual(word, "_full message phrases")) { + fullMessagePhraseList = phrases.Clone(); + continue; + } + #if defined DEBUG_PHRASE_LOAD + PrintToServer("Loaded %d phrases for word \"%s\"", phrases.Length, word); + #endif REPLACEMENT_PHRASES.SetValue(word, phrases.Clone(), true); } while (kv.GotoNextKey(false)); delete kv; @@ -193,7 +204,6 @@ bool IsAnyPlayerNear(int source, float range) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) && i != source) { GetClientAbsOrigin(i, pos2); float dist = GetVectorDistance(pos1, pos2); - PrintToChatAll("%f <= %f: %b", dist, range, (dist <= range)); if(dist <= range) return true; } } @@ -202,7 +212,7 @@ bool IsAnyPlayerNear(int source, float range) { void ThrowItemToClosestPlayer(int victim, int slot) { int wpn = GetPlayerWeaponSlot(victim, slot); - if(slot != 1 || DoesClientHaveMelee(victim)) { + if(wpn > 0 && (slot != 1 || DoesClientHaveMelee(victim))) { static float pos[3]; int clients[4]; GetClientAbsOrigin(victim, pos); @@ -229,3 +239,13 @@ void DropItem(int victim, int slot) { } } +int GetSpectatorClient(int bot) { + if(!IsFakeClient(bot)) return -1; + static char netclass[16]; + GetEntityNetClass(bot, netclass, sizeof(netclass)); + if(strcmp(netclass, "SurvivorBot") == 0 ) { + int user = GetEntProp(bot, Prop_Send, "m_humanSpectatorUserID"); + if(user > 0) return GetClientOfUserId(user); + } + return -1; +} \ No newline at end of file diff --git a/scripting/include/feedthetrolls/specials.inc b/scripting/include/feedthetrolls/specials.inc index 6af943a..90418cb 100644 --- a/scripting/include/feedthetrolls/specials.inc +++ b/scripting/include/feedthetrolls/specials.inc @@ -1,9 +1,9 @@ char SPECIAL_NAMES[][] = { - "Smoker", "Boomer", "Hunter", "Spitter", "Jockey", "Charger", "Witch" + "Smoker", "Boomer", "Hunter", "Spitter", "Jockey", "Charger", "Witch", "Tank" }; stock int GetSpecialType(const char[] input) { - for(int i = 0; i < sizeof(SPECIAL_NAMES); i++) { + for(int i = 0; i < 8; i++) { if(strcmp(SPECIAL_NAMES[i], input, false) == 0) return i + 1; } return -1; @@ -37,16 +37,18 @@ float GetIdealMinDistance(int specialType) { } bool SpawnSpecialInFace(int target, int specialType) { - if(specialType >= sizeof(SPECIAL_NAMES)) return false; + if(specialType > 8) return false; static float pos[3], ang[3]; static float testPos[3]; testPos = pos; GetClientAbsOrigin(target, pos); GetClientEyeAngles(target, ang); - if(specialType != 5 && specialType != 2) { //If charger/hunter, find a suitable area that is at least 5 m away + if(specialType != 5 && specialType != 2) { //If not jockey/hunter find a suitable area that is at least 5 m away float minDistance = GetIdealMinDistance(specialType); GetHorizontalPositionFromOrigin(pos, ang, minDistance, testPos); - FindSuitablePosition(target, pos, testPos, minDistance, 100); + if(!FindSuitablePosition(target, pos, testPos, minDistance, 100)) { + L4D_GetRandomPZSpawnPosition(target, specialType, 10, testPos); + } pos = testPos; } else { // Else spawn a little bit off, and above (above for jockeys) pos[2] += 10.0; @@ -54,31 +56,49 @@ bool SpawnSpecialInFace(int target, int specialType) { } pos[2] += 1.0; NegateVector(ang); - int special = (specialType == 7) ? L4D2_SpawnWitch(pos, ang) : L4D2_SpawnSpecial(specialType, pos, ang); - if(special == -1) return false; - if(specialType == 7) - SetWitchTarget(special, target); - else - g_iAttackerTarget[special] = GetClientUserId(target); - - if(specialType == 2) { //Kill boomer - ForcePlayerSuicide(special); - } - return true; + return SpawnSpecialInternal(specialType, target, pos, NULL_VECTOR) > 0; } bool SpawnSpecialNear(int target, int specialType) { - if(specialType >= sizeof(SPECIAL_NAMES)) return false; + if(specialType > 8) return false; static float pos[3]; if(L4D_GetRandomPZSpawnPosition(target, specialType, 10, pos)) { - int special = (specialType == 7) ? L4D2_SpawnWitch(pos, NULL_VECTOR) : L4D2_SpawnSpecial(specialType, pos, NULL_VECTOR); - if(special == -1) return false; - if(specialType == 7) - SetWitchTarget(special, target); - else - g_iAttackerTarget[special] = GetClientUserId(target); - return true; + return SpawnSpecialInternal(specialType, target, pos, NULL_VECTOR) > 0; } return false; +} + +// doesnt seem to work with l4dhooks methods +void BypassLimit() { + int bot = CreateFakeClient("InfectedBot"); + if (bot != 0) { + ChangeClientTeam(bot, 3); + CreateTimer(0.1, Timer_KickBot, bot); + } +} + +int SpawnSpecialInternal(int type, int target, float pos[3], float ang[3]) { + if(type <= 6) { + // BypassLimit(); + int special = L4D2_SpawnSpecial(type, pos, ang); + if(special != -1) + g_iAttackerTarget[special] = GetClientUserId(target); + return special; + } + else if(type == 7) { + int witch = L4D2_SpawnWitch(pos, ang); + if(witch != -1) + SetWitchTarget(witch, target); + return witch; + } + else if(type == 8) { + // BypassLimit(); + int tank = L4D2_SpawnTank(pos, ang); + if(tank <= 0 || !IsClientConnected(tank)) return -1; + if(tank != -1) + g_iAttackerTarget[tank] = GetClientUserId(target); + return tank; + } + else return -1; } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/timers.inc b/scripting/include/feedthetrolls/timers.inc index 4bc0932..c19002f 100644 --- a/scripting/include/feedthetrolls/timers.inc +++ b/scripting/include/feedthetrolls/timers.inc @@ -1,7 +1,8 @@ + public Action Timer_ThrowTimer(Handle timer) { int count = 0; for(int i = 1; i < MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && IsTrollActive(i, "ThrowItAll")) { + if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && IsTrollActive(i, "Throw It All")) { ThrowAllItems(i); count++; } @@ -13,14 +14,14 @@ public Action Timer_Main(Handle timer) { static int loop; for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i)) { - if(IsTrollActive(i, "SlowDrain")) { + if(IsTrollActive(i, "Slow Drain")) { if(loop % 4 == 0) { int hp = GetClientHealth(i); if(hp > 50) { SetEntProp(i, Prop_Send, "m_iHealth", hp - 1); } } - }else if(IsTrollActive(i, "TempHealthQuickDrain")) { + }else if(IsTrollActive(i, "Temp Health Quick Drain")) { if(loop % 2 == 0) { float bufferTime = GetEntPropFloat(i, Prop_Send, "m_healthBufferTime"); float buffer = GetEntPropFloat(i, Prop_Send, "m_healthBuffer"); @@ -82,9 +83,9 @@ public Action Timer_ResetAutoPunish(Handle timer, int user) { int client = GetClientOfUserId(user); if(client) { if(hAutoPunish.IntValue & 2 == 2) - DisableTroll(client, "SpecialMagnet"); + DisableTroll(client, "Special Magnet"); if(hAutoPunish.IntValue & 1 == 1) - DisableTroll(client, "TankMagnet"); + DisableTroll(client, "Tank Magnet"); } } @@ -93,4 +94,10 @@ public Action Timer_NextWitchSet(Handle timer, DataPack pack) { int client = GetClientOfUserId(pack.ReadCell()); int witch = pack.ReadCell(); SetWitchTarget(witch, client); +} + +public Action Timer_KickBot(Handle timer, int client) { + if(IsClientInGame(client) && (!IsClientInKickQueue(client))) { + if(IsFakeClient(client)) KickClient(client); + } } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/trolls.inc b/scripting/include/feedthetrolls/trolls.inc new file mode 100644 index 0000000..21e2142 --- /dev/null +++ b/scripting/include/feedthetrolls/trolls.inc @@ -0,0 +1,48 @@ +// UP THE VALUE 'MAX_TROLLS' in base.inc before adding new ones! + +void SetupTrolls() { + trollKV = new StringMap(); + categories = new ArrayList(ByteCountToCells(16)); + SetupTroll("Reset User", "Resets the user, removes all troll effects", TrollMod_Instant); + + SetCategory("Magnets"); + SetupTroll("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant); + SetupTroll("Tank Magnet", "Attracts ALL tanks to any alive target with this troll enabled", TrollMod_Constant); + SetupTroll("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant); + + SetCategory("Infected"); + SetupTroll("Swarm", "Swarms a player with zombies. Requires swarm plugin", TrollMod_Instant | TrollMod_Constant); + SetupTroll("Vomit Player", "Shortcut to sm_vomitplayer. vomits the player.", TrollMod_Instant); + SetupTroll("Inface Special", "Shortcut to sm_inface", TrollMod_Instant); + SetupTroll("Insta Special", "Shortcut to sm_insta", TrollMod_Instant); + + SetCategory("Items"); + SetupTroll("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant); + SetupTroll("Bad Throw", "Player drops throwables on throw, and biles/molotovs themselves", TrollMod_Constant); + SetupTroll("No Pickup", "Prevents a player from picking up ANY (new) item. Use ThrowItAll to make them drop", TrollMod_Constant); + SetupTroll("UziRules", "Picking up a weapon gives them a UZI instead", TrollMod_Constant); + SetupTroll("Primary Disable", "Player cannot pickup any weapons, only melee/pistols", TrollMod_Constant); + SetupTroll("Half Primary Ammo", "Cuts their primary reserve ammo in half", TrollMod_Instant); + + SetCategory("Chat"); + SetupTroll("iCantSpellNoMore", "Chat messages letter will randomly changed with wrong letters", TrollMod_Instant); + SetupTroll("No Profanity", "Replaces some words with random phrases", TrollMod_Constant); + SetupTroll("Vocalize Gag", "Prevents player from sending any vocalizations (even automatic)", TrollMod_Constant); + SetupTroll("Honk", "Honk", TrollMod_Constant); + SetupTroll("Meow", "Makes the player meow", TrollMod_Constant); + + SetCategory("Health"); + SetupTroll("Damage Boost", "Makes a player take more damage than normal", TrollMod_Constant); + SetupTroll("Temp Health Quick Drain", "Makes a player's temporarily health drain very quickly", TrollMod_Constant); + SetupTroll("Slow Drain", "Will make the player slowly lose health over time", TrollMod_Constant); + SetupTroll("KillMeSoftly", "Make player eat or waste pills whenever possible", TrollMod_Instant | TrollMod_Constant); + + SetCategory("Misc"); + SetupTroll("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant); + SetupTroll("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant); + SetupTroll("Higher Gravity", "Sets player gravity to 1.3x of normal gravity", TrollMod_Constant); + SetupTroll("No Shove", "Prevents a player from shoving", TrollMod_Constant); + SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant); + SetupTroll("Inverted Controls", "Well, aint it obvious", TrollMod_Constant); + //INFO: UP MAX_TROLLS when adding new trolls! +} \ No newline at end of file diff --git a/scripting/include/ftt.inc b/scripting/include/ftt.inc index eebd39b..d21b480 100644 --- a/scripting/include/ftt.inc +++ b/scripting/include/ftt.inc @@ -29,9 +29,8 @@ bool bChooseVictimAvailable = false; //For charge player feature, is it availabl int g_iAmmoTable; //Loads the ammo table to get ammo amounts int gChargerVictim = -1; //For charge player feature - - #include +#include #include #include #include diff --git a/scripting/include/jutils.inc b/scripting/include/jutils.inc index c9250cf..1e5ae56 100644 --- a/scripting/include/jutils.inc +++ b/scripting/include/jutils.inc @@ -580,4 +580,21 @@ stock void MakeEntityGlow(int entity, const int color[3], float distance = 1500. SetEntProp(entity, Prop_Send, "m_iGlowType", 3); SetEntProp(entity, Prop_Send, "m_nGlowRange", distance); SetEntProp(entity, Prop_Send, "m_glowColorOverride", color[0] + (color[1] * 256) + (color[2] * 65536)); +} + +//Unless I'm dumb, this does not exist +stock void StringToLower(char[] str) { + int len = strlen(str); + for(int i = 0; i < len; i++) { + str[i] = CharToLower(str[i]); + } +} + +stock int GetRealClient(int client) { + if(IsFakeClient(client)) { + int realPlayer = GetClientOfUserId(GetEntProp(client, Prop_Send, "m_humanSpectatorUserID")); + return realPlayer > 0 ? realPlayer : -1; + }else{ + return client; + } } \ No newline at end of file diff --git a/scripting/include/left4dhooks.inc b/scripting/include/left4dhooks.inc index a8224dd..c11b615 100644 --- a/scripting/include/left4dhooks.inc +++ b/scripting/include/left4dhooks.inc @@ -93,6 +93,9 @@ public void __pl_l4dh_SetNTVOptional() MarkNativeAsOptional("L4D2_IsReachable"); MarkNativeAsOptional("L4D_HasPlayerControlledZombies"); MarkNativeAsOptional("L4D_PipeBombPrj"); + MarkNativeAsOptional("L4D_MolotovPrj"); + MarkNativeAsOptional("L4D2_VomitJarPrj"); + MarkNativeAsOptional("L4D2_GrenadeLauncherPrj"); MarkNativeAsOptional("L4D2_ExecVScriptCode"); MarkNativeAsOptional("L4D2_GetVScriptOutput"); @@ -301,6 +304,9 @@ public void __pl_l4dh_SetNTVOptional() +// ==================================================================================================== +// VARIOUS ENUMS +// ==================================================================================================== // For the game mode native and forward. enum { @@ -311,6 +317,23 @@ enum GAMEMODE_SCAVENGE = 8 } +// Provided by "BHaType": +// For the "L4D_State_Transition" native. +// X -> Y (means X state will become Y state on next frame or some seconds later) +enum +{ + STATE_ACTIVE = 0, + STATE_WELCOME, // -> STATE_PICKING_TEAM + STATE_PICKING_TEAM, + STATE_PICKINGCLASS, // -> STATE_ACTIVE + STATE_DEATH_ANIM, // -> STATE_DEATH_WAIT_FOR_KEY + STATE_DEATH_WAIT_FOR_KEY, // -> STATE_OBSERVER_MODE + STATE_OBSERVER_MODE, + STATE_WAITING_FOR_RESCUE, + STATE_GHOST, + STATE_INTRO_CAMERA +} + @@ -320,6 +343,7 @@ enum // ==================================================================================================== // To find list: Search for "ACT_RESET" for example, in the server binary. XRef to the "ActivityList_RegisterSharedActivities" function. View in HexRays to see the full list of constants and values. // These constants can be used in a plugins source code instead of hard coding the sequence number when working with the animation pre-hook. +// Only for Survivors, not Special Infected. enum { L4D1_ACT_RESET, @@ -5128,7 +5152,6 @@ native int L4D_Dissolve(int entity); * @brief Removes the boomer vomit effect from a player. * * @param client Client id of the player to remove the effect from - * @param vecAng Angular velocity vector, director to spin the projectile * * @noreturn */ @@ -5271,6 +5294,41 @@ native bool L4D_HasPlayerControlledZombies(); */ native int L4D_PipeBombPrj(int client, const float vecPos[3], const float vecAng[3]); +/** + * @brief Creates an activated Molotov projectile. + * + * @param client Client id to attribute the projectile to for damage credit + * @param vecPos Vector coordinate of the projectile on creation + * @param vecAng Vector velocity and direction of the projectile + * + * @return Entity index of the projectile + */ +native int L4D_MolotovPrj(int client, const float vecPos[3], const float vecAng[3]); + +/** + * @brief Creates an activated VomitJar projectile. + * + * @param client Client id to attribute the projectile to for damage credit + * @param vecPos Vector coordinate of the projectile on creation + * @param vecAng Vector velocity and direction of the projectile + * + * @return Entity index of the projectile + */ +// L4D2 Only. +native int L4D2_VomitJarPrj(int client, const float vecPos[3], const float vecAng[3]); + +/** + * @brief Creates an activated Grenade Launcher projectile. + * + * @param client Client id to attribute the projectile to for damage credit + * @param vecPos Vector coordinate of the projectile on creation + * @param vecAng Vector velocity and direction of the projectile + * + * @return Entity index of the projectile + */ +// L4D2 Only. +native int L4D2_GrenadeLauncherPrj(int client, const float vecPos[3], const float vecAng[3]); + /** * @brief Creates a Spitter goo projectile. * diff --git a/scripting/l4d2_TKStopper.sp b/scripting/l4d2_TKStopper.sp index 2e41282..ae2418b 100644 --- a/scripting/l4d2_TKStopper.sp +++ b/scripting/l4d2_TKStopper.sp @@ -193,17 +193,18 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo lastFF[attacker] = time; if(playerTotalDamageFF[attacker] > hThreshold.IntValue && !IsFinaleEnding && isDamageDirect) { + LogAction(-1, attacker, "Excessive FF (%.2f HP)", playerTotalDamageFF[attacker]); if(hAction.IntValue == 1) { - LogMessage("[NOTICE] Kicking %N for excessive FF (%f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); - NotifyAllAdmins("[Notice] Kicking %N for excessive FF (%f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); + 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); KickClient(attacker, "Excessive FF"); } else if(hAction.IntValue == 2) { - LogMessage("[NOTICE] Banning %N for excessive FF (%f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); - NotifyAllAdmins("[Notice] Banning %N for excessive FF (%f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); + 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); BanClient(attacker, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF", "Excessive Friendly Fire", "TKStopper"); } else if(hAction.IntValue == 3) { - LogMessage("[NOTICE] %N will be banned for FF on disconnect (%f HP) for %d minutes. ", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); - NotifyAllAdmins("[Notice] %N will be banned for FF on disconnect (%f HP) for %d minutes. Use /ignore to make them immune.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); + 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 to make them immune.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue); isPlayerTroll[attacker] = true; } damage = 0.0; @@ -254,8 +255,10 @@ public Action Command_IgnorePlayer(int client, int args) { ReplyToCommand(client, "%N is an admin and is already immune."); }else{ if(isImmune[target]) { + 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]; diff --git a/scripting/l4d2_feedthetrolls.sp b/scripting/l4d2_feedthetrolls.sp index 8950dc4..8174416 100644 --- a/scripting/l4d2_feedthetrolls.sp +++ b/scripting/l4d2_feedthetrolls.sp @@ -15,6 +15,7 @@ #include #include #include +#include public Plugin myinfo = @@ -103,7 +104,6 @@ public void Change_ThrowInterval(ConVar convar, const char[] oldValue, const cha //If a throw timer exists (someone has mode 11), destroy & recreate w/ new interval if(hThrowTimer != INVALID_HANDLE) { delete hThrowTimer; - PrintToServer("Reset new throw item timer"); hThrowTimer = CreateTimer(convar.FloatValue, Timer_ThrowTimer, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); } } diff --git a/scripting/l4d2_swarm.sp b/scripting/l4d2_swarm.sp index 11985d1..3012b32 100644 --- a/scripting/l4d2_swarm.sp +++ b/scripting/l4d2_swarm.sp @@ -40,7 +40,7 @@ public void OnPluginStart() SwarmRadius = hSwarmDefaultRange.IntValue; LoadTranslations("common.phrases"); - RegAdminCmd("sm_swarm", Cmd_Swarm, ADMFLAG_ROOT, "sm_swarm [player] [range] - Zombies swarm player (or random if not set)"); + RegAdminCmd("sm_swarm", Cmd_Swarm, ADMFLAG_CHEATS, "sm_swarm [player] [range] - Zombies swarm player (or random if not set)"); RegAdminCmd("sm_rush", Cmd_Swarm, ADMFLAG_ROOT, "sm_swarm [player] [range] - Zombies swarm player (or random if not set)"); RegAdminCmd("sm_rushmenu", Cmd_SwarmMenu, ADMFLAG_ROOT, "sm_swarmmenu - Open swarm menu"); RegAdminCmd("sm_rmenu", Cmd_SwarmMenu, ADMFLAG_ROOT, "sm_swarmmenu - Open swarm menu");