Update FTT

This commit is contained in:
Jackzie 2021-11-09 10:25:34 -06:00
parent 956df81ba8
commit ca2831ec53
No known key found for this signature in database
GPG key ID: 1E834FE36520537A
10 changed files with 271 additions and 127 deletions

View file

@ -4,13 +4,14 @@
//Allow MAX_TROLLS to be defined elsewhere
#if defined MAX_TROLLS
#else
#define MAX_TROLLS 32
#define MAX_TROLLS 33
#endif
enum trollModifier {
TrollMod_Invalid = 0,
TrollMod_Instant = 1,
TrollMod_Constant = 2
TrollMod_Instant = 1 << 0,
TrollMod_Constant = 1 << 1,
TrollMod_PlayerOnly = 1 << 2, // Does the troll only work on players, not bots? If set, troll only applied on real user. If not, troll applied to both bot and idler
}
//up to 30 flags technically possiible
@ -32,6 +33,7 @@ char DEFAULT_FLAG_PROMPT[] = "Choose flags";
bool SilentMenuSelected[MAXPLAYERS+1];
static int g_trollAddPromptIndex;
ArrayList gRandomClients;
enum struct TrollFlagPrompt {
char promptText[MAX_TROLL_FLAG_LENGTH];
@ -57,13 +59,14 @@ enum struct Troll {
char name[MAX_TROLL_NAME_LENGTH];
char description[128];
bool hidden;
int mods;
// Flags
int activeFlagClients[MAXPLAYERS+1];
ArrayList flagNames;
bool flagsMultiselectable;
char flagPrompt[MAX_TROLL_FLAG_LENGTH];
ArrayList flagNames;
ArrayList flagPrompts;
bool HasMod(trollModifier mod) {
@ -177,6 +180,7 @@ enum struct Troll {
void Activate(int client, int activator, trollModifier modifier = TrollMod_Invalid, int flags = 0) {
if(modifier == TrollMod_Invalid) modifier = this.GetDefaultMod();
// Sadly, unable to pass in <this> to ApplyTroll, so it has to do unnecessary lookup via string
ApplyTroll(client, this.name, activator, modifier, flags);
}
@ -195,6 +199,17 @@ enum struct Troll {
bool IsActive(int client) {
return this.activeFlagClients[client] != -1;
}
int GetRandomClient(int start = 0) {
gRandomClients.Clear();
for(int i = start + 1; i <= MaxClients; i++) {
if(this.activeFlagClients[i] != -1) {
gRandomClients.Push(i);
}
}
if(gRandomClients.Length == 0) return -1;
return GetRandomInt(0, gRandomClients.Length);
}
}
Troll Trolls[MAX_TROLLS+1];
@ -203,12 +218,13 @@ ArrayList categories;
static int categoryID = -1;
void ResetClient(int victim, bool wipe = true) {
if(victim == 0) return;
if(victim == 0 || !IsClientConnected(victim)) return;
if(wipe) {
for(int i = 0; i <= MAX_TROLLS; i++) {
Trolls[i].activeFlagClients[victim] = -1;
}
}
SetEntityGravity(victim, 1.0);
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
SDKUnhook(victim, SDKHook_WeaponCanUse, Event_ItemPickup);
@ -217,14 +233,14 @@ void ResetClient(int victim, bool wipe = true) {
SDKUnhook(wpn, SDKHook_Reload, Event_WeaponReload);
}
int SetupTroll(const char[] name, const char description[128], int mods, bool flagsMultiselectable = false) {
if(mods == 0) {
ThrowError("Troll \"%s\" has no flags defined.", name);
return -1;
}
int SetupTroll(const char[] name, const char description[128], int mods) {
static int i = 0;
if(i == MAX_TROLLS + 1) {
if(mods == 0) {
ThrowError("Troll \"%s\" has no modifiers defined.", name);
return -1;
} else if(i == MAX_TROLLS + 1) {
ThrowError("Maximum number of trolls (%d) reached. Up MAX_TROLLS value.", MAX_TROLLS);
return -1;
}
g_trollAddPromptIndex = 0;
Trolls[i].id = i;
@ -232,7 +248,6 @@ int SetupTroll(const char[] name, const char description[128], int mods, bool fl
strcopy(Trolls[i].description, 128, description);
Trolls[i].categoryID = categoryID;
Trolls[i].mods = mods;
Trolls[i].flagsMultiselectable = flagsMultiselectable;
Trolls[i].flagPrompts = new ArrayList(sizeof(TrollFlagPrompt));
strcopy(trollIds[i], MAX_TROLL_NAME_LENGTH, name);
@ -284,7 +299,7 @@ void ToggleTroll(int client, const char[] name, int flags = 0) {
troll.activeFlagClients[client] = flags;
}
void ApplyTroll(int victim, const char[] name, int activator, trollModifier modifier, int flags = 0) {
void ApplyTroll(int victim, const char[] name, int activator, trollModifier modifier, int flags = 0, bool silent = false) {
static Troll troll;
int trollIndex = GetTroll(name, troll);
if(trollIndex == -1) {
@ -292,21 +307,46 @@ void ApplyTroll(int victim, const char[] name, int activator, trollModifier modi
return;
}
if(GetClientTeam(victim) == 1) {
//Victim is spectating, find its bot
victim = FindIdlePlayerBot(victim);
}
if(!silent && SilentMenuSelected[activator]) silent = true;
static int MetaInverseTrollID;
if(!MetaInverseTrollID) MetaInverseTrollID = GetTrollID("Meta: Inverse");
if(activator > 0 && Trolls[MetaInverseTrollID].IsActive(activator)) {
float max = 1.0;
if(Trolls[MetaInverseTrollID].activeFlagClients[activator] & 2) max = 0.5;
else if(Trolls[MetaInverseTrollID].activeFlagClients[activator] & 4) max = 0.1;
if(GetRandomFloat() <= max) {
victim = activator;
}
}
// If victim is a survivor bot, check if has an idle player
if(IsFakeClient(victim) && GetClientTeam(victim) == 2) {
int player = GetSpectatorClient(victim);
if(player > 0) {
// If there is an idle player, apply troll to them
ApplyTroll(player, name, activator, modifier, flags, silent);
// And continue IF there is TrollMod_PlayerOnly mod
if(troll.mods & view_as<int>(TrollMod_PlayerOnly)) return;
// Don't want to show two logs, so just ignore the bot
silent = true;
}
}
// Applies any custom logic needed for a troll, mostly only used for TrollMod_Instant
if(!ApplyAffect(victim, troll, activator, modifier, flags)) {
return;
}
// Log all actions, indicating if constant or single-fire, and if any flags
bool isActive = IsTrollActive(victim, troll.name);
if(!SilentMenuSelected[activator]) {
if(!silent) {
if(isActive) {
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) {
if(modifier & TrollMod_Constant) {
if(flags > 0) {
ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" (%d) for %N. ", troll.name, flags, victim);
} else
@ -318,8 +358,11 @@ void ApplyTroll(int victim, const char[] name, int activator, trollModifier modi
LogAction(activator, victim, "\"%L\" activated troll \"%s\" (%d) for \"%L\"", activator, troll.name, flags, victim);
}
} else SilentMenuSelected[activator] = false;
if(modifier == TrollMod_Constant) {
} else {
ReplyToCommand(activator, "apply troll \"%s\" flags=%d on %N", troll.name, flags, victim);
}
// Toggle on flags for client, if it's not a single run.
if(modifier & TrollMod_Constant) {
Trolls[troll.id].activeFlagClients[victim] = isActive ? -1 : flags;
}
}

View file

@ -13,6 +13,10 @@ public Action Command_InstaSpecial(int client, int args) {
menu.ExitButton = true;
menu.Display(client, 0);
} else {
if(gInstaSpecialType > -1) {
ReplyToCommand(client, "Please wait for last Insta to spawn.");
return Plugin_Handled;
}
char arg1[32], arg2[32] = "jockey";
GetCmdArg(1, arg1, sizeof(arg1));
if(args >= 2) {
@ -78,6 +82,10 @@ public Action Command_InstaSpecialFace(int client, int args) {
menu.ExitButton = true;
menu.Display(client, 0);
} else {
if(gInstaSpecialType > -1) {
ReplyToCommand(client, "Please wait for last Insta to spawn.");
return Plugin_Handled;
}
char arg1[32], arg2[32] = "jockey";
GetCmdArg(1, arg1, sizeof(arg1));
if(args >= 2) {
@ -215,6 +223,7 @@ public Action Command_ResetUser(int client, int args) {
public Action Command_ApplyUser(int client, int args) {
if(args < 1) {
SilentMenuSelected[client] = false;
ShowTrollMenu(client);
}else{
char arg1[32], arg2[16];
@ -244,12 +253,14 @@ public Action Command_ApplyUser(int client, int args) {
for(int i = 0; i < categories.Length; i++) {
categories.GetString(i, key, sizeof(key));
if(StrEqual(key, arg2, false)) {
SilentMenuSelected[client] = false;
ShowTrollsForCategory(client, GetClientUserId(target_list[0]), i);
return Plugin_Handled;
}
}
ReplyToCommand(client, "[FTT] Unknown category: '%s'", arg2);
}
SilentMenuSelected[client] = false;
SetupCategoryMenu(client, target_list[0]);
}
return Plugin_Handled;
@ -304,6 +315,7 @@ public Action Command_ListModes(int client, int args) {
static Troll troll;
for(int i = 0; i <= MAX_TROLLS; i++) {
GetTrollByKeyIndex(i, troll);
if(troll.hidden) continue;
ReplyToCommand(client, "%d. %s - %s", i, troll.name, troll.description);
}
return Plugin_Handled;
@ -346,6 +358,7 @@ public Action Command_ListTheTrolls(int client, int args) {
}
for(int j = 1; j <= MAX_TROLLS; j++) {
if(Trolls[j].hidden) continue;
if(trollIds[j][0] != '\0' && IsTrollActive(target, trollIds[j])) {
if(Trolls[j].activeFlagClients[target] > 0) {
static char list[MAX_TROLL_FLAG_LENGTH*8]; //May in future need to up magic number 8 (supports 8 active flags )
@ -423,7 +436,7 @@ public Action Command_MarkPendingTroll(int client, int args) {
menu.ExitButton = true;
menu.Display(client, 0);
} else {
char arg1[32];
static char arg1[32];
GetCmdArg(1, arg1, sizeof(arg1));
char target_name[MAX_TARGET_LENGTH];
int target_list[MAXPLAYERS], target_count;
@ -452,13 +465,45 @@ public Action Command_MarkPendingTroll(int client, int args) {
return Plugin_Handled;
}
public Action Command_MarkNoob(int client, int args) {
if(args == 0) {
ReplyToCommand(client, "sm_noob <player>");
return Plugin_Handled;
}
static char target_name[MAX_TARGET_LENGTH];
GetCmdArg(1, target_name, sizeof(target_name));
int target_list[MAXPLAYERS], target_count;
bool tn_is_ml;
if ((target_count = ProcessTargetString(
target_name,
client,
target_list,
1,
COMMAND_FILTER_NO_MULTI, /* 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;
}
int target = target_list[0];
//Todo: Check if marked as noob or not, undo if so, add if not
ShowActivityEx(client, "[FTT] ", "marked %N as a noob", target_name);
LogAction(client, target, "\"%L\" marked \"%L\" as a noob", client, target);
return Plugin_Handled;
}
public Action Command_FeedTheTrollMenu(int client, int args) {
ReplyToCommand(client, "sm_ftl - Lists all the active trolls on players");
ReplyToCommand(client, "sm_ftm - Lists all available troll modes & descriptions");
ReplyToCommand(client, "sm_ftr - Resets target users' of their trolls");
ReplyToCommand(client, "sm_fta - Applies a troll mode on targets");
ReplyToCommand(client, "sm_ftt - Opens this menu");
ReplyToCommand(client, "sm_ftc - Will apply a punishment to last crescendo activator");
ReplyToCommand(client, "sm_ftl [player(s)] - Lists all the active trolls on players. Will show flag names if a player is specified.");
ReplyToCommand(client, "sm_ftm - Lists all available trolls & descriptions");
ReplyToCommand(client, "sm_ftr <player(s)> - Resets target users' of any active trolls");
ReplyToCommand(client, "sm_fta [player] [category] - Apply a troll on a player, with optional shortcut to player and/or category");
ReplyToCommand(client, "sm_ftt - Shows this text");
ReplyToCommand(client, "sm_ftc - Will apply a punishment to the last crescendo/event activator");
ReplyToCommand(client, "sm_mark - Marks the user to be banned on disconnect, prevents their FF.");
return Plugin_Handled;
}

View file

@ -19,6 +19,29 @@ public void OnMapStart() {
public void OnClientPutInServer(int client) {
g_PendingBanTroll[client] = 0;
SDKHook(client, SDKHook_OnTakeDamage, Event_TakeDamage);
}
public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("userid");
CreateTimer(0.1, Timer_CheckSpecial, userid);
}
public Action Timer_CheckSpecial(Handle h, int specialID) {
int special = GetClientOfUserId(specialID);
if(special > 0 && gInstaSpecialType > -1 && IsFakeClient(special) && GetClientTeam(special) == 3) {
int type = GetEntProp(special, Prop_Send, "m_zombieClass");
if(type == gInstaSpecialType) {
gInstaSpecialType = -1;
g_iAttackerTarget[special] = gInstaSpecialTarget;
gInstaSpecialMagnet[GetClientOfUserId(gInstaSpecialTarget)]++;
TeleportEntity(special, gInstaSpecialSpawnPos, gInstaSpecialSpawnAng, NULL_VECTOR);
if(gInstaSpecialInstaKill) {
SDKHooks_TakeDamage(special, special, special, 1000.0);
}
}
}
}
public void Frame_InstaSpawned(int special) {
}
public void OnClientAuthorized(int client, const char[] auth) {
if(!IsFakeClient(client)) {
@ -39,6 +62,14 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa
}
public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(client > 0 && g_iAttackerTarget[client] > 0) {
int target = GetClientOfUserId(g_iAttackerTarget[client]);
gInstaSpecialMagnet[target]--;
if(gInstaSpecialMagnet[target] == 0) {
PrintToServer("[FTT] gInstaSpecialMagnet droped below 0");
gInstaSpecialMagnet[target] = 0;
}
}
g_iAttackerTarget[client] = 0;
}
public Action Event_WeaponReload(int weapon) {
@ -85,11 +116,14 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
if(hMagnetChance.FloatValue < GetRandomFloat()) return Plugin_Continue;
L4D2Infected class = view_as<L4D2Infected>(GetEntProp(attacker, Prop_Send, "m_zombieClass"));
int existingTarget = GetClientOfUserId(g_iAttackerTarget[attacker]);
if(existingTarget > 0 && IsPlayerAlive(existingTarget) && (hMagnetTargetMode.IntValue & 1 != 1 || !IsPlayerIncapped(existingTarget))) {
if(class == L4D2Infected_Tank && (hMagnetTargetMode.IntValue % 2 != 2 || !IsPlayerIncapped(existingTarget))) {
if(existingTarget > 0 && IsPlayerAlive(existingTarget)) {
if(gInstaSpecialMagnet[existingTarget] > 0) {
curTarget = existingTarget;
return Plugin_Changed;
}else if(hMagnetTargetMode.IntValue & 1 != 1 || !IsPlayerIncapped(existingTarget)) {
} else if(class == L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 2) && WillMagnetRun(Trolls[tankMagnetID], existingTarget)) {
curTarget = existingTarget;
return Plugin_Changed;
}else if((!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 1) && WillMagnetRun(Trolls[spMagnetID], existingTarget)) {
curTarget = existingTarget;
return Plugin_Changed;
}
@ -102,7 +136,7 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
//Ignore incapped players if turned on:
if(IsPlayerIncapped(i)) {
if((class == L4D2Infected_Tank && hMagnetTargetMode.IntValue & 2 == 2) || hMagnetTargetMode.IntValue & 1 == 1 ) continue;
if((class == L4D2Infected_Tank && hMagnetTargetMode.IntValue & 2) || (class != L4D2Infected_Tank && hMagnetTargetMode.IntValue & 1)) continue;
}
if(class == L4D2Infected_Tank && Trolls[tankMagnetID].IsActive(i) || (class != L4D2Infected_Tank && Trolls[spMagnetID].IsActive(i))) {
@ -355,10 +389,10 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
return Plugin_Continue;
}
static int invertedTrollIndex;
if(invertedTrollIndex <= 0) {
if(invertedTrollIndex == 0) {
invertedTrollIndex = GetTrollID("Inverted Controls");
}
if(IsTrollActiveByRawID(client, invertedTrollIndex)) {
if(Trolls[invertedTrollIndex].IsActive(client)) {
if(buttons & IN_MOVELEFT || buttons & IN_MOVERIGHT) {
vel[1] = -vel[1];
}
@ -397,7 +431,7 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float&
if(IsTrollActive(victim, "Damage Boost")) {
damage * 2;
return Plugin_Changed;
} else if(Trolls[reverseFF].IsActive(attacker) && attacker != victim && GetClientTeam(attacker) != GetClientTeam(victim)) {
} else if(Trolls[reverseFF].IsActive(attacker) && attacker != victim && GetClientTeam(attacker) == GetClientTeam(victim)) {
float returnDmg = damage; //default is 1:1
if(Trolls[reverseFF].activeFlagClients[attacker] & 4) {
returnDmg /= 2.0;
@ -413,6 +447,8 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float&
}
public Action SoundHook(int[] clients, int& numClients, char sample[PLATFORM_MAX_PATH], int& entity, int& channel, float& volume, int& level, int& pitch, int& flags, char[] soundEntry, int& seed) {
static int honkID;
if(honkID == 0) honkID = GetTrollID("Honk / Meow");
if(lastButtonUser > -1 && StrEqual(sample, "npc/mega_mob/mega_mob_incoming.wav")) {
PrintToConsoleAll("CRESCENDO STARTED BY %N", lastButtonUser);
#if defined DEBUG
@ -429,22 +465,16 @@ public Action SoundHook(int[] clients, int& numClients, char sample[PLATFORM_MAX
lastButtonUser = -1;
}else if(numClients > 0 && entity > 0 && entity <= MaxClients) {
if(StrContains(sample, "survivor\\voice") > -1) {
static int honkID;
if(honkID == 0) {
honkID = GetTrollID("Honk / Meow");
}
if(Trolls[honkID].IsActive(entity)) {
if(Trolls[honkID].activeFlagClients[entity] & 1)
strcopy(sample, sizeof(sample), "player/footsteps/clown/concrete1.wav");
else if(Trolls[honkID].activeFlagClients[entity] & 2)
strcopy(sample, sizeof(sample), "custom/meow1.mp3");
else return Plugin_Continue;
return Plugin_Changed;
} else if(IsTrollActive(entity, "Vocalize Gag")) {
return Plugin_Handled;
}
}
}
return Plugin_Continue;
}
@ -457,7 +487,7 @@ public Action Event_WitchVictimSet(Event event, const char[] name, bool dontBroa
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
//Ignore incapped players if hWitchIgnoreIncapp turned on:
if(IsPlayerIncapped(i) && !hWitchTargetIncapp.BoolValue) {
if(IsPlayerIncapped(i) && ~hMagnetTargetMode.IntValue & 4) {
continue;
}

View file

@ -227,14 +227,15 @@ public int ChooseTrollFlagHandler(Menu menu, MenuAction action, int param1, int
if (action == MenuAction_Select) {
static char info[32];
menu.GetItem(param2, info, sizeof(info));
static char str[5][8];
ExplodeString(info, "|", str, 5, 8, false);
static char str[6][8];
ExplodeString(info, "|", str, 6, 8, false);
int userid = StringToInt(str[0]);
int client = GetClientOfUserId(userid);
int keyIndex = StringToInt(str[1]);
int modifiers = StringToInt(str[2]);
int flags = StringToInt(str[3]);
int nextIndex = StringToInt(str[4]);
int index = StringToInt(str[4]);
bool isDone = StringToInt(str[5]) == 1; // 0 = cont, 1 = done
if(client == 0) {
ReplyToCommand(param1, "FTT: Could not acquire player");
@ -247,13 +248,17 @@ public int ChooseTrollFlagHandler(Menu menu, MenuAction action, int param1, int
static TrollFlagPrompt prompt;
// If told to go to next prompt, find the next VALID prompt
// Valid prompt is one where the required flags for it, are active
if(nextIndex != -1) {
nextIndex = GetNextPrompt(troll, flags, nextIndex);
if(isDone || index == -1) {
int nextIndex = GetNextPrompt(troll, flags, index);
// If there is a prompt available, show it, else fall down
if(nextIndex != -1) {
ShowSelectFlagMenu(param1, userid, modifiers, troll, flags, nextIndex);
return;
}
// else fall through & apply
} else {
ShowSelectFlagMenu(param1, userid, modifiers, troll, flags, index);
return;
}
// Done with prompts, apply flags & modifiers
@ -302,6 +307,7 @@ void ShowTrollMenu(int client) {
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
IntToString(GetClientUserId(i), userid, sizeof(userid));
int specClient = GetSpectatorClient(i);
// Incase player is idle, grab their bot instead of them
if(specClient > 0)
Format(display, sizeof(display), "%N (Idle)", specClient);
else
@ -328,6 +334,8 @@ void ShowTrollsForCategory(int client, int userid, int category) {
static char name[MAX_TROLL_NAME_LENGTH+8];
for(int i = 0; i < trollKV.Size; i++) {
GetTrollByKeyIndex(i, troll);
// If troll is hidden and using normal menu, do not show
if(troll.hidden && !SilentMenuSelected[client]) continue;
if(troll.categoryID == category) {
Format(info, sizeof(info), "%d|%d", userid, i);
if(troll.IsActive(victim)) {
@ -356,19 +364,11 @@ void ShowSelectFlagMenu(int activator, int victimUserID, int modifiers, Troll tr
Format(info, sizeof(info), "%s: %s", troll.name, info);
flagMenu.SetTitle(info);
// If there is another prompt, go to this index, or be done (-1)
int nextIndex = (promptIndex < troll.flagPrompts.Length - 1) ? promptIndex + 1 : -1;
if(prompt.multiselect) {
if(prevFlags == 0) prevFlags = prompt.defaults;
// If there is a next prompt (even if may not be suitable), show a "next prompt" msg instead
Format(info, sizeof(info), "%d|%d|%d|%d|%d", victimUserID, troll.id, modifiers, prevFlags, nextIndex);
if(nextIndex == -1)
flagMenu.AddItem(info, "Apply Troll / Finish");
else
flagMenu.AddItem(info, "Next Prompt");
Format(info, sizeof(info), "%d|%d|%d|%d|%d|1", victimUserID, troll.id, modifiers, prevFlags, promptIndex);
flagMenu.AddItem(info, "Apply / Next Prompt");
for(int i = 0; i < troll.flagNames.Length; i++) {
int a = 1 << i;
@ -378,21 +378,22 @@ void ShowSelectFlagMenu(int activator, int victimUserID, int modifiers, Troll tr
if(prevFlags > 0 && prevFlags & a)
Format(name, sizeof(name), "%s (On)", name);
int newFlags = prevFlags ^ a; //Toggle the flag instead of setting like below, as it's toggleable here
Format(info, sizeof(info), "%d|%d|%d|%d|%d", victimUserID, troll.id, modifiers, newFlags, promptIndex);
Format(info, sizeof(info), "%d|%d|%d|%d|%d|0", victimUserID, troll.id, modifiers, newFlags, promptIndex);
flagMenu.AddItem(info, name);
}
}
} else {
// Single choice only
for(int i = 0; i < troll.flagNames.Length; i++) {
if(prompt.flags & 1 << i) {
int a = 1 << i;
if(prompt.flags & a) {
troll.flagNames.GetString(i, name, sizeof(name));
// Add (default) indicator
if(prompt.defaults & 1 << i)
if(prompt.defaults & a)
Format(name, sizeof(name), "%s (default)", name);
int newFlags = prevFlags | 1 << i; //Set flag with any from previous prompts
Format(info, sizeof(info), "%d|%d|%d|%d|%d", victimUserID, troll.id, modifiers, newFlags, nextIndex);
int newFlags = prevFlags | a; //Set flag with any from previous prompts
Format(info, sizeof(info), "%d|%d|%d|%d|%d|1", victimUserID, troll.id, modifiers, newFlags, promptIndex);
flagMenu.AddItem(info, name);
}
}
@ -433,7 +434,8 @@ void ShowThrowItAllMenu(int client, int userid) {
int GetNextPrompt(Troll troll, int flags, int currentPrompt = 0) {
static TrollFlagPrompt prompt;
//If this prompt requires flags but they don't exist, skip to next that is valid or be done:
for(int i = currentPrompt; i < troll.flagPrompts.Length; i++) {
if(currentPrompt + 1 == troll.flagPrompts.Length) return -1;
for(int i = currentPrompt + 1; i < troll.flagPrompts.Length; i++) {
troll.GetFlagPrompt(i, prompt);
if(prompt.requireFlags & flags) {
return i;

View file

@ -53,16 +53,18 @@ bool ToggleMarkPlayer(int client, int target) {
}
}
stock int FindIdlePlayerBot(int client) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsFakeClient(i)) {
int user = GetEntProp(i, Prop_Send, "m_humanSpectatorUserID");
int bot = GetClientOfUserId(user);
return bot > 0 ? bot : client;
}
// Finds the survivor bot that took over an idle player
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 client;
return -1;
}
stock bool IsPlayerIncapped(int client) {
return GetEntProp(client, Prop_Send, "m_isIncapacitated") == 1;
}
@ -237,15 +239,4 @@ void DropItem(int victim, int slot) {
if(slot != 1 || DoesClientHaveMelee(victim)) {
SDKHooks_DropWeapon(victim, wpn, NULL_VECTOR);
}
}
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;
}

View file

@ -43,6 +43,8 @@ bool SpawnSpecialInFace(int target, int specialType) {
testPos = pos;
GetClientAbsOrigin(target, pos);
GetClientEyeAngles(target, ang);
if(specialType == 2)
gInstaSpecialInstaKill = true;
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);
@ -57,14 +59,15 @@ bool SpawnSpecialInFace(int target, int specialType) {
pos[2] += 1.0;
NegateVector(ang);
return SpawnSpecialInternal(specialType, target, pos, NULL_VECTOR) > 0;
return SpawnSpecialInternal(specialType, target, pos, NULL_VECTOR) != -1;
}
bool SpawnSpecialNear(int target, int specialType) {
gInstaSpecialInstaKill = false;
if(specialType > 8) return false;
static float pos[3];
if(L4D_GetRandomPZSpawnPosition(target, specialType, 10, pos)) {
return SpawnSpecialInternal(specialType, target, pos, NULL_VECTOR) > 0;
return SpawnSpecialInternal(specialType, target, pos, NULL_VECTOR) != -1;
}
return false;
}
@ -80,11 +83,19 @@ void BypassLimit() {
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;
// Bypass limit:
gInstaSpecialType = type;
gInstaSpecialTarget = GetClientUserId(target);
gInstaSpecialSpawnPos = pos;
gInstaSpecialSpawnAng = pos;
CreateTimer(2.0, Timer_InstaFailed);
int bot = CreateFakeClient("ManualDirectorBot");
if (bot != 0) {
ChangeClientTeam(bot, 3);
CreateTimer(0.1, Timer_KickBot, bot);
}
CheatCommand(target, "z_spawn_old", SPECIAL_NAMES[type-1], "auto");
return 0;
}
else if(type == 7) {
int witch = L4D2_SpawnWitch(pos, ang);
@ -101,4 +112,10 @@ int SpawnSpecialInternal(int type, int target, float pos[3], float ang[3]) {
return tank;
}
else return -1;
}
}
/* TODO: Bypass limit:
Spawn special auto, far away, mark with global vars:
1. special type (check)
2. its target
3. float[3] to TP it to

View file

@ -100,4 +100,11 @@ public Action Timer_KickBot(Handle timer, int client) {
if(IsClientInGame(client) && (!IsClientInKickQueue(client))) {
if(IsFakeClient(client)) KickClient(client);
}
}
public Action Timer_InstaFailed(Handle h) {
if(gInstaSpecialType != -1) {
gInstaSpecialType = -1;
gInstaSpecialTarget = 0;
}
}

View file

@ -3,15 +3,16 @@
void SetupTrolls() {
trollKV = new StringMap();
categories = new ArrayList(ByteCountToCells(16));
gRandomClients = new ArrayList();
int index;
SetupTroll("Reset User", "Resets the user, removes all troll effects", TrollMod_Instant);
SetCategory("Magnets");
index = SetupTroll("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant, false);
index = SetupTroll("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(index);
index = SetupTroll("Tank Magnet", "Attracts ALL tanks to any alive target with this troll enabled", TrollMod_Constant, false);
index = SetupTroll("Tank Magnet", "Attracts ALL tanks to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(index);
index = SetupTroll("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant, false);
index = 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);
@ -21,12 +22,12 @@ void SetupTrolls() {
SetupTroll("Goo", "Spawns a spitter puddle underneath them", TrollMod_Instant);
SetCategory("Items");
index = SetupTroll("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant, false);
index = SetupTroll("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant);
//Can't add directly, is custom troll:
// Trolls[index].AddFlag("Throw to Admin", true);
// Trolls[index].AddFlag("Drop At Feet", false);
// Trolls[index].AddFlag("Drop At Admin", false);
index = SetupTroll("Bad Throw", "Player drops throwables on throw, and biles/molotovs themselves", TrollMod_Constant, true);
index = SetupTroll("Bad Throw", "Player drops throwables on throw, and biles/molotovs themselves", TrollMod_Constant | TrollMod_PlayerOnly);
Trolls[index].AddFlagPrompt(true);
Trolls[index].AddFlag("Biles", true);
Trolls[index].AddFlag("Molotovs", true);
@ -42,8 +43,8 @@ void SetupTrolls() {
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_Constant);
index = SetupTroll("No Profanity", "Replaces some words with random phrases", TrollMod_Constant);
SetupTroll("iCantSpellNoMore", "Chat messages letter will randomly changed with wrong letters", TrollMod_Constant | TrollMod_PlayerOnly);
index = SetupTroll("No Profanity", "Replaces some words with random phrases", TrollMod_Constant | TrollMod_PlayerOnly);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Only Replace Swears", false);
Trolls[index].AddFlag("Replace Full Messages", true);
@ -51,21 +52,21 @@ void SetupTrolls() {
Trolls[index].AddFlag("Show Modified to Them", true);
Trolls[index].AddFlag("Show Original to Them", false);
SetupTroll("Vocalize Gag", "Prevents player from sending any vocalizations (even automatic)", TrollMod_Constant);
index = SetupTroll("Honk / Meow", "Honk or Meow", TrollMod_Constant, false);
index = SetupTroll("Honk / Meow", "Honk or Meow", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Choose Sound Type:");
Trolls[index].AddFlag("Honk", true);
Trolls[index].AddFlag("Meow", false);
Trolls[index].AddCustomFlagPrompt("Choose Chat modifier:", false, 1);
Trolls[index].AddFlag("Show Modified to Them", true);
Trolls[index].AddFlag("Show Original to Them", false);
SetupTroll("Reversed", "Reserves their message", TrollMod_Constant);
SetupTroll("Reversed", "Reserves their message", TrollMod_Constant | TrollMod_PlayerOnly);
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);
index = SetupTroll("Reverse FF", "All damage dealt to a player is reversed", TrollMod_Constant, false);
index = SetupTroll("Reverse FF", "All damage dealt to a player is reversed", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Choose Reverse FF", false);
Trolls[index].AddFlag("1:1 Ratio", true);
Trolls[index].AddFlag("2x Ratio", false);
@ -76,9 +77,15 @@ void SetupTrolls() {
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);
SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant | TrollMod_PlayerOnly);
SetupTroll("Inverted Controls", "Well, aint it obvious", TrollMod_Constant | TrollMod_PlayerOnly);
SetupTroll("Stagger", "Like a slap, but different", TrollMod_Instant);
index = SetupTroll("Meta: Inverse", "Uhm you are not supposed to see this...", TrollMod_Instant | TrollMod_PlayerOnly);
Trolls[index].hidden = true;
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("100%", true);
Trolls[index].AddFlag("50%", false);
Trolls[index].AddFlag("10%", false);
//INFO: UP MAX_TROLLS when adding new trolls!
@ -142,13 +149,13 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
//TODO: Implement TrollMod_Constant
return false;
} else if(StrEqual(troll.name, "Throw It All")) {
if(modifier == TrollMod_Instant)
if(modifier & TrollMod_Instant)
ThrowAllItems(victim);
if(hThrowTimer == INVALID_HANDLE && modifier == TrollMod_Constant) {
if(hThrowTimer == INVALID_HANDLE && modifier & TrollMod_Constant) {
hThrowTimer = CreateTimer(hThrowItemInterval.FloatValue, Timer_ThrowTimer, _, TIMER_REPEAT);
}
} else if(StrEqual(troll.name, "Swarm")) {
if(modifier == TrollMod_Instant) {
if(modifier & TrollMod_Instant) {
L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", victim, 15000);
}
return true;
@ -163,10 +170,10 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
} else if(StrEqual(troll.name, "Vomit Player"))
L4D_CTerrorPlayer_OnVomitedUpon(victim, victim);
else if(StrEqual(troll.name, "Inface Special")) {
FakeClientCommand(victim, "sm_inface");
FakeClientCommand(activator, "sm_inface");
return false;
} else if(StrEqual(troll.name, "Insta Special")) {
FakeClientCommand(victim, "sm_insta");
FakeClientCommand(activator, "sm_insta");
return false;
} else if(StrEqual(troll.name, "Goo")) {
static float pos[3], ang[3];

View file

@ -38,14 +38,16 @@ public void OnPluginStart() {
SetFailState("This plugin is for L4D/L4D2 only.");
}
LoadTranslations("common.phrases");
g_iAmmoTable = FindSendPropInfo("CTerrorPlayer", "m_iAmmo");
g_PlayerMarkedForward = new GlobalForward("FTT_OnClientMarked", ET_Ignore, Param_Cell, Param_Cell);
// Load core things (trolls & phrases):
REPLACEMENT_PHRASES = new StringMap();
LoadPhrases();
SetupTrolls();
// Witch target overwrite stuff:
GameData data = new GameData("l4d2_behavior");
StartPrepSDKCall(SDKCall_Raw);
@ -60,24 +62,25 @@ public void OnPluginStart() {
hAutoPunish = CreateConVar("sm_ftt_autopunish_action", "0", "Setup automatic punishment of players. Add bits together\n0=Disabled, 1=Tank magnet, 2=Special magnet, 4=Swarm, 8=InstantVomit", FCVAR_NONE, true, 0.0);
hAutoPunishExpire = CreateConVar("sm_ftt_autopunish_expire", "0", "How many minutes of gametime until autopunish is turned off? 0 for never.", FCVAR_NONE, true, 0.0);
hMagnetChance = CreateConVar("sm_ftt_magnet_chance", "1.0", "% of the time that the magnet will work on a player.", FCVAR_NONE, true, 0.0, true, 1.0);
hMagnetTargetMode = CreateConVar("sm_ftt_magnet_targetting", "1", "How does the specials target players. Add bits together\n0= Target until Dead, 1=Specials ignore incapped, 2=Tank ignores incapped");
hMagnetTargetMode = CreateConVar("sm_ftt_magnet_targetting", "6", "How does the specials target players. Add bits together\n0=Incapped are ignored, 1=Specials targets incapped, 2=Tank targets incapped 4=Witch targets incapped");
hShoveFailChance = CreateConVar("sm_ftt_shove_fail_chance", "0.65", "The % chance that a shove fails", FCVAR_NONE, true, 0.0, true, 1.0);
hWitchTargetIncapp = CreateConVar("sm_ftt_witch_target_incapped", "1", "Should the witch target witch magnet victims who are incapped?\n 0 = No, 1 = Yes", 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);
RegAdminCmd("sm_ftl", Command_ListTheTrolls, ADMFLAG_KICK, "Lists all the trolls currently ingame.");
RegAdminCmd("sm_ftm", Command_ListModes, ADMFLAG_KICK, "Lists all the troll modes and their description");
RegAdminCmd("sm_ftr", Command_ResetUser, ADMFLAG_KICK, "Resets user of any troll effects.");
RegAdminCmd("sm_fta", Command_ApplyUser, ADMFLAG_KICK, "Apply a troll mod to a player, or shows menu if no parameters.");
RegAdminCmd("sm_ftas", Command_ApplyUserSilent, ADMFLAG_CHEATS, "Apply a troll mod to a player, or shows menu if no parameters.");
RegAdminCmd("sm_ftt", Command_FeedTheTrollMenu, ADMFLAG_KICK, "Opens a list that shows all the commands");
RegAdminCmd("sm_ftl", Command_ListTheTrolls, ADMFLAG_KICK, "Lists all the trolls currently ingame.");
RegAdminCmd("sm_ftm", Command_ListModes, ADMFLAG_KICK, "Lists all the troll modes and their description");
RegAdminCmd("sm_ftr", Command_ResetUser, ADMFLAG_KICK, "Resets user of any troll effects.");
RegAdminCmd("sm_fta", Command_ApplyUser, ADMFLAG_KICK, "Apply a troll mod to a player, or shows menu if no parameters.");
RegAdminCmd("sm_ftas", Command_ApplyUserSilent, ADMFLAG_CHEATS, "Apply a troll mod to a player, or shows menu if no parameters.");
RegAdminCmd("sm_ftt", Command_FeedTheTrollMenu, ADMFLAG_KICK, "Opens a list that shows all the commands");
RegAdminCmd("sm_mark", Command_MarkPendingTroll, ADMFLAG_KICK, "Marks a player as to be banned on disconnect");
RegAdminCmd("sm_ftc", Command_FeedTheCrescendoTroll, ADMFLAG_KICK, "Applies a manual punish on the last crescendo activator");
RegAdminCmd("sm_witch_attack", Command_WitchAttack, ADMFLAG_CHEATS, "Makes all witches target a player");
RegAdminCmd("sm_ftc", Command_FeedTheCrescendoTroll, ADMFLAG_KICK, "Applies a manual punish on the last crescendo activator");
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.");
RegAdminCmd("sm_noob", Command_MarkNoob, ADMFLAG_KICK, "Marks a player as a noob. stored in a database");
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("player_disconnect", Event_PlayerDisconnect);
HookEvent("player_death", Event_PlayerDeath);
HookEvent("triggered_car_alarm", Event_CarAlarm);
@ -86,6 +89,8 @@ public void OnPluginStart() {
AddNormalSoundHook(view_as<NormalSHook>(SoundHook));
AutoExecConfig(true, "l4d2_feedthetrolls");
}
///////////////////////////////////////////////////////////////////////////////
// CVAR CHANGES
@ -103,11 +108,12 @@ public void Change_ThrowInterval(ConVar convar, const char[] oldValue, const cha
// METHODS - Old methods, some are also in feedthetrolls/misc.inc
///////////////////////////////////////////////////////////////////////////////
void ThrowAllItems(int victim) {
float vicPos[3], destPos[3];
int clients[4];
GetClientAbsOrigin(victim, vicPos);
//Find a bot to throw to
//Find a survivor to throw to (grabs the first nearest non-self survivor)
int clientCount = GetClientsInRange(vicPos, RangeType_Visibility, clients, sizeof(clients));
for(int i = 0; i < clientCount; i++) {
if(clients[i] != victim) {
@ -126,7 +132,6 @@ void ThrowAllItems(int victim) {
WritePackFloat(pack, destPos[2]);
WritePackCell(pack, slot);
WritePackCell(pack, victim);
}
}
@ -164,18 +169,15 @@ bool IsPlayerFarDistance(int client, float distance) {
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));
if(weapon > 0) {
return GetEntProp(client, Prop_Send, "m_iAmmo", _, GetEntProp(weapon, Prop_Send, "m_iPrimaryAmmoType"));
}
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;
SetEntProp(client, Prop_Send, "m_iAmmo", amount, _, GetEntProp(weapon, Prop_Send, "m_iPrimaryAmmoType"));
}
return false;
}