Update things

This commit is contained in:
Jackzie 2024-03-06 17:59:55 -06:00
parent 9590ceb207
commit d4f9241b3c
25 changed files with 650 additions and 345 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -5,20 +5,22 @@
#define PLUGIN_VERSION "1.0" #define PLUGIN_VERSION "1.0"
#define PRECACHE_SOUNDS_COUNT 5 #define PRECACHE_SOUNDS_COUNT 6
char PRECACHE_SOUNDS[PRECACHE_SOUNDS_COUNT][] = { char PRECACHE_SOUNDS[PRECACHE_SOUNDS_COUNT][] = {
"custom/meow1.mp3", "custom/meow1.mp3",
"custom/xen_teleport.mp3", "custom/xen_teleport.mp3",
"custom/mariokartmusic.mp3", "custom/mariokartmusic.mp3",
"custom/spookyscaryskeletons.mp3", "custom/spookyscaryskeletons.mp3",
"custom/wearenumberone2.mp3" "custom/wearenumberone2.mp3",
"custom/quack.mp3"
}; };
#include <sourcemod> #include <sourcemod>
#include <sdkhooks> #include <sdkhooks>
#include <left4dhooks> #include <left4dhooks>
#include <jutils.inc> #include <jutils.inc>
#include <sceneprocessor> #undef REQUIRE_PLUGIN
#tryinclude <sceneprocessor>
#include <multicolors> #include <multicolors>
#include "l4d_survivor_identity_fix.inc" #include "l4d_survivor_identity_fix.inc"
@ -177,7 +179,7 @@ Action Timer_CheckPlayerPings(Handle timer) {
if(iHighPingCount[i]++ > 2) { if(iHighPingCount[i]++ > 2) {
PrintToChat(i, "Due to your high ping (%d ms) you have been moved to AFK.", ping); PrintToChat(i, "Due to your high ping (%d ms) you have been moved to AFK.", ping);
PrintToChat(i, "You will be automatically switched back once your ping restores"); PrintToChat(i, "You will be automatically switched back once your ping restores");
SDKCall(hGoAwayFromKeyboard, i); // SDKCall(hGoAwayFromKeyboard, i);
//PrintToChat(i, "Type /pingignore to disable this feature."); //PrintToChat(i, "Type /pingignore to disable this feature.");
// L4D_ReplaceWithBot(i); // L4D_ReplaceWithBot(i);
isHighPingIdle[i] = true; isHighPingIdle[i] = true;
@ -462,7 +464,7 @@ void SetCharacter(int target, int survivorIndex, L4DModelId modelIndex, bool kee
if (IsFakeClient(target)) { if (IsFakeClient(target)) {
char name[32]; char name[32];
GetSurvivorName(target, name, sizeof(name)); GetSurvivorName(target, name, sizeof(name));
SetClientInfo(target, "name", name); // SetClientInfo(target, "name", name);
} }
UpdatePlayerIdentity(target, view_as<Character>(survivorIndex), keepModel); UpdatePlayerIdentity(target, view_as<Character>(survivorIndex), keepModel);
@ -636,6 +638,7 @@ public void OnConfigsExecuted() {
} }
} }
#if defined _sceneprocessor_included
public void OnSceneStageChanged(int scene, SceneStages stage) { public void OnSceneStageChanged(int scene, SceneStages stage) {
if(stage == SceneStage_Started) { if(stage == SceneStage_Started) {
int activator = GetSceneInitiator(scene); int activator = GetSceneInitiator(scene);
@ -650,6 +653,7 @@ public void OnSceneStageChanged(int scene, SceneStages stage) {
} }
} }
} }
#endif
///AFK BOT WEAPON FIX ///AFK BOT WEAPON FIX
public void Event_BotPlayerSwap(Event event, const char[] name, bool dontBroadcast) { public void Event_BotPlayerSwap(Event event, const char[] name, bool dontBroadcast) {
int bot = GetClientOfUserId(event.GetInt("bot")); int bot = GetClientOfUserId(event.GetInt("bot"));

View file

@ -16,3 +16,21 @@ native void ApplyTroll(int victim, const char[] name, TrollModifier modifier = T
forward void OnTrollApplied(int victim, const char[] trollName, int flags = 0, int activator = 0); forward void OnTrollApplied(int victim, const char[] trollName, int flags = 0, int activator = 0);
forward void OnTrollMarked(int activator, int victim); forward void OnTrollMarked(int activator, int victim);
public SharedPlugin __pl_myfile =
{
name = "feedthetrolls",
file = "feedthetrolls.smx",
#if defined REQUIRE_PLUGIN
required = 1,
#else
required = 0,
#endif
};
#if !defined REQUIRE_PLUGIN
public void __pl_myfile_SetNTVOptional()
{
MarkNativeAsOptional("ApplyTroll");
}
#endif

View file

@ -4,7 +4,7 @@
//Allow MAX_TROLLS to be defined elsewhere //Allow MAX_TROLLS to be defined elsewhere
#if defined MAX_TROLLS #if defined MAX_TROLLS
#else #else
#define MAX_TROLLS 56 #define MAX_TROLLS 55
#endif #endif
Troll t_metaReverse; Troll t_metaReverse;
@ -21,13 +21,15 @@ enum TrollEffectResponse {
TE_Error, // Error, continue menu (retry) TE_Error, // Error, continue menu (retry)
TE_Menu // Switching menus / etc, don't continue menu TE_Menu // Switching menus / etc, don't continue menu
} }
typeset PromptActivateFunction {
function TrollEffectResponse (Troll troll, int activator, int victim, any data, int flags, trollModifier mod)
function void (Troll troll, int activator, int victim, any data, int flags, trollModifier mod)
}
typedef ActivateFunction = function void (Troll troll, int activator, int victim, int flags, trollModifier mod); 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 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); // typedef PromptActivateFunction = function TrollEffectResponse (Troll troll, int activator, int victim, any data, int flags, trollModifier mod);
StringMap trollKV; StringMap trollKV;
char trollIds[MAX_TROLLS+1][MAX_TROLL_NAME_LENGTH];
char DEFAULT_FLAG_PROMPT_MULTIPLE[] = "Enable options (Multiple)"; char DEFAULT_FLAG_PROMPT_MULTIPLE[] = "Enable options (Multiple)";
char DEFAULT_FLAG_PROMPT[] = "Select an option"; char DEFAULT_FLAG_PROMPT[] = "Select an option";
bool SilentMenuSelected[MAXPLAYERS+1]; bool SilentMenuSelected[MAXPLAYERS+1];
@ -91,45 +93,12 @@ enum struct TrollData {
Handle timerHandles[MAXPLAYERS+1]; Handle timerHandles[MAXPLAYERS+1];
float timerInterval; float timerInterval;
int timerRequiredFlags; int timerRequiredFlags;
bool timerIsDataPack;
void Toggle(int client, int flags) { // TODO: REMOVE OLD
if(this.IsActive(client)) {
this.Disable(client);
} else {
this.Enable(client, flags);
}
}
void Enable(int client, int flags) {
this.activeFlagClients[client] = flags;
// If a timer is assigned, start it:
if(this.timerHandles[client] != null) {
delete this.timerHandles[client];
PrintToServer("FTT Debug: Old timer for %N, killing", client);
}
if(this.timerInterval > 0.0) {
this.timerHandles[client] = CreateTimer(this.timerInterval, this.timerFunction, GetClientUserId(client), TIMER_REPEAT);
}
}
void Disable(int client) {
this.activeFlagClients[client] = -1;
// Stop any running timer:
if(this.timerHandles[client] != null) {
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) { bool IsActive(int client) {
return this.activeFlagClients[client] != -1; if(this.id == 0 || client == 0) return false; // bug fix
return this.activeFlagClients[client] >= 0;
} }
} }
@ -170,6 +139,12 @@ methodmap Troll {
LogError("Unknown troll \"%s\"", name); LogError("Unknown troll \"%s\"", name);
return view_as<Troll>(i); return view_as<Troll>(i);
} }
public static bool TryFromName(const char[] name, Troll &troll) {
int i = GetTrollID(name);
if(i > -1)
troll = Troll(i);
return i > -1;
}
property bool Hidden { property bool Hidden {
public get() { return Trolls[this.Id].hidden; } public get() { return Trolls[this.Id].hidden; }
} }
@ -194,8 +169,13 @@ methodmap Troll {
public get() { return this.TotalOptionsCount > 0; } public get() { return this.TotalOptionsCount > 0; }
} }
public bool IsActive(int client) { /// Is troll active for client. If flags is > 0, will do bitwise and
return Trolls[this.Id].activeFlagClients[client] != -1; public bool IsActive(int client, int flags = 0) {
if(this.Id == 0 || client == 0) return false; // bug fix
if(flags > 0) {
return (Trolls[this.Id].activeFlagClients[client] & flags) == flags;
} else
return Trolls[this.Id].activeFlagClients[client] >= 0;
} }
public bool HasFlag(int client, int flag) { public bool HasFlag(int client, int flag) {
@ -203,7 +183,7 @@ methodmap Troll {
} }
public int GetFlags(int client) { public int GetFlags(int client) {
return Trolls[this.Id].activeFlagClients[client] return Trolls[this.Id].activeFlagClients[client];
} }
public bool HasMod(trollModifier mod) { public bool HasMod(trollModifier mod) {
@ -215,18 +195,23 @@ methodmap Troll {
} }
public TrollEffectResponse Activate(int activator, int victim, trollModifier modifier = TrollMod_Invalid, int flags = 0, bool silent = false) { 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(); if(modifier == TrollMod_Invalid) modifier = this.GetDefaultMod();
if(victim == 0) ThrowError("Victim is invalid");
return ApplyTroll(victim, this, activator, modifier, flags, silent); return ApplyTroll(victim, this, activator, modifier, flags, silent);
} }
public void Reset(int victim) { public void Reset(int victim) {
Trolls[this.Id].activeFlagClients[victim] = -1; Trolls[this.Id].activeFlagClients[victim] = -1;
// Stop any running timer:
if(Trolls[this.Id].timerHandles[victim] != null) {
PrintToServer("FTT Debug: Disabling timer for %N", victim);
delete Trolls[this.Id].timerHandles[victim];
}
if(Trolls[this.Id].resetFn != null) { if(Trolls[this.Id].resetFn != null) {
Call_StartForward(Trolls[this.Id].resetFn); Call_StartForward(Trolls[this.Id].resetFn);
Call_PushCell(this); Call_PushCell(Troll(this.Id));
Call_PushCell(0); Call_PushCell(0);
Call_PushCell(victim); Call_PushCell(victim);
Call_PushCell(0);
Call_Finish(); Call_Finish();
} }
} }
@ -310,6 +295,43 @@ methodmap Troll {
else if(Trolls[this.Id].mods == view_as<int>(TrollMod_Constant)) return TrollMod_Constant; else if(Trolls[this.Id].mods == view_as<int>(TrollMod_Constant)) return TrollMod_Constant;
else return TrollMod_Invalid; else return TrollMod_Invalid;
} }
public TrollEffectResponse _triggerActivateFn(int activator, int victim, int flags, trollModifier modifier) {
if(Trolls[this.Id].activateFn == null) return;
Call_StartForward(Trolls[this.Id].activateFn);
Call_PushCell(this);
Call_PushCell(activator);
Call_PushCell(victim);
Call_PushCell(flags);
Call_PushCell(modifier);
Call_Finish();
// 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; j++) {
// 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;
// }
// }
// return
}
} }
int g_iTrollIndex; int g_iTrollIndex;
@ -349,16 +371,22 @@ methodmap TrollBuilder {
strcopy(Trolls[this.Id].description, 128, description); strcopy(Trolls[this.Id].description, 128, description);
} }
public TrollBuilder SetTimer(float interval, Timer timer, int requiredFlags = 0) { public TrollBuilder SetTimer(float interval, Timer timer, int requiredFlags = 0, bool isDatapack = false) {
Trolls[this.Id].timerInterval = interval; Trolls[this.Id].timerInterval = interval;
Trolls[this.Id].timerFunction = timer; Trolls[this.Id].timerFunction = timer;
Trolls[this.Id].timerRequiredFlags = requiredFlags; Trolls[this.Id].timerRequiredFlags = requiredFlags;
Trolls[this.Id].timerIsDataPack = isDatapack;
// Don't think this is necessary but whatever
for(int i = 0; i <= MAXPLAYERS; i++) { for(int i = 0; i <= MAXPLAYERS; i++) {
Trolls[this.Id].timerHandles[i] = null; Trolls[this.Id].timerHandles[i] = null;
} }
return this; return this;
} }
public TrollBuilder SetAutoTimer(float interval, int requiredFlags = 0) {
this.SetTimer(interval, Timer_GenericTrollActivate, requiredFlags, true);
}
public TrollBuilder AddPrompt(const char[] customPrompt = "", int requiredFlags = 0) { public TrollBuilder AddPrompt(const char[] customPrompt = "", int requiredFlags = 0) {
this._AddPrompt(false, requiredFlags, customPrompt); this._AddPrompt(false, requiredFlags, customPrompt);
return this; return this;
@ -436,7 +464,7 @@ methodmap TrollBuilder {
} }
public TrollBuilder SetActivationFunction(ActivateFunction fn) { public TrollBuilder OnActivate(ActivateFunction fn) {
if(Trolls[this.Id].activateFn == null) { 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 = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
} }
@ -444,7 +472,7 @@ methodmap TrollBuilder {
return this; return this;
} }
public TrollBuilder SetResetFunction(ResetFunction fn) { public TrollBuilder OnReset(ResetFunction fn) {
if(Trolls[this.Id].resetFn == null) { if(Trolls[this.Id].resetFn == null) {
Trolls[this.Id].resetFn = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell); Trolls[this.Id].resetFn = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
} }
@ -458,6 +486,7 @@ methodmap TrollBuilder {
} }
int GetTrollID(const char[] name) { int GetTrollID(const char[] name) {
static int i = 0; static int i = 0;
char buffer[MAX_TROLL_NAME_LENGTH]; char buffer[MAX_TROLL_NAME_LENGTH];
@ -471,21 +500,12 @@ int GetTrollID(const char[] name) {
} }
bool IsAnyTrollActive(int victim) { bool IsAnyTrollActive(int victim) {
for(int i = 0; i <= MAX_TROLLS; i++) { for(int i = 1; i <= MAX_TROLLS; i++) {
if(Trolls[i].activeFlagClients[victim] >= 0) return true; if(Troll(i).IsActive(victim)) return true;
} }
return false; return false;
} }
void SetTrollFlags(int client, const char[] name, int flags = -1) {
int index = GetTrollID(name);
if(flags == -1)
Trolls[index].Disable(client);
else
Trolls[index].Enable(client, flags);
}
TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModifier modifier, int flags = 0, bool silent = false) { TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModifier modifier, int flags = 0, bool silent = false) {
char name[MAX_TROLL_NAME_LENGTH]; char name[MAX_TROLL_NAME_LENGTH];
troll.GetName(name, sizeof(name)); troll.GetName(name, sizeof(name));
@ -497,7 +517,16 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
if(troll.HasTimer) { if(troll.HasTimer) {
if(!isActive) { if(!isActive) {
if(modifier & TrollMod_Constant && (Trolls[trollIndex].timerRequiredFlags == 0 || Trolls[trollIndex].timerRequiredFlags & flags)) { 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); if(Trolls[trollIndex].timerIsDataPack) {
DataPack pack;
Trolls[trollIndex].timerHandles[victim] = CreateDataTimer(Trolls[trollIndex].timerInterval, Trolls[trollIndex].timerFunction, pack, TIMER_REPEAT);
pack.WriteCell(troll);
pack.WriteCell(activator);
pack.WriteCell(victim);
pack.WriteCell(flags);
} else {
Trolls[trollIndex].timerHandles[victim] = CreateTimer(Trolls[trollIndex].timerInterval, Trolls[trollIndex].timerFunction, victim, TIMER_REPEAT);
}
} }
} else if(Trolls[trollIndex].timerHandles[victim] != null) { } else if(Trolls[trollIndex].timerHandles[victim] != null) {
delete Trolls[trollIndex].timerHandles[victim]; delete Trolls[trollIndex].timerHandles[victim];
@ -517,7 +546,7 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
// If victim is a survivor bot, check if has an idle player // If victim is a survivor bot, check if has an idle player
if(IsFakeClient(victim) && GetClientTeam(victim) == 2) { if(IsFakeClient(victim) && GetClientTeam(victim) == 2) {
int player = GetSpectatorClient(victim); int player = GetRealClient(victim);
if(player > 0) { if(player > 0) {
// If there is an idle player, apply troll to them // If there is an idle player, apply troll to them
ApplyTroll(player, troll, activator, modifier, flags, silent); ApplyTroll(player, troll, activator, modifier, flags, silent);
@ -530,8 +559,10 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
// Toggle on flags for client, if it's not a single run. // Toggle on flags for client, if it's not a single run.
if(modifier & TrollMod_Constant) { if(isActive) {
Trolls[trollIndex].activeFlagClients[victim] = isActive ? -1 : flags; Trolls[trollIndex].activeFlagClients[victim] = -1;
} else if(modifier & TrollMod_Constant) {
Trolls[trollIndex].activeFlagClients[victim] = flags;
} }
// Applies any custom logic needed for a troll, mostly only used for TrollMod_Instant // Applies any custom logic needed for a troll, mostly only used for TrollMod_Instant
@ -541,15 +572,7 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
// Invoke Callbacks: // Invoke Callbacks:
if(!isActive) { if(!isActive) {
Troll instance = Troll(trollIndex); Troll instance = Troll(trollIndex);
if(Trolls[trollIndex].activateFn != null) { instance._triggerActivateFn(activator, victim, flags, modifier);
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 // Call the corresponding prompt callback if applicable
TrollFlagPrompt prompt; TrollFlagPrompt prompt;
@ -558,7 +581,7 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
if(!prompt.multiselect && prompt.activateFn != null) { if(!prompt.multiselect && prompt.activateFn != null) {
int value; int value;
instance.GetPromptDataInt(victim, i, value); instance.GetPromptDataInt(victim, i, value);
for(int j = 0; j < Trolls[trollIndex].promptOptions.Length; i++) { for(int j = 0; j < Trolls[trollIndex].promptOptions.Length; j++) {
int bit = 1 << j; int bit = 1 << j;
if(flags & bit && prompt.flags & bit) { if(flags & bit && prompt.flags & bit) {
Call_StartForward(prompt.activateFn); Call_StartForward(prompt.activateFn);
@ -582,7 +605,6 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
Call_PushCell(Troll(trollIndex)); Call_PushCell(Troll(trollIndex));
Call_PushCell(activator); Call_PushCell(activator);
Call_PushCell(victim); Call_PushCell(victim);
Call_PushCell(modifier);
Call_Finish(); Call_Finish();
} }
@ -611,11 +633,11 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
void EnableTroll(int client, const char[] troll, int flags = 0) { void EnableTroll(int client, const char[] troll, int flags = 0) {
SetTrollFlags(client, troll, flags); Troll.FromName(troll).Activate(0, client, TrollMod_Invalid, flags);
} }
void DisableTroll(int client, const char[] troll) { void DisableTroll(int client, const char[] troll) {
SetTrollFlags(client, troll, -1); Troll.FromName(troll).Reset(client);
} }
public void SetCategory(const char[] newCat) { public void SetCategory(const char[] newCat) {
@ -627,7 +649,6 @@ void GetCategory(int category, char[] buffer, int size) {
categories.GetString(category, buffer, size); categories.GetString(category, buffer, size);
} }
public int Native_ApplyTroll(Handle plugin, int numParams) { public int Native_ApplyTroll(Handle plugin, int numParams) {
int victim = GetNativeCell(1); int victim = GetNativeCell(1);
char name[MAX_TROLL_NAME_LENGTH]; char name[MAX_TROLL_NAME_LENGTH];
@ -640,7 +661,7 @@ public int Native_ApplyTroll(Handle plugin, int numParams) {
int activator = GetNativeCell(5); int activator = GetNativeCell(5);
Troll troll = Troll.FromName(name); Troll troll = Troll.FromName(name);
troll.Activate(victim, activator, modifier, flags, GetNativeCell(6)); troll.Activate(activator, victim, modifier, flags, GetNativeCell(6));
return 0; return 0;
} }

View file

@ -6,39 +6,45 @@ enum struct SpecifiedTroll {
trollModifier mod; trollModifier mod;
int flags; int flags;
} }
enum struct TrollCombo { enum struct TrollComboData {
char name[32]; char name[32];
ArrayList trolls; ArrayList trolls;
void AddTroll(const char[] name, int flags = 0, trollModifier mod = TrollMod_Invalid) { bool AddTroll(const char[] name, int flags = 0, trollModifier mod = TrollMod_Invalid) {
Troll instance = Troll.FromName(name); Troll instance;
if(!Troll.TryFromName(name, instance)) {
PrintToServer("[FTT] Combo \"%s\": unknown troll named \"%s\"", this.name, name);
return false;
}
if(mod == TrollMod_Invalid) mod = instance.GetDefaultMod(); if(mod == TrollMod_Invalid) mod = instance.GetDefaultMod();
SpecifiedTroll troll; SpecifiedTroll troll;
troll.id = instance.Id; troll.id = instance.Id;
troll.mod = mod; troll.mod = mod;
troll.flags = flags; troll.flags = flags;
this.trolls.PushArray(troll, sizeof(troll)); this.trolls.PushArray(troll, sizeof(troll));
return true;
} }
void Activate(int client, int target) { void Activate(int client, int target) {
PrintToServer("Applying %d trolls for combo %s for %N", this.trolls.Length, this.name, target);
SpecifiedTroll troll;
for(int i = 0; i < this.trolls.Length; i++) { for(int i = 0; i < this.trolls.Length; i++) {
SpecifiedTroll troll;
this.trolls.GetArray(i, troll, sizeof(troll)); this.trolls.GetArray(i, troll, sizeof(troll));
Troll(troll.id).Activate(target, client, troll.mod, troll.flags); Troll(troll.id).Activate(client, target, troll.mod, troll.flags);
} }
} }
} }
void SetupCombo(TrollCombo combo, const char[] name) { void SetupCombo(TrollComboData combo, const char[] name) {
strcopy(combo.name, sizeof(combo.name), name); strcopy(combo.name, sizeof(combo.name), name);
combo.trolls = new ArrayList(sizeof(SpecifiedTroll)); combo.trolls = new ArrayList(sizeof(SpecifiedTroll));
combos.PushArray(combo, sizeof(combo)); combos.PushArray(combo, sizeof(combo));
} }
void SetupsTrollCombos() { void SetupsTrollCombos() {
combos = new ArrayList(sizeof(TrollCombo)); combos = new ArrayList(sizeof(TrollComboData));
TrollCombo combo; TrollComboData combo;
SetupCombo(combo, "Magnet Galore"); SetupCombo(combo, "Magnet Galore");
combo.AddTroll("Special Magnet"); combo.AddTroll("Special Magnet");
combo.AddTroll("Tank Magnet"); combo.AddTroll("Tank Magnet");
@ -79,7 +85,7 @@ void SetupsTrollCombos() {
SetupCombo(combo, "Shut up"); SetupCombo(combo, "Shut up");
combo.AddTroll("Vocalize Gag"); combo.AddTroll("Vocalize Gag");
combo.AddTroll("Honk / Meow / Woof", .flags=1); combo.AddTroll("Honk & Animal Sounds", .flags=1);
SetupCombo(combo, "Weakness Compels You"); SetupCombo(combo, "Weakness Compels You");
combo.AddTroll("No Shove"); combo.AddTroll("No Shove");

View file

@ -383,10 +383,7 @@ public Action Command_ListTheTrolls(int client, int args) {
char buffer[50]; char buffer[50];
for(int p = 0; p < target_count; p++) { for(int p = 0; p < target_count; p++) {
int target = target_list[p]; int target = target_list[p];
if(IsPlayerAlive(target)) CReplyToCommand(client, "> Active Trolls for {olive}%N:", target);
ReplyToCommand(client, "> Active Trolls for %N:", target);
else
ReplyToCommand(client, "> Active Trolls for %N: (Paused)", target);
if(IsFakeClient(target)) { if(IsFakeClient(target)) {
int player = GetRealClient(target); int player = GetRealClient(target);
@ -401,9 +398,9 @@ public Action Command_ListTheTrolls(int client, int args) {
if(flags > 0) { if(flags > 0) {
buffer[0] = '\0'; buffer[0] = '\0';
troll.GetFlagNames(target, flags, buffer, sizeof(buffer)); troll.GetFlagNames(target, flags, buffer, sizeof(buffer));
ReplyToCommand(client, "\"%s\" Flags: %s", Trolls[troll.Id].name, buffer); CReplyToCommand(client, "\t{green}%s:{default} %s", Trolls[troll.Id].name, buffer);
} else } else
ReplyToCommand(client, "%s", Trolls[troll.Id].name); CReplyToCommand(client, "\t{green}%s", Trolls[troll.Id].name);
} }
} }
} }
@ -411,31 +408,29 @@ public Action Command_ListTheTrolls(int client, int args) {
} }
int count = 0; int count = 0;
char[][] modeListArr = new char[MAX_TROLLS+1][MAX_TROLL_NAME_LENGTH]; char[][] bufferList = new char[MAX_TROLLS+1][MAX_TROLL_NAME_LENGTH];
static char modeList[255]; char buffer[255];
for(int i = 1; i <= MaxClients; i++) { for(int player = 1; player <= MaxClients; player++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) > 1 && IsAnyTrollActive(i)) { if(IsClientConnected(player) && IsClientInGame(player) && GetClientTeam(player) > 1 && IsAnyTrollActive(player)) {
if(IsFakeClient(i)) { if(IsFakeClient(player)) {
int player = GetRealClient(i); int realPlayer = GetRealClient(player);
if(player != -1) i = player; if(realPlayer != -1) player = realPlayer;
} }
int modeCount = 0; int trollCount = 0;
for(int j = 1; j <= MAX_TROLLS; j++) { for(int j = 1; j < MAX_TROLLS; j++) {
Troll troll = Troll(j); Troll troll = Troll(j);
if(troll.IsActive(i)) { if(troll.IsActive(player)) {
if(troll.GetFlags(i) > 0) int flags = troll.GetFlags(player);
Format(modeListArr[modeCount], MAX_TROLL_NAME_LENGTH, "%s(%d)", trollIds[j], Trolls[j].activeFlagClients[i]); if(flags > 0)
Format(bufferList[trollCount], MAX_TROLL_NAME_LENGTH, "%s(%d)", Trolls[j].name, flags);
else else
strcopy(modeListArr[modeCount], MAX_TROLL_NAME_LENGTH, trollIds[j]); strcopy(bufferList[trollCount], MAX_TROLL_NAME_LENGTH, Trolls[j].name);
modeCount++; trollCount++;
} }
} }
ImplodeStrings(modeListArr, modeCount, ", ", modeList, sizeof(modeList)); ImplodeStrings(bufferList, trollCount, ", ", buffer, sizeof(buffer));
if(IsPlayerAlive(i)) ReplyToCommand(client, "%N | %s", player, buffer);
ReplyToCommand(client, "%N | %s", i, modeList);
else
ReplyToCommand(client, "%N (Paused) | %s", i, modeList);
count++; count++;
} }
} }

View file

@ -196,7 +196,7 @@ void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
int client = GetClientOfUserId(event.GetInt("userid")); int client = GetClientOfUserId(event.GetInt("userid"));
if(client > 0) { if(client > 0) {
for(int i = 0 ; i < MAX_TROLLS; i++) { for(int i = 0 ; i < MAX_TROLLS; i++) {
Trolls[i].activeFlagClients[client] = 0; Trolls[i].activeFlagClients[client] = -1;
if(Trolls[i].timerHandles[client] != null) { if(Trolls[i].timerHandles[client] != null) {
delete Trolls[i].timerHandles[client]; delete Trolls[i].timerHandles[client];
} }
@ -321,10 +321,6 @@ public Action RushPlayer(Handle h, int user) {
return Plugin_Handled; return Plugin_Handled;
} }
public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
static int spMagnetID, tankMagnetID;
if(spMagnetID == 0) spMagnetID = GetTrollID("Special Magnet");
if(tankMagnetID == 0) tankMagnetID = GetTrollID("Tank Magnet");
L4D2Infected class = view_as<L4D2Infected>(GetEntProp(attacker, Prop_Send, "m_zombieClass")); L4D2Infected class = view_as<L4D2Infected>(GetEntProp(attacker, Prop_Send, "m_zombieClass"));
// Check for any existing victims // Check for any existing victims
int existingTarget = GetClientOfUserId(pdata[attacker].attackerTargetUid); int existingTarget = GetClientOfUserId(pdata[attacker].attackerTargetUid);
@ -337,16 +333,16 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
} }
// Stop targetting if no longer magnetted: // Stop targetting if no longer magnetted:
if(class == L4D2Infected_Tank) { if(class == L4D2Infected_Tank) {
if(!Trolls[tankMagnetID].IsActive(existingTarget) || !WillMagnetRun(Trolls[tankMagnetID], existingTarget)) return Plugin_Continue; if(!t_tankMagnet.IsActive(existingTarget) || !WillMagnetRun(t_tankMagnet, existingTarget)) return Plugin_Continue;
} else if(class != L4D2Infected_Tank) { } else if(class != L4D2Infected_Tank) {
if(!Trolls[spMagnetID].IsActive(existingTarget) || !WillMagnetRun(Trolls[spMagnetID], existingTarget)) return Plugin_Continue; if(!t_specialMagnet.IsActive(existingTarget) || !WillMagnetRun(t_specialMagnet, existingTarget)) return Plugin_Continue;
} }
// Only set target based on incap rules: // Only set target based on incap rules:
if(class == L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 2) && WillMagnetRun(Trolls[tankMagnetID], existingTarget)) { if(class == L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 2) && WillMagnetRun(t_tankMagnet, existingTarget)) {
curTarget = existingTarget; curTarget = existingTarget;
return Plugin_Changed; return Plugin_Changed;
} else if(class != L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 1) && WillMagnetRun(Trolls[spMagnetID], existingTarget)) { } else if(class != L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 1) && WillMagnetRun(t_specialMagnet, existingTarget)) {
curTarget = existingTarget; curTarget = existingTarget;
return Plugin_Changed; return Plugin_Changed;
} }
@ -367,9 +363,9 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
if(class == L4D2Infected_Tank) { if(class == L4D2Infected_Tank) {
if(!Trolls[tankMagnetID].IsActive(i) || !WillMagnetRun(Trolls[tankMagnetID], i)) continue; if(!t_tankMagnet.IsActive(i) || !WillMagnetRun(t_tankMagnet, i)) continue;
} else if(class != L4D2Infected_Tank) { } else if(class != L4D2Infected_Tank) {
if(!Trolls[spMagnetID].IsActive(i) || !WillMagnetRun(Trolls[spMagnetID], i)) continue; if(!t_specialMagnet.IsActive(i) || !WillMagnetRun(t_specialMagnet, i)) continue;
} }
if(IsPlayerIncapped(i)) { if(IsPlayerIncapped(i)) {
@ -395,26 +391,21 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
return Plugin_Continue; return Plugin_Continue;
} }
// TODO: migrate to Troll bool WillMagnetRun(Troll troll, int client) {
bool WillMagnetRun(const TrollData troll, int i) {
// In the case none of the flags are set, return true (100% chance) // In the case none of the flags are set, return true (100% chance)
// Some systems may give magnet w/ no flags // Some systems may give magnet w/ no flags
if(troll.activeFlagClients[i] == 0) return true; int flags = troll.GetFlags(client);
if(flags == 0) return true;
float cChance = 1.0; float chance = 1.0;
//Skip first bit as it is ('Always') troll.GetPromptDataFloat(client, 0, chance);
if(troll.activeFlagClients[i] & 2) // 2nd: 50% return GetRandomFloat() <= chance;
cChance = 0.5;
else if(troll.activeFlagClients[i] & 4) //3rd: 10%
cChance = 0.1;
return GetRandomFloat() <= cChance;
} }
public Action L4D2_OnEntityShoved(int client, int entity, int weapon, float vecDir[3], bool bIsHighPounce) { public Action L4D2_OnEntityShoved(int client, int entity, int weapon, float vecDir[3], bool bIsHighPounce) {
if(client > 0 && client <= MaxClients) { if(client > 0 && client <= MaxClients) {
static int noShoveIndex; static Troll t_noShove;
if(noShoveIndex == 0) noShoveIndex == GetTrollID("No Shove"); if(t_noShove.Id == 0) t_noShove == Troll.FromName("No Shove");
if(Trolls[noShoveIndex].IsActive(client) && GetRandomFloat() < hShoveFailChance.FloatValue) { if(t_noShove.IsActive(client) && GetRandomFloat() < hShoveFailChance.FloatValue) {
float shoveTime = L4D2Direct_GetNextShoveTime(client); float shoveTime = L4D2Direct_GetNextShoveTime(client);
L4D2Direct_SetNextShoveTime(client, shoveTime + 2.0); L4D2Direct_SetNextShoveTime(client, shoveTime + 2.0);
return Plugin_Handled; return Plugin_Handled;
@ -426,14 +417,12 @@ public Action L4D2_OnEntityShoved(int client, int entity, int weapon, float vecD
public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) { public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) {
if(client <= 0 || sArgs[0] == '@') return Plugin_Continue; //Ignore admin chat or console if(client <= 0 || sArgs[0] == '@') return Plugin_Continue; //Ignore admin chat or console
static int honkID;
static int profanityID; static int profanityID;
static int typooId; static int typooId;
if(honkID == 0) honkID = GetTrollID("Honk / Meow / Woof");
if(profanityID == 0) profanityID = GetTrollID("No Profanity"); if(profanityID == 0) profanityID = GetTrollID("No Profanity");
if(typooId == 0) typooId = GetTrollID("Typoos"); if(typooId == 0) typooId = GetTrollID("Typoos");
if(Trolls[honkID].IsActive(client) && Trolls[honkID].activeFlagClients[client] & 1) { if(t_honk.IsActive(client) && t_honk.GetFlags(client) & 1) {
// Honk Processing // Honk Processing
static char strings[32][8]; static char strings[32][8];
int words = ExplodeString(sArgs, " ", strings, sizeof(strings), 5); int words = ExplodeString(sArgs, " ", strings, sizeof(strings), 5);
@ -446,17 +435,14 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
int length = 8 * words; int length = 8 * words;
char[] message = new char[length]; char[] message = new char[length];
ImplodeStrings(strings, 32, " ", message, length); ImplodeStrings(strings, 32, " ", message, length);
if(Trolls[honkID].activeFlagClients[client] & 1) if(t_honk.HasFlag(client, 16)) {
// Show modified to them
CPrintToChatAll("{blue}%N {default}: %s", client, message); CPrintToChatAll("{blue}%N {default}: %s", client, message);
else { } else {
CPrintToChat(client, "{blue}%N {default}: %s", client, message); CPrintToChat(client, "{blue}%N {default}: %s", client, message);
bool showOriginalToOthers = Trolls[honkID].activeFlagClients[client] & 4 != 0;
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && i != client) { if(IsClientConnected(i) && IsClientInGame(i) && i != client) {
if(showOriginalToOthers) CPrintToChat(i, "{blue}%N {default}: %s", client, sArgs);
CPrintToChat(i, "{blue}%N {default}: %s", client, sArgs);
else
CPrintToChat(i, "{blue}%N {default}: %s", client, message);
} }
} }
} }
@ -742,7 +728,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
} }
// Inverted control code: // Inverted control code:
if(Trolls[t_invertedTrollIndex].IsActive(client)) { if(t_invertedTroll.IsActive(client)) {
if(buttons & IN_MOVELEFT || buttons & IN_MOVERIGHT) { if(buttons & IN_MOVELEFT || buttons & IN_MOVERIGHT) {
vel[1] = -vel[1]; vel[1] = -vel[1];
} }
@ -786,7 +772,7 @@ Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& damage
if(GetClientTeam(attacker) == 4 && IsFakeClient(attacker)) return Plugin_Continue; if(GetClientTeam(attacker) == 4 && IsFakeClient(attacker)) return Plugin_Continue;
} }
// Boost all damage no matter what // Boost all damage no matter what
if(Trolls[t_damageBoostIndex].IsActive(victim)) { if(t_damageBoost.IsActive(victim)) {
damage * 2; damage * 2;
return Plugin_Changed; return Plugin_Changed;
} }
@ -804,29 +790,22 @@ Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& damage
if(damage > 0.0 && Trolls[t_slipperyShoesIndex].IsActive(victim) && Trolls[t_slipperyShoesIndex].activeFlagClients[victim] & 16) { if(damage > 0.0 && Trolls[t_slipperyShoesIndex].IsActive(victim) && Trolls[t_slipperyShoesIndex].activeFlagClients[victim] & 16) {
L4D_StaggerPlayer(victim, victim, NULL_VECTOR); L4D_StaggerPlayer(victim, victim, NULL_VECTOR);
} }
if(isSameTeam && Trolls[t_reverseFFIndex].IsActive(attacker)) { if(isSameTeam && t_reverseFF.IsActive(attacker)) {
// Should this be applied? (as in no FF granted) // Should this be applied? (as in no FF granted)
bool disableFF = false; bool disableFF = false;
int flags = t_reverseFF.GetFlags(attacker);
if(damagetype & DMG_BURN) { if(damagetype & DMG_BURN) {
disableFF = Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 32 != 0; disableFF = flags & 64 != 0;
} else if(damagetype & DMG_BLAST) { } else if(damagetype & DMG_BLAST) {
disableFF = Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 64 != 0; disableFF = flags & 32 != 0;
} else { } else {
// Does not run if DMG_BURN or DMG_BLAST // Does not run if DMG_BURN or DMG_BLAST, basically any other damage was caused besides burn/blast, then allow it
disableFF = true; disableFF = true;
} }
if(disableFF) { if(disableFF) {
float returnDmg = damage; //default is 1:1 float returnDmg = damage; //default is 1:1
if(Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 2) { t_reverseFF.GetPromptDataFloat(attacker, 0, returnDmg);
returnDmg *= 2.0;
} else if(Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 4) {
returnDmg /= 2.0;
} else if(Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 8) {
returnDmg = 0.0;
} else if(Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 16) {
returnDmg *= 3.0;
}
SDKHooks_TakeDamage(attacker, attacker, attacker, returnDmg, damagetype, -1); SDKHooks_TakeDamage(attacker, attacker, attacker, returnDmg, damagetype, -1);
damage = 0.0; damage = 0.0;
return Plugin_Changed; return Plugin_Changed;
@ -863,11 +842,7 @@ Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& damage
} }
public Action OnVocalizeCommand(int client, const char[] vocalize, int initiator) { public Action OnVocalizeCommand(int client, const char[] vocalize, int initiator) {
static int vocalGagID; if(t_noRushingUs.IsActive(client) && (StrEqual(vocalize, "PlayerHurryUp") || StrEqual(vocalize, "PlayerYellRun") || StrEqual(vocalize, "PlayerMoveOn") || StrEqual(vocalize, "PlayerLeadOn"))) {
static int noRushingUsID;
if(vocalGagID == 0) vocalGagID = GetTrollID("Vocalize Gag");
if(noRushingUsID == 0) noRushingUsID = GetTrollID("No Rushing Us");
if(Trolls[noRushingUsID].IsActive(client) && (StrEqual(vocalize, "PlayerHurryUp") || StrEqual(vocalize, "PlayerYellRun") || StrEqual(vocalize, "PlayerMoveOn") || StrEqual(vocalize, "PlayerLeadOn"))) {
noRushingUsSpeed[client] -= 0.01; noRushingUsSpeed[client] -= 0.01;
if(noRushingUsSpeed[client] < 0.05) { if(noRushingUsSpeed[client] < 0.05) {
noRushingUsSpeed[client] = 0.05; noRushingUsSpeed[client] = 0.05;
@ -878,7 +853,7 @@ public Action OnVocalizeCommand(int client, const char[] vocalize, int initiator
if(Trolls[t_slotRouletteIndex].IsActive(client) && GetURandomFloat() < 0.10) { if(Trolls[t_slotRouletteIndex].IsActive(client) && GetURandomFloat() < 0.10) {
SetSlot(client, -1); SetSlot(client, -1);
} }
if(Trolls[vocalGagID].IsActive(client)) { if(t_vocalGag.IsActive(client) && t_vocalGag.GetFlags(client) == 0) {
return Plugin_Handled; return Plugin_Handled;
} }
return Plugin_Continue; return Plugin_Continue;
@ -922,11 +897,6 @@ public void OnSceneStageChanged(int scene, SceneStages stage) {
#endif #endif
public Action SoundHook(int clients[MAXPLAYERS], int& numClients, char sample[PLATFORM_MAX_PATH], int& entity, int& channel, float& volume, int& level, int& pitch, int& flags, char soundEntry[PLATFORM_MAX_PATH], int& seed) { public Action SoundHook(int clients[MAXPLAYERS], int& numClients, char sample[PLATFORM_MAX_PATH], int& entity, int& channel, float& volume, int& level, int& pitch, int& flags, char soundEntry[PLATFORM_MAX_PATH], int& seed) {
static int honkID;
static int vocalGagID;
if(honkID == 0) honkID = GetTrollID("Honk / Meow / Woof");
if(vocalGagID == 0) vocalGagID = GetTrollID("Vocalize Gag");
if(lastButtonUser > 0 && IsClientConnected(lastButtonUser) && !IsFakeClient(lastButtonUser) && StrEqual(sample, "npc/mega_mob/mega_mob_incoming.wav")) { if(lastButtonUser > 0 && IsClientConnected(lastButtonUser) && !IsFakeClient(lastButtonUser) && StrEqual(sample, "npc/mega_mob/mega_mob_incoming.wav")) {
PrintToConsoleAll("CRESCENDO STARTED BY %N", lastButtonUser); PrintToConsoleAll("CRESCENDO STARTED BY %N", lastButtonUser);
#if defined DEBUG #if defined DEBUG
@ -943,24 +913,37 @@ public Action SoundHook(int clients[MAXPLAYERS], int& numClients, char sample[PL
lastButtonUser = -1; lastButtonUser = -1;
}else if(numClients > 0 && entity > 0 && entity <= MaxClients) { }else if(numClients > 0 && entity > 0 && entity <= MaxClients) {
if(StrContains(sample, "survivor\\voice") > -1) { if(StrContains(sample, "survivor\\voice") > -1) {
if(Trolls[honkID].IsActive(entity)) { if(t_honk.IsActive(entity)) {
if(Trolls[honkID].activeFlagClients[entity] & 1) int trollFlags = t_honk.GetFlags(entity);
if(trollFlags & 1) {
strcopy(sample, sizeof(sample), "player/footsteps/clown/concrete1.wav"); strcopy(sample, sizeof(sample), "player/footsteps/clown/concrete1.wav");
else if(Trolls[honkID].activeFlagClients[entity] & 2) { } else if(trollFlags & 2) {
strcopy(sample, sizeof(sample), "custom/quack.mp3");
// volume += 0.2;
} else if(trollFlags & 4) {
strcopy(sample, sizeof(sample), "custom/meow1.mp3"); strcopy(sample, sizeof(sample), "custom/meow1.mp3");
volume += 0.2; volume += 0.2;
} else if(Trolls[honkID].activeFlagClients[entity] & 4) { } else if(trollFlags & 8) {
strcopy(sample, sizeof(sample), "custom/woof1.mp3"); strcopy(sample, sizeof(sample), "custom/woof1.mp3");
volume += 0.6; volume += 0.6;
} else } else
return Plugin_Continue; return Plugin_Continue;
return Plugin_Changed; return Plugin_Changed;
} else if(Trolls[vocalGagID].IsActive(entity)) { } else if(t_vocalGag.IsActive(entity)) {
if(Trolls[vocalGagID].activeFlagClients[entity] & 2) { int trollFlags = t_vocalGag.GetFlags(entity);
clients[0] = entity; if(trollFlags & 2) {
numClients = 1; SDKHooks_TakeDamage(entity, entity, entity, 1.0, DMG_GENERIC);
}
if(trollFlags & 1) {
volume /= 2.0;
return Plugin_Changed; return Plugin_Changed;
} }
// if(Trolls[vocalGagID].activeFlagClients[entity] & 2) {
// clients[0] = entity;
// numClients = 1;
// return Plugin_Changed;
// }
return Plugin_Handled; return Plugin_Handled;
} }
} }
@ -1008,11 +991,10 @@ public void Event_WitchVictimSet(Event event, const char[] name, bool dontBroadc
public Action L4D2_MeleeGetDamageForVictim(int client, int weapon, int victim, float &damage) { public Action L4D2_MeleeGetDamageForVictim(int client, int weapon, int victim, float &damage) {
static int dullMeleeID; static int dullMeleeID;
if(!dullMeleeID) dullMeleeID = GetTrollID("Dull Melee"); if(!dullMeleeID) dullMeleeID = GetTrollID("Dull Melee");
if(Trolls[dullMeleeID].IsActive(client)) { if(t_dullMelee.IsActive(client)) {
float max = 1.0; float chance = 1.0;
if(Trolls[dullMeleeID].activeFlagClients[client] & 2) max = 0.5; t_dullMelee.GetPromptDataFloat(client, 0, chance);
else if(Trolls[dullMeleeID].activeFlagClients[client] & 4) max = 0.1; if(GetURandomFloat() <= chance) {
if(GetRandomFloat() <= max) {
damage = 0.0; damage = 0.0;
return Plugin_Changed; return Plugin_Changed;
} }

View file

@ -129,7 +129,7 @@ public int ChooseComboHandler(Menu menu, MenuAction action, int client, int para
return 0; return 0;
} }
static TrollCombo combo; static TrollComboData combo;
combos.GetArray(comboID, combo, sizeof(combo)); combos.GetArray(comboID, combo, sizeof(combo));
combo.Activate(client, victim); combo.Activate(client, victim);
} else if (action == MenuAction_End) } else if (action == MenuAction_End)
@ -377,7 +377,7 @@ void ShowTrollCombosMenu(int client, int victimUserID) {
Format(id, sizeof(id), "Choose troll combo"); Format(id, sizeof(id), "Choose troll combo");
comboMenu.SetTitle(id); comboMenu.SetTitle(id);
static TrollCombo combo; static TrollComboData combo;
if(combos.Length == 0) { if(combos.Length == 0) {
ReplyToCommand(client, "FTT: No troll combos available"); ReplyToCommand(client, "FTT: No troll combos available");
@ -457,7 +457,8 @@ void ShowSelectFlagMenu(int activator, int victimUserID, int modifiers, Troll tr
prompt.GetPromptText(info, sizeof(info)); prompt.GetPromptText(info, sizeof(info));
flagMenu.SetTitle("%s", info); flagMenu.SetTitle("%s", info);
if(prevFlags == -1) prevFlags = prompt.defaults; if(prevFlags == -1 && prompt.multiselect) 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);

View file

@ -63,18 +63,6 @@ bool ToggleMarkPlayer(int client, int target) {
} }
} }
// Finds the survivor bot that took over an idle player
int GetSpectatorClient(int bot) {
if(!IsFakeClient(bot)) return -1;
static char netclass[16];
GetEntityNetClass(bot, netclass, sizeof(netclass));
if(strcmp(netclass, "SurvivorBot") == 0 ) {
int user = GetEntProp(bot, Prop_Send, "m_humanSpectatorUserID");
if(user > 0) return GetClientOfUserId(user);
}
return -1;
}
stock bool IsPlayerIncapped(int client) { stock bool IsPlayerIncapped(int client) {
return GetEntProp(client, Prop_Send, "m_isIncapacitated") == 1; return GetEntProp(client, Prop_Send, "m_isIncapacitated") == 1;
} }
@ -641,13 +629,13 @@ void SetSlot(int client, int slot) {
ClientCommand(client, slotStr); ClientCommand(client, slotStr);
} }
void RewindPlayer(int client) { void RewindPlayer(int client, float distance = 100.0) {
float curFlow = L4D2Direct_GetFlowDistance(client); float curFlow = L4D2Direct_GetFlowDistance(client);
ArrayList navs = new ArrayList(); ArrayList navs = new ArrayList();
L4D_GetAllNavAreas(navs); L4D_GetAllNavAreas(navs);
navs.Sort(Sort_Random, Sort_Integer); navs.Sort(Sort_Random, Sort_Integer);
float minFlow = curFlow - 300.0; float minFlow = curFlow - (3.0*distance);
float maxFlow = curFlow - 150.0; float maxFlow = curFlow - (1.5*distance);
// This finds the first nav area in range, usually closer // This finds the first nav area in range, usually closer
for(int i = 0; i < navs.Length; i++) { for(int i = 0; i < navs.Length; i++) {
float flow = L4D2Direct_GetTerrorNavAreaFlow(navs.Get(i)); float flow = L4D2Direct_GetTerrorNavAreaFlow(navs.Get(i));

View file

@ -154,6 +154,9 @@ Action Timer_SetWitchTarget(Handle h, DataPack pack) {
} }
stock SpecialType GetSpecialType(const char[] input) { stock SpecialType GetSpecialType(const char[] input) {
if(StrEqual(input, "random")) {
return view_as<SpecialType>(GetRandomInt(0, 6))
}
for(int i = 0; i < 8; i++) { for(int i = 0; i < 8; i++) {
if(strcmp(SPECIAL_NAMES[i], input, false) == 0) return view_as<SpecialType>(i + 1); if(strcmp(SPECIAL_NAMES[i], input, false) == 0) return view_as<SpecialType>(i + 1);
} }

View file

@ -481,8 +481,21 @@ Action Timer_RestoreHud(Handle h, int userid) {
} }
Action Timer_RandomRewind(Handle h, int client) { Action Timer_RandomRewind(Handle h, int client) {
if(IsClientInGame(client) && GetURandomFloat() > 0.3) { if(IsClientInGame(client) && GetURandomFloat() > 0.3) {
RewindPlayer(client); float distance = 100.0;
t_rewind.GetPromptDataFloat(client, 0, distance);
RewindPlayer(client, distance);
} }
return Plugin_Handled; return Plugin_Handled;
} }
Action Timer_GenericTrollActivate(Handle h, DataPack pack) {
pack.Reset();
Troll troll = Troll(pack.ReadCell());
int activator = pack.ReadCell();
int victim = pack.ReadCell();
if(IsClientInGame(victim)) {
int flags = pack.ReadCell();
troll._triggerActivateFn(activator, victim, flags, TrollMod_Constant);
}
return Plugin_Handled;
}

View file

@ -1,19 +1,26 @@
// UP THE VALUE 'MAX_TROLLS' in base.inc before adding new ones! // UP THE VALUE 'MAX_TROLLS' in base.inc before adding new ones!
Troll t_specialMagnet;
Troll t_tankMagnet;
int t_slipperyShoesIndex = 0; int t_slipperyShoesIndex = 0;
Troll t_stickyGoo; Troll t_stickyGoo;
int t_invertedTrollIndex; Troll t_invertedTroll;
int t_randomizeAnglesIndex; int t_randomizeAnglesIndex;
int t_randomizeVelocityIndex; int t_randomizeVelocityIndex;
int t_vomitPlayerIndex; int t_vomitPlayerIndex;
int t_shakeyCameraIndex; int t_shakeyCameraIndex;
int t_slotRouletteIndex; int t_slotRouletteIndex;
int t_damageBoostIndex; Troll t_damageBoost;
int t_reverseFFIndex; Troll t_reverseFF;
int t_hideHUDIndex; int t_hideHUDIndex;
Troll t_throwItAll; Troll t_throwItAll;
Troll t_voiceMute; Troll t_voiceMute;
Troll t_gunJam; Troll t_gunJam;
Troll t_honk;
Troll t_vocalGag;
Troll t_dullMelee;
Troll t_rewind;
Troll t_noRushingUs;
void SetupTrolls() { void SetupTrolls() {
trollKV = new StringMap(); trollKV = new StringMap();
@ -25,8 +32,10 @@ void SetupTrolls() {
SetCategory("Magnets"); SetCategory("Magnets");
troll = TrollBuilder("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant); troll = TrollBuilder("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(troll); AddMagnetFlags(troll);
t_specialMagnet = troll.Build();
troll = TrollBuilder("Tank Magnet", "Attracts ALL tanks to any alive target with this troll enabled", TrollMod_Constant); troll = TrollBuilder("Tank Magnet", "Attracts ALL tanks to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(troll); AddMagnetFlags(troll);
t_tankMagnet = troll.Build();
#if defined _actions_included #if defined _actions_included
TrollBuilder("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 #endif
@ -112,14 +121,14 @@ void SetupTrolls() {
.AddOption("UZI Only", true) .AddOption("UZI Only", true)
.AddOption("AWP Only", false) .AddOption("AWP Only", false)
TrollBuilder("Primary Disable", "Player cannot pickup any weapons, only melee/pistols", TrollMod_Constant); 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) t_dullMelee = TrollBuilder("Dull Melee", "Player's melee weapon does 0 damage (based on %). Headshots still work", TrollMod_Constant)
.AddPrompt() .AddPrompt()
.AddOption("Always (100%)", false) .AddOptionFloat("Always (100%)", false, 1.0)
.AddOption("Half Time (50%)", true) .AddOptionFloat("Half Time (50%)", true, 0.5)
.AddOption("Rare (10%)", false); .AddOptionFloat("Rare (10%)", false, 0.10)
.Build();
TrollBuilder("Nerf Gun", "When they shoot it does no damage.", TrollMod_Constant); 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("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) t_slotRouletteIndex = TrollBuilder("Slot Roulette", "Randomize their slots", TrollMod_Constant)
.SetTimer(0.2, Timer_SlotRoulette, 8) .SetTimer(0.2, Timer_SlotRoulette, 8)
@ -146,40 +155,42 @@ void SetupTrolls() {
.AddPrompt() .AddPrompt()
.AddOption("Show Modified to Them", true) .AddOption("Show Modified to Them", true)
.AddOption("Show Original to Them"); .AddOption("Show Original to Them");
TrollBuilder("Vocalize Gag", "Prevents player from sending any vocalizations (even automatic)", TrollMod_Constant); t_vocalGag = TrollBuilder("Vocalize Gag", "Prevents player from sending any vocalizations (even automatic)", TrollMod_Constant)
// .AddPrompt() .AddPromptMulti()
// .AddOption("Mute for All", true); .AddOption("Quieter", false)
// .AddOption("Mute For All But Them", false); .AddOption("Painful", false)
TrollBuilder("Honk / Meow / Woof", "Custom sounds", TrollMod_Constant) .Build()
t_honk = TrollBuilder("Honk & Animal Sounds", "Custom sounds", TrollMod_Constant)
.AddPrompt("Choose Sound:") .AddPrompt("Choose Sound:")
.AddOption("Honk", true) .AddOption("Honk", true) // 1 << 0
.AddOption("Meow", false) .AddOption("Quack", false) // 1 << 1
.AddOption("Woof", false) .AddOption("Meow", false) // 1 << 2
.AddOption("Woof", false) // 1 << 3
.AddPrompt("Choose Chat modifier:", 1) .AddPrompt("Choose Chat modifier:", 1)
.AddOption("Show Modified to Them", true) .AddOption("Show Modified To All", true) // 1 << 4
.AddOption("Show Original to Them", false) .AddOption("Show Original To Others", false) // 1 << 5
.AddOption("Show Modified Only To Them", false); .Build();
TrollBuilder("Reversed", "Reserves their message", TrollMod_Constant); TrollBuilder("Reversed", "Reserves their message", TrollMod_Constant);
t_voiceMute = TrollBuilder("Voice Mute", "Mutes from voice", TrollMod_Constant).Build(); 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); t_noRushingUs = TrollBuilder("No Rushing Us", "Decreases player speed everytime they yell hurry up", TrollMod_Constant).Build();
/// CATEGORY: Health /// CATEGORY: Health
SetCategory("Health"); SetCategory("Health");
t_damageBoostIndex = TrollBuilder("Damage Boost", "Makes a player take more damage than normal", TrollMod_Constant).Id; t_damageBoost = TrollBuilder("Damage Boost", "Makes a player take more damage than normal", TrollMod_Constant).Build();
TrollBuilder("Temp Health Quick Drain", "Makes a player's temporarily health drain very quickly", TrollMod_Constant); 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("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); 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) t_reverseFF = TrollBuilder("Reverse FF", "All damage dealt to a player is reversed", TrollMod_Constant)
.AddPrompt("Choose Reverse FF", false) .AddPrompt("Choose Reverse FF", false)
.AddOption("1:1 Ratio", true) //1 .AddOptionFloat("1:1 Ratio", true, 1.0) //1
.AddOption("2x Ratio", false) //2 .AddOptionFloat("2x Ratio", false, 2.0) //2
.AddOption("0.5x Ratio", false) //4 .AddOptionFloat("0.5x Ratio", false, 0.5) //4
.AddOption("0.0x Ratio (None)", false) //8 .AddOptionFloat("0.0x Ratio (None)", false, 0.0) //8
.AddOption("3x Ratio", false) //16 .AddOptionFloat("3x Ratio", false, 3.0) //16
.AddPromptMulti("Modes") .AddPromptMulti("Modes")
.AddOption("Reverse Fire Damage", false) //32 .AddOption("Reverse Fire Damage", false) //32
.AddOption("Reverse Explosions", false) //64 .AddOption("Reverse Explosions", false) //64
.Id; .Build();
TrollBuilder("Dep Bots", "Makes bots heal a player. At any cost", TrollMod_Constant) TrollBuilder("Dep Bots", "Makes bots heal a player. At any cost", TrollMod_Constant)
@ -202,15 +213,19 @@ void SetupTrolls() {
/// CATEGORY: Movement /// CATEGORY: Movement
SetCategory("Movement"); SetCategory("Movement");
TrollBuilder("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant) TrollBuilder("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant)
.OnReset(Reset_SlowSpeed)
.AddPrompt() .AddPrompt()
.OnPromptActivate(Activate_SlowSpeed)
.AddOptionFloat("90% Movement Speed", true, 0.9) .AddOptionFloat("90% Movement Speed", true, 0.9)
.AddOptionFloat("80% Movement Speed", false, 0.8) .AddOptionFloat("80% Movement Speed", false, 0.8)
.AddOptionFloat("70% Movement Speed", false, 0.7) .AddOptionFloat("70% Movement Speed", false, 0.7)
.AddOptionFloat("50% Movement Speed", false, 0.5) .AddOptionFloat("50% Movement Speed", false, 0.5)
.AddOptionFloat("30% Movement Speed", false, 0.3) .AddOptionFloat("30% Movement Speed", false, 0.3)
.AddOptionFloat("0% Movement Speed", false, 0.0); .AddOptionFloat("0% Movement Speed", false, 0.0);
TrollBuilder("Higher Gravity", "Sets player gravity to 1.3x of normal gravity", TrollMod_Constant); 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; .OnActivate(Activate_HighGravity)
.OnReset(Reset_HighGravity);
t_invertedTroll = TrollBuilder("Inverted Controls", "Well, aint it obvious", TrollMod_Constant).Build();
t_slipperyShoesIndex = TrollBuilder("Slippery Shoes", "Periodically stumbles around.", TrollMod_Constant | TrollMod_Instant) t_slipperyShoesIndex = TrollBuilder("Slippery Shoes", "Periodically stumbles around.", TrollMod_Constant | TrollMod_Instant)
.AddPromptMulti() .AddPromptMulti()
.AddOption("Periodically", true) // 1 << 0 .AddOption("Periodically", true) // 1 << 0
@ -236,8 +251,16 @@ void SetupTrolls() {
.AddOption("Severe Earthquake", false) //8 .AddOption("Severe Earthquake", false) //8
.AddOption("Bouncy Castle", false) //16 .AddOption("Bouncy Castle", false) //16
.Id; .Id;
TrollBuilder("Rewind", "Teleports player backwards", TrollMod_Instant | TrollMod_Constant) t_rewind = TrollBuilder("Rewind", "Teleports player backwards", TrollMod_Instant | TrollMod_Constant)
.SetTimer(10.0, Timer_RandomRewind); // .SetTimer(10.0, Timer_RandomRewind)
.SetAutoTimer(10.0)
.OnActivate(Activate_Rewind)
.AddPrompt("Distance")
.AddOptionFloat("Subtle", false, 10.0)
.AddOptionFloat("Tiny", false, 50.0)
.AddOptionFloat("Normal", true, 100.0)
.AddOptionFloat("Far", false, 250.0)
.Build();
/// CATEGORY: MISC /// CATEGORY: MISC
SetCategory("Misc"); SetCategory("Misc");
@ -297,8 +320,25 @@ TrollEffectResponse Activate_SmartCharge(Troll troll, int activator, int victim,
return TE_Success; return TE_Success;
} }
void Activate_Rewind(Troll troll, int activator, int victim, int flags, trollModifier mod) {
float distance;
troll.GetPromptDataFloat(activator, 0, distance);
RewindPlayer(victim, distance);
}
void Activate_SlowSpeed(Troll troll, int activator, int victim, float movement, int flags, trollModifier mod) {
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", movement);
}
void Reset_SlowSpeed(Troll troll, int activator, int victim) {
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
}
void Activate_HighGravity(Troll troll, int activator, int victim, int flags, trollModifier mod) {
SetEntityGravity(victim, 1.3);
}
void Reset_HighGravity(Troll troll, int activator, int victim) {
SetEntityGravity(victim, 1.0);
}
void AddMagnetFlags(TrollBuilder troll) { void AddMagnetFlags(TrollBuilder troll) {
PrintToServer("adding: %d", troll.Id);
troll.AddPrompt("Choose Magnet Chance:") troll.AddPrompt("Choose Magnet Chance:")
.AddOptionFloat("Always (100%)", true, 1.0) .AddOptionFloat("Always (100%)", true, 1.0)
.AddOptionFloat("Half Time (50%)", false, 0.5) .AddOptionFloat("Half Time (50%)", false, 0.5)
@ -309,30 +349,12 @@ TrollEffectResponse ApplyAffect(int victim, Troll troll, int activator, trollMod
bool toActive = troll.IsActive(victim); bool toActive = troll.IsActive(victim);
char name[MAX_TROLL_NAME_LENGTH]; char name[MAX_TROLL_NAME_LENGTH];
troll.GetName(name, sizeof(name)); troll.GetName(name, sizeof(name));
if(StrEqual(name, "Reset User")) { if(troll.Id == 0) {
LogAction(activator, victim, "\"%L\" reset all effects for \"%L\"", activator, victim); LogAction(activator, victim, "\"%L\" reset all effects for \"%L\"", activator, victim);
ShowActivityEx(activator, "[FTT] ", "reset effects for %N. ", victim); ShowActivityEx(activator, "[FTT] ", "reset effects for %N. ", victim);
// for(int i = 0; i <= MAX_TROLLS; i++) {
// Trolls[i].activeFlagClients[victim] = -1;
// }
// SetEntityGravity(victim, 1.0);
// SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
ResetClient(victim, true); ResetClient(victim, true);
return TE_Error; // Not an error, but don't want to show activation return TE_Error; // Not an error, but don't want to show activation
} else if(StrEqual(name, "Slow Speed")) { } else if(StrEqual(name, "UziRules / AwpSmells")) {
if(toActive) {
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;
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", movement);
} else
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
} else if(StrEqual(name, "Higher Gravity"))
SetEntityGravity(victim, toActive ? 1.3 : 1.0);
else if(StrEqual(name, "UziRules / AwpSmells")) {
DisableTroll(victim, "No Pickup"); DisableTroll(victim, "No Pickup");
DisableTroll(victim, "Primary Disable"); DisableTroll(victim, "Primary Disable");
} else if(StrEqual(name, "Primary Disable")) { } else if(StrEqual(name, "Primary Disable")) {
@ -343,8 +365,6 @@ TrollEffectResponse ApplyAffect(int victim, Troll troll, int activator, trollMod
DisableTroll(victim, "UziRules / AwpSmells"); DisableTroll(victim, "UziRules / AwpSmells");
DisableTroll(victim, "Primary Disable"); DisableTroll(victim, "Primary Disable");
SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup); SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup);
} else if(StrEqual(name, "CameTooEarly")) {
ReplyToCommand(activator, "This troll mode is not implemented.");
} else if(StrEqual(name, "KillMeSoftly")) { } else if(StrEqual(name, "KillMeSoftly")) {
static char wpn[32]; static char wpn[32];
GetClientWeaponName(victim, 4, wpn, sizeof(wpn)); GetClientWeaponName(victim, 4, wpn, sizeof(wpn));

View file

@ -0,0 +1 @@
native bool SpawnSchematic(const char name[32], const float pos[3], const float angles[3] = NULL_VECTOR);

View file

@ -155,7 +155,7 @@ enum struct EditorData {
bool CheckEntity() { bool CheckEntity() {
if(this.entity != INVALID_ENT_REFERENCE) { if(this.entity != INVALID_ENT_REFERENCE) {
if(!IsValidEntity(this.entity)) { if(this.entity == -1 && !IsValidEntity(this.entity)) {
PrintToChat(this.client, "\x04[Editor]\x01 Entity has vanished, editing cancelled."); PrintToChat(this.client, "\x04[Editor]\x01 Entity has vanished, editing cancelled.");
this.Reset(); this.Reset();
return false; return false;
@ -301,7 +301,7 @@ enum struct EditorData {
char component[16]; char component[16];
for(int i = 0; i < 4; i++) { for(int i = 0; i < 4; i++) {
if(this.colorIndex == i) if(this.colorIndex == i)
Format(component, sizeof(component), "%s \x05 %c \x01", component, COLOR_INDEX[i]); Format(component, sizeof(component), "%s \x05%c\x01", component, COLOR_INDEX[i]);
else else
Format(component, sizeof(component), "%s %c", component, COLOR_INDEX[i]); Format(component, sizeof(component), "%s %c", component, COLOR_INDEX[i]);
} }

View file

@ -1,5 +1,9 @@
char g_pendingSaveName[64];
int g_pendingSaveClient; int g_pendingSaveClient;
ArrayList g_previewItems;
CategoryData ROOT_CATEGORY;
ArrayList g_spawnedItems; // ArrayList(block=2)<entRef, [creator]>
ArrayList g_savedItems; // ArrayList<entRef>
StringMap g_recentItems; // Key: model[128], value: RecentEntry
/* Wish to preface this file: /* Wish to preface this file:
* It's kinda messy. The main structs are: * It's kinda messy. The main structs are:
@ -12,7 +16,123 @@ The rest are kinda necessary, for sorting reasons (SearchData, RecentEntry).
enum ChatPrompt { enum ChatPrompt {
Prompt_None, Prompt_None,
Prompt_Search, Prompt_Search,
Prompt_Save Prompt_SaveScene,
Prompt_SaveSchematic
}
enum SaveType {
Save_None,
Save_Scene,
Save_Schematic
}
enum struct Schematic {
char name[64];
char creatorSteamid[32];
char creatorName[32];
ArrayList entities;
void Reset() {
this.name[0] = '\0';
this.creatorSteamid[0] = '\0';
this.creatorName[0] = '\0';
if(this.entities != null) delete this.entities;
}
void AddEntity(int entity, int client) {
SaveData save;
save.FromEntity(entity);
this.entities.PushArray(save);
}
void New(int client, const char[] name) {
if(client > 0) {
GetClientName(client, this.creatorName, sizeof(this.creatorName));
GetClientAuthId(client, AuthId_Steam2, this.creatorSteamid, sizeof(this.creatorSteamid));
}
strcopy(this.name, sizeof(this.name), name);
this.entities = new ArrayList(sizeof(SaveData));
}
bool Save() {
char path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/schematics/%s.schem", this.name);
CreateDirectory("data/prop_spawner/schematics", 0775);
KeyValues kv = new KeyValues(this.name);
kv.SetString("creator_steamid", this.creatorSteamid);
kv.SetString("creator_name", this.creatorName);
kv.JumpToKey("entities");
this.entities = new ArrayList(sizeof(SaveData));
SaveData ent;
while(kv.GotoNextKey()) {
kv.GetVector("offset", ent.origin);
kv.GetVector("angles", ent.angles);
kv.GetColor4("color", ent.color);
kv.GetString("model", ent.model, sizeof(ent.model));
this.entities.PushArray(ent);
}
kv.ExportToFile(path);
delete kv;
return true;
}
bool Import(const char[] name) {
char path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/schematics/%s.schem", name);
KeyValues kv = new KeyValues("root");
if(kv.ImportFromFile(path)) {
delete kv;
return false;
}
strcopy(this.name, sizeof(this.name), name);
kv.GetString("creator_steamid", this.creatorSteamid, sizeof(this.creatorSteamid));
kv.GetString("creator_name", this.creatorName, sizeof(this.creatorName));
kv.JumpToKey("entities");
this.entities = new ArrayList(sizeof(SaveData));
SaveData ent;
while(kv.GotoNextKey()) {
kv.GetVector("offset", ent.origin);
kv.GetVector("angles", ent.angles);
kv.GetColor4("color", ent.color);
kv.GetString("model", ent.model, sizeof(ent.model));
this.entities.PushArray(ent);
}
delete kv;
return true;
}
/// Spawns all schematics entities, returns list of entities, first being parent.
ArrayList SpawnEntities(const float origin[3], bool asPreview = true) {
if(this.entities == null) return null;
SaveData ent;
int parent = -1;
ArrayList spawnedEntities = new ArrayList();
for(int i = 0; i < this.entities.Length; i++) {
this.entities.GetArray(i, ent, sizeof(ent));
int entity = ent.ToEntity(origin, asPreview);
spawnedEntities.Push(EntIndexToEntRef(entity));
if(i == 0) {
SetParent(entity, parent)
} else {
parent = entity;
}
}
return spawnedEntities;
}
}
public any Native_SpawnSchematic(Handle plugin, int numParams) {
char name[32];
float pos[3];
float ang[3];
GetNativeString(0, name, sizeof(name));
GetNativeArray(1, pos, 3);
GetNativeArray(1, ang, 3);
Schematic schem;
if(!schem.Import(name)) {
return false;
}
ArrayList list = schem.SpawnEntities(pos, false);
delete list;
return true;
} }
enum struct PlayerPropData { enum struct PlayerPropData {
ArrayList categoryStack; ArrayList categoryStack;
@ -25,6 +145,9 @@ enum struct PlayerPropData {
char classnameOverride[64]; char classnameOverride[64];
ChatPrompt chatPrompt; ChatPrompt chatPrompt;
ArrayList markedProps; ArrayList markedProps;
SaveType pendingSaveType;
Schematic schematic;
// Called on PlayerDisconnect // Called on PlayerDisconnect
void Reset() { void Reset() {
@ -36,6 +159,15 @@ enum struct PlayerPropData {
this.lastActiveTime = 0; this.lastActiveTime = 0;
this.classnameOverride[0] = '\0'; this.classnameOverride[0] = '\0';
this.CleanupBuffers(); this.CleanupBuffers();
this.pendingSaveType = Save_None;
this.schematic.Reset();
}
void StartSchematic(int client, const char[] name) {
this.schematic.New(client, name);
this.pendingSaveType = Save_Schematic;
PrintToChat(client, "\x04[Editor]\x01 Started new schematic: \x05%s", name);
ShowCategoryList(client, ROOT_CATEGORY);
} }
// Sets the list buffer // Sets the list buffer
@ -97,6 +229,7 @@ enum struct PlayerPropData {
} }
PlayerPropData g_PropData[MAXPLAYERS+1]; PlayerPropData g_PropData[MAXPLAYERS+1];
enum struct CategoryData { enum struct CategoryData {
// The display name of category // The display name of category
char name[64]; char name[64];
@ -124,6 +257,8 @@ enum struct SearchData {
strcopy(this.name, sizeof(this.name), item.name); strcopy(this.name, sizeof(this.name), item.name);
} }
} }
enum struct SaveData { enum struct SaveData {
char model[128]; char model[128];
buildType type; buildType type;
@ -148,6 +283,43 @@ enum struct SaveData {
GetEntityRenderColor(entity, this.color[0],this.color[1],this.color[2],this.color[3]); GetEntityRenderColor(entity, this.color[0],this.color[1],this.color[2],this.color[3]);
} }
int ToEntity(const float offset[3], bool asPreview = true) {
int entity = -1;
if(this.type == Build_Physics)
entity = CreateEntityByName("prop_physics");
else
entity = CreateEntityByName("prop_dynamic_override");
if(entity == -1) {
return -1;
}
PrecacheModel(this.model);
DispatchKeyValue(entity, "model", this.model);
DispatchKeyValue(entity, "targetname", "saved_prop");
if(asPreview) {
DispatchKeyValue(entity, "rendermode", "1");
DispatchKeyValue(entity, "solid", "0");
} else {
DispatchKeyValue(entity, "solid", this.type == Build_NonSolid ? "0" : "6");
}
float pos[3];
for(int i = 0; i < 3; i++)
pos[i] = this.origin[i] + offset[i];
TeleportEntity(entity, pos, this.angles, NULL_VECTOR);
if(!DispatchSpawn(entity)) {
return -1;
}
int alpha = asPreview ? 200 : this.color[3];
SetEntityRenderColor(entity, this.color[0], this.color[1], this.color[2], alpha);
if(asPreview)
g_previewItems.Push(EntIndexToEntRef(entity));
else
AddSpawnedItem(entity);
return entity;
}
void Serialize(char[] output, int maxlen) { void Serialize(char[] output, int maxlen) {
Format( Format(
output, maxlen, "%s,%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d,%d,%d", output, maxlen, "%s,%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d,%d,%d",
@ -181,10 +353,6 @@ enum struct RecentEntry {
char name[64]; char name[64];
int count; int count;
} }
CategoryData ROOT_CATEGORY;
ArrayList g_spawnedItems; // ArrayList(block=2)<entRef, [creator]>
ArrayList g_savedItems; // ArrayList<entRef>
StringMap g_recentItems; // Key: model[128], value: RecentEntry
#include <hats/props/db.sp> #include <hats/props/db.sp>
#include <hats/props/methods.sp> #include <hats/props/methods.sp>

View file

@ -12,6 +12,27 @@ Action Command_Props(int client, int args) {
PrintToConsole(client, "favorite - favorites active editor entity"); PrintToConsole(client, "favorite - favorites active editor entity");
PrintToConsole(client, "controls - list all the controls"); PrintToConsole(client, "controls - list all the controls");
PrintToConsole(client, "reload - reload prop list"); PrintToConsole(client, "reload - reload prop list");
PrintToConsole(client, "schem[atic] <new/save/edit/delete/load> <name>");
} else if(StrEqual(arg, "schem") || StrEqual(arg, "schematic")) {
char arg2[16];
GetCmdArg(2, arg2, sizeof(arg2));
if(StrEqual(arg2, "new")) {
char name[32];
GetCmdArg(3, name, sizeof(name));
if(name[0] == '\0') {
PrintToChat(client, "\x04[Editor]\x01 Please enter a name");
} else {
g_PropData[client].StartSchematic(client, name);
}
} else if(StrEqual(arg2, "save")) {
if(g_PropData[client].pendingSaveType == Save_Schematic) {
g_PropData[client].schematic.Save();
} else {
PrintToChat(client, "\x04[Editor]\x01 No schematic to save.");
}
} else {
PrintToChat(client, "\x04[Editor]\x01 Unknown option: %s", arg2);
}
} else if(StrEqual(arg, "list")) { } else if(StrEqual(arg, "list")) {
char arg2[16]; char arg2[16];
GetCmdArg(2, arg2, sizeof(arg2)); GetCmdArg(2, arg2, sizeof(arg2));

View file

@ -75,44 +75,89 @@ void AdminMenu_SaveLoad(TopMenu topmenu, TopMenuAction action, TopMenuObject obj
if(action == TopMenuAction_DisplayOption) { if(action == TopMenuAction_DisplayOption) {
Format(buffer, maxlength, "Save / Load"); Format(buffer, maxlength, "Save / Load");
} else if(action == TopMenuAction_SelectOption) { } else if(action == TopMenuAction_SelectOption) {
Menu menu = new Menu(SaveLoadHandler); Spawn_ShowSaveLoadMainMenu(param);
menu.SetTitle("Save / Load");
char name[64];
// TODO: possibly let you overwrite saves?
menu.AddItem("", "[New Save]");
ArrayList saves = LoadSaves();
if(saves != null) {
for(int i = 0; i < saves.Length; i++) {
saves.GetString(i, name, sizeof(name));
menu.AddItem(name, name);
}
delete saves;
}
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(param, MENU_TIME_FOREVER);
} }
} }
int SaveLoadHandler(Menu menu, MenuAction action, int client, int param2) { int SaveLoadMainMenuHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char info[2];
menu.GetItem(param2, info, sizeof(info));
SaveType type = view_as<SaveType>(StringToInt(info));
ShowSaves(client, type);
} else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
Spawn_ShowSaveLoadMainMenu(client);
}
} else if (action == MenuAction_End)
delete menu;
return 0;
}
int SaveLoadSceneHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) { if (action == MenuAction_Select) {
char saveName[64]; char saveName[64];
menu.GetItem(param2, saveName, sizeof(saveName)); menu.GetItem(param2, saveName, sizeof(saveName));
if(saveName[0] == '\0') { if(saveName[0] == '\0') {
// Save new // Save new
FormatTime(saveName, sizeof(saveName), "%Y-%m-%d_%H-%I-%M"); FormatTime(saveName, sizeof(saveName), "%Y-%m-%d_%H-%I-%M");
if(CreateSave(saveName)) { if(CreateSceneSave(saveName)) {
PrintToChat(client, "\x04[Editor]\x01 Saved as \x05%s/%s.txt", g_currentMap, saveName); PrintToChat(client, "\x04[Editor]\x01 Saved as \x05%s/%s.txt", g_currentMap, saveName);
} else { } else {
PrintToChat(client, "\x04[Editor]\x01 Unable to save. Sorry."); PrintToChat(client, "\x04[Editor]\x01 Unable to save. Sorry.");
} }
} else if(LoadSave(saveName, true)) { } else if(g_pendingSaveClient != 0 && g_pendingSaveClient != client) {
strcopy(g_pendingSaveName, sizeof(g_pendingSaveName), saveName); PrintToChat(client, "\x04[Editor]\x01 Another user is currently loading a save.");
} else if(g_PropData[client].pendingSaveType == Save_Schematic) {
PrintToChat(client, "\x04[Editor]\x01 Please complete or cancel current schematic to continue.");
} else if(LoadScene(saveName, true)) {
ConfirmSave(client, saveName);
g_pendingSaveClient = client;
PrintToChat(client, "\x04[Editor]\x01 Previewing save \x05%s", saveName);
PrintToChat(client, "\x04[Editor]\x01 Press \x05Shift + Middle Mouse\x01 to spawn, \x05Middle Mouse\x01 to cancel");
} else {
PrintToChat(client, "\x04[Editor]\x01 Could not load save file.");
}
} else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
Spawn_ShowSaveLoadMainMenu(client);
}
} else if (action == MenuAction_End)
delete menu;
return 0;
}
int SaveLoadSchematicHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char saveName[64];
menu.GetItem(param2, saveName, sizeof(saveName));
Schematic schem;
if(saveName[0] == '\0') {
if(g_PropData[client].pendingSaveType == Save_Schematic) {
if(g_PropData[client].schematic.Save()) {
PrintToChat(client, "\x04[Editor]\x01 Saved schematic as \x05%s", g_PropData[client].schematic.name);
} else {
PrintToChat(client, "\x04[Editor]\x01 Failed to save schematic.");
}
g_PropData[client].schematic.Reset();
g_PropData[client].pendingSaveType = Save_None;
} else {
g_PropData[client].chatPrompt = Prompt_SaveSchematic;
PrintToChat(client, "\x04[Editor]\x01 Enter in chat a name for schematic");
}
} else if(schem.Import(saveName)) {
float pos[3];
GetCursorLocation(client, pos);
ArrayList list = schem.SpawnEntities(pos, true);
SaveData save;
int parent = list.GetArray(0, save);
delete list;
Editor[client].Import(parent);
if(g_pendingSaveClient != 0 && g_pendingSaveClient != client) { if(g_pendingSaveClient != 0 && g_pendingSaveClient != client) {
PrintToChat(client, "\x04[Editor]\x01 Another user is currently loading a save."); PrintToChat(client, "\x04[Editor]\x01 Another user is currently loading a scene.");
} else { } else {
g_pendingSaveClient = client; g_pendingSaveClient = client;
PrintToChat(client, "\x04[Editor]\x01 Previewing save \x05%s", saveName); PrintToChat(client, "\x04[Editor]\x01 Previewing schematic \x05%s", saveName);
PrintToChat(client, "\x04[Editor]\x01 Press \x05Shift + Middle Mouse\x01 to spawn, \x05Middle Mouse\x01 to cancel"); PrintToChat(client, "\x04[Editor]\x01 Press \x05Shift + Middle Mouse\x01 to spawn, \x05Middle Mouse\x01 to cancel");
} }
} else { } else {
@ -120,12 +165,31 @@ int SaveLoadHandler(Menu menu, MenuAction action, int client, int param2) {
} }
} else if (action == MenuAction_Cancel) { } else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) { if(param2 == MenuCancel_ExitBack) {
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client); Spawn_ShowSaveLoadMainMenu(client);
} }
} else if (action == MenuAction_End) } else if (action == MenuAction_End)
delete menu; delete menu;
return 0; return 0;
} }
int SaveLoadConfirmHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
ClearSavePreview();
char info[64];
menu.GetItem(param2, info, sizeof(info));
if(info[0] != '\0') {
PrintToChat(client, "\x04[Editor]\x01 Loaded scene \x05%s", info);
LoadScene(info, false);
}
} else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
Spawn_ShowSaveLoadMainMenu(client);
}
} else if (action == MenuAction_End)
delete menu;
return 0;
}
int DeleteHandler(Menu menu, MenuAction action, int client, int param2) { int DeleteHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) { if (action == MenuAction_Select) {
char info[8]; char info[8];

View file

@ -657,16 +657,15 @@ stock void StringToUpper(char[] str) {
} }
} }
stock int GetRealClient(int client) { stock int GetRealClient(int bot) {
if(IsFakeClient(client)) { if(!IsFakeClient(bot)) return -1;
char netclass[32]; static char netclass[16];
GetEntityClassname(client, netclass, sizeof(netclass)); GetEntityNetClass(bot, netclass, sizeof(netclass));
if(!StrEqual(netclass, "SurvivorBot", false)) return false; if(StrEqual(netclass, "SurvivorBot")) {
int realPlayer = GetClientOfUserId(GetEntProp(client, Prop_Send, "m_humanSpectatorUserID")); int user = GetEntProp(bot, Prop_Send, "m_humanSpectatorUserID");
return realPlayer > 0 ? realPlayer : -1; if(user > 0) return GetClientOfUserId(user);
}else{
return client;
} }
return -1;
} }
stock int FindIdleBot(int client) { stock int FindIdleBot(int client) {

View file

@ -259,6 +259,7 @@ public void OnPluginStart() {
HookEvent("player_spawn", Event_PlayerSpawn); HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("player_first_spawn", Event_PlayerFirstSpawn); HookEvent("player_first_spawn", Event_PlayerFirstSpawn);
HookEvent("player_left_start_area", Event_LeaveStartArea);
//Tracking player items: //Tracking player items:
HookEvent("item_pickup", Event_ItemPickup); HookEvent("item_pickup", Event_ItemPickup);
HookEvent("weapon_drop", Event_ItemPickup); HookEvent("weapon_drop", Event_ItemPickup);
@ -939,6 +940,10 @@ void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
delete g_saveTimer[client]; delete g_saveTimer[client];
} }
void Event_LeaveStartArea(Event event, const char[] name, bool dontBroadcast) {
PopulateItems();
}
void Event_PlayerInfo(Event event, const char[] name, bool dontBroadcast) { void Event_PlayerInfo(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid")); int client = GetClientOfUserId(event.GetInt("userid"));
if(client && !IsFakeClient(client)) { if(client && !IsFakeClient(client)) {
@ -1336,7 +1341,6 @@ public void OnMapStart() {
L4D2_RunScript(HUD_SCRIPT_CLEAR); L4D2_RunScript(HUD_SCRIPT_CLEAR);
Director_OnMapStart(); Director_OnMapStart();
g_areItemsPopulated = false; g_areItemsPopulated = false;
CreateTimer(30.0, Timer_Populate);
if(g_isLateLoaded) { if(g_isLateLoaded) {
UpdateSurvivorCount(); UpdateSurvivorCount();
@ -1486,11 +1490,6 @@ public void OnMapEnd() {
void Event_FinaleStart(Event event, const char[] name, bool dontBroadcast) { void Event_FinaleStart(Event event, const char[] name, bool dontBroadcast) {
g_finaleStage = Stage_Active; g_finaleStage = Stage_Active;
} }
Action Timer_Populate(Handle h) {
PopulateItems();
return Plugin_Continue;
}
public void OnClientSpeaking(int client) { public void OnClientSpeaking(int client) {
g_isSpeaking[client] = true; g_isSpeaking[client] = true;
} }
@ -1566,7 +1565,7 @@ Action Hook_CabinetItemSpawn(int entity) {
int cabEnt = cabinets[ci].items[block]; int cabEnt = cabinets[ci].items[block];
PrintDebug(DEBUG_ANY, "cabinet %d spawner %d block %d: %d", cabinet, entity, block, cabEnt); PrintDebug(DEBUG_ANY, "cabinet %d spawner %d block %d: %d", cabinet, entity, block, cabEnt);
if(cabEnt <= 0) { if(cabEnt <= 0) {
cabinets[ci].items[block] = entity; cabinets[ci].items[block] = EntIndexToEntRef(entity);
PrintDebug(DEBUG_SPAWNLOGIC, "Adding spawner %d for cabinet %d block %d", entity, cabinet, block); PrintDebug(DEBUG_SPAWNLOGIC, "Adding spawner %d for cabinet %d block %d", entity, cabinet, block);
break; break;
} }
@ -1770,6 +1769,7 @@ Action Timer_UpdateHud(Handle h) {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
void PopulateItems() { void PopulateItems() {
PrintToServer("[EPI:TEMP] PopulateItems hasRan=%b finale=%b", g_areItemsPopulated, L4D_IsMissionFinalMap(true));
if(g_areItemsPopulated) return; if(g_areItemsPopulated) return;
UpdateSurvivorCount(); UpdateSurvivorCount();
if(!IsEPIActive()) return; if(!IsEPIActive()) return;
@ -1831,7 +1831,8 @@ void PopulateCabinets() {
while(extraAmount > 0) { while(extraAmount > 0) {
//FIXME: spawner is sometimes invalid entity. Ref needed? //FIXME: spawner is sometimes invalid entity. Ref needed?
for(int block = 0; block < CABINET_ITEM_BLOCKS; block++) { for(int block = 0; block < CABINET_ITEM_BLOCKS; block++) {
spawner = cabinets[i].items[block]; if(cabinets[i].items[block] == 0) break;
spawner = EntRefToEntIndex(cabinets[i].items[block]);
if(spawner > 0) { if(spawner > 0) {
if(!HasEntProp(spawner, Prop_Data, "m_itemCount")) continue; if(!HasEntProp(spawner, Prop_Data, "m_itemCount")) continue;
hasSpawner = true; hasSpawner = true;

View file

@ -32,7 +32,9 @@ public Plugin myinfo =
}; };
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
// return APLRes_SilentFailure;
CreateNative("ApplyTroll", Native_ApplyTroll); CreateNative("ApplyTroll", Native_ApplyTroll);
RegPluginLibrary("feedthetrolls");
return APLRes_Success; return APLRes_Success;
} }
public void OnLibraryAdded(const char[] name) { public void OnLibraryAdded(const char[] name) {

View file

@ -575,12 +575,8 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
// OnPlayerRunCmd :: ENTITY EDITOR // OnPlayerRunCmd :: ENTITY EDITOR
///////////////////////////// /////////////////////////////
if(g_pendingSaveClient == client) { if(g_pendingSaveClient == client) {
if(buttons & IN_ZOOM) { if(g_PropData[client].pendingSaveType == Save_Schematic) {
ClearSavePreview(); // move cursor? or should be editor anyway
if(buttons & IN_SPEED) {
PrintToChat(client, "\x04[Editor]\x01 Loaded save \x05%s", g_pendingSaveName);
LoadSave(g_pendingSaveName, false);
}
} }
} else if(g_PropData[client].markedProps != null) { } else if(g_PropData[client].markedProps != null) {
SetWeaponDelay(client, 0.5); SetWeaponDelay(client, 0.5);
@ -615,10 +611,10 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
cmdThrottle[client] = tick; cmdThrottle[client] = tick;
} }
} else if(Editor[client].IsActive()) { } else if(Editor[client].IsActive()) {
if(buttons & IN_USE && buttons & IN_RELOAD) { // if(buttons & IN_USE && buttons & IN_RELOAD) {
ClientCommand(client, "sm_wall done"); // ClientCommand(client, "sm_wall done");
return Plugin_Handled; // return Plugin_Handled;
} // }
bool allowMove = true; bool allowMove = true;
switch(Editor[client].mode) { switch(Editor[client].mode) {
case MOVE_ORIGIN: { case MOVE_ORIGIN: {
@ -626,7 +622,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
bool isRotate; bool isRotate;
int flags = GetEntityFlags(client); int flags = GetEntityFlags(client);
if(buttons & IN_USE && ~buttons & IN_ZOOM) { if(buttons & IN_RELOAD && ~buttons & IN_ZOOM) {
if(!g_inRotate[client]) { if(!g_inRotate[client]) {
g_inRotate[client] = true; g_inRotate[client] = true;
} }
@ -717,22 +713,19 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
} }
} }
} }
if(tick - cmdThrottle[client] > 0.13) { if(!(oldButtons & IN_USE) && buttons & IN_USE) {
if(buttons & IN_RELOAD) if(buttons & IN_SPEED) {
Editor[client].CycleMode(); // R: Cycle forward Editor[client].Cancel();
else if(buttons & IN_ZOOM) { } else if(buttons & IN_DUCK) {
buttons &= ~IN_ZOOM; if(Editor[client].flags & Edit_Preview)
Editor[client].CycleBuildType();
if(buttons & IN_SPEED) { } else {
int entity; int entity;
Editor[client].Done(entity); Editor[client].Done(entity);
} else if(Editor[client].flags & Edit_Preview && buttons & IN_DUCK) {
Editor[client].CycleBuildType();
} else if(~buttons & IN_DUCK) {
Editor[client].Cancel();
}
} }
cmdThrottle[client] = tick;
} else if(!(oldButtons & IN_ZOOM) && buttons & IN_ZOOM) {
Editor[client].CycleMode(); // ZOOM: Cycle forward
} }
Editor[client].Draw(BUILDER_COLOR, 0.1, 0.1); Editor[client].Draw(BUILDER_COLOR, 0.1, 0.1);
@ -900,7 +893,7 @@ stock bool Filter_NoPlayers(int entity, int mask, int data) {
} }
stock bool Filter_IgnorePlayerAndWall(int entity, int mask, int data) { stock bool Filter_IgnorePlayerAndWall(int entity, int mask, int data) {
if(entity > MaxClients && entity != data && EntRefToEntIndex(Editor[data].entity) != entity) { if(entity > MaxClients && entity != data && EntRefToEntIndex(Editor[data].entity) != entity && EntRefToEntIndex(hatData[data].entity) != entity) {
static char classname[16]; static char classname[16];
GetEntityClassname(entity, classname, sizeof(classname)); GetEntityClassname(entity, classname, sizeof(classname));
// Ignore infected // Ignore infected
@ -912,8 +905,9 @@ stock bool Filter_IgnorePlayerAndWall(int entity, int mask, int data) {
bool Filter_ValidHats(int entity, int mask, int data) { bool Filter_ValidHats(int entity, int mask, int data) {
if(entity == data) return false; if(entity == data) return false;
if(entity <= MaxClients) { if(entity <= MaxClients && entity > 0) {
int client = GetRealClient(data); int client = GetRealClient(data);
if(client == -1) client = data;
return CanTarget(client); // Don't target if player targetting off return CanTarget(client); // Don't target if player targetting off
} }
return CheckBlacklist(entity); return CheckBlacklist(entity);

View file

@ -14,7 +14,7 @@
#include <jutils> #include <jutils>
// Addons: // Addons:
#undef REQUIRE_PLUGIN #undef REQUIRE_PLUGIN
#tryinclude <feedthetrolls> #include <feedthetrolls>
#undef REQUIRE_PLUGIN #undef REQUIRE_PLUGIN
#tryinclude <tkstopper> #tryinclude <tkstopper>
@ -504,6 +504,7 @@ bool ApplyAction(int targetUserId, const char[] action, const char[] key, const
#if defined _ftt_included_ #if defined _ftt_included_
if(GetFeatureStatus(FeatureType_Native, "ApplyTroll") != FeatureStatus_Available) { if(GetFeatureStatus(FeatureType_Native, "ApplyTroll") != FeatureStatus_Available) {
PrintToServer("[PlayerNotes] Warn: Action \"%s\" for %N has missing plugin: Feed The Trolls", action, target); PrintToServer("[PlayerNotes] Warn: Action \"%s\" for %N has missing plugin: Feed The Trolls", action, target);
return false;
} }
// Replace under scores with spaces // Replace under scores with spaces
char newKey[32]; char newKey[32];
@ -524,6 +525,7 @@ bool ApplyAction(int targetUserId, const char[] action, const char[] key, const
#if defined _tkstopper_included_ #if defined _tkstopper_included_
if(GetFeatureStatus(FeatureType_Native, "SetImmunity") != FeatureStatus_Available) { if(GetFeatureStatus(FeatureType_Native, "SetImmunity") != FeatureStatus_Available) {
PrintToServer("[PlayerNotes] Warn: Action \"%s\" for %N has missing plugin: TKStopper", action, target); PrintToServer("[PlayerNotes] Warn: Action \"%s\" for %N has missing plugin: TKStopper", action, target);
return false;
} else if(StrEqual(key, "rff")) { } else if(StrEqual(key, "rff")) {
SetImmunity(target, TKImmune_ReverseFriendlyFire, true); SetImmunity(target, TKImmune_ReverseFriendlyFire, true);
} else if(StrEqual(key, "tk")) { } else if(StrEqual(key, "tk")) {
@ -544,6 +546,8 @@ bool ApplyAction(int targetUserId, const char[] action, const char[] key, const
} else { } else {
} }
} else if(strncmp(action, "model", 4) == 0) {
ServerCommand("sm_model %s #%d", key, GetClientUserId(target));
} else { } else {
PrintToServer("[PlayerNotes] Warn: Action (\"%s\") for %N is not valid", action, target); PrintToServer("[PlayerNotes] Warn: Action (\"%s\") for %N is not valid", action, target);
return false; return false;