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 PRECACHE_SOUNDS_COUNT 5
#define PRECACHE_SOUNDS_COUNT 6
char PRECACHE_SOUNDS[PRECACHE_SOUNDS_COUNT][] = {
"custom/meow1.mp3",
"custom/xen_teleport.mp3",
"custom/mariokartmusic.mp3",
"custom/spookyscaryskeletons.mp3",
"custom/wearenumberone2.mp3"
"custom/wearenumberone2.mp3",
"custom/quack.mp3"
};
#include <sourcemod>
#include <sdkhooks>
#include <left4dhooks>
#include <jutils.inc>
#include <sceneprocessor>
#undef REQUIRE_PLUGIN
#tryinclude <sceneprocessor>
#include <multicolors>
#include "l4d_survivor_identity_fix.inc"
@ -177,7 +179,7 @@ Action Timer_CheckPlayerPings(Handle timer) {
if(iHighPingCount[i]++ > 2) {
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");
SDKCall(hGoAwayFromKeyboard, i);
// SDKCall(hGoAwayFromKeyboard, i);
//PrintToChat(i, "Type /pingignore to disable this feature.");
// L4D_ReplaceWithBot(i);
isHighPingIdle[i] = true;
@ -462,7 +464,7 @@ void SetCharacter(int target, int survivorIndex, L4DModelId modelIndex, bool kee
if (IsFakeClient(target)) {
char name[32];
GetSurvivorName(target, name, sizeof(name));
SetClientInfo(target, "name", name);
// SetClientInfo(target, "name", name);
}
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) {
if(stage == SceneStage_Started) {
int activator = GetSceneInitiator(scene);
@ -650,6 +653,7 @@ public void OnSceneStageChanged(int scene, SceneStages stage) {
}
}
}
#endif
///AFK BOT WEAPON FIX
public void Event_BotPlayerSwap(Event event, const char[] name, bool dontBroadcast) {
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 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
#if defined MAX_TROLLS
#else
#define MAX_TROLLS 56
#define MAX_TROLLS 55
#endif
Troll t_metaReverse;
@ -21,13 +21,15 @@ enum TrollEffectResponse {
TE_Error, // Error, continue menu (retry)
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 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;
char trollIds[MAX_TROLLS+1][MAX_TROLL_NAME_LENGTH];
char DEFAULT_FLAG_PROMPT_MULTIPLE[] = "Enable options (Multiple)";
char DEFAULT_FLAG_PROMPT[] = "Select an option";
bool SilentMenuSelected[MAXPLAYERS+1];
@ -91,45 +93,12 @@ enum struct TrollData {
Handle timerHandles[MAXPLAYERS+1];
float timerInterval;
int timerRequiredFlags;
bool timerIsDataPack;
void Toggle(int client, int flags) {
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();
}
}
// TODO: REMOVE OLD
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);
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 {
public get() { return Trolls[this.Id].hidden; }
}
@ -194,8 +169,13 @@ methodmap Troll {
public get() { return this.TotalOptionsCount > 0; }
}
public bool IsActive(int client) {
return Trolls[this.Id].activeFlagClients[client] != -1;
/// Is troll active for client. If flags is > 0, will do bitwise and
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) {
@ -203,7 +183,7 @@ methodmap Troll {
}
public int GetFlags(int client) {
return Trolls[this.Id].activeFlagClients[client]
return Trolls[this.Id].activeFlagClients[client];
}
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) {
PrintToServer("Activate: act:%d vic:%d", activator, victim);
if(modifier == TrollMod_Invalid) modifier = this.GetDefaultMod();
if(victim == 0) ThrowError("Victim is invalid");
return ApplyTroll(victim, this, activator, modifier, flags, silent);
}
public void Reset(int victim) {
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) {
Call_StartForward(Trolls[this.Id].resetFn);
Call_PushCell(this);
Call_PushCell(Troll(this.Id));
Call_PushCell(0);
Call_PushCell(victim);
Call_PushCell(0);
Call_Finish();
}
}
@ -310,6 +295,43 @@ methodmap Troll {
else if(Trolls[this.Id].mods == view_as<int>(TrollMod_Constant)) return TrollMod_Constant;
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;
@ -349,16 +371,22 @@ methodmap TrollBuilder {
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].timerFunction = timer;
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++) {
Trolls[this.Id].timerHandles[i] = null;
}
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) {
this._AddPrompt(false, requiredFlags, customPrompt);
return this;
@ -436,7 +464,7 @@ methodmap TrollBuilder {
}
public TrollBuilder SetActivationFunction(ActivateFunction fn) {
public TrollBuilder OnActivate(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);
}
@ -444,7 +472,7 @@ methodmap TrollBuilder {
return this;
}
public TrollBuilder SetResetFunction(ResetFunction fn) {
public TrollBuilder OnReset(ResetFunction fn) {
if(Trolls[this.Id].resetFn == null) {
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) {
static int i = 0;
char buffer[MAX_TROLL_NAME_LENGTH];
@ -471,21 +500,12 @@ int GetTrollID(const char[] name) {
}
bool IsAnyTrollActive(int victim) {
for(int i = 0; i <= MAX_TROLLS; i++) {
if(Trolls[i].activeFlagClients[victim] >= 0) return true;
for(int i = 1; i <= MAX_TROLLS; i++) {
if(Troll(i).IsActive(victim)) return true;
}
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) {
char name[MAX_TROLL_NAME_LENGTH];
troll.GetName(name, sizeof(name));
@ -497,7 +517,16 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
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);
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) {
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(IsFakeClient(victim) && GetClientTeam(victim) == 2) {
int player = GetSpectatorClient(victim);
int player = GetRealClient(victim);
if(player > 0) {
// If there is an idle player, apply troll to them
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.
if(modifier & TrollMod_Constant) {
Trolls[trollIndex].activeFlagClients[victim] = isActive ? -1 : flags;
if(isActive) {
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
@ -541,15 +572,7 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
// 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();
}
instance._triggerActivateFn(activator, victim, flags, modifier);
// Call the corresponding prompt callback if applicable
TrollFlagPrompt prompt;
@ -558,7 +581,7 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
if(!prompt.multiselect && prompt.activateFn != null) {
int 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;
if(flags & bit && prompt.flags & bit) {
Call_StartForward(prompt.activateFn);
@ -582,7 +605,6 @@ TrollEffectResponse ApplyTroll(int victim, Troll troll, int activator, trollModi
Call_PushCell(Troll(trollIndex));
Call_PushCell(activator);
Call_PushCell(victim);
Call_PushCell(modifier);
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) {
SetTrollFlags(client, troll, flags);
Troll.FromName(troll).Activate(0, client, TrollMod_Invalid, flags);
}
void DisableTroll(int client, const char[] troll) {
SetTrollFlags(client, troll, -1);
Troll.FromName(troll).Reset(client);
}
public void SetCategory(const char[] newCat) {
@ -627,7 +649,6 @@ void GetCategory(int category, char[] buffer, int size) {
categories.GetString(category, buffer, size);
}
public int Native_ApplyTroll(Handle plugin, int numParams) {
int victim = GetNativeCell(1);
char name[MAX_TROLL_NAME_LENGTH];
@ -640,7 +661,7 @@ public int Native_ApplyTroll(Handle plugin, int numParams) {
int activator = GetNativeCell(5);
Troll troll = Troll.FromName(name);
troll.Activate(victim, activator, modifier, flags, GetNativeCell(6));
troll.Activate(activator, victim, modifier, flags, GetNativeCell(6));
return 0;
}

View file

@ -6,39 +6,45 @@ enum struct SpecifiedTroll {
trollModifier mod;
int flags;
}
enum struct TrollCombo {
enum struct TrollComboData {
char name[32];
ArrayList trolls;
void AddTroll(const char[] name, int flags = 0, trollModifier mod = TrollMod_Invalid) {
Troll instance = Troll.FromName(name);
bool AddTroll(const char[] name, int flags = 0, trollModifier mod = TrollMod_Invalid) {
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();
SpecifiedTroll troll;
troll.id = instance.Id;
troll.mod = mod;
troll.flags = flags;
this.trolls.PushArray(troll, sizeof(troll));
return true;
}
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++) {
SpecifiedTroll 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);
combo.trolls = new ArrayList(sizeof(SpecifiedTroll));
combos.PushArray(combo, sizeof(combo));
}
void SetupsTrollCombos() {
combos = new ArrayList(sizeof(TrollCombo));
combos = new ArrayList(sizeof(TrollComboData));
TrollCombo combo;
TrollComboData combo;
SetupCombo(combo, "Magnet Galore");
combo.AddTroll("Special Magnet");
combo.AddTroll("Tank Magnet");
@ -79,7 +85,7 @@ void SetupsTrollCombos() {
SetupCombo(combo, "Shut up");
combo.AddTroll("Vocalize Gag");
combo.AddTroll("Honk / Meow / Woof", .flags=1);
combo.AddTroll("Honk & Animal Sounds", .flags=1);
SetupCombo(combo, "Weakness Compels You");
combo.AddTroll("No Shove");

View file

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

View file

@ -196,7 +196,7 @@ 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;
Trolls[i].activeFlagClients[client] = -1;
if(Trolls[i].timerHandles[client] != null) {
delete Trolls[i].timerHandles[client];
}
@ -321,10 +321,6 @@ public Action RushPlayer(Handle h, int user) {
return Plugin_Handled;
}
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"));
// Check for any existing victims
int existingTarget = GetClientOfUserId(pdata[attacker].attackerTargetUid);
@ -337,16 +333,16 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
}
// Stop targetting if no longer magnetted:
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) {
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:
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;
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;
return Plugin_Changed;
}
@ -367,9 +363,9 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
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) {
if(!Trolls[spMagnetID].IsActive(i) || !WillMagnetRun(Trolls[spMagnetID], i)) continue;
if(!t_specialMagnet.IsActive(i) || !WillMagnetRun(t_specialMagnet, i)) continue;
}
if(IsPlayerIncapped(i)) {
@ -395,26 +391,21 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
return Plugin_Continue;
}
// TODO: migrate to Troll
bool WillMagnetRun(const TrollData troll, int i) {
bool WillMagnetRun(Troll troll, int client) {
// 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;
float cChance = 1.0;
//Skip first bit as it is ('Always')
if(troll.activeFlagClients[i] & 2) // 2nd: 50%
cChance = 0.5;
else if(troll.activeFlagClients[i] & 4) //3rd: 10%
cChance = 0.1;
return GetRandomFloat() <= cChance;
int flags = troll.GetFlags(client);
if(flags == 0) return true;
float chance = 1.0;
troll.GetPromptDataFloat(client, 0, chance);
return GetRandomFloat() <= chance;
}
public Action L4D2_OnEntityShoved(int client, int entity, int weapon, float vecDir[3], bool bIsHighPounce) {
if(client > 0 && client <= MaxClients) {
static int noShoveIndex;
if(noShoveIndex == 0) noShoveIndex == GetTrollID("No Shove");
if(Trolls[noShoveIndex].IsActive(client) && GetRandomFloat() < hShoveFailChance.FloatValue) {
static Troll t_noShove;
if(t_noShove.Id == 0) t_noShove == Troll.FromName("No Shove");
if(t_noShove.IsActive(client) && GetRandomFloat() < hShoveFailChance.FloatValue) {
float shoveTime = L4D2Direct_GetNextShoveTime(client);
L4D2Direct_SetNextShoveTime(client, shoveTime + 2.0);
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) {
if(client <= 0 || sArgs[0] == '@') return Plugin_Continue; //Ignore admin chat or console
static int honkID;
static int profanityID;
static int typooId;
if(honkID == 0) honkID = GetTrollID("Honk / Meow / Woof");
if(profanityID == 0) profanityID = GetTrollID("No Profanity");
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
static char strings[32][8];
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;
char[] message = new char[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);
else {
} else {
CPrintToChat(client, "{blue}%N {default}: %s", client, message);
bool showOriginalToOthers = Trolls[honkID].activeFlagClients[client] & 4 != 0;
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && i != client) {
if(showOriginalToOthers)
CPrintToChat(i, "{blue}%N {default}: %s", client, sArgs);
else
CPrintToChat(i, "{blue}%N {default}: %s", client, message);
CPrintToChat(i, "{blue}%N {default}: %s", client, sArgs);
}
}
}
@ -742,7 +728,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
}
// Inverted control code:
if(Trolls[t_invertedTrollIndex].IsActive(client)) {
if(t_invertedTroll.IsActive(client)) {
if(buttons & IN_MOVELEFT || buttons & IN_MOVERIGHT) {
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;
}
// Boost all damage no matter what
if(Trolls[t_damageBoostIndex].IsActive(victim)) {
if(t_damageBoost.IsActive(victim)) {
damage * 2;
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) {
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)
bool disableFF = false;
int flags = t_reverseFF.GetFlags(attacker);
if(damagetype & DMG_BURN) {
disableFF = Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 32 != 0;
disableFF = flags & 64 != 0;
} else if(damagetype & DMG_BLAST) {
disableFF = Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 64 != 0;
disableFF = flags & 32 != 0;
} 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;
}
if(disableFF) {
float returnDmg = damage; //default is 1:1
if(Trolls[t_reverseFFIndex].activeFlagClients[attacker] & 2) {
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;
}
t_reverseFF.GetPromptDataFloat(attacker, 0, returnDmg);
SDKHooks_TakeDamage(attacker, attacker, attacker, returnDmg, damagetype, -1);
damage = 0.0;
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) {
static int vocalGagID;
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"))) {
if(t_noRushingUs.IsActive(client) && (StrEqual(vocalize, "PlayerHurryUp") || StrEqual(vocalize, "PlayerYellRun") || StrEqual(vocalize, "PlayerMoveOn") || StrEqual(vocalize, "PlayerLeadOn"))) {
noRushingUsSpeed[client] -= 0.01;
if(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) {
SetSlot(client, -1);
}
if(Trolls[vocalGagID].IsActive(client)) {
if(t_vocalGag.IsActive(client) && t_vocalGag.GetFlags(client) == 0) {
return Plugin_Handled;
}
return Plugin_Continue;
@ -922,11 +897,6 @@ public void OnSceneStageChanged(int scene, SceneStages stage) {
#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) {
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")) {
PrintToConsoleAll("CRESCENDO STARTED BY %N", lastButtonUser);
#if defined DEBUG
@ -943,24 +913,37 @@ public Action SoundHook(int clients[MAXPLAYERS], int& numClients, char sample[PL
lastButtonUser = -1;
}else if(numClients > 0 && entity > 0 && entity <= MaxClients) {
if(StrContains(sample, "survivor\\voice") > -1) {
if(Trolls[honkID].IsActive(entity)) {
if(Trolls[honkID].activeFlagClients[entity] & 1)
if(t_honk.IsActive(entity)) {
int trollFlags = t_honk.GetFlags(entity);
if(trollFlags & 1) {
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");
volume += 0.2;
} else if(Trolls[honkID].activeFlagClients[entity] & 4) {
} else if(trollFlags & 8) {
strcopy(sample, sizeof(sample), "custom/woof1.mp3");
volume += 0.6;
} else
return Plugin_Continue;
return Plugin_Changed;
} else if(Trolls[vocalGagID].IsActive(entity)) {
if(Trolls[vocalGagID].activeFlagClients[entity] & 2) {
clients[0] = entity;
numClients = 1;
} else if(t_vocalGag.IsActive(entity)) {
int trollFlags = t_vocalGag.GetFlags(entity);
if(trollFlags & 2) {
SDKHooks_TakeDamage(entity, entity, entity, 1.0, DMG_GENERIC);
}
if(trollFlags & 1) {
volume /= 2.0;
return Plugin_Changed;
}
// if(Trolls[vocalGagID].activeFlagClients[entity] & 2) {
// clients[0] = entity;
// numClients = 1;
// return Plugin_Changed;
// }
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) {
static int dullMeleeID;
if(!dullMeleeID) dullMeleeID = GetTrollID("Dull Melee");
if(Trolls[dullMeleeID].IsActive(client)) {
float max = 1.0;
if(Trolls[dullMeleeID].activeFlagClients[client] & 2) max = 0.5;
else if(Trolls[dullMeleeID].activeFlagClients[client] & 4) max = 0.1;
if(GetRandomFloat() <= max) {
if(t_dullMelee.IsActive(client)) {
float chance = 1.0;
t_dullMelee.GetPromptDataFloat(client, 0, chance);
if(GetURandomFloat() <= chance) {
damage = 0.0;
return Plugin_Changed;
}

View file

@ -129,7 +129,7 @@ public int ChooseComboHandler(Menu menu, MenuAction action, int client, int para
return 0;
}
static TrollCombo combo;
static TrollComboData combo;
combos.GetArray(comboID, combo, sizeof(combo));
combo.Activate(client, victim);
} else if (action == MenuAction_End)
@ -377,7 +377,7 @@ void ShowTrollCombosMenu(int client, int victimUserID) {
Format(id, sizeof(id), "Choose troll combo");
comboMenu.SetTitle(id);
static TrollCombo combo;
static TrollComboData combo;
if(combos.Length == 0) {
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));
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);

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) {
return GetEntProp(client, Prop_Send, "m_isIncapacitated") == 1;
}
@ -641,13 +629,13 @@ void SetSlot(int client, int slot) {
ClientCommand(client, slotStr);
}
void RewindPlayer(int client) {
void RewindPlayer(int client, float distance = 100.0) {
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;
float minFlow = curFlow - (3.0*distance);
float maxFlow = curFlow - (1.5*distance);
// 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));

View file

@ -154,6 +154,9 @@ Action Timer_SetWitchTarget(Handle h, DataPack pack) {
}
stock SpecialType GetSpecialType(const char[] input) {
if(StrEqual(input, "random")) {
return view_as<SpecialType>(GetRandomInt(0, 6))
}
for(int i = 0; i < 8; i++) {
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) {
if(IsClientInGame(client) && GetURandomFloat() > 0.3) {
RewindPlayer(client);
float distance = 100.0;
t_rewind.GetPromptDataFloat(client, 0, distance);
RewindPlayer(client, distance);
}
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!
Troll t_specialMagnet;
Troll t_tankMagnet;
int t_slipperyShoesIndex = 0;
Troll t_stickyGoo;
int t_invertedTrollIndex;
Troll t_invertedTroll;
int t_randomizeAnglesIndex;
int t_randomizeVelocityIndex;
int t_vomitPlayerIndex;
int t_shakeyCameraIndex;
int t_slotRouletteIndex;
int t_damageBoostIndex;
int t_reverseFFIndex;
Troll t_damageBoost;
Troll t_reverseFF;
int t_hideHUDIndex;
Troll t_throwItAll;
Troll t_voiceMute;
Troll t_gunJam;
Troll t_honk;
Troll t_vocalGag;
Troll t_dullMelee;
Troll t_rewind;
Troll t_noRushingUs;
void SetupTrolls() {
trollKV = new StringMap();
@ -25,8 +32,10 @@ void SetupTrolls() {
SetCategory("Magnets");
troll = TrollBuilder("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(troll);
t_specialMagnet = troll.Build();
troll = TrollBuilder("Tank Magnet", "Attracts ALL tanks to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(troll);
t_tankMagnet = troll.Build();
#if defined _actions_included
TrollBuilder("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant);
#endif
@ -112,14 +121,14 @@ void SetupTrolls() {
.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)
t_dullMelee = 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);
.AddOptionFloat("Always (100%)", false, 1.0)
.AddOptionFloat("Half Time (50%)", true, 0.5)
.AddOptionFloat("Rare (10%)", false, 0.10)
.Build();
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)
@ -146,40 +155,42 @@ void SetupTrolls() {
.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)
t_vocalGag = TrollBuilder("Vocalize Gag", "Prevents player from sending any vocalizations (even automatic)", TrollMod_Constant)
.AddPromptMulti()
.AddOption("Quieter", false)
.AddOption("Painful", false)
.Build()
t_honk = TrollBuilder("Honk & Animal Sounds", "Custom sounds", TrollMod_Constant)
.AddPrompt("Choose Sound:")
.AddOption("Honk", true)
.AddOption("Meow", false)
.AddOption("Woof", false)
.AddOption("Honk", true) // 1 << 0
.AddOption("Quack", false) // 1 << 1
.AddOption("Meow", false) // 1 << 2
.AddOption("Woof", false) // 1 << 3
.AddPrompt("Choose Chat modifier:", 1)
.AddOption("Show Modified to Them", true)
.AddOption("Show Original to Them", false)
.AddOption("Show Modified Only To Them", false);
.AddOption("Show Modified To All", true) // 1 << 4
.AddOption("Show Original To Others", false) // 1 << 5
.Build();
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);
t_noRushingUs = TrollBuilder("No Rushing Us", "Decreases player speed everytime they yell hurry up", TrollMod_Constant).Build();
/// CATEGORY: 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("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)
t_reverseFF = 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
.AddOptionFloat("1:1 Ratio", true, 1.0) //1
.AddOptionFloat("2x Ratio", false, 2.0) //2
.AddOptionFloat("0.5x Ratio", false, 0.5) //4
.AddOptionFloat("0.0x Ratio (None)", false, 0.0) //8
.AddOptionFloat("3x Ratio", false, 3.0) //16
.AddPromptMulti("Modes")
.AddOption("Reverse Fire Damage", false) //32
.AddOption("Reverse Explosions", false) //64
.Id;
.Build();
TrollBuilder("Dep Bots", "Makes bots heal a player. At any cost", TrollMod_Constant)
@ -202,15 +213,19 @@ void SetupTrolls() {
/// CATEGORY: Movement
SetCategory("Movement");
TrollBuilder("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant)
.OnReset(Reset_SlowSpeed)
.AddPrompt()
.OnPromptActivate(Activate_SlowSpeed)
.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;
TrollBuilder("Higher Gravity", "Sets player gravity to 1.3x of normal gravity", TrollMod_Constant)
.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)
.AddPromptMulti()
.AddOption("Periodically", true) // 1 << 0
@ -236,8 +251,16 @@ void SetupTrolls() {
.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);
t_rewind = TrollBuilder("Rewind", "Teleports player backwards", TrollMod_Instant | TrollMod_Constant)
// .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
SetCategory("Misc");
@ -297,8 +320,25 @@ TrollEffectResponse Activate_SmartCharge(Troll troll, int activator, int victim,
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) {
PrintToServer("adding: %d", troll.Id);
troll.AddPrompt("Choose Magnet Chance:")
.AddOptionFloat("Always (100%)", true, 1.0)
.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);
char name[MAX_TROLL_NAME_LENGTH];
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);
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);
return TE_Error; // Not an error, but don't want to show activation
} else if(StrEqual(name, "Slow Speed")) {
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")) {
} else if(StrEqual(name, "UziRules / AwpSmells")) {
DisableTroll(victim, "No Pickup");
DisableTroll(victim, "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, "Primary Disable");
SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup);
} else if(StrEqual(name, "CameTooEarly")) {
ReplyToCommand(activator, "This troll mode is not implemented.");
} else if(StrEqual(name, "KillMeSoftly")) {
static char wpn[32];
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() {
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.");
this.Reset();
return false;
@ -301,7 +301,7 @@ enum struct EditorData {
char component[16];
for(int i = 0; i < 4; 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
Format(component, sizeof(component), "%s %c", component, COLOR_INDEX[i]);
}

View file

@ -1,5 +1,9 @@
char g_pendingSaveName[64];
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:
* It's kinda messy. The main structs are:
@ -12,7 +16,123 @@ The rest are kinda necessary, for sorting reasons (SearchData, RecentEntry).
enum ChatPrompt {
Prompt_None,
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 {
ArrayList categoryStack;
@ -25,6 +145,9 @@ enum struct PlayerPropData {
char classnameOverride[64];
ChatPrompt chatPrompt;
ArrayList markedProps;
SaveType pendingSaveType;
Schematic schematic;
// Called on PlayerDisconnect
void Reset() {
@ -36,6 +159,15 @@ enum struct PlayerPropData {
this.lastActiveTime = 0;
this.classnameOverride[0] = '\0';
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
@ -97,6 +229,7 @@ enum struct PlayerPropData {
}
PlayerPropData g_PropData[MAXPLAYERS+1];
enum struct CategoryData {
// The display name of category
char name[64];
@ -124,6 +257,8 @@ enum struct SearchData {
strcopy(this.name, sizeof(this.name), item.name);
}
}
enum struct SaveData {
char model[128];
buildType type;
@ -148,6 +283,43 @@ enum struct SaveData {
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) {
Format(
output, maxlen, "%s,%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d,%d,%d",
@ -181,10 +353,6 @@ enum struct RecentEntry {
char name[64];
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/methods.sp>

View file

@ -12,6 +12,27 @@ Action Command_Props(int client, int args) {
PrintToConsole(client, "favorite - favorites active editor entity");
PrintToConsole(client, "controls - list all the controls");
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")) {
char arg2[16];
GetCmdArg(2, arg2, sizeof(arg2));

View file

@ -75,44 +75,89 @@ void AdminMenu_SaveLoad(TopMenu topmenu, TopMenuAction action, TopMenuObject obj
if(action == TopMenuAction_DisplayOption) {
Format(buffer, maxlength, "Save / Load");
} else if(action == TopMenuAction_SelectOption) {
Menu menu = new Menu(SaveLoadHandler);
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);
Spawn_ShowSaveLoadMainMenu(param);
}
}
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) {
char saveName[64];
menu.GetItem(param2, saveName, sizeof(saveName));
if(saveName[0] == '\0') {
// Save new
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);
} else {
PrintToChat(client, "\x04[Editor]\x01 Unable to save. Sorry.");
}
} else if(LoadSave(saveName, true)) {
strcopy(g_pendingSaveName, sizeof(g_pendingSaveName), saveName);
} else if(g_pendingSaveClient != 0 && g_pendingSaveClient != client) {
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) {
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 {
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");
}
} else {
@ -120,12 +165,31 @@ int SaveLoadHandler(Menu menu, MenuAction action, int client, int param2) {
}
} else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
Spawn_ShowSaveLoadMainMenu(client);
}
} else if (action == MenuAction_End)
delete menu;
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) {
if (action == MenuAction_Select) {
char info[8];

View file

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

View file

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

View file

@ -32,7 +32,9 @@ public Plugin myinfo =
};
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
// return APLRes_SilentFailure;
CreateNative("ApplyTroll", Native_ApplyTroll);
RegPluginLibrary("feedthetrolls");
return APLRes_Success;
}
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
/////////////////////////////
if(g_pendingSaveClient == client) {
if(buttons & IN_ZOOM) {
ClearSavePreview();
if(buttons & IN_SPEED) {
PrintToChat(client, "\x04[Editor]\x01 Loaded save \x05%s", g_pendingSaveName);
LoadSave(g_pendingSaveName, false);
}
if(g_PropData[client].pendingSaveType == Save_Schematic) {
// move cursor? or should be editor anyway
}
} else if(g_PropData[client].markedProps != null) {
SetWeaponDelay(client, 0.5);
@ -615,10 +611,10 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
cmdThrottle[client] = tick;
}
} else if(Editor[client].IsActive()) {
if(buttons & IN_USE && buttons & IN_RELOAD) {
ClientCommand(client, "sm_wall done");
return Plugin_Handled;
}
// if(buttons & IN_USE && buttons & IN_RELOAD) {
// ClientCommand(client, "sm_wall done");
// return Plugin_Handled;
// }
bool allowMove = true;
switch(Editor[client].mode) {
case MOVE_ORIGIN: {
@ -626,7 +622,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
bool isRotate;
int flags = GetEntityFlags(client);
if(buttons & IN_USE && ~buttons & IN_ZOOM) {
if(buttons & IN_RELOAD && ~buttons & IN_ZOOM) {
if(!g_inRotate[client]) {
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(buttons & IN_RELOAD)
Editor[client].CycleMode(); // R: Cycle forward
else if(buttons & IN_ZOOM) {
buttons &= ~IN_ZOOM;
if(buttons & IN_SPEED) {
int 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();
}
if(!(oldButtons & IN_USE) && buttons & IN_USE) {
if(buttons & IN_SPEED) {
Editor[client].Cancel();
} else if(buttons & IN_DUCK) {
if(Editor[client].flags & Edit_Preview)
Editor[client].CycleBuildType();
} else {
int entity;
Editor[client].Done(entity);
}
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);
@ -900,7 +893,7 @@ stock bool Filter_NoPlayers(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];
GetEntityClassname(entity, classname, sizeof(classname));
// 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) {
if(entity == data) return false;
if(entity <= MaxClients) {
if(entity <= MaxClients && entity > 0) {
int client = GetRealClient(data);
if(client == -1) client = data;
return CanTarget(client); // Don't target if player targetting off
}
return CheckBlacklist(entity);

View file

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