Internal refactor

This commit is contained in:
Jackzie 2024-02-15 09:01:54 -06:00
parent 88b7ac09fc
commit 23cbb7aeac
11 changed files with 967 additions and 697 deletions

Binary file not shown.

View file

@ -4,9 +4,11 @@
//Allow MAX_TROLLS to be defined elsewhere
#if defined MAX_TROLLS
#else
#define MAX_TROLLS 55
#define MAX_TROLLS 56
#endif
Troll t_metaReverse;
enum trollModifier {
TrollMod_Invalid = 0,
TrollMod_Instant = 1 << 0,
@ -20,6 +22,10 @@ enum TrollEffectResponse {
TE_Menu // Switching menus / etc, don't continue menu
}
typedef ActivateFunction = function void (Troll troll, int activator, int victim, int flags, trollModifier mod);
typedef ResetFunction = function void (Troll troll, int activator, int victim);
typedef PromptActivateFunction = function TrollEffectResponse (Troll troll, int activator, int victim, any data, int flags, trollModifier mod);
StringMap trollKV;
char trollIds[MAX_TROLLS+1][MAX_TROLL_NAME_LENGTH];
char DEFAULT_FLAG_PROMPT_MULTIPLE[] = "Enable options (Multiple)";
@ -27,7 +33,6 @@ char DEFAULT_FLAG_PROMPT[] = "Select an option";
bool SilentMenuSelected[MAXPLAYERS+1];
static int g_trollAddPromptIndex;
ArrayList gRandomClients;
char SPECIAL_NAMES[][] = {
"Smoker", "Boomer", "Hunter", "Spitter", "Jockey", "Charger", "Witch", "Tank"
@ -35,11 +40,17 @@ char SPECIAL_NAMES[][] = {
enum struct TrollFlagPrompt {
char promptText[MAX_TROLL_FLAG_LENGTH];
// enabled flags
int flags;
// default values
int defaults;
// is multiple flags selectable?
bool multiselect;
// flags that need to be active to show this prompt
int requireFlags;
PrivateForward activateFn;
void GetPromptText(char[] prompt, int maxlength) {
if(this.promptText[0] != '\0') {
strcopy(prompt, maxlength, this.promptText);
@ -51,19 +62,12 @@ enum struct TrollFlagPrompt {
}
}
// Very hacky but map a power (say 16, 32) to the index in a list
// Used to get the name of a flag (flag #16 for example) in ArrayList<string> of flag names
int GetIndexFromPower(int powerOfTwo) {
for(int i = 0; i < 16; i++) {
if(1 << i == powerOfTwo) {
return i;
}
}
return -1;
enum struct TrollOptionData {
char name[MAX_TROLL_FLAG_LENGTH];
int data; // can also be float
}
enum struct Troll {
enum struct TrollData {
int id; // The id or the index into the global Trolls[] array
int categoryID; // The category this troll belongs in
@ -71,11 +75,15 @@ enum struct Troll {
char description[128];
bool hidden;
PrivateForward activateFn;
PrivateForward resetFn;
int mods; // Combination of valid modifiers. Only two are ever supported
// Flags
int activeFlagClients[MAXPLAYERS+1];
ArrayList flagNames;
ArrayList promptOptions;
ArrayList flagPrompts;
// Custom timer
@ -84,140 +92,6 @@ enum struct Troll {
float timerInterval;
int timerRequiredFlags;
void SetTimer(float interval, Timer timer, int requiredFlags = 0) {
this.timerInterval = interval;
this.timerFunction = timer;
this.timerRequiredFlags = requiredFlags;
for(int i = 0; i <= MAXPLAYERS; i++) {
this.timerHandles[i] = null;
}
}
bool HasMod(trollModifier mod) {
return ((this.mods >> (view_as<int>(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.mods == view_as<int>(TrollMod_Instant)) return TrollMod_Instant;
else if(this.mods == view_as<int>(TrollMod_Constant)) return TrollMod_Constant;
else return TrollMod_Invalid;
}
/////// FLAGS
bool GetFlagName(int index, char[] buffer, int maxlength) {
if(this.flagNames == null || index >= this.flagNames.Length) return false;
this.flagNames.GetString(index, buffer, maxlength);
return true;
}
bool GetFlagNames(int client, char[] output, int maxlength) {
if(this.flagNames == null) return false;
char buffer[16];
for(int i = 0; i < this.flagNames.Length; i++) {
int value = 1 << i;
// If client has this flag:
if(this.activeFlagClients[client] & value) {
this.flagNames.GetString(i, buffer, sizeof(buffer));
Format(output, maxlength, "%s%s,", output, buffer);
}
}
return true;
}
int AddCustomFlagPrompt(const char[] promptText, bool multiselect = false, int requireFlags = 0) {
TrollFlagPrompt prompt;
prompt.multiselect = multiselect;
prompt.requireFlags = requireFlags;
strcopy(prompt.promptText, MAX_TROLL_FLAG_LENGTH, promptText);
int index = this.flagPrompts.PushArray(prompt);
g_trollAddPromptIndex = index;
return index;
}
int AddFlagPrompt(bool multiselect = false, int requireFlags = 0) {
//g_trollAddPromptIndex
TrollFlagPrompt prompt;
prompt.multiselect = multiselect;
prompt.requireFlags = requireFlags;
int index = this.flagPrompts.PushArray(prompt);
g_trollAddPromptIndex = index;
return index;
}
int AddFlag(const char[] name, bool defaultOn) {
if(this.flagNames == null) {
this.flagNames = new ArrayList(MAX_TROLL_FLAG_LENGTH);
}
// Check if flag already added
int flagIndex = this.GetFlagIndex(name);
if(flagIndex == -1) flagIndex = this.flagNames.PushString(name);
// Grab the prompt
static TrollFlagPrompt prompt;
// TODO: CHECK IF MISSING PROMPT
if(g_trollAddPromptIndex >= this.flagPrompts.Length) {
ThrowError("No prompt added for troll \"%s\", for flag \"%s\"", this.id, name);
}
this.flagPrompts.GetArray(g_trollAddPromptIndex, prompt);
prompt.flags |= (1 << flagIndex);
if(defaultOn) {
// If out of bounds, set to default -1 -> pick global prompt
if(this.flagPrompts.Length == 0) {
ThrowError("Troll \"%s\" does not have any flag prompts, thus a default value cannot be set. (flag=\"%s\")", this.name, name);
return -1;
}
if(!prompt.multiselect && prompt.defaults > 0) {
ThrowError("Flag \"%s\" cannot be set as default flag in single select mode, as one has already been set for prompt %d", name, g_trollAddPromptIndex);
return -1;
}
prompt.defaults |= (1 << flagIndex);
}
this.flagPrompts.SetArray(g_trollAddPromptIndex, prompt); //May not be required
return flagIndex;
}
int GetFlagIndex(const char[] flagName) {
static char comprFlag[MAX_TROLL_FLAG_LENGTH];
for(int i = 0; i < this.flagNames.Length; i++) {
this.flagNames.GetString(i, comprFlag, sizeof(comprFlag));
if(StrEqual(comprFlag, flagName)) {
return i;
}
}
return -1;
}
bool HasFlags() {
return this.flagNames != null && this.flagNames.Length > 0 && this.flagPrompts.Length > 0;
}
int GetFlagCount() {
return this.flagNames != null ? this.flagNames.Length : 0;
}
int GetClientFlags(int client) {
return this.activeFlagClients[client];
}
void GetFlagPrompt(int index, TrollFlagPrompt prompt) {
this.flagPrompts.GetArray(index, prompt);
}
/////// TROLL ACTIVATION
TrollEffectResponse Activate(int client, int activator, trollModifier modifier = TrollMod_Invalid, int flags = 0, bool silent = false) {
if(modifier == TrollMod_Invalid) modifier = this.GetDefaultMod();
// Sadly, unable to pass in <this> to ApplyTroll, so it has to do unnecessary lookup via string
return ApplyTroll(client, this.name, activator, modifier, flags, silent);
}
void Toggle(int client, int flags) {
if(this.IsActive(client)) {
this.Disable(client);
@ -245,25 +119,22 @@ enum struct Troll {
PrintToServer("FTT Debug: Disabling timer for %N", client);
delete this.timerHandles[client];
}
if(this.resetFn != null) {
Call_StartForward(this.resetFn);
Call_PushCell(0);
Call_PushCell(client);
Call_PushCell(0);
Call_Finish();
}
}
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];
TrollData Trolls[MAX_TROLLS+1];
ArrayList categories;
static int categoryID = -1;
@ -271,10 +142,11 @@ static int categoryID = -1;
void ResetClient(int victim, bool wipe = true) {
if(victim == 0 || !IsClientConnected(victim)) return;
if(wipe) {
for(int i = 0; i <= MAX_TROLLS; i++) {
Trolls[i].Disable(victim);
for(int i = 1; i <= MAX_TROLLS; i++) {
Troll(i).Reset(victim);
}
}
// TODO: move to reset functions!!
noRushingUsSpeed[victim] = 1.0;
BaseComm_SetClientMute(victim, false);
SetEntityGravity(victim, 1.0);
@ -286,42 +158,312 @@ void ResetClient(int victim, bool wipe = true) {
SDKUnhook(wpn, SDKHook_Reload, Event_WeaponReload);
}
int SetupTroll(const char[] name, const char description[128], int mods) {
static int i = 0;
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;
// TrollInstance of TrollData
methodmap Troll {
public Troll(int index) {
return view_as<Troll>(index);
}
g_trollAddPromptIndex = 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].mods = mods;
Trolls[i].flagPrompts = new ArrayList(sizeof(TrollFlagPrompt));
strcopy(trollIds[i], MAX_TROLL_NAME_LENGTH, name);
trollKV.SetValue(name, i);
return i++;
public static Troll FromName(const char[] name) {
int i = GetTrollID(name);
if(i == -1)
LogError("Unknown troll \"%s\"", name);
return view_as<Troll>(i);
}
property bool Hidden {
public get() { return Trolls[this.Id].hidden; }
}
property int CategoryId {
public get() { return Trolls[this.Id].categoryID; }
}
property int PromptCount {
public get() { return Trolls[this.Id].flagPrompts.Length; }
}
property int TotalOptionsCount {
public get() {
return Trolls[this.Id].promptOptions == null ? -1 : Trolls[this.Id].promptOptions.Length;
}
}
property bool HasTimer {
public get() { return Trolls[this.Id].timerInterval > 0.0; }
}
property int Id {
public get() { return view_as<int>(this); }
}
property bool HasOptions {
public get() { return this.TotalOptionsCount > 0; }
}
public bool IsActive(int client) {
return Trolls[this.Id].activeFlagClients[client] != -1;
}
public bool HasFlag(int client, int flag) {
return Trolls[this.Id].activeFlagClients[client] & flag != 0;
}
public int GetFlags(int client) {
return Trolls[this.Id].activeFlagClients[client]
}
public bool HasMod(trollModifier mod) {
return Trolls[this.Id].mods & view_as<int>(mod) != 0;
}
public void GetName(char[] output, int maxlen) {
strcopy(output, maxlen, Trolls[this.Id].name);
}
public TrollEffectResponse Activate(int activator, int victim, trollModifier modifier = TrollMod_Invalid, int flags = 0, bool silent = false) {
PrintToServer("Activate: act:%d vic:%d", activator, victim);
if(modifier == TrollMod_Invalid) modifier = this.GetDefaultMod();
return ApplyTroll(victim, this, activator, modifier, flags, silent);
}
public void Reset(int victim) {
Trolls[this.Id].activeFlagClients[victim] = -1;
if(Trolls[this.Id].resetFn != null) {
Call_StartForward(Trolls[this.Id].resetFn);
Call_PushCell(this);
Call_PushCell(0);
Call_PushCell(victim);
Call_PushCell(0);
Call_Finish();
}
}
public bool GetOptionData(int optionIndex, TrollOptionData data) {
if(optionIndex < 0 || optionIndex >= Trolls[this.Id].promptOptions.Length) return false;
Trolls[this.Id].promptOptions.GetArray(optionIndex, data);
return true;
}
/// If prompt is NOT multiselect, returns the selected value from the option's data property
public bool GetPromptDataInt(int client, int promptIndex, int &out) {
if(promptIndex < 0 || promptIndex >= Trolls[this.Id].flagPrompts.Length) {
ThrowError(".GetPromptData called with invalid prompt index (%d, max %d) on troll #%d", promptIndex, Trolls[this.Id].flagPrompts.Length, this.Id);
}
TrollFlagPrompt prompt;
Trolls[this.Id].flagPrompts.GetArray(promptIndex, prompt);
if(prompt.multiselect) {
ThrowError(".GetPromptData: attempted to receive data for a multiselect prompt. Operation unspported. promptIndex:%d troll:%d", promptIndex, this.Id);
}
TrollOptionData option;
int flags = this.GetFlags(client);
for(int i = 0; i < Trolls[this.Id].promptOptions.Length; i++) {
int bit = 1 << i;
// If prompt has flag AND flag is active:
if(prompt.flags & bit && flags & bit) {
Trolls[this.Id].promptOptions.GetArray(i, option);
out = option.data;
return true;
}
}
return false;
}
public bool GetPromptDataFloat(int client, int promptIndex, float &out) {
int value;
if(this.GetPromptDataInt(client, promptIndex, value)) {
// We just retagged it as int, but it's float data
out = view_as<float>(value);
return true;
}
return false;
}
public bool GetPrompt(int promptIndex, TrollFlagPrompt prompt) {
if(promptIndex < 0 || promptIndex >= Trolls[this.Id].flagPrompts.Length) return false;
Trolls[this.Id].flagPrompts.GetArray(promptIndex, prompt);
return true;
}
public void GetOptionName(int optionIndex, char[] output, int maxlen) {
TrollOptionData option;
this.GetOptionData(optionIndex, option);
strcopy(output, maxlen, option.name);
}
public bool GetFlagNames(int client, int flags = -1, char[] output, int maxlength) {
if(this.TotalOptionsCount == 0) return false;
char buffer[32];
if(flags == -1) flags = Trolls[this.Id].activeFlagClients[client];
int count;
for(int i = 0; i < this.TotalOptionsCount; i++) {
int bit = 1 << i;
// If client has this flag:
if(flags & bit) {
this.GetOptionName(i, buffer, sizeof(buffer));
if(count == 0)
Format(output, maxlength, "%s", buffer);
else
Format(output, maxlength, "%s,%s", output, buffer);
count++;
}
}
return true;
}
/// Gets the default modifier to use
public trollModifier GetDefaultMod() {
// If the flags is equal to the 2^n flag, then it must be the only flag:
if(Trolls[this.Id].mods == view_as<int>(TrollMod_Instant)) return TrollMod_Instant;
else if(Trolls[this.Id].mods == view_as<int>(TrollMod_Constant)) return TrollMod_Constant;
else return TrollMod_Invalid;
}
}
// Gets the Troll enum struct via name
// Returns index of troll enum
int GetTroll(const char[] name, Troll troll) {
static int i = 0;
if(trollKV.GetValue(name, i)) {
troll = Trolls[i];
return i;
int g_iTrollIndex;
methodmap TrollBuilder {
public TrollBuilder(const char[] name, const char description[128], int mods) {
if(mods == 0) {
ThrowError("Troll \"%s\" has no modifiers defined.", name);
} else if(g_iTrollIndex == MAX_TROLLS + 1) {
ThrowError("Maximum number of trolls (%d) reached. Up MAX_TROLLS value.", MAX_TROLLS);
}
int i = g_iTrollIndex;
g_iTrollIndex++;
g_trollAddPromptIndex = 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].mods = mods;
Trolls[i].flagPrompts = new ArrayList(sizeof(TrollFlagPrompt));
char buffer[MAX_TROLL_NAME_LENGTH];
strcopy(buffer, sizeof(buffer), name);
StringToLower(buffer);
trollKV.SetValue(buffer, i);
return view_as<TrollBuilder>(i);
}
ThrowError("GetTroll: Troll was not found \"%s\"", name);
return -1;
property int Id {
public get() { return view_as<int>(this); }
}
public TrollBuilder Hide() {
Trolls[this.Id].hidden = true;
}
public TrollBuilder SetDescription(const char description[128]) {
strcopy(Trolls[this.Id].description, 128, description);
}
public TrollBuilder SetTimer(float interval, Timer timer, int requiredFlags = 0) {
Trolls[this.Id].timerInterval = interval;
Trolls[this.Id].timerFunction = timer;
Trolls[this.Id].timerRequiredFlags = requiredFlags;
for(int i = 0; i <= MAXPLAYERS; i++) {
Trolls[this.Id].timerHandles[i] = null;
}
return this;
}
public TrollBuilder AddPrompt(const char[] customPrompt = "", int requiredFlags = 0) {
this._AddPrompt(false, requiredFlags, customPrompt);
return this;
}
public TrollBuilder AddPromptMulti(const char[] customPrompt = "", int requiredFlags = 0) {
this._AddPrompt(true, requiredFlags, customPrompt);
return this;
}
// Adds event handle for when an option for a non-multi prompt is selected. If current prompt is multi, will error
public TrollBuilder OnPromptActivate(PromptActivateFunction fn) {
TrollFlagPrompt prompt;
Trolls[this.Id].flagPrompts.GetArray(g_trollAddPromptIndex, prompt);
if(prompt.multiselect) ThrowError("Current prompt is multiselect");
if(prompt.activateFn == null) prompt.activateFn = new PrivateForward(ET_Single, Param_Cell, Param_Cell, Param_Cell, Param_Any, Param_Cell, Param_Cell);
prompt.activateFn.AddFunction(INVALID_HANDLE, fn);
Trolls[this.Id].flagPrompts.SetArray(g_trollAddPromptIndex, prompt);
return this;
}
public void _AddPrompt(bool multiselect, int requiredFlags = 0, const char[] customPrompt) {
TrollFlagPrompt prompt;
prompt.multiselect = multiselect;
prompt.requireFlags = requiredFlags;
if(customPrompt[0] != '\0')
strcopy(prompt.promptText, MAX_TROLL_FLAG_LENGTH, customPrompt);
int index = Trolls[this.Id].flagPrompts.PushArray(prompt);
g_trollAddPromptIndex = index;
}
public TrollBuilder AddOption(const char[] name, bool defaultOn = false) {
this._AddOption(name, defaultOn);
return this;
}
public TrollBuilder AddOptionInt(const char[] name, bool defaultOn = false, int data) {
this._AddOption(name, defaultOn, data);
return this;
}
public TrollBuilder AddOptionFloat(const char[] name, bool defaultOn = false, float data) {
// This is intentional - we do not want to convert float -> int, just change type
this._AddOption(name, defaultOn, view_as<int>(data));
return this;
}
public void _AddOption(const char[] name, bool defaultOn = false, int data = 0) {
if(Trolls[this.Id].promptOptions == null) {
Trolls[this.Id].promptOptions = new ArrayList(sizeof(TrollOptionData));
}
TrollOptionData option;
strcopy(option.name, MAX_TROLL_FLAG_LENGTH, name);
option.data = data;
int optionIndex = Trolls[this.Id].promptOptions.PushArray(option);
// Add option to current prompt
TrollFlagPrompt prompt;
if(g_trollAddPromptIndex >= Trolls[this.Id].flagPrompts.Length) {
ThrowError("No prompt added for troll \"%s\", for flag \"%s\"", this.Id, name);
}
Trolls[this.Id].flagPrompts.GetArray(g_trollAddPromptIndex, prompt);
prompt.flags |= ( 1 << optionIndex );
if(defaultOn) {
// If out of bounds, set to default -1 -> pick global prompt
if(Trolls[this.Id].flagPrompts.Length == 0) {
ThrowError("Troll \"%s\" does not have any flag prompts, thus a default value cannot be set. (flag=\"%s\")", Trolls[this.Id].name, name);
} else if(!prompt.multiselect && prompt.defaults > 0) {
ThrowError("Flag \"%s\" cannot be set as default flag in single select mode, as one has already been set for prompt %d", name, g_trollAddPromptIndex);
}
prompt.defaults |= (1 << optionIndex);
}
// Save changes to prompt
Trolls[this.Id].flagPrompts.SetArray(g_trollAddPromptIndex, prompt);
}
public TrollBuilder SetActivationFunction(ActivateFunction fn) {
if(Trolls[this.Id].activateFn == null) {
Trolls[this.Id].activateFn = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
}
Trolls[this.Id].activateFn.AddFunction(INVALID_HANDLE, fn);
return this;
}
public TrollBuilder SetResetFunction(ResetFunction fn) {
if(Trolls[this.Id].resetFn == null) {
Trolls[this.Id].resetFn = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
}
Trolls[this.Id].resetFn.AddFunction(INVALID_HANDLE, fn);
return this;
}
public Troll Build() {
return Troll(this.Id);
}
}
int GetTrollID(const char[] name) {
static int i = 0;
if(trollKV.GetValue(name, i)) {
char buffer[MAX_TROLL_NAME_LENGTH];
strcopy(buffer, sizeof(buffer), name);
StringToLower(buffer);
if(trollKV.GetValue(buffer, i)) {
return i;
}
PrintToServer("GetTrollID: Troll was not found \"%s\"", name);
@ -334,11 +476,6 @@ bool IsAnyTrollActive(int victim) {
}
return false;
}
// Gets the Troll enum struct via key index
// Returns index of troll enum
void GetTrollByKeyIndex(int index, Troll troll) {
troll = Trolls[index];
}
void SetTrollFlags(int client, const char[] name, int flags = -1) {
int index = GetTrollID(name);
@ -349,19 +486,15 @@ void SetTrollFlags(int client, const char[] name, int flags = -1) {
}
TrollEffectResponse 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) {
ReplyToCommand(activator, "Unknown troll \"%s\"", name);
PrintToServer("[FTT] %N attempted to apply unknown troll: %s", activator, name);
return TE_Error;
}
TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModifier modifier, int flags = 0, bool silent = false) {
char name[MAX_TROLL_NAME_LENGTH];
troll.GetName(name, sizeof(name));
int trollIndex = troll.Id;
bool isActive = Trolls[trollIndex].activeFlagClients[victim] > -1;
bool isActive = troll.IsActive(victim);
// Clear troll specific timer:
if(Trolls[trollIndex].timerInterval > 0.0) {
if(troll.HasTimer) {
if(!isActive) {
if(modifier & TrollMod_Constant && (Trolls[trollIndex].timerRequiredFlags == 0 || Trolls[trollIndex].timerRequiredFlags & flags)) {
Trolls[trollIndex].timerHandles[victim] = CreateTimer(Trolls[trollIndex].timerInterval, Trolls[trollIndex].timerFunction, victim, TIMER_REPEAT);
@ -374,14 +507,10 @@ TrollEffectResponse ApplyTroll(int victim, const char[] name, int activator, tro
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(GetURandomFloat() <= max) {
if(activator > 0 && t_metaReverse.IsActive(activator)) {
float chance;
t_metaReverse.GetPromptDataFloat(activator, 0, chance);
if(GetURandomFloat() <= chance) {
victim = activator;
}
}
@ -391,9 +520,9 @@ TrollEffectResponse ApplyTroll(int victim, const char[] name, int activator, tro
int player = GetSpectatorClient(victim);
if(player > 0) {
// If there is an idle player, apply troll to them
ApplyTroll(player, name, activator, modifier, flags, silent);
ApplyTroll(player, troll, activator, modifier, flags, silent);
// And continue IF there is TrollMod_PlayerOnly mod
if(troll.mods & view_as<int>(TrollMod_PlayerOnly)) return TE_Success;
if(troll.HasMod(TrollMod_PlayerOnly)) return TE_Success;
// Don't want to show two logs, so just ignore the bot
silent = true;
}
@ -409,63 +538,77 @@ TrollEffectResponse ApplyTroll(int victim, const char[] name, int activator, tro
TrollEffectResponse response = ApplyAffect(victim, troll, activator, modifier, flags);
if(response != TE_Success) return response; // Let the menu handler deal with checking
// Invoke Callbacks:
if(!isActive) {
Troll instance = Troll(trollIndex);
if(Trolls[trollIndex].activateFn != null) {
Call_StartForward(Trolls[trollIndex].activateFn);
Call_PushCell(instance);
Call_PushCell(activator);
Call_PushCell(victim);
Call_PushCell(flags);
Call_PushCell(modifier);
Call_Finish();
}
// Call the corresponding prompt callback if applicable
TrollFlagPrompt prompt;
for(int i = 0; i < Trolls[trollIndex].flagPrompts.Length; i++) {
Trolls[trollIndex].flagPrompts.GetArray(i, prompt);
if(!prompt.multiselect && prompt.activateFn != null) {
int value;
instance.GetPromptDataInt(victim, i, value);
for(int j = 0; j < Trolls[trollIndex].promptOptions.Length; i++) {
int bit = 1 << j;
if(flags & bit && prompt.flags & bit) {
Call_StartForward(prompt.activateFn);
Call_PushCell(instance);
Call_PushCell(activator);
Call_PushCell(victim);
Call_PushCell(value);
Call_PushCell(flags);
Call_PushCell(modifier);
response = view_as<TrollEffectResponse>(Call_Finish());
if(response != TE_Success) return response; // Let the menu handler deal with checking
break;
}
}
break;
}
}
} else if(isActive && Trolls[trollIndex].resetFn != null) {
Call_StartForward(Trolls[trollIndex].resetFn);
Call_PushCell(Troll(trollIndex));
Call_PushCell(activator);
Call_PushCell(victim);
Call_PushCell(modifier);
Call_Finish();
}
// Log all actions, indicating if constant or single-fire, and if any flags
if(!silent) {
if(isActive) {
CShowActivityEx(activator, "[FTT] ", "deactivated {yellow}%s{default} on %N. ", troll.name, victim);
LogAction(activator, victim, "\"%L\" deactivated \"%s\" on \"%L\"", activator, troll.name, victim);
CShowActivityEx(activator, "[FTT] ", "deactivated {yellow}%s{default} on %N. ", name, victim);
LogAction(activator, victim, "\"%L\" deactivated \"%s\" on \"%L\"", activator, name, victim);
} else {
char flagName[MAX_TROLL_FLAG_LENGTH];
// strcopy(flagName, sizeof(flagName), troll.name)
// Call_StartForward(g_TrollAppliedForward);
// Call_PushCell(victim);
// Call_PushString(flagName);
// Call_PushCell(flags);
// Call_PushCell(activator);
// Call_Finish();
if(flags > 0) {
troll.GetFlagNames(victim, flagName, sizeof(flagName));
// Checks if there is not more than one flag set on the bitfield
// if(flags & flags - 1 == 0 && flags & flags + 1 == 0) {
// // Get the flag name if there is only one flag set
// troll.GetFlagName(GetIndexFromPower(flags), flagName, sizeof(flagName));
// } else {
// Format(flagName, sizeof(flagName), "%d", flags);
// }
char flagName[50];
if(flags > 0 && troll.GetFlagNames(victim, flags, flagName, sizeof(flagName))) {
Format(flagName, sizeof(flagName), " (\x04%s|%d\x01)", flagName, flags);
// CFormatColor(flagName, sizeof(flagName));
}
if(modifier & TrollMod_Constant) {
CShowActivityEx(activator, "[FTT] ", "activated constant {yellow}%s{default}%s for %N. ", troll.name, flagName, victim);
CShowActivityEx(activator, "[FTT] ", "activated constant {yellow}%s{default}%s for %N. ", name, flagName, victim);
} else {
CShowActivityEx(activator, "[FTT] ", "activated {yellow}%s{default}%s for %N. ", troll.name, flagName, victim);
CShowActivityEx(activator, "[FTT] ", "activated {yellow}%s{default}%s for %N. ", name, flagName, victim);
}
LogAction(activator, victim, "\"%L\" activated \"%s\" (%d) for \"%L\"", activator, troll.name, flags, victim);
LogAction(activator, victim, "\"%L\" activated \"%s\" (%d) for \"%L\"", activator, name, flags, victim);
}
} else {
CReplyToCommand(activator, "[FTT] Applied silently {yellow}\"%s\"{default} on %N with flags=%d", troll.name, victim, flags);
CReplyToCommand(activator, "[FTT] Applied silently {yellow}\"%s\"{default} on %N with flags=%d", name, victim, flags);
}
return TE_Success;
}
bool IsTrollActive(int client, const char[] troll) {
if(troll[0] == '\0') {
ThrowError("Troll name is empty");
return false;
}
static int i = 0;
if(trollKV.GetValue(troll, i)) {
return Trolls[i].activeFlagClients[client] != -1;
}
ThrowError("Troll \"%s\" does not exist", troll);
return false; //errors instead but compiler no like
}
bool IsTrollActiveByRawID(int client, int id) {
return Trolls[id].activeFlagClients[client] != -1;
}
void EnableTroll(int client, const char[] troll, int flags = 0) {
SetTrollFlags(client, troll, flags);
@ -496,12 +639,8 @@ public int Native_ApplyTroll(Handle plugin, int numParams) {
int flags = GetNativeCell(4);
int activator = GetNativeCell(5);
int index = GetTrollID(name);
if(index > 0) {
Trolls[index].Activate(victim, activator, modifier, flags, GetNativeCell(6));
} else {
ThrowNativeError(SP_ERROR_NATIVE, "Could not find troll with name \"%s\"", name);
}
Troll troll = Troll.FromName(name);
troll.Activate(victim, activator, modifier, flags, GetNativeCell(6));
return 0;
}

View file

@ -11,10 +11,10 @@ enum struct TrollCombo {
ArrayList trolls;
void AddTroll(const char[] name, int flags = 0, trollModifier mod = TrollMod_Invalid) {
int id = GetTrollID(name);
if(mod == TrollMod_Invalid) mod = Trolls[id].GetDefaultMod();
Troll instance = Troll.FromName(name);
if(mod == TrollMod_Invalid) mod = instance.GetDefaultMod();
SpecifiedTroll troll;
troll.id = id;
troll.id = instance.Id;
troll.mod = mod;
troll.flags = flags;
this.trolls.PushArray(troll, sizeof(troll));
@ -24,7 +24,7 @@ enum struct TrollCombo {
for(int i = 0; i < this.trolls.Length; i++) {
SpecifiedTroll troll;
this.trolls.GetArray(i, troll, sizeof(troll));
Trolls[troll.id].Activate(target, client, troll.mod, troll.flags);
Troll(troll.id).Activate(target, client, troll.mod, troll.flags);
}
}
}

View file

@ -130,9 +130,12 @@ Action Command_InstaSpecialFace(int client, int args) {
Action Command_WitchAttack(int client, int args) {
if(args < 1) {
if(!g_actionsAvailable) {
ReplyToCommand(client, "Unavailable: Missing \"actions\"");
return Plugin_Handled;
} else if(args < 1) {
ReplyToCommand(client, "Usage: sm_witch_attack <user> [# of witches or 0 for all]");
}else{
} else{
char arg1[32];
GetCmdArg(1, arg1, sizeof(arg1));
char target_name[MAX_TARGET_LENGTH];
@ -377,6 +380,7 @@ public Action Command_ListTheTrolls(int client, int args) {
return Plugin_Handled;
}
char buffer[50];
for(int p = 0; p < target_count; p++) {
int target = target_list[p];
if(IsPlayerAlive(target))
@ -389,24 +393,17 @@ public Action Command_ListTheTrolls(int client, int args) {
if(player != -1) target = player;
}
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 )
static char buffer[MAX_TROLL_FLAG_LENGTH];
for(int i = 0; i < Trolls[j].flagNames.Length; i++) {
int a = (1 << i);
if(Trolls[j].activeFlagClients[target] & a) {
Trolls[j].flagNames.GetString(i, buffer, sizeof(buffer));
Format(list, sizeof(list), "%s%s;", list, buffer);
} else {
Trolls[j].flagNames.GetString(i, buffer, sizeof(buffer));
}
}
ReplyToCommand(client, "\"%s\" Flags: %s", trollIds[j], list);
for(int j = 1; j < MAX_TROLLS; j++) {
Troll troll = Troll(j);
if(troll.Hidden) continue;
if(troll.IsActive(target)) {
int flags = troll.GetFlags(target);
if(flags > 0) {
buffer[0] = '\0';
troll.GetFlagNames(target, flags, buffer, sizeof(buffer));
ReplyToCommand(client, "\"%s\" Flags: %s", Trolls[troll.Id].name, buffer);
} else
ReplyToCommand(client, trollIds[j]);
ReplyToCommand(client, "%s", Trolls[troll.Id].name);
}
}
}
@ -424,8 +421,9 @@ public Action Command_ListTheTrolls(int client, int args) {
}
int modeCount = 0;
for(int j = 1; j <= MAX_TROLLS; j++) {
if(trollIds[j][0] != '\0' && IsTrollActive(i, trollIds[j])) {
if(Trolls[j].activeFlagClients[i] > 0)
Troll troll = Troll(j);
if(troll.IsActive(i)) {
if(troll.GetFlags(i) > 0)
Format(modeListArr[modeCount], MAX_TROLL_NAME_LENGTH, "%s(%d)", trollIds[j], Trolls[j].activeFlagClients[i]);
else
strcopy(modeListArr[modeCount], MAX_TROLL_NAME_LENGTH, trollIds[j]);
@ -729,7 +727,7 @@ Action Command_SetReverseFF(int client, int args) {
}
}
ApplyTroll(target, "Reverse FF", client, TrollMod_Constant, flag);
ApplyTroll(target, Troll.FromName("Reverse FF"), client, TrollMod_Constant, flag);
return Plugin_Handled;
}
@ -749,7 +747,7 @@ Action Command_SetMagnetShortcut(int client, int args) {
}
Action Command_CarSplat(int client, int args) {
if(args < 1) {
ReplyToCommand(client, "Usage: sm_magnet <target> [top/front/back]");
ReplyToCommand(client, "Usage: sm_carsplat <target> [top/front/back]");
return Plugin_Handled;
}
char arg[32];
@ -779,9 +777,21 @@ Action Command_CarSplat(int client, int args) {
LogAction(client, target, "spawned car on/in %s of \"%L\"", arg, target);
ShowActivity(client, "spawned car (%s) of %N", arg, target);
} else {
Troll troll;
GetTroll("Car Splat", troll);
Troll troll = Troll.FromName("Car Splat");
ShowSelectFlagMenu(client, GetClientUserId(target), view_as<int>(TrollMod_Instant), troll);
}
return Plugin_Handled;
}
Action Command_AddTypo(int client, int args) {
if(args == 2) {
char src[32], replacement[32];
GetCmdArg(1, src, sizeof(src));
GetCmdArg(2, replacement, sizeof(replacement));
AddTypo(src, replacement, true);
ShowActivity(client, "added typo \"%s\" -> \"%s\"", src, replacement);
} else {
ReplyToCommand(client, "Syntax: /typo <source> <typo>");
}
return Plugin_Handled;
}

View file

@ -22,13 +22,13 @@ public void OnMapStart() {
g_spSpawnQueue.Clear();
spIsActive = false;
//CreateTimer(30.0, Timer_AutoPunishCheck, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
}
public void OnClientPutInServer(int client) {
pdata[client].pendingTrollBan = 0;
pdata[client].shootAtTarget = 0;
fAntiRushFrequencyCounter[client] = 0.0;
if(IsTrollActive(client, "Voice Mute"))
if(t_voiceMute.IsActive(client))
BaseComm_SetClientMute(client, true);
SDKHook(client, SDKHook_OnTakeDamage, Event_TakeDamage);
SDKHook(client, SDKHook_OnTakeDamageAlive, NerfGun_OnTakeDamage);
@ -63,7 +63,7 @@ void OnCarHitByTank(const char[] output, int caller, int activator, float delay)
}
void EntityCreateCallback(int entity) {
if(!HasEntProp(entity, Prop_Send, "m_hOwnerEntity") || !IsValidEntity(entity)) return;
if(!IsValidEntity(entity) || !HasEntProp(entity, Prop_Send, "m_hOwnerEntity")) return;
static char class[16];
static int badThrowID;
@ -126,9 +126,8 @@ enum ProjectileMagnetType {
ProjType_Cars = 4,
}
public void Event_DoorToggle(Event event, const char[] name, bool dontBroadcast) {
void Event_DoorToggle(Event event, const char[] name, bool dontBroadcast) {
// TODO: hook OnOpen entity output?
int client = GetClientOfUserId(event.GetInt("userid"));
if(client && Trolls[t_slipperyShoesIndex].IsActive(client) && Trolls[t_slipperyShoesIndex].activeFlagClients[client] & 2) {
L4D_StaggerPlayer(client, client, NULL_VECTOR);
@ -185,14 +184,25 @@ Action Timer_CheckSpecial(Handle h, int specialID) {
}
return Plugin_Handled;
}
public void Frame_Boom(int special) {
void Frame_Boom(int special) {
SDKHooks_TakeDamage(special, special, special, 1000.0);
}
public void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(client > 0)
ResetClient(client, true);
}
void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(client > 0) {
for(int i = 0 ; i < MAX_TROLLS; i++) {
Trolls[i].activeFlagClients[client] = 0;
if(Trolls[i].timerHandles[client] != null) {
delete Trolls[i].timerHandles[client];
}
}
}
}
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("userid");
@ -216,7 +226,7 @@ public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast
}
public Action Event_WeaponReload(int weapon) {
int client = GetEntPropEnt(weapon, Prop_Send, "m_hOwner");
if(client > 0 && IsTrollActive(client, "Gun Jam")) {
if(client > 0 && t_gunJam.IsActive(client)) {
if(GetRandomFloat() < 0.10) { //10% chance gun jams
return Plugin_Stop;
}
@ -385,7 +395,8 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
return Plugin_Continue;
}
bool WillMagnetRun(const Troll troll, int i) {
// TODO: migrate to Troll
bool WillMagnetRun(const TrollData troll, int i) {
// In the case none of the flags are set, return true (100% chance)
// Some systems may give magnet w/ no flags
if(troll.activeFlagClients[i] == 0) return true;
@ -451,7 +462,7 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
}
PrintToServer("%N: %s", client, sArgs);
return Plugin_Handled;
} else if(IsTrollActive(client, "Reversed")) {
} else if(Troll.FromName("Reversed").IsActive(client)) {
int length = strlen(sArgs);
char[] message = new char[length+1];
int j = 0;
@ -462,7 +473,7 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
CPrintToChatAll("{blue}%N {default}: %s", client, message);
PrintToServer("%N: %s", client, sArgs);
return Plugin_Handled;
}else if(IsTrollActive(client, "iCantSpellNoMore")) {
}else if(Troll.FromName("iCantSpellNoMore").IsActive(client)) {
int type = GetRandomInt(1, 14 + 3);
char letterSrc, replaceChar;
switch(type) {
@ -585,18 +596,21 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
PrintToServer("%N: %s", client, sArgs);
return Plugin_Handled;
} else if(Trolls[typooId].IsActive(client)) {
char strings[32][MAX_TYPOS_LENGTH];
int words = ExplodeString(sArgs, " ", strings, 32, MAX_TYPOS_LENGTH);
// Replace all typos
static char typoReplacement[32];
for(int i = 0; i < words; i++) {
if(TYPOS_DICT.GetString(strings[i], typoReplacement, sizeof(typoReplacement))) {
strcopy(strings[i], MAX_TYPOS_LENGTH, typoReplacement);
}
}
int length = MAX_TYPOS_LENGTH * words;
char[] message = new char[length];
ImplodeStrings(strings, 32, " ", message, length);
int len = strlen(sArgs) + 40;
char[] message = new char[len];
ReplaceWithTypos(sArgs, message, len);
// char strings[32][MAX_TYPOS_LENGTH];
// int words = ExplodeString(sArgs, " ", strings, 32, MAX_TYPOS_LENGTH);
// // Replace all typos
// static char typoReplacement[32];
// for(int i = 0; i < words; i++) {
// if(TYPOS_DICT.GetString(strings[i], typoReplacement, sizeof(typoReplacement))) {
// strcopy(strings[i], MAX_TYPOS_LENGTH, typoReplacement);
// }
// }
// int length = MAX_TYPOS_LENGTH * words;
// char[] message = new char[length];
// ImplodeStrings(strings, 32, " ", message, length);
CPrintToChatAll("{blue}%N {default}: %s", client, message);
PrintToServer("%N: %s", client, sArgs);
@ -636,7 +650,7 @@ public Action Event_ItemPickup(int client, int weapon) {
} else if(flags & 32 && GetEntityClassname(weapon, wpnName, sizeof(wpnName)) && StrEqual(wpnName, "weapon_gascan")) {
return Plugin_Handled;
}
} else if(Trolls[UziRulesIndex].IsActive(client) || IsTrollActive(client, "Primary Disable")) {
} else if(Trolls[UziRulesIndex].IsActive(client) || Troll.FromName("Primary Disable").IsActive(client)) {
GetEdictClassname(weapon, wpnName, sizeof(wpnName));
if(strcmp(wpnName[7], "rifle") >= 0
|| strcmp(wpnName[7], "smg") >= 0
@ -664,7 +678,7 @@ public Action Event_ItemPickup(int client, int weapon) {
SetCommandFlags("give", flags);
return Plugin_Stop;
}
} else if(IsTrollActive(client, "Primary Disable")) {
} else if(Troll.FromName("Primary Disable").IsActive(client)) {
return Plugin_Stop;
}
}
@ -793,9 +807,9 @@ Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& damage
if(isSameTeam && Trolls[t_reverseFFIndex].IsActive(attacker)) {
// Should this be applied? (as in no FF granted)
bool disableFF = false;
if(damagetype == DMG_BURN) {
if(damagetype & DMG_BURN) {
disableFF = Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 32 != 0;
} else if(damagetype == DMG_BLAST) {
} else if(damagetype & DMG_BLAST) {
disableFF = Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 64 != 0;
} else {
// Does not run if DMG_BURN or DMG_BLAST
@ -932,11 +946,13 @@ public Action SoundHook(int clients[MAXPLAYERS], int& numClients, char sample[PL
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)
else if(Trolls[honkID].activeFlagClients[entity] & 2) {
strcopy(sample, sizeof(sample), "custom/meow1.mp3");
else if(Trolls[honkID].activeFlagClients[entity] & 4)
volume += 0.2;
} else if(Trolls[honkID].activeFlagClients[entity] & 4) {
strcopy(sample, sizeof(sample), "custom/woof1.mp3");
else
volume += 0.6;
} else
return Plugin_Continue;
return Plugin_Changed;
} else if(Trolls[vocalGagID].IsActive(entity)) {
@ -1050,14 +1066,9 @@ public void L4D2_CInsectSwarm_CanHarm_Post(int acid, int spitter, int entity) {
public void Event_EnteredSpit(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(Trolls[t_stickyGooIndex].IsActive(client)) {
int flags = Trolls[t_stickyGooIndex].activeFlagClients[client];
float movement = 0.0;
if(flags & 1) movement = 0.9;
else if(flags & 2) movement = 0.8;
else if(flags & 4) movement = 0.7;
else if(flags & 8) movement = 0.5;
else if(flags & 16) movement = 0.3;
if(t_stickyGoo.IsActive(client)) {
float movement;
t_stickyGoo.GetPromptDataFloat(client, 0, movement);
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", movement);
pdata[client].lastInSpitTime = GetGameTime();
if(~pdata[client].flags & view_as<int>(Flag_HasSpitTimer)) {
@ -1065,22 +1076,18 @@ public void Event_EnteredSpit(Event event, const char[] name, bool dontBroadcast
pdata[client].flags |= view_as<int>(Flag_HasSpitTimer);
}
}
}
public void Event_BotPlayerSwap(Event event, const char[] name, bool dontBroadcast) {
//Player replaced their idle bot
int client = GetClientOfUserId(event.GetInt("player"));
if(client > 0) {
bool debug_hadTroll = false;
for(int i = 1; i <= MAX_TROLLS; i++) {
if(Trolls[i].IsActive(client) && Trolls[i].HasMod(TrollMod_Constant)) { //Add activeFlagClients >= 0 check possibly?
ApplyAffect(client, Trolls[i], -1, TrollMod_Constant, Trolls[i].activeFlagClients[client]);
debug_hadTroll = true;
Troll troll = Troll(i);
if(troll.IsActive(client) && troll.HasMod(TrollMod_Constant)) { //Add activeFlagClients >= 0 check possibly?
ApplyAffect(client, troll, -1, TrollMod_Constant, troll.GetFlags(client));
}
}
if(debug_hadTroll)
PrintToServer("[FTT] Re-applied trolls for was-idle player %N", client);
}
}

View file

@ -158,7 +158,7 @@ public int ChooseCategoryHandler(Menu menu, MenuAction action, int param1, int p
// Reset troll:
if(category == -1) {
ApplyTroll(GetClientOfUserId(userid), "Reset User", param1, TrollMod_Instant);
Troll.FromName("Reset User").Activate(param1, victim, TrollMod_Instant);
return 0;
}
@ -182,22 +182,22 @@ public int ChooseModeMenuHandler(Menu menu, MenuAction action, int param1, int p
static char str[2][8];
ExplodeString(info, "|", str, 2, 8, false);
int userid = StringToInt(str[0]);
int client = GetClientOfUserId(userid);
if(client == 0) {
int victim = GetClientOfUserId(userid);
if(victim == 0) {
ReplyToCommand(param1, "FTT: Could not acquire player");
return 0;
}
int keyIndex = StringToInt(str[1]);
static Troll troll;
GetTrollByKeyIndex(keyIndex, troll);
Troll troll = Troll(keyIndex);
//If troll has multiple flags, prompt:
if(StrEqual(troll.name, "Throw It All")) {
if(troll == t_throwItAll) {
// Setup menu to call itself, but with an extra data point
ShowThrowItAllMenu(param1, userid);
} else if(!troll.IsActive(client) && troll.HasMod(TrollMod_Instant) && troll.HasMod(TrollMod_Constant)) {
} else if(!troll.IsActive(victim) && troll.HasMod(TrollMod_Instant) && troll.HasMod(TrollMod_Constant)) {
Menu modiferMenu = new Menu(ChooseTrollModiferHandler);
Format(info, sizeof(info), "%s: Choose Modifier", troll.name);
// sadly cannot use methodmap easily to return name
Format(info, sizeof(info), "%s: Choose Modifier", Trolls[troll.Id].name);
modiferMenu.SetTitle(info);
Format(info, sizeof(info), "%d|%d|1", userid, keyIndex);
@ -209,13 +209,13 @@ public int ChooseModeMenuHandler(Menu menu, MenuAction action, int param1, int p
modiferMenu.ExitButton = true;
modiferMenu.Display(param1, 0);
} else if(!troll.IsActive(client) && troll.HasFlags()) {
} else if(!troll.IsActive(victim) && troll.HasOptions) {
ShowSelectFlagMenu(param1, userid, -1, troll);
} else {
TrollEffectResponse response = troll.Activate(client, param1);
TrollEffectResponse response = troll.Activate(param1, victim);
// Only show menu if success or error, not TE_Menu
if(response != TE_Menu)
ShowTrollsForCategory(param1, userid, troll.categoryID);
ShowTrollsForCategory(param1, userid, troll.CategoryId);
}
} else if (action == MenuAction_End)
@ -263,29 +263,27 @@ public int ChooseTrollModiferHandler(Menu menu, MenuAction action, int param1, i
static char str[3][8];
ExplodeString(info, "|", str, 3, 8, false);
int userid = StringToInt(str[0]);
int client = GetClientOfUserId(userid);
int victim = GetClientOfUserId(userid);
int keyIndex = StringToInt(str[1]);
int modifiers = StringToInt(str[2]);
if(client == 0) {
if(victim == 0) {
ReplyToCommand(param1, "FTT: Could not acquire player");
return 0;
}
static Troll troll;
GetTrollByKeyIndex(keyIndex, troll);
if(!troll.IsActive(client) && troll.HasFlags()) {
Troll troll = Troll(keyIndex);
if(!troll.IsActive(victim) && troll.HasOptions) {
// Show flag selection if troll is not enabled already
ShowSelectFlagMenu(param1, userid, modifiers, troll);
} else {
TrollEffectResponse response;
if(modifiers == 1 || modifiers == 3)
response = troll.Activate(client, param1, TrollMod_Instant);
response = troll.Activate(param1, victim, TrollMod_Instant);
if(modifiers == 2 || modifiers == 3)
response = troll.Activate(client, param1, TrollMod_Constant);
response = troll.Activate(param1, victim, TrollMod_Constant);
if(response != TE_Menu)
ShowTrollsForCategory(param1, userid, troll.categoryID);
ShowTrollsForCategory(param1, userid, troll.CategoryId);
}
} else if (action == MenuAction_End)
@ -300,20 +298,19 @@ public int ChooseTrollFlagHandler(Menu menu, MenuAction action, int param1, int
static char str[6][8];
ExplodeString(info, "|", str, 6, 8, false);
int userid = StringToInt(str[0]);
int client = GetClientOfUserId(userid);
int victim = GetClientOfUserId(userid);
int keyIndex = StringToInt(str[1]);
int modifiers = StringToInt(str[2]);
int flags = StringToInt(str[3]);
int index = StringToInt(str[4]);
bool isDone = StringToInt(str[5]) == 1; // 0 = cont, 1 = done
if(client == 0) {
if(victim == 0) {
ReplyToCommand(param1, "FTT: Could not acquire player");
return 0;
}
static Troll troll;
GetTrollByKeyIndex(keyIndex, troll);
Troll troll = Troll(keyIndex);
// If told to go to next prompt, find the next VALID prompt
// Valid prompt is one where the required flags for it, are active
@ -321,7 +318,7 @@ public int ChooseTrollFlagHandler(Menu menu, MenuAction action, int param1, int
if(isDone || index == -1) {
int nextIndex = GetNextPrompt(troll, flags, index);
// If there is a prompt available, show it, else fall down
if(nextIndex != -1) {
if(nextIndex >= 0) {
ShowSelectFlagMenu(param1, userid, modifiers, troll, flags, nextIndex);
return 0;
}
@ -335,15 +332,15 @@ public int ChooseTrollFlagHandler(Menu menu, MenuAction action, int param1, int
// Done with prompts, apply flags & modifiers
if(modifiers > 0) {
if(modifiers & view_as<int>(TrollMod_Instant))
response = troll.Activate(client, param1, TrollMod_Instant, flags);
response = troll.Activate(param1, victim, TrollMod_Instant, flags);
if(modifiers & view_as<int>(TrollMod_Constant))
response = troll.Activate(client, param1, TrollMod_Constant, flags);
response = troll.Activate(param1, victim, TrollMod_Constant, flags);
} else {
response = troll.Activate(client, param1, TrollMod_Invalid, flags);
response = troll.Activate(param1, victim, TrollMod_Invalid, flags);
}
// Jump back to selection screen
if(response != TE_Menu)
ShowTrollsForCategory(param1, userid, troll.categoryID);
ShowTrollsForCategory(param1, userid, troll.CategoryId);
} else if (action == MenuAction_End)
delete menu;
return 0;
@ -426,23 +423,21 @@ void ShowTrollsForCategory(int client, int userid, int category) {
Format(info, sizeof(info), "Category: %s", info);
trollMenu.SetTitle(info);
static Troll troll;
int victim = GetClientOfUserId(userid);
// Add all menus that have same category ID to list
static char name[MAX_TROLL_NAME_LENGTH+8];
char name[MAX_TROLL_NAME_LENGTH+8];
for(int i = 0; i < trollKV.Size; i++) {
GetTrollByKeyIndex(i, troll);
Troll troll = Troll(i);
// If troll is hidden and using normal menu, do not show
if(troll.hidden && !SilentMenuSelected[client]) continue;
if(troll.categoryID == category) {
if(troll.Hidden && !SilentMenuSelected[client]) continue;
if(troll.CategoryId == category) {
Format(info, sizeof(info), "%d|%d", userid, i);
if(troll.IsActive(victim)) {
Format(name, sizeof(name), "%s (Active)", troll.name);
Format(name, sizeof(name), "%s (Active)", Trolls[i].name);
trollMenu.AddItem(info, name);
} else
trollMenu.AddItem(info, troll.name);
trollMenu.AddItem(info, Trolls[i].name);
}
}
trollMenu.ExitButton = true;
@ -452,51 +447,44 @@ void ShowTrollsForCategory(int client, int userid, int category) {
// Called with defaults on start, then recalled by ChooseTrollFlagHandler until prompt selection finished
void ShowSelectFlagMenu(int activator, int victimUserID, int modifiers, Troll troll, int prevFlags = -1, int promptIndex = 0) {
static char info[MAX_TROLL_NAME_LENGTH+16]; //victimUSERID|trollID|modifiers|flags||flagIndex
static char name[32];
char info[MAX_TROLL_NAME_LENGTH+16]; //victimUSERID|trollID|modifiers|flags||flagIndex
char name[32];
Menu flagMenu = new Menu(ChooseTrollFlagHandler);
TrollFlagPrompt prompt;
troll.GetFlagPrompt(promptIndex, prompt);
troll.GetPrompt(promptIndex, prompt);
prompt.GetPromptText(info, sizeof(info));
flagMenu.SetTitle("%s", info);
Format(info, sizeof(info), "%s: %s", troll.name, info);
flagMenu.SetTitle(info);
if(prevFlags == -1) prevFlags = prompt.defaults;
Format(info, sizeof(info), "%d|%d|%d|%d|%d|1", victimUserID, troll.Id, modifiers, prevFlags, promptIndex);
if(prompt.multiselect) {
if(prevFlags == -1) prevFlags = prompt.defaults;
Format(info, sizeof(info), "%d|%d|%d|%d|%d|1", victimUserID, troll.id, modifiers, prevFlags, promptIndex);
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;
if(prompt.flags & a) {
troll.flagNames.GetString(i, name, sizeof(name));
// If flag is enabled, show indication (On)
if(prevFlags > 0 && prevFlags & a)
Format(name, sizeof(name), "%s ✓", 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|0", victimUserID, troll.id, modifiers, newFlags, promptIndex);
flagMenu.AddItem(info, name);
}
}
} else {
// Single choice only
if(prevFlags == -1) prevFlags = 0;
for(int i = 0; i < troll.flagNames.Length; i++) {
int a = 1 << i;
if(prompt.flags & a) {
troll.flagNames.GetString(i, name, sizeof(name));
// Add (default) indicator
if(prompt.defaults & a)
}
for(int i = 0; i < troll.TotalOptionsCount; i++) {
int bit = 1 << i;
// Does prompt have bit
if(prompt.flags & bit) {
troll.GetOptionName(i, name, sizeof(name));
// If flag is enabled, show indication (On)
int newFlags;
if(prompt.multiselect) {
if(prevFlags & bit)
Format(name, sizeof(name), "%s ✓", name);
newFlags = prevFlags ^ bit; //Toggle the flag instead of setting like below, as it's toggleable here
} else {
if(prompt.defaults & bit)
Format(name, sizeof(name), "%s (default)", name);
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);
newFlags = prevFlags | bit;
}
Format(info, sizeof(info), "%d|%d|%d|%d|%d|%b", victimUserID, troll.Id, modifiers, newFlags, promptIndex, !prompt.multiselect);
flagMenu.AddItem(info, name);
}
}
flagMenu.ExitButton = true;
@ -535,11 +523,12 @@ void ShowThrowItAllMenu(int client, int userid) {
}
int GetNextPrompt(Troll troll, int flags, int currentPrompt = 0) {
static TrollFlagPrompt prompt;
TrollFlagPrompt prompt;
// Check if we at the end of all possible prompts:
if(currentPrompt + 1 == troll.PromptCount) return -2;
//If this prompt requires flags but they don't exist, skip to next that is valid or be done:
if(currentPrompt + 1 == troll.flagPrompts.Length) return -1;
for(int i = currentPrompt + 1; i < troll.flagPrompts.Length; i++) {
troll.GetFlagPrompt(i, prompt);
for(int i = currentPrompt + 1; i < troll.PromptCount; i++) {
troll.GetPrompt(i, prompt);
if(flags & prompt.requireFlags == prompt.requireFlags) {
return i;
}

View file

@ -4,13 +4,13 @@
void ActivateAutoPunish(int client) {
if(hAutoPunish.IntValue & 2 == 2)
ApplyTroll(client, "Special Magnet", 0, TrollMod_Constant);
Troll.FromName("Special Magnet").Activate(0, client, TrollMod_Constant);
if(hAutoPunish.IntValue & 1 == 1)
ApplyTroll(client, "Tank Magnet", 0, TrollMod_Constant);
Troll.FromName("Tank Magnet").Activate(0, client, TrollMod_Constant);
if(hAutoPunish.IntValue & 8 == 8)
ApplyTroll(client, "Vomit Player", 0, TrollMod_Instant);
Troll.FromName("Vomit Player").Activate(0, client, TrollMod_Instant);
else if(hAutoPunish.IntValue & 4 == 4)
ApplyTroll(client, "Swarm", 0, TrollMod_Instant);
Troll.FromName("Swarm").Activate(0, client, TrollMod_Instant);
if(hAutoPunishExpire.IntValue > 0) {
CreateTimer(60.0 * hAutoPunishExpire.FloatValue, Timer_ResetAutoPunish, GetClientOfUserId(client));
}
@ -82,7 +82,7 @@ stock bool IsPlayerIncapped(int client) {
#define MAX_TYPOS_LENGTH 16
StringMap TYPOS_DICT;
void LoadTypos() {
TYPOS_DICT.Clear();
TYPOS_DICT = new StringMap();
char sPath[PLATFORM_MAX_PATH];
BuildPath(Path_SM, sPath, sizeof(sPath), "data/ftt_typos.txt");
@ -100,10 +100,72 @@ void LoadTypos() {
char buffer[140], key[32];
while(file.ReadLine(buffer, sizeof(buffer))) {
int index = SplitString(buffer, " ", key, sizeof(key));
TYPOS_DICT.SetString(key, buffer[index]);
AddTypo(key, buffer[index]);
}
file.Close();
delete file;
}
ArrayList SplitStringList(const char[] message, char separator, int wordSize = 64) {
ArrayList words = new ArrayList(ByteCountToCells(wordSize));
char[] word = new char[wordSize];
int len = strlen(message);
int prevIndex;
for(int i = 0; i < len; i++) {
if(message[i] == separator) {
// Only copy the length of the string. The len includes space, which is used as null term
int wordLen = (i - prevIndex);
if(wordSize < wordLen) wordLen = wordSize;
strcopy(word, wordLen, message[prevIndex]);
words.PushString(word);
prevIndex = i;
}
}
// End of string, copy the remainder
strcopy(word, len, message[prevIndex]);
words.PushString(word);
return words;
}
void ReplaceWithTypos(const char[] message, char[] output, int maxlen) {
ArrayList words = SplitStringList(message, ' ');
message[0] = '\0';
char word[64];
ArrayList replaceList;
for(int i = 0; i < words.Length; i++) {
words.GetString(i, word, sizeof(word));
if(TYPOS_DICT.GetValue(word, replaceList)) {
int index = GetRandomInt(0, replaceList.Length - 1);
replaceList.GetString(index, word, sizeof(word));
if(i == 0)
Format(output, maxlen, "%s", word);
else
Format(output, maxlen, "%s %s", message, word);
}
}
delete words;
}
void AddTypo(const char[] src, const char[] typo, bool save = false) {
ArrayList list;
TYPOS_DICT.GetValue(src, list);
if(list == null) {
list = new ArrayList(ByteCountToCells(MAX_TYPOS_LENGTH));
}
list.PushString(typo);
TYPOS_DICT.SetValue(src, list);
if(save) {
char sPath[PLATFORM_MAX_PATH];
BuildPath(Path_SM, sPath, sizeof(sPath), "data/ftt_typos.txt");
File file = OpenFile(sPath, "a", false, NULL_STRING);
if(file == null) {
PrintToServer("[FTT] Cannot open for saving: data/ftt_typos.txt");
return;
}
file.Seek(SEEK_END, 0);
file.WriteLine("%s %s", src, typo);
file.Flush();
delete file;
}
}
#define MAX_PHRASES_PER_WORD 8
@ -578,3 +640,24 @@ void SetSlot(int client, int slot) {
Format(slotStr, sizeof(slotStr), "slot%d", slot);
ClientCommand(client, slotStr);
}
void RewindPlayer(int client) {
float curFlow = L4D2Direct_GetFlowDistance(client);
ArrayList navs = new ArrayList();
L4D_GetAllNavAreas(navs);
navs.Sort(Sort_Random, Sort_Integer);
float minFlow = curFlow - 300.0;
float maxFlow = curFlow - 150.0;
// This finds the first nav area in range, usually closer
for(int i = 0; i < navs.Length; i++) {
float flow = L4D2Direct_GetTerrorNavAreaFlow(navs.Get(i));
if(flow >= minFlow && flow <= maxFlow) {
float pos[3];
L4D_FindRandomSpot(navs.Get(i), pos);
TeleportEntity(client, pos, NULL_VECTOR, NULL_VECTOR);
L4D_WarpToValidPositionIfStuck(client);
break;
}
}
delete navs;
}

View file

@ -1,10 +1,7 @@
Action Timer_ThrowTimer(Handle timer, int client) {
if(!IsClientInGame(client)) {
Trolls[t_throwItAllIndex].timerHandles[client] = null;
return Plugin_Stop;
}
ThrowAllItems(client);
if(IsClientInGame(client))
ThrowAllItems(client);
return Plugin_Continue;
}
int instantCommonRef[MAXPLAYERS+1];
@ -482,3 +479,10 @@ Action Timer_RestoreHud(Handle h, int userid) {
}
return Plugin_Handled;
}
Action Timer_RandomRewind(Handle h, int client) {
if(IsClientInGame(client) && GetURandomFloat() > 0.3) {
RewindPlayer(client);
}
return Plugin_Handled;
}

View file

@ -1,7 +1,7 @@
// UP THE VALUE 'MAX_TROLLS' in base.inc before adding new ones!
int t_slipperyShoesIndex = 0;
int t_stickyGooIndex = 0;
Troll t_stickyGoo;
int t_invertedTrollIndex;
int t_randomizeAnglesIndex;
int t_randomizeVelocityIndex;
@ -10,272 +10,270 @@ int t_shakeyCameraIndex;
int t_slotRouletteIndex;
int t_damageBoostIndex;
int t_reverseFFIndex;
int t_throwItAllIndex;
int t_hideHUDIndex;
Troll t_throwItAll;
Troll t_voiceMute;
Troll t_gunJam;
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);
TrollBuilder("Reset User", "Resets the user, removes all troll effects", TrollMod_Instant);
/// CATEGORY: Magnets
TrollBuilder troll;
SetCategory("Magnets");
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);
AddMagnetFlags(index);
troll = TrollBuilder("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(troll);
troll = TrollBuilder("Tank Magnet", "Attracts ALL tanks to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(troll);
#if defined _actions_included
index = SetupTroll("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant);
TrollBuilder("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant);
#endif
index = SetupTroll("Projectile Magnet", "Makes all projectiles (biles, molotovs, pipes, tank rocks) go to player", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Target Sources", true);
// Tied to: ProjectileMagnetType
Trolls[index].AddFlag("Infected (rocks/goo)", true);
Trolls[index].AddFlag("Teammates (grenades)", false);
Trolls[index].AddFlag("Thrown Tank Objects", false);
TrollBuilder("Projectile Magnet", "Makes all projectiles (biles, molotovs, pipes, tank rocks) go to player", TrollMod_Constant)
.AddPromptMulti("Target Sources")
// Tied to: ProjectileMagnetType
.AddOption("Infected (rocks/goo)", true)
.AddOption("Teammates (grenades)")
.AddOption("Thrown Tank Objects");
/// CATEGORY: Infected
SetCategory("Infected");
SetupTroll("Swarm", "Swarms a player with zombies. Requires swarm plugin", TrollMod_Instant | TrollMod_Constant);
t_vomitPlayerIndex = SetupTroll("Vomit Player", "Shortcut to sm_vomitplayer. vomits the player.", TrollMod_Instant | TrollMod_Constant);
index = SetupTroll("Insta Special", "Shortcut to sm_insta", TrollMod_Instant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Around them (Director)", true);
Trolls[index].AddFlag("On top / in-face", false);
SetupTroll("Goo", "Spawns a spitter puddle underneath them", TrollMod_Instant);
index = SetupTroll("Sticky Goo", "Slows player down in goo", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("90% Movement Speed", true);
Trolls[index].AddFlag("80% Movement Speed", false);
Trolls[index].AddFlag("70% Movement Speed", false);
Trolls[index].AddFlag("50% Movement Speed", false);
Trolls[index].AddFlag("30% Movement Speed", false);
Trolls[index].AddFlag("0% Movement Speed", false);
t_stickyGooIndex = index;
index = SetupTroll("Vocalize Specials", "Spawn commons on special vocals", TrollMod_Constant)
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Mute Vocalization", true);
Trolls[index].AddFlag("Do not mute", false);
index = SetupTroll("Instant Commons", "Spawns commons behind or infront", TrollMod_Instant | TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("In Back", true);
Trolls[index].AddFlag("In Front", false);
index = SetupTroll("Smart Charge", "Waits until coast is clear to charge", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Attempt Timeout", false);
Trolls[index].AddFlag("15 Seconds", true);
Trolls[index].AddFlag("30 Seconds", false);
Trolls[index].AddFlag("1 minute", false);
Trolls[index].AddFlag("5 minutes", false);
TrollBuilder("Swarm", "Swarms a player with zombies. Requires swarm plugin", TrollMod_Instant | TrollMod_Constant);
t_vomitPlayerIndex = TrollBuilder("Vomit Player", "Shortcut to sm_vomitplayer. vomits the player.", TrollMod_Instant | TrollMod_Constant).Id;
TrollBuilder("Insta Special", "Shortcut to sm_insta", TrollMod_Instant)
.AddPrompt()
.AddOption("Around them (Director)", true)
.AddOption("On top / in-face");
TrollBuilder("Goo", "Spawns a spitter puddle underneath them", TrollMod_Instant);
t_stickyGoo = TrollBuilder("Sticky Goo", "Slows player down in goo", TrollMod_Constant)
.AddPrompt()
.AddOptionFloat("90% Movement Speed", true, 0.9)
.AddOptionFloat("80% Movement Speed", false, 0.8)
.AddOptionFloat("70% Movement Speed", false, 0.7)
.AddOptionFloat("50% Movement Speed", false, 0.5)
.AddOptionFloat("30% Movement Speed", false, 0.3)
.AddOptionFloat("0% Movement Speed", false, 0.0)
.Build();
TrollBuilder("Vocalize Specials", "Spawn commons on special vocals", TrollMod_Constant)
.AddPrompt()
.AddOption("Mute Vocalization", true)
.AddOption("Do not mute", false)
TrollBuilder("Instant Commons", "Spawns commons behind or infront", TrollMod_Instant | TrollMod_Constant)
.AddPrompt()
.AddOption("In Back", true)
.AddOption("In Front", false);
TrollBuilder("Smart Charge", "Waits until coast is clear to charge", TrollMod_Constant)
.AddPrompt("Attempt Timeout")
.OnPromptActivate(Activate_SmartCharge)
.AddOptionInt("15 Seconds", true, 15)
.AddOptionInt("30 Seconds", false, 30)
.AddOptionInt("1 minute", false, 60)
.AddOptionInt("5 minutes", false, 300);
// CATEGORY: Projectiles
SetCategory("Projectiles");
index = SetupTroll("Rock Dropper", "Drops on a rock. On their head.", TrollMod_Instant);
// Trolls[index].AddFlagPrompt(false);
// Trolls[index].AddFlag("Drop From Above", true);
// Trolls[index].AddFlag("From behind", false);
index = SetupTroll("Car Splat", "Car. splats.", TrollMod_Instant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("On Top", true);
Trolls[index].AddFlag("Into (Infront)", false);
Trolls[index].AddFlag("Into (Behind)", false);
index = SetupTroll("Bad Throw", "Player drops throwables on throw, and biles/molotovs themselves", TrollMod_Constant);
Trolls[index].AddFlagPrompt(true);
Trolls[index].AddFlag("Biles", true);
Trolls[index].AddFlag("Molotovs", true);
Trolls[index].AddFlag("Pipebombs", true);
index = SetupTroll("Molotov Bath", "Throws a molotov on their feet", TrollMod_Instant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Normal", true);
Trolls[index].AddFlag("Set the town ablaze", false);
TrollBuilder("Rock Dropper", "Drops on a rock. On their head.", TrollMod_Instant);
TrollBuilder("Car Splat", "Car. splats.", TrollMod_Instant)
.AddPrompt()
.AddOption("On Top", true)
.AddOption("Into (Infront)", false)
.AddOption("Into (Behind)", false);
TrollBuilder("Bad Throw", "Player drops throwables on throw, and biles/molotovs themselves", TrollMod_Constant)
.AddPromptMulti()
.AddOption("Biles", true)
.AddOption("Molotovs", true)
.AddOption("Pipebombs", true)
TrollBuilder("Molotov Bath", "Throws a molotov on their feet", TrollMod_Instant)
.AddPrompt()
.AddOption("Normal", true)
.AddOption("Set the town ablaze", false);
// CATEGORY: Items
SetCategory("Items");
index = SetupTroll("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant);
Trolls[index].SetTimer(THROWITALL_INTERVAL, Timer_ThrowTimer);
index = SetupTroll("Spicy Gas", "Gascans player picks up just ignite. Magic.", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Always (100%)", false);
Trolls[index].AddFlag("Half Time (50%)", true);
Trolls[index].AddFlag("Rare (10%)", false);
index = SetupTroll("No Pickup", "Prevents a player from picking up ANY (new) item. Use ThrowItAll to make them drop", TrollMod_Constant);
Trolls[index].AddFlagPrompt(true);
Trolls[index].AddFlag("No Primary", false);
Trolls[index].AddFlag("No Melee", false);
Trolls[index].AddFlag("No Throwables", true);
Trolls[index].AddFlag("No Kits", true);
Trolls[index].AddFlag("No Pills / Adr", true);
Trolls[index].AddFlag("No GASCANS", true);
index = SetupTroll("UziRules / AwpSmells", "Picking up a weapon gives them a UZI or AWP instead", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("UZI Only", true);
Trolls[index].AddFlag("AWP Only", false);
SetupTroll("Primary Disable", "Player cannot pickup any weapons, only melee/pistols", TrollMod_Constant);
index = SetupTroll("Dull Melee", "Player's melee weapon does 0 damage (based on %). Headshots still work", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Always (100%)", false);
Trolls[index].AddFlag("Half Time (50%)", true);
Trolls[index].AddFlag("Rare (10%)", false);
SetupTroll("Nerf Gun", "When they shoot it does no damage.", TrollMod_Constant);
SetupTroll("Randomize Clip Ammo", "Randomly changes their clip ammo downwards", TrollMod_Constant | TrollMod_Instant);
SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant);
index = SetupTroll("Slot Roulette", "Randomize their slots", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Activations:", true);
Trolls[index].AddFlag("On Vomitted", false); // 1
Trolls[index].AddFlag("On Damage", false); // 2
Trolls[index].AddFlag("On Vocalize", false); // 4
Trolls[index].AddFlag("Periodically", true); //8
Trolls[index].AddCustomFlagPrompt("Frequency:", false, 8);
Trolls[index].AddFlag("Subtle", false); // 16
Trolls[index].AddFlag("Confusing", false); // 32
Trolls[index].AddFlag("Annoying", false); // 64
Trolls[index].AddFlag("Unusable", false); // 128
Trolls[index].SetTimer(0.2, Timer_SlotRoulette, 8);
t_slotRouletteIndex = index;
t_throwItAll = TrollBuilder("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant)
.SetTimer(THROWITALL_INTERVAL, Timer_ThrowTimer)
.Build();
TrollBuilder("Spicy Gas", "Gascans player picks up just ignite. Magic.", TrollMod_Constant)
.AddPrompt()
.AddOption("Always (100%)", false)
.AddOption("Half Time (50%)", true)
.AddOption("Rare (10%)", false);
TrollBuilder("No Pickup", "Prevents a player from picking up ANY (new) item. Use ThrowItAll to make them drop", TrollMod_Constant)
.AddPromptMulti()
.AddOption("No Primary", false)
.AddOption("No Melee", false)
.AddOption("No Throwables", true)
.AddOption("No Kits", true)
.AddOption("No Pills / Adr", true)
.AddOption("No GASCANS", true);
TrollBuilder("UziRules / AwpSmells", "Picking up a weapon gives them a UZI or AWP instead", TrollMod_Constant)
.AddPrompt()
.AddOption("UZI Only", true)
.AddOption("AWP Only", false)
TrollBuilder("Primary Disable", "Player cannot pickup any weapons, only melee/pistols", TrollMod_Constant);
TrollBuilder("Dull Melee", "Player's melee weapon does 0 damage (based on %). Headshots still work", TrollMod_Constant)
.AddPrompt()
.AddOption("Always (100%)", false)
.AddOption("Half Time (50%)", true)
.AddOption("Rare (10%)", false);
TrollBuilder("Nerf Gun", "When they shoot it does no damage.", TrollMod_Constant);
TrollBuilder("Randomize Clip Ammo", "Randomly changes their clip ammo downwards", TrollMod_Constant | TrollMod_Instant);
TrollBuilder("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant);
t_slotRouletteIndex = TrollBuilder("Slot Roulette", "Randomize their slots", TrollMod_Constant)
.SetTimer(0.2, Timer_SlotRoulette, 8)
.AddPromptMulti("Activiations")
.AddOption("On Vomitted") // 1 << 0
.AddOption("On Damage") // 1 << 1
.AddOption("On Vocalize") // 1 << 2
.AddOption("Periodically") // 1 << 3
.AddPrompt("Frequency", 1 << 3)
.AddOption("Subtle") // 1 << 4
.AddOption("Confusing") // 1 << 5
.AddOption("Annoying") // 1 << 6
.AddOption("Unusuable") // 1 << 7
.Id;
/// CATEGORY: Chat
SetCategory("Chat");
SetupTroll("Typoos", "", TrollMod_Constant);
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);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Only Replace Swears", false);
Trolls[index].AddFlag("Replace Full Messages", true);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Show Modified to Them", true);
Trolls[index].AddFlag("Show Original to Them", false);
index = SetupTroll("Vocalize Gag", "Prevents player from sending any vocalizations (even automatic)", TrollMod_Constant);
// Trolls[index].AddFlagPrompt(false);
// Trolls[index].AddFlag("Mute for All", true);
// Trolls[index].AddFlag("Mute For All But Them", false);
index = SetupTroll("Honk / Meow / Woof", "Custom sounds", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Choose Sound:");
Trolls[index].AddFlag("Honk", true);
Trolls[index].AddFlag("Meow", false);
Trolls[index].AddFlag("Woof", 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);
Trolls[index].AddFlag("Show Modified Only To Them", false);
SetupTroll("Reversed", "Reserves their message", TrollMod_Constant);
SetupTroll("Voice Mute", "Mutes from voice", TrollMod_Constant);
SetupTroll("No Rushing Us", "Decreases player speed everytime they yell hurry up", TrollMod_Constant);
TrollBuilder("Typoos", "", TrollMod_Constant);
TrollBuilder("iCantSpellNoMore", "Chat messages letter will randomly changed with wrong letters", TrollMod_Constant);
TrollBuilder("No Profanity", "Replaces some words with random phrases", TrollMod_Constant)
.AddPrompt()
.AddOption("Only Replace Swears")
.AddOption("Replace Full Messages", true)
.AddPrompt()
.AddOption("Show Modified to Them", true)
.AddOption("Show Original to Them");
TrollBuilder("Vocalize Gag", "Prevents player from sending any vocalizations (even automatic)", TrollMod_Constant);
// .AddPrompt()
// .AddOption("Mute for All", true);
// .AddOption("Mute For All But Them", false);
TrollBuilder("Honk / Meow / Woof", "Custom sounds", TrollMod_Constant)
.AddPrompt("Choose Sound:")
.AddOption("Honk", true)
.AddOption("Meow", false)
.AddOption("Woof", false)
.AddPrompt("Choose Chat modifier:", 1)
.AddOption("Show Modified to Them", true)
.AddOption("Show Original to Them", false)
.AddOption("Show Modified Only To Them", false);
TrollBuilder("Reversed", "Reserves their message", TrollMod_Constant);
t_voiceMute = TrollBuilder("Voice Mute", "Mutes from voice", TrollMod_Constant).Build();
TrollBuilder("No Rushing Us", "Decreases player speed everytime they yell hurry up", TrollMod_Constant);
/// CATEGORY: Health
SetCategory("Health");
t_damageBoostIndex = 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);
Trolls[index].AddCustomFlagPrompt("Choose Reverse FF", false);
Trolls[index].AddFlag("1:1 Ratio", true); //1
Trolls[index].AddFlag("2x Ratio", false); //2
Trolls[index].AddFlag("0.5x Ratio", false); //4
Trolls[index].AddFlag("0.0x Ratio (None)", false); //8
Trolls[index].AddFlag("3x Ratio", false); //16
Trolls[index].AddCustomFlagPrompt("Modes", true);
Trolls[index].AddFlag("Reverse Fire Damage", false); //32
Trolls[index].AddFlag("Reverse Explosions", false); //64
t_reverseFFIndex = index;
t_damageBoostIndex = TrollBuilder("Damage Boost", "Makes a player take more damage than normal", TrollMod_Constant).Id;
TrollBuilder("Temp Health Quick Drain", "Makes a player's temporarily health drain very quickly", TrollMod_Constant);
TrollBuilder("Slow Drain", "Will make the player slowly lose health over time", TrollMod_Constant);
TrollBuilder("KillMeSoftly", "Make player eat or waste pills whenever possible", TrollMod_Instant | TrollMod_Constant);
t_reverseFFIndex = TrollBuilder("Reverse FF", "All damage dealt to a player is reversed", TrollMod_Constant)
.AddPrompt("Choose Reverse FF", false)
.AddOption("1:1 Ratio", true) //1
.AddOption("2x Ratio", false) //2
.AddOption("0.5x Ratio", false) //4
.AddOption("0.0x Ratio (None)", false) //8
.AddOption("3x Ratio", false) //16
.AddPromptMulti("Modes")
.AddOption("Reverse Fire Damage", false) //32
.AddOption("Reverse Explosions", false) //64
.Id;
index = SetupTroll("Dep Bots", "Makes bots heal a player. At any cost", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Do not spawn extra", true); // 1
Trolls[index].AddFlag("Spawn extra bots (broke)", false); // 2
Trolls[index].AddCustomFlagPrompt("# Of Bots", false);
Trolls[index].AddFlag("1", false); // 4
Trolls[index].AddFlag("2", false); // 8
Trolls[index].AddFlag("3", false); // 16
Trolls[index].AddFlag("4", true); // 32
Trolls[index].AddFlag("5", false); // 64
Trolls[index].AddCustomFlagPrompt("Auto Timeout", false, 0);
Trolls[index].AddFlag("Until Healed / Map Change", false); // 128
Trolls[index].AddFlag("15 seconds", true); // 255
Trolls[index].AddFlag("30 seconds", false); // 512
Trolls[index].AddFlag("1 minute", false); //1024
Trolls[index].AddFlag("5 minutes", false); //2048
TrollBuilder("Dep Bots", "Makes bots heal a player. At any cost", TrollMod_Constant)
.AddPrompt()
.AddOption("Do not spawn extra", true) // 1
.AddOption("Spawn extra bots (broke)", false) // 2
.AddPrompt("# Of Bots")
.AddOption("1", false) // 4
.AddOption("2", false) // 8
.AddOption("3", false) // 16
.AddOption("4", true) // 32
.AddOption("5", false) // 64
.AddPrompt("Auto Timeout")
.AddOption("Until Healed / Map Change", false) // 128
.AddOption("15 seconds", true) // 255
.AddOption("30 seconds", false) // 512
.AddOption("1 minute", false) //1024
.AddOption("5 minutes", false); //2048
/// CATEGORY: Movement
SetCategory("Movement");
index = SetupTroll("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("90% Movement Speed", true);
Trolls[index].AddFlag("80% Movement Speed", false);
Trolls[index].AddFlag("70% Movement Speed", false);
Trolls[index].AddFlag("50% Movement Speed", false);
Trolls[index].AddFlag("30% Movement Speed", false);
Trolls[index].AddFlag("0% Movement Speed", false);
SetupTroll("Higher Gravity", "Sets player gravity to 1.3x of normal gravity", TrollMod_Constant);
t_invertedTrollIndex = SetupTroll("Inverted Controls", "Well, aint it obvious", TrollMod_Constant);
SetupTroll("Stagger", "Like a slap, but different", TrollMod_Instant);
index = SetupTroll("Slippery Shoes", "Periodically stumbles around.", TrollMod_Constant);
Trolls[index].AddFlagPrompt(true);
Trolls[index].AddFlag("Periodically", true);
Trolls[index].AddFlag("When using doors", false);
Trolls[index].AddFlag("On throwable use", false);
Trolls[index].AddFlag("On pills/adrenaline use", false);
Trolls[index].AddFlag("On zombie bite", false);
t_slipperyShoesIndex = index;
index = SetupTroll("Randomize Angles", "Randomly change their angles", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Frequency:", false);
Trolls[index].AddFlag("Once in a while", true); //1
Trolls[index].AddFlag("Periodically", false); //2
Trolls[index].AddFlag("A lot", false); //4
Trolls[index].AddFlag("Painful", false); //8
Trolls[index].AddFlag("Seizure", false); //16
t_randomizeAnglesIndex = index;
index = SetupTroll("Randomize Velocity", "Randomly change their velocity", TrollMod_Constant);
Trolls[index].SetTimer(0.1, Timer_RandomVelocity);
Trolls[index].AddCustomFlagPrompt("Frequency:", false);
Trolls[index].AddFlag("Loose", true); //1
Trolls[index].AddFlag("Slippery", false); //2
Trolls[index].AddFlag("Earthquake", false); //4
Trolls[index].AddFlag("Severe Earthquake", false); //8
Trolls[index].AddFlag("Bouncy Castle", false); //16
t_randomizeVelocityIndex = index;
TrollBuilder("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant)
.AddPrompt()
.AddOptionFloat("90% Movement Speed", true, 0.9)
.AddOptionFloat("80% Movement Speed", false, 0.8)
.AddOptionFloat("70% Movement Speed", false, 0.7)
.AddOptionFloat("50% Movement Speed", false, 0.5)
.AddOptionFloat("30% Movement Speed", false, 0.3)
.AddOptionFloat("0% Movement Speed", false, 0.0);
TrollBuilder("Higher Gravity", "Sets player gravity to 1.3x of normal gravity", TrollMod_Constant);
t_invertedTrollIndex = TrollBuilder("Inverted Controls", "Well, aint it obvious", TrollMod_Constant).Id;
t_slipperyShoesIndex = TrollBuilder("Slippery Shoes", "Periodically stumbles around.", TrollMod_Constant | TrollMod_Instant)
.AddPromptMulti()
.AddOption("Periodically", true) // 1 << 0
.AddOption("When using doors") // 1 << 1
.AddOption("On throwable use")
.AddOption("On pills/adrenaline use")
.AddOption("On zombie bite")
.Id
t_randomizeAnglesIndex = TrollBuilder("Randomize Angles", "Randomly change their angles", TrollMod_Constant)
.AddPrompt("Frequency:")
.AddOption("Once in a while", true) //1
.AddOption("Periodically", false) //2
.AddOption("A lot", false) //4
.AddOption("Painful", false) //8
.AddOption("Seizure", false) //16
.Id;
t_randomizeVelocityIndex = TrollBuilder("Randomize Velocity", "Randomly change their velocity", TrollMod_Constant)
.SetTimer(0.1, Timer_RandomVelocity)
.AddPrompt("Frequency:")
.AddOption("Loose", true) //1
.AddOption("Slippery", false) //2
.AddOption("Earthquake", false) //4
.AddOption("Severe Earthquake", false) //8
.AddOption("Bouncy Castle", false) //16
.Id;
TrollBuilder("Rewind", "Teleports player backwards", TrollMod_Instant | TrollMod_Constant)
.SetTimer(10.0, Timer_RandomRewind);
/// CATEGORY: MISC
SetCategory("Misc");
SetupTroll("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant);
SetupTroll("No Shove", "Prevents a player from shoving", TrollMod_Constant);
index = SetupTroll("No Button Touchie", "Stops people from pressing buttons", TrollMod_Constant);
Trolls[index].AddFlagPrompt(true);
Trolls[index].AddFlag("Prevent Use", true);
Trolls[index].AddFlag("Vomit On Touch", false);
Trolls[index].AddFlag("Incap On Touch", false);
Trolls[index].AddFlag("Slay On Touch", false);
Trolls[index].AddFlag("0.8x Speed", false);
t_gunJam = TrollBuilder("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant).Build();
TrollBuilder("No Shove", "Prevents a player from shoving", TrollMod_Constant);
TrollBuilder("No Button Touchie", "Stops people from pressing buttons", TrollMod_Constant)
.AddPromptMulti()
.AddOption("Prevent Use", true)
.AddOption("Vomit On Touch", false)
.AddOption("Incap On Touch", false)
.AddOption("Slay On Touch", false)
.AddOption("0.8x Speed", false);
// TODO: setup instant
index = SetupTroll("Shakey Camera", "Horrible", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
t_shakeyCameraIndex = TrollBuilder("Shakey Camera", "Horrible", TrollMod_Constant)
.AddPrompt()
// add flag: vomit on touch
Trolls[index].AddFlag("Annoying but playable", false);
Trolls[index].AddFlag("Bad", true);
Trolls[index].AddFlag("Sickness", false);
Trolls[index].AddFlag("Violent", false);
Trolls[index].AddFlag("Violent XX", false);
t_shakeyCameraIndex = index;
index = SetupTroll("Hide HUD", "Horrible", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Rare & Short", false);
Trolls[index].AddFlag("Sometimes & Medium", false);
Trolls[index].AddFlag("Constantly", true);
t_hideHUDIndex = index;
index = SetupTroll("Meta: Random", "Picks a random troll", TrollMod_Instant);
index = SetupTroll("Meta: Inverse", "Uhm you are not supposed to see this...", TrollMod_Instant);
Trolls[index].hidden = true;
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("100%", true);
Trolls[index].AddFlag("50%", false);
Trolls[index].AddFlag("10%", false);
.AddOption("Annoying but playable", false)
.AddOption("Bad", true)
.AddOption("Sickness", false)
.AddOption("Violent", false)
.AddOption("Violent XX", false)
.Id;
t_hideHUDIndex = TrollBuilder("Hide HUD", "Horrible", TrollMod_Constant)
.AddPrompt()
.AddOption("Rare & Short", false)
.AddOption("Sometimes & Medium", false)
.AddOption("Constantly", true)
.Id;
TrollBuilder("Meta: Random", "Picks a random troll", TrollMod_Instant);
t_metaReverse = TrollBuilder("Meta: Inverse", "Uhm you are not supposed to see this...", TrollMod_Instant)
.Hide()
.AddPrompt()
.AddOptionFloat("100%", true, 1.0)
.AddOptionFloat("50%", false, 0.5)
.AddOptionFloat("10%", false, 0.1)
.Build();
// Initialize the default flag values to -1
for(int i = 0; i <= MAX_TROLLS; i++) {
@ -287,16 +285,31 @@ void SetupTrolls() {
}
void AddMagnetFlags(int index) {
Trolls[index].AddCustomFlagPrompt("Choose Magnet Chance:", false);
Trolls[index].AddFlag("Always (100%)", true);
Trolls[index].AddFlag("Half Time (50%)", false);
Trolls[index].AddFlag("Rare (10%)", false);
TrollEffectResponse Activate_SmartCharge(Troll troll, int activator, int victim, int timeout, int flags, trollModifier mod) {
if(pdata[victim].smartChargeActivator > 0) {
ReplyToCommand(activator, "Target already has smart charge enabled");
return TE_Error;
}
pdata[victim].smartChargeAttempts = 0;
pdata[victim].smartChargeMaxAttempts = timeout;
pdata[victim].smartChargeActivator = GetClientUserId(activator);
CreateTimer(1.0, Timer_CheckForChargerOpportunity, GetClientUserId(victim), TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
return TE_Success;
}
TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, trollModifier modifier, int flags) {
bool toActive = IsTrollActiveByRawID(victim, troll.id);
if(StrEqual(troll.name, "Reset User")) {
void AddMagnetFlags(TrollBuilder troll) {
PrintToServer("adding: %d", troll.Id);
troll.AddPrompt("Choose Magnet Chance:")
.AddOptionFloat("Always (100%)", true, 1.0)
.AddOptionFloat("Half Time (50%)", false, 0.5)
.AddOptionFloat("Rare (10%)", false, 0.1);
}
TrollEffectResponse ApplyAffect(int victim, Troll troll, int activator, trollModifier modifier, int flags) {
bool toActive = troll.IsActive(victim);
char name[MAX_TROLL_NAME_LENGTH];
troll.GetName(name, sizeof(name));
if(StrEqual(name, "Reset User")) {
LogAction(activator, victim, "\"%L\" reset all effects for \"%L\"", activator, victim);
ShowActivityEx(activator, "[FTT] ", "reset effects for %N. ", victim);
// for(int i = 0; i <= MAX_TROLLS; i++) {
@ -306,7 +319,7 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
// SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
ResetClient(victim, true);
return TE_Error; // Not an error, but don't want to show activation
} else if(StrEqual(troll.name, "Slow Speed")) {
} else if(StrEqual(name, "Slow Speed")) {
if(toActive) {
float movement = 0.0;
if(flags & 1) movement = 0.9;
@ -317,22 +330,22 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", movement);
} else
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
} else if(StrEqual(troll.name, "Higher Gravity"))
} else if(StrEqual(name, "Higher Gravity"))
SetEntityGravity(victim, toActive ? 1.3 : 1.0);
else if(StrEqual(troll.name, "UziRules / AwpSmells")) {
else if(StrEqual(name, "UziRules / AwpSmells")) {
DisableTroll(victim, "No Pickup");
DisableTroll(victim, "Primary Disable");
} else if(StrEqual(troll.name, "Primary Disable")) {
} else if(StrEqual(name, "Primary Disable")) {
DisableTroll(victim, "UziRules / AwpSmells");
DisableTroll(victim, "No Pickup");
SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup);
} else if(StrEqual(troll.name, "No Pickup")) {
} else if(StrEqual(name, "No Pickup")) {
DisableTroll(victim, "UziRules / AwpSmells");
DisableTroll(victim, "Primary Disable");
SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup);
} else if(StrEqual(troll.name, "CameTooEarly")) {
} else if(StrEqual(name, "CameTooEarly")) {
ReplyToCommand(activator, "This troll mode is not implemented.");
} else if(StrEqual(troll.name, "KillMeSoftly")) {
} else if(StrEqual(name, "KillMeSoftly")) {
static char wpn[32];
GetClientWeaponName(victim, 4, wpn, sizeof(wpn));
if(StrEqual(wpn, "weapon_adrenaline") || StrEqual(wpn, "weapon_pain_pills")) {
@ -344,17 +357,17 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
}
//TODO: Implement TrollMod_Constant
return TE_Error;
} else if(StrEqual(troll.name, "Throw It All")) {
} else if(StrEqual(name, "Throw It All")) {
if(modifier & TrollMod_Instant) {
if(flags & 1) { // Hacky, just throw their kit
ThrowItemToPlayer(victim, activator, 3);
} else ThrowAllItems(victim);
}
} else if(StrEqual(troll.name, "Swarm")) {
} else if(StrEqual(name, "Swarm")) {
if(modifier & TrollMod_Instant) {
L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", victim, 15000);
}
} else if(StrEqual(troll.name, "Gun Jam")) {
} else if(StrEqual(name, "Gun Jam")) {
int wpn = GetClientWeaponEntIndex(victim, 0);
if(wpn > -1)
SDKHook(wpn, SDKHook_Reload, Event_WeaponReload);
@ -362,25 +375,25 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
ReplyToCommand(activator, "Victim does not have a primary weapon.");
return TE_Error;
}
} else if(StrEqual(troll.name, "Vomit Player"))
} else if(StrEqual(name, "Vomit Player"))
L4D_CTerrorPlayer_OnVomitedUpon(victim, victim);
else if(StrEqual(troll.name, "Insta Special")) {
else if(StrEqual(name, "Insta Special")) {
int mode = 0;
if(flags & 2) mode = 1;
ShowInstaSpecialChooser(activator, GetClientUserId(victim), mode);
return TE_Menu;
} else if(StrEqual(troll.name, "Goo")) {
} else if(StrEqual(name, "Goo")) {
static float pos[3], ang[3];
GetClientAbsOrigin(victim, pos);
GetClientAbsAngles(victim, ang);
L4D2_SpitterPrj(victim, pos, ang);
} else if(StrEqual(troll.name, "Stagger")) {
} else if(StrEqual(name, "Stagger")) {
L4D_StaggerPlayer(victim, victim, NULL_VECTOR);
} else if(StrEqual(troll.name, "Voice Mute")) {
} else if(StrEqual(name, "Voice Mute")) {
BaseComm_SetClientMute(victim, toActive);
} else if(StrEqual(troll.name, "Spicy Gas")) {
} else if(StrEqual(name, "Spicy Gas")) {
SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup);
} else if(StrEqual(troll.name, "Car Splat")) {
} else if(StrEqual(name, "Car Splat")) {
if(flags & 1) {
if(!SpawnCarOnPlayer(victim)) {
ReplyToCommand(activator, "Could not find a suitable area to spawn a car. Requires vertical space above victim.");
@ -397,7 +410,7 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
return TE_Error;
}
}
} else if(StrEqual(troll.name, "Instant Commons")) {
} else if(StrEqual(name, "Instant Commons")) {
if(modifier & TrollMod_Instant) {
float pos[3];
GetHorizontalPositionFromClient(victim, flags & 1 ? -40.0 : 40.0, pos);
@ -407,7 +420,7 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
L4D2_RunScript("CommandABot({cmd=0,bot=EntIndexToHScript(%i),target=GetPlayerFromUserID(%i)})", c, victimId);
}
}
} else if(StrEqual(troll.name, "Randomize Clip Ammo")) {
} else if(StrEqual(name, "Randomize Clip Ammo")) {
if(modifier & TrollMod_Instant) {
int primaryWpn = GetPlayerWeaponSlot(victim, 0);
if(primaryWpn > 0) {
@ -415,7 +428,7 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
SetEntProp(primaryWpn, Prop_Send, "m_iClip1", GetRandomInt(0, maxCap));
}
}
} else if(StrEqual(troll.name, "Rock Dropper")) {
} else if(StrEqual(name, "Rock Dropper")) {
float pos[3], dropPos[3];
GetClientEyePosition(victim, pos);
dropPos = pos;
@ -428,7 +441,7 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
float ang[3];
ang[0] = 90.0;
L4D_TankRockPrj(0, dropPos, ang);
} else if(StrEqual(troll.name, "Molotov Bath")) {
} else if(StrEqual(name, "Molotov Bath")) {
int count = 1;
if(flags & 2) count = 8;
float pos[3], dropPos[3];
@ -449,7 +462,7 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
for(int i = 0; i < count; i++) {
L4D_MolotovPrj(victim, dropPos, vel);
}
} else if(StrEqual(troll.name, "Dep Bots")) {
} else if(StrEqual(name, "Dep Bots")) {
if(!toActive) {
StopHealingBots();
return TE_Success;
@ -513,7 +526,7 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
wasSbFixEnabled = hSbFixEnabled.BoolValue;
hSbFixEnabled.BoolValue = false;
}
} else if(StrEqual(troll.name, "Smart Charge")) {
} else if(StrEqual(name, "Smart Charge")) {
if(pdata[victim].smartChargeActivator > 0) {
ReplyToCommand(activator, "Target already has smart charge enabled");
return TE_Error;
@ -526,32 +539,36 @@ TrollEffectResponse ApplyAffect(int victim, const Troll troll, int activator, tr
pdata[victim].smartChargeMaxAttempts = timeout;
pdata[victim].smartChargeActivator = GetClientUserId(activator);
CreateTimer(1.0, Timer_CheckForChargerOpportunity, GetClientUserId(victim), TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
} else if(StrEqual(troll.name, "No Rushing Us")) {
} else if(StrEqual(name, "No Rushing Us")) {
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
} else if(StrEqual(troll.name, "Hide HUD")) {
} else if(StrEqual(name, "Hide HUD")) {
if(toActive)
HideHUDRandom(victim);
else
SetEntProp(victim, Prop_Send, "m_iHideHUD", 0);
} else if(StrEqual(troll.name, "Meta: Random")) {
int rndTroll = GetRandomInt(0, MAX_TROLLS);
} else if(StrEqual(name, "Rewind")) {
if(modifier & TrollMod_Instant) {
RewindPlayer(victim);
}
} else if(StrEqual(name, "Meta: Random")) {
Troll rndTroll = Troll(GetRandomInt(0, MAX_TROLLS));
int rndFlags = 0;
int maxFlags = Trolls[rndTroll].GetFlagCount();
int maxFlags = rndTroll.TotalOptionsCount;
int numFlags = GetRandomInt(0, maxFlags);
while(numFlags > 0) {
// Apply a random flag
rndFlags |= GetRandomInt(0, maxFlags)
numFlags--;
}
trollModifier rndMod = Trolls[rndTroll].GetDefaultMod();
if(Trolls[rndTroll].HasMod(TrollMod_Constant) && GetURandomFloat() > 0.5) {
trollModifier rndMod = rndTroll.GetDefaultMod();
if(rndTroll.HasMod(TrollMod_Constant) && GetURandomFloat() > 0.5) {
rndMod = TrollMod_Instant;
} else if(Trolls[rndTroll].HasMod(TrollMod_Instant) && GetURandomFloat() > 0.5) {
} else if(rndTroll.HasMod(TrollMod_Instant) && GetURandomFloat() > 0.5) {
rndMod = TrollMod_Constant;
}
Trolls[rndTroll].Activate(victim, activator, rndMod, rndFlags);
rndTroll.Activate(victim, activator, rndMod, rndFlags);
} else if(~modifier & TrollMod_Constant) {
PrintToServer("[FTT] Warn: Possibly invalid troll, no apply action defined for \"%s\"", troll.name);
PrintToServer("[FTT] Warn: Possibly invalid troll, no apply action defined for \"%s\"", name);
#if defined DEBUG
ReplyToCommand(activator, "[FTT/Debug] If nothing occurs, this troll possibly was not implemented correctly. ");
#endif

View file

@ -11,6 +11,8 @@ enum L4D2Infected
L4D2Infected_Tank = 8
};
bool g_actionsAvailable;
GlobalForward g_PlayerMarkedForward;
GlobalForward g_TrollAppliedForward;
Handle g_hWitchAttack;

View file

@ -10,13 +10,16 @@
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <jutils>
#include <left4dhooks>
#include <jutils>
#undef REQUIRE_PLUGIN
#tryinclude <sceneprocessor>
#undef REQUIRE_PLUGIN
#tryinclude <actions>
#include <basecomm>
#include <ftt>
#include <multicolors>
#undef REQUIRE_PLUGIN
#tryinclude <l4d_anti_rush>
public Plugin myinfo =
@ -32,6 +35,19 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
CreateNative("ApplyTroll", Native_ApplyTroll);
return APLRes_Success;
}
public void OnLibraryAdded(const char[] name) {
if(StrEqual(name, "actionslib")) {
g_actionsAvailable = true;
}
}
public void OnLibraryRemoved(const char[] name) {
if(StrEqual(name, "actionslib")) {
g_actionsAvailable = false;
}
}
public void OnAllPluginsLoaded() {
g_actionsAvailable = LibraryExists("actionslib");
}
public void OnPluginStart() {
@ -47,7 +63,6 @@ public void OnPluginStart() {
// Load core things (trolls & phrases):
REPLACEMENT_PHRASES = new StringMap();
TYPOS_DICT = new StringMap();
LoadPhrases();
LoadTypos();
SetupTrolls();
@ -102,9 +117,11 @@ public void OnPluginStart() {
RegAdminCmd("sm_rff", Command_SetReverseFF, ADMFLAG_KICK, "Set reverse FF on player");
RegAdminCmd("sm_magnet", Command_SetMagnetShortcut, ADMFLAG_KICK, "");
RegAdminCmd("sm_csplat", Command_CarSplat, ADMFLAG_KICK, "");
RegAdminCmd("sm_typo", Command_AddTypo, ADMFLAG_GENERIC);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("player_first_spawn", Event_PlayerFirstSpawn);
HookEvent("player_disconnect", Event_PlayerDisconnect);
HookEvent("player_death", Event_PlayerDeath);
HookEvent("triggered_car_alarm", Event_CarAlarm);
HookEvent("witch_harasser_set", Event_WitchVictimSet);
@ -198,6 +215,7 @@ bool IsPlayerFarDistance(int client, float distance) {
return client == farthestClient && difference > distance;
}
#if defined _actions_included
BehaviorAction CreateWitchAttackAction(int target = 0) {
BehaviorAction action = ActionsManager.Allocate(18556);
SDKCall(g_hWitchAttack, action, target);
@ -211,6 +229,7 @@ Action OnWitchActionUpdate(BehaviorAction action, int actor, float interval, Act
result.SetReason("FTT");
return Plugin_Handled;
}
#endif
void HideHUD(int victim, float timeout = 0.0) {
SetEntProp(victim, Prop_Send, "m_iHideHUD", 64);