From 902a8a140672bc7143712dce26ad215f4ce169db Mon Sep 17 00:00:00 2001 From: Jackz Date: Fri, 25 Dec 2020 22:28:21 -0600 Subject: [PATCH] Add l4d2_feedthetrolls --- README.md | 27 +- plugins/l4d2_feedthetrolls.smx | Bin 0 -> 11333 bytes scripting/l4d2_feedthetrolls.sp | 482 ++++++++++++++++++++++++++++++++ 3 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 plugins/l4d2_feedthetrolls.smx create mode 100644 scripting/l4d2_feedthetrolls.sp diff --git a/README.md b/README.md index 27f485d..bfeffd6 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ All my sourcemod plugins... shitty probably * [l4d2_ai_minigun](#l4d2_ai_minigun) * [L4D2Tools](#L4D2Tools) * [l4d2_swarm](#l4d2_swarm) +* [l4d2_feedthetrolls](#l4d2_feedthetrolls) ## Descriptions ### csgo-knifehp @@ -136,4 +137,28 @@ This really only affects wandering zombies, mobs and panic events, but it may wo * `sm_swarmtoggle [range]` - Will continuously run the swarm method on the player at the range. Use the command again or type "disable" for player to disable. Switching players will not disable, just switches target. * Aliases: `sm_rushtoggle`, `sm_rt` * `sm_rushtogglemenu` - Will open a menu to quickly select a player to continuously rush. - * Aliases: `sm_rtmenu` \ No newline at end of file + * Aliases: `sm_rtmenu` + +### l4d2_feedthetrolls +This plugin allows you to enact certain troll modes on any player, some are subtle some are less so. Either way, it works great to deal with a rusher, an asshole or even your friends. + +Troll Modes: (updated 12/25/2020) +1. **SlowSpeed** (0.8 < 1.0 base) Slows down a user +2. **HigherGravity** (1.3 > 1.0) Adds more gravity to a user +3. **HalfPrimary** Sets user's primary reserve ammo in half +4. **UziRules** Picking up a weapon only gives them a UZI +5. **PrimaryDisable** Cannot pickup primary weapons at all +6. **SlowDrain** - Health slowly drains every few seconds (controlled by cvar) +7. **Clusmy** - Randomly drops any melee weapon, great with a swarm +8. **IcantSpellNoMore** (Not Implemented) - Garble their chat messages +9. **CameTooEarly** - A chance that when shooting, they empty a whole clip at once +10. **KillMeSoftly** - Makes the player eat or waste their pills +11. **ThrowItAll** - Makes a player throw all their items at any nearby players. Runs on the interval set by sm_ftt_throw_interval. + +* **Convars:** + * `sm_ftt_victims` - A comma separated list of troll targets. Unused while new version is being implemented + * `sm_ftt_throw_interval` - For troll mode 'ThrowItAll' (#11), how often players will re-throw all their items. 0 to disable +* **Commands:** + * `sm_fta ` - Applies a mode to a set of users. See list above + * `sm_ftr ` - Removes the current troll mode, deactivates any feature. + * `sm_ftl` - Lists all players that have a troll mode activated. \ No newline at end of file diff --git a/plugins/l4d2_feedthetrolls.smx b/plugins/l4d2_feedthetrolls.smx new file mode 100644 index 0000000000000000000000000000000000000000..0f1cd1c9b62da04d65fd0c68d2e980e5dbd8eb08 GIT binary patch literal 11333 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 +#include +#include +#include "jutils.inc" +#include + +#undef REQUIRE_PLUGIN +#include + +/* +1 -> Slow speed (0.8 < 1.0 base) +2 -> Higher gravity (1.3 > 1.0) +3 -> Set primary reserve ammo in half +4 -> UziRules (Pickup weapon defaults to uzi) +5 -> PrimaryDisable (Cannot pickup primary weapons at all) +6 -> Slow Drain +7 -> Clusmy +8 -> IcantSpellNoMore +9 -> CameTooEarly +10 -> KillMeSoftly +*/ +enum trollMode { + Disabled, + SlowSpeed, + HigherGravity, + HalfPrimary, + UziRules, + PrimaryDisable, + SlowDrain, + Clusmy, + iCantSpellNoMore, + CameTooEArly, + KillMeSoftly +} + +public Plugin myinfo = +{ + name = "L4D(2) Feed The Trolls", + author = "jackzmc", + description = "https://forums.alliedmods.net/showthread.php?t=325331", + version = PLUGIN_VERSION, + url = "" +}; +Handle hThrowTimer; +ConVar hVictimsList, hThrowItemInterval; +bool bTrollTargets[MAXPLAYERS+1], lateLoaded, bTimerEnabled = false; +int iTrollMode = 0; //troll mode. 0 -> Slosdown | 1 -> Higher Gravity | 2 -> CameTooEarly | 3 -> UziRules + +int g_iAmmoTable; +int iTrollUsers[MAXPLAYERS+1]; +int gChargerVictim = -1; + +bool bChooseVictimAvailable = false; + +//plugin start +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + if(late) { + lateLoaded = true; + } +} +//TODO: Register a main loop (create / destroy on troll targets count). Implement 'slow drain' with loop. + +public void OnPluginStart() { + EngineVersion g_Game = GetEngineVersion(); + if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) { + SetFailState("This plugin is for L4D/L4D2 only."); + } + TopMenu topmenu; + if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != null)) + { + /* If so, manually fire the callback */ + OnAdminMenuReady(topmenu); + } + LoadTranslations("common.phrases"); + g_iAmmoTable = FindSendPropInfo("CTerrorPlayer", "m_iAmmo"); + + hVictimsList = CreateConVar("sm_ftt_victims", "", "Comma seperated list of steamid64 targets (ex: STEAM_0:0:75141700)", FCVAR_NONE); + hVictimsList.AddChangeHook(Change_VictimList); + hThrowItemInterval = CreateConVar("sm_ftt_throw_interval", "30", "The interval in seconds to throw items. 0 to disable", FCVAR_NONE, true, 0.0); + hThrowItemInterval.AddChangeHook(Change_ThrowInterval); + + RegAdminCmd("sm_ftl", Command_ListTheTrolls, ADMFLAG_ROOT, "Lists all the trolls currently ingame."); + + RegAdminCmd("sm_ftr", Command_ResetUser, ADMFLAG_ROOT, "Reset user"); + RegAdminCmd("sm_fta", Command_ApplyUser, ADMFLAG_ROOT, "apply mode"); + + if(lateLoaded) UpdateTrollTargets(); + + CreateTimer(10.0, Timer_MainProcess, _, TIMER_REPEAT); + hThrowTimer = CreateTimer(hThrowItemInterval.FloatValue, Timer_ThrowTimer, _, TIMER_REPEAT); +} +public void OnLibraryRemoved(const char[] name) { + if (StrEqual(name, "adminmenu", false)) { + hAdminMenu = null; + } +} +public void OnAdminMenuReady(Handle aTopMenu) { + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + + /* Try to add the category first, if we want to add one. + Leave this out, if you don't add a new category. */ + if (obj_dmcommands == INVALID_TOPMENUOBJECT) { + OnAdminMenuCreated(topmenu); + } + if (topmenu == hAdminMenu) { + return; + } + hAdminMenu = topmenu; +} + + +//(dis)connection events +public void OnClientAuthorized(int client, const char[] auth) { + if(StrContains(auth, "BOT", true) == -1) { + TestForTarget(client, auth); + } +} +public void OnClientDisconnect(int client) { + bTrollTargets[client] = false; +} +// #region evrnts +public void Change_VictimList(ConVar convar, const char[] oldValue, const char[] newValue) { + UpdateTrollTargets(); +} +public void Change_ThrowInterval(ConVar convar, const char[] oldValue, const char[] newValue) { + CloseHandle(hThrowTimer); + hThrowTimer = CreateTimer(convar.FloatValue, Timer_ThrowTimer, _, TIMER_REPEAT); +} +// #endregion +// #region commands +public Action Command_ResetUser(int client, int args) { + if(args < 1) { + ReplyToCommand(client, "Usage: sm_ftr "); + }else{ + char arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + 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, + COMMAND_FILTER_ALIVE, /* Only allow alive players */ + 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++) + { + ResetClient(target_list[i]); + } + ReplyToCommand(client, "Cleared troll effects for %d players", target_count); + } + return Plugin_Handled; +} +public Action Command_ApplyUser(int client, int args) { + if(args < 2) { + ReplyToCommand(client, "Usage: sm_fta "); + }else{ + char arg1[32], arg2[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + + int mode = StringToInt(arg2); + if(mode == 0) { + ReplyToCommand(client, "Not a valid mode. Must be greater than 0. Usage: sm_fta "); + }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, + COMMAND_FILTER_ALIVE, /* Only allow alive players */ + 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++) + { + ReplyToCommand(client, "Applied troll mode %d to %N", mode, target_list[i]); + ApplyModeToClient(client, target_list[i], mode); + } + } + } + return Plugin_Handled; +} +public Action Command_ListTheTrolls(int client, int args) { + int count = 0; + for(int i = 1; i < MaxClients; i++) { + if(IsClientConnected(i) && IsPlayerAlive(i) && iTrollUsers[i] > 0) { + ReplyToCommand(client, "%N | Mode %d", i, iTrollUsers[i]); + count++; + } + } + if(count == 0) { + ReplyToCommand(client, "No clients have a mode applied."); + } + return Plugin_Handled; +} +public Action Event_ItemPickup(int client, int weapon) { + char wpnName[64]; + GetEdictClassname(weapon, wpnName, sizeof(wpnName)); + if(StrContains(wpnName, "rifle") > -1 + || StrContains(wpnName, "smg") > -1 + || StrContains(wpnName, "weapon_grenade_launcher") > -1 + || StrContains(wpnName, "sniper") > -1 + || StrContains(wpnName, "shotgun") > -1 + ) { + //If 4: Only UZI, if 5: Can't switch. + if(iTrollUsers[client] == 4) { + char currentWpn[16]; + //TODO: Fix new weapons being given when user has one?? + GetClientWeapon(client, currentWpn, sizeof(currentWpn)); + if(StrEqual(wpnName, "weapon_smg", true)) { + return StrEqual(currentWpn, "weapon_smg", true) ? Plugin_Stop : Plugin_Continue; + } else if(StrEqual(currentWpn, "weapon_smg", true)) { + return Plugin_Stop; + }else{ + int flags = GetCommandFlags("give"); + SetCommandFlags("give", flags & ~FCVAR_CHEAT); + FakeClientCommand(client, "give smg"); + SetCommandFlags("give", flags|FCVAR_CHEAT); + return Plugin_Stop; + } + }else{ + return Plugin_Stop; + } + }else{ + return Plugin_Continue; + } +} +// #endregion +// #region timer +public Action Timer_MainProcess(Handle timer) { + for(int i = 1; i < MaxClients; i++) { + if(IsClientConnected(i) && iTrollUsers[i] > 0) { + int mode = iTrollUsers[i]; + if(mode == 11) { + ThrowAllItems(i); + } + } + } +} +public Action Timer_ThrowTimer(Handle timer) { + for(int i = 1; i < MaxClients; i++) { + if(IsClientConnected(i) && iTrollUsers[i] == 11) { + ThrowAllItems(i); + } + } +} + +// #endregion +// #region methods +/* +TROLL MODES +1 -> Slow speed (0.8 < 1.0 base) +2 -> Higher gravity (1.3 > 1.0) +3 -> Set primary reserve ammo in half +4 -> UziRules (Pickup weapon defaults to uzi) +5 -> PrimaryDisable (Cannot pickup primary weapons at all) +6 -> Slow Drain +7 -> Clusmy +8 -> IcantSpellNoMore +9 -> CameTooEarly +10 -> KillMeSoftly +11 -> ThrowItAll +12 -> TakeMyPills +*/ +void ApplyModeToClient(int client, int victim, int mode) { + ResetClient(victim); + switch(mode) { + case 1: + SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 0.8); + case 2: + SetEntityGravity(victim, 1.3); + case 3: { + int current = GetPrimaryReserveAmmo(victim); + SetPrimaryReserveAmmo(victim, current / 2); + } + case 4: + SDKHook(victim, SDKHook_WeaponEquip, Event_ItemPickup); + case 5: + SDKHook(victim, SDKHook_WeaponEquip, Event_ItemPickup); + case 7: { + int wpn = GetClientSecondaryWeapon(victim); + bool hasMelee = DoesClientHaveMelee(victim); + if(hasMelee) { + float pos[3]; + int clients[4]; + GetClientAbsOrigin(victim, pos); + int clientCount = GetClientsInRange(pos, RangeType_Visibility, clients, sizeof(clients)); + for(int i = 0; i < clientCount; i++) { + if(clients[i] != victim) { + PrintToChatAll("client %d throw to %d", victim, clients[i]); + float targPos[3]; + GetClientAbsOrigin(clients[i], targPos); + SDKHooks_DropWeapon(victim, wpn, targPos); + iTrollUsers[victim] = mode; + return; + } + } + SDKHooks_DropWeapon(victim, wpn); + } + ReplyToCommand(client, "res = %d" , hasMelee ? 1 : 0); + } + case 8: + ReplyToCommand(client, "This troll mode is not implemented."); + case 11: { + if(IsFakeClient(victim)) { + ReplyToCommand(client, "This mode does not work for bots."); + return; + } + ThrowAllItems(victim); + } + default: { + ReplyToCommand(client, "Unknown troll mode: %d", mode); + PrintToServer("Unknown troll mode to apply: %d", mode); + } + } + iTrollUsers[victim] = mode; +} + +public Action Timer_GivePistol(Handle timer, int client) { + int flags = GetCommandFlags("give"); + SetCommandFlags("give", flags & ~FCVAR_CHEAT); + FakeClientCommand(client, "give pistol"); + SetCommandFlags("give", flags|FCVAR_CHEAT); +} +public Action Timer_ThrowWeapon(Handle timer, Handle pack) { + ResetPack(pack); + float dest[3]; + dest[0] = ReadPackFloat(pack); + dest[1] = ReadPackFloat(pack); + dest[2] = ReadPackFloat(pack); + int slot = ReadPackCell(pack); + int victim = ReadPackCell(pack); + + int wpnRef = GetPlayerWeaponSlot(victim, slot); + if(wpnRef != -1) { + int wpn = EntRefToEntIndex(wpnRef); + if(wpn != INVALID_ENT_REFERENCE) { + if(slot == 1) { + char name[16]; + GetEdictClassname(wpn, name, sizeof(name)); + if(!StrEqual(name, "weapon_pistol", false)) { + SDKHooks_DropWeapon(victim, wpn, dest); + CreateTimer(0.2, Timer_GivePistol, victim); + } + }else + SDKHooks_DropWeapon(victim, wpn, dest); + } + } +} + +void ResetClient(int victim) { + iTrollUsers[victim] = 0; + SetEntityGravity(victim, 1.0); + SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0); + SDKUnhook(victim, SDKHook_WeaponEquip, Event_ItemPickup); +} + +void ThrowAllItems(int victim) { + + float vicPos[3], destPos[3]; + int clients[4]; + GetClientAbsOrigin(victim, vicPos); + //Find a bot to throw to + int clientCount = GetClientsInRange(vicPos, RangeType_Visibility, clients, sizeof(clients)); + for(int i = 0; i < clientCount; i++) { + if(clients[i] != victim) { + GetClientAbsOrigin(clients[i], destPos); + break; + } + } + + //Loop all item slots + for(int slot = 0; slot <= 4; slot++) { + Handle pack; + CreateDataTimer(0.22 * float(slot), Timer_ThrowWeapon, pack); + + WritePackFloat(pack, destPos[0]); + WritePackFloat(pack, destPos[1]); + WritePackFloat(pack, destPos[2]); + WritePackCell(pack, slot); + WritePackCell(pack, victim); + + } +} + + +bool ApplyModeToTargets() { + int users = 0; + for(int i=1; i < MaxClients; i++) { + if(bTrollTargets[i]) { + users++; + //clear effects from previous troll: + SetEntityGravity(i, 1.0); + SetEntPropFloat(i, Prop_Send, "m_flLaggedMovementValue", 1.0); + + if(iTrollMode == 0) { //slow mode, apply slow down affects + SetEntPropFloat(i, Prop_Send, "m_flLaggedMovementValue", 0.8); + }else if(iTrollMode == 1) { //higher gravity + SetEntityGravity(i, 1.2); + } + } + } + //Stop loop if no one is being affected. + return (users == 0) ? true : false; + +} +void UpdateTrollTargets() { + for(int i = 1; i <= MaxClients; i++) { + bTrollTargets[i] = false; + if(IsClientInGame(i) && IsClientAuthorized(i)) { + if(!IsFakeClient(i)) { + char auth[64]; + GetClientAuthId(i, AuthId_Steam2, auth, sizeof(auth)); + TestForTarget(i, auth); + } + } + } +} +bool TestForTarget(int client, const char[] auth) { + char targets[32][8]; + char raw_targets[64]; + hVictimsList.GetString(raw_targets, sizeof(raw_targets)); + ExplodeString(raw_targets, ",", targets, 8, 32, false); + for(int i = 0; i < 8; i++) { + if(StrEqual(targets[i], auth, true)) { + #if defined debug + PrintToServer("[Debug] Troll target detected with id %d and steamid %s", client, auth); + #endif + bTrollTargets[client] = true; + if(!bTimerEnabled) { + bTimerEnabled = true; + + } + return true; + } + } + return false; +} +// #endregion +stock int GetPrimaryReserveAmmo(int client) { + int weapon = GetPlayerWeaponSlot(client, 0); + if(weapon > -1) { + int primaryAmmoType = GetEntProp(weapon, Prop_Send, "m_iPrimaryAmmoType"); + return GetEntData(client, g_iAmmoTable + (primaryAmmoType * 4)); + } else { + return -1; + } +} +stock bool SetPrimaryReserveAmmo(int client, int amount) { + int weapon = GetPlayerWeaponSlot(client, 0); + if(weapon > -1) { + int primaryAmmoType = GetEntProp(weapon, Prop_Send, "m_iPrimaryAmmoType"); + SetEntData(client, g_iAmmoTable + (primaryAmmoType * 4), amount); + return true; + } else { + return false; + } +}